File size: 7,803 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
#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();
} |