| /* SPDX-License-Identifier: LGPL-2.1-only */ |
| |
| #ifndef __NL_TEST_UTIL_H__ |
| #define __NL_TEST_UTIL_H__ |
| |
| #include <sys/stat.h> |
| #include <check.h> |
| |
| #include <netlink/object.h> |
| #include <netlink/cache.h> |
| |
| #include "base/nl-base-utils.h" |
| #include "nl-aux-core/nl-core.h" |
| #include "nl-aux-route/nl-route.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) { \ |
| } else { \ |
| const int _errno = (errno); \ |
| \ |
| ck_assert_msg(0, "assert(%s) failed (errno=%d, %s)", \ |
| #expr, _errno, strerror(_errno)); \ |
| } \ |
| } 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; \ |
| \ |
| _r = _nl_close((fd)); \ |
| _nltst_assert_errno(_r == 0); \ |
| } while (0) |
| |
| #define _nltst_fclose(f) \ |
| do { \ |
| int _r; \ |
| \ |
| _r = fclose((f)); \ |
| _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); |
| void nltst_netns_fixture_teardown(void); |
| |
| struct nltst_netns; |
| |
| struct nltst_netns *nltst_netns_enter(void); |
| 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, |
| 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__ */ |