#include "../../unity/unity.h" #include #include #include /* 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(); }