summaryrefslogtreecommitdiffstats
path: root/lib/common/tests/strings
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 06:53:20 +0000
commite5a812082ae033afb1eed82c0f2df3d0f6bdc93f (patch)
treea6716c9275b4b413f6c9194798b34b91affb3cc7 /lib/common/tests/strings
parentInitial commit. (diff)
downloadpacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.tar.xz
pacemaker-e5a812082ae033afb1eed82c0f2df3d0f6bdc93f.zip
Adding upstream version 2.1.6.upstream/2.1.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/common/tests/strings')
-rw-r--r--lib/common/tests/strings/Makefile.am41
-rw-r--r--lib/common/tests/strings/crm_get_msec_test.c50
-rw-r--r--lib/common/tests/strings/crm_is_true_test.c57
-rw-r--r--lib/common/tests/strings/crm_str_to_boolean_test.c92
-rw-r--r--lib/common/tests/strings/pcmk__add_word_test.c93
-rw-r--r--lib/common/tests/strings/pcmk__btoa_test.c22
-rw-r--r--lib/common/tests/strings/pcmk__char_in_any_str_test.c46
-rw-r--r--lib/common/tests/strings/pcmk__compress_test.c58
-rw-r--r--lib/common/tests/strings/pcmk__ends_with_test.c57
-rw-r--r--lib/common/tests/strings/pcmk__g_strcat_test.c73
-rw-r--r--lib/common/tests/strings/pcmk__guint_from_hash_test.c76
-rw-r--r--lib/common/tests/strings/pcmk__numeric_strcasecmp_test.c79
-rw-r--r--lib/common/tests/strings/pcmk__parse_ll_range_test.c117
-rw-r--r--lib/common/tests/strings/pcmk__s_test.c29
-rw-r--r--lib/common/tests/strings/pcmk__scan_double_test.c158
-rw-r--r--lib/common/tests/strings/pcmk__scan_min_int_test.c60
-rw-r--r--lib/common/tests/strings/pcmk__scan_port_test.c59
-rw-r--r--lib/common/tests/strings/pcmk__starts_with_test.c35
-rw-r--r--lib/common/tests/strings/pcmk__str_any_of_test.c48
-rw-r--r--lib/common/tests/strings/pcmk__str_in_list_test.c107
-rw-r--r--lib/common/tests/strings/pcmk__str_table_dup_test.c59
-rw-r--r--lib/common/tests/strings/pcmk__str_update_test.c78
-rw-r--r--lib/common/tests/strings/pcmk__strcmp_test.c80
-rw-r--r--lib/common/tests/strings/pcmk__strikey_table_test.c40
-rw-r--r--lib/common/tests/strings/pcmk__strkey_table_test.c40
-rw-r--r--lib/common/tests/strings/pcmk__trim_test.c72
26 files changed, 1726 insertions, 0 deletions
diff --git a/lib/common/tests/strings/Makefile.am b/lib/common/tests/strings/Makefile.am
new file mode 100644
index 0000000..9abb8e9
--- /dev/null
+++ b/lib/common/tests/strings/Makefile.am
@@ -0,0 +1,41 @@
+#
+# Copyright 2020-2022 the Pacemaker project contributors
+#
+# The version control history for this file may have further details.
+#
+# This source code is licensed under the GNU General Public License version 2
+# or later (GPLv2+) WITHOUT ANY WARRANTY.
+#
+
+include $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = \
+ crm_get_msec_test \
+ crm_is_true_test \
+ crm_str_to_boolean_test \
+ pcmk__add_word_test \
+ pcmk__btoa_test \
+ pcmk__char_in_any_str_test \
+ pcmk__compress_test \
+ pcmk__ends_with_test \
+ pcmk__g_strcat_test \
+ pcmk__guint_from_hash_test \
+ pcmk__numeric_strcasecmp_test \
+ pcmk__parse_ll_range_test \
+ pcmk__s_test \
+ pcmk__scan_double_test \
+ pcmk__scan_min_int_test \
+ pcmk__scan_port_test \
+ pcmk__starts_with_test \
+ pcmk__str_any_of_test \
+ pcmk__str_in_list_test \
+ pcmk__str_table_dup_test \
+ pcmk__str_update_test \
+ pcmk__strcmp_test \
+ pcmk__strkey_table_test \
+ pcmk__strikey_table_test \
+ pcmk__trim_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/strings/crm_get_msec_test.c b/lib/common/tests/strings/crm_get_msec_test.c
new file mode 100644
index 0000000..5da548b
--- /dev/null
+++ b/lib/common/tests/strings/crm_get_msec_test.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+bad_input(void **state) {
+ assert_int_equal(crm_get_msec(NULL), PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(crm_get_msec(" "), PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(crm_get_msec("abcxyz"), PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(crm_get_msec("100xs"), PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(crm_get_msec(" 100 xs "), PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(crm_get_msec("-100ms"), PCMK__PARSE_INT_DEFAULT);
+}
+
+static void
+good_input(void **state) {
+ assert_int_equal(crm_get_msec("100"), 100000);
+ assert_int_equal(crm_get_msec(" 100 "), 100000);
+ assert_int_equal(crm_get_msec("\t100\n"), 100000);
+
+ assert_int_equal(crm_get_msec("100ms"), 100);
+ assert_int_equal(crm_get_msec("100 MSEC"), 100);
+ assert_int_equal(crm_get_msec("1000US"), 1);
+ assert_int_equal(crm_get_msec("1000usec"), 1);
+ assert_int_equal(crm_get_msec("12s"), 12000);
+ assert_int_equal(crm_get_msec("12 sec"), 12000);
+ assert_int_equal(crm_get_msec("1m"), 60000);
+ assert_int_equal(crm_get_msec("13 min"), 780000);
+ assert_int_equal(crm_get_msec("2\th"), 7200000);
+ assert_int_equal(crm_get_msec("1 hr"), 3600000);
+}
+
+static void
+overflow(void **state) {
+ assert_int_equal(crm_get_msec("9223372036854775807s"), LLONG_MAX);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_input),
+ cmocka_unit_test(good_input),
+ cmocka_unit_test(overflow))
diff --git a/lib/common/tests/strings/crm_is_true_test.c b/lib/common/tests/strings/crm_is_true_test.c
new file mode 100644
index 0000000..2a9e31c
--- /dev/null
+++ b/lib/common/tests/strings/crm_is_true_test.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+bad_input(void **state) {
+ assert_false(crm_is_true(NULL));
+}
+
+static void
+is_true(void **state) {
+ assert_true(crm_is_true("true"));
+ assert_true(crm_is_true("TrUe"));
+ assert_true(crm_is_true("on"));
+ assert_true(crm_is_true("ON"));
+ assert_true(crm_is_true("yes"));
+ assert_true(crm_is_true("yES"));
+ assert_true(crm_is_true("y"));
+ assert_true(crm_is_true("Y"));
+ assert_true(crm_is_true("1"));
+}
+
+static void
+is_false(void **state) {
+ assert_false(crm_is_true("false"));
+ assert_false(crm_is_true("fAlSe"));
+ assert_false(crm_is_true("off"));
+ assert_false(crm_is_true("OFF"));
+ assert_false(crm_is_true("no"));
+ assert_false(crm_is_true("No"));
+ assert_false(crm_is_true("n"));
+ assert_false(crm_is_true("N"));
+ assert_false(crm_is_true("0"));
+
+ assert_false(crm_is_true(""));
+ assert_false(crm_is_true("blahblah"));
+
+ assert_false(crm_is_true("truedat"));
+ assert_false(crm_is_true("onnn"));
+ assert_false(crm_is_true("yep"));
+ assert_false(crm_is_true("Y!"));
+ assert_false(crm_is_true("100"));
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_input),
+ cmocka_unit_test(is_true),
+ cmocka_unit_test(is_false))
diff --git a/lib/common/tests/strings/crm_str_to_boolean_test.c b/lib/common/tests/strings/crm_str_to_boolean_test.c
new file mode 100644
index 0000000..3bd2e5d
--- /dev/null
+++ b/lib/common/tests/strings/crm_str_to_boolean_test.c
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+bad_input(void **state) {
+ assert_int_equal(crm_str_to_boolean(NULL, NULL), -1);
+ assert_int_equal(crm_str_to_boolean("", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("blahblah", NULL), -1);
+}
+
+static void
+is_true(void **state) {
+ int ret;
+
+ assert_int_equal(crm_str_to_boolean("true", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("TrUe", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("on", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("ON", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("yes", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("yES", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("y", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("Y", &ret), 1);
+ assert_true(ret);
+ assert_int_equal(crm_str_to_boolean("1", &ret), 1);
+ assert_true(ret);
+}
+
+static void
+is_not_true(void **state) {
+ assert_int_equal(crm_str_to_boolean("truedat", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("onnn", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("yep", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("Y!", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("100", NULL), -1);
+}
+
+static void
+is_false(void **state) {
+ int ret;
+
+ assert_int_equal(crm_str_to_boolean("false", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("fAlSe", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("off", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("OFF", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("no", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("No", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("n", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("N", &ret), 1);
+ assert_false(ret);
+ assert_int_equal(crm_str_to_boolean("0", &ret), 1);
+ assert_false(ret);
+}
+
+static void
+is_not_false(void **state) {
+ assert_int_equal(crm_str_to_boolean("falseee", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("of", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("nope", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("N!", NULL), -1);
+ assert_int_equal(crm_str_to_boolean("000", NULL), -1);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_input),
+ cmocka_unit_test(is_true),
+ cmocka_unit_test(is_not_true),
+ cmocka_unit_test(is_false),
+ cmocka_unit_test(is_not_false))
diff --git a/lib/common/tests/strings/pcmk__add_word_test.c b/lib/common/tests/strings/pcmk__add_word_test.c
new file mode 100644
index 0000000..16a749e
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__add_word_test.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2020-2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+add_words(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_word(&list, 16, "hello");
+ pcmk__add_word(&list, 16, "world");
+ assert_int_equal(strcmp((const char *) list->str, "hello world"), 0);
+ g_string_free(list, TRUE);
+}
+
+static void
+add_with_no_len(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_word(&list, 0, "hello");
+ pcmk__add_word(&list, 0, "world");
+ assert_int_equal(strcmp((const char *) list->str, "hello world"), 0);
+ g_string_free(list, TRUE);
+}
+
+static void
+add_nothing(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_word(&list, 0, "hello");
+ pcmk__add_word(&list, 0, NULL);
+ pcmk__add_word(&list, 0, "");
+ assert_int_equal(strcmp((const char *) list->str, "hello"), 0);
+ g_string_free(list, TRUE);
+}
+
+static void
+add_with_null(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_separated_word(&list, 32, "hello", NULL);
+ pcmk__add_separated_word(&list, 32, "world", NULL);
+ pcmk__add_separated_word(&list, 32, "I am a unit test", NULL);
+ assert_int_equal(strcmp((const char *) list->str,
+ "hello world I am a unit test"), 0);
+ g_string_free(list, TRUE);
+}
+
+static void
+add_with_comma(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_separated_word(&list, 32, "hello", ",");
+ pcmk__add_separated_word(&list, 32, "world", ",");
+ pcmk__add_separated_word(&list, 32, "I am a unit test", ",");
+ assert_int_equal(strcmp((const char *) list->str,
+ "hello,world,I am a unit test"), 0);
+ g_string_free(list, TRUE);
+}
+
+static void
+add_with_comma_and_space(void **state)
+{
+ GString *list = NULL;
+
+ pcmk__add_separated_word(&list, 32, "hello", ", ");
+ pcmk__add_separated_word(&list, 32, "world", ", ");
+ pcmk__add_separated_word(&list, 32, "I am a unit test", ", ");
+ assert_int_equal(strcmp((const char *) list->str,
+ "hello, world, I am a unit test"), 0);
+ g_string_free(list, TRUE);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(add_words),
+ cmocka_unit_test(add_with_no_len),
+ cmocka_unit_test(add_nothing),
+ cmocka_unit_test(add_with_null),
+ cmocka_unit_test(add_with_comma),
+ cmocka_unit_test(add_with_comma_and_space))
diff --git a/lib/common/tests/strings/pcmk__btoa_test.c b/lib/common/tests/strings/pcmk__btoa_test.c
new file mode 100644
index 0000000..f7dee9e
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__btoa_test.c
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2020-2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+btoa(void **state) {
+ assert_string_equal(pcmk__btoa(false), "false");
+ assert_string_equal(pcmk__btoa(true), "true");
+ assert_string_equal(pcmk__btoa(1 == 0), "false");
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(btoa))
diff --git a/lib/common/tests/strings/pcmk__char_in_any_str_test.c b/lib/common/tests/strings/pcmk__char_in_any_str_test.c
new file mode 100644
index 0000000..e70dfb4
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__char_in_any_str_test.c
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2020-2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_list(void **state)
+{
+ assert_false(pcmk__char_in_any_str('x', NULL));
+ assert_false(pcmk__char_in_any_str('\0', NULL));
+}
+
+static void
+null_char(void **state)
+{
+ assert_true(pcmk__char_in_any_str('\0', "xxx", "yyy", NULL));
+ assert_true(pcmk__char_in_any_str('\0', "", NULL));
+}
+
+static void
+in_list(void **state)
+{
+ assert_true(pcmk__char_in_any_str('x', "aaa", "bbb", "xxx", NULL));
+}
+
+static void
+not_in_list(void **state)
+{
+ assert_false(pcmk__char_in_any_str('x', "aaa", "bbb", NULL));
+ assert_false(pcmk__char_in_any_str('A', "aaa", "bbb", NULL));
+ assert_false(pcmk__char_in_any_str('x', "", NULL));
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_list),
+ cmocka_unit_test(null_char),
+ cmocka_unit_test(in_list),
+ cmocka_unit_test(not_in_list))
diff --git a/lib/common/tests/strings/pcmk__compress_test.c b/lib/common/tests/strings/pcmk__compress_test.c
new file mode 100644
index 0000000..7480937
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__compress_test.c
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include "mock_private.h"
+
+#define SIMPLE_DATA "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"
+
+const char *SIMPLE_COMPRESSED = "BZh41AY&SYO\x1ai";
+
+static void
+simple_compress(void **state)
+{
+ char *result = calloc(1024, sizeof(char));
+ unsigned int len;
+
+ assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok);
+ assert_memory_equal(result, SIMPLE_COMPRESSED, 13);
+}
+
+static void
+max_too_small(void **state)
+{
+ char *result = calloc(1024, sizeof(char));
+ unsigned int len;
+
+ assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), pcmk_rc_error);
+}
+
+static void
+calloc_fails(void **state) {
+ char *result = calloc(1024, sizeof(char));
+ unsigned int len;
+
+ pcmk__assert_asserts(
+ {
+ pcmk__mock_calloc = true; // calloc() will return NULL
+ expect_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601));
+ expect_value(__wrap_calloc, size, sizeof(char));
+ pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len);
+ pcmk__mock_calloc = false; // Use the real calloc()
+ }
+ );
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(simple_compress),
+ cmocka_unit_test(max_too_small),
+ cmocka_unit_test(calloc_fails))
diff --git a/lib/common/tests/strings/pcmk__ends_with_test.c b/lib/common/tests/strings/pcmk__ends_with_test.c
new file mode 100644
index 0000000..7503571
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__ends_with_test.c
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+bad_input(void **state) {
+ assert_false(pcmk__ends_with(NULL, "xyz"));
+
+ assert_true(pcmk__ends_with(NULL, NULL));
+ assert_true(pcmk__ends_with(NULL, ""));
+ assert_true(pcmk__ends_with("", NULL));
+ assert_true(pcmk__ends_with("", ""));
+ assert_true(pcmk__ends_with("abc", NULL));
+ assert_true(pcmk__ends_with("abc", ""));
+}
+
+static void
+ends_with(void **state) {
+ assert_true(pcmk__ends_with("abc", "abc"));
+ assert_true(pcmk__ends_with("abc", "bc"));
+ assert_true(pcmk__ends_with("abc", "c"));
+ assert_true(pcmk__ends_with("abcbc", "bc"));
+
+ assert_false(pcmk__ends_with("abc", "def"));
+ assert_false(pcmk__ends_with("abc", "defg"));
+ assert_false(pcmk__ends_with("abc", "bcd"));
+ assert_false(pcmk__ends_with("abc", "ab"));
+
+ assert_false(pcmk__ends_with("abc", "BC"));
+}
+
+static void
+ends_with_ext(void **state) {
+ assert_true(pcmk__ends_with_ext("ab.c", ".c"));
+ assert_true(pcmk__ends_with_ext("ab.cb.c", ".c"));
+
+ assert_false(pcmk__ends_with_ext("ab.c", ".def"));
+ assert_false(pcmk__ends_with_ext("ab.c", ".defg"));
+ assert_false(pcmk__ends_with_ext("ab.c", ".cd"));
+ assert_false(pcmk__ends_with_ext("ab.c", "ab"));
+
+ assert_false(pcmk__ends_with_ext("ab.c", ".C"));
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_input),
+ cmocka_unit_test(ends_with),
+ cmocka_unit_test(ends_with_ext))
diff --git a/lib/common/tests/strings/pcmk__g_strcat_test.c b/lib/common/tests/strings/pcmk__g_strcat_test.c
new file mode 100644
index 0000000..2116f0e
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__g_strcat_test.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2020-2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+add_to_null(void **state)
+{
+ pcmk__assert_asserts(pcmk__g_strcat(NULL, NULL));
+ pcmk__assert_asserts(pcmk__g_strcat(NULL, "hello", NULL));
+}
+
+static void
+add_nothing(void **state)
+{
+ GString *buf = g_string_new(NULL);
+
+ // Start with empty string
+ pcmk__g_strcat(buf, NULL);
+ assert_string_equal((const char *) buf->str, "");
+
+ pcmk__g_strcat(buf, "", NULL);
+ assert_string_equal((const char *) buf->str, "");
+
+ // Start with populated string
+ g_string_append(buf, "hello");
+ pcmk__g_strcat(buf, NULL);
+ assert_string_equal((const char *) buf->str, "hello");
+
+ pcmk__g_strcat(buf, "", NULL);
+ assert_string_equal((const char *) buf->str, "hello");
+ g_string_free(buf, TRUE);
+}
+
+static void
+add_words(void **state)
+{
+ GString *buf = g_string_new(NULL);
+
+ // Verify a call with multiple words
+ pcmk__g_strcat(buf, "hello", " ", NULL);
+ assert_string_equal((const char *) buf->str, "hello ");
+
+ // Verify that a second call doesn't overwrite the first one
+ pcmk__g_strcat(buf, "world", NULL);
+ assert_string_equal((const char *) buf->str, "hello world");
+ g_string_free(buf, TRUE);
+}
+
+static void
+stop_early(void **state)
+{
+ GString *buf = g_string_new(NULL);
+
+ // NULL anywhere after buf in the arg list should cause a return
+ pcmk__g_strcat(buf, "hello", NULL, " world", NULL);
+ assert_string_equal((const char *) buf->str, "hello");
+ g_string_free(buf, TRUE);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(add_to_null),
+ cmocka_unit_test(add_nothing),
+ cmocka_unit_test(add_words),
+ cmocka_unit_test(stop_early))
diff --git a/lib/common/tests/strings/pcmk__guint_from_hash_test.c b/lib/common/tests/strings/pcmk__guint_from_hash_test.c
new file mode 100644
index 0000000..e2b4762
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__guint_from_hash_test.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+null_args(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ guint result;
+
+ assert_int_equal(pcmk__guint_from_hash(NULL, "abc", 123, &result), EINVAL);
+ assert_int_equal(pcmk__guint_from_hash(tbl, NULL, 123, &result), EINVAL);
+
+ g_hash_table_destroy(tbl);
+}
+
+static void
+missing_key(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ guint result;
+
+ assert_int_equal(pcmk__guint_from_hash(tbl, "abc", 123, &result), pcmk_rc_ok);
+ assert_int_equal(result, 123);
+
+ g_hash_table_destroy(tbl);
+}
+
+static void
+standard_usage(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ guint result;
+
+ g_hash_table_insert(tbl, strdup("abc"), strdup("123"));
+
+ assert_int_equal(pcmk__guint_from_hash(tbl, "abc", 456, &result), pcmk_rc_ok);
+ assert_int_equal(result, 123);
+
+ g_hash_table_destroy(tbl);
+}
+
+static void
+conversion_errors(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ guint result;
+
+ g_hash_table_insert(tbl, strdup("negative"), strdup("-3"));
+ g_hash_table_insert(tbl, strdup("toobig"), strdup("20000000000000000"));
+
+ assert_int_equal(pcmk__guint_from_hash(tbl, "negative", 456, &result), ERANGE);
+ assert_int_equal(result, 456);
+
+ assert_int_equal(pcmk__guint_from_hash(tbl, "toobig", 456, &result), ERANGE);
+ assert_int_equal(result, 456);
+
+ g_hash_table_destroy(tbl);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_args),
+ cmocka_unit_test(missing_key),
+ cmocka_unit_test(standard_usage),
+ cmocka_unit_test(conversion_errors))
diff --git a/lib/common/tests/strings/pcmk__numeric_strcasecmp_test.c b/lib/common/tests/strings/pcmk__numeric_strcasecmp_test.c
new file mode 100644
index 0000000..df7b11c
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__numeric_strcasecmp_test.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+null_ptr(void **state)
+{
+ pcmk__assert_asserts(pcmk__numeric_strcasecmp(NULL, NULL));
+ pcmk__assert_asserts(pcmk__numeric_strcasecmp("a", NULL));
+ pcmk__assert_asserts(pcmk__numeric_strcasecmp(NULL, "a"));
+}
+
+static void
+no_numbers(void **state)
+{
+ /* All comparisons are done case-insensitively. */
+ assert_int_equal(pcmk__numeric_strcasecmp("abcd", "efgh"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("abcd", "abcd"), 0);
+ assert_int_equal(pcmk__numeric_strcasecmp("efgh", "abcd"), 1);
+
+ assert_int_equal(pcmk__numeric_strcasecmp("AbCd", "eFgH"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("ABCD", "abcd"), 0);
+ assert_int_equal(pcmk__numeric_strcasecmp("EFgh", "ABcd"), 1);
+}
+
+static void
+trailing_numbers(void **state)
+{
+ assert_int_equal(pcmk__numeric_strcasecmp("node1", "node2"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node1", "node1"), 0);
+ assert_int_equal(pcmk__numeric_strcasecmp("node2", "node1"), 1);
+
+ assert_int_equal(pcmk__numeric_strcasecmp("node1", "node10"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node10", "node10"), 0);
+ assert_int_equal(pcmk__numeric_strcasecmp("node10", "node1"), 1);
+
+ assert_int_equal(pcmk__numeric_strcasecmp("node10", "remotenode9"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("remotenode9", "node10"), 1);
+
+ /* Longer numbers sort higher than shorter numbers. */
+ assert_int_equal(pcmk__numeric_strcasecmp("node001", "node1"), 1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node1", "node001"), -1);
+}
+
+static void
+middle_numbers(void **state)
+{
+ assert_int_equal(pcmk__numeric_strcasecmp("node1abc", "node1def"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node1def", "node1abc"), 1);
+
+ assert_int_equal(pcmk__numeric_strcasecmp("node1abc", "node2abc"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node2abc", "node1abc"), 1);
+}
+
+static void
+unequal_lengths(void **state)
+{
+ assert_int_equal(pcmk__numeric_strcasecmp("node-ab", "node-abc"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node-abc", "node-ab"), 1);
+
+ assert_int_equal(pcmk__numeric_strcasecmp("node1ab", "node1abc"), -1);
+ assert_int_equal(pcmk__numeric_strcasecmp("node1abc", "node1ab"), 1);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_ptr),
+ cmocka_unit_test(no_numbers),
+ cmocka_unit_test(trailing_numbers),
+ cmocka_unit_test(middle_numbers),
+ cmocka_unit_test(unequal_lengths))
diff --git a/lib/common/tests/strings/pcmk__parse_ll_range_test.c b/lib/common/tests/strings/pcmk__parse_ll_range_test.c
new file mode 100644
index 0000000..7656ad7
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__parse_ll_range_test.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright 2020-2023 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include "crm/common/results.h"
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_input_string(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range(NULL, &start, &end), ENODATA);
+ assert_int_equal(pcmk__parse_ll_range("", &start, &end), ENODATA);
+}
+
+static void
+null_input_variables(void **state)
+{
+ long long start, end;
+
+ pcmk__assert_asserts(pcmk__parse_ll_range("1234", NULL, &end));
+ pcmk__assert_asserts(pcmk__parse_ll_range("1234", &start, NULL));
+}
+
+static void
+missing_separator(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("1234", &start, &end), pcmk_rc_ok);
+ assert_int_equal(start, 1234);
+ assert_int_equal(end, 1234);
+}
+
+static void
+only_separator(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("-", &start, &end), pcmk_rc_bad_input);
+ assert_int_equal(start, PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(end, PCMK__PARSE_INT_DEFAULT);
+}
+
+static void
+no_range_end(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("2000-", &start, &end), pcmk_rc_ok);
+ assert_int_equal(start, 2000);
+ assert_int_equal(end, PCMK__PARSE_INT_DEFAULT);
+}
+
+static void
+no_range_start(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("-2020", &start, &end), pcmk_rc_ok);
+ assert_int_equal(start, PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(end, 2020);
+}
+
+static void
+range_start_and_end(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("2000-2020", &start, &end), pcmk_rc_ok);
+ assert_int_equal(start, 2000);
+ assert_int_equal(end, 2020);
+
+ assert_int_equal(pcmk__parse_ll_range("2000-2020-2030", &start, &end), pcmk_rc_bad_input);
+}
+
+static void
+garbage(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("2000x-", &start, &end), pcmk_rc_bad_input);
+ assert_int_equal(start, PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(end, PCMK__PARSE_INT_DEFAULT);
+
+ assert_int_equal(pcmk__parse_ll_range("-x2000", &start, &end), pcmk_rc_bad_input);
+ assert_int_equal(start, PCMK__PARSE_INT_DEFAULT);
+ assert_int_equal(end, PCMK__PARSE_INT_DEFAULT);
+}
+
+static void
+strtoll_errors(void **state)
+{
+ long long start, end;
+
+ assert_int_equal(pcmk__parse_ll_range("20000000000000000000-", &start, &end), EOVERFLOW);
+ assert_int_equal(pcmk__parse_ll_range("100-20000000000000000000", &start, &end), EOVERFLOW);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_string),
+ cmocka_unit_test(null_input_variables),
+ cmocka_unit_test(missing_separator),
+ cmocka_unit_test(only_separator),
+ cmocka_unit_test(no_range_end),
+ cmocka_unit_test(no_range_start),
+ cmocka_unit_test(range_start_and_end),
+ cmocka_unit_test(strtoll_errors),
+ cmocka_unit_test(garbage))
diff --git a/lib/common/tests/strings/pcmk__s_test.c b/lib/common/tests/strings/pcmk__s_test.c
new file mode 100644
index 0000000..cdc2551
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__s_test.c
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+s_is_null(void **state) {
+ assert_null(pcmk__s(NULL, NULL));
+ assert_string_equal(pcmk__s(NULL, ""), "");
+ assert_string_equal(pcmk__s(NULL, "something"), "something");
+}
+
+static void
+s_is_not_null(void **state) {
+ assert_string_equal(pcmk__s("something", NULL), "something");
+ assert_string_equal(pcmk__s("something", "default"), "something");
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(s_is_null),
+ cmocka_unit_test(s_is_not_null))
diff --git a/lib/common/tests/strings/pcmk__scan_double_test.c b/lib/common/tests/strings/pcmk__scan_double_test.c
new file mode 100644
index 0000000..a1a180a
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__scan_double_test.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright 2004-2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <float.h> // DBL_MAX, etc.
+#include <math.h> // fabs()
+
+// Ensure plenty of characters for %f display
+#define LOCAL_BUF_SIZE 2 * DBL_MAX_10_EXP
+
+/*
+ * assert_float_equal doesn't exist for older versions of cmocka installed on some
+ * of our builders, so define it in terms of regular assert() here in that case.
+ */
+#if HAVE_DECL_ASSERT_FLOAT_EQUAL == 0
+#define assert_float_equal(a, b, epsilon) assert_true(fabs((a) - (b)) < (epsilon))
+#endif
+
+static void
+empty_input_string(void **state)
+{
+ double result;
+
+ // Without default_text
+ assert_int_equal(pcmk__scan_double(NULL, &result, NULL, NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("", &result, NULL, NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+
+ // With default_text
+ assert_int_equal(pcmk__scan_double(NULL, &result, "2.0", NULL), pcmk_rc_ok);
+ assert_float_equal(result, 2.0, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("", &result, "2.0", NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+}
+
+static void
+bad_input_string(void **state)
+{
+ double result;
+
+ // Without default text
+ assert_int_equal(pcmk__scan_double("asdf", &result, NULL, NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("as2.0", &result, NULL, NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+
+ // With default text (not used)
+ assert_int_equal(pcmk__scan_double("asdf", &result, "2.0", NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("as2.0", &result, "2.0", NULL), EINVAL);
+ assert_float_equal(result, PCMK__PARSE_DBL_DEFAULT, DBL_EPSILON);
+}
+
+static void
+trailing_chars(void **state)
+{
+ double result;
+ char *end_text;
+
+ assert_int_equal(pcmk__scan_double("2.0asdf", &result, NULL, &end_text), pcmk_rc_ok);
+ assert_float_equal(result, 2.0, DBL_EPSILON);
+ assert_string_equal(end_text, "asdf");
+}
+
+static void
+no_result_variable(void **state)
+{
+ pcmk__assert_asserts(pcmk__scan_double("asdf", NULL, NULL, NULL));
+}
+
+static void
+typical_case(void **state)
+{
+ char str[LOCAL_BUF_SIZE];
+ double result;
+
+ assert_int_equal(pcmk__scan_double("0.0", &result, NULL, NULL), pcmk_rc_ok);
+ assert_float_equal(result, 0.0, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("1.0", &result, NULL, NULL), pcmk_rc_ok);
+ assert_float_equal(result, 1.0, DBL_EPSILON);
+
+ assert_int_equal(pcmk__scan_double("-1.0", &result, NULL, NULL), pcmk_rc_ok);
+ assert_float_equal(result, -1.0, DBL_EPSILON);
+
+ snprintf(str, LOCAL_BUF_SIZE, "%f", DBL_MAX);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), pcmk_rc_ok);
+ assert_float_equal(result, DBL_MAX, DBL_EPSILON);
+
+ snprintf(str, LOCAL_BUF_SIZE, "%f", -DBL_MAX);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), pcmk_rc_ok);
+ assert_float_equal(result, -DBL_MAX, DBL_EPSILON);
+}
+
+static void
+double_overflow(void **state)
+{
+ char str[LOCAL_BUF_SIZE];
+ double result;
+
+ /*
+ * 1e(DBL_MAX_10_EXP + 1) produces an inf value
+ * Can't use assert_float_equal() because (inf - inf) == NaN
+ */
+ snprintf(str, LOCAL_BUF_SIZE, "1e%d", DBL_MAX_10_EXP + 1);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), EOVERFLOW);
+ assert_true(result > DBL_MAX);
+
+ snprintf(str, LOCAL_BUF_SIZE, "-1e%d", DBL_MAX_10_EXP + 1);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), EOVERFLOW);
+ assert_true(result < -DBL_MAX);
+}
+
+static void
+double_underflow(void **state)
+{
+ char str[LOCAL_BUF_SIZE];
+ double result;
+
+ /*
+ * 1e(DBL_MIN_10_EXP - 1) produces a denormalized value (between 0
+ * and DBL_MIN)
+ *
+ * C99/C11: result will be **no greater than** DBL_MIN
+ */
+ snprintf(str, LOCAL_BUF_SIZE, "1e%d", DBL_MIN_10_EXP - 1);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), pcmk_rc_underflow);
+ assert_true(result >= 0.0);
+ assert_true(result <= DBL_MIN);
+
+ snprintf(str, LOCAL_BUF_SIZE, "-1e%d", DBL_MIN_10_EXP - 1);
+ assert_int_equal(pcmk__scan_double(str, &result, NULL, NULL), pcmk_rc_underflow);
+ assert_true(result <= 0.0);
+ assert_true(result >= -DBL_MIN);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_string),
+ cmocka_unit_test(bad_input_string),
+ cmocka_unit_test(trailing_chars),
+ cmocka_unit_test(no_result_variable),
+ cmocka_unit_test(typical_case),
+ cmocka_unit_test(double_overflow),
+ cmocka_unit_test(double_underflow))
diff --git a/lib/common/tests/strings/pcmk__scan_min_int_test.c b/lib/common/tests/strings/pcmk__scan_min_int_test.c
new file mode 100644
index 0000000..90c3e87
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__scan_min_int_test.c
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_input_string(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_min_int("", &result, 1), EINVAL);
+ assert_int_equal(result, 1);
+
+ assert_int_equal(pcmk__scan_min_int(NULL, &result, 1), pcmk_rc_ok);
+ assert_int_equal(result, 1);
+}
+
+static void
+input_below_minimum(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_min_int("100", &result, 1024), pcmk_rc_ok);
+ assert_int_equal(result, 1024);
+}
+
+static void
+input_above_maximum(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_min_int("20000000000000000", &result, 100), EOVERFLOW);
+ assert_int_equal(result, INT_MAX);
+}
+
+static void
+input_just_right(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_min_int("1024", &result, 1024), pcmk_rc_ok);
+ assert_int_equal(result, 1024);
+
+ assert_int_equal(pcmk__scan_min_int("2048", &result, 1024), pcmk_rc_ok);
+ assert_int_equal(result, 2048);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_string),
+ cmocka_unit_test(input_below_minimum),
+ cmocka_unit_test(input_above_maximum),
+ cmocka_unit_test(input_just_right))
diff --git a/lib/common/tests/strings/pcmk__scan_port_test.c b/lib/common/tests/strings/pcmk__scan_port_test.c
new file mode 100644
index 0000000..cf927e4
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__scan_port_test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_input_string(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_port("", &result), EINVAL);
+ assert_int_equal(result, -1);
+}
+
+static void
+bad_input_string(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_port("abc", &result), EINVAL);
+ assert_int_equal(result, -1);
+}
+
+static void
+out_of_range(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_port("-1", &result), pcmk_rc_before_range);
+ assert_int_equal(result, -1);
+ assert_int_equal(pcmk__scan_port("65536", &result), pcmk_rc_after_range);
+ assert_int_equal(result, -1);
+}
+
+static void
+typical_case(void **state)
+{
+ int result;
+
+ assert_int_equal(pcmk__scan_port("0", &result), pcmk_rc_ok);
+ assert_int_equal(result, 0);
+
+ assert_int_equal(pcmk__scan_port("80", &result), pcmk_rc_ok);
+ assert_int_equal(result, 80);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_string),
+ cmocka_unit_test(bad_input_string),
+ cmocka_unit_test(out_of_range),
+ cmocka_unit_test(typical_case))
diff --git a/lib/common/tests/strings/pcmk__starts_with_test.c b/lib/common/tests/strings/pcmk__starts_with_test.c
new file mode 100644
index 0000000..5429417
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__starts_with_test.c
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+bad_input(void **state) {
+ assert_false(pcmk__starts_with(NULL, "x"));
+ assert_false(pcmk__starts_with("abc", NULL));
+}
+
+static void
+starts_with(void **state) {
+ assert_true(pcmk__starts_with("abc", "a"));
+ assert_true(pcmk__starts_with("abc", "ab"));
+ assert_true(pcmk__starts_with("abc", "abc"));
+
+ assert_false(pcmk__starts_with("abc", "A"));
+ assert_false(pcmk__starts_with("abc", "bc"));
+
+ assert_false(pcmk__starts_with("", "x"));
+ assert_true(pcmk__starts_with("xyz", ""));
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_input),
+ cmocka_unit_test(starts_with))
diff --git a/lib/common/tests/strings/pcmk__str_any_of_test.c b/lib/common/tests/strings/pcmk__str_any_of_test.c
new file mode 100644
index 0000000..bd4ae2c
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__str_any_of_test.c
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2020-2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+empty_input_list(void **state) {
+ assert_false(pcmk__strcase_any_of("xxx", NULL));
+ assert_false(pcmk__str_any_of("xxx", NULL));
+ assert_false(pcmk__strcase_any_of("", NULL));
+ assert_false(pcmk__str_any_of("", NULL));
+}
+
+static void
+empty_string(void **state) {
+ assert_false(pcmk__strcase_any_of("", "xxx", "yyy", NULL));
+ assert_false(pcmk__str_any_of("", "xxx", "yyy", NULL));
+ assert_false(pcmk__strcase_any_of(NULL, "xxx", "yyy", NULL));
+ assert_false(pcmk__str_any_of(NULL, "xxx", "yyy", NULL));
+}
+
+static void
+in_list(void **state) {
+ assert_true(pcmk__strcase_any_of("xxx", "aaa", "bbb", "xxx", NULL));
+ assert_true(pcmk__str_any_of("xxx", "aaa", "bbb", "xxx", NULL));
+ assert_true(pcmk__strcase_any_of("XXX", "aaa", "bbb", "xxx", NULL));
+}
+
+static void
+not_in_list(void **state) {
+ assert_false(pcmk__strcase_any_of("xxx", "aaa", "bbb", NULL));
+ assert_false(pcmk__str_any_of("xxx", "aaa", "bbb", NULL));
+ assert_false(pcmk__str_any_of("AAA", "aaa", "bbb", NULL));
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_list),
+ cmocka_unit_test(empty_string),
+ cmocka_unit_test(in_list),
+ cmocka_unit_test(not_in_list))
diff --git a/lib/common/tests/strings/pcmk__str_in_list_test.c b/lib/common/tests/strings/pcmk__str_in_list_test.c
new file mode 100644
index 0000000..cff536a
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__str_in_list_test.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright 2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+empty_input_list(void **state) {
+ assert_false(pcmk__str_in_list(NULL, NULL, pcmk__str_none));
+ assert_false(pcmk__str_in_list(NULL, NULL, pcmk__str_null_matches));
+ assert_false(pcmk__str_in_list("xxx", NULL, pcmk__str_none));
+ assert_false(pcmk__str_in_list("", NULL, pcmk__str_none));
+}
+
+static void
+empty_string(void **state) {
+ GList *list = NULL;
+
+ list = g_list_prepend(list, (gpointer) "xxx");
+
+ assert_false(pcmk__str_in_list(NULL, list, pcmk__str_none));
+ assert_true(pcmk__str_in_list(NULL, list, pcmk__str_null_matches));
+ assert_false(pcmk__str_in_list("", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("", list, pcmk__str_null_matches));
+
+ g_list_free(list);
+}
+
+static void
+star_matches(void **state) {
+ GList *list = NULL;
+
+ list = g_list_prepend(list, (gpointer) "*");
+ list = g_list_append(list, (gpointer) "more");
+
+ assert_true(pcmk__str_in_list("xxx", list, pcmk__str_star_matches));
+ assert_true(pcmk__str_in_list("yyy", list, pcmk__str_star_matches));
+ assert_true(pcmk__str_in_list("XXX", list, pcmk__str_star_matches|pcmk__str_casei));
+ assert_true(pcmk__str_in_list("", list, pcmk__str_star_matches));
+
+ g_list_free(list);
+}
+
+static void
+star_doesnt_match(void **state) {
+ GList *list = NULL;
+
+ list = g_list_prepend(list, (gpointer) "*");
+
+ assert_false(pcmk__str_in_list("xxx", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("yyy", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("XXX", list, pcmk__str_casei));
+ assert_false(pcmk__str_in_list("", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list(NULL, list, pcmk__str_star_matches));
+
+ g_list_free(list);
+}
+
+static void
+in_list(void **state) {
+ GList *list = NULL;
+
+ list = g_list_prepend(list, (gpointer) "xxx");
+ list = g_list_prepend(list, (gpointer) "yyy");
+ list = g_list_prepend(list, (gpointer) "zzz");
+
+ assert_true(pcmk__str_in_list("xxx", list, pcmk__str_none));
+ assert_true(pcmk__str_in_list("XXX", list, pcmk__str_casei));
+ assert_true(pcmk__str_in_list("yyy", list, pcmk__str_none));
+ assert_true(pcmk__str_in_list("YYY", list, pcmk__str_casei));
+ assert_true(pcmk__str_in_list("zzz", list, pcmk__str_none));
+ assert_true(pcmk__str_in_list("ZZZ", list, pcmk__str_casei));
+
+ g_list_free(list);
+}
+
+static void
+not_in_list(void **state) {
+ GList *list = NULL;
+
+ list = g_list_prepend(list, (gpointer) "xxx");
+ list = g_list_prepend(list, (gpointer) "yyy");
+
+ assert_false(pcmk__str_in_list("xx", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("XXX", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("zzz", list, pcmk__str_none));
+ assert_false(pcmk__str_in_list("zzz", list, pcmk__str_casei));
+
+ g_list_free(list);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input_list),
+ cmocka_unit_test(empty_string),
+ cmocka_unit_test(star_matches),
+ cmocka_unit_test(star_doesnt_match),
+ cmocka_unit_test(in_list),
+ cmocka_unit_test(not_in_list))
diff --git a/lib/common/tests/strings/pcmk__str_table_dup_test.c b/lib/common/tests/strings/pcmk__str_table_dup_test.c
new file mode 100644
index 0000000..754bde6
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__str_table_dup_test.c
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+null_input_table(void **state)
+{
+ assert_null(pcmk__str_table_dup(NULL));
+}
+
+static void
+empty_input_table(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ GHashTable *copy = NULL;
+
+ copy = pcmk__str_table_dup(tbl);
+ assert_int_equal(g_hash_table_size(copy), 0);
+
+ g_hash_table_destroy(tbl);
+ g_hash_table_destroy(copy);
+}
+
+static void
+regular_input_table(void **state)
+{
+ GHashTable *tbl = pcmk__strkey_table(free, free);
+ GHashTable *copy = NULL;
+
+ g_hash_table_insert(tbl, strdup("abc"), strdup("123"));
+ g_hash_table_insert(tbl, strdup("def"), strdup("456"));
+ g_hash_table_insert(tbl, strdup("ghi"), strdup("789"));
+
+ copy = pcmk__str_table_dup(tbl);
+ assert_int_equal(g_hash_table_size(copy), 3);
+
+ assert_string_equal(g_hash_table_lookup(tbl, "abc"), "123");
+ assert_string_equal(g_hash_table_lookup(tbl, "def"), "456");
+ assert_string_equal(g_hash_table_lookup(tbl, "ghi"), "789");
+
+ g_hash_table_destroy(tbl);
+ g_hash_table_destroy(copy);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(null_input_table),
+ cmocka_unit_test(empty_input_table),
+ cmocka_unit_test(regular_input_table))
diff --git a/lib/common/tests/strings/pcmk__str_update_test.c b/lib/common/tests/strings/pcmk__str_update_test.c
new file mode 100644
index 0000000..571031d
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__str_update_test.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include "mock_private.h"
+
+static void
+update_null(void **state) {
+ char *str = NULL;
+
+ // These just make sure they don't crash
+ pcmk__str_update(NULL, NULL);
+ pcmk__str_update(NULL, "value");
+
+ // Update an already NULL string to NULL
+ pcmk__str_update(&str, NULL);
+ assert_null(str);
+
+ // Update an already allocated string to NULL
+ str = strdup("hello");
+ pcmk__str_update(&str, NULL);
+ assert_null(str);
+}
+
+static void
+update_same(void **state) {
+ char *str = NULL;
+ char *saved = NULL;
+
+ str = strdup("hello");
+ saved = str;
+ pcmk__str_update(&str, "hello");
+ assert_ptr_equal(saved, str); // No free and reallocation
+ free(str);
+}
+
+static void
+update_different(void **state) {
+ char *str = NULL;
+
+ str = strdup("hello");
+ pcmk__str_update(&str, "world");
+ assert_string_equal(str, "world");
+ free(str);
+}
+
+static void
+strdup_fails(void **state) {
+ char *str = NULL;
+
+ str = strdup("hello");
+
+ pcmk__assert_asserts(
+ {
+ pcmk__mock_strdup = true; // strdup() will return NULL
+ expect_string(__wrap_strdup, s, "world");
+ pcmk__str_update(&str, "world");
+ pcmk__mock_strdup = false; // Use the real strdup()
+ }
+ );
+
+ free(str);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(update_null),
+ cmocka_unit_test(update_same),
+ cmocka_unit_test(update_different),
+ cmocka_unit_test(strdup_fails))
diff --git a/lib/common/tests/strings/pcmk__strcmp_test.c b/lib/common/tests/strings/pcmk__strcmp_test.c
new file mode 100644
index 0000000..a709f18
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__strcmp_test.c
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2020-2021 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+static void
+same_pointer(void **state) {
+ const char *s1 = "abcd";
+ const char *s2 = "wxyz";
+
+ assert_int_equal(pcmk__strcmp(s1, s1, pcmk__str_none), 0);
+ assert_true(pcmk__str_eq(s1, s1, pcmk__str_none));
+ assert_int_not_equal(pcmk__strcmp(s1, s2, pcmk__str_none), 0);
+ assert_false(pcmk__str_eq(s1, s2, pcmk__str_none));
+ assert_int_equal(pcmk__strcmp(NULL, NULL, pcmk__str_none), 0);
+}
+
+static void
+one_is_null(void **state) {
+ const char *s1 = "abcd";
+
+ assert_int_equal(pcmk__strcmp(s1, NULL, pcmk__str_null_matches), 0);
+ assert_true(pcmk__str_eq(s1, NULL, pcmk__str_null_matches));
+ assert_int_equal(pcmk__strcmp(NULL, s1, pcmk__str_null_matches), 0);
+ assert_true(pcmk__strcmp(s1, NULL, pcmk__str_none) > 0);
+ assert_false(pcmk__str_eq(s1, NULL, pcmk__str_none));
+ assert_true(pcmk__strcmp(NULL, s1, pcmk__str_none) < 0);
+}
+
+static void
+case_matters(void **state) {
+ const char *s1 = "abcd";
+ const char *s2 = "ABCD";
+
+ assert_true(pcmk__strcmp(s1, s2, pcmk__str_none) > 0);
+ assert_false(pcmk__str_eq(s1, s2, pcmk__str_none));
+ assert_true(pcmk__strcmp(s2, s1, pcmk__str_none) < 0);
+}
+
+static void
+case_insensitive(void **state) {
+ const char *s1 = "abcd";
+ const char *s2 = "ABCD";
+
+ assert_int_equal(pcmk__strcmp(s1, s2, pcmk__str_casei), 0);
+ assert_true(pcmk__str_eq(s1, s2, pcmk__str_casei));
+}
+
+static void
+regex(void **state) {
+ const char *s1 = "abcd";
+ const char *s2 = "ABCD";
+
+ assert_true(pcmk__strcmp(NULL, "a..d", pcmk__str_regex) > 0);
+ assert_true(pcmk__strcmp(s1, NULL, pcmk__str_regex) > 0);
+ assert_int_equal(pcmk__strcmp(s1, "a..d", pcmk__str_regex), 0);
+ assert_true(pcmk__str_eq(s1, "a..d", pcmk__str_regex));
+ assert_int_not_equal(pcmk__strcmp(s1, "xxyy", pcmk__str_regex), 0);
+ assert_false(pcmk__str_eq(s1, "xxyy", pcmk__str_regex));
+ assert_int_equal(pcmk__strcmp(s2, "a..d", pcmk__str_regex|pcmk__str_casei), 0);
+ assert_true(pcmk__str_eq(s2, "a..d", pcmk__str_regex|pcmk__str_casei));
+ assert_int_not_equal(pcmk__strcmp(s2, "a..d", pcmk__str_regex), 0);
+ assert_false(pcmk__str_eq(s2, "a..d", pcmk__str_regex));
+ assert_true(pcmk__strcmp(s2, "*ab", pcmk__str_regex) > 0);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(same_pointer),
+ cmocka_unit_test(one_is_null),
+ cmocka_unit_test(case_matters),
+ cmocka_unit_test(case_insensitive),
+ cmocka_unit_test(regex))
diff --git a/lib/common/tests/strings/pcmk__strikey_table_test.c b/lib/common/tests/strings/pcmk__strikey_table_test.c
new file mode 100644
index 0000000..d5f8635
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__strikey_table_test.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+store_strs(void **state)
+{
+ GHashTable *tbl = NULL;
+
+ tbl = pcmk__strikey_table(free, free);
+ assert_non_null(tbl);
+
+ assert_true(g_hash_table_insert(tbl, strdup("key-abc"), strdup("val-abc")));
+ assert_int_equal(g_hash_table_size(tbl), 1);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-abc"), "val-abc");
+
+ assert_false(g_hash_table_insert(tbl, strdup("key-abc"), strdup("val-def")));
+ assert_int_equal(g_hash_table_size(tbl), 1);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-abc"), "val-def");
+
+ assert_false(g_hash_table_insert(tbl, strdup("key-ABC"), strdup("val-ABC")));
+ assert_int_equal(g_hash_table_size(tbl), 1);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-ABC"), "val-ABC");
+
+ g_hash_table_destroy(tbl);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(store_strs))
diff --git a/lib/common/tests/strings/pcmk__strkey_table_test.c b/lib/common/tests/strings/pcmk__strkey_table_test.c
new file mode 100644
index 0000000..ac6d92e
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__strkey_table_test.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+store_strs(void **state)
+{
+ GHashTable *tbl = NULL;
+
+ tbl = pcmk__strkey_table(free, free);
+ assert_non_null(tbl);
+
+ assert_true(g_hash_table_insert(tbl, strdup("key-abc"), strdup("val-abc")));
+ assert_int_equal(g_hash_table_size(tbl), 1);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-abc"), "val-abc");
+
+ assert_false(g_hash_table_insert(tbl, strdup("key-abc"), strdup("val-def")));
+ assert_int_equal(g_hash_table_size(tbl), 1);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-abc"), "val-def");
+
+ assert_true(g_hash_table_insert(tbl, strdup("key-ABC"), strdup("val-abc")));
+ assert_int_equal(g_hash_table_size(tbl), 2);
+ assert_string_equal(g_hash_table_lookup(tbl, "key-ABC"), "val-abc");
+
+ g_hash_table_destroy(tbl);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(store_strs))
diff --git a/lib/common/tests/strings/pcmk__trim_test.c b/lib/common/tests/strings/pcmk__trim_test.c
new file mode 100644
index 0000000..56bdd17
--- /dev/null
+++ b/lib/common/tests/strings/pcmk__trim_test.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2022 the Pacemaker project contributors
+ *
+ * The version control history for this file may have further details.
+ *
+ * This source code is licensed under the GNU General Public License version 2
+ * or later (GPLv2+) WITHOUT ANY WARRANTY.
+ */
+
+#include <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <string.h>
+
+static void
+empty_input(void **state)
+{
+ char *s = strdup("");
+
+ assert_null(pcmk__trim(NULL));
+ assert_string_equal(pcmk__trim(s), "");
+
+ free(s);
+}
+
+static void
+leading_newline(void **state)
+{
+ char *s = strdup("\nabcd");
+
+ assert_string_equal(pcmk__trim(s), "\nabcd");
+ free(s);
+}
+
+static void
+middle_newline(void **state)
+{
+ char *s = strdup("ab\ncd");
+
+ assert_string_equal(pcmk__trim(s), "ab\ncd");
+ free(s);
+}
+
+static void
+trailing_newline(void **state)
+{
+ char *s = strdup("abcd\n\n");
+
+ assert_string_equal(pcmk__trim(s), "abcd");
+ free(s);
+
+ s = strdup("abcd\n ");
+ assert_string_equal(pcmk__trim(s), "abcd\n ");
+ free(s);
+}
+
+static void
+other_whitespace(void **state)
+{
+ char *s = strdup(" ab\t\ncd \t");
+
+ assert_string_equal(pcmk__trim(s), " ab\t\ncd \t");
+ free(s);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_input),
+ cmocka_unit_test(leading_newline),
+ cmocka_unit_test(middle_newline),
+ cmocka_unit_test(trailing_newline),
+ cmocka_unit_test(other_whitespace))