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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
|
/*
* Copyright (C) 2013 Tobias Brunner
* Hochschule fuer Technik Rapperswil
* Copyright (C) 2013 Martin Willi
* Copyright (C) 2013 revosec AG
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*/
/**
* @defgroup test_suite test_suite
* @{ @ingroup libtest
*/
#ifndef TEST_SUITE_H_
#define TEST_SUITE_H_
#define _GNU_SOURCE
#include <setjmp.h>
#include <library.h>
#include <utils/debug.h>
#include <utils/backtrace.h>
#include <collections/array.h>
typedef struct test_suite_t test_suite_t;
typedef struct test_case_t test_case_t;
typedef struct test_function_t test_function_t;
typedef struct test_fixture_t test_fixture_t;
/**
* Default timeout for a single test function
*/
#ifndef TEST_FUNCTION_DEFAULT_TIMEOUT
#define TEST_FUNCTION_DEFAULT_TIMEOUT 2
#endif
/**
* Test function implementation
*/
typedef void (*test_function_cb_t)(int);
/**
* Fixture for a test case.
*/
typedef void (*test_fixture_cb_t)(void);
/**
* A test suite; a collection of test cases with fixtures
*/
struct test_suite_t {
/** name of the test suite */
const char *name;
/** test cases registered, as test_case_t* */
array_t *tcases;
};
/**
* A test case; multiple test functions using the same fixtures
*/
struct test_case_t {
/** name of the test case */
const char *name;
/** tests registered, as test_function_t */
array_t *functions;
/** fixture for tests, as test_fixture_t */
array_t *fixtures;
/** timeout for each function, in s */
int timeout;
};
/**
* A test function, with optional loop setup
*/
struct test_function_t {
/** name of test function */
char *name;
/** tests function registered, test_function_t* */
test_function_cb_t cb;
/** start for loop test */
int start;
/** end for loop test */
int end;
};
/**
* Registered fixture for a test case
*/
struct test_fixture_t {
test_fixture_cb_t setup;
test_fixture_cb_t teardown;
};
/**
* Create a new test suite
*
* @param name name of the test suite
* @return test suite
*/
test_suite_t* test_suite_create(const char *name);
/**
* Create a new test case
*
* @param name name of test case
* @return test case
*/
test_case_t* test_case_create(const char *name);
/**
* Add a setup/teardown function to the test case
*
* @param tcase test case to add a fixture to
* @param setup setup function called before each test
* @param teardown cleanup function called after each test
*/
void test_case_add_checked_fixture(test_case_t *tcase, test_fixture_cb_t setup,
test_fixture_cb_t teardown);
/**
* Add a test function to a test case, with a name, looped several times
*
* @param name name of the test case
* @param tcase test case to add test function to
* @param cb callback function to invoke for test
* @param start start of loop counter
* @param end end of loop counter
*/
void test_case_add_test_name(test_case_t *tcase, char *name,
test_function_cb_t cb, int start, int end);
/**
* Add a test function to a test case
*
* @param tcase test case to add test function to
* @param cb callback function to invoke for test
*/
#define test_case_add_test(tcase, cb) \
test_case_add_test_name(tcase, #cb, cb, 0, 1)
/**
* Add a test function to a test case, looped several times
*
* @param tcase test case to add test function to
* @param cb callback function to invoke for test
* @param start start of loop counter
* @param end end of loop counter
*/
#define test_case_add_loop_test(tcase, cb, start, end) \
test_case_add_test_name(tcase, #cb, cb, start, end)
/**
* Set a custom timeout for test functions in a test case
*
* @param tcase test case to set timeout for
* @param s test timeout in s
*/
void test_case_set_timeout(test_case_t *tcase, int s);
/**
* Add a test function to a test case, looped several times
*
* @param suite test suite to add test case to
* @param tcase test case to add
*/
void test_suite_add_case(test_suite_t *suite, test_case_t *tcase);
/**
* sigjmp restore point used by test_restore_point
*/
#ifdef WIN32
extern jmp_buf test_restore_point_env;
#else
extern sigjmp_buf test_restore_point_env;
#endif
/**
* Set or return from an execution restore point
*
* This call sets a restore execution point and returns TRUE after it has
* been set up. On test failure, the execution is returned to the restore point
* and FALSE is returned to indicate test failure.
*
* @return TRUE if restore point set, FALSE when restored
*/
#ifdef WIN32
# define test_restore_point() (setjmp(test_restore_point_env) == 0)
#else
# define test_restore_point() (sigsetjmp(test_restore_point_env, 1) == 0)
#endif
/**
* Set up signal handlers for test cases
*/
void test_setup_handler();
/**
* Set up a timeout to let a test fail
*
* @param s timeout, 0 to disable timeout
*/
void test_setup_timeout(int s);
/**
* Get info about a test failure
*
* @param msg buffer receiving failure info
* @param len size of msg buffer
* @param file pointer receiving source code file
* @return source code line number
*/
int test_failure_get(char *msg, int len, const char **file);
/**
* Get a backtrace for a failure.
*
* @return allocated backtrace of test failure, if any
*/
backtrace_t *test_failure_backtrace();
/**
* Let a test fail and set a message using vprintf style arguments.
*
* @param file source code file name
* @param line source code line number
* @param fmt printf format string
* @param args argument list for fmt
*/
void test_fail_vmsg(const char *file, int line, char *fmt, va_list args);
/**
* Let a test fail and set a message using printf style arguments.
*
* @param file source code file name
* @param line source code line number
* @param fmt printf format string
* @param ... arguments for fmt
*/
void test_fail_msg(const char *file, int line, char *fmt, ...);
/**
* Let a test fail if one of the worker threads has failed (only if called from
* the main thread).
*/
void test_fail_if_worker_failed();
/**
* Check if two integers equal, fail test if not
*
* @param a first integer
* @param b second integer
*/
#define test_int_eq(a, b) \
({ \
typeof(a) _a = a; \
typeof(b) _b = b; \
test_fail_if_worker_failed(); \
if (_a != _b) \
{ \
test_fail_msg(__FILE__, __LINE__, #a " != " #b " (%d != %d)", _a, _b); \
} \
})
/**
* Check if two strings equal, fail test if not
*
* @param a first string
* @param b second string
*/
#define test_str_eq(a, b) \
({ \
char* _a = (char*)a; \
char* _b = (char*)b; \
test_fail_if_worker_failed(); \
if (!_a || !_b || !streq(_a, _b)) \
{ \
test_fail_msg(__FILE__, __LINE__, \
#a " != " #b " (\"%s\" != \"%s\")", _a, _b); \
} \
})
/**
* Check if two chunks are equal, fail test if not
*
* @param a first chunk
* @param b second chunk
*/
#define test_chunk_eq(a, b) \
({ \
chunk_t _a = (chunk_t)a; \
chunk_t _b = (chunk_t)b; \
test_fail_if_worker_failed(); \
if (_a.len != _b.len || !memeq(_a.ptr, _b.ptr, _a.len)) \
{ \
test_fail_msg(__FILE__, __LINE__, \
#a " != " #b " (\"%#B\" != \"%#B\")", &_a, &_b); \
} \
})
/**
* Check if a statement evaluates to TRUE, fail test if not
*
* @param x statement to evaluate
*/
#define test_assert(x) \
({ \
test_fail_if_worker_failed(); \
if (!(x)) \
{ \
test_fail_msg(__FILE__, __LINE__, "%s", #x); \
} \
})
/**
* Check if a statement evaluates to TRUE, fail and print a message if not
*
* @param x statement to evaluate
* @param fmt message format string
* @param ... fmt printf arguments
*/
#define test_assert_msg(x, fmt, ...) \
({ \
test_fail_if_worker_failed(); \
if (!(x)) \
{ \
test_fail_msg(__FILE__, __LINE__, "%s: " fmt, #x, ##__VA_ARGS__); \
} \
})
/* "check unit testing" compatibility */
#define Suite test_suite_t
#define TCase test_case_t
#define ck_assert_int_eq test_int_eq
#define ck_assert test_assert
#define ck_assert_msg test_assert_msg
#define ck_assert_str_eq test_str_eq
#define ck_assert_chunk_eq test_chunk_eq
#define fail(fmt, ...) test_fail_msg(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
#define fail_if(x, fmt, ...) \
({ \
test_fail_if_worker_failed(); \
if (x) \
{ \
test_fail_msg(__FILE__, __LINE__, "%s : " fmt, #x, ##__VA_ARGS__); \
} \
})
#define fail_unless test_assert_msg
#define suite_create test_suite_create
#define tcase_create test_case_create
#define tcase_add_checked_fixture test_case_add_checked_fixture
#define tcase_add_test test_case_add_test
#define tcase_add_loop_test test_case_add_loop_test
#define tcase_set_timeout test_case_set_timeout
#define suite_add_tcase test_suite_add_case
#define START_TEST(name) static void name (int _i) {
#define END_TEST test_fail_if_worker_failed(); }
#define START_SETUP(name) static void name() {
#define END_SETUP test_fail_if_worker_failed(); }
#define START_TEARDOWN(name) static void name() {
#define END_TEARDOWN test_fail_if_worker_failed(); }
#endif /** TEST_SUITE_H_ @}*/
|