| | #include "../../unity/unity.h" |
| | #include <stdio.h> |
| | #include <stdlib.h> |
| | #include <string.h> |
| | #include <signal.h> |
| | #include <unistd.h> |
| | #include <errno.h> |
| | #include <sys/stat.h> |
| | #include <limits.h> |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | static volatile sig_atomic_t g_test_signal_count = 0; |
| |
|
| | static void test_dummy_handler(int sig) |
| | { |
| | (void)sig; |
| | g_test_signal_count++; |
| | } |
| |
|
| | |
| | |
| | static int pick_safe_signal(void) |
| | { |
| | #ifdef SIGCHLD |
| | return SIGCHLD; |
| | #elif defined(SIGURG) |
| | return SIGURG; |
| | #elif defined(SIGWINCH) |
| | return SIGWINCH; |
| | #elif defined(SIGCONT) |
| | return SIGCONT; |
| | #else |
| | |
| | |
| | return SIGCONT; |
| | #endif |
| | } |
| |
|
| | |
| | static char* create_temp_dir(void) |
| | { |
| | char templ[] = "/tmp/csplit_interrupt_test.XXXXXX"; |
| | char *path = (char*)malloc(sizeof(templ)); |
| | TEST_ASSERT_NOT_NULL_MESSAGE(path, "malloc failed for temp dir path"); |
| | strcpy(path, templ); |
| | char *ret = mkdtemp(path); |
| | if (!ret) |
| | { |
| | free(path); |
| | TEST_FAIL_MESSAGE("mkdtemp failed to create temporary directory"); |
| | } |
| | return path; |
| | } |
| |
|
| | |
| | static char* path_join2(const char* a, const char* b) |
| | { |
| | size_t la = strlen(a); |
| | size_t lb = strlen(b); |
| | size_t need = la + 1 + lb + 1; |
| | char *p = (char*)malloc(need); |
| | TEST_ASSERT_NOT_NULL_MESSAGE(p, "malloc failed in path_join2"); |
| | memcpy(p, a, la); |
| | p[la] = '/'; |
| | memcpy(p + la + 1, b, lb); |
| | p[la + 1 + lb] = '\0'; |
| | return p; |
| | } |
| |
|
| | |
| | |
| | static void prepare_filename_env(const char* pre, const char* suf, int dig, int nfiles) |
| | { |
| | |
| | if (filename_space) |
| | { |
| | free((void*)filename_space); |
| | filename_space = NULL; |
| | } |
| | filename_space = (char*)malloc(1024); |
| | TEST_ASSERT_NOT_NULL_MESSAGE((void*)filename_space, "malloc failed for filename_space"); |
| |
|
| | |
| | |
| | prefix = pre; |
| | if (suffix) |
| | { |
| | free((void*)suffix); |
| | suffix = NULL; |
| | } |
| | if (suf) |
| | { |
| | char *s = (char*)malloc(strlen(suf) + 1); |
| | TEST_ASSERT_NOT_NULL(s); |
| | strcpy(s, suf); |
| | suffix = s; |
| | } |
| | else |
| | { |
| | suffix = NULL; |
| | } |
| | digits = dig; |
| | files_created = nfiles; |
| | } |
| |
|
| | |
| | |
| | static char** create_numbered_files(int n) |
| | { |
| | char **names = (char**)calloc(n, sizeof(char*)); |
| | TEST_ASSERT_NOT_NULL_MESSAGE(names, "calloc failed for file names array"); |
| |
|
| | for (int i = 0; i < n; i++) |
| | { |
| | char *name = strdup(make_filename(i)); |
| | TEST_ASSERT_NOT_NULL_MESSAGE(name, "strdup failed on make_filename"); |
| | FILE *fp = fopen(name, "w"); |
| | if (!fp) |
| | { |
| | |
| | for (int j = 0; j <= i; j++) |
| | { |
| | if (names[j]) { unlink(names[j]); free(names[j]); } |
| | } |
| | free(names); |
| | TEST_FAIL_MESSAGE("Failed to create test file"); |
| | } |
| | fclose(fp); |
| | names[i] = name; |
| | } |
| | return names; |
| | } |
| |
|
| | static void free_names_and_dir(char **names, int n, const char* dir) |
| | { |
| | if (names) |
| | { |
| | for (int i = 0; i < n; i++) |
| | { |
| | if (names[i]) free(names[i]); |
| | } |
| | free(names); |
| | } |
| | if (dir) |
| | { |
| | rmdir(dir); |
| | } |
| | } |
| |
|
| | |
| | void setUp(void) |
| | { |
| | |
| | } |
| | void tearDown(void) |
| | { |
| | |
| | int sig = pick_safe_signal(); |
| | signal(sig, SIG_DFL); |
| |
|
| | |
| | remove_files = false; |
| | if (suffix) |
| | { |
| | free((void*)suffix); |
| | suffix = NULL; |
| | } |
| | prefix = NULL; |
| | if (filename_space) |
| | { |
| | free((void*)filename_space); |
| | filename_space = NULL; |
| | } |
| | files_created = 0; |
| | } |
| |
|
| | |
| | static void test_interrupt_handler_deletes_files_and_resets_handler(void) |
| | { |
| | int sig = pick_safe_signal(); |
| | g_test_signal_count = 0; |
| |
|
| | char *dir = create_temp_dir(); |
| | char *pre = path_join2(dir, "xx"); |
| |
|
| | prepare_filename_env(pre, NULL, 2, 3); |
| | remove_files = true; |
| |
|
| | char **names = create_numbered_files(3); |
| |
|
| | |
| | signal(sig, test_dummy_handler); |
| |
|
| | |
| | interrupt_handler(sig); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, g_test_signal_count, "dummy handler invoked unexpectedly"); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(0, files_created); |
| |
|
| | |
| | for (int i = 0; i < 3; i++) |
| | { |
| | int rc = access(names[i], F_OK); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(-1, rc, "Expected file to be deleted"); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(ENOENT, errno, "Expected ENOENT for deleted file"); |
| | } |
| |
|
| | |
| | g_test_signal_count = 0; |
| | raise(sig); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, g_test_signal_count, "dummy handler invoked after reset"); |
| |
|
| | |
| | free_names_and_dir(names, 3, dir); |
| | free(pre); |
| | } |
| |
|
| | |
| | static void test_interrupt_handler_keeps_files_when_remove_files_false(void) |
| | { |
| | int sig = pick_safe_signal(); |
| | g_test_signal_count = 0; |
| |
|
| | char *dir = create_temp_dir(); |
| | char *pre = path_join2(dir, "part"); |
| |
|
| | |
| | prepare_filename_env(pre, "%03d", 2, 2); |
| | remove_files = false; |
| |
|
| | char **names = create_numbered_files(2); |
| |
|
| | |
| | for (int i = 0; i < 2; i++) |
| | { |
| | int rc = access(names[i], F_OK); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "Expected file to exist before handler"); |
| | } |
| |
|
| | |
| | signal(sig, test_dummy_handler); |
| | interrupt_handler(sig); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(0, g_test_signal_count); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(2, files_created); |
| |
|
| | |
| | for (int i = 0; i < 2; i++) |
| | { |
| | int rc = access(names[i], F_OK); |
| | TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "File should not have been deleted"); |
| | } |
| |
|
| | |
| | g_test_signal_count = 0; |
| | raise(sig); |
| | TEST_ASSERT_EQUAL_INT(0, g_test_signal_count); |
| |
|
| | |
| | for (int i = 0; i < 2; i++) |
| | { |
| | unlink(names[i]); |
| | } |
| | free_names_and_dir(names, 2, dir); |
| | free(pre); |
| | } |
| |
|
| | |
| | static void test_interrupt_handler_zero_files(void) |
| | { |
| | int sig = pick_safe_signal(); |
| | g_test_signal_count = 0; |
| |
|
| | char *dir = create_temp_dir(); |
| | char *pre = path_join2(dir, "xx"); |
| |
|
| | prepare_filename_env(pre, NULL, 2, 0); |
| | remove_files = true; |
| |
|
| | signal(sig, test_dummy_handler); |
| | interrupt_handler(sig); |
| |
|
| | TEST_ASSERT_EQUAL_INT(0, g_test_signal_count); |
| | TEST_ASSERT_EQUAL_INT(0, files_created); |
| |
|
| | |
| | g_test_signal_count = 0; |
| | raise(sig); |
| | TEST_ASSERT_EQUAL_INT(0, g_test_signal_count); |
| |
|
| | |
| | rmdir(dir); |
| | free(pre); |
| | free(dir); |
| | } |
| |
|
| | int main(void) |
| | { |
| | UNITY_BEGIN(); |
| | RUN_TEST(test_interrupt_handler_deletes_files_and_resets_handler); |
| | RUN_TEST(test_interrupt_handler_keeps_files_when_remove_files_false); |
| | RUN_TEST(test_interrupt_handler_zero_files); |
| | return UNITY_END(); |
| | } |