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