/* SPDX-License-Identifier: LGPL-2.1+ */ /* * Copyright (C) 2018 Red Hat, Inc. */ #define NM_TEST_UTILS_NO_LIBNM 1 #include "nm-default.h" #include "nm-std-aux/unaligned.h" #include "nm-glib-aux/nm-random-utils.h" #include "nm-glib-aux/nm-str-buf.h" #include "nm-glib-aux/nm-time-utils.h" #include "nm-glib-aux/nm-ref-string.h" #include "nm-utils/nm-test-utils.h" /*****************************************************************************/ G_STATIC_ASSERT(NM_AF_UNSPEC == AF_UNSPEC); G_STATIC_ASSERT(NM_AF_INET == AF_INET); G_STATIC_ASSERT(NM_AF_INET6 == AF_INET6); G_STATIC_ASSERT(NM_AF_INET_SIZE == sizeof(in_addr_t)); G_STATIC_ASSERT(NM_AF_INET_SIZE == sizeof(struct in_addr)); G_STATIC_ASSERT(NM_AF_INET6_SIZE == sizeof(struct in6_addr)); G_STATIC_ASSERT(4 == _nm_alignof(in_addr_t)); G_STATIC_ASSERT(4 == _nm_alignof(struct in_addr)); G_STATIC_ASSERT(4 == _nm_alignof(struct in6_addr)); G_STATIC_ASSERT(4 == _nm_alignof(NMIPAddr)); /*****************************************************************************/ static void test_gpid(void) { const int *int_ptr; GPid pid = 42; /* We redefine G_PID_FORMAT, because it's only available since glib 2.53.5. * * Also, this is the format for GPid, which for glib is always a typedef * for "int". Add a check for that here. * * G_PID_FORMAT is not about pid_t, which might be a smaller int, and which we would * check with SIZEOF_PID_T. */ G_STATIC_ASSERT(sizeof(GPid) == sizeof(int)); g_assert_cmpstr("" G_PID_FORMAT, ==, "i"); /* check that it's really "int". We will get a compiler warning, if that's not * the case. */ int_ptr = &pid; g_assert_cmpint(*int_ptr, ==, 42); } /*****************************************************************************/ static void test_monotonic_timestamp(void) { g_assert(nm_utils_get_monotonic_timestamp_sec() > 0); } /*****************************************************************************/ static void test_nmhash(void) { int rnd; nm_utils_random_bytes(&rnd, sizeof(rnd)); g_assert(nm_hash_val(555, 4) != 0); } /*****************************************************************************/ static const char * _make_strv_foo(void) { return "foo"; } static const char *const *const _tst_make_strv_1 = NM_MAKE_STRV("1", "2"); static void test_make_strv(void) { const char *const *v1a = NM_MAKE_STRV("a"); const char *const *v1b = NM_MAKE_STRV("a", ); const char *const *v2a = NM_MAKE_STRV("a", "b"); const char *const *v2b = NM_MAKE_STRV("a", "b", ); const char *const v3[] = { "a", "b", }; const char *const *v4b = NM_MAKE_STRV("a", _make_strv_foo(), ); g_assert(NM_PTRARRAY_LEN(v1a) == 1); g_assert(NM_PTRARRAY_LEN(v1b) == 1); g_assert(NM_PTRARRAY_LEN(v2a) == 2); g_assert(NM_PTRARRAY_LEN(v2b) == 2); g_assert(NM_PTRARRAY_LEN(_tst_make_strv_1) == 2); g_assert_cmpstr(_tst_make_strv_1[0], ==, "1"); g_assert_cmpstr(_tst_make_strv_1[1], ==, "2"); /* writing the static read-only variable leads to crash .*/ //((char **) _tst_make_strv_1)[0] = NULL; //((char **) _tst_make_strv_1)[2] = "c"; G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(v3) == 2); g_assert(NM_PTRARRAY_LEN(v4b) == 2); G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(NM_MAKE_STRV("a", "b")) == 3); G_STATIC_ASSERT_EXPR(G_N_ELEMENTS(NM_MAKE_STRV("a", "b", )) == 3); nm_strquote_a(300, ""); } /*****************************************************************************/ typedef enum { TEST_NM_STRDUP_ENUM_m1 = -1, TEST_NM_STRDUP_ENUM_3 = 3, } TestNMStrdupIntEnum; static void test_nm_strdup_int(void) { #define _NM_STRDUP_INT_TEST(num, str) \ G_STMT_START \ { \ gs_free char *_s1 = NULL; \ \ _s1 = nm_strdup_int((num)); \ \ g_assert(_s1); \ g_assert_cmpstr(_s1, ==, str); \ } \ G_STMT_END #define _NM_STRDUP_INT_TEST_TYPED(type, num) \ G_STMT_START \ { \ type _num = ((type) num); \ \ _NM_STRDUP_INT_TEST(_num, G_STRINGIFY(num)); \ } \ G_STMT_END _NM_STRDUP_INT_TEST_TYPED(char, 0); _NM_STRDUP_INT_TEST_TYPED(char, 1); _NM_STRDUP_INT_TEST_TYPED(guint8, 0); _NM_STRDUP_INT_TEST_TYPED(gint8, 25); _NM_STRDUP_INT_TEST_TYPED(char, 47); _NM_STRDUP_INT_TEST_TYPED(short, 47); _NM_STRDUP_INT_TEST_TYPED(int, 47); _NM_STRDUP_INT_TEST_TYPED(long, 47); _NM_STRDUP_INT_TEST_TYPED(unsigned char, 47); _NM_STRDUP_INT_TEST_TYPED(unsigned short, 47); _NM_STRDUP_INT_TEST_TYPED(unsigned, 47); _NM_STRDUP_INT_TEST_TYPED(unsigned long, 47); _NM_STRDUP_INT_TEST_TYPED(gint64, 9223372036854775807); _NM_STRDUP_INT_TEST_TYPED(gint64, -9223372036854775807); _NM_STRDUP_INT_TEST_TYPED(guint64, 0); _NM_STRDUP_INT_TEST_TYPED(guint64, 9223372036854775807); _NM_STRDUP_INT_TEST(TEST_NM_STRDUP_ENUM_m1, "-1"); _NM_STRDUP_INT_TEST(TEST_NM_STRDUP_ENUM_3, "3"); } /*****************************************************************************/ static void test_nm_strndup_a(void) { int run; for (run = 0; run < 20; run++) { gs_free char *input = NULL; char ch; gsize i, l; input = g_strnfill(nmtst_get_rand_uint32() % 20, 'x'); for (i = 0; input[i]; i++) { while ((ch = ((char) nmtst_get_rand_uint32())) == '\0') { /* repeat. */ } input[i] = ch; } { gs_free char *dup_free = NULL; const char * dup; l = strlen(input) + 1; dup = nm_strndup_a(10, input, l - 1, &dup_free); g_assert_cmpstr(dup, ==, input); if (strlen(dup) < 10) g_assert(!dup_free); else g_assert(dup == dup_free); } { gs_free char *dup_free = NULL; const char * dup; l = nmtst_get_rand_uint32() % 23; dup = nm_strndup_a(10, input, l, &dup_free); g_assert(strncmp(dup, input, l) == 0); g_assert(strlen(dup) <= l); if (l < 10) g_assert(!dup_free); else g_assert(dup == dup_free); if (strlen(input) < l) g_assert(nm_utils_memeqzero(&dup[strlen(input)], l - strlen(input))); } } } /*****************************************************************************/ static void test_nm_ip4_addr_is_localhost(void) { g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.0.0.0"))); g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.0.0.1"))); g_assert(nm_ip4_addr_is_localhost(nmtst_inet4_from_string("127.5.0.1"))); g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("126.5.0.1"))); g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("128.5.0.1"))); g_assert(!nm_ip4_addr_is_localhost(nmtst_inet4_from_string("129.5.0.1"))); } /*****************************************************************************/ static void test_unaligned(void) { int shift; for (shift = 0; shift <= 32; shift++) { guint8 buf[100] = {}; guint8 val = 0; while (val == 0) val = nmtst_get_rand_uint32() % 256; buf[shift] = val; g_assert_cmpint(unaligned_read_le64(&buf[shift]), ==, (guint64) val); g_assert_cmpint(unaligned_read_be64(&buf[shift]), ==, ((guint64) val) << 56); g_assert_cmpint(unaligned_read_ne64(&buf[shift]), !=, 0); g_assert_cmpint(unaligned_read_le32(&buf[shift]), ==, (guint32) val); g_assert_cmpint(unaligned_read_be32(&buf[shift]), ==, ((guint32) val) << 24); g_assert_cmpint(unaligned_read_ne32(&buf[shift]), !=, 0); g_assert_cmpint(unaligned_read_le16(&buf[shift]), ==, (guint16) val); g_assert_cmpint(unaligned_read_be16(&buf[shift]), ==, ((guint16) val) << 8); g_assert_cmpint(unaligned_read_ne16(&buf[shift]), !=, 0); } } /*****************************************************************************/ static void _strv_cmp_fuzz_input(const char *const * in, gssize l, const char *** out_strv_free_shallow, char *** out_strv_free_deep, const char *const **out_s1, const char *const **out_s2) { const char **strv; gsize i; /* Fuzz the input argument. It will return two output arrays that are semantically * equal the input. */ if (nmtst_get_rand_bool()) { char **ss; if (l < 0) ss = g_strdupv((char **) in); else if (l == 0) { ss = nmtst_get_rand_bool() ? NULL : g_new0(char *, 1); } else { ss = nm_memdup(in, sizeof(const char *) * l); for (i = 0; i < (gsize) l; i++) ss[i] = g_strdup(ss[i]); } strv = (const char **) ss; *out_strv_free_deep = ss; } else { if (l < 0) { strv = in ? nm_memdup(in, sizeof(const char *) * (NM_PTRARRAY_LEN(in) + 1)) : NULL; } else if (l == 0) { strv = nmtst_get_rand_bool() ? NULL : g_new0(const char *, 1); } else strv = nm_memdup(in, sizeof(const char *) * l); *out_strv_free_shallow = strv; } *out_s1 = in; *out_s2 = strv; if (nmtst_get_rand_bool()) { /* randomly swap the original and the clone. That means, out_s1 is either * the input argument (as-is) or the sementically equal clone. */ NM_SWAP(out_s1, out_s2); } if (nmtst_get_rand_bool()) { /* randomly make s1 and s2 the same. This is for testing that * comparing two identical pointers yields the same result. */ *out_s2 = *out_s1; } } static void _strv_cmp_free_deep(char **strv, gssize len) { gssize i; if (strv) { if (len < 0) g_strfreev(strv); else { for (i = 0; i < len; i++) g_free(strv[i]); g_free(strv); } } } static void test_strv_cmp(void) { const char *const strv0[1] = {}; const char *const strv1[2] = { "", }; #define _STRV_CMP(a1, l1, a2, l2, equal) \ G_STMT_START \ { \ gssize _l1 = (l1); \ gssize _l2 = (l2); \ const char *const * _a1; \ const char *const * _a2; \ const char *const * _a1x; \ const char *const * _a2x; \ char ** _a1_free_deep = NULL; \ char ** _a2_free_deep = NULL; \ gs_free const char **_a1_free_shallow = NULL; \ gs_free const char **_a2_free_shallow = NULL; \ int _c1, _c2; \ \ _strv_cmp_fuzz_input((a1), _l1, &_a1_free_shallow, &_a1_free_deep, &_a1, &_a1x); \ _strv_cmp_fuzz_input((a2), _l2, &_a2_free_shallow, &_a2_free_deep, &_a2, &_a2x); \ \ _c1 = _nm_utils_strv_cmp_n(_a1, _l1, _a2, _l2); \ _c2 = _nm_utils_strv_cmp_n(_a2, _l2, _a1, _l1); \ if (equal) { \ g_assert_cmpint(_c1, ==, 0); \ g_assert_cmpint(_c2, ==, 0); \ } else { \ g_assert_cmpint(_c1, ==, -1); \ g_assert_cmpint(_c2, ==, 1); \ } \ \ /* Compare with self. _strv_cmp_fuzz_input() randomly swapped the arguments (_a1 and _a1x). * Either way, the arrays must compare equal to their semantically equal alternative. */ \ g_assert_cmpint(_nm_utils_strv_cmp_n(_a1, _l1, _a1x, _l1), ==, 0); \ g_assert_cmpint(_nm_utils_strv_cmp_n(_a2, _l2, _a2x, _l2), ==, 0); \ \ _strv_cmp_free_deep(_a1_free_deep, _l1); \ _strv_cmp_free_deep(_a2_free_deep, _l2); \ } \ G_STMT_END _STRV_CMP(NULL, -1, NULL, -1, TRUE); _STRV_CMP(NULL, -1, NULL, 0, FALSE); _STRV_CMP(NULL, -1, strv0, 0, FALSE); _STRV_CMP(NULL, -1, strv0, -1, FALSE); _STRV_CMP(NULL, 0, NULL, 0, TRUE); _STRV_CMP(NULL, 0, strv0, 0, TRUE); _STRV_CMP(NULL, 0, strv0, -1, TRUE); _STRV_CMP(strv0, 0, strv0, 0, TRUE); _STRV_CMP(strv0, 0, strv0, -1, TRUE); _STRV_CMP(strv0, -1, strv0, -1, TRUE); _STRV_CMP(NULL, 0, strv1, -1, FALSE); _STRV_CMP(NULL, 0, strv1, 1, FALSE); _STRV_CMP(strv0, 0, strv1, -1, FALSE); _STRV_CMP(strv0, 0, strv1, 1, FALSE); _STRV_CMP(strv0, -1, strv1, -1, FALSE); _STRV_CMP(strv0, -1, strv1, 1, FALSE); _STRV_CMP(strv1, -1, strv1, 1, TRUE); _STRV_CMP(strv1, 1, strv1, 1, TRUE); } /*****************************************************************************/ static void _do_strstrip_avoid_copy(const char *str) { gs_free char *str1 = g_strdup(str); gs_free char *str2 = g_strdup(str); gs_free char *str3 = NULL; gs_free char *str4 = NULL; const char * s3; const char * s4; if (str1) g_strstrip(str1); nm_strstrip(str2); g_assert_cmpstr(str1, ==, str2); s3 = nm_strstrip_avoid_copy(str, &str3); g_assert_cmpstr(str1, ==, s3); s4 = nm_strstrip_avoid_copy_a(10, str, &str4); g_assert_cmpstr(str1, ==, s4); g_assert(!str == !s4); g_assert(!s4 || strlen(s4) <= strlen(str)); if (s4 && s4 == &str[strlen(str) - strlen(s4)]) { g_assert(!str4); g_assert(s3 == s4); } else if (s4 && strlen(s4) >= 10) { g_assert(str4); g_assert(s4 == str4); } else g_assert(!str4); if (!nm_streq0(str1, str)) _do_strstrip_avoid_copy(str1); } static void test_strstrip_avoid_copy(void) { _do_strstrip_avoid_copy(NULL); _do_strstrip_avoid_copy(""); _do_strstrip_avoid_copy(" "); _do_strstrip_avoid_copy(" a "); _do_strstrip_avoid_copy(" 012345678 "); _do_strstrip_avoid_copy(" 0123456789 "); _do_strstrip_avoid_copy(" 01234567890 "); _do_strstrip_avoid_copy(" 012345678901 "); } /*****************************************************************************/ static void test_nm_utils_bin2hexstr(void) { int n_run; for (n_run = 0; n_run < 500; n_run++) { guint8 buf[100]; guint8 buf2[G_N_ELEMENTS(buf) + 1]; gsize len = nmtst_get_rand_uint32() % (G_N_ELEMENTS(buf) + 1); char strbuf1[G_N_ELEMENTS(buf) * 3]; gboolean allocate = nmtst_get_rand_bool(); char delimiter = nmtst_get_rand_bool() ? ':' : '\0'; gboolean upper_case = nmtst_get_rand_bool(); gboolean hexdigit_pairs_mangled; gsize expected_strlen; char * str_hex; gsize required_len; gboolean outlen_set; gsize outlen; guint8 * bin2; guint i, j; nmtst_rand_buf(NULL, buf, len); if (len == 0) expected_strlen = 0; else if (delimiter != '\0') expected_strlen = (len * 3u) - 1; else expected_strlen = len * 2u; g_assert_cmpint(expected_strlen, <, G_N_ELEMENTS(strbuf1)); str_hex = nm_utils_bin2hexstr_full(buf, len, delimiter, upper_case, !allocate ? strbuf1 : NULL); g_assert(str_hex); if (!allocate) g_assert(str_hex == strbuf1); g_assert_cmpint(strlen(str_hex), ==, expected_strlen); g_assert(NM_STRCHAR_ALL( str_hex, ch, (ch >= '0' && ch <= '9') || ch == delimiter || (upper_case ? (ch >= 'A' && ch <= 'F') : (ch >= 'a' && ch <= 'f')))); hexdigit_pairs_mangled = FALSE; if (delimiter && len > 1 && nmtst_get_rand_bool()) { /* randomly convert "0?" sequences to single digits, so we can get hexdigit_pairs_required * parameter. */ g_assert(strlen(str_hex) >= 5); g_assert(str_hex[2] == delimiter); i = 0; j = 0; for (;;) { g_assert(g_ascii_isxdigit(str_hex[i])); g_assert(g_ascii_isxdigit(str_hex[i + 1])); g_assert(NM_IN_SET(str_hex[i + 2], delimiter, '\0')); if (str_hex[i] == '0' && nmtst_get_rand_bool()) { i++; str_hex[j++] = str_hex[i++]; hexdigit_pairs_mangled = TRUE; } else { str_hex[j++] = str_hex[i++]; str_hex[j++] = str_hex[i++]; } if (str_hex[i] == '\0') { str_hex[j] = '\0'; break; } g_assert(str_hex[i] == delimiter); str_hex[j++] = str_hex[i++]; } } required_len = nmtst_get_rand_bool() ? len : 0u; outlen_set = required_len == 0 || nmtst_get_rand_bool(); memset(buf2, 0, sizeof(buf2)); bin2 = nm_utils_hexstr2bin_full(str_hex, nmtst_get_rand_bool(), delimiter != '\0' && nmtst_get_rand_bool(), !hexdigit_pairs_mangled && nmtst_get_rand_bool(), delimiter != '\0' ? nmtst_rand_select((const char *) ":", ":-") : nmtst_rand_select((const char *) ":", ":-", "", NULL), required_len, buf2, len, outlen_set ? &outlen : NULL); if (len > 0) { g_assert(bin2); g_assert(bin2 == buf2); } else g_assert(!bin2); if (outlen_set) g_assert_cmpint(outlen, ==, len); g_assert_cmpmem(buf, len, buf2, len); g_assert(buf2[len] == '\0'); if (hexdigit_pairs_mangled) { /* we mangled the hexstr to contain single digits. Trying to parse with * hexdigit_pairs_required must now fail. */ bin2 = nm_utils_hexstr2bin_full( str_hex, nmtst_get_rand_bool(), delimiter != '\0' && nmtst_get_rand_bool(), TRUE, delimiter != '\0' ? nmtst_rand_select((const char *) ":", ":-") : nmtst_rand_select((const char *) ":", ":-", "", NULL), required_len, buf2, len, outlen_set ? &outlen : NULL); g_assert(!bin2); } if (allocate) g_free(str_hex); } } /*****************************************************************************/ static void test_nm_ref_string(void) { nm_auto_ref_string NMRefString *s1 = NULL; NMRefString * s2; s1 = nm_ref_string_new("hallo"); g_assert(s1); g_assert_cmpstr(s1->str, ==, "hallo"); g_assert_cmpint(s1->len, ==, strlen("hallo")); s2 = nm_ref_string_new("hallo"); g_assert(s2 == s1); nm_ref_string_unref(s2); s2 = nm_ref_string_new(NULL); g_assert(!s2); nm_ref_string_unref(s2); #define STR_WITH_NUL "hallo\0test\0" s2 = nm_ref_string_new_len(STR_WITH_NUL, NM_STRLEN(STR_WITH_NUL)); g_assert(s2); g_assert_cmpstr(s2->str, ==, "hallo"); g_assert_cmpint(s2->len, ==, NM_STRLEN(STR_WITH_NUL)); g_assert_cmpint(s2->len, >, strlen(s2->str)); g_assert_cmpmem(s2->str, s2->len, STR_WITH_NUL, NM_STRLEN(STR_WITH_NUL)); g_assert(s2->str[s2->len] == '\0'); nm_ref_string_unref(s2); } /*****************************************************************************/ static NM_UTILS_STRING_TABLE_LOOKUP_DEFINE( _do_string_table_lookup, int, { ; }, { return -1; }, {"0", 0}, {"1", 1}, {"2", 2}, {"3", 3}, ) static void test_string_table_lookup(void) { const char *const args[] = { NULL, "0", "1", "2", "3", "x", }; int i; for (i = 0; i < G_N_ELEMENTS(args); i++) { const char *needle = args[i]; const int val2 = _nm_utils_ascii_str_to_int64(needle, 10, 0, 100, -1); int val; val = _do_string_table_lookup(needle); g_assert_cmpint(val, ==, val2); } } /*****************************************************************************/ static void test_nm_utils_get_next_realloc_size(void) { static const struct { gsize requested; gsize reserved_true; gsize reserved_false; } test_data[] = { {0, 8, 8}, {1, 8, 8}, {8, 8, 8}, {9, 16, 16}, {16, 16, 16}, {17, 32, 32}, {32, 32, 32}, {33, 40, 40}, {40, 40, 40}, {41, 104, 104}, {104, 104, 104}, {105, 232, 232}, {232, 232, 232}, {233, 488, 488}, {488, 488, 488}, {489, 1000, 1000}, {1000, 1000, 1000}, {1001, 2024, 2024}, {2024, 2024, 2024}, {2025, 4072, 4072}, {4072, 4072, 4072}, {4073, 8168, 8168}, {8168, 8168, 8168}, {8169, 12264, 16360}, {12263, 12264, 16360}, {12264, 12264, 16360}, {12265, 16360, 16360}, {16360, 16360, 16360}, {16361, 20456, 32744}, {20456, 20456, 32744}, {20457, 24552, 32744}, {24552, 24552, 32744}, {24553, 28648, 32744}, {28648, 28648, 32744}, {28649, 32744, 32744}, {32744, 32744, 32744}, {32745, 36840, 65512}, {36840, 36840, 65512}, {G_MAXSIZE - 0x1000u, G_MAXSIZE, G_MAXSIZE}, {G_MAXSIZE - 25u, G_MAXSIZE, G_MAXSIZE}, {G_MAXSIZE - 24u, G_MAXSIZE, G_MAXSIZE}, {G_MAXSIZE - 1u, G_MAXSIZE, G_MAXSIZE}, {G_MAXSIZE, G_MAXSIZE, G_MAXSIZE}, {NM_UTILS_GET_NEXT_REALLOC_SIZE_32, NM_UTILS_GET_NEXT_REALLOC_SIZE_32, NM_UTILS_GET_NEXT_REALLOC_SIZE_32}, {NM_UTILS_GET_NEXT_REALLOC_SIZE_40, NM_UTILS_GET_NEXT_REALLOC_SIZE_40, NM_UTILS_GET_NEXT_REALLOC_SIZE_40}, {NM_UTILS_GET_NEXT_REALLOC_SIZE_104, NM_UTILS_GET_NEXT_REALLOC_SIZE_104, NM_UTILS_GET_NEXT_REALLOC_SIZE_104}, {NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000, NM_UTILS_GET_NEXT_REALLOC_SIZE_1000}, }; guint i; G_STATIC_ASSERT_EXPR(NM_UTILS_GET_NEXT_REALLOC_SIZE_104 == 104u); G_STATIC_ASSERT_EXPR(NM_UTILS_GET_NEXT_REALLOC_SIZE_1000 == 1000u); for (i = 0; i < G_N_ELEMENTS(test_data) + 5000u; i++) { gsize requested0; if (i < G_N_ELEMENTS(test_data)) requested0 = test_data[i].requested; else { /* find some interesting random values for testing. */ switch (nmtst_get_rand_uint32() % 5) { case 0: requested0 = nmtst_get_rand_size(); break; case 1: /* values close to G_MAXSIZE. */ requested0 = G_MAXSIZE - (nmtst_get_rand_uint32() % 12000u); break; case 2: /* values around G_MAXSIZE/2. */ requested0 = (G_MAXSIZE / 2u) + 6000u - (nmtst_get_rand_uint32() % 12000u); break; case 3: /* values around powers of 2. */ requested0 = (((gsize) 1) << (nmtst_get_rand_uint32() % (sizeof(gsize) * 8u))) + 6000u - (nmtst_get_rand_uint32() % 12000u); break; case 4: /* values around 4k borders. */ requested0 = (nmtst_get_rand_size() & ~((gsize) 0xFFFu)) + 30u - (nmtst_get_rand_uint32() % 60u); break; default: g_assert_not_reached(); } } { const gsize requested = requested0; const gsize reserved_true = nm_utils_get_next_realloc_size(TRUE, requested); const gsize reserved_false = nm_utils_get_next_realloc_size(FALSE, requested); g_assert_cmpuint(reserved_true, >, 0); g_assert_cmpuint(reserved_false, >, 0); g_assert_cmpuint(reserved_true, >=, requested); g_assert_cmpuint(reserved_false, >=, requested); g_assert_cmpuint(reserved_false, >=, reserved_true); if (i < G_N_ELEMENTS(test_data)) { g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true); g_assert_cmpuint(reserved_false, ==, test_data[i].reserved_false); } /* reserved_false is generally the next power of two - 24. */ if (reserved_false == G_MAXSIZE) g_assert_cmpuint(requested, >, G_MAXSIZE / 2u - 24u); else { g_assert_cmpuint(reserved_false, <=, G_MAXSIZE - 24u); if (reserved_false >= 40) { const gsize _pow2 = reserved_false + 24u; /* reserved_false must always be a power of two minus 24. */ g_assert_cmpuint(_pow2, >=, 64u); g_assert_cmpuint(_pow2, >, requested); g_assert(nm_utils_is_power_of_two(_pow2)); /* but _pow2/2 must also be smaller than what we requested. */ g_assert_cmpuint(_pow2 / 2u - 24u, <, requested); } else { /* smaller values are hard-coded. */ } } /* reserved_true is generally the next 4k border - 24. */ if (reserved_true == G_MAXSIZE) g_assert_cmpuint(requested, >, G_MAXSIZE - 0x1000u - 24u); else { g_assert_cmpuint(reserved_true, <=, G_MAXSIZE - 24u); if (reserved_true > 8168u) { const gsize page_border = reserved_true + 24u; /* reserved_true must always be aligned to 4k (minus 24). */ g_assert_cmpuint(page_border % 0x1000u, ==, 0); if (requested > 0x1000u - 24u) { /* page_border not be more than 4k above requested. */ g_assert_cmpuint(page_border, >=, 0x1000u - 24u); g_assert_cmpuint(page_border - 0x1000u - 24u, <, requested); } } else { /* for smaller sizes, reserved_true and reserved_false are the same. */ g_assert_cmpuint(reserved_true, ==, reserved_false); } } } } } /*****************************************************************************/ static void test_nm_str_buf(void) { guint i_run; for (i_run = 0; TRUE; i_run++) { nm_auto_str_buf NMStrBuf strbuf = {}; nm_auto_free_gstring GString *gstr = NULL; int i, j, k; int c; nm_str_buf_init(&strbuf, nmtst_get_rand_uint32() % 200u + 1u, nmtst_get_rand_bool()); if (i_run < 1000) { c = nmtst_get_rand_word_length(NULL); for (i = 0; i < c; i++) nm_str_buf_append_c(&strbuf, '0' + (i % 10)); gstr = g_string_new(nm_str_buf_get_str(&strbuf)); j = nmtst_get_rand_uint32() % (strbuf.len + 1); k = nmtst_get_rand_uint32() % (strbuf.len - j + 2) - 1; nm_str_buf_erase(&strbuf, j, k, nmtst_get_rand_bool()); g_string_erase(gstr, j, k); g_assert_cmpstr(gstr->str, ==, nm_str_buf_get_str(&strbuf)); } else return; } } /*****************************************************************************/ static void test_nm_utils_parse_next_line(void) { const char *data; const char *data0; gsize data_len; const char *line_start; gsize line_len; int i_run; gsize j, k; data = NULL; data_len = 0; g_assert(!nm_utils_parse_next_line(&data, &data_len, &line_start, &line_len)); for (i_run = 0; i_run < 1000; i_run++) { gs_unref_ptrarray GPtrArray *strv = g_ptr_array_new_with_free_func(g_free); gs_unref_ptrarray GPtrArray *strv2 = g_ptr_array_new_with_free_func(g_free); gsize strv_len = nmtst_get_rand_word_length(NULL); nm_auto_str_buf NMStrBuf strbuf = NM_STR_BUF_INIT(0, nmtst_get_rand_bool()); /* create a list of random words. */ for (j = 0; j < strv_len; j++) { gsize w_len = nmtst_get_rand_word_length(NULL); NMStrBuf w_buf = NM_STR_BUF_INIT(nmtst_get_rand_uint32() % (w_len + 1), nmtst_get_rand_bool()); for (k = 0; k < w_len; k++) nm_str_buf_append_c(&w_buf, '0' + (k % 10)); nm_str_buf_maybe_expand(&w_buf, 1, TRUE); g_ptr_array_add(strv, nm_str_buf_finalize(&w_buf, NULL)); } /* join the list of random words with (random) line delimiters * ("\0", "\n", "\r" or EOF). */ for (j = 0; j < strv_len; j++) { nm_str_buf_append(&strbuf, strv->pdata[j]); again: switch (nmtst_get_rand_uint32() % 5) { case 0: nm_str_buf_append_c(&strbuf, '\0'); break; case 1: if (strbuf.len > 0 && (nm_str_buf_get_str_unsafe(&strbuf))[strbuf.len - 1] == '\r') { /* the previous line was empty and terminated by "\r". We * must not join with "\n". Retry. */ goto again; } nm_str_buf_append_c(&strbuf, '\n'); break; case 2: nm_str_buf_append_c(&strbuf, '\r'); break; case 3: nm_str_buf_append(&strbuf, "\r\n"); break; case 4: /* the last word randomly is delimited or not, but not if the last * word is "". */ if (j + 1 < strv_len) { /* it's not the last word. Retry. */ goto again; } g_assert(j == strv_len - 1); if (((const char *) strv->pdata[j])[0] == '\0') { /* if the last word was "", we need a delimiter (to parse it back). * Retry. */ goto again; } /* The final delimiter gets omitted. It's EOF. */ break; } } data0 = nm_str_buf_get_str_unsafe(&strbuf); if (!data0 && nmtst_get_rand_bool()) { nm_str_buf_maybe_expand(&strbuf, 1, TRUE); data0 = nm_str_buf_get_str_unsafe(&strbuf); g_assert(data0); } data_len = strbuf.len; g_assert((data_len > 0 && data0) || data_len == 0); data = data0; while (nm_utils_parse_next_line(&data, &data_len, &line_start, &line_len)) { g_assert(line_start); g_assert(line_start >= data0); g_assert(line_start < &data0[strbuf.len]); g_assert(!memchr(line_start, '\0', line_len)); g_ptr_array_add(strv2, g_strndup(line_start, line_len)); } g_assert(data_len == 0); if (data0) g_assert(data == &data0[strbuf.len]); else g_assert(!data); g_assert(_nm_utils_strv_cmp_n((const char *const *) strv->pdata, strv->len, (const char *const *) strv2->pdata, strv2->len) == 0); } } /*****************************************************************************/ static void test_in_strset_ascii_case(void) { const char *x; x = NULL; g_assert(NM_IN_STRSET_ASCII_CASE(x, NULL)); g_assert(NM_IN_STRSET_ASCII_CASE(x, NULL, "b")); g_assert(!NM_IN_STRSET_ASCII_CASE(x, "b")); x = "b"; g_assert(NM_IN_STRSET(x, "b")); g_assert(NM_IN_STRSET_ASCII_CASE(x, "b")); g_assert(!NM_IN_STRSET(x, "B")); g_assert(NM_IN_STRSET_ASCII_CASE(x, "B")); } /*****************************************************************************/ static void test_is_specific_hostname(void) { g_assert(!nm_utils_is_specific_hostname(NULL)); g_assert(!nm_utils_is_specific_hostname("")); g_assert(!nm_utils_is_specific_hostname("(none)")); g_assert(nm_utils_is_specific_hostname("(NONE)")); g_assert(!nm_utils_is_specific_hostname("localhost")); g_assert(!nm_utils_is_specific_hostname("lOcalHost")); g_assert(!nm_utils_is_specific_hostname("LOCALHOST")); g_assert(!nm_utils_is_specific_hostname("LOCALHOST.localdomain")); g_assert(nm_utils_is_specific_hostname("xlocalhost")); g_assert(nm_utils_is_specific_hostname("lOcalHxost")); g_assert(nm_utils_is_specific_hostname("LOCALxHOST")); g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOST")); g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOsT6.")); g_assert(!nm_utils_is_specific_hostname("foo.LOCALHOsT6.localdomain6")); g_assert(!nm_utils_is_specific_hostname(".LOCALHOsT6.localdomain6")); g_assert(!nm_utils_is_specific_hostname("LOCALHOsT6.localdomain6")); g_assert(!nm_utils_is_specific_hostname("LOCALHOsT6.localdomain6.")); g_assert(nm_utils_is_specific_hostname("LOCALHOsT6.localdomain.")); g_assert(nm_utils_is_specific_hostname(" ")); } /*****************************************************************************/ static void test_strv_dup_packed(void) { gs_unref_ptrarray GPtrArray *src = NULL; int i_run; src = g_ptr_array_new_with_free_func(g_free); for (i_run = 0; i_run < 500; i_run++) { const int strv_len = nmtst_get_rand_word_length(NULL); gs_free const char **strv_cpy = NULL; const char *const * strv_src; int i, j; g_ptr_array_set_size(src, 0); for (i = 0; i < strv_len; i++) { const int word_len = nmtst_get_rand_word_length(NULL); NMStrBuf sbuf = NM_STR_BUF_INIT(0, nmtst_get_rand_bool()); for (j = 0; j < word_len; j++) nm_str_buf_append_c(&sbuf, 'a' + (nmtst_get_rand_uint32() % 20)); g_ptr_array_add(src, nm_str_buf_finalize(&sbuf, NULL) ?: g_new0(char, 1)); } g_ptr_array_add(src, NULL); strv_src = (const char *const *) src->pdata; g_assert(strv_src); g_assert(NM_PTRARRAY_LEN(strv_src) == strv_len); strv_cpy = nm_utils_strv_dup_packed(strv_src, nmtst_get_rand_bool() ? (gssize) strv_len : (gssize) -1); if (strv_len == 0) g_assert(!strv_cpy); else g_assert(strv_cpy); g_assert(NM_PTRARRAY_LEN(strv_cpy) == strv_len); if (strv_cpy) g_assert(_nm_utils_strv_equal((char **) strv_cpy, (char **) strv_src)); } } /*****************************************************************************/ static int _hash_func_cmp_direct(gconstpointer a, gconstpointer b, gpointer user_data) { NM_CMP_DIRECT(GPOINTER_TO_INT(a), GPOINTER_TO_INT(b)); return 0; } static void test_utils_hashtable_cmp(void) { static struct { int val_i; const char *val_s; } vals[] = { { 0, "0", }, { 1, "1", }, { 2, "2", }, { 3, "3", }, { 4, "4", }, { 5, "5", }, { 6, "6", }, { 7, "7", }, { 8, "8", }, { 9, "9", }, { 0, "a", }, { 1, "a", }, { 2, "a", }, { 3, "a", }, { 4, "a", }, { 5, "a", }, { 0, "0", }, { 0, "1", }, { 0, "2", }, { 0, "3", }, { 0, "4", }, { 0, "5", }, }; guint test_run; int is_num_key; for (test_run = 0; test_run < 30; test_run++) { for (is_num_key = 0; is_num_key < 2; is_num_key++) { GHashFunc func_key_hash = is_num_key ? nm_direct_hash : nm_str_hash; GEqualFunc func_key_equal = is_num_key ? g_direct_equal : g_str_equal; GCompareDataFunc func_key_cmp = is_num_key ? _hash_func_cmp_direct : (GCompareDataFunc) nm_strcmp_with_data; GCompareDataFunc func_val_cmp = !is_num_key ? _hash_func_cmp_direct : (GCompareDataFunc) nm_strcmp_with_data; gs_unref_hashtable GHashTable *h1 = NULL; gs_unref_hashtable GHashTable *h2 = NULL; gboolean has_same_keys; guint i, n; h1 = g_hash_table_new(func_key_hash, func_key_equal); h2 = g_hash_table_new(func_key_hash, func_key_equal); n = nmtst_get_rand_word_length(NULL); for (i = 0; i < n; i++) { typeof(vals[0]) *v = &vals[nmtst_get_rand_uint32() % G_N_ELEMENTS(vals)]; gconstpointer v_key = is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; gconstpointer v_val = !is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; g_hash_table_insert(h1, (gpointer) v_key, (gpointer) v_val); g_hash_table_insert(h2, (gpointer) v_key, (gpointer) v_val); } g_assert(nm_utils_hashtable_same_keys(h1, h2)); g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL)); g_assert(nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) == 0); n = nmtst_get_rand_word_length(NULL) + 1; has_same_keys = TRUE; for (i = 0; i < n; i++) { again: { typeof(vals[0]) *v = &vals[nmtst_get_rand_uint32() % G_N_ELEMENTS(vals)]; gconstpointer v_key = is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; gconstpointer v_val = !is_num_key ? GINT_TO_POINTER(v->val_i) : v->val_s; gpointer v_key2; gpointer v_val2; if (g_hash_table_lookup_extended(h1, v_key, &v_key2, &v_val2)) { g_assert(func_key_cmp(v_key, v_key2, NULL) == 0); if (func_val_cmp(v_val, v_val2, NULL) == 0) goto again; } else has_same_keys = FALSE; g_hash_table_insert(h2, (gpointer) v_key, (gpointer) v_val); } } if (has_same_keys) { g_assert(nm_utils_hashtable_same_keys(h1, h2)); g_assert(nm_utils_hashtable_equal(h1, h2, NULL, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) == 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) == 0); } else { g_assert(!nm_utils_hashtable_same_keys(h1, h2)); g_assert(!nm_utils_hashtable_equal(h1, h2, NULL, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, NULL, NULL) != 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, NULL, NULL) != 0); } g_assert(!nm_utils_hashtable_equal(h1, h2, func_val_cmp, NULL)); g_assert(nm_utils_hashtable_cmp(h1, h2, FALSE, func_key_cmp, func_val_cmp, NULL) != 0); g_assert(nm_utils_hashtable_cmp(h1, h2, TRUE, func_key_cmp, func_val_cmp, NULL) != 0); } } } /*****************************************************************************/ NMTST_DEFINE(); int main(int argc, char **argv) { nmtst_init(&argc, &argv, TRUE); g_test_add_func("/general/test_gpid", test_gpid); g_test_add_func("/general/test_monotonic_timestamp", test_monotonic_timestamp); g_test_add_func("/general/test_nmhash", test_nmhash); g_test_add_func("/general/test_nm_make_strv", test_make_strv); g_test_add_func("/general/test_nm_strdup_int", test_nm_strdup_int); g_test_add_func("/general/test_nm_strndup_a", test_nm_strndup_a); g_test_add_func("/general/test_nm_ip4_addr_is_localhost", test_nm_ip4_addr_is_localhost); g_test_add_func("/general/test_unaligned", test_unaligned); g_test_add_func("/general/test_strv_cmp", test_strv_cmp); g_test_add_func("/general/test_strstrip_avoid_copy", test_strstrip_avoid_copy); g_test_add_func("/general/test_nm_utils_bin2hexstr", test_nm_utils_bin2hexstr); g_test_add_func("/general/test_nm_ref_string", test_nm_ref_string); g_test_add_func("/general/test_string_table_lookup", test_string_table_lookup); g_test_add_func("/general/test_nm_utils_get_next_realloc_size", test_nm_utils_get_next_realloc_size); g_test_add_func("/general/test_nm_str_buf", test_nm_str_buf); g_test_add_func("/general/test_nm_utils_parse_next_line", test_nm_utils_parse_next_line); g_test_add_func("/general/test_in_strset_ascii_case", test_in_strset_ascii_case); g_test_add_func("/general/test_is_specific_hostname", test_is_specific_hostname); g_test_add_func("/general/test_strv_dup_packed", test_strv_dup_packed); g_test_add_func("/general/test_utils_hashtable_cmp", test_utils_hashtable_cmp); return g_test_run(); }