#include "../../unity/unity.h" #include #include /* Globals from the fmt implementation that line_cost depends on. */ extern int goal_width; /* preferred width */ extern struct Word *word_limit; /* sentinel pointer for paragraph words */ /* Save/restore originals between tests */ static int saved_goal_width; static struct Word *saved_word_limit; void setUp(void) { saved_goal_width = goal_width; saved_word_limit = word_limit; } void tearDown(void) { goal_width = saved_goal_width; word_limit = saved_word_limit; } /* Helper to zero-initialize an array of WORDs safely */ static void zero_words(WORD *arr, size_t n) { memset(arr, 0, n * sizeof(*arr)); } /* Test: when next == word_limit, cost must be zero */ void test_line_cost_next_is_word_limit_returns_zero(void) { WORD arr[2]; zero_words(arr, 2); goal_width = 70; word_limit = &arr[1]; COST c = line_cost(word_limit, 123); /* len arbitrary */ TEST_ASSERT_EQUAL_INT(0, (int)c); } /* Test: base cost only, exact goal width yields zero cost */ void test_line_cost_base_only_exact_goal_width_zero(void) { WORD arr[2]; zero_words(arr, 2); goal_width = 50; word_limit = &arr[1]; WORD *next = &arr[0]; next->next_break = word_limit; /* no ragged cost path */ next->line_length = 0; /* ignored in this path */ int len = 50; /* equals goal_width */ COST expected = SHORT_COST(goal_width - len); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: base cost only, line shorter than goal (underfull) */ void test_line_cost_base_only_under_goal(void) { WORD arr[2]; zero_words(arr, 2); goal_width = 50; word_limit = &arr[1]; WORD *next = &arr[0]; next->next_break = word_limit; /* no ragged cost path */ int len = 45; /* 5 under goal */ COST expected = SHORT_COST(goal_width - len); /* n = 5 */ COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: base cost only, line longer than goal (overfull) */ void test_line_cost_base_only_over_goal(void) { WORD arr[2]; zero_words(arr, 2); goal_width = 50; word_limit = &arr[1]; WORD *next = &arr[0]; next->next_break = word_limit; /* no ragged cost path */ int len = 60; /* 10 over goal, n = -10 for base */ COST expected = SHORT_COST(goal_width - len); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: includes ragged cost when next->next_break != word_limit (positive ragged n) */ void test_line_cost_includes_ragged_positive_difference(void) { WORD arr[3]; zero_words(arr, 3); goal_width = 50; word_limit = &arr[2]; WORD *next = &arr[0]; next->next_break = &arr[1]; /* != word_limit -> ragged path taken */ next->line_length = 43; int len = 48; /* base n = 2, ragged n = 48 - 43 = 5 */ COST expected = SHORT_COST(goal_width - len) + RAGGED_COST(len - next->line_length); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: includes ragged cost when next->next_break != word_limit (negative ragged n) */ void test_line_cost_includes_ragged_negative_difference(void) { WORD arr[3]; zero_words(arr, 3); goal_width = 50; word_limit = &arr[2]; WORD *next = &arr[0]; next->next_break = &arr[1]; /* != word_limit -> ragged path taken */ next->line_length = 45; int len = 40; /* base n = 10, ragged n = 40 - 45 = -5 (squared in cost) */ COST expected = SHORT_COST(goal_width - len) + RAGGED_COST(len - next->line_length); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: ragged cost is not added when next->next_break == word_limit, regardless of line_length */ void test_line_cost_no_ragged_when_next_break_is_word_limit(void) { WORD arr[2]; zero_words(arr, 2); goal_width = 52; word_limit = &arr[1]; WORD *next = &arr[0]; next->next_break = word_limit; /* disables ragged */ next->line_length = 10; /* would be used if ragged applied */ int len = 49; /* base n = 3 */ COST expected = SHORT_COST(goal_width - len); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } /* Test: zero total cost when len == goal_width and ragged diff is zero (ragged path taken) */ void test_line_cost_zero_when_at_goal_and_no_ragged_diff(void) { WORD arr[3]; zero_words(arr, 3); goal_width = 80; word_limit = &arr[2]; WORD *next = &arr[0]; next->next_break = &arr[1]; /* ensure ragged path considered */ next->line_length = 80; int len = 80; COST expected = SHORT_COST(0) + RAGGED_COST(0); COST c = line_cost(next, len); TEST_ASSERT_EQUAL_INT((int)expected, (int)c); } int main(void) { UNITY_BEGIN(); RUN_TEST(test_line_cost_next_is_word_limit_returns_zero); RUN_TEST(test_line_cost_base_only_exact_goal_width_zero); RUN_TEST(test_line_cost_base_only_under_goal); RUN_TEST(test_line_cost_base_only_over_goal); RUN_TEST(test_line_cost_includes_ragged_positive_difference); RUN_TEST(test_line_cost_includes_ragged_negative_difference); RUN_TEST(test_line_cost_no_ragged_when_next_break_is_word_limit); RUN_TEST(test_line_cost_zero_when_at_goal_and_no_ragged_diff); return UNITY_END(); }