diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am index 084a0fda27..18de1c97e3 100644 --- a/lib/common/tests/rules/Makefile.am +++ b/lib/common/tests/rules/Makefile.am @@ -1,23 +1,24 @@ # # 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__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_attr_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c new file mode 100644 index 0000000000..1bd41e02a0 --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c @@ -0,0 +1,834 @@ +/* + * 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 + */ + +#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), + pcmk_rc_unpack_error); + assert_int_equal(pcmk__evaluate_attr_expression(xml, NULL), + pcmk_rc_op_unsatisfied); + assert_int_equal(pcmk__evaluate_attr_expression(NULL, &rule_input), + pcmk_rc_unpack_error); + + 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_op_unsatisfied); +} + +/* + * 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_op_unsatisfied); +} + +#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_op_unsatisfied); +} + +#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_op_unsatisfied); +} + +#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_op_unsatisfied); +} + +// 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(NULL, 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))