tests: Cover accounting

This commit is contained in:
Sebastian Pipping 2021-04-21 00:11:04 +02:00
parent 29c3748788
commit 271efb6069
2 changed files with 304 additions and 2 deletions

View file

@ -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

View file

@ -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;
}