unit-tests: Support testing multi-threaded code
This commit is contained in:
@@ -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)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user