]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-test: test-subprocess - Move most signal and cleanup code to test-common for...
authorStephan Bosch <stephan.bosch@open-xchange.com>
Mon, 27 Oct 2025 20:48:35 +0000 (21:48 +0100)
committertimo.sirainen <timo.sirainen@open-xchange.com>
Wed, 5 Nov 2025 10:17:48 +0000 (10:17 +0000)
19 files changed:
src/imap/test-imap-client-hibernate.c
src/lib-auth-client/test-auth-client.c
src/lib-auth-client/test-auth-master.c
src/lib-http/test-http-client-errors.c
src/lib-http/test-http-payload.c
src/lib-http/test-http-server-errors.c
src/lib-imap-client/test-imapc-client.c
src/lib-login/test-login-server-auth.c
src/lib-lua/test-lua-http-client.c
src/lib-smtp/test-smtp-client-errors.c
src/lib-smtp/test-smtp-payload.c
src/lib-smtp/test-smtp-server-errors.c
src/lib-smtp/test-smtp-submit.c
src/lib-test/Makefile.am
src/lib-test/test-common.c
src/lib-test/test-common.h
src/lib-test/test-private.h [new file with mode: 0644]
src/lib-test/test-subprocess.c
src/lib-test/test-subprocess.h

index 873c928476d7d6490077d606527d96352be84c26..3f18b966e4c4c7bb3871d13a73f3d8bbec56b535 100644 (file)
@@ -282,8 +282,6 @@ static void test_tmp_dir_init(void)
        test_cleanup();
        if (mkdir(tmpdir, 0700) < 0)
                i_fatal("mkdir() failed: %m");
-
-       test_subprocesses_init(FALSE);
 }
 
 struct test_service_settings {
@@ -324,6 +322,7 @@ int main(int argc, char *argv[])
 
        master_service_init_finish(master_service);
        test_tmp_dir_init();
+       test_subprocesses_init(FALSE);
 
        static void (*const test_functions[])(void) = {
                test_imap_client_hibernate,
@@ -331,7 +330,6 @@ int main(int argc, char *argv[])
        };
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        test_cleanup();
        master_service_deinit(&master_service);
        return ret;
index 2d8333a68841e3194c34caf611a30bef2d761785..2007f10b1fc4f0aff6e64187f0814c566d88e393 100644 (file)
@@ -1267,12 +1267,12 @@ int main(int argc, char *argv[])
                }
        }
 
+       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;
index 7fa7089856e1b51c514b8edbe7958b3dad91364d..596fa01e9359dedac3ae458f68614f79e032c570 100644 (file)
@@ -1921,12 +1921,12 @@ int main(int argc, char *argv[])
                }
        }
 
+       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;
index d80ad4348370e69fd463c5b709951096ede428f9..38c9f48e3218fa1d2cb742368f800e1ce0dfc6df 100644 (file)
@@ -3818,6 +3818,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -3827,7 +3828,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        event_set_ptr(cctx->event, SETTINGS_EVENT_ROOT, NULL);
 
        main_deinit();
index ada971dcd14b6a0c9300b7c34af4c2d0dd229e7d..1c5f290484200001a7a2b8eeb7da6b6b98ec1604 100644 (file)
@@ -2476,6 +2476,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -2485,7 +2486,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
        return ret;
index 98336284a2c5c366bd9f2c8d829aa5cfd4581875..efb15dedc3788f9afb32a7fc6a78f51026aba8b1 100644 (file)
@@ -1026,6 +1026,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -1035,7 +1036,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
 
index e8ca2be8bd13a2c66b2a28a303953a473e22b30c..e1bf9c8ba3697d597f6c01ee14d3e733e7d084b6 100644 (file)
@@ -960,6 +960,7 @@ int main(int argc ATTR_UNUSED, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -969,7 +970,6 @@ int main(int argc ATTR_UNUSED, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
 
index 631c000c57eacd0e72697a2f87c6f9f7a732bfdb..97caa218e3cf3a32231a95474a15756f7d78558a 100644 (file)
@@ -981,12 +981,13 @@ int main(int argc, char *argv[])
        }
 
        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);
 
index 198ee0ecc15b5f0958e1a090fb4d2ce7f57b94fb..216d6a4a5ca989b6dc3c779ca2ba0b91d4b464d2 100644 (file)
@@ -813,6 +813,7 @@ int main(int argc, char *argv[])
 
        master_service_init_finish(master_service);
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -827,7 +828,6 @@ int main(int argc, char *argv[])
 
        event_unref(&test_event);
 
-       test_subprocesses_deinit();
        main_deinit();
        master_service_deinit(&master_service);
 
index 07ba6c535e7ed6940e7517411dd4ffdca39d0a5c..abb69c7bc539882bbe1e911fddfabc2d0abc3c4b 100644 (file)
@@ -4343,6 +4343,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -4352,7 +4353,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
 
index 16f1aa196c7fbaf23473427775433da3ef424933..98724eeeb7d06abfb4381e793aa47e4f3204cba3 100644 (file)
@@ -1536,6 +1536,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -1545,7 +1546,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
 
index 3113b94b111c32a4209ffec4a0885da8d433b0d6..804ecb0c104b55d9d4b40ede00ccd2655245d26e 100644 (file)
@@ -4167,6 +4167,7 @@ int main(int argc, char *argv[])
                }
        }
 
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -4176,7 +4177,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        lib_deinit();
 
