2 * Copyright (C) 2013 Tobias Brunner
3 * Hochschule fuer Technik Rapperswil
4 * Copyright (C) 2013 Martin Willi
5 * Copyright (C) 2013 revosec AG
7 * This program is free software; you can redistribute it and/or modify it
8 * under the terms of the GNU General Public License as published by the
9 * Free Software Foundation; either version 2 of the License, or (at your
10 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
18 #include "test_runner.h"
21 #include <plugins/plugin_feature.h>
22 #include <collections/array.h>
23 #include <utils/test.h>
31 * Get a tty color escape character for stderr
33 #define TTY(color) tty_escape_get(2, TTY_FG_##color)
36 * Initialize the lookup table for testable functions (defined in
37 * libstrongswan). We don't use the constructor attribute as the order can't
38 * really be defined (clang does not support it and gcc does not adhere to it in
39 * the monolithic build). The function here is a weak symbol in libstrongswan.
41 void testable_functions_create()
43 if (!testable_functions
)
45 testable_functions
= hashtable_create(hashtable_hash_str
,
46 hashtable_equals_str
, 8);
51 * Destroy the lookup table for testable functions
53 static void testable_functions_destroy() __attribute__ ((destructor
));
54 static void testable_functions_destroy()
56 DESTROY_IF(testable_functions
);
57 /* if leak detective is enabled plugins are not actually unloaded, which
58 * means their destructor is called AFTER this one when the process
59 * terminates, make sure this does not crash */
60 testable_functions
= NULL
;
64 * Destroy a single test suite and associated data
66 static void destroy_suite(test_suite_t
*suite
)
70 while (array_remove(suite
->tcases
, 0, &tcase
))
72 array_destroy(tcase
->functions
);
73 array_destroy(tcase
->fixtures
);
79 * Removes and destroys test suites that are not selected.
81 static void filter_suites(array_t
*loaded
)
83 enumerator_t
*enumerator
, *names
;
84 hashtable_t
*selected
;
88 suites
= getenv("TESTS_SUITES");
93 selected
= hashtable_create(hashtable_hash_str
, hashtable_equals_str
, 8);
94 names
= enumerator_create_token(suites
, ",", " ");
95 while (names
->enumerate(names
, &name
))
97 selected
->put(selected
, name
, name
);
99 enumerator
= array_create_enumerator(loaded
);
100 while (enumerator
->enumerate(enumerator
, &suite
))
102 if (!selected
->get(selected
, suite
->name
))
104 array_remove_at(loaded
, enumerator
);
105 destroy_suite(suite
);
108 enumerator
->destroy(enumerator
);
109 selected
->destroy(selected
);
110 names
->destroy(names
);
114 * Load all available test suites, or optionally only selected ones.
116 static array_t
*load_suites(test_configuration_t configs
[],
117 test_runner_init_t init
)
123 library_init(NULL
, "test-runner");
125 test_setup_handler();
127 if (init
&& !init(TRUE
))
132 lib
->plugins
->status(lib
->plugins
, LEVEL_CTRL
);
134 if (lib
->leak_detective
)
136 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
139 suites
= array_create(0, 0);
141 for (i
= 0; configs
[i
].suite
; i
++)
143 if (configs
[i
].feature
.type
== 0 ||
144 lib
->plugins
->has_feature(lib
->plugins
, configs
[i
].feature
))
146 array_insert(suites
, -1, configs
[i
].suite());
149 filter_suites(suites
);
151 if (lib
->leak_detective
)
153 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
166 * Unload and destroy test suites and associated data
168 static void unload_suites(array_t
*suites
)
172 while (array_remove(suites
, 0, &suite
))
174 destroy_suite(suite
);
176 array_destroy(suites
);
180 * Run a single test function, return FALSE on failure
182 static bool run_test(test_function_t
*tfun
, int i
)
184 if (test_restore_point())
193 * Invoke fixture setup/teardown
195 static bool call_fixture(test_case_t
*tcase
, bool up
)
197 enumerator_t
*enumerator
;
198 test_fixture_t
*fixture
;
199 bool failure
= FALSE
;
201 enumerator
= array_create_enumerator(tcase
->fixtures
);
202 while (enumerator
->enumerate(enumerator
, &fixture
))
204 if (test_restore_point())
221 enumerator
->destroy(enumerator
);
227 * Test initialization, initializes libstrongswan for the next run
229 static bool pre_test(test_runner_init_t init
)
231 level_t level
= LEVEL_SILENT
;
234 library_init(NULL
, "test-runner");
236 /* use non-blocking RNG to generate keys fast */
237 lib
->settings
->set_default_str(lib
->settings
,
238 "libstrongswan.plugins.random.random",
239 lib
->settings
->get_str(lib
->settings
,
240 "libstrongswan.plugins.random.urandom", "/dev/urandom"));
241 /* same for the gcrypt plugin */
242 lib
->settings
->set_default_str(lib
->settings
,
243 "libstrongswan.plugins.gcrypt.quick_random", "yes");
245 if (lib
->leak_detective
)
247 /* disable leak reports during testing */
248 lib
->leak_detective
->set_report_cb(lib
->leak_detective
,
251 if (init
&& !init(TRUE
))
256 verbosity
= getenv("TESTS_VERBOSITY");
259 level
= atoi(verbosity
);
261 dbg_default_set_level(level
);
266 * Failure description
270 char msg
[512 - sizeof(char*) - 2 * sizeof(int)];
278 * Data passed to leak report callbacks
288 * Leak report callback, build failures from leaks
290 static void report_leaks(report_data_t
*data
, int count
, size_t bytes
,
291 backtrace_t
*bt
, bool detailed
)
293 failure_t failure
= {
299 snprintf(failure
.msg
, sizeof(failure
.msg
),
300 "Leak detected: %d allocations using %zu bytes", count
, bytes
);
302 array_insert(data
->failures
, -1, &failure
);
306 * Leak summary callback, check if any leaks found
308 static void sum_leaks(report_data_t
*data
, int count
, size_t bytes
,
315 * Do library cleanup and optionally check for memory leaks
317 static bool post_test(test_runner_init_t init
, bool check_leaks
,
318 array_t
*failures
, char *name
, int i
, int *leaks
)
320 report_data_t data
= {
321 .failures
= failures
,
328 if (test_restore_point())
338 if (check_leaks
&& lib
->leak_detective
)
340 lib
->leak_detective
->set_report_cb(lib
->leak_detective
,
341 (leak_detective_report_cb_t
)report_leaks
,
342 (leak_detective_summary_cb_t
)sum_leaks
, &data
);
351 * Collect failure information, add failure_t to array
353 static void collect_failure_info(array_t
*failures
, char *name
, int i
)
355 failure_t failure
= {
358 .bt
= test_failure_backtrace(),
361 failure
.line
= test_failure_get(failure
.msg
, sizeof(failure
.msg
),
364 array_insert(failures
, -1, &failure
);
368 * Print array of collected failure_t to stderr
370 static void print_failures(array_t
*failures
)
376 while (array_remove(failures
, 0, &failure
))
378 fprintf(stderr
, " %sFailure in '%s': %s (",
379 TTY(RED
), failure
.name
, failure
.msg
);
382 fprintf(stderr
, "%s:%d, ", failure
.file
, failure
.line
);
384 fprintf(stderr
, "i = %d)%s\n", failure
.i
, TTY(DEF
));
387 failure
.bt
->log(failure
.bt
, stderr
, TRUE
);
388 failure
.bt
->destroy(failure
.bt
);
396 * Run a single test case with fixtures
398 static bool run_case(test_case_t
*tcase
, test_runner_init_t init
)
400 enumerator_t
*enumerator
;
401 test_function_t
*tfun
;
405 failures
= array_create(sizeof(failure_t
), 0);
407 fprintf(stderr
, " Running case '%s': ", tcase
->name
);
410 enumerator
= array_create_enumerator(tcase
->functions
);
411 while (enumerator
->enumerate(enumerator
, &tfun
))
415 for (i
= tfun
->start
; i
< tfun
->end
; i
++)
422 test_setup_timeout(tcase
->timeout
);
424 if (call_fixture(tcase
, TRUE
))
426 if (run_test(tfun
, i
))
428 if (call_fixture(tcase
, FALSE
))
435 call_fixture(tcase
, FALSE
);
438 if (!post_test(init
, ok
, failures
, tfun
->name
, i
, &leaks
))
443 test_setup_timeout(0);
450 fprintf(stderr
, "%s+%s", TTY(GREEN
), TTY(DEF
));
455 collect_failure_info(failures
, tfun
->name
, i
);
459 fprintf(stderr
, "%s-%s", TTY(RED
), TTY(DEF
));
464 fprintf(stderr
, "!");
468 if (rounds
== tfun
->end
- tfun
->start
)
473 enumerator
->destroy(enumerator
);
475 fprintf(stderr
, "\n");
477 print_failures(failures
);
478 array_destroy(failures
);
480 return passed
== array_count(tcase
->functions
);
484 * Run a single test suite
486 static bool run_suite(test_suite_t
*suite
, test_runner_init_t init
)
488 enumerator_t
*enumerator
;
492 fprintf(stderr
, " Running suite '%s':\n", suite
->name
);
494 enumerator
= array_create_enumerator(suite
->tcases
);
495 while (enumerator
->enumerate(enumerator
, &tcase
))
497 if (run_case(tcase
, init
))
502 enumerator
->destroy(enumerator
);
504 if (passed
== array_count(suite
->tcases
))
506 fprintf(stderr
, " %sPassed all %u '%s' test cases%s\n",
507 TTY(GREEN
), array_count(suite
->tcases
), suite
->name
, TTY(DEF
));
510 fprintf(stderr
, " %sPassed %u/%u '%s' test cases%s\n",
511 TTY(RED
), passed
, array_count(suite
->tcases
), suite
->name
, TTY(DEF
));
518 int test_runner_run(const char *name
, test_configuration_t configs
[],
519 test_runner_init_t init
)
523 enumerator_t
*enumerator
;
524 int passed
= 0, result
;
526 /* redirect all output to stderr (to redirect make's stdout to /dev/null) */
529 suites
= load_suites(configs
, init
);
535 fprintf(stderr
, "Running %u '%s' test suites:\n", array_count(suites
), name
);
537 enumerator
= array_create_enumerator(suites
);
538 while (enumerator
->enumerate(enumerator
, &suite
))
540 if (run_suite(suite
, init
))
545 enumerator
->destroy(enumerator
);
547 if (passed
== array_count(suites
))
549 fprintf(stderr
, "%sPassed all %u '%s' suites%s\n",
550 TTY(GREEN
), array_count(suites
), name
, TTY(DEF
));
551 result
= EXIT_SUCCESS
;
555 fprintf(stderr
, "%sPassed %u of %u '%s' suites%s\n",
556 TTY(RED
), passed
, array_count(suites
), name
, TTY(DEF
));
557 result
= EXIT_FAILURE
;
560 unload_suites(suites
);