diff --git a/lib/common/tests/rules/Makefile.am b/lib/common/tests/rules/Makefile.am index 4bbacde082..3343e2c8a6 100644 --- a/lib/common/tests/rules/Makefile.am +++ b/lib/common/tests/rules/Makefile.am @@ -1,17 +1,18 @@ # # 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__evaluate_date_spec_test \ +check_PROGRAMS = pcmk__evaluate_date_expression_test \ + pcmk__evaluate_date_spec_test \ pcmk__unpack_duration_test TESTS = $(check_PROGRAMS) 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 0000000000..f6698a459c --- /dev/null +++ b/lib/common/tests/rules/pcmk__evaluate_date_expression_test.c @@ -0,0 +1,682 @@ +/* + * 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" + +/*! + * \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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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 = string2xml(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' />" \ + "" + +static void +range_valid_start_invalid_duration(void **state) +{ + // Currently treated same as end equals start + xmlNodePtr xml = string2xml(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' />" \ + "" + +static void +range_valid_start_valid_duration(void **state) +{ + xmlNodePtr xml = string2xml(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' />" \ + "" + +static void +range_valid_start_duration_missing_id(void **state) +{ + // Currently acceptable + xmlNodePtr xml = string2xml(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 = string2xml(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'/>" \ + "" + +static void +spec_invalid(void **state) +{ + // Currently treated as date_spec with no ranges (which passes) + xmlNodePtr xml = string2xml(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'/>" \ + "" + +static void +spec_valid(void **state) +{ + // date_spec does not currently support next_change + xmlNodePtr xml = string2xml(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'/>" \ + "" + +static void +spec_missing_id(void **state) +{ + // Currently acceptable; date_spec does not currently support next_change + xmlNodePtr xml = string2xml(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(NULL, 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))