2 * Copyright (C) 2013 Martin Willi
4 * Copyright (C) secunet Security Networks AG
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
17 #include "test_suite.h"
26 #include <threading/thread.h>
31 static char failure_buf
[4096];
34 * Source file failure occurred
36 static const char *failure_file
;
39 * Line of source file failure occurred
41 static int failure_line
;
44 * Backtrace of failure, if any
46 static backtrace_t
*failure_backtrace
;
49 * Flag to indicate if a worker thread failed
51 static bool worker_failed
;
57 /** Warning message */
59 /** Source file warning was issued */
61 /** Line of source warning was issued */
66 * Warnings that occurred
68 static warning_info_t warnings
[3];
71 * Current warning index
73 static int warning_idx
= -1;
78 test_suite_t
* test_suite_create(const char *name
)
84 .tcases
= array_create(0, 0),
92 test_case_t
* test_case_create(const char *name
)
98 .functions
= array_create(sizeof(test_function_t
), 0),
99 .fixtures
= array_create(sizeof(test_fixture_t
), 0),
100 .timeout
= TEST_FUNCTION_DEFAULT_TIMEOUT
,
108 void test_case_add_checked_fixture(test_case_t
*tcase
, test_fixture_cb_t setup
,
109 test_fixture_cb_t teardown
)
111 test_fixture_t fixture
= {
113 .teardown
= teardown
,
115 array_insert(tcase
->fixtures
, -1, &fixture
);
121 void test_case_add_test_name(test_case_t
*tcase
, char *name
,
122 test_function_cb_t cb
, int start
, int end
)
124 test_function_t fun
= {
130 array_insert(tcase
->functions
, -1, &fun
);
136 void test_case_set_timeout(test_case_t
*tcase
, int s
)
144 void test_suite_add_case(test_suite_t
*suite
, test_case_t
*tcase
)
146 array_insert(suite
->tcases
, -1, tcase
);
152 * Longjump restore point when failing
154 jmp_buf test_restore_point_env
;
157 * Thread ID of main thread
159 static DWORD main_thread
;
162 * APC routine invoked by main thread on worker failure
164 static void WINAPI
set_worker_failure(ULONG_PTR dwParam
)
166 worker_failed
= TRUE
;
172 static void test_failure()
174 if (GetCurrentThreadId() == main_thread
)
176 longjmp(test_restore_point_env
, 1);
182 thread
= OpenThread(THREAD_SET_CONTEXT
, FALSE
, main_thread
);
185 QueueUserAPC(set_worker_failure
, thread
, (uintptr_t)NULL
);
195 void test_fail_if_worker_failed()
197 if (GetCurrentThreadId() == main_thread
&& worker_failed
)
204 * Vectored exception handler
206 static long WINAPI
eh_handler(PEXCEPTION_POINTERS ei
)
211 switch (ei
->ExceptionRecord
->ExceptionCode
)
213 case EXCEPTION_ACCESS_VIOLATION
:
214 ename
= "ACCESS_VIOLATION";
216 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED
:
217 ename
= "ARRAY_BOUNDS_EXCEEDED";
219 case EXCEPTION_DATATYPE_MISALIGNMENT
:
220 ename
= "DATATYPE_MISALIGNMENT";
222 case EXCEPTION_FLT_DENORMAL_OPERAND
:
223 ename
= "FLT_DENORMAL_OPERAND";
225 case EXCEPTION_FLT_DIVIDE_BY_ZERO
:
226 ename
= "FLT_DIVIDE_BY_ZERO";
228 case EXCEPTION_FLT_INEXACT_RESULT
:
229 ename
= "FLT_INEXACT_RESULT";
231 case EXCEPTION_FLT_INVALID_OPERATION
:
232 ename
= "FLT_INVALID_OPERATION";
234 case EXCEPTION_FLT_OVERFLOW
:
235 ename
= "FLT_OVERFLOW";
237 case EXCEPTION_FLT_STACK_CHECK
:
238 ename
= "FLT_STACK_CHECK";
240 case EXCEPTION_FLT_UNDERFLOW
:
241 ename
= "FLT_UNDERFLOW";
243 case EXCEPTION_ILLEGAL_INSTRUCTION
:
244 ename
= "ILLEGAL_INSTRUCTION";
246 case EXCEPTION_IN_PAGE_ERROR
:
247 ename
= "IN_PAGE_ERROR";
249 case EXCEPTION_INT_DIVIDE_BY_ZERO
:
250 ename
= "INT_DIVIDE_BY_ZERO";
252 case EXCEPTION_INT_OVERFLOW
:
253 ename
= "INT_OVERFLOW";
255 case EXCEPTION_INVALID_DISPOSITION
:
256 ename
= "INVALID_DISPOSITION";
258 case EXCEPTION_NONCONTINUABLE_EXCEPTION
:
259 ename
= "NONCONTINUABLE_EXCEPTION";
261 case EXCEPTION_PRIV_INSTRUCTION
:
262 ename
= "PRIV_INSTRUCTION";
264 case EXCEPTION_STACK_OVERFLOW
:
265 ename
= "STACK_OVERFLOW";
268 return EXCEPTION_CONTINUE_EXECUTION
;
271 if (lib
->leak_detective
)
273 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
275 failure_backtrace
= backtrace_create(5);
276 if (lib
->leak_detective
)
278 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
281 test_fail_msg(NULL
, 0, "%s exception", ename
);
283 return EXCEPTION_CONTINUE_EXECUTION
;
289 void test_setup_handler()
291 main_thread
= GetCurrentThreadId();
292 AddVectoredExceptionHandler(0, eh_handler
);
298 void test_setup_timeout(int s
)
300 /* TODO: currently not supported. SetTimer()? */
302 worker_failed
= FALSE
;
308 * Longjump restore point when failing
310 sigjmp_buf test_restore_point_env
;
313 * Main thread performing tests
315 static pthread_t main_thread
;
320 static inline void test_failure()
322 if (pthread_self() == main_thread
)
324 siglongjmp(test_restore_point_env
, 1);
328 pthread_kill(main_thread
, SIGUSR1
);
329 /* terminate thread to prevent it from going wild */
337 void test_fail_if_worker_failed()
339 if (pthread_self() == main_thread
&& worker_failed
)
346 * Signal handler catching critical and alarm signals
348 static void test_sighandler(int signal
)
356 /* a different thread failed, abort test at the next opportunity */
357 worker_failed
= TRUE
;
375 if (lib
->leak_detective
)
377 old
= lib
->leak_detective
->set_state(lib
->leak_detective
, FALSE
);
379 failure_backtrace
= backtrace_create(3);
380 if (lib
->leak_detective
)
382 lib
->leak_detective
->set_state(lib
->leak_detective
, old
);
384 test_fail_msg(NULL
, 0, "%s(%d)", signame
, signal
);
385 /* unable to restore a valid context for that thread, terminate */
386 fprintf(stderr
, "\n%s(%d) outside of main thread:\n", signame
, signal
);
387 failure_backtrace
->log(failure_backtrace
, stderr
, TRUE
);
388 fprintf(stderr
, "terminating...\n");
395 void test_setup_handler()
397 struct sigaction action
= {
398 .sa_handler
= test_sighandler
,
401 main_thread
= pthread_self();
403 /* signal handler inherited by all threads */
404 sigaction(SIGSEGV
, &action
, NULL
);
405 sigaction(SIGILL
, &action
, NULL
);
406 sigaction(SIGBUS
, &action
, NULL
);
407 /* ignore ALRM/USR1, these are caught by main thread only */
408 action
.sa_handler
= SIG_IGN
;
409 sigaction(SIGALRM
, &action
, NULL
);
410 sigaction(SIGUSR1
, &action
, NULL
);
416 void test_setup_timeout(int s
)
418 struct sigaction action
= {
419 .sa_handler
= test_sighandler
,
422 /* This called by main thread only. Setup handler for timeout and
423 * failure cross-thread signaling. */
424 sigaction(SIGALRM
, &action
, NULL
);
425 sigaction(SIGUSR1
, &action
, NULL
);
429 worker_failed
= FALSE
;
437 void test_fail_vmsg(const char *file
, int line
, char *fmt
, va_list args
)
439 vsnprintf(failure_buf
, sizeof(failure_buf
), fmt
, args
);
449 void test_warn_msg(const char *file
, int line
, char *fmt
, ...)
453 if (++warning_idx
>= countof(warnings
))
458 vsnprintf(warnings
[warning_idx
].msg
, sizeof(warnings
[warning_idx
].msg
),
461 warnings
[warning_idx
].file
= file
;
462 warnings
[warning_idx
].line
= line
;
468 void test_fail_msg(const char *file
, int line
, char *fmt
, ...)
473 vsnprintf(failure_buf
, sizeof(failure_buf
), fmt
, args
);
484 int test_failure_get(char *msg
, int len
, const char **file
)
486 strncpy(msg
, failure_buf
, len
- 1);
488 *file
= failure_file
;
495 bool test_warnings_get(void (*cb
)(void *ctx
, const char *msg
, const char *file
,
496 const int line
), void *ctx
)
504 for (i
= 0; i
<= warning_idx
&& i
< countof(warnings
); i
++)
506 cb(ctx
, warnings
[i
].msg
, warnings
[i
].file
, warnings
[i
].line
);
516 backtrace_t
*test_failure_backtrace()
520 bt
= failure_backtrace
;
521 failure_backtrace
= NULL
;