aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md246
-rw-r--r--doc/a_simple_example.c68
-rw-r--r--doc/example.c103
-rw-r--r--doc/run-01.pngbin2766 -> 0 bytes
-rw-r--r--doc/run-02.pngbin1065 -> 0 bytes
-rw-r--r--doc/run-03.pngbin1273 -> 0 bytes
-rw-r--r--doc/run_01.pngbin0 -> 21120 bytes
-rw-r--r--doc/run_02.pngbin0 -> 34615 bytes
-rw-r--r--doc/run_03.pngbin0 -> 16912 bytes
-rw-r--r--simple-test.h (renamed from src/simple-test.h)1
10 files changed, 247 insertions, 171 deletions
diff --git a/README.md b/README.md
index e6709cf..619972d 100644
--- a/README.md
+++ b/README.md
@@ -1,154 +1,194 @@
simple-test
===========
-simple unit testing for C implemented as a single header file
+[simple-test.h](simple-test.h) is a single header file which implements
+quick-and-dirty unit testing for C. robust unit testing suites for C exist, but
+require a degree of boilerplate which can be prohibitive for their use in
+spare-time projects. if you're writing something for fun, lots of boilerplate
+defeats the purpose of writing it. simple-test is made to be as easy to use as
+possible: include the header, compile, and run.
+
+_note:_ although it has no dependencies outside the standard library, this
+project makes heavy use of `_Generic`, so C11 support is required.
+
brief summary
-------------
-Your tests should be written as a single .c file separate from
-the body of text containing your functionality to be tested.
-Write the tests, include simple-test.h, point your compiler at
-the necessary files / libs, and you're done!
+test suites are to be written as a single .c file separate from the body of
+functionality to be tested. include the functionality needed (including headers
+and linking against objects) to produce an executable file from the .c file,
+then run the executable to test. simple-test internally defines a `main`
+function, so none is required, and the `argc` and `argv` arguments are made
+available to all `TEST`s.
-an example
-----------
-consider the following simple program:
+example
+-------
-```c
-/* keep this include at the very top of the file */
-#include "simple-test.h"
+the following example file is available as [example.c](doc/example.c), which
+you can compile and play with yourself.
-/* any global variables, functions, other inclusions, etc.
- * should be declared here */
-#include "header_with_stuff_to_be_tested.h"
+```C
+#include "../simple-test.h"
-int add_here(int a, int b)
+/* global variables, functions, and includes must come before BEGIN_TEST */
+char *global_s;
+
+/* a teardown function is called by TESTs which include USE_TEARDOWN. the call
+ * occurs either after an ASSERT fails or after the TEST is finished.
+ *
+ * defining one is optional, but, if used, REGISTER_TEARDOWN must appear
+ * between BEGIN_TEST and the first TEST
+ *
+ * here i'm using it to free memory that's alloced inside tests below */
+void teardown(void)
{
- /* ASSERT / ECHO statements within functions
- * like this are perfectly valid. this is
- * useful for writing initialisation functions
- * called at the beginning of multiple TESTs */
- ECHO("this is the local add");
+ free(global_s);
+}
- /* ensure a and b are non-0 using generic ASSERT */
- ASSERT(a);
- ASSERT(b);
+/* must appear before an (optional) REGISTER_TEARDOWN and all TESTs */
+BEGIN_TEST
+
+/* if used, must appear before first TEST and after BEGIN_TEST */
+REGISTER_TEARDOWN(teardown);
- return a + b;
+/* run a test. provided description must be a string literal */
+TEST("basic assertion")
+{
+ /* ASSERT fails if passed some sort of non-true value (0, false, NULL) */
+ ASSERT(1);
}
-/* must come before all TESTs */
-BEGIN_TEST
+TEST("basic not assertion")
+{
+ bool b = false;
+
+ ASSERT_NOT(b);
+}
-/* the string is a description of the test being run */
-TEST("check add()'s return value")
+TEST("boolean comparison")
{
- /* mixing different precisions is allowed */
- long var1 = 2;
- int8_t var2 = 2;
+ bool a = false, b = true;
- /* add is a function included from our hypothetical
- * header_with_stuff_to_be_tested.h */
- ASSERT_INT_EQ(var1+var2, add(var1, var2));
+ /* fail if parameters are not equal */
+ ASSERT_EQ(a, b);
+}
- /* generic versions of ASSERTions are also valid,
- * but only for number types (int / uint / float ) */
- ASSERT_EQ(var1+var2, add_here(var1, var2));
+TEST("type mismatch")
+{
+ char a = 'a';
+ int i = 97;
+ char *b = NULL;
+
+ /* for convenience's sake, when presented with unmatched types, assertions
+ * try to resolve them in a way that's most likely to match the
+ * programmer's intentions. here 'i' is interpreted as a char */
+ ASSERT_EQ(a, i);
+
+ /* if there isn't a straightforward comparison to make, though (as is the
+ * case here, with a 'char' and 'char *'), a type mismatch error occurs */
+ ASSERT_EQ(a, b);
}
-TEST("compare two arrays of strings")
+TEST("ECHO example")
{
int i;
- char array1[][10] = {
- "str1",
- "str2",
- "str3",
- };
- char array2[][10] = {
- "str1",
- "str2",
- /* matching will fail here */
- "different",
- };
-
- for(i = 0; i < sizeof(array1) / sizeof(char[10]); i++) {
- /* ECHO can be used to print (with pretty
- * formatting) the current state within the
- * test */
- ECHO("checking strs at i == %i", i);
-
- ASSERT_STR_EQ(array1[i], array2[i]);
+
+ /* ECHO can be used to neatly report information during a run */
+ if (true)
+ ECHO("loop until i not less than 1");
+
+ for (i = 0; i < 2; i++) {
+ /* it takes printf format strings and variable args as well */
+ ECHO("i == %d", i);
+ ASSERT_LT(i, 1);
}
}
+TEST("string comparison")
+{
+ char *s = "test";
+ global_s = strdup("test");
+
+ /* USE_TEARDOWN; tells this test to call the previously defined teardown
+ * function on exiting (successfully or otherwise) */
+ USE_TEARDOWN;
+
+ /* strings are compared by content, so this assertion succeeds */
+ ASSERT_EQ(s, global_s);
+}
+
+TEST("pointer comparison")
+{
+ char *s = "test";
+ global_s = strdup("test");
+ USE_TEARDOWN;
+
+ /* you can cast parameters in order to use a different type of comparison.
+ * here i'm casting the 'char *' to 'void *' so assertion performs a
+ * pointer comparison, which fails */
+ ASSERT_EQ((void*)s, (void*)global_s);
+}
+
/* must come after all TESTs */
END_TEST
```
-running this test, the output might look something like this:
+on compiling and running this file, the output will look something like this:
+
+![run 1](doc/run_01.png)
-![run 1](doc/run-01.png)
+an error, because the types passed to the second assertion in the "type
+mismatch" test are... well, mismatched. simple-test does its best to
+accommodate mismatched types when it makes sense to do so (e.g. comparing a
+signed and unsigned integer), but there is no meaningful comparison to be made
+between a `char` and a `char *`, so the error is returned.
-if var1 in the first test were changed to 0, like so:
+if that assertion is commented out:
-```c
- /* mixing different precisions is allowed */
- long var1 = 0;
- int8_t var2 = 2;
+```C
+ /* if there isn't a straightforward comparison to make, though (as is the
+ * case here, with a 'char' and 'char *'), a type mismatch error occurs */
+ /* ASSERT_EQ(a, b); */
```
-ASSERT(a); in add_here() would fail like this:
+and the tests re-run, the output would look like this:
-![run 2](doc/run-02.png)
+![run 2](doc/run_02.png)
-if array2 were modified instead so the third strings would match,
-a successful output would look like this:
+as you can see, there are multiple failing assertions, and their failures are
+reported and a total count of failing tests printed. if all of these failing
+asserts are commented out and the tests re-run, the output looks like this:
-![run 3](doc/run-03.png)
+![run 3](doc/run_03.png)
+hooray! ^_^
-defined macros
---------------
+
+interface
+---------
+
+simple-test's interface consists of a series of preprocessor macros:
| **NAME** | **DESCRIPTION** |
|---------:|:----------------|
-| **BEGIN_TEST** | must be included once, after includes and global declarations and before the first TEST statement |
-| **END_TEST** | must be included once, after the final TEST statement |
+| **BEGIN_TEST** | must be included once, after includes and global declarations and before the first `TEST` statement |
+| **END_TEST** | must be included once, after the final `TEST` statement |
| **TEST(<description>)**{} | declare a test, described by `description`, which consists of statements between the {} |
+| **REGISTER_TEARDOWN(<func>)** | optionally included after `BEGIN_TEST` and before the first `TEST` statement. registers function `func`, which must be of type `void func(void)`, as a teardown function to be called after subscribing tests exit |
+| **USE_TEARDOWN;** | if included within a `TEST` body (before any `ASSERT` statements), the designated `TEST` will call the function registered with `REGISTER_TEARDOWN` on terminating |
| **ECHO(<...>)** | print a formatted description of the state within the current test |
-| **ASSERT<_type_name><_condition>(<args>)** | assert, on either one or two `args` of type `_type_name`, that `_condition` is true |
+| **ASSERT<_condition>(<args>)** | assert, on either one or two `args`, that `_condition` is true |
-valid `_type_name` and `_condition` values for ASSERT statements are:
-
-| **_type_name** | **DESCRIPTION** |
-|---------------:|:----------------|
-| | if the _type_name is omitted, generic assertions which automatically determine type are used. valid only for number types, i.e. int, unsigned, double. |
-| **_BOOL** | operate on args of type `bool` |
-| **_INT** | operate on signed integer types |
-| **_UINT** | operate on unsigned integer types |
-| **_HEX** | operate on unsigned integer types. output with %X formatting |
-| **_FLOAT** | operate on floating point types |
-| **_PTR** | operate on generic pointer types. casts to void* |
-| **_CHAR** | operate on args of type `char` |
-| **_STR** | operate on strings pointed to by args of type `char*`. |
-| **_WCHAR** | operate on wide character types |
-| **_WSTR** | operate on wide strings pointed to by args of type `wchar_t*` |
+valid values for `_condition` are:
| **_condition** | **DESCRIPTION** |
|---------------:|:----------------|
-| | if the _condition is omitted, fail if the single arg is a "not" value, i.e. NULL, 0, '\0' etc. |
-| **_EQ** | fail if the two args are not equal |
-| **_NEQ** | fail if the two args are equal |
-| **_G** | fail if the first arg is not greater than the second |
-| **_GEQ** | fail if the first arg is not greater than or equal to the second |
-| **_L** | fail if the first arg is not less than the second |
-| **_LEQ** | fail if the first arg is not less than or equal to the second |
-
-note
-----
-
-this makes heavy use of _Generic, so C11 support is required
-
-tested successfully with both gcc and clang
+| | if the _condition is omitted, fail if the single argument is a "not" value, i.e. NULL, 0, '\0' etc. |
+| **_EQ** | fail if the two arguments are not equal |
+| **_NEQ** | fail if the two arguments are equal |
+| **_GT** | fail if the first argument is not greater than the second |
+| **_GEQ** | fail if the first argument is not greater than or equal to the second |
+| **_LT** | fail if the first argument is not less than the second |
+| **_LEQ** | fail if the first argument is not less than or equal to the second |
diff --git a/doc/a_simple_example.c b/doc/a_simple_example.c
deleted file mode 100644
index 6784713..0000000
--- a/doc/a_simple_example.c
+++ /dev/null
@@ -1,68 +0,0 @@
-/* keep this include at the very top of the file */
-#include "../src/simple-test.h"
-
-/* any global variables, functions, other inclusions, etc.
- * should be declared here */
-#include "header_with_stuff_to_be_tested.h"
-
-int add_here(int a, int b)
-{
- /* ASSERT / ECHO statements within functions
- * like this are perfectly valid. this is
- * useful for writing initialisation functions
- * called at the beginning of multiple TESTs */
- ECHO("this is the local add");
-
- /* ensure a and b are non-0 using generic ASSERT */
- ASSERT(a);
- ASSERT(b);
-
- return a + b;
-}
-
-/* must come before all TESTs */
-BEGIN_TEST
-
-/* the string is a description of the test being run */
-TEST("check add()'s return value")
-{
- /* mixing different precisions is allowed */
- long var1 = 2;
- int8_t var2 = 2;
-
- /* add is a function included from our hypothetical
- * header_with_stuff_to_be_tested.h */
- ASSERT_INT_EQ(var1+var2, add(var1, var2));
-
- /* generic versions of ASSERTions are also valid,
- * but only for number types (int / uint / float ) */
- ASSERT_EQ(var1+var2, add_here(var1, var2));
-}
-
-TEST("compare two arrays of strings")
-{
- int i;
- char array1[][10] = {
- "str1",
- "str2",
- "str3",
- };
- char array2[][10] = {
- "str1",
- "str2",
- /* matching will fail here */
- "different",
- };
-
- for(i = 0; i < sizeof(array1) / sizeof(char[10]); i++) {
- /* ECHO can be used to print (with pretty
- * formatting) the current state within the
- * test */
- ECHO("checking strs at i == %i", i);
-
- ASSERT_STR_EQ(array1[i], array2[i]);
- }
-}
-
-/* must come after all TESTs */
-END_TEST
diff --git a/doc/example.c b/doc/example.c
new file mode 100644
index 0000000..26d8f66
--- /dev/null
+++ b/doc/example.c
@@ -0,0 +1,103 @@
+#include "../simple-test.h"
+
+/* global variables, functions, and includes must come before BEGIN_TEST */
+char *global_s;
+
+/* a teardown function is called by TESTs which include USE_TEARDOWN. the call
+ * occurs either after an ASSERT fails or after the TEST is finished.
+ *
+ * defining one is optional, but, if used, REGISTER_TEARDOWN must appear
+ * between BEGIN_TEST and the first TEST
+ *
+ * here i'm using it to free memory that's alloced inside tests below */
+void teardown(void)
+{
+ free(global_s);
+}
+
+/* must appear before an (optional) REGISTER_TEARDOWN and all TESTs */
+BEGIN_TEST
+
+/* if used, must appear before first TEST and after BEGIN_TEST */
+REGISTER_TEARDOWN(teardown);
+
+/* run a test. provided description must be a string literal */
+TEST("basic assertion")
+{
+ /* ASSERT fails if passed some sort of non-true value (0, false, NULL) */
+ ASSERT(1);
+}
+
+TEST("basic not assertion")
+{
+ bool b = false;
+
+ ASSERT_NOT(b);
+}
+
+TEST("boolean comparison")
+{
+ bool a = false, b = true;
+
+ /* fail if parameters are not equal */
+ ASSERT_EQ(a, b);
+}
+
+TEST("type mismatch")
+{
+ char a = 'a';
+ int i = 97;
+ char *b = NULL;
+
+ /* for convenience's sake, when presented with unmatched types, assertions
+ * try to resolve them in a way that's most likely to match the
+ * programmer's intentions. here 'i' is interpreted as a char */
+ ASSERT_EQ(a, i);
+
+ /* if there isn't a straightforward comparison to make, though (as is the
+ * case here, with a 'char' and 'char *'), a type mismatch error occurs */
+ ASSERT_EQ(a, b);
+}
+
+TEST("ECHO example")
+{
+ int i;
+
+ /* ECHO can be used to neatly report information during a run */
+ if (true)
+ ECHO("loop until i not less than 1");
+
+ for (i = 0; i < 2; i++) {
+ /* it takes printf format strings and variable args as well */
+ ECHO("i == %d", i);
+ ASSERT_LT(i, 1);
+ }
+}
+
+TEST("string comparison")
+{
+ char *s = "test";
+ global_s = strdup("test");
+
+ /* USE_TEARDOWN; tells this test to call the previously defined teardown
+ * function on exiting (successfully or otherwise) */
+ USE_TEARDOWN;
+
+ /* strings are compared by content, so this assertion succeeds */
+ ASSERT_EQ(s, global_s);
+}
+
+TEST("pointer comparison")
+{
+ char *s = "test";
+ global_s = strdup("test");
+ USE_TEARDOWN;
+
+ /* you can cast parameters in order to use a different type of comparison.
+ * here i'm casting the 'char *' to 'void *' so assertion performs a
+ * pointer comparison, which fails */
+ ASSERT_EQ((void*)s, (void*)global_s);
+}
+
+/* must come after all TESTs */
+END_TEST
diff --git a/doc/run-01.png b/doc/run-01.png
deleted file mode 100644
index b7c6838..0000000
--- a/doc/run-01.png
+++ /dev/null
Binary files differ
diff --git a/doc/run-02.png b/doc/run-02.png
deleted file mode 100644
index aedd395..0000000
--- a/doc/run-02.png
+++ /dev/null
Binary files differ
diff --git a/doc/run-03.png b/doc/run-03.png
deleted file mode 100644
index f6843dc..0000000
--- a/doc/run-03.png
+++ /dev/null
Binary files differ
diff --git a/doc/run_01.png b/doc/run_01.png
new file mode 100644
index 0000000..df74574
--- /dev/null
+++ b/doc/run_01.png
Binary files differ
diff --git a/doc/run_02.png b/doc/run_02.png
new file mode 100644
index 0000000..b84d5e5
--- /dev/null
+++ b/doc/run_02.png
Binary files differ
diff --git a/doc/run_03.png b/doc/run_03.png
new file mode 100644
index 0000000..05fd99c
--- /dev/null
+++ b/doc/run_03.png
Binary files differ
diff --git a/src/simple-test.h b/simple-test.h
index 63da668..fa05d36 100644
--- a/src/simple-test.h
+++ b/simple-test.h
@@ -300,6 +300,7 @@ static enum simple_test_type simple_test_type_resolve(enum simple_test_type t1,
simple_test_pad_width, ' ', __LINE__); \
printf(__VA_ARGS__); \
printf("\n"); \
+ printf("\e[1;31mtesting aborted\e[m\n"); \
exit(1); \
} while (0)