/*
* Copyright (c) 2019 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <vppinfra/llist.h>
#include <vlib/vlib.h>
#define LLIST_TEST_I(_cond, _comment, _args...) \
({ \
int _evald = (_cond); \
if (!(_evald)) { \
fformat(stderr, "FAIL:%d: " _comment "\n", \
__LINE__, ##_args); \
} else { \
fformat(stderr, "PASS:%d: " _comment "\n", \
__LINE__, ##_args); \
} \
_evald; \
})
#define LLIST_TEST(_cond, _comment, _args...) \
{ \
if (!LLIST_TEST_I(_cond, _comment, ##_args)) { \
return 1; \
} \
}
typedef struct list_elt
{
clib_llist_anchor_t ll_test;
clib_llist_anchor_t ll_test2;
u32 data;
} list_elt_t;
#define list_elt_is_sane(pl,name,E,P,N) \
(E->name.next == (N) - pl \
&& E->name.prev == (P) - pl \
&& P->name.next == (E) - pl \
&& N->name.prev == (E) - pl)
#define list_test_is_sane(pl,name,h) \
do { \
typeof (pl) e; \
int rv; \
clib_llist_foreach (pl, name, h, e, ({ \
rv = list_elt_is_sane ((pl), name, (e), \
clib_llist_prev (pl,name,e), \
clib_llist_next (pl,name,e)); \
if (!rv) \
LLIST_TEST (0, "invalid elt %u prev %u/%u next %u/%u", e - pl, \
e->name.prev, clib_llist_prev (pl,name,e) - pl, \
e->name.next, clib_llist_next (pl,name,e) - pl); \
})); \
} while (0)
static int
llist_test_basic (vlib_main_t * vm, unformat_input_t * input)
{
list_elt_t *pelts = 0, *he, *he2, *he3, *e, *next, *nnext;
int __clib_unused verbose, i, rv;
clib_llist_index_t head, head2, head3;
u32 old_tail;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "verbose"))
verbose = 1;
else
{
vlib_cli_output (vm, "parse error: '%U'", format_unformat_error,
input);
return -1;
}
}
head = clib_llist_make_head (pelts, ll_test);
he = clib_llist_elt (pelts, head);
LLIST_TEST (he->ll_test.next == head, "head next points to itself");
LLIST_TEST (he->ll_test.prev == head, "head prev points to itself");
LLIST_TEST (he == clib_llist_next (pelts, ll_test, he),
"should be the same");
LLIST_TEST (he == clib_llist_prev (pelts, ll_test, he),
"should be the same");
/*
* Add and remove one element
*/
clib_llist_get (pelts, e);
e->data = 1;
he = clib_llist_elt (pelts, head);
clib_llist_add (pelts, ll_test, e, he);
LLIST_TEST (e->ll_test.next == head, "next should be head");
LLIST_TEST (e->ll_test.prev == head, "prev should be head");
LLIST_TEST (he->ll_test.prev == e - pelts, "prev should be new");
LLIST_TEST (he->ll_test.next == e - pelts, "prev should be new");
clib_llist_remove (pelts, ll_test, e);
clib_llist_put (pelts, e);
LLIST_TEST (he->ll_test.prev == head, "prev should be head");
LLIST_TEST (he->ll_test.prev == head, "next should be head");
LLIST_TEST (he == clib_llist_next (pelts, ll_test, he),
"should be the same");
LLIST_TEST (he == clib_llist_prev (pelts, ll_test, he),
"should be the same");
/*
* Add multiple head elements and test insertion
*/
for (i = 0; i < 100; i++)
{
clib_llist_get (pelts, e);
e->data = i;
he = clib_llist_elt (pelts, head);
clib_llist_add (pelts, ll_test, e, he);
}
he = clib_llist_elt (pelts, head);
LLIST_TEST (!clib_llist_is_empty (pelts, ll_test, he),
"shoud not be empty");
list_test_is_sane (pelts, ll_test, he);
i--;
/* *INDENT-OFF* */
clib_llist_foreach (pelts, ll_test, he, e, ({
if (i != e->data)
LLIST_TEST (0, "incorrect element i = %u data = %u", i, e->data);
i--;
}));
/* *INDENT-ON* */
LLIST_TEST (i == -1, "head insertion works i = %d", i);
/*
* Remove elements from head
*/
i = 99;
e = clib_llist_next (pelts, ll_test, he);
while (e != he)
{
next = clib_llist_next (pelts, ll_test, e);
clib_llist_remove (pelts, ll_test, e);
clib_llist_put (pelts, e);
i--;
e = next;
}
he = clib_llist_elt (pelts, head);
list_test_is_sane (pelts, ll_test, he);
LLIST_TEST (clib_llist_is_empty (pelts, ll_test, he),
"list should be empty");
LLIST_TEST (clib_llist_elts (pelts) == 1, "pool should have only 1 element");
/*
* Add tail elements to ll_test2 and test
*/
head2 = clib_llist_make_head (pelts, ll_test2);
for (i = 0; i < 100; i++)
{
clib_llist_get (pelts, e);
e->data = i;
he2 = clib_llist_elt (pelts, head2);
clib_llist_add_tail (pelts, ll_test2, e, he2);
}
he2 = clib_llist_elt (pelts, head2);
list_test_is_sane (pelts, ll_test2, he2);
LLIST_TEST (!clib_llist_is_empty (pelts, ll_test2, he2),
"list should not be empty");
i--;
/* *INDENT-OFF* */
clib_llist_foreach_reverse (pelts, ll_test2, he2, e, ({
if (i != e->data)
LLIST_TEST (0, "incorrect element i = %u data = %u", i, e->data);
i--;
}));
/* *INDENT-ON* */
LLIST_TEST (i == -1, "tail insertion works");
/*
* Remove in from ll_test2 and add to ll_test
*/
i = 0;
he = clib_llist_elt (pelts, head);
e = clib_llist_next (pelts, ll_test2, he2);
while (e != he2)
{
next = clib_llist_next (pelts, ll_test2, e);
if (e->data != i)
LLIST_TEST (0, "incorrect element i = %u data = %u", i, e->data);
clib_llist_remove (pelts, ll_test2, e);
clib_llist_add_tail (pelts, ll_test, e, he);
i++;
e = next;
}
he = clib_llist_elt (pelts, head);
he2 = clib_llist_elt (pelts, head2);
list_test_is_sane (pelts, ll_test, he);
LLIST_TEST (!clib_llist_is_empty (pelts, ll_test, he),
"shoud not be empty");
LLIST_TEST (clib_llist_is_empty (pelts, ll_test2, he2), "shoud be empty");
i = 0;
/* *INDENT-OFF* */
clib_llist_foreach (pelts, ll_test, he, e, ({
if (i != e->data)
LLIST_TEST (0, "incorrect element i = %u data = %u", i, e->data);
i++;
}));
/* *INDENT-ON* */
LLIST_TEST (i == 100, "move from ll_test2 to ll_test worked i %u", i);
/*
* Delete and insert at random position
*/
e = clib_llist_elt (pelts, head);
for (i = 0; i < 10; i++)
e = clib_llist_next (pelts, ll_test, e);
next = clib_llist_next (pelts, ll_test, e);
nnext = clib_llist_next (pelts, ll_test, next);
LLIST_TEST (e->data == 9, "data should be 9 is %u", e->data);
LLIST_TEST (next->data == 10, "data should be 10");
LLIST_TEST (nnext->data == 11, "data should be 11");
clib_llist_remove (pelts, ll_test, next);
clib_llist_put (pelts, next);
memset (next, 0xfc, sizeof (*next));
next = clib_llist_next (pelts, ll_test, e);
LLIST_TEST (next->data == 11, "data should be 11");
LLIST_TEST (next == nnext, "should be nnext");
clib_llist_get (pelts, next);
next->data = 10;
clib_llist_insert (pelts, ll_test, next, e);
next = clib_llist_next (pelts, ll_test, e);
LLIST_TEST (next->data == 10, "new next data should be 10");
next = clib_llist_next (pelts, ll_test, next);
LLIST_TEST (nnext == next, "next should be linked to old nnext");
he = clib_llist_elt (pelts, head);
list_test_is_sane (pelts, ll_test, he);
/*
* Make a new list that uses ll_test anchor
*/
head3 = clib_llist_make_head (pelts, ll_test);
for (i = 0; i < 10; i++)
{
clib_llist_get (pelts, e);
e->data = 300 + i;
he3 = clib_llist_elt (pelts, head3);
clib_llist_add (pelts, ll_test, e, he3);
}
he = clib_llist_elt (pelts, head);
he3 = clib_llist_elt (pelts, head3);
list_test_is_sane (pelts, ll_test, he3);
e = clib_llist_prev (pelts, ll_test, he);
old_tail = e->data;
/*
* Splice third list into the tail of the first
*/
clib_llist_splice (pelts, ll_test, e, he3);
list_test_is_sane (pelts, ll_test, he);
LLIST_TEST (clib_llist_is_empty (pelts, ll_test, he3), "should be empty");
e = clib_llist_prev (pelts, ll_test, he);
LLIST_TEST (e->data == 300, "data for last spliced should be 300 is %u",
e->data);
for (i = 0; i < 10; i++)
{
if (e->data != 300 + i)
LLIST_TEST (0, "incorrect element i = %u data = %u", i, e->data);
e = clib_llist_prev (pelts, ll_test, e);
}
LLIST_TEST (e->data == old_tail, "data should be old tail %u is %u",
old_tail, e->data);
/*
* Cleanup
*/
clib_llist_free (pelts);
return 0;
}
static clib_error_t *
llist_test (vlib_main_t * vm,
unformat_input_t * input, vlib_cli_command_t * cmd_arg)
{
int res = 0;
while (unformat_check_input (input) != UNFORMAT_END_OF_INPUT)
{
if (unformat (input, "basic"))
{
res = llist_test_basic (vm, input);
}
else if (unformat (input, "all"))
{
if ((res = llist_test_basic (vm, input)))
goto done;
}
else
break;
}
done:
if (res)
return clib_error_return (0, "llist unit test failed");
return 0;
}
/* *INDENT-OFF* */
VLIB_CLI_COMMAND (llist_test_command, static) =
{
.path = "test llist",
.short_help = "internal llist unit tests",
.function = llist_test,
};
/* *INDENT-ON* */
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/