NetworkManager/shared/nm-glib-aux/tests/test-shared-general.c
Thomas Haller 478d5bdafe
shared: fix unit tests for nm_utils_get_next_realloc_size()
The change broke unit tests on 32 bit systems.

Change the code again to make it more similar to what it was
before. Now only on 64 bit systems there is any difference compared
to before. That makes it easier about reasoning for how the unit test
should be (in most cases, it is unchanged).

Fixes: 040c86f15c ('shared: avoid compiler warning for nm_utils_get_next_realloc_size() returning huge sizes')
2021-01-12 09:56:43 +01:00

1291 lines
45 KiB
C

/* SPDX-License-Identifier: LGPL-2.1-or-later */
/*
* 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;
gsize reserved_true;
gsize reserved_false;
bool truncated_true = FALSE;
bool truncated_false = FALSE;
if (sizeof(gsize) > 4 && requested > SIZE_MAX / 2u - 24u) {
reserved_false = G_MAXSSIZE;
truncated_false = TRUE;
} else
reserved_false = nm_utils_get_next_realloc_size(FALSE, requested);
if (sizeof(gsize) > 4 && requested > SIZE_MAX - 0x1000u - 24u) {
reserved_true = G_MAXSSIZE;
truncated_true = TRUE;
} else
reserved_true = nm_utils_get_next_realloc_size(TRUE, requested);
g_assert_cmpuint(reserved_true, >, 0);
g_assert_cmpuint(reserved_false, >, 0);
if (!truncated_true)
g_assert_cmpuint(reserved_true, >=, requested);
if (!truncated_false)
g_assert_cmpuint(reserved_false, >=, requested);
if (!truncated_true && !truncated_false)
g_assert_cmpuint(reserved_false, >=, reserved_true);
if (i < G_N_ELEMENTS(test_data)) {
if (!truncated_true)
g_assert_cmpuint(reserved_true, ==, test_data[i].reserved_true);
if (!truncated_false)
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 if (!reserved_false) {
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 if (!truncated_true) {
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(strv_cpy, 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_cmp_equal(h1, h2, NULL, NULL));
g_assert(nm_utils_hashtable_cmp_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_cmp_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_cmp_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_cmp_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();
}