From 271efb6069c5c979545cfbe00b738184c5632b93 Mon Sep 17 00:00:00 2001 From: Sebastian Pipping Date: Wed, 21 Apr 2021 00:11:04 +0200 Subject: tests: Cover accounting --- expat/lib/internal.h | 6 +- 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[] = { + {"", NULL, NULL, 0, 0}, + {"", NULL, NULL, 0, 0}, + + /* Attributes */ + {"", NULL, NULL, 0, filled_later}, + {"", NULL, NULL, 0, 0}, + {"", NULL, NULL, 0, + filled_later}, + {"", NULL, NULL, + sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later}, + {"\n" + " \n" + "", + NULL, NULL, 0, filled_later}, + + /* Text */ + {"text", NULL, NULL, 0, filled_later}, + {"text1text2", NULL, NULL, 0, filled_later}, + {"&'><"", NULL, NULL, + sizeof(XML_Char) * 5 /* number of predefined entites */, filled_later}, + {"A)", NULL, NULL, 0, filled_later}, + + /* Prolog */ + {"", NULL, NULL, 0, filled_later}, + + /* Whitespace */ + {" ", NULL, NULL, 0, filled_later}, + {"", NULL, NULL, 0, filled_later}, + {"", NULL, NULL, 0, filled_later}, + + /* Comments */ + {"", NULL, NULL, 0, filled_later}, + + /* Processing instructions */ + {"", + NULL, NULL, 0, filled_later}, + {"", + "%e1;", "", + 0, filled_later}, + + /* CDATA */ + {"", NULL, NULL, 0, filled_later}, + + /* Conditional sections */ + {"\n" + "\n" + "\n" + "%import;\n" + "]>\n" + "\n", + "]]>\n" + "]]>", + NULL, sizeof(XML_Char) * (strlen("INCLUDE") + strlen("IGNORE")), + filled_later}, + + /* General entities */ + {"\n" + "]>\n" + "&nine;", + NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later}, + {"\n" + "]>\n" + "", + NULL, NULL, sizeof(XML_Char) * strlen("123456789"), filled_later}, + {"\n" + "\n" + "]>\n" + "&nine2;&nine2;&nine2;", + NULL, NULL, + sizeof(XML_Char) * 3 /* calls to &nine2; */ * 2 /* calls to &nine; */ + * (strlen("&nine;") + strlen("123456789")), + filled_later}, + {"\n" + "]>\n" + "&five;", + "12345", NULL, 0, filled_later}, + + /* Parameter entities */ + {"\">\n" + "%comment;\n" + "]>\n" + "", + NULL, NULL, sizeof(XML_Char) * strlen(""), filled_later}, + {"\n" + "%ninedef;\n" + "]>\n" + "&nine;", + NULL, NULL, + sizeof(XML_Char) + * (strlen("") + strlen("123456789")), + filled_later}, + {"\">\n" + "%comment;\">\n" + "%comment2;\n" + "]>\n" + "\n", + NULL, NULL, + sizeof(XML_Char) + * (strlen("%comment;%comment;") + 2 * strlen("")), + filled_later}, + {"\n" + " \n" + " %five2def;\n" + "]>\n" + "&five2;", + NULL, NULL, /* from "%five2def;": */ + sizeof(XML_Char) + * (strlen("") + + 2 /* calls to "%five;" */ * strlen("12345") + + /* from "&five2;": */ strlen("[12345][12345]]]]")), + filled_later}, + {"\n" + "", + "'>\n" + "%comment;%comment;'>\n" + "%comment2;", + NULL, + sizeof(XML_Char) + * (strlen("%comment;%comment;") + + 2 /* calls to "%comment;" */ * strlen("")), + filled_later}, + {"\n" + "", + "\n" + "%e1;'>\n" + "%e2;\n", + "", sizeof(XML_Char) * strlen(""), + filled_later}, + { + "\n" + "", + "\n" + "", + "\n" + "hello\n" + "xml" /* without trailing newline! */, + 0, + filled_later, + }, + { + "\n" + "", + "\n" + "", + "\n" + "hello\n" + "xml\n" /* with trailing newline! */, + 0, + filled_later, + }, + {"\n" + "\n", + "\n" + "\n" + "\n" + "%e1;\n", + "\xEF\xBB\xBF" /* UTF-8 BOM */, + strlen("\xEF\xBB\xBF"), filled_later}, + {"\n" + "]>\n" + "&five;", + "\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; } -- cgit v1.2.1