| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <stdbool.h> |
|
|
| |
| |
| |
| |
| |
| |
|
|
| void setUp(void) { |
| |
| } |
| void tearDown(void) { |
| |
| } |
|
|
| |
| static void append_char(char **buf, size_t *len, size_t *cap, char c) { |
| if (*len + 1 >= *cap) { |
| size_t ncap = (*cap == 0 ? 128 : (*cap * 2)); |
| char *nbuf = (char *)realloc(*buf, ncap); |
| if (!nbuf) { |
| |
| fprintf(stderr, "OOM in test helper\n"); |
| abort(); |
| } |
| *buf = nbuf; |
| *cap = ncap; |
| } |
| (*buf)[(*len)++] = c; |
| } |
|
|
| |
| static void append_str(char **buf, size_t *len, size_t *cap, const char *s) { |
| while (*s) { |
| append_char(buf, len, cap, *s++); |
| } |
| } |
|
|
| |
| |
| |
| static void append_space_run(char **buf, size_t *len, size_t *cap, |
| int *col, int target, bool use_tabs) { |
| if (target <= *col) return; |
| int out_column = *col; |
| int space_target = target; |
| if (use_tabs) { |
| int tab_target = (space_target / 8) * 8; |
| if (out_column + 1 < tab_target) { |
| while (out_column < tab_target) { |
| append_char(buf, len, cap, '\t'); |
| out_column = (out_column / 8 + 1) * 8; |
| } |
| } |
| } |
| while (out_column < space_target) { |
| append_char(buf, len, cap, ' '); |
| out_column++; |
| } |
| *col = out_column; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| static char *build_expected_output(const char *trimmed_prefix, int prefix_len, |
| int prefix_ind, int indent, |
| const char **words_texts, const int *spaces, size_t nwords, |
| bool use_tabs, int *out_expected_len) { |
| char *buf = NULL; |
| size_t len = 0, cap = 0; |
| int col = 0; |
|
|
| |
| append_space_run(&buf, &len, &cap, &col, prefix_ind, use_tabs); |
|
|
| |
| if (trimmed_prefix && *trimmed_prefix) { |
| append_str(&buf, &len, &cap, trimmed_prefix); |
| col += prefix_len; |
| } |
|
|
| |
| append_space_run(&buf, &len, &cap, &col, indent, use_tabs); |
|
|
| |
| for (size_t i = 0; i < nwords; i++) { |
| const char *w = words_texts[i]; |
| append_str(&buf, &len, &cap, w); |
| col += (int)strlen(w); |
| if (i + 1 < nwords) { |
| int target = col + spaces[i]; |
| append_space_run(&buf, &len, &cap, &col, target, use_tabs); |
| } |
| } |
|
|
| |
| append_char(&buf, &len, &cap, '\n'); |
|
|
| append_char(&buf, &len, &cap, '\0'); |
| if (out_expected_len) *out_expected_len = col; |
| return buf; |
| } |
|
|
| |
| |
| static char *run_put_line_case(WORD *start_word, size_t nwords, |
| int indent, |
| const char *prefix_original, |
| int prefix_ind, |
| bool use_tabs) { |
| |
| |
| char *error_msg = NULL; |
|
|
| char *prefix_buf = NULL; |
| size_t pblen = strlen(prefix_original); |
| prefix_buf = (char *)malloc(pblen + 1); |
| if (!prefix_buf) { |
| error_msg = strdup("OOM allocating prefix buffer"); |
| return error_msg; |
| } |
| memcpy(prefix_buf, prefix_original, pblen + 1); |
| |
| const char *old_prefix = prefix; |
| int old_prefix_length = prefix_length; |
| int old_prefix_indent = prefix_indent; |
| bool old_tabs = tabs; |
|
|
| set_prefix(prefix_buf); |
| prefix_indent = prefix_ind; |
| tabs = use_tabs; |
|
|
| |
| const char *wtexts_stack[64]; |
| int wspaces_stack[64]; |
| if (nwords > 64) { |
| free(prefix_buf); |
| prefix = old_prefix; prefix_length = old_prefix_length; prefix_indent = old_prefix_indent; tabs = old_tabs; |
| return strdup("Too many words for test helper"); |
| } |
| for (size_t i = 0; i < nwords; i++) { |
| wtexts_stack[i] = start_word[i].text; |
| wspaces_stack[i] = start_word[i].space; |
| } |
|
|
| int expected_len = 0; |
| char *expected = build_expected_output(prefix, prefix_length, prefix_indent, indent, |
| wtexts_stack, wspaces_stack, nwords, use_tabs, |
| &expected_len); |
| if (!expected) { |
| free(prefix_buf); |
| prefix = old_prefix; prefix_length = old_prefix_length; prefix_indent = old_prefix_indent; tabs = old_tabs; |
| return strdup("Failed to build expected output"); |
| } |
|
|
| |
| fflush(stdout); |
| int saved_stdout_fd = dup(fileno(stdout)); |
| if (saved_stdout_fd < 0) { |
| free(prefix_buf); |
| free(expected); |
| prefix = old_prefix; prefix_length = old_prefix_length; prefix_indent = old_prefix_indent; tabs = old_tabs; |
| char buf[128]; |
| snprintf(buf, sizeof buf, "dup stdout failed: %s", strerror(errno)); |
| return strdup(buf); |
| } |
|
|
| FILE *tmp = tmpfile(); |
| if (!tmp) { |
| free(prefix_buf); |
| free(expected); |
| close(saved_stdout_fd); |
| char buf[128]; |
| snprintf(buf, sizeof buf, "tmpfile failed: %s", strerror(errno)); |
| return strdup(buf); |
| } |
|
|
| if (dup2(fileno(tmp), fileno(stdout)) < 0) { |
| free(prefix_buf); |
| free(expected); |
| fclose(tmp); |
| close(saved_stdout_fd); |
| char buf[128]; |
| snprintf(buf, sizeof buf, "dup2 to stdout failed: %s", strerror(errno)); |
| return strdup(buf); |
| } |
|
|
| |
| start_word->next_break = start_word + nwords; |
|
|
| |
| put_line(start_word, indent); |
|
|
| |
| fflush(stdout); |
| fseek(tmp, 0, SEEK_END); |
| long sz = ftell(tmp); |
| if (sz < 0) sz = 0; |
| fseek(tmp, 0, SEEK_SET); |
|
|
| char *got = (char *)malloc((size_t)sz + 1); |
| if (!got) { |
| |
| dup2(saved_stdout_fd, fileno(stdout)); |
| close(saved_stdout_fd); |
| fclose(tmp); |
| free(prefix_buf); |
| free(expected); |
| return strdup("OOM allocating capture buffer"); |
| } |
| size_t rd = fread(got, 1, (size_t)sz, tmp); |
| got[rd] = '\0'; |
|
|
| |
| dup2(saved_stdout_fd, fileno(stdout)); |
| close(saved_stdout_fd); |
| fclose(tmp); |
|
|
| |
| if (strcmp(got, expected) != 0) { |
| size_t emlen = strlen(expected); |
| size_t gmlen = strlen(got); |
| |
| size_t msgcap = emlen + gmlen + 256; |
| error_msg = (char *)malloc(msgcap); |
| if (!error_msg) { |
| error_msg = strdup("Mismatch and OOM building error message"); |
| } else { |
| snprintf(error_msg, msgcap, |
| "Output mismatch.\nExpected(%zu): [%s]\nGot (%zu): [%s]", |
| emlen, expected, gmlen, got); |
| } |
| free(got); |
| free(expected); |
| free(prefix_buf); |
| |
| prefix = old_prefix; prefix_length = old_prefix_length; prefix_indent = old_prefix_indent; tabs = old_tabs; |
| return error_msg; |
| } |
|
|
| |
| if (last_line_length != expected_len) { |
| char bufmsg[128]; |
| snprintf(bufmsg, sizeof bufmsg, |
| "last_line_length mismatch: expected %d, got %d", |
| expected_len, last_line_length); |
| error_msg = strdup(bufmsg); |
| } |
|
|
| free(got); |
| free(expected); |
| free(prefix_buf); |
|
|
| |
| prefix = old_prefix; prefix_length = old_prefix_length; prefix_indent = old_prefix_indent; tabs = old_tabs; |
|
|
| return error_msg; |
| } |
|
|
| |
| static void init_word(WORD *w, const char *text, int space_after) { |
| w->text = text; |
| w->length = (int)strlen(text); |
| w->space = space_after; |
| w->paren = 0; |
| w->period = 0; |
| w->punct = 0; |
| w->final = 0; |
| w->line_length = 0; |
| w->best_cost = 0; |
| w->next_break = NULL; |
| } |
|
|
| |
|
|
| static void test_put_line_single_word_no_prefix_no_tabs_impl(void) { |
| WORD arr[2]; |
| init_word(&arr[0], "Hello", 1); |
| |
|
|
| char *err = run_put_line_case(&arr[0], 1, 0, |
| "", 0, false); |
| if (err) { |
| TEST_FAIL_MESSAGE(err); |
| free(err); |
| } |
| } |
|
|
| void test_put_line_single_word_no_prefix_no_tabs(void) { |
| test_put_line_single_word_no_prefix_no_tabs_impl(); |
| } |
|
|
| static void test_put_line_multiple_words_with_prefix_spaces_impl(void) { |
| WORD arr[3]; |
| init_word(&arr[0], "A", 1); |
| init_word(&arr[1], "BB", 1); |
|
|
| |
| char *err = run_put_line_case(&arr[0], 2, 8, |
| " ## ", 2, false); |
| if (err) { |
| TEST_FAIL_MESSAGE(err); |
| free(err); |
| } |
| } |
|
|
| void test_put_line_multiple_words_with_prefix_spaces(void) { |
| test_put_line_multiple_words_with_prefix_spaces_impl(); |
| } |
|
|
| static void test_put_line_tabs_for_indent_impl(void) { |
| WORD arr[3]; |
| init_word(&arr[0], "X", 1); |
| init_word(&arr[1], "Y", 1); |
|
|
| char *err = run_put_line_case(&arr[0], 2, 16, |
| "", 0, true); |
| if (err) { |
| TEST_FAIL_MESSAGE(err); |
| free(err); |
| } |
| } |
|
|
| void test_put_line_tabs_for_indent(void) { |
| test_put_line_tabs_for_indent_impl(); |
| } |
|
|
| static void test_put_line_mixed_prefix_indent_tabs_and_spaces_impl(void) { |
| WORD arr[3]; |
| init_word(&arr[0], "Hi", 2); |
| init_word(&arr[1], "There", 1); |
|
|
| char *err = run_put_line_case(&arr[0], 2, 18, |
| "", 3, true); |
| if (err) { |
| TEST_FAIL_MESSAGE(err); |
| free(err); |
| } |
| } |
|
|
| void test_put_line_mixed_prefix_indent_tabs_and_spaces(void) { |
| test_put_line_mixed_prefix_indent_tabs_and_spaces_impl(); |
| } |
|
|
| static void test_put_line_no_extra_space_after_prefix_impl(void) { |
| WORD arr[2]; |
| init_word(&arr[0], "Y", 1); |
|
|
| |
| char *err = run_put_line_case(&arr[0], 1, 1, |
| "X", 0, false); |
| if (err) { |
| TEST_FAIL_MESSAGE(err); |
| free(err); |
| } |
| } |
|
|
| void test_put_line_no_extra_space_after_prefix(void) { |
| test_put_line_no_extra_space_after_prefix_impl(); |
| } |
|
|
| int main(void) { |
| UNITY_BEGIN(); |
| RUN_TEST(test_put_line_single_word_no_prefix_no_tabs); |
| RUN_TEST(test_put_line_multiple_words_with_prefix_spaces); |
| RUN_TEST(test_put_line_tabs_for_indent); |
| RUN_TEST(test_put_line_mixed_prefix_indent_tabs_and_spaces); |
| RUN_TEST(test_put_line_no_extra_space_after_prefix); |
| return UNITY_END(); |
| } |