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