#include "../../unity/unity.h" #include #include #include #include #include /* We rely on internal symbols from fmt.c: - static void set_prefix(char *p); - static int copy_rest(FILE *f, int c); - static bool tabs; - static int in_column; - static int next_prefix_indent; - static char const *prefix; - static int prefix_length; These are available since this test file is included into the same translation unit. */ /* Prototypes to silence any compiler warnings (available in this TU). */ static void set_prefix (char *p); static int copy_rest (FILE *f, int c); /* Globals from the program (declared in the same TU) */ extern bool tabs; extern int in_column; extern int next_prefix_indent; /* Helper: create an input FILE* containing 'content' and rewind it. */ static FILE* make_input_stream(const char* content) { FILE* f = tmpfile(); if (!f) return NULL; size_t len = content ? strlen(content) : 0; if (len > 0) { if (fwrite(content, 1, len, f) != len) { fclose(f); return NULL; } } rewind(f); return f; } /* Helper: capture stdout while invoking copy_rest(fin, c). - Returns a heap-allocated string with the captured output (caller frees). - Writes the return value of copy_rest into *ret_c. - Returns NULL on failure (allocation or tmpfile issues). Note: No Unity assertions while stdout is redirected. */ static char* capture_copy_rest_output(FILE* fin, int c, int* ret_c) { if (!ret_c) return NULL; fflush(stdout); int saved_fd = dup(fileno(stdout)); if (saved_fd == -1) return NULL; FILE* cap = tmpfile(); if (!cap) { close(saved_fd); return NULL; } int cap_fd = fileno(cap); if (dup2(cap_fd, fileno(stdout)) == -1) { fclose(cap); close(saved_fd); return NULL; } int r = copy_rest(fin, c); fflush(stdout); fflush(cap); /* Restore stdout */ if (dup2(saved_fd, fileno(stdout)) == -1) { fclose(cap); close(saved_fd); return NULL; } close(saved_fd); /* Read captured output */ if (fseek(cap, 0, SEEK_END) != 0) { fclose(cap); return NULL; } long sz = ftell(cap); if (sz < 0) { fclose(cap); return NULL; } if (fseek(cap, 0, SEEK_SET) != 0) { fclose(cap); return NULL; } char* buf = (char*)malloc((size_t)sz + 1); if (!buf) { fclose(cap); return NULL; } size_t nread = fread(buf, 1, (size_t)sz, cap); buf[nread] = '\0'; fclose(cap); *ret_c = r; return buf; } void setUp(void) { /* Ensure deterministic spacing (no tabs in output) */ tabs = false; } void tearDown(void) { /* Nothing to clean */ } /* Test: c == '\n' with no prefix progress; expect no output and return '\n'. */ static void test_copy_rest_blank_line_no_output(void) { char pfx[] = ""; set_prefix(pfx); next_prefix_indent = 0; in_column = 0; FILE* fin = make_input_stream(""); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, '\n', &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT('\n', retc); TEST_ASSERT_EQUAL_UINT(0, strlen(out)); free(out); } /* Test: c == '\n' with some indentation and partial prefix print. */ static void test_copy_rest_newline_with_partial_prefix(void) { char pfx[] = "ABC"; set_prefix(pfx); next_prefix_indent = 2; /* two leading spaces */ in_column = 3; /* matched 'A' before newline */ FILE* fin = make_input_stream(""); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, '\n', &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT('\n', retc); /* Expect: " A" (2 spaces + 'A') */ TEST_ASSERT_EQUAL_STRING(" A", out); free(out); } /* Test: mismatch (c not '\n'/EOF) with no extra padding beyond trimmed prefix. */ static void test_copy_rest_mismatch_no_padding(void) { char pfx[] = "ABC"; set_prefix(pfx); next_prefix_indent = 2; in_column = 3; /* matched 'A' */ /* The remainder of the line (after 'X') will be "YZ\n" */ FILE* fin = make_input_stream("YZ\n"); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, 'X', &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT('\n', retc); /* Expect: " A" + 'X' + "YZ" = " AXYZ" */ TEST_ASSERT_EQUAL_STRING(" AXYZ", out); free(out); } /* Test: mismatch with in_column beyond trimmed prefix -> extra padding spaces added. */ static void test_copy_rest_mismatch_with_padding_beyond_trimmed_prefix(void) { char pfx[] = "AB "; /* trimmed prefix becomes "AB", prefix_length = 2 */ set_prefix(pfx); next_prefix_indent = 1; in_column = next_prefix_indent + 4; /* 1 (indent) + 4 matched columns */ /* After printing indent + "AB", we need (4 - 2) = 2 padding spaces. */ FILE* fin = make_input_stream("T\n"); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, 'X', &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT('\n', retc); /* Expect: " " + "AB" + " " + 'X' + "T" = " AB XT" */ TEST_ASSERT_EQUAL_STRING(" AB XT", out); free(out); } /* Test: EOF where in_column >= next_prefix_indent + prefix_length -> emits newline. */ static void test_copy_rest_eof_emits_newline_after_full_prefix(void) { char pfx[] = "HELLO"; set_prefix(pfx); next_prefix_indent = 3; in_column = next_prefix_indent + (int)strlen("HELLO"); /* full trimmed prefix matched */ FILE* fin = make_input_stream(""); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, EOF, &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT(EOF, retc); /* Expect: " HELLO\n" */ TEST_ASSERT_EQUAL_STRING(" HELLO\n", out); free(out); } /* Test: EOF where no progress beyond indent -> no output. */ static void test_copy_rest_eof_no_output_when_no_progress(void) { char pfx[] = "HELLO"; set_prefix(pfx); next_prefix_indent = 2; in_column = 2; /* no progress beyond indent */ FILE* fin = make_input_stream(""); TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, EOF, &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT(EOF, retc); TEST_ASSERT_EQUAL_UINT(0, strlen(out)); free(out); } /* Test: No prefix context (empty prefix), c not newline/EOF. */ static void test_copy_rest_no_prefix_context_non_newline(void) { char pfx[] = ""; set_prefix(pfx); next_prefix_indent = 0; in_column = 0; FILE* fin = make_input_stream("RS\n"); /* tail after 'Q' */ TEST_ASSERT_NOT_NULL(fin); int retc = 0; char* out = capture_copy_rest_output(fin, 'Q', &retc); fclose(fin); TEST_ASSERT_NOT_NULL(out); TEST_ASSERT_EQUAL_INT('\n', retc); /* Expect: 'Q' + "RS" */ TEST_ASSERT_EQUAL_STRING("QRS", out); free(out); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_copy_rest_blank_line_no_output); RUN_TEST(test_copy_rest_newline_with_partial_prefix); RUN_TEST(test_copy_rest_mismatch_no_padding); RUN_TEST(test_copy_rest_mismatch_with_padding_beyond_trimmed_prefix); RUN_TEST(test_copy_rest_eof_emits_newline_after_full_prefix); RUN_TEST(test_copy_rest_eof_no_output_when_no_progress); RUN_TEST(test_copy_rest_no_prefix_context_non_newline); return UNITY_END(); }