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