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