File size: 6,292 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
#include "../../unity/unity.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

/* We rely on the program's globals and the static function under test:
   - static void fmt_paragraph(void);
   - Globals: word[], word_limit, max_width, goal_width, first_indent, other_indent, last_line_length
   These are declared/defined in the fmt program source that includes this test. */

/* Helper to reset a slice of the global word array to zero. */
static void reset_words_range(int from, int to_exclusive) {
    /* word is a global array defined in the program. */
    memset(&word[from], 0, (size_t)(to_exclusive - from) * sizeof(word[0]));
}

/* Helper to prepare a paragraph with given lengths and spaces.
   count: number of real words
   lengths[count], spaces[count] (spaces for each word; last one's space value is harmless)
   sentinel_len: a known value to verify restoration after fmt_paragraph
*/
static void prepare_words(const int *lengths, const int *spaces, int count, int sentinel_len) {
    /* Clear a safe range including the sentinel slot. */
    reset_words_range(0, count + 1);

    for (int i = 0; i < count; i++) {
        word[i].length = lengths[i];
        word[i].space = spaces ? spaces[i] : 1;
        word[i].paren = 0;
        word[i].period = 0;
        word[i].punct = 0;
        word[i].final = 0;
        word[i].text = NULL; /* Not used by fmt_paragraph */
        /* best_cost/next_break/line_length are set by fmt_paragraph */
    }

    /* Set word_limit to point just past the last real word. */
    word_limit = &word[count];
    /* Set a recognizable sentinel length to ensure it's restored. */
    word_limit->length = sentinel_len;
    /* Also zero fields that fmt_paragraph expects to use for the sentinel. */
    word_limit->best_cost = 0;
    word_limit->next_break = NULL;
    word_limit->line_length = 0;
}

void setUp(void) {
    /* Ensure a neutral default for globals possibly affecting costs. */
    last_line_length = 0;
    first_indent = 0;
    other_indent = 0;
    max_width = 0;
    goal_width = 0;
}

void tearDown(void) {
    /* Nothing persistent to clean. */
}

/* Test 1: Forced wrap with two words not fitting the max width.
   Expect: first line contains only word[0], second line word[1]. */
static void test_fmt_paragraph_forced_wrap(void) {
    /* Two words of length 3, one space between them -> combined line 7 */
    const int lengths[] = {3, 3};
    const int spaces[] = {1, 1};
    prepare_words(lengths, spaces, 2, 123);

    max_width = 5;   /* Force wrap: 7 >= 5 so only one word can fit per line */
    goal_width = 5;  /* Goal same as max for simple short-cost expectations */
    first_indent = 0;
    other_indent = 0;
    last_line_length = 0;

    fmt_paragraph();

    /* Verify breaking decisions */
    TEST_ASSERT_EQUAL_PTR(&word[1], word[0].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[0].line_length); /* indent 0 + len 3 */

    TEST_ASSERT_EQUAL_PTR(word_limit, word[1].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[1].line_length); /* indent 0 + len 3 */

    /* Sentinel length restored */
    TEST_ASSERT_EQUAL_INT(123, word_limit->length);
}

/* Test 2: Two words fit on one line with indentation; verify line_length includes indents and best break goes to word_limit. */
static void test_fmt_paragraph_two_words_single_line_with_indent(void) {
    const int lengths[] = {3, 3};
    const int spaces[] = {1, 1};
    prepare_words(lengths, spaces, 2, 99);

    max_width = 20;
    goal_width = 20;
    first_indent = 2;
    other_indent = 4;

    fmt_paragraph();

    /* With ample width and large goal, the best is to keep both words on the first line. */
    TEST_ASSERT_EQUAL_PTR(word_limit, word[0].next_break);
    /* line_length = first_indent + 3 + 1 + 3 = 2 + 7 = 9 */
    TEST_ASSERT_EQUAL_INT(9, word[0].line_length);

    /* Second word alone (as if starting a line) uses other_indent. */
    TEST_ASSERT_EQUAL_PTR(word_limit, word[1].next_break);
    TEST_ASSERT_EQUAL_INT(other_indent + 3, word[1].line_length); /* 4 + 3 = 7 */

    TEST_ASSERT_EQUAL_INT(99, word_limit->length);
}

/* Test 3: Three words, small width forces one word per line.
   Verify the next_break chain and the computed line_length values. */
static void test_fmt_paragraph_three_words_forced_each_line(void) {
    const int lengths[] = {3, 3, 3};
    const int spaces[] = {1, 1, 1};
    prepare_words(lengths, spaces, 3, 77);

    max_width = 5;   /* 3 + 1 + 3 = 7 >= 5, so only one per line */
    goal_width = 5;
    first_indent = 0;
    other_indent = 0;

    fmt_paragraph();

    TEST_ASSERT_EQUAL_PTR(&word[1], word[0].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[0].line_length);

    TEST_ASSERT_EQUAL_PTR(&word[2], word[1].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[1].line_length);

    TEST_ASSERT_EQUAL_PTR(word_limit, word[2].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[2].line_length);

    TEST_ASSERT_EQUAL_INT(77, word_limit->length);
}

/* Test 4: last_line_length influences the first-line decision via raggedness.
   Construct a tie on short-costs for len=3 vs len=7 around goal=5, then
   set last_line_length=10 to prefer len=7 (smaller raggedness). */
static void test_fmt_paragraph_raggedness_with_last_line_length(void) {
    const int lengths[] = {3, 3};
    const int spaces[] = {1, 1};
    prepare_words(lengths, spaces, 2, 55);

    max_width = 20;  /* Allow both options (3 and 7) */
    goal_width = 5;  /* |5-3| == |5-7| -> tie in short-cost */
    first_indent = 0;
    other_indent = 0;
    last_line_length = 10; /* Penalize len=3 more than len=7 */

    fmt_paragraph();

    /* Expect the first line to include both words due to smaller raggedness penalty */
    TEST_ASSERT_EQUAL_PTR(word_limit, word[0].next_break);
    TEST_ASSERT_EQUAL_INT(7, word[0].line_length); /* 3 + 1 + 3 */

    TEST_ASSERT_EQUAL_PTR(word_limit, word[1].next_break);
    TEST_ASSERT_EQUAL_INT(3, word[1].line_length);

    TEST_ASSERT_EQUAL_INT(55, word_limit->length);
}

int main(void) {
    UNITY_BEGIN();
    RUN_TEST(test_fmt_paragraph_forced_wrap);
    RUN_TEST(test_fmt_paragraph_two_words_single_line_with_indent);
    RUN_TEST(test_fmt_paragraph_three_words_forced_each_line);
    RUN_TEST(test_fmt_paragraph_raggedness_with_last_line_length);
    return UNITY_END();
}