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