summaryrefslogtreecommitdiffstats
path: root/lib/pengine/tests
diff options
context:
space:
mode:
Diffstat (limited to 'lib/pengine/tests')
-rw-r--r--lib/pengine/tests/Makefile.am1
-rw-r--r--lib/pengine/tests/native/Makefile.am22
-rw-r--r--lib/pengine/tests/native/native_find_rsc_test.c677
-rw-r--r--lib/pengine/tests/native/pe_base_name_eq_test.c149
-rw-r--r--lib/pengine/tests/rules/Makefile.am18
-rw-r--r--lib/pengine/tests/rules/pe_cron_range_satisfied_test.c165
-rw-r--r--lib/pengine/tests/status/Makefile.am22
-rw-r--r--lib/pengine/tests/status/pe_find_node_any_test.c62
-rw-r--r--lib/pengine/tests/status/pe_find_node_id_test.c51
-rw-r--r--lib/pengine/tests/status/pe_find_node_test.c51
-rw-r--r--lib/pengine/tests/status/pe_new_working_set_test.c46
-rw-r--r--lib/pengine/tests/status/set_working_set_defaults_test.c46
-rw-r--r--lib/pengine/tests/unpack/Makefile.am18
-rw-r--r--lib/pengine/tests/unpack/pe_base_name_end_test.c36
-rw-r--r--lib/pengine/tests/utils/Makefile.am21
-rw-r--r--lib/pengine/tests/utils/pe__cmp_node_name_test.c55
-rw-r--r--lib/pengine/tests/utils/pe__cmp_rsc_priority_test.c50
17 files changed, 1490 insertions, 0 deletions
diff --git a/lib/pengine/tests/Makefile.am b/lib/pengine/tests/Makefile.am
new file mode 100644
index 0000000..4986ef2
--- /dev/null
+++ b/lib/pengine/tests/Makefile.am
@@ -0,0 +1 @@
+SUBDIRS = rules native status unpack utils
diff --git a/lib/pengine/tests/native/Makefile.am b/lib/pengine/tests/native/Makefile.am
new file mode 100644
index 0000000..5046ff1
--- /dev/null
+++ b/lib/pengine/tests/native/Makefile.am
@@ -0,0 +1,22 @@
+#
+# 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 $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+AM_CPPFLAGS += -I$(top_srcdir)
+LDADD += $(top_builddir)/lib/pengine/libpe_status_test.la
+
+AM_TESTS_ENVIRONMENT += PCMK_CTS_CLI_DIR=$(top_srcdir)/cts/cli
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = native_find_rsc_test \
+ pe_base_name_eq_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/pengine/tests/native/native_find_rsc_test.c b/lib/pengine/tests/native/native_find_rsc_test.c
new file mode 100644
index 0000000..22aaf41
--- /dev/null
+++ b/lib/pengine/tests/native/native_find_rsc_test.c
@@ -0,0 +1,677 @@
+/*
+ * 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 <crm/common/xml.h>
+#include <crm/pengine/internal.h>
+#include <crm/pengine/status.h>
+#include <crm/pengine/pe_types.h>
+
+/* Needed to access replicas inside a bundle. */
+#define PE__VARIANT_BUNDLE 1
+#include <lib/pengine/variant.h>
+
+xmlNode *input = NULL;
+pe_working_set_t *data_set = NULL;
+
+pe_node_t *cluster01, *cluster02, *httpd_bundle_0;
+pe_resource_t *exim_group, *inactive_group, *promotable_clone, *inactive_clone;
+pe_resource_t *httpd_bundle, *mysql_clone_group;
+
+static int
+setup(void **state) {
+ char *path = NULL;
+
+ crm_xml_init();
+
+ path = crm_strdup_printf("%s/crm_mon.xml", getenv("PCMK_CTS_CLI_DIR"));
+ input = filename2xml(path);
+ free(path);
+
+ if (input == NULL) {
+ return 1;
+ }
+
+ data_set = pe_new_working_set();
+
+ if (data_set == NULL) {
+ return 1;
+ }
+
+ pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
+ data_set->input = input;
+
+ cluster_status(data_set);
+
+ /* Get references to the cluster nodes so we don't have to find them repeatedly. */
+ cluster01 = pe_find_node(data_set->nodes, "cluster01");
+ cluster02 = pe_find_node(data_set->nodes, "cluster02");
+ httpd_bundle_0 = pe_find_node(data_set->nodes, "httpd-bundle-0");
+
+ /* Get references to several resources we use frequently. */
+ for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "exim-group") == 0) {
+ exim_group = rsc;
+ } else if (strcmp(rsc->id, "httpd-bundle") == 0) {
+ httpd_bundle = rsc;
+ } else if (strcmp(rsc->id, "inactive-clone") == 0) {
+ inactive_clone = rsc;
+ } else if (strcmp(rsc->id, "inactive-group") == 0) {
+ inactive_group = rsc;
+ } else if (strcmp(rsc->id, "mysql-clone-group") == 0) {
+ mysql_clone_group = rsc;
+ } else if (strcmp(rsc->id, "promotable-clone") == 0) {
+ promotable_clone = rsc;
+ }
+ }
+
+ return 0;
+}
+
+static int
+teardown(void **state) {
+ pe_free_working_set(data_set);
+
+ return 0;
+}
+
+static void
+bad_args(void **state) {
+ pe_resource_t *rsc = (pe_resource_t *) g_list_first(data_set->resources)->data;
+ char *id = rsc->id;
+ char *name = NULL;
+
+ assert_non_null(rsc);
+
+ assert_null(native_find_rsc(NULL, "dummy", NULL, 0));
+ assert_null(native_find_rsc(rsc, NULL, NULL, 0));
+
+ /* No resources exist with these names. */
+ name = crm_strdup_printf("%sX", rsc->id);
+ assert_null(native_find_rsc(rsc, name, NULL, 0));
+ free(name);
+
+ name = crm_strdup_printf("x%s", rsc->id);
+ assert_null(native_find_rsc(rsc, name, NULL, 0));
+ free(name);
+
+ name = g_ascii_strup(rsc->id, -1);
+ assert_null(native_find_rsc(rsc, name, NULL, 0));
+ g_free(name);
+
+ /* Fails because resource ID is NULL. */
+ rsc->id = NULL;
+ assert_null(native_find_rsc(rsc, id, NULL, 0));
+ rsc->id = id;
+}
+
+static void
+primitive_rsc(void **state) {
+ pe_resource_t *dummy = NULL;
+
+ /* Find the "dummy" resource, which is the only one with that ID in the set. */
+ for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "dummy") == 0) {
+ dummy = rsc;
+ break;
+ }
+ }
+
+ assert_non_null(dummy);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(dummy, native_find_rsc(dummy, "dummy", NULL, 0));
+ assert_ptr_equal(dummy, native_find_rsc(dummy, "dummy", NULL, pe_find_current));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(dummy, "dummy", NULL, pe_find_clone));
+ assert_null(native_find_rsc(dummy, "dummy", cluster02, pe_find_clone));
+
+ /* Fails because dummy is not running on cluster01, even with the right flags. */
+ assert_null(native_find_rsc(dummy, "dummy", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(dummy, "dummy", cluster02, 0));
+
+ /* Passes because dummy is running on cluster02. */
+ assert_ptr_equal(dummy, native_find_rsc(dummy, "dummy", cluster02, pe_find_current));
+}
+
+static void
+group_rsc(void **state) {
+ assert_non_null(exim_group);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(exim_group, native_find_rsc(exim_group, "exim-group", NULL, 0));
+ assert_ptr_equal(exim_group, native_find_rsc(exim_group, "exim-group", NULL, pe_find_current));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(exim_group, "exim-group", NULL, pe_find_clone));
+ assert_null(native_find_rsc(exim_group, "exim-group", cluster01, pe_find_clone));
+
+ /* Fails because none of exim-group's children are running on cluster01, even with the right flags. */
+ assert_null(native_find_rsc(exim_group, "exim-group", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(exim_group, "exim-group", cluster01, 0));
+
+ /* Passes because one of exim-group's children is running on cluster02. */
+ assert_ptr_equal(exim_group, native_find_rsc(exim_group, "exim-group", cluster02, pe_find_current));
+}
+
+static void
+inactive_group_rsc(void **state) {
+ assert_non_null(inactive_group);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(inactive_group, native_find_rsc(inactive_group, "inactive-group", NULL, 0));
+ assert_ptr_equal(inactive_group, native_find_rsc(inactive_group, "inactive-group", NULL, pe_find_current));
+ assert_ptr_equal(inactive_group, native_find_rsc(inactive_group, "inactive-group", NULL, pe_find_inactive));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(inactive_group, "inactive-group", NULL, pe_find_clone));
+ assert_null(native_find_rsc(inactive_group, "inactive-group", cluster01, pe_find_clone));
+
+ /* Fails because none of inactive-group's children are running. */
+ assert_null(native_find_rsc(inactive_group, "inactive-group", cluster01, pe_find_current));
+ assert_null(native_find_rsc(inactive_group, "inactive-group", cluster02, pe_find_current));
+
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_group, native_find_rsc(inactive_group, "inactive-group", cluster01, pe_find_inactive));
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_group, native_find_rsc(inactive_group, "inactive-group", cluster02, pe_find_inactive));
+}
+
+static void
+group_member_rsc(void **state) {
+ pe_resource_t *public_ip = NULL;
+
+ /* Find the "Public-IP" resource, a member of "exim-group". */
+ for (GList *iter = exim_group->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "Public-IP") == 0) {
+ public_ip = rsc;
+ break;
+ }
+ }
+
+ assert_non_null(public_ip);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(public_ip, native_find_rsc(public_ip, "Public-IP", NULL, 0));
+ assert_ptr_equal(public_ip, native_find_rsc(public_ip, "Public-IP", NULL, pe_find_current));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(public_ip, "Public-IP", NULL, pe_find_clone));
+ assert_null(native_find_rsc(public_ip, "Public-IP", cluster02, pe_find_clone));
+
+ /* Fails because Public-IP is not running on cluster01, even with the right flags. */
+ assert_null(native_find_rsc(public_ip, "Public-IP", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(public_ip, "Public-IP", cluster02, 0));
+
+ /* Passes because Public-IP is running on cluster02. */
+ assert_ptr_equal(public_ip, native_find_rsc(public_ip, "Public-IP", cluster02, pe_find_current));
+}
+
+static void
+inactive_group_member_rsc(void **state) {
+ pe_resource_t *inactive_dummy_1 = NULL;
+
+ /* Find the "inactive-dummy-1" resource, a member of "inactive-group". */
+ for (GList *iter = inactive_group->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "inactive-dummy-1") == 0) {
+ inactive_dummy_1 = rsc;
+ break;
+ }
+ }
+
+ assert_non_null(inactive_dummy_1);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(inactive_dummy_1, native_find_rsc(inactive_dummy_1, "inactive-dummy-1", NULL, 0));
+ assert_ptr_equal(inactive_dummy_1, native_find_rsc(inactive_dummy_1, "inactive-dummy-1", NULL, pe_find_current));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(inactive_dummy_1, "inactive-dummy-1", NULL, pe_find_clone));
+ assert_null(native_find_rsc(inactive_dummy_1, "inactive-dummy-1", cluster01, pe_find_clone));
+
+ /* Fails because inactive-dummy-1 is not running. */
+ assert_null(native_find_rsc(inactive_dummy_1, "inactive-dummy-1", cluster01, pe_find_current));
+ assert_null(native_find_rsc(inactive_dummy_1, "inactive-dummy-1", cluster02, pe_find_current));
+
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_dummy_1, native_find_rsc(inactive_dummy_1, "inactive-dummy-1", cluster01, pe_find_inactive));
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_dummy_1, native_find_rsc(inactive_dummy_1, "inactive-dummy-1", cluster02, pe_find_inactive));
+}
+
+static void
+clone_rsc(void **state) {
+ assert_non_null(promotable_clone);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", NULL, 0));
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", NULL, pe_find_current));
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", NULL, pe_find_clone));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(promotable_clone, "promotable-clone", cluster01, 0));
+
+ /* Passes because one of ping-clone's children is running on cluster01. */
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(promotable_clone, "promotable-clone", cluster02, 0));
+
+ /* Passes because one of ping_clone's children is running on cluster02. */
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", cluster02, pe_find_current));
+
+ /* Passes for previous reasons, plus includes pe_find_clone check. */
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", cluster01, pe_find_clone|pe_find_current));
+ assert_ptr_equal(promotable_clone, native_find_rsc(promotable_clone, "promotable-clone", cluster02, pe_find_clone|pe_find_current));
+}
+
+static void
+inactive_clone_rsc(void **state) {
+ assert_non_null(inactive_clone);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", NULL, 0));
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", NULL, pe_find_current));
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", NULL, pe_find_clone));
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", NULL, pe_find_inactive));
+
+ /* Fails because none of inactive-clone's children are running. */
+ assert_null(native_find_rsc(inactive_clone, "inactive-clone", cluster01, pe_find_current|pe_find_clone));
+ assert_null(native_find_rsc(inactive_clone, "inactive-clone", cluster02, pe_find_current|pe_find_clone));
+
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", cluster01, pe_find_inactive));
+ /* Passes because of flags. */
+ assert_ptr_equal(inactive_clone, native_find_rsc(inactive_clone, "inactive-clone", cluster02, pe_find_inactive));
+}
+
+static void
+clone_instance_rsc(void **state) {
+ pe_resource_t *promotable_0 = NULL;
+ pe_resource_t *promotable_1 = NULL;
+
+ /* Find the "promotable-rsc:0" and "promotable-rsc:1" resources, members of "promotable-clone". */
+ for (GList *iter = promotable_clone->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "promotable-rsc:0") == 0) {
+ promotable_0 = rsc;
+ } else if (strcmp(rsc->id, "promotable-rsc:1") == 0) {
+ promotable_1 = rsc;
+ }
+ }
+
+ assert_non_null(promotable_0);
+ assert_non_null(promotable_1);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc:0", NULL, 0));
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc:0", NULL, pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc:1", NULL, 0));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc:1", NULL, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(promotable_0, "promotable-rsc:0", cluster02, 0));
+ assert_null(native_find_rsc(promotable_1, "promotable-rsc:1", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc:0", cluster02, pe_find_current));
+ assert_null(native_find_rsc(promotable_0, "promotable-rsc:0", cluster01, pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc:1", cluster01, pe_find_current));
+ assert_null(native_find_rsc(promotable_1, "promotable-rsc:1", cluster02, pe_find_current));
+
+ /* Passes because NULL was passed for node and primitive name was given, with correct flags. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", NULL, pe_find_clone));
+
+ /* Passes because pe_find_any matches any instance's base name. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", NULL, pe_find_any));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc", NULL, pe_find_any));
+
+ /* Passes because pe_find_anon matches. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", NULL, pe_find_anon));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc", NULL, pe_find_anon));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", cluster02, pe_find_any|pe_find_current));
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", cluster02, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(promotable_0, "promotable-rsc", cluster01, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(promotable_0, "promotable-rsc", cluster01, pe_find_anon|pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc", cluster01, pe_find_any|pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc", cluster01, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(promotable_1, "promotable-rsc", cluster02, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(promotable_1, "promotable-rsc", cluster02, pe_find_anon|pe_find_current));
+
+ /* Fails because incorrect flags were given along with primitive name. */
+ assert_null(native_find_rsc(promotable_0, "promotable-rsc", NULL, pe_find_current));
+ assert_null(native_find_rsc(promotable_1, "promotable-rsc", NULL, pe_find_current));
+
+ /* And then we check failure possibilities again, except passing promotable_clone
+ * instead of promotable_X as the first argument to native_find_rsc.
+ */
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(promotable_clone, "promotable-rsc:0", cluster02, 0));
+ assert_null(native_find_rsc(promotable_clone, "promotable-rsc:1", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_clone, "promotable-rsc:0", cluster02, pe_find_current));
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_clone, "promotable-rsc", cluster02, pe_find_any|pe_find_current));
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_clone, "promotable-rsc", cluster02, pe_find_anon|pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_clone, "promotable-rsc:1", cluster01, pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_clone, "promotable-rsc", cluster01, pe_find_any|pe_find_current));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_clone, "promotable-rsc", cluster01, pe_find_anon|pe_find_current));
+}
+
+static void
+renamed_rsc(void **state) {
+ pe_resource_t *promotable_0 = NULL;
+ pe_resource_t *promotable_1 = NULL;
+
+ /* Find the "promotable-rsc:0" and "promotable-rsc:1" resources, members of "promotable-clone". */
+ for (GList *iter = promotable_clone->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "promotable-rsc:0") == 0) {
+ promotable_0 = rsc;
+ } else if (strcmp(rsc->id, "promotable-rsc:1") == 0) {
+ promotable_1 = rsc;
+ }
+ }
+
+ assert_non_null(promotable_0);
+ assert_non_null(promotable_1);
+
+ /* Passes because pe_find_renamed means the base name matches clone_name. */
+ assert_ptr_equal(promotable_0, native_find_rsc(promotable_0, "promotable-rsc", NULL, pe_find_renamed));
+ assert_ptr_equal(promotable_1, native_find_rsc(promotable_1, "promotable-rsc", NULL, pe_find_renamed));
+}
+
+static void
+bundle_rsc(void **state) {
+ assert_non_null(httpd_bundle);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(httpd_bundle, native_find_rsc(httpd_bundle, "httpd-bundle", NULL, 0));
+ assert_ptr_equal(httpd_bundle, native_find_rsc(httpd_bundle, "httpd-bundle", NULL, pe_find_current));
+
+ /* Fails because resource is not a clone (nor cloned). */
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle", NULL, pe_find_clone));
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle", cluster01, pe_find_clone));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle", cluster01, 0));
+
+ /* Passes because one of httpd_bundle's children is running on cluster01. */
+ assert_ptr_equal(httpd_bundle, native_find_rsc(httpd_bundle, "httpd-bundle", cluster01, pe_find_current));
+}
+
+static void
+bundle_replica_rsc(void **state) {
+ pe__bundle_variant_data_t *bundle_data = NULL;
+ pe__bundle_replica_t *replica_0 = NULL;
+
+ pe_resource_t *ip_0 = NULL;
+ pe_resource_t *child_0 = NULL;
+ pe_resource_t *container_0 = NULL;
+ pe_resource_t *remote_0 = NULL;
+
+ get_bundle_variant_data(bundle_data, httpd_bundle);
+ replica_0 = (pe__bundle_replica_t *) bundle_data->replicas->data;
+
+ ip_0 = replica_0->ip;
+ child_0 = replica_0->child;
+ container_0 = replica_0->container;
+ remote_0 = replica_0->remote;
+
+ assert_non_null(ip_0);
+ assert_non_null(child_0);
+ assert_non_null(container_0);
+ assert_non_null(remote_0);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(ip_0, native_find_rsc(ip_0, "httpd-bundle-ip-192.168.122.131", NULL, 0));
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd:0", NULL, 0));
+ assert_ptr_equal(container_0, native_find_rsc(container_0, "httpd-bundle-docker-0", NULL, 0));
+ assert_ptr_equal(remote_0, native_find_rsc(remote_0, "httpd-bundle-0", NULL, 0));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(ip_0, "httpd-bundle-ip-192.168.122.131", cluster01, 0));
+ assert_null(native_find_rsc(child_0, "httpd:0", httpd_bundle_0, 0));
+ assert_null(native_find_rsc(container_0, "httpd-bundle-docker-0", cluster01, 0));
+ assert_null(native_find_rsc(remote_0, "httpd-bundle-0", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(ip_0, native_find_rsc(ip_0, "httpd-bundle-ip-192.168.122.131", cluster01, pe_find_current));
+ assert_null(native_find_rsc(ip_0, "httpd-bundle-ip-192.168.122.131", cluster02, pe_find_current));
+ assert_null(native_find_rsc(ip_0, "httpd-bundle-ip-192.168.122.131", httpd_bundle_0, pe_find_current));
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd:0", httpd_bundle_0, pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd:0", cluster01, pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd:0", cluster02, pe_find_current));
+ assert_ptr_equal(container_0, native_find_rsc(container_0, "httpd-bundle-docker-0", cluster01, pe_find_current));
+ assert_null(native_find_rsc(container_0, "httpd-bundle-docker-0", cluster02, pe_find_current));
+ assert_null(native_find_rsc(container_0, "httpd-bundle-docker-0", httpd_bundle_0, pe_find_current));
+ assert_ptr_equal(remote_0, native_find_rsc(remote_0, "httpd-bundle-0", cluster01, pe_find_current));
+ assert_null(native_find_rsc(remote_0, "httpd-bundle-0", cluster02, pe_find_current));
+ assert_null(native_find_rsc(remote_0, "httpd-bundle-0", httpd_bundle_0, pe_find_current));
+
+ /* Passes because pe_find_any matches any replica's base name. */
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd", NULL, pe_find_any));
+
+ /* Passes because pe_find_anon matches. */
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd", NULL, pe_find_anon));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd", httpd_bundle_0, pe_find_any|pe_find_current));
+ assert_ptr_equal(child_0, native_find_rsc(child_0, "httpd", httpd_bundle_0, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd", cluster01, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd", cluster01, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd", cluster02, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(child_0, "httpd", cluster02, pe_find_anon|pe_find_current));
+
+ /* Fails because incorrect flags were given along with base name. */
+ assert_null(native_find_rsc(child_0, "httpd", NULL, pe_find_current));
+
+ /* And then we check failure possibilities again, except passing httpd-bundle
+ * instead of X_0 as the first argument to native_find_rsc.
+ */
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle-ip-192.168.122.131", cluster01, 0));
+ assert_null(native_find_rsc(httpd_bundle, "httpd:0", httpd_bundle_0, 0));
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle-docker-0", cluster01, 0));
+ assert_null(native_find_rsc(httpd_bundle, "httpd-bundle-0", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(ip_0, native_find_rsc(httpd_bundle, "httpd-bundle-ip-192.168.122.131", cluster01, pe_find_current));
+ assert_ptr_equal(child_0, native_find_rsc(httpd_bundle, "httpd:0", httpd_bundle_0, pe_find_current));
+ assert_ptr_equal(container_0, native_find_rsc(httpd_bundle, "httpd-bundle-docker-0", cluster01, pe_find_current));
+ assert_ptr_equal(remote_0, native_find_rsc(httpd_bundle, "httpd-bundle-0", cluster01, pe_find_current));
+}
+
+static void
+clone_group_rsc(void **rsc) {
+ assert_non_null(mysql_clone_group);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", NULL, 0));
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", NULL, pe_find_current));
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", NULL, pe_find_clone));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster01, 0));
+
+ /* Passes because one of mysql-clone-group's children is running on cluster01. */
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster02, 0));
+
+ /* Passes because one of mysql-clone-group's children is running on cluster02. */
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster02, pe_find_current));
+
+ /* Passes for previous reasons, plus includes pe_find_clone check. */
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster01, pe_find_clone|pe_find_current));
+ assert_ptr_equal(mysql_clone_group, native_find_rsc(mysql_clone_group, "mysql-clone-group", cluster02, pe_find_clone|pe_find_current));
+}
+
+static void
+clone_group_instance_rsc(void **rsc) {
+ pe_resource_t *mysql_group_0 = NULL;
+ pe_resource_t *mysql_group_1 = NULL;
+
+ /* Find the "mysql-group:0" and "mysql-group:1" resources, members of "mysql-clone-group". */
+ for (GList *iter = mysql_clone_group->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "mysql-group:0") == 0) {
+ mysql_group_0 = rsc;
+ } else if (strcmp(rsc->id, "mysql-group:1") == 0) {
+ mysql_group_1 = rsc;
+ }
+ }
+
+ assert_non_null(mysql_group_0);
+ assert_non_null(mysql_group_1);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group:0", NULL, 0));
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group:0", NULL, pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group:1", NULL, 0));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group:1", NULL, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(mysql_group_0, "mysql-group:0", cluster02, 0));
+ assert_null(native_find_rsc(mysql_group_1, "mysql-group:1", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group:0", cluster02, pe_find_current));
+ assert_null(native_find_rsc(mysql_group_0, "mysql-group:0", cluster01, pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group:1", cluster01, pe_find_current));
+ assert_null(native_find_rsc(mysql_group_1, "mysql-group:1", cluster02, pe_find_current));
+
+ /* Passes because NULL was passed for node and base name was given, with correct flags. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group" , NULL, pe_find_clone));
+
+ /* Passes because pe_find_any matches any base name. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group" , NULL, pe_find_any));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group" , NULL, pe_find_any));
+
+ /* Passes because pe_find_anon matches. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group" , NULL, pe_find_anon));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group" , NULL, pe_find_anon));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group", cluster02, pe_find_any|pe_find_current));
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_group_0, "mysql-group", cluster02, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(mysql_group_0, "mysql-group", cluster01, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(mysql_group_0, "mysql-group", cluster01, pe_find_anon|pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group", cluster01, pe_find_any|pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_group_1, "mysql-group", cluster01, pe_find_anon|pe_find_current));
+ assert_null(native_find_rsc(mysql_group_1, "mysql-group", cluster02, pe_find_any|pe_find_current));
+ assert_null(native_find_rsc(mysql_group_1, "mysql-group", cluster02, pe_find_anon|pe_find_current));
+
+ /* Fails because incorrect flags were given along with base name. */
+ assert_null(native_find_rsc(mysql_group_0, "mysql-group", NULL, pe_find_current));
+ assert_null(native_find_rsc(mysql_group_1, "mysql-group", NULL, pe_find_current));
+
+ /* And then we check failure possibilities again, except passing mysql_clone_group
+ * instead of mysql_group_X as the first argument to native_find_rsc.
+ */
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(mysql_clone_group, "mysql-group:0", cluster02, 0));
+ assert_null(native_find_rsc(mysql_clone_group, "mysql-group:1", cluster01, 0));
+
+ /* Check that the resource is running on the node we expect. */
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_clone_group, "mysql-group:0", cluster02, pe_find_current));
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_clone_group, "mysql-group", cluster02, pe_find_any|pe_find_current));
+ assert_ptr_equal(mysql_group_0, native_find_rsc(mysql_clone_group, "mysql-group", cluster02, pe_find_anon|pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_clone_group, "mysql-group:1", cluster01, pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_clone_group, "mysql-group", cluster01, pe_find_any|pe_find_current));
+ assert_ptr_equal(mysql_group_1, native_find_rsc(mysql_clone_group, "mysql-group", cluster01, pe_find_anon|pe_find_current));
+}
+
+static void
+clone_group_member_rsc(void **state) {
+ pe_resource_t *mysql_proxy = NULL;
+
+ /* Find the "mysql-proxy" resource, a member of "mysql-group". */
+ for (GList *iter = mysql_clone_group->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "mysql-group:0") == 0) {
+ for (GList *iter2 = rsc->children; iter2 != NULL; iter2 = iter2->next) {
+ pe_resource_t *child = (pe_resource_t *) iter2->data;
+
+ if (strcmp(child->id, "mysql-proxy:0") == 0) {
+ mysql_proxy = child;
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ assert_non_null(mysql_proxy);
+
+ /* Passes because NULL was passed for node, regardless of flags. */
+ assert_ptr_equal(mysql_proxy, native_find_rsc(mysql_proxy, "mysql-proxy:0", NULL, 0));
+ assert_ptr_equal(mysql_proxy, native_find_rsc(mysql_proxy, "mysql-proxy:0", NULL, pe_find_current));
+
+ /* Passes because resource's parent is a clone. */
+ assert_ptr_equal(mysql_proxy, native_find_rsc(mysql_proxy, "mysql-proxy:0", NULL, pe_find_clone));
+ assert_ptr_equal(mysql_proxy, native_find_rsc(mysql_proxy, "mysql-proxy:0", cluster02, pe_find_clone|pe_find_current));
+
+ /* Fails because mysql-proxy:0 is not running on cluster01, even with the right flags. */
+ assert_null(native_find_rsc(mysql_proxy, "mysql-proxy:0", cluster01, pe_find_current));
+
+ /* Fails because pe_find_current is required if a node is given. */
+ assert_null(native_find_rsc(mysql_proxy, "mysql-proxy:0", cluster02, 0));
+
+ /* Passes because mysql-proxy:0 is running on cluster02. */
+ assert_ptr_equal(mysql_proxy, native_find_rsc(mysql_proxy, "mysql-proxy:0", cluster02, pe_find_current));
+}
+
+/* TODO: Add tests for finding on allocated node (passing a node without
+ * pe_find_current, after scheduling, for a resource that is starting/stopping/moving.
+ */
+PCMK__UNIT_TEST(setup, teardown,
+ cmocka_unit_test(bad_args),
+ cmocka_unit_test(primitive_rsc),
+ cmocka_unit_test(group_rsc),
+ cmocka_unit_test(inactive_group_rsc),
+ cmocka_unit_test(group_member_rsc),
+ cmocka_unit_test(inactive_group_member_rsc),
+ cmocka_unit_test(clone_rsc),
+ cmocka_unit_test(inactive_clone_rsc),
+ cmocka_unit_test(clone_instance_rsc),
+ cmocka_unit_test(renamed_rsc),
+ cmocka_unit_test(bundle_rsc),
+ cmocka_unit_test(bundle_replica_rsc),
+ cmocka_unit_test(clone_group_rsc),
+ cmocka_unit_test(clone_group_instance_rsc),
+ cmocka_unit_test(clone_group_member_rsc))
diff --git a/lib/pengine/tests/native/pe_base_name_eq_test.c b/lib/pengine/tests/native/pe_base_name_eq_test.c
new file mode 100644
index 0000000..67a62f8
--- /dev/null
+++ b/lib/pengine/tests/native/pe_base_name_eq_test.c
@@ -0,0 +1,149 @@
+/*
+ * 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 <crm/common/xml.h>
+#include <crm/pengine/internal.h>
+#include <crm/pengine/status.h>
+#include <crm/pengine/pe_types.h>
+
+xmlNode *input = NULL;
+pe_working_set_t *data_set = NULL;
+
+pe_resource_t *exim_group, *promotable_0, *promotable_1, *dummy;
+pe_resource_t *httpd_bundle, *mysql_group_0, *mysql_group_1;
+
+static int
+setup(void **state) {
+ char *path = NULL;
+
+ crm_xml_init();
+
+ path = crm_strdup_printf("%s/crm_mon.xml", getenv("PCMK_CTS_CLI_DIR"));
+ input = filename2xml(path);
+ free(path);
+
+ if (input == NULL) {
+ return 1;
+ }
+
+ data_set = pe_new_working_set();
+
+ if (data_set == NULL) {
+ return 1;
+ }
+
+ pe__set_working_set_flags(data_set, pe_flag_no_counts|pe_flag_no_compat);
+ data_set->input = input;
+
+ cluster_status(data_set);
+
+ /* Get references to several resources we use frequently. */
+ for (GList *iter = data_set->resources; iter != NULL; iter = iter->next) {
+ pe_resource_t *rsc = (pe_resource_t *) iter->data;
+
+ if (strcmp(rsc->id, "dummy") == 0) {
+ dummy = rsc;
+ } else if (strcmp(rsc->id, "exim-group") == 0) {
+ exim_group = rsc;
+ } else if (strcmp(rsc->id, "httpd-bundle") == 0) {
+ httpd_bundle = rsc;
+ } else if (strcmp(rsc->id, "mysql-clone-group") == 0) {
+ for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *child = (pe_resource_t *) iter->data;
+
+ if (strcmp(child->id, "mysql-group:0") == 0) {
+ mysql_group_0 = child;
+ } else if (strcmp(child->id, "mysql-group:1") == 0) {
+ mysql_group_1 = child;
+ }
+ }
+ } else if (strcmp(rsc->id, "promotable-clone") == 0) {
+ for (GList *iter = rsc->children; iter != NULL; iter = iter->next) {
+ pe_resource_t *child = (pe_resource_t *) iter->data;
+
+ if (strcmp(child->id, "promotable-rsc:0") == 0) {
+ promotable_0 = child;
+ } else if (strcmp(child->id, "promotable-rsc:1") == 0) {
+ promotable_1 = child;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+teardown(void **state) {
+ pe_free_working_set(data_set);
+
+ return 0;
+}
+
+static void
+bad_args(void **state) {
+ char *id = dummy->id;
+
+ assert_false(pe_base_name_eq(NULL, "dummy"));
+ assert_false(pe_base_name_eq(dummy, NULL));
+
+ dummy->id = NULL;
+ assert_false(pe_base_name_eq(dummy, "dummy"));
+ dummy->id = id;
+}
+
+static void
+primitive_rsc(void **state) {
+ assert_true(pe_base_name_eq(dummy, "dummy"));
+ assert_false(pe_base_name_eq(dummy, "DUMMY"));
+ assert_false(pe_base_name_eq(dummy, "dUmMy"));
+ assert_false(pe_base_name_eq(dummy, "dummy0"));
+ assert_false(pe_base_name_eq(dummy, "dummy:0"));
+}
+
+static void
+group_rsc(void **state) {
+ assert_true(pe_base_name_eq(exim_group, "exim-group"));
+ assert_false(pe_base_name_eq(exim_group, "EXIM-GROUP"));
+ assert_false(pe_base_name_eq(exim_group, "exim-group0"));
+ assert_false(pe_base_name_eq(exim_group, "exim-group:0"));
+ assert_false(pe_base_name_eq(exim_group, "Public-IP"));
+}
+
+static void
+clone_rsc(void **state) {
+ assert_true(pe_base_name_eq(promotable_0, "promotable-rsc"));
+ assert_true(pe_base_name_eq(promotable_1, "promotable-rsc"));
+
+ assert_false(pe_base_name_eq(promotable_0, "promotable-rsc:0"));
+ assert_false(pe_base_name_eq(promotable_1, "promotable-rsc:1"));
+ assert_false(pe_base_name_eq(promotable_0, "PROMOTABLE-RSC"));
+ assert_false(pe_base_name_eq(promotable_1, "PROMOTABLE-RSC"));
+ assert_false(pe_base_name_eq(promotable_0, "Promotable-rsc"));
+ assert_false(pe_base_name_eq(promotable_1, "Promotable-rsc"));
+}
+
+static void
+bundle_rsc(void **state) {
+ assert_true(pe_base_name_eq(httpd_bundle, "httpd-bundle"));
+ assert_false(pe_base_name_eq(httpd_bundle, "HTTPD-BUNDLE"));
+ assert_false(pe_base_name_eq(httpd_bundle, "httpd"));
+ assert_false(pe_base_name_eq(httpd_bundle, "httpd-docker-0"));
+}
+
+PCMK__UNIT_TEST(setup, teardown,
+ cmocka_unit_test(bad_args),
+ cmocka_unit_test(primitive_rsc),
+ cmocka_unit_test(group_rsc),
+ cmocka_unit_test(clone_rsc),
+ cmocka_unit_test(bundle_rsc))
diff --git a/lib/pengine/tests/rules/Makefile.am b/lib/pengine/tests/rules/Makefile.am
new file mode 100644
index 0000000..261ec16
--- /dev/null
+++ b/lib/pengine/tests/rules/Makefile.am
@@ -0,0 +1,18 @@
+#
+# 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 $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+LDADD += $(top_builddir)/lib/pengine/libpe_rules_test.la
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = pe_cron_range_satisfied_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c b/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c
new file mode 100644
index 0000000..a8ba6cf
--- /dev/null
+++ b/lib/pengine/tests/rules/pe_cron_range_satisfied_test.c
@@ -0,0 +1,165 @@
+/*
+ * 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 <glib.h>
+
+#include <crm/msg_xml.h>
+#include <crm/common/unittest_internal.h>
+#include <crm/common/xml.h>
+#include <crm/pengine/rules_internal.h>
+
+static void
+run_one_test(const char *t, const char *x, int expected) {
+ crm_time_t *tm = crm_time_new(t);
+ xmlNodePtr xml = string2xml(x);
+
+ assert_int_equal(pe_cron_range_satisfied(tm, xml), expected);
+
+ crm_time_free(tm);
+ free_xml(xml);
+}
+
+static void
+no_time_given(void **state) {
+ assert_int_equal(pe_cron_range_satisfied(NULL, NULL), pcmk_rc_op_unsatisfied);
+}
+
+static void
+any_time_satisfies_empty_spec(void **state) {
+ crm_time_t *tm = crm_time_new(NULL);
+
+ assert_int_equal(pe_cron_range_satisfied(tm, NULL), pcmk_rc_ok);
+
+ crm_time_free(tm);
+}
+
+static void
+time_satisfies_year_spec(void **state) {
+ run_one_test("2020-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2020'/>",
+ pcmk_rc_ok);
+}
+
+static void
+time_after_year_spec(void **state) {
+ run_one_test("2020-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2019'/>",
+ pcmk_rc_after_range);
+}
+
+static void
+time_satisfies_year_range(void **state) {
+ run_one_test("2020-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2010-2030'/>",
+ pcmk_rc_ok);
+}
+
+static void
+time_before_year_range(void **state) {
+ run_one_test("2000-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2010-2030'/>",
+ pcmk_rc_before_range);
+}
+
+static void
+time_after_year_range(void **state) {
+ run_one_test("2020-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2010-2015'/>",
+ pcmk_rc_after_range);
+}
+
+static void
+range_without_start_year_passes(void **state) {
+ run_one_test("2010-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='-2020'/>",
+ pcmk_rc_ok);
+}
+
+static void
+range_without_end_year_passes(void **state) {
+ run_one_test("2010-01-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2000-'/>",
+ pcmk_rc_ok);
+ run_one_test("2000-10-01",
+ "<date_spec " XML_ATTR_ID "='spec' years='2000-'/>",
+ pcmk_rc_ok);
+}
+
+static void
+yeardays_satisfies(void **state) {
+ run_one_test("2020-01-30",
+ "<date_spec " XML_ATTR_ID "='spec' yeardays='30'/>",
+ pcmk_rc_ok);
+}
+
+static void
+time_after_yeardays_spec(void **state) {
+ run_one_test("2020-02-15",
+ "<date_spec " XML_ATTR_ID "='spec' yeardays='40'/>",
+ pcmk_rc_after_range);
+}
+
+static void
+yeardays_feb_29_satisfies(void **state) {
+ run_one_test("2016-02-29",
+ "<date_spec " XML_ATTR_ID "='spec' yeardays='60'/>",
+ pcmk_rc_ok);
+}
+
+static void
+exact_ymd_satisfies(void **state) {
+ run_one_test("2001-12-31",
+ "<date_spec " XML_ATTR_ID "='spec' years='2001' months='12' "
+ "monthdays='31'/>",
+ pcmk_rc_ok);
+}
+
+static void
+range_in_month_satisfies(void **state) {
+ run_one_test("2001-06-10",
+ "<date_spec " XML_ATTR_ID "='spec' years='2001' months='6' "
+ "monthdays='1-10'/>",
+ pcmk_rc_ok);
+}
+
+static void
+exact_ymd_after_range(void **state) {
+ run_one_test("2001-12-31",
+ "<date_spec " XML_ATTR_ID "='spec' years='2001' months='12' "
+ "monthdays='30'/>",
+ pcmk_rc_after_range);
+}
+
+static void
+time_after_monthdays_range(void **state) {
+ run_one_test("2001-06-10",
+ "<date_spec " XML_ATTR_ID "='spec' years='2001' months='6' "
+ "monthdays='11-15'/>",
+ pcmk_rc_before_range);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(no_time_given),
+ cmocka_unit_test(any_time_satisfies_empty_spec),
+ cmocka_unit_test(time_satisfies_year_spec),
+ cmocka_unit_test(time_after_year_spec),
+ cmocka_unit_test(time_satisfies_year_range),
+ cmocka_unit_test(time_before_year_range),
+ cmocka_unit_test(time_after_year_range),
+ cmocka_unit_test(range_without_start_year_passes),
+ cmocka_unit_test(range_without_end_year_passes),
+ cmocka_unit_test(yeardays_satisfies),
+ cmocka_unit_test(time_after_yeardays_spec),
+ cmocka_unit_test(yeardays_feb_29_satisfies),
+ cmocka_unit_test(exact_ymd_satisfies),
+ cmocka_unit_test(range_in_month_satisfies),
+ cmocka_unit_test(exact_ymd_after_range),
+ cmocka_unit_test(time_after_monthdays_range))
diff --git a/lib/pengine/tests/status/Makefile.am b/lib/pengine/tests/status/Makefile.am
new file mode 100644
index 0000000..3f95496
--- /dev/null
+++ b/lib/pengine/tests/status/Makefile.am
@@ -0,0 +1,22 @@
+#
+# 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 $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+LDADD += $(top_builddir)/lib/pengine/libpe_status_test.la
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = pe_find_node_any_test \
+ pe_find_node_id_test \
+ pe_find_node_test \
+ pe_new_working_set_test \
+ set_working_set_defaults_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/pengine/tests/status/pe_find_node_any_test.c b/lib/pengine/tests/status/pe_find_node_any_test.c
new file mode 100644
index 0000000..b911424
--- /dev/null
+++ b/lib/pengine/tests/status/pe_find_node_any_test.c
@@ -0,0 +1,62 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+static void
+empty_list(void **state) {
+ assert_null(pe_find_node_any(NULL, NULL, NULL));
+ assert_null(pe_find_node_any(NULL, NULL, "cluster1"));
+ assert_null(pe_find_node_any(NULL, "id1", NULL));
+ assert_null(pe_find_node_any(NULL, "id1", "cluster1"));
+}
+
+static void
+non_null_list(void **state) {
+ GList *nodes = NULL;
+
+ pe_node_t *a = calloc(1, sizeof(pe_node_t));
+ pe_node_t *b = calloc(1, sizeof(pe_node_t));
+
+ a->details = calloc(1, sizeof(struct pe_node_shared_s));
+ a->details->uname = "cluster1";
+ a->details->id = "id1";
+ b->details = calloc(1, sizeof(struct pe_node_shared_s));
+ b->details->uname = "cluster2";
+ b->details->id = "id2";
+
+ nodes = g_list_append(nodes, a);
+ nodes = g_list_append(nodes, b);
+
+ assert_ptr_equal(b, pe_find_node_any(nodes, "id2", NULL));
+ assert_ptr_equal(b, pe_find_node_any(nodes, "ID2", NULL));
+
+ assert_ptr_equal(a, pe_find_node_any(nodes, "xyz", "cluster1"));
+ assert_ptr_equal(a, pe_find_node_any(nodes, NULL, "cluster1"));
+
+ assert_null(pe_find_node_any(nodes, "id10", NULL));
+ assert_null(pe_find_node_any(nodes, "nodeid1", NULL));
+ assert_null(pe_find_node_any(nodes, NULL, "cluster10"));
+ assert_null(pe_find_node_any(nodes, NULL, "nodecluster1"));
+ assert_null(pe_find_node_any(nodes, "id3", "cluster3"));
+ assert_null(pe_find_node_any(nodes, NULL, NULL));
+
+ free(a->details);
+ free(a);
+ free(b->details);
+ free(b);
+ g_list_free(nodes);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_list),
+ cmocka_unit_test(non_null_list))
diff --git a/lib/pengine/tests/status/pe_find_node_id_test.c b/lib/pengine/tests/status/pe_find_node_id_test.c
new file mode 100644
index 0000000..832a40a
--- /dev/null
+++ b/lib/pengine/tests/status/pe_find_node_id_test.c
@@ -0,0 +1,51 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+static void
+empty_list(void **state) {
+ assert_null(pe_find_node_id(NULL, NULL));
+ assert_null(pe_find_node_id(NULL, "id1"));
+}
+
+static void
+non_null_list(void **state) {
+ GList *nodes = NULL;
+
+ pe_node_t *a = calloc(1, sizeof(pe_node_t));
+ pe_node_t *b = calloc(1, sizeof(pe_node_t));
+
+ a->details = calloc(1, sizeof(struct pe_node_shared_s));
+ a->details->id = "id1";
+ b->details = calloc(1, sizeof(struct pe_node_shared_s));
+ b->details->id = "id2";
+
+ nodes = g_list_append(nodes, a);
+ nodes = g_list_append(nodes, b);
+
+ assert_ptr_equal(a, pe_find_node_id(nodes, "id1"));
+ assert_null(pe_find_node_id(nodes, "id10"));
+ assert_null(pe_find_node_id(nodes, "nodeid1"));
+ assert_ptr_equal(b, pe_find_node_id(nodes, "ID2"));
+ assert_null(pe_find_node_id(nodes, "xyz"));
+
+ free(a->details);
+ free(a);
+ free(b->details);
+ free(b);
+ g_list_free(nodes);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_list),
+ cmocka_unit_test(non_null_list))
diff --git a/lib/pengine/tests/status/pe_find_node_test.c b/lib/pengine/tests/status/pe_find_node_test.c
new file mode 100644
index 0000000..7c7ea30
--- /dev/null
+++ b/lib/pengine/tests/status/pe_find_node_test.c
@@ -0,0 +1,51 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+static void
+empty_list(void **state) {
+ assert_null(pe_find_node(NULL, NULL));
+ assert_null(pe_find_node(NULL, "cluster1"));
+}
+
+static void
+non_null_list(void **state) {
+ GList *nodes = NULL;
+
+ pe_node_t *a = calloc(1, sizeof(pe_node_t));
+ pe_node_t *b = calloc(1, sizeof(pe_node_t));
+
+ a->details = calloc(1, sizeof(struct pe_node_shared_s));
+ a->details->uname = "cluster1";
+ b->details = calloc(1, sizeof(struct pe_node_shared_s));
+ b->details->uname = "cluster2";
+
+ nodes = g_list_append(nodes, a);
+ nodes = g_list_append(nodes, b);
+
+ assert_ptr_equal(a, pe_find_node(nodes, "cluster1"));
+ assert_null(pe_find_node(nodes, "cluster10"));
+ assert_null(pe_find_node(nodes, "nodecluster1"));
+ assert_ptr_equal(b, pe_find_node(nodes, "CLUSTER2"));
+ assert_null(pe_find_node(nodes, "xyz"));
+
+ free(a->details);
+ free(a);
+ free(b->details);
+ free(b);
+ g_list_free(nodes);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(empty_list),
+ cmocka_unit_test(non_null_list))
diff --git a/lib/pengine/tests/status/pe_new_working_set_test.c b/lib/pengine/tests/status/pe_new_working_set_test.c
new file mode 100644
index 0000000..cf2df4f
--- /dev/null
+++ b/lib/pengine/tests/status/pe_new_working_set_test.c
@@ -0,0 +1,46 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+#include "mock_private.h"
+
+static void
+calloc_fails(void **state) {
+ pcmk__mock_calloc = true; // calloc() will return NULL
+
+ expect_value(__wrap_calloc, nmemb, 1);
+ expect_value(__wrap_calloc, size, sizeof(pe_working_set_t));
+ assert_null(pe_new_working_set());
+
+ pcmk__mock_calloc = false; // Use real calloc()
+}
+
+static void
+calloc_succeeds(void **state) {
+ pe_working_set_t *data_set = pe_new_working_set();
+
+ /* Nothing else to test about this function, as all it does is call
+ * set_working_set_defaults which is also a public function and should
+ * get its own unit test.
+ */
+ assert_non_null(data_set);
+
+ /* Avoid calling pe_free_working_set here so we don't artificially
+ * inflate the coverage numbers.
+ */
+ free(data_set);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(calloc_fails),
+ cmocka_unit_test(calloc_succeeds))
diff --git a/lib/pengine/tests/status/set_working_set_defaults_test.c b/lib/pengine/tests/status/set_working_set_defaults_test.c
new file mode 100644
index 0000000..c822278
--- /dev/null
+++ b/lib/pengine/tests/status/set_working_set_defaults_test.c
@@ -0,0 +1,46 @@
+/*
+ * 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 <crm/pengine/internal.h>
+#include <crm/pengine/pe_types.h>
+#include <crm/pengine/status.h>
+
+#include "mock_private.h"
+
+static void
+check_defaults(void **state) {
+ uint32_t flags;
+ pe_working_set_t *data_set = calloc(1, sizeof(pe_working_set_t));
+
+ set_working_set_defaults(data_set);
+
+ flags = pe_flag_stop_rsc_orphans|pe_flag_symmetric_cluster|pe_flag_stop_action_orphans;
+
+ if (!strcmp(PCMK__CONCURRENT_FENCING_DEFAULT, "true")) {
+ flags |= pe_flag_concurrent_fencing;
+ }
+
+
+ assert_null(data_set->priv);
+ assert_int_equal(data_set->order_id, 1);
+ assert_int_equal(data_set->action_id, 1);
+ assert_int_equal(data_set->no_quorum_policy, no_quorum_stop);
+ assert_int_equal(data_set->flags, flags);
+
+ /* Avoid calling pe_free_working_set here so we don't artificially
+ * inflate the coverage numbers.
+ */
+ free(data_set);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(check_defaults))
diff --git a/lib/pengine/tests/unpack/Makefile.am b/lib/pengine/tests/unpack/Makefile.am
new file mode 100644
index 0000000..baa8633
--- /dev/null
+++ b/lib/pengine/tests/unpack/Makefile.am
@@ -0,0 +1,18 @@
+#
+# 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 $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+LDADD += $(top_builddir)/lib/pengine/libpe_status_test.la
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = pe_base_name_end_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/pengine/tests/unpack/pe_base_name_end_test.c b/lib/pengine/tests/unpack/pe_base_name_end_test.c
new file mode 100644
index 0000000..6f1c165
--- /dev/null
+++ b/lib/pengine/tests/unpack/pe_base_name_end_test.c
@@ -0,0 +1,36 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+static void
+bad_args(void **state) {
+ assert_null(pe_base_name_end(NULL));
+ assert_null(pe_base_name_end(""));
+}
+
+static void
+no_suffix(void **state) {
+ assert_string_equal(pe_base_name_end("rsc"), "c");
+ assert_string_equal(pe_base_name_end("rsc0"), "0");
+}
+
+static void
+has_suffix(void **state) {
+ assert_string_equal(pe_base_name_end("rsc:0"), "c:0");
+ assert_string_equal(pe_base_name_end("rsc:100"), "c:100");
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(bad_args),
+ cmocka_unit_test(no_suffix),
+ cmocka_unit_test(has_suffix))
diff --git a/lib/pengine/tests/utils/Makefile.am b/lib/pengine/tests/utils/Makefile.am
new file mode 100644
index 0000000..4a3e8a2
--- /dev/null
+++ b/lib/pengine/tests/utils/Makefile.am
@@ -0,0 +1,21 @@
+#
+# 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 $(top_srcdir)/mk/tap.mk
+include $(top_srcdir)/mk/unittest.mk
+
+AM_CPPFLAGS += -I$(top_srcdir)/lib/pengine
+LDADD += $(top_builddir)/lib/pengine/libpe_status_test.la
+
+# Add "_test" to the end of all test program names to simplify .gitignore.
+check_PROGRAMS = \
+ pe__cmp_node_name_test \
+ pe__cmp_rsc_priority_test
+
+TESTS = $(check_PROGRAMS)
diff --git a/lib/pengine/tests/utils/pe__cmp_node_name_test.c b/lib/pengine/tests/utils/pe__cmp_node_name_test.c
new file mode 100644
index 0000000..45d87ee
--- /dev/null
+++ b/lib/pengine/tests/utils/pe__cmp_node_name_test.c
@@ -0,0 +1,55 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+struct pe_node_shared_s node1_details;
+struct pe_node_shared_s node2_details;
+
+pe_node_t node1 = {.details = &node1_details};
+pe_node_t node2 = {.details = &node2_details};
+
+static void
+nodes_equal(void **state)
+{
+ assert_int_equal(pe__cmp_node_name(NULL, NULL), 0);
+
+ node1.details->uname = "node10";
+ node2.details->uname = "node10";
+ assert_int_equal(pe__cmp_node_name(&node1, &node2), 0);
+}
+
+static void
+node1_first(void **state)
+{
+ assert_int_equal(pe__cmp_node_name(NULL, &node2), -1);
+
+ // The heavy testing is done in pcmk__numeric_strcasecmp()'s unit tests
+ node1.details->uname = "node9";
+ node2.details->uname = "node10";
+ assert_int_equal(pe__cmp_node_name(&node1, &node2), -1);
+}
+
+static void
+node2_first(void **state)
+{
+ assert_int_equal(pe__cmp_node_name(&node1, NULL), 1);
+
+ node1.details->uname = "node10";
+ node2.details->uname = "node9";
+ assert_int_equal(pe__cmp_node_name(&node1, &node2), 1);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(nodes_equal),
+ cmocka_unit_test(node1_first),
+ cmocka_unit_test(node2_first))
diff --git a/lib/pengine/tests/utils/pe__cmp_rsc_priority_test.c b/lib/pengine/tests/utils/pe__cmp_rsc_priority_test.c
new file mode 100644
index 0000000..669e7a9
--- /dev/null
+++ b/lib/pengine/tests/utils/pe__cmp_rsc_priority_test.c
@@ -0,0 +1,50 @@
+/*
+ * 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 <crm/pengine/internal.h>
+
+#include "pe_status_private.h"
+
+pe_resource_t rsc1;
+pe_resource_t rsc2;
+
+static void
+rscs_equal(void **state)
+{
+ rsc1.priority = 0;
+ rsc2.priority = 0;
+ assert_int_equal(pe__cmp_rsc_priority(NULL, NULL), 0);
+ assert_int_equal(pe__cmp_rsc_priority(&rsc1, &rsc2), 0);
+}
+
+static void
+rsc1_first(void **state)
+{
+ rsc1.priority = 1;
+ rsc2.priority = 0;
+ assert_int_equal(pe__cmp_rsc_priority(&rsc1, NULL), -1);
+ assert_int_equal(pe__cmp_rsc_priority(&rsc1, &rsc2), -1);
+}
+
+static void
+rsc2_first(void **state)
+{
+ rsc1.priority = 0;
+ rsc2.priority = 1;
+ assert_int_equal(pe__cmp_rsc_priority(NULL, &rsc2), 1);
+ assert_int_equal(pe__cmp_rsc_priority(&rsc1, &rsc2), 1);
+}
+
+PCMK__UNIT_TEST(NULL, NULL,
+ cmocka_unit_test(rscs_equal),
+ cmocka_unit_test(rsc1_first),
+ cmocka_unit_test(rsc2_first))