Merge branch 'unit-tests'
Replace the "check" based libstrongswan unit test framework with our own, giving us more flexibility for our specific needs. The new framework is more portable and uses complete libstrongswan init/deinit cycles for each test to properly catch leaks. It fully supports multi-threaded tests, and brings many of them for all threading primitives, watcher and streams. The --enable-unit-tests option is not required anymore for libstrongswan tests, but still is for the still "check" based charon-tkm tests.
This commit is contained in:
+11
-1
@@ -20,7 +20,17 @@
|
|||||||
# ============================
|
# ============================
|
||||||
|
|
||||||
AC_INIT([strongSwan],[5.1.1])
|
AC_INIT([strongSwan],[5.1.1])
|
||||||
AM_INIT_AUTOMAKE([tar-ustar subdir-objects])
|
AM_INIT_AUTOMAKE(m4_esyscmd([
|
||||||
|
echo tar-ustar
|
||||||
|
echo subdir-objects
|
||||||
|
case `automake --version | head -n 1` in
|
||||||
|
*" 1.9"*);;
|
||||||
|
*" 1.10"*);;
|
||||||
|
*" 1.11"*);;
|
||||||
|
# don't use parallel test harness in 1.12 and up
|
||||||
|
*) echo serial-tests;;
|
||||||
|
esac
|
||||||
|
]))
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES])
|
||||||
AC_CONFIG_MACRO_DIR([m4/config])
|
AC_CONFIG_MACRO_DIR([m4/config])
|
||||||
AC_CONFIG_HEADERS([config.h])
|
AC_CONFIG_HEADERS([config.h])
|
||||||
|
|||||||
@@ -489,6 +489,25 @@ static void stroke_leases(private_stroke_socket_t *this,
|
|||||||
this->list->leases(this->list, msg, out);
|
this->list->leases(this->list, msg, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for usage report
|
||||||
|
*/
|
||||||
|
static void report_usage(FILE *out, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed)
|
||||||
|
{
|
||||||
|
fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
|
||||||
|
bytes, count, bytes / count);
|
||||||
|
bt->log(bt, out, detailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function for memusage summary
|
||||||
|
*/
|
||||||
|
static void sum_usage(FILE *out, int count, size_t bytes, int whitelisted)
|
||||||
|
{
|
||||||
|
fprintf(out, "Total memory usage: %zu\n", bytes);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show memory usage
|
* Show memory usage
|
||||||
*/
|
*/
|
||||||
@@ -497,7 +516,9 @@ static void stroke_memusage(private_stroke_socket_t *this,
|
|||||||
{
|
{
|
||||||
if (lib->leak_detective)
|
if (lib->leak_detective)
|
||||||
{
|
{
|
||||||
lib->leak_detective->usage(lib->leak_detective, out);
|
lib->leak_detective->usage(lib->leak_detective,
|
||||||
|
(leak_detective_report_cb_t)report_usage,
|
||||||
|
(leak_detective_summary_cb_t)sum_usage, out);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -488,9 +488,7 @@ if MONOLITHIC
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if UNITTESTS
|
|
||||||
if MONOLITHIC
|
if MONOLITHIC
|
||||||
SUBDIRS += .
|
SUBDIRS += .
|
||||||
endif
|
endif
|
||||||
SUBDIRS += tests
|
SUBDIRS += tests
|
||||||
endif
|
|
||||||
|
|||||||
@@ -61,6 +61,39 @@ struct private_library_t {
|
|||||||
*/
|
*/
|
||||||
library_t *lib = NULL;
|
library_t *lib = NULL;
|
||||||
|
|
||||||
|
#ifdef LEAK_DETECTIVE
|
||||||
|
/**
|
||||||
|
* Default leak report callback
|
||||||
|
*/
|
||||||
|
static void report_leaks(void *user, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%zu bytes total, %d allocations, %zu bytes average:\n",
|
||||||
|
bytes, count, bytes / count);
|
||||||
|
bt->log(bt, stderr, detailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default leak report summary callback
|
||||||
|
*/
|
||||||
|
static void sum_leaks(void* user, int count, size_t bytes, int whitelisted)
|
||||||
|
{
|
||||||
|
switch (count)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
fprintf(stderr, "No leaks detected");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
fprintf(stderr, "One leak detected");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%d leaks detected, %zu bytes", count, bytes);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
|
||||||
|
}
|
||||||
|
#endif /* LEAK_DETECTIVE */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deinitialize library
|
* Deinitialize library
|
||||||
*/
|
*/
|
||||||
@@ -227,6 +260,8 @@ bool library_init(char *settings)
|
|||||||
|
|
||||||
#ifdef LEAK_DETECTIVE
|
#ifdef LEAK_DETECTIVE
|
||||||
lib->leak_detective = leak_detective_create();
|
lib->leak_detective = leak_detective_create();
|
||||||
|
lib->leak_detective->set_report_cb(lib->leak_detective,
|
||||||
|
report_leaks, sum_leaks, NULL);
|
||||||
#endif /* LEAK_DETECTIVE */
|
#endif /* LEAK_DETECTIVE */
|
||||||
|
|
||||||
pfh = printf_hook_create();
|
pfh = printf_hook_create();
|
||||||
|
|||||||
@@ -1170,3 +1170,22 @@ plugin_loader_t *plugin_loader_create()
|
|||||||
|
|
||||||
return &this->public;
|
return &this->public;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* See header
|
||||||
|
*/
|
||||||
|
void plugin_loader_add_plugindirs(char *basedir, char *plugins)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
char *name, path[PATH_MAX], dir[64];
|
||||||
|
|
||||||
|
enumerator = enumerator_create_token(plugins, " ", "");
|
||||||
|
while (enumerator->enumerate(enumerator, &name))
|
||||||
|
{
|
||||||
|
snprintf(dir, sizeof(dir), "%s", name);
|
||||||
|
translate(dir, "-", "_");
|
||||||
|
snprintf(path, sizeof(path), "%s/%s/.libs", basedir, dir);
|
||||||
|
lib->plugins->add_path(lib->plugins, path);
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
}
|
||||||
|
|||||||
@@ -146,4 +146,13 @@ struct plugin_loader_t {
|
|||||||
*/
|
*/
|
||||||
plugin_loader_t *plugin_loader_create();
|
plugin_loader_t *plugin_loader_create();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience function to add plugin directories for the given plugins within
|
||||||
|
* the given base directory according to the conventions in the src/build tree.
|
||||||
|
*
|
||||||
|
* @param basedir base directory
|
||||||
|
* @param plugins space separated list of plugins
|
||||||
|
*/
|
||||||
|
void plugin_loader_add_plugindirs(char *basedir, char *plugins);
|
||||||
|
|
||||||
#endif /** PLUGIN_LOADER_H_ @}*/
|
#endif /** PLUGIN_LOADER_H_ @}*/
|
||||||
|
|||||||
@@ -1,24 +1,54 @@
|
|||||||
TESTS = test_runner
|
check_LTLIBRARIES = libtest.la
|
||||||
|
|
||||||
|
libtest_la_SOURCES = \
|
||||||
|
test_suite.c test_suite.h \
|
||||||
|
test_runner.c test_runner.h
|
||||||
|
|
||||||
|
libtest_la_CFLAGS = \
|
||||||
|
-I$(top_srcdir)/src/libstrongswan \
|
||||||
|
@COVERAGE_CFLAGS@
|
||||||
|
|
||||||
|
libtest_la_LDFLAGS = @COVERAGE_LDFLAGS@
|
||||||
|
libtest_la_LIBADD = \
|
||||||
|
$(top_builddir)/src/libstrongswan/libstrongswan.la \
|
||||||
|
$(PTHREADLIB)
|
||||||
|
|
||||||
|
|
||||||
|
TESTS = tests
|
||||||
|
|
||||||
check_PROGRAMS = $(TESTS)
|
check_PROGRAMS = $(TESTS)
|
||||||
|
|
||||||
test_runner_SOURCES = \
|
tests_SOURCES = tests.h tests.c \
|
||||||
test_runner.c test_runner.h test_suite.h \
|
suites/test_linked_list.c \
|
||||||
test_linked_list.c test_enumerator.c test_linked_list_enumerator.c \
|
suites/test_enumerator.c \
|
||||||
test_bio_reader.c test_bio_writer.c test_chunk.c test_enum.c test_hashtable.c \
|
suites/test_linked_list_enumerator.c \
|
||||||
test_identification.c test_threading.c test_utils.c test_vectors.c \
|
suites/test_bio_reader.c \
|
||||||
test_array.c test_ecdsa.c test_rsa.c test_host.c test_printf.c test_pen.c \
|
suites/test_bio_writer.c \
|
||||||
test_asn1.c
|
suites/test_chunk.c \
|
||||||
|
suites/test_enum.c \
|
||||||
|
suites/test_hashtable.c \
|
||||||
|
suites/test_identification.c \
|
||||||
|
suites/test_threading.c \
|
||||||
|
suites/test_watcher.c \
|
||||||
|
suites/test_stream.c \
|
||||||
|
suites/test_utils.c \
|
||||||
|
suites/test_vectors.c \
|
||||||
|
suites/test_array.c \
|
||||||
|
suites/test_ecdsa.c \
|
||||||
|
suites/test_rsa.c \
|
||||||
|
suites/test_host.c \
|
||||||
|
suites/test_pen.c \
|
||||||
|
suites/test_asn1.c \
|
||||||
|
suites/test_printf.c
|
||||||
|
|
||||||
test_runner_CFLAGS = \
|
tests_CFLAGS = \
|
||||||
-I$(top_srcdir)/src/libstrongswan \
|
-I$(top_srcdir)/src/libstrongswan \
|
||||||
|
-I$(top_srcdir)/src/libstrongswan/tests \
|
||||||
-DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \
|
-DPLUGINDIR=\""$(top_builddir)/src/libstrongswan/plugins\"" \
|
||||||
-DPLUGINS=\""${s_plugins}\"" \
|
-DPLUGINS=\""${s_plugins}\"" \
|
||||||
@COVERAGE_CFLAGS@ \
|
@COVERAGE_CFLAGS@
|
||||||
@CHECK_CFLAGS@
|
|
||||||
|
|
||||||
test_runner_LDFLAGS = @COVERAGE_LDFLAGS@
|
tests_LDFLAGS = @COVERAGE_LDFLAGS@
|
||||||
test_runner_LDADD = \
|
tests_LDADD = \
|
||||||
$(top_builddir)/src/libstrongswan/libstrongswan.la \
|
$(top_builddir)/src/libstrongswan/libstrongswan.la \
|
||||||
$(PTHREADLIB) \
|
libtest.la
|
||||||
@CHECK_LIBS@
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ START_TEST(test_asn1_algorithmIdentifier)
|
|||||||
{
|
{
|
||||||
algid = asn1_algorithmIdentifier(test[i].n);
|
algid = asn1_algorithmIdentifier(test[i].n);
|
||||||
ck_assert(chunk_equals(algid, test[i].algid));
|
ck_assert(chunk_equals(algid, test[i].algid));
|
||||||
|
free(algid.ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -527,6 +528,7 @@ START_TEST(test_asn1_from_time)
|
|||||||
}
|
}
|
||||||
chunk = asn1_from_time(&test[i].time, test[i].type);
|
chunk = asn1_from_time(&test[i].time, test[i].type);
|
||||||
ck_assert(chunk_equals(chunk, test[i].chunk));
|
ck_assert(chunk_equals(chunk, test[i].chunk));
|
||||||
|
free(chunk.ptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -600,7 +602,7 @@ START_TEST(test_asn1_build_object)
|
|||||||
pos = asn1_build_object(&a, test[i].b[0], test[i].len);
|
pos = asn1_build_object(&a, test[i].b[0], test[i].len);
|
||||||
ck_assert(pos == (a.ptr + test[i].size));
|
ck_assert(pos == (a.ptr + test[i].size));
|
||||||
ck_assert(a.len == test[i].size + test[i].len);
|
ck_assert(a.len == test[i].size + test[i].len);
|
||||||
ck_assert(memeq(a.ptr, test[i].b, test[i].size));
|
ck_assert(memeq(a.ptr, test[i].b, test[i].size));
|
||||||
chunk_free(&a);
|
chunk_free(&a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+1
-1
@@ -329,7 +329,7 @@ END_TEST
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define assert_read_data_len(bits) ({ \
|
#define assert_read_data_len(bits) ({ \
|
||||||
bio_reader_t *reader; \
|
bio_reader_t *reader; \
|
||||||
chunk_t read, data; \
|
chunk_t read, data; \
|
||||||
int i, len = bits / 8; \
|
int i, len = bits / 8; \
|
||||||
data = chunk_empty; \
|
data = chunk_empty; \
|
||||||
+2
-2
@@ -181,7 +181,7 @@ END_TEST
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define assert_write_data_len(init, bits) ({ \
|
#define assert_write_data_len(init, bits) ({ \
|
||||||
bio_writer_t *writer; \
|
bio_writer_t *writer; \
|
||||||
chunk_t buf, data; \
|
chunk_t buf, data; \
|
||||||
int i, len = bits / 8; \
|
int i, len = bits / 8; \
|
||||||
writer = bio_writer_create(init); \
|
writer = bio_writer_create(init); \
|
||||||
@@ -240,7 +240,7 @@ END_TEST
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#define assert_wrap_data(init, bits) ({ \
|
#define assert_wrap_data(init, bits) ({ \
|
||||||
bio_writer_t *writer; \
|
bio_writer_t *writer; \
|
||||||
chunk_t buf, data; \
|
chunk_t buf, data; \
|
||||||
int i, len = bits / 8; \
|
int i, len = bits / 8; \
|
||||||
writer = bio_writer_create(init); \
|
writer = bio_writer_create(init); \
|
||||||
+1
-1
@@ -179,7 +179,7 @@ static struct {
|
|||||||
START_TEST(test_from_string)
|
START_TEST(test_from_string)
|
||||||
{
|
{
|
||||||
identification_t *a;
|
identification_t *a;
|
||||||
chunk_t encoding, expected;
|
chunk_t encoding, expected = chunk_empty;
|
||||||
char *id;
|
char *id;
|
||||||
|
|
||||||
id = string_data[_i].id;
|
id = string_data[_i].id;
|
||||||
+16
-12
@@ -246,10 +246,10 @@ struct invoke_t {
|
|||||||
|
|
||||||
static void invoke(intptr_t item, void *a, void *b, void *c, void *d, int *sum)
|
static void invoke(intptr_t item, void *a, void *b, void *c, void *d, int *sum)
|
||||||
{
|
{
|
||||||
ck_assert(a == (void*)1);
|
ck_assert_int_eq((uintptr_t)a, 1);
|
||||||
ck_assert(b == (void*)2);
|
ck_assert_int_eq((uintptr_t)b, 2);
|
||||||
ck_assert(c == (void*)3);
|
ck_assert_int_eq((uintptr_t)c, 3);
|
||||||
ck_assert(d == (void*)4);
|
ck_assert_int_eq((uintptr_t)d, 4);
|
||||||
*sum += item;
|
*sum += item;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -267,7 +267,9 @@ START_TEST(test_invoke_function)
|
|||||||
list->insert_last(list, (void*)3);
|
list->insert_last(list, (void*)3);
|
||||||
list->insert_last(list, (void*)4);
|
list->insert_last(list, (void*)4);
|
||||||
list->insert_last(list, (void*)5);
|
list->insert_last(list, (void*)5);
|
||||||
list->invoke_function(list, (linked_list_invoke_t)invoke, 1, 2, 3, 4, &sum);
|
list->invoke_function(list, (linked_list_invoke_t)invoke,
|
||||||
|
(uintptr_t)1, (uintptr_t)2,
|
||||||
|
(uintptr_t)3, (uintptr_t)4, &sum);
|
||||||
ck_assert_int_eq(sum, 15);
|
ck_assert_int_eq(sum, 15);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -287,7 +289,9 @@ START_TEST(test_invoke_offset)
|
|||||||
{
|
{
|
||||||
list->insert_last(list, &items[i]);
|
list->insert_last(list, &items[i]);
|
||||||
}
|
}
|
||||||
list->invoke_offset(list, offsetof(invoke_t, invoke), 1, 2, 3, 4, &sum);
|
list->invoke_offset(list, offsetof(invoke_t, invoke),
|
||||||
|
(uintptr_t)1, (uintptr_t)2,
|
||||||
|
(uintptr_t)3, (uintptr_t)4, &sum);
|
||||||
ck_assert_int_eq(sum, 15);
|
ck_assert_int_eq(sum, 15);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
@@ -303,7 +307,7 @@ struct clone_t {
|
|||||||
void *(*clone)(clone_t *item);
|
void *(*clone)(clone_t *item);
|
||||||
};
|
};
|
||||||
|
|
||||||
static void *clone(clone_t *item)
|
static void *clonefn(clone_t *item)
|
||||||
{
|
{
|
||||||
return item->val;
|
return item->val;
|
||||||
}
|
}
|
||||||
@@ -326,11 +330,11 @@ START_TEST(test_clone_offset)
|
|||||||
{
|
{
|
||||||
linked_list_t *other;
|
linked_list_t *other;
|
||||||
clone_t items[] = {
|
clone_t items[] = {
|
||||||
{ .val = (void*)1, .clone = clone, },
|
{ .val = (void*)1, .clone = clonefn, },
|
||||||
{ .val = (void*)2, .clone = clone, },
|
{ .val = (void*)2, .clone = clonefn, },
|
||||||
{ .val = (void*)3, .clone = clone, },
|
{ .val = (void*)3, .clone = clonefn, },
|
||||||
{ .val = (void*)4, .clone = clone, },
|
{ .val = (void*)4, .clone = clonefn, },
|
||||||
{ .val = (void*)5, .clone = clone, },
|
{ .val = (void*)5, .clone = clonefn, },
|
||||||
};
|
};
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
+39
-7
@@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
static void verify(char *expected, char *format, ...)
|
static void verify(char *expected, char *format, ...)
|
||||||
{
|
{
|
||||||
FILE *mem;
|
|
||||||
char buf[128];
|
char buf[128];
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
@@ -29,12 +29,18 @@ static void verify(char *expected, char *format, ...)
|
|||||||
ck_assert_str_eq(expected, buf);
|
ck_assert_str_eq(expected, buf);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
mem = fmemopen(buf, sizeof(buf), "w");
|
#ifdef HAVE_FMEMOPEN
|
||||||
va_start(args, format);
|
{
|
||||||
vfprintf(mem, format, args);
|
FILE *mem;
|
||||||
va_end(args);
|
|
||||||
fclose(mem);
|
mem = fmemopen(buf, sizeof(buf), "w");
|
||||||
ck_assert_str_eq(expected, buf);
|
va_start(args, format);
|
||||||
|
vfprintf(mem, format, args);
|
||||||
|
va_end(args);
|
||||||
|
fclose(mem);
|
||||||
|
ck_assert_str_eq(expected, buf);
|
||||||
|
}
|
||||||
|
#endif /* HAVE_FMEMOPEN */
|
||||||
}
|
}
|
||||||
|
|
||||||
START_TEST(test_printf_strings)
|
START_TEST(test_printf_strings)
|
||||||
@@ -42,6 +48,8 @@ START_TEST(test_printf_strings)
|
|||||||
verify("a bc def", "%s %s %s", "a", "bc", "def");
|
verify("a bc def", "%s %s %s", "a", "bc", "def");
|
||||||
verify("asd", "%.3s", "asdfg");
|
verify("asd", "%.3s", "asdfg");
|
||||||
verify("asdf", "%.*s", (int)4, "asdfg");
|
verify("asdf", "%.*s", (int)4, "asdfg");
|
||||||
|
verify("", "%.0s", NULL);
|
||||||
|
verify("", "%.*s", (int)0, NULL);
|
||||||
verify(" asdf", "%6s", "asdf");
|
verify(" asdf", "%6s", "asdf");
|
||||||
verify(" asdf", "%+6s", "asdf");
|
verify(" asdf", "%+6s", "asdf");
|
||||||
verify("asdf ", "%-6s", "asdf");
|
verify("asdf ", "%-6s", "asdf");
|
||||||
@@ -150,6 +158,26 @@ START_TEST(test_printf_float)
|
|||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
|
START_TEST(test_printf_pri)
|
||||||
|
{
|
||||||
|
verify("255", "%" PRIu8, (u_int8_t)0xFF);
|
||||||
|
verify("65535", "%" PRIu16, (u_int16_t)0xFFFF);
|
||||||
|
verify("4294967295", "%" PRIu32, (u_int32_t)0x1FFFFFFFFll);
|
||||||
|
verify("18446744073709551615", "%" PRIu64, (u_int64_t)0xFFFFFFFFFFFFFFFFll);
|
||||||
|
|
||||||
|
verify("-1", "%" PRId8, (int8_t)-1);
|
||||||
|
verify("-1", "%" PRId16, (int16_t)-1);
|
||||||
|
verify("-1", "%" PRId32, (int32_t)-1);
|
||||||
|
verify("-1", "%" PRId64, (int64_t)-1);
|
||||||
|
|
||||||
|
verify("1", "%" PRIuMAX, (uintmax_t)1);
|
||||||
|
verify("1", "%" PRIuPTR, (uintptr_t)1);
|
||||||
|
|
||||||
|
verify("-1", "%" PRIdMAX, (intmax_t)-1);
|
||||||
|
verify("-1", "%" PRIdPTR, (intptr_t)-1);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
Suite *printf_suite_create()
|
Suite *printf_suite_create()
|
||||||
{
|
{
|
||||||
Suite *s;
|
Suite *s;
|
||||||
@@ -181,5 +209,9 @@ Suite *printf_suite_create()
|
|||||||
tcase_add_test(tc, test_printf_float);
|
tcase_add_test(tc, test_printf_float);
|
||||||
suite_add_tcase(s, tc);
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("PRI*");
|
||||||
|
tcase_add_test(tc, test_printf_pri);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,267 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_suite.h"
|
||||||
|
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
static char* services[] = {
|
||||||
|
"unix:///tmp/strongswan-test-service.sck",
|
||||||
|
"tcp://127.0.0.1:7766",
|
||||||
|
"tcp://[::1]:7766",
|
||||||
|
};
|
||||||
|
|
||||||
|
static char msg[] = "testmessage";
|
||||||
|
static int msglen = 12;
|
||||||
|
|
||||||
|
static bool servicing(void *data, stream_t *stream)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
ssize_t len, total;
|
||||||
|
|
||||||
|
ck_assert(streq((char*)data, "test"));
|
||||||
|
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->read(stream, buf, sizeof(buf), TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->write(stream, buf, len, TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_sync)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
stream_service_t *service;
|
||||||
|
stream_t *stream;
|
||||||
|
ssize_t len, total;
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
service = lib->streams->create_service(lib->streams, services[_i], 1);
|
||||||
|
ck_assert(service != NULL);
|
||||||
|
service->on_accept(service, servicing, "test", JOB_PRIO_HIGH, 1);
|
||||||
|
|
||||||
|
stream = lib->streams->connect(lib->streams, services[_i]);
|
||||||
|
ck_assert(stream != NULL);
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->write(stream, msg, msglen, TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->read(stream, buf, sizeof(buf), TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
ck_assert(streq(buf, msg));
|
||||||
|
stream->destroy(stream);
|
||||||
|
|
||||||
|
service->destroy(service);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool on_write(void *data, stream_t *stream)
|
||||||
|
{
|
||||||
|
ssize_t len, total;
|
||||||
|
|
||||||
|
ck_assert(streq((char*)data, "test-write"));
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->write(stream, msg, msglen, TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_done = FALSE;
|
||||||
|
|
||||||
|
static bool on_read(void *data, stream_t *stream)
|
||||||
|
{
|
||||||
|
ssize_t len, total;
|
||||||
|
char buf[64];
|
||||||
|
|
||||||
|
ck_assert(streq((char*)data, "test-read"));
|
||||||
|
for (total = 0; total < msglen;)
|
||||||
|
{
|
||||||
|
len = stream->read(stream, buf, sizeof(buf), TRUE);
|
||||||
|
ck_assert(len > 0);
|
||||||
|
total += len;
|
||||||
|
}
|
||||||
|
ck_assert(streq(buf, msg));
|
||||||
|
read_done = TRUE;
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_async)
|
||||||
|
{
|
||||||
|
stream_service_t *service;
|
||||||
|
stream_t *stream;
|
||||||
|
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
service = lib->streams->create_service(lib->streams, services[_i], 1);
|
||||||
|
ck_assert(service != NULL);
|
||||||
|
service->on_accept(service, servicing, "test", JOB_PRIO_HIGH, 0);
|
||||||
|
|
||||||
|
stream = lib->streams->connect(lib->streams, services[_i]);
|
||||||
|
ck_assert(stream != NULL);
|
||||||
|
read_done = FALSE;
|
||||||
|
stream->on_write(stream, (stream_cb_t)on_write, "test-write");
|
||||||
|
stream->on_read(stream, (stream_cb_t)on_read, "test-read");
|
||||||
|
|
||||||
|
while (!read_done)
|
||||||
|
{
|
||||||
|
usleep(1000);
|
||||||
|
}
|
||||||
|
stream->destroy(stream);
|
||||||
|
|
||||||
|
service->destroy(service);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool all(void *data, stream_t *stream)
|
||||||
|
{
|
||||||
|
char buf[64], *pos;
|
||||||
|
ssize_t len;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
pos = buf;
|
||||||
|
for (i = 0; i < msglen; i++)
|
||||||
|
{
|
||||||
|
len = stream->read(stream, pos, 1, TRUE);
|
||||||
|
ck_assert_int_eq(len, 1);
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
pos = buf;
|
||||||
|
for (i = 0; i < msglen; i++)
|
||||||
|
{
|
||||||
|
len = stream->write(stream, pos, 1, TRUE);
|
||||||
|
ck_assert_int_eq(len, 1);
|
||||||
|
pos += len;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_all)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
stream_service_t *service;
|
||||||
|
stream_t *stream;
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
service = lib->streams->create_service(lib->streams, services[_i], 1);
|
||||||
|
ck_assert(service != NULL);
|
||||||
|
service->on_accept(service, all, NULL, JOB_PRIO_HIGH, 1);
|
||||||
|
|
||||||
|
stream = lib->streams->connect(lib->streams, services[_i]);
|
||||||
|
ck_assert(stream != NULL);
|
||||||
|
ck_assert(stream->write_all(stream, msg, msglen));
|
||||||
|
ck_assert(stream->read_all(stream, buf, msglen));
|
||||||
|
ck_assert(streq(buf, msg));
|
||||||
|
stream->destroy(stream);
|
||||||
|
|
||||||
|
service->destroy(service);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool concurrency(void *data, stream_t *stream)
|
||||||
|
{
|
||||||
|
static refcount_t refs = 0;
|
||||||
|
u_int current;
|
||||||
|
ssize_t len;
|
||||||
|
|
||||||
|
current = ref_get(&refs);
|
||||||
|
ck_assert(current <= 3);
|
||||||
|
len = stream->write(stream, "x", 1, TRUE);
|
||||||
|
ck_assert_int_eq(len, 1);
|
||||||
|
usleep(1000);
|
||||||
|
ignore_result(ref_put(&refs));
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_concurrency)
|
||||||
|
{
|
||||||
|
stream_service_t *service;
|
||||||
|
stream_t *streams[10];
|
||||||
|
ssize_t len;
|
||||||
|
char x;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
service = lib->streams->create_service(lib->streams, services[_i], 10);
|
||||||
|
ck_assert(service != NULL);
|
||||||
|
service->on_accept(service, concurrency, NULL, JOB_PRIO_HIGH, 3);
|
||||||
|
|
||||||
|
for (i = 0; i < countof(streams); i++)
|
||||||
|
{
|
||||||
|
streams[i] = lib->streams->connect(lib->streams, services[_i]);
|
||||||
|
ck_assert(streams[i] != NULL);
|
||||||
|
}
|
||||||
|
for (i = 0; i < countof(streams); i++)
|
||||||
|
{
|
||||||
|
len = streams[i]->read(streams[i], &x, 1, TRUE);
|
||||||
|
ck_assert_int_eq(len, 1);
|
||||||
|
ck_assert_int_eq(x, 'x');
|
||||||
|
}
|
||||||
|
for (i = 0; i < countof(streams); i++)
|
||||||
|
{
|
||||||
|
streams[i]->destroy(streams[i]);
|
||||||
|
}
|
||||||
|
service->destroy(service);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *stream_suite_create()
|
||||||
|
{
|
||||||
|
Suite *s;
|
||||||
|
TCase *tc;
|
||||||
|
|
||||||
|
s = suite_create("stream");
|
||||||
|
|
||||||
|
tc = tcase_create("sync");
|
||||||
|
tcase_add_loop_test(tc, test_sync, 0, countof(services));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("async");
|
||||||
|
tcase_add_loop_test(tc, test_async, 0, countof(services));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("all");
|
||||||
|
tcase_add_loop_test(tc, test_all, 0, countof(services));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("concurrency");
|
||||||
|
tcase_add_loop_test(tc, test_concurrency, 0, countof(services));
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
File diff suppressed because it is too large
Load Diff
+2
-1
@@ -21,7 +21,8 @@
|
|||||||
|
|
||||||
START_TEST(test_vectors)
|
START_TEST(test_vectors)
|
||||||
{
|
{
|
||||||
fail_if(lib->crypto->get_test_vector_failures(lib->crypto));
|
u_int failed = lib->crypto->get_test_vector_failures(lib->crypto);
|
||||||
|
fail_if(failed > 0, "%u test vectors failed", failed);
|
||||||
}
|
}
|
||||||
END_TEST
|
END_TEST
|
||||||
|
|
||||||
@@ -0,0 +1,214 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_suite.h"
|
||||||
|
|
||||||
|
#include <library.h>
|
||||||
|
|
||||||
|
#include <sched.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
static char testbuf[1] = "";
|
||||||
|
|
||||||
|
static bool readcb(void *data, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(*(int*)data, fd);
|
||||||
|
ck_assert_int_eq(event, WATCHER_READ);
|
||||||
|
|
||||||
|
if (recv(fd, testbuf, 1, MSG_DONTWAIT) != 1)
|
||||||
|
{
|
||||||
|
ck_assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_read)
|
||||||
|
{
|
||||||
|
int fd[2];
|
||||||
|
char c;
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1);
|
||||||
|
|
||||||
|
lib->watcher->add(lib->watcher, fd[0], WATCHER_READ, readcb, &fd[0]);
|
||||||
|
|
||||||
|
for (c = 'a'; c <= 'z'; c++)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(write(fd[1], &c, 1), 1);
|
||||||
|
while (testbuf[0] != c)
|
||||||
|
{
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->watcher->remove(lib->watcher, fd[0]);
|
||||||
|
close(fd[0]);
|
||||||
|
close(fd[1]);
|
||||||
|
|
||||||
|
lib->processor->cancel(lib->processor);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool writecb(void *data, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(event, WATCHER_WRITE);
|
||||||
|
if (send(fd, data, 1, MSG_DONTWAIT) != 1)
|
||||||
|
{
|
||||||
|
ck_assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_write)
|
||||||
|
{
|
||||||
|
int fd[2];
|
||||||
|
char in = 'x', out;
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) != -1);
|
||||||
|
|
||||||
|
lib->watcher->add(lib->watcher, fd[1], WATCHER_WRITE, writecb, &in);
|
||||||
|
|
||||||
|
ck_assert_int_eq(read(fd[0], &out, 1), 1);
|
||||||
|
ck_assert_int_eq(out, in);
|
||||||
|
|
||||||
|
lib->watcher->remove(lib->watcher, fd[1]);
|
||||||
|
close(fd[1]);
|
||||||
|
close(fd[0]);
|
||||||
|
|
||||||
|
lib->processor->cancel(lib->processor);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool multiread(void *data, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(event, WATCHER_READ);
|
||||||
|
if (recv(fd, data, 1, MSG_DONTWAIT) != 1)
|
||||||
|
{
|
||||||
|
ck_assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_multiread)
|
||||||
|
{
|
||||||
|
int fd[10][2], i;
|
||||||
|
char in, out[countof(fd)];
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd[i]) != -1);
|
||||||
|
lib->watcher->add(lib->watcher, fd[i][0],
|
||||||
|
WATCHER_READ, multiread, &out[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
for (in = 'a'; in <= 'z'; in++)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(write(fd[i][1], &in, 1), 1);
|
||||||
|
while (out[i] != in)
|
||||||
|
{
|
||||||
|
sched_yield();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
lib->watcher->remove(lib->watcher, fd[i][0]);
|
||||||
|
close(fd[i][1]);
|
||||||
|
close(fd[i][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->processor->cancel(lib->processor);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
static bool multiwrite(void *data, int fd, watcher_event_t event)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(event, WATCHER_WRITE);
|
||||||
|
if (send(fd, data, 1, MSG_DONTWAIT) != 1)
|
||||||
|
{
|
||||||
|
ck_assert(errno == EAGAIN || errno == EWOULDBLOCK);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
START_TEST(test_multiwrite)
|
||||||
|
{
|
||||||
|
int fd[10][2], i, j;
|
||||||
|
u_char out, in[countof(fd)];
|
||||||
|
|
||||||
|
lib->processor->set_threads(lib->processor, 8);
|
||||||
|
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
ck_assert(socketpair(AF_UNIX, SOCK_STREAM, 0, fd[i]) != -1);
|
||||||
|
in[i] = i;
|
||||||
|
lib->watcher->add(lib->watcher, fd[i][1],
|
||||||
|
WATCHER_WRITE, multiwrite, &in[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (j = 0; j < 10; j++)
|
||||||
|
{
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
ck_assert_int_eq(read(fd[i][0], &out, 1), 1);
|
||||||
|
ck_assert_int_eq(out, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < countof(fd); i++)
|
||||||
|
{
|
||||||
|
lib->watcher->remove(lib->watcher, fd[i][1]);
|
||||||
|
close(fd[i][1]);
|
||||||
|
close(fd[i][0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
lib->processor->cancel(lib->processor);
|
||||||
|
}
|
||||||
|
END_TEST
|
||||||
|
|
||||||
|
Suite *watcher_suite_create()
|
||||||
|
{
|
||||||
|
Suite *s;
|
||||||
|
TCase *tc;
|
||||||
|
|
||||||
|
s = suite_create("watcher");
|
||||||
|
|
||||||
|
tc = tcase_create("read");
|
||||||
|
tcase_add_test(tc, test_read);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("write");
|
||||||
|
tcase_add_test(tc, test_write);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("multiread");
|
||||||
|
tcase_add_test(tc, test_multiread);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
tc = tcase_create("multiwrite");
|
||||||
|
tcase_add_test(tc, test_multiwrite);
|
||||||
|
suite_add_tcase(s, tc);
|
||||||
|
|
||||||
|
return s;
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Tobias Brunner
|
* Copyright (C) 2013 Tobias Brunner
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Hochschule fuer Technik Rapperswil
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the
|
* under the terms of the GNU General Public License as published by the
|
||||||
@@ -13,47 +15,144 @@
|
|||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "test_runner.h"
|
#include "test_runner.h"
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
#include <plugins/plugin_feature.h>
|
#include <plugins/plugin_feature.h>
|
||||||
|
#include <collections/array.h>
|
||||||
|
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <limits.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load plugins from builddir
|
* Get a tty color escape character for stderr
|
||||||
*/
|
*/
|
||||||
static bool load_plugins()
|
#define TTY(color) tty_escape_get(2, TTY_FG_##color)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load all available test suites
|
||||||
|
*/
|
||||||
|
static array_t *load_suites(test_configuration_t configs[],
|
||||||
|
test_runner_init_t init)
|
||||||
|
{
|
||||||
|
array_t *suites;
|
||||||
|
bool old = FALSE;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
library_init(NULL);
|
||||||
|
|
||||||
|
test_setup_handler();
|
||||||
|
|
||||||
|
if (init && !init(TRUE))
|
||||||
|
{
|
||||||
|
library_deinit();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
||||||
|
|
||||||
|
if (lib->leak_detective)
|
||||||
|
{
|
||||||
|
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
suites = array_create(0, 0);
|
||||||
|
|
||||||
|
for (i = 0; configs[i].suite; i++)
|
||||||
|
{
|
||||||
|
if (configs[i].feature.type == 0 ||
|
||||||
|
lib->plugins->has_feature(lib->plugins, configs[i].feature))
|
||||||
|
{
|
||||||
|
array_insert(suites, -1, configs[i].suite());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lib->leak_detective)
|
||||||
|
{
|
||||||
|
lib->leak_detective->set_state(lib->leak_detective, old);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (init)
|
||||||
|
{
|
||||||
|
init(FALSE);
|
||||||
|
}
|
||||||
|
library_deinit();
|
||||||
|
|
||||||
|
return suites;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unload and destroy test suites and associated data
|
||||||
|
*/
|
||||||
|
static void unload_suites(array_t *suites)
|
||||||
|
{
|
||||||
|
test_suite_t *suite;
|
||||||
|
test_case_t *tcase;
|
||||||
|
|
||||||
|
while (array_remove(suites, 0, &suite))
|
||||||
|
{
|
||||||
|
while (array_remove(suite->tcases, 0, &tcase))
|
||||||
|
{
|
||||||
|
array_destroy(tcase->functions);
|
||||||
|
array_destroy(tcase->fixtures);
|
||||||
|
}
|
||||||
|
free(suite);
|
||||||
|
}
|
||||||
|
array_destroy(suites);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a single test function, return FALSE on failure
|
||||||
|
*/
|
||||||
|
static bool run_test(test_function_t *tfun, int i)
|
||||||
|
{
|
||||||
|
if (test_restore_point())
|
||||||
|
{
|
||||||
|
tfun->cb(i);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoke fixture setup/teardown
|
||||||
|
*/
|
||||||
|
static bool call_fixture(test_case_t *tcase, bool up)
|
||||||
{
|
{
|
||||||
enumerator_t *enumerator;
|
enumerator_t *enumerator;
|
||||||
char *name, path[PATH_MAX], dir[64];
|
test_fixture_t *fixture;
|
||||||
|
bool failure = FALSE;
|
||||||
|
|
||||||
enumerator = enumerator_create_token(PLUGINS, " ", "");
|
enumerator = array_create_enumerator(tcase->fixtures);
|
||||||
while (enumerator->enumerate(enumerator, &name))
|
while (enumerator->enumerate(enumerator, &fixture))
|
||||||
{
|
{
|
||||||
snprintf(dir, sizeof(dir), "%s", name);
|
if (test_restore_point())
|
||||||
translate(dir, "-", "_");
|
{
|
||||||
snprintf(path, sizeof(path), "%s/%s/.libs", PLUGINDIR, dir);
|
if (up)
|
||||||
lib->plugins->add_path(lib->plugins, path);
|
{
|
||||||
|
fixture->setup();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fixture->teardown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
failure = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
enumerator->destroy(enumerator);
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
return lib->plugins->load(lib->plugins, PLUGINS);
|
return !failure;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main()
|
/**
|
||||||
|
* Test initialization, initializes libstrongswan for the next run
|
||||||
|
*/
|
||||||
|
static bool pre_test(test_runner_init_t init)
|
||||||
{
|
{
|
||||||
SRunner *sr;
|
|
||||||
int nf;
|
|
||||||
|
|
||||||
/* test cases are forked and there is no cleanup, so disable leak detective.
|
|
||||||
* if test_suite.h is included leak detective is enabled in test cases */
|
|
||||||
setenv("LEAK_DETECTIVE_DISABLE", "1", 1);
|
|
||||||
/* redirect all output to stderr (to redirect make's stdout to /dev/null) */
|
|
||||||
dup2(2, 1);
|
|
||||||
|
|
||||||
library_init(NULL);
|
library_init(NULL);
|
||||||
|
|
||||||
/* use non-blocking RNG to generate keys fast */
|
/* use non-blocking RNG to generate keys fast */
|
||||||
@@ -62,47 +161,304 @@ int main()
|
|||||||
lib->settings->get_str(lib->settings,
|
lib->settings->get_str(lib->settings,
|
||||||
"libstrongswan.plugins.random.urandom", "/dev/urandom"));
|
"libstrongswan.plugins.random.urandom", "/dev/urandom"));
|
||||||
|
|
||||||
if (!load_plugins())
|
if (lib->leak_detective)
|
||||||
|
{
|
||||||
|
/* disable leak reports during testing */
|
||||||
|
lib->leak_detective->set_report_cb(lib->leak_detective,
|
||||||
|
NULL, NULL, NULL);
|
||||||
|
}
|
||||||
|
if (init && !init(TRUE))
|
||||||
{
|
{
|
||||||
library_deinit();
|
library_deinit();
|
||||||
return EXIT_FAILURE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
dbg_default_set_level(LEVEL_SILENT);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
sr = srunner_create(NULL);
|
/**
|
||||||
srunner_add_suite(sr, bio_reader_suite_create());
|
* Failure description
|
||||||
srunner_add_suite(sr, bio_writer_suite_create());
|
*/
|
||||||
srunner_add_suite(sr, chunk_suite_create());
|
typedef struct {
|
||||||
srunner_add_suite(sr, enum_suite_create());
|
char *name;
|
||||||
srunner_add_suite(sr, enumerator_suite_create());
|
char msg[512 - sizeof(char*) - 2 * sizeof(int)];
|
||||||
srunner_add_suite(sr, linked_list_suite_create());
|
const char *file;
|
||||||
srunner_add_suite(sr, linked_list_enumerator_suite_create());
|
int line;
|
||||||
srunner_add_suite(sr, hashtable_suite_create());
|
int i;
|
||||||
srunner_add_suite(sr, array_suite_create());
|
backtrace_t *bt;
|
||||||
srunner_add_suite(sr, identification_suite_create());
|
} failure_t;
|
||||||
srunner_add_suite(sr, threading_suite_create());
|
|
||||||
srunner_add_suite(sr, utils_suite_create());
|
/**
|
||||||
srunner_add_suite(sr, host_suite_create());
|
* Data passed to leak report callbacks
|
||||||
srunner_add_suite(sr, vectors_suite_create());
|
*/
|
||||||
srunner_add_suite(sr, printf_suite_create());
|
typedef struct {
|
||||||
srunner_add_suite(sr, pen_suite_create());
|
array_t *failures;
|
||||||
srunner_add_suite(sr, asn1_suite_create());
|
char *name;
|
||||||
if (lib->plugins->has_feature(lib->plugins,
|
int i;
|
||||||
PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_RSA)))
|
int leaks;
|
||||||
|
} report_data_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leak report callback, build failures from leaks
|
||||||
|
*/
|
||||||
|
static void report_leaks(report_data_t *data, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed)
|
||||||
|
{
|
||||||
|
failure_t failure = {
|
||||||
|
.name = data->name,
|
||||||
|
.i = data->i,
|
||||||
|
.bt = bt->clone(bt),
|
||||||
|
};
|
||||||
|
|
||||||
|
snprintf(failure.msg, sizeof(failure.msg),
|
||||||
|
"Leak detected: %d allocations using %zu bytes", count, bytes);
|
||||||
|
|
||||||
|
array_insert(data->failures, -1, &failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leak summary callback, check if any leaks found
|
||||||
|
*/
|
||||||
|
static void sum_leaks(report_data_t *data, int count, size_t bytes,
|
||||||
|
int whitelisted)
|
||||||
|
{
|
||||||
|
data->leaks = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do library cleanup and optionally check for memory leaks
|
||||||
|
*/
|
||||||
|
static bool post_test(test_runner_init_t init, bool check_leaks,
|
||||||
|
array_t *failures, char *name, int i)
|
||||||
|
{
|
||||||
|
report_data_t data = {
|
||||||
|
.failures = failures,
|
||||||
|
.name = name,
|
||||||
|
.i = i,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (init)
|
||||||
{
|
{
|
||||||
srunner_add_suite(sr, rsa_suite_create());
|
init(FALSE);
|
||||||
}
|
}
|
||||||
if (lib->plugins->has_feature(lib->plugins,
|
if (check_leaks && lib->leak_detective)
|
||||||
PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_ECDSA)))
|
|
||||||
{
|
{
|
||||||
srunner_add_suite(sr, ecdsa_suite_create());
|
lib->leak_detective->set_report_cb(lib->leak_detective,
|
||||||
|
(leak_detective_report_cb_t)report_leaks,
|
||||||
|
(leak_detective_summary_cb_t)sum_leaks, &data);
|
||||||
}
|
}
|
||||||
|
|
||||||
srunner_run_all(sr, CK_NORMAL);
|
|
||||||
nf = srunner_ntests_failed(sr);
|
|
||||||
|
|
||||||
srunner_free(sr);
|
|
||||||
library_deinit();
|
library_deinit();
|
||||||
|
|
||||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
return data.leaks != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collect failure information, add failure_t to array
|
||||||
|
*/
|
||||||
|
static void collect_failure_info(array_t *failures, char *name, int i)
|
||||||
|
{
|
||||||
|
failure_t failure = {
|
||||||
|
.name = name,
|
||||||
|
.i = i,
|
||||||
|
.bt = test_failure_backtrace(),
|
||||||
|
};
|
||||||
|
|
||||||
|
failure.line = test_failure_get(failure.msg, sizeof(failure.msg),
|
||||||
|
&failure.file);
|
||||||
|
|
||||||
|
array_insert(failures, -1, &failure);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Print array of collected failure_t to stderr
|
||||||
|
*/
|
||||||
|
static void print_failures(array_t *failures)
|
||||||
|
{
|
||||||
|
failure_t failure;
|
||||||
|
|
||||||
|
backtrace_init();
|
||||||
|
|
||||||
|
while (array_remove(failures, 0, &failure))
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %sFailure in '%s': %s (",
|
||||||
|
TTY(RED), failure.name, failure.msg);
|
||||||
|
if (failure.line)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s:%d, ", failure.file, failure.line);
|
||||||
|
}
|
||||||
|
fprintf(stderr, "i = %d)%s\n", failure.i, TTY(DEF));
|
||||||
|
if (failure.bt)
|
||||||
|
{
|
||||||
|
failure.bt->log(failure.bt, stderr, TRUE);
|
||||||
|
failure.bt->destroy(failure.bt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
backtrace_deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a single test case with fixtures
|
||||||
|
*/
|
||||||
|
static bool run_case(test_case_t *tcase, test_runner_init_t init)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
test_function_t *tfun;
|
||||||
|
int passed = 0;
|
||||||
|
array_t *failures;
|
||||||
|
|
||||||
|
failures = array_create(sizeof(failure_t), 0);
|
||||||
|
|
||||||
|
fprintf(stderr, " Running case '%s': ", tcase->name);
|
||||||
|
fflush(stderr);
|
||||||
|
|
||||||
|
enumerator = array_create_enumerator(tcase->functions);
|
||||||
|
while (enumerator->enumerate(enumerator, &tfun))
|
||||||
|
{
|
||||||
|
int i, rounds = 0;
|
||||||
|
|
||||||
|
for (i = tfun->start; i < tfun->end; i++)
|
||||||
|
{
|
||||||
|
if (pre_test(init))
|
||||||
|
{
|
||||||
|
bool ok = FALSE, leaks = FALSE;
|
||||||
|
|
||||||
|
test_setup_timeout(tcase->timeout);
|
||||||
|
|
||||||
|
if (call_fixture(tcase, TRUE))
|
||||||
|
{
|
||||||
|
if (run_test(tfun, i))
|
||||||
|
{
|
||||||
|
if (call_fixture(tcase, FALSE))
|
||||||
|
{
|
||||||
|
ok = TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
call_fixture(tcase, FALSE);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
leaks = post_test(init, ok, failures, tfun->name, i);
|
||||||
|
|
||||||
|
test_setup_timeout(0);
|
||||||
|
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
if (!leaks)
|
||||||
|
{
|
||||||
|
rounds++;
|
||||||
|
fprintf(stderr, "%s+%s", TTY(GREEN), TTY(DEF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
collect_failure_info(failures, tfun->name, i);
|
||||||
|
}
|
||||||
|
if (!ok || leaks)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%s-%s", TTY(RED), TTY(DEF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fflush(stderr);
|
||||||
|
if (rounds == tfun->end - tfun->start)
|
||||||
|
{
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
fprintf(stderr, "\n");
|
||||||
|
|
||||||
|
print_failures(failures);
|
||||||
|
array_destroy(failures);
|
||||||
|
|
||||||
|
return passed == array_count(tcase->functions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a single test suite
|
||||||
|
*/
|
||||||
|
static bool run_suite(test_suite_t *suite, test_runner_init_t init)
|
||||||
|
{
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
test_case_t *tcase;
|
||||||
|
int passed = 0;
|
||||||
|
|
||||||
|
fprintf(stderr, " Running suite '%s':\n", suite->name);
|
||||||
|
|
||||||
|
enumerator = array_create_enumerator(suite->tcases);
|
||||||
|
while (enumerator->enumerate(enumerator, &tcase))
|
||||||
|
{
|
||||||
|
if (run_case(tcase, init))
|
||||||
|
{
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
if (passed == array_count(suite->tcases))
|
||||||
|
{
|
||||||
|
fprintf(stderr, " %sPassed all %u '%s' test cases%s\n",
|
||||||
|
TTY(GREEN), array_count(suite->tcases), suite->name, TTY(DEF));
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
fprintf(stderr, " %sPassed %u/%u '%s' test cases%s\n",
|
||||||
|
TTY(RED), passed, array_count(suite->tcases), suite->name, TTY(DEF));
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
int test_runner_run(test_configuration_t configs[], test_runner_init_t init)
|
||||||
|
{
|
||||||
|
array_t *suites;
|
||||||
|
test_suite_t *suite;
|
||||||
|
enumerator_t *enumerator;
|
||||||
|
int passed = 0, result;
|
||||||
|
|
||||||
|
/* redirect all output to stderr (to redirect make's stdout to /dev/null) */
|
||||||
|
dup2(2, 1);
|
||||||
|
|
||||||
|
suites = load_suites(configs, init);
|
||||||
|
if (!suites)
|
||||||
|
{
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
fprintf(stderr, "Running %u test suites:\n", array_count(suites));
|
||||||
|
|
||||||
|
enumerator = array_create_enumerator(suites);
|
||||||
|
while (enumerator->enumerate(enumerator, &suite))
|
||||||
|
{
|
||||||
|
if (run_suite(suite, init))
|
||||||
|
{
|
||||||
|
passed++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enumerator->destroy(enumerator);
|
||||||
|
|
||||||
|
if (passed == array_count(suites))
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%sPassed all %u suites%s\n",
|
||||||
|
TTY(GREEN), array_count(suites), TTY(DEF));
|
||||||
|
result = EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
fprintf(stderr, "%sPassed %u of %u suites%s\n",
|
||||||
|
TTY(RED), passed, array_count(suites), TTY(DEF));
|
||||||
|
result = EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
unload_suites(suites);
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Tobias Brunner
|
* Copyright (C) 2013 Martin Willi
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Copyright (C) 2013 revosec AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the
|
* under the terms of the GNU General Public License as published by the
|
||||||
@@ -13,29 +13,48 @@
|
|||||||
* for more details.
|
* for more details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef TEST_RUNNER_H_
|
#include "test_suite.h"
|
||||||
#define TEST_RUNNER_H_
|
|
||||||
|
|
||||||
#include <check.h>
|
#include <plugins/plugin_feature.h>
|
||||||
|
|
||||||
Suite *bio_reader_suite_create();
|
typedef struct test_configuration_t test_configuration_t;
|
||||||
Suite *bio_writer_suite_create();
|
|
||||||
Suite *chunk_suite_create();
|
|
||||||
Suite *enum_suite_create();
|
|
||||||
Suite *enumerator_suite_create();
|
|
||||||
Suite *linked_list_suite_create();
|
|
||||||
Suite *linked_list_enumerator_suite_create();
|
|
||||||
Suite *hashtable_suite_create();
|
|
||||||
Suite *array_suite_create();
|
|
||||||
Suite *identification_suite_create();
|
|
||||||
Suite *threading_suite_create();
|
|
||||||
Suite *utils_suite_create();
|
|
||||||
Suite *vectors_suite_create();
|
|
||||||
Suite *ecdsa_suite_create();
|
|
||||||
Suite *rsa_suite_create();
|
|
||||||
Suite *host_suite_create();
|
|
||||||
Suite *printf_suite_create();
|
|
||||||
Suite *pen_suite_create();
|
|
||||||
Suite *asn1_suite_create();
|
|
||||||
|
|
||||||
#endif /** TEST_RUNNER_H_ */
|
/**
|
||||||
|
* Callback called before and after each test case to de-/initialize the
|
||||||
|
* environment (e.g. to load plugins). It is also called before and after the
|
||||||
|
* test suites are loaded.
|
||||||
|
*
|
||||||
|
* It is called after libstrongswan has been initialized and likewise before it
|
||||||
|
* gets deinitialized.
|
||||||
|
*
|
||||||
|
* @param init TRUE during initialization
|
||||||
|
* @return FALSE if de-/init failed
|
||||||
|
*/
|
||||||
|
typedef bool (*test_runner_init_t)(bool init);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test configuration, suite constructor with plugin dependency
|
||||||
|
*/
|
||||||
|
struct test_configuration_t {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor function to create suite.
|
||||||
|
*/
|
||||||
|
test_suite_t *(*suite)();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plugin feature this test suite depends on
|
||||||
|
*/
|
||||||
|
plugin_feature_t feature;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run test configuration.
|
||||||
|
*
|
||||||
|
* The configs array must be terminated with a NULL element.
|
||||||
|
*
|
||||||
|
* @param configs test suite constructors with dependencies
|
||||||
|
* @param init_cb init/deinit callback
|
||||||
|
* @return test result, EXIT_SUCCESS if all tests passed
|
||||||
|
*/
|
||||||
|
int test_runner_run(test_configuration_t config[], test_runner_init_t init_cb);
|
||||||
|
|||||||
@@ -0,0 +1,277 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "test_suite.h"
|
||||||
|
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <pthread.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failure message buf
|
||||||
|
*/
|
||||||
|
static char failure_buf[512];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Source file failure occured
|
||||||
|
*/
|
||||||
|
static const char *failure_file;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Line of source file failure occured
|
||||||
|
*/
|
||||||
|
static int failure_line;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backtrace of failure, if any
|
||||||
|
*/
|
||||||
|
static backtrace_t *failure_backtrace;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Longjump restore point when failing
|
||||||
|
*/
|
||||||
|
sigjmp_buf test_restore_point_env;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
test_suite_t* test_suite_create(const char *name)
|
||||||
|
{
|
||||||
|
test_suite_t *suite;
|
||||||
|
|
||||||
|
INIT(suite,
|
||||||
|
.name = name,
|
||||||
|
.tcases = array_create(0, 0),
|
||||||
|
);
|
||||||
|
return suite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
test_case_t* test_case_create(const char *name)
|
||||||
|
{
|
||||||
|
test_case_t *tcase;
|
||||||
|
|
||||||
|
INIT(tcase,
|
||||||
|
.name = name,
|
||||||
|
.functions = array_create(sizeof(test_function_t), 0),
|
||||||
|
.fixtures = array_create(sizeof(test_fixture_t), 0),
|
||||||
|
.timeout = TEST_FUNCTION_DEFAULT_TIMEOUT,
|
||||||
|
);
|
||||||
|
return tcase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
void test_case_add_checked_fixture(test_case_t *tcase, test_fixture_cb_t setup,
|
||||||
|
test_fixture_cb_t teardown)
|
||||||
|
{
|
||||||
|
test_fixture_t fixture = {
|
||||||
|
.setup = setup,
|
||||||
|
.teardown = teardown,
|
||||||
|
};
|
||||||
|
array_insert(tcase->fixtures, -1, &fixture);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
void test_case_add_test_name(test_case_t *tcase, char *name,
|
||||||
|
test_function_cb_t cb, int start, int end)
|
||||||
|
{
|
||||||
|
test_function_t fun = {
|
||||||
|
.name = name,
|
||||||
|
.cb = cb,
|
||||||
|
.start = start,
|
||||||
|
.end = end,
|
||||||
|
};
|
||||||
|
array_insert(tcase->functions, -1, &fun);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
void test_case_set_timeout(test_case_t *tcase, int s)
|
||||||
|
{
|
||||||
|
tcase->timeout = s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
void test_suite_add_case(test_suite_t *suite, test_case_t *tcase)
|
||||||
|
{
|
||||||
|
array_insert(suite->tcases, -1, tcase);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
|
/* how can we stop just the thread? longjmp to a restore point? */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Signal handler catching critical and alarm signals
|
||||||
|
*/
|
||||||
|
static void test_sighandler(int signal)
|
||||||
|
{
|
||||||
|
char *signame;
|
||||||
|
bool old = FALSE;
|
||||||
|
|
||||||
|
switch (signal)
|
||||||
|
{
|
||||||
|
case SIGUSR1:
|
||||||
|
/* a different thread failed, abort test */
|
||||||
|
return test_failure();
|
||||||
|
case SIGSEGV:
|
||||||
|
signame = "SIGSEGV";
|
||||||
|
break;
|
||||||
|
case SIGILL:
|
||||||
|
signame = "SIGILL";
|
||||||
|
break;
|
||||||
|
case SIGBUS:
|
||||||
|
signame = "SIGBUS";
|
||||||
|
break;
|
||||||
|
case SIGALRM:
|
||||||
|
signame = "timeout";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
signame = "SIG";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (lib->leak_detective)
|
||||||
|
{
|
||||||
|
old = lib->leak_detective->set_state(lib->leak_detective, FALSE);
|
||||||
|
}
|
||||||
|
failure_backtrace = backtrace_create(3);
|
||||||
|
if (lib->leak_detective)
|
||||||
|
{
|
||||||
|
lib->leak_detective->set_state(lib->leak_detective, old);
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
void test_setup_handler()
|
||||||
|
{
|
||||||
|
struct sigaction action = {
|
||||||
|
.sa_handler = test_sighandler,
|
||||||
|
};
|
||||||
|
|
||||||
|
main_thread = pthread_self();
|
||||||
|
|
||||||
|
/* signal handler inherited by all threads */
|
||||||
|
sigaction(SIGSEGV, &action, NULL);
|
||||||
|
sigaction(SIGILL, &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(SIGUSR1, &action, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
int test_failure_get(char *msg, int len, const char **file)
|
||||||
|
{
|
||||||
|
strncpy(msg, failure_buf, len - 1);
|
||||||
|
msg[len - 1] = 0;
|
||||||
|
*file = failure_file;
|
||||||
|
return failure_line;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* See header.
|
||||||
|
*/
|
||||||
|
backtrace_t *test_failure_backtrace()
|
||||||
|
{
|
||||||
|
backtrace_t *bt;
|
||||||
|
|
||||||
|
bt = failure_backtrace;
|
||||||
|
failure_backtrace = NULL;
|
||||||
|
|
||||||
|
return bt;
|
||||||
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (C) 2013 Tobias Brunner
|
* Copyright (C) 2013 Tobias Brunner
|
||||||
* Hochschule fuer Technik Rapperswil
|
* Hochschule fuer Technik Rapperswil
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
*
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
* under the terms of the GNU General Public License as published by the
|
* under the terms of the GNU General Public License as published by the
|
||||||
@@ -16,95 +18,312 @@
|
|||||||
#ifndef TEST_UTILS_H_
|
#ifndef TEST_UTILS_H_
|
||||||
#define TEST_UTILS_H_
|
#define TEST_UTILS_H_
|
||||||
|
|
||||||
#include <check.h>
|
#define _GNU_SOURCE
|
||||||
|
#include <setjmp.h>
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
#include <utils/debug.h>
|
#include <utils/debug.h>
|
||||||
|
#include <utils/backtrace.h>
|
||||||
|
#include <collections/array.h>
|
||||||
|
|
||||||
|
typedef struct test_suite_t test_suite_t;
|
||||||
|
typedef struct test_case_t test_case_t;
|
||||||
|
typedef struct test_function_t test_function_t;
|
||||||
|
typedef struct test_fixture_t test_fixture_t;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to mark test cases that use test fixtures.
|
* Default timeout for a single test function
|
||||||
*/
|
*/
|
||||||
#define UNIT_TEST_FIXTURE_USED "UNIT_TEST_FIXTURE_USED"
|
#define TEST_FUNCTION_DEFAULT_TIMEOUT 2
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check for memory leaks and fail if any are encountered.
|
* Test function implementation
|
||||||
*/
|
*/
|
||||||
#define CHECK_FOR_LEAKS() do \
|
typedef void (*test_function_cb_t)(int);
|
||||||
{ \
|
|
||||||
if (lib->leak_detective) \
|
|
||||||
{ \
|
|
||||||
if (lib->leak_detective->leaks(lib->leak_detective)) { \
|
|
||||||
lib->leak_detective->report(lib->leak_detective, TRUE); \
|
|
||||||
} \
|
|
||||||
ck_assert_int_eq(lib->leak_detective->leaks(lib->leak_detective), 0); \
|
|
||||||
} \
|
|
||||||
} \
|
|
||||||
while(0)
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extended versions of the START|END_TEST macros that use leak detective.
|
* Fixture for a test case.
|
||||||
|
*/
|
||||||
|
typedef void (*test_fixture_cb_t)(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test suite; a collection of test cases with fixtures
|
||||||
|
*/
|
||||||
|
struct test_suite_t {
|
||||||
|
/** name of the test suite */
|
||||||
|
const char *name;
|
||||||
|
/** test cases registered, as test_case_t* */
|
||||||
|
array_t *tcases;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test case; multiple test functions using the same fixtures
|
||||||
|
*/
|
||||||
|
struct test_case_t {
|
||||||
|
/** name of the test case */
|
||||||
|
const char *name;
|
||||||
|
/** tests registered, as test_function_t */
|
||||||
|
array_t *functions;
|
||||||
|
/** fixture for tests, as test_fixture_t */
|
||||||
|
array_t *fixtures;
|
||||||
|
/** timeout for each function, in s */
|
||||||
|
int timeout;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A test function, with optional loop setup
|
||||||
|
*/
|
||||||
|
struct test_function_t {
|
||||||
|
/** name of test function */
|
||||||
|
char *name;
|
||||||
|
/** tests function registered, test_function_t* */
|
||||||
|
test_function_cb_t cb;
|
||||||
|
/** start for loop test */
|
||||||
|
int start;
|
||||||
|
/** end for loop test */
|
||||||
|
int end;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered fixture for a test case
|
||||||
|
*/
|
||||||
|
struct test_fixture_t {
|
||||||
|
test_fixture_cb_t setup;
|
||||||
|
test_fixture_cb_t teardown;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new test suite
|
||||||
*
|
*
|
||||||
* Since each test case runs in its own fork of the test runner the stuff
|
* @param name name of the test suite
|
||||||
* allocated before the test starts is not freed, so leak detective is disabled
|
* @return test suite
|
||||||
* by default to prevent false positives. By enabling it right when the test
|
*/
|
||||||
* starts we at least capture leaks created by the tested objects/functions and
|
test_suite_t* test_suite_create(const char *name);
|
||||||
* the test case itself. This allows writing test cases for cleanup functions.
|
|
||||||
|
/**
|
||||||
|
* Create a new test case
|
||||||
*
|
*
|
||||||
* To define test fixture with possibly allocated/destroyed memory that is
|
* @param name name of test case
|
||||||
* allocated/freed in a test case use the START|END_SETUP|TEARDOWN macros.
|
* @return test case
|
||||||
*/
|
*/
|
||||||
#undef START_TEST
|
test_case_t* test_case_create(const char *name);
|
||||||
#define START_TEST(name) \
|
|
||||||
static void name (int _i CK_ATTRIBUTE_UNUSED) \
|
|
||||||
{ \
|
|
||||||
tcase_fn_start(""#name, __FILE__, __LINE__); \
|
|
||||||
dbg_default_set_level(LEVEL_SILENT); \
|
|
||||||
if (lib->leak_detective) \
|
|
||||||
{ \
|
|
||||||
lib->leak_detective->set_state(lib->leak_detective, TRUE); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#undef END_TEST
|
/**
|
||||||
#define END_TEST \
|
* Add a setup/teardown function to the test case
|
||||||
if (!lib->get(lib, UNIT_TEST_FIXTURE_USED)) \
|
*
|
||||||
|
* @param tcase test case to add a fixture to
|
||||||
|
* @param setup setup function called before each test
|
||||||
|
* @param teardown cleanup function called after each test
|
||||||
|
*/
|
||||||
|
void test_case_add_checked_fixture(test_case_t *tcase, test_fixture_cb_t setup,
|
||||||
|
test_fixture_cb_t teardown);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a test function to a test case, with a name, looped several times
|
||||||
|
*
|
||||||
|
* @param name name of the test case
|
||||||
|
* @param tcase test case to add test function to
|
||||||
|
* @param cb callback function to invoke for test
|
||||||
|
* @param start start of loop counter
|
||||||
|
* @param end end of loop counter
|
||||||
|
*/
|
||||||
|
void test_case_add_test_name(test_case_t *tcase, char *name,
|
||||||
|
test_function_cb_t cb, int start, int end);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a test function to a test case
|
||||||
|
*
|
||||||
|
* @param tcase test case to add test function to
|
||||||
|
* @param cb callback function to invoke for test
|
||||||
|
*/
|
||||||
|
#define test_case_add_test(tcase, cb) \
|
||||||
|
test_case_add_test_name(tcase, #cb, cb, 0, 1)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a test function to a test case, looped several times
|
||||||
|
*
|
||||||
|
* @param tcase test case to add test function to
|
||||||
|
* @param cb callback function to invoke for test
|
||||||
|
* @param start start of loop counter
|
||||||
|
* @param end end of loop counter
|
||||||
|
*/
|
||||||
|
#define test_case_add_loop_test(tcase, cb, start, end) \
|
||||||
|
test_case_add_test_name(tcase, #cb, cb, start, end)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a custom timeout for test functions in a test case
|
||||||
|
*
|
||||||
|
* @param tcase test case to set timeout for
|
||||||
|
* @param s test timeout in s
|
||||||
|
*/
|
||||||
|
void test_case_set_timeout(test_case_t *tcase, int s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add a test function to a test case, looped several times
|
||||||
|
*
|
||||||
|
* @param tcase test case to add test function to
|
||||||
|
* @param cb callback function to invoke for test
|
||||||
|
* @param start start of loop counter
|
||||||
|
* @param end end of loop counter
|
||||||
|
*/
|
||||||
|
void test_suite_add_case(test_suite_t *suite, test_case_t *tcase);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sigjmp restore point used by test_restore_point
|
||||||
|
*/
|
||||||
|
extern sigjmp_buf test_restore_point_env;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set or return from an execution restore point
|
||||||
|
*
|
||||||
|
* This call sets a restore execution point and returns TRUE after it has
|
||||||
|
* been set up. On test failure, the execution is returned to the restore point
|
||||||
|
* and FALSE is returned to indicate test failure.
|
||||||
|
*
|
||||||
|
* @return TRUE if restore point set, FALSE when restored
|
||||||
|
*/
|
||||||
|
#define test_restore_point() (sigsetjmp(test_restore_point_env, 1) == 0)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up signal handlers for test cases
|
||||||
|
*/
|
||||||
|
void test_setup_handler();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up a timeout to let a test fail
|
||||||
|
*
|
||||||
|
* @param s timeout, 0 to disable timeout
|
||||||
|
*/
|
||||||
|
void test_setup_timeout(int s);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get info about a test failure
|
||||||
|
*
|
||||||
|
* @param msg buffer receiving failure info
|
||||||
|
* @param len size of msg buffer
|
||||||
|
* @param file pointer receiving source code file
|
||||||
|
* @return source code line number
|
||||||
|
*/
|
||||||
|
int test_failure_get(char *msg, int len, const char **file);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a backtrace for a failure.
|
||||||
|
*
|
||||||
|
* @return allocated backtrace of test failure, if any
|
||||||
|
*/
|
||||||
|
backtrace_t *test_failure_backtrace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let a test fail and set a message using vprintf style arguments.
|
||||||
|
*
|
||||||
|
* @param file source code file name
|
||||||
|
* @param line source code line number
|
||||||
|
* @param fmt printf format string
|
||||||
|
* @param args argument list for fmt
|
||||||
|
*/
|
||||||
|
void test_fail_vmsg(const char *file, int line, char *fmt, va_list args);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Let a test fail and set a message using printf style arguments.
|
||||||
|
*
|
||||||
|
* @param file source code file name
|
||||||
|
* @param line source code line number
|
||||||
|
* @param fmt printf format string
|
||||||
|
* @param ... arguments for fmt
|
||||||
|
*/
|
||||||
|
void test_fail_msg(const char *file, int line, char *fmt, ...);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if two integers equal, fail test if not
|
||||||
|
*
|
||||||
|
* @param a first integer
|
||||||
|
* @param b second integer
|
||||||
|
*/
|
||||||
|
#define test_int_eq(a, b) \
|
||||||
|
({ \
|
||||||
|
typeof(a) _a = a; \
|
||||||
|
typeof(b) _b = b; \
|
||||||
|
if (_a != _b) \
|
||||||
{ \
|
{ \
|
||||||
CHECK_FOR_LEAKS(); \
|
test_fail_msg(__FILE__, __LINE__, #a " != " #b " (%d != %d)", _a, _b); \
|
||||||
} \
|
} \
|
||||||
}
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Define a function to setup a test fixture that can be used with the above
|
* Check if two strings equal, fail test if not
|
||||||
* macros.
|
*
|
||||||
|
* @param a first string
|
||||||
|
* @param b second string
|
||||||
*/
|
*/
|
||||||
#define START_SETUP(name) \
|
#define test_str_eq(a, b) \
|
||||||
static void name() \
|
({ \
|
||||||
{ \
|
char* _a = (char*)a; \
|
||||||
lib->set(lib, UNIT_TEST_FIXTURE_USED, (void*)TRUE); \
|
char* _b = (char*)b; \
|
||||||
if (lib->leak_detective) \
|
if (!_a || !_b || !streq(_a, _b)) \
|
||||||
{ \
|
{ \
|
||||||
lib->leak_detective->set_state(lib->leak_detective, TRUE); \
|
test_fail_msg(__FILE__, __LINE__, \
|
||||||
}
|
#a " != " #b " (\"%s\" != \"%s\")", _a, _b); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* End a setup function
|
* Check if a statement evaluates to TRUE, fail test if not
|
||||||
|
*
|
||||||
|
* @param x statement to evaluate
|
||||||
*/
|
*/
|
||||||
|
#define test_assert(x) \
|
||||||
|
({ \
|
||||||
|
if (!(x)) \
|
||||||
|
{ \
|
||||||
|
test_fail_msg(__FILE__, __LINE__, #x); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a statement evaluates to TRUE, fail and print a message if not
|
||||||
|
*
|
||||||
|
* @param x statement to evaluate
|
||||||
|
* @param fmt message format string
|
||||||
|
* @param ... fmt printf arguments
|
||||||
|
*/
|
||||||
|
#define test_assert_msg(x, fmt, ...) \
|
||||||
|
({ \
|
||||||
|
if (!(x)) \
|
||||||
|
{ \
|
||||||
|
test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* "check unit testing" compatibility */
|
||||||
|
#define Suite test_suite_t
|
||||||
|
#define TCase test_case_t
|
||||||
|
#define ck_assert_int_eq test_int_eq
|
||||||
|
#define ck_assert test_assert
|
||||||
|
#define ck_assert_msg test_assert_msg
|
||||||
|
#define ck_assert_str_eq test_str_eq
|
||||||
|
#define fail(fmt, ...) test_fail_msg(__FILE__, __LINE__, fmt, ##__VA_ARGS__)
|
||||||
|
#define fail_if(x, fmt, ...) \
|
||||||
|
({ \
|
||||||
|
if (x) \
|
||||||
|
{ \
|
||||||
|
test_fail_msg(__FILE__, __LINE__, #x ": " fmt, ##__VA_ARGS__); \
|
||||||
|
} \
|
||||||
|
})
|
||||||
|
#define fail_unless test_assert_msg
|
||||||
|
#define suite_create test_suite_create
|
||||||
|
#define tcase_create test_case_create
|
||||||
|
#define tcase_add_checked_fixture test_case_add_checked_fixture
|
||||||
|
#define tcase_add_test test_case_add_test
|
||||||
|
#define tcase_add_loop_test test_case_add_loop_test
|
||||||
|
#define tcase_set_timeout test_case_set_timeout
|
||||||
|
#define suite_add_tcase test_suite_add_case
|
||||||
|
#define START_TEST(name) static void name (int _i) {
|
||||||
|
#define END_TEST }
|
||||||
|
#define START_SETUP(name) static void name() {
|
||||||
#define END_SETUP }
|
#define END_SETUP }
|
||||||
|
#define START_TEARDOWN(name) static void name() {
|
||||||
/**
|
#define END_TEARDOWN }
|
||||||
* Define a function to teardown a test fixture that can be used with the above
|
|
||||||
* macros.
|
|
||||||
*/
|
|
||||||
#define START_TEARDOWN(name) \
|
|
||||||
static void name() \
|
|
||||||
{
|
|
||||||
|
|
||||||
/**
|
|
||||||
* End a teardown function
|
|
||||||
*/
|
|
||||||
#define END_TEARDOWN \
|
|
||||||
if (lib->get(lib, UNIT_TEST_FIXTURE_USED)) \
|
|
||||||
{ \
|
|
||||||
CHECK_FOR_LEAKS(); \
|
|
||||||
} \
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif /** TEST_UTILS_H_ */
|
#endif /** TEST_UTILS_H_ */
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2013 Tobias Brunner
|
|
||||||
* Copyright (C) 2008 Martin Willi
|
|
||||||
* Hochschule fuer Technik Rapperswil
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation; either version 2 of the License, or (at your
|
|
||||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
||||||
* for more details.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <sched.h>
|
|
||||||
#include <pthread.h>
|
|
||||||
|
|
||||||
#include "test_suite.h"
|
|
||||||
|
|
||||||
#include <threading/mutex.h>
|
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* recursive mutex test
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define THREADS 20
|
|
||||||
|
|
||||||
static mutex_t *mutex;
|
|
||||||
|
|
||||||
static pthread_barrier_t mutex_barrier;
|
|
||||||
|
|
||||||
static int mutex_locked = 0;
|
|
||||||
|
|
||||||
static void *mutex_run(void *data)
|
|
||||||
{
|
|
||||||
int i;
|
|
||||||
|
|
||||||
/* wait for all threads before getting in action */
|
|
||||||
pthread_barrier_wait(&mutex_barrier);
|
|
||||||
|
|
||||||
for (i = 0; i < 100; i++)
|
|
||||||
{
|
|
||||||
mutex->lock(mutex);
|
|
||||||
mutex->lock(mutex);
|
|
||||||
mutex->lock(mutex);
|
|
||||||
mutex_locked++;
|
|
||||||
sched_yield();
|
|
||||||
if (mutex_locked > 1)
|
|
||||||
{
|
|
||||||
fail("two threads locked the mutex concurrently");
|
|
||||||
}
|
|
||||||
mutex_locked--;
|
|
||||||
mutex->unlock(mutex);
|
|
||||||
mutex->unlock(mutex);
|
|
||||||
mutex->unlock(mutex);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
START_TEST(test_mutex)
|
|
||||||
{
|
|
||||||
pthread_t threads[THREADS];
|
|
||||||
int i;
|
|
||||||
|
|
||||||
mutex = mutex_create(MUTEX_TYPE_RECURSIVE);
|
|
||||||
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
mutex->lock(mutex);
|
|
||||||
mutex->unlock(mutex);
|
|
||||||
}
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
mutex->lock(mutex);
|
|
||||||
}
|
|
||||||
for (i = 0; i < 10; i++)
|
|
||||||
{
|
|
||||||
mutex->unlock(mutex);
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_barrier_init(&mutex_barrier, NULL, THREADS);
|
|
||||||
for (i = 0; i < THREADS; i++)
|
|
||||||
{
|
|
||||||
pthread_create(&threads[i], NULL, mutex_run, NULL);
|
|
||||||
}
|
|
||||||
for (i = 0; i < THREADS; i++)
|
|
||||||
{
|
|
||||||
pthread_join(threads[i], NULL);
|
|
||||||
}
|
|
||||||
pthread_barrier_destroy(&mutex_barrier);
|
|
||||||
|
|
||||||
mutex->destroy(mutex);
|
|
||||||
}
|
|
||||||
END_TEST
|
|
||||||
|
|
||||||
Suite *threading_suite_create()
|
|
||||||
{
|
|
||||||
Suite *s;
|
|
||||||
TCase *tc;
|
|
||||||
|
|
||||||
s = suite_create("threading");
|
|
||||||
|
|
||||||
tc = tcase_create("recursive mutex");
|
|
||||||
tcase_add_test(tc, test_mutex);
|
|
||||||
suite_add_tcase(s, tc);
|
|
||||||
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Martin Willi
|
||||||
|
* Copyright (C) 2013 revosec AG
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <test_runner.h>
|
||||||
|
|
||||||
|
/* declare test suite constructors */
|
||||||
|
#define TEST_SUITE(x) test_suite_t* x();
|
||||||
|
#define TEST_SUITE_DEPEND(x, ...) TEST_SUITE(x)
|
||||||
|
#include "tests.h"
|
||||||
|
#undef TEST_SUITE
|
||||||
|
#undef TEST_SUITE_DEPEND
|
||||||
|
|
||||||
|
static test_configuration_t tests[] = {
|
||||||
|
#define TEST_SUITE(x) \
|
||||||
|
{ .suite = x, },
|
||||||
|
#define TEST_SUITE_DEPEND(x, type, args) \
|
||||||
|
{ .suite = x, .feature = PLUGIN_DEPENDS(type, args) },
|
||||||
|
#include "tests.h"
|
||||||
|
{ .suite = NULL, }
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool test_runner_init(bool init)
|
||||||
|
{
|
||||||
|
if (init)
|
||||||
|
{
|
||||||
|
plugin_loader_add_plugindirs(PLUGINDIR, PLUGINS);
|
||||||
|
if (!lib->plugins->load(lib->plugins, PLUGINS))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lib->processor->set_threads(lib->processor, 0);
|
||||||
|
lib->processor->cancel(lib->processor);
|
||||||
|
lib->plugins->unload(lib->plugins);
|
||||||
|
}
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
return test_runner_run(tests, test_runner_init);
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Tobias Brunner
|
||||||
|
* Hochschule fuer Technik Rapperswil
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation; either version 2 of the License, or (at your
|
||||||
|
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||||
|
* for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
TEST_SUITE(bio_reader_suite_create)
|
||||||
|
TEST_SUITE(bio_writer_suite_create)
|
||||||
|
TEST_SUITE(chunk_suite_create)
|
||||||
|
TEST_SUITE(enum_suite_create)
|
||||||
|
TEST_SUITE(enumerator_suite_create)
|
||||||
|
TEST_SUITE(linked_list_suite_create)
|
||||||
|
TEST_SUITE(linked_list_enumerator_suite_create)
|
||||||
|
TEST_SUITE(hashtable_suite_create)
|
||||||
|
TEST_SUITE(array_suite_create)
|
||||||
|
TEST_SUITE(identification_suite_create)
|
||||||
|
TEST_SUITE(threading_suite_create)
|
||||||
|
TEST_SUITE(watcher_suite_create)
|
||||||
|
TEST_SUITE(stream_suite_create)
|
||||||
|
TEST_SUITE(utils_suite_create)
|
||||||
|
TEST_SUITE(vectors_suite_create)
|
||||||
|
TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA)
|
||||||
|
TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA)
|
||||||
|
TEST_SUITE(host_suite_create)
|
||||||
|
TEST_SUITE(printf_suite_create)
|
||||||
|
TEST_SUITE(pen_suite_create)
|
||||||
|
TEST_SUITE(asn1_suite_create)
|
||||||
@@ -71,7 +71,6 @@ typedef void *(*thread_main_t)(void *arg);
|
|||||||
*/
|
*/
|
||||||
typedef void (*thread_cleanup_t)(void *arg);
|
typedef void (*thread_cleanup_t)(void *arg);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Thread wrapper implements simple, portable and advanced thread functions.
|
* Thread wrapper implements simple, portable and advanced thread functions.
|
||||||
*
|
*
|
||||||
@@ -110,10 +109,8 @@ struct thread_t {
|
|||||||
* a call to exit.
|
* a call to exit.
|
||||||
*/
|
*/
|
||||||
void *(*join)(thread_t *this);
|
void *(*join)(thread_t *this);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new thread instance.
|
* Create a new thread instance.
|
||||||
*
|
*
|
||||||
@@ -168,6 +165,10 @@ bool thread_cancelability(bool enable);
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Force creation of a cancellation point in the calling thread.
|
* Force creation of a cancellation point in the calling thread.
|
||||||
|
*
|
||||||
|
* This temporarily enables thread cancelability, tests for a pending
|
||||||
|
* cancellation request and then disables cancelability again if it was
|
||||||
|
* disabled before the call to thread_cancellation_point().
|
||||||
*/
|
*/
|
||||||
void thread_cancellation_point();
|
void thread_cancellation_point();
|
||||||
|
|
||||||
@@ -188,6 +189,4 @@ void threads_init();
|
|||||||
*/
|
*/
|
||||||
void threads_deinit();
|
void threads_deinit();
|
||||||
|
|
||||||
|
|
||||||
#endif /** THREADING_THREAD_H_ @} */
|
#endif /** THREADING_THREAD_H_ @} */
|
||||||
|
|
||||||
|
|||||||
@@ -314,7 +314,7 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
|
|||||||
bool old = FALSE;
|
bool old = FALSE;
|
||||||
|
|
||||||
bfd_mutex->lock(bfd_mutex);
|
bfd_mutex->lock(bfd_mutex);
|
||||||
if (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);
|
||||||
}
|
}
|
||||||
@@ -324,7 +324,7 @@ static void print_sourceline(FILE *file, char *filename, void *ptr, void *base)
|
|||||||
data.entry = entry;
|
data.entry = entry;
|
||||||
bfd_map_over_sections(entry->abfd, (void*)find_addr, &data);
|
bfd_map_over_sections(entry->abfd, (void*)find_addr, &data);
|
||||||
}
|
}
|
||||||
if (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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,12 +21,12 @@
|
|||||||
#ifndef BACKTRACE_H_
|
#ifndef BACKTRACE_H_
|
||||||
#define BACKTRACE_H_
|
#define BACKTRACE_H_
|
||||||
|
|
||||||
|
typedef struct backtrace_t backtrace_t;
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
|
|
||||||
typedef struct backtrace_t backtrace_t;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A backtrace registers the frames on the stack during creation.
|
* A backtrace registers the frames on the stack during creation.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -59,6 +59,21 @@ struct private_leak_detective_t {
|
|||||||
* public functions
|
* public functions
|
||||||
*/
|
*/
|
||||||
leak_detective_t public;
|
leak_detective_t public;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered report() function
|
||||||
|
*/
|
||||||
|
leak_detective_report_cb_t report_cb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered report() summary function
|
||||||
|
*/
|
||||||
|
leak_detective_summary_cb_t report_scb;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registered user data for callbacks
|
||||||
|
*/
|
||||||
|
void *report_data;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -318,9 +333,16 @@ HOOK(size_t, size, const void *ptr)
|
|||||||
*/
|
*/
|
||||||
static bool register_hooks()
|
static bool register_hooks()
|
||||||
{
|
{
|
||||||
|
static bool once = FALSE;
|
||||||
malloc_zone_t *zone;
|
malloc_zone_t *zone;
|
||||||
void *page;
|
void *page;
|
||||||
|
|
||||||
|
if (once)
|
||||||
|
{
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
once = TRUE;
|
||||||
|
|
||||||
zone = malloc_default_zone();
|
zone = malloc_default_zone();
|
||||||
if (zone->version != MALLOC_ZONE_VERSION)
|
if (zone->version != MALLOC_ZONE_VERSION)
|
||||||
{
|
{
|
||||||
@@ -565,7 +587,12 @@ char *whitelist[] = {
|
|||||||
*/
|
*/
|
||||||
static void init_static_allocations()
|
static void init_static_allocations()
|
||||||
{
|
{
|
||||||
|
struct tm tm;
|
||||||
|
time_t t = 0;
|
||||||
|
|
||||||
tzset();
|
tzset();
|
||||||
|
gmtime_r(&t, &tm);
|
||||||
|
localtime_r(&t, &tm);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -599,7 +626,8 @@ static bool equals(backtrace_t *a, backtrace_t *b)
|
|||||||
* Summarize and print backtraces
|
* Summarize and print backtraces
|
||||||
*/
|
*/
|
||||||
static int print_traces(private_leak_detective_t *this,
|
static int print_traces(private_leak_detective_t *this,
|
||||||
FILE *out, int thresh, int thresh_count,
|
leak_detective_report_cb_t cb, void *user,
|
||||||
|
int thresh, int thresh_count,
|
||||||
bool detailed, int *whitelisted, size_t *sum)
|
bool detailed, int *whitelisted, size_t *sum)
|
||||||
{
|
{
|
||||||
int leaks = 0;
|
int leaks = 0;
|
||||||
@@ -652,16 +680,20 @@ static int print_traces(private_leak_detective_t *this,
|
|||||||
leaks++;
|
leaks++;
|
||||||
}
|
}
|
||||||
lock->unlock(lock);
|
lock->unlock(lock);
|
||||||
|
|
||||||
enumerator = entries->create_enumerator(entries);
|
enumerator = entries->create_enumerator(entries);
|
||||||
while (enumerator->enumerate(enumerator, NULL, &entry))
|
while (enumerator->enumerate(enumerator, NULL, &entry))
|
||||||
{
|
{
|
||||||
if (out &&
|
if (cb)
|
||||||
(!thresh || entry->bytes >= thresh) &&
|
|
||||||
(!thresh_count || entry->count >= thresh_count))
|
|
||||||
{
|
{
|
||||||
fprintf(out, "%d bytes total, %d allocations, %d bytes average:\n",
|
if (!thresh || entry->bytes >= thresh)
|
||||||
entry->bytes, entry->count, entry->bytes / entry->count);
|
{
|
||||||
entry->backtrace->log(entry->backtrace, out, detailed);
|
if (!thresh_count || entry->count >= thresh_count)
|
||||||
|
{
|
||||||
|
this->report_cb(this->report_data, entry->count,
|
||||||
|
entry->bytes, entry->backtrace, detailed);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
entry->backtrace->destroy(entry->backtrace);
|
entry->backtrace->destroy(entry->backtrace);
|
||||||
free(entry);
|
free(entry);
|
||||||
@@ -681,38 +713,30 @@ METHOD(leak_detective_t, report, void,
|
|||||||
int leaks, whitelisted = 0;
|
int leaks, whitelisted = 0;
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
|
|
||||||
leaks = print_traces(this, stderr, 0, 0, detailed, &whitelisted, &sum);
|
leaks = print_traces(this, this->report_cb, this->report_data,
|
||||||
switch (leaks)
|
0, 0, detailed, &whitelisted, &sum);
|
||||||
|
if (this->report_scb)
|
||||||
{
|
{
|
||||||
case 0:
|
this->report_scb(this->report_data, leaks, sum, whitelisted);
|
||||||
fprintf(stderr, "No leaks detected");
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
fprintf(stderr, "One leak detected");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
fprintf(stderr, "%d leaks detected, %zu bytes", leaks, sum);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
fprintf(stderr, ", %d suppressed by whitelist\n", whitelisted);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fprintf(stderr, "Leak detective disabled\n");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
METHOD(leak_detective_t, set_report_cb, void,
|
||||||
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user)
|
||||||
|
{
|
||||||
|
this->report_cb = cb;
|
||||||
|
this->report_scb = scb;
|
||||||
|
this->report_data = user;
|
||||||
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, leaks, int,
|
METHOD(leak_detective_t, leaks, int,
|
||||||
private_leak_detective_t *this)
|
private_leak_detective_t *this)
|
||||||
{
|
{
|
||||||
if (lib->leak_detective)
|
int whitelisted = 0;
|
||||||
{
|
|
||||||
int leaks, whitelisted = 0;
|
|
||||||
|
|
||||||
leaks = print_traces(this, NULL, 0, 0, FALSE, &whitelisted, NULL);
|
return print_traces(this, NULL, NULL, 0, 0, FALSE, &whitelisted, NULL);
|
||||||
return leaks;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, set_state, bool,
|
METHOD(leak_detective_t, set_state, bool,
|
||||||
@@ -722,10 +746,11 @@ METHOD(leak_detective_t, set_state, bool,
|
|||||||
}
|
}
|
||||||
|
|
||||||
METHOD(leak_detective_t, usage, void,
|
METHOD(leak_detective_t, usage, void,
|
||||||
private_leak_detective_t *this, FILE *out)
|
private_leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user)
|
||||||
{
|
{
|
||||||
bool detailed;
|
bool detailed;
|
||||||
int thresh, thresh_count;
|
int thresh, thresh_count, leaks, whitelisted = 0;
|
||||||
size_t sum = 0;
|
size_t sum = 0;
|
||||||
|
|
||||||
thresh = lib->settings->get_int(lib->settings,
|
thresh = lib->settings->get_int(lib->settings,
|
||||||
@@ -735,9 +760,12 @@ METHOD(leak_detective_t, usage, void,
|
|||||||
detailed = lib->settings->get_bool(lib->settings,
|
detailed = lib->settings->get_bool(lib->settings,
|
||||||
"libstrongswan.leak_detective.detailed", TRUE);
|
"libstrongswan.leak_detective.detailed", TRUE);
|
||||||
|
|
||||||
print_traces(this, out, thresh, thresh_count, detailed, NULL, &sum);
|
leaks = print_traces(this, cb, user, thresh, thresh_count,
|
||||||
|
detailed, &whitelisted, &sum);
|
||||||
fprintf(out, "Total memory usage: %zu\n", sum);
|
if (scb)
|
||||||
|
{
|
||||||
|
scb(user, leaks, sum, whitelisted);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -924,6 +952,7 @@ METHOD(leak_detective_t, destroy, void,
|
|||||||
lock->destroy(lock);
|
lock->destroy(lock);
|
||||||
thread_disabled->destroy(thread_disabled);
|
thread_disabled->destroy(thread_disabled);
|
||||||
free(this);
|
free(this);
|
||||||
|
first_header.next = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -936,8 +965,9 @@ leak_detective_t *leak_detective_create()
|
|||||||
INIT(this,
|
INIT(this,
|
||||||
.public = {
|
.public = {
|
||||||
.report = _report,
|
.report = _report,
|
||||||
.leaks = _leaks,
|
.set_report_cb = _set_report_cb,
|
||||||
.usage = _usage,
|
.usage = _usage,
|
||||||
|
.leaks = _leaks,
|
||||||
.set_state = _set_state,
|
.set_state = _set_state,
|
||||||
.destroy = _destroy,
|
.destroy = _destroy,
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -24,6 +24,30 @@
|
|||||||
typedef struct leak_detective_t leak_detective_t;
|
typedef struct leak_detective_t leak_detective_t;
|
||||||
|
|
||||||
#include <library.h>
|
#include <library.h>
|
||||||
|
#include <utils/backtrace.h>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to report leak/usage information
|
||||||
|
*
|
||||||
|
* @param user user specific data
|
||||||
|
* @param count number of allocations
|
||||||
|
* @param bytes total size of allocations
|
||||||
|
* @param bt backtrace of allocation
|
||||||
|
* @param detailed TRUE to show a detailed backtrace
|
||||||
|
*/
|
||||||
|
typedef void (*leak_detective_report_cb_t)(void *user, int count, size_t bytes,
|
||||||
|
backtrace_t *bt, bool detailed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback function to report leak/usage summary information
|
||||||
|
*
|
||||||
|
* @param user user specific data
|
||||||
|
* @param count total number of allocations
|
||||||
|
* @param bytes total size of all reported allocations
|
||||||
|
* @param whitelisted number of allocations suppressed by whitelist
|
||||||
|
*/
|
||||||
|
typedef void (*leak_detective_summary_cb_t)(void* user, int count, size_t bytes,
|
||||||
|
int whitelisted);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Leak detective finds leaks and bad frees using malloc hooks.
|
* Leak detective finds leaks and bad frees using malloc hooks.
|
||||||
@@ -36,12 +60,33 @@ typedef struct leak_detective_t leak_detective_t;
|
|||||||
struct leak_detective_t {
|
struct leak_detective_t {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Report leaks to stderr.
|
* Report leaks to the registered callback functions.
|
||||||
*
|
*
|
||||||
* @param detailed TRUE to resolve line/filename of leak (slow)
|
* @param detailed TRUE to resolve line/filename of leak (slow)
|
||||||
*/
|
*/
|
||||||
void (*report)(leak_detective_t *this, bool detailed);
|
void (*report)(leak_detective_t *this, bool detailed);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report current memory usage to out.
|
||||||
|
* Set callback functions invoked during a report().
|
||||||
|
*
|
||||||
|
* @param cb callback invoked for each detected leak
|
||||||
|
* @param scb summary callback invoked at end of report
|
||||||
|
* @param user user data to supply to callbacks
|
||||||
|
*/
|
||||||
|
void (*set_report_cb)(leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Report current memory usage using a callbacks.
|
||||||
|
*
|
||||||
|
* @param cb callback invoked for each allocation
|
||||||
|
* @param scb summary callback invoked at end of usage report
|
||||||
|
* @param user user data supplied to callbacks
|
||||||
|
*/
|
||||||
|
void (*usage)(leak_detective_t *this, leak_detective_report_cb_t cb,
|
||||||
|
leak_detective_summary_cb_t scb, void *user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Number of detected leaks.
|
* Number of detected leaks.
|
||||||
*
|
*
|
||||||
@@ -49,13 +94,6 @@ struct leak_detective_t {
|
|||||||
*/
|
*/
|
||||||
int (*leaks)(leak_detective_t *this);
|
int (*leaks)(leak_detective_t *this);
|
||||||
|
|
||||||
/**
|
|
||||||
* Report current memory usage to out.
|
|
||||||
*
|
|
||||||
* @param out target to write usage report to
|
|
||||||
*/
|
|
||||||
void (*usage)(leak_detective_t *this, FILE *out);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Enable/disable leak detective hooks for the current thread.
|
* Enable/disable leak detective hooks for the current thread.
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user