| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
|
|
| |
| |
|
|
| |
| static bool fmt (FILE *f, char const *file); |
| static void set_prefix (char *p); |
|
|
| |
|
|
| static void test_set_width(int w) |
| { |
| |
| |
| max_width = w; |
| goal_width = (int)((long long)w * (100 - LEEWAY) / 100); |
| if (goal_width <= 0) goal_width = w; |
| } |
|
|
| static void test_reset_options(void) |
| { |
| |
| crown = false; |
| tagged = false; |
| split = false; |
| uniform = false; |
|
|
| |
| prefix = ""; |
| prefix_full_length = 0; |
| prefix_lead_space = 0; |
| prefix_length = 0; |
|
|
| |
| in_column = 0; |
| out_column = 0; |
| tabs = false; |
| prefix_indent = 0; |
| first_indent = 0; |
| other_indent = 0; |
| next_char = '\0'; |
| next_prefix_indent = 0; |
| last_line_length = 0; |
| } |
|
|
| |
| |
| |
| static char *run_fmt_and_capture(const char *input, const char *file_label, bool *out_ok) |
| { |
| |
| FILE *in = tmpfile(); |
| if (!in) return NULL; |
| size_t in_len = strlen(input); |
| if (in_len && fwrite(input, 1, in_len, in) != in_len) { |
| fclose(in); |
| return NULL; |
| } |
| fflush(in); |
| rewind(in); |
|
|
| |
| FILE *cap = tmpfile(); |
| if (!cap) { |
| fclose(in); |
| return NULL; |
| } |
| int saved_stdout_fd = dup(fileno(stdout)); |
| if (saved_stdout_fd < 0) { |
| fclose(in); |
| fclose(cap); |
| return NULL; |
| } |
| fflush(stdout); |
| if (dup2(fileno(cap), fileno(stdout)) < 0) { |
| fclose(in); |
| fclose(cap); |
| close(saved_stdout_fd); |
| return NULL; |
| } |
|
|
| |
| bool ok = fmt(in, file_label); |
|
|
| |
| fflush(stdout); |
| if (dup2(saved_stdout_fd, fileno(stdout)) < 0) { |
| |
| } |
| close(saved_stdout_fd); |
|
|
| |
| fflush(cap); |
| fseek(cap, 0, SEEK_END); |
| long cap_len = ftell(cap); |
| if (cap_len < 0) cap_len = 0; |
| fseek(cap, 0, SEEK_SET); |
|
|
| char *buf = (char *)malloc((size_t)cap_len + 1); |
| if (!buf) { |
| fclose(cap); |
| return NULL; |
| } |
| size_t nread = fread(buf, 1, (size_t)cap_len, cap); |
| buf[nread] = '\0'; |
| fclose(cap); |
|
|
| if (out_ok) *out_ok = ok; |
| return buf; |
| } |
|
|
| |
| |
| static char *normalize_words(const char *s) |
| { |
| size_t len = strlen(s); |
| |
| char *out = (char *)malloc(len + 1); |
| if (!out) return NULL; |
| size_t oi = 0; |
| int in_word = 0; |
| for (size_t i = 0; i < len; i++) { |
| unsigned char c = (unsigned char)s[i]; |
| int is_ws = (c == ' ' || c == '\t' || c == '\n' || c == '\r'); |
| if (!is_ws) { |
| out[oi++] = c; |
| in_word = 1; |
| } else { |
| if (in_word) { |
| out[oi++] = ' '; |
| in_word = 0; |
| } |
| } |
| } |
| |
| if (oi > 0 && out[oi-1] == ' ') |
| oi--; |
| out[oi] = '\0'; |
| return out; |
| } |
|
|
| |
| static size_t line_visible_length(const char *start, const char *end) |
| { |
| |
| size_t n = 0; |
| for (const char *p = start; p < end; p++) { |
| if (*p == '\n') break; |
| n++; |
| } |
| return n; |
| } |
|
|
| |
| void setUp(void) { |
| test_reset_options(); |
| test_set_width(80); |
| } |
|
|
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| void test_fmt_basic_no_wrap(void) |
| { |
| test_reset_options(); |
| test_set_width(80); |
|
|
| const char *input = |
| "Hello world\n" |
| "Second line\n" |
| "\n" |
| "Third paragraph line\n"; |
|
|
| bool ok = false; |
| char *out = run_fmt_and_capture(input, "basic_no_wrap", &ok); |
|
|
| TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture fmt output"); |
| TEST_ASSERT_TRUE_MESSAGE(ok, "fmt should succeed for simple input"); |
|
|
| |
| TEST_ASSERT_EQUAL_STRING(input, out); |
|
|
| free(out); |
| } |
|
|
| void test_fmt_wrap_lines_constraints(void) |
| { |
| test_reset_options(); |
| test_set_width(10); |
|
|
| const char *input = "one two three four five\n"; |
| bool ok = false; |
| char *out = run_fmt_and_capture(input, "wrap_constraints", &ok); |
|
|
| TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture fmt output"); |
| TEST_ASSERT_TRUE_MESSAGE(ok, "fmt should succeed"); |
|
|
| |
| const char *p = out; |
| while (*p) { |
| const char *line_end = strchr(p, '\n'); |
| if (!line_end) line_end = p + strlen(p); |
| size_t vis = line_visible_length(p, line_end); |
| TEST_ASSERT_LESS_OR_EQUAL_UINT32_MESSAGE((unsigned)max_width, (unsigned)max_width, "sanity"); |
| TEST_ASSERT_LESS_OR_EQUAL_UINT_MESSAGE((unsigned)max_width, (unsigned)vis); |
| |
| TEST_ASSERT_TRUE_MESSAGE(vis <= (size_t)max_width, "A line exceeds max_width"); |
| p = (*line_end == '\n') ? line_end + 1 : line_end; |
| } |
|
|
| |
| char *norm_out = normalize_words(out); |
| char *norm_in = normalize_words(input); |
| TEST_ASSERT_NOT_NULL(norm_out); |
| TEST_ASSERT_NOT_NULL(norm_in); |
| TEST_ASSERT_EQUAL_STRING_MESSAGE(norm_in, norm_out, "Words/content changed by formatting"); |
|
|
| free(norm_out); |
| free(norm_in); |
| free(out); |
| } |
|
|
| void test_fmt_prefix_trimming_and_reattach(void) |
| { |
| test_reset_options(); |
| test_set_width(80); |
|
|
| |
| char *pf = (char *)malloc(8); |
| TEST_ASSERT_NOT_NULL(pf); |
| strcpy(pf, " > "); |
| set_prefix(pf); |
|
|
| const char *input = |
| " >Hello\n" |
| " >world\n" |
| "\n"; |
|
|
| bool ok = false; |
| char *out = run_fmt_and_capture(input, "prefix_trim", &ok); |
|
|
| |
| prefix = ""; |
| prefix_full_length = 0; |
| prefix_lead_space = 0; |
| prefix_length = 0; |
| free(pf); |
|
|
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_TRUE_MESSAGE(ok, "fmt should succeed with prefixed lines"); |
|
|
| |
| const char *expected = |
| " >Hello\n" |
| " >world\n" |
| "\n"; |
| TEST_ASSERT_EQUAL_STRING(expected, out); |
|
|
| free(out); |
| } |
|
|
| void test_fmt_returns_false_on_read_error(void) |
| { |
| test_reset_options(); |
| test_set_width(80); |
|
|
| |
| FILE *in = tmpfile(); |
| TEST_ASSERT_NOT_NULL(in); |
| const char *input = "data\n"; |
| fwrite(input, 1, strlen(input), in); |
| fflush(in); |
| rewind(in); |
|
|
| int fd = fileno(in); |
| |
| close(fd); |
|
|
| bool ok = fmt(in, "read_error_expected"); |
| |
| TEST_ASSERT_FALSE_MESSAGE(ok, "fmt should return false on read error"); |
| } |
|
|
| |
| int main(void) |
| { |
| UNITY_BEGIN(); |
|
|
| RUN_TEST(test_fmt_basic_no_wrap); |
| RUN_TEST(test_fmt_wrap_lines_constraints); |
| RUN_TEST(test_fmt_prefix_trimming_and_reattach); |
| RUN_TEST(test_fmt_returns_false_on_read_error); |
|
|
| return UNITY_END(); |
| } |