diff options
author | Thomas Haller <thaller@redhat.com> | 2022-05-06 10:06:55 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2022-05-09 07:02:28 (GMT) |
commit | 7efeca2d575678c037850e8f27de9b10969c508f (patch) | |
tree | 7e62e5bcc6033f8ca384f289ae4b845017b509c8 | |
parent | f6f4d36ce15b0d7ceba5e98c122d3d70beba744a (diff) | |
download | libnl-7efeca2d575678c037850e8f27de9b10969c508f.zip libnl-7efeca2d575678c037850e8f27de9b10969c508f.tar.gz libnl-7efeca2d575678c037850e8f27de9b10969c508f.tar.bz2 |
tests: add test utils
-rw-r--r-- | tests/cksuite-all-attr.c | 26 | ||||
-rw-r--r-- | tests/cksuite-all-netns.c | 9 | ||||
-rw-r--r-- | tests/nl-test-util.c | 412 | ||||
-rw-r--r-- | tests/nl-test-util.h | 336 |
4 files changed, 777 insertions, 6 deletions
diff --git a/tests/cksuite-all-attr.c b/tests/cksuite-all-attr.c index 3b511f6..824a596 100644 --- a/tests/cksuite-all-attr.c +++ b/tests/cksuite-all-attr.c @@ -117,6 +117,31 @@ START_TEST(clone_cls_u32) } END_TEST +/*****************************************************************************/ + +START_TEST(test_nltst_strtok) +{ +#define _assert_strtok(str, ...) \ + do { \ + const char *const _expected[] = { NULL, ##__VA_ARGS__, NULL }; \ + _nltst_auto_strfreev char **_tokens = NULL; \ + \ + _tokens = _nltst_strtokv(str); \ + _nltst_assert_strv_equal(_tokens, &_expected[1]); \ + } while (0) + + _assert_strtok(""); + _assert_strtok(" \n"); + _assert_strtok("a", "a"); + _assert_strtok(" a ", "a"); + _assert_strtok(" a\\ b", "a\\ ", "b"); + _assert_strtok(" a\\ b cc\\d", "a\\ ", "b", "cc\\d"); + _assert_strtok(" a\\ b\\ cc\\d", "a\\ ", "b\\ ", "cc\\d"); +} +END_TEST + +/*****************************************************************************/ + Suite *make_nl_attr_suite(void) { Suite *suite = suite_create("Netlink attributes"); @@ -125,6 +150,7 @@ Suite *make_nl_attr_suite(void) tcase_add_test(tc, attr_size); tcase_add_test(tc, msg_construct); tcase_add_test(tc, clone_cls_u32); + tcase_add_test(tc, test_nltst_strtok); suite_add_tcase(suite, tc); return suite; diff --git a/tests/cksuite-all-netns.c b/tests/cksuite-all-netns.c index 872c54e..48aacf1 100644 --- a/tests/cksuite-all-netns.c +++ b/tests/cksuite-all-netns.c @@ -45,7 +45,8 @@ START_TEST(cache_and_clone) for (i = 0; i < _NL_N_ELEMENTS(links); i++) { if (links[i].add) - _nltst_add_link(NULL, links[i].ifname, links[i].kind); + _nltst_add_link(NULL, links[i].ifname, links[i].kind, + NULL); } sk = _nltst_socket(NETLINK_ROUTE); @@ -57,11 +58,11 @@ START_TEST(cache_and_clone) ck_assert_int_eq(r, 0); for (i = 0; i < _NL_N_ELEMENTS(links); i++) { - _nl_auto_rtnl_link struct rtnl_link *link = NULL; _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL; + struct rtnl_link *link; - link = rtnl_link_get_by_name(link_cache, links[i].ifname); - ck_assert(link); + link = _nltst_cache_get_link(link_cache, links[i].ifname); + ck_assert_ptr_nonnull(link); ck_assert_str_eq(rtnl_link_get_name(link), links[i].ifname); diff --git a/tests/nl-test-util.c b/tests/nl-test-util.c index 5f5059f..1f67ac8 100644 --- a/tests/nl-test-util.c +++ b/tests/nl-test-util.c @@ -3,6 +3,8 @@ #include "nl-test-util.h" #include <fcntl.h> +#include <inttypes.h> +#include <limits.h> #include <sched.h> #include <stdbool.h> #include <stdio.h> @@ -13,10 +15,70 @@ #include "netlink-private/utils.h" #include "netlink/netlink.h" #include "netlink/route/link.h" +#include "netlink/route/route.h" #include "netlink/socket.h" /*****************************************************************************/ +void _nltst_get_urandom(void *ptr, size_t len) +{ + int fd; + ssize_t nread; + + ck_assert_int_gt(len, 0); + ck_assert_ptr_nonnull(ptr); + + fd = open("/dev/urandom", O_RDONLY | O_CLOEXEC | O_NOCTTY); + _nltst_assert_errno(fd >= 0); + + nread = read(fd, ptr, len); + _nltst_assert_errno(nread == len); + + _nltst_close(fd); +} + +uint32_t _nltst_rand_u32(void) +{ + _nl_thread_local static unsigned short entropy[3]; + _nl_thread_local static bool has_entropy = false; + + if (!has_entropy) { + unsigned long long seed; + uint64_t seed64; + const char *s; + char *s_end; + + memset(entropy, 0, sizeof(entropy)); + s = getenv("NLTST_SEED_RAND"); + if (!s) + seed = 0; + else if (s[0] != '\0') { + errno = 0; + seed = strtoull(s, &s_end, 10); + if (errno != 0 || s_end[0] != '\0') { + ck_assert_msg( + 0, + "invalid NLTST_SEED_RAND=\"%s\". Must be an integer to seed the random numbers", + s); + } + } else + _nltst_get_urandom(&seed, sizeof(seed)); + + seed64 = seed; + printf("runs with NLTST_SEED_RAND=%" PRIu64 "\n", seed64); + + entropy[0] = (seed64 >> 0) ^ (seed64 >> 48); + entropy[1] = (seed64 >> 16) ^ (seed64 >> 0); + entropy[2] = (seed64 >> 32) ^ (seed64 >> 16); + has_entropy = true; + } + + _NL_STATIC_ASSERT(sizeof(long) >= sizeof(uint32_t)); + return jrand48(entropy); +} + +/*****************************************************************************/ + #define _CANARY 539339 struct nltst_netns { @@ -157,6 +219,26 @@ void _nltst_object_identical(const void *a, const void *b) /*****************************************************************************/ +char *_nltst_object_to_string(struct nl_object *obj) +{ + size_t L = 1024; + size_t l; + char *s; + + if (!obj) + return strdup("(null)"); + + s = malloc(L); + ck_assert_ptr_nonnull(s); + + nl_object_dump_buf(obj, s, L); + l = strlen(s); + ck_assert_int_lt(l, L); + s = realloc(s, l + 1); + ck_assert_ptr_nonnull(s); + return s; +} + struct cache_get_all_data { struct nl_object **arr; size_t len; @@ -184,6 +266,9 @@ struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len) .idx = 0, .len = 0, }; + size_t len2 = 0; + size_t i; + size_t j; ck_assert(cache); @@ -198,12 +283,58 @@ struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len) ck_assert_int_eq(data.idx, data.len); + ck_assert_int_le(data.len, SSIZE_MAX); + data.arr[data.len] = NULL; if (out_len) *out_len = data.len; + + /* double check the result. */ + for (struct nl_object *obj = nl_cache_get_first(cache); obj; + obj = nl_cache_get_next(obj)) { + ck_assert_ptr_eq(data.arr[len2], obj); + len2++; + } + ck_assert_ptr_null(data.arr[len2]); + + for (i = 0; i < data.len; i++) { + ck_assert_ptr_nonnull(data.arr[i]); + for (j = i + 1; j < data.len; j++) + ck_assert_ptr_ne(data.arr[i], data.arr[j]); + } + return data.arr; } +struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache, + const char *ifname) +{ + _nl_auto_free struct nl_object **objs = NULL; + struct rtnl_link *link = NULL; + size_t i; + + ck_assert_ptr_nonnull(cache); + ck_assert_ptr_nonnull(ifname); + + objs = _nltst_cache_get_all(cache, NULL); + for (i = 0; objs[i]; i++) { + if (_nl_streq(rtnl_link_get_name((struct rtnl_link *)objs[i]), + ifname)) { + ck_assert_ptr_null(link); + link = (struct rtnl_link *)objs[i]; + } + } + + if (_nltst_rand_u32_range(5) == 0) { + _nl_auto_rtnl_link struct rtnl_link *link2 = NULL; + + link2 = rtnl_link_get_by_name(cache, ifname); + ck_assert_ptr_eq(link2, link); + } + + return link; +} + /*****************************************************************************/ struct nl_sock *_nltst_socket(int protocol) @@ -217,18 +348,32 @@ struct nl_sock *_nltst_socket(int protocol) r = nl_connect(sk, protocol); ck_assert_int_eq(r, 0); + if (_nltst_rand_u32_range(5) == 0) + nl_cache_free(_nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0)); + + if (_nltst_rand_u32_range(5) == 0) + nl_cache_free(_nltst_rtnl_route_alloc_cache( + sk, _nltst_rand_select(AF_UNSPEC, AF_INET, AF_INET6))); + return sk; } -void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind) +void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind, + int *out_ifindex) { _nl_auto_nl_socket struct nl_sock *sk_free = NULL; _nl_auto_rtnl_link struct rtnl_link *link = NULL; + _nl_auto_nl_cache struct nl_cache *cache = NULL; + struct rtnl_link *link2; + int ifindex; int r; ck_assert(ifname); ck_assert(kind); + if (_nltst_rand_u32_range(5) == 0) + _nltst_assert_link_not_exists(ifname); + if (!sk) { sk = _nltst_socket(NETLINK_ROUTE); sk_free = sk; @@ -244,4 +389,269 @@ void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind) r = rtnl_link_add(sk, link, NLM_F_CREATE); ck_assert_int_eq(r, 0); + + if (!out_ifindex && _nltst_rand_u32_range(5) != 0) + return; + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + + link2 = _nltst_cache_get_link(cache, ifname); + ck_assert_ptr_nonnull(link2); + + ifindex = rtnl_link_get_ifindex(link2); + ck_assert_int_gt(ifindex, 0); + + if (out_ifindex) + *out_ifindex = ifindex; +} + +void _nltst_delete_link(struct nl_sock *sk, const char *ifname) +{ + _nl_auto_nl_socket struct nl_sock *sk_free = NULL; + _nl_auto_rtnl_link struct rtnl_link *link = NULL; + + _nltst_assert_link_exists(ifname); + + if (!sk) { + sk = _nltst_socket(NETLINK_ROUTE); + sk_free = sk; + } + + if (_nltst_rand_u32_range(5) == 0) { + _nl_auto_nl_cache struct nl_cache *cache = NULL; + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + ck_assert_ptr_nonnull(_nltst_cache_get_link(cache, ifname)); + } + + link = rtnl_link_alloc(); + ck_assert_ptr_nonnull(link); + + rtnl_link_set_name(link, ifname); + + _nltst_assert_retcode(rtnl_link_delete(sk, link)); + + _nltst_assert_link_not_exists(ifname); +} + +void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex, + struct rtnl_link **out_link) +{ + _nl_auto_nl_cache struct nl_cache *cache = NULL; + struct rtnl_link *link; + + if (_nltst_rand_u32_range(5) == 0) + _nltst_assert_link_exists(ifname); + + cache = _nltst_rtnl_link_alloc_cache(sk, AF_UNSPEC, 0); + + link = _nltst_cache_get_link(cache, ifname); + ck_assert(link); + + if (out_ifindex) + *out_ifindex = rtnl_link_get_ifindex(link); + + if (out_link) { + nl_object_get((struct nl_object *)link); + *out_link = link; + } +} + +struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk, + int addr_family, unsigned flags) +{ + _nl_auto_nl_socket struct nl_sock *sk_free = NULL; + struct nl_cache *cache; + int r; + + if (!sk) { + sk = _nltst_socket(NETLINK_ROUTE); + sk_free = sk; + } + + if (flags == 0 && _nltst_rand_bool()) + r = rtnl_link_alloc_cache(sk, addr_family, &cache); + else + r = rtnl_link_alloc_cache_flags(sk, addr_family, &cache, flags); + + _nltst_assert_retcode(r); + + if (_nltst_rand_u32_range(5) == 0) + free(_nltst_cache_get_all(cache, NULL)); + + return _nltst_assert(cache); +} + +struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk, + int addr_family) +{ + struct nl_cache *cache; + + ck_assert_ptr_nonnull(sk); + ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET || + addr_family == AF_INET6); + + _nltst_assert_retcode( + rtnl_route_alloc_cache(sk, addr_family, 0, &cache)); + + if (_nltst_rand_u32_range(5) == 0) + free(_nltst_cache_get_all(cache, NULL)); + + return _nltst_assert(cache); +} + +/*****************************************************************************/ + +char *_nltst_strtok(const char **p_str) +{ + const char *str; + _nl_auto_free char *dst = NULL; + size_t dst_len = 0; + size_t dst_alloc = 0; + size_t i; + + ck_assert_ptr_nonnull(p_str); + + str = _nltst_str_skip_space(*p_str); + + if (str[0] == '\0') { + *p_str = str; + return NULL; + } + + dst_len = 0; + dst_alloc = 10; + dst = malloc(dst_alloc); + ck_assert_ptr_nonnull(dst); + + i = 0; + while (true) { + char ch1 = '\0'; + char ch2 = '\0'; + + /* We take the first word, up until whitespace. Note that backslash + * escape is honored, so you can backslash escape spaces. The returned + * string will NOT have backslashes removed. */ + + if (str[i] == '\0') { + *p_str = &str[i]; + break; + } + if (_nltst_char_is_space(str[i])) { + *p_str = _nltst_str_skip_space(&str[i + 1]); + break; + } + ch1 = str[i]; + if (str[i] == '\\') { + if (str[i + 1] != '\0') { + ch2 = str[i + 1]; + i += 2; + } else + i += 1; + } else + i += 1; + + if (dst_len + 3 >= dst_alloc) { + dst_alloc *= 2; + dst = realloc(dst, dst_alloc); + ck_assert_ptr_nonnull(dst); + } + dst[dst_len++] = ch1; + if (ch2 != '\0') + dst[dst_len++] = ch2; + } + + ck_assert_int_gt(dst_len, 0); + return strndup(dst, dst_len); +} + +char **_nltst_strtokv(const char *str) +{ + _nl_auto_free char *s = NULL; + _nltst_auto_strfreev char **result = NULL; + size_t r_len = 0; + size_t r_alloc = 0; + + if (!str) + return NULL; + + r_alloc = 4; + result = malloc(sizeof(char *) * r_alloc); + ck_assert_ptr_nonnull(result); + + while ((s = _nltst_strtok(&str))) { + if (r_len + 2 >= r_alloc) { + r_alloc *= 2; + result = realloc(result, sizeof(char *) * r_alloc); + ck_assert_ptr_nonnull(result); + } + result[r_len++] = _nl_steal_pointer(&s); + } + ck_assert_int_lt(r_len, r_alloc); + result[r_len] = NULL; + return _nl_steal_pointer(&result); +} + +/*****************************************************************************/ + +void _nltst_assert_link_exists_full(const char *ifname, bool exists) +{ + _nl_auto_nl_cache struct nl_cache *cache = NULL; + _nl_auto_rtnl_link struct rtnl_link *link_clone = NULL; + struct rtnl_link *link; + char path[100]; + struct stat st; + int rnd; + int r; + + ck_assert_pstr_ne(ifname, NULL); + ck_assert_int_lt(strlen(ifname), IFNAMSIZ); + + strcpy(path, "/sys/class/net/"); + strcat(path, ifname); + ck_assert_int_lt(strlen(path), sizeof(path)); + + r = stat(path, &st); + if (exists) { + if (r != 0) { + const int errsv = (errno); + + ck_assert_msg( + 0, + "link(%s) does not exist (stat(%s) failed with r=%d, errno=%d (%s)", + ifname, path, r, errsv, strerror(errsv)); + } + } else { + if (r != -1 && errno != ENOENT) { + const int errsv = (errno); + + ck_assert_msg( + 0, + "link(%s) should not exist but stat(%s) gave r=%d, errno=%d (%s)", + ifname, path, r, errsv, strerror(errsv)); + } + } + + rnd = _nltst_rand_u32_range(3); + + if (rnd == 0) + return; + + cache = _nltst_rtnl_link_alloc_cache(NULL, AF_UNSPEC, 0); + + link = _nltst_cache_get_link(cache, ifname); + if (exists) + ck_assert_ptr_nonnull(link); + else + ck_assert_ptr_null(link); + + if (!link || rnd == 1) + return; + + link_clone = + (struct rtnl_link *)nl_object_clone((struct nl_object *)link); + ck_assert(link_clone); + + /* FIXME: we would expect that the cloned object is identical. It is not. */ + /* _nltst_object_identical(link, link_clone); */ } diff --git a/tests/nl-test-util.h b/tests/nl-test-util.h index 259ab59..0ff9752 100644 --- a/tests/nl-test-util.h +++ b/tests/nl-test-util.h @@ -4,17 +4,102 @@ #define __NL_TEST_UTIL_H__ #include <errno.h> +#include <sys/stat.h> #include <check.h> +#include <string.h> +#include <stdbool.h> +#include <arpa/inet.h> #include "netlink/object.h" #include "netlink/cache.h" +#include "netlink-private/nl-auto.h" +#include "netlink-private/utils.h" + +/*****************************************************************************/ + +static inline void _nltst_strfreev(char **strv) +{ + size_t i; + + if (strv) { + for (i = 0; strv[i]; i++) + free(strv[i]); + free(strv); + } +} + +#define _nltst_auto_strfreev _nl_auto(_nltst_auto_strfreev_fcn) +_NL_AUTO_DEFINE_FCN_TYPED0(char **, _nltst_auto_strfreev_fcn, _nltst_strfreev); + /*****************************************************************************/ #ifndef ck_assert_ptr_nonnull #define ck_assert_ptr_nonnull(ptr) ck_assert(ptr) #endif +#ifndef ck_assert_pstr_ne +#define ck_assert_pstr_ne(a, b) \ + do { \ + const char *_a = (a); \ + const char *_b = (b); \ + \ + ck_assert(!(_a == _b || (_a && _b && strcmp(_a, _b) == 0))); \ + } while (0) +#endif + +#ifndef ck_assert_ptr_null +#define ck_assert_ptr_null(ptr) ck_assert(!(ptr)) +#endif + +/*****************************************************************************/ + +void _nltst_get_urandom(void *ptr, size_t len); + +uint32_t _nltst_rand_u32(void); + +static inline uint32_t _nltst_rand_u32_range(uint32_t n) +{ + uint32_t rem; + uint32_t i; + + if (n == 0) + return _nltst_rand_u32(); + if (n == 1) + return 0; + + rem = UINT32_MAX % n; + for (;;) { + i = _nltst_rand_u32(); + if (i < (UINT32_MAX - rem)) + return i % n; + } +} + +static inline bool _nltst_rand_bool(void) +{ + return _nltst_rand_u32() % 2 == 0; +} + +#define _nltst_rand_select(a, ...) \ + ({ \ + const typeof(a) _lst[] = { (a), ##__VA_ARGS__ }; \ + \ + _lst[_nltst_rand_u32_range(_NL_N_ELEMENTS(_lst))]; \ + }) + +/*****************************************************************************/ + +#define _nltst_assert(expr) \ + ({ \ + typeof(expr) _expr = (expr); \ + \ + if (!_expr) { \ + ck_assert_msg(0, "assert(%s) failed", #expr); \ + } \ + _expr; \ + }) + #define _nltst_assert_errno(expr) \ do { \ if (expr) { \ @@ -26,6 +111,23 @@ } \ } while (0) +#define _nltst_assert_retcode(expr) \ + do { \ + const int _r = (expr); \ + \ + if (_r < 0) { \ + ck_assert_msg( \ + 0, "command(%s) failed with return code %d", \ + #expr, _r); \ + } \ + if (_r > 0) { \ + ck_assert_msg( \ + 0, \ + "command(%s) has unexpected positive return code %d", \ + #expr, _r); \ + } \ + } while (0) + #define _nltst_close(fd) \ do { \ int _r; \ @@ -42,6 +144,221 @@ _nltst_assert_errno(_r == 0); \ } while (0) +void _nltst_assert_link_exists_full(const char *ifname, bool exists); + +#define _nltst_assert_link_exists(ifname) \ + _nltst_assert_link_exists_full((ifname), true) + +#define _nltst_assert_link_not_exists(ifname) \ + _nltst_assert_link_exists_full((ifname), false) + +/*****************************************************************************/ + +typedef union { + in_addr_t addr4; + struct in_addr a4; + struct in6_addr a6; +} NLTstIPAddr; + +static inline char *_nltst_inet_ntop(int addr_family, const void *addr, + char buf[static INET_ADDRSTRLEN]) +{ + char *r; + + ck_assert(addr_family == AF_INET || addr_family == AF_INET6); + ck_assert(addr); + + r = (char *)inet_ntop(addr_family, addr, buf, + (addr_family == AF_INET) ? INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + ck_assert_ptr_eq(r, buf); + ck_assert_int_lt(strlen(r), (addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN); + return r; +} + +static inline char *_nltst_inet_ntop_dup(int addr_family, const void *addr) +{ + return (char *)_nltst_inet_ntop(addr_family, addr, + malloc((addr_family == AF_INET) ? + INET_ADDRSTRLEN : + INET6_ADDRSTRLEN)); +} + +static inline bool _nltst_inet_pton(int addr_family, const char *str, + int *out_addr_family, void *out_addr) +{ + NLTstIPAddr a; + int r; + + ck_assert(addr_family == AF_UNSPEC || addr_family == AF_INET || + addr_family == AF_INET6); + + /* when requesting @out_addr, then the addr-family must either be + * pre-determined or requested too. */ + ck_assert(!out_addr || out_addr_family || addr_family != AF_UNSPEC); + + if (!str) + return false; + + if (addr_family == AF_UNSPEC) + addr_family = strchr(str, ':') ? AF_INET6 : AF_INET; + + r = inet_pton(addr_family, str, &a); + if (r != 1) + return false; + + if (out_addr) { + memcpy(out_addr, &a, + addr_family == AF_INET ? sizeof(in_addr_t) : + sizeof(struct in6_addr)); + } + if (out_addr_family) + *out_addr_family = addr_family; + + return true; +} + +static inline bool _nltst_inet_valid(int addr_family, const char *addr) +{ + return _nltst_inet_pton(addr_family, addr, NULL, NULL); +} + +static inline in_addr_t _nltst_inet4(const char *addr) +{ + in_addr_t addr_bin = 0; + + _nltst_assert(_nltst_inet_pton(AF_INET, addr, NULL, &addr_bin)); + return addr_bin; +} + +static inline struct in6_addr *_nltst_inet6p(const char *addr) +{ + _nl_thread_local static struct in6_addr addr_bin; + + ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); + return &addr_bin; +} + +static inline struct in6_addr _nltst_inet6(const char *addr) +{ + struct in6_addr addr_bin; + + ck_assert(_nltst_inet_pton(AF_INET6, addr, NULL, &addr_bin)); + return addr_bin; +} + +static inline int _nltst_inet_addr_family(int addr_family, const char *addr) +{ + if (!_nltst_inet_pton(addr_family, addr, &addr_family, NULL)) + return AF_UNSPEC; + return addr_family; +} + +static inline char *_nltst_inet_normalize(int addr_family, const char *addr, + char buf[static INET_ADDRSTRLEN]) +{ + NLTstIPAddr a; + + buf[0] = '\0'; + if (!_nltst_inet_pton(addr_family, addr, &addr_family, &a)) + return NULL; + return _nltst_inet_ntop(addr_family, &a, buf); +} + +/*****************************************************************************/ + +char *_nltst_strtok(const char **p_str); + +char **_nltst_strtokv(const char *str); + +#define _nltst_assert_strv_equal(strv1, strv2) \ + do { \ + typeof(strv1) _strv1 = (strv1); \ + typeof(strv2) _strv2 = (strv2); \ + _nl_unused const void *_strv1_typecheck1 = _strv1; \ + _nl_unused const void *_strv2_typecheck1 = _strv2; \ + _nl_unused const char *_strv1_typecheck2 = \ + _strv1 ? _strv1[0] : NULL; \ + _nl_unused const char *_strv2_typecheck2 = \ + _strv2 ? _strv2[0] : NULL; \ + size_t _i; \ + \ + ck_assert_int_eq(!!_strv1, !!_strv2); \ + if (_strv1) { \ + for (_i = 0; _strv1[_i] || _strv2[_i]; _i++) { \ + ck_assert_str_eq(_strv1[_i], _strv2[_i]); \ + } \ + } \ + } while (0) + +#define _NLTST_CHARSET_SPACE " \n\r\t" + +#define _nltst_char_is(ch, charset) (!!(strchr("" charset "", (ch)))) + +#define _nltst_char_is_space(ch) _nltst_char_is(ch, _NLTST_CHARSET_SPACE) + +#define _nltst_str_skip_predicate(s, ch, predicate) \ + ({ \ + typeof(s) _s1 = (s); \ + _nl_unused const char *_s1_typecheck = (_s1); \ + \ + if (_s1) { \ + while (({ \ + const char ch = _s1[0]; \ + \ + (ch != '\0') && (predicate); \ + })) \ + _s1++; \ + } \ + _s1; \ + }) + +#define _nltst_str_skip_charset(s, charset) \ + _nltst_str_skip_predicate(s, _ch, _nltst_char_is(_ch, "" charset "")) + +#define _nltst_str_skip_space(s) \ + _nltst_str_skip_charset(s, _NLTST_CHARSET_SPACE) + +#define _nltst_str_has_prefix_and_space(s, prefix) \ + ({ \ + typeof(s) _s2 = (s); \ + _nl_unused const char *_s2_typecheck = (_s2); \ + const size_t _l = strlen("" prefix ""); \ + \ + if (_s2) { \ + if ((strncmp(_s2, "" prefix "", _l)) == 0 && \ + _nltst_char_is_space(_s2[_l])) \ + _s2 = _nltst_str_skip_space(&_s2[_l + 1]); \ + else \ + _s2 = NULL; \ + } \ + _s2; \ + }) + +#define _nltst_str_find_first_not_from_charset(s, charset) \ + ({ \ + typeof(s) _s3 = (s); \ + _nl_unused const char *_s3_typecheck = (_s3); \ + size_t _l3; \ + \ + _l3 = strspn(_s3, "" charset ""); \ + \ + &_s3[_l3]; \ + }) + +#define _nltst_str_find_first_from_charset(s, charset) \ + ({ \ + typeof(s) _s3 = (s); \ + _nl_unused const char *_s3_typecheck = (_s3); \ + size_t _l3; \ + \ + _l3 = strcspn(_s3, "" charset ""); \ + \ + &_s3[_l3]; \ + }) + /*****************************************************************************/ void nltst_netns_fixture_setup(void); @@ -56,11 +373,28 @@ void nltst_netns_leave(struct nltst_netns *nsdata); void _nltst_object_identical(const void *a, const void *b); +char *_nltst_object_to_string(struct nl_object *obj); + struct nl_object **_nltst_cache_get_all(struct nl_cache *cache, size_t *out_len); +struct rtnl_link *_nltst_cache_get_link(struct nl_cache *cache, + const char *ifname); + +struct nl_cache *_nltst_rtnl_link_alloc_cache(struct nl_sock *sk, + int addr_family, unsigned flags); + +struct nl_cache *_nltst_rtnl_route_alloc_cache(struct nl_sock *sk, + int addr_family); + struct nl_sock *_nltst_socket(int protocol); -void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind); +void _nltst_add_link(struct nl_sock *sk, const char *ifname, const char *kind, + int *out_ifindex); + +void _nltst_delete_link(struct nl_sock *sk, const char *ifname); + +void _nltst_get_link(struct nl_sock *sk, const char *ifname, int *out_ifindex, + struct rtnl_link **out_link); #endif /* __NL_TEST_UTIL_H__ */ |