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