summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Pipping <sebastian@pipping.org>2021-04-21 00:11:04 +0200
committerSebastian Pipping <sebastian@pipping.org>2021-05-07 18:25:07 +0200
commit271efb6069c5c979545cfbe00b738184c5632b93 (patch)
treedc2e00c6c0b81dde5cd7e11168f7133ea478cf3c
parent29c3748788ff5ba0e4b14b02dfa15080177a3c8c (diff)
downloadlibexpat-git-271efb6069c5c979545cfbe00b738184c5632b93.tar.gz
tests: Cover accounting
-rw-r--r--expat/lib/internal.h6
-rw-r--r--expat/tests/runtests.c300
2 files changed, 304 insertions, 2 deletions
diff --git a/expat/lib/internal.h b/expat/lib/internal.h
index ce6d27a2..377c12b6 100644
--- a/expat/lib/internal.h
+++ b/expat/lib/internal.h
@@ -109,10 +109,12 @@
#if defined(_WIN32) && ! defined(__USE_MINGW_ANSI_STDIO)
# define EXPAT_FMT_ULL(midpart) "%" midpart "I64u"
-# if defined(_WIN64) // Note: modifier "td" does not work for MinGW
+# if defined(_WIN64) // Note: modifiers "td" and "zu" do not work for MinGW
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "I64d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "I64u"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#else
# define EXPAT_FMT_ULL(midpart) "%" midpart "llu"
@@ -120,8 +122,10 @@
# error Compiler did not define ULONG_MAX for us
# elif ULONG_MAX == 18446744073709551615u // 2^64-1
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "ld"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "lu"
# else
# define EXPAT_FMT_PTRDIFF_T(midpart) "%" midpart "d"
+# define EXPAT_FMT_SIZE_T(midpart) "%" midpart "u"
# endif
#endif
diff --git a/expat/tests/runtests.c b/expat/tests/runtests.c
index 40fdfb43..5234f49f 100644
--- a/expat/tests/runtests.c
+++ b/expat/tests/runtests.c
@@ -61,7 +61,7 @@
#include "expat.h"
#include "chardata.h"
#include "structdata.h"
-#include "internal.h" /* for UNUSED_P only */
+#include "internal.h"
#include "minicheck.h"
#include "memcheck.h"
#include "siphash.h"
@@ -11225,6 +11225,296 @@ START_TEST(test_nsalloc_prefixed_element) {
}
END_TEST
+#if defined(XML_DTD)
+typedef enum XML_Status (*XmlParseFunction)(XML_Parser, const char *, int, int);
+
+struct AccountingTestCase {
+ const char *primaryText;
+ const char *firstExternalText; /* often NULL */
+ const char *secondExternalText; /* often NULL */
+ const unsigned long long expectedCountBytesIndirectExtra;
+ XML_Bool singleBytesWanted;
+};
+
+static int
+accounting_external_entity_ref_handler(XML_Parser parser,
+ const XML_Char *context,
+ const XML_Char *base,
+ const XML_Char *systemId,
+ const XML_Char *publicId) {
+ UNUSED_P(context);
+ UNUSED_P(base);
+ UNUSED_P(publicId);
+
+ const struct AccountingTestCase *const testCase
+ = (const struct AccountingTestCase *)XML_GetUserData(parser);
+
+ const char *externalText = NULL;
+ if (xcstrcmp(systemId, XCS("first.ent")) == 0) {
+ externalText = testCase->firstExternalText;
+ } else if (xcstrcmp(systemId, XCS("second.ent")) == 0) {
+ externalText = testCase->secondExternalText;
+ } else {
+ assert(! "systemId is neither \"first.ent\" nor \"second.ent\"");
+ }
+ assert(externalText);
+
+ XML_Parser entParser = XML_ExternalEntityParserCreate(parser, context, 0);
+ assert(entParser);
+
+ const XmlParseFunction xmlParseFunction
+ = testCase->singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
+
+ const enum XML_Status status = xmlParseFunction(
+ entParser, externalText, (int)strlen(externalText), XML_TRUE);
+
+ XML_ParserFree(entParser);
+ return status;
+}
+
+START_TEST(test_accounting_precision) {
+ const XML_Bool filled_later = XML_TRUE; /* value is arbitrary */
+ struct AccountingTestCase cases[] = {
+ {"<e/>", NULL, NULL, 0, 0},
+ {"<e></e>", NULL, NULL, 0, 0},
+
+ /* Attributes */
+ {"<e k1=\"v2\" k2=\"v2\"/>", NULL, NULL, 0, filled_later},
+ {"<e k1=\"v2\" k2=\"v2\"></e>", NULL, NULL, 0, 0},
+ {"<p:e xmlns:p=\"https://domain.invalid/\" />", NULL, NULL, 0,
+ filled_later},
+ {"<e k=\"&amp;&apos;&gt;&lt;&quot;\" />", NULL, NULL,
+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+ {"<e1 xmlns='https://example.org/'>\n"
+ " <e2 xmlns=''/>\n"
+ "</e1>",
+ NULL, NULL, 0, filled_later},
+
+ /* Text */
+ {"<e>text</e>", NULL, NULL, 0, filled_later},
+ {"<e1><e2>text1<e3/>text2</e2></e1>", NULL, NULL, 0, filled_later},
+ {"<e>&amp;&apos;&gt;&lt;&quot;</e>", NULL, NULL,
+ sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later},
+ {"<e>&#65;&#41;</e>", NULL, NULL, 0, filled_later},
+
+ /* Prolog */
+ {"<?xml version=\"1.0\"?><root/>", NULL, NULL, 0, filled_later},
+
+ /* Whitespace */
+ {" <e1> <e2> </e2> </e1> ", NULL, NULL, 0, filled_later},
+ {"<e1 ><e2 /></e1 >", NULL, NULL, 0, filled_later},
+ {"<e1><e2 k = \"v\"/><e3 k = 'v'/></e1>", NULL, NULL, 0, filled_later},
+
+ /* Comments */
+ {"<!-- Comment --><e><!-- Comment --></e>", NULL, NULL, 0, filled_later},
+
+ /* Processing instructions */
+ {"<?xml-stylesheet type=\"text/xsl\" href=\"https://domain.invalid/\" media=\"all\"?><e/>",
+ NULL, NULL, 0, filled_later},
+ {"<?pi0?><?pi1 ?><?pi2 ?><!DOCTYPE r SYSTEM 'first.ent'><r/>",
+ "<?pi3?><!ENTITY % e1 SYSTEM 'second.ent'><?pi4?>%e1;<?pi5?>", "<?pi6?>",
+ 0, filled_later},
+
+ /* CDATA */
+ {"<e><![CDATA[one two three]]></e>", NULL, NULL, 0, filled_later},
+
+ /* Conditional sections */
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % draft 'INCLUDE'>\n"
+ "<!ENTITY % final 'IGNORE'>\n"
+ "<!ENTITY % import SYSTEM \"first.ent\">\n"
+ "%import;\n"
+ "]>\n"
+ "<r/>\n",
+ "<![%draft;[<!--1-->]]>\n"
+ "<![%final;[<!--22-->]]>",
+ NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")),
+ filled_later},
+
+ /* General entities */
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "]>\n"
+ "<root>&nine;</root>",
+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "]>\n"
+ "<root k1=\"&nine;\"/>",
+ NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later},
+ {"<!DOCTYPE root [\n"
+ "<!ENTITY nine \"123456789\">\n"
+ "<!ENTITY nine2 \"&nine;&nine;\">\n"
+ "]>\n"
+ "<root>&nine2;&nine2;&nine2;</root>",
+ NULL, NULL,
+ sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */
+ * (strlen("&nine;") + strlen("123456789")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY five SYSTEM 'first.ent'>\n"
+ "]>\n"
+ "<r>&five;</r>",
+ "12345", NULL, 0, filled_later},
+
+ /* Parameter entities */
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % comment \"<!---->\">\n"
+ "%comment;\n"
+ "]>\n"
+ "<r/>",
+ NULL, NULL, sizeof(XML_Char) * strlen("<!---->"), filled_later},
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % ninedef \"&#60;!ENTITY nine &#34;123456789&#34;&#62;\">\n"
+ "%ninedef;\n"
+ "]>\n"
+ "<r>&nine;</r>",
+ NULL, NULL,
+ sizeof(XML_Char)
+ * (strlen("<!ENTITY nine \"123456789\">") + strlen("123456789")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ "<!ENTITY % comment \"<!--1-->\">\n"
+ "<!ENTITY % comment2 \"&#37;comment;<!--22-->&#37;comment;\">\n"
+ "%comment2;\n"
+ "]>\n"
+ "<r/>\n",
+ NULL, NULL,
+ sizeof(XML_Char)
+ * (strlen("%comment;<!--22-->%comment;") + 2 * strlen("<!--1-->")),
+ filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY % five \"12345\">\n"
+ " <!ENTITY % five2def \"&#60;!ENTITY five2 &#34;[&#37;five;][&#37;five;]]]]&#34;&#62;\">\n"
+ " %five2def;\n"
+ "]>\n"
+ "<r>&five2;</r>",
+ NULL, NULL, /* from "%five2def;": */
+ sizeof(XML_Char)
+ * (strlen("<!ENTITY five2 \"[%five;][%five;]]]]\">")
+ + 2 /* calls to "%five;" */ * strlen("12345")
+ + /* from "&five2;": */ strlen("[12345][12345]]]]")),
+ filled_later},
+ {"<!DOCTYPE r SYSTEM \"first.ent\">\n"
+ "<r/>",
+ "<!ENTITY % comment '<!--1-->'>\n"
+ "<!ENTITY % comment2 '<!--22-->%comment;<!--22-->%comment;<!--22-->'>\n"
+ "%comment2;",
+ NULL,
+ sizeof(XML_Char)
+ * (strlen("<!--22-->%comment;<!--22-->%comment;<!--22-->")
+ + 2 /* calls to "%comment;" */ * strlen("<!---->")),
+ filled_later},
+ {"<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 PUBLIC 'foo' 'second.ent'>\n"
+ "<!ENTITY % e2 '<!--22-->%e1;<!--22-->'>\n"
+ "%e2;\n",
+ "<!--1-->", sizeof(XML_Char) * strlen("<!--22--><!--1--><!--22-->"),
+ filled_later},
+ {
+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>",
+ "<?xml version='1.0' encoding='utf-8'?>\n"
+ "hello\n"
+ "xml" /* without trailing newline! */,
+ 0,
+ filled_later,
+ },
+ {
+ "<!DOCTYPE r SYSTEM 'first.ent'>\n"
+ "<r/>",
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>",
+ "<?xml version='1.0' encoding='utf-8'?>\n"
+ "hello\n"
+ "xml\n" /* with trailing newline! */,
+ 0,
+ filled_later,
+ },
+ {"<!DOCTYPE doc SYSTEM 'first.ent'>\n"
+ "<doc></doc>\n",
+ "<!ELEMENT doc EMPTY>\n"
+ "<!ENTITY % e1 SYSTEM 'second.ent'>\n"
+ "<!ENTITY % e2 '%e1;'>\n"
+ "%e1;\n",
+ "\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>" /* UTF-8 BOM */,
+ strlen("\xEF\xBB\xBF<!ATTLIST doc a1 CDATA 'value'>"), filled_later},
+ {"<!DOCTYPE r [\n"
+ " <!ENTITY five SYSTEM 'first.ent'>\n"
+ "]>\n"
+ "<r>&five;</r>",
+ "\xEF\xBB\xBF" /* UTF-8 BOM */, NULL, 0, filled_later},
+ };
+
+ const size_t countCases = sizeof(cases) / sizeof(cases[0]);
+ size_t u = 0;
+ for (; u < countCases; u++) {
+ size_t v = 0;
+ for (; v < 2; v++) {
+ const XML_Bool singleBytesWanted = (v == 0) ? XML_FALSE : XML_TRUE;
+ const unsigned long long expectedCountBytesDirect
+ = strlen(cases[u].primaryText);
+ const unsigned long long expectedCountBytesIndirect
+ = (cases[u].firstExternalText ? strlen(cases[u].firstExternalText)
+ : 0)
+ + (cases[u].secondExternalText ? strlen(cases[u].secondExternalText)
+ : 0)
+ + cases[u].expectedCountBytesIndirectExtra;
+
+ XML_Parser parser = XML_ParserCreate(NULL);
+ XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS);
+ if (cases[u].firstExternalText) {
+ XML_SetExternalEntityRefHandler(parser,
+ accounting_external_entity_ref_handler);
+ XML_SetUserData(parser, (void *)&cases[u]);
+ cases[u].singleBytesWanted = singleBytesWanted;
+ }
+
+ const XmlParseFunction xmlParseFunction
+ = singleBytesWanted ? _XML_Parse_SINGLE_BYTES : XML_Parse;
+
+ enum XML_Status status
+ = xmlParseFunction(parser, cases[u].primaryText,
+ (int)strlen(cases[u].primaryText), XML_TRUE);
+ if (status != XML_STATUS_OK) {
+ _xml_failure(parser, __FILE__, __LINE__);
+ }
+
+ const unsigned long long actualCountBytesDirect
+ = testingAccountingGetCountBytesDirect(parser);
+ const unsigned long long actualCountBytesIndirect
+ = testingAccountingGetCountBytesIndirect(parser);
+
+ XML_ParserFree(parser);
+
+ if (actualCountBytesDirect != expectedCountBytesDirect) {
+ fprintf(
+ stderr,
+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
+ "") " count direct bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
+ expectedCountBytesDirect, actualCountBytesDirect);
+ fail("Count of direct bytes is off");
+ }
+
+ if (actualCountBytesIndirect != expectedCountBytesIndirect) {
+ fprintf(
+ stderr,
+ "Document " EXPAT_FMT_SIZE_T("") " of " EXPAT_FMT_SIZE_T("") ", %s: Expected " EXPAT_FMT_ULL(
+ "") " count indirect bytes, got " EXPAT_FMT_ULL("") " instead.\n",
+ u + 1, countCases, singleBytesWanted ? "single bytes" : "chunks",
+ expectedCountBytesIndirect, actualCountBytesIndirect);
+ fail("Count of indirect bytes is off");
+ }
+ }
+ }
+}
+END_TEST
+#endif // defined(XML_DTD)
+
static Suite *
make_suite(void) {
Suite *s = suite_create("basic");
@@ -11233,6 +11523,9 @@ make_suite(void) {
TCase *tc_misc = tcase_create("miscellaneous tests");
TCase *tc_alloc = tcase_create("allocation tests");
TCase *tc_nsalloc = tcase_create("namespace allocation tests");
+#if defined(XML_DTD)
+ TCase *tc_accounting = tcase_create("accounting tests");
+#endif
suite_add_tcase(s, tc_basic);
tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown);
@@ -11593,6 +11886,11 @@ make_suite(void) {
tcase_add_test(tc_nsalloc, test_nsalloc_long_systemid_in_ext);
tcase_add_test(tc_nsalloc, test_nsalloc_prefixed_element);
+#if defined(XML_DTD)
+ suite_add_tcase(s, tc_accounting);
+ tcase_add_test(tc_accounting, test_accounting_precision);
+#endif
+
return s;
}