coreutils / tests /fmt /tests_for_flush_paragraph.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
/* The test file is included after the program source, so we can access
internal symbols directly. We will set up the global state to drive
flush_paragraph(). */
/* Prototypes for functions under test (already defined above in the TU). */
/* static void flush_paragraph(void); -- available since included before */
/* Globals from the program under test (declared earlier in same TU). */
extern char parabuf[]; /* paragraph character buffer */
extern char *wptr; /* first unused char position in parabuf */
extern int max_width; /* maximum line width */
extern int goal_width; /* preferred goal width */
extern int first_indent; /* indentation first line */
extern int other_indent; /* indentation other lines */
extern int last_line_length; /* used by fmt_paragraph */
extern int prefix_indent; /* prefix indent for current paragraph */
extern char const *prefix; /* prefix string */
extern int prefix_length; /* prefix length (trimmed) */
extern int prefix_full_length; /* prefix full length */
extern int prefix_lead_space; /* prefix leading spaces */
extern int out_column; /* output column tracker */
extern int tabs; /* whether tabs can be used */
/* Word array and pointer. Note: 'word' is a macro-renamed identifier in
the main file, but our references will be preprocessed consistently. */
extern struct Word; /* forward reference for type completeness */
extern __typeof__(((struct Word*)0)->length) dummy_len_for_type; /* ensure type loaded */
extern __typeof__(((struct Word*)0)) dummy_word_type; /* ensure struct visible */
extern struct Word *word_limit;
extern struct Word word[];
/* Helper: redirect stdout to a temporary file; returns 0 on success. */
static int redirect_stdout_start(char *path_buf, size_t path_buf_sz, FILE **cap_fp, int *saved_fd)
{
if (!path_buf || path_buf_sz < 32 || !cap_fp || !saved_fd) return -1;
snprintf(path_buf, path_buf_sz, "/tmp/fmt_flush_paragraph_XXXXXX");
int fd = mkstemp(path_buf);
if (fd < 0) return -1;
FILE *fp = fdopen(fd, "w+");
if (!fp) {
close(fd);
return -1;
}
fflush(stdout);
*saved_fd = dup(fileno(stdout));
if (*saved_fd < 0) {
fclose(fp);
unlink(path_buf);
return -1;
}
if (dup2(fd, fileno(stdout)) < 0) {
close(*saved_fd);
fclose(fp);
unlink(path_buf);
return -1;
}
*cap_fp = fp;
return 0;
}
/* Helper: restore stdout; leaves cap_fp open for reading. */
static void redirect_stdout_end(FILE *cap_fp, int saved_fd)
{
fflush(stdout);
if (saved_fd >= 0) {
dup2(saved_fd, fileno(stdout));
close(saved_fd);
}
fflush(stdout);
(void)cap_fp; /* still open for caller to read */
}
/* Convenience: reset globals to a clean baseline for tests. */
static void reset_globals_defaults(void)
{
/* No prefix, no indentation, no tabs. */
prefix = "";
prefix_length = 0;
prefix_full_length = 0;
prefix_lead_space = 0;
prefix_indent = 0;
first_indent = 0;
other_indent = 0;
out_column = 0;
last_line_length = 0;
tabs = 0;
/* Set widths; goal equals max for stable behavior. */
max_width = 12;
goal_width = 12;
/* Clear buffer and pointers. */
wptr = parabuf;
}
/*--- Unity setUp/tearDown ---*/
void setUp(void) {
reset_globals_defaults();
}
void tearDown(void) {
/* Nothing to clean explicitly. */
}
/* Test 1: Special case where word_limit == word; flush_paragraph should
write the entire parabuf to stdout and reset wptr to parabuf. */
void test_flush_paragraph_single_chunk_no_words(void)
{
/* Prepare parabuf with raw bytes. */
const char *payload = "XYZ";
memcpy(parabuf, payload, 3);
wptr = parabuf + 3;
/* Set word_limit to point to the start: no complete words present. */
word_limit = word; /* word == first element; indicates zero words */
char path[64];
FILE *cap = NULL; int saved = -1;
TEST_ASSERT_EQUAL_INT(0, redirect_stdout_start(path, sizeof(path), &cap, &saved));
/* Do not use TEST_ASSERT while stdout is redirected. */
flush_paragraph();
redirect_stdout_end(cap, saved);
/* Read captured output and verify. */
fseek(cap, 0, SEEK_SET);
char buf[32] = {0};
size_t n = fread(buf, 1, sizeof(buf), cap);
fclose(cap);
/* Cleanup the file. */
unlink(path);
TEST_ASSERT_EQUAL_size_t(3, n);
TEST_ASSERT_EQUAL_UINT8_ARRAY(payload, buf, 3);
/* wptr should be reset to the start of parabuf. */
TEST_ASSERT_EQUAL_PTR(parabuf, wptr);
}
/* Helper to initialize a simple 3-word paragraph with 5-char words and
1-space gaps, designed so the optimal split is after the 2nd word. */
static void init_three_word_paragraph(void)
{
/* Words: "aaaaa" "bbbbb" "ccccc" */
const char *w1 = "aaaaa";
const char *w2 = "bbbbb";
const char *w3 = "ccccc";
/* Place texts contiguously in parabuf. */
char *p = parabuf;
memcpy(p, w1, 5); word[0].text = p; word[0].length = 5; p += 5;
memcpy(p, w2, 5); word[1].text = p; word[1].length = 5; p += 5;
memcpy(p, w3, 5); word[2].text = p; word[2].length = 5; p += 5;
wptr = p;
/* Set inter-word spaces to 1; flags to non-punctuation, non-final. */
word[0].space = 1; word[0].paren = 0; word[0].period = 0; word[0].punct = 0; word[0].final = 0;
word[1].space = 1; word[1].paren = 0; word[1].period = 0; word[1].punct = 0; word[1].final = 0;
word[2].space = 1; word[2].paren = 0; word[2].period = 0; word[2].punct = 0; word[2].final = 0;
/* Three complete words. */
word_limit = word + 3;
/* Width settings ensure that only two words (5 + 1 + 5 = 11) fit on a line. */
max_width = 12;
goal_width = 12;
first_indent = 0;
other_indent = 0;
last_line_length = 0;
prefix = "";
prefix_length = 0;
prefix_full_length = 0;
prefix_indent = 0;
tabs = 0;
}
/* Test 2: Multi-word paragraph split and compaction. After flush_paragraph,
verify that the first two words are output as one line, and the remaining
word is compacted to the front of parabuf and word array updated. */
void test_flush_paragraph_split_and_compact(void)
{
init_three_word_paragraph();
char path[64];
FILE *cap = NULL; int saved = -1;
TEST_ASSERT_EQUAL_INT(0, redirect_stdout_start(path, sizeof(path), &cap, &saved));
/* Avoid Unity asserts while redirected. */
flush_paragraph();
redirect_stdout_end(cap, saved);
/* Read captured output. */
fseek(cap, 0, SEEK_SET);
char outbuf[128] = {0};
size_t n = fread(outbuf, 1, sizeof(outbuf), cap);
fclose(cap);
unlink(path);
/* Should have printed the first line with two words and a newline. */
const char *expected_line = "aaaaa bbbbb\n";
size_t expected_len = strlen(expected_line);
TEST_ASSERT_TRUE(n >= expected_len);
TEST_ASSERT_EQUAL_MEMORY(expected_line, outbuf, expected_len);
/* Buffer should now contain only the remaining word "ccccc". */
TEST_ASSERT_EQUAL_INT(5, (int)(wptr - parabuf));
TEST_ASSERT_EQUAL_MEMORY("ccccc", parabuf, 5);
/* Word array should now start with the third word, and word_limit reduced. */
TEST_ASSERT_EQUAL_PTR(word + 1, word_limit);
TEST_ASSERT_EQUAL_INT(5, word[0].length);
TEST_ASSERT_EQUAL_MEMORY("ccccc", word[0].text, 5);
TEST_ASSERT_EQUAL_INT(1, word[0].space);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_flush_paragraph_single_chunk_no_words);
RUN_TEST(test_flush_paragraph_split_and_compact);
return UNITY_END();
}