unit-tests: Support testing multi-threaded code

This commit is contained in:
Martin Willi
2013-11-06 10:31:01 +01:00
parent f23fd4c59b
commit 23b8f9bf86
2 changed files with 49 additions and 7 deletions
+7 -2
View File
@@ -75,6 +75,9 @@ static array_t *load_suites()
} otest = 0; } otest = 0;
library_init(NULL); library_init(NULL);
test_setup_handler();
if (!load_plugins()) if (!load_plugins())
{ {
library_deinit(); library_deinit();
@@ -276,6 +279,10 @@ static bool post_test(bool check_leaks, array_t *failures, char *name, int i)
.i = 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) if (check_leaks && lib->leak_detective)
{ {
lib->leak_detective->set_report_cb(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) */ /* redirect all output to stderr (to redirect make's stdout to /dev/null) */
dup2(2, 1); dup2(2, 1);
test_setup_handler();
suites = load_suites(); suites = load_suites();
if (!suites) if (!suites)
{ {
+42 -5
View File
@@ -18,6 +18,8 @@
#include <signal.h> #include <signal.h>
#include <unistd.h> #include <unistd.h>
#include <pthread.h>
/** /**
* Failure message buf * 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); array_insert(suite->tcases, -1, tcase);
} }
/**
* Main thread performing tests
*/
static pthread_t main_thread;
/** /**
* Let test case fail * Let test case fail
*/ */
static inline void test_failure() 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) switch (signal)
{ {
case SIGUSR1:
/* a different thread failed, abort test */
return test_failure();
case SIGSEGV: case SIGSEGV:
signame = "SIGSEGV"; signame = "SIGSEGV";
break; break;
@@ -189,6 +207,11 @@ static void test_sighandler(int signal)
lib->leak_detective->set_state(lib->leak_detective, old); lib->leak_detective->set_state(lib->leak_detective, old);
} }
test_fail_msg(NULL, 0, "%s(%d)", signame, signal); 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() void test_setup_handler()
{ {
struct sigaction action; struct sigaction action = {
.sa_handler = test_sighandler,
};
action.sa_handler = test_sighandler; main_thread = pthread_self();
action.sa_flags = 0;
sigemptyset(&action.sa_mask); /* signal handler inherited by all threads */
sigaction(SIGSEGV, &action, NULL); sigaction(SIGSEGV, &action, NULL);
sigaction(SIGILL, &action, NULL); sigaction(SIGILL, &action, NULL);
sigaction(SIGBUS, &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(SIGALRM, &action, NULL);
sigaction(SIGUSR1, &action, NULL);
} }
/** /**
@@ -212,6 +240,15 @@ void test_setup_handler()
*/ */
void test_setup_timeout(int s) 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); alarm(s);
} }