index 28a90970b6f724ea0a038613e07488f4c2588afc..dab6c45761926206bbd7f591a9064a9a12935737 100644 (file)
@@ -2189,6 +2189,8 @@ int main(int argc, char *argv[])
        }
 
        master_service_init_finish(master_service);
+
+       test_init();
        test_subprocesses_init(debug);
 
        /* listen on localhost */
@@ -2198,7 +2200,6 @@ int main(int argc, char *argv[])
 
        ret = test_run(test_functions);
 
-       test_subprocesses_deinit();
        main_deinit();
        master_service_deinit(&master_service);
 
index cb6c66edb9dd96edacbe69dc726d24a6bfc7916c..bc4515e9f3b40645af6c3d1bb35fb94bb01377ea 100644 (file)
@@ -16,6 +16,7 @@ headers = \
        fuzzer.h \
        ostream-final-trickle.h \
        test-common.h \
+       test-private.h \
        test-subprocess.h
 
 pkginc_libdir=$(pkgincludedir)
index e556bf0ad25384d67c36b3756e316f098ab33c69..9ae966c35100eca6b50bf6f69643d1ba73eda5a0 100644 (file)
@@ -1,14 +1,20 @@
 /* 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;
@@ -18,12 +24,15 @@ static jmp_buf fatal_jmpbuf;
 
 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)
 {
@@ -356,8 +365,52 @@ test_fatal_handler(const struct failure_context *ctx,
        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;
@@ -372,12 +425,31 @@ static void test_init(void)
        /* 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;
 }
 
@@ -385,22 +457,26 @@ static void test_run_funcs(void (*const test_functions[])(void))
 {
        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)
@@ -438,21 +514,25 @@ static void test_run_fatals(test_fatal_func_t *const fatal_functions[])
 {
        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))
@@ -494,7 +574,8 @@ void test_forked_end(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
@@ -503,7 +584,8 @@ test_exit(int status)
        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);
 }
@@ -520,3 +602,8 @@ int test_create_temp_fd(void)
        i_unlink(str_c(str));
        return fd;
 }
+
+void test_set_cleanup_callback(void (*callback)(void))
+{
+       test_cleanup_callback = callback;
+}
index 2f06d67b13a60094daafcc7e4221d426a1a9e3bd..fadae3b382db7443fe7fa27867dd5ad316b4a7c9 100644 (file)
@@ -146,6 +146,8 @@ void test_out_quiet(const char *name, bool success);
 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;
@@ -204,4 +206,8 @@ void test_exit(int status) ATTR_NORETURN;
    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
diff --git a/src/lib-test/test-private.h b/src/lib-test/test-private.h
new file mode 100644 (file)
index 0000000..c56e2cd
--- /dev/null
@@ -0,0 +1,15 @@
+#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
index 8562cc2cc6d55c63f6680a997ebcdac2c40751d2..7e8ce087e3900f993a12f4bcc9b229974ca7bbb1 100644 (file)
@@ -8,6 +8,7 @@
 #include "sleep.h"
 #include "time-util.h"
 #include "test-common.h"
+#include "test-private.h"
 #include "test-subprocess.h"
 
 #include <sys/types.h>
@@ -18,12 +19,10 @@ struct test_subprocess {
        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);
 
@@ -38,6 +37,8 @@ static void test_subprocess_free_all(void)
 {
        struct test_subprocess *subp;
 
+       if (!array_is_created(&test_subprocesses))
+               return;
        array_foreach_elem(&test_subprocesses, subp)
                i_free(subp);
        array_free(&test_subprocesses);
@@ -85,8 +86,8 @@ test_subprocess_child(int (*func)(void *context), void *context,
 }
 
 #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;
 
@@ -96,7 +97,7 @@ void test_subprocess_fork(int (*func)(void *context), void *context,
 
        /* 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) {
@@ -104,15 +105,18 @@ void test_subprocess_fork(int (*func)(void *context), void *context,
                 * 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)
@@ -301,15 +305,13 @@ static void test_subprocess_kill_all_forced(void)
  * 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. */
@@ -318,10 +320,6 @@ static void test_subprocess_cleanup(void)
           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
@@ -335,38 +333,6 @@ test_subprocess_alarm(const siginfo_t *si ATTR_UNUSED,
         */
 }
 
-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)
@@ -417,24 +383,13 @@ test_subprocess_notification_signal(const siginfo_t *si,
 
 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);
 
@@ -450,8 +405,4 @@ void test_subprocesses_deinit(void)
        array_free(&test_subprocesses);
 
        event_unref(&test_subprocess_event);
-       lib_signals_deinit();
-
-       if (test_subprocess_lib_init)
-               lib_deinit();
 }
index 9a072924e9cbeab6d3350b409fae38d460233b15..edfccbadc44242fd7aabe5bf8d804948c2323340 100644 (file)
@@ -3,15 +3,14 @@
 
 #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, \
@@ -27,9 +26,7 @@ void test_subprocess_wait_all(unsigned int timeout_secs);
    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);
@@ -46,6 +43,5 @@ void test_subprocess_notify_signal_reset(int signo);
 void test_subprocess_notify_signal_wait(int signo, unsigned int timeout_msecs);
 
 void test_subprocesses_init(bool debug);
-void test_subprocesses_deinit(void);
 
 #endif