| #include "../../unity/unity.h" |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdio.h> |
|
|
| |
| |
| |
|
|
| |
| static int test_create_temp_file(char *tmpl_out, size_t tmpl_out_size, const char *prefix, const char *data, size_t data_len) |
| { |
| char tmpl[256]; |
| snprintf(tmpl, sizeof(tmpl), "/tmp/%sXXXXXX", prefix ? prefix : "copy_cat_"); |
| if (strlen(tmpl) + 1 > tmpl_out_size) { |
| return -1; |
| } |
| strcpy(tmpl_out, tmpl); |
|
|
| int fd = mkstemp(tmpl_out); |
| if (fd < 0) { |
| return -1; |
| } |
| |
| if (ftruncate(fd, 0) != 0) { |
| close(fd); |
| unlink(tmpl_out); |
| return -1; |
| } |
| if (data && data_len > 0) { |
| ssize_t wr = write(fd, data, data_len); |
| if (wr < 0 || (size_t)wr != data_len) { |
| close(fd); |
| unlink(tmpl_out); |
| return -1; |
| } |
| |
| if (lseek(fd, 0, SEEK_SET) < 0) { |
| close(fd); |
| unlink(tmpl_out); |
| return -1; |
| } |
| } |
| return fd; |
| } |
|
|
| |
| static char *test_read_fd_all(int fd, size_t *out_len) |
| { |
| if (lseek(fd, 0, SEEK_SET) < 0) { |
| |
| } |
| const size_t chunk = 4096; |
| size_t cap = chunk; |
| size_t len = 0; |
| char *buf = (char *)malloc(cap); |
| if (!buf) return NULL; |
|
|
| while (1) { |
| if (len + chunk > cap) { |
| size_t ncap = cap * 2; |
| char *nbuf = (char *)realloc(buf, ncap); |
| if (!nbuf) { free(buf); return NULL; } |
| buf = nbuf; |
| cap = ncap; |
| } |
| ssize_t rd = read(fd, buf + len, chunk); |
| if (rd < 0) { free(buf); return NULL; } |
| if (rd == 0) break; |
| len += (size_t)rd; |
| } |
| *out_len = len; |
| return buf; |
| } |
|
|
| |
| static int test_redirect_stdout(int newfd, int *saved_out) |
| { |
| int so = dup(STDOUT_FILENO); |
| if (so < 0) return -1; |
| if (dup2(newfd, STDOUT_FILENO) < 0) { |
| int e = errno; |
| close(so); |
| errno = e; |
| return -1; |
| } |
| *saved_out = so; |
| return 0; |
| } |
|
|
| |
| static void test_restore_stdout(int saved_out) |
| { |
| dup2(saved_out, STDOUT_FILENO); |
| close(saved_out); |
| } |
|
|
| void setUp(void) { |
| |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
| |
| |
| void test_copy_cat_unsupported_or_empty_pipe_returns_0(void) |
| { |
| int pipefd[2]; |
| TEST_ASSERT_EQUAL_INT(0, pipe(pipefd)); |
| |
| close(pipefd[1]); |
|
|
| |
| infile = "pipe_in"; |
| input_desc = pipefd[0]; |
|
|
| int ret = copy_cat(); |
|
|
| |
| close(pipefd[0]); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, ret); |
| } |
|
|
| |
| |
| |
| void test_copy_cat_empty_file_returns_0_and_no_output(void) |
| { |
| char in_path[256]; |
| char out_path[256]; |
| int in_fd = -1, out_fd = -1; |
| int saved_out = -1; |
|
|
| in_fd = test_create_temp_file(in_path, sizeof(in_path), "cc_in_empty_", NULL, 0); |
| TEST_ASSERT_TRUE_MESSAGE(in_fd >= 0, "Failed to create input temp file"); |
|
|
| out_fd = test_create_temp_file(out_path, sizeof(out_path), "cc_out_empty_", NULL, 0); |
| TEST_ASSERT_TRUE_MESSAGE(out_fd >= 0, "Failed to create output temp file"); |
|
|
| |
| infile = in_path; |
| input_desc = in_fd; |
|
|
| |
| int redir_ok = test_redirect_stdout(out_fd, &saved_out); |
| TEST_ASSERT_EQUAL_INT_MESSAGE(0, redir_ok, "Failed to redirect stdout"); |
|
|
| int ret = copy_cat(); |
|
|
| |
| test_restore_stdout(saved_out); |
|
|
| |
| TEST_ASSERT_EQUAL_INT(0, ret); |
|
|
| |
| size_t out_len = 0; |
| if (lseek(out_fd, 0, SEEK_SET) >= 0) { } |
| char *out_data = test_read_fd_all(out_fd, &out_len); |
| TEST_ASSERT_NOT_NULL(out_data); |
| TEST_ASSERT_EQUAL_UINT32(0u, (unsigned int)out_len); |
| free(out_data); |
|
|
| |
| close(in_fd); |
| close(out_fd); |
| unlink(in_path); |
| unlink(out_path); |
| } |
|
|
| |
| |
| |
| void test_copy_cat_regular_files_success_or_unsupported(void) |
| { |
| const char *payload = "The quick brown fox jumps over the lazy dog.\nSecond line.\n"; |
| size_t payload_len = strlen(payload); |
|
|
| char in_path[256]; |
| char out_path[256]; |
| int in_fd = -1, out_fd = -1; |
| int saved_out = -1; |
|
|
| in_fd = test_create_temp_file(in_path, sizeof(in_path), "cc_in_", payload, payload_len); |
| TEST_ASSERT_TRUE_MESSAGE(in_fd >= 0, "Failed to create input file"); |
|
|
| out_fd = test_create_temp_file(out_path, sizeof(out_path), "cc_out_", NULL, 0); |
| TEST_ASSERT_TRUE_MESSAGE(out_fd >= 0, "Failed to create output file"); |
|
|
| infile = in_path; |
| input_desc = in_fd; |
|
|
| int redir_ok = test_redirect_stdout(out_fd, &saved_out); |
| TEST_ASSERT_EQUAL_INT_MESSAGE(0, redir_ok, "Failed to redirect stdout"); |
|
|
| int ret = copy_cat(); |
|
|
| test_restore_stdout(saved_out); |
|
|
| |
| if (lseek(out_fd, 0, SEEK_SET) >= 0) { } |
| size_t out_len = 0; |
| char *out_data = test_read_fd_all(out_fd, &out_len); |
| TEST_ASSERT_NOT_NULL(out_data); |
|
|
| if (ret == 1) { |
| |
| TEST_ASSERT_EQUAL_UINT32((unsigned int)payload_len, (unsigned int)out_len); |
| TEST_ASSERT_EQUAL_MEMORY(payload, out_data, payload_len); |
| } else if (ret == 0) { |
| |
| TEST_ASSERT_EQUAL_UINT32(0u, (unsigned int)out_len); |
| } else { |
| |
| TEST_FAIL_MESSAGE("copy_cat returned -1 (unexpected serious error)"); |
| } |
|
|
| free(out_data); |
| close(in_fd); |
| close(out_fd); |
| unlink(in_path); |
| unlink(out_path); |
| } |
|
|
| |
| void test_copy_cat_invalid_input_fd_returns_0(void) |
| { |
| infile = "invalid_fd"; |
| input_desc = -1; |
|
|
| int ret = copy_cat(); |
|
|
| TEST_ASSERT_EQUAL_INT(0, ret); |
| } |
|
|
| int main(void) |
| { |
| UNITY_BEGIN(); |
|
|
| RUN_TEST(test_copy_cat_unsupported_or_empty_pipe_returns_0); |
| RUN_TEST(test_copy_cat_empty_file_returns_0_and_no_output); |
| RUN_TEST(test_copy_cat_regular_files_success_or_unsupported); |
| RUN_TEST(test_copy_cat_invalid_input_fd_returns_0); |
|
|
| return UNITY_END(); |
| } |