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