summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Haller <thaller@redhat.com>2024-08-22 15:18:43 (GMT)
committerThomas Haller <thaller@redhat.com>2024-08-22 16:26:15 (GMT)
commit9f5fac78f5d3b7605eb3ef359974748201b07446 (patch)
treeedebfe6ab68984ce8dac132b32d63f05ad09cf58
parent01f06b57620d0098777ce00951ce46dca9655ae3 (diff)
downloadlibnl-9f5fac78f5d3b7605eb3ef359974748201b07446.zip
libnl-9f5fac78f5d3b7605eb3ef359974748201b07446.tar.gz
libnl-9f5fac78f5d3b7605eb3ef359974748201b07446.tar.bz2
tests: in _nltst_assert_route_list() accept arbitrary order
_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 implements a similar cache and throws it's hands in the air and requests 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.
-rw-r--r--tests/cksuite-all-netns.c2
-rw-r--r--tests/nl-test-util.c153
2 files changed, 104 insertions, 51 deletions
diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c
index 5b9d3a5..1948c3e 100644
--- a/tests/cksuite-all-netns.c
+++ b/tests/cksuite-all-netns.c
@@ -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
diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c
index 3540c6a..9fd6c8b 100644
--- a/tests/nl-test-util.c
+++ b/tests/nl-test-util.c
@@ -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]);
}
}
}