| | #include "../../unity/unity.h" |
| | #include <stdio.h> |
| | #include <stdlib.h> |
| | #include <string.h> |
| | #include <unistd.h> |
| | #include <errno.h> |
| |
|
| | |
| | |
| | |
| | static char *read_all_from_FILE(FILE *f, size_t *len_out) |
| | { |
| | if (!f || !len_out) |
| | return NULL; |
| |
|
| | if (fflush(f) != 0) |
| | return NULL; |
| |
|
| | if (fseek(f, 0, SEEK_END) != 0) |
| | return NULL; |
| | long end = ftell(f); |
| | if (end < 0) |
| | return NULL; |
| | if (fseek(f, 0, SEEK_SET) != 0) |
| | return NULL; |
| |
|
| | size_t len = (size_t)end; |
| | char *buf = (char *)malloc(len + 1); |
| | if (!buf) |
| | return NULL; |
| |
|
| | size_t n = fread(buf, 1, len, f); |
| | if (n != len) |
| | { |
| | free(buf); |
| | return NULL; |
| | } |
| | buf[len] = '\0'; |
| | *len_out = len; |
| | return buf; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | static char *capture_stdout_from_wrap(const char *buffer, idx_t len, |
| | idx_t wrap_column, idx_t *current_column, |
| | FILE *out_stream, size_t *captured_len) |
| | { |
| | if (!captured_len) |
| | return NULL; |
| |
|
| | FILE *cap = tmpfile(); |
| | if (!cap) |
| | return NULL; |
| |
|
| | int saved = dup(fileno(stdout)); |
| | if (saved < 0) |
| | { |
| | fclose(cap); |
| | return NULL; |
| | } |
| |
|
| | int cap_fd = fileno(cap); |
| | if (cap_fd < 0) |
| | { |
| | close(saved); |
| | fclose(cap); |
| | return NULL; |
| | } |
| |
|
| | if (fflush(stdout) != 0) |
| | { |
| | close(saved); |
| | fclose(cap); |
| | return NULL; |
| | } |
| |
|
| | if (dup2(cap_fd, fileno(stdout)) < 0) |
| | { |
| | close(saved); |
| | fclose(cap); |
| | return NULL; |
| | } |
| |
|
| | |
| | wrap_write(buffer, len, wrap_column, current_column, out_stream); |
| |
|
| | |
| | fflush(stdout); |
| | if (dup2(saved, fileno(stdout)) < 0) |
| | { |
| | close(saved); |
| | fclose(cap); |
| | return NULL; |
| | } |
| | close(saved); |
| |
|
| | |
| | size_t outlen = 0; |
| | char *captured = read_all_from_FILE(cap, &outlen); |
| | fclose(cap); |
| |
|
| | if (!captured) |
| | return NULL; |
| |
|
| | *captured_len = outlen; |
| | return captured; |
| | } |
| |
|
| | void setUp(void) { |
| | |
| | } |
| |
|
| | void tearDown(void) { |
| | |
| | } |
| |
|
| | |
| | void test_wrap_write_no_wrap(void) |
| | { |
| | const char *input = "hello"; |
| | idx_t wrap = 0; |
| | idx_t cur = (idx_t)7; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap(input, (idx_t)strlen(input), wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL_MESSAGE(cap, "Failed to capture stdout"); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(input), (uint64_t)cap_len); |
| | TEST_ASSERT_EQUAL_MEMORY(input, cap, cap_len); |
| |
|
| | |
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)out_len); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(7, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_simple_partial_line(void) |
| | { |
| | const char *input = "abcde"; |
| | idx_t wrap = (idx_t)10; |
| | idx_t cur = (idx_t)0; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap(input, (idx_t)strlen(input), wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL(cap); |
| |
|
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(input), (uint64_t)cap_len); |
| | TEST_ASSERT_EQUAL_MEMORY(input, cap, cap_len); |
| |
|
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)out_len); |
| |
|
| | TEST_ASSERT_EQUAL_INT(5, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_crosses_boundary(void) |
| | { |
| | const char *input = "WXYZ"; |
| | idx_t wrap = (idx_t)5; |
| | idx_t cur = (idx_t)3; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap(input, (idx_t)strlen(input), wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL(cap); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(input), (uint64_t)cap_len); |
| | TEST_ASSERT_EQUAL_MEMORY(input, cap, cap_len); |
| |
|
| | |
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(1, (uint64_t)out_len); |
| | TEST_ASSERT_EQUAL_CHAR('\n', out_buf[0]); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(2, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_multiple_wraps(void) |
| | { |
| | const char *input = "abcdefghij"; |
| | idx_t wrap = (idx_t)4; |
| | idx_t cur = (idx_t)0; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap(input, (idx_t)strlen(input), wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL(cap); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(input), (uint64_t)cap_len); |
| | TEST_ASSERT_EQUAL_MEMORY(input, cap, cap_len); |
| |
|
| | |
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(2, (uint64_t)out_len); |
| | TEST_ASSERT_EQUAL_CHAR('\n', out_buf[0]); |
| | TEST_ASSERT_EQUAL_CHAR('\n', out_buf[1]); |
| |
|
| | |
| | TEST_ASSERT_EQUAL_INT(2, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_exact_fill_then_next_call_emits_newline(void) |
| | { |
| | idx_t wrap = (idx_t)4; |
| | idx_t cur = (idx_t)2; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | |
| | const char *first = "XY"; |
| | size_t cap1_len = 0; |
| | char *cap1 = capture_stdout_from_wrap(first, (idx_t)strlen(first), wrap, &cur, out, &cap1_len); |
| | TEST_ASSERT_NOT_NULL(cap1); |
| |
|
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(first), (uint64_t)cap1_len); |
| | TEST_ASSERT_EQUAL_MEMORY(first, cap1, cap1_len); |
| |
|
| | size_t out_len1 = 0; |
| | char *out_buf1 = read_all_from_FILE(out, &out_len1); |
| | TEST_ASSERT_NOT_NULL(out_buf1); |
| | TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)out_len1); |
| | TEST_ASSERT_EQUAL_INT(4, (int)cur); |
| |
|
| | free(cap1); |
| | free(out_buf1); |
| |
|
| | |
| | const char *second = "Z"; |
| | size_t cap2_len = 0; |
| | char *cap2 = capture_stdout_from_wrap(second, (idx_t)strlen(second), wrap, &cur, out, &cap2_len); |
| | TEST_ASSERT_NOT_NULL(cap2); |
| |
|
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(second), (uint64_t)cap2_len); |
| | TEST_ASSERT_EQUAL_MEMORY(second, cap2, cap2_len); |
| |
|
| | size_t out_len2 = 0; |
| | char *out_buf2 = read_all_from_FILE(out, &out_len2); |
| | TEST_ASSERT_NOT_NULL(out_buf2); |
| | TEST_ASSERT_EQUAL_UINT64(1, (uint64_t)out_len2); |
| | TEST_ASSERT_EQUAL_CHAR('\n', out_buf2[0]); |
| |
|
| | TEST_ASSERT_EQUAL_INT(1, (int)cur); |
| |
|
| | free(cap2); |
| | free(out_buf2); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_zero_length_no_effect(void) |
| | { |
| | idx_t wrap = (idx_t)6; |
| | idx_t cur = (idx_t)3; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap("", (idx_t)0, wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL(cap); |
| |
|
| | TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)cap_len); |
| |
|
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(0, (uint64_t)out_len); |
| |
|
| | TEST_ASSERT_EQUAL_INT(3, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | |
| | void test_wrap_write_starts_full_line_emits_newline_first(void) |
| | { |
| | const char *input = "123"; |
| | idx_t wrap = (idx_t)4; |
| | idx_t cur = (idx_t)4; |
| |
|
| | FILE *out = tmpfile(); |
| | TEST_ASSERT_NOT_NULL(out); |
| |
|
| | size_t cap_len = 0; |
| | char *cap = capture_stdout_from_wrap(input, (idx_t)strlen(input), wrap, &cur, out, &cap_len); |
| | TEST_ASSERT_NOT_NULL(cap); |
| |
|
| | TEST_ASSERT_EQUAL_UINT64((uint64_t)strlen(input), (uint64_t)cap_len); |
| | TEST_ASSERT_EQUAL_MEMORY(input, cap, cap_len); |
| |
|
| | size_t out_len = 0; |
| | char *out_buf = read_all_from_FILE(out, &out_len); |
| | TEST_ASSERT_NOT_NULL(out_buf); |
| | TEST_ASSERT_EQUAL_UINT64(1, (uint64_t)out_len); |
| | TEST_ASSERT_EQUAL_CHAR('\n', out_buf[0]); |
| |
|
| | TEST_ASSERT_EQUAL_INT(3, (int)cur); |
| |
|
| | free(cap); |
| | free(out_buf); |
| | fclose(out); |
| | } |
| |
|
| | int main(void) |
| | { |
| | UNITY_BEGIN(); |
| |
|
| | RUN_TEST(test_wrap_write_no_wrap); |
| | RUN_TEST(test_wrap_write_simple_partial_line); |
| | RUN_TEST(test_wrap_write_crosses_boundary); |
| | RUN_TEST(test_wrap_write_multiple_wraps); |
| | RUN_TEST(test_wrap_write_exact_fill_then_next_call_emits_newline); |
| | RUN_TEST(test_wrap_write_zero_length_no_effect); |
| | RUN_TEST(test_wrap_write_starts_full_line_emits_newline_first); |
| |
|
| | return UNITY_END(); |
| | } |