diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am index a4e534431d..dd0df9c5b5 100644 --- a/lib/common/tests/rules/Makefile.am +++ b/lib/common/tests/rules/Makefile.am @@ -1,27 +1,28 @@ # # 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_comparison_test \ pcmk__parse_source_test \ pcmk__parse_type_test \ pcmk__replace_submatches_test \ - pcmk__unpack_duration_test + pcmk__unpack_duration_test \ + pcmk_evaluate_rule_test TESTS = $(check_PROGRAMS) 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 0000000000..8a1bdd9d56 --- /dev/null +++ b/lib/common/tests/rules/pcmk_evaluate_rule_test.c @@ -0,0 +1,383 @@ +/* + * 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 + +#include +#include + +#include +#include +#include + +/* + * 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' />" \ + "" + +static void +null_invalid(void **state) +{ +#if 0 + xmlNode *xml = NULL; +#endif + crm_time_t *next_change = crm_time_new_undefined(); + + assert_int_equal(pcmk_evaluate_rule(NULL, NULL, next_change), + pcmk_rc_unpack_error); + +#if 0 // Currently segfaults + xml = pcmk__xml_parse(RULE_OP); + assert_int_equal(pcmk_evaluate_rule(xml, NULL, next_change), EINVAL); + free_xml(xml); +#endif + + assert_int_equal(pcmk_evaluate_rule(NULL, &rule_input, next_change), + pcmk_rc_unpack_error); + + 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' />" \ + "" + +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 "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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' />" \ + "" + +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(NULL, 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))