unit-tests: Support testable functions on Windows, avoid weak GCC symbols
Instead of using weak symbols, we use dlsym() on Windows to find an arbitrary symbol in libtest to detect its linkage. Instead of creating the associated hashtable in the test runner, we maintain it in libstrongswan, making it significantly simpler.
This commit is contained in:
@@ -35,35 +35,12 @@
|
|||||||
#define TTY(color) tty_escape_get(2, TTY_FG_##color)
|
#define TTY(color) tty_escape_get(2, TTY_FG_##color)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the lookup table for testable functions (defined in
|
* A global symbol indicating libtest linkage
|
||||||
* libstrongswan). We don't use the constructor attribute as the order can't
|
|
||||||
* really be defined (clang does not support it and gcc does not adhere to it in
|
|
||||||
* the monolithic build). The function here is a weak symbol in libstrongswan.
|
|
||||||
*/
|
*/
|
||||||
void testable_functions_create()
|
#ifdef WIN32
|
||||||
{
|
__declspec(dllexport)
|
||||||
if (!testable_functions)
|
#endif
|
||||||
{
|
bool test_runner_available = TRUE;
|
||||||
/* as this is executed before chunk_hash() seed initialization used
|
|
||||||
* by hashtables, we enforce seeding it here. */
|
|
||||||
chunk_hash_seed();
|
|
||||||
testable_functions = hashtable_create(hashtable_hash_str,
|
|
||||||
hashtable_equals_str, 8);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy the lookup table for testable functions
|
|
||||||
*/
|
|
||||||
static void testable_functions_destroy() __attribute__ ((destructor));
|
|
||||||
static void testable_functions_destroy()
|
|
||||||
{
|
|
||||||
DESTROY_IF(testable_functions);
|
|
||||||
/* if leak detective is enabled plugins are not actually unloaded, which
|
|
||||||
* means their destructor is called AFTER this one when the process
|
|
||||||
* terminates, make sure this does not crash */
|
|
||||||
testable_functions = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Destroy a single test suite and associated data
|
* Destroy a single test suite and associated data
|
||||||
|
|||||||
@@ -20,13 +20,23 @@
|
|||||||
/**
|
/**
|
||||||
* A collection of testable functions
|
* A collection of testable functions
|
||||||
*/
|
*/
|
||||||
hashtable_t *testable_functions;
|
static hashtable_t *functions = NULL;
|
||||||
|
|
||||||
|
#ifndef WIN32
|
||||||
|
bool test_runner_available __attribute__((weak));
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The function that actually initializes the hash table above. Provided
|
* Check if we have libtest linkage and need testable functions
|
||||||
* by the test runner.
|
|
||||||
*/
|
*/
|
||||||
void testable_functions_create() __attribute__((weak));
|
static bool has_libtest_linkage()
|
||||||
|
{
|
||||||
|
#ifdef WIN32
|
||||||
|
return dlsym(RTLD_DEFAULT, "test_runner_available");
|
||||||
|
#else
|
||||||
|
return test_runner_available;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Described in header.
|
* Described in header.
|
||||||
@@ -35,33 +45,48 @@ void testable_function_register(char *name, void *fn)
|
|||||||
{
|
{
|
||||||
bool old = FALSE;
|
bool old = FALSE;
|
||||||
|
|
||||||
if (!testable_functions_create)
|
|
||||||
{ /* not linked to the test runner */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (!fn && !testable_functions)
|
|
||||||
{ /* ignore as testable_functions has already been destroyed */
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (lib && lib->leak_detective)
|
if (lib && lib->leak_detective)
|
||||||
{
|
{
|
||||||
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
|
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
|
||||||
}
|
}
|
||||||
if (!testable_functions)
|
|
||||||
|
if (has_libtest_linkage())
|
||||||
{
|
{
|
||||||
testable_functions_create();
|
if (!functions)
|
||||||
}
|
{
|
||||||
if (fn)
|
chunk_hash_seed();
|
||||||
{
|
functions = hashtable_create(hashtable_hash_str,
|
||||||
testable_functions->put(testable_functions, name, fn);
|
hashtable_equals_str, 8);
|
||||||
}
|
}
|
||||||
else
|
if (fn)
|
||||||
{
|
{
|
||||||
testable_functions->remove(testable_functions, name);
|
functions->put(functions, name, fn);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
functions->remove(functions, name);
|
||||||
|
if (functions->get_count(functions) == 0)
|
||||||
|
{
|
||||||
|
functions->destroy(functions);
|
||||||
|
functions = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lib && lib->leak_detective)
|
if (lib && lib->leak_detective)
|
||||||
{
|
{
|
||||||
lib->leak_detective->set_state(lib->leak_detective, old);
|
lib->leak_detective->set_state(lib->leak_detective, old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Described in header.
|
||||||
|
*/
|
||||||
|
void* testable_function_get(char *name)
|
||||||
|
{
|
||||||
|
if (functions)
|
||||||
|
{
|
||||||
|
return functions->get(functions, name);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|||||||
@@ -23,13 +23,6 @@
|
|||||||
|
|
||||||
#include "collections/hashtable.h"
|
#include "collections/hashtable.h"
|
||||||
|
|
||||||
/**
|
|
||||||
* Collection of testable functions.
|
|
||||||
*
|
|
||||||
* @note Is initialized only if libtest is loaded.
|
|
||||||
*/
|
|
||||||
extern hashtable_t *testable_functions;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register a (possibly static) function so that it can be called from tests.
|
* Register a (possibly static) function so that it can be called from tests.
|
||||||
*
|
*
|
||||||
@@ -38,6 +31,14 @@ extern hashtable_t *testable_functions;
|
|||||||
*/
|
*/
|
||||||
void testable_function_register(char *name, void *fn);
|
void testable_function_register(char *name, void *fn);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find a previously registered testable function.
|
||||||
|
*
|
||||||
|
* @param name name (namespace/function)
|
||||||
|
* @return function, NULL if not found
|
||||||
|
*/
|
||||||
|
void* testable_function_get(char *name);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Macro to automatically register/unregister a function that can be called
|
* Macro to automatically register/unregister a function that can be called
|
||||||
* from tests.
|
* from tests.
|
||||||
@@ -82,10 +83,7 @@ static ret (*TEST_##ns##name)(__VA_ARGS__);
|
|||||||
*/
|
*/
|
||||||
#define TEST_FUNCTION(ns, name, ...) \
|
#define TEST_FUNCTION(ns, name, ...) \
|
||||||
({ \
|
({ \
|
||||||
if (testable_functions) \
|
TEST_##ns##name = testable_function_get( #ns "/" #name); \
|
||||||
{ \
|
|
||||||
TEST_##ns##name = testable_functions->get(testable_functions, #ns "/" #name); \
|
|
||||||
} \
|
|
||||||
if (!TEST_##ns##name) \
|
if (!TEST_##ns##name) \
|
||||||
{ \
|
{ \
|
||||||
test_fail_msg(__FILE__, __LINE__, "function " #name " (" #ns ") not found"); \
|
test_fail_msg(__FILE__, __LINE__, "function " #name " (" #ns ") not found"); \
|
||||||
|
|||||||
Reference in New Issue
Block a user