|
|
#include "../../unity/unity.h" |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <errno.h> |
|
|
#include <stdio.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void setup_buffer_and_blocksize(idx_t bs) |
|
|
{ |
|
|
|
|
|
if (obuf) |
|
|
{ |
|
|
free(obuf); |
|
|
obuf = NULL; |
|
|
} |
|
|
output_blocksize = bs; |
|
|
oc = 0; |
|
|
obuf = (char *)malloc((size_t)bs); |
|
|
TEST_ASSERT_NOT_NULL(obuf); |
|
|
} |
|
|
|
|
|
|
|
|
typedef struct { |
|
|
int saved_stdout; |
|
|
int pipe_rd; |
|
|
} capture_t; |
|
|
|
|
|
static capture_t begin_capture_stdout(void) |
|
|
{ |
|
|
capture_t c; |
|
|
c.saved_stdout = -1; |
|
|
c.pipe_rd = -1; |
|
|
|
|
|
int pipefd[2]; |
|
|
if (pipe(pipefd) != 0) |
|
|
{ |
|
|
TEST_FAIL_MESSAGE("pipe() failed"); |
|
|
return c; |
|
|
} |
|
|
|
|
|
fflush(stdout); |
|
|
int saved = dup(STDOUT_FILENO); |
|
|
if (saved < 0) |
|
|
{ |
|
|
close(pipefd[0]); |
|
|
close(pipefd[1]); |
|
|
TEST_FAIL_MESSAGE("dup(STDOUT_FILENO) failed"); |
|
|
return c; |
|
|
} |
|
|
if (dup2(pipefd[1], STDOUT_FILENO) < 0) |
|
|
{ |
|
|
close(saved); |
|
|
close(pipefd[0]); |
|
|
close(pipefd[1]); |
|
|
TEST_FAIL_MESSAGE("dup2() failed for STDOUT"); |
|
|
return c; |
|
|
} |
|
|
close(pipefd[1]); |
|
|
c.saved_stdout = saved; |
|
|
c.pipe_rd = pipefd[0]; |
|
|
return c; |
|
|
} |
|
|
|
|
|
static void end_capture_stdout(capture_t *c, unsigned char **out, size_t *out_len) |
|
|
{ |
|
|
|
|
|
if (dup2(c->saved_stdout, STDOUT_FILENO) < 0) |
|
|
{ |
|
|
|
|
|
} |
|
|
close(c->saved_stdout); |
|
|
|
|
|
|
|
|
size_t cap = 256; |
|
|
size_t len = 0; |
|
|
unsigned char *buf = (unsigned char *)malloc(cap); |
|
|
if (!buf) |
|
|
{ |
|
|
close(c->pipe_rd); |
|
|
TEST_FAIL_MESSAGE("malloc failed in end_capture_stdout"); |
|
|
return; |
|
|
} |
|
|
|
|
|
for (;;) |
|
|
{ |
|
|
unsigned char tmp[256]; |
|
|
ssize_t n = read(c->pipe_rd, tmp, sizeof tmp); |
|
|
if (n < 0) |
|
|
{ |
|
|
if (errno == EINTR) continue; |
|
|
free(buf); |
|
|
close(c->pipe_rd); |
|
|
TEST_FAIL_MESSAGE("read() failed on capture pipe"); |
|
|
return; |
|
|
} |
|
|
if (n == 0) |
|
|
break; |
|
|
if (len + (size_t)n > cap) |
|
|
{ |
|
|
size_t new_cap = (len + (size_t)n) * 2; |
|
|
unsigned char *nb = (unsigned char *)realloc(buf, new_cap); |
|
|
if (!nb) |
|
|
{ |
|
|
free(buf); |
|
|
close(c->pipe_rd); |
|
|
TEST_FAIL_MESSAGE("realloc failed in end_capture_stdout"); |
|
|
return; |
|
|
} |
|
|
buf = nb; |
|
|
cap = new_cap; |
|
|
} |
|
|
memcpy(buf + len, tmp, (size_t)n); |
|
|
len += (size_t)n; |
|
|
} |
|
|
close(c->pipe_rd); |
|
|
|
|
|
*out = buf; |
|
|
*out_len = len; |
|
|
} |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
oc = 0; |
|
|
w_bytes = 0; |
|
|
w_full = 0; |
|
|
w_partial = 0; |
|
|
conversions_mask = 0; |
|
|
|
|
|
if (obuf) { |
|
|
free(obuf); |
|
|
obuf = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
if (obuf) { |
|
|
free(obuf); |
|
|
obuf = NULL; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_copy_simple_partial_no_flush(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)8); |
|
|
const char *input = "12345"; |
|
|
idx_t nread = (idx_t)strlen(input); |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
|
|
|
copy_simple(input, nread); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(5, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(0, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_bytes); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(obuf, input, 5)); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
void test_copy_simple_exact_flush(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)4); |
|
|
const char *input = "ABCD"; |
|
|
idx_t nread = (idx_t)strlen(input); |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
copy_simple(input, nread); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(0, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(4, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(captured, input, 4)); |
|
|
TEST_ASSERT_EQUAL_INT(1, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(4, w_bytes); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
void test_copy_simple_more_than_block(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)4); |
|
|
const char *input = "ABCDEFG"; |
|
|
idx_t nread = (idx_t)strlen(input); |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
copy_simple(input, nread); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(3, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(4, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(captured, "ABCD", 4)); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(obuf, "EFG", 3)); |
|
|
TEST_ASSERT_EQUAL_INT(1, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(4, w_bytes); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
void test_copy_simple_with_existing_oc_and_partial_flush(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)5); |
|
|
|
|
|
|
|
|
oc = 3; |
|
|
obuf[0] = 'A'; |
|
|
obuf[1] = 'B'; |
|
|
obuf[2] = 'C'; |
|
|
|
|
|
const char *input = "DEFGH"; |
|
|
idx_t nread = (idx_t)strlen(input); |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
copy_simple(input, nread); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(3, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(5, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(captured, "ABCDE", 5)); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(obuf, "FGH", 3)); |
|
|
TEST_ASSERT_EQUAL_INT(1, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(5, w_bytes); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
void test_copy_simple_zero_length(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)5); |
|
|
|
|
|
|
|
|
oc = 2; |
|
|
obuf[0] = 'Z'; |
|
|
obuf[1] = 'Y'; |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
copy_simple("ignored", (idx_t)0); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(2, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(0, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_bytes); |
|
|
TEST_ASSERT_EQUAL_INT('Z', obuf[0]); |
|
|
TEST_ASSERT_EQUAL_INT('Y', obuf[1]); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
void test_copy_simple_multiple_full_blocks_and_remainder(void) |
|
|
{ |
|
|
setup_buffer_and_blocksize((idx_t)3); |
|
|
const char *input = "ABCDEFGHIJK"; |
|
|
idx_t nread = (idx_t)strlen(input); |
|
|
|
|
|
capture_t cap = begin_capture_stdout(); |
|
|
copy_simple(input, nread); |
|
|
unsigned char *captured = NULL; |
|
|
size_t captured_len = 0; |
|
|
end_capture_stdout(&cap, &captured, &captured_len); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT32(2, oc); |
|
|
TEST_ASSERT_EQUAL_UINT32(9, captured_len); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(captured, "ABCDEFGHI", 9)); |
|
|
TEST_ASSERT_EQUAL_INT(0, memcmp(obuf, "JK", 2)); |
|
|
TEST_ASSERT_EQUAL_INT(3, w_full); |
|
|
TEST_ASSERT_EQUAL_INT(0, w_partial); |
|
|
TEST_ASSERT_EQUAL_INT(9, w_bytes); |
|
|
|
|
|
free(captured); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_copy_simple_partial_no_flush); |
|
|
RUN_TEST(test_copy_simple_exact_flush); |
|
|
RUN_TEST(test_copy_simple_more_than_block); |
|
|
RUN_TEST(test_copy_simple_with_existing_oc_and_partial_flush); |
|
|
RUN_TEST(test_copy_simple_zero_length); |
|
|
RUN_TEST(test_copy_simple_multiple_full_blocks_and_remainder); |
|
|
return UNITY_END(); |
|
|
} |