diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-03 13:39:29 +0000 |
commit | b41961d74fe7ff2d4d4abaca92454e87c561e49f (patch) | |
tree | b34e3826a7b649dafdbd05081140c990c96d736d /lib/common/tests/xml | |
parent | Releasing progress-linux version 2.1.7-1~progress7.99u1. (diff) | |
download | pacemaker-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.am | 11 | ||||
-rw-r--r-- | lib/common/tests/xml/crm_xml_init_test.c | 230 | ||||
-rw-r--r-- | lib/common/tests/xml/pcmk__xe_copy_attrs_test.c | 188 | ||||
-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.c | 20 | ||||
-rw-r--r-- | lib/common/tests/xml/pcmk__xe_set_score_test.c | 188 | ||||
-rw-r--r-- | lib/common/tests/xml/pcmk__xml_escape_test.c | 213 | ||||
-rw-r--r-- | lib/common/tests/xml/pcmk__xml_needs_escape_test.c | 337 |
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 = "
abc
def
"; + 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 = "	abc	def	"; + 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 = "
abc
def
"; + 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 = ""; + + 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)); |