| #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(); |
| } |