| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <stdint.h> |
| #include <stdbool.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <sys/stat.h> |
| #include <sys/types.h> |
|
|
| |
| |
| |
| |
|
|
| |
| void setUp(void) { |
| |
| } |
| void tearDown(void) { |
| |
| } |
|
|
| |
| |
| static int create_temp_file_with_content(const void *data, size_t len) |
| { |
| char tmpl[] = "/tmp/head_bytes_test_XXXXXX"; |
| int fd = mkstemp(tmpl); |
| if (fd < 0) { |
| return -1; |
| } |
|
|
| |
| unlink(tmpl); |
|
|
| |
| size_t off = 0; |
| while (off < len) { |
| ssize_t w = write(fd, (const char*)data + off, len - off); |
| if (w < 0) { |
| close(fd); |
| return -1; |
| } |
| off += (size_t)w; |
| } |
|
|
| |
| if (lseek(fd, 0, SEEK_SET) < 0) { |
| close(fd); |
| return -1; |
| } |
|
|
| return fd; |
| } |
|
|
| |
| |
| typedef struct { |
| char *data; |
| size_t len; |
| bool func_ret; |
| } CaptureResult; |
|
|
| static CaptureResult capture_head_bytes_output(int fd, uintmax_t n_bytes, const char *filename) |
| { |
| CaptureResult out = { NULL, 0, false }; |
| int saved_stdout = dup(STDOUT_FILENO); |
| if (saved_stdout < 0) { |
| return out; |
| } |
|
|
| int pipefd[2]; |
| if (pipe(pipefd) != 0) { |
| close(saved_stdout); |
| return out; |
| } |
|
|
| fflush(stdout); |
|
|
| |
| if (dup2(pipefd[1], STDOUT_FILENO) < 0) { |
| close(saved_stdout); |
| close(pipefd[0]); |
| close(pipefd[1]); |
| return out; |
| } |
| |
| close(pipefd[1]); |
|
|
| |
| bool r = head_bytes(filename, fd, n_bytes); |
|
|
| |
| fflush(stdout); |
|
|
| |
| dup2(saved_stdout, STDOUT_FILENO); |
| close(saved_stdout); |
|
|
| |
| size_t cap = 1024; |
| char *buf = (char *)malloc(cap); |
| if (!buf) { |
| close(pipefd[0]); |
| out.func_ret = r; |
| return out; |
| } |
| size_t total = 0; |
| while (1) { |
| if (total == cap) { |
| size_t new_cap = cap * 2; |
| char *nb = (char *)realloc(buf, new_cap); |
| if (!nb) { |
| free(buf); |
| close(pipefd[0]); |
| out.func_ret = r; |
| return out; |
| } |
| buf = nb; |
| cap = new_cap; |
| } |
| ssize_t nr = read(pipefd[0], buf + total, cap - total); |
| if (nr < 0) { |
| free(buf); |
| close(pipefd[0]); |
| out.func_ret = r; |
| return out; |
| } |
| if (nr == 0) { |
| break; |
| } |
| total += (size_t)nr; |
| } |
| close(pipefd[0]); |
|
|
| out.data = buf; |
| out.len = total; |
| out.func_ret = r; |
| return out; |
| } |
|
|
| |
|
|
| static void free_capture(CaptureResult *c) { |
| if (c && c->data) { |
| free(c->data); |
| c->data = NULL; |
| } |
| } |
|
|
| |
| void test_head_bytes_zero_bytes(void) |
| { |
| const char *content = "abcdef"; |
| int fd = create_temp_file_with_content(content, strlen(content)); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| CaptureResult cap = capture_head_bytes_output(fd, 0, "temp-zero"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)0, (uint64_t)cap.len); |
| free_capture(&cap); |
| } |
|
|
| |
| void test_head_bytes_less_than_file_size(void) |
| { |
| const char *content = "Hello, Unity!"; |
| size_t content_len = strlen(content); |
| int fd = create_temp_file_with_content(content, content_len); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| uintmax_t req = 5; |
| CaptureResult cap = capture_head_bytes_output(fd, req, "temp-partial"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)req, (uint64_t)cap.len); |
| TEST_ASSERT_EQUAL_INT(0, memcmp(content, cap.data, (size_t)req)); |
| free_capture(&cap); |
| } |
|
|
| |
| void test_head_bytes_exact_file_size(void) |
| { |
| const char *content = "exactsize"; |
| size_t content_len = strlen(content); |
| int fd = create_temp_file_with_content(content, content_len); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| CaptureResult cap = capture_head_bytes_output(fd, content_len, "temp-exact"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)content_len, (uint64_t)cap.len); |
| TEST_ASSERT_EQUAL_INT(0, memcmp(content, cap.data, content_len)); |
| free_capture(&cap); |
| } |
|
|
| |
| void test_head_bytes_more_than_file_size(void) |
| { |
| const char *content = "short"; |
| size_t content_len = strlen(content); |
| int fd = create_temp_file_with_content(content, content_len); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| uintmax_t req = content_len + 10; |
| CaptureResult cap = capture_head_bytes_output(fd, req, "temp-more"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)content_len, (uint64_t)cap.len); |
| TEST_ASSERT_EQUAL_INT(0, memcmp(content, cap.data, content_len)); |
| free_capture(&cap); |
| } |
|
|
| |
| void test_head_bytes_empty_file_request_nonzero(void) |
| { |
| int fd = create_temp_file_with_content("", 0); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| CaptureResult cap = capture_head_bytes_output(fd, 100, "temp-empty"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)0, (uint64_t)cap.len); |
| free_capture(&cap); |
| } |
|
|
| |
| void test_head_bytes_large_multi_buffer(void) |
| { |
| size_t biglen = (size_t)(3 * BUFSIZ + 123); |
| char *bigbuf = (char *)malloc(biglen); |
| TEST_ASSERT_NOT_NULL_MESSAGE(bigbuf, "Allocation failed for big buffer"); |
| |
| for (size_t i = 0; i < biglen; i++) { |
| bigbuf[i] = (char)('A' + (int)(i % 26)); |
| } |
|
|
| int fd = create_temp_file_with_content(bigbuf, biglen); |
| TEST_ASSERT_MESSAGE(fd >= 0, "Failed to create temp file"); |
|
|
| uintmax_t req = (uintmax_t)(2 * BUFSIZ + 10); |
| CaptureResult cap = capture_head_bytes_output(fd, req, "temp-large"); |
| close(fd); |
|
|
| TEST_ASSERT_TRUE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)req, (uint64_t)cap.len); |
| TEST_ASSERT_EQUAL_INT(0, memcmp(bigbuf, cap.data, (size_t)req)); |
|
|
| free_capture(&cap); |
| free(bigbuf); |
| } |
|
|
| |
| void test_head_bytes_read_error_returns_false_and_no_output(void) |
| { |
| int invalid_fd = -1; |
| CaptureResult cap = capture_head_bytes_output(invalid_fd, 100, "invalid-fd"); |
| TEST_ASSERT_FALSE(cap.func_ret); |
| TEST_ASSERT_EQUAL_UINT64((uint64_t)0, (uint64_t)cap.len); |
| free_capture(&cap); |
| } |
|
|
| int main(void) |
| { |
| UNITY_BEGIN(); |
| RUN_TEST(test_head_bytes_zero_bytes); |
| RUN_TEST(test_head_bytes_less_than_file_size); |
| RUN_TEST(test_head_bytes_exact_file_size); |
| RUN_TEST(test_head_bytes_more_than_file_size); |
| RUN_TEST(test_head_bytes_empty_file_request_nonzero); |
| RUN_TEST(test_head_bytes_large_multi_buffer); |
| RUN_TEST(test_head_bytes_read_error_returns_false_and_no_output); |
| return UNITY_END(); |
| } |