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