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:
commit
27467a6881
40 files changed
+3370
-342
No files matched your search
+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@
|
|
||||||
File renamed without changes.
@@ -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
|
||||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+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;
|
||||||
|
|
||||||
File renamed without changes.
File renamed without changes.
+33
-1
@@ -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,6 +29,10 @@ 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);
|
||||||
|
|
||||||
|
#ifdef HAVE_FMEMOPEN
|
||||||
|
{
|
||||||
|
FILE *mem;
|
||||||
|
|
||||||
mem = fmemopen(buf, sizeof(buf), "w");
|
mem = fmemopen(buf, sizeof(buf), "w");
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
vfprintf(mem, format, args);
|
vfprintf(mem, format, args);
|
||||||
@@ -36,12 +40,16 @@ static void verify(char *expected, char *format, ...)
|
|||||||
fclose(mem);
|
fclose(mem);
|
||||||
ck_assert_str_eq(expected, buf);
|
ck_assert_str_eq(expected, buf);
|
||||||
}
|
}
|
||||||
|
#endif /* HAVE_FMEMOPEN */
|
||||||
|
}
|
||||||
|
|
||||||
START_TEST(test_printf_strings)
|
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;
|
||||||
}
|
}
|
||||||
File renamed without changes.
@@ -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
File renamed without changes.
+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 FALSE;
|
||||||
|
}
|
||||||
|
dbg_default_set_level(LEVEL_SILENT);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Failure description
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
char *name;
|
||||||
|
char msg[512 - sizeof(char*) - 2 * sizeof(int)];
|
||||||
|
const char *file;
|
||||||
|
int line;
|
||||||
|
int i;
|
||||||
|
backtrace_t *bt;
|
||||||
|
} failure_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Data passed to leak report callbacks
|
||||||
|
*/
|
||||||
|
typedef struct {
|
||||||
|
array_t *failures;
|
||||||
|
char *name;
|
||||||
|
int i;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
init(FALSE);
|
||||||
|
}
|
||||||
|
if (check_leaks && lib->leak_detective)
|
||||||
|
{
|
||||||
|
lib->leak_detective->set_report_cb(lib->leak_detective,
|
||||||
|
(leak_detective_report_cb_t)report_leaks,
|
||||||
|
(leak_detective_summary_cb_t)sum_leaks, &data);
|
||||||
|
}
|
||||||
|
library_deinit();
|
||||||
|
|
||||||
|
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;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
lib->plugins->status(lib->plugins, LEVEL_CTRL);
|
|
||||||
|
|
||||||
sr = srunner_create(NULL);
|
fprintf(stderr, "Running %u test suites:\n", array_count(suites));
|
||||||
srunner_add_suite(sr, bio_reader_suite_create());
|
|
||||||
srunner_add_suite(sr, bio_writer_suite_create());
|
enumerator = array_create_enumerator(suites);
|
||||||
srunner_add_suite(sr, chunk_suite_create());
|
while (enumerator->enumerate(enumerator, &suite))
|
||||||
srunner_add_suite(sr, enum_suite_create());
|
|
||||||
srunner_add_suite(sr, enumerator_suite_create());
|
|
||||||
srunner_add_suite(sr, linked_list_suite_create());
|
|
||||||
srunner_add_suite(sr, linked_list_enumerator_suite_create());
|
|
||||||
srunner_add_suite(sr, hashtable_suite_create());
|
|
||||||
srunner_add_suite(sr, array_suite_create());
|
|
||||||
srunner_add_suite(sr, identification_suite_create());
|
|
||||||
srunner_add_suite(sr, threading_suite_create());
|
|
||||||
srunner_add_suite(sr, utils_suite_create());
|
|
||||||
srunner_add_suite(sr, host_suite_create());
|
|
||||||
srunner_add_suite(sr, vectors_suite_create());
|
|
||||||
srunner_add_suite(sr, printf_suite_create());
|
|
||||||
srunner_add_suite(sr, pen_suite_create());
|
|
||||||
srunner_add_suite(sr, asn1_suite_create());
|
|
||||||
if (lib->plugins->has_feature(lib->plugins,
|
|
||||||
PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_RSA)))
|
|
||||||
{
|
{
|
||||||
srunner_add_suite(sr, rsa_suite_create());
|
if (run_suite(suite, init))
|
||||||
}
|
|
||||||
if (lib->plugins->has_feature(lib->plugins,
|
|
||||||
PLUGIN_DEPENDS(PRIVKEY_GEN, KEY_ECDSA)))
|
|
||||||
{
|
{
|
||||||
srunner_add_suite(sr, ecdsa_suite_create());
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
srunner_run_all(sr, CK_NORMAL);
|
unload_suites(suites);
|
||||||
nf = srunner_ntests_failed(sr);
|
|
||||||
|
|
||||||
srunner_free(sr);
|
return result;
|
||||||
library_deinit();
|
|
||||||
|
|
||||||
return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE;
|
|
||||||
}
|
}
|
||||||
@@ -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; \
|
||||||
|
char* _b = (char*)b; \
|
||||||
|
if (!_a || !_b || !streq(_a, _b)) \
|
||||||
{ \
|
{ \
|
||||||
lib->set(lib, UNIT_TEST_FIXTURE_USED, (void*)TRUE); \
|
test_fail_msg(__FILE__, __LINE__, \
|
||||||
if (lib->leak_detective) \
|
#a " != " #b " (\"%s\" != \"%s\")", _a, _b); \
|
||||||
{ \
|
} \
|
||||||
lib->leak_detective->set_state(lib->leak_detective, TRUE); \
|
})
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
fprintf(stderr, "Leak detective disabled\n");
|
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