aboutsummaryrefslogtreecommitdiffstats
path: root/README.md
blob: 0fffdc3e7b72997d08cc17134547d34243e1ad06 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
simple-test
===========

[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
they 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
-------------

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 tests.


example
-------

the following example file is available as [example.c](doc/example.c), which
you can compile and play with yourself.

```C
/* SIMPLE_TEST_PRINT_FUN may be redefined like so. default is `printf` */
#define stderr_redirect(...) fprintf(stderr, __VA_ARGS__)
#define SIMPLE_TEST_PRINT_FUN stderr_redirect

/* colour output is on by default, but can be disabled */
/* #define SIMPLE_TEST_USE_COLOUR false */

#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 statement.
 * 
 * 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 mismatched 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
```

on compiling and running this file, the output will look something like this:

![run 1](doc/run_01.png)

an error occurs because the types passed to the second assertion in the "type
mismatch" test are... well, mismatched. assertions do their 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 that assertion is commented out:

```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); */
```

and the tests re-run, the output would look like this:

![run 2](doc/run_02.png)

as you can see, there are multiple failing assertions. 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)

hooray! ^_^


interface
---------

simple-test's interface consists of a series of preprocessor macros:

| **NAME** | **DESCRIPTION** |
|---------:|:----------------|
| **SIMPLE_TEST_USE_COLOUR** | may be defined, before the header is included, as either a `true` or `false` resolving expression. default is `true` |
| **SIMPLE_TEST_PRINT_FUN** | may be defined, before the header is included, as a function or macro which takes `printf` style arguments. default is `printf` |
| **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 |

assertions are used to check that the behaviour of code executed inside the
body of a `TEST` statement is correct. values are passed and the assertion
fails if those values do not reflect expectations. valid assertions are:

| **NAME** | **DESCRIPTION** |
|---------:|:----------------|
| **ASSERT(a)** | assert that the value of `a` is not a "not" value, i.e. NULL, 0, or '\0' |
| **ASSERT_NOT(a)** | assert that the value of `a` is a "not" value |
| **ASSERT_EQ(a, b)** | fail if the values of `a` and `b` are not equal |
| **ASSERT_NEQ(a, b)** | fail if the values of `a` and `b` are equal |
| **ASSERT_GT(a, b)** | fail if the value of `a` is not greater the value of `b` |
| **ASSERT_GEQ(a, b)** | fail if the value of `a` is not greater than or equal to the value of `b` |
| **ASSERT_LT(a, b)** | fail if the value of `a` is not less than the value of `b` |
| **ASSERT_LEQ(a, b)** | fail if the value of `a` is not less than or equal to the value of `b` |


note
----

simple-test.h internally uses macros prefixed with `SIMPLE_TEST_` and functions
and variables prefixed with `simple_test_`. these should not be used directly.