test_cleanup();
if (mkdir(tmpdir, 0700) < 0)
i_fatal("mkdir() failed: %m");
-
- test_subprocesses_init(FALSE);
}
struct test_service_settings {
master_service_init_finish(master_service);
test_tmp_dir_init();
+ test_subprocesses_init(FALSE);
static void (*const test_functions[])(void) = {
test_imap_client_hibernate,
};
ret = test_run(test_functions);
- test_subprocesses_deinit();
test_cleanup();
master_service_deinit(&master_service);
return ret;
}
}
+ test_init();
+ test_set_cleanup_callback(main_cleanup);
test_subprocesses_init(debug);
- test_subprocess_set_cleanup_callback(main_cleanup);
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
return ret;
}
}
+ test_init();
+ test_set_cleanup_callback(main_cleanup);
test_subprocesses_init(debug);
- test_subprocess_set_cleanup_callback(main_cleanup);
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
return ret;
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
event_set_ptr(cctx->event, SETTINGS_EVENT_ROOT, NULL);
main_deinit();
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
return ret;
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
}
master_service_init_finish(master_service);
+
+ test_init();
+ test_set_cleanup_callback(main_cleanup);
test_subprocesses_init(debug);
- test_subprocess_set_cleanup_callback(main_cleanup);
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
master_service_deinit(&master_service);
master_service_init_finish(master_service);
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
event_unref(&test_event);
- test_subprocesses_deinit();
main_deinit();
master_service_deinit(&master_service);
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
}
}
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
lib_deinit();
}
master_service_init_finish(master_service);
+
+ test_init();
test_subprocesses_init(debug);
/* listen on localhost */
ret = test_run(test_functions);
- test_subprocesses_deinit();
main_deinit();
master_service_deinit(&master_service);
fuzzer.h \
ostream-final-trickle.h \
test-common.h \
+ test-private.h \
test-subprocess.h
pkginc_libdir=$(pkgincludedir)
/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
+#include "lib-signals.h"
#include "str.h"
#include "safe-mkstemp.h"
#include "test-common.h"
+#include "test-private.h"
+#include "test-subprocess.h"
+#include <unistd.h>
#include <stdio.h>
#include <setjmp.h> /* for fatal tests */
-static bool test_deinit_lib;
+static bool test_initialized = FALSE;
+static bool test_deinit_lib = FALSE;
+static bool test_deinit_lib_signals = FALSE;
/* To test the firing of i_assert, we need non-local jumps, i.e. setjmp */
static volatile bool expecting_fatal = FALSE;
static char *test_prefix;
static bool test_success;
+static bool test_running;
static unsigned int failure_count;
static unsigned int total_count;
static unsigned int expected_errors;
static char *expected_error_str, *expected_fatal_str;
static test_fatal_callback_t *test_fatal_callback;
static void *test_fatal_context;
+static void (*test_cleanup_callback)(void) = NULL;
+volatile sig_atomic_t terminating = 0;
void test_begin(const char *name)
{
i_unreached(); /* we simply can't get here */
}
-static void test_init(void)
+static void test_cleanup(void)
{
+ if (test_subprocess_is_child()) {
+ /* Child processes must not execute the cleanups */
+ return;
+ }
+
+ test_subprocess_cleanup();
+
+ /* Perform any additional important cleanup specific to the test. */
+ if (test_cleanup_callback != NULL)
+ test_cleanup_callback();
+}
+
+static void test_terminate(const siginfo_t *si, void *context ATTR_UNUSED)
+{
+ int signo = si->si_signo;
+
+ if (terminating != 0)
+ raise(signo);
+ terminating = 1;
+
+ /* Perform important cleanups */
+ test_cleanup();
+
+ (void)signal(signo, SIG_DFL);
+ if (signo == SIGTERM)
+ _exit(0);
+ else
+ raise(signo);
+}
+
+static void test_atexit(void)
+{
+ /* NOTICE: This is also called by children, so be careful. */
+
+ /* Perform important cleanups */
+ test_cleanup();
+}
+
+void test_init(void)
+{
+ if (test_initialized)
+ return;
+ test_initialized = TRUE;
+
test_prefix = NULL;
failure_count = 0;
total_count = 0;
/* Don't set fatal handler until actually needed for fatal testing */
}
+void test_init_signals(void)
+{
+ lib_signals_init();
+ test_deinit_lib_signals = TRUE;
+
+ atexit(test_atexit);
+ lib_signals_ignore(SIGPIPE, TRUE);
+ lib_signals_set_handler(SIGTERM, 0, test_terminate, NULL);
+ lib_signals_set_handler(SIGQUIT, 0, test_terminate, NULL);
+ lib_signals_set_handler(SIGINT, 0, test_terminate, NULL);
+ lib_signals_set_handler(SIGSEGV, 0, test_terminate, NULL);
+ lib_signals_set_handler(SIGABRT, 0, test_terminate, NULL);
+}
+
static int test_deinit(void)
{
i_assert(test_prefix == NULL);
printf("%u / %u tests failed\n", failure_count, total_count);
+
+ test_subprocesses_deinit();
+
if (test_deinit_lib)
lib_deinit();
+ if (test_deinit_lib_signals)
+ lib_signals_deinit();
return failure_count == 0 ? 0 : 1;
}
{
unsigned int i;
+ test_running = TRUE;
for (i = 0; test_functions[i] != NULL; i++) {
T_BEGIN {
test_functions[i]();
} T_END;
}
+ test_running = FALSE;
}
static void test_run_named_funcs(const struct named_test tests[],
const char *match)
{
unsigned int i;
+ test_running = TRUE;
for (i = 0; tests[i].func != NULL; i++) {
if (strstr(tests[i].name, match) != NULL) T_BEGIN {
tests[i].func();
} T_END;
}
+ test_running = FALSE;
}
static void run_one_fatal(test_fatal_func_t *fatal_function)
{
unsigned int i;
+ test_running = TRUE;
for (i = 0; fatal_functions[i] != NULL; i++) {
T_BEGIN {
run_one_fatal(fatal_functions[i]);
} T_END;
}
+ test_running = FALSE;
}
static void test_run_named_fatals(const struct named_fatal fatals[], const char *match)
{
unsigned int i;
+ test_running = TRUE;
for (i = 0; fatals[i].func != NULL; i++) {
if (strstr(fatals[i].name, match) != NULL) T_BEGIN {
run_one_fatal(fatals[i].func);
} T_END;
}
+ test_running = FALSE;
}
int test_run(void (*const test_functions[])(void))
i_free_and_null(expected_error_str);
i_free_and_null(expected_fatal_str);
i_free_and_null(test_prefix);
- t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
+ if (test_running)
+ t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
}
void ATTR_NORETURN
i_free_and_null(expected_error_str);
i_free_and_null(expected_fatal_str);
i_free_and_null(test_prefix);
- t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
+ if (test_running)
+ t_pop_last_unsafe(); /* as we were within a T_BEGIN { tests[i].func(); } T_END */
lib_deinit();
lib_exit(status);
}
i_unlink(str_c(str));
return fd;
}
+
+void test_set_cleanup_callback(void (*callback)(void))
+{
+ test_cleanup_callback = callback;
+}
void test_out_reason_quiet(const char *name, bool success, const char *reason)
ATTR_NULL(3);
+void test_init(void);
+
int test_run(void (*const test_functions[])(void)) ATTR_WARN_UNUSED_RESULT;
struct named_test {
const char *name;
function handles failures by calling i_fatal(). */
int test_create_temp_fd(void);
+/* Set a cleanup callback that is executed even when the test program crashes or
+ exit()s unexpectedly. Note that this may be run in signal context. */
+void test_set_cleanup_callback(void (*callback)(void));
+
#endif
--- /dev/null
+#ifndef TEST_PRIVATE_H
+#define TEST_PRIVATE_H
+
+#include "test-common.h"
+
+void test_init_signals(void);
+
+void test_dir_cleanup(void);
+void test_dir_deinit(void);
+void test_dir_deinit_forked(void);
+
+void test_subprocess_cleanup(void);
+void test_subprocesses_deinit(void);
+
+#endif
#include "sleep.h"
#include "time-util.h"
#include "test-common.h"
+#include "test-private.h"
#include "test-subprocess.h"
#include <sys/types.h>
pid_t pid;
};
-volatile sig_atomic_t test_subprocess_is_child = 0;
-static bool test_subprocess_lib_init = FALSE;
+volatile sig_atomic_t test_subprocess_child_mark = 0;
static volatile bool test_subprocess_notification_signal_received[SIGUSR1 + 1];
static struct event *test_subprocess_event = NULL;
static ARRAY(struct test_subprocess *) test_subprocesses = ARRAY_INIT;
-static void (*test_subprocess_cleanup_callback)(void) = NULL;
static void
test_subprocess_notification_signal(const siginfo_t *si, void *context);
{
struct test_subprocess *subp;
+ if (!array_is_created(&test_subprocesses))
+ return;
array_foreach_elem(&test_subprocesses, subp)
i_free(subp);
array_free(&test_subprocesses);
}
#undef test_subprocess_fork
-void test_subprocess_fork(int (*func)(void *context), void *context,
- bool continue_test)
+pid_t test_subprocess_fork(int (*func)(void *context), void *context,
+ bool continue_test)
{
struct test_subprocess *subprocess;
/* avoid races: fork the child process with test_subprocess_is_child
set to 1 in case it immediately receives a signal. */
- test_subprocess_is_child = 1;
+ test_subprocess_child_mark = 1;
if ((subprocess->pid = fork()) == (pid_t)-1)
i_fatal("test: sub-process: fork() failed: %m");
if (subprocess->pid == 0) {
* kill of PID 0, so just free it here explicitly. */
i_free(subprocess);
io_loop_recreate(current_ioloop);
+
+ test_subprocess_child_mark = 1;
test_subprocess_free_all();
test_subprocess_child(func, context, continue_test);
i_unreached();
}
- test_subprocess_is_child = 0;
+ test_subprocess_child_mark = 0;
array_push_back(&test_subprocesses, &subprocess);
lib_signals_ioloop_attach();
+ return subprocess->pid;
}
static void test_subprocess_verify_exit_status(int status)
* Main
*/
-volatile sig_atomic_t terminating = 0;
-
-static void test_subprocess_cleanup(void)
+bool test_subprocess_is_child(void)
{
- if (test_subprocess_is_child != 0) {
- /* Child processes must not execute the cleanups */
- return;
- }
+ return (test_subprocess_child_mark != 0);
+}
+void test_subprocess_cleanup(void)
+{
/* We get here when the test ended normally, badly failed, crashed,
terminated, or executed exit() unexpectedly. The cleanups performed
here are important and must be executed at all times. */
child processes will not be handled so well. So, we need to make sure
here that we don't leave any pesky child processes alive. */
test_subprocess_kill_all_forced();
-
- /* Perform any additional important cleanup specific to the test. */
- if (test_subprocess_cleanup_callback != NULL)
- test_subprocess_cleanup_callback();
}
static void
*/
}
-static void
-test_subprocess_terminate(const siginfo_t *si, void *context ATTR_UNUSED)
-{
- int signo = si->si_signo;
-
- if (terminating != 0)
- raise(signo);
- terminating = 1;
-
- /* Perform important cleanups */
- test_subprocess_cleanup();
-
- (void)signal(signo, SIG_DFL);
- if (signo == SIGTERM)
- _exit(0);
- else
- raise(signo);
-}
-
-static void test_atexit(void)
-{
- /* NOTICE: This is also called by children, so be careful. */
-
- /* Perform important cleanups */
- test_subprocess_cleanup();
-}
-
-void test_subprocess_set_cleanup_callback(void (*callback)(void))
-{
- test_subprocess_cleanup_callback = callback;
-}
-
void test_subprocess_notify_signal_send(int signo, pid_t pid)
{
if (kill(pid, signo) < 0)
void test_subprocesses_init(bool debug)
{
- if (!lib_is_initialized()) {
- lib_init();
- test_subprocess_lib_init = TRUE;
- }
- lib_signals_init();
-
- atexit(test_atexit);
- lib_signals_ignore(SIGPIPE, TRUE);
- lib_signals_set_handler(SIGALRM, 0, test_subprocess_alarm, NULL);
- lib_signals_set_handler(SIGTERM, 0, test_subprocess_terminate, NULL);
- lib_signals_set_handler(SIGQUIT, 0, test_subprocess_terminate, NULL);
- lib_signals_set_handler(SIGINT, 0, test_subprocess_terminate, NULL);
- lib_signals_set_handler(SIGSEGV, 0, test_subprocess_terminate, NULL);
- lib_signals_set_handler(SIGABRT, 0, test_subprocess_terminate, NULL);
+ test_init();
+ test_init_signals();
lib_signals_set_handler(SIGHUP, LIBSIG_FLAG_RESTART,
test_subprocess_notification_signal, NULL);
lib_signals_set_handler(SIGUSR1, LIBSIG_FLAG_RESTART,
test_subprocess_notification_signal, NULL);
+ lib_signals_set_handler(SIGALRM, 0, test_subprocess_alarm, NULL);
i_array_init(&test_subprocesses, 8);
array_free(&test_subprocesses);
event_unref(&test_subprocess_event);
- lib_signals_deinit();
-
- if (test_subprocess_lib_init)
- lib_deinit();
}
#define TEST_SIGNALS_DEFAULT_TIMEOUT_MS 10000
-struct test_subprocess;
-
/* Fork a sub-process for this test. The func is the main function for the
forked sub-process. The provided context is passed to the provided function.
When continue_test=FALSE, the test is ended immediately in the sub-process,
otherwise, the test continues and its result is used to set the exit code
when the process ends gracefully. */
-void test_subprocess_fork(int (*func)(void *), void *context,
- bool continue_test);
+pid_t ATTR_NOWARN_UNUSED_RESULT
+test_subprocess_fork(int (*func)(void *), void *context,
+ bool continue_test);
#define test_subprocess_fork(func, context, continue_test) \
test_subprocess_fork( \
(int(*)(void*))func, \
timeout. */
void test_subprocess_kill_all(unsigned int timeout_secs);
-/* Set a cleanup callback that is executed even when the test program crashes or
- exit()s unexpectedly. Note that this may be run in signal context. */
-void test_subprocess_set_cleanup_callback(void (*callback)(void));
+bool test_subprocess_is_child(void);
/* Send a notification signal (SIGHUP) to the given PID */
void test_subprocess_notify_signal_send(int signo, pid_t pid);
void test_subprocess_notify_signal_wait(int signo, unsigned int timeout_msecs);
void test_subprocesses_init(bool debug);
-void test_subprocesses_deinit(void);
#endif