1 /* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
2
3 #include "lib.h"
4 #include "test-common.h"
5
6 #include <stdio.h>
7 #include <setjmp.h> /* for fatal tests */
8
9 static bool test_deinit_lib;
10
11 /* To test the firing of i_assert, we need non-local jumps, i.e. setjmp */
12 static volatile bool expecting_fatal = FALSE;
13 static jmp_buf fatal_jmpbuf;
14
15 #define OUT_NAME_ALIGN 70
16
17 static char *test_prefix;
18 static bool test_success;
19 static unsigned int failure_count;
20 static unsigned int total_count;
21 static unsigned int expected_errors;
22 static char *expected_error_str, *expected_fatal_str;
23 static test_fatal_callback_t *test_fatal_callback;
24 static void *test_fatal_context;
25
test_begin(const char * name)26 void test_begin(const char *name)
27 {
28 test_success = TRUE;
29 expected_errors = 0;
30 if (!expecting_fatal)
31 i_assert(test_prefix == NULL);
32 else
33 test_assert((test_success = (test_prefix == NULL)));
34 test_prefix = i_strdup(name);
35 }
36
test_has_failed(void)37 bool test_has_failed(void)
38 {
39 return !test_success;
40 }
41
test_assert_failed(const char * code,const char * file,unsigned int line)42 void test_assert_failed(const char *code, const char *file, unsigned int line)
43 {
44 printf("%s:%u: Assert failed: %s\n", file, line, code);
45 fflush(stdout);
46 test_success = FALSE;
47 #ifdef STATIC_CHECKER
48 i_unreached();
49 #endif
50 }
51
test_assert_failed_idx(const char * code,const char * file,unsigned int line,long long i)52 void test_assert_failed_idx(const char *code, const char *file, unsigned int line, long long i)
53 {
54 printf("%s:%u: Assert(#%lld) failed: %s\n", file, line, i, code);
55 fflush(stdout);
56 test_success = FALSE;
57 #ifdef STATIC_CHECKER
58 i_unreached();
59 #endif
60 }
61
test_assert_failed_strcmp_idx(const char * code,const char * file,unsigned int line,const char * src,const char * dst,long long i)62 void test_assert_failed_strcmp_idx(const char *code, const char *file, unsigned int line,
63 const char * src, const char * dst, long long i)
64 {
65 printf("%s:%u: Assert", file, line);
66 if (i == LLONG_MIN)
67 printf(" failed: %s\n", code);
68 else
69 printf("(#%lld) failed: %s\n", i, code);
70 if (src != NULL)
71 printf(" \"%s\" != ", src);
72 else
73 printf(" NULL != ");
74 if (dst != NULL)
75 printf("\"%s\"\n", dst);
76 else
77 printf("NULL\n");
78 fflush(stdout);
79 test_success = FALSE;
80 #ifdef STATIC_CHECKER
81 i_unreached();
82 #endif
83 }
84
test_assert_failed_cmp_intmax_idx(const char * code,const char * file,unsigned int line,intmax_t src,intmax_t dst,const char * op,long long i)85 void test_assert_failed_cmp_intmax_idx(const char *code, const char *file,
86 unsigned int line,
87 intmax_t src, intmax_t dst,
88 const char *op, long long i)
89 {
90 printf("%s:%u: Assert", file, line);
91 if (i == LLONG_MIN)
92 printf(" failed: %s\n", code);
93 else
94 printf("(#%lld) failed: %s\n", i, code);
95 printf(" %jd %s %jd is not true\n", src, op, dst);
96 fflush(stdout);
97 test_success = FALSE;
98 #ifdef STATIC_CHECKER
99 i_unreached();
100 #endif
101 }
102
test_assert_failed_ucmp_intmax_idx(const char * code,const char * file,unsigned int line,uintmax_t src,uintmax_t dst,const char * op,long long i)103 void test_assert_failed_ucmp_intmax_idx(const char *code, const char *file,
104 unsigned int line,
105 uintmax_t src, uintmax_t dst,
106 const char *op, long long i)
107 {
108 printf("%s:%u: Assert", file, line);
109 if (i == LLONG_MIN)
110 printf(" failed: %s\n", code);
111 else
112 printf("(#%lld) failed: %s\n", i, code);
113 printf(" %ju %s %ju is not true\n", src, op, dst);
114 fflush(stdout);
115 test_success = FALSE;
116 #ifdef STATIC_CHECKER
117 i_unreached();
118 #endif
119 }
120
121 #ifdef DEBUG
122 #include "randgen.h"
123 static void
test_dump_rand_state(void)124 test_dump_rand_state(void)
125 {
126 static int64_t seen_seed = -1;
127 unsigned int seed;
128 if (rand_get_last_seed(&seed) < 0) {
129 if (seen_seed == -1) {
130 printf("test: random sequence not reproduceable, use DOVECOT_SRAND=kiss\n");
131 seen_seed = -2;
132 }
133 return;
134 }
135 if (seed == seen_seed)
136 return;
137 seen_seed = seed;
138 printf("test: DOVECOT_SRAND random seed was %u\n", seed);
139 }
140 #else
test_dump_rand_state(void)141 static inline void test_dump_rand_state(void) { }
142 #endif
143
test_end(void)144 void test_end(void)
145 {
146 if (!expecting_fatal)
147 i_assert(test_prefix != NULL);
148 else
149 test_assert(test_prefix != NULL);
150
151 test_out("", test_success);
152 if (!test_success)
153 test_dump_rand_state();
154 i_free_and_null(test_prefix);
155 test_success = FALSE;
156 }
157
test_out(const char * name,bool success)158 void test_out(const char *name, bool success)
159 {
160 test_out_reason(name, success, NULL);
161 }
162
test_out_quiet(const char * name,bool success)163 void test_out_quiet(const char *name, bool success)
164 {
165 if (success) {
166 total_count++;
167 return;
168 }
169 test_out(name, success);
170 }
171
test_out_reason(const char * name,bool success,const char * reason)172 void test_out_reason(const char *name, bool success, const char *reason)
173 {
174 int i = 0;
175
176 if (test_prefix != NULL) {
177 fputs(test_prefix, stdout);
178 i += strlen(test_prefix);
179 if (*name != '\0') {
180 putchar(':');
181 i++;
182 }
183 putchar(' ');
184 i++;
185 }
186 if (*name != '\0') {
187 fputs(name, stdout);
188 putchar(' ');
189 i += strlen(name) + 1;
190 }
191 for (; i < OUT_NAME_ALIGN; i++)
192 putchar('.');
193 fputs(" : ", stdout);
194 if (success)
195 fputs("ok", stdout);
196 else {
197 fputs("FAILED", stdout);
198 test_success = FALSE;
199 failure_count++;
200 }
201 if (reason != NULL && *reason != '\0')
202 printf(": %s", reason);
203 putchar('\n');
204 fflush(stdout);
205 total_count++;
206 }
207
208 void
test_expect_error_string_n_times(const char * substr,unsigned int times)209 test_expect_error_string_n_times(const char *substr, unsigned int times)
210 {
211 i_assert(expected_errors == 0);
212 expected_errors = times;
213 expected_error_str = i_strdup(substr);
214 }
215 void
test_expect_error_string(const char * substr)216 test_expect_error_string(const char *substr)
217 {
218 test_expect_error_string_n_times(substr, 1);
219 }
220 void
test_expect_errors(unsigned int expected)221 test_expect_errors(unsigned int expected)
222 {
223 i_assert(expected_errors == 0);
224 expected_errors = expected;
225 }
226 void
test_expect_no_more_errors(void)227 test_expect_no_more_errors(void)
228 {
229 test_assert(expected_errors == 0 && expected_error_str == NULL);
230 i_free_and_null(expected_error_str);
231 expected_errors = 0;
232 }
233
234 static bool ATTR_FORMAT(2, 0)
expect_error_check(char ** error_strp,const char * format,va_list args)235 expect_error_check(char **error_strp, const char *format, va_list args)
236 {
237 if (*error_strp == NULL)
238 return TRUE;
239
240 bool suppress;
241 T_BEGIN {
242 /* test_assert() will reset test_success if need be. */
243 const char *str = t_strdup_vprintf(format, args);
244 suppress = strstr(str, *error_strp) != NULL;
245 test_assert(suppress == TRUE);
246 i_free_and_null(*error_strp);
247 } T_END;
248 return suppress;
249 }
250
251 static void ATTR_FORMAT(2, 0)
test_error_handler(const struct failure_context * ctx,const char * format,va_list args)252 test_error_handler(const struct failure_context *ctx,
253 const char *format, va_list args)
254 {
255 bool suppress = FALSE;
256
257 if (expected_errors > 0) {
258 va_list args2;
259 VA_COPY(args2, args);
260 suppress = expect_error_check(&expected_error_str, format, args2);
261 expected_errors--;
262 va_end(args2);
263 } else {
264 test_success = FALSE;
265 }
266
267 if (!suppress) {
268 test_dump_rand_state();
269 default_error_handler(ctx, format, args);
270 }
271 }
272
test_expect_fatal_string(const char * substr)273 void test_expect_fatal_string(const char *substr)
274 {
275 i_free(expected_fatal_str);
276 expected_fatal_str = i_strdup(substr);
277 }
278
279 #undef test_fatal_set_callback
test_fatal_set_callback(test_fatal_callback_t * callback,void * context)280 void test_fatal_set_callback(test_fatal_callback_t *callback, void *context)
281 {
282 i_assert(test_fatal_callback == NULL);
283 test_fatal_callback = callback;
284 test_fatal_context = context;
285 }
286
287 static void ATTR_FORMAT(2, 0) ATTR_NORETURN
test_fatal_handler(const struct failure_context * ctx,const char * format,va_list args)288 test_fatal_handler(const struct failure_context *ctx,
289 const char *format, va_list args)
290 {
291 /* Prevent recursion, we can't handle our own errors */
292 i_set_fatal_handler(default_fatal_handler);
293 i_assert(expecting_fatal); /* if not at the right time, bail */
294
295 va_list args2;
296 VA_COPY(args2, args);
297 bool suppress = expect_error_check(&expected_fatal_str, format, args2);
298 va_end(args);
299
300 if (suppress) {
301 if (test_fatal_callback != NULL) {
302 test_fatal_callback(test_fatal_context);
303 test_fatal_callback = NULL;
304 test_fatal_context = NULL;
305 }
306
307 i_set_fatal_handler(test_fatal_handler);
308 longjmp(fatal_jmpbuf, 1);
309 } else {
310 default_fatal_handler(ctx, format, args);
311 }
312 i_unreached(); /* we simply can't get here */
313 }
314
test_init(void)315 static void test_init(void)
316 {
317 test_prefix = NULL;
318 failure_count = 0;
319 total_count = 0;
320
321 if (!lib_is_initialized()) {
322 lib_init();
323 test_deinit_lib = TRUE;
324 } else
325 test_deinit_lib = FALSE;
326
327 i_set_error_handler(test_error_handler);
328 /* Don't set fatal handler until actually needed for fatal testing */
329 }
330
test_deinit(void)331 static int test_deinit(void)
332 {
333 i_assert(test_prefix == NULL);
334 printf("%u / %u tests failed\n", failure_count, total_count);
335 if (test_deinit_lib)
336 lib_deinit();
337 return failure_count == 0 ? 0 : 1;
338 }
339
test_run_funcs(void (* const test_functions[])(void))340 static void test_run_funcs(void (*const test_functions[])(void))
341 {
342 unsigned int i;
343
344 for (i = 0; test_functions[i] != NULL; i++) {
345 T_BEGIN {
346 test_functions[i]();
347 } T_END;
348 }
349 }
test_run_named_funcs(const struct named_test tests[],const char * match)350 static void test_run_named_funcs(const struct named_test tests[],
351 const char *match)
352 {
353 unsigned int i;
354
355 for (i = 0; tests[i].func != NULL; i++) {
356 if (strstr(tests[i].name, match) != NULL) T_BEGIN {
357 tests[i].func();
358 } T_END;
359 }
360 }
361
run_one_fatal(test_fatal_func_t * fatal_function)362 static void run_one_fatal(test_fatal_func_t *fatal_function)
363 {
364 static unsigned int index = 0;
365 for (;;) {
366 volatile int jumped = setjmp(fatal_jmpbuf);
367 if (jumped == 0) {
368 /* normal flow */
369 expecting_fatal = TRUE;
370 enum fatal_test_state ret = fatal_function(index);
371 expecting_fatal = FALSE;
372 if (ret == FATAL_TEST_FINISHED) {
373 /* ran out of tests - good */
374 index = 0;
375 break;
376 } else if (ret == FATAL_TEST_FAILURE) {
377 /* failed to fire assert - bad, but can continue */
378 test_success = FALSE;
379 i_error("Desired assert failed to fire at step %i", index);
380 index++;
381 } else { /* FATAL_TEST_ABORT or other value */
382 test_success = FALSE;
383 test_end();
384 index = 0;
385 break;
386 }
387 } else {
388 /* assert fired, continue with next test */
389 index++;
390 }
391 }
392 }
test_run_fatals(test_fatal_func_t * const fatal_functions[])393 static void test_run_fatals(test_fatal_func_t *const fatal_functions[])
394 {
395 unsigned int i;
396
397 for (i = 0; fatal_functions[i] != NULL; i++) {
398 T_BEGIN {
399 run_one_fatal(fatal_functions[i]);
400 } T_END;
401 }
402 }
test_run_named_fatals(const struct named_fatal fatals[],const char * match)403 static void test_run_named_fatals(const struct named_fatal fatals[], const char *match)
404 {
405 unsigned int i;
406
407 for (i = 0; fatals[i].func != NULL; i++) {
408 if (strstr(fatals[i].name, match) != NULL) T_BEGIN {
409 run_one_fatal(fatals[i].func);
410 } T_END;
411 }
412 }
413
test_run(void (* const test_functions[])(void))414 int test_run(void (*const test_functions[])(void))
415 {
416 test_init();
417 test_run_funcs(test_functions);
418 return test_deinit();
419 }
test_run_named(const struct named_test tests[],const char * match)420 int test_run_named(const struct named_test tests[], const char *match)
421 {
422 test_init();
423 test_run_named_funcs(tests, match);
424 return test_deinit();
425 }
test_run_with_fatals(void (* const test_functions[])(void),test_fatal_func_t * const fatal_functions[])426 int test_run_with_fatals(void (*const test_functions[])(void),
427 test_fatal_func_t *const fatal_functions[])
428 {
429 test_init();
430 test_run_funcs(test_functions);
431 i_set_fatal_handler(test_fatal_handler);
432 test_run_fatals(fatal_functions);
433 return test_deinit();
434 }
test_run_named_with_fatals(const char * match,const struct named_test tests[],const struct named_fatal fatals[])435 int test_run_named_with_fatals(const char *match, const struct named_test tests[],
436 const struct named_fatal fatals[])
437 {
438 test_init();
439 test_run_named_funcs(tests, match);
440 i_set_fatal_handler(test_fatal_handler);
441 test_run_named_fatals(fatals, match);
442 return test_deinit();
443 }
444
test_forked_end(void)445 void test_forked_end(void)
446 {
447 i_set_error_handler(default_error_handler);
448 i_set_fatal_handler(default_fatal_handler);
449
450 i_free_and_null(expected_error_str);
451 i_free_and_null(expected_fatal_str);
452 i_free_and_null(test_prefix);
453 t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
454 }
455
456 void ATTR_NORETURN
test_exit(int status)457 test_exit(int status)
458 {
459 i_free_and_null(expected_error_str);
460 i_free_and_null(expected_fatal_str);
461 i_free_and_null(test_prefix);
462 t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
463 lib_deinit();
464 lib_exit(status);
465 }
466