diff options
Diffstat (limited to 'lib/common/tests')
89 files changed, 7396 insertions, 828 deletions
diff --git a/lib/common/tests/Makefile.am b/lib/common/tests/Makefile.am index c0407e5..bde6605 100644 --- a/lib/common/tests/Makefile.am +++ b/lib/common/tests/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -17,10 +17,16 @@ SUBDIRS = \ io \ iso8601 \ lists \ + nodes \ nvpair \ options \ output \ + probes \ + resources \ results \ + rules \ + scheduler \ + schemas \ scores \ strings \ utils \ diff --git a/lib/common/tests/acl/xml_acl_denied_test.c b/lib/common/tests/acl/xml_acl_denied_test.c index faf2a39..7c5457e 100644 --- a/lib/common/tests/acl/xml_acl_denied_test.c +++ b/lib/common/tests/acl/xml_acl_denied_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ static void is_xml_acl_denied_without_node(void **state) { - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); assert_false(xml_acl_denied(test_xml)); test_xml->doc->_private = NULL; @@ -35,10 +35,10 @@ is_xml_acl_denied_with_node(void **state) { xml_doc_private_t *docpriv; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); // allocate memory for _private, which is NULL by default - test_xml->doc->_private = calloc(1, sizeof(xml_doc_private_t)); + test_xml->doc->_private = pcmk__assert_alloc(1, sizeof(xml_doc_private_t)); assert_false(xml_acl_denied(test_xml)); @@ -56,6 +56,6 @@ is_xml_acl_denied_with_node(void **state) assert_true(xml_acl_denied(test_xml)); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(is_xml_acl_denied_without_node), cmocka_unit_test(is_xml_acl_denied_with_node)) diff --git a/lib/common/tests/acl/xml_acl_enabled_test.c b/lib/common/tests/acl/xml_acl_enabled_test.c index 28665f4..97e361e 100644 --- a/lib/common/tests/acl/xml_acl_enabled_test.c +++ b/lib/common/tests/acl/xml_acl_enabled_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -17,7 +17,7 @@ static void is_xml_acl_enabled_without_node(void **state) { - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); assert_false(xml_acl_enabled(test_xml)); test_xml->doc->_private = NULL; @@ -35,10 +35,10 @@ is_xml_acl_enabled_with_node(void **state) { xml_doc_private_t *docpriv; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); // allocate memory for _private, which is NULL by default - test_xml->doc->_private = calloc(1, sizeof(xml_doc_private_t)); + test_xml->doc->_private = pcmk__assert_alloc(1, sizeof(xml_doc_private_t)); assert_false(xml_acl_enabled(test_xml)); @@ -56,6 +56,6 @@ is_xml_acl_enabled_with_node(void **state) assert_true(xml_acl_enabled(test_xml)); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(is_xml_acl_enabled_without_node), cmocka_unit_test(is_xml_acl_enabled_with_node)) diff --git a/lib/common/tests/actions/Makefile.am b/lib/common/tests/actions/Makefile.am index 6890b84..0dfe521 100644 --- a/lib/common/tests/actions/Makefile.am +++ b/lib/common/tests/actions/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,12 +11,6 @@ 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 = copy_in_properties_test \ - expand_plus_plus_test \ - fix_plus_plus_recursive_test \ - parse_op_key_test \ - pcmk_is_probe_test \ - pcmk_xe_is_probe_test \ - pcmk_xe_mask_probe_failure_test +check_PROGRAMS = parse_op_key_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/actions/copy_in_properties_test.c b/lib/common/tests/actions/copy_in_properties_test.c deleted file mode 100644 index 7882551..0000000 --- a/lib/common/tests/actions/copy_in_properties_test.c +++ /dev/null @@ -1,62 +0,0 @@ - /* - * 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 -target_is_NULL(void **state) -{ - xmlNode *test_xml_1 = create_xml_node(NULL, "test_xml_1"); - xmlNode *test_xml_2 = NULL; - - pcmk__xe_set_props(test_xml_1, "test_prop", "test_value", NULL); - - copy_in_properties(test_xml_2, test_xml_1); - - assert_ptr_equal(test_xml_2, NULL); -} - -static void -src_is_NULL(void **state) -{ - xmlNode *test_xml_1 = NULL; - xmlNode *test_xml_2 = create_xml_node(NULL, "test_xml_2"); - - copy_in_properties(test_xml_2, test_xml_1); - - assert_ptr_equal(test_xml_2->properties, NULL); -} - -static void -copying_is_successful(void **state) -{ - const char *xml_1_value; - const char *xml_2_value; - - xmlNode *test_xml_1 = create_xml_node(NULL, "test_xml_1"); - xmlNode *test_xml_2 = create_xml_node(NULL, "test_xml_2"); - - pcmk__xe_set_props(test_xml_1, "test_prop", "test_value", NULL); - - copy_in_properties(test_xml_2, test_xml_1); - - xml_1_value = crm_element_value(test_xml_1, "test_prop"); - xml_2_value = crm_element_value(test_xml_2, "test_prop"); - - assert_string_equal(xml_1_value, xml_2_value); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(target_is_NULL), - cmocka_unit_test(src_is_NULL), - cmocka_unit_test(copying_is_successful)) diff --git a/lib/common/tests/actions/expand_plus_plus_test.c b/lib/common/tests/actions/expand_plus_plus_test.c deleted file mode 100644 index 41471f9..0000000 --- a/lib/common/tests/actions/expand_plus_plus_test.c +++ /dev/null @@ -1,256 +0,0 @@ -/* - * 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 -value_is_name_plus_plus(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_name_plus_equals_integer(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "7"); -} - -// NULL input - -static void -target_is_NULL(void **state) -{ - - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(NULL, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -name_is_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, NULL, "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -value_is_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", NULL); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -// the value input doesn't start with the name input - -static void -value_is_wrong_name(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "Y++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "Y++"); -} - -static void -value_is_only_an_integer(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "2"); -} - -// non-integers - -static void -variable_is_initialized_to_be_NULL(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", NULL); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "X++"); -} - -static void -variable_is_initialized_to_be_non_numeric(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "hello"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "1"); -} - -static void -variable_is_initialized_to_be_non_numeric_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "hello"); - expand_plus_plus(test_xml, "X", "X+=2"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "2"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.01"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.50"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -variable_is_initialized_to_be_numeric_and_decimal_point_containing_3(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5.99"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_non_numeric(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=hello"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "5"); -} - -static void -value_is_numeric_and_decimal_point_containing(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=2.01"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "7"); -} - -static void -value_is_numeric_and_decimal_point_containing_2(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=1.50"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -static void -value_is_numeric_and_decimal_point_containing_3(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=1.99"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "6"); -} - -// undefined input - -static void -name_is_undefined(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "Y", "5"); - expand_plus_plus(test_xml, "X", "X++"); - new_value = crm_element_value(test_xml, "X"); - assert_string_equal(new_value, "X++"); -} - -// large input - -static void -assignment_result_is_too_large(void **state) -{ - const char *new_value; - xmlNode *test_xml = create_xml_node(NULL, "test_xml"); - crm_xml_add(test_xml, "X", "5"); - expand_plus_plus(test_xml, "X", "X+=100000000000"); - new_value = crm_element_value(test_xml, "X"); - printf("assignment result is too large %s\n", new_value); - assert_string_equal(new_value, "1000000"); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(value_is_name_plus_plus), - cmocka_unit_test(value_is_name_plus_equals_integer), - cmocka_unit_test(target_is_NULL), - cmocka_unit_test(name_is_NULL), - cmocka_unit_test(value_is_NULL), - cmocka_unit_test(value_is_wrong_name), - cmocka_unit_test(value_is_only_an_integer), - cmocka_unit_test(variable_is_initialized_to_be_NULL), - cmocka_unit_test(variable_is_initialized_to_be_non_numeric), - cmocka_unit_test(variable_is_initialized_to_be_non_numeric_2), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_2), - cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_3), - cmocka_unit_test(value_is_non_numeric), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing_2), - cmocka_unit_test(value_is_numeric_and_decimal_point_containing_3), - cmocka_unit_test(name_is_undefined), - cmocka_unit_test(assignment_result_is_too_large)) diff --git a/lib/common/tests/actions/fix_plus_plus_recursive_test.c b/lib/common/tests/actions/fix_plus_plus_recursive_test.c deleted file mode 100644 index b3c7cc2..0000000 --- a/lib/common/tests/actions/fix_plus_plus_recursive_test.c +++ /dev/null @@ -1,47 +0,0 @@ -/* - * 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 -element_nodes(void **state) -{ - const char *new_value_root; - const char *new_value_child; - const char *new_value_grandchild; - - xmlNode *test_xml_root = create_xml_node(NULL, "test_xml_root"); - xmlNode *test_xml_child = create_xml_node(test_xml_root, "test_xml_child"); - xmlNode *test_xml_grandchild = create_xml_node(test_xml_child, "test_xml_grandchild"); - xmlNode *test_xml_text = pcmk_create_xml_text_node(test_xml_root, "text_xml_text", "content"); - xmlNode *test_xml_comment = string2xml("<!-- a comment -->"); - - crm_xml_add(test_xml_root, "X", "5"); - crm_xml_add(test_xml_child, "X", "X++"); - crm_xml_add(test_xml_grandchild, "X", "X+=2"); - crm_xml_add(test_xml_text, "X", "X++"); - - fix_plus_plus_recursive(test_xml_root); - fix_plus_plus_recursive(test_xml_comment); - - new_value_root = crm_element_value(test_xml_root, "X"); - new_value_child = crm_element_value(test_xml_child, "X"); - new_value_grandchild = crm_element_value(test_xml_grandchild, "X"); - - assert_string_equal(new_value_root, "5"); - assert_string_equal(new_value_child, "1"); - assert_string_equal(new_value_grandchild, "2"); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(element_nodes)) diff --git a/lib/common/tests/actions/pcmk_xe_is_probe_test.c b/lib/common/tests/actions/pcmk_xe_is_probe_test.c deleted file mode 100644 index 62b21d9..0000000 --- a/lib/common/tests/actions/pcmk_xe_is_probe_test.c +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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 -op_is_probe_test(void **state) -{ - xmlNode *node = NULL; - - assert_false(pcmk_xe_is_probe(NULL)); - - node = string2xml("<lrm_rsc_op/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation_key=\"blah\" interval=\"30s\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"30s\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"start\" interval=\"0\"/>"); - assert_false(pcmk_xe_is_probe(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\"/>"); - assert_true(pcmk_xe_is_probe(node)); - free_xml(node); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(op_is_probe_test)) diff --git a/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c b/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c deleted file mode 100644 index 9e38019..0000000 --- a/lib/common/tests/actions/pcmk_xe_mask_probe_failure_test.c +++ /dev/null @@ -1,150 +0,0 @@ -/* - * 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 -op_is_not_probe_test(void **state) { - xmlNode *node = NULL; - - /* Not worth testing this thoroughly since it's just a duplicate of whether - * pcmk_op_is_probe works or not. - */ - - node = string2xml("<lrm_rsc_op operation=\"start\" interval=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -static void -op_does_not_have_right_values_test(void **state) { - xmlNode *node = NULL; - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -static void -check_values_test(void **state) { - xmlNode *node = NULL; - - /* PCMK_EXEC_NOT_SUPPORTED */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"3\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"3\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_DONE */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"0\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"0\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"0\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_NOT_INSTALLED */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"7\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"7\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"4\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"4\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"4\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR_HARD */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"5\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"5\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"5\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - /* PCMK_EXEC_ERROR_FATAL */ - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"0\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"2\" op-status=\"6\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"5\" op-status=\"6\"/>"); - assert_true(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"6\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); - - node = string2xml("<lrm_rsc_op operation=\"monitor\" interval=\"0\" rc-code=\"7\" op-status=\"6\"/>"); - assert_false(pcmk_xe_mask_probe_failure(node)); - free_xml(node); -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(op_is_not_probe_test), - cmocka_unit_test(op_does_not_have_right_values_test), - cmocka_unit_test(check_values_test)) diff --git a/lib/common/tests/health/pcmk__parse_health_strategy_test.c b/lib/common/tests/health/pcmk__parse_health_strategy_test.c index 28cc702..197ad1f 100644 --- a/lib/common/tests/health/pcmk__parse_health_strategy_test.c +++ b/lib/common/tests/health/pcmk__parse_health_strategy_test.c @@ -16,7 +16,7 @@ valid(void **state) { assert_int_equal(pcmk__parse_health_strategy(NULL), pcmk__health_strategy_none); - assert_int_equal(pcmk__parse_health_strategy("none"), + assert_int_equal(pcmk__parse_health_strategy(PCMK_VALUE_NONE), pcmk__health_strategy_none); assert_int_equal(pcmk__parse_health_strategy("NONE"), diff --git a/lib/common/tests/health/pcmk__validate_health_strategy_test.c b/lib/common/tests/health/pcmk__validate_health_strategy_test.c index c7c60aa..e706185 100644 --- a/lib/common/tests/health/pcmk__validate_health_strategy_test.c +++ b/lib/common/tests/health/pcmk__validate_health_strategy_test.c @@ -15,7 +15,7 @@ static void valid_strategy(void **state) { - assert_true(pcmk__validate_health_strategy("none")); + assert_true(pcmk__validate_health_strategy(PCMK_VALUE_NONE)); assert_true(pcmk__validate_health_strategy("None")); assert_true(pcmk__validate_health_strategy("NONE")); assert_true(pcmk__validate_health_strategy("NoNe")); diff --git a/lib/common/tests/io/pcmk__full_path_test.c b/lib/common/tests/io/pcmk__full_path_test.c index dbbd71b..2f514aa 100644 --- a/lib/common/tests/io/pcmk__full_path_test.c +++ b/lib/common/tests/io/pcmk__full_path_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the Pacemaker project contributors + * Copyright 2020-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -18,8 +18,13 @@ function_asserts(void **state) { pcmk__assert_asserts(pcmk__full_path(NULL, "/dir")); pcmk__assert_asserts(pcmk__full_path("file", NULL)); +} - pcmk__assert_asserts( +static void +function_exits(void **state) +{ + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_strdup = true; // strdup() will return NULL expect_string(__wrap_strdup, s, "/full/path"); @@ -49,4 +54,5 @@ full_path(void **state) PCMK__UNIT_TEST(NULL, NULL, cmocka_unit_test(function_asserts), + cmocka_unit_test(function_exits), cmocka_unit_test(full_path)) diff --git a/lib/common/tests/iso8601/Makefile.am b/lib/common/tests/iso8601/Makefile.am index 5187aec..581115a 100644 --- a/lib/common/tests/iso8601/Makefile.am +++ b/lib/common/tests/iso8601/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2022 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,6 +11,8 @@ 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 = pcmk__readable_interval_test +check_PROGRAMS = pcmk__add_time_from_xml_test \ + pcmk__readable_interval_test \ + pcmk__set_time_if_earlier_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c b/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c new file mode 100644 index 0000000..60a71c0 --- /dev/null +++ b/lib/common/tests/iso8601/pcmk__add_time_from_xml_test.c @@ -0,0 +1,243 @@ +/* + * Copyright 2024 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 <libxml/tree.h> // xmlNode + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/iso8601_internal.h> +#include <crm/common/xml.h> +#include "../../crmcommon_private.h" + +#define ALL_VALID "<duration id=\"duration1\" years=\"1\" months=\"2\" " \ + "weeks=\"3\" days=\"-1\" hours=\"1\" minutes=\"1\" " \ + "seconds=\"1\" />" + +#define YEARS_INVALID "<duration id=\"duration1\" years=\"not-a-number\" />" + +#define YEARS_TOO_BIG "<duration id=\"duration1\" years=\"2222222222\" />" + +#define YEARS_TOO_SMALL "<duration id=\"duration1\" years=\"-2222222222\" />" + +static void +null_time_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(NULL, pcmk__time_years, xml), + EINVAL); + free_xml(xml); +} + +static void +null_xml_ok(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, NULL), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); +} + +static void +invalid_component(void **state) +{ + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(NULL, pcmk__time_unknown, xml), + EINVAL); + free_xml(xml); +} + +static void +missing_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = pcmk__xml_parse(YEARS_INVALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_months, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +invalid_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = pcmk__xml_parse(YEARS_INVALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), + pcmk_rc_unpack_error); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +out_of_range_attr(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = pcmk_copy_time(t); + xmlNode *xml = NULL; + + xml = pcmk__xml_parse(YEARS_TOO_BIG); + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), ERANGE); + assert_int_equal(crm_time_compare(t, reference), 0); + free_xml(xml); + + xml = pcmk__xml_parse(YEARS_TOO_SMALL); + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), ERANGE); + assert_int_equal(crm_time_compare(t, reference), 0); + free_xml(xml); + + crm_time_free(t); + crm_time_free(reference); +} + +static void +add_years(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2025-01-01 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_years, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_months(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-03-01 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_months, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_weeks(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-22 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_weeks, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_days(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2023-12-31 15:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_days, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_hours(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 16:00:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_hours, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_minutes(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 15:01:00"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_minutes, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +add_seconds(void **state) +{ + crm_time_t *t = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *reference = crm_time_new("2024-01-01 15:00:01"); + xmlNode *xml = pcmk__xml_parse(ALL_VALID); + + assert_int_equal(pcmk__add_time_from_xml(t, pcmk__time_seconds, xml), + pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_time_invalid), + cmocka_unit_test(null_xml_ok), + cmocka_unit_test(invalid_component), + cmocka_unit_test(missing_attr), + cmocka_unit_test(invalid_attr), + cmocka_unit_test(out_of_range_attr), + cmocka_unit_test(add_years), + cmocka_unit_test(add_months), + cmocka_unit_test(add_weeks), + cmocka_unit_test(add_days), + cmocka_unit_test(add_hours), + cmocka_unit_test(add_minutes), + cmocka_unit_test(add_seconds)); diff --git a/lib/common/tests/iso8601/pcmk__readable_interval_test.c b/lib/common/tests/iso8601/pcmk__readable_interval_test.c index 43b5541..d354975 100644 --- a/lib/common/tests/iso8601/pcmk__readable_interval_test.c +++ b/lib/common/tests/iso8601/pcmk__readable_interval_test.c @@ -17,9 +17,11 @@ static void readable_interval(void **state) { assert_string_equal(pcmk__readable_interval(0), "0s"); + assert_string_equal(pcmk__readable_interval(503), "503ms"); + assert_string_equal(pcmk__readable_interval(3333), "3.333s"); assert_string_equal(pcmk__readable_interval(30000), "30s"); + assert_string_equal(pcmk__readable_interval(61000), "1m1s"); assert_string_equal(pcmk__readable_interval(150000), "2m30s"); - assert_string_equal(pcmk__readable_interval(3333), "3.333s"); assert_string_equal(pcmk__readable_interval(UINT_MAX), "49d17h2m47.295s"); } diff --git a/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c b/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c new file mode 100644 index 0000000..c4bf014 --- /dev/null +++ b/lib/common/tests/iso8601/pcmk__set_time_if_earlier_test.c @@ -0,0 +1,80 @@ +/* + * Copyright 2024 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/iso8601.h> +#include "../../crmcommon_private.h" + +static void +null_ok(void **state) +{ + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + crm_time_t *target_copy = pcmk_copy_time(target); + + // Should do nothing (just checking it doesn't assert or crash) + pcmk__set_time_if_earlier(NULL, NULL); + pcmk__set_time_if_earlier(NULL, target); + + // Shouldn't assert, crash, or change target + pcmk__set_time_if_earlier(target, NULL); + assert_int_equal(crm_time_compare(target, target_copy), 0); + + crm_time_free(target); + crm_time_free(target_copy); +} + +static void +target_undefined(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:29:59 +01:00"); + crm_time_t *target = crm_time_new_undefined(); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, source), 0); + + crm_time_free(source); + crm_time_free(target); +} + +static void +source_earlier(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:29:59 +01:00"); + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, source), 0); + + crm_time_free(source); + crm_time_free(target); +} + +static void +source_later(void **state) +{ + crm_time_t *source = crm_time_new("2024-01-01 00:31:00 +01:00"); + crm_time_t *target = crm_time_new("2024-01-01 00:30:00 +01:00"); + crm_time_t *target_copy = pcmk_copy_time(target); + + pcmk__set_time_if_earlier(target, source); + assert_int_equal(crm_time_compare(target, target_copy), 0); + + crm_time_free(source); + crm_time_free(target); + crm_time_free(target_copy); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_ok), + cmocka_unit_test(target_undefined), + cmocka_unit_test(source_earlier), + cmocka_unit_test(source_later)) diff --git a/lib/common/tests/nodes/Makefile.am b/lib/common/tests/nodes/Makefile.am new file mode 100644 index 0000000..f52c615 --- /dev/null +++ b/lib/common/tests/nodes/Makefile.am @@ -0,0 +1,23 @@ +# +# Copyright 2024 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 = pcmk__find_node_in_list_test \ + pcmk_foreach_active_resource_test \ + pcmk_node_is_clean_test \ + pcmk_node_is_in_maintenance_test \ + pcmk_node_is_online_test \ + pcmk_node_is_pending_test \ + pcmk_node_is_shutting_down_test \ + pcmk__xe_add_node_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/nodes/pcmk__find_node_in_list_test.c b/lib/common/tests/nodes/pcmk__find_node_in_list_test.c new file mode 100644 index 0000000..7726bf5 --- /dev/null +++ b/lib/common/tests/nodes/pcmk__find_node_in_list_test.c @@ -0,0 +1,53 @@ +/* + * Copyright 2022-2024 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(pcmk__find_node_in_list(NULL, NULL)); + assert_null(pcmk__find_node_in_list(NULL, "cluster1")); +} + +static void +non_null_list(void **state) +{ + GList *nodes = NULL; + + pcmk_node_t *a = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + pcmk_node_t *b = pcmk__assert_alloc(1, sizeof(pcmk_node_t)); + + a->details = pcmk__assert_alloc(1, sizeof(struct pe_node_shared_s)); + a->details->uname = "cluster1"; + b->details = pcmk__assert_alloc(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, pcmk__find_node_in_list(nodes, "cluster1")); + assert_null(pcmk__find_node_in_list(nodes, "cluster10")); + assert_null(pcmk__find_node_in_list(nodes, "nodecluster1")); + assert_ptr_equal(b, pcmk__find_node_in_list(nodes, "CLUSTER2")); + assert_null(pcmk__find_node_in_list(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/common/tests/nodes/pcmk__xe_add_node_test.c b/lib/common/tests/nodes/pcmk__xe_add_node_test.c new file mode 100644 index 0000000..dd77527 --- /dev/null +++ b/lib/common/tests/nodes/pcmk__xe_add_node_test.c @@ -0,0 +1,71 @@ +/* + * Copyright 2024 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/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +static void +bad_input(void **state) { + xmlNode *node = NULL; + + pcmk__assert_asserts(pcmk__xe_add_node(NULL, NULL, 0)); + + node = pcmk__xe_create(NULL, "test"); + + pcmk__xe_add_node(node, NULL, 0); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST)); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST_ID)); + + pcmk__xe_add_node(node, NULL, -100); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST)); + assert_null(xmlHasProp(node, (pcmkXmlStr) PCMK__XA_ATTR_HOST_ID)); + + free_xml(node); +} + +static void +expected_input(void **state) { + xmlNode *node = pcmk__xe_create(NULL, "test"); + int i; + + pcmk__xe_add_node(node, "somenode", 47); + assert_string_equal("somenode", + crm_element_value(node, PCMK__XA_ATTR_HOST)); + assert_int_equal(pcmk_rc_ok, + crm_element_value_int(node, PCMK__XA_ATTR_HOST_ID, &i)); + assert_int_equal(i, 47); + + free_xml(node); +} + +static void +repeated_use(void **state) { + xmlNode *node = pcmk__xe_create(NULL, "test"); + int i; + + /* Later calls override settings from earlier calls. */ + pcmk__xe_add_node(node, "nodeA", 1); + pcmk__xe_add_node(node, "nodeB", 2); + pcmk__xe_add_node(node, "nodeC", 3); + + assert_string_equal("nodeC", crm_element_value(node, PCMK__XA_ATTR_HOST)); + assert_int_equal(pcmk_rc_ok, + crm_element_value_int(node, PCMK__XA_ATTR_HOST_ID, &i)); + assert_int_equal(i, 3); + + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(bad_input), + cmocka_unit_test(expected_input), + cmocka_unit_test(repeated_use)) diff --git a/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c b/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c new file mode 100644 index 0000000..2402789 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_foreach_active_resource_test.c @@ -0,0 +1,149 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // GList, TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static int counter = 1; +static int return_false = -1; + +static char rsc1_id[] = "rsc1"; +static char rsc2_id[] = "rsc2"; +static char rsc3_id[] = "rsc3"; + +static pcmk_resource_t rsc1 = { + .id = rsc1_id, +}; +static pcmk_resource_t rsc2 = { + .id = rsc2_id, +}; +static pcmk_resource_t rsc3 = { + .id = rsc3_id, +}; + +static bool +fn(pcmk_resource_t *rsc, void *user_data) +{ + char *expected_id = crm_strdup_printf("rsc%d", counter); + + assert_string_equal(rsc->id, expected_id); + free(expected_id); + + return counter++ != return_false; +} + +static void +null_args(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + counter = 1; + + // These just test that it doesn't crash + pcmk_foreach_active_resource(NULL, NULL, NULL); + pcmk_foreach_active_resource(&node, NULL, NULL); + + pcmk_foreach_active_resource(NULL, fn, NULL); + assert_int_equal(counter, 1); +} + +static void +list_of_0(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 1); +} + +static void +list_of_1(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 2); + + g_list_free(shared.running_rsc); +} + +static void +list_of_3(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc2); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc3); + + counter = 1; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 4); + + g_list_free(shared.running_rsc); +} + +static void +list_of_3_return_false(void **state) +{ + struct pe_node_shared_s shared = { + .running_rsc = NULL, + }; + pcmk_node_t node = { + .details = &shared, + }; + + shared.running_rsc = g_list_append(shared.running_rsc, &rsc1); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc2); + shared.running_rsc = g_list_append(shared.running_rsc, &rsc3); + + counter = 1; + return_false = 2; + pcmk_foreach_active_resource(&node, fn, NULL); + assert_int_equal(counter, 3); + + g_list_free(shared.running_rsc); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_args), + cmocka_unit_test(list_of_0), + cmocka_unit_test(list_of_1), + cmocka_unit_test(list_of_3), + cmocka_unit_test(list_of_3_return_false)) diff --git a/lib/common/tests/nodes/pcmk_node_is_clean_test.c b/lib/common/tests/nodes/pcmk_node_is_clean_test.c new file mode 100644 index 0000000..0534633 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_clean_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_unclean(void **state) +{ + assert_false(pcmk_node_is_clean(NULL)); +} + +static void +node_is_clean(void **state) +{ + struct pe_node_shared_s shared = { + .unclean = FALSE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_clean(&node)); +} + +static void +node_is_unclean(void **state) +{ + struct pe_node_shared_s shared = { + .unclean = TRUE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_clean(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_unclean), + cmocka_unit_test(node_is_clean), + cmocka_unit_test(node_is_unclean)) diff --git a/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c b/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c new file mode 100644 index 0000000..45a3b6f --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_in_maintenance_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_in_maintenance(void **state) +{ + assert_false(pcmk_node_is_in_maintenance(NULL)); +} + +static void +node_is_in_maintenance(void **state) +{ + struct pe_node_shared_s shared = { + .maintenance = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_in_maintenance(&node)); +} + +static void +node_is_not_in_maintenance(void **state) +{ + struct pe_node_shared_s shared = { + .maintenance = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_in_maintenance(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_in_maintenance), + cmocka_unit_test(node_is_in_maintenance), + cmocka_unit_test(node_is_not_in_maintenance)) diff --git a/lib/common/tests/nodes/pcmk_node_is_online_test.c b/lib/common/tests/nodes/pcmk_node_is_online_test.c new file mode 100644 index 0000000..d22e3b4 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_online_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_offline(void **state) +{ + assert_false(pcmk_node_is_online(NULL)); +} + +static void +node_is_online(void **state) +{ + struct pe_node_shared_s shared = { + .online = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_online(&node)); +} + +static void +node_is_offline(void **state) +{ + struct pe_node_shared_s shared = { + .online = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_online(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_offline), + cmocka_unit_test(node_is_online), + cmocka_unit_test(node_is_offline)) diff --git a/lib/common/tests/nodes/pcmk_node_is_pending_test.c b/lib/common/tests/nodes/pcmk_node_is_pending_test.c new file mode 100644 index 0000000..9f2abca --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_pending_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_pending(void **state) +{ + assert_false(pcmk_node_is_pending(NULL)); +} + +static void +node_is_pending(void **state) +{ + struct pe_node_shared_s shared = { + .pending = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_pending(&node)); +} + +static void +node_is_not_pending(void **state) +{ + struct pe_node_shared_s shared = { + .pending = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_pending(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_pending), + cmocka_unit_test(node_is_pending), + cmocka_unit_test(node_is_not_pending)) diff --git a/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c b/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c new file mode 100644 index 0000000..b6054b0 --- /dev/null +++ b/lib/common/tests/nodes/pcmk_node_is_shutting_down_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL +#include <glib.h> // TRUE, FALSE + +#include <crm/common/nodes.h> +#include <crm/common/unittest_internal.h> + +static void +null_is_not_shutting_down(void **state) +{ + assert_false(pcmk_node_is_shutting_down(NULL)); +} + +static void +node_is_shutting_down(void **state) +{ + struct pe_node_shared_s shared = { + .shutdown = TRUE, + }; + + pcmk_node_t node = { + .details = &shared, + }; + + assert_true(pcmk_node_is_shutting_down(&node)); +} + +static void +node_is_not_shutting_down(void **state) +{ + struct pe_node_shared_s shared = { + .shutdown = FALSE, + }; + pcmk_node_t node = { + .details = &shared, + }; + + assert_false(pcmk_node_is_shutting_down(&node)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_is_not_shutting_down), + cmocka_unit_test(node_is_shutting_down), + cmocka_unit_test(node_is_not_shutting_down)) diff --git a/lib/common/tests/nvpair/Makefile.am b/lib/common/tests/nvpair/Makefile.am index 7f406bd..9f762d4 100644 --- a/lib/common/tests/nvpair/Makefile.am +++ b/lib/common/tests/nvpair/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2021-2023 the Pacemaker project contributors +# Copyright 2021-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,7 +11,10 @@ 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 = pcmk__xe_attr_is_true_test \ +check_PROGRAMS = crm_meta_name_test \ + crm_meta_value_test \ + pcmk__xe_attr_is_true_test \ + pcmk__xe_get_datetime_test \ pcmk__xe_get_bool_attr_test \ pcmk__xe_set_bool_attr_test diff --git a/lib/common/tests/utils/crm_meta_name_test.c b/lib/common/tests/nvpair/crm_meta_name_test.c index 06fecc5..7c6c32b 100644 --- a/lib/common/tests/utils/crm_meta_name_test.c +++ b/lib/common/tests/nvpair/crm_meta_name_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,12 +10,12 @@ #include <crm_internal.h> #include <crm/common/unittest_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> static void empty_params(void **state) { - assert_null(crm_meta_name(NULL)); + pcmk__assert_asserts(crm_meta_name(NULL)); } static void @@ -23,11 +23,11 @@ standard_usage(void **state) { char *s = NULL; - s = crm_meta_name(XML_RSC_ATTR_NOTIFY); + s = crm_meta_name(PCMK_META_NOTIFY); assert_string_equal(s, "CRM_meta_notify"); free(s); - s = crm_meta_name(XML_RSC_ATTR_STICKINESS); + s = crm_meta_name(PCMK_META_RESOURCE_STICKINESS); assert_string_equal(s, "CRM_meta_resource_stickiness"); free(s); diff --git a/lib/common/tests/utils/crm_meta_value_test.c b/lib/common/tests/nvpair/crm_meta_value_test.c index 0bde5c6..ffe9619 100644 --- a/lib/common/tests/utils/crm_meta_value_test.c +++ b/lib/common/tests/nvpair/crm_meta_value_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -10,7 +10,7 @@ #include <crm_internal.h> #include <crm/common/unittest_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <glib.h> @@ -30,8 +30,8 @@ key_not_in_table(void **state) { GHashTable *tbl = pcmk__strkey_table(free, free); - assert_null(crm_meta_value(tbl, XML_RSC_ATTR_NOTIFY)); - assert_null(crm_meta_value(tbl, XML_RSC_ATTR_STICKINESS)); + assert_null(crm_meta_value(tbl, PCMK_META_NOTIFY)); + assert_null(crm_meta_value(tbl, PCMK_META_RESOURCE_STICKINESS)); g_hash_table_destroy(tbl); } @@ -41,11 +41,13 @@ key_in_table(void **state) { GHashTable *tbl = pcmk__strkey_table(free, free); - g_hash_table_insert(tbl, crm_meta_name(XML_RSC_ATTR_NOTIFY), strdup("1")); - g_hash_table_insert(tbl, crm_meta_name(XML_RSC_ATTR_STICKINESS), strdup("2")); + g_hash_table_insert(tbl, crm_meta_name(PCMK_META_NOTIFY), strdup("1")); + g_hash_table_insert(tbl, crm_meta_name(PCMK_META_RESOURCE_STICKINESS), + strdup("2")); - assert_string_equal(crm_meta_value(tbl, XML_RSC_ATTR_NOTIFY), "1"); - assert_string_equal(crm_meta_value(tbl, XML_RSC_ATTR_STICKINESS), "2"); + assert_string_equal(crm_meta_value(tbl, PCMK_META_NOTIFY), "1"); + assert_string_equal(crm_meta_value(tbl, PCMK_META_RESOURCE_STICKINESS), + "2"); g_hash_table_destroy(tbl); } diff --git a/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c b/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c index 3723707..84187da 100644 --- a/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_attr_is_true_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ static void empty_input(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); assert_false(pcmk__xe_attr_is_true(NULL, NULL)); assert_false(pcmk__xe_attr_is_true(NULL, "whatever")); @@ -27,7 +27,7 @@ empty_input(void **state) static void attr_missing(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); assert_false(pcmk__xe_attr_is_true(node, "c")); free_xml(node); @@ -36,7 +36,7 @@ attr_missing(void **state) static void attr_present(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); assert_true(pcmk__xe_attr_is_true(node, "a")); assert_false(pcmk__xe_attr_is_true(node, "b")); @@ -44,7 +44,7 @@ attr_present(void **state) free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(empty_input), cmocka_unit_test(attr_missing), cmocka_unit_test(attr_present)) diff --git a/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c b/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c index 500d8a6..4823f6a 100644 --- a/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_get_bool_attr_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2023 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -15,7 +15,7 @@ static void empty_input(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); bool value; assert_int_equal(pcmk__xe_get_bool_attr(NULL, NULL, &value), ENODATA); @@ -29,7 +29,7 @@ empty_input(void **state) static void attr_missing(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\"/>"); bool value; assert_int_equal(pcmk__xe_get_bool_attr(node, "c", &value), ENODATA); @@ -39,7 +39,8 @@ attr_missing(void **state) static void attr_present(void **state) { - xmlNode *node = string2xml("<node a=\"true\" b=\"false\" c=\"blah\"/>"); + xmlNode *node = pcmk__xml_parse("<node a=\"true\" b=\"false\" " + "c=\"blah\"/>"); bool value; value = false; @@ -53,7 +54,7 @@ attr_present(void **state) free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(empty_input), cmocka_unit_test(attr_missing), cmocka_unit_test(attr_present)) diff --git a/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c b/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c new file mode 100644 index 0000000..6da1e23 --- /dev/null +++ b/lib/common/tests/nvpair/pcmk__xe_get_datetime_test.c @@ -0,0 +1,108 @@ +/* + * Copyright 2024 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 <errno.h> +#include <libxml/tree.h> + +#include <crm/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/xml.h> +#include <crm/common/nvpair_internal.h> + +#define REFERENCE_ISO8601 "2024-001" +#define ATTR_PRESENT "start" +#define ATTR_MISSING "end" +#define REFERENCE_XML "<date_expression id=\"id1\" " \ + ATTR_PRESENT "=\"" REFERENCE_ISO8601 "\"" \ + " operation=\"gt\">" +#define BAD_XML "<date_expression id=\"id1\" " \ + ATTR_PRESENT "=\"not_a_time\"" \ + " operation=\"gt\">" + +static void +null_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, NULL, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(xml, NULL, &t), EINVAL); + assert_null(t); + assert_int_equal(pcmk__xe_get_datetime(NULL, ATTR_PRESENT, NULL), EINVAL); + assert_int_equal(pcmk__xe_get_datetime(NULL, ATTR_PRESENT, &t), EINVAL); + assert_null(t); + assert_int_equal(pcmk__xe_get_datetime(NULL, NULL, &t), EINVAL); + assert_null(t); + + free_xml(xml); +} + +static void +nonnull_time_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = crm_time_new_undefined(); + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), EINVAL); + + crm_time_free(t); + free_xml(xml); +} + +static void +attr_missing(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_MISSING, &t), pcmk_rc_ok); + assert_null(t); + + free_xml(xml); +} + +static void +attr_valid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(REFERENCE_XML); + crm_time_t *t = NULL; + crm_time_t *reference = crm_time_new(REFERENCE_ISO8601); + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), pcmk_rc_ok); + assert_int_equal(crm_time_compare(t, reference), 0); + + crm_time_free(t); + crm_time_free(reference); + free_xml(xml); +} + +static void +attr_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(BAD_XML); + crm_time_t *t = NULL; + + assert_int_equal(pcmk__xe_get_datetime(xml, ATTR_PRESENT, &t), + pcmk_rc_unpack_error); + assert_null(t); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(nonnull_time_invalid), + cmocka_unit_test(attr_missing), + cmocka_unit_test(attr_valid), + cmocka_unit_test(attr_invalid)) diff --git a/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c b/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c index e1fb9c4..dda2878 100644 --- a/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c +++ b/lib/common/tests/nvpair/pcmk__xe_set_bool_attr_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -11,21 +11,21 @@ #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> static void set_attr(void **state) { - xmlNode *node = string2xml("<node/>"); + xmlNode *node = pcmk__xml_parse("<node/>"); pcmk__xe_set_bool_attr(node, "a", true); pcmk__xe_set_bool_attr(node, "b", false); - assert_string_equal(crm_element_value(node, "a"), XML_BOOLEAN_TRUE); - assert_string_equal(crm_element_value(node, "b"), XML_BOOLEAN_FALSE); + assert_string_equal(crm_element_value(node, "a"), PCMK_VALUE_TRUE); + assert_string_equal(crm_element_value(node, "b"), PCMK_VALUE_FALSE); free_xml(node); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(set_attr)) diff --git a/lib/common/tests/probes/Makefile.am b/lib/common/tests/probes/Makefile.am new file mode 100644 index 0000000..f5a3fb4 --- /dev/null +++ b/lib/common/tests/probes/Makefile.am @@ -0,0 +1,18 @@ +# +# Copyright 2024 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 = pcmk_is_probe_test \ + pcmk_xe_is_probe_test \ + pcmk_xe_mask_probe_failure_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/actions/pcmk_is_probe_test.c b/lib/common/tests/probes/pcmk_is_probe_test.c index 4a65e3f..835aac1 100644 --- a/lib/common/tests/actions/pcmk_is_probe_test.c +++ b/lib/common/tests/probes/pcmk_is_probe_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * diff --git a/lib/common/tests/probes/pcmk_xe_is_probe_test.c b/lib/common/tests/probes/pcmk_xe_is_probe_test.c new file mode 100644 index 0000000..e42476a --- /dev/null +++ b/lib/common/tests/probes/pcmk_xe_is_probe_test.c @@ -0,0 +1,54 @@ +/* + * Copyright 2021-2024 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 +op_is_probe_test(void **state) +{ + xmlNode *node = NULL; + + assert_false(pcmk_xe_is_probe(NULL)); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP "/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK__XA_OPERATION_KEY "=\"blah\" " + PCMK_META_INTERVAL "=\"30s\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"30s\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_START "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_is_probe(node)); + free_xml(node); + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_true(pcmk_xe_is_probe(node)); + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(op_is_probe_test)) diff --git a/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c b/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c new file mode 100644 index 0000000..ad378bf --- /dev/null +++ b/lib/common/tests/probes/pcmk_xe_mask_probe_failure_test.c @@ -0,0 +1,333 @@ +/* + * Copyright 2021-2024 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 +op_is_not_probe_test(void **state) { + xmlNode *node = NULL; + + /* Not worth testing this thoroughly since it's just a duplicate of whether + * pcmk_op_is_probe works or not. + */ + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_START "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_mask_probe_failure(node)); + free_xml(node); +} + +static void +op_does_not_have_right_values_test(void **state) { + xmlNode *node = NULL; + char *s = NULL; + + node = pcmk__xml_parse("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION + "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\"/>"); + assert_false(pcmk_xe_mask_probe_failure(node)); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"\"/>", + PCMK_OCF_OK); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); +} + +static void +check_values_test(void **state) { + xmlNode *node = NULL; + char *s = NULL; + + /* PCMK_EXEC_NOT_SUPPORTED */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_NOT_SUPPORTED); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_NOT_SUPPORTED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_DONE */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_DONE); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_NOT_INSTALLED */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_NOT_INSTALLED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_NOT_INSTALLED); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR_HARD */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR_HARD); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + /* PCMK_EXEC_ERROR_FATAL */ + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_OK, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_INVALID_PARAM, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_INSTALLED, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_true(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_CONFIGURED, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); + + s = crm_strdup_printf("<" PCMK__XE_LRM_RSC_OP " " + PCMK_XA_OPERATION "=\"" PCMK_ACTION_MONITOR "\" " + PCMK_META_INTERVAL "=\"0\" " + PCMK__XA_RC_CODE "=\"%d\" " + PCMK__XA_OP_STATUS "=\"%d\"/>", + PCMK_OCF_NOT_RUNNING, PCMK_EXEC_ERROR_FATAL); + node = pcmk__xml_parse(s); + assert_false(pcmk_xe_mask_probe_failure(node)); + free(s); + free_xml(node); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(op_is_not_probe_test), + cmocka_unit_test(op_does_not_have_right_values_test), + cmocka_unit_test(check_values_test)) diff --git a/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c b/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c index 2bae541..52fe006 100644 --- a/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c +++ b/lib/common/tests/procfs/pcmk__procfs_pid2path_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -21,7 +21,7 @@ static void no_exe_file(void **state) { size_t len = PATH_MAX; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; @@ -43,7 +43,7 @@ static void contents_too_long(void **state) { size_t len = 10; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; @@ -66,7 +66,7 @@ static void contents_ok(void **state) { size_t len = PATH_MAX; - char *path = calloc(len, sizeof(char)); + char *path = pcmk__assert_alloc(len, sizeof(char)); // Set readlink() errno and link contents pcmk__mock_readlink = true; diff --git a/lib/common/tests/resources/Makefile.am b/lib/common/tests/resources/Makefile.am new file mode 100644 index 0000000..91e29a6 --- /dev/null +++ b/lib/common/tests/resources/Makefile.am @@ -0,0 +1,17 @@ +# +# Copyright 2024 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 = pcmk_resource_id_test \ + pcmk_resource_is_managed_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/resources/pcmk_resource_id_test.c b/lib/common/tests/resources/pcmk_resource_id_test.c new file mode 100644 index 0000000..5676d2a --- /dev/null +++ b/lib/common/tests/resources/pcmk_resource_id_test.c @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL + +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static void +null_resource(void **state) +{ + assert_null(pcmk_resource_id(NULL)); +} + +static void +resource_with_id(void **state) +{ + char rsc1_id[] = "rsc1"; + pcmk_resource_t rsc1 = { + .id = rsc1_id, + }; + + assert_string_equal(pcmk_resource_id(&rsc1), "rsc1"); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_resource), + cmocka_unit_test(resource_with_id)) diff --git a/lib/common/tests/resources/pcmk_resource_is_managed_test.c b/lib/common/tests/resources/pcmk_resource_is_managed_test.c new file mode 100644 index 0000000..958bdc2 --- /dev/null +++ b/lib/common/tests/resources/pcmk_resource_is_managed_test.c @@ -0,0 +1,46 @@ +/* + * Copyright 2024 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 <stdio.h> // NULL + +#include <crm/common/resources.h> +#include <crm/common/unittest_internal.h> + +static void +null_resource(void **state) +{ + assert_false(pcmk_resource_is_managed(NULL)); +} + +static void +resource_is_managed(void **state) +{ + pcmk_resource_t rsc1 = { + .flags = pcmk_rsc_managed, + }; + + assert_true(pcmk_resource_is_managed(&rsc1)); +} + +static void +resource_is_not_managed(void **state) +{ + pcmk_resource_t rsc1 = { + .flags = 0, + }; + + assert_false(pcmk_resource_is_managed(&rsc1)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_resource), + cmocka_unit_test(resource_is_managed), + cmocka_unit_test(resource_is_not_managed)) diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am new file mode 100644 index 0000000..4163037 --- /dev/null +++ b/lib/common/tests/rules/Makefile.am @@ -0,0 +1,29 @@ +# +# Copyright 2020-2024 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 = pcmk__cmp_by_type_test \ + pcmk__evaluate_attr_expression_test \ + pcmk__evaluate_date_expression_test \ + pcmk__evaluate_date_spec_test \ + pcmk__evaluate_condition_test \ + pcmk__evaluate_op_expression_test \ + pcmk__evaluate_rsc_expression_test \ + pcmk__parse_combine_test \ + pcmk__parse_comparison_test \ + pcmk__parse_source_test \ + pcmk__parse_type_test \ + pcmk__replace_submatches_test \ + pcmk__unpack_duration_test \ + pcmk_evaluate_rule_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/rules/pcmk__cmp_by_type_test.c b/lib/common/tests/rules/pcmk__cmp_by_type_test.c new file mode 100644 index 0000000..cf468f1 --- /dev/null +++ b/lib/common/tests/rules/pcmk__cmp_by_type_test.c @@ -0,0 +1,102 @@ +/* + * Copyright 2024 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 <limits.h> // INT_MIN, INT_MAX + +#include <crm/common/util.h> // crm_strdup_printf() +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +null_compares_lesser(void **state) +{ + assert_int_equal(pcmk__cmp_by_type(NULL, NULL, pcmk__type_string), 0); + assert_true(pcmk__cmp_by_type("0", NULL, pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type(NULL, "0", pcmk__type_number) < 0); +} + +static void +invalid_compares_equal(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("0", "1", pcmk__type_unknown), 0); + assert_int_equal(pcmk__cmp_by_type("hi", "bye", pcmk__type_unknown), 0); + assert_int_equal(pcmk__cmp_by_type("-1.0", "2.0", pcmk__type_unknown), 0); +} + +static void +compare_string_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("bye", "bye", pcmk__type_string), 0); + assert_int_equal(pcmk__cmp_by_type("bye", "BYE", pcmk__type_string), 0); + assert_true(pcmk__cmp_by_type("bye", "hello", pcmk__type_string) < 0); + assert_true(pcmk__cmp_by_type("bye", "HELLO", pcmk__type_string) < 0); + assert_true(pcmk__cmp_by_type("bye", "boo", pcmk__type_string) > 0); + assert_true(pcmk__cmp_by_type("bye", "Boo", pcmk__type_string) > 0); +} + +static void +compare_integer_type(void **state) +{ + char *int_min = crm_strdup_printf("%d", INT_MIN); + char *int_max = crm_strdup_printf("%d", INT_MAX); + + assert_int_equal(pcmk__cmp_by_type("0", "0", pcmk__type_integer), 0); + assert_true(pcmk__cmp_by_type("0", "1", pcmk__type_integer) < 0); + assert_true(pcmk__cmp_by_type("1", "0", pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_integer) > 0); + assert_true(pcmk__cmp_by_type(int_min, int_max, pcmk__type_integer) < 0); + assert_true(pcmk__cmp_by_type(int_max, int_min, pcmk__type_integer) > 0); + free(int_min); + free(int_max); + + // Non-integers compare as strings + assert_int_equal(pcmk__cmp_by_type("0", "x", pcmk__type_integer), + pcmk__cmp_by_type("0", "x", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "0", pcmk__type_integer), + pcmk__cmp_by_type("x", "0", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "X", pcmk__type_integer), + pcmk__cmp_by_type("x", "X", pcmk__type_string)); +} + +static void +compare_number_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("0", "0.0", pcmk__type_number), 0); + assert_true(pcmk__cmp_by_type("0.345", "0.5", pcmk__type_number) < 0); + assert_true(pcmk__cmp_by_type("5", "3.1", pcmk__type_number) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_number) > 0); + + // Non-numbers compare as strings + assert_int_equal(pcmk__cmp_by_type("0.0", "x", pcmk__type_number), + pcmk__cmp_by_type("0.0", "x", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "0.0", pcmk__type_number), + pcmk__cmp_by_type("x", "0.0", pcmk__type_string)); + assert_int_equal(pcmk__cmp_by_type("x", "X", pcmk__type_number), + pcmk__cmp_by_type("x", "X", pcmk__type_string)); +} + +static void +compare_version_type(void **state) +{ + assert_int_equal(pcmk__cmp_by_type("1.0", "1.0", pcmk__type_version), 0); + assert_true(pcmk__cmp_by_type("1.0.0", "1.0.1", pcmk__type_version) < 0); + assert_true(pcmk__cmp_by_type("5.0", "3.1.15", pcmk__type_version) > 0); + assert_true(pcmk__cmp_by_type("3999", "399", pcmk__type_version) > 0); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_compares_lesser), + cmocka_unit_test(invalid_compares_equal), + cmocka_unit_test(compare_string_type), + cmocka_unit_test(compare_integer_type), + cmocka_unit_test(compare_number_type), + cmocka_unit_test(compare_version_type)) diff --git a/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c new file mode 100644 index 0000000..d28cb11 --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c @@ -0,0 +1,831 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +#define MATCHED_STRING "server-north" + +static const regmatch_t submatches[] = { + { .rm_so = 0, .rm_eo = 12 }, // %0 = Entire string + { .rm_so = 7, .rm_eo = 12 }, // %1 = "north" +}; + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate attribute expressions + + // Used to replace submatches in attribute name + .rsc_id = MATCHED_STRING, + .rsc_id_submatches = submatches, + .rsc_id_nmatches = 2, + + // Used when source is instance attributes + .rsc_params = NULL, + + // Used when source is meta-attributes + .rsc_meta = NULL, + + // Used to get actual value of node attribute + .node_attrs = NULL, +}; + +static int +setup(void **state) +{ + rule_input.rsc_params = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.rsc_params, "foo-param", "bar"); + pcmk__insert_dup(rule_input.rsc_params, "myparam", "different"); + + rule_input.rsc_meta = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.rsc_meta, "foo-meta", "bar"); + pcmk__insert_dup(rule_input.rsc_params, "mymeta", "different"); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, "foo", "bar"); + pcmk__insert_dup(rule_input.node_attrs, "num", "10"); + pcmk__insert_dup(rule_input.node_attrs, "ver", "3.5.0"); + pcmk__insert_dup(rule_input.node_attrs, "prefer-north", "100"); + + return 0; +} + +static int +teardown(void **state) +{ + g_hash_table_destroy(rule_input.rsc_params); + g_hash_table_destroy(rule_input.rsc_meta); + g_hash_table_destroy(rule_input.node_attrs); + return 0; +} + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Node attribute expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_attr_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_attr_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_SOURCE_LITERAL_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_SOURCE_LITERAL_PASSES); + + assert_int_equal(pcmk__evaluate_attr_expression(NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_attr_expression(xml, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_attr_expression(NULL, &rule_input), EINVAL); + + free_xml(xml); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_EXPRESSION " " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_attr_expression(EXPR_ID_MISSING, pcmk_rc_ok); +} + + +/* + * Test PCMK_XA_ATTRIBUTE + */ + +#define EXPR_ATTR_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +attr_missing(void **state) +{ + assert_attr_expression(EXPR_ATTR_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_ATTR_SUBMATCH_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='prefer-%1' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +attr_with_submatch_passes(void **state) +{ + assert_attr_expression(EXPR_ATTR_SUBMATCH_PASSES, pcmk_rc_ok); +} + +#define EXPR_ATTR_SUBMATCH_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='undefined-%1' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +attr_with_submatch_fails(void **state) +{ + assert_attr_expression(EXPR_ATTR_SUBMATCH_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_VALUE_SOURCE + */ + +#define EXPR_SOURCE_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +source_missing(void **state) +{ + // Defaults to literal + assert_attr_expression(EXPR_SOURCE_MISSING, pcmk_rc_ok); +} + +#define EXPR_SOURCE_INVALID \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='not-a-source' />" + +static void +source_invalid(void **state) +{ + // Currently treated as literal + assert_attr_expression(EXPR_SOURCE_INVALID, pcmk_rc_ok); +} + +static void +source_literal_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_LITERAL_VALUE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='wrong-value' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +source_literal_value_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_VALUE_FAILS, + pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_LITERAL_ATTR_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='not-an-attribute' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +source_literal_attr_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_LITERAL_ATTR_FAILS, + pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_PARAM_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='not-a-param' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_missing(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_MISSING, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_PARAM_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='foo-param' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_PARAM_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='myparam' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_PARAM "' />" + +static void +source_params_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_PARAM_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_META_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='not-a-meta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_missing(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_MISSING, pcmk_rc_op_unsatisfied); +} + +#define EXPR_SOURCE_META_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='foo-meta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_passes(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_PASSES, pcmk_rc_ok); +} + +#define EXPR_SOURCE_META_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='mymeta' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_META "' />" + +static void +source_meta_fails(void **state) +{ + assert_attr_expression(EXPR_SOURCE_META_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_TYPE + */ + +#define EXPR_TYPE_DEFAULT_NUMBER \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2.5' />" + +static void +type_default_number(void **state) +{ + // Defaults to number for "gt" if either value contains a decimal point + assert_attr_expression(EXPR_TYPE_DEFAULT_NUMBER, pcmk_rc_ok); +} + +#define EXPR_TYPE_DEFAULT_INT \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +type_default_int(void **state) +{ + // Defaults to integer for "gt" if neither value contains a decimal point + assert_attr_expression(EXPR_TYPE_DEFAULT_INT, pcmk_rc_ok); +} + +#define EXPR_TYPE_STRING_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +type_string_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_STRING_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_STRING_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bat' />" + +static void +type_string_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_STRING_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_INTEGER_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +type_integer_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_INTEGER_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='11' />" + +static void +type_integer_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_INTEGER_TRUNCATION \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.5' />" + +static void +type_integer_truncation(void **state) +{ + assert_attr_expression(EXPR_TYPE_INTEGER_TRUNCATION, pcmk_rc_ok); +} + +#define EXPR_TYPE_NUMBER_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.0' />" + +static void +type_number_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_NUMBER_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_NUMBER_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10.1' />" + +static void +type_number_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_NUMBER_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_TYPE_VERSION_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='3.4.9' />" + +static void +type_version_passes(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_PASSES, pcmk_rc_ok); +} + +#define EXPR_TYPE_VERSION_EQUALITY \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='3.5' />" + +static void +type_version_equality(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_EQUALITY, pcmk_rc_ok); +} + +#define EXPR_TYPE_VERSION_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_VERSION "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='ver' " \ + PCMK_XA_VALUE "='4.0' />" + +static void +type_version_fails(void **state) +{ + assert_attr_expression(EXPR_TYPE_VERSION_FAILS, pcmk_rc_before_range); +} + +/* + * Test PCMK_XA_OPERATION + */ + +#define EXPR_OP_MISSING \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_missing(void **state) +{ + assert_attr_expression(EXPR_OP_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_OP_INVALID \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='not-an-operation' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_invalid(void **state) +{ + assert_attr_expression(EXPR_OP_INVALID, pcmk_rc_unpack_error); +} + +#define EXPR_OP_LT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_lt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LT_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +op_lt_fails(void **state) +{ + assert_attr_expression(EXPR_OP_LT_FAILS, pcmk_rc_after_range); +} + +#define EXPR_OP_GT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='2' />" + +static void +op_gt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GT_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_gt_fails(void **state) +{ + assert_attr_expression(EXPR_OP_GT_FAILS, pcmk_rc_before_range); +} + +#define EXPR_OP_LTE_LT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='20' />" + +static void +op_lte_lt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_LT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LTE_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_lte_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_LTE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='9' />" + +static void +op_lte_fails(void **state) +{ + assert_attr_expression(EXPR_OP_LTE_FAILS, pcmk_rc_after_range); +} + +#define EXPR_OP_GTE_GT_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='1' />" + +static void +op_gte_gt_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_GT_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GTE_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_gte_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_GTE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GTE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='11' />" + +static void +op_gte_fails(void **state) +{ + assert_attr_expression(EXPR_OP_GTE_FAILS, pcmk_rc_before_range); +} + +// This also tests that string is used if values aren't parseable as numbers +#define EXPR_OP_EQ_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_NUMBER "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +op_eq_passes(void **state) +{ + assert_attr_expression(EXPR_OP_EQ_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_EQ_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='bar' />" + +static void +op_eq_fails(void **state) +{ + assert_attr_expression(EXPR_OP_EQ_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_NE_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_STRING "' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NE "' " \ + PCMK_XA_VALUE "='bat' " \ + PCMK_XA_VALUE_SOURCE "='" PCMK_VALUE_LITERAL "' />" + +static void +op_ne_passes(void **state) +{ + assert_attr_expression(EXPR_OP_NE_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_NE_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_TYPE "='" PCMK_VALUE_INTEGER "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NE "' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_VALUE "='10' />" + +static void +op_ne_fails(void **state) +{ + assert_attr_expression(EXPR_OP_NE_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_DEFINED_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_passes(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_DEFINED_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='boo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_fails(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_FAILS, pcmk_rc_op_unsatisfied); +} + +#define EXPR_OP_DEFINED_WITH_VALUE \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_VALUE "='bar' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_with_value(void **state) +{ + // Ill-formed but currently accepted + assert_attr_expression(EXPR_OP_DEFINED_WITH_VALUE, pcmk_rc_ok); +} + +#define EXPR_OP_UNDEFINED_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='boo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NOT_DEFINED "' />" + +static void +op_undefined_passes(void **state) +{ + assert_attr_expression(EXPR_OP_UNDEFINED_PASSES, pcmk_rc_ok); +} + +#define EXPR_OP_UNDEFINED_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NOT_DEFINED "' />" + +static void +op_undefined_fails(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_FAILS, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_VALUE + */ + +#define EXPR_VALUE_MISSING_DEFINED_OK \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='num' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +value_missing_defined_ok(void **state) +{ + assert_attr_expression(EXPR_VALUE_MISSING_DEFINED_OK, pcmk_rc_ok); +} + +#define EXPR_VALUE_MISSING_EQ_OK \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='not-an-attr' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' />" + +static void +value_missing_eq_ok(void **state) +{ + // Currently treated as NULL reference value + assert_attr_expression(EXPR_VALUE_MISSING_EQ_OK, pcmk_rc_ok); +} + + +#define expr_test(f) cmocka_unit_test_setup_teardown(f, setup, teardown) + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + expr_test(id_missing), + expr_test(attr_missing), + expr_test(attr_with_submatch_passes), + expr_test(attr_with_submatch_fails), + expr_test(source_missing), + expr_test(source_invalid), + expr_test(source_literal_passes), + expr_test(source_literal_value_fails), + expr_test(source_literal_attr_fails), + expr_test(source_params_missing), + expr_test(source_params_passes), + expr_test(source_params_fails), + expr_test(source_meta_missing), + expr_test(source_meta_passes), + expr_test(source_meta_fails), + expr_test(type_default_number), + expr_test(type_default_int), + expr_test(type_string_passes), + expr_test(type_string_fails), + expr_test(type_integer_passes), + expr_test(type_integer_fails), + expr_test(type_integer_truncation), + expr_test(type_number_passes), + expr_test(type_number_fails), + expr_test(type_version_passes), + expr_test(type_version_equality), + expr_test(type_version_fails), + expr_test(op_missing), + expr_test(op_invalid), + expr_test(op_lt_passes), + expr_test(op_lt_fails), + expr_test(op_gt_passes), + expr_test(op_gt_fails), + expr_test(op_lte_lt_passes), + expr_test(op_lte_eq_passes), + expr_test(op_lte_fails), + expr_test(op_gte_gt_passes), + expr_test(op_gte_eq_passes), + expr_test(op_gte_fails), + expr_test(op_eq_passes), + expr_test(op_eq_fails), + expr_test(op_ne_passes), + expr_test(op_ne_fails), + expr_test(op_defined_passes), + expr_test(op_defined_fails), + expr_test(op_defined_with_value), + expr_test(op_undefined_passes), + expr_test(op_undefined_fails), + expr_test(value_missing_defined_ok), + expr_test(value_missing_eq_ok)) diff --git a/lib/common/tests/rules/pcmk__evaluate_condition_test.c b/lib/common/tests/rules/pcmk__evaluate_condition_test.c new file mode 100644 index 0000000..bcb13a0 --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_condition_test.c @@ -0,0 +1,197 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + + +/* + * Test invalid arguments + */ + +#define EXPR_ATTRIBUTE \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='foo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='bar' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk__evaluate_condition(NULL, NULL, next_change), EINVAL); + + xml = pcmk__xml_parse(EXPR_ATTRIBUTE); + assert_int_equal(pcmk__evaluate_condition(xml, NULL, next_change), EINVAL); + free_xml(xml); + + assert_int_equal(pcmk__evaluate_condition(NULL, &rule_input, next_change), + EINVAL); + + crm_time_free(next_change); +} + + +#define EXPR_INVALID "<not_an_expression " PCMK_XA_ID "='e' />" + +static void +invalid_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_INVALID); + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, next_change), + pcmk_rc_unpack_error); + + crm_time_free(next_change); + free_xml(xml); +} + + +/* Each expression type function already has unit tests, so we just need to test + * that they are called correctly (essentially, one of each one's own tests). + */ + +static void +attribute_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_ATTRIBUTE); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, "foo", "bar"); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + + g_hash_table_destroy(rule_input.node_attrs); + rule_input.node_attrs = NULL; + free_xml(xml); +} + +#define EXPR_LOCATION \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='" CRM_ATTR_UNAME "' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='node1' />" + +static void +location_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_LOCATION); + + rule_input.node_attrs = pcmk__strkey_table(free, free); + pcmk__insert_dup(rule_input.node_attrs, CRM_ATTR_UNAME, "node1"); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + + g_hash_table_destroy(rule_input.node_attrs); + rule_input.node_attrs = NULL; + free_xml(xml); +} + +#define EXPR_DATE \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +date_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_DATE); + crm_time_t *now = crm_time_new("2024-02-01 11:59:59"); + crm_time_t *next_change = crm_time_new("2024-02-01 14:00:00"); + crm_time_t *reference = crm_time_new("2024-02-01 12:00:00"); + + rule_input.now = now; + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, next_change), + pcmk_rc_before_range); + assert_int_equal(crm_time_compare(next_change, reference), 0); + rule_input.now = NULL; + + crm_time_free(reference); + crm_time_free(next_change); + crm_time_free(now); +} + +#define EXPR_RESOURCE \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +resource_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_RESOURCE); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_OP \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +op_expression(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_OP); + + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_SUBRULE \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' /> />" + +static void +subrule(void **state) +{ + xmlNode *xml = pcmk__xml_parse(EXPR_SUBRULE); + assert_int_equal(pcmk__evaluate_condition(xml, &rule_input, NULL), + pcmk_rc_ok); + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(invalid_expression), + cmocka_unit_test(attribute_expression), + cmocka_unit_test(location_expression), + cmocka_unit_test(date_expression), + cmocka_unit_test(resource_expression), + cmocka_unit_test(op_expression), + cmocka_unit_test(subrule)) diff --git a/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c new file mode 100644 index 0000000..df8dcbf --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c @@ -0,0 +1,684 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/*! + * \internal + * \brief Run one test, comparing return value and output argument + * + * \param[in] xml Date expression XML + * \param[in] now_s Time to evaluate expression with (as string) + * \param[in] next_change_s If this and \p reference_s are not NULL, initialize + * next change time with this time (as string), + * and assert that its value after evaluation is the + * reference + * \param[in] reference_s If not NULL, time (as string) that next change + * should be after expression evaluation + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_date_expression(const xmlNode *xml, const char *now_s, + const char *next_change_s, const char *reference_s, + int reference_rc) +{ + crm_time_t *now = NULL; + crm_time_t *next_change = NULL; + bool check_next_change = (next_change_s != NULL) && (reference_s != NULL); + + if (check_next_change) { + next_change = crm_time_new(next_change_s); + } + + now = crm_time_new(now_s); + assert_int_equal(pcmk__evaluate_date_expression(xml, now, next_change), + reference_rc); + crm_time_free(now); + + if (check_next_change) { + crm_time_t *reference = crm_time_new(reference_s); + + assert_int_equal(crm_time_compare(next_change, reference), 0); + crm_time_free(reference); + crm_time_free(next_change); + } +} + +#define EXPR_LT_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +null_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + crm_time_t *t = crm_time_new("2024-02-01"); + + assert_int_equal(pcmk__evaluate_date_expression(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_expression(xml, NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_expression(NULL, t, NULL), EINVAL); + + crm_time_free(t); + free_xml(xml); +} + +static void +null_next_change_ok(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_within_range); + free_xml(xml); +} + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + xmlNodePtr xml = pcmk__xml_parse(EXPR_ID_MISSING); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_within_range); + free_xml(xml); +} + +#define EXPR_OP_INVALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='not-a-choice' />" + +static void +op_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_OP_INVALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_LT_MISSING_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' />" + +static void +lt_missing_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_MISSING_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_LT_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_LT "' " \ + PCMK_XA_END "='not-a-datetime' />" + +static void +lt_invalid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_INVALID_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +static void +lt_valid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_LT_VALID); + + // Now and next change are both before end + assert_date_expression(xml, "2023-01-01 05:00:00", "2024-02-01 10:00:00", + "2024-02-01 10:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:00", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 20:00:00", + "2024-02-01 20:00:00", pcmk_rc_after_range); + + // Now and next change are both after end + assert_date_expression(xml, "2024-03-01 12:00:00", "2024-02-01 20:00:00", + "2024-02-01 20:00:00", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_GT_MISSING_START \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' />" + +static void +gt_missing_start(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_MISSING_START); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_GT_INVALID_START \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_START "='not-a-datetime' />" + +static void +gt_invalid_start(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_INVALID_START); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_GT_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_GT "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' />" + +static void +gt_valid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_GT_VALID); + + // Now and next change are both before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:01", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 12:00:01", pcmk_rc_before_range); + + // Now is one second after start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:01", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // t is after start, next change is after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' />" + +static void +range_missing(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_MISSING); + crm_time_t *t = crm_time_new("2024-01-01"); + + assert_int_equal(pcmk__evaluate_date_expression(xml, t, NULL), + pcmk_rc_undetermined); + + crm_time_free(t); + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_invalid_start_invalid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_INVALID_END); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' />" + +static void +range_invalid_start_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_ONLY); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' />" + +static void +range_valid_start_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_ONLY); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_END_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_invalid_end_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_END_ONLY); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_RANGE_VALID_END_ONLY \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_valid_end_only(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_END_ONLY); + + // Now and next change are before end + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_INVALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='not-a-date' />" + +static void +range_valid_start_invalid_end(void **state) +{ + // Currently treated same as start without end + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_INVALID_END); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 18:00:00", + "2024-02-01 18:00:00", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-03-01 05:03:11", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_within_range); + + free_xml(xml); +} + +#define EXPR_RANGE_INVALID_START_VALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='not-a-date' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_invalid_start_valid_end(void **state) +{ + // Currently treated same as end without start + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_INVALID_START_VALID_END); + + // Now and next change are before end + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_within_range); + + // Now is before end, next change is after end + assert_date_expression(xml, "2024-02-01 14:59:59", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2024-02-01 18:00:00", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2024-04-04 04:04:04", + "2024-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_VALID_END \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00' " \ + PCMK_XA_END "='2024-02-01 15:00:00' />" + +static void +range_valid_start_valid_end(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_VALID_END); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_INVALID_DURATION \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='not-a-number' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_invalid_duration(void **state) +{ + // Currently treated same as end equals start + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_INVALID_DURATION); + + // Now and next change are before start + assert_date_expression(xml, "2024-02-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is after start + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 18:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is after start + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 12:00:01", pcmk_rc_within_range); + + // Now and next change are after start + assert_date_expression(xml, "2024-02-01 12:00:01", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_VALID_DURATION \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='3' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_valid_duration(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_VALID_DURATION); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_RANGE_VALID_START_DURATION_MISSING_ID \ + "<" PCMK_XE_DATE_EXPRESSION " " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_IN_RANGE "' " \ + PCMK_XA_START "='2024-02-01 12:00:00'>" \ + "<" PCMK_XE_DURATION " " PCMK_XA_ID "='d' " \ + PCMK_XA_HOURS "='3' />" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +range_valid_start_duration_missing_id(void **state) +{ + // Currently acceptable + xmlNodePtr xml = NULL; + + xml = pcmk__xml_parse(EXPR_RANGE_VALID_START_DURATION_MISSING_ID); + + // Now and next change are before start + assert_date_expression(xml, "2024-01-01 04:30:05", "2024-01-01 11:00:00", + "2024-01-01 11:00:00", pcmk_rc_before_range); + + // Now is before start, next change is between start and end + assert_date_expression(xml, "2024-02-01 11:59:59", "2024-02-01 14:00:00", + "2024-02-01 12:00:00", pcmk_rc_before_range); + + // Now is equal to start, next change is between start and end + assert_date_expression(xml, "2024-02-01 12:00:00", "2024-02-01 14:30:00", + "2024-02-01 14:30:00", pcmk_rc_within_range); + + // Now is between start and end, next change is after end + assert_date_expression(xml, "2024-02-01 14:03:11", "2024-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now is equal to end, next change is after end + assert_date_expression(xml, "2024-02-01 15:00:00", "2028-04-04 04:04:04", + "2024-02-01 15:00:01", pcmk_rc_within_range); + + // Now and next change are after end + assert_date_expression(xml, "2024-02-01 15:00:01", "2028-04-04 04:04:04", + "2028-04-04 04:04:04", pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_SPEC_MISSING \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "' />" + +static void +spec_missing(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_MISSING); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_undetermined); + free_xml(xml); +} + +#define EXPR_SPEC_INVALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " PCMK_XA_ID "='s' " \ + PCMK_XA_MONTHS "='not-a-number'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_invalid(void **state) +{ + // Currently treated as date_spec with no ranges (which passes) + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_INVALID); + + assert_date_expression(xml, "2024-01-01", NULL, NULL, pcmk_rc_ok); + free_xml(xml); +} + +#define EXPR_SPEC_VALID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " PCMK_XA_ID "='s' " \ + PCMK_XA_MONTHS "='2'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_valid(void **state) +{ + // date_spec does not currently support next_change + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_VALID); + + // Now is just before spec start + assert_date_expression(xml, "2024-01-01 23:59:59", NULL, NULL, + pcmk_rc_before_range); + + // Now matches spec start + assert_date_expression(xml, "2024-02-01 00:00:00", NULL, NULL, pcmk_rc_ok); + + // Now is within spec range + assert_date_expression(xml, "2024-02-22 22:22:22", NULL, NULL, pcmk_rc_ok); + + // Now matches spec end + assert_date_expression(xml, "2024-02-29 23:59:59", NULL, NULL, pcmk_rc_ok); + + // Now is just past spec end + assert_date_expression(xml, "2024-03-01 00:00:00", NULL, NULL, + pcmk_rc_after_range); + + free_xml(xml); +} + +#define EXPR_SPEC_MISSING_ID \ + "<" PCMK_XE_DATE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DATE_SPEC "'>" \ + "<" PCMK_XE_DATE_SPEC " " \ + PCMK_XA_MONTHS "='2'/>" \ + "</" PCMK_XE_DATE_EXPRESSION ">" + +static void +spec_missing_id(void **state) +{ + // Currently acceptable; date_spec does not currently support next_change + xmlNodePtr xml = pcmk__xml_parse(EXPR_SPEC_MISSING_ID); + + // Now is just before spec start + assert_date_expression(xml, "2024-01-01 23:59:59", NULL, NULL, + pcmk_rc_before_range); + + // Now matches spec start + assert_date_expression(xml, "2024-02-01 00:00:00", NULL, NULL, pcmk_rc_ok); + + // Now is within spec range + assert_date_expression(xml, "2024-02-22 22:22:22", NULL, NULL, pcmk_rc_ok); + + // Now matches spec end + assert_date_expression(xml, "2024-02-29 23:59:59", NULL, NULL, pcmk_rc_ok); + + // Now is just past spec end + assert_date_expression(xml, "2024-03-01 00:00:00", NULL, NULL, + pcmk_rc_after_range); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(null_next_change_ok), + cmocka_unit_test(id_missing), + cmocka_unit_test(op_invalid), + cmocka_unit_test(lt_missing_end), + cmocka_unit_test(lt_invalid_end), + cmocka_unit_test(lt_valid), + cmocka_unit_test(gt_missing_start), + cmocka_unit_test(gt_invalid_start), + cmocka_unit_test(gt_valid), + cmocka_unit_test(range_missing), + cmocka_unit_test(range_invalid_start_invalid_end), + cmocka_unit_test(range_invalid_start_only), + cmocka_unit_test(range_valid_start_only), + cmocka_unit_test(range_invalid_end_only), + cmocka_unit_test(range_valid_end_only), + cmocka_unit_test(range_valid_start_invalid_end), + cmocka_unit_test(range_invalid_start_valid_end), + cmocka_unit_test(range_valid_start_valid_end), + cmocka_unit_test(range_valid_start_invalid_duration), + cmocka_unit_test(range_valid_start_valid_duration), + cmocka_unit_test(range_valid_start_duration_missing_id), + cmocka_unit_test(spec_missing), + cmocka_unit_test(spec_invalid), + cmocka_unit_test(spec_valid), + cmocka_unit_test(spec_missing_id)) diff --git a/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c b/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c new file mode 100644 index 0000000..6048adf --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_date_spec_test.c @@ -0,0 +1,231 @@ +/* + * Copyright 2020-2024 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 <errno.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +run_one_test(const char *t, const char *x, int expected) +{ + crm_time_t *tm = crm_time_new(t); + xmlNodePtr xml = pcmk__xml_parse(x); + + assert_int_equal(pcmk__evaluate_date_spec(xml, tm), expected); + + crm_time_free(tm); + free_xml(xml); +} + +static void +null_invalid(void **state) +{ + xmlNodePtr xml = pcmk__xml_parse("<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2019'/>"); + crm_time_t *tm = crm_time_new(NULL); + + assert_int_equal(pcmk__evaluate_date_spec(NULL, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_spec(xml, NULL), EINVAL); + assert_int_equal(pcmk__evaluate_date_spec(NULL, tm), EINVAL); + + crm_time_free(tm); + free_xml(xml); +} + +static void +spec_id_missing(void **state) +{ + // Currently acceptable + run_one_test("2020-01-01", "<date_spec years='2020'/>", pcmk_rc_ok); +} + +static void +invalid_range(void **state) +{ + // Currently acceptable + run_one_test("2020-01-01", "<date_spec years='not-a-year' months='1'/>", + pcmk_rc_ok); +} + +static void +time_satisfies_year_spec(void **state) +{ + run_one_test("2020-01-01", + "<date_spec " PCMK_XA_ID "='spec' years='2020'/>", + pcmk_rc_ok); +} + +static void +time_after_year_spec(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2019'/>", + pcmk_rc_after_range); +} + +static void +time_satisfies_year_range(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2030'/>", + pcmk_rc_ok); +} + +static void +time_before_year_range(void **state) +{ + run_one_test("2000-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2030'/>", + pcmk_rc_before_range); +} + +static void +time_after_year_range(void **state) +{ + run_one_test("2020-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2010-2015'/>", + pcmk_rc_after_range); +} + +static void +range_without_start_year_passes(void **state) +{ + run_one_test("2010-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='-2020'/>", + pcmk_rc_ok); +} + +static void +range_without_end_year_passes(void **state) +{ + run_one_test("2010-01-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2000-'/>", + pcmk_rc_ok); + run_one_test("2000-10-01", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2000-'/>", + pcmk_rc_ok); +} + +static void +yeardays_satisfies(void **state) +{ + run_one_test("2020-01-30", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='30'/>", + pcmk_rc_ok); +} + +static void +time_after_yeardays_spec(void **state) +{ + run_one_test("2020-02-15", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='40'/>", + pcmk_rc_after_range); +} + +static void +yeardays_feb_29_satisfies(void **state) +{ + run_one_test("2016-02-29", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARDAYS "='60'/>", + pcmk_rc_ok); +} + +static void +exact_ymd_satisfies(void **state) +{ + run_one_test("2001-12-31", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='12' " + PCMK_XA_MONTHDAYS "='31'/>", + pcmk_rc_ok); +} + +static void +range_in_month_satisfies(void **state) +{ + run_one_test("2001-06-10", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='6' " + PCMK_XA_MONTHDAYS "='1-10'/>", + pcmk_rc_ok); +} + +static void +exact_ymd_after_range(void **state) +{ + run_one_test("2001-12-31", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='12' " + PCMK_XA_MONTHDAYS "='30'/>", + pcmk_rc_after_range); +} + +static void +time_after_monthdays_range(void **state) +{ + run_one_test("2001-06-10", + "<" PCMK_XE_DATE_SPEC " " + PCMK_XA_ID "='spec' " + PCMK_XA_YEARS "='2001' " + PCMK_XA_MONTHS "='6' " + PCMK_XA_MONTHDAYS "='11-15'/>", + pcmk_rc_before_range); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(spec_id_missing), + cmocka_unit_test(invalid_range), + 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/common/tests/rules/pcmk__evaluate_op_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_op_expression_test.c new file mode 100644 index 0000000..d1cb35f --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_op_expression_test.c @@ -0,0 +1,207 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate operation expressions + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Operation expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_op_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_op_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_FAIL_BOTH \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_START "' " \ + PCMK_XA_INTERVAL "='0' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk__evaluate_op_expression(NULL, NULL), EINVAL); + + xml = pcmk__xml_parse(EXPR_FAIL_BOTH); + assert_int_equal(pcmk__evaluate_op_expression(xml, NULL), EINVAL); + free_xml(xml); + + assert_op_expression(NULL, EINVAL); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_OP_EXPRESSION " " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +#define EXPR_ID_EMPTY \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_op_expression(EXPR_ID_MISSING, pcmk_rc_ok); + assert_op_expression(EXPR_ID_EMPTY, pcmk_rc_ok); +} + + +/* + * Test PCMK_XA_NAME + */ + +#define EXPR_NAME_MISSING \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +name_missing(void **state) +{ + assert_op_expression(EXPR_NAME_MISSING, pcmk_rc_unpack_error); +} + +#define EXPR_MATCH_BOTH \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" + +#define EXPR_EMPTY_NAME \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='' " PCMK_XA_INTERVAL "='10s' />" + +static void +input_name_missing(void **state) +{ + rule_input.op_name = NULL; + assert_op_expression(EXPR_MATCH_BOTH, pcmk_rc_op_unsatisfied); + assert_op_expression(EXPR_EMPTY_NAME, pcmk_rc_op_unsatisfied); + rule_input.op_name = PCMK_ACTION_MONITOR; +} + +#define EXPR_FAIL_NAME \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_START "' " \ + PCMK_XA_INTERVAL "='10s' />" + +static void +fail_name(void **state) +{ + assert_op_expression(EXPR_FAIL_NAME, pcmk_rc_op_unsatisfied); + + // An empty name is meaningless but accepted, so not an unpack error + assert_op_expression(EXPR_EMPTY_NAME, pcmk_rc_op_unsatisfied); +} + + +/* + * Test PCMK_XA_INTERVAL + */ + +#define EXPR_EMPTY_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='' />" + +#define EXPR_INVALID_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='not-an-interval' />" + +static void +invalid_interval(void **state) +{ + assert_op_expression(EXPR_EMPTY_INTERVAL, pcmk_rc_unpack_error); + assert_op_expression(EXPR_INVALID_INTERVAL, pcmk_rc_unpack_error); +} + +#define EXPR_DEFAULT_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' />" + +static void +default_interval(void **state) +{ + assert_op_expression(EXPR_DEFAULT_INTERVAL, pcmk_rc_ok); +} + +#define EXPR_FAIL_INTERVAL \ + "<" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='9s' />" + +static void +fail_interval(void **state) +{ + assert_op_expression(EXPR_FAIL_INTERVAL, pcmk_rc_op_unsatisfied); +} + + +static void +match_both(void **state) +{ + assert_op_expression(EXPR_MATCH_BOTH, pcmk_rc_ok); +} + +static void +fail_both(void **state) +{ + assert_op_expression(EXPR_FAIL_BOTH, pcmk_rc_op_unsatisfied); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(name_missing), + cmocka_unit_test(input_name_missing), + cmocka_unit_test(fail_name), + cmocka_unit_test(invalid_interval), + cmocka_unit_test(default_interval), + cmocka_unit_test(fail_interval), + cmocka_unit_test(match_both), + cmocka_unit_test(fail_both)) diff --git a/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c new file mode 100644 index 0000000..c3a164e --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_rsc_expression_test.c @@ -0,0 +1,227 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + // These are the only members used to evaluate resource expressions + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", +}; + +/*! + * \internal + * \brief Run one test, comparing return value + * + * \param[in] xml_string Resource expression XML as string + * \param[in] reference_rc Assert that evaluation result equals this + */ +static void +assert_rsc_expression(const char *xml_string, int reference_rc) +{ + xmlNode *xml = pcmk__xml_parse(xml_string); + + assert_int_equal(pcmk__evaluate_rsc_expression(xml, &rule_input), + reference_rc); + free_xml(xml); +} + + +/* + * Invalid arguments + */ + +#define EXPR_ALL_MATCH \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + + assert_int_equal(pcmk__evaluate_rsc_expression(NULL, NULL), EINVAL); + + xml = pcmk__xml_parse(EXPR_ALL_MATCH); + assert_int_equal(pcmk__evaluate_rsc_expression(xml, NULL), EINVAL); + free_xml(xml); + + assert_rsc_expression(NULL, EINVAL); +} + + +/* + * Test PCMK_XA_ID + */ + +#define EXPR_ID_MISSING \ + "<" PCMK_XE_RSC_EXPRESSION " " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +#define EXPR_ID_EMPTY \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +id_missing(void **state) +{ + // Currently acceptable + assert_rsc_expression(EXPR_ID_MISSING, pcmk_rc_ok); + assert_rsc_expression(EXPR_ID_EMPTY, pcmk_rc_ok); +} + + +/* + * Test standard, provider, and agent + */ + +#define EXPR_FAIL_STANDARD \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_LSB "' />" + +#define EXPR_EMPTY_STANDARD \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='' />" + +static void +fail_standard(void **state) +{ + assert_rsc_expression(EXPR_FAIL_STANDARD, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_STANDARD, pcmk_rc_op_unsatisfied); + + rule_input.rsc_standard = NULL; + assert_rsc_expression(EXPR_FAIL_STANDARD, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_STANDARD, pcmk_rc_op_unsatisfied); + rule_input.rsc_standard = PCMK_RESOURCE_CLASS_OCF; +} + +#define EXPR_FAIL_PROVIDER \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='pacemaker' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +#define EXPR_EMPTY_PROVIDER \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='' " PCMK_XA_TYPE "='IPaddr2' />" + +static void +fail_provider(void **state) +{ + assert_rsc_expression(EXPR_FAIL_PROVIDER, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_PROVIDER, pcmk_rc_op_unsatisfied); + + rule_input.rsc_provider = NULL; + assert_rsc_expression(EXPR_FAIL_PROVIDER, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_PROVIDER, pcmk_rc_op_unsatisfied); + rule_input.rsc_provider = "heartbeat"; +} + +#define EXPR_FAIL_AGENT \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr3' />" + +#define EXPR_EMPTY_AGENT \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' " PCMK_XA_TYPE "='' />" + +static void +fail_agent(void **state) +{ + assert_rsc_expression(EXPR_FAIL_AGENT, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_AGENT, pcmk_rc_op_unsatisfied); + + rule_input.rsc_agent = NULL; + assert_rsc_expression(EXPR_FAIL_AGENT, pcmk_rc_op_unsatisfied); + assert_rsc_expression(EXPR_EMPTY_AGENT, pcmk_rc_op_unsatisfied); + rule_input.rsc_agent = "IPaddr2"; +} + +#define EXPR_NO_STANDARD_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_PROVIDER "='heartbeat' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +no_standard_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_STANDARD_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_PROVIDER_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_TYPE "='IPaddr2' />" + +static void +no_provider_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_PROVIDER_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_AGENT_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_CLASS "='" PCMK_RESOURCE_CLASS_OCF "' " \ + PCMK_XA_PROVIDER "='heartbeat' />" + +static void +no_agent_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_AGENT_MATCHES, pcmk_rc_ok); +} + +#define EXPR_NO_CRITERIA_MATCHES \ + "<" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e' />" + +static void +no_criteria_matches(void **state) +{ + assert_rsc_expression(EXPR_NO_CRITERIA_MATCHES, pcmk_rc_ok); +} + +static void +all_match(void **state) +{ + assert_rsc_expression(EXPR_ALL_MATCH, pcmk_rc_ok); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(fail_standard), + cmocka_unit_test(fail_provider), + cmocka_unit_test(fail_agent), + cmocka_unit_test(no_standard_matches), + cmocka_unit_test(no_provider_matches), + cmocka_unit_test(no_agent_matches), + cmocka_unit_test(no_criteria_matches), + cmocka_unit_test(all_match)) diff --git a/lib/common/tests/rules/pcmk__parse_combine_test.c b/lib/common/tests/rules/pcmk__parse_combine_test.c new file mode 100644 index 0000000..afebcf8 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_combine_test.c @@ -0,0 +1,52 @@ +/* + * Copyright 2024 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 <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +static void +default_and(void **state) +{ + assert_int_equal(pcmk__parse_combine(NULL), pcmk__combine_and); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_combine(""), pcmk__combine_unknown); + assert_int_equal(pcmk__parse_combine(" "), pcmk__combine_unknown); + assert_int_equal(pcmk__parse_combine("but"), pcmk__combine_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_combine(PCMK_VALUE_AND), pcmk__combine_and); + assert_int_equal(pcmk__parse_combine(PCMK_VALUE_OR), pcmk__combine_or); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_combine("And"), + pcmk__combine_and); + + assert_int_equal(pcmk__parse_combine("OR"), + pcmk__combine_or); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(default_and), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_comparison_test.c b/lib/common/tests/rules/pcmk__parse_comparison_test.c new file mode 100644 index 0000000..a995596 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_comparison_test.c @@ -0,0 +1,72 @@ +/* + * Copyright 2024 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 <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +null_unknown(void **state) +{ + assert_int_equal(pcmk__parse_comparison(NULL), pcmk__comparison_unknown); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_comparison("nope"), pcmk__comparison_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_DEFINED), + pcmk__comparison_defined); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_NOT_DEFINED), + pcmk__comparison_undefined); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_EQ), + pcmk__comparison_eq); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_NE), + pcmk__comparison_ne); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_LT), + pcmk__comparison_lt); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_LTE), + pcmk__comparison_lte); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_GT), + pcmk__comparison_gt); + + assert_int_equal(pcmk__parse_comparison(PCMK_VALUE_GTE), + pcmk__comparison_gte); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_comparison("DEFINED"), + pcmk__comparison_defined); + + assert_int_equal(pcmk__parse_comparison("Not_Defined"), + pcmk__comparison_undefined); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_unknown), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_source_test.c b/lib/common/tests/rules/pcmk__parse_source_test.c new file mode 100644 index 0000000..9cf9b32 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_source_test.c @@ -0,0 +1,62 @@ +/* + * Copyright 2024 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 <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +default_literal(void **state) +{ + assert_int_equal(pcmk__parse_source(NULL), pcmk__source_literal); +} + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_source(""), pcmk__source_unknown); + assert_int_equal(pcmk__parse_source(" "), pcmk__source_unknown); + assert_int_equal(pcmk__parse_source("params"), pcmk__source_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_source(PCMK_VALUE_LITERAL), + pcmk__source_literal); + + assert_int_equal(pcmk__parse_source(PCMK_VALUE_PARAM), + pcmk__source_instance_attrs); + + assert_int_equal(pcmk__parse_source(PCMK_VALUE_META), + pcmk__source_meta_attrs); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_source("LITERAL"), + pcmk__source_literal); + + assert_int_equal(pcmk__parse_source("Param"), + pcmk__source_instance_attrs); + + assert_int_equal(pcmk__parse_source("MeTa"), + pcmk__source_meta_attrs); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(default_literal), + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive)) diff --git a/lib/common/tests/rules/pcmk__parse_type_test.c b/lib/common/tests/rules/pcmk__parse_type_test.c new file mode 100644 index 0000000..96f02c8 --- /dev/null +++ b/lib/common/tests/rules/pcmk__parse_type_test.c @@ -0,0 +1,127 @@ +/* + * Copyright 2024 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 <stdio.h> + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +static void +invalid(void **state) +{ + assert_int_equal(pcmk__parse_type("nope", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_unknown); +} + +static void +valid(void **state) +{ + assert_int_equal(pcmk__parse_type(PCMK_VALUE_STRING, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_INTEGER, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_NUMBER, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(PCMK_VALUE_VERSION, + pcmk__comparison_unknown, NULL, NULL), + pcmk__type_version); +} + +static void +case_insensitive(void **state) +{ + assert_int_equal(pcmk__parse_type("STRING", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type("Integer", pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_integer); +} + +static void +default_number(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lt, "1.0", "2.5"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lte, "1.", "2"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gt, "1", ".5"), + pcmk__type_number); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1.0", "2"), + pcmk__type_number); +} + +static void +default_integer(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lt, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_lte, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gt, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1", "2"), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, NULL, NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, "1", NULL), + pcmk__type_integer); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_gte, NULL, "2.5"), + pcmk__type_number); +} + +static void +default_string(void **state) +{ + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_unknown, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_defined, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_undefined, + NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_eq, NULL, NULL), + pcmk__type_string); + + assert_int_equal(pcmk__parse_type(NULL, pcmk__comparison_ne, NULL, NULL), + pcmk__type_string); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(invalid), + cmocka_unit_test(valid), + cmocka_unit_test(case_insensitive), + cmocka_unit_test(default_number), + cmocka_unit_test(default_integer), + cmocka_unit_test(default_string)) diff --git a/lib/common/tests/rules/pcmk__replace_submatches_test.c b/lib/common/tests/rules/pcmk__replace_submatches_test.c new file mode 100644 index 0000000..d404fcc --- /dev/null +++ b/lib/common/tests/rules/pcmk__replace_submatches_test.c @@ -0,0 +1,81 @@ +/* + * Copyright 2024 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 <regex.h> // regmatch_t + +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +// An example matched string with submatches +static const char *match = "this is a string"; +static const regmatch_t submatches[] = { + { .rm_so = 0, .rm_eo = 16 }, // %0 = entire string + { .rm_so = 5, .rm_eo = 7 }, // %1 = "is" + { .rm_so = 9, .rm_eo = 9 }, // %2 = empty match +}; +static const int nmatches = 3; + +static void +assert_submatch(const char *string, const char *reference) +{ + char *expanded = NULL; + + expanded = pcmk__replace_submatches(string, match, submatches, nmatches); + if ((expanded == NULL) || (reference == NULL)) { + assert_null(expanded); + assert_null(reference); + } else { + assert_int_equal(strcmp(expanded, reference), 0); + } + free(expanded); +} + +static void +no_source(void **state) +{ + assert_null(pcmk__replace_submatches(NULL, NULL, NULL, 0)); + assert_submatch(NULL, NULL); + assert_submatch("", NULL); +} + +static void +source_has_no_variables(void **state) +{ + assert_null(pcmk__replace_submatches("this has no submatch variables", + match, submatches, nmatches)); + assert_null(pcmk__replace_submatches("this ends in a %", + match, submatches, nmatches)); + assert_null(pcmk__replace_submatches("%this starts with one", + match, submatches, nmatches)); +} + +static void +without_matches(void **state) +{ + assert_submatch("this has an empty submatch %2", + "this has an empty submatch "); + assert_submatch("this has a nonexistent submatch %3", + "this has a nonexistent submatch "); +} + +static void +with_matches(void **state) +{ + assert_submatch("%0", match); // %0 matches entire string + assert_submatch("this %1", "this is"); + assert_submatch("%1 this %ok", "is this %ok"); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(no_source), + cmocka_unit_test(source_has_no_variables), + cmocka_unit_test(without_matches), + cmocka_unit_test(with_matches)) diff --git a/lib/common/tests/rules/pcmk__unpack_duration_test.c b/lib/common/tests/rules/pcmk__unpack_duration_test.c new file mode 100644 index 0000000..e82546c --- /dev/null +++ b/lib/common/tests/rules/pcmk__unpack_duration_test.c @@ -0,0 +1,120 @@ +/* + * Copyright 2024 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/common/unittest_internal.h> + +#include <crm/common/iso8601.h> +#include <crm/common/xml.h> +#include "../../crmcommon_private.h" + +#define MONTHS_TO_SECONDS "months=\"2\" weeks=\"3\" days=\"-1\" " \ + "hours=\"1\" minutes=\"1\" seconds=\"1\" />" + +#define ALL_VALID "<duration id=\"duration1\" years=\"1\" " MONTHS_TO_SECONDS + +#define NO_ID "<duration years=\"1\" " MONTHS_TO_SECONDS + +#define YEARS_INVALID "<duration id=\"duration1\" years=\"not-a-number\" " \ + MONTHS_TO_SECONDS + +static void +null_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + + assert_int_equal(pcmk__unpack_duration(NULL, NULL, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, NULL, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, start, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(duration, NULL, &end), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, start, NULL), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, start, &end), EINVAL); + assert_int_equal(pcmk__unpack_duration(NULL, NULL, &end), EINVAL); + + crm_time_free(start); + free_xml(duration); +} + +static void +nonnull_end_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = crm_time_new("2024-01-01 15:00:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), EINVAL); + + crm_time_free(start); + crm_time_free(end); + free_xml(duration); +} + +static void +no_id(void **state) +{ + xmlNode *duration = pcmk__xml_parse(NO_ID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2025-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), pcmk_rc_ok); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +static void +years_invalid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(YEARS_INVALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2024-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), + pcmk_rc_unpack_error); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +static void +all_valid(void **state) +{ + xmlNode *duration = pcmk__xml_parse(ALL_VALID); + crm_time_t *start = crm_time_new("2024-01-01 15:00:00"); + crm_time_t *end = NULL; + crm_time_t *reference = crm_time_new("2025-03-21 16:01:01"); + + assert_int_equal(pcmk__unpack_duration(duration, start, &end), pcmk_rc_ok); + assert_int_equal(crm_time_compare(end, reference), 0); + + crm_time_free(start); + crm_time_free(end); + crm_time_free(reference); + free_xml(duration); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(nonnull_end_invalid), + cmocka_unit_test(no_id), + cmocka_unit_test(years_invalid), + cmocka_unit_test(all_valid)) diff --git a/lib/common/tests/rules/pcmk_evaluate_rule_test.c b/lib/common/tests/rules/pcmk_evaluate_rule_test.c new file mode 100644 index 0000000..6b6f9eb --- /dev/null +++ b/lib/common/tests/rules/pcmk_evaluate_rule_test.c @@ -0,0 +1,379 @@ +/* + * Copyright 2024 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 <stdio.h> +#include <glib.h> + +#include <crm/common/xml.h> +#include <crm/common/rules_internal.h> +#include <crm/common/unittest_internal.h> + +/* + * Shared data + */ + +static pcmk_rule_input_t rule_input = { + .rsc_standard = PCMK_RESOURCE_CLASS_OCF, + .rsc_provider = "heartbeat", + .rsc_agent = "IPaddr2", + .op_name = PCMK_ACTION_MONITOR, + .op_interval_ms = 10000, +}; + + +/* + * Test invalid arguments + */ + +#define RULE_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' > " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +null_invalid(void **state) +{ + xmlNode *xml = NULL; + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk_evaluate_rule(NULL, NULL, next_change), + EINVAL); + + xml = pcmk__xml_parse(RULE_OP); + assert_int_equal(pcmk_evaluate_rule(xml, NULL, next_change), EINVAL); + free_xml(xml); + + assert_int_equal(pcmk_evaluate_rule(NULL, &rule_input, next_change), + EINVAL); + + crm_time_free(next_change); +} + +#define RULE_OP_MISSING_ID \ + "<" PCMK_XE_RULE "> " \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +id_missing(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_OP_MISSING_ID); + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, next_change), + pcmk_rc_ok); + + crm_time_free(next_change); + free_xml(xml); +} + +#define RULE_IDREF_PARENT "<" PCMK_XE_CIB ">" RULE_OP "</" PCMK_XE_CIB ">" + +static void +good_idref(void **state) +{ + xmlNode *parent_xml = pcmk__xml_parse(RULE_IDREF_PARENT); + xmlNode *rule_xml = pcmk__xe_create(parent_xml, PCMK_XE_RULE); + crm_time_t *next_change = crm_time_new_undefined(); + + crm_xml_add(rule_xml, PCMK_XA_ID_REF, "r"); + assert_int_equal(pcmk_evaluate_rule(rule_xml, &rule_input, next_change), + pcmk_rc_ok); + + crm_time_free(next_change); + free_xml(parent_xml); +} + +static void +bad_idref(void **state) +{ + xmlNode *parent_xml = pcmk__xml_parse(RULE_IDREF_PARENT); + xmlNode *rule_xml = pcmk__xe_create(parent_xml, PCMK_XE_RULE); + crm_time_t *next_change = crm_time_new_undefined(); + + crm_xml_add(rule_xml, PCMK_XA_ID_REF, "x"); + assert_int_equal(pcmk_evaluate_rule(rule_xml, &rule_input, next_change), + pcmk_rc_unpack_error); + + crm_time_free(next_change); + free_xml(parent_xml); +} + +#define RULE_EMPTY "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' />" + +static void +empty_default(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_EMPTY_AND \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' />" + +static void +empty_and(void **state) +{ + // Currently acceptable + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY_AND); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_EMPTY_OR \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' />" + +static void +empty_or(void **state) +{ + // Currently treated as unsatisfied + xmlNode *xml = pcmk__xml_parse(RULE_EMPTY_OR); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_DEFAULT_BOOLEAN_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +default_boolean_op(void **state) +{ + // Defaults to PCMK_VALUE_AND + xmlNode *xml = pcmk__xml_parse(RULE_DEFAULT_BOOLEAN_OP); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_INVALID_BOOLEAN_OP \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='not-an-op' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +invalid_boolean_op(void **state) +{ + // Currently defaults to PCMK_VALUE_AND + xmlNode *xml = pcmk__xml_parse(RULE_INVALID_BOOLEAN_OP); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_AND_PASSES \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPaddr2' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_PASSES); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_LONELY_AND \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPaddr2' />" \ + "</" PCMK_XE_RULE ">" + +static void +lonely_and_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_LONELY_AND); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_AND_ONE_FAILS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_one_fails(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_ONE_FAILS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_AND_TWO_FAIL \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_AND "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='9s' />" \ + "</" PCMK_XE_RULE ">" + +static void +and_two_fail(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_AND_TWO_FAIL); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +#define RULE_OR_ONE_PASSES \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_one_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_ONE_PASSES); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_OR_TWO_PASS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='IPAddr2' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_two_pass(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_TWO_PASS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_LONELY_OR \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='10s' />" \ + "</" PCMK_XE_RULE ">" + +static void +lonely_or_passes(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_LONELY_OR); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), pcmk_rc_ok); + + free_xml(xml); +} + +#define RULE_OR_FAILS \ + "<" PCMK_XE_RULE " " PCMK_XA_ID "='r' " \ + PCMK_XA_BOOLEAN_OP "='" PCMK_VALUE_OR "' >" \ + " <" PCMK_XE_RSC_EXPRESSION " " PCMK_XA_ID "='e1' " \ + PCMK_XA_TYPE "='Dummy' />" \ + " <" PCMK_XE_OP_EXPRESSION " " PCMK_XA_ID "='e2' " \ + PCMK_XA_NAME "='" PCMK_ACTION_MONITOR "' " \ + PCMK_XA_INTERVAL "='20s' />" \ + "</" PCMK_XE_RULE ">" + +static void +or_fails(void **state) +{ + xmlNode *xml = pcmk__xml_parse(RULE_OR_FAILS); + + assert_int_equal(pcmk_evaluate_rule(xml, &rule_input, NULL), + pcmk_rc_op_unsatisfied); + + free_xml(xml); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_invalid), + cmocka_unit_test(id_missing), + cmocka_unit_test(good_idref), + cmocka_unit_test(bad_idref), + cmocka_unit_test(empty_default), + cmocka_unit_test(empty_and), + cmocka_unit_test(empty_or), + cmocka_unit_test(default_boolean_op), + cmocka_unit_test(invalid_boolean_op), + cmocka_unit_test(and_passes), + cmocka_unit_test(lonely_and_passes), + cmocka_unit_test(and_one_fails), + cmocka_unit_test(and_two_fail), + cmocka_unit_test(or_one_passes), + cmocka_unit_test(or_two_pass), + cmocka_unit_test(lonely_or_passes), + cmocka_unit_test(or_fails)) diff --git a/lib/common/tests/scheduler/Makefile.am b/lib/common/tests/scheduler/Makefile.am new file mode 100644 index 0000000..6d5f4f8 --- /dev/null +++ b/lib/common/tests/scheduler/Makefile.am @@ -0,0 +1,19 @@ +# +# Copyright 2024 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 = pcmk_get_dc_test \ + pcmk_get_no_quorum_policy_test \ + pcmk_has_quorum_test \ + pcmk_set_scheduler_cib_test + +TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/scheduler/pcmk_get_dc_test.c b/lib/common/tests/scheduler/pcmk_get_dc_test.c new file mode 100644 index 0000000..5d9d459 --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_get_dc_test.c @@ -0,0 +1,47 @@ +/* + * Copyright 2024 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/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_null(pcmk_get_dc(NULL)); +} + +static void +null_dc(void **state) +{ + pcmk_scheduler_t scheduler = { + .dc_node = NULL, + }; + + assert_null(pcmk_get_dc(&scheduler)); +} + +static void +valid_dc(void **state) +{ + pcmk_node_t dc = { + .weight = 1, + }; + pcmk_scheduler_t scheduler = { + .dc_node = &dc, + }; + + assert_ptr_equal(&dc, pcmk_get_dc(&scheduler)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(null_dc), + cmocka_unit_test(valid_dc)) diff --git a/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c b/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c new file mode 100644 index 0000000..61c97e6 --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_get_no_quorum_policy_test.c @@ -0,0 +1,34 @@ +/* + * Copyright 2024 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/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_int_equal(pcmk_get_no_quorum_policy(NULL), pcmk_no_quorum_stop); +} + +static void +valid_no_quorum_policy(void **state) +{ + pcmk_scheduler_t scheduler = { + .no_quorum_policy = pcmk_no_quorum_fence, + }; + + assert_int_equal(pcmk_get_no_quorum_policy(&scheduler), + pcmk_no_quorum_fence); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(valid_no_quorum_policy)) diff --git a/lib/common/tests/scheduler/pcmk_has_quorum_test.c b/lib/common/tests/scheduler/pcmk_has_quorum_test.c new file mode 100644 index 0000000..51903df --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_has_quorum_test.c @@ -0,0 +1,36 @@ +/* + * Copyright 2024 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/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + assert_false(pcmk_has_quorum(NULL)); +} + +static void +valid_scheduler(void **state) +{ + pcmk_scheduler_t scheduler = { + .flags = pcmk_sched_quorate, + }; + + assert_true(pcmk_has_quorum(&scheduler)); + + scheduler.flags = pcmk_sched_none; + assert_false(pcmk_has_quorum(&scheduler)); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(valid_scheduler)) diff --git a/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c b/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c new file mode 100644 index 0000000..71e690b --- /dev/null +++ b/lib/common/tests/scheduler/pcmk_set_scheduler_cib_test.c @@ -0,0 +1,71 @@ +/* + * Copyright 2024 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/scheduler.h> +#include <crm/common/unittest_internal.h> + +static void +null_scheduler(void **state) +{ + xmlNode *cib = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk_set_scheduler_cib(NULL, NULL), EINVAL); + assert_int_equal(pcmk_set_scheduler_cib(NULL, cib), EINVAL); + + free_xml(cib); +} + +static void +null_cib(void **state) +{ + pcmk_scheduler_t scheduler = { + .input = NULL, + }; + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, NULL), pcmk_rc_ok); + assert_null(scheduler.input); +} + +static void +previous_cib_null(void **state) +{ + pcmk_scheduler_t scheduler = { + .input = NULL, + }; + xmlNode *cib = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, cib), pcmk_rc_ok); + assert_ptr_equal(scheduler.input, cib); + + free_xml(cib); +} + +static void +previous_cib_nonnull(void **state) +{ + xmlNode *old_cib = pcmk__xe_create(NULL, "old"); + xmlNode *new_cib = pcmk__xe_create(NULL, "new"); + pcmk_scheduler_t scheduler = { + .input = old_cib, + }; + + assert_int_equal(pcmk_set_scheduler_cib(&scheduler, new_cib), pcmk_rc_ok); + assert_ptr_equal(scheduler.input, new_cib); + + free_xml(old_cib); + free_xml(new_cib); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(null_scheduler), + cmocka_unit_test(null_cib), + cmocka_unit_test(previous_cib_null), + cmocka_unit_test(previous_cib_nonnull)) diff --git a/lib/common/tests/schemas/Makefile.am b/lib/common/tests/schemas/Makefile.am new file mode 100644 index 0000000..ba0f805 --- /dev/null +++ b/lib/common/tests/schemas/Makefile.am @@ -0,0 +1,88 @@ +# +# Copyright 2023-2024 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 + +CFLAGS += -DPCMK__TEST_SCHEMA_DIR='"$(abs_builddir)/schemas"' + +# Add "_test" to the end of all test program names to simplify .gitignore. + +# These tests share a schema subdirectory +SHARED_SCHEMA_TESTS = pcmk__cmp_schemas_by_name_test \ + crm_schema_init_test \ + pcmk__build_schema_xml_node_test \ + pcmk__get_schema_test \ + pcmk__schema_files_later_than_test + +# This test has its own schema directory +FIND_X_0_SCHEMA_TEST = pcmk__find_x_0_schema_test + +check_PROGRAMS = $(SHARED_SCHEMA_TESTS) $(FIND_X_0_SCHEMA_TEST) + +TESTS = $(check_PROGRAMS) + +$(SHARED_SCHEMA_TESTS): setup-schema-dir + +$(FIND_X_0_SCHEMA_TEST): setup-find_x_0-schema-dir + +# Set up a temporary schemas/ directory containing only some of the full set of +# pacemaker schema files. This lets us know exactly how many schemas are present, +# allowing us to write tests without having to make changes when new schemas are +# added. +# +# This directory contains the following: +# +# * pacemaker-next.rng - Used to verify that this sorts before all versions +# * upgrade-*.xsl - Required by various schema versions +# * pacemaker-[0-9]*.rng - We're only pulling in 15 schemas, which is enough +# to get everything through pacemaker-3.0.rng. This +# includes 2.10, needed so we can check that versions +# are compared as numbers instead of strings. +# * other RNG files - This catches everything except the pacemaker-*rng +# files. These files are included by the top-level +# pacemaker-*rng files, so we need them for tests. +# This will glob more than we need, but the extra ones +# won't get in the way. + +LINK_FILES = $(abs_top_builddir)/xml/pacemaker-next.rng \ + $(abs_top_builddir)/xml/upgrade-*.xsl +ROOT_RNGS = $(shell ls -1v $(abs_top_builddir)/xml/pacemaker-[0-9]*.rng | head -15) +INCLUDED_RNGS = $(shell ls -1 $(top_srcdir)/xml/*.rng | grep -v pacemaker-[0-9]) + +# Most tests share a common, read-only schema directory +.PHONY: setup-schema-dir +setup-schema-dir: + $(MKDIR_P) schemas + ( cd schemas ; \ + ln -sf $(LINK_FILES) . ; \ + for f in $(ROOT_RNGS); do \ + ln -sf $$f $$(basename $$f); \ + done ; \ + for f in $(INCLUDED_RNGS); do \ + ln -sf ../$$f $$(basename $$f); \ + done ) + +# pcmk__find_x_0_schema_test moves schema files around, so it needs its +# own directory, otherwise other tests run in parallel could fail. +.PHONY: setup-find_x_0-schema-dir +setup-find_x_0-schema-dir: + $(MKDIR_P) schemas/find_x_0 + ( cd schemas/find_x_0 ; \ + ln -sf $(LINK_FILES) . ; \ + for f in $(ROOT_RNGS); do \ + ln -sf $$f $$(basename $$f); \ + done ; \ + for f in $(INCLUDED_RNGS); do \ + ln -sf ../$$f $$(basename $$f); \ + done ) + +.PHONY: clean-local +clean-local: + -rm -rf schemas diff --git a/lib/common/tests/schemas/crm_schema_init_test.c b/lib/common/tests/schemas/crm_schema_init_test.c new file mode 100644 index 0000000..8da79cb --- /dev/null +++ b/lib/common/tests/schemas/crm_schema_init_test.c @@ -0,0 +1,152 @@ +/* + * Copyright 2023-2024 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 <ftw.h> +#include <unistd.h> + +#include <crm/common/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static char *remote_schema_dir = NULL; + +static int +symlink_schema(const char *tmpdir, const char *target_file, const char *link_file) +{ + int rc = 0; + char *oldpath = NULL; + char *newpath = NULL; + + oldpath = crm_strdup_printf("%s/%s", PCMK__TEST_SCHEMA_DIR, target_file); + newpath = crm_strdup_printf("%s/%s", tmpdir, link_file); + + rc = symlink(oldpath, newpath); + + free(oldpath); + free(newpath); + return rc; +} + +static int +rm_files(const char *pathname, const struct stat *sbuf, int type, struct FTW *ftwb) +{ + return remove(pathname); +} + +static int +rmtree(const char *dir) +{ + return nftw(dir, rm_files, 10, FTW_DEPTH|FTW_MOUNT|FTW_PHYS); +} + +static int +setup(void **state) +{ + char *dir = NULL; + + /* Create a directory to hold additional schema files. These don't need + * to be anything special - we can just copy existing schemas but give + * them new names. + */ + dir = crm_strdup_printf("%s/test-schemas.XXXXXX", pcmk__get_tmpdir()); + remote_schema_dir = mkdtemp(dir); + + if (remote_schema_dir == NULL) { + free(dir); + return -1; + } + + /* Add new files to simulate a remote node not being up-to-date. We can't + * add a new major version here without also creating an XSL transform, and + * we can't add an older version (like 1.1 or 2.11 or something) because + * remotes will only ever ask for stuff newer than their newest. + */ + if (symlink_schema(dir, "pacemaker-3.0.rng", "pacemaker-3.1.rng") != 0) { + rmdir(dir); + free(dir); + return -1; + } + + if (symlink_schema(dir, "pacemaker-3.0.rng", "pacemaker-3.2.rng") != 0) { + rmdir(dir); + free(dir); + return -1; + } + + setenv("PCMK_remote_schema_directory", remote_schema_dir, 1); + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + + /* Do not call crm_schema_init here because that is the function we're + * testing. It needs to be called in each unit test. However, we can + * call crm_schema_cleanup in teardown(). + */ + + return 0; +} + +static int +teardown(void **state) +{ + int rc = 0; + char *f = NULL; + + crm_schema_cleanup(); + unsetenv("PCMK_remote_schema_directory"); + unsetenv("PCMK_schema_directory"); + + rc = rmtree(remote_schema_dir); + + free(remote_schema_dir); + free(f); + return rc; +} + +static void +assert_schema(const char *schema_name, int schema_index) +{ + GList *entry = NULL; + pcmk__schema_t *schema = NULL; + + entry = pcmk__get_schema(schema_name); + assert_non_null(entry); + + schema = entry->data; + assert_non_null(schema); + + assert_int_equal(schema_index, schema->schema_index); +} + +static void +extra_schema_files(void **state) +{ + crm_schema_init(); + + /* Just iterate through the list of schemas and make sure everything + * (including the new schemas we loaded from a second directory) is in + * the right order. + */ + assert_schema("pacemaker-1.0", 0); + assert_schema("pacemaker-1.2", 1); + assert_schema("pacemaker-2.0", 3); + assert_schema("pacemaker-3.0", 14); + assert_schema("pacemaker-3.1", 15); + assert_schema("pacemaker-3.2", 16); + + // @COMPAT pacemaker-next is deprecated since 2.1.5 + assert_schema("pacemaker-next", 17); + + // @COMPAT none is deprecated since 2.1.8 + assert_schema(PCMK_VALUE_NONE, 18); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(extra_schema_files)); diff --git a/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c b/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c new file mode 100644 index 0000000..e4454e2 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__build_schema_xml_node_test.c @@ -0,0 +1,158 @@ +/* + * Copyright 2023-2024 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/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/lists_internal.h> + +#include <glib.h> + +const char *rngs1[] = { "pacemaker-3.0.rng", "status-1.0.rng", "alerts-2.10.rng", + "nvset-2.9.rng", "score.rng", "rule-2.9.rng", + "tags-1.3.rng", "acls-2.0.rng", "fencing-2.4.rng", + "constraints-3.0.rng", "resources-3.0.rng", "nvset-3.0.rng", + "nodes-3.0.rng", "options-3.0.rng", NULL }; + +const char *rngs2[] = { "pacemaker-2.0.rng", "status-1.0.rng", "tags-1.3.rng", + "acls-2.0.rng", "fencing-1.2.rng", "constraints-1.2.rng", + "rule.rng", "score.rng", "resources-1.3.rng", + "nvset-1.3.rng", "nodes-1.3.rng", "options-1.0.rng", + "nvset.rng", "cib-1.2.rng", NULL }; + +const char *rngs3[] = { "pacemaker-2.1.rng", "constraints-2.1.rng", NULL }; + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + pcmk__xml_test_setup_group(state); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +invalid_name(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + + pcmk__build_schema_xml_node(parent, "pacemaker-9.0", &already_included); + assert_null(parent->children); + assert_null(already_included); + free_xml(parent); +} + +static void +single_schema(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + xmlNode *schema_node = NULL; + xmlNode *file_node = NULL; + int i = 0; + + pcmk__build_schema_xml_node(parent, "pacemaker-3.0", &already_included); + + assert_non_null(already_included); + assert_non_null(parent->children); + + /* Test that the result looks like this: + * + * <schemas> + * <schema version="pacemaker-3.0"> + * <file path="pacemaker-3.0.rng">CDATA</file> + * <file path="status-1.0.rng">CDATA</file> + * ... + * </schema> + * </schemas> + */ + schema_node = pcmk__xe_first_child(parent, NULL, NULL, NULL); + assert_string_equal("pacemaker-3.0", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + while (file_node != NULL && rngs1[i] != NULL) { + assert_string_equal(rngs1[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + g_list_free_full(already_included, free); + free_xml(parent); +} + +static void +multiple_schemas(void **state) +{ + GList *already_included = NULL; + xmlNode *parent = pcmk__xe_create(NULL, PCMK__XA_SCHEMAS); + xmlNode *schema_node = NULL; + xmlNode *file_node = NULL; + int i = 0; + + pcmk__build_schema_xml_node(parent, "pacemaker-2.0", &already_included); + pcmk__build_schema_xml_node(parent, "pacemaker-2.1", &already_included); + + assert_non_null(already_included); + assert_non_null(parent->children); + + /* Like single_schema, but make sure files aren't included multiple times + * when the function is called repeatedly. + */ + schema_node = pcmk__xe_first_child(parent, NULL, NULL, NULL); + assert_string_equal("pacemaker-2.0", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + while (file_node != NULL && rngs2[i] != NULL) { + assert_string_equal(rngs2[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + schema_node = pcmk__xe_next(schema_node); + assert_string_equal("pacemaker-2.1", + crm_element_value(schema_node, PCMK_XA_VERSION)); + + file_node = pcmk__xe_first_child(schema_node, NULL, NULL, NULL); + i = 0; + + while (file_node != NULL && rngs3[i] != NULL) { + assert_string_equal(rngs3[i], + crm_element_value(file_node, PCMK_XA_PATH)); + assert_int_equal(pcmk__xml_first_child(file_node)->type, XML_CDATA_SECTION_NODE); + + file_node = pcmk__xe_next(file_node); + i++; + } + + g_list_free_full(already_included, free); + free_xml(parent); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(invalid_name), + cmocka_unit_test(single_schema), + cmocka_unit_test(multiple_schemas)) diff --git a/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c b/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c new file mode 100644 index 0000000..19ec743 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__cmp_schemas_by_name_test.c @@ -0,0 +1,121 @@ +/* + * Copyright 2024 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/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +// NULL schema name defaults to the "none" schema +// @COMPAT none is deprecated since 2.1.8 + +static void +unknown_is_lesser(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.1", + "pacemaker-0.2") == 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.1", + "pacemaker-1.0") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.0", + "pacemaker-0.1") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.1", NULL) < 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, "pacemaker-0.0") > 0); + + /* @COMPAT pacemaker-next is deprecated since 2.1.5, + * and pacemaker-0.6 and pacemaker-0.7 since 2.1.8 + */ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-0.6", + "pacemaker-next") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-0.7") > 0); +} + +// @COMPAT none is deprecated since 2.1.8 +static void +none_is_greater(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name(NULL, NULL) == 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, PCMK_VALUE_NONE) == 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, NULL) == 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + PCMK_VALUE_NONE) == 0); + + assert_true(pcmk__cmp_schemas_by_name("pacemaker-3.0", + PCMK_VALUE_NONE) < 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + "pacemaker-1.0") > 0); + + // @COMPAT pacemaker-next is deprecated since 2.1.5 + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + PCMK_VALUE_NONE) < 0); + assert_true(pcmk__cmp_schemas_by_name(PCMK_VALUE_NONE, + "pacemaker-next") > 0); +} + +// @COMPAT pacemaker-next is deprecated since 2.1.5 +// @COMPAT none is deprecated since 2.1.8 +static void +next_is_before_none(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-next") == 0); + assert_true(pcmk__cmp_schemas_by_name(NULL, "pacemaker-next") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", NULL) < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-3.0", + "pacemaker-next") < 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-next", + "pacemaker-1.0") > 0); +} + +static void +known_numeric(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.0", + "pacemaker-1.0") == 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.2", + "pacemaker-1.0") > 0); + assert_true(pcmk__cmp_schemas_by_name("pacemaker-1.2", + "pacemaker-2.0") < 0); +} + +static void +case_insensitive(void **state) +{ + assert_true(pcmk__cmp_schemas_by_name("Pacemaker-1.0", + "pacemaker-1.0") == 0); + assert_true(pcmk__cmp_schemas_by_name("PACEMAKER-1.2", + "pacemaker-1.0") > 0); + assert_true(pcmk__cmp_schemas_by_name("PaceMaker-1.2", + "pacemaker-2.0") < 0); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(unknown_is_lesser), + cmocka_unit_test(none_is_greater), + cmocka_unit_test(next_is_before_none), + cmocka_unit_test(known_numeric), + cmocka_unit_test(case_insensitive)); diff --git a/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c b/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c new file mode 100644 index 0000000..25ba0f3 --- /dev/null +++ b/lib/common/tests/schemas/pcmk__find_x_0_schema_test.c @@ -0,0 +1,100 @@ +/* + * Copyright 2023-2024 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 <stdio.h> // NULL, rename() +#include <stdlib.h> // setenv(), unsetenv() +#include <glib.h> + +#include <crm/common/unittest_internal.h> +#include "crmcommon_private.h" + +#define SCHEMA_PREFIX PCMK__TEST_SCHEMA_DIR "/find_x_0/pacemaker-" + +static int +setup(void **state) +{ + // Use a unique schema directory so we can move files around + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR "/find_x_0", 1); + return 0; +} + +static int +teardown(void **state) +{ + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +assert_schema_0(int schema_index, const char *schema_name) +{ + GList *entry = NULL; + pcmk__schema_t *schema = NULL; + + entry = pcmk__find_x_0_schema(); + assert_non_null(entry); + + schema = entry->data; + assert_non_null(schema); + + assert_int_equal(schema->schema_index, schema_index); + assert_string_equal(schema->name, schema_name); +} + +static void +last_is_0(void **state) +{ + /* This loads all the schemas normally linked for unit testing, so we have + * many 1.x and 2.x schemas and a single pacemaker-3.0 schema at index 14. + */ + crm_schema_init(); + assert_schema_0(14, "pacemaker-3.0"); + crm_schema_cleanup(); +} + +static void +last_is_not_0(void **state) +{ + /* Disable the pacemaker-3.0 schema, so we now should get pacemaker-2.0 at + * index 3. + */ + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.rng", + SCHEMA_PREFIX "3.0.bak")); + crm_schema_init(); + assert_schema_0(3, "pacemaker-2.0"); + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.bak", + SCHEMA_PREFIX "3.0.rng")); + crm_schema_cleanup(); +} + +static void +schema_0_missing(void **state) +{ + /* Disable the pacemaker-3.0 and pacemaker-2.0 schemas, so we now should get + * pacemaker-2.1 at index 3. + */ + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.rng", + SCHEMA_PREFIX "3.0.bak")); + assert_int_equal(0, rename(SCHEMA_PREFIX "2.0.rng", + SCHEMA_PREFIX "2.0.bak")); + crm_schema_init(); + assert_schema_0(3, "pacemaker-2.1"); + assert_int_equal(0, rename(SCHEMA_PREFIX "2.0.bak", + SCHEMA_PREFIX "2.0.rng")); + assert_int_equal(0, rename(SCHEMA_PREFIX "3.0.bak", + SCHEMA_PREFIX "3.0.rng")); + crm_schema_cleanup(); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(last_is_0), + cmocka_unit_test(last_is_not_0), + cmocka_unit_test(schema_0_missing)) diff --git a/lib/common/tests/schemas/pcmk__get_schema_test.c b/lib/common/tests/schemas/pcmk__get_schema_test.c new file mode 100644 index 0000000..6513dfc --- /dev/null +++ b/lib/common/tests/schemas/pcmk__get_schema_test.c @@ -0,0 +1,81 @@ +/* + * Copyright 2023-2024 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/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> +#include "crmcommon_private.h" + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +assert_schema(const char *name, int expected_index) +{ + GList *schema_entry = NULL; + pcmk__schema_t *schema = NULL; + + schema_entry = pcmk__get_schema(name); + assert_non_null(schema_entry); + + schema = schema_entry->data; + assert_non_null(schema); + + assert_int_equal(schema->schema_index, expected_index); +} + +static void +unknown_schema(void **state) +{ + assert_null(pcmk__get_schema("")); + assert_null(pcmk__get_schema("blahblah")); + assert_null(pcmk__get_schema("pacemaker-2.47")); + assert_null(pcmk__get_schema("pacemaker-47.0")); +} + +static void +known_schema(void **state) +{ + // @COMPAT none is deprecated since 2.1.8 + assert_schema(NULL, 16); // defaults to "none" + + assert_schema("pacemaker-1.0", 0); + assert_schema("pacemaker-1.2", 1); + assert_schema("pacemaker-2.0", 3); + assert_schema("pacemaker-2.5", 8); + assert_schema("pacemaker-3.0", 14); +} + +static void +case_insensitive(void **state) +{ + assert_schema("PACEMAKER-1.0", 0); + assert_schema("pAcEmAkEr-2.0", 3); + assert_schema("paceMAKER-3.0", 14); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(unknown_schema), + cmocka_unit_test(known_schema), + cmocka_unit_test(case_insensitive)); diff --git a/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c b/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c new file mode 100644 index 0000000..68744ef --- /dev/null +++ b/lib/common/tests/schemas/pcmk__schema_files_later_than_test.c @@ -0,0 +1,106 @@ +/* + * Copyright 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_internal.h> + +#include <crm/common/unittest_internal.h> +#include <crm/common/lists_internal.h> + +#include <glib.h> + +static int +setup(void **state) +{ + setenv("PCMK_schema_directory", PCMK__TEST_SCHEMA_DIR, 1); + crm_schema_init(); + return 0; +} + +static int +teardown(void **state) +{ + crm_schema_cleanup(); + unsetenv("PCMK_schema_directory"); + return 0; +} + +static void +invalid_name(void **state) +{ + assert_null(pcmk__schema_files_later_than("xyz")); + assert_null(pcmk__schema_files_later_than("pacemaker-")); +} + +static void +valid_name(void **state) +{ + GList *schemas = NULL; + + schemas = pcmk__schema_files_later_than("pacemaker-1.0"); + assert_int_equal(g_list_length(schemas), 18); + /* There is no "pacemaker-1.1". */ + assert_string_equal("pacemaker-1.2.rng", g_list_nth_data(schemas, 0)); + assert_string_equal("upgrade-1.3.xsl", g_list_nth_data(schemas, 1)); + assert_string_equal("pacemaker-1.3.rng", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.0.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-2.1.rng", g_list_nth_data(schemas, 4)); + assert_string_equal("pacemaker-2.2.rng", g_list_nth_data(schemas, 5)); + assert_string_equal("pacemaker-2.3.rng", g_list_nth_data(schemas, 6)); + assert_string_equal("pacemaker-2.4.rng", g_list_nth_data(schemas, 7)); + assert_string_equal("pacemaker-2.5.rng", g_list_nth_data(schemas, 8)); + assert_string_equal("pacemaker-2.6.rng", g_list_nth_data(schemas, 9)); + assert_string_equal("pacemaker-2.7.rng", g_list_nth_data(schemas, 10)); + assert_string_equal("pacemaker-2.8.rng", g_list_nth_data(schemas, 11)); + assert_string_equal("pacemaker-2.9.rng", g_list_nth_data(schemas, 12)); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 13)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 14)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 15)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 16)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 17)); + g_list_free_full(schemas, free); + + /* Adding .rng to the end of the schema we're requesting is also valid. */ + schemas = pcmk__schema_files_later_than("pacemaker-2.0.rng"); + assert_int_equal(g_list_length(schemas), 14); + assert_string_equal("pacemaker-2.1.rng", g_list_nth_data(schemas, 0)); + assert_string_equal("pacemaker-2.2.rng", g_list_nth_data(schemas, 1)); + assert_string_equal("pacemaker-2.3.rng", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.4.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-2.5.rng", g_list_nth_data(schemas, 4)); + assert_string_equal("pacemaker-2.6.rng", g_list_nth_data(schemas, 5)); + assert_string_equal("pacemaker-2.7.rng", g_list_nth_data(schemas, 6)); + assert_string_equal("pacemaker-2.8.rng", g_list_nth_data(schemas, 7)); + assert_string_equal("pacemaker-2.9.rng", g_list_nth_data(schemas, 8)); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 9)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 10)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 11)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 12)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 13)); + g_list_free_full(schemas, free); + + /* Check that "pacemaker-2.10" counts as later than "pacemaker-2.9". */ + schemas = pcmk__schema_files_later_than("pacemaker-2.9"); + assert_int_equal(g_list_length(schemas), 5); + assert_string_equal("upgrade-2.10-leave.xsl", g_list_nth_data(schemas, 0)); + assert_string_equal("upgrade-2.10-enter.xsl", g_list_nth_data(schemas, 1)); + assert_string_equal("upgrade-2.10.xsl", g_list_nth_data(schemas, 2)); + assert_string_equal("pacemaker-2.10.rng", g_list_nth_data(schemas, 3)); + assert_string_equal("pacemaker-3.0.rng", g_list_nth_data(schemas, 4)); + g_list_free_full(schemas, free); + + /* And then something way in the future that will never apply due to our + * special schema directory. + */ + schemas = pcmk__schema_files_later_than("pacemaker-9.0"); + assert_null(schemas); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(invalid_name), + cmocka_unit_test(valid_name)) diff --git a/lib/common/tests/scores/char2score_test.c b/lib/common/tests/scores/char2score_test.c index fbba12a..5d7252f 100644 --- a/lib/common/tests/scores/char2score_test.c +++ b/lib/common/tests/scores/char2score_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -32,9 +32,9 @@ bad_input(void **state) static void special_values(void **state) { - assert_int_equal(char2score("-INFINITY"), -CRM_SCORE_INFINITY); - assert_int_equal(char2score("INFINITY"), CRM_SCORE_INFINITY); - assert_int_equal(char2score("+INFINITY"), CRM_SCORE_INFINITY); + assert_int_equal(char2score("-INFINITY"), -PCMK_SCORE_INFINITY); + assert_int_equal(char2score("INFINITY"), PCMK_SCORE_INFINITY); + assert_int_equal(char2score("+INFINITY"), PCMK_SCORE_INFINITY); pcmk__score_red = 10; pcmk__score_green = 20; @@ -56,8 +56,10 @@ special_values(void **state) static void outside_limits(void **state) { - assert_int_equal(char2score(B(CRM_SCORE_INFINITY) "00"), CRM_SCORE_INFINITY); - assert_int_equal(char2score("-" B(CRM_SCORE_INFINITY) "00"), -CRM_SCORE_INFINITY); + assert_int_equal(char2score(B(PCMK_SCORE_INFINITY) "00"), + PCMK_SCORE_INFINITY); + assert_int_equal(char2score("-" B(PCMK_SCORE_INFINITY) "00"), + -PCMK_SCORE_INFINITY); } static void diff --git a/lib/common/tests/scores/pcmk__add_scores_test.c b/lib/common/tests/scores/pcmk__add_scores_test.c index 1309659..952cf97 100644 --- a/lib/common/tests/scores/pcmk__add_scores_test.c +++ b/lib/common/tests/scores/pcmk__add_scores_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,48 +14,71 @@ static void score1_minus_inf(void **state) { - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, -1), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, 0), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, 1), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY, CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, + -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, -1), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, 0), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, 1), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY, + PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); } static void score2_minus_inf(void **state) { - assert_int_equal(pcmk__add_scores(-1, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(0, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(1, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, -CRM_SCORE_INFINITY), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-1, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(0, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(1, -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, + -PCMK_SCORE_INFINITY), + -PCMK_SCORE_INFINITY); } static void score1_pos_inf(void **state) { - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, -1), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, 0), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY, 1), CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, -1), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, 0), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY, 1), + PCMK_SCORE_INFINITY); } static void score2_pos_inf(void **state) { - assert_int_equal(pcmk__add_scores(-1, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(0, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(1, CRM_SCORE_INFINITY), CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-1, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(0, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(1, PCMK_SCORE_INFINITY), + PCMK_SCORE_INFINITY); } static void result_infinite(void **state) { - assert_int_equal(pcmk__add_scores(INT_MAX, INT_MAX), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(INT_MIN, INT_MIN), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(2000000, 50), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(CRM_SCORE_INFINITY/2, CRM_SCORE_INFINITY/2), CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-CRM_SCORE_INFINITY/2, -CRM_SCORE_INFINITY/2), -CRM_SCORE_INFINITY); - assert_int_equal(pcmk__add_scores(-4000000, 50), -CRM_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(INT_MAX, INT_MAX), PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(INT_MIN, INT_MIN), -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(2000000, 50), PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(PCMK_SCORE_INFINITY/2, + PCMK_SCORE_INFINITY/2), + PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-PCMK_SCORE_INFINITY/2, + -PCMK_SCORE_INFINITY/2), + -PCMK_SCORE_INFINITY); + assert_int_equal(pcmk__add_scores(-4000000, 50), -PCMK_SCORE_INFINITY); } static void diff --git a/lib/common/tests/scores/pcmk_readable_score_test.c b/lib/common/tests/scores/pcmk_readable_score_test.c index ae24159..c3d66f6 100644 --- a/lib/common/tests/scores/pcmk_readable_score_test.c +++ b/lib/common/tests/scores/pcmk_readable_score_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -14,10 +14,10 @@ static void outside_limits(void **state) { - assert_string_equal(pcmk_readable_score(CRM_SCORE_INFINITY * 2), - CRM_INFINITY_S); - assert_string_equal(pcmk_readable_score(-CRM_SCORE_INFINITY * 2), - CRM_MINUS_INFINITY_S); + assert_string_equal(pcmk_readable_score(PCMK_SCORE_INFINITY * 2), + PCMK_VALUE_INFINITY); + assert_string_equal(pcmk_readable_score(-PCMK_SCORE_INFINITY * 2), + PCMK_VALUE_MINUS_INFINITY); } static void diff --git a/lib/common/tests/strings/Makefile.am b/lib/common/tests/strings/Makefile.am index e66af0d..439b6bf 100644 --- a/lib/common/tests/strings/Makefile.am +++ b/lib/common/tests/strings/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -16,7 +16,6 @@ check_PROGRAMS = crm_get_msec_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 \ diff --git a/lib/common/tests/strings/crm_get_msec_test.c b/lib/common/tests/strings/crm_get_msec_test.c index 5da548b..14b87cf 100644 --- a/lib/common/tests/strings/crm_get_msec_test.c +++ b/lib/common/tests/strings/crm_get_msec_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -19,6 +19,11 @@ bad_input(void **state) { 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); + + assert_int_equal(crm_get_msec("3.xs"), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec(" 3. xs "), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec("3.14xs"), PCMK__PARSE_INT_DEFAULT); + assert_int_equal(crm_get_msec(" 3.14 xs "), PCMK__PARSE_INT_DEFAULT); } static void @@ -28,6 +33,7 @@ good_input(void **state) { assert_int_equal(crm_get_msec("\t100\n"), 100000); assert_int_equal(crm_get_msec("100ms"), 100); + assert_int_equal(crm_get_msec(" 100 ms "), 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); @@ -37,6 +43,28 @@ good_input(void **state) { 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); + + assert_int_equal(crm_get_msec("3."), 3000); + assert_int_equal(crm_get_msec(" 3. ms "), 3); + assert_int_equal(crm_get_msec("3.14"), 3000); + assert_int_equal(crm_get_msec(" 3.14 ms "), 3); + + // Questionable + assert_int_equal(crm_get_msec("3.14."), 3000); + assert_int_equal(crm_get_msec(" 3.14. ms "), 3); + assert_int_equal(crm_get_msec("3.14.159"), 3000); + assert_int_equal(crm_get_msec(" 3.14.159 "), 3000); + assert_int_equal(crm_get_msec("3.14.159ms"), 3); + assert_int_equal(crm_get_msec(" 3.14.159 ms "), 3); + + // Questionable + assert_int_equal(crm_get_msec(" 100 mshr "), 100); + assert_int_equal(crm_get_msec(" 100 ms hr "), 100); + assert_int_equal(crm_get_msec(" 100 sasdf "), 100000); + assert_int_equal(crm_get_msec(" 100 s asdf "), 100000); + assert_int_equal(crm_get_msec(" 3.14 shour "), 3000); + assert_int_equal(crm_get_msec(" 3.14 s hour "), 3000); + assert_int_equal(crm_get_msec(" 3.14 ms!@#$ "), 3); } static void diff --git a/lib/common/tests/strings/crm_str_to_boolean_test.c b/lib/common/tests/strings/crm_str_to_boolean_test.c index 3bd2e5d..1b0ba45 100644 --- a/lib/common/tests/strings/crm_str_to_boolean_test.c +++ b/lib/common/tests/strings/crm_str_to_boolean_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -40,6 +40,13 @@ is_true(void **state) { assert_true(ret); assert_int_equal(crm_str_to_boolean("1", &ret), 1); assert_true(ret); + + // Ensure it still validates the string with a NULL result argument + assert_int_equal(crm_str_to_boolean("true", NULL), 1); + assert_int_equal(crm_str_to_boolean("on", NULL), 1); + assert_int_equal(crm_str_to_boolean("yes", NULL), 1); + assert_int_equal(crm_str_to_boolean("y", NULL), 1); + assert_int_equal(crm_str_to_boolean("1", NULL), 1); } static void @@ -73,6 +80,13 @@ is_false(void **state) { assert_false(ret); assert_int_equal(crm_str_to_boolean("0", &ret), 1); assert_false(ret); + + // Ensure it still validates the string with a NULL result argument + assert_int_equal(crm_str_to_boolean("false", NULL), 1); + assert_int_equal(crm_str_to_boolean("off", NULL), 1); + assert_int_equal(crm_str_to_boolean("no", NULL), 1); + assert_int_equal(crm_str_to_boolean("n", NULL), 1); + assert_int_equal(crm_str_to_boolean("0", NULL), 1); } static void 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 deleted file mode 100644 index e70dfb4..0000000 --- a/lib/common/tests/strings/pcmk__char_in_any_str_test.c +++ /dev/null @@ -1,46 +0,0 @@ -/* - * 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 index 7b59d9d..813bcdb 100644 --- a/lib/common/tests/strings/pcmk__compress_test.c +++ b/lib/common/tests/strings/pcmk__compress_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -20,7 +20,7 @@ const char *SIMPLE_COMPRESSED = "BZh41AY&SYO\x1ai"; static void simple_compress(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 0, &result, &len), pcmk_rc_ok); @@ -30,7 +30,7 @@ simple_compress(void **state) static void max_too_small(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; assert_int_equal(pcmk__compress(SIMPLE_DATA, 40, 10, &result, &len), EFBIG); @@ -38,10 +38,11 @@ max_too_small(void **state) static void calloc_fails(void **state) { - char *result = calloc(1024, sizeof(char)); + char *result = pcmk__assert_alloc(1024, sizeof(char)); unsigned int len; - pcmk__assert_asserts( + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_calloc = true; // calloc() will return NULL expect_value(__wrap_calloc, nmemb, (size_t) ((40 * 1.01) + 601)); diff --git a/lib/common/tests/strings/pcmk__str_update_test.c b/lib/common/tests/strings/pcmk__str_update_test.c index 571031d..4a44fba 100644 --- a/lib/common/tests/strings/pcmk__str_update_test.c +++ b/lib/common/tests/strings/pcmk__str_update_test.c @@ -59,7 +59,8 @@ strdup_fails(void **state) { str = strdup("hello"); - pcmk__assert_asserts( + pcmk__assert_exits( + CRM_EX_OSERR, { pcmk__mock_strdup = true; // strdup() will return NULL expect_string(__wrap_strdup, s, "world"); diff --git a/lib/common/tests/utils/Makefile.am b/lib/common/tests/utils/Makefile.am index f028ce4..fb9d5c3 100644 --- a/lib/common/tests/utils/Makefile.am +++ b/lib/common/tests/utils/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2020-2023 the Pacemaker project contributors +# Copyright 2020-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -12,8 +12,6 @@ include $(top_srcdir)/mk/unittest.mk # Add "_test" to the end of all test program names to simplify .gitignore. check_PROGRAMS = compare_version_test \ - crm_meta_name_test \ - crm_meta_value_test \ crm_user_lookup_test \ pcmk_daemon_user_test \ pcmk_str_is_infinity_test \ @@ -21,10 +19,7 @@ check_PROGRAMS = compare_version_test \ pcmk__fail_attr_name_test \ pcmk__failcount_name_test \ pcmk__getpid_s_test \ - pcmk__lastfailure_name_test - -if WRAPPABLE_UNAME -check_PROGRAMS += pcmk_hostname_test -endif + pcmk__lastfailure_name_test \ + pcmk__realloc_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/utils/compare_version_test.c b/lib/common/tests/utils/compare_version_test.c index 35ebb63..d191f4a 100644 --- a/lib/common/tests/utils/compare_version_test.c +++ b/lib/common/tests/utils/compare_version_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2022-2023 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -46,6 +46,9 @@ shorter_versions(void **state) { assert_int_equal(compare_version("1.0", "1.0.1"), -1); assert_int_equal(compare_version("1.0.1", "1.0"), 1); + assert_int_equal(compare_version("1.0", "1"), 0); + assert_int_equal(compare_version("1", "1.2"), -1); + assert_int_equal(compare_version("1.2", "1"), 1); } PCMK__UNIT_TEST(NULL, NULL, diff --git a/lib/common/tests/utils/pcmk__realloc_test.c b/lib/common/tests/utils/pcmk__realloc_test.c new file mode 100644 index 0000000..62d51df --- /dev/null +++ b/lib/common/tests/utils/pcmk__realloc_test.c @@ -0,0 +1,69 @@ +/* + * Copyright 2024 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 +bad_size(void **state) +{ + char *ptr = NULL; + + pcmk__assert_asserts(pcmk__realloc(ptr, 0)); +} + +static void +realloc_fails(void **state) +{ + char *ptr = NULL; + + pcmk__assert_aborts( + { + pcmk__mock_realloc = true; // realloc() will return NULL + expect_any(__wrap_realloc, ptr); + expect_value(__wrap_realloc, size, 1000); + pcmk__realloc(ptr, 1000); + pcmk__mock_realloc = false; // Use real realloc() + } + ); +} + +static void +realloc_succeeds(void **state) +{ + char *ptr = NULL; + + /* We can't really test that the resulting pointer is the size we asked + * for - it might be larger if that's what the memory allocator decides + * to do. And anyway, testing realloc isn't really the point. All we + * want to do here is make sure the function works when given good input. + */ + + /* Allocate new memory */ + ptr = pcmk__realloc(ptr, 1000); + assert_non_null(ptr); + + /* Grow previously allocated memory */ + ptr = pcmk__realloc(ptr, 2000); + assert_non_null(ptr); + + /* Shrink previously allocated memory */ + ptr = pcmk__realloc(ptr, 500); + assert_non_null(ptr); + + free(ptr); +} + +PCMK__UNIT_TEST(NULL, NULL, + cmocka_unit_test(bad_size), + cmocka_unit_test(realloc_fails), + cmocka_unit_test(realloc_succeeds)) diff --git a/lib/common/tests/utils/pcmk_hostname_test.c b/lib/common/tests/utils/pcmk_hostname_test.c deleted file mode 100644 index 7329486..0000000 --- a/lib/common/tests/utils/pcmk_hostname_test.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - * 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 "mock_private.h" - -#include <sys/utsname.h> - -static void -uname_succeeded_test(void **state) -{ - char *retval; - - // Set uname() return value and buf parameter node name - pcmk__mock_uname = true; - - expect_any(__wrap_uname, buf); - will_return(__wrap_uname, 0); - will_return(__wrap_uname, "somename"); - - retval = pcmk_hostname(); - assert_non_null(retval); - assert_string_equal("somename", retval); - - free(retval); - - pcmk__mock_uname = false; -} - -static void -uname_failed_test(void **state) -{ - // Set uname() return value and buf parameter node name - pcmk__mock_uname = true; - - expect_any(__wrap_uname, buf); - will_return(__wrap_uname, -1); - will_return(__wrap_uname, NULL); - - assert_null(pcmk_hostname()); - - pcmk__mock_uname = false; -} - -PCMK__UNIT_TEST(NULL, NULL, - cmocka_unit_test(uname_succeeded_test), - cmocka_unit_test(uname_failed_test)) diff --git a/lib/common/tests/xml/Makefile.am b/lib/common/tests/xml/Makefile.am index 465c950..9ed1620 100644 --- a/lib/common/tests/xml/Makefile.am +++ b/lib/common/tests/xml/Makefile.am @@ -1,5 +1,5 @@ # -# Copyright 2022-2023 the Pacemaker project contributors +# Copyright 2022-2024 the Pacemaker project contributors # # The version control history for this file may have further details. # @@ -11,7 +11,12 @@ 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 = pcmk__xe_foreach_child_test \ - pcmk__xe_match_test +check_PROGRAMS = crm_xml_init_test \ + pcmk__xe_copy_attrs_test \ + pcmk__xe_first_child_test \ + pcmk__xe_foreach_child_test \ + pcmk__xe_set_score_test \ + pcmk__xml_escape_test \ + pcmk__xml_needs_escape_test TESTS = $(check_PROGRAMS) diff --git a/lib/common/tests/xml/crm_xml_init_test.c b/lib/common/tests/xml/crm_xml_init_test.c new file mode 100644 index 0000000..1f78728 --- /dev/null +++ b/lib/common/tests/xml/crm_xml_init_test.c @@ -0,0 +1,230 @@ +/* + * Copyright 2023-2024 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/xml.h> +#include <crm/common/unittest_internal.h> +#include <crm/common/xml_internal.h> + +#include "crmcommon_private.h" + +/* Copied from lib/common/xml.c */ +#define XML_DOC_PRIVATE_MAGIC 0x81726354UL +#define XML_NODE_PRIVATE_MAGIC 0x54637281UL + +static int +setup(void **state) { + crm_xml_init(); + return 0; +} + +static int +teardown(void **state) { + crm_xml_cleanup(); + return 0; +} + +static void +buffer_scheme_test(void **state) { + assert_int_equal(XML_BUFFER_ALLOC_DOUBLEIT, xmlGetBufferAllocationScheme()); +} + +/* These functions also serve as unit tests of the static new_private_data + * function. We can't test free_private_data because libxml will call that as + * part of freeing everything else. By the time we'd get back into a unit test + * where we could check that private members are NULL, the structure containing + * the private data would have been freed. + * + * This could probably be tested with a lot of function mocking, but that + * doesn't seem worth it. + */ + +static void +create_document_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + + /* Double check things */ + assert_non_null(doc); + assert_int_equal(doc->type, XML_DOCUMENT_NODE); + + /* Check that the private data is initialized correctly */ + docpriv = doc->_private; + assert_non_null(docpriv); + assert_int_equal(docpriv->check, XML_DOC_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeDoc(doc); +} + +static void +create_element_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_ELEMENT_NODE); + + /* Check that the private data is initialized correctly */ + priv = node->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_attr_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL); + xmlAttrPtr attr = xmlNewProp(node, (pcmkXmlStr) PCMK_XA_NAME, + (pcmkXmlStr) "dummy-value"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(attr); + assert_int_equal(attr->type, XML_ATTRIBUTE_NODE); + + /* Check that the private data is initialized correctly */ + priv = attr->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_comment_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocComment(doc, (pcmkXmlStr) "blahblah"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_COMMENT_NODE); + + /* Check that the private data is initialized correctly */ + priv = node->_private; + assert_non_null(priv); + assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC); + assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created)); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_text_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewDocText(doc, (pcmkXmlStr) "blahblah"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_TEXT_NODE); + + /* Check that no private data was created */ + priv = node->_private; + assert_null(priv); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +static void +create_dtd_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlDtdPtr dtd = xmlNewDtd(doc, (pcmkXmlStr) PCMK_XA_NAME, + (pcmkXmlStr) "externalId", + (pcmkXmlStr) "systemId"); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(dtd); + assert_int_equal(dtd->type, XML_DTD_NODE); + + /* Check that no private data was created */ + priv = dtd->_private; + assert_null(priv); + + /* Clean up */ + /* If you call xmlFreeDtd before xmlFreeDoc, you get a segfault */ + xmlFreeDoc(doc); +} + +static void +create_cdata_node(void **state) { + xml_doc_private_t *docpriv = NULL; + xml_node_private_t *priv = NULL; + xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION); + xmlNodePtr node = xmlNewCDataBlock(doc, (pcmkXmlStr) "blahblah", 8); + + /* Adding a node to the document marks it as dirty */ + docpriv = doc->_private; + assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty)); + + /* Double check things */ + assert_non_null(node); + assert_int_equal(node->type, XML_CDATA_SECTION_NODE); + + /* Check that no private data was created */ + priv = node->_private; + assert_null(priv); + + /* Clean up */ + xmlFreeNode(node); + xmlFreeDoc(doc); +} + +PCMK__UNIT_TEST(setup, teardown, + cmocka_unit_test(buffer_scheme_test), + cmocka_unit_test(create_document_node), + cmocka_unit_test(create_element_node), + cmocka_unit_test(create_attr_node), + cmocka_unit_test(create_comment_node), + cmocka_unit_test(create_text_node), + cmocka_unit_test(create_dtd_node), + cmocka_unit_test(create_cdata_node)); diff --git a/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c new file mode 100644 index 0000000..146317c --- /dev/null +++ b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c @@ -0,0 +1,188 @@ + /* + * Copyright 2022-2024 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) +{ + // This test dumps core via CRM_CHECK() + xmlNode *xml = pcmk__xe_create(NULL, "test"); + + assert_int_equal(pcmk__xe_copy_attrs(NULL, NULL, pcmk__xaf_none), EINVAL); + assert_int_equal(pcmk__xe_copy_attrs(NULL, xml, pcmk__xaf_none), EINVAL); + assert_int_equal(pcmk__xe_copy_attrs(xml, NULL, pcmk__xaf_none), EINVAL); + assert_ptr_equal(xml->properties, NULL); + + free_xml(xml); +} + +static void +no_source_attrs(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + // Ensure copying from empty source doesn't create target properties + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_ptr_equal(target->properties, NULL); + + // Ensure copying from empty source doesn't delete target attributes + crm_xml_add(target, "attr", "value"); + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "attr"), "value"); + + free_xml(src); + free_xml(target); +} + +static void +copy_one(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "value"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + free_xml(src); + free_xml(target); +} + +static void +copy_multiple(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + pcmk__xe_set_props(src, + "attr1", "value1", + "attr2", "value2", + "attr3", "value3", + NULL); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr1"), + crm_element_value(target, "attr1")); + assert_string_equal(crm_element_value(src, "attr2"), + crm_element_value(target, "attr2")); + assert_string_equal(crm_element_value(src, "attr3"), + crm_element_value(target, "attr3")); + + free_xml(src); + free_xml(target); +} + +static void +overwrite(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "src_value"); + crm_xml_add(target, "attr", "target_value"); + + // Overwrite enabled by default + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + free_xml(src); + free_xml(target); +} + +static void +no_overwrite(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "attr", "src_value"); + crm_xml_add(target, "attr", "target_value"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite), + pcmk_rc_ok); + assert_string_not_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + // no_overwrite doesn't prevent copy if there's no conflict + pcmk__xe_remove_attr(target, "attr"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite), + pcmk_rc_ok); + assert_string_equal(crm_element_value(src, "attr"), + crm_element_value(target, "attr")); + + free_xml(src); + free_xml(target); +} + +static void +score_update(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++"); + crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2"); + crm_xml_add(target, "plus_plus_attr", "1"); + crm_xml_add(target, "plus_two_attr", "1"); + + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_score_update), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "plus_plus_attr"), "2"); + assert_string_equal(crm_element_value(target, "plus_two_attr"), "3"); + + free_xml(src); + free_xml(target); +} + +static void +no_score_update(void **state) +{ + xmlNode *src = pcmk__xe_create(NULL, "test"); + xmlNode *target = pcmk__xe_create(NULL, "test"); + + crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++"); + crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2"); + crm_xml_add(target, "plus_plus_attr", "1"); + crm_xml_add(target, "plus_two_attr", "1"); + + // Score update disabled by default + assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none), + pcmk_rc_ok); + assert_string_equal(crm_element_value(target, "plus_plus_attr"), + "plus_plus_attr++"); + assert_string_equal(crm_element_value(target, "plus_two_attr"), + "plus_two_attr+=2"); + + free_xml(src); + free_xml(target); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_args), + cmocka_unit_test(no_source_attrs), + cmocka_unit_test(copy_one), + cmocka_unit_test(copy_multiple), + cmocka_unit_test(overwrite), + cmocka_unit_test(no_overwrite), + cmocka_unit_test(score_update), + cmocka_unit_test(no_score_update)); diff --git a/lib/common/tests/xml/pcmk__xe_match_test.c b/lib/common/tests/xml/pcmk__xe_first_child_test.c index be2c949..64b90b0 100644 --- a/lib/common/tests/xml/pcmk__xe_match_test.c +++ b/lib/common/tests/xml/pcmk__xe_first_child_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022 the Pacemaker project contributors + * Copyright 2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,97 +9,97 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> const char *str1 = "<xml>\n" " <!-- This is an A node -->\n" - " <nodeA attrA=\"123\" " XML_ATTR_ID "=\"1\">\n" + " <nodeA attrA=\"123\" " PCMK_XA_ID "=\"1\">\n" " content\n" " </nodeA>\n" " <!-- This is an A node -->\n" - " <nodeA attrA=\"456\" " XML_ATTR_ID "=\"2\">\n" + " <nodeA attrA=\"456\" " PCMK_XA_ID "=\"2\">\n" " content\n" " </nodeA>\n" " <!-- This is an A node -->\n" - " <nodeA attrB=\"XYZ\" " XML_ATTR_ID "=\"3\">\n" + " <nodeA attrB=\"XYZ\" " PCMK_XA_ID "=\"3\">\n" " content\n" " </nodeA>\n" " <!-- This is a B node -->\n" - " <nodeB attrA=\"123\" " XML_ATTR_ID "=\"4\">\n" + " <nodeB attrA=\"123\" " PCMK_XA_ID "=\"4\">\n" " content\n" " </nodeA>\n" " <!-- This is a B node -->\n" - " <nodeB attrB=\"ABC\" " XML_ATTR_ID "=\"5\">\n" + " <nodeB attrB=\"ABC\" " PCMK_XA_ID "=\"5\">\n" " content\n" " </nodeA>\n" "</xml>"; static void bad_input(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); - assert_null(pcmk__xe_match(NULL, NULL, NULL, NULL)); - assert_null(pcmk__xe_match(NULL, NULL, NULL, "attrX")); + assert_null(pcmk__xe_first_child(NULL, NULL, NULL, NULL)); + assert_null(pcmk__xe_first_child(NULL, NULL, NULL, "attrX")); free_xml(xml); } static void not_found(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* No node with an attrX attribute */ - assert_null(pcmk__xe_match(xml, NULL, "attrX", NULL)); + assert_null(pcmk__xe_first_child(xml, NULL, "attrX", NULL)); /* No nodeX node */ - assert_null(pcmk__xe_match(xml, "nodeX", NULL, NULL)); + assert_null(pcmk__xe_first_child(xml, "nodeX", NULL, NULL)); /* No nodeA node with attrX */ - assert_null(pcmk__xe_match(xml, "nodeA", "attrX", NULL)); + assert_null(pcmk__xe_first_child(xml, "nodeA", "attrX", NULL)); /* No nodeA node with attrA=XYZ */ - assert_null(pcmk__xe_match(xml, "nodeA", "attrA", "XYZ")); + assert_null(pcmk__xe_first_child(xml, "nodeA", "attrA", "XYZ")); free_xml(xml); } static void find_attrB(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); xmlNode *result = NULL; /* Find the first node with attrB */ - result = pcmk__xe_match(xml, NULL, "attrB", NULL); + result = pcmk__xe_first_child(xml, NULL, "attrB", NULL); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "3"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "3"); /* Find the first nodeB with attrB */ - result = pcmk__xe_match(xml, "nodeB", "attrB", NULL); + result = pcmk__xe_first_child(xml, "nodeB", "attrB", NULL); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "5"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "5"); free_xml(xml); } static void find_attrA_matching(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); xmlNode *result = NULL; /* Find attrA=456 */ - result = pcmk__xe_match(xml, NULL, "attrA", "456"); + result = pcmk__xe_first_child(xml, NULL, "attrA", "456"); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "2"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "2"); /* Find a nodeB with attrA=123 */ - result = pcmk__xe_match(xml, "nodeB", "attrA", "123"); + result = pcmk__xe_first_child(xml, "nodeB", "attrA", "123"); assert_non_null(result); - assert_string_equal(crm_element_value(result, "id"), "4"); + assert_string_equal(crm_element_value(result, PCMK_XA_ID), "4"); free_xml(xml); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(bad_input), cmocka_unit_test(not_found), cmocka_unit_test(find_attrB), diff --git a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c index ffb9171..a833dde 100644 --- a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c +++ b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2022-2023 the Pacemaker project contributors + * Copyright 2022-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -36,7 +36,7 @@ const char *str1 = static void bad_input(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); pcmk__assert_asserts(pcmk__xe_foreach_child(xml, NULL, NULL, NULL)); @@ -45,7 +45,7 @@ bad_input(void **state) { static void name_given_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -58,7 +58,7 @@ name_given_test(void **state) { static void no_name_given_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -71,7 +71,7 @@ no_name_given_test(void **state) { static void name_doesnt_exist_test(void **state) { - xmlNode *xml = string2xml(str1); + xmlNode *xml = pcmk__xml_parse(str1); pcmk__xe_foreach_child(xml, "xxx", compare_name_handler, NULL); free_xml(xml); } @@ -100,7 +100,7 @@ const char *str2 = static void multiple_levels_test(void **state) { - xmlNode *xml = string2xml(str2); + xmlNode *xml = pcmk__xml_parse(str2); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -112,7 +112,7 @@ multiple_levels_test(void **state) { static void multiple_levels_no_name_test(void **state) { - xmlNode *xml = string2xml(str2); + xmlNode *xml = pcmk__xml_parse(str2); /* The handler should be called once for every <level1> node. */ expect_function_call(compare_name_handler); @@ -147,7 +147,7 @@ static int any_of_handler(xmlNode *xml, void *userdata) { static void any_of_test(void **state) { - xmlNode *xml = string2xml(str3); + xmlNode *xml = pcmk__xml_parse(str3); /* The handler should be called once for every <nodeX> node. */ expect_function_call(any_of_handler); @@ -190,7 +190,7 @@ static int stops_on_third_handler(xmlNode *xml, void *userdata) { static void one_of_test(void **state) { - xmlNode *xml = string2xml(str3); + xmlNode *xml = pcmk__xml_parse(str3); /* The handler should be called once. */ expect_function_call(stops_on_first_handler); @@ -205,7 +205,7 @@ one_of_test(void **state) { free_xml(xml); } -PCMK__UNIT_TEST(NULL, NULL, +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, cmocka_unit_test(bad_input), cmocka_unit_test(name_given_test), cmocka_unit_test(no_name_given_test), diff --git a/lib/common/tests/xml/pcmk__xe_set_score_test.c b/lib/common/tests/xml/pcmk__xe_set_score_test.c new file mode 100644 index 0000000..deb85b0 --- /dev/null +++ b/lib/common/tests/xml/pcmk__xe_set_score_test.c @@ -0,0 +1,188 @@ +/* + * Copyright 2022-2024 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> + +#include "crmcommon_private.h" // pcmk__xe_set_score() + +/*! + * \internal + * \brief Update an XML attribute value and check it against a reference value + * + * The attribute name is hard-coded as \c "X". + * + * \param[in] initial Initial value + * \param[in] new Value to set + * \param[in] reference_val Expected attribute value after update + * \param[in] reference_rc Expected return code from \c pcmk__xe_set_score() + */ +static void +assert_set_score(const char *initial, const char *new, + const char *reference_val, int reference_rc) +{ + const char *name = "X"; + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); + + crm_xml_add(test_xml, name, initial); + assert_int_equal(pcmk__xe_set_score(test_xml, name, new), reference_rc); + assert_string_equal(crm_element_value(test_xml, name), reference_val); + + free_xml(test_xml); +} + +static void +value_is_name_plus_plus(void **state) +{ + assert_set_score("5", "X++", "6", pcmk_rc_ok); +} + +static void +value_is_name_plus_equals_integer(void **state) +{ + assert_set_score("5", "X+=2", "7", pcmk_rc_ok); +} + +// NULL input + +static void +target_is_NULL(void **state) +{ + // Dumps core via CRM_CHECK() + assert_int_equal(pcmk__xe_set_score(NULL, "X", "X++"), EINVAL); +} + +static void +name_is_NULL(void **state) +{ + xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml"); + + crm_xml_add(test_xml, "X", "5"); + + // Dumps core via CRM_CHECK() + assert_int_equal(pcmk__xe_set_score(test_xml, NULL, "X++"), EINVAL); + assert_string_equal(crm_element_value(test_xml, "X"), "5"); + + free_xml(test_xml); +} + +static void +value_is_NULL(void **state) +{ + assert_set_score("5", NULL, "5", pcmk_rc_ok); +} + +// the value input doesn't start with the name input + +static void +value_is_wrong_name(void **state) +{ + assert_set_score("5", "Y++", "Y++", pcmk_rc_ok); +} + +static void +value_is_only_an_integer(void **state) +{ + assert_set_score("5", "2", "2", pcmk_rc_ok); +} + +// non-integers + +static void +variable_is_initialized_to_be_non_numeric(void **state) +{ + assert_set_score("hello", "X++", "1", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_non_numeric_2(void **state) +{ + assert_set_score("hello", "X+=2", "2", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing(void **state) +{ + assert_set_score("5.01", "X++", "6", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing_2(void **state) +{ + assert_set_score("5.50", "X++", "6", pcmk_rc_ok); +} + +static void +variable_is_initialized_to_be_numeric_and_decimal_point_containing_3(void **state) +{ + assert_set_score("5.99", "X++", "6", pcmk_rc_ok); +} + +static void +value_is_non_numeric(void **state) +{ + assert_set_score("5", "X+=hello", "5", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing(void **state) +{ + assert_set_score("5", "X+=2.01", "7", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing_2(void **state) +{ + assert_set_score("5", "X+=1.50", "6", pcmk_rc_ok); +} + +static void +value_is_numeric_and_decimal_point_containing_3(void **state) +{ + assert_set_score("5", "X+=1.99", "6", pcmk_rc_ok); +} + +// undefined input + +static void +name_is_undefined(void **state) +{ + assert_set_score(NULL, "X++", "X++", pcmk_rc_ok); +} + +// large input + +static void +assignment_result_is_too_large(void **state) +{ + assert_set_score("5", "X+=100000000000", "1000000", pcmk_rc_ok); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(value_is_name_plus_plus), + cmocka_unit_test(value_is_name_plus_equals_integer), + cmocka_unit_test(target_is_NULL), + cmocka_unit_test(name_is_NULL), + cmocka_unit_test(value_is_NULL), + cmocka_unit_test(value_is_wrong_name), + cmocka_unit_test(value_is_only_an_integer), + cmocka_unit_test(variable_is_initialized_to_be_non_numeric), + cmocka_unit_test(variable_is_initialized_to_be_non_numeric_2), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_2), + cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_3), + cmocka_unit_test(value_is_non_numeric), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing_2), + cmocka_unit_test(value_is_numeric_and_decimal_point_containing_3), + cmocka_unit_test(name_is_undefined), + cmocka_unit_test(assignment_result_is_too_large)) diff --git a/lib/common/tests/xml/pcmk__xml_escape_test.c b/lib/common/tests/xml/pcmk__xml_escape_test.c new file mode 100644 index 0000000..8c6fd21 --- /dev/null +++ b/lib/common/tests/xml/pcmk__xml_escape_test.c @@ -0,0 +1,213 @@ +/* + * Copyright 2024 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_internal.h> + +#include "crmcommon_private.h" + +static void +assert_escape(const char *str, const char *reference, + enum pcmk__xml_escape_type type) +{ + gchar *buf = pcmk__xml_escape(str, type); + + assert_string_equal(buf, reference); + g_free(buf); +} + +static void +null_empty(void **state) +{ + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_text)); + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr)); + assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr_pretty)); + + assert_escape("", "", pcmk__xml_escape_text); + assert_escape("", "", pcmk__xml_escape_attr); + assert_escape("", "", pcmk__xml_escape_attr_pretty); +} + +static void +invalid_type(void **state) +{ + const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1; + + // Easier to ignore invalid type for NULL or empty string + assert_null(pcmk__xml_escape(NULL, type)); + assert_escape("", "", type); + + // Otherwise, assert if we somehow passed an invalid type + pcmk__assert_asserts(pcmk__xml_escape("he<>llo", type)); +} + +static void +escape_unchanged(void **state) +{ + // No escaped characters (note: this string includes single quote at end) + const char *unchanged = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "`~!@#$%^*()-_=+/|\\[]{}?.,'"; + + assert_escape(unchanged, unchanged, pcmk__xml_escape_text); + assert_escape(unchanged, unchanged, pcmk__xml_escape_attr); + assert_escape(unchanged, unchanged, pcmk__xml_escape_attr_pretty); +} + +// Ensure special characters get escaped at start, middle, and end + +static void +escape_left_angle(void **state) +{ + const char *l_angle = "<abc<def<"; + const char *l_angle_esc = PCMK__XML_ENTITY_LT "abc" + PCMK__XML_ENTITY_LT "def" PCMK__XML_ENTITY_LT; + + assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_text); + assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_attr); + assert_escape(l_angle, l_angle, pcmk__xml_escape_attr_pretty); +} + +static void +escape_right_angle(void **state) +{ + const char *r_angle = ">abc>def>"; + const char *r_angle_esc = PCMK__XML_ENTITY_GT "abc" + PCMK__XML_ENTITY_GT "def" PCMK__XML_ENTITY_GT; + + assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_text); + assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_attr); + assert_escape(r_angle, r_angle, pcmk__xml_escape_attr_pretty); +} + +static void +escape_ampersand(void **state) +{ + const char *ampersand = "&abc&def&"; + const char *ampersand_esc = PCMK__XML_ENTITY_AMP "abc" + PCMK__XML_ENTITY_AMP "def" PCMK__XML_ENTITY_AMP; + + assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_text); + assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_attr); + assert_escape(ampersand, ampersand, pcmk__xml_escape_attr_pretty); +} + +static void +escape_double_quote(void **state) +{ + const char *double_quote = "\"abc\"def\""; + const char *double_quote_esc_ref = PCMK__XML_ENTITY_QUOT "abc" + PCMK__XML_ENTITY_QUOT "def" + PCMK__XML_ENTITY_QUOT; + const char *double_quote_esc_backslash = "\\\"abc\\\"def\\\""; + + assert_escape(double_quote, double_quote, pcmk__xml_escape_text); + assert_escape(double_quote, double_quote_esc_ref, pcmk__xml_escape_attr); + assert_escape(double_quote, double_quote_esc_backslash, + pcmk__xml_escape_attr_pretty); +} + +static void +escape_newline(void **state) +{ + const char *newline = "\nabc\ndef\n"; + const char *newline_esc_ref = "
abc
def
"; + const char *newline_esc_backslash = "\\nabc\\ndef\\n"; + + assert_escape(newline, newline, pcmk__xml_escape_text); + assert_escape(newline, newline_esc_ref, pcmk__xml_escape_attr); + assert_escape(newline, newline_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_tab(void **state) +{ + const char *tab = "\tabc\tdef\t"; + const char *tab_esc_ref = "	abc	def	"; + const char *tab_esc_backslash = "\\tabc\\tdef\\t"; + + assert_escape(tab, tab, pcmk__xml_escape_text); + assert_escape(tab, tab_esc_ref, pcmk__xml_escape_attr); + assert_escape(tab, tab_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_carriage_return(void **state) +{ + const char *cr = "\rabc\rdef\r"; + const char *cr_esc_ref = "
abc
def
"; + const char *cr_esc_backslash = "\\rabc\\rdef\\r"; + + assert_escape(cr, cr_esc_ref, pcmk__xml_escape_text); + assert_escape(cr, cr_esc_ref, pcmk__xml_escape_attr); + assert_escape(cr, cr_esc_backslash, pcmk__xml_escape_attr_pretty); +} + +static void +escape_nonprinting(void **state) +{ + const char *nonprinting = "\a\x7F\x1B"; + const char *nonprinting_esc = ""; + + assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_text); + assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_attr); + assert_escape(nonprinting, nonprinting, pcmk__xml_escape_attr_pretty); +} + +static void +escape_utf8(void **state) +{ + /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide + * and should not be escaped. + */ + const char *chinese = "仅高级使用"; + const char *two_byte = "abc""\xCF\xA6""d<ef"; + const char *two_byte_esc = "abc""\xCF\xA6""d" PCMK__XML_ENTITY_LT "ef"; + + const char *three_byte = "abc""\xEF\x98\x98""d<ef"; + const char *three_byte_esc = "abc""\xEF\x98\x98""d" + PCMK__XML_ENTITY_LT "ef"; + + const char *four_byte = "abc""\xF0\x94\x81\x90""d<ef"; + const char *four_byte_esc = "abc""\xF0\x94\x81\x90""d" + PCMK__XML_ENTITY_LT "ef"; + + assert_escape(chinese, chinese, pcmk__xml_escape_text); + assert_escape(chinese, chinese, pcmk__xml_escape_attr); + assert_escape(chinese, chinese, pcmk__xml_escape_attr_pretty); + + assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_text); + assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_attr); + assert_escape(two_byte, two_byte, pcmk__xml_escape_attr_pretty); + + assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_text); + assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_attr); + assert_escape(three_byte, three_byte, pcmk__xml_escape_attr_pretty); + + assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_text); + assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_attr); + assert_escape(four_byte, four_byte, pcmk__xml_escape_attr_pretty); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_empty), + cmocka_unit_test(invalid_type), + cmocka_unit_test(escape_unchanged), + cmocka_unit_test(escape_left_angle), + cmocka_unit_test(escape_right_angle), + cmocka_unit_test(escape_ampersand), + cmocka_unit_test(escape_double_quote), + cmocka_unit_test(escape_newline), + cmocka_unit_test(escape_tab), + cmocka_unit_test(escape_carriage_return), + cmocka_unit_test(escape_nonprinting), + cmocka_unit_test(escape_utf8)); diff --git a/lib/common/tests/xml/pcmk__xml_needs_escape_test.c b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c new file mode 100644 index 0000000..612f61b --- /dev/null +++ b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c @@ -0,0 +1,337 @@ +/* + * Copyright 2024 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_internal.h> + +#include "crmcommon_private.h" + +static void +null_empty(void **state) +{ + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr_pretty)); +} + +static void +invalid_type(void **state) +{ + const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1; + + // Easier to ignore invalid type for NULL or empty string + assert_false(pcmk__xml_needs_escape(NULL, type)); + assert_false(pcmk__xml_needs_escape("", type)); + + // Otherwise, assert if we somehow passed an invalid type + pcmk__assert_asserts(pcmk__xml_needs_escape("he<>llo", type)); +} + +static void +escape_unchanged(void **state) +{ + // No escaped characters (note: this string includes single quote at end) + const char *unchanged = "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "0123456789" + "`~!@#$%^*()-_=+/|\\[]{}?.,'"; + + assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(unchanged, + pcmk__xml_escape_attr_pretty)); +} + +// Ensure special characters get escaped at start, middle, and end + +static void +escape_left_angle(void **state) +{ + const char *l_angle_left = "<abcdef"; + const char *l_angle_mid = "abc<def"; + const char *l_angle_right = "abcdef<"; + + assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(l_angle_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(l_angle_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(l_angle_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_right_angle(void **state) +{ + const char *r_angle_left = ">abcdef"; + const char *r_angle_mid = "abc>def"; + const char *r_angle_right = "abcdef>"; + + assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(r_angle_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(r_angle_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(r_angle_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_ampersand(void **state) +{ + const char *ampersand_left = "&abcdef"; + const char *ampersand_mid = "abc&def"; + const char *ampersand_right = "abcdef&"; + + assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(ampersand_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(ampersand_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(ampersand_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_double_quote(void **state) +{ + const char *double_quote_left = "\"abcdef"; + const char *double_quote_mid = "abc\"def"; + const char *double_quote_right = "abcdef\""; + + assert_false(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(double_quote_left, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(double_quote_mid, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(double_quote_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_newline(void **state) +{ + const char *newline_left = "\nabcdef"; + const char *newline_mid = "abc\ndef"; + const char *newline_right = "abcdef\n"; + + assert_false(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(newline_left, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(newline_mid, + pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(newline_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_tab(void **state) +{ + const char *tab_left = "\tabcdef"; + const char *tab_mid = "abc\tdef"; + const char *tab_right = "abcdef\t"; + + assert_false(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(tab_right, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_carriage_return(void **state) +{ + const char *cr_left = "\rabcdef"; + const char *cr_mid = "abc\rdef"; + const char *cr_right = "abcdef\r"; + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr)); + + assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr_pretty)); + assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr_pretty)); +} + +static void +escape_nonprinting(void **state) +{ + const char *alert_left = "\aabcdef"; + const char *alert_mid = "abc\adef"; + const char *alert_right = "abcdef\a"; + + const char *delete_left = "\x7F""abcdef"; + const char *delete_mid = "abc\x7F""def"; + const char *delete_right = "abcdef\x7F"; + + const char *nonprinting_all = "\a\x7F\x1B"; + + assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(alert_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(alert_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(alert_right, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_text)); + + assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_attr)); + assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_attr)); + + assert_false(pcmk__xml_needs_escape(delete_left, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(delete_mid, + pcmk__xml_escape_attr_pretty)); + assert_false(pcmk__xml_needs_escape(delete_right, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(nonprinting_all, + pcmk__xml_escape_attr_pretty)); +} + +static void +escape_utf8(void **state) +{ + /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide + * and should not be escaped. + */ + const char *chinese = "仅高级使用"; + const char *two_byte = "abc""\xCF\xA6""def"; + const char *two_byte_special = "abc""\xCF\xA6""d<ef"; + const char *three_byte = "abc""\xEF\x98\x98""def"; + const char *three_byte_special = "abc""\xEF\x98\x98""d<ef"; + const char *four_byte = "abc""\xF0\x94\x81\x90""def"; + const char *four_byte_special = "abc""\xF0\x94\x81\x90""d<ef"; + + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(two_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(two_byte_special, + pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(three_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(three_byte_special, + pcmk__xml_escape_attr_pretty)); + + assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_text)); + assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(four_byte, + pcmk__xml_escape_attr_pretty)); + + assert_true(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_text)); + assert_true(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_attr)); + assert_false(pcmk__xml_needs_escape(four_byte_special, + pcmk__xml_escape_attr_pretty)); +} + +PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL, + cmocka_unit_test(null_empty), + cmocka_unit_test(invalid_type), + cmocka_unit_test(escape_unchanged), + cmocka_unit_test(escape_left_angle), + cmocka_unit_test(escape_right_angle), + cmocka_unit_test(escape_ampersand), + cmocka_unit_test(escape_double_quote), + cmocka_unit_test(escape_newline), + cmocka_unit_test(escape_tab), + cmocka_unit_test(escape_carriage_return), + cmocka_unit_test(escape_nonprinting), + cmocka_unit_test(escape_utf8)); diff --git a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c index 3922b34..86500c2 100644 --- a/lib/common/tests/xpath/pcmk__xpath_node_id_test.c +++ b/lib/common/tests/xpath/pcmk__xpath_node_id_test.c @@ -1,5 +1,5 @@ /* - * Copyright 2021-2022 the Pacemaker project contributors + * Copyright 2021-2024 the Pacemaker project contributors * * The version control history for this file may have further details. * @@ -9,14 +9,14 @@ #include <crm_internal.h> -#include <crm/msg_xml.h> +#include <crm/common/xml.h> #include <crm/common/unittest_internal.h> #include <crm/common/xml_internal.h> static void empty_input(void **state) { - assert_null(pcmk__xpath_node_id(NULL, "lrm")); - assert_null(pcmk__xpath_node_id("", "lrm")); + assert_null(pcmk__xpath_node_id(NULL, PCMK__XE_LRM)); + assert_null(pcmk__xpath_node_id("", PCMK__XE_LRM)); assert_null(pcmk__xpath_node_id("/blah/blah", NULL)); assert_null(pcmk__xpath_node_id("/blah/blah", "")); assert_null(pcmk__xpath_node_id(NULL, NULL)); @@ -24,30 +24,31 @@ empty_input(void **state) { static void no_quotes(void **state) { - const char *xpath = "/some/xpath/lrm[@" XML_ATTR_ID "=xyz]"; - pcmk__assert_asserts(pcmk__xpath_node_id(xpath, "lrm")); + const char *xpath = "/some/xpath/" PCMK__XE_LRM "[@" PCMK_XA_ID "=xyz]"; + pcmk__assert_asserts(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); } static void not_present(void **state) { - const char *xpath = "/some/xpath/string[@" XML_ATTR_ID "='xyz']"; - assert_null(pcmk__xpath_node_id(xpath, "lrm")); + const char *xpath = "/some/xpath/string[@" PCMK_XA_ID "='xyz']"; + assert_null(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); - xpath = "/some/xpath/containing[@" XML_ATTR_ID "='lrm']"; - assert_null(pcmk__xpath_node_id(xpath, "lrm")); + xpath = "/some/xpath/containing[@" PCMK_XA_ID "='" PCMK__XE_LRM "']"; + assert_null(pcmk__xpath_node_id(xpath, PCMK__XE_LRM)); } static void present(void **state) { char *s = NULL; - const char *xpath = "/some/xpath/containing/lrm[@" XML_ATTR_ID "='xyz']"; + const char *xpath = "/some/xpath/containing" + "/" PCMK__XE_LRM "[@" PCMK_XA_ID "='xyz']"; - s = pcmk__xpath_node_id(xpath, "lrm"); + s = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); assert_int_equal(strcmp(s, "xyz"), 0); free(s); - xpath = "/some/other/lrm[@" XML_ATTR_ID "='xyz']/xpath"; - s = pcmk__xpath_node_id(xpath, "lrm"); + xpath = "/some/other/" PCMK__XE_LRM "[@" PCMK_XA_ID "='xyz']/xpath"; + s = pcmk__xpath_node_id(xpath, PCMK__XE_LRM); assert_int_equal(strcmp(s, "xyz"), 0); free(s); } |