#include "../../unity/unity.h" #include #include #include #include #include #include #include #include #include /* Helper: create a temp file with given data, return fd and path. */ static int create_temp_file_with_data(const void *data, size_t len, char *path_buf, size_t path_buf_sz) { /* Create a template in current directory to avoid platform-specific temp dirs */ const char *tmpl = "./test_copyfd_src_XXXXXX"; if (path_buf_sz < strlen(tmpl) + 1) return -1; strcpy(path_buf, tmpl); int fd = mkstemp(path_buf); if (fd < 0) return -1; if (len > 0) { ssize_t wr = write(fd, data, len); if (wr < 0 || (size_t)wr != len) { int saved = errno; close(fd); unlink(path_buf); errno = saved; return -1; } } /* Rewind for reading */ if (lseek(fd, 0, SEEK_SET) < 0) { int saved = errno; close(fd); unlink(path_buf); errno = saved; return -1; } return fd; } /* Helper: capture stdout to a temp file, run copy_fd, restore stdout, and validate. */ static char *run_and_check_copy_fd(int src_fd, uintmax_t n_bytes, enum Copy_fd_status expected_status, const void *expected_output, size_t expected_output_len) { /* Flush stdout before redirect */ fflush(stdout); /* Save current stdout */ int saved_stdout = dup(STDOUT_FILENO); if (saved_stdout < 0) { char *msg = (char*)malloc(256); snprintf(msg, 256, "dup(STDOUT) failed: errno=%d", errno); return msg; } /* Create output capture file */ char out_tmpl[] = "./test_copyfd_out_XXXXXX"; int out_fd = mkstemp(out_tmpl); if (out_fd < 0) { close(saved_stdout); char *msg = (char*)malloc(256); snprintf(msg, 256, "mkstemp for stdout capture failed: errno=%d", errno); return msg; } /* Redirect stdout to capture file */ if (dup2(out_fd, STDOUT_FILENO) < 0) { int saved = errno; close(out_fd); close(saved_stdout); char *msg = (char*)malloc(256); snprintf(msg, 256, "dup2 to redirect stdout failed: errno=%d", saved); return msg; } close(out_fd); /* Now captured by fd 1 */ /* Call the function under test (no Unity asserts while redirected!) */ enum Copy_fd_status st = copy_fd(src_fd, n_bytes); /* Ensure all data is flushed to the capture file */ fflush(stdout); /* Restore stdout */ if (dup2(saved_stdout, STDOUT_FILENO) < 0) { int saved = errno; close(saved_stdout); char *msg = (char*)malloc(256); snprintf(msg, 256, "dup2 to restore stdout failed: errno=%d", saved); return msg; } close(saved_stdout); /* Read back captured output */ int cap_fd = open(out_tmpl, O_RDONLY); if (cap_fd < 0) { int saved = errno; unlink(out_tmpl); char *msg = (char*)malloc(256); snprintf(msg, 256, "open captured file failed: errno=%d", saved); return msg; } struct stat stbuf; if (fstat(cap_fd, &stbuf) < 0) { int saved = errno; close(cap_fd); unlink(out_tmpl); char *msg = (char*)malloc(256); snprintf(msg, 256, "fstat captured file failed: errno=%d", saved); return msg; } size_t cap_sz = (size_t)stbuf.st_size; char *cap = NULL; if (cap_sz > 0) { cap = (char*)malloc(cap_sz); if (!cap) { close(cap_fd); unlink(out_tmpl); char *msg = (char*)malloc(256); snprintf(msg, 256, "malloc(%zu) failed for capture buffer", cap_sz); return msg; } size_t off = 0; while (off < cap_sz) { ssize_t r = read(cap_fd, cap + off, cap_sz - off); if (r < 0) { int saved = errno; free(cap); close(cap_fd); unlink(out_tmpl); char *msg = (char*)malloc(256); snprintf(msg, 256, "read captured file failed: errno=%d", saved); return msg; } if (r == 0) break; off += (size_t)r; } /* cap_sz is from st_size; off should equal cap_sz. */ } close(cap_fd); unlink(out_tmpl); /* Validate status */ if (st != expected_status) { char *msg = (char*)malloc(256); snprintf(msg, 256, "Status mismatch: got %d, expected %d", (int)st, (int)expected_status); free(cap); return msg; } /* Validate output length */ if (cap_sz != expected_output_len) { char *msg = (char*)malloc(256); snprintf(msg, 256, "Output length mismatch: got %zu, expected %zu", cap_sz, expected_output_len); free(cap); return msg; } /* Validate output content if any */ if (cap_sz > 0 && memcmp(cap, expected_output, cap_sz) != 0) { /* Find first mismatch for easier debugging */ size_t idx = 0; while (idx < cap_sz && ((unsigned char*)cap)[idx] == ((const unsigned char*)expected_output)[idx]) idx++; char *msg = (char*)malloc(256); snprintf(msg, 256, "Output content mismatch at byte %zu", idx); free(cap); return msg; } free(cap); return NULL; /* success */ } void setUp(void) { /* Setup code here, or leave empty */ } void tearDown(void) { /* Cleanup code here, or leave empty */ } static void fill_pattern(char *buf, size_t len) { for (size_t i = 0; i < len; i++) buf[i] = (char)('A' + (i % 26)); } /* Tests */ void test_copy_fd_zero_bytes_no_output(void) { const char data[] = "ignored"; char path[64]; int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); /* Request 0 bytes */ char *err = run_and_check_copy_fd(fd, 0, COPY_FD_OK, "", 0); close(fd); unlink(path); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } void test_copy_fd_partial_copy(void) { const char data[] = "abcdef"; const size_t req = 3; char path[64]; int fd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); char *err = run_and_check_copy_fd(fd, req, COPY_FD_OK, data, req); close(fd); unlink(path); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } void test_copy_fd_exact_copy(void) { const char data[] = "Hello, world!"; const size_t len = sizeof(data) - 1; char path[64]; int fd = create_temp_file_with_data(data, len, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len); close(fd); unlink(path); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } void test_copy_fd_oversized_request_unexpected_eof(void) { const char data[] = "abcd"; const size_t len = sizeof(data) - 1; const uintmax_t req = 10; /* larger than len */ char path[64]; int fd = create_temp_file_with_data(data, len, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); /* Expect all available bytes written, status UNEXPECTED_EOF */ char *err = run_and_check_copy_fd(fd, req, COPY_FD_UNEXPECTED_EOF, data, len); close(fd); unlink(path); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } void test_copy_fd_read_error_returns_status(void) { /* Create a file, then open it write-only to induce read error */ const char data[] = "some data"; char path[64]; int tmpfd = create_temp_file_with_data(data, sizeof(data) - 1, path, sizeof(path)); TEST_ASSERT_MESSAGE(tmpfd >= 0, "Failed to create temp source file"); close(tmpfd); int wfd = open(path, O_WRONLY); TEST_ASSERT_MESSAGE(wfd >= 0, "Failed to open file write-only to induce read error"); /* Expect READ_ERROR and no output */ char *err = run_and_check_copy_fd(wfd, 5, COPY_FD_READ_ERROR, "", 0); close(wfd); unlink(path); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } void test_copy_fd_large_transfer_multiple_buffers(void) { size_t len = (size_t)BUFSIZ * 3 + 123; /* span multiple reads */ char *data = (char*)malloc(len); TEST_ASSERT_NOT_NULL(data); fill_pattern(data, len); char path[64]; int fd = create_temp_file_with_data(data, len, path, sizeof(path)); TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp source file"); char *err = run_and_check_copy_fd(fd, len, COPY_FD_OK, data, len); close(fd); unlink(path); free(data); TEST_ASSERT_NULL_MESSAGE(err, err ? err : ""); free(err); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_copy_fd_zero_bytes_no_output); RUN_TEST(test_copy_fd_partial_copy); RUN_TEST(test_copy_fd_exact_copy); RUN_TEST(test_copy_fd_oversized_request_unexpected_eof); RUN_TEST(test_copy_fd_read_error_returns_status); RUN_TEST(test_copy_fd_large_transfer_multiple_buffers); return UNITY_END(); }