2 * Copyright (C) 2013 Martin Willi
3 * Copyright (C) 2013 revosec AG
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 #include "test_suite.h"
25 #include <threading/thread.h>
30 static char failure_buf
[512];
33 * Source file failure occurred
35 static const char *failure_file
;
38 * Line of source file failure occurred
40 static int failure_line
;
43 * Backtrace of failure, if any
45 static backtrace_t
*failure_backtrace
;
48 * Flag to indicate if a worker thread failed
50 static bool worker_failed
;
55 test_suite_t
* test_suite_create(const char *name
)
61 .tcases
= array_create(0, 0),
69 test_case_t
* test_case_create(const char *name
)
75 .functions
= array_create(sizeof(test_function_t
), 0),
76 .fixtures
= array_create(sizeof(test_fixture_t
), 0),
77 .timeout
= TEST_FUNCTION_DEFAULT_TIMEOUT
,
85 void test_case_add_checked_fixture(test_case_t
*tcase
, test_fixture_cb_t setup
,
86 test_fixture_cb_t teardown
)
88 test_fixture_t fixture
= {
92 array_insert(tcase
->fixtures
, -1, &fixture
);
98 void test_case_add_test_name(test_case_t
*tcase
, char *name
,
99 test_function_cb_t cb
, int start
, int end
)
101 test_function_t fun
= {
107 array_insert(tcase
->functions
, -1, &fun
);
113 void test_case_set_timeout(test_case_t
*tcase
, int s
)
121 void test_suite_add_case(test_suite_t
*suite
, test_case_t
*tcase
)
123 array_insert(suite
->tcases
, -1, tcase
);
129 * Longjump restore point when failing
131 jmp_buf test_restore_point_env
;
134 * Thread ID of main thread
136 static DWORD main_thread
;
139 * APC routine invoked by main thread on worker failure
141 static void WINAPI
set_worker_failure(ULONG_PTR dwParam
)
143 worker_failed
= TRUE
;
149 static void test_failure()
151 if (GetCurrentThreadId() == main_thread
)
153 longjmp(test_restore_point_env
, 1);
159 thread
= OpenThread(THREAD_SET_CONTEXT
, FALSE
, main_thread
);
162 QueueUserAPC(set_worker_failure
, thread
, (uintptr_t)NULL
);
172 void test_fail_if_worker_failed()
174 if (GetCurrentThreadId() == main_thread
&& worker_failed
)
181 * Vectored exception handler
183 static long WINAPI
eh_handler(PEXCEPTION_POINTERS ei
)
188 switch (ei
->ExceptionRecord
->ExceptionCode
)
190 case EXCEPTION_ACCESS_VIOLATION
:
191 ename
= "ACCESS_VIOLATION";
193 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
194 ename
= "ARRAY_BOUNDS_EXCEEDED";
196 case EXCEPTION_DATATYPE_MISALIGNMENT
:
197 ename
= "DATATYPE_MISALIGNMENT";
199 case EXCEPTION_FLT_DENORMAL_OPERAND
:
200 ename
= "FLT_DENORMAL_OPERAND";
202 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
203 ename
= "FLT_DIVIDE_BY_ZERO";
205 case EXCEPTION_FLT_INEXACT_RESULT
:
206 ename
= "FLT_INEXACT_RESULT";
208 case EXCEPTION_FLT_INVALID_OPERATION
:
209 ename
= "FLT_INVALID_OPERATION";
211 case EXCEPTION_FLT_OVERFLOW
:
212 ename
= "FLT_OVERFLOW";
214 case EXCEPTION_FLT_STACK_CHECK
:
215 ename
= "FLT_STACK_CHECK";
217 case EXCEPTION_FLT_UNDERFLOW
:
218 ename
= "FLT_UNDERFLOW";
220 case EXCEPTION_ILLEGAL_INSTRUCTION
:
221 ename
= "ILLEGAL_INSTRUCTION";
223 case EXCEPTION_IN_PAGE_ERROR
:
224 ename
= "IN_PAGE_ERROR";
226 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
227 ename
= "INT_DIVIDE_BY_ZERO";
229 case EXCEPTION_INT_OVERFLOW
:
230 ename
= "INT_OVERFLOW";
232 case EXCEPTION_INVALID_DISPOSITION
:
233 ename
= "INVALID_DISPOSITION";
235 case EXCEPTION_NONCONTINUABLE_EXCEPTION
:
236 ename
= "NONCONTINUABLE_EXCEPTION";
238 case EXCEPTION_PRIV_INSTRUCTION
:
239 ename
= "PRIV_INSTRUCTION";
241 case EXCEPTION_STACK_OVERFLOW
:
242 ename
= "STACK_OVERFLOW";
245 return EXCEPTION_CONTINUE_EXECUTION
;
248 if (lib
->leak_detective
)
250 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
252 failure_backtrace
= backtrace_create(5);
253 if (lib
->leak_detective
)
255 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
258 test_fail_msg(NULL
, 0, "%s exception", ename
);
260 return EXCEPTION_CONTINUE_EXECUTION
;
266 void test_setup_handler()
268 main_thread
= GetCurrentThreadId();
269 AddVectoredExceptionHandler(0, eh_handler
);
275 void test_setup_timeout(int s
)
277 /* TODO: currently not supported. SetTimer()? */
279 worker_failed
= FALSE
;
285 * Longjump restore point when failing
287 sigjmp_buf test_restore_point_env
;
290 * Main thread performing tests
292 static pthread_t main_thread
;
297 static inline void test_failure()
299 if (pthread_self() == main_thread
)
301 siglongjmp(test_restore_point_env
, 1);
305 pthread_kill(main_thread
, SIGUSR1
);
306 /* terminate thread to prevent it from going wild */
314 void test_fail_if_worker_failed()
316 if (pthread_self() == main_thread
&& worker_failed
)
323 * Signal handler catching critical and alarm signals
325 static void test_sighandler(int signal
)
333 /* a different thread failed, abort test at the next opportunity */
334 worker_failed
= TRUE
;
352 if (lib
->leak_detective
)
354 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
356 failure_backtrace
= backtrace_create(3);
357 if (lib
->leak_detective
)
359 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
361 test_fail_msg(NULL
, 0, "%s(%d)", signame
, signal
);
362 /* unable to restore a valid context for that thread, terminate */
363 fprintf(stderr
, "\n%s(%d) outside of main thread:\n", signame
, signal
);
364 failure_backtrace
->log(failure_backtrace
, stderr
, TRUE
);
365 fprintf(stderr
, "terminating...\n");
372 void test_setup_handler()
374 struct sigaction action
= {
375 .sa_handler
= test_sighandler
,
378 main_thread
= pthread_self();
380 /* signal handler inherited by all threads */
381 sigaction(SIGSEGV
, &action
, NULL
);
382 sigaction(SIGILL
, &action
, NULL
);
383 sigaction(SIGBUS
, &action
, NULL
);
384 /* ignore ALRM/USR1, these are catched by main thread only */
385 action
.sa_handler
= SIG_IGN
;
386 sigaction(SIGALRM
, &action
, NULL
);
387 sigaction(SIGUSR1
, &action
, NULL
);
393 void test_setup_timeout(int s
)
395 struct sigaction action
= {
396 .sa_handler
= test_sighandler
,
399 /* This called by main thread only. Setup handler for timeout and
400 * failure cross-thread signaling. */
401 sigaction(SIGALRM
, &action
, NULL
);
402 sigaction(SIGUSR1
, &action
, NULL
);
406 worker_failed
= FALSE
;
414 void test_fail_vmsg(const char *file
, int line
, char *fmt
, va_list args
)
416 vsnprintf(failure_buf
, sizeof(failure_buf
), fmt
, args
);
425 void test_fail_msg(const char *file
, int line
, char *fmt
, ...)
430 vsnprintf(failure_buf
, sizeof(failure_buf
), fmt
, args
);
441 int test_failure_get(char *msg
, int len
, const char **file
)
443 strncpy(msg
, failure_buf
, len
- 1);
445 *file
= failure_file
;
452 backtrace_t
*test_failure_backtrace()
456 bt
= failure_backtrace
;
457 failure_backtrace
= NULL
;