From 9ae1140118837d981d7566e8dbdca3f0ebc3466b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 14 Oct 2013 16:44:27 +0200 Subject: [PATCH 01/54] unit-tests: Move test suites to its own subfolder --- src/libstrongswan/tests/Makefile.am | 24 +++++++++++++++---- .../tests/{ => suites}/test_array.c | 0 .../tests/{ => suites}/test_asn1.c | 0 .../tests/{ => suites}/test_bio_reader.c | 2 +- .../tests/{ => suites}/test_bio_writer.c | 4 ++-- .../tests/{ => suites}/test_chunk.c | 0 .../tests/{ => suites}/test_ecdsa.c | 0 .../tests/{ => suites}/test_enum.c | 0 .../tests/{ => suites}/test_enumerator.c | 0 .../tests/{ => suites}/test_hashtable.c | 0 .../tests/{ => suites}/test_host.c | 0 .../tests/{ => suites}/test_identification.c | 0 .../tests/{ => suites}/test_linked_list.c | 0 .../test_linked_list_enumerator.c | 0 .../tests/{ => suites}/test_pen.c | 0 .../tests/{ => suites}/test_printf.c | 0 .../tests/{ => suites}/test_rsa.c | 0 .../tests/{ => suites}/test_threading.c | 0 .../tests/{ => suites}/test_utils.c | 0 .../tests/{ => suites}/test_vectors.c | 0 20 files changed, 22 insertions(+), 8 deletions(-) rename src/libstrongswan/tests/{ => suites}/test_array.c (100%) rename src/libstrongswan/tests/{ => suites}/test_asn1.c (100%) rename src/libstrongswan/tests/{ => suites}/test_bio_reader.c (99%) rename src/libstrongswan/tests/{ => suites}/test_bio_writer.c (99%) rename src/libstrongswan/tests/{ => suites}/test_chunk.c (100%) rename src/libstrongswan/tests/{ => suites}/test_ecdsa.c (100%) rename src/libstrongswan/tests/{ => suites}/test_enum.c (100%) rename src/libstrongswan/tests/{ => suites}/test_enumerator.c (100%) rename src/libstrongswan/tests/{ => suites}/test_hashtable.c (100%) rename src/libstrongswan/tests/{ => suites}/test_host.c (100%) rename src/libstrongswan/tests/{ => suites}/test_identification.c (100%) rename src/libstrongswan/tests/{ => suites}/test_linked_list.c (100%) rename src/libstrongswan/tests/{ => suites}/test_linked_list_enumerator.c (100%) rename src/libstrongswan/tests/{ => suites}/test_pen.c (100%) rename src/libstrongswan/tests/{ => suites}/test_printf.c (100%) rename src/libstrongswan/tests/{ => suites}/test_rsa.c (100%) rename src/libstrongswan/tests/{ => suites}/test_threading.c (100%) rename src/libstrongswan/tests/{ => suites}/test_utils.c (100%) rename src/libstrongswan/tests/{ => suites}/test_vectors.c (100%) diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 994619ef6..54ceaf53b 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -4,11 +4,25 @@ check_PROGRAMS = $(TESTS) test_runner_SOURCES = \ test_runner.c test_runner.h test_suite.h \ - test_linked_list.c test_enumerator.c test_linked_list_enumerator.c \ - test_bio_reader.c test_bio_writer.c test_chunk.c test_enum.c test_hashtable.c \ - test_identification.c test_threading.c test_utils.c test_vectors.c \ - test_array.c test_ecdsa.c test_rsa.c test_host.c test_printf.c test_pen.c \ - test_asn1.c + suites/test_linked_list.c \ + suites/test_enumerator.c \ + suites/test_linked_list_enumerator.c \ + suites/test_bio_reader.c \ + suites/test_bio_writer.c \ + suites/test_chunk.c \ + suites/test_enum.c \ + suites/test_hashtable.c \ + suites/test_identification.c \ + suites/test_threading.c \ + suites/test_utils.c \ + suites/test_vectors.c \ + suites/test_array.c \ + suites/test_ecdsa.c \ + suites/test_rsa.c \ + suites/test_host.c \ + suites/test_pen.c \ + suites/test_asn1.c \ + suites/test_printf.c test_runner_CFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ diff --git a/src/libstrongswan/tests/test_array.c b/src/libstrongswan/tests/suites/test_array.c similarity index 100% rename from src/libstrongswan/tests/test_array.c rename to src/libstrongswan/tests/suites/test_array.c diff --git a/src/libstrongswan/tests/test_asn1.c b/src/libstrongswan/tests/suites/test_asn1.c similarity index 100% rename from src/libstrongswan/tests/test_asn1.c rename to src/libstrongswan/tests/suites/test_asn1.c diff --git a/src/libstrongswan/tests/test_bio_reader.c b/src/libstrongswan/tests/suites/test_bio_reader.c similarity index 99% rename from src/libstrongswan/tests/test_bio_reader.c rename to src/libstrongswan/tests/suites/test_bio_reader.c index 45b20db00..6a9743d62 100644 --- a/src/libstrongswan/tests/test_bio_reader.c +++ b/src/libstrongswan/tests/suites/test_bio_reader.c @@ -329,7 +329,7 @@ END_TEST */ #define assert_read_data_len(bits) ({ \ - bio_reader_t *reader; \ + bio_reader_t *reader; \ chunk_t read, data; \ int i, len = bits / 8; \ data = chunk_empty; \ diff --git a/src/libstrongswan/tests/test_bio_writer.c b/src/libstrongswan/tests/suites/test_bio_writer.c similarity index 99% rename from src/libstrongswan/tests/test_bio_writer.c rename to src/libstrongswan/tests/suites/test_bio_writer.c index 665cd2d7c..e74288eb7 100644 --- a/src/libstrongswan/tests/test_bio_writer.c +++ b/src/libstrongswan/tests/suites/test_bio_writer.c @@ -181,7 +181,7 @@ END_TEST */ #define assert_write_data_len(init, bits) ({ \ - bio_writer_t *writer; \ + bio_writer_t *writer; \ chunk_t buf, data; \ int i, len = bits / 8; \ writer = bio_writer_create(init); \ @@ -240,7 +240,7 @@ END_TEST */ #define assert_wrap_data(init, bits) ({ \ - bio_writer_t *writer; \ + bio_writer_t *writer; \ chunk_t buf, data; \ int i, len = bits / 8; \ writer = bio_writer_create(init); \ diff --git a/src/libstrongswan/tests/test_chunk.c b/src/libstrongswan/tests/suites/test_chunk.c similarity index 100% rename from src/libstrongswan/tests/test_chunk.c rename to src/libstrongswan/tests/suites/test_chunk.c diff --git a/src/libstrongswan/tests/test_ecdsa.c b/src/libstrongswan/tests/suites/test_ecdsa.c similarity index 100% rename from src/libstrongswan/tests/test_ecdsa.c rename to src/libstrongswan/tests/suites/test_ecdsa.c diff --git a/src/libstrongswan/tests/test_enum.c b/src/libstrongswan/tests/suites/test_enum.c similarity index 100% rename from src/libstrongswan/tests/test_enum.c rename to src/libstrongswan/tests/suites/test_enum.c diff --git a/src/libstrongswan/tests/test_enumerator.c b/src/libstrongswan/tests/suites/test_enumerator.c similarity index 100% rename from src/libstrongswan/tests/test_enumerator.c rename to src/libstrongswan/tests/suites/test_enumerator.c diff --git a/src/libstrongswan/tests/test_hashtable.c b/src/libstrongswan/tests/suites/test_hashtable.c similarity index 100% rename from src/libstrongswan/tests/test_hashtable.c rename to src/libstrongswan/tests/suites/test_hashtable.c diff --git a/src/libstrongswan/tests/test_host.c b/src/libstrongswan/tests/suites/test_host.c similarity index 100% rename from src/libstrongswan/tests/test_host.c rename to src/libstrongswan/tests/suites/test_host.c diff --git a/src/libstrongswan/tests/test_identification.c b/src/libstrongswan/tests/suites/test_identification.c similarity index 100% rename from src/libstrongswan/tests/test_identification.c rename to src/libstrongswan/tests/suites/test_identification.c diff --git a/src/libstrongswan/tests/test_linked_list.c b/src/libstrongswan/tests/suites/test_linked_list.c similarity index 100% rename from src/libstrongswan/tests/test_linked_list.c rename to src/libstrongswan/tests/suites/test_linked_list.c diff --git a/src/libstrongswan/tests/test_linked_list_enumerator.c b/src/libstrongswan/tests/suites/test_linked_list_enumerator.c similarity index 100% rename from src/libstrongswan/tests/test_linked_list_enumerator.c rename to src/libstrongswan/tests/suites/test_linked_list_enumerator.c diff --git a/src/libstrongswan/tests/test_pen.c b/src/libstrongswan/tests/suites/test_pen.c similarity index 100% rename from src/libstrongswan/tests/test_pen.c rename to src/libstrongswan/tests/suites/test_pen.c diff --git a/src/libstrongswan/tests/test_printf.c b/src/libstrongswan/tests/suites/test_printf.c similarity index 100% rename from src/libstrongswan/tests/test_printf.c rename to src/libstrongswan/tests/suites/test_printf.c diff --git a/src/libstrongswan/tests/test_rsa.c b/src/libstrongswan/tests/suites/test_rsa.c similarity index 100% rename from src/libstrongswan/tests/test_rsa.c rename to src/libstrongswan/tests/suites/test_rsa.c diff --git a/src/libstrongswan/tests/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c similarity index 100% rename from src/libstrongswan/tests/test_threading.c rename to src/libstrongswan/tests/suites/test_threading.c diff --git a/src/libstrongswan/tests/test_utils.c b/src/libstrongswan/tests/suites/test_utils.c similarity index 100% rename from src/libstrongswan/tests/test_utils.c rename to src/libstrongswan/tests/suites/test_utils.c diff --git a/src/libstrongswan/tests/test_vectors.c b/src/libstrongswan/tests/suites/test_vectors.c similarity index 100% rename from src/libstrongswan/tests/test_vectors.c rename to src/libstrongswan/tests/suites/test_vectors.c From a426851f6362f8d1d6cbecd133ef39a831b4ea2a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 10:37:38 +0200 Subject: [PATCH 02/54] leak-detective: Use callback functions to report leaks and usage information This is more flexible than printing reports to a FILE. --- src/libcharon/plugins/stroke/stroke_socket.c | 23 ++++- src/libstrongswan/library.c | 35 ++++++++ src/libstrongswan/utils/backtrace.h | 4 +- src/libstrongswan/utils/leak_detective.c | 89 ++++++++++++-------- src/libstrongswan/utils/leak_detective.h | 54 ++++++++++-- 5 files changed, 158 insertions(+), 47 deletions(-) diff --git a/src/libcharon/plugins/stroke/stroke_socket.c b/src/libcharon/plugins/stroke/stroke_socket.c index 3adebb523..cf7ec369c 100644 --- a/src/libcharon/plugins/stroke/stroke_socket.c +++ b/src/libcharon/plugins/stroke/stroke_socket.c @@ -489,6 +489,25 @@ static void stroke_leases(private_stroke_socket_t *this, this->list->leases(this->list, msg, out); } +/** + * Callback function for usage report + */ +static void report_usage(FILE *out, int count, size_t bytes, + backtrace_t *bt, bool detailed) +{ + fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n", + bytes, count, bytes / count); + bt->log(bt, out, detailed); +} + +/** + * Callback function for memusage summary + */ +static void sum_usage(FILE *out, int count, size_t bytes, int whitelisted) +{ + fprintf(out, "Total memory usage: %zu\n", bytes); +} + /** * Show memory usage */ @@ -497,7 +516,9 @@ static void stroke_memusage(private_stroke_socket_t *this, { if (lib->leak_detective) { - lib->leak_detective->usage(lib->leak_detective, out); + lib->leak_detective->usage(lib->leak_detective, + (leak_detective_report_cb_t)report_usage, + (leak_detective_summary_cb_t)sum_usage, out); } } diff --git a/src/libstrongswan/library.c b/src/libstrongswan/library.c index f2fa3e0aa..72fc2fa44 100644 --- a/src/libstrongswan/library.c +++ b/src/libstrongswan/library.c @@ -61,6 +61,39 @@ struct private_library_t { */ library_t *lib = NULL; +#ifdef LEAK_DETECTIVE +/** + * Default leak report callback + */ +static void report_leaks(void *user, int count, size_t bytes, + backtrace_t *bt, bool detailed) +{ + fprintf(stderr, "%zu bytes total, %d allocations, %zu bytes average:\n", + bytes, count, bytes / count); + bt->log(bt, stderr, detailed); +} + +/** + * Default leak report summary callback + */ +static void sum_leaks(void* user, int count, size_t bytes, int whitelisted) +{ + switch (count) + { + case 0: + fprintf(stderr, "No leaks detected"); + break; + case 1: + fprintf(stderr, "One leak detected"); + break; + default: + fprintf(stderr, "%d leaks detected, %zu bytes", count, bytes); + break; + } + fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted); +} +#endif /* LEAK_DETECTIVE */ + /** * Deinitialize library */ @@ -227,6 +260,8 @@ bool library_init(char *settings) #ifdef LEAK_DETECTIVE lib->leak_detective = leak_detective_create(); + lib->leak_detective->set_report_cb(lib->leak_detective, + report_leaks, sum_leaks, NULL); #endif /* LEAK_DETECTIVE */ pfh = printf_hook_create(); diff --git a/src/libstrongswan/utils/backtrace.h b/src/libstrongswan/utils/backtrace.h index 416f58898..16e84c4d9 100644 --- a/src/libstrongswan/utils/backtrace.h +++ b/src/libstrongswan/utils/backtrace.h @@ -21,12 +21,12 @@ #ifndef BACKTRACE_H_ #define BACKTRACE_H_ +typedef struct backtrace_t backtrace_t; + #include #include -typedef struct backtrace_t backtrace_t; - /** * A backtrace registers the frames on the stack during creation. */ diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 725e04f7c..b001eeedc 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -59,6 +59,21 @@ struct private_leak_detective_t { * public functions */ leak_detective_t public; + + /** + * Registered report() function + */ + leak_detective_report_cb_t report_cb; + + /** + * Registered report() summary function + */ + leak_detective_summary_cb_t report_scb; + + /** + * Registered user data for callbacks + */ + void *report_data; }; /** @@ -599,7 +614,8 @@ static bool equals(backtrace_t *a, backtrace_t *b) * Summarize and print backtraces */ static int print_traces(private_leak_detective_t *this, - FILE *out, int thresh, int thresh_count, + leak_detective_report_cb_t cb, void *user, + int thresh, int thresh_count, bool detailed, int *whitelisted, size_t *sum) { int leaks = 0; @@ -652,16 +668,20 @@ static int print_traces(private_leak_detective_t *this, leaks++; } lock->unlock(lock); + enumerator = entries->create_enumerator(entries); while (enumerator->enumerate(enumerator, NULL, &entry)) { - if (out && - (!thresh || entry->bytes >= thresh) && - (!thresh_count || entry->count >= thresh_count)) + if (cb) { - fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n", - entry->bytes, entry->count, entry->bytes / entry->count); - entry->backtrace->log(entry->backtrace, out, detailed); + if (!thresh || entry->bytes >= thresh) + { + if (!thresh_count || entry->count >= thresh_count) + { + this->report_cb(this->report_data, entry->count, + entry->bytes, entry->backtrace, detailed); + } + } } entry->backtrace->destroy(entry->backtrace); free(entry); @@ -681,38 +701,30 @@ METHOD(leak_detective_t, report, void, int leaks, whitelisted = 0; size_t sum = 0; - leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum); - switch (leaks) + leaks = print_traces(this, this->report_cb, this->report_data, + 0, 0, detailed, &whitelisted, &sum); + if (this->report_scb) { - case 0: - fprintf(stderr, "No leaks detected"); - break; - case 1: - fprintf(stderr, "One leak detected"); - break; - default: - fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum); - break; + this->report_scb(this->report_data, leaks, sum, whitelisted); } - fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted); - } - else - { - fprintf(stderr, "Leak detective disabled\n"); } } +METHOD(leak_detective_t, set_report_cb, void, + private_leak_detective_t *this, leak_detective_report_cb_t cb, + leak_detective_summary_cb_t scb, void *user) +{ + this->report_cb = cb; + this->report_scb = scb; + this->report_data = user; +} + METHOD(leak_detective_t, leaks, int, private_leak_detective_t *this) { - if (lib->leak_detective) - { - int leaks, whitelisted = 0; + int whitelisted = 0; - leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL); - return leaks; - } - return 0; + return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL); } METHOD(leak_detective_t, set_state, bool, @@ -722,10 +734,11 @@ METHOD(leak_detective_t, set_state, bool, } METHOD(leak_detective_t, usage, void, - private_leak_detective_t *this, FILE *out) + private_leak_detective_t *this, leak_detective_report_cb_t cb, + leak_detective_summary_cb_t scb, void *user) { bool detailed; - int thresh, thresh_count; + int thresh, thresh_count, leaks, whitelisted = 0; size_t sum = 0; thresh = lib->settings->get_int(lib->settings, @@ -735,9 +748,12 @@ METHOD(leak_detective_t, usage, void, detailed = lib->settings->get_bool(lib->settings, "libstrongswan.leak_detective.detailed", TRUE); - print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum); - - fprintf(out, "Total memory usage: %zu\n", sum); + leaks = print_traces(this, cb, user, thresh, thresh_count, + detailed, &whitelisted, &sum); + if (scb) + { + scb(user, leaks, sum, whitelisted); + } } /** @@ -936,8 +952,9 @@ leak_detective_t *leak_detective_create() INIT(this, .public = { .report = _report, - .leaks = _leaks, + .set_report_cb = _set_report_cb, .usage = _usage, + .leaks = _leaks, .set_state = _set_state, .destroy = _destroy, }, diff --git a/src/libstrongswan/utils/leak_detective.h b/src/libstrongswan/utils/leak_detective.h index 7a29e81d7..3fd0b8c93 100644 --- a/src/libstrongswan/utils/leak_detective.h +++ b/src/libstrongswan/utils/leak_detective.h @@ -24,6 +24,30 @@ typedef struct leak_detective_t leak_detective_t; #include +#include + +/** + * Callback function to report leak/usage information + * + * @param user user specific data + * @param count number of allocations + * @param bytes total size of allocations + * @param bt backtrace of allocation + * @param detailed TRUE to show a detailed backtrace + */ +typedef void (*leak_detective_report_cb_t)(void *user, int count, size_t bytes, + backtrace_t *bt, bool detailed); + +/** + * Callback function to report leak/usage summary information + * + * @param user user specific data + * @param count total number of allocations + * @param bytes total size of all reported allocations + * @param whitelisted number of allocations suppressed by whitelist + */ +typedef void (*leak_detective_summary_cb_t)(void* user, int count, size_t bytes, + int whitelisted); /** * Leak detective finds leaks and bad frees using malloc hooks. @@ -36,12 +60,33 @@ typedef struct leak_detective_t leak_detective_t; struct leak_detective_t { /** - * Report leaks to stderr. + * Report leaks to the registered callback functions. * * @param detailed TRUE to resolve line/filename of leak (slow) */ void (*report)(leak_detective_t *this, bool detailed); + /** + * Report current memory usage to out. + * Set callback functions invoked during a report(). + * + * @param cb callback invoked for each detected leak + * @param scb summary callback invoked at end of report + * @param user user data to supply to callbacks + */ + void (*set_report_cb)(leak_detective_t *this, leak_detective_report_cb_t cb, + leak_detective_summary_cb_t scb, void *user); + + /** + * Report current memory usage using a callbacks. + * + * @param cb callback invoked for each allocation + * @param scb summary callback invoked at end of usage report + * @param user user data supplied to callbacks + */ + void (*usage)(leak_detective_t *this, leak_detective_report_cb_t cb, + leak_detective_summary_cb_t scb, void *user); + /** * Number of detected leaks. * @@ -49,13 +94,6 @@ struct leak_detective_t { */ int (*leaks)(leak_detective_t *this); - /** - * Report current memory usage to out. - * - * @param out target to write usage report to - */ - void (*usage)(leak_detective_t *this, FILE *out); - /** * Enable/disable leak detective hooks for the current thread. * From f192526c3f57ea8f3bc37b6061b0fb05802e1c67 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 11:16:41 +0200 Subject: [PATCH 03/54] leak-detective: Reset leak list during cleanup This resets leak detective state should it get created/destroyed more than once. --- src/libstrongswan/utils/leak_detective.c | 1 + 1 file changed, 1 insertion(+) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index b001eeedc..87e21bb46 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -940,6 +940,7 @@ METHOD(leak_detective_t, destroy, void, lock->destroy(lock); thread_disabled->destroy(thread_disabled); free(this); + first_header.next = NULL; } /* From ef6d78d6ef0c84ff19753cf5f72fa1514d525af7 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 6 Nov 2013 10:09:04 +0100 Subject: [PATCH 04/54] leak-detective: Register OS X specific hooks just once If we initialize libstrongswan more than once in the same process, we may not register the hooks twice. --- src/libstrongswan/utils/leak_detective.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 87e21bb46..16e023790 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -333,9 +333,16 @@ HOOK(size_t, size, const void *ptr) */ static bool register_hooks() { + static bool once = FALSE; malloc_zone_t *zone; void *page; + if (once) + { + return TRUE; + } + once = TRUE; + zone = malloc_default_zone(); if (zone->version != MALLOC_ZONE_VERSION) { From 56866ecf3d469cacadd36c2d37e397b8daa45fcc Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 6 Nov 2013 10:10:49 +0100 Subject: [PATCH 05/54] leak-detective: Call {gm,local}time_r() to allocate static buffer On OS X Mavericks, these functions use a static allocation and are hard to whitelist using other means. --- src/libstrongswan/utils/leak_detective.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/libstrongswan/utils/leak_detective.c b/src/libstrongswan/utils/leak_detective.c index 16e023790..36ed1735c 100644 --- a/src/libstrongswan/utils/leak_detective.c +++ b/src/libstrongswan/utils/leak_detective.c @@ -587,7 +587,12 @@ char *whitelist[] = { */ static void init_static_allocations() { + struct tm tm; + time_t t = 0; + tzset(); + gmtime_r(&t, &tm); + localtime_r(&t, &tm); } /** From 35e8eb93a0fcbc396fc58dfee3ff05db6c5dbd79 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 14 Oct 2013 20:29:06 +0200 Subject: [PATCH 06/54] unit-tests: Implement testing framework without "check" --- src/libstrongswan/tests/Makefile.am | 8 +- src/libstrongswan/tests/test_runner.c | 474 +++++++++++++++++++++++--- src/libstrongswan/tests/test_runner.h | 2 +- src/libstrongswan/tests/test_suite.c | 240 +++++++++++++ src/libstrongswan/tests/test_suite.h | 351 +++++++++++++++---- 5 files changed, 959 insertions(+), 116 deletions(-) create mode 100644 src/libstrongswan/tests/test_suite.c diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 54ceaf53b..c9d10e9fd 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -3,7 +3,7 @@ TESTS = test_runner check_PROGRAMS = $(TESTS) test_runner_SOURCES = \ - test_runner.c test_runner.h test_suite.h \ + test_runner.c test_runner.h test_suite.c test_suite.h \ suites/test_linked_list.c \ suites/test_enumerator.c \ suites/test_linked_list_enumerator.c \ @@ -28,11 +28,9 @@ test_runner_CFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ -DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \ -DPLUGINS=\""${s_plugins}\"" \ - @COVERAGE_CFLAGS@ \ - @CHECK_CFLAGS@ + @COVERAGE_CFLAGS@ test_runner_LDFLAGS = @COVERAGE_LDFLAGS@ test_runner_LDADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(PTHREADLIB) \ - @CHECK_LIBS@ + $(PTHREADLIB) diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index e0e7e2b81..a46007a3a 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -1,6 +1,8 @@ /* * 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 @@ -13,14 +15,20 @@ * for more details. */ -#include - #include "test_runner.h" #include #include +#include #include +#include +#include + +/** + * Get a tty color escape character for stderr + */ +#define TTY(color) tty_escape_get(2, TTY_FG_##color) /** * Load plugins from builddir @@ -43,17 +51,147 @@ static bool load_plugins() return lib->plugins->load(lib->plugins, PLUGINS); } -int main() +/** + * Check if a specific feature is available, return falg if so + */ +static int check_feature(plugin_feature_t feature, int flag) { - SRunner *sr; - int nf; + if (lib->plugins->has_feature(lib->plugins, feature)) + { + return flag; + } + return 0; +} - /* test cases are forked and there is no cleanup, so disable leak detective. - * if test_suite.h is included leak detective is enabled in test cases */ - setenv("LEAK_DETECTIVE_DISABLE", "1", 1); - /* redirect all output to stderr (to redirect make's stdout to /dev/null) */ - dup2(2, 1); +/** + * Load all available test suites + */ +static array_t *load_suites() +{ + array_t *suites; + enum { + OTEST_RSA = (1<<0), + OTEST_ECDSA = (1<<1), + } otest = 0; + library_init(NULL); + if (!load_plugins()) + { + library_deinit(); + return NULL; + } + lib->plugins->status(lib->plugins, LEVEL_CTRL); + + /* we have to build the test suite array without leak detective, so + * separate plugin checks and suite creation */ + otest |= check_feature(PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_RSA), OTEST_RSA); + otest |= check_feature(PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_ECDSA), OTEST_ECDSA); + + library_deinit(); + + suites = array_create(0, 0); + + array_insert(suites, -1, bio_reader_suite_create()); + array_insert(suites, -1, bio_writer_suite_create()); + array_insert(suites, -1, chunk_suite_create()); + array_insert(suites, -1, enum_suite_create()); + array_insert(suites, -1, enumerator_suite_create()); + array_insert(suites, -1, linked_list_suite_create()); + array_insert(suites, -1, linked_list_enumerator_suite_create()); + array_insert(suites, -1, hashtable_suite_create()); + array_insert(suites, -1, array_suite_create()); + array_insert(suites, -1, identification_suite_create()); + array_insert(suites, -1, threading_suite_create()); + array_insert(suites, -1, utils_suite_create()); + array_insert(suites, -1, host_suite_create()); + array_insert(suites, -1, vectors_suite_create()); + array_insert(suites, -1, pen_suite_create()); + array_insert(suites, -1, asn1_suite_create()); + array_insert(suites, -1, printf_suite_create()); + if (otest & OTEST_RSA) + { + array_insert(suites, -1, rsa_suite_create()); + } + if (otest & OTEST_ECDSA) + { + array_insert(suites, -1, ecdsa_suite_create()); + } + + return suites; +} + +/** + * Unload and destroy test suites and associated data + */ +static void unload_suites(array_t *suites) +{ + test_suite_t *suite; + test_case_t *tcase; + + while (array_remove(suites, 0, &suite)) + { + while (array_remove(suite->tcases, 0, &tcase)) + { + array_destroy(tcase->functions); + array_destroy(tcase->fixtures); + } + free(suite); + } + array_destroy(suites); +} + +/** + * Run a single test function, return FALSE on failure + */ +static bool run_test(test_function_t *tfun, int i) +{ + if (test_restore_point()) + { + tfun->cb(i); + return TRUE; + } + return FALSE; +} + +/** + * Invoke fixture setup/teardown + */ +static bool call_fixture(test_case_t *tcase, bool up) +{ + enumerator_t *enumerator; + test_fixture_t *fixture; + bool failure = FALSE; + + enumerator = array_create_enumerator(tcase->fixtures); + while (enumerator->enumerate(enumerator, &fixture)) + { + if (test_restore_point()) + { + if (up) + { + fixture->setup(); + } + else + { + fixture->teardown(); + } + } + else + { + failure = TRUE; + break; + } + } + enumerator->destroy(enumerator); + + return !failure; +} + +/** + * Test initialization, initializes libstrongswan for the next run + */ +static bool pre_test() +{ library_init(NULL); /* use non-blocking RNG to generate keys fast */ @@ -62,47 +200,295 @@ int main() lib->settings->get_str(lib->settings, "libstrongswan.plugins.random.urandom", "/dev/urandom")); + if (lib->leak_detective) + { + /* disable leak reports during testing */ + lib->leak_detective->set_report_cb(lib->leak_detective, + NULL, NULL, NULL); + } if (!load_plugins()) { library_deinit(); - return EXIT_FAILURE; + return FALSE; } - lib->plugins->status(lib->plugins, LEVEL_CTRL); - sr = srunner_create(NULL); - srunner_add_suite(sr, bio_reader_suite_create()); - srunner_add_suite(sr, bio_writer_suite_create()); - srunner_add_suite(sr, chunk_suite_create()); - srunner_add_suite(sr, enum_suite_create()); - srunner_add_suite(sr, enumerator_suite_create()); - srunner_add_suite(sr, linked_list_suite_create()); - srunner_add_suite(sr, linked_list_enumerator_suite_create()); - srunner_add_suite(sr, hashtable_suite_create()); - srunner_add_suite(sr, array_suite_create()); - srunner_add_suite(sr, identification_suite_create()); - srunner_add_suite(sr, threading_suite_create()); - srunner_add_suite(sr, utils_suite_create()); - srunner_add_suite(sr, host_suite_create()); - srunner_add_suite(sr, vectors_suite_create()); - srunner_add_suite(sr, printf_suite_create()); - srunner_add_suite(sr, pen_suite_create()); - srunner_add_suite(sr, asn1_suite_create()); - if (lib->plugins->has_feature(lib->plugins, - PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_RSA))) + dbg_default_set_level(LEVEL_SILENT); + return TRUE; +} + +/** + * Failure description + */ +typedef struct { + char *name; + char msg[512 - sizeof(char*) - 2 * sizeof(int)]; + const char *file; + int line; + int i; + backtrace_t *bt; +} failure_t; + +/** + * Data passed to leak report callbacks + */ +typedef struct { + array_t *failures; + char *name; + int i; + int leaks; +} report_data_t; + +/** + * Leak report callback, build failures from leaks + */ +static void report_leaks(report_data_t *data, int count, size_t bytes, + backtrace_t *bt, bool detailed) +{ + failure_t failure = { + .name = data->name, + .i = data->i, + .bt = bt->clone(bt), + }; + + snprintf(failure.msg, sizeof(failure.msg), + "Leak detected: %d allocations using %zu bytes", count, bytes); + + array_insert(data->failures, -1, &failure); +} + +/** + * Leak summary callback, check if any leaks found + */ +static void sum_leaks(report_data_t *data, int count, size_t bytes, + int whitelisted) +{ + data->leaks = count; +} + +/** + * Do library cleanup and optionally check for memory leaks + */ +static bool post_test(bool check_leaks, array_t *failures, char *name, int i) +{ + report_data_t data = { + .failures = failures, + .name = name, + .i = i, + }; + + if (check_leaks && lib->leak_detective) { - srunner_add_suite(sr, rsa_suite_create()); + lib->leak_detective->set_report_cb(lib->leak_detective, + (leak_detective_report_cb_t)report_leaks, + (leak_detective_summary_cb_t)sum_leaks, &data); } - if (lib->plugins->has_feature(lib->plugins, - PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_ECDSA))) - { - srunner_add_suite(sr, ecdsa_suite_create()); - } - - srunner_run_all(sr, CK_NORMAL); - nf = srunner_ntests_failed(sr); - - srunner_free(sr); library_deinit(); - return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; + return data.leaks != 0; +} + +/** + * Collect failure information, add failure_t to array + */ +static void collect_failure_info(array_t *failures, char *name, int i) +{ + failure_t failure = { + .name = name, + .i = i, + .bt = test_failure_backtrace(), + }; + + failure.line = test_failure_get(failure.msg, sizeof(failure.msg), + &failure.file); + + array_insert(failures, -1, &failure); +} + +/** + * Print array of collected failure_t to stderr + */ +static void print_failures(array_t *failures) +{ + failure_t failure; + + while (array_remove(failures, 0, &failure)) + { + fprintf(stderr, " %sFailure in '%s': %s (", + TTY(RED), failure.name, failure.msg); + if (failure.line) + { + fprintf(stderr, "%s:%d, ", failure.file, failure.line); + } + fprintf(stderr, "i = %d)%s\n", failure.i, TTY(DEF)); + if (failure.bt) + { + failure.bt->log(failure.bt, stderr, TRUE); + failure.bt->destroy(failure.bt); + } + } +} + +/** + * Run a single test case with fixtures + */ +static bool run_case(test_case_t *tcase) +{ + enumerator_t *enumerator; + test_function_t *tfun; + int passed = 0; + array_t *failures; + + failures = array_create(sizeof(failure_t), 0); + + fprintf(stderr, " Running case '%s': ", tcase->name); + fflush(stderr); + + enumerator = array_create_enumerator(tcase->functions); + while (enumerator->enumerate(enumerator, &tfun)) + { + int i, rounds = 0; + + for (i = tfun->start; i < tfun->end; i++) + { + if (pre_test()) + { + bool ok = FALSE, leaks = FALSE; + + test_setup_timeout(tcase->timeout); + + if (call_fixture(tcase, TRUE)) + { + if (run_test(tfun, i)) + { + if (call_fixture(tcase, FALSE)) + { + ok = TRUE; + } + } + else + { + call_fixture(tcase, FALSE); + } + + } + leaks = post_test(ok, failures, tfun->name, i); + + test_setup_timeout(0); + + if (ok) + { + if (!leaks) + { + rounds++; + fprintf(stderr, "%s+%s", TTY(GREEN), TTY(DEF)); + } + } + else + { + collect_failure_info(failures, tfun->name, i); + } + if (!ok || leaks) + { + fprintf(stderr, "%s-%s", TTY(RED), TTY(DEF)); + } + } + else + { + fprintf(stderr, "!"); + } + } + fflush(stderr); + if (rounds == tfun->end - tfun->start) + { + passed++; + } + } + enumerator->destroy(enumerator); + + fprintf(stderr, "\n"); + + print_failures(failures); + array_destroy(failures); + + return passed == array_count(tcase->functions); +} + +/** + * Run a single test suite + */ +static bool run_suite(test_suite_t *suite) +{ + enumerator_t *enumerator; + test_case_t *tcase; + int passed = 0; + + fprintf(stderr, " Running suite '%s':\n", suite->name); + + enumerator = array_create_enumerator(suite->tcases); + while (enumerator->enumerate(enumerator, &tcase)) + { + if (run_case(tcase)) + { + passed++; + } + } + enumerator->destroy(enumerator); + + if (passed == array_count(suite->tcases)) + { + fprintf(stderr, " %sPassed all %u '%s' test cases%s\n", + TTY(GREEN), array_count(suite->tcases), suite->name, TTY(DEF)); + return TRUE; + } + fprintf(stderr, " %sPassed %u/%u '%s' test cases%s\n", + TTY(RED), passed, array_count(suite->tcases), suite->name, TTY(DEF)); + return FALSE; +} + +int main(int argc, char *argv[]) +{ + array_t *suites; + test_suite_t *suite; + enumerator_t *enumerator; + int passed = 0, result; + + /* redirect all output to stderr (to redirect make's stdout to /dev/null) */ + dup2(2, 1); + + test_setup_handler(); + + suites = load_suites(); + if (!suites) + { + return EXIT_FAILURE; + } + + fprintf(stderr, "Running %u test suites:\n", array_count(suites)); + + enumerator = array_create_enumerator(suites); + while (enumerator->enumerate(enumerator, &suite)) + { + if (run_suite(suite)) + { + passed++; + } + } + enumerator->destroy(enumerator); + + if (passed == array_count(suites)) + { + fprintf(stderr, "%sPassed all %u suites%s\n", + TTY(GREEN), array_count(suites), TTY(DEF)); + result = EXIT_SUCCESS; + } + else + { + fprintf(stderr, "%sPassed %u of %u suites%s\n", + TTY(RED), passed, array_count(suites), TTY(DEF)); + result = EXIT_FAILURE; + } + + unload_suites(suites); + + return result; } diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index 63b71f724..a35c01241 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -16,7 +16,7 @@ #ifndef TEST_RUNNER_H_ #define TEST_RUNNER_H_ -#include +#include Suite *bio_reader_suite_create(); Suite *bio_writer_suite_create(); diff --git a/src/libstrongswan/tests/test_suite.c b/src/libstrongswan/tests/test_suite.c new file mode 100644 index 000000000..29221eb68 --- /dev/null +++ b/src/libstrongswan/tests/test_suite.c @@ -0,0 +1,240 @@ +/* + * 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 . + * + * 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. + */ + +#include "test_suite.h" + +#include +#include + +/** + * Failure message buf + */ +static char failure_buf[512]; + +/** + * Source file failure occured + */ +static const char *failure_file; + +/** + * Line of source file failure occured + */ +static int failure_line; + +/** + * Backtrace of failure, if any + */ +static backtrace_t *failure_backtrace; + +/** + * Longjump restore point when failing + */ +sigjmp_buf test_restore_point_env; + +/** + * See header. + */ +test_suite_t* test_suite_create(const char *name) +{ + test_suite_t *suite; + + INIT(suite, + .name = name, + .tcases = array_create(0, 0), + ); + return suite; +} + +/** + * See header. + */ +test_case_t* test_case_create(const char *name) +{ + test_case_t *tcase; + + INIT(tcase, + .name = name, + .functions = array_create(sizeof(test_function_t), 0), + .fixtures = array_create(sizeof(test_fixture_t), 0), + .timeout = TEST_FUNCTION_DEFAULT_TIMEOUT, + ); + return tcase; +} + +/** + * See header. + */ +void test_case_add_checked_fixture(test_case_t *tcase, test_fixture_cb_t setup, + test_fixture_cb_t teardown) +{ + test_fixture_t fixture = { + .setup = setup, + .teardown = teardown, + }; + array_insert(tcase->fixtures, -1, &fixture); +} + +/** + * See header. + */ +void test_case_add_test_name(test_case_t *tcase, char *name, + test_function_cb_t cb, int start, int end) +{ + test_function_t fun = { + .name = name, + .cb = cb, + .start = start, + .end = end, + }; + array_insert(tcase->functions, -1, &fun); +} + +/** + * See header. + */ +void test_case_set_timeout(test_case_t *tcase, int s) +{ + tcase->timeout = s; +} + +/** + * See header. + */ +void test_suite_add_case(test_suite_t *suite, test_case_t *tcase) +{ + array_insert(suite->tcases, -1, tcase); +} + +/** + * Let test case fail + */ +static inline void test_failure() +{ + siglongjmp(test_restore_point_env, 1); +} + +/** + * See header. + */ +void test_fail_vmsg(const char *file, int line, char *fmt, va_list args) +{ + vsnprintf(failure_buf, sizeof(failure_buf), fmt, args); + failure_line = line; + failure_file = file; + + test_failure(); +} + +/** + * See header. + */ +void test_fail_msg(const char *file, int line, char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsnprintf(failure_buf, sizeof(failure_buf), fmt, args); + failure_line = line; + failure_file = file; + va_end(args); + + test_failure(); +} + +/** + * Signal handler catching critical and alarm signals + */ +static void test_sighandler(int signal) +{ + char *signame; + bool old = FALSE; + + switch (signal) + { + case SIGSEGV: + signame = "SIGSEGV"; + break; + case SIGILL: + signame = "SIGILL"; + break; + case SIGBUS: + signame = "SIGBUS"; + break; + case SIGALRM: + signame = "timeout"; + break; + default: + signame = "SIG"; + break; + } + if (lib->leak_detective) + { + old = lib->leak_detective->set_state(lib->leak_detective, FALSE); + } + failure_backtrace = backtrace_create(3); + if (lib->leak_detective) + { + lib->leak_detective->set_state(lib->leak_detective, old); + } + test_fail_msg(NULL, 0, "%s(%d)", signame, signal); +} + +/** + * See header. + */ +void test_setup_handler() +{ + struct sigaction action; + + action.sa_handler = test_sighandler; + action.sa_flags = 0; + sigemptyset(&action.sa_mask); + sigaction(SIGSEGV, &action, NULL); + sigaction(SIGILL, &action, NULL); + sigaction(SIGBUS, &action, NULL); + sigaction(SIGALRM, &action, NULL); +} + +/** + * See header. + */ +void test_setup_timeout(int s) +{ + alarm(s); +} + +/** + * See header. + */ +int test_failure_get(char *msg, int len, const char **file) +{ + strncpy(msg, failure_buf, len - 1); + msg[len - 1] = 0; + *file = failure_file; + return failure_line; +} + +/** + * See header. + */ +backtrace_t *test_failure_backtrace() +{ + backtrace_t *bt; + + bt = failure_backtrace; + failure_backtrace = NULL; + + return bt; +} diff --git a/src/libstrongswan/tests/test_suite.h b/src/libstrongswan/tests/test_suite.h index 2a2861323..11aca3b5a 100644 --- a/src/libstrongswan/tests/test_suite.h +++ b/src/libstrongswan/tests/test_suite.h @@ -1,6 +1,8 @@ /* * 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 @@ -16,95 +18,312 @@ #ifndef TEST_UTILS_H_ #define TEST_UTILS_H_ -#include +#define _GNU_SOURCE +#include + #include #include +#include +#include + +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; /** - * Used to mark test cases that use test fixtures. + * Default timeout for a single test function */ -#define UNIT_TEST_FIXTURE_USED "UNIT_TEST_FIXTURE_USED" +#define TEST_FUNCTION_DEFAULT_TIMEOUT 2 /** - * Check for memory leaks and fail if any are encountered. + * Test function implementation */ -#define CHECK_FOR_LEAKS() do \ -{ \ - if (lib->leak_detective) \ - { \ - if (lib->leak_detective->leaks(lib->leak_detective)) { \ - lib->leak_detective->report(lib->leak_detective, TRUE); \ - } \ - ck_assert_int_eq(lib->leak_detective->leaks(lib->leak_detective), 0); \ - } \ -} \ -while(0) +typedef void (*test_function_cb_t)(int); /** - * Extended versions of the START|END_TEST macros that use leak detective. + * 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 * - * Since each test case runs in its own fork of the test runner the stuff - * allocated before the test starts is not freed, so leak detective is disabled - * by default to prevent false positives. By enabling it right when the test - * starts we at least capture leaks created by the tested objects/functions and - * the test case itself. This allows writing test cases for cleanup functions. + * @param name name of the test suite + * @return test suite + */ +test_suite_t* test_suite_create(const char *name); + +/** + * Create a new test case * - * To define test fixture with possibly allocated/destroyed memory that is - * allocated/freed in a test case use the START|END_SETUP|TEARDOWN macros. + * @param name name of test case + * @return test case */ -#undef START_TEST -#define START_TEST(name) \ -static void name (int _i CK_ATTRIBUTE_UNUSED) \ -{ \ - tcase_fn_start(""#name, __FILE__, __LINE__); \ - dbg_default_set_level(LEVEL_SILENT); \ - if (lib->leak_detective) \ - { \ - lib->leak_detective->set_state(lib->leak_detective, TRUE); \ - } +test_case_t* test_case_create(const char *name); -#undef END_TEST -#define END_TEST \ - if (!lib->get(lib, UNIT_TEST_FIXTURE_USED)) \ +/** + * 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 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_suite_add_case(test_suite_t *suite, test_case_t *tcase); + +/** + * sigjmp restore point used by test_restore_point + */ +extern sigjmp_buf test_restore_point_env; + +/** + * 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 + */ +#define test_restore_point() (sigsetjmp(test_restore_point_env, 1) == 0) + +/** + * 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, ...); + +/** + * 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; \ + if (_a != _b) \ { \ - CHECK_FOR_LEAKS(); \ + test_fail_msg(__FILE__, __LINE__, #a " != " #b " (%d != %d)", _a, _b); \ } \ -} +}) /** - * Define a function to setup a test fixture that can be used with the above - * macros. + * Check if two strings equal, fail test if not + * + * @param a first string + * @param b second string */ -#define START_SETUP(name) \ -static void name() \ -{ \ - lib->set(lib, UNIT_TEST_FIXTURE_USED, (void*)TRUE); \ - if (lib->leak_detective) \ +#define test_str_eq(a, b) \ +({ \ + char* _a = (char*)a; \ + char* _b = (char*)b; \ + if (!_a || !_b || !streq(_a, _b)) \ { \ - lib->leak_detective->set_state(lib->leak_detective, TRUE); \ - } + test_fail_msg(__FILE__, __LINE__, \ + #a " != " #b " (\"%s\" != \"%s\")", _a, _b); \ + } \ +}) /** - * End a setup function + * Check if a statement evaluates to TRUE, fail test if not + * + * @param x statement to evaluate */ +#define test_assert(x) \ +({ \ + if (!(x)) \ + { \ + test_fail_msg(__FILE__, __LINE__, #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, ...) \ +({ \ + if (!(x)) \ + { \ + test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__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 fail(fmt, ...) test_fail_msg(__FILE__, __LINE__, fmt, ##__VA_ARGS__) +#define fail_if(x, fmt, ...) \ +({ \ + if (x) \ + { \ + test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__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 } +#define START_SETUP(name) static void name() { #define END_SETUP } - -/** - * Define a function to teardown a test fixture that can be used with the above - * macros. - */ -#define START_TEARDOWN(name) \ -static void name() \ -{ - -/** - * End a teardown function - */ -#define END_TEARDOWN \ - if (lib->get(lib, UNIT_TEST_FIXTURE_USED)) \ - { \ - CHECK_FOR_LEAKS(); \ - } \ -} +#define START_TEARDOWN(name) static void name() { +#define END_TEARDOWN } #endif /** TEST_UTILS_H_ */ From 6531afb5570324f236c9166abd66318e1b5e2e32 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Nov 2013 11:59:11 +0100 Subject: [PATCH 07/54] automake: Don't use parallel test harness being the default with automake 1.13 We have no need for the parallel test harness, and we prefer to have the output of make check on the console --- configure.ac | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index df1dc6847..4d687cef7 100644 --- a/configure.ac +++ b/configure.ac @@ -20,7 +20,17 @@ # ============================ AC_INIT([strongSwan],[5.1.1]) -AM_INIT_AUTOMAKE([tar-ustar subdir-objects]) +AM_INIT_AUTOMAKE(m4_esyscmd([ + echo tar-ustar + echo subdir-objects + case `automake --version | head -n 1` in + *" 1.9"*);; + *" 1.10"*);; + *" 1.11"*);; + # don't use parallel test harness in 1.12 and up + *) echo serial-tests;; + esac +])) m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES]) AC_CONFIG_MACRO_DIR([m4/config]) AC_CONFIG_HEADERS([config.h]) From a5860cddae7a8c8f8734faf046f1a63746727565 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Nov 2013 11:26:33 +0100 Subject: [PATCH 08/54] unit-tests: Enable libstrongswan tests even if --enable-unit-tests not set As we don't depend on the check framework anymore, we can enable the unit tests by default. These are built/executed with "make check" only, so it makes no sense to disable them. --- src/libstrongswan/Makefile.am | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/libstrongswan/Makefile.am b/src/libstrongswan/Makefile.am index 3804adb03..63603bfe9 100644 --- a/src/libstrongswan/Makefile.am +++ b/src/libstrongswan/Makefile.am @@ -488,9 +488,7 @@ if MONOLITHIC endif endif -if UNITTESTS if MONOLITHIC SUBDIRS += . endif - SUBDIRS += tests -endif +SUBDIRS += tests From 7a13990964f2d8e5d2e85493c00a78229912e6e5 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 12:32:15 +0200 Subject: [PATCH 09/54] backtrace: Support backtracing even if library is not initialized But of course backtracing must be initialized anyway using backtrace_init(). --- src/libstrongswan/utils/backtrace.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/libstrongswan/utils/backtrace.c b/src/libstrongswan/utils/backtrace.c index fb2c4d1e8..f1584620b 100644 --- a/src/libstrongswan/utils/backtrace.c +++ b/src/libstrongswan/utils/backtrace.c @@ -314,7 +314,7 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) bool old = FALSE; bfd_mutex->lock(bfd_mutex); - if (lib->leak_detective) + if (lib && lib->leak_detective) { old = lib->leak_detective->set_state(lib->leak_detective, FALSE); } @@ -324,7 +324,7 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void *base) data.entry = entry; bfd_map_over_sections(entry->abfd, (void*)find_addr, &data); } - if (lib->leak_detective) + if (lib && lib->leak_detective) { lib->leak_detective->set_state(lib->leak_detective, old); } From bbb62267e030606325d16407ecce092ad3700a4b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 15:50:17 +0200 Subject: [PATCH 10/54] thread: Note that tread_cancellation_point temporarily activates cancelability --- src/libstrongswan/threading/thread.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/libstrongswan/threading/thread.h b/src/libstrongswan/threading/thread.h index 31b9e1b3a..8d3c30e9b 100644 --- a/src/libstrongswan/threading/thread.h +++ b/src/libstrongswan/threading/thread.h @@ -71,7 +71,6 @@ typedef void *(*thread_main_t)(void *arg); */ typedef void (*thread_cleanup_t)(void *arg); - /** * Thread wrapper implements simple, portable and advanced thread functions. * @@ -110,10 +109,8 @@ struct thread_t { * a call to exit. */ void *(*join)(thread_t *this); - }; - /** * Create a new thread instance. * @@ -168,6 +165,10 @@ bool thread_cancelability(bool enable); /** * Force creation of a cancellation point in the calling thread. + * + * This temporarily enables thread cancelability, tests for a pending + * cancellation request and then disables cancelability again if it was + * disabled before the call to thread_cancellation_point(). */ void thread_cancellation_point(); @@ -188,6 +189,4 @@ void threads_init(); */ void threads_deinit(); - #endif /** THREADING_THREAD_H_ @} */ - From f7b8396af08102b7136f36fd394cb13ae92cb3ed Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 12:24:21 +0200 Subject: [PATCH 11/54] unit-tests: Initialize backtracing before printing any backtraces --- src/libstrongswan/tests/test_runner.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index a46007a3a..b6087a816 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -311,6 +311,8 @@ static void print_failures(array_t *failures) { failure_t failure; + backtrace_init(); + while (array_remove(failures, 0, &failure)) { fprintf(stderr, " %sFailure in '%s': %s (", @@ -326,6 +328,8 @@ static void print_failures(array_t *failures) failure.bt->destroy(failure.bt); } } + + backtrace_deinit(); } /** From 712940d1619e4e49f3aa9e87a67d89cd1d50a4b1 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Thu, 17 Oct 2013 17:05:38 +0200 Subject: [PATCH 12/54] unit-tests: Pass linked_list->invoke* varargs as uintptr_t Passing integers of unspecified length may result in passing an integer shorter than uintptr_t. When reading them back, we might get more data than passed, resulting in a failure. --- .../tests/suites/test_linked_list.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_linked_list.c b/src/libstrongswan/tests/suites/test_linked_list.c index 9e85c58d8..c0c997e1a 100644 --- a/src/libstrongswan/tests/suites/test_linked_list.c +++ b/src/libstrongswan/tests/suites/test_linked_list.c @@ -246,10 +246,10 @@ struct invoke_t { static void invoke(intptr_t item, void *a, void *b, void *c, void *d, int *sum) { - ck_assert(a == (void*)1); - ck_assert(b == (void*)2); - ck_assert(c == (void*)3); - ck_assert(d == (void*)4); + ck_assert_int_eq((uintptr_t)a, 1); + ck_assert_int_eq((uintptr_t)b, 2); + ck_assert_int_eq((uintptr_t)c, 3); + ck_assert_int_eq((uintptr_t)d, 4); *sum += item; } @@ -267,7 +267,9 @@ START_TEST(test_invoke_function) list->insert_last(list, (void*)3); list->insert_last(list, (void*)4); list->insert_last(list, (void*)5); - list->invoke_function(list, (linked_list_invoke_t)invoke, 1, 2, 3, 4, &sum); + list->invoke_function(list, (linked_list_invoke_t)invoke, + (uintptr_t)1, (uintptr_t)2, + (uintptr_t)3, (uintptr_t)4, &sum); ck_assert_int_eq(sum, 15); } END_TEST @@ -287,7 +289,9 @@ START_TEST(test_invoke_offset) { list->insert_last(list, &items[i]); } - list->invoke_offset(list, offsetof(invoke_t, invoke), 1, 2, 3, 4, &sum); + list->invoke_offset(list, offsetof(invoke_t, invoke), + (uintptr_t)1, (uintptr_t)2, + (uintptr_t)3, (uintptr_t)4, &sum); ck_assert_int_eq(sum, 15); } END_TEST From 382fa8b41991a366eafc84f845a20e0bf5b05a51 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Nov 2013 12:13:08 +0100 Subject: [PATCH 13/54] unit-tests: Clean up memory in new asn1 unit tests Test runner checks for leaks when leak detective is enabled. --- src/libstrongswan/tests/suites/test_asn1.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/suites/test_asn1.c b/src/libstrongswan/tests/suites/test_asn1.c index 5ac4a9423..1cd81e0f1 100644 --- a/src/libstrongswan/tests/suites/test_asn1.c +++ b/src/libstrongswan/tests/suites/test_asn1.c @@ -45,6 +45,7 @@ START_TEST(test_asn1_algorithmIdentifier) { algid = asn1_algorithmIdentifier(test[i].n); ck_assert(chunk_equals(algid, test[i].algid)); + free(algid.ptr); } } END_TEST @@ -527,6 +528,7 @@ START_TEST(test_asn1_from_time) } chunk = asn1_from_time(&test[i].time, test[i].type); ck_assert(chunk_equals(chunk, test[i].chunk)); + free(chunk.ptr); } } END_TEST @@ -600,7 +602,7 @@ START_TEST(test_asn1_build_object) pos = asn1_build_object(&a, test[i].b[0], test[i].len); ck_assert(pos == (a.ptr + test[i].size)); ck_assert(a.len == test[i].size + test[i].len); - ck_assert(memeq(a.ptr, test[i].b, test[i].size)); + ck_assert(memeq(a.ptr, test[i].b, test[i].size)); chunk_free(&a); } } From 1254ad01b9f7977b0d2d7be8f734f8c10a702727 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 15 Oct 2013 15:15:45 +0200 Subject: [PATCH 14/54] unit-tests: Fix a compiler warning in identification tests --- src/libstrongswan/tests/suites/test_identification.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/suites/test_identification.c b/src/libstrongswan/tests/suites/test_identification.c index 1dc6776d1..edf53f0fd 100644 --- a/src/libstrongswan/tests/suites/test_identification.c +++ b/src/libstrongswan/tests/suites/test_identification.c @@ -179,7 +179,7 @@ static struct { START_TEST(test_from_string) { identification_t *a; - chunk_t encoding, expected; + chunk_t encoding, expected = chunk_empty; char *id; id = string_data[_i].id; From 45766923b86e79168ed9331ce3a1e0902822495d Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 18:21:01 +0200 Subject: [PATCH 15/54] unit-tests: Avoid name clash with clone() from --- src/libstrongswan/tests/suites/test_linked_list.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_linked_list.c b/src/libstrongswan/tests/suites/test_linked_list.c index c0c997e1a..922f954e3 100644 --- a/src/libstrongswan/tests/suites/test_linked_list.c +++ b/src/libstrongswan/tests/suites/test_linked_list.c @@ -307,7 +307,7 @@ struct clone_t { void *(*clone)(clone_t *item); }; -static void *clone(clone_t *item) +static void *clonefn(clone_t *item) { return item->val; } @@ -330,11 +330,11 @@ START_TEST(test_clone_offset) { linked_list_t *other; clone_t items[] = { - { .val = (void*)1, .clone = clone, }, - { .val = (void*)2, .clone = clone, }, - { .val = (void*)3, .clone = clone, }, - { .val = (void*)4, .clone = clone, }, - { .val = (void*)5, .clone = clone, }, + { .val = (void*)1, .clone = clonefn, }, + { .val = (void*)2, .clone = clonefn, }, + { .val = (void*)3, .clone = clonefn, }, + { .val = (void*)4, .clone = clonefn, }, + { .val = (void*)5, .clone = clonefn, }, }; int i; From b4d43a542fd4ee85f081040475224da7fa4ba4be Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 15 Oct 2013 18:15:29 +0200 Subject: [PATCH 16/54] unit-tests: Skip fmemopen() based printf() tests if not available --- src/libstrongswan/tests/suites/test_printf.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_printf.c b/src/libstrongswan/tests/suites/test_printf.c index 6c15fbea1..8558cca40 100644 --- a/src/libstrongswan/tests/suites/test_printf.c +++ b/src/libstrongswan/tests/suites/test_printf.c @@ -20,7 +20,6 @@ static void verify(char *expected, char *format, ...) { - FILE *mem; char buf[128]; va_list args; @@ -29,12 +28,18 @@ static void verify(char *expected, char *format, ...) ck_assert_str_eq(expected, buf); va_end(args); - mem = fmemopen(buf, sizeof(buf), "w"); - va_start(args, format); - vfprintf(mem, format, args); - va_end(args); - fclose(mem); - ck_assert_str_eq(expected, buf); +#ifdef HAVE_FMEMOPEN + { + FILE *mem; + + mem = fmemopen(buf, sizeof(buf), "w"); + va_start(args, format); + vfprintf(mem, format, args); + va_end(args); + fclose(mem); + ck_assert_str_eq(expected, buf); + } +#endif /* HAVE_FMEMOPEN */ } START_TEST(test_printf_strings) From b74b8addf8a9f925d0ea8ed77049eb1b3e45450a Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 5 Nov 2013 10:13:36 +0100 Subject: [PATCH 17/54] unit-tests: Show how many test vectors have failed on test failure --- src/libstrongswan/tests/suites/test_vectors.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/libstrongswan/tests/suites/test_vectors.c b/src/libstrongswan/tests/suites/test_vectors.c index f2817d314..242ac9d09 100644 --- a/src/libstrongswan/tests/suites/test_vectors.c +++ b/src/libstrongswan/tests/suites/test_vectors.c @@ -21,7 +21,8 @@ START_TEST(test_vectors) { - fail_if(lib->crypto->get_test_vector_failures(lib->crypto)); + u_int failed = lib->crypto->get_test_vector_failures(lib->crypto); + fail_if(failed > 0, "%u test vectors failed", failed); } END_TEST From f23fd4c59be0046418de059fe3b08de6f8ae4cf1 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 11:38:29 +0200 Subject: [PATCH 18/54] unit-tests: Use a home-brew thread barrier to remove pthread dependency --- .../tests/suites/test_threading.c | 101 +++++++++++++++--- 1 file changed, 86 insertions(+), 15 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 0c768b3e2..c75d6de21 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -14,12 +14,13 @@ * for more details. */ -#include -#include - #include "test_suite.h" +#include + +#include #include +#include /******************************************************************************* * recursive mutex test @@ -27,31 +28,100 @@ #define THREADS 20 -static mutex_t *mutex; +/** + * Thread barrier data + */ +typedef struct { + mutex_t *mutex; + condvar_t *cond; + int count; + int current; + bool active; +} barrier_t; -static pthread_barrier_t mutex_barrier; +/** + * Create a thread barrier for count threads + */ +static barrier_t* barrier_create(int count) +{ + barrier_t *this; -static int mutex_locked = 0; + INIT(this, + .mutex = mutex_create(MUTEX_TYPE_DEFAULT), + .cond = condvar_create(CONDVAR_TYPE_DEFAULT), + .count = count, + ); + + return this; +} + +/** + * Destroy a thread barrier + */ +static void barrier_destroy(barrier_t *this) +{ + this->mutex->destroy(this->mutex); + this->cond->destroy(this->cond); + free(this); +} + +/** + * Wait to have configured number of threads in barrier + */ +static bool barrier_wait(barrier_t *this) +{ + bool winner = FALSE; + + this->mutex->lock(this->mutex); + if (!this->active) + { /* first, reset */ + this->active = TRUE; + this->current = 0; + } + + this->current++; + while (this->current < this->count) + { + this->cond->wait(this->cond, this->mutex); + } + if (this->active) + { /* first, win */ + winner = TRUE; + this->active = FALSE; + } + this->mutex->unlock(this->mutex); + this->cond->broadcast(this->cond); + sched_yield(); + + return winner; +} + +/** + * Barrier for some tests + */ +static barrier_t *barrier; static void *mutex_run(void *data) { + mutex_t *mutex = (mutex_t*)data; + static int locked = 0; int i; /* wait for all threads before getting in action */ - pthread_barrier_wait(&mutex_barrier); + barrier_wait(barrier); for (i = 0; i < 100; i++) { mutex->lock(mutex); mutex->lock(mutex); mutex->lock(mutex); - mutex_locked++; + locked++; sched_yield(); - if (mutex_locked > 1) + if (locked > 1) { fail("two threads locked the mutex concurrently"); } - mutex_locked--; + locked--; mutex->unlock(mutex); mutex->unlock(mutex); mutex->unlock(mutex); @@ -61,9 +131,11 @@ static void *mutex_run(void *data) START_TEST(test_mutex) { - pthread_t threads[THREADS]; + thread_t *threads[THREADS]; + mutex_t *mutex; int i; + barrier = barrier_create(THREADS); mutex = mutex_create(MUTEX_TYPE_RECURSIVE); for (i = 0; i < 10; i++) @@ -80,18 +152,17 @@ START_TEST(test_mutex) mutex->unlock(mutex); } - pthread_barrier_init(&mutex_barrier, NULL, THREADS); for (i = 0; i < THREADS; i++) { - pthread_create(&threads[i], NULL, mutex_run, NULL); + threads[i] = thread_create(mutex_run, mutex); } for (i = 0; i < THREADS; i++) { - pthread_join(threads[i], NULL); + threads[i]->join(threads[i]); } - pthread_barrier_destroy(&mutex_barrier); mutex->destroy(mutex); + barrier_destroy(barrier); } END_TEST From 23b8f9bf86ae7fa49536bd49aa5ed20e7f7f9150 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 15:49:58 +0200 Subject: [PATCH 19/54] unit-tests: Support testing multi-threaded code --- src/libstrongswan/tests/test_runner.c | 9 +++-- src/libstrongswan/tests/test_suite.c | 47 ++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index b6087a816..2b63ec72e 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -75,6 +75,9 @@ static array_t *load_suites() } otest = 0; library_init(NULL); + + test_setup_handler(); + if (!load_plugins()) { library_deinit(); @@ -276,6 +279,10 @@ static bool post_test(bool check_leaks, array_t *failures, char *name, int i) .i = i, }; + lib->processor->set_threads(lib->processor, 0); + lib->processor->cancel(lib->processor); + lib->plugins->unload(lib->plugins); + if (check_leaks && lib->leak_detective) { lib->leak_detective->set_report_cb(lib->leak_detective, @@ -459,8 +466,6 @@ int main(int argc, char *argv[]) /* redirect all output to stderr (to redirect make's stdout to /dev/null) */ dup2(2, 1); - test_setup_handler(); - suites = load_suites(); if (!suites) { diff --git a/src/libstrongswan/tests/test_suite.c b/src/libstrongswan/tests/test_suite.c index 29221eb68..6a4c7b96b 100644 --- a/src/libstrongswan/tests/test_suite.c +++ b/src/libstrongswan/tests/test_suite.c @@ -18,6 +18,8 @@ #include #include +#include + /** * Failure message buf */ @@ -117,12 +119,25 @@ void test_suite_add_case(test_suite_t *suite, test_case_t *tcase) array_insert(suite->tcases, -1, tcase); } +/** + * Main thread performing tests + */ +static pthread_t main_thread; + /** * Let test case fail */ static inline void test_failure() { - siglongjmp(test_restore_point_env, 1); + if (pthread_self() == main_thread) + { + siglongjmp(test_restore_point_env, 1); + } + else + { + pthread_kill(main_thread, SIGUSR1); + /* how can we stop just the thread? longjmp to a restore point? */ + } } /** @@ -163,6 +178,9 @@ static void test_sighandler(int signal) switch (signal) { + case SIGUSR1: + /* a different thread failed, abort test */ + return test_failure(); case SIGSEGV: signame = "SIGSEGV"; break; @@ -189,6 +207,11 @@ static void test_sighandler(int signal) lib->leak_detective->set_state(lib->leak_detective, old); } test_fail_msg(NULL, 0, "%s(%d)", signame, signal); + /* unable to restore a valid context for that thread, terminate */ + fprintf(stderr, "\n%s(%d) outside of main thread:\n", signame, signal); + failure_backtrace->log(failure_backtrace, stderr, TRUE); + fprintf(stderr, "terminating...\n"); + abort(); } /** @@ -196,15 +219,20 @@ static void test_sighandler(int signal) */ void test_setup_handler() { - struct sigaction action; + struct sigaction action = { + .sa_handler = test_sighandler, + }; - action.sa_handler = test_sighandler; - action.sa_flags = 0; - sigemptyset(&action.sa_mask); + main_thread = pthread_self(); + + /* signal handler inherited by all threads */ sigaction(SIGSEGV, &action, NULL); sigaction(SIGILL, &action, NULL); sigaction(SIGBUS, &action, NULL); + /* ignore ALRM/USR1, these are catched by main thread only */ + action.sa_handler = SIG_IGN; sigaction(SIGALRM, &action, NULL); + sigaction(SIGUSR1, &action, NULL); } /** @@ -212,6 +240,15 @@ void test_setup_handler() */ void test_setup_timeout(int s) { + struct sigaction action = { + .sa_handler = test_sighandler, + }; + + /* This called by main thread only. Setup handler for timeout and + * failure cross-thread signaling. */ + sigaction(SIGALRM, &action, NULL); + sigaction(SIGUSR1, &action, NULL); + alarm(s); } From 8eda87af86903f3432be412a83440912ee101e81 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 13:45:48 +0200 Subject: [PATCH 20/54] unit-tests: Add a few test cases for watcher --- src/libstrongswan/tests/Makefile.am | 1 + src/libstrongswan/tests/suites/test_watcher.c | 214 ++++++++++++++++++ src/libstrongswan/tests/test_runner.c | 1 + src/libstrongswan/tests/test_runner.h | 1 + 4 files changed, 217 insertions(+) create mode 100644 src/libstrongswan/tests/suites/test_watcher.c diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index c9d10e9fd..85060378c 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -14,6 +14,7 @@ test_runner_SOURCES = \ suites/test_hashtable.c \ suites/test_identification.c \ suites/test_threading.c \ + suites/test_watcher.c \ suites/test_utils.c \ suites/test_vectors.c \ suites/test_array.c \ diff --git a/src/libstrongswan/tests/suites/test_watcher.c b/src/libstrongswan/tests/suites/test_watcher.c new file mode 100644 index 000000000..9415bead9 --- /dev/null +++ b/src/libstrongswan/tests/suites/test_watcher.c @@ -0,0 +1,214 @@ +/* + * 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 . + * + * 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. + */ + +#include "test_suite.h" + +#include + +#include +#include +#include + +static char testbuf[1] = ""; + +static bool readcb(void *data, int fd, watcher_event_t event) +{ + ck_assert_int_eq(*(int*)data, fd); + ck_assert_int_eq(event, WATCHER_READ); + + if (recv(fd, testbuf, 1, MSG_DONTWAIT) != 1) + { + ck_assert(errno == EAGAIN || errno == EWOULDBLOCK); + } + return TRUE; +} + +START_TEST(test_read) +{ + int fd[2]; + char c; + + lib->processor->set_threads(lib->processor, 8); + + ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1); + + lib->watcher->add(lib->watcher, fd[0], WATCHER_READ, readcb, &fd[0]); + + for (c = 'a'; c <= 'z'; c++) + { + ck_assert_int_eq(write(fd[1], &c, 1), 1); + while (testbuf[0] != c) + { + sched_yield(); + } + } + + lib->watcher->remove(lib->watcher, fd[0]); + close(fd[0]); + close(fd[1]); + + lib->processor->cancel(lib->processor); +} +END_TEST + +static bool writecb(void *data, int fd, watcher_event_t event) +{ + ck_assert_int_eq(event, WATCHER_WRITE); + if (send(fd, data, 1, MSG_DONTWAIT) != 1) + { + ck_assert(errno == EAGAIN || errno == EWOULDBLOCK); + } + return TRUE; +} + +START_TEST(test_write) +{ + int fd[2]; + char in = 'x', out; + + lib->processor->set_threads(lib->processor, 8); + + ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1); + + lib->watcher->add(lib->watcher, fd[1], WATCHER_WRITE, writecb, &in); + + ck_assert_int_eq(read(fd[0], &out, 1), 1); + ck_assert_int_eq(out, in); + + lib->watcher->remove(lib->watcher, fd[1]); + close(fd[1]); + close(fd[0]); + + lib->processor->cancel(lib->processor); +} +END_TEST + +static bool multiread(void *data, int fd, watcher_event_t event) +{ + ck_assert_int_eq(event, WATCHER_READ); + if (recv(fd, data, 1, MSG_DONTWAIT) != 1) + { + ck_assert(errno == EAGAIN || errno == EWOULDBLOCK); + } + return TRUE; +} + +START_TEST(test_multiread) +{ + int fd[10][2], i; + char in, out[countof(fd)]; + + lib->processor->set_threads(lib->processor, 8); + + for (i = 0; i < countof(fd); i++) + { + ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd[i]) != -1); + lib->watcher->add(lib->watcher, fd[i][0], + WATCHER_READ, multiread, &out[i]); + } + + for (i = 0; i < countof(fd); i++) + { + for (in = 'a'; in <= 'z'; in++) + { + ck_assert_int_eq(write(fd[i][1], &in, 1), 1); + while (out[i] != in) + { + sched_yield(); + } + } + } + + for (i = 0; i < countof(fd); i++) + { + lib->watcher->remove(lib->watcher, fd[i][0]); + close(fd[i][1]); + close(fd[i][0]); + } + + lib->processor->cancel(lib->processor); +} +END_TEST + +static bool multiwrite(void *data, int fd, watcher_event_t event) +{ + ck_assert_int_eq(event, WATCHER_WRITE); + if (send(fd, data, 1, MSG_DONTWAIT) != 1) + { + ck_assert(errno == EAGAIN || errno == EWOULDBLOCK); + } + return TRUE; +} + +START_TEST(test_multiwrite) +{ + int fd[10][2], i, j; + u_char out, in[countof(fd)]; + + lib->processor->set_threads(lib->processor, 8); + + for (i = 0; i < countof(fd); i++) + { + ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd[i]) != -1); + in[i] = i; + lib->watcher->add(lib->watcher, fd[i][1], + WATCHER_WRITE, multiwrite, &in[i]); + } + + for (j = 0; j < 10; j++) + { + for (i = 0; i < countof(fd); i++) + { + ck_assert_int_eq(read(fd[i][0], &out, 1), 1); + ck_assert_int_eq(out, i); + } + } + + for (i = 0; i < countof(fd); i++) + { + lib->watcher->remove(lib->watcher, fd[i][1]); + close(fd[i][1]); + close(fd[i][0]); + } + + lib->processor->cancel(lib->processor); +} +END_TEST + +Suite *watcher_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("watcher"); + + tc = tcase_create("read"); + tcase_add_test(tc, test_read); + suite_add_tcase(s, tc); + + tc = tcase_create("write"); + tcase_add_test(tc, test_write); + suite_add_tcase(s, tc); + + tc = tcase_create("multiread"); + tcase_add_test(tc, test_multiread); + suite_add_tcase(s, tc); + + tc = tcase_create("multiwrite"); + tcase_add_test(tc, test_multiwrite); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 2b63ec72e..09f425429 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -105,6 +105,7 @@ static array_t *load_suites() array_insert(suites, -1, array_suite_create()); array_insert(suites, -1, identification_suite_create()); array_insert(suites, -1, threading_suite_create()); + array_insert(suites, -1, watcher_suite_create()); array_insert(suites, -1, utils_suite_create()); array_insert(suites, -1, host_suite_create()); array_insert(suites, -1, vectors_suite_create()); diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index a35c01241..ea881dae9 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -29,6 +29,7 @@ Suite *hashtable_suite_create(); Suite *array_suite_create(); Suite *identification_suite_create(); Suite *threading_suite_create(); +Suite *watcher_suite_create(); Suite *utils_suite_create(); Suite *vectors_suite_create(); Suite *ecdsa_suite_create(); From b942528419a36280f7172e60ea3183b418a41aa5 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 16 Oct 2013 15:51:12 +0200 Subject: [PATCH 21/54] unit-tests: Add test suite for streams and services --- src/libstrongswan/tests/Makefile.am | 1 + src/libstrongswan/tests/suites/test_stream.c | 267 +++++++++++++++++++ src/libstrongswan/tests/test_runner.c | 1 + src/libstrongswan/tests/test_runner.h | 1 + 4 files changed, 270 insertions(+) create mode 100644 src/libstrongswan/tests/suites/test_stream.c diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 85060378c..fd85ee283 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -15,6 +15,7 @@ test_runner_SOURCES = \ suites/test_identification.c \ suites/test_threading.c \ suites/test_watcher.c \ + suites/test_stream.c \ suites/test_utils.c \ suites/test_vectors.c \ suites/test_array.c \ diff --git a/src/libstrongswan/tests/suites/test_stream.c b/src/libstrongswan/tests/suites/test_stream.c new file mode 100644 index 000000000..2d3173d46 --- /dev/null +++ b/src/libstrongswan/tests/suites/test_stream.c @@ -0,0 +1,267 @@ +/* + * 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 . + * + * 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. + */ + +#include "test_suite.h" + +#include + +static char* services[] = { + "unix:///tmp/strongswan-test-service.sck", + "tcp://127.0.0.1:7766", + "tcp://[::1]:7766", +}; + +static char msg[] = "testmessage"; +static int msglen = 12; + +static bool servicing(void *data, stream_t *stream) +{ + char buf[64]; + ssize_t len, total; + + ck_assert(streq((char*)data, "test")); + + for (total = 0; total < msglen;) + { + len = stream->read(stream, buf, sizeof(buf), TRUE); + ck_assert(len > 0); + total += len; + } + for (total = 0; total < msglen;) + { + len = stream->write(stream, buf, len, TRUE); + ck_assert(len > 0); + total += len; + } + + return FALSE; +} + +START_TEST(test_sync) +{ + char buf[64]; + stream_service_t *service; + stream_t *stream; + ssize_t len, total; + + lib->processor->set_threads(lib->processor, 8); + + service = lib->streams->create_service(lib->streams, services[_i], 1); + ck_assert(service != NULL); + service->on_accept(service, servicing, "test", JOB_PRIO_HIGH, 1); + + stream = lib->streams->connect(lib->streams, services[_i]); + ck_assert(stream != NULL); + for (total = 0; total < msglen;) + { + len = stream->write(stream, msg, msglen, TRUE); + ck_assert(len > 0); + total += len; + } + for (total = 0; total < msglen;) + { + len = stream->read(stream, buf, sizeof(buf), TRUE); + ck_assert(len > 0); + total += len; + } + ck_assert(streq(buf, msg)); + stream->destroy(stream); + + service->destroy(service); +} +END_TEST + +static bool on_write(void *data, stream_t *stream) +{ + ssize_t len, total; + + ck_assert(streq((char*)data, "test-write")); + for (total = 0; total < msglen;) + { + len = stream->write(stream, msg, msglen, TRUE); + ck_assert(len > 0); + total += len; + } + return FALSE; +} + +static bool read_done = FALSE; + +static bool on_read(void *data, stream_t *stream) +{ + ssize_t len, total; + char buf[64]; + + ck_assert(streq((char*)data, "test-read")); + for (total = 0; total < msglen;) + { + len = stream->read(stream, buf, sizeof(buf), TRUE); + ck_assert(len > 0); + total += len; + } + ck_assert(streq(buf, msg)); + read_done = TRUE; + return FALSE; +} + +START_TEST(test_async) +{ + stream_service_t *service; + stream_t *stream; + + + lib->processor->set_threads(lib->processor, 8); + + service = lib->streams->create_service(lib->streams, services[_i], 1); + ck_assert(service != NULL); + service->on_accept(service, servicing, "test", JOB_PRIO_HIGH, 0); + + stream = lib->streams->connect(lib->streams, services[_i]); + ck_assert(stream != NULL); + read_done = FALSE; + stream->on_write(stream, (stream_cb_t)on_write, "test-write"); + stream->on_read(stream, (stream_cb_t)on_read, "test-read"); + + while (!read_done) + { + usleep(1000); + } + stream->destroy(stream); + + service->destroy(service); +} +END_TEST + +static bool all(void *data, stream_t *stream) +{ + char buf[64], *pos; + ssize_t len; + int i; + + pos = buf; + for (i = 0; i < msglen; i++) + { + len = stream->read(stream, pos, 1, TRUE); + ck_assert_int_eq(len, 1); + pos += len; + } + pos = buf; + for (i = 0; i < msglen; i++) + { + len = stream->write(stream, pos, 1, TRUE); + ck_assert_int_eq(len, 1); + pos += len; + } + + return FALSE; +} + +START_TEST(test_all) +{ + char buf[64]; + stream_service_t *service; + stream_t *stream; + + lib->processor->set_threads(lib->processor, 8); + + service = lib->streams->create_service(lib->streams, services[_i], 1); + ck_assert(service != NULL); + service->on_accept(service, all, NULL, JOB_PRIO_HIGH, 1); + + stream = lib->streams->connect(lib->streams, services[_i]); + ck_assert(stream != NULL); + ck_assert(stream->write_all(stream, msg, msglen)); + ck_assert(stream->read_all(stream, buf, msglen)); + ck_assert(streq(buf, msg)); + stream->destroy(stream); + + service->destroy(service); +} +END_TEST + +static bool concurrency(void *data, stream_t *stream) +{ + static refcount_t refs = 0; + u_int current; + ssize_t len; + + current = ref_get(&refs); + ck_assert(current <= 3); + len = stream->write(stream, "x", 1, TRUE); + ck_assert_int_eq(len, 1); + usleep(1000); + ignore_result(ref_put(&refs)); + + return FALSE; +} + +START_TEST(test_concurrency) +{ + stream_service_t *service; + stream_t *streams[10]; + ssize_t len; + char x; + int i; + + lib->processor->set_threads(lib->processor, 8); + + service = lib->streams->create_service(lib->streams, services[_i], 10); + ck_assert(service != NULL); + service->on_accept(service, concurrency, NULL, JOB_PRIO_HIGH, 3); + + for (i = 0; i < countof(streams); i++) + { + streams[i] = lib->streams->connect(lib->streams, services[_i]); + ck_assert(streams[i] != NULL); + } + for (i = 0; i < countof(streams); i++) + { + len = streams[i]->read(streams[i], &x, 1, TRUE); + ck_assert_int_eq(len, 1); + ck_assert_int_eq(x, 'x'); + } + for (i = 0; i < countof(streams); i++) + { + streams[i]->destroy(streams[i]); + } + service->destroy(service); +} +END_TEST + +Suite *stream_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("stream"); + + tc = tcase_create("sync"); + tcase_add_loop_test(tc, test_sync, 0, countof(services)); + suite_add_tcase(s, tc); + + tc = tcase_create("async"); + tcase_add_loop_test(tc, test_async, 0, countof(services)); + suite_add_tcase(s, tc); + + tc = tcase_create("all"); + tcase_add_loop_test(tc, test_all, 0, countof(services)); + suite_add_tcase(s, tc); + + tc = tcase_create("concurrency"); + tcase_add_loop_test(tc, test_concurrency, 0, countof(services)); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 09f425429..14db14a89 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -106,6 +106,7 @@ static array_t *load_suites() array_insert(suites, -1, identification_suite_create()); array_insert(suites, -1, threading_suite_create()); array_insert(suites, -1, watcher_suite_create()); + array_insert(suites, -1, stream_suite_create()); array_insert(suites, -1, utils_suite_create()); array_insert(suites, -1, host_suite_create()); array_insert(suites, -1, vectors_suite_create()); diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index ea881dae9..435883648 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -30,6 +30,7 @@ Suite *array_suite_create(); Suite *identification_suite_create(); Suite *threading_suite_create(); Suite *watcher_suite_create(); +Suite *stream_suite_create(); Suite *utils_suite_create(); Suite *vectors_suite_create(); Suite *ecdsa_suite_create(); From 5d4a882f4573cb83eb8f7d504f6afe6b10a36c8b Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 11:45:10 +0200 Subject: [PATCH 22/54] unit-tests: Add a simple thread join() test --- .../tests/suites/test_threading.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index c75d6de21..23c2b9311 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -166,6 +166,29 @@ START_TEST(test_mutex) } END_TEST +static void *join_run(void *data) +{ + /* force some context switches */ + sched_yield(); + return (void*)((uintptr_t)data + THREADS); +} + +START_TEST(test_join) +{ + thread_t *threads[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(join_run, (void*)(uintptr_t)i); + } + for (i = 0; i < THREADS; i++) + { + ck_assert_int_eq((uintptr_t)threads[i]->join(threads[i]), i + THREADS); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -177,5 +200,9 @@ Suite *threading_suite_create() tcase_add_test(tc, test_mutex); suite_add_tcase(s, tc); + tc = tcase_create("thread joining"); + tcase_add_test(tc, test_join); + suite_add_tcase(s, tc); + return s; } From 274e6beb0028041cd70c008261b05b0a5b773dc3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 14:10:38 +0200 Subject: [PATCH 23/54] unit-tests: Add a simple thread detach test --- .../tests/suites/test_threading.c | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 23c2b9311..e04ec565d 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -189,6 +189,38 @@ START_TEST(test_join) } END_TEST +static void *detach_run(void *data) +{ + refcount_t *running = (refcount_t*)data; + + ignore_result(ref_put(running)); + return NULL; +} + +START_TEST(test_detach) +{ + thread_t *threads[THREADS]; + int i; + refcount_t running = 0; + + for (i = 0; i < THREADS; i++) + { + ref_get(&running); + threads[i] = thread_create(detach_run, &running); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->detach(threads[i]); + } + while (running > 0) + { + sched_yield(); + } + /* no checks done here, but we check that thread state gets cleaned + * up with leak detective. */ +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -204,5 +236,9 @@ Suite *threading_suite_create() tcase_add_test(tc, test_join); suite_add_tcase(s, tc); + tc = tcase_create("thread detaching"); + tcase_add_test(tc, test_detach); + suite_add_tcase(s, tc); + return s; } From c320c611605f0553364251a8071c839f624d2216 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 14:17:16 +0200 Subject: [PATCH 24/54] unit-tests: Add thread_exit() tests to both join and detach test cases --- .../tests/suites/test_threading.c | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index e04ec565d..2293af1bc 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -189,6 +189,31 @@ START_TEST(test_join) } END_TEST +static void *exit_join_run(void *data) +{ + sched_yield(); + thread_exit((void*)((uintptr_t)data + THREADS)); + /* not reached */ + ck_assert(FALSE); + return NULL; +} + +START_TEST(test_join_exit) +{ + thread_t *threads[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(exit_join_run, (void*)(uintptr_t)i); + } + for (i = 0; i < THREADS; i++) + { + ck_assert_int_eq((uintptr_t)threads[i]->join(threads[i]), i + THREADS); + } +} +END_TEST + static void *detach_run(void *data) { refcount_t *running = (refcount_t*)data; @@ -221,6 +246,41 @@ START_TEST(test_detach) } END_TEST +static void *detach_exit_run(void *data) +{ + refcount_t *running = (refcount_t*)data; + + ignore_result(ref_put(running)); + thread_exit(NULL); + /* not reached */ + ck_assert(FALSE); + return NULL; +} + +START_TEST(test_detach_exit) +{ + thread_t *threads[THREADS]; + int i; + refcount_t running = 0; + + for (i = 0; i < THREADS; i++) + { + ref_get(&running); + threads[i] = thread_create(detach_exit_run, &running); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->detach(threads[i]); + } + while (running > 0) + { + sched_yield(); + } + /* no checks done here, but we check that thread state gets cleaned + * up with leak detective. */ +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -234,10 +294,12 @@ Suite *threading_suite_create() tc = tcase_create("thread joining"); tcase_add_test(tc, test_join); + tcase_add_test(tc, test_join_exit); suite_add_tcase(s, tc); tc = tcase_create("thread detaching"); tcase_add_test(tc, test_detach); + tcase_add_test(tc, test_detach_exit); suite_add_tcase(s, tc); return s; From 855747eab7f78b7d9dc76a9259e0da4351f21356 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 14:32:06 +0200 Subject: [PATCH 25/54] unit-tests: Add a simple thread_cancel() test --- .../tests/suites/test_threading.c | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 2293af1bc..b8c155e4d 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -17,6 +17,7 @@ #include "test_suite.h" #include +#include #include #include @@ -281,6 +282,36 @@ START_TEST(test_detach_exit) } END_TEST +static void *cancel_run(void *data) +{ + /* default cancellability should be TRUE, so don't change it */ + while (TRUE) + { + sleep(10); + } + return NULL; +} + +START_TEST(test_cancel) +{ + thread_t *threads[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(cancel_run, NULL); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -302,5 +333,9 @@ Suite *threading_suite_create() tcase_add_test(tc, test_detach_exit); suite_add_tcase(s, tc); + tc = tcase_create("thread cancellation"); + tcase_add_test(tc, test_cancel); + suite_add_tcase(s, tc); + return s; } From 49e6848bd01ecd2f5c87f31862e999af91a724ad Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 14:41:12 +0200 Subject: [PATCH 26/54] unit-tests: Add thread cancellability testing --- .../tests/suites/test_threading.c | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index b8c155e4d..bf43d88c9 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -312,6 +312,55 @@ START_TEST(test_cancel) } END_TEST +static void *cancel_onoff_run(void *data) +{ + bool *cancellable = (bool*)data; + + thread_cancelability(FALSE); + *cancellable = FALSE; + + /* we should not get cancelled here */ + usleep(50000); + + *cancellable = TRUE; + thread_cancelability(TRUE); + + /* but here */ + while (TRUE) + { + sleep(10); + } + return NULL; +} + +START_TEST(test_cancel_onoff) +{ + thread_t *threads[THREADS]; + bool cancellable[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + cancellable[i] = TRUE; + threads[i] = thread_create(cancel_onoff_run, &cancellable[i]); + } + for (i = 0; i < THREADS; i++) + { + /* wait until thread has cleared its cancellability */ + while (cancellable[i]) + { + sched_yield(); + } + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert(cancellable[i]); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -335,6 +384,7 @@ Suite *threading_suite_create() tc = tcase_create("thread cancellation"); tcase_add_test(tc, test_cancel); + tcase_add_test(tc, test_cancel_onoff); suite_add_tcase(s, tc); return s; From e5b34086f157ad6c4bf6086b35e85d293fe0a4a6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 15:08:14 +0200 Subject: [PATCH 27/54] unit-tests: Add a test for thread_cancellation_point() --- .../tests/suites/test_threading.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index bf43d88c9..fffad0c18 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -361,6 +361,38 @@ START_TEST(test_cancel_onoff) } END_TEST +static void *cancel_point_run(void *data) +{ + thread_cancelability(FALSE); + while (TRUE) + { + /* implicitly enables cancellability */ + thread_cancellation_point(); + } + return NULL; +} + +START_TEST(test_cancel_point) +{ + thread_t *threads[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(cancel_point_run, NULL); + } + sched_yield(); + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -385,6 +417,7 @@ Suite *threading_suite_create() tc = tcase_create("thread cancellation"); tcase_add_test(tc, test_cancel); tcase_add_test(tc, test_cancel_onoff); + tcase_add_test(tc, test_cancel_point); suite_add_tcase(s, tc); return s; From 4aec0c554349294bf3e385cc351534888eb2fde3 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 16:09:10 +0200 Subject: [PATCH 28/54] unit-tests: Add cleanup test cases for different thread exit situations --- .../tests/suites/test_threading.c | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index fffad0c18..2db967244 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -393,6 +393,126 @@ START_TEST(test_cancel_point) } END_TEST +static void cleanup1(void *data) +{ + uintptr_t *value = (uintptr_t*)data; + + ck_assert_int_eq(*value, 1); + (*value)++; +} + +static void cleanup2(void *data) +{ + uintptr_t *value = (uintptr_t*)data; + + ck_assert_int_eq(*value, 2); + (*value)++; +} + +static void cleanup3(void *data) +{ + uintptr_t *value = (uintptr_t*)data; + + ck_assert_int_eq(*value, 3); + (*value)++; +} + +static void *cleanup_run(void *data) +{ + thread_cleanup_push(cleanup3, data); + thread_cleanup_push(cleanup2, data); + thread_cleanup_push(cleanup1, data); + return NULL; +} + +START_TEST(test_cleanup) +{ + thread_t *threads[THREADS]; + uintptr_t values[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + values[i] = 1; + threads[i] = thread_create(cleanup_run, &values[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert_int_eq(values[i], 4); + } +} +END_TEST + +static void *cleanup_exit_run(void *data) +{ + thread_cleanup_push(cleanup3, data); + thread_cleanup_push(cleanup2, data); + thread_cleanup_push(cleanup1, data); + thread_exit(NULL); + ck_assert(FALSE); + return NULL; +} + +START_TEST(test_cleanup_exit) +{ + thread_t *threads[THREADS]; + uintptr_t values[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + values[i] = 1; + threads[i] = thread_create(cleanup_exit_run, &values[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert_int_eq(values[i], 4); + } +} +END_TEST + +static void *cleanup_cancel_run(void *data) +{ + thread_cancelability(FALSE); + + thread_cleanup_push(cleanup3, data); + thread_cleanup_push(cleanup2, data); + thread_cleanup_push(cleanup1, data); + + thread_cancelability(TRUE); + + while (TRUE) + { + sleep(1); + } + return NULL; +} + +START_TEST(test_cleanup_cancel) +{ + thread_t *threads[THREADS]; + uintptr_t values[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + values[i] = 1; + threads[i] = thread_create(cleanup_cancel_run, &values[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert_int_eq(values[i], 4); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -420,5 +540,11 @@ Suite *threading_suite_create() tcase_add_test(tc, test_cancel_point); suite_add_tcase(s, tc); + tc = tcase_create("thread cleanup"); + tcase_add_test(tc, test_cleanup); + tcase_add_test(tc, test_cleanup_exit); + tcase_add_test(tc, test_cleanup_cancel); + suite_add_tcase(s, tc); + return s; } From fd26b7ff1b412378b41f7f1a90d1816b5a45d439 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 16:12:52 +0200 Subject: [PATCH 29/54] unit-tests: Add a thread cleanup pop test --- .../tests/suites/test_threading.c | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 2db967244..3282fb975 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -513,6 +513,38 @@ START_TEST(test_cleanup_cancel) } END_TEST +static void *cleanup_pop_run(void *data) +{ + thread_cleanup_push(cleanup3, data); + thread_cleanup_push(cleanup2, data); + thread_cleanup_push(cleanup1, data); + + thread_cleanup_push(cleanup2, data); + thread_cleanup_pop(FALSE); + + thread_cleanup_pop(TRUE); + return NULL; +} + +START_TEST(test_cleanup_pop) +{ + thread_t *threads[THREADS]; + uintptr_t values[THREADS]; + int i; + + for (i = 0; i < THREADS; i++) + { + values[i] = 1; + threads[i] = thread_create(cleanup_pop_run, &values[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert_int_eq(values[i], 4); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -544,6 +576,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_cleanup); tcase_add_test(tc, test_cleanup_exit); tcase_add_test(tc, test_cleanup_cancel); + tcase_add_test(tc, test_cleanup_pop); suite_add_tcase(s, tc); return s; From 0b00e63e4980127451607be6145944353e5f0487 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 16:37:51 +0200 Subject: [PATCH 30/54] unit-tests: Add a thread local storage fuzzer test --- .../tests/suites/test_threading.c | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 3282fb975..7247c3d59 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -22,6 +22,7 @@ #include #include #include +#include /******************************************************************************* * recursive mutex test @@ -545,6 +546,65 @@ START_TEST(test_cleanup_pop) } END_TEST +static thread_value_t *tls[10]; + +static void *tls_run(void *data) +{ + uintptr_t value = (uintptr_t)data; + int i, j; + + for (i = 0; i < countof(tls); i++) + { + ck_assert(tls[i]->get(tls[i]) == NULL); + } + for (i = 0; i < countof(tls); i++) + { + tls[i]->set(tls[i], (void*)(value * i)); + } + for (j = 0; j < 1000; j++) + { + for (i = 0; i < countof(tls); i++) + { + tls[i]->set(tls[i], (void*)(value * i)); + ck_assert(tls[i]->get(tls[i]) == (void*)(value * i)); + } + sched_yield(); + } + for (i = 0; i < countof(tls); i++) + { + ck_assert(tls[i]->get(tls[i]) == (void*)(value * i)); + } + return (void*)(value + 1); +} + +START_TEST(test_tls) +{ + thread_t *threads[THREADS]; + int i; + + for (i = 0; i < countof(tls); i++) + { + tls[i] = thread_value_create(NULL); + } + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(tls_run, (void*)(uintptr_t)i); + } + + ck_assert_int_eq((uintptr_t)tls_run((void*)(uintptr_t)(THREADS + 1)), + THREADS + 2); + + for (i = 0; i < THREADS; i++) + { + ck_assert_int_eq((uintptr_t)threads[i]->join(threads[i]), i + 1); + } + for (i = 0; i < countof(tls); i++) + { + tls[i]->destroy(tls[i]); + } +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -579,5 +639,9 @@ Suite *threading_suite_create() tcase_add_test(tc, test_cleanup_pop); suite_add_tcase(s, tc); + tc = tcase_create("thread local storage"); + tcase_add_test(tc, test_tls); + suite_add_tcase(s, tc); + return s; } From 21df9851486517721f9b07e04ba38d4c13d7cdbc Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 16:58:22 +0200 Subject: [PATCH 31/54] unit-tests: Add a thread local storage cleanup test --- .../tests/suites/test_threading.c | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 7247c3d59..7a4bb45da 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -605,6 +605,55 @@ START_TEST(test_tls) } END_TEST +static void tls_cleanup(void *data) +{ + uintptr_t *value = (uintptr_t*)data; + + (*value)--; +} + +static void *tls_cleanup_run(void *data) +{ + int i; + + for (i = 0; i < countof(tls); i++) + { + tls[i]->set(tls[i], data); + } + return NULL; +} + +START_TEST(test_tls_cleanup) +{ + thread_t *threads[THREADS]; + uintptr_t values[THREADS], main_value = countof(tls); + int i; + + for (i = 0; i < countof(tls); i++) + { + tls[i] = thread_value_create(tls_cleanup); + } + for (i = 0; i < THREADS; i++) + { + values[i] = countof(tls); + threads[i] = thread_create(tls_cleanup_run, &values[i]); + } + + tls_cleanup_run(&main_value); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + ck_assert_int_eq(values[i], 0); + } + for (i = 0; i < countof(tls); i++) + { + tls[i]->destroy(tls[i]); + } + ck_assert_int_eq(main_value, 0); +} +END_TEST + Suite *threading_suite_create() { Suite *s; @@ -641,6 +690,7 @@ Suite *threading_suite_create() tc = tcase_create("thread local storage"); tcase_add_test(tc, test_tls); + tcase_add_test(tc, test_tls_cleanup); suite_add_tcase(s, tc); return s; From 13183a74d445592a805e704b2b0e38bd3e6ea341 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 17:24:43 +0200 Subject: [PATCH 32/54] unit-tests: Add a simple condvar test --- .../tests/suites/test_threading.c | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 7a4bb45da..97e6e3303 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -103,10 +103,24 @@ static bool barrier_wait(barrier_t *this) */ static barrier_t *barrier; +/** + * A mutex for tests requiring one + */ +static mutex_t *mutex; + +/** + * A condvar for tests requiring one + */ +static condvar_t *condvar; + +/** + * A counter for signaling + */ +static int sigcount; + static void *mutex_run(void *data) { - mutex_t *mutex = (mutex_t*)data; - static int locked = 0; + int locked = 0; int i; /* wait for all threads before getting in action */ @@ -134,7 +148,6 @@ static void *mutex_run(void *data) START_TEST(test_mutex) { thread_t *threads[THREADS]; - mutex_t *mutex; int i; barrier = barrier_create(THREADS); @@ -156,7 +169,7 @@ START_TEST(test_mutex) for (i = 0; i < THREADS; i++) { - threads[i] = thread_create(mutex_run, mutex); + threads[i] = thread_create(mutex_run, NULL); } for (i = 0; i < THREADS; i++) { @@ -168,6 +181,46 @@ START_TEST(test_mutex) } END_TEST +static void *condvar_run(void *data) +{ + mutex->lock(mutex); + sigcount++; + condvar->signal(condvar); + mutex->unlock(mutex); + return NULL; +} + +START_TEST(test_condvar) +{ + thread_t *threads[THREADS]; + int i; + + mutex = mutex_create(MUTEX_TYPE_DEFAULT); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(condvar_run, NULL); + } + + mutex->lock(mutex); + while (sigcount < THREADS) + { + condvar->wait(condvar, mutex); + } + mutex->unlock(mutex); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -665,6 +718,10 @@ Suite *threading_suite_create() tcase_add_test(tc, test_mutex); suite_add_tcase(s, tc); + tc = tcase_create("condvar"); + tcase_add_test(tc, test_condvar); + suite_add_tcase(s, tc); + tc = tcase_create("thread joining"); tcase_add_test(tc, test_join); tcase_add_test(tc, test_join_exit); From 9a0a891e6b5912585aef3ba03393f2a2d6252797 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 17:27:49 +0200 Subject: [PATCH 33/54] unit-tests: Add condvar broadcast test --- .../tests/suites/test_threading.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 97e6e3303..108c4fea5 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -221,6 +221,48 @@ START_TEST(test_condvar) } END_TEST +static void *condvar_run_broad(void *data) +{ + mutex->lock(mutex); + while (sigcount < 0) + { + condvar->wait(condvar, mutex); + } + mutex->unlock(mutex); + return NULL; +} + +START_TEST(test_condvar_broad) +{ + thread_t *threads[THREADS]; + int i; + + mutex = mutex_create(MUTEX_TYPE_DEFAULT); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(condvar_run_broad, NULL); + } + + sched_yield(); + + mutex->lock(mutex); + sigcount = 1; + condvar->broadcast(condvar); + mutex->unlock(mutex); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -720,6 +762,7 @@ Suite *threading_suite_create() tc = tcase_create("condvar"); tcase_add_test(tc, test_condvar); + tcase_add_test(tc, test_condvar_broad); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From 31f9f777b33eae8a05705a9c625933e2e9aec3db Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 17:45:58 +0200 Subject: [PATCH 34/54] unit-tests: Add a condvar timed wait test --- .../tests/suites/test_threading.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 108c4fea5..27a29d177 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -263,6 +263,45 @@ START_TEST(test_condvar_broad) } END_TEST +START_TEST(test_condvar_timed) +{ + thread_t *thread; + timeval_t start, end, diff = { .tv_usec = 50000 }; + + mutex = mutex_create(MUTEX_TYPE_DEFAULT); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + mutex->lock(mutex); + while (TRUE) + { + time_monotonic(&start); + if (condvar->timed_wait(condvar, mutex, diff.tv_usec / 1000)) + { + break; + } + } + time_monotonic(&end); + mutex->unlock(mutex); + timersub(&end, &start, &end); + ck_assert_msg(timercmp(&end, &diff, >), "end: %u.%u, diff: %u.%u", + end.tv_sec, end.tv_usec, diff.tv_sec, diff.tv_usec); + + thread = thread_create(condvar_run, NULL); + + mutex->lock(mutex); + while (sigcount == 0) + { + ck_assert(!condvar->timed_wait(condvar, mutex, 1000)); + } + mutex->unlock(mutex); + + thread->join(thread); + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -763,6 +802,7 @@ Suite *threading_suite_create() tc = tcase_create("condvar"); tcase_add_test(tc, test_condvar); tcase_add_test(tc, test_condvar_broad); + tcase_add_test(tc, test_condvar_timed); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From 8699a32b74d601ac61603c26645744b07fc989b6 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 21 Oct 2013 17:53:37 +0200 Subject: [PATCH 35/54] unit-tests: Add a condvar absolute timed wait test --- .../tests/suites/test_threading.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 27a29d177..810b2053d 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -302,6 +302,48 @@ START_TEST(test_condvar_timed) } END_TEST +START_TEST(test_condvar_timed_abs) +{ + thread_t *thread; + timeval_t start, end, abso, diff = { .tv_usec = 50000 }; + + mutex = mutex_create(MUTEX_TYPE_DEFAULT); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + mutex->lock(mutex); + while (TRUE) + { + time_monotonic(&start); + timeradd(&start, &diff, &abso); + if (condvar->timed_wait_abs(condvar, mutex, abso)) + { + break; + } + } + time_monotonic(&end); + mutex->unlock(mutex); + ck_assert_msg(timercmp(&end, &diff, >), "end: %u.%u, diff: %u.%u", + end.tv_sec, end.tv_usec, abso.tv_sec, abso.tv_usec); + + thread = thread_create(condvar_run, NULL); + + time_monotonic(&start); + diff.tv_sec = 1; + timeradd(&start, &diff, &abso); + mutex->lock(mutex); + while (sigcount == 0) + { + ck_assert(!condvar->timed_wait_abs(condvar, mutex, abso)); + } + mutex->unlock(mutex); + + thread->join(thread); + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -803,6 +845,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_condvar); tcase_add_test(tc, test_condvar_broad); tcase_add_test(tc, test_condvar_timed); + tcase_add_test(tc, test_condvar_timed_abs); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From b7db393d01a97ae14c4483d5fdd6e60ef0be1988 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 16:04:25 +0200 Subject: [PATCH 36/54] unit-tests: Add a condvar test working on a recursive mutex --- .../tests/suites/test_threading.c | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 810b2053d..1abf215a2 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -221,6 +221,55 @@ START_TEST(test_condvar) } END_TEST +static void *condvar_recursive_run(void *data) +{ + mutex->lock(mutex); + mutex->lock(mutex); + mutex->lock(mutex); + sigcount++; + condvar->signal(condvar); + mutex->unlock(mutex); + mutex->unlock(mutex); + mutex->unlock(mutex); + return NULL; +} + +START_TEST(test_condvar_recursive) +{ + thread_t *threads[THREADS]; + int i; + + mutex = mutex_create(MUTEX_TYPE_RECURSIVE); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + mutex->lock(mutex); + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(condvar_recursive_run, NULL); + } + + mutex->lock(mutex); + mutex->lock(mutex); + while (sigcount < THREADS) + { + condvar->wait(condvar, mutex); + } + mutex->unlock(mutex); + mutex->unlock(mutex); + mutex->unlock(mutex); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *condvar_run_broad(void *data) { mutex->lock(mutex); @@ -843,6 +892,7 @@ Suite *threading_suite_create() tc = tcase_create("condvar"); tcase_add_test(tc, test_condvar); + tcase_add_test(tc, test_condvar_recursive); tcase_add_test(tc, test_condvar_broad); tcase_add_test(tc, test_condvar_timed); tcase_add_test(tc, test_condvar_timed_abs); From 8b25b5c36f40ead3a4da0a77882a49d148a511dc Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 16:05:11 +0200 Subject: [PATCH 37/54] unit-tests: Add a condvar test where wait gets cancelled --- .../tests/suites/test_threading.c | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 1abf215a2..170449680 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -393,6 +393,62 @@ START_TEST(test_condvar_timed_abs) } END_TEST +static void *condvar_cancel_run(void *data) +{ + thread_cancelability(FALSE); + + mutex->lock(mutex); + + sigcount++; + condvar->broadcast(condvar); + + thread_cleanup_push((void*)mutex->unlock, mutex); + thread_cancelability(TRUE); + while (TRUE) + { + condvar->wait(condvar, mutex); + } + thread_cleanup_pop(TRUE); + + return NULL; +} + +START_TEST(test_condvar_cancel) +{ + thread_t *threads[THREADS]; + int i; + + mutex = mutex_create(MUTEX_TYPE_DEFAULT); + condvar = condvar_create(CONDVAR_TYPE_DEFAULT); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(condvar_cancel_run, NULL); + } + + /* wait for all threads */ + mutex->lock(mutex); + while (sigcount < THREADS) + { + condvar->wait(condvar, mutex); + } + mutex->unlock(mutex); + + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + mutex->destroy(mutex); + condvar->destroy(condvar); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -896,6 +952,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_condvar_broad); tcase_add_test(tc, test_condvar_timed); tcase_add_test(tc, test_condvar_timed_abs); + tcase_add_test(tc, test_condvar_cancel); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From dac31fe1a0126df786650ed33859a38e0e297562 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:24:59 +0200 Subject: [PATCH 38/54] unit-tests: Add a rwlock test case --- .../tests/suites/test_threading.c | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 170449680..37b2bd05d 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -22,6 +22,7 @@ #include #include #include +#include #include /******************************************************************************* @@ -449,6 +450,63 @@ START_TEST(test_condvar_cancel) } END_TEST +/** + * RWlock for different tests + */ +static rwlock_t *rwlock; + +static void *rwlock_run(refcount_t *refs) +{ + rwlock->read_lock(rwlock); + ref_get(refs); + sched_yield(); + ignore_result(ref_put(refs)); + rwlock->unlock(rwlock); + + if (rwlock->try_write_lock(rwlock)) + { + ck_assert_int_eq(*refs, 0); + sched_yield(); + rwlock->unlock(rwlock); + } + + rwlock->write_lock(rwlock); + ck_assert_int_eq(*refs, 0); + sched_yield(); + rwlock->unlock(rwlock); + + rwlock->read_lock(rwlock); + rwlock->read_lock(rwlock); + ref_get(refs); + sched_yield(); + ignore_result(ref_put(refs)); + rwlock->unlock(rwlock); + rwlock->unlock(rwlock); + + return NULL; +} + +START_TEST(test_rwlock) +{ + thread_t *threads[THREADS]; + refcount_t refs = 0; + int i; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create((void*)rwlock_run, &refs); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + rwlock->destroy(rwlock); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -955,6 +1013,10 @@ Suite *threading_suite_create() tcase_add_test(tc, test_condvar_cancel); suite_add_tcase(s, tc); + tc = tcase_create("rwlock"); + tcase_add_test(tc, test_rwlock); + suite_add_tcase(s, tc); + tc = tcase_create("thread joining"); tcase_add_test(tc, test_join); tcase_add_test(tc, test_join_exit); From f644b9e8536f0331967eba63c05f293c86ba4de8 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:32:33 +0200 Subject: [PATCH 39/54] unit-tests: Add a rwlock condvar test --- .../tests/suites/test_threading.c | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 37b2bd05d..830410e77 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -23,6 +23,7 @@ #include #include #include +#include #include /******************************************************************************* @@ -507,6 +508,51 @@ START_TEST(test_rwlock) } END_TEST +/** + * Rwlock condvar + */ +static rwlock_condvar_t *rwcond; + +static void *rwlock_condvar_run(void *data) +{ + rwlock->write_lock(rwlock); + sigcount++; + rwcond->signal(rwcond); + rwlock->unlock(rwlock); + return NULL; +} + +START_TEST(test_rwlock_condvar) +{ + thread_t *threads[THREADS]; + int i; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + rwcond = rwlock_condvar_create(); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(rwlock_condvar_run, NULL); + } + + rwlock->write_lock(rwlock); + while (sigcount < THREADS) + { + rwcond->wait(rwcond, rwlock); + } + rwlock->unlock(rwlock); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + rwlock->destroy(rwlock); + rwcond->destroy(rwcond); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1017,6 +1063,10 @@ Suite *threading_suite_create() tcase_add_test(tc, test_rwlock); suite_add_tcase(s, tc); + tc = tcase_create("rwlock condvar"); + tcase_add_test(tc, test_rwlock_condvar); + suite_add_tcase(s, tc); + tc = tcase_create("thread joining"); tcase_add_test(tc, test_join); tcase_add_test(tc, test_join_exit); From 1032f52d68b268d06972e12fa6b912d111634694 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:36:29 +0200 Subject: [PATCH 40/54] unit-tests: Add a rwlock condvar broadcast test --- .../tests/suites/test_threading.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 830410e77..f1d19e355 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -553,6 +553,48 @@ START_TEST(test_rwlock_condvar) } END_TEST +static void *rwlock_condvar_run_broad(void *data) +{ + rwlock->write_lock(rwlock); + while (sigcount < 0) + { + rwcond->wait(rwcond, rwlock); + } + rwlock->unlock(rwlock); + return NULL; +} + +START_TEST(test_rwlock_condvar_broad) +{ + thread_t *threads[THREADS]; + int i; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + rwcond = rwlock_condvar_create(); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(rwlock_condvar_run_broad, NULL); + } + + sched_yield(); + + rwlock->write_lock(rwlock); + sigcount = 1; + rwcond->broadcast(rwcond); + rwlock->unlock(rwlock); + + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + rwlock->destroy(rwlock); + rwcond->destroy(rwcond); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1065,6 +1107,7 @@ Suite *threading_suite_create() tc = tcase_create("rwlock condvar"); tcase_add_test(tc, test_rwlock_condvar); + tcase_add_test(tc, test_rwlock_condvar_broad); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From af19213c54aaff4541738127d90e08df48691f42 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:39:29 +0200 Subject: [PATCH 41/54] unit-tests: Add a rwlock condvar wait test --- .../tests/suites/test_threading.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index f1d19e355..a8de5c3d6 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -595,6 +595,45 @@ START_TEST(test_rwlock_condvar_broad) } END_TEST +START_TEST(test_rwlock_condvar_timed) +{ + thread_t *thread; + timeval_t start, end, diff = { .tv_usec = 50000 }; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + rwcond = rwlock_condvar_create(); + sigcount = 0; + + rwlock->write_lock(rwlock); + while (TRUE) + { + time_monotonic(&start); + if (rwcond->timed_wait(rwcond, rwlock, diff.tv_usec / 1000)) + { + break; + } + } + rwlock->unlock(rwlock); + time_monotonic(&end); + timersub(&end, &start, &end); + ck_assert_msg(timercmp(&end, &diff, >), "end: %u.%u, diff: %u.%u", + end.tv_sec, end.tv_usec, diff.tv_sec, diff.tv_usec); + + thread = thread_create(rwlock_condvar_run, NULL); + + rwlock->write_lock(rwlock); + while (sigcount == 0) + { + ck_assert(!rwcond->timed_wait(rwcond, rwlock, 1000)); + } + rwlock->unlock(rwlock); + + thread->join(thread); + rwlock->destroy(rwlock); + rwcond->destroy(rwcond); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1108,6 +1147,7 @@ Suite *threading_suite_create() tc = tcase_create("rwlock condvar"); tcase_add_test(tc, test_rwlock_condvar); tcase_add_test(tc, test_rwlock_condvar_broad); + tcase_add_test(tc, test_rwlock_condvar_timed); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From b92c173b28d802f59535af9dcc6adaf6bad3cd07 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:41:37 +0200 Subject: [PATCH 42/54] unit-tests: Add a rwlock condvar absolute timed wait test --- .../tests/suites/test_threading.c | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index a8de5c3d6..02be9ce7c 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -634,6 +634,48 @@ START_TEST(test_rwlock_condvar_timed) } END_TEST +START_TEST(test_rwlock_condvar_timed_abs) +{ + thread_t *thread; + timeval_t start, end, abso, diff = { .tv_usec = 50000 }; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + rwcond = rwlock_condvar_create(); + sigcount = 0; + + rwlock->write_lock(rwlock); + while (TRUE) + { + time_monotonic(&start); + timeradd(&start, &diff, &abso); + if (rwcond->timed_wait_abs(rwcond, rwlock, abso)) + { + break; + } + } + rwlock->unlock(rwlock); + time_monotonic(&end); + ck_assert_msg(timercmp(&end, &abso, >), "end: %u.%u, abso: %u.%u", + end.tv_sec, end.tv_usec, abso.tv_sec, abso.tv_usec); + + thread = thread_create(rwlock_condvar_run, NULL); + + time_monotonic(&start); + diff.tv_sec = 1; + timeradd(&start, &diff, &abso); + rwlock->write_lock(rwlock); + while (sigcount == 0) + { + ck_assert(!rwcond->timed_wait_abs(rwcond, rwlock, abso)); + } + rwlock->unlock(rwlock); + + thread->join(thread); + rwlock->destroy(rwlock); + rwcond->destroy(rwcond); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1148,6 +1190,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_rwlock_condvar); tcase_add_test(tc, test_rwlock_condvar_broad); tcase_add_test(tc, test_rwlock_condvar_timed); + tcase_add_test(tc, test_rwlock_condvar_timed_abs); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From 478dc0257cbe2aad7bc01d243b69bb41ccb813ae Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 22 Oct 2013 17:44:57 +0200 Subject: [PATCH 43/54] unit-tests: Add a rwlock condvar thread cancel test --- .../tests/suites/test_threading.c | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 02be9ce7c..49b249ae9 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -676,6 +676,62 @@ START_TEST(test_rwlock_condvar_timed_abs) } END_TEST +static void *rwlock_condvar_cancel_run(void *data) +{ + thread_cancelability(FALSE); + + rwlock->write_lock(rwlock); + + sigcount++; + rwcond->broadcast(rwcond); + + thread_cleanup_push((void*)rwlock->unlock, rwlock); + thread_cancelability(TRUE); + while (TRUE) + { + rwcond->wait(rwcond, rwlock); + } + thread_cleanup_pop(TRUE); + + return NULL; +} + +START_TEST(test_rwlock_condvar_cancel) +{ + thread_t *threads[THREADS]; + int i; + + rwlock = rwlock_create(RWLOCK_TYPE_DEFAULT); + rwcond = rwlock_condvar_create(); + sigcount = 0; + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(rwlock_condvar_cancel_run, NULL); + } + + /* wait for all threads */ + rwlock->write_lock(rwlock); + while (sigcount < THREADS) + { + rwcond->wait(rwcond, rwlock); + } + rwlock->unlock(rwlock); + + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + rwlock->destroy(rwlock); + rwcond->destroy(rwcond); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1191,6 +1247,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_rwlock_condvar_broad); tcase_add_test(tc, test_rwlock_condvar_timed); tcase_add_test(tc, test_rwlock_condvar_timed_abs); + tcase_add_test(tc, test_rwlock_condvar_cancel); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From b1bfe595601cf3806c4831d0c7607be833d6608f Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 14:14:21 +0200 Subject: [PATCH 44/54] unit-tests: Add a spinlock test case --- .../tests/suites/test_threading.c | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 49b249ae9..295b647ab 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /******************************************************************************* @@ -183,6 +184,50 @@ START_TEST(test_mutex) } END_TEST +/** + * Spinlock for testing + */ +static spinlock_t *spinlock; + +static void *spinlock_run(void *data) +{ + int i, *locked = (int*)data; + + barrier_wait(barrier); + + for (i = 0; i < 1000; i++) + { + spinlock->lock(spinlock); + (*locked)++; + ck_assert_int_eq(*locked, 1); + (*locked)--; + spinlock->unlock(spinlock); + } + return NULL; +} + +START_TEST(test_spinlock) +{ + thread_t *threads[THREADS]; + int i, locked = 0; + + barrier = barrier_create(THREADS); + spinlock = spinlock_create(); + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(spinlock_run, &locked); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + spinlock->destroy(spinlock); + barrier_destroy(barrier); +} +END_TEST + static void *condvar_run(void *data) { mutex->lock(mutex); @@ -1229,6 +1274,10 @@ Suite *threading_suite_create() tcase_add_test(tc, test_mutex); suite_add_tcase(s, tc); + tc = tcase_create("spinlock"); + tcase_add_test(tc, test_spinlock); + suite_add_tcase(s, tc); + tc = tcase_create("condvar"); tcase_add_test(tc, test_condvar); tcase_add_test(tc, test_condvar_recursive); From ffab2e0c953281aa0708a5dde4b7e64ddcd47c20 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 14:54:00 +0200 Subject: [PATCH 45/54] unit-tests: Add a simple semaphore test --- .../tests/suites/test_threading.c | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 295b647ab..dc7796d76 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -25,6 +25,7 @@ #include #include #include +#include #include /******************************************************************************* @@ -777,6 +778,41 @@ START_TEST(test_rwlock_condvar_cancel) } END_TEST +/** + * Semaphore for different tests + */ +static semaphore_t *semaphore; + +static void *semaphore_run(void *data) +{ + semaphore->post(semaphore); + return NULL; +} + +START_TEST(test_semaphore) +{ + thread_t *threads[THREADS]; + int i, initial = 5; + + semaphore = semaphore_create(initial); + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(semaphore_run, NULL); + } + for (i = 0; i < THREADS + initial; i++) + { + semaphore->wait(semaphore); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + semaphore->destroy(semaphore); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1299,6 +1335,10 @@ Suite *threading_suite_create() tcase_add_test(tc, test_rwlock_condvar_cancel); suite_add_tcase(s, tc); + tc = tcase_create("semaphore"); + tcase_add_test(tc, test_semaphore); + suite_add_tcase(s, tc); + tc = tcase_create("thread joining"); tcase_add_test(tc, test_join); tcase_add_test(tc, test_join_exit); From a14935ea4b8c490784905fbe0a5c3a0171ebac97 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 15:18:59 +0200 Subject: [PATCH 46/54] unit-tests: Add a semaphore timed wait test case --- .../tests/suites/test_threading.c | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index dc7796d76..11b98cea4 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -813,6 +813,29 @@ START_TEST(test_semaphore) } END_TEST +START_TEST(test_semaphore_timed) +{ + thread_t *thread; + timeval_t start, end, diff = { .tv_usec = 50000 }; + + semaphore = semaphore_create(0); + + time_monotonic(&start); + ck_assert(semaphore->timed_wait(semaphore, diff.tv_usec / 1000)); + time_monotonic(&end); + timersub(&end, &start, &end); + ck_assert_msg(timercmp(&end, &diff, >), "end: %u.%u, diff: %u.%u", + end.tv_sec, end.tv_usec, diff.tv_sec, diff.tv_usec); + + thread = thread_create(semaphore_run, NULL); + + ck_assert(!semaphore->timed_wait(semaphore, 1000)); + + thread->join(thread); + semaphore->destroy(semaphore); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1337,6 +1360,7 @@ Suite *threading_suite_create() tc = tcase_create("semaphore"); tcase_add_test(tc, test_semaphore); + tcase_add_test(tc, test_semaphore_timed); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From fae1b85223a1fe706293c394545824cc767b7ddf Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 15:23:42 +0200 Subject: [PATCH 47/54] unit-tests: Add a semaphore absolute timed wait test --- .../tests/suites/test_threading.c | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 11b98cea4..5938bd959 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -836,6 +836,32 @@ START_TEST(test_semaphore_timed) } END_TEST +START_TEST(test_semaphore_timed_abs) +{ + thread_t *thread; + timeval_t start, end, abso, diff = { .tv_usec = 50000 }; + + semaphore = semaphore_create(0); + + time_monotonic(&start); + timeradd(&start, &diff, &abso); + ck_assert(semaphore->timed_wait_abs(semaphore, abso)); + time_monotonic(&end); + ck_assert_msg(timercmp(&end, &abso, >), "end: %u.%u, abso: %u.%u", + end.tv_sec, end.tv_usec, abso.tv_sec, abso.tv_usec); + + thread = thread_create(semaphore_run, NULL); + + time_monotonic(&start); + diff.tv_sec = 1; + timeradd(&start, &diff, &abso); + ck_assert(!semaphore->timed_wait_abs(semaphore, abso)); + + thread->join(thread); + semaphore->destroy(semaphore); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1361,6 +1387,7 @@ Suite *threading_suite_create() tc = tcase_create("semaphore"); tcase_add_test(tc, test_semaphore); tcase_add_test(tc, test_semaphore_timed); + tcase_add_test(tc, test_semaphore_timed_abs); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From a4cbda35cec0a3fb836dbff6656152fa3f656f35 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 23 Oct 2013 15:44:22 +0200 Subject: [PATCH 48/54] unit-tests: Add a semaphore wait cancel test --- .../tests/suites/test_threading.c | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_threading.c b/src/libstrongswan/tests/suites/test_threading.c index 5938bd959..844959e46 100644 --- a/src/libstrongswan/tests/suites/test_threading.c +++ b/src/libstrongswan/tests/suites/test_threading.c @@ -862,6 +862,49 @@ START_TEST(test_semaphore_timed_abs) } END_TEST +static void *semaphore_cancel_run(void *data) +{ + refcount_t *ready = (refcount_t*)data; + + thread_cancelability(FALSE); + ref_get(ready); + + thread_cancelability(TRUE); + semaphore->wait(semaphore); + + ck_assert(FALSE); + return NULL; +} + +START_TEST(test_semaphore_cancel) +{ + thread_t *threads[THREADS]; + refcount_t ready = 0; + int i; + + semaphore = semaphore_create(0); + + for (i = 0; i < THREADS; i++) + { + threads[i] = thread_create(semaphore_cancel_run, &ready); + } + while (ready < THREADS) + { + sched_yield(); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->cancel(threads[i]); + } + for (i = 0; i < THREADS; i++) + { + threads[i]->join(threads[i]); + } + + semaphore->destroy(semaphore); +} +END_TEST + static void *join_run(void *data) { /* force some context switches */ @@ -1388,6 +1431,7 @@ Suite *threading_suite_create() tcase_add_test(tc, test_semaphore); tcase_add_test(tc, test_semaphore_timed); tcase_add_test(tc, test_semaphore_timed_abs); + tcase_add_test(tc, test_semaphore_cancel); suite_add_tcase(s, tc); tc = tcase_create("thread joining"); From 61934203e2f4edc15691109a96b037ad90f3cef4 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 25 Oct 2013 14:38:05 +0200 Subject: [PATCH 49/54] unit-tests: Add some basic tests if PRI* printf specifiers work as expected --- src/libstrongswan/tests/suites/test_printf.c | 25 ++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_printf.c b/src/libstrongswan/tests/suites/test_printf.c index 8558cca40..ad8c225ed 100644 --- a/src/libstrongswan/tests/suites/test_printf.c +++ b/src/libstrongswan/tests/suites/test_printf.c @@ -17,6 +17,7 @@ #include #include +#include static void verify(char *expected, char *format, ...) { @@ -155,6 +156,26 @@ START_TEST(test_printf_float) } END_TEST +START_TEST(test_printf_pri) +{ + verify("255", "%" PRIu8, (u_int8_t)0xFF); + verify("65535", "%" PRIu16, (u_int16_t)0xFFFF); + verify("4294967295", "%" PRIu32, (u_int32_t)0x1FFFFFFFFll); + verify("18446744073709551615", "%" PRIu64, (u_int64_t)0xFFFFFFFFFFFFFFFFll); + + verify("-1", "%" PRId8, (int8_t)-1); + verify("-1", "%" PRId16, (int16_t)-1); + verify("-1", "%" PRId32, (int32_t)-1); + verify("-1", "%" PRId64, (int64_t)-1); + + verify("1", "%" PRIuMAX, (uintmax_t)1); + verify("1", "%" PRIuPTR, (uintptr_t)1); + + verify("-1", "%" PRIdMAX, (intmax_t)-1); + verify("-1", "%" PRIdPTR, (intptr_t)-1); +} +END_TEST + Suite *printf_suite_create() { Suite *s; @@ -186,5 +207,9 @@ Suite *printf_suite_create() tcase_add_test(tc, test_printf_float); suite_add_tcase(s, tc); + tc = tcase_create("PRI*"); + tcase_add_test(tc, test_printf_pri); + suite_add_tcase(s, tc); + return s; } From d9d0eef92bb08d3e168c7ebc5faa2429a30d56df Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 25 Oct 2013 14:41:28 +0200 Subject: [PATCH 50/54] unit-tests: Check printing of strings having zero length --- src/libstrongswan/tests/suites/test_printf.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_printf.c b/src/libstrongswan/tests/suites/test_printf.c index ad8c225ed..7d38c60df 100644 --- a/src/libstrongswan/tests/suites/test_printf.c +++ b/src/libstrongswan/tests/suites/test_printf.c @@ -48,6 +48,8 @@ START_TEST(test_printf_strings) verify("a bc def", "%s %s %s", "a", "bc", "def"); verify("asd", "%.3s", "asdfg"); verify("asdf", "%.*s", (int)4, "asdfg"); + verify("", "%.0s", NULL); + verify("", "%.*s", (int)0, NULL); verify(" asdf", "%6s", "asdf"); verify(" asdf", "%+6s", "asdf"); verify("asdf ", "%-6s", "asdf"); From 5a3230a2503a941431e891d2d43bc760f9f56cca Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Mon, 4 Nov 2013 16:41:22 +0100 Subject: [PATCH 51/54] unit-tests: Use some include magic to define test suite constructors Avoid editing of several files when creating test suites by using a single header file to define test suite constructor functions. --- src/libstrongswan/tests/test_runner.c | 79 ++++++++++++--------------- src/libstrongswan/tests/test_runner.h | 49 +++++++---------- 2 files changed, 55 insertions(+), 73 deletions(-) diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 14db14a89..3b2bdacc7 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -15,7 +15,7 @@ * for more details. */ -#include "test_runner.h" +#include "test_suite.h" #include #include @@ -51,17 +51,12 @@ static bool load_plugins() return lib->plugins->load(lib->plugins, PLUGINS); } -/** - * Check if a specific feature is available, return falg if so - */ -static int check_feature(plugin_feature_t feature, int flag) -{ - if (lib->plugins->has_feature(lib->plugins, feature)) - { - return flag; - } - return 0; -} +/* declare test suite constructors */ +#define TEST_SUITE(x) test_suite_t* x(); +#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x) +#include "test_runner.h" +#undef TEST_SUITE +#undef TEST_SUITE_DEPEND /** * Load all available test suites @@ -69,10 +64,18 @@ static int check_feature(plugin_feature_t feature, int flag) static array_t *load_suites() { array_t *suites; - enum { - OTEST_RSA = (1<<0), - OTEST_ECDSA = (1<<1), - } otest = 0; + struct { + test_suite_t *(*suite)(); + plugin_feature_t feature; + } constructors[] = { +#define TEST_SUITE(x) \ + { .suite = x, }, +#define TEST_SUITE_DEPEND(x, type, args) \ + { .suite = x, .feature = PLUGIN_DEPENDS(type, args) }, +#include "test_runner.h" + }; + bool old = FALSE; + int i; library_init(NULL); @@ -85,43 +88,29 @@ static array_t *load_suites() } lib->plugins->status(lib->plugins, LEVEL_CTRL); - /* we have to build the test suite array without leak detective, so - * separate plugin checks and suite creation */ - otest |= check_feature(PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_RSA), OTEST_RSA); - otest |= check_feature(PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_ECDSA), OTEST_ECDSA); - - library_deinit(); + if (lib->leak_detective) + { + old = lib->leak_detective->set_state(lib->leak_detective, FALSE); + } suites = array_create(0, 0); - array_insert(suites, -1, bio_reader_suite_create()); - array_insert(suites, -1, bio_writer_suite_create()); - array_insert(suites, -1, chunk_suite_create()); - array_insert(suites, -1, enum_suite_create()); - array_insert(suites, -1, enumerator_suite_create()); - array_insert(suites, -1, linked_list_suite_create()); - array_insert(suites, -1, linked_list_enumerator_suite_create()); - array_insert(suites, -1, hashtable_suite_create()); - array_insert(suites, -1, array_suite_create()); - array_insert(suites, -1, identification_suite_create()); - array_insert(suites, -1, threading_suite_create()); - array_insert(suites, -1, watcher_suite_create()); - array_insert(suites, -1, stream_suite_create()); - array_insert(suites, -1, utils_suite_create()); - array_insert(suites, -1, host_suite_create()); - array_insert(suites, -1, vectors_suite_create()); - array_insert(suites, -1, pen_suite_create()); - array_insert(suites, -1, asn1_suite_create()); - array_insert(suites, -1, printf_suite_create()); - if (otest & OTEST_RSA) + for (i = 0; i < countof(constructors); i++) { - array_insert(suites, -1, rsa_suite_create()); + if (constructors[i].feature.type == 0 || + lib->plugins->has_feature(lib->plugins, constructors[i].feature)) + { + array_insert(suites, -1, constructors[i].suite()); + } } - if (otest & OTEST_ECDSA) + + if (lib->leak_detective) { - array_insert(suites, -1, ecdsa_suite_create()); + lib->leak_detective->set_state(lib->leak_detective, old); } + library_deinit(); + return suites; } diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index 435883648..6316692aa 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -13,31 +13,24 @@ * for more details. */ -#ifndef TEST_RUNNER_H_ -#define TEST_RUNNER_H_ - -#include - -Suite *bio_reader_suite_create(); -Suite *bio_writer_suite_create(); -Suite *chunk_suite_create(); -Suite *enum_suite_create(); -Suite *enumerator_suite_create(); -Suite *linked_list_suite_create(); -Suite *linked_list_enumerator_suite_create(); -Suite *hashtable_suite_create(); -Suite *array_suite_create(); -Suite *identification_suite_create(); -Suite *threading_suite_create(); -Suite *watcher_suite_create(); -Suite *stream_suite_create(); -Suite *utils_suite_create(); -Suite *vectors_suite_create(); -Suite *ecdsa_suite_create(); -Suite *rsa_suite_create(); -Suite *host_suite_create(); -Suite *printf_suite_create(); -Suite *pen_suite_create(); -Suite *asn1_suite_create(); - -#endif /** TEST_RUNNER_H_ */ +TEST_SUITE(bio_reader_suite_create) +TEST_SUITE(bio_writer_suite_create) +TEST_SUITE(chunk_suite_create) +TEST_SUITE(enum_suite_create) +TEST_SUITE(enumerator_suite_create) +TEST_SUITE(linked_list_suite_create) +TEST_SUITE(linked_list_enumerator_suite_create) +TEST_SUITE(hashtable_suite_create) +TEST_SUITE(array_suite_create) +TEST_SUITE(identification_suite_create) +TEST_SUITE(threading_suite_create) +TEST_SUITE(watcher_suite_create) +TEST_SUITE(stream_suite_create) +TEST_SUITE(utils_suite_create) +TEST_SUITE(vectors_suite_create) +TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA) +TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA) +TEST_SUITE(host_suite_create) +TEST_SUITE(printf_suite_create) +TEST_SUITE(pen_suite_create) +TEST_SUITE(asn1_suite_create) From 09d0c9030ae92f80a24fd03f81a96887e7a17800 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 5 Nov 2013 14:40:03 +0100 Subject: [PATCH 52/54] unit-tests: Separate test runner to a library, reusable by other tests Other users may make use of the noinst libtest.la helper library to implement unit tests. For libstrongswan, tests.[ch] provide the configuration for test runner to perform unit tests in a simple manner. --- src/libstrongswan/tests/Makefile.am | 30 +++++++++--- src/libstrongswan/tests/test_runner.c | 68 ++++++++++++--------------- src/libstrongswan/tests/test_runner.h | 60 ++++++++++++++--------- src/libstrongswan/tests/tests.c | 42 +++++++++++++++++ src/libstrongswan/tests/tests.h | 36 ++++++++++++++ 5 files changed, 168 insertions(+), 68 deletions(-) create mode 100644 src/libstrongswan/tests/tests.c create mode 100644 src/libstrongswan/tests/tests.h diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index fd85ee283..d785f240d 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -1,9 +1,24 @@ -TESTS = test_runner +check_LTLIBRARIES = libtest.la + +libtest_la_SOURCES = \ + test_suite.c test_suite.h \ + test_runner.c test_runner.h + +libtest_la_CFLAGS = \ + -I$(top_srcdir)/src/libstrongswan \ + @COVERAGE_CFLAGS@ + +libtest_la_LDFLAGS = @COVERAGE_LDFLAGS@ +libtest_la_LIBADD = \ + $(top_builddir)/src/libstrongswan/libstrongswan.la \ + $(PTHREADLIB) + + +TESTS = tests check_PROGRAMS = $(TESTS) -test_runner_SOURCES = \ - test_runner.c test_runner.h test_suite.c test_suite.h \ +tests_SOURCES = tests.h tests.c \ suites/test_linked_list.c \ suites/test_enumerator.c \ suites/test_linked_list_enumerator.c \ @@ -26,13 +41,14 @@ test_runner_SOURCES = \ suites/test_asn1.c \ suites/test_printf.c -test_runner_CFLAGS = \ +tests_CFLAGS = \ -I$(top_srcdir)/src/libstrongswan \ + -I$(top_srcdir)/src/libstrongswan/tests \ -DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \ -DPLUGINS=\""${s_plugins}\"" \ @COVERAGE_CFLAGS@ -test_runner_LDFLAGS = @COVERAGE_LDFLAGS@ -test_runner_LDADD = \ +tests_LDFLAGS = @COVERAGE_LDFLAGS@ +tests_LDADD = \ $(top_builddir)/src/libstrongswan/libstrongswan.la \ - $(PTHREADLIB) + libtest.la diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 3b2bdacc7..5aa65bab9 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -15,7 +15,7 @@ * for more details. */ -#include "test_suite.h" +#include "test_runner.h" #include #include @@ -33,47 +33,35 @@ /** * Load plugins from builddir */ -static bool load_plugins() +static bool load_plugins(char *plugindirs[], char *plugins) { enumerator_t *enumerator; char *name, path[PATH_MAX], dir[64]; + int i; - enumerator = enumerator_create_token(PLUGINS, " ", ""); + enumerator = enumerator_create_token(plugins, " ", ""); while (enumerator->enumerate(enumerator, &name)) { snprintf(dir, sizeof(dir), "%s", name); translate(dir, "-", "_"); - snprintf(path, sizeof(path), "%s/%s/.libs", PLUGINDIR, dir); - lib->plugins->add_path(lib->plugins, path); + for (i = 0; plugindirs[i]; i++) + { + snprintf(path, sizeof(path), "%s/%s/.libs", plugindirs[i], dir); + lib->plugins->add_path(lib->plugins, path); + } } enumerator->destroy(enumerator); - return lib->plugins->load(lib->plugins, PLUGINS); + return lib->plugins->load(lib->plugins, plugins); } -/* declare test suite constructors */ -#define TEST_SUITE(x) test_suite_t* x(); -#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x) -#include "test_runner.h" -#undef TEST_SUITE -#undef TEST_SUITE_DEPEND - /** * Load all available test suites */ -static array_t *load_suites() +static array_t *load_suites(test_configuration_t configs[], + char *plugindirs[], char *plugins) { array_t *suites; - struct { - test_suite_t *(*suite)(); - plugin_feature_t feature; - } constructors[] = { -#define TEST_SUITE(x) \ - { .suite = x, }, -#define TEST_SUITE_DEPEND(x, type, args) \ - { .suite = x, .feature = PLUGIN_DEPENDS(type, args) }, -#include "test_runner.h" - }; bool old = FALSE; int i; @@ -81,7 +69,7 @@ static array_t *load_suites() test_setup_handler(); - if (!load_plugins()) + if (!load_plugins(plugindirs, plugins)) { library_deinit(); return NULL; @@ -95,12 +83,12 @@ static array_t *load_suites() suites = array_create(0, 0); - for (i = 0; i < countof(constructors); i++) + for (i = 0; configs[i].suite; i++) { - if (constructors[i].feature.type == 0 || - lib->plugins->has_feature(lib->plugins, constructors[i].feature)) + if (configs[i].feature.type == 0 || + lib->plugins->has_feature(lib->plugins, configs[i].feature)) { - array_insert(suites, -1, constructors[i].suite()); + array_insert(suites, -1, configs[i].suite()); } } @@ -184,7 +172,7 @@ static bool call_fixture(test_case_t *tcase, bool up) /** * Test initialization, initializes libstrongswan for the next run */ -static bool pre_test() +static bool pre_test(char *plugindirs[], char *plugins) { library_init(NULL); @@ -200,7 +188,7 @@ static bool pre_test() lib->leak_detective->set_report_cb(lib->leak_detective, NULL, NULL, NULL); } - if (!load_plugins()) + if (!load_plugins(plugindirs, plugins)) { library_deinit(); return FALSE; @@ -333,7 +321,7 @@ static void print_failures(array_t *failures) /** * Run a single test case with fixtures */ -static bool run_case(test_case_t *tcase) +static bool run_case(test_case_t *tcase, char *plugindirs[], char *plugins) { enumerator_t *enumerator; test_function_t *tfun; @@ -352,7 +340,7 @@ static bool run_case(test_case_t *tcase) for (i = tfun->start; i < tfun->end; i++) { - if (pre_test()) + if (pre_test(plugindirs, plugins)) { bool ok = FALSE, leaks = FALSE; @@ -418,7 +406,7 @@ static bool run_case(test_case_t *tcase) /** * Run a single test suite */ -static bool run_suite(test_suite_t *suite) +static bool run_suite(test_suite_t *suite, char *plugindirs[], char *plugins) { enumerator_t *enumerator; test_case_t *tcase; @@ -429,7 +417,7 @@ static bool run_suite(test_suite_t *suite) enumerator = array_create_enumerator(suite->tcases); while (enumerator->enumerate(enumerator, &tcase)) { - if (run_case(tcase)) + if (run_case(tcase, plugindirs, plugins)) { passed++; } @@ -447,7 +435,11 @@ static bool run_suite(test_suite_t *suite) return FALSE; } -int main(int argc, char *argv[]) +/** + * See header. + */ +int test_runner_run(test_configuration_t configs[], + char *plugindirs[], char *plugins) { array_t *suites; test_suite_t *suite; @@ -457,7 +449,7 @@ int main(int argc, char *argv[]) /* redirect all output to stderr (to redirect make's stdout to /dev/null) */ dup2(2, 1); - suites = load_suites(); + suites = load_suites(configs, plugindirs, plugins); if (!suites) { return EXIT_FAILURE; @@ -468,7 +460,7 @@ int main(int argc, char *argv[]) enumerator = array_create_enumerator(suites); while (enumerator->enumerate(enumerator, &suite)) { - if (run_suite(suite)) + if (run_suite(suite, plugindirs, plugins)) { passed++; } diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index 6316692aa..86b6f1831 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -1,6 +1,6 @@ /* - * 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 @@ -13,24 +13,38 @@ * for more details. */ -TEST_SUITE(bio_reader_suite_create) -TEST_SUITE(bio_writer_suite_create) -TEST_SUITE(chunk_suite_create) -TEST_SUITE(enum_suite_create) -TEST_SUITE(enumerator_suite_create) -TEST_SUITE(linked_list_suite_create) -TEST_SUITE(linked_list_enumerator_suite_create) -TEST_SUITE(hashtable_suite_create) -TEST_SUITE(array_suite_create) -TEST_SUITE(identification_suite_create) -TEST_SUITE(threading_suite_create) -TEST_SUITE(watcher_suite_create) -TEST_SUITE(stream_suite_create) -TEST_SUITE(utils_suite_create) -TEST_SUITE(vectors_suite_create) -TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA) -TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA) -TEST_SUITE(host_suite_create) -TEST_SUITE(printf_suite_create) -TEST_SUITE(pen_suite_create) -TEST_SUITE(asn1_suite_create) +#include "test_suite.h" + +#include + +typedef struct test_configuration_t test_configuration_t; + +/** + * Test configuration, suite constructor with plugin dependency + */ +struct test_configuration_t { + + /** + * Constructor function to create suite. + */ + test_suite_t *(*suite)(); + + /** + * Plugin feature this test suite depends on + */ + plugin_feature_t feature; +}; + +/** + * Run test configuration, loading plugins from plugin base directory. + * + * Both the configs and the plugindirs array must be terminated with a NULL + * element. + * + * @param configs test suite constructors with dependencies + * @param plugindirs base directories containing plugin directories to load + * @param plugins plugin names to load, space separated + * @return test result, EXIT_SUCCESS if all tests passed + */ +int test_runner_run(test_configuration_t config[], + char *plugindirs[], char *plugins); diff --git a/src/libstrongswan/tests/tests.c b/src/libstrongswan/tests/tests.c new file mode 100644 index 000000000..a32f384ca --- /dev/null +++ b/src/libstrongswan/tests/tests.c @@ -0,0 +1,42 @@ +/* + * 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 . + * + * 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. + */ + +#include + +/* declare test suite constructors */ +#define TEST_SUITE(x) test_suite_t* x(); +#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x) +#include "tests.h" +#undef TEST_SUITE +#undef TEST_SUITE_DEPEND + +static test_configuration_t tests[] = { +#define TEST_SUITE(x) \ + { .suite = x, }, +#define TEST_SUITE_DEPEND(x, type, args) \ + { .suite = x, .feature = PLUGIN_DEPENDS(type, args) }, +#include "tests.h" + { .suite = NULL, } +}; + +static char *plugindirs[] = { + PLUGINDIR, + NULL, +}; + +int main(int argc, char *argv[]) +{ + return test_runner_run(tests, plugindirs, PLUGINS); +} diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h new file mode 100644 index 000000000..6316692aa --- /dev/null +++ b/src/libstrongswan/tests/tests.h @@ -0,0 +1,36 @@ +/* + * Copyright (C) 2013 Tobias Brunner + * Hochschule fuer Technik Rapperswil + * + * 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 . + * + * 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. + */ + +TEST_SUITE(bio_reader_suite_create) +TEST_SUITE(bio_writer_suite_create) +TEST_SUITE(chunk_suite_create) +TEST_SUITE(enum_suite_create) +TEST_SUITE(enumerator_suite_create) +TEST_SUITE(linked_list_suite_create) +TEST_SUITE(linked_list_enumerator_suite_create) +TEST_SUITE(hashtable_suite_create) +TEST_SUITE(array_suite_create) +TEST_SUITE(identification_suite_create) +TEST_SUITE(threading_suite_create) +TEST_SUITE(watcher_suite_create) +TEST_SUITE(stream_suite_create) +TEST_SUITE(utils_suite_create) +TEST_SUITE(vectors_suite_create) +TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA) +TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA) +TEST_SUITE(host_suite_create) +TEST_SUITE(printf_suite_create) +TEST_SUITE(pen_suite_create) +TEST_SUITE(asn1_suite_create) From 8d2450d8b8f4ac88f6178c7a2bffda9abad606a7 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 5 Nov 2013 17:43:20 +0100 Subject: [PATCH 53/54] plugin-loader: Convenience function added to add plugin dirs in build tree --- src/libstrongswan/plugins/plugin_loader.c | 19 +++++++++++++++++++ src/libstrongswan/plugins/plugin_loader.h | 9 +++++++++ 2 files changed, 28 insertions(+) diff --git a/src/libstrongswan/plugins/plugin_loader.c b/src/libstrongswan/plugins/plugin_loader.c index 5ed0a9b0f..c9043239b 100644 --- a/src/libstrongswan/plugins/plugin_loader.c +++ b/src/libstrongswan/plugins/plugin_loader.c @@ -1170,3 +1170,22 @@ plugin_loader_t *plugin_loader_create() return &this->public; } + +/* + * See header + */ +void plugin_loader_add_plugindirs(char *basedir, char *plugins) +{ + enumerator_t *enumerator; + char *name, path[PATH_MAX], dir[64]; + + enumerator = enumerator_create_token(plugins, " ", ""); + while (enumerator->enumerate(enumerator, &name)) + { + snprintf(dir, sizeof(dir), "%s", name); + translate(dir, "-", "_"); + snprintf(path, sizeof(path), "%s/%s/.libs", basedir, dir); + lib->plugins->add_path(lib->plugins, path); + } + enumerator->destroy(enumerator); +} diff --git a/src/libstrongswan/plugins/plugin_loader.h b/src/libstrongswan/plugins/plugin_loader.h index 285b33910..164360c30 100644 --- a/src/libstrongswan/plugins/plugin_loader.h +++ b/src/libstrongswan/plugins/plugin_loader.h @@ -146,4 +146,13 @@ struct plugin_loader_t { */ plugin_loader_t *plugin_loader_create(); +/** + * Convenience function to add plugin directories for the given plugins within + * the given base directory according to the conventions in the src/build tree. + * + * @param basedir base directory + * @param plugins space separated list of plugins + */ +void plugin_loader_add_plugindirs(char *basedir, char *plugins); + #endif /** PLUGIN_LOADER_H_ @}*/ From 334f44cd292889173ed4a07d5d843fbd278d9e83 Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 5 Nov 2013 17:45:20 +0100 Subject: [PATCH 54/54] unit-tests: Initialize tests with a callback --- src/libstrongswan/tests/test_runner.c | 66 +++++++++------------------ src/libstrongswan/tests/test_runner.h | 24 +++++++--- src/libstrongswan/tests/tests.c | 24 ++++++++-- 3 files changed, 58 insertions(+), 56 deletions(-) diff --git a/src/libstrongswan/tests/test_runner.c b/src/libstrongswan/tests/test_runner.c index 5aa65bab9..0bb1ab309 100644 --- a/src/libstrongswan/tests/test_runner.c +++ b/src/libstrongswan/tests/test_runner.c @@ -30,36 +30,11 @@ */ #define TTY(color) tty_escape_get(2, TTY_FG_##color) -/** - * Load plugins from builddir - */ -static bool load_plugins(char *plugindirs[], char *plugins) -{ - enumerator_t *enumerator; - char *name, path[PATH_MAX], dir[64]; - int i; - - enumerator = enumerator_create_token(plugins, " ", ""); - while (enumerator->enumerate(enumerator, &name)) - { - snprintf(dir, sizeof(dir), "%s", name); - translate(dir, "-", "_"); - for (i = 0; plugindirs[i]; i++) - { - snprintf(path, sizeof(path), "%s/%s/.libs", plugindirs[i], dir); - lib->plugins->add_path(lib->plugins, path); - } - } - enumerator->destroy(enumerator); - - return lib->plugins->load(lib->plugins, plugins); -} - /** * Load all available test suites */ static array_t *load_suites(test_configuration_t configs[], - char *plugindirs[], char *plugins) + test_runner_init_t init) { array_t *suites; bool old = FALSE; @@ -69,7 +44,7 @@ static array_t *load_suites(test_configuration_t configs[], test_setup_handler(); - if (!load_plugins(plugindirs, plugins)) + if (init && !init(TRUE)) { library_deinit(); return NULL; @@ -97,6 +72,10 @@ static array_t *load_suites(test_configuration_t configs[], lib->leak_detective->set_state(lib->leak_detective, old); } + if (init) + { + init(FALSE); + } library_deinit(); return suites; @@ -172,7 +151,7 @@ static bool call_fixture(test_case_t *tcase, bool up) /** * Test initialization, initializes libstrongswan for the next run */ -static bool pre_test(char *plugindirs[], char *plugins) +static bool pre_test(test_runner_init_t init) { library_init(NULL); @@ -188,12 +167,11 @@ static bool pre_test(char *plugindirs[], char *plugins) lib->leak_detective->set_report_cb(lib->leak_detective, NULL, NULL, NULL); } - if (!load_plugins(plugindirs, plugins)) + if (init && !init(TRUE)) { library_deinit(); return FALSE; } - dbg_default_set_level(LEVEL_SILENT); return TRUE; } @@ -250,7 +228,8 @@ static void sum_leaks(report_data_t *data, int count, size_t bytes, /** * Do library cleanup and optionally check for memory leaks */ -static bool post_test(bool check_leaks, array_t *failures, char *name, int i) +static bool post_test(test_runner_init_t init, bool check_leaks, + array_t *failures, char *name, int i) { report_data_t data = { .failures = failures, @@ -258,10 +237,10 @@ static bool post_test(bool check_leaks, array_t *failures, char *name, int i) .i = i, }; - lib->processor->set_threads(lib->processor, 0); - lib->processor->cancel(lib->processor); - lib->plugins->unload(lib->plugins); - + if (init) + { + init(FALSE); + } if (check_leaks && lib->leak_detective) { lib->leak_detective->set_report_cb(lib->leak_detective, @@ -321,7 +300,7 @@ static void print_failures(array_t *failures) /** * Run a single test case with fixtures */ -static bool run_case(test_case_t *tcase, char *plugindirs[], char *plugins) +static bool run_case(test_case_t *tcase, test_runner_init_t init) { enumerator_t *enumerator; test_function_t *tfun; @@ -340,7 +319,7 @@ static bool run_case(test_case_t *tcase, char *plugindirs[], char *plugins) for (i = tfun->start; i < tfun->end; i++) { - if (pre_test(plugindirs, plugins)) + if (pre_test(init)) { bool ok = FALSE, leaks = FALSE; @@ -361,7 +340,7 @@ static bool run_case(test_case_t *tcase, char *plugindirs[], char *plugins) } } - leaks = post_test(ok, failures, tfun->name, i); + leaks = post_test(init, ok, failures, tfun->name, i); test_setup_timeout(0); @@ -406,7 +385,7 @@ static bool run_case(test_case_t *tcase, char *plugindirs[], char *plugins) /** * Run a single test suite */ -static bool run_suite(test_suite_t *suite, char *plugindirs[], char *plugins) +static bool run_suite(test_suite_t *suite, test_runner_init_t init) { enumerator_t *enumerator; test_case_t *tcase; @@ -417,7 +396,7 @@ static bool run_suite(test_suite_t *suite, char *plugindirs[], char *plugins) enumerator = array_create_enumerator(suite->tcases); while (enumerator->enumerate(enumerator, &tcase)) { - if (run_case(tcase, plugindirs, plugins)) + if (run_case(tcase, init)) { passed++; } @@ -438,8 +417,7 @@ static bool run_suite(test_suite_t *suite, char *plugindirs[], char *plugins) /** * See header. */ -int test_runner_run(test_configuration_t configs[], - char *plugindirs[], char *plugins) +int test_runner_run(test_configuration_t configs[], test_runner_init_t init) { array_t *suites; test_suite_t *suite; @@ -449,7 +427,7 @@ int test_runner_run(test_configuration_t configs[], /* redirect all output to stderr (to redirect make's stdout to /dev/null) */ dup2(2, 1); - suites = load_suites(configs, plugindirs, plugins); + suites = load_suites(configs, init); if (!suites) { return EXIT_FAILURE; @@ -460,7 +438,7 @@ int test_runner_run(test_configuration_t configs[], enumerator = array_create_enumerator(suites); while (enumerator->enumerate(enumerator, &suite)) { - if (run_suite(suite, plugindirs, plugins)) + if (run_suite(suite, init)) { passed++; } diff --git a/src/libstrongswan/tests/test_runner.h b/src/libstrongswan/tests/test_runner.h index 86b6f1831..fb4f52af1 100644 --- a/src/libstrongswan/tests/test_runner.h +++ b/src/libstrongswan/tests/test_runner.h @@ -19,6 +19,19 @@ typedef struct test_configuration_t test_configuration_t; +/** + * Callback called before and after each test case to de-/initialize the + * environment (e.g. to load plugins). It is also called before and after the + * test suites are loaded. + * + * It is called after libstrongswan has been initialized and likewise before it + * gets deinitialized. + * + * @param init TRUE during initialization + * @return FALSE if de-/init failed + */ +typedef bool (*test_runner_init_t)(bool init); + /** * Test configuration, suite constructor with plugin dependency */ @@ -36,15 +49,12 @@ struct test_configuration_t { }; /** - * Run test configuration, loading plugins from plugin base directory. + * Run test configuration. * - * Both the configs and the plugindirs array must be terminated with a NULL - * element. + * The configs array must be terminated with a NULL element. * * @param configs test suite constructors with dependencies - * @param plugindirs base directories containing plugin directories to load - * @param plugins plugin names to load, space separated + * @param init_cb init/deinit callback * @return test result, EXIT_SUCCESS if all tests passed */ -int test_runner_run(test_configuration_t config[], - char *plugindirs[], char *plugins); +int test_runner_run(test_configuration_t config[], test_runner_init_t init_cb); diff --git a/src/libstrongswan/tests/tests.c b/src/libstrongswan/tests/tests.c index a32f384ca..c3f43e71c 100644 --- a/src/libstrongswan/tests/tests.c +++ b/src/libstrongswan/tests/tests.c @@ -31,12 +31,26 @@ static test_configuration_t tests[] = { { .suite = NULL, } }; -static char *plugindirs[] = { - PLUGINDIR, - NULL, -}; +static bool test_runner_init(bool init) +{ + if (init) + { + plugin_loader_add_plugindirs(PLUGINDIR, PLUGINS); + if (!lib->plugins->load(lib->plugins, PLUGINS)) + { + return FALSE; + } + } + else + { + lib->processor->set_threads(lib->processor, 0); + lib->processor->cancel(lib->processor); + lib->plugins->unload(lib->plugins); + } + return TRUE; +} int main(int argc, char *argv[]) { - return test_runner_run(tests, plugindirs, PLUGINS); + return test_runner_run(tests, test_runner_init); }