unit-tests: Add support for Windows build
authorMartin Willi <martin@revosec.ch>
Tue, 15 Oct 2013 15:40:58 +0000 (17:40 +0200)
committerMartin Willi <martin@revosec.ch>
Wed, 4 Jun 2014 13:52:58 +0000 (15:52 +0200)
Instead of signals we catch Windows exceptions. Currently not supported are
timers, which is more a convenience thing anyway.

src/libstrongswan/tests/test_suite.c
src/libstrongswan/tests/test_suite.h

index a636d6f..c80c6ef 100644 (file)
 #include <signal.h>
 #include <unistd.h>
 
+#ifndef WIN32
 #include <pthread.h>
+#endif
+
+#include <threading/thread.h>
 
 /**
  * Failure message buf
@@ -46,11 +50,6 @@ static backtrace_t *failure_backtrace;
 static bool worker_failed;
 
 /**
- * Longjump restore point when failing
- */
-sigjmp_buf test_restore_point_env;
-
-/**
  * See header.
  */
 test_suite_t* test_suite_create(const char *name)
@@ -124,54 +123,189 @@ void test_suite_add_case(test_suite_t *suite, test_case_t *tcase)
        array_insert(suite->tcases, -1, tcase);
 }
 
+#ifdef WIN32
+
 /**
- * Main thread performing tests
+ * Longjump restore point when failing
  */
-static pthread_t main_thread;
+jmp_buf test_restore_point_env;
+
+/**
+ * Thread ID of main thread
+ */
+static DWORD main_thread;
+
+/**
+ * APC routine invoked by main thread on worker failure
+ */
+static void set_worker_failure(ULONG_PTR dwParam)
+{
+       worker_failed = TRUE;
+}
 
 /**
  * Let test case fail
  */
-static inline void test_failure()
+static void test_failure()
 {
-       if (pthread_self() == main_thread)
+       if (GetCurrentThreadId() == main_thread)
        {
-               siglongjmp(test_restore_point_env, 1);
+               longjmp(test_restore_point_env, 1);
        }
        else
        {
-               pthread_kill(main_thread, SIGUSR1);
-               /* terminate thread to prevent it from going wild */
-               pthread_exit(NULL);
+               HANDLE *thread;
+
+               thread = OpenThread(THREAD_SET_CONTEXT, FALSE, main_thread);
+               if (thread)
+               {
+                       QueueUserAPC(set_worker_failure, thread, (uintptr_t)NULL);
+                       CloseHandle(thread);
+               }
+               thread_exit(NULL);
        }
 }
 
 /**
  * See header.
  */
-void test_fail_vmsg(const char *file, int line, char *fmt, va_list args)
+void test_fail_if_worker_failed()
 {
-       vsnprintf(failure_buf, sizeof(failure_buf), fmt, args);
-       failure_line = line;
-       failure_file = file;
+       if (GetCurrentThreadId() == main_thread && worker_failed)
+       {
+               test_failure();
+       }
+}
 
-       test_failure();
+/**
+ * Vectored exception handler
+ */
+static long eh_handler(PEXCEPTION_POINTERS ei)
+{
+       char *ename;
+       bool old = FALSE;
+
+       switch (ei->ExceptionRecord->ExceptionCode)
+       {
+               case EXCEPTION_ACCESS_VIOLATION:
+                       ename = "ACCESS_VIOLATION";
+                       break;
+               case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
+                       ename = "ARRAY_BOUNDS_EXCEEDED";
+                       break;
+               case EXCEPTION_DATATYPE_MISALIGNMENT:
+                       ename = "DATATYPE_MISALIGNMENT";
+                       break;
+               case EXCEPTION_FLT_DENORMAL_OPERAND:
+                       ename = "FLT_DENORMAL_OPERAND";
+                       break;
+               case EXCEPTION_FLT_DIVIDE_BY_ZERO:
+                       ename = "FLT_DIVIDE_BY_ZERO";
+                       break;
+               case EXCEPTION_FLT_INEXACT_RESULT:
+                       ename = "FLT_INEXACT_RESULT";
+                       break;
+               case EXCEPTION_FLT_INVALID_OPERATION:
+                       ename = "FLT_INVALID_OPERATION";
+                       break;
+               case EXCEPTION_FLT_OVERFLOW:
+                       ename = "FLT_OVERFLOW";
+                       break;
+               case EXCEPTION_FLT_STACK_CHECK:
+                       ename = "FLT_STACK_CHECK";
+                       break;
+               case EXCEPTION_FLT_UNDERFLOW:
+                       ename = "FLT_UNDERFLOW";
+                       break;
+               case EXCEPTION_ILLEGAL_INSTRUCTION:
+                       ename = "ILLEGAL_INSTRUCTION";
+                       break;
+               case EXCEPTION_IN_PAGE_ERROR:
+                       ename = "IN_PAGE_ERROR";
+                       break;
+               case EXCEPTION_INT_DIVIDE_BY_ZERO:
+                       ename = "INT_DIVIDE_BY_ZERO";
+                       break;
+               case EXCEPTION_INT_OVERFLOW:
+                       ename = "INT_OVERFLOW";
+                       break;
+               case EXCEPTION_INVALID_DISPOSITION:
+                       ename = "INVALID_DISPOSITION";
+                       break;
+               case EXCEPTION_NONCONTINUABLE_EXCEPTION:
+                       ename = "NONCONTINUABLE_EXCEPTION";
+                       break;
+               case EXCEPTION_PRIV_INSTRUCTION:
+                       ename = "PRIV_INSTRUCTION";
+                       break;
+               case EXCEPTION_STACK_OVERFLOW:
+                       ename = "STACK_OVERFLOW";
+                       break;
+               default:
+                       return EXCEPTION_CONTINUE_EXECUTION;
+       }
+
+       if (lib->leak_detective)
+       {
+               old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
+       }
+       failure_backtrace = backtrace_create(5);
+       if (lib->leak_detective)
+       {
+               lib->leak_detective->set_state(lib->leak_detective, old);
+       }
+       failure_line = 0;
+       test_fail_msg(NULL, 0, "%s exception", ename);
+       /* not reached */
+       return EXCEPTION_CONTINUE_EXECUTION;
 }
 
 /**
  * See header.
  */
-void test_fail_msg(const char *file, int line, char *fmt, ...)
+void test_setup_handler()
 {
-       va_list args;
+       main_thread = GetCurrentThreadId();
+       AddVectoredExceptionHandler(0, eh_handler);
+}
 
-       va_start(args, fmt);
-       vsnprintf(failure_buf, sizeof(failure_buf), fmt, args);
-       failure_line = line;
-       failure_file = file;
-       va_end(args);
+/**
+ * See header.
+ */
+void test_setup_timeout(int s)
+{
+       /* TODO: currently not supported. SetTimer()? */
 
-       test_failure();
+       worker_failed = FALSE;
+}
+
+#else /* !WIN32 */
+
+/**
+ * Longjump restore point when failing
+ */
+sigjmp_buf test_restore_point_env;
+
+/**
+ * Main thread performing tests
+ */
+static pthread_t main_thread;
+
+/**
+ * Let test case fail
+ */
+static inline void test_failure()
+{
+       if (pthread_self() == main_thread)
+       {
+               siglongjmp(test_restore_point_env, 1);
+       }
+       else
+       {
+               pthread_kill(main_thread, SIGUSR1);
+               /* terminate thread to prevent it from going wild */
+               pthread_exit(NULL);
+       }
 }
 
 /**
@@ -272,6 +406,35 @@ void test_setup_timeout(int s)
        worker_failed = FALSE;
 }
 
+#endif /* !WIN32 */
+
+/**
+ * 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();
+}
+
 /**
  * See header.
  */
index 4bef6ff..da57ab4 100644 (file)
@@ -174,7 +174,11 @@ void test_suite_add_case(test_suite_t *suite, test_case_t *tcase);
 /**
  * sigjmp restore point used by test_restore_point
  */
+#ifdef WIN32
+extern jmp_buf test_restore_point_env;
+#else
 extern sigjmp_buf test_restore_point_env;
+#endif
 
 /**
  * Set or return from an execution restore point
@@ -185,7 +189,11 @@ extern sigjmp_buf test_restore_point_env;
  *
  * @return                     TRUE if restore point set, FALSE when restored
  */
-#define test_restore_point() (sigsetjmp(test_restore_point_env, 1) == 0)
+#ifdef WIN32
+# define test_restore_point() (setjmp(test_restore_point_env) == 0)
+#else
+# define test_restore_point() (sigsetjmp(test_restore_point_env, 1) == 0)
+#endif
 
 /**
  * Set up signal handlers for test cases