diff --git a/tests/common/test_os_calls_signals.c b/tests/common/test_os_calls_signals.c new file mode 100644 index 00000000..1f9745cb --- /dev/null +++ b/tests/common/test_os_calls_signals.c @@ -0,0 +1,251 @@ + +#if defined(HAVE_CONFIG_H) +#include "config_ac.h" +#endif + +#include + +#include "os_calls.h" + +#include "test_common.h" + +static tintptr g_wobj1 = 0; + +/******************************************************************************/ +void +os_calls_signals_init(void) +{ + g_wobj1 = g_create_wait_obj(""); +} + +/******************************************************************************/ +void +os_calls_signals_deinit(void) +{ + g_delete_wait_obj(g_wobj1); + g_wobj1 = 0; +} + +/******************************************************************************/ +/** + * Set the global wait object g_wobj1 + */ +static void +set_wobj1(int signum) +{ + g_set_wait_obj(g_wobj1); +} + +/******************************************************************************/ +/** + * Sends a number of signals to the process and checks they are all delivered + * + * @param sig Signal number + * @param count Number of signals to send + * + * The caller is expected to establish a signal handler before this call + * which sets the global g_wobj1 on signal delivery */ + +static +void send_multiple_signals(int sig, unsigned int count) +{ + while (count-- > 0) + { + g_reset_wait_obj(g_wobj1); + ck_assert_int_eq(g_is_wait_obj_set(g_wobj1), 0); + // Expect the signal to be delivered synchronously + raise(sig); + ck_assert_int_ne(g_is_wait_obj_set(g_wobj1), 0); + } +} + +/******************************************************************************/ +START_TEST(test_g_set_alarm) +{ + g_reset_wait_obj(g_wobj1); + ck_assert_int_eq(g_is_wait_obj_set(g_wobj1), 0); + + g_set_alarm(set_wobj1, 1); + + g_obj_wait(&g_wobj1, 1, NULL, 0, 2000); + + ck_assert_int_ne(g_is_wait_obj_set(g_wobj1), 0); + + // Clean up + g_set_alarm(NULL, 0); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_g_signal_child_stop_1) +{ + struct exit_status e; + + g_reset_wait_obj(g_wobj1); + ck_assert_int_eq(g_is_wait_obj_set(g_wobj1), 0); + + g_signal_child_stop(set_wobj1); + + int pid = g_fork(); + + if (pid == 0) + { + g_exit(45); + } + ck_assert_int_ne(pid, 0); + g_obj_wait(&g_wobj1, 1, NULL, 0, 2000); + ck_assert_int_ne(g_is_wait_obj_set(g_wobj1), 0); + + e = g_waitpid_status(pid); + + ck_assert_int_eq(e.reason, E_XR_STATUS_CODE); + ck_assert_int_eq(e.val, 45); + + // Try another one to make sure the signal handler is still in place. + // This one can generate a signal + g_reset_wait_obj(g_wobj1); + + pid = g_fork(); + if (pid == 0) + { + raise(SIGSEGV); + } + ck_assert_int_ne(pid, 0); + g_obj_wait(&g_wobj1, 1, NULL, 0, 2000); + ck_assert_int_ne(g_is_wait_obj_set(g_wobj1), 0); + + e = g_waitpid_status(pid); + + ck_assert_int_eq(e.reason, E_XR_SIGNAL); + ck_assert_int_eq(e.val, SIGSEGV); + + // Clean up + g_signal_child_stop(NULL); +} +END_TEST + +/******************************************************************************/ +/* Checks that multiple children finishing do not interrupt + * g_waitpid_status() */ +START_TEST(test_g_signal_child_stop_2) +{ +#define CHILD_COUNT 20 + int pids[CHILD_COUNT]; + unsigned int i; + + struct exit_status e; + + g_reset_wait_obj(g_wobj1); + ck_assert_int_eq(g_is_wait_obj_set(g_wobj1), 0); + + g_signal_child_stop(set_wobj1); + + for (i = 0 ; i < CHILD_COUNT; ++i) + { + int pid = g_fork(); + if (pid == 0) + { + g_sleep((i + 1) * 100); + g_exit(i + 1); + } + ck_assert_int_ne(pid, 0); + pids[i] = pid; + } + g_obj_wait(&g_wobj1, 1, NULL, 0, 2000); + ck_assert_int_ne(g_is_wait_obj_set(g_wobj1), 0); + + for (i = 0 ; i < CHILD_COUNT; ++i) + { + e = g_waitpid_status(pids[i]); + ck_assert_int_eq(e.reason, E_XR_STATUS_CODE); + ck_assert_int_eq(e.val, (i + 1)); + } + + // Clean up + g_signal_child_stop(NULL); +#undef CHILD_COUNT +} +END_TEST + + +/******************************************************************************/ +START_TEST(test_g_signal_segfault) +{ + g_signal_segfault(set_wobj1); + + // Only one signal can be received in this way. After handling + // the signal the handler should be automatically reset. + send_multiple_signals(SIGSEGV, 1); + + g_signal_segfault(NULL); +} +END_TEST + +/******************************************************************************/ +START_TEST(test_g_signal_hang_up) +{ + g_signal_hang_up(set_wobj1); + + send_multiple_signals(SIGHUP, 5); + + g_signal_hang_up(NULL); +} + +/******************************************************************************/ +START_TEST(test_g_signal_user_interrupt) +{ + g_signal_user_interrupt(set_wobj1); + + send_multiple_signals(SIGINT, 5); + + g_signal_user_interrupt(NULL); +} + +/******************************************************************************/ +START_TEST(test_g_signal_terminate) +{ + g_signal_terminate(set_wobj1); + + send_multiple_signals(SIGTERM, 5); + + g_signal_terminate(NULL); +} + +/******************************************************************************/ +START_TEST(test_g_signal_pipe) +{ + g_signal_pipe(set_wobj1); + + send_multiple_signals(SIGPIPE, 5); + + g_signal_pipe(NULL); +} + +/******************************************************************************/ +START_TEST(test_g_signal_usr1) +{ + g_signal_usr1(set_wobj1); + + send_multiple_signals(SIGUSR1, 5); + + g_signal_usr1(NULL); +} + +/******************************************************************************/ +TCase * +make_tcase_test_os_calls_signals(void) +{ + TCase *tc = tcase_create("oscalls-signals"); + + tcase_add_test(tc, test_g_set_alarm); + tcase_add_test(tc, test_g_signal_child_stop_1); + tcase_add_test(tc, test_g_signal_child_stop_2); + tcase_add_test(tc, test_g_signal_segfault); + tcase_add_test(tc, test_g_signal_hang_up); + tcase_add_test(tc, test_g_signal_user_interrupt); + tcase_add_test(tc, test_g_signal_terminate); + tcase_add_test(tc, test_g_signal_pipe); + tcase_add_test(tc, test_g_signal_usr1); + + return tc; +}