diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am index 18de1c97e3..5b8e0e66ab 100644 --- a/lib/common/tests/rules/Makefile.am +++ b/lib/common/tests/rules/Makefile.am @@ -1,24 +1,25 @@ # # 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_op_expression_test \ pcmk__parse_comparison_test \ pcmk__parse_source_test \ pcmk__parse_type_test \ pcmk__replace_submatches_test \ pcmk__unpack_duration_test TESTS = $(check_PROGRAMS) 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 0000000000..85d8045c9d --- /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 + +#include +#include + +#include +#include +#include +#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(NULL, 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))