| #include "../../unity/unity.h" |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <unistd.h> |
| #include <fcntl.h> |
| #include <errno.h> |
| #include <stdbool.h> |
|
|
| |
| |
| |
|
|
| |
|
|
| static char *create_temp_file_with_content(const char *content, size_t len) { |
| char tmpl[] = "/tmp/fold_test_in_XXXXXX"; |
| int fd = mkstemp(tmpl); |
| if (fd < 0) { |
| return NULL; |
| } |
| ssize_t w = write(fd, content, len); |
| if (w < 0 || (size_t)w != len) { |
| close(fd); |
| unlink(tmpl); |
| return NULL; |
| } |
| if (close(fd) != 0) { |
| unlink(tmpl); |
| return NULL; |
| } |
| |
| char *path = (char *)malloc(strlen(tmpl) + 1); |
| if (!path) { |
| unlink(tmpl); |
| return NULL; |
| } |
| strcpy(path, tmpl); |
| return path; |
| } |
|
|
| static void destroy_temp_file(char *path) { |
| if (path) { |
| unlink(path); |
| free(path); |
| } |
| } |
|
|
| typedef struct { |
| FILE *cap_file; |
| int saved_fd; |
| } StdoutCapture; |
|
|
| static int start_capture_stdout(StdoutCapture *cap) { |
| if (!cap) return -1; |
| fflush(stdout); |
| cap->saved_fd = dup(fileno(stdout)); |
| if (cap->saved_fd < 0) { |
| return -1; |
| } |
| cap->cap_file = tmpfile(); |
| if (!cap->cap_file) { |
| close(cap->saved_fd); |
| return -1; |
| } |
| if (dup2(fileno(cap->cap_file), fileno(stdout)) < 0) { |
| fclose(cap->cap_file); |
| close(cap->saved_fd); |
| return -1; |
| } |
| return 0; |
| } |
|
|
| static int stop_capture_stdout(StdoutCapture *cap) { |
| if (!cap) return -1; |
| fflush(stdout); |
| int rc = 0; |
| if (dup2(cap->saved_fd, fileno(stdout)) < 0) { |
| rc = -1; |
| } |
| close(cap->saved_fd); |
| |
| return rc; |
| } |
|
|
| static char *slurp_stream(FILE *f, size_t *out_len) { |
| if (!f) return NULL; |
| if (fseek(f, 0, SEEK_SET) != 0) return NULL; |
| size_t cap = 1024; |
| size_t len = 0; |
| char *buf = (char *)malloc(cap + 1); |
| if (!buf) return NULL; |
| for (;;) { |
| size_t space = cap - len; |
| size_t n = fread(buf + len, 1, space, f); |
| len += n; |
| if (n < space) { |
| if (feof(f)) break; |
| if (ferror(f)) { |
| free(buf); |
| return NULL; |
| } |
| } |
| if (len == cap) { |
| cap *= 2; |
| char *nb = (char *)realloc(buf, cap + 1); |
| if (!nb) { |
| free(buf); |
| return NULL; |
| } |
| buf = nb; |
| } |
| } |
| buf[len] = '\0'; |
| if (out_len) *out_len = len; |
| return buf; |
| } |
|
|
| typedef struct { |
| int saved_fd; |
| FILE *in_file; |
| } StdinRedirect; |
|
|
| static int redirect_stdin_from_path(const char *path, StdinRedirect *redir) { |
| if (!path || !redir) return -1; |
| redir->in_file = fopen(path, "r"); |
| if (!redir->in_file) return -1; |
| redir->saved_fd = dup(fileno(stdin)); |
| if (redir->saved_fd < 0) { |
| fclose(redir->in_file); |
| return -1; |
| } |
| if (dup2(fileno(redir->in_file), fileno(stdin)) < 0) { |
| close(redir->saved_fd); |
| fclose(redir->in_file); |
| return -1; |
| } |
| return 0; |
| } |
|
|
| static int restore_stdin(StdinRedirect *redir) { |
| if (!redir) return -1; |
| int rc = 0; |
| if (dup2(redir->saved_fd, fileno(stdin)) < 0) { |
| rc = -1; |
| } |
| close(redir->saved_fd); |
| fclose(redir->in_file); |
| return rc; |
| } |
|
|
| |
| void setUp(void) { |
| |
| break_spaces = false; |
| counting_mode = COUNT_COLUMNS; |
| have_read_stdin = false; |
| last_character_width = 0; |
| } |
| void tearDown(void) { |
| |
| } |
|
|
| |
|
|
| void test_fold_file_basic_no_fold(void) { |
| const char *input = "abc\n"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL_MESSAGE(path, "Failed to create temp input file"); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| bool ok = fold_file(path, 80); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
|
|
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("abc\n", out); |
| free(out); |
| } |
|
|
| void test_fold_file_basic_fold_columns(void) { |
| const char *input = "abcdef\n"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok = fold_file(path, 3); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("abc\ndef\n", out); |
| free(out); |
| } |
|
|
| void test_fold_file_no_trailing_newline(void) { |
| const char *input = "abcdef"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok = fold_file(path, 3); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("abc\ndef", out); |
| free(out); |
| } |
|
|
| void test_fold_file_break_spaces_false_vs_true(void) { |
| const char *input = "ab cd"; |
| char *path1 = create_temp_file_with_content(input, strlen(input)); |
| char *path2 = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path1); |
| TEST_ASSERT_NOT_NULL(path2); |
|
|
| |
| StdoutCapture cap1; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap1)); |
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok1 = fold_file(path1, 4); |
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap1)); |
| size_t out_len1 = 0; |
| char *out1 = slurp_stream(cap1.cap_file, &out_len1); |
| fclose(cap1.cap_file); |
| destroy_temp_file(path1); |
| TEST_ASSERT_TRUE(ok1); |
| TEST_ASSERT_NOT_NULL(out1); |
| TEST_ASSERT_EQUAL_STRING("ab c\nd", out1); |
|
|
| |
| StdoutCapture cap2; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap2)); |
| counting_mode = COUNT_COLUMNS; |
| break_spaces = true; |
| bool ok2 = fold_file(path2, 4); |
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap2)); |
| size_t out_len2 = 0; |
| char *out2 = slurp_stream(cap2.cap_file, &out_len2); |
| fclose(cap2.cap_file); |
| destroy_temp_file(path2); |
| TEST_ASSERT_TRUE(ok2); |
| TEST_ASSERT_NOT_NULL(out2); |
| TEST_ASSERT_EQUAL_STRING("ab \ncd", out2); |
|
|
| free(out1); |
| free(out2); |
| } |
|
|
| void test_fold_file_tab_handling_columns(void) { |
| const char *input = "\tX\n"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok = fold_file(path, 4); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| |
| |
| TEST_ASSERT_EQUAL_STRING("\t\nX\n", out); |
| free(out); |
| } |
|
|
| void test_fold_file_tab_handling_bytes(void) { |
| const char *input = "\tX\n"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| counting_mode = COUNT_BYTES; |
| break_spaces = false; |
| bool ok = fold_file(path, 4); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("\tX\n", out); |
| free(out); |
| } |
|
|
| void test_fold_file_read_from_stdin_and_flag_set(void) { |
| const char *input = "abc\n"; |
| char *inpath = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(inpath); |
|
|
| StdinRedirect inred; |
| TEST_ASSERT_EQUAL_INT(0, redirect_stdin_from_path(inpath, &inred)); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| have_read_stdin = false; |
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok = fold_file("-", 80); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
|
|
| TEST_ASSERT_EQUAL_INT(0, restore_stdin(&inred)); |
| destroy_temp_file(inpath); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_TRUE(have_read_stdin); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("abc\n", out); |
| free(out); |
| } |
|
|
| void test_fold_file_nonexistent_returns_false(void) { |
| |
| const char *path = "/tmp/this_file_does_not_exist_12345_abcdef.txt"; |
| bool ok = fold_file(path, 10); |
| TEST_ASSERT_FALSE(ok); |
| } |
|
|
| void test_fold_file_width_zero_edge(void) { |
| const char *input = "ab\n"; |
| char *path = create_temp_file_with_content(input, strlen(input)); |
| TEST_ASSERT_NOT_NULL(path); |
|
|
| StdoutCapture cap; |
| TEST_ASSERT_EQUAL_INT(0, start_capture_stdout(&cap)); |
|
|
| counting_mode = COUNT_COLUMNS; |
| break_spaces = false; |
| bool ok = fold_file(path, 0); |
|
|
| TEST_ASSERT_EQUAL_INT(0, stop_capture_stdout(&cap)); |
| size_t out_len = 0; |
| char *out = slurp_stream(cap.cap_file, &out_len); |
| fclose(cap.cap_file); |
| destroy_temp_file(path); |
|
|
| TEST_ASSERT_TRUE(ok); |
| TEST_ASSERT_NOT_NULL(out); |
| TEST_ASSERT_EQUAL_STRING("a\nb\n", out); |
| free(out); |
| } |
|
|
| int main(void) { |
| UNITY_BEGIN(); |
| RUN_TEST(test_fold_file_basic_no_fold); |
| RUN_TEST(test_fold_file_basic_fold_columns); |
| RUN_TEST(test_fold_file_no_trailing_newline); |
| RUN_TEST(test_fold_file_break_spaces_false_vs_true); |
| RUN_TEST(test_fold_file_tab_handling_columns); |
| RUN_TEST(test_fold_file_tab_handling_bytes); |
| RUN_TEST(test_fold_file_read_from_stdin_and_flag_set); |
| RUN_TEST(test_fold_file_nonexistent_returns_false); |
| RUN_TEST(test_fold_file_width_zero_edge); |
| return UNITY_END(); |
| } |