diff options
author | Sebastian Pipping <sebastian@pipping.org> | 2021-04-21 00:11:04 +0200 |
---|---|---|
committer | Sebastian Pipping <sebastian@pipping.org> | 2021-05-07 18:25:07 +0200 |
commit | 271efb6069c5c979545cfbe00b738184c5632b93 (patch) | |
tree | dc2e00c6c0b81dde5cd7e11168f7133ea478cf3c | |
parent | 29c3748788ff5ba0e4b14b02dfa15080177a3c8c (diff) | |
download | libexpat-git-271efb6069c5c979545cfbe00b738184c5632b93.tar.gz |
tests: Cover accounting
-rw-r--r-- | expat/lib/internal.h | 6 | ||||
-rw-r--r-- | expat/tests/runtests.c | 300 |
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=\"&'><"\" />", 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>&'><"</e>", NULL, NULL, + sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later}, + {"<e>A)</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 \"<!ENTITY nine "123456789">\">\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 \"%comment;<!--22-->%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 \"<!ENTITY five2 "[%five;][%five;]]]]">\">\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; } |