coreutils / tests /expand /tests_for_expand.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <errno.h>
/* Helper: run expand() with given input string.
If initial_only is true, simulate -i by setting convert_entire_line = false.
Returns a newly malloc'd string with the captured output, or NULL on error. */
static char* run_expand_on_input(const char* input, bool initial_only)
{
int saved_stdin = -1;
int saved_stdout = -1;
FILE* in_file = NULL;
FILE* out_file = NULL;
int in_fd, out_fd;
char* out_buf = NULL;
/* Create temp files for input and output */
in_file = tmpfile();
if (!in_file) goto fail;
out_file = tmpfile();
if (!out_file) goto fail;
/* Write input to in_file and rewind */
if (input) {
if (fwrite(input, 1, strlen(input), in_file) != strlen(input))
goto fail;
}
if (fflush(in_file) != 0) goto fail;
if (fseek(in_file, 0, SEEK_SET) != 0) goto fail;
in_fd = fileno(in_file);
out_fd = fileno(out_file);
if (in_fd < 0 || out_fd < 0) goto fail;
/* Save original fds */
saved_stdin = dup(STDIN_FILENO);
saved_stdout = dup(STDOUT_FILENO);
if (saved_stdin < 0 || saved_stdout < 0) goto fail;
/* Redirect stdin/stdout */
if (dup2(in_fd, STDIN_FILENO) < 0) goto fail;
if (dup2(out_fd, STDOUT_FILENO) < 0) goto fail;
/* Configure initial-only behavior via global (declared in expand-common.h) */
extern bool convert_entire_line;
convert_entire_line = !initial_only;
/* Call the function under test */
expand();
/* Ensure all output is flushed to out_file before restoring stdout */
fflush(stdout);
/* Restore stdout and stdin before any assertions/logging */
if (dup2(saved_stdout, STDOUT_FILENO) < 0) goto fail;
if (dup2(saved_stdin, STDIN_FILENO) < 0) goto fail;
/* No longer need the saved fds */
close(saved_stdout); saved_stdout = -1;
close(saved_stdin); saved_stdin = -1;
/* Read captured output */
if (fflush(out_file) != 0) goto fail;
if (fseek(out_file, 0, SEEK_END) != 0) goto fail;
long sz = ftell(out_file);
if (sz < 0) goto fail;
if (fseek(out_file, 0, SEEK_SET) != 0) goto fail;
out_buf = (char*)malloc((size_t)sz + 1);
if (!out_buf) goto fail;
if (sz > 0) {
size_t rd = fread(out_buf, 1, (size_t)sz, out_file);
if (rd != (size_t)sz) goto fail;
}
out_buf[sz] = '\0';
/* Cleanup temp files */
fclose(in_file);
fclose(out_file);
return out_buf;
fail:
{
int e = errno; /* preserve */
if (saved_stdout >= 0) { dup2(saved_stdout, STDOUT_FILENO); close(saved_stdout); }
if (saved_stdin >= 0) { dup2(saved_stdin, STDIN_FILENO); close(saved_stdin); }
if (in_file) fclose(in_file);
if (out_file) fclose(out_file);
errno = e;
}
return NULL;
}
void setUp(void) {
/* Default to full-line conversion before each test */
extern bool convert_entire_line;
convert_entire_line = true;
}
void tearDown(void) {
/* Nothing to clean up */
}
/* Test: empty input -> empty output */
void test_expand_empty_input(void)
{
char* out = run_expand_on_input("", false);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING("", out);
free(out);
}
/* Test: basic single tab expansion with default tab size 8: "a<TAB>b\n" */
void test_expand_basic_single_tab(void)
{
const char* in = "a\tb\n";
const char* expected = "a b\n"; /* 7 spaces between a and b */
char* out = run_expand_on_input(in, false);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: two leading tabs expand to 16 spaces before newline */
void test_expand_two_leading_tabs(void)
{
const char* in = "\t\t\n";
const char* expected = " \n"; /* 16 spaces then newline */
char* out = run_expand_on_input(in, false);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: backspace affects column but is preserved in output */
void test_expand_backspace_behavior(void)
{
/* After "abc", backspace moves back to col 2; tab then expands to col 8:
6 spaces inserted, and backspace is preserved in output. */
const char* in = "abc\b\tZ\n";
const char* expected = "abc\b Z\n"; /* backspace + 6 spaces */
char* out = run_expand_on_input(in, false);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: initial-only mode (-i) on a single line */
void test_expand_initial_only_simple(void)
{
/* Leading space + tab should expand to 8 spaces; after 'X' a following tab must remain */
const char* in = " \tX\tY\n";
const char* expected = " X\tY\n"; /* 8 spaces, then X, then literal tab, then Y, nl */
char* out = run_expand_on_input(in, true /* initial_only */);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: initial-only mode resets at each new line */
void test_expand_initial_only_resets_each_line(void)
{
/* First line: leading tab converts; second line: leading tab converts again */
const char* in = "\tA\n\tB\n";
const char* expected = " A\n B\n";
char* out = run_expand_on_input(in, true /* initial_only */);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: no trailing newline in input */
void test_expand_no_trailing_newline(void)
{
const char* in = "A\tB";
const char* expected = "A B"; /* 7 spaces between A and B */
char* out = run_expand_on_input(in, false);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "run_expand_on_input failed");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_expand_empty_input);
RUN_TEST(test_expand_basic_single_tab);
RUN_TEST(test_expand_two_leading_tabs);
RUN_TEST(test_expand_backspace_behavior);
RUN_TEST(test_expand_initial_only_simple);
RUN_TEST(test_expand_initial_only_resets_each_line);
RUN_TEST(test_expand_no_trailing_newline);
return UNITY_END();
}