Add KUnit tests for list.h macros/functions that currently lack test coverage but have non-trivial edge cases:
- list_next_entry_circular / list_prev_entry_circular: verify both normal traversal and the wraparound case where iteration crosses the list head boundary. - list_prepare_entry: verify the NULL pos case, which is the primary use case for this macro -- it allows list_for_each_entry_continue() to start from the beginning of the list when no prior position exists. - hlist_splice_init: verify that splicing one hlist onto another correctly chains the nodes and empties the source list. Written with the assistance of Claude (Anthropic). Signed-off-by: [email protected] Co-developed-by: Claude (Anthropic) --- lib/tests/list-test.c | 115 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/lib/tests/list-test.c b/lib/tests/list-test.c index 6d9227a..9e0eec7 100644 --- a/lib/tests/list-test.c +++ b/lib/tests/list-test.c @@ -765,6 +765,83 @@ static void list_test_list_for_each_entry_reverse(struct kunit *test) KUNIT_EXPECT_EQ(test, i, -1); } +static void list_test_list_next_entry_circular(struct kunit *test) +{ + struct list_test_struct entries[3], *cur; + LIST_HEAD(list); + int i; + + for (i = 0; i < 3; ++i) { + entries[i].data = i; + list_add_tail(&entries[i].list, &list); + } + + /* Normal advance */ + cur = &entries[0]; + cur = list_next_entry_circular(cur, &list, list); + KUNIT_EXPECT_PTR_EQ(test, cur, &entries[1]); + + /* Wraparound: next from last entry should return first */ + cur = &entries[2]; + cur = list_next_entry_circular(cur, &list, list); + KUNIT_EXPECT_PTR_EQ(test, cur, &entries[0]); +} + +static void list_test_list_prev_entry_circular(struct kunit *test) +{ + struct list_test_struct entries[3], *cur; + LIST_HEAD(list); + int i; + + for (i = 0; i < 3; ++i) { + entries[i].data = i; + list_add_tail(&entries[i].list, &list); + } + + /* Normal retreat */ + cur = &entries[2]; + cur = list_prev_entry_circular(cur, &list, list); + KUNIT_EXPECT_PTR_EQ(test, cur, &entries[1]); + + /* Wraparound: prev from first entry should return last */ + cur = &entries[0]; + cur = list_prev_entry_circular(cur, &list, list); + KUNIT_EXPECT_PTR_EQ(test, cur, &entries[2]); +} + +static void list_test_list_prepare_entry(struct kunit *test) +{ + struct list_test_struct entries[3], *cur; + LIST_HEAD(list); + int i; + + for (i = 0; i < 3; ++i) { + entries[i].data = i; + list_add_tail(&entries[i].list, &list); + } + + /* With a valid pos, prepare_entry returns it unchanged */ + cur = &entries[1]; + cur = list_prepare_entry(cur, &list, list); + KUNIT_EXPECT_PTR_EQ(test, cur, &entries[1]); + + /* + * With NULL pos, prepare_entry returns the list head as an entry, + * so that a subsequent list_for_each_entry_continue() starts from + * the first real entry. This NULL case is the main reason the + * macro exists and is worth testing explicitly. + */ + cur = NULL; + cur = list_prepare_entry(cur, &list, list); + + i = 0; + list_for_each_entry_continue(cur, &list, list) { + KUNIT_EXPECT_EQ(test, cur->data, i); + i++; + } + KUNIT_EXPECT_EQ(test, i, 3); +} + static struct kunit_case list_test_cases[] = { KUNIT_CASE(list_test_list_init), KUNIT_CASE(list_test_list_add), @@ -805,6 +882,9 @@ static struct kunit_case list_test_cases[] = { KUNIT_CASE(list_test_list_for_each_prev_safe), KUNIT_CASE(list_test_list_for_each_entry), KUNIT_CASE(list_test_list_for_each_entry_reverse), + KUNIT_CASE(list_test_list_next_entry_circular), + KUNIT_CASE(list_test_list_prev_entry_circular), + KUNIT_CASE(list_test_list_prepare_entry), {}, }; @@ -1181,6 +1261,40 @@ static void hlist_test_for_each_entry_safe(struct kunit *test) } +static void hlist_test_splice_init(struct kunit *test) +{ + struct hlist_node entries[4]; + struct hlist_node *cur; + HLIST_HEAD(list1); + HLIST_HEAD(list2); + int count; + + /* list1: entries[0] -> entries[1] */ + hlist_add_head(&entries[0], &list1); + hlist_add_behind(&entries[1], &entries[0]); + + /* list2: entries[2] -> entries[3] */ + hlist_add_head(&entries[2], &list2); + hlist_add_behind(&entries[3], &entries[2]); + + /* + * Splice list1 (up to and including entries[1]) onto the front + * of list2. list1 should be emptied. + */ + hlist_splice_init(&list1, &entries[1], &list2); + + KUNIT_EXPECT_TRUE(test, hlist_empty(&list1)); + + /* list2 should now be: entries[0] -> entries[1] -> entries[2] -> entries[3] */ + KUNIT_EXPECT_PTR_EQ(test, list2.first, &entries[0]); + KUNIT_EXPECT_PTR_EQ(test, entries[1].next, &entries[2]); + + count = 0; + hlist_for_each(cur, &list2) + count++; + KUNIT_EXPECT_EQ(test, count, 4); +} + static struct kunit_case hlist_test_cases[] = { KUNIT_CASE(hlist_test_init), KUNIT_CASE(hlist_test_unhashed), @@ -1200,6 +1314,7 @@ static struct kunit_case hlist_test_cases[] = { KUNIT_CASE(hlist_test_for_each_entry_continue), KUNIT_CASE(hlist_test_for_each_entry_from), KUNIT_CASE(hlist_test_for_each_entry_safe), + KUNIT_CASE(hlist_test_splice_init), {}, }; -- 2.53.0 -- Rango [email protected]
