diff --git a/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c index bf54c29e92..64a9f926c2 100644 --- a/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c +++ b/lib/common/tests/rules/pcmk__evaluate_attr_expression_test.c @@ -1,841 +1,894 @@ /* * 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"); + pcmk__insert_dup(rule_input.node_attrs, "empty", ""); 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); pcmk__xml_free(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); pcmk__xml_free(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) { assert_attr_expression(EXPR_ID_MISSING, pcmk_rc_unpack_error); } /* * 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) { assert_attr_expression(EXPR_SOURCE_INVALID, pcmk_rc_unpack_error); } 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_INVALID \ "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ PCMK_XA_TYPE "='not-a-value' " \ PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ PCMK_XA_ATTRIBUTE "='foo' " \ PCMK_XA_VALUE "='bar' />" static void type_invalid(void **state) { assert_attr_expression(EXPR_TYPE_INVALID, pcmk_rc_unpack_error); } #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_EQ_EMPTY_VS_EMPTY_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='empty' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='' />" + +static void +op_eq_empty_vs_empty_passes(void **state) +{ + assert_attr_expression(EXPR_EQ_EMPTY_VS_EMPTY_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_EQ_UNDEFINED_VS_EMPTY_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='boo' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' " \ + PCMK_XA_VALUE "='' />" + +static void +op_eq_undefined_vs_empty_fails(void **state) +{ + assert_attr_expression(EXPR_EQ_UNDEFINED_VS_EMPTY_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_EMPTY_PASSES \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='empty' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_DEFINED "' />" + +static void +op_defined_empty_passes(void **state) +{ + assert_attr_expression(EXPR_OP_DEFINED_EMPTY_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) { assert_attr_expression(EXPR_OP_DEFINED_WITH_VALUE, pcmk_rc_unpack_error); } #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_UNDEFINED_FAILS, pcmk_rc_op_unsatisfied); } +#define EXPR_OP_UNDEFINED_EMPTY_FAILS \ + "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ + PCMK_XA_ATTRIBUTE "='empty' " \ + PCMK_XA_OPERATION "='" PCMK_VALUE_NOT_DEFINED "' />" + +static void +op_undefined_empty_fails(void **state) +{ + assert_attr_expression(EXPR_OP_UNDEFINED_EMPTY_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_FAILS \ "<" PCMK_XE_EXPRESSION " " PCMK_XA_ID "='e' " \ PCMK_XA_ATTRIBUTE "='not-an-attr' " \ PCMK_XA_OPERATION "='" PCMK_VALUE_EQ "' />" static void value_missing_eq_fails(void **state) { assert_attr_expression(EXPR_VALUE_MISSING_EQ_FAILS, pcmk_rc_unpack_error); } #define expr_test(f) cmocka_unit_test_setup_teardown(f, setup, teardown) PCMK__UNIT_TEST(pcmk__xml_test_setup_group, pcmk__xml_test_teardown_group, 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_invalid), 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_empty_vs_empty_passes), expr_test(op_eq_fails), + expr_test(op_eq_undefined_vs_empty_fails), expr_test(op_ne_passes), expr_test(op_ne_fails), expr_test(op_defined_passes), + expr_test(op_defined_empty_passes), expr_test(op_defined_fails), expr_test(op_defined_with_value), expr_test(op_undefined_passes), expr_test(op_undefined_fails), + expr_test(op_undefined_empty_fails), expr_test(value_missing_defined_ok), expr_test(value_missing_eq_fails))