mirror of
https://github.com/libexpat/libexpat.git
synced 2025-04-13 08:02:56 +00:00
tests: Cover accounting
This commit is contained in:
parent
29c3748788
commit
271efb6069
2 changed files with 304 additions and 2 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue