Skip to content

Commit

Permalink
tests: in _nltst_assert_route_list() accept arbitrary order
Browse files Browse the repository at this point in the history
_nltst_assert_route_list() compares the content of the cache with a list
of expected routes.

The cache does not have a well defined order.

Well, maybe the cache *should* have a well defined order, because when
we use `ip route replace`, it will replace some route that has certain
similar attributes, but is otherwise another route (as far as route
identity and caching is concerned). But on netlink, we are only told
that one route was replaced, but not which one. Some kernel developers
think that in face of that, we just iterate the cache (in the same order
as kernel exports the route), find the first similar route and know that
was the one that is replaced. Does that work? Dunno, I never saw anybody
implementing that successfully. NetworkManager throws it's hands in the
air and request a new dump of all routes that it caches.

Anyway. Unless somebody shows that the cache is always in a consistent and
reproducible state, this unit test was wrong.

Instead, permutate through all lists of routes, and try to find a
one-to-one match with the expected routes.
  • Loading branch information
thom311 committed Aug 22, 2024
1 parent 01f06b5 commit 2195a3f
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 51 deletions.
2 changes: 1 addition & 1 deletion tests/cksuite-all-netns.c
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ START_TEST(route_1)

_route_init(AF_INET6, &sk, &cache);

_nltst_assert_route_cache(cache, "fe80::/64", "6 fe80::*/128",
_nltst_assert_route_cache(cache, "6 fe80::*/128", "fe80::/64",
"ff00::/8");
}
END_TEST
Expand Down
153 changes: 103 additions & 50 deletions tests/nl-test-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -1036,67 +1036,120 @@ bool _nltst_select_route_match(struct nl_object *route,

/*****************************************************************************/

static void _nltst_assert_route_list_print(struct nl_object *const *objs,
size_t l_objs,
const char *const *expected_routes,
size_t l_expected)
{
_nl_auto_free char *s2 =
_nltst_objects_to_string("route-list", objs, l_objs);
size_t j;

printf("route content: %s", s2);
printf("expected routes: %zu\n", l_expected);
for (j = 0; j < l_expected; j++) {
printf("expected route: [%zu] %s\n", j, expected_routes[j]);
}
printf("ip-route:>>>\n");
_nltst_system("ip -d -4 route show table all");
_nltst_system("ip -d -6 route show table all");
printf("<<<\n");
}

static bool
_nltst_assert_route_list_equal(struct nl_object *const *objs,
const NLTstSelectRoute *expected_route_selects,
size_t len)
{
size_t i;

for (i = 0; i < len; i++) {
struct nl_object *route = objs[i];
const NLTstSelectRoute *select_route =
&expected_route_selects[i];

if (!_nltst_select_route_match(route, select_route, false))
return false;
}
return true;
}

typedef struct {
const NLTstSelectRoute *expected_route_selects;
} NltstAssertRouteListPermData;

static bool
_nltst_assert_route_list_permutate(const NltstAssertRouteListPermData *data,
struct nl_object **objs, size_t idx,
size_t num)
{
size_t i;

if (idx + 1 == num) {
return _nltst_assert_route_list_equal(
objs, data->expected_route_selects, num);
}

for (i = idx; i < num; i++) {
_nl_swap(&objs[idx], &objs[i]);
if (_nltst_assert_route_list_permutate(data, objs, idx + 1,
num)) {
/* This is a permutation that matches. Leave objs in
* this order and return */
return true;
}
_nl_swap(&objs[idx], &objs[i]);
}
return false;
}

void _nltst_assert_route_list(struct nl_object *const *objs, ssize_t len,
const char *const *expected_routes)
{
const size_t l_expected = _nl_ptrarray_len(expected_routes, -1);
const size_t l_objs = _nl_ptrarray_len(objs, len);
_nl_auto_free NLTstSelectRoute *expected_route_selects = NULL;
_nl_auto_free struct nl_object **objs2 = NULL;
size_t i;

for (i = 0; true; i++) {
_nltst_auto_clear_select_route NLTstSelectRoute select_route = {
0
};
struct nl_object *route = i < l_objs ? objs[i] : NULL;
const char *expected_route =
i < l_expected ? expected_routes[i] : NULL;
bool good;
if (l_expected != l_objs)
goto out_fail;

if (!expected_route && !route)
break;

if (!route) {
good = false;
} else if (!expected_route) {
good = false;
} else {
_nltst_select_route_parse(expected_route,
&select_route);
good = _nltst_select_route_match(route, &select_route,
false);
}
if (l_objs == 0)
goto out_free;

if (!good) {
_nl_auto_free char *s2 = _nltst_objects_to_string(
"route-list", objs, l_objs);
size_t j;

printf("route content: %s", s2);
printf("expected routes: %zu\n", l_expected);
for (j = 0; j < l_expected; j++) {
printf("expected route: [%zu] %s %s\n", j,
i == j ? "-->" : " ",
expected_routes[j]);
}
printf("ip-route:>>>\n");
_nltst_system("ip -d -4 addr show");
_nltst_system("ip -d -6 addr show");
printf("<<<\n");
}
objs2 = _nltst_assert_nonnull(
_nl_memdup(objs, sizeof(struct nl_object *) * l_objs));

if (!route) {
ck_abort_msg(
"No more route, but have expected route %zu (of %zu) as %s",
i + 1, l_expected, expected_route);
} else if (!expected_route) {
_nl_auto_free char *route_str =
_nltst_object_to_string(route);
expected_route_selects =
_nltst_malloc0(sizeof(NLTstSelectRoute) * (l_expected + 1u));
for (i = 0; i < l_expected; i++) {
_nltst_select_route_parse(expected_routes[i],
&expected_route_selects[i]);
}

ck_abort_msg(
"No more expected route, but have route %zu (of %zu) as %s",
i + 1, l_objs, route_str);
} else {
_nltst_select_route_match(route, &select_route, true);
/* Permutate through the list of objects and check that we find at
* least one match with the list of expected routes. */
if (!_nltst_assert_route_list_permutate(
&((const NltstAssertRouteListPermData){
.expected_route_selects = expected_route_selects,
}),
objs2, 0, l_objs))
goto out_fail;

goto out_free;

out_fail:
_nltst_assert_route_list_print(objs, l_objs, expected_routes,
l_expected);
ck_abort_msg(
"The list of %zu routes did not find a one-to-one match with the list of %zu expected routes",
l_objs, l_expected);

out_free:
if (expected_route_selects) {
for (i = 0; i < l_expected; i++) {
_nltst_select_route_clear(&expected_route_selects[i]);
}
}
}
Expand Down

0 comments on commit 2195a3f

Please sign in to comment.