1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2025-12-25 06:23:53 +01:00

tests/unittests/texts-core-clist: improve unit test

- Iterate of different lengths of unsorted data to also cover corner case
  one-item list
- Actually check that the list is sorted afterwards (and not just that
  the maximum got sorted in last)
- Actually check that the list was stable-sorted (as the API doc
  promises a stable sort)
- Increase the length of the input data for better test coverage
- Check that no elements get lost while sorting

Co-authored-by: crasbe <crasbe@gmail.com>
This commit is contained in:
Marian Buschsieweke 2025-04-12 10:55:42 +02:00
parent be8820876e
commit 0602bea5ae
No known key found for this signature in database
GPG Key ID: 758BD52517F79C41

View File

@ -6,20 +6,33 @@
* directory for more details.
*/
#include <assert.h>
#include <string.h>
#include <stdint.h>
#include "embUnit.h"
#include "clist.h"
#include "container.h"
#include "tests-core.h"
#define TEST_CLIST_LEN (8)
#define TEST_CLIST_LEN (64)
static list_node_t tests_clist_buf[TEST_CLIST_LEN];
/* Test list data structure */
struct test_list_node {
list_node_t list;
int value;
};
static struct test_list_node tests_clist_buf[TEST_CLIST_LEN];
static list_node_t test_clist;
static struct test_list_node * _get_test_list_node(list_node_t *node)
{
return container_of(node, struct test_list_node, list);
}
static void set_up(void)
{
memset(tests_clist_buf, 0, sizeof(tests_clist_buf));
@ -28,7 +41,7 @@ static void set_up(void)
static void test_clist_rpush(void)
{
list_node_t *elem = &(tests_clist_buf[0]);
list_node_t *elem = &(tests_clist_buf[0].list);
list_node_t *list = &test_clist;
clist_rpush(list, elem);
@ -41,7 +54,7 @@ static void test_clist_add_two(void)
{
list_node_t *list = &test_clist;
list_node_t *elem = &(tests_clist_buf[1]);
list_node_t *elem = &(tests_clist_buf[1].list);
test_clist_rpush();
@ -57,14 +70,14 @@ static void test_clist_add_three(void)
list_node_t *list = &test_clist;
for (int i = 0; i < 3; i++) {
clist_rpush(list, &(tests_clist_buf[i]));
clist_rpush(list, &(tests_clist_buf[i].list));
}
TEST_ASSERT_NOT_NULL(list->next);
TEST_ASSERT(list->next == &(tests_clist_buf[2]));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0]));
TEST_ASSERT(list->next->next->next == &(tests_clist_buf[1]));
TEST_ASSERT(list->next->next->next->next == &(tests_clist_buf[2]));
TEST_ASSERT(list->next == &(tests_clist_buf[2].list));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0].list));
TEST_ASSERT(list->next->next->next == &(tests_clist_buf[1].list));
TEST_ASSERT(list->next->next->next->next == &(tests_clist_buf[2].list));
}
static void test_clist_find(void)
@ -74,10 +87,10 @@ static void test_clist_find(void)
test_clist_add_three();
for (int i = 0; i < 3; i++) {
TEST_ASSERT(clist_find(list, &(tests_clist_buf[i])) == &(tests_clist_buf[i]));
TEST_ASSERT(clist_find(list, &(tests_clist_buf[i].list)) == &(tests_clist_buf[i].list));
}
TEST_ASSERT_NULL(clist_find(list, &(tests_clist_buf[3])));
TEST_ASSERT_NULL(clist_find(list, &(tests_clist_buf[3].list)));
}
static void test_clist_find_before(void)
@ -86,13 +99,13 @@ static void test_clist_find_before(void)
test_clist_add_three();
TEST_ASSERT(clist_find_before(list, &(tests_clist_buf[0])) == &(tests_clist_buf[2]));
TEST_ASSERT(clist_find_before(list, &(tests_clist_buf[0].list)) == &(tests_clist_buf[2].list));
for (int i = 1; i < 3; i++) {
TEST_ASSERT(clist_find_before(list, &(tests_clist_buf[i])) == &(tests_clist_buf[i-1]));
TEST_ASSERT(clist_find_before(list, &(tests_clist_buf[i].list)) == &(tests_clist_buf[i-1].list));
}
TEST_ASSERT_NULL(clist_find_before(list, &(tests_clist_buf[3])));
TEST_ASSERT_NULL(clist_find_before(list, &(tests_clist_buf[3].list)));
}
static void test_clist_remove(void)
@ -102,27 +115,27 @@ static void test_clist_remove(void)
for (int i = 0; i < 3; i++) {
set_up();
test_clist_add_three();
clist_remove(list, &(tests_clist_buf[i]));
clist_remove(list, &(tests_clist_buf[i].list));
for (int j = 0; j < 3; j++) {
if (i == j) {
TEST_ASSERT_NULL(clist_find(list, &(tests_clist_buf[j])));
TEST_ASSERT_NULL(clist_find(list, &(tests_clist_buf[j].list)));
}
else {
TEST_ASSERT(clist_find(list, &(tests_clist_buf[j])) == &(tests_clist_buf[j]));
TEST_ASSERT(clist_find(list, &(tests_clist_buf[j].list)) == &(tests_clist_buf[j].list));
}
}
}
/* list now contains 0, 1 */
TEST_ASSERT(list->next == &(tests_clist_buf[1]));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0]));
TEST_ASSERT(list->next == &(tests_clist_buf[1].list));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0].list));
clist_remove(list, &(tests_clist_buf[1]));
TEST_ASSERT(list->next == &(tests_clist_buf[0]));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0]));
clist_remove(list, &(tests_clist_buf[1].list));
TEST_ASSERT(list->next == &(tests_clist_buf[0].list));
TEST_ASSERT(list->next->next == &(tests_clist_buf[0].list));
clist_remove(list, &(tests_clist_buf[0]));
clist_remove(list, &(tests_clist_buf[0].list));
TEST_ASSERT_NULL(list->next);
}
@ -132,13 +145,13 @@ static void test_clist_lpop(void)
test_clist_add_three();
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[0]);
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[0].list);
TEST_ASSERT_NOT_NULL(list->next);
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[1]);
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[1].list);
TEST_ASSERT_NOT_NULL(list->next);
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[2]);
TEST_ASSERT(clist_lpop(list) == &tests_clist_buf[2].list);
TEST_ASSERT_NULL(list->next);
TEST_ASSERT_NULL(clist_lpop(list));
}
@ -148,10 +161,10 @@ static void test_clist_lpush(void)
list_node_t *list = &test_clist;
test_clist_add_two();
clist_lpush(list, &tests_clist_buf[2]);
clist_lpush(list, &tests_clist_buf[2].list);
TEST_ASSERT_NOT_NULL(list->next);
TEST_ASSERT(list->next->next == &tests_clist_buf[2]);
TEST_ASSERT(list->next->next == &tests_clist_buf[2].list);
}
static void test_clist_rpop(void)
@ -163,7 +176,7 @@ static void test_clist_rpop(void)
clist_rpop(list);
TEST_ASSERT_NOT_NULL(list->next);
TEST_ASSERT(list->next->next == &tests_clist_buf[0]);
TEST_ASSERT(list->next->next == &tests_clist_buf[0].list);
}
static void test_clist_remove_two(void)
@ -187,11 +200,11 @@ static void test_clist_lpoprpush(void)
clist_lpoprpush(list);
TEST_ASSERT(list->next->next == &tests_clist_buf[1]);
TEST_ASSERT(list->next->next == &tests_clist_buf[1].list);
clist_lpoprpush(list);
TEST_ASSERT(list->next->next == &tests_clist_buf[0]);
TEST_ASSERT(list->next->next == &tests_clist_buf[0].list);
}
static int _foreach_called;
@ -200,10 +213,10 @@ static int _foreach_abort_after = TEST_CLIST_LEN/2;
static void _foreach_test(clist_node_t *node)
{
TEST_ASSERT(node == &tests_clist_buf[_foreach_called]);
TEST_ASSERT(node == &tests_clist_buf[_foreach_called].list);
for (int i = 0; i < TEST_CLIST_LEN; i++) {
if (node == &tests_clist_buf[i]) {
if (node == &tests_clist_buf[i].list) {
_foreach_visited[i]++;
break;
}
@ -246,7 +259,7 @@ static void test_clist_foreach(void)
TEST_ASSERT(res == NULL);
for (int i = 0; i < TEST_CLIST_LEN; i++) {
clist_rpush(list, &tests_clist_buf[i]);
clist_rpush(list, &tests_clist_buf[i].list);
}
res = clist_foreach(list, _foreach_test_trampoline, NULL);
@ -266,51 +279,104 @@ static void test_clist_foreach(void)
TEST_ASSERT(res == NULL);
}
static int _cmp(clist_node_t *a, clist_node_t *b)
static int _cmp(clist_node_t *_a, clist_node_t *_b)
{
/* this comparison function will sort by the actual memory address of the
* list node (descending) */
return (uintptr_t)a - (uintptr_t) b;
struct test_list_node *a = _get_test_list_node(_a);
struct test_list_node *b = _get_test_list_node(_b);
return a->value - b->value;
}
static void test_clist_sort_empty(void)
static bool _is_clist_stable_sorted(clist_node_t *list)
{
clist_node_t empty = { .next=NULL };
clist_sort(&empty, _cmp);
/* empty list is always considered as sorted */
if (!list->next) {
return true;
}
TEST_ASSERT(empty.next == NULL);
clist_node_t *end_of_clist = list->next;
clist_node_t *prev = list->next->next;
clist_node_t *cur = prev->next;
while (prev != end_of_clist) {
int cmp = _cmp(prev, cur);
if (cmp > 0) {
return false;
}
if (cmp == 0) {
/* stable sort requires that order is not touched when elements
* are equal */
if ((uintptr_t)prev > (uintptr_t)cur) {
return false;
}
}
prev = cur;
cur = cur->next;
}
return true;
}
static void _prepare_unsorted_clist(size_t len)
{
TEST_ASSERT(len <= ARRAY_SIZE(tests_clist_buf));
/*
* Test data generated using following python snippet:
*
* import random
*
* random.seed(1337)
* for i in range(8):
* result = " " * 8
* for i in range(7):
* result += f"0x{random.getrandbits(8):02x}, "
* result += f"0x{random.getrandbits(8):02x},"
* print(result)
*
* Note that the given seed was selected to produce duplicates (e.g. 0xb2)
* in the input, so that stable sorting is tested.
*/
static const unsigned values[] = {
0x9e, 0xec, 0x88, 0xb5, 0x5d, 0x92, 0x95, 0xbb,
0x2a, 0xc6, 0xd3, 0x55, 0x62, 0xa2, 0xca, 0x5c,
0xeb, 0xfe, 0x4e, 0x64, 0xfe, 0xb2, 0x34, 0xc2,
0xa8, 0xdd, 0xe9, 0x5d, 0x1b, 0x6c, 0xd2, 0xa2,
0x66, 0xe6, 0x10, 0x81, 0xb2, 0xab, 0x59, 0xe3,
0x67, 0x67, 0xcd, 0xbc, 0xcc, 0x4f, 0xa9, 0x04,
0xd5, 0x5a, 0x98, 0x1e, 0x75, 0x3f, 0xf5, 0x2b,
0xcb, 0xda, 0x50, 0xad, 0xb0, 0xeb, 0xea, 0x9a,
};
static_assert(ARRAY_SIZE(tests_clist_buf) <= ARRAY_SIZE(values),
"Not enough test data for sort test");
set_up();
clist_node_t *list = &test_clist;
for (size_t i = 0; i < len; i++) {
tests_clist_buf[i].value = values[i];
clist_rpush(list, &tests_clist_buf[i].list);
}
}
/*
* This test works by first adding all list nodes of tests_clist_buf to a new
* list. As the array is traversed in order, the memory addresses of the list
* nodes are naturally sorted ascending.
* The list is then rotated (using clist_lpoprpush()) a couple of times in
* order to create a somewhat arbitrary sorting.
* Then clist_sort() is run with a comparison function that just returns the
* difference (a-b), which effectively leads to a list sorted by descending
* list node addresses.
* This test iterates over lists lengths starting with 0 up to TEST_CLIST_LEN
* and adds the buffer elements in ascending memory order to the list. The
* elements are filled with demo test data that contains duplicates.
*
* Afert sorting, it is verified that
* - the resulting list is sorted
* - the resulting list still has the correct size (no elements lost in sort)
* - duplicate elements of the input data are still in the original order
* (the sort is stable)
*/
static void test_clist_sort(void)
{
clist_node_t *list = &test_clist;
for (int i = 0; i < TEST_CLIST_LEN; i++) {
clist_rpush(list, &tests_clist_buf[i]);
}
/* rotate the list a couple of times in order to mess up the sorting */
clist_lpoprpush(list);
clist_lpoprpush(list);
clist_lpoprpush(list);
/* sort list */
clist_sort(list, _cmp);
uintptr_t last = (uintptr_t) list->next;
for (int i = 0; i < TEST_CLIST_LEN; i++) {
TEST_ASSERT((uintptr_t) clist_rpop(list) <= last);
for (size_t cur_len = 0; cur_len < TEST_CLIST_LEN; cur_len++) {
_prepare_unsorted_clist(cur_len);
clist_sort(list, _cmp);
TEST_ASSERT(_is_clist_stable_sorted(list));
TEST_ASSERT_EQUAL_INT(cur_len, clist_count(list));
}
}
@ -320,7 +386,7 @@ static void test_clist_count(void)
TEST_ASSERT(n == 0);
for (unsigned i = 1; i <= TEST_CLIST_LEN; i++) {
clist_rpush(&test_clist, &tests_clist_buf[i - 1]);
clist_rpush(&test_clist, &tests_clist_buf[i - 1].list);
n = clist_count(&test_clist);
TEST_ASSERT(n == i);
}
@ -336,7 +402,7 @@ static void test_clist_is_empty(void)
TEST_ASSERT(clist_is_empty(&test_clist));
for (unsigned i = 1; i <= TEST_CLIST_LEN; i++) {
clist_rpush(&test_clist, &tests_clist_buf[i - 1]);
clist_rpush(&test_clist, &tests_clist_buf[i - 1].list);
TEST_ASSERT(!clist_is_empty(&test_clist));
}
for (unsigned i = TEST_CLIST_LEN; i > 0; i--) {
@ -355,14 +421,14 @@ static void test_clist_special_cardinality(void)
TEST_ASSERT(!clist_exactly_one(&test_clist));
TEST_ASSERT(!clist_more_than_one(&test_clist));
clist_rpush(&test_clist, &tests_clist_buf[i++]);
clist_rpush(&test_clist, &tests_clist_buf[i++].list);
TEST_ASSERT(!clist_is_empty(&test_clist));
TEST_ASSERT(clist_exactly_one(&test_clist));
TEST_ASSERT(!clist_more_than_one(&test_clist));
while (i < TEST_CLIST_LEN) {
clist_rpush(&test_clist, &tests_clist_buf[i++]);
clist_rpush(&test_clist, &tests_clist_buf[i++].list);
TEST_ASSERT(!clist_is_empty(&test_clist));
TEST_ASSERT(!clist_exactly_one(&test_clist));
@ -402,7 +468,6 @@ Test *tests_core_clist_tests(void)
new_TestFixture(test_clist_remove),
new_TestFixture(test_clist_lpoprpush),
new_TestFixture(test_clist_foreach),
new_TestFixture(test_clist_sort_empty),
new_TestFixture(test_clist_sort),
new_TestFixture(test_clist_count),
new_TestFixture(test_clist_is_empty),