summaryrefslogtreecommitdiffstats
path: root/lib/common/tests/xml
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:39:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-06-03 13:39:29 +0000
commitb41961d74fe7ff2d4d4abaca92454e87c561e49f (patch)
treeb34e3826a7b649dafdbd05081140c990c96d736d /lib/common/tests/xml
parentReleasing progress-linux version 2.1.7-1~progress7.99u1. (diff)
downloadpacemaker-b41961d74fe7ff2d4d4abaca92454e87c561e49f.tar.xz
pacemaker-b41961d74fe7ff2d4d4abaca92454e87c561e49f.zip
Merging upstream version 2.1.8~rc1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'lib/common/tests/xml')
-rw-r--r--lib/common/tests/xml/Makefile.am11
-rw-r--r--lib/common/tests/xml/crm_xml_init_test.c230
-rw-r--r--lib/common/tests/xml/pcmk__xe_copy_attrs_test.c188
-rw-r--r--lib/common/tests/xml/pcmk__xe_first_child_test.c (renamed from lib/common/tests/xml/pcmk__xe_match_test.c)52
-rw-r--r--lib/common/tests/xml/pcmk__xe_foreach_child_test.c20
-rw-r--r--lib/common/tests/xml/pcmk__xe_set_score_test.c188
-rw-r--r--lib/common/tests/xml/pcmk__xml_escape_test.c213
-rw-r--r--lib/common/tests/xml/pcmk__xml_needs_escape_test.c337
8 files changed, 1200 insertions, 39 deletions
diff --git a/lib/common/tests/xml/Makefile.am b/lib/common/tests/xml/Makefile.am
index 465c950..9ed1620 100644
--- a/lib/common/tests/xml/Makefile.am
+++ b/lib/common/tests/xml/Makefile.am
@@ -1,5 +1,5 @@
#
-# Copyright 2022-2023 the Pacemaker project contributors
+# Copyright 2022-2024 the Pacemaker project contributors
#
# The version control history for this file may have further details.
#
@@ -11,7 +11,12 @@ 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__xe_foreach_child_test \
- pcmk__xe_match_test
+check_PROGRAMS = crm_xml_init_test \
+ pcmk__xe_copy_attrs_test \
+ pcmk__xe_first_child_test \
+ pcmk__xe_foreach_child_test \
+ pcmk__xe_set_score_test \
+ pcmk__xml_escape_test \
+ pcmk__xml_needs_escape_test
TESTS = $(check_PROGRAMS)
diff --git a/lib/common/tests/xml/crm_xml_init_test.c b/lib/common/tests/xml/crm_xml_init_test.c
new file mode 100644
index 0000000..1f78728
--- /dev/null
+++ b/lib/common/tests/xml/crm_xml_init_test.c
@@ -0,0 +1,230 @@
+/*
+ * Copyright 2023-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 <crm_internal.h>
+
+#include <crm/common/xml.h>
+#include <crm/common/unittest_internal.h>
+#include <crm/common/xml_internal.h>
+
+#include "crmcommon_private.h"
+
+/* Copied from lib/common/xml.c */
+#define XML_DOC_PRIVATE_MAGIC 0x81726354UL
+#define XML_NODE_PRIVATE_MAGIC 0x54637281UL
+
+static int
+setup(void **state) {
+ crm_xml_init();
+ return 0;
+}
+
+static int
+teardown(void **state) {
+ crm_xml_cleanup();
+ return 0;
+}
+
+static void
+buffer_scheme_test(void **state) {
+ assert_int_equal(XML_BUFFER_ALLOC_DOUBLEIT, xmlGetBufferAllocationScheme());
+}
+
+/* These functions also serve as unit tests of the static new_private_data
+ * function. We can't test free_private_data because libxml will call that as
+ * part of freeing everything else. By the time we'd get back into a unit test
+ * where we could check that private members are NULL, the structure containing
+ * the private data would have been freed.
+ *
+ * This could probably be tested with a lot of function mocking, but that
+ * doesn't seem worth it.
+ */
+
+static void
+create_document_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+
+ /* Double check things */
+ assert_non_null(doc);
+ assert_int_equal(doc->type, XML_DOCUMENT_NODE);
+
+ /* Check that the private data is initialized correctly */
+ docpriv = doc->_private;
+ assert_non_null(docpriv);
+ assert_int_equal(docpriv->check, XML_DOC_PRIVATE_MAGIC);
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty|pcmk__xf_created));
+
+ /* Clean up */
+ xmlFreeDoc(doc);
+}
+
+static void
+create_element_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL);
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(node);
+ assert_int_equal(node->type, XML_ELEMENT_NODE);
+
+ /* Check that the private data is initialized correctly */
+ priv = node->_private;
+ assert_non_null(priv);
+ assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC);
+ assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created));
+
+ /* Clean up */
+ xmlFreeNode(node);
+ xmlFreeDoc(doc);
+}
+
+static void
+create_attr_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlNodePtr node = xmlNewDocNode(doc, NULL, (pcmkXmlStr) "test", NULL);
+ xmlAttrPtr attr = xmlNewProp(node, (pcmkXmlStr) PCMK_XA_NAME,
+ (pcmkXmlStr) "dummy-value");
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(attr);
+ assert_int_equal(attr->type, XML_ATTRIBUTE_NODE);
+
+ /* Check that the private data is initialized correctly */
+ priv = attr->_private;
+ assert_non_null(priv);
+ assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC);
+ assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created));
+
+ /* Clean up */
+ xmlFreeNode(node);
+ xmlFreeDoc(doc);
+}
+
+static void
+create_comment_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlNodePtr node = xmlNewDocComment(doc, (pcmkXmlStr) "blahblah");
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(node);
+ assert_int_equal(node->type, XML_COMMENT_NODE);
+
+ /* Check that the private data is initialized correctly */
+ priv = node->_private;
+ assert_non_null(priv);
+ assert_int_equal(priv->check, XML_NODE_PRIVATE_MAGIC);
+ assert_true(pcmk_all_flags_set(priv->flags, pcmk__xf_dirty|pcmk__xf_created));
+
+ /* Clean up */
+ xmlFreeNode(node);
+ xmlFreeDoc(doc);
+}
+
+static void
+create_text_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlNodePtr node = xmlNewDocText(doc, (pcmkXmlStr) "blahblah");
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(node);
+ assert_int_equal(node->type, XML_TEXT_NODE);
+
+ /* Check that no private data was created */
+ priv = node->_private;
+ assert_null(priv);
+
+ /* Clean up */
+ xmlFreeNode(node);
+ xmlFreeDoc(doc);
+}
+
+static void
+create_dtd_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlDtdPtr dtd = xmlNewDtd(doc, (pcmkXmlStr) PCMK_XA_NAME,
+ (pcmkXmlStr) "externalId",
+ (pcmkXmlStr) "systemId");
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(dtd);
+ assert_int_equal(dtd->type, XML_DTD_NODE);
+
+ /* Check that no private data was created */
+ priv = dtd->_private;
+ assert_null(priv);
+
+ /* Clean up */
+ /* If you call xmlFreeDtd before xmlFreeDoc, you get a segfault */
+ xmlFreeDoc(doc);
+}
+
+static void
+create_cdata_node(void **state) {
+ xml_doc_private_t *docpriv = NULL;
+ xml_node_private_t *priv = NULL;
+ xmlDocPtr doc = xmlNewDoc(PCMK__XML_VERSION);
+ xmlNodePtr node = xmlNewCDataBlock(doc, (pcmkXmlStr) "blahblah", 8);
+
+ /* Adding a node to the document marks it as dirty */
+ docpriv = doc->_private;
+ assert_true(pcmk_all_flags_set(docpriv->flags, pcmk__xf_dirty));
+
+ /* Double check things */
+ assert_non_null(node);
+ assert_int_equal(node->type, XML_CDATA_SECTION_NODE);
+
+ /* Check that no private data was created */
+ priv = node->_private;
+ assert_null(priv);
+
+ /* Clean up */
+ xmlFreeNode(node);
+ xmlFreeDoc(doc);
+}
+
+PCMK__UNIT_TEST(setup, teardown,
+ cmocka_unit_test(buffer_scheme_test),
+ cmocka_unit_test(create_document_node),
+ cmocka_unit_test(create_element_node),
+ cmocka_unit_test(create_attr_node),
+ cmocka_unit_test(create_comment_node),
+ cmocka_unit_test(create_text_node),
+ cmocka_unit_test(create_dtd_node),
+ cmocka_unit_test(create_cdata_node));
diff --git a/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c
new file mode 100644
index 0000000..146317c
--- /dev/null
+++ b/lib/common/tests/xml/pcmk__xe_copy_attrs_test.c
@@ -0,0 +1,188 @@
+ /*
+ * Copyright 2022-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 <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+static void
+null_args(void **state)
+{
+ // This test dumps core via CRM_CHECK()
+ xmlNode *xml = pcmk__xe_create(NULL, "test");
+
+ assert_int_equal(pcmk__xe_copy_attrs(NULL, NULL, pcmk__xaf_none), EINVAL);
+ assert_int_equal(pcmk__xe_copy_attrs(NULL, xml, pcmk__xaf_none), EINVAL);
+ assert_int_equal(pcmk__xe_copy_attrs(xml, NULL, pcmk__xaf_none), EINVAL);
+ assert_ptr_equal(xml->properties, NULL);
+
+ free_xml(xml);
+}
+
+static void
+no_source_attrs(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ // Ensure copying from empty source doesn't create target properties
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_ptr_equal(target->properties, NULL);
+
+ // Ensure copying from empty source doesn't delete target attributes
+ crm_xml_add(target, "attr", "value");
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(target, "attr"), "value");
+
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+copy_one(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ crm_xml_add(src, "attr", "value");
+
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(src, "attr"),
+ crm_element_value(target, "attr"));
+
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+copy_multiple(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ pcmk__xe_set_props(src,
+ "attr1", "value1",
+ "attr2", "value2",
+ "attr3", "value3",
+ NULL);
+
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(src, "attr1"),
+ crm_element_value(target, "attr1"));
+ assert_string_equal(crm_element_value(src, "attr2"),
+ crm_element_value(target, "attr2"));
+ assert_string_equal(crm_element_value(src, "attr3"),
+ crm_element_value(target, "attr3"));
+
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+overwrite(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ crm_xml_add(src, "attr", "src_value");
+ crm_xml_add(target, "attr", "target_value");
+
+ // Overwrite enabled by default
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(src, "attr"),
+ crm_element_value(target, "attr"));
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+no_overwrite(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ crm_xml_add(src, "attr", "src_value");
+ crm_xml_add(target, "attr", "target_value");
+
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite),
+ pcmk_rc_ok);
+ assert_string_not_equal(crm_element_value(src, "attr"),
+ crm_element_value(target, "attr"));
+
+ // no_overwrite doesn't prevent copy if there's no conflict
+ pcmk__xe_remove_attr(target, "attr");
+
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_no_overwrite),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(src, "attr"),
+ crm_element_value(target, "attr"));
+
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+score_update(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++");
+ crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2");
+ crm_xml_add(target, "plus_plus_attr", "1");
+ crm_xml_add(target, "plus_two_attr", "1");
+
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_score_update),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(target, "plus_plus_attr"), "2");
+ assert_string_equal(crm_element_value(target, "plus_two_attr"), "3");
+
+ free_xml(src);
+ free_xml(target);
+}
+
+static void
+no_score_update(void **state)
+{
+ xmlNode *src = pcmk__xe_create(NULL, "test");
+ xmlNode *target = pcmk__xe_create(NULL, "test");
+
+ crm_xml_add(src, "plus_plus_attr", "plus_plus_attr++");
+ crm_xml_add(src, "plus_two_attr", "plus_two_attr+=2");
+ crm_xml_add(target, "plus_plus_attr", "1");
+ crm_xml_add(target, "plus_two_attr", "1");
+
+ // Score update disabled by default
+ assert_int_equal(pcmk__xe_copy_attrs(target, src, pcmk__xaf_none),
+ pcmk_rc_ok);
+ assert_string_equal(crm_element_value(target, "plus_plus_attr"),
+ "plus_plus_attr++");
+ assert_string_equal(crm_element_value(target, "plus_two_attr"),
+ "plus_two_attr+=2");
+
+ free_xml(src);
+ free_xml(target);
+}
+
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
+ cmocka_unit_test(null_args),
+ cmocka_unit_test(no_source_attrs),
+ cmocka_unit_test(copy_one),
+ cmocka_unit_test(copy_multiple),
+ cmocka_unit_test(overwrite),
+ cmocka_unit_test(no_overwrite),
+ cmocka_unit_test(score_update),
+ cmocka_unit_test(no_score_update));
diff --git a/lib/common/tests/xml/pcmk__xe_match_test.c b/lib/common/tests/xml/pcmk__xe_first_child_test.c
index be2c949..64b90b0 100644
--- a/lib/common/tests/xml/pcmk__xe_match_test.c
+++ b/lib/common/tests/xml/pcmk__xe_first_child_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022 the Pacemaker project contributors
+ * Copyright 2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -9,97 +9,97 @@
#include <crm_internal.h>
-#include <crm/msg_xml.h>
+#include <crm/common/xml.h>
#include <crm/common/unittest_internal.h>
#include <crm/common/xml_internal.h>
const char *str1 =
"<xml>\n"
" <!-- This is an A node -->\n"
- " <nodeA attrA=\"123\" " XML_ATTR_ID "=\"1\">\n"
+ " <nodeA attrA=\"123\" " PCMK_XA_ID "=\"1\">\n"
" content\n"
" </nodeA>\n"
" <!-- This is an A node -->\n"
- " <nodeA attrA=\"456\" " XML_ATTR_ID "=\"2\">\n"
+ " <nodeA attrA=\"456\" " PCMK_XA_ID "=\"2\">\n"
" content\n"
" </nodeA>\n"
" <!-- This is an A node -->\n"
- " <nodeA attrB=\"XYZ\" " XML_ATTR_ID "=\"3\">\n"
+ " <nodeA attrB=\"XYZ\" " PCMK_XA_ID "=\"3\">\n"
" content\n"
" </nodeA>\n"
" <!-- This is a B node -->\n"
- " <nodeB attrA=\"123\" " XML_ATTR_ID "=\"4\">\n"
+ " <nodeB attrA=\"123\" " PCMK_XA_ID "=\"4\">\n"
" content\n"
" </nodeA>\n"
" <!-- This is a B node -->\n"
- " <nodeB attrB=\"ABC\" " XML_ATTR_ID "=\"5\">\n"
+ " <nodeB attrB=\"ABC\" " PCMK_XA_ID "=\"5\">\n"
" content\n"
" </nodeA>\n"
"</xml>";
static void
bad_input(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
- assert_null(pcmk__xe_match(NULL, NULL, NULL, NULL));
- assert_null(pcmk__xe_match(NULL, NULL, NULL, "attrX"));
+ assert_null(pcmk__xe_first_child(NULL, NULL, NULL, NULL));
+ assert_null(pcmk__xe_first_child(NULL, NULL, NULL, "attrX"));
free_xml(xml);
}
static void
not_found(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
/* No node with an attrX attribute */
- assert_null(pcmk__xe_match(xml, NULL, "attrX", NULL));
+ assert_null(pcmk__xe_first_child(xml, NULL, "attrX", NULL));
/* No nodeX node */
- assert_null(pcmk__xe_match(xml, "nodeX", NULL, NULL));
+ assert_null(pcmk__xe_first_child(xml, "nodeX", NULL, NULL));
/* No nodeA node with attrX */
- assert_null(pcmk__xe_match(xml, "nodeA", "attrX", NULL));
+ assert_null(pcmk__xe_first_child(xml, "nodeA", "attrX", NULL));
/* No nodeA node with attrA=XYZ */
- assert_null(pcmk__xe_match(xml, "nodeA", "attrA", "XYZ"));
+ assert_null(pcmk__xe_first_child(xml, "nodeA", "attrA", "XYZ"));
free_xml(xml);
}
static void
find_attrB(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
xmlNode *result = NULL;
/* Find the first node with attrB */
- result = pcmk__xe_match(xml, NULL, "attrB", NULL);
+ result = pcmk__xe_first_child(xml, NULL, "attrB", NULL);
assert_non_null(result);
- assert_string_equal(crm_element_value(result, "id"), "3");
+ assert_string_equal(crm_element_value(result, PCMK_XA_ID), "3");
/* Find the first nodeB with attrB */
- result = pcmk__xe_match(xml, "nodeB", "attrB", NULL);
+ result = pcmk__xe_first_child(xml, "nodeB", "attrB", NULL);
assert_non_null(result);
- assert_string_equal(crm_element_value(result, "id"), "5");
+ assert_string_equal(crm_element_value(result, PCMK_XA_ID), "5");
free_xml(xml);
}
static void
find_attrA_matching(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
xmlNode *result = NULL;
/* Find attrA=456 */
- result = pcmk__xe_match(xml, NULL, "attrA", "456");
+ result = pcmk__xe_first_child(xml, NULL, "attrA", "456");
assert_non_null(result);
- assert_string_equal(crm_element_value(result, "id"), "2");
+ assert_string_equal(crm_element_value(result, PCMK_XA_ID), "2");
/* Find a nodeB with attrA=123 */
- result = pcmk__xe_match(xml, "nodeB", "attrA", "123");
+ result = pcmk__xe_first_child(xml, "nodeB", "attrA", "123");
assert_non_null(result);
- assert_string_equal(crm_element_value(result, "id"), "4");
+ assert_string_equal(crm_element_value(result, PCMK_XA_ID), "4");
free_xml(xml);
}
-PCMK__UNIT_TEST(NULL, NULL,
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
cmocka_unit_test(bad_input),
cmocka_unit_test(not_found),
cmocka_unit_test(find_attrB),
diff --git a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
index ffb9171..a833dde 100644
--- a/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
+++ b/lib/common/tests/xml/pcmk__xe_foreach_child_test.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2022-2023 the Pacemaker project contributors
+ * Copyright 2022-2024 the Pacemaker project contributors
*
* The version control history for this file may have further details.
*
@@ -36,7 +36,7 @@ const char *str1 =
static void
bad_input(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
pcmk__assert_asserts(pcmk__xe_foreach_child(xml, NULL, NULL, NULL));
@@ -45,7 +45,7 @@ bad_input(void **state) {
static void
name_given_test(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
/* The handler should be called once for every <level1> node. */
expect_function_call(compare_name_handler);
@@ -58,7 +58,7 @@ name_given_test(void **state) {
static void
no_name_given_test(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
/* The handler should be called once for every <level1> node. */
expect_function_call(compare_name_handler);
@@ -71,7 +71,7 @@ no_name_given_test(void **state) {
static void
name_doesnt_exist_test(void **state) {
- xmlNode *xml = string2xml(str1);
+ xmlNode *xml = pcmk__xml_parse(str1);
pcmk__xe_foreach_child(xml, "xxx", compare_name_handler, NULL);
free_xml(xml);
}
@@ -100,7 +100,7 @@ const char *str2 =
static void
multiple_levels_test(void **state) {
- xmlNode *xml = string2xml(str2);
+ xmlNode *xml = pcmk__xml_parse(str2);
/* The handler should be called once for every <level1> node. */
expect_function_call(compare_name_handler);
@@ -112,7 +112,7 @@ multiple_levels_test(void **state) {
static void
multiple_levels_no_name_test(void **state) {
- xmlNode *xml = string2xml(str2);
+ xmlNode *xml = pcmk__xml_parse(str2);
/* The handler should be called once for every <level1> node. */
expect_function_call(compare_name_handler);
@@ -147,7 +147,7 @@ static int any_of_handler(xmlNode *xml, void *userdata) {
static void
any_of_test(void **state) {
- xmlNode *xml = string2xml(str3);
+ xmlNode *xml = pcmk__xml_parse(str3);
/* The handler should be called once for every <nodeX> node. */
expect_function_call(any_of_handler);
@@ -190,7 +190,7 @@ static int stops_on_third_handler(xmlNode *xml, void *userdata) {
static void
one_of_test(void **state) {
- xmlNode *xml = string2xml(str3);
+ xmlNode *xml = pcmk__xml_parse(str3);
/* The handler should be called once. */
expect_function_call(stops_on_first_handler);
@@ -205,7 +205,7 @@ one_of_test(void **state) {
free_xml(xml);
}
-PCMK__UNIT_TEST(NULL, NULL,
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
cmocka_unit_test(bad_input),
cmocka_unit_test(name_given_test),
cmocka_unit_test(no_name_given_test),
diff --git a/lib/common/tests/xml/pcmk__xe_set_score_test.c b/lib/common/tests/xml/pcmk__xe_set_score_test.c
new file mode 100644
index 0000000..deb85b0
--- /dev/null
+++ b/lib/common/tests/xml/pcmk__xe_set_score_test.c
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2022-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 <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+
+#include <glib.h>
+
+#include "crmcommon_private.h" // pcmk__xe_set_score()
+
+/*!
+ * \internal
+ * \brief Update an XML attribute value and check it against a reference value
+ *
+ * The attribute name is hard-coded as \c "X".
+ *
+ * \param[in] initial Initial value
+ * \param[in] new Value to set
+ * \param[in] reference_val Expected attribute value after update
+ * \param[in] reference_rc Expected return code from \c pcmk__xe_set_score()
+ */
+static void
+assert_set_score(const char *initial, const char *new,
+ const char *reference_val, int reference_rc)
+{
+ const char *name = "X";
+ xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml");
+
+ crm_xml_add(test_xml, name, initial);
+ assert_int_equal(pcmk__xe_set_score(test_xml, name, new), reference_rc);
+ assert_string_equal(crm_element_value(test_xml, name), reference_val);
+
+ free_xml(test_xml);
+}
+
+static void
+value_is_name_plus_plus(void **state)
+{
+ assert_set_score("5", "X++", "6", pcmk_rc_ok);
+}
+
+static void
+value_is_name_plus_equals_integer(void **state)
+{
+ assert_set_score("5", "X+=2", "7", pcmk_rc_ok);
+}
+
+// NULL input
+
+static void
+target_is_NULL(void **state)
+{
+ // Dumps core via CRM_CHECK()
+ assert_int_equal(pcmk__xe_set_score(NULL, "X", "X++"), EINVAL);
+}
+
+static void
+name_is_NULL(void **state)
+{
+ xmlNode *test_xml = pcmk__xe_create(NULL, "test_xml");
+
+ crm_xml_add(test_xml, "X", "5");
+
+ // Dumps core via CRM_CHECK()
+ assert_int_equal(pcmk__xe_set_score(test_xml, NULL, "X++"), EINVAL);
+ assert_string_equal(crm_element_value(test_xml, "X"), "5");
+
+ free_xml(test_xml);
+}
+
+static void
+value_is_NULL(void **state)
+{
+ assert_set_score("5", NULL, "5", pcmk_rc_ok);
+}
+
+// the value input doesn't start with the name input
+
+static void
+value_is_wrong_name(void **state)
+{
+ assert_set_score("5", "Y++", "Y++", pcmk_rc_ok);
+}
+
+static void
+value_is_only_an_integer(void **state)
+{
+ assert_set_score("5", "2", "2", pcmk_rc_ok);
+}
+
+// non-integers
+
+static void
+variable_is_initialized_to_be_non_numeric(void **state)
+{
+ assert_set_score("hello", "X++", "1", pcmk_rc_ok);
+}
+
+static void
+variable_is_initialized_to_be_non_numeric_2(void **state)
+{
+ assert_set_score("hello", "X+=2", "2", pcmk_rc_ok);
+}
+
+static void
+variable_is_initialized_to_be_numeric_and_decimal_point_containing(void **state)
+{
+ assert_set_score("5.01", "X++", "6", pcmk_rc_ok);
+}
+
+static void
+variable_is_initialized_to_be_numeric_and_decimal_point_containing_2(void **state)
+{
+ assert_set_score("5.50", "X++", "6", pcmk_rc_ok);
+}
+
+static void
+variable_is_initialized_to_be_numeric_and_decimal_point_containing_3(void **state)
+{
+ assert_set_score("5.99", "X++", "6", pcmk_rc_ok);
+}
+
+static void
+value_is_non_numeric(void **state)
+{
+ assert_set_score("5", "X+=hello", "5", pcmk_rc_ok);
+}
+
+static void
+value_is_numeric_and_decimal_point_containing(void **state)
+{
+ assert_set_score("5", "X+=2.01", "7", pcmk_rc_ok);
+}
+
+static void
+value_is_numeric_and_decimal_point_containing_2(void **state)
+{
+ assert_set_score("5", "X+=1.50", "6", pcmk_rc_ok);
+}
+
+static void
+value_is_numeric_and_decimal_point_containing_3(void **state)
+{
+ assert_set_score("5", "X+=1.99", "6", pcmk_rc_ok);
+}
+
+// undefined input
+
+static void
+name_is_undefined(void **state)
+{
+ assert_set_score(NULL, "X++", "X++", pcmk_rc_ok);
+}
+
+// large input
+
+static void
+assignment_result_is_too_large(void **state)
+{
+ assert_set_score("5", "X+=100000000000", "1000000", pcmk_rc_ok);
+}
+
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
+ cmocka_unit_test(value_is_name_plus_plus),
+ cmocka_unit_test(value_is_name_plus_equals_integer),
+ cmocka_unit_test(target_is_NULL),
+ cmocka_unit_test(name_is_NULL),
+ cmocka_unit_test(value_is_NULL),
+ cmocka_unit_test(value_is_wrong_name),
+ cmocka_unit_test(value_is_only_an_integer),
+ cmocka_unit_test(variable_is_initialized_to_be_non_numeric),
+ cmocka_unit_test(variable_is_initialized_to_be_non_numeric_2),
+ cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing),
+ cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_2),
+ cmocka_unit_test(variable_is_initialized_to_be_numeric_and_decimal_point_containing_3),
+ cmocka_unit_test(value_is_non_numeric),
+ cmocka_unit_test(value_is_numeric_and_decimal_point_containing),
+ cmocka_unit_test(value_is_numeric_and_decimal_point_containing_2),
+ cmocka_unit_test(value_is_numeric_and_decimal_point_containing_3),
+ cmocka_unit_test(name_is_undefined),
+ cmocka_unit_test(assignment_result_is_too_large))
diff --git a/lib/common/tests/xml/pcmk__xml_escape_test.c b/lib/common/tests/xml/pcmk__xml_escape_test.c
new file mode 100644
index 0000000..8c6fd21
--- /dev/null
+++ b/lib/common/tests/xml/pcmk__xml_escape_test.c
@@ -0,0 +1,213 @@
+/*
+ * 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 <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+#include <crm/common/xml_internal.h>
+
+#include "crmcommon_private.h"
+
+static void
+assert_escape(const char *str, const char *reference,
+ enum pcmk__xml_escape_type type)
+{
+ gchar *buf = pcmk__xml_escape(str, type);
+
+ assert_string_equal(buf, reference);
+ g_free(buf);
+}
+
+static void
+null_empty(void **state)
+{
+ assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_text));
+ assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr));
+ assert_null(pcmk__xml_escape(NULL, pcmk__xml_escape_attr_pretty));
+
+ assert_escape("", "", pcmk__xml_escape_text);
+ assert_escape("", "", pcmk__xml_escape_attr);
+ assert_escape("", "", pcmk__xml_escape_attr_pretty);
+}
+
+static void
+invalid_type(void **state)
+{
+ const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1;
+
+ // Easier to ignore invalid type for NULL or empty string
+ assert_null(pcmk__xml_escape(NULL, type));
+ assert_escape("", "", type);
+
+ // Otherwise, assert if we somehow passed an invalid type
+ pcmk__assert_asserts(pcmk__xml_escape("he<>llo", type));
+}
+
+static void
+escape_unchanged(void **state)
+{
+ // No escaped characters (note: this string includes single quote at end)
+ const char *unchanged = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "`~!@#$%^*()-_=+/|\\[]{}?.,'";
+
+ assert_escape(unchanged, unchanged, pcmk__xml_escape_text);
+ assert_escape(unchanged, unchanged, pcmk__xml_escape_attr);
+ assert_escape(unchanged, unchanged, pcmk__xml_escape_attr_pretty);
+}
+
+// Ensure special characters get escaped at start, middle, and end
+
+static void
+escape_left_angle(void **state)
+{
+ const char *l_angle = "<abc<def<";
+ const char *l_angle_esc = PCMK__XML_ENTITY_LT "abc"
+ PCMK__XML_ENTITY_LT "def" PCMK__XML_ENTITY_LT;
+
+ assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_text);
+ assert_escape(l_angle, l_angle_esc, pcmk__xml_escape_attr);
+ assert_escape(l_angle, l_angle, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_right_angle(void **state)
+{
+ const char *r_angle = ">abc>def>";
+ const char *r_angle_esc = PCMK__XML_ENTITY_GT "abc"
+ PCMK__XML_ENTITY_GT "def" PCMK__XML_ENTITY_GT;
+
+ assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_text);
+ assert_escape(r_angle, r_angle_esc, pcmk__xml_escape_attr);
+ assert_escape(r_angle, r_angle, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_ampersand(void **state)
+{
+ const char *ampersand = "&abc&def&";
+ const char *ampersand_esc = PCMK__XML_ENTITY_AMP "abc"
+ PCMK__XML_ENTITY_AMP "def" PCMK__XML_ENTITY_AMP;
+
+ assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_text);
+ assert_escape(ampersand, ampersand_esc, pcmk__xml_escape_attr);
+ assert_escape(ampersand, ampersand, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_double_quote(void **state)
+{
+ const char *double_quote = "\"abc\"def\"";
+ const char *double_quote_esc_ref = PCMK__XML_ENTITY_QUOT "abc"
+ PCMK__XML_ENTITY_QUOT "def"
+ PCMK__XML_ENTITY_QUOT;
+ const char *double_quote_esc_backslash = "\\\"abc\\\"def\\\"";
+
+ assert_escape(double_quote, double_quote, pcmk__xml_escape_text);
+ assert_escape(double_quote, double_quote_esc_ref, pcmk__xml_escape_attr);
+ assert_escape(double_quote, double_quote_esc_backslash,
+ pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_newline(void **state)
+{
+ const char *newline = "\nabc\ndef\n";
+ const char *newline_esc_ref = "&#x0A;abc&#x0A;def&#x0A;";
+ const char *newline_esc_backslash = "\\nabc\\ndef\\n";
+
+ assert_escape(newline, newline, pcmk__xml_escape_text);
+ assert_escape(newline, newline_esc_ref, pcmk__xml_escape_attr);
+ assert_escape(newline, newline_esc_backslash, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_tab(void **state)
+{
+ const char *tab = "\tabc\tdef\t";
+ const char *tab_esc_ref = "&#x09;abc&#x09;def&#x09;";
+ const char *tab_esc_backslash = "\\tabc\\tdef\\t";
+
+ assert_escape(tab, tab, pcmk__xml_escape_text);
+ assert_escape(tab, tab_esc_ref, pcmk__xml_escape_attr);
+ assert_escape(tab, tab_esc_backslash, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_carriage_return(void **state)
+{
+ const char *cr = "\rabc\rdef\r";
+ const char *cr_esc_ref = "&#x0D;abc&#x0D;def&#x0D;";
+ const char *cr_esc_backslash = "\\rabc\\rdef\\r";
+
+ assert_escape(cr, cr_esc_ref, pcmk__xml_escape_text);
+ assert_escape(cr, cr_esc_ref, pcmk__xml_escape_attr);
+ assert_escape(cr, cr_esc_backslash, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_nonprinting(void **state)
+{
+ const char *nonprinting = "\a\x7F\x1B";
+ const char *nonprinting_esc = "&#x07;&#x7F;&#x1B;";
+
+ assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_text);
+ assert_escape(nonprinting, nonprinting_esc, pcmk__xml_escape_attr);
+ assert_escape(nonprinting, nonprinting, pcmk__xml_escape_attr_pretty);
+}
+
+static void
+escape_utf8(void **state)
+{
+ /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide
+ * and should not be escaped.
+ */
+ const char *chinese = "仅高级使用";
+ const char *two_byte = "abc""\xCF\xA6""d<ef";
+ const char *two_byte_esc = "abc""\xCF\xA6""d" PCMK__XML_ENTITY_LT "ef";
+
+ const char *three_byte = "abc""\xEF\x98\x98""d<ef";
+ const char *three_byte_esc = "abc""\xEF\x98\x98""d"
+ PCMK__XML_ENTITY_LT "ef";
+
+ const char *four_byte = "abc""\xF0\x94\x81\x90""d<ef";
+ const char *four_byte_esc = "abc""\xF0\x94\x81\x90""d"
+ PCMK__XML_ENTITY_LT "ef";
+
+ assert_escape(chinese, chinese, pcmk__xml_escape_text);
+ assert_escape(chinese, chinese, pcmk__xml_escape_attr);
+ assert_escape(chinese, chinese, pcmk__xml_escape_attr_pretty);
+
+ assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_text);
+ assert_escape(two_byte, two_byte_esc, pcmk__xml_escape_attr);
+ assert_escape(two_byte, two_byte, pcmk__xml_escape_attr_pretty);
+
+ assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_text);
+ assert_escape(three_byte, three_byte_esc, pcmk__xml_escape_attr);
+ assert_escape(three_byte, three_byte, pcmk__xml_escape_attr_pretty);
+
+ assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_text);
+ assert_escape(four_byte, four_byte_esc, pcmk__xml_escape_attr);
+ assert_escape(four_byte, four_byte, pcmk__xml_escape_attr_pretty);
+}
+
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
+ cmocka_unit_test(null_empty),
+ cmocka_unit_test(invalid_type),
+ cmocka_unit_test(escape_unchanged),
+ cmocka_unit_test(escape_left_angle),
+ cmocka_unit_test(escape_right_angle),
+ cmocka_unit_test(escape_ampersand),
+ cmocka_unit_test(escape_double_quote),
+ cmocka_unit_test(escape_newline),
+ cmocka_unit_test(escape_tab),
+ cmocka_unit_test(escape_carriage_return),
+ cmocka_unit_test(escape_nonprinting),
+ cmocka_unit_test(escape_utf8));
diff --git a/lib/common/tests/xml/pcmk__xml_needs_escape_test.c b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c
new file mode 100644
index 0000000..612f61b
--- /dev/null
+++ b/lib/common/tests/xml/pcmk__xml_needs_escape_test.c
@@ -0,0 +1,337 @@
+/*
+ * 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 <crm_internal.h>
+
+#include <crm/common/unittest_internal.h>
+#include <crm/common/xml_internal.h>
+
+#include "crmcommon_private.h"
+
+static void
+null_empty(void **state)
+{
+ assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(NULL, pcmk__xml_escape_attr_pretty));
+
+ assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape("", pcmk__xml_escape_attr_pretty));
+}
+
+static void
+invalid_type(void **state)
+{
+ const enum pcmk__xml_escape_type type = (enum pcmk__xml_escape_type) -1;
+
+ // Easier to ignore invalid type for NULL or empty string
+ assert_false(pcmk__xml_needs_escape(NULL, type));
+ assert_false(pcmk__xml_needs_escape("", type));
+
+ // Otherwise, assert if we somehow passed an invalid type
+ pcmk__assert_asserts(pcmk__xml_needs_escape("he<>llo", type));
+}
+
+static void
+escape_unchanged(void **state)
+{
+ // No escaped characters (note: this string includes single quote at end)
+ const char *unchanged = "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "0123456789"
+ "`~!@#$%^*()-_=+/|\\[]{}?.,'";
+
+ assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(unchanged, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(unchanged,
+ pcmk__xml_escape_attr_pretty));
+}
+
+// Ensure special characters get escaped at start, middle, and end
+
+static void
+escape_left_angle(void **state)
+{
+ const char *l_angle_left = "<abcdef";
+ const char *l_angle_mid = "abc<def";
+ const char *l_angle_right = "abcdef<";
+
+ assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(l_angle_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(l_angle_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(l_angle_right, pcmk__xml_escape_attr));
+
+ assert_false(pcmk__xml_needs_escape(l_angle_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(l_angle_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(l_angle_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_right_angle(void **state)
+{
+ const char *r_angle_left = ">abcdef";
+ const char *r_angle_mid = "abc>def";
+ const char *r_angle_right = "abcdef>";
+
+ assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(r_angle_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(r_angle_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(r_angle_right, pcmk__xml_escape_attr));
+
+ assert_false(pcmk__xml_needs_escape(r_angle_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(r_angle_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(r_angle_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_ampersand(void **state)
+{
+ const char *ampersand_left = "&abcdef";
+ const char *ampersand_mid = "abc&def";
+ const char *ampersand_right = "abcdef&";
+
+ assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(ampersand_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(ampersand_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(ampersand_right, pcmk__xml_escape_attr));
+
+ assert_false(pcmk__xml_needs_escape(ampersand_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(ampersand_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(ampersand_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_double_quote(void **state)
+{
+ const char *double_quote_left = "\"abcdef";
+ const char *double_quote_mid = "abc\"def";
+ const char *double_quote_right = "abcdef\"";
+
+ assert_false(pcmk__xml_needs_escape(double_quote_left,
+ pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(double_quote_mid,
+ pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(double_quote_right,
+ pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(double_quote_left,
+ pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(double_quote_mid,
+ pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(double_quote_right,
+ pcmk__xml_escape_attr));
+
+ assert_true(pcmk__xml_needs_escape(double_quote_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(double_quote_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(double_quote_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_newline(void **state)
+{
+ const char *newline_left = "\nabcdef";
+ const char *newline_mid = "abc\ndef";
+ const char *newline_right = "abcdef\n";
+
+ assert_false(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(newline_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(newline_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(newline_right, pcmk__xml_escape_attr));
+
+ assert_true(pcmk__xml_needs_escape(newline_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(newline_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(newline_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_tab(void **state)
+{
+ const char *tab_left = "\tabcdef";
+ const char *tab_mid = "abc\tdef";
+ const char *tab_right = "abcdef\t";
+
+ assert_false(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(tab_right, pcmk__xml_escape_attr));
+
+ assert_true(pcmk__xml_needs_escape(tab_left, pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(tab_mid, pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(tab_right,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_carriage_return(void **state)
+{
+ const char *cr_left = "\rabcdef";
+ const char *cr_mid = "abc\rdef";
+ const char *cr_right = "abcdef\r";
+
+ assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr));
+
+ assert_true(pcmk__xml_needs_escape(cr_left, pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(cr_mid, pcmk__xml_escape_attr_pretty));
+ assert_true(pcmk__xml_needs_escape(cr_right, pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_nonprinting(void **state)
+{
+ const char *alert_left = "\aabcdef";
+ const char *alert_mid = "abc\adef";
+ const char *alert_right = "abcdef\a";
+
+ const char *delete_left = "\x7F""abcdef";
+ const char *delete_mid = "abc\x7F""def";
+ const char *delete_right = "abcdef\x7F";
+
+ const char *nonprinting_all = "\a\x7F\x1B";
+
+ assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(alert_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(alert_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(alert_right, pcmk__xml_escape_attr));
+
+ assert_false(pcmk__xml_needs_escape(alert_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(alert_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(alert_right,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_text));
+
+ assert_true(pcmk__xml_needs_escape(delete_left, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(delete_mid, pcmk__xml_escape_attr));
+ assert_true(pcmk__xml_needs_escape(delete_right, pcmk__xml_escape_attr));
+
+ assert_false(pcmk__xml_needs_escape(delete_left,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(delete_mid,
+ pcmk__xml_escape_attr_pretty));
+ assert_false(pcmk__xml_needs_escape(delete_right,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(nonprinting_all, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(nonprinting_all,
+ pcmk__xml_escape_attr_pretty));
+}
+
+static void
+escape_utf8(void **state)
+{
+ /* Non-ASCII UTF-8 characters may be two, three, or four 8-bit bytes wide
+ * and should not be escaped.
+ */
+ const char *chinese = "仅高级使用";
+ const char *two_byte = "abc""\xCF\xA6""def";
+ const char *two_byte_special = "abc""\xCF\xA6""d<ef";
+ const char *three_byte = "abc""\xEF\x98\x98""def";
+ const char *three_byte_special = "abc""\xEF\x98\x98""d<ef";
+ const char *four_byte = "abc""\xF0\x94\x81\x90""def";
+ const char *four_byte_special = "abc""\xF0\x94\x81\x90""d<ef";
+
+ assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(chinese, pcmk__xml_escape_attr_pretty));
+
+ assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(two_byte, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(two_byte,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_true(pcmk__xml_needs_escape(two_byte_special,
+ pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(two_byte_special,
+ pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(two_byte_special,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(three_byte, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(three_byte,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_true(pcmk__xml_needs_escape(three_byte_special,
+ pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(three_byte_special,
+ pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(three_byte_special,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_text));
+ assert_false(pcmk__xml_needs_escape(four_byte, pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(four_byte,
+ pcmk__xml_escape_attr_pretty));
+
+ assert_true(pcmk__xml_needs_escape(four_byte_special,
+ pcmk__xml_escape_text));
+ assert_true(pcmk__xml_needs_escape(four_byte_special,
+ pcmk__xml_escape_attr));
+ assert_false(pcmk__xml_needs_escape(four_byte_special,
+ pcmk__xml_escape_attr_pretty));
+}
+
+PCMK__UNIT_TEST(pcmk__xml_test_setup_group, NULL,
+ cmocka_unit_test(null_empty),
+ cmocka_unit_test(invalid_type),
+ cmocka_unit_test(escape_unchanged),
+ cmocka_unit_test(escape_left_angle),
+ cmocka_unit_test(escape_right_angle),
+ cmocka_unit_test(escape_ampersand),
+ cmocka_unit_test(escape_double_quote),
+ cmocka_unit_test(escape_newline),
+ cmocka_unit_test(escape_tab),
+ cmocka_unit_test(escape_carriage_return),
+ cmocka_unit_test(escape_nonprinting),
+ cmocka_unit_test(escape_utf8));