#include "../../unity/unity.h" #include #include #include #include #include #include /* Unity hooks */ void setUp(void) { /* No global setup needed */ } void tearDown(void) { /* No global teardown needed */ } /* Helper to capture output of put_space while respecting the redirection rule. It sets tabs and out_column as requested, calls put_space(space), captures the emitted bytes into 'out' (NUL-terminated), returns the final out_column via final_col, and the captured byte count via out_len. Returns NULL on success, or a static error message string on failure. */ static char* capture_put_space_call(int start_col, bool tabs_flag, int space, char* out, size_t out_cap, int* final_col, size_t* out_len) { static char errbuf[128]; if (!out || out_cap == 0 || !final_col || !out_len) { return "invalid arguments"; } /* Save original globals */ bool tabs_saved = tabs; int out_saved = out_column; /* Prepare redirection */ fflush(stdout); int saved_fd = dup(fileno(stdout)); if (saved_fd < 0) { snprintf(errbuf, sizeof(errbuf), "dup failed: %s", strerror(errno)); return errbuf; } FILE* tmp = tmpfile(); if (!tmp) { close(saved_fd); snprintf(errbuf, sizeof(errbuf), "tmpfile failed: %s", strerror(errno)); return errbuf; } int tmpfd = fileno(tmp); if (dup2(tmpfd, fileno(stdout)) < 0) { fclose(tmp); close(saved_fd); snprintf(errbuf, sizeof(errbuf), "dup2 failed: %s", strerror(errno)); return errbuf; } /* Set globals for this call */ tabs = tabs_flag; out_column = start_col; /* Call the function under test (do not assert while redirected) */ put_space(space); int result_col = out_column; /* Flush and read back from tmp */ fflush(stdout); long pos = ftell(tmp); if (pos < 0) pos = 0; if (fseek(tmp, 0, SEEK_SET) != 0) { /* Attempt to restore stdout before returning error */ dup2(saved_fd, fileno(stdout)); close(saved_fd); fclose(tmp); snprintf(errbuf, sizeof(errbuf), "fseek failed: %s", strerror(errno)); /* Restore globals */ tabs = tabs_saved; out_column = out_saved; return errbuf; } size_t toread = (size_t)pos; if (toread >= out_cap) toread = out_cap - 1; size_t nread = fread(out, 1, toread, tmp); out[nread] = '\0'; *out_len = nread; /* Restore stdout */ if (dup2(saved_fd, fileno(stdout)) < 0) { close(saved_fd); fclose(tmp); snprintf(errbuf, sizeof(errbuf), "restore dup2 failed: %s", strerror(errno)); /* Restore globals */ tabs = tabs_saved; out_column = out_saved; return errbuf; } close(saved_fd); fclose(tmp); /* Restore globals except report the result of the call */ tabs = tabs_saved; out_column = out_saved; *final_col = result_col; return NULL; } /* Tests */ void test_put_space_zero_no_tabs(void) { char buf[16]; size_t n = 0; int final_col = -1; char* err = capture_put_space_call(0, false, 0, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(0, (int)n); TEST_ASSERT_EQUAL_INT(0, final_col); TEST_ASSERT_EQUAL_STRING("", buf); } void test_put_space_only_spaces_simple(void) { char buf[32]; size_t n = 0; int final_col = -1; char* err = capture_put_space_call(0, false, 5, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(5, (int)n); TEST_ASSERT_EQUAL_INT(5, final_col); TEST_ASSERT_EQUAL_STRING(" ", buf); } void test_put_space_preserves_starting_column_no_tabs(void) { char buf[32]; size_t n = 0; int final_col = -1; /* Start not at column 0; still must print exactly 'space' spaces */ char* err = capture_put_space_call(7, false, 3, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(3, (int)n); TEST_ASSERT_EQUAL_INT(10, final_col); /* 7 + 3 */ TEST_ASSERT_EQUAL_STRING(" ", buf); } void test_put_space_tabs_exact_tabstop_from_zero(void) { char buf[32]; size_t n = 0; int final_col = -1; /* From col 0 to exactly 8: expect one tab, final col 8 */ char* err = capture_put_space_call(0, true, 8, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(1, (int)n); TEST_ASSERT_EQUAL_INT(8, final_col); TEST_ASSERT_EQUAL_STRING("\t", buf); } void test_put_space_tabs_to_non_tabstop_from_zero(void) { char buf[32]; size_t n = 0; int final_col = -1; /* From col 0 to 10: expect tab (to 8) + two spaces, final col 10 */ char* err = capture_put_space_call(0, true, 10, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(3, (int)n); TEST_ASSERT_EQUAL_INT(10, final_col); TEST_ASSERT_EQUAL_STRING("\t ", buf); } void test_put_space_tabs_one_before_tabstop_no_tab_used(void) { char buf[32]; size_t n = 0; int final_col = -1; /* out_column=7, space=1 -> target=8; condition prevents tab; expect one space */ char* err = capture_put_space_call(7, true, 1, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(1, (int)n); TEST_ASSERT_EQUAL_INT(8, final_col); TEST_ASSERT_EQUAL_STRING(" ", buf); } void test_put_space_tabs_multiple_tabs_then_spaces(void) { char buf[32]; size_t n = 0; int final_col = -1; /* From 0 to 20: two tabs to 16, then 4 spaces */ char* err = capture_put_space_call(0, true, 20, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(6, (int)n); TEST_ASSERT_EQUAL_INT(20, final_col); TEST_ASSERT_EQUAL_STRING("\t\t ", buf); } void test_put_space_negative_space_no_output(void) { char buf[16]; size_t n = 0; int final_col = -1; /* Negative space should result in no output and no backward move */ char* err = capture_put_space_call(5, false, -3, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(0, (int)n); TEST_ASSERT_EQUAL_INT(5, final_col); TEST_ASSERT_EQUAL_STRING("", buf); } void test_put_space_tabs_from_nonzero_cross_one_tab(void) { char buf[32]; size_t n = 0; int final_col = -1; /* Start at col 3, target 13: tab to 8, then 5 spaces */ char* err = capture_put_space_call(3, true, 10, buf, sizeof(buf), &final_col, &n); TEST_ASSERT_MESSAGE(err == NULL, err ? err : ""); TEST_ASSERT_EQUAL_INT(6, (int)n); TEST_ASSERT_EQUAL_INT(13, final_col); TEST_ASSERT_EQUAL_STRING("\t ", buf); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_put_space_zero_no_tabs); RUN_TEST(test_put_space_only_spaces_simple); RUN_TEST(test_put_space_preserves_starting_column_no_tabs); RUN_TEST(test_put_space_tabs_exact_tabstop_from_zero); RUN_TEST(test_put_space_tabs_to_non_tabstop_from_zero); RUN_TEST(test_put_space_tabs_one_before_tabstop_no_tab_used); RUN_TEST(test_put_space_tabs_multiple_tabs_then_spaces); RUN_TEST(test_put_space_negative_space_no_output); RUN_TEST(test_put_space_tabs_from_nonzero_cross_one_tab); return UNITY_END(); }