#include "../../unity/unity.h" #include #include #include #include #include #include #include #include /* The test file is included into dd.c after the function definitions, so internal static functions and variables (like ifd_reopen and info_signal_count) are visible here. */ static void remove_file_if_exists(const char *path) { if (path && *path) unlink(path); } static void rmdir_if_exists(const char *path) { if (path && *path) rmdir(path); } static int write_all(int fd, const void *buf, size_t len) { const char *p = (const char *)buf; while (len > 0) { ssize_t n = write(fd, p, len); if (n < 0) { if (errno == EINTR) continue; return -1; } if (n == 0) return -1; p += n; len -= (size_t)n; } return 0; } static int read_all(int fd, void *buf, size_t len) { char *p = (char *)buf; size_t got = 0; while (got < len) { ssize_t n = read(fd, p + got, len - got); if (n < 0) { if (errno == EINTR) continue; return -1; } if (n == 0) break; got += (size_t)n; } return (int)got; } /* Declarations of internal globals we will touch, which are defined above in dd.c. */ extern int status_level; /* from dd.c; default STATUS_DEFAULT */ /* These are file-scope static in dd.c, but since this test file is included into the same translation unit, we can reference them directly. */ extern sig_atomic_t volatile info_signal_count; /* from dd.c */ extern sig_atomic_t volatile interrupt_signal; /* from dd.c */ void setUp(void) { /* Ensure a quiet environment for tests that may trigger internal printing. */ status_level = 1; /* STATUS_NONE == 1 in dd.c */ info_signal_count = 0; interrupt_signal = 0; } void tearDown(void) { /* Restore baseline state between tests. */ status_level = 3; /* STATUS_DEFAULT == 3 in dd.c */ info_signal_count = 0; interrupt_signal = 0; } /* Helper to create a temporary file path; returns malloc'd path string. */ static char *make_temp_file(char *out_template) { int fd = mkstemp(out_template); if (fd < 0) return NULL; close(fd); return out_template; } /* Helper to create a temporary directory path; returns malloc'd path string. */ static char *make_temp_dir(char *out_template) { char *p = mkdtemp(out_template); return p; /* returns NULL on failure */ } /* Test 1: Successful reopen of a duplicate FD to a regular file and verify content written via that FD. */ void test_ifd_reopen_success_reopens_fd(void) { char tmpl[] = "/tmp/dd_ifd_reopen_file_XXXXXX"; char *path = make_temp_file(tmpl); TEST_ASSERT_MESSAGE(path != NULL, "mkstemp failed to create temp file"); int dupfd = dup(STDERR_FILENO); TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed"); errno = 0; int ret = ifd_reopen(dupfd, path, O_WRONLY | O_CREAT | O_TRUNC, 0600); TEST_ASSERT_EQUAL_MESSAGE(0, ret, "ifd_reopen should succeed"); const char payload[] = "hello world"; TEST_ASSERT_EQUAL_INT_MESSAGE(0, write_all(dupfd, payload, sizeof(payload)), "write_all failed"); close(dupfd); int rfd = open(path, O_RDONLY); TEST_ASSERT_MESSAGE(rfd >= 0, "open for readback failed"); char buf[sizeof(payload)] = {0}; int n = read_all(rfd, buf, sizeof(buf)); close(rfd); TEST_ASSERT_EQUAL_INT((int)sizeof(payload), n); TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, buf, sizeof(payload)); remove_file_if_exists(path); } /* Test 2: Failure case - attempt to reopen FD to a directory using O_WRONLY should fail. */ void test_ifd_reopen_failure_returns_error(void) { char dtmpl[] = "/tmp/dd_ifd_reopen_dir_XXXXXX"; char *dirpath = make_temp_dir(dtmpl); TEST_ASSERT_MESSAGE(dirpath != NULL, "mkdtemp failed to create temp dir"); int dupfd = dup(STDERR_FILENO); TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed"); errno = 0; int ret = ifd_reopen(dupfd, dirpath, O_WRONLY, 0); TEST_ASSERT_TRUE_MESSAGE(ret < 0, "ifd_reopen should fail for directory with O_WRONLY"); /* We won't be strict about errno value, different platforms may set EISDIR or EACCES. */ close(dupfd); rmdir_if_exists(dirpath); } /* Test 3: Verify that pending info signals are processed during ifd_reopen (process_signals is called). */ void test_ifd_reopen_processes_pending_infosignal(void) { /* Set a pending info signal. */ info_signal_count = 1; status_level = 1; /* STATUS_NONE to avoid output during stats printing. */ char tmpl[] = "/tmp/dd_ifd_reopen_file_XXXXXX"; char *path = make_temp_file(tmpl); TEST_ASSERT_MESSAGE(path != NULL, "mkstemp failed to create temp file"); int dupfd = dup(STDERR_FILENO); TEST_ASSERT_MESSAGE(dupfd >= 0, "dup failed"); errno = 0; int ret = ifd_reopen(dupfd, path, O_WRONLY | O_CREAT | O_TRUNC, 0600); TEST_ASSERT_EQUAL_MESSAGE(0, ret, "ifd_reopen should succeed"); /* The pending info signal count should have been consumed by process_signals(). */ TEST_ASSERT_EQUAL_INT_MESSAGE(0, (int)info_signal_count, "info_signal_count should be 0 after ifd_reopen"); close(dupfd); remove_file_if_exists(path); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_ifd_reopen_success_reopens_fd); RUN_TEST(test_ifd_reopen_failure_returns_error); RUN_TEST(test_ifd_reopen_processes_pending_infosignal); return UNITY_END(); }