| /* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd |
| See the file COPYING for copying permission. |
| |
| runtest.c : run the Expat test suite |
| */ |
| |
| #ifdef HAVE_EXPAT_CONFIG_H |
| #include <expat_config.h> |
| #endif |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <stdio.h> |
| #include <string.h> |
| |
| #include "expat.h" |
| #include "chardata.h" |
| #include "minicheck.h" |
| |
| #if defined(__amigaos__) && defined(__USE_INLINE__) |
| #include <proto/expat.h> |
| #endif |
| |
| #ifdef XML_LARGE_SIZE |
| #define XML_FMT_INT_MOD "ll" |
| #else |
| #define XML_FMT_INT_MOD "l" |
| #endif |
| |
| static XML_Parser parser; |
| |
| |
| static void |
| basic_setup(void) |
| { |
| parser = XML_ParserCreate(NULL); |
| if (parser == NULL) |
| fail("Parser not created."); |
| } |
| |
| static void |
| basic_teardown(void) |
| { |
| if (parser != NULL) |
| XML_ParserFree(parser); |
| } |
| |
| /* Generate a failure using the parser state to create an error message; |
| this should be used when the parser reports an error we weren't |
| expecting. |
| */ |
| static void |
| _xml_failure(XML_Parser parser, const char *file, int line) |
| { |
| char buffer[1024]; |
| enum XML_Error err = XML_GetErrorCode(parser); |
| sprintf(buffer, |
| " %d: %s (line %" XML_FMT_INT_MOD "u, offset %"\ |
| XML_FMT_INT_MOD "u)\n reported from %s, line %d\n", |
| err, |
| XML_ErrorString(err), |
| XML_GetCurrentLineNumber(parser), |
| XML_GetCurrentColumnNumber(parser), |
| file, line); |
| _fail_unless(0, file, line, buffer); |
| } |
| |
| #define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) |
| |
| static void |
| _expect_failure(char *text, enum XML_Error errorCode, char *errorMessage, |
| char *file, int lineno) |
| { |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) |
| /* Hackish use of _fail_unless() macro, but let's us report |
| the right filename and line number. */ |
| _fail_unless(0, file, lineno, errorMessage); |
| if (XML_GetErrorCode(parser) != errorCode) |
| _xml_failure(parser, file, lineno); |
| } |
| |
| #define expect_failure(text, errorCode, errorMessage) \ |
| _expect_failure((text), (errorCode), (errorMessage), \ |
| __FILE__, __LINE__) |
| |
| /* Dummy handlers for when we need to set a handler to tickle a bug, |
| but it doesn't need to do anything. |
| */ |
| |
| static void XMLCALL |
| dummy_start_doctype_handler(void *userData, |
| const XML_Char *doctypeName, |
| const XML_Char *sysid, |
| const XML_Char *pubid, |
| int has_internal_subset) |
| {} |
| |
| static void XMLCALL |
| dummy_end_doctype_handler(void *userData) |
| {} |
| |
| static void XMLCALL |
| dummy_entity_decl_handler(void *userData, |
| const XML_Char *entityName, |
| int is_parameter_entity, |
| const XML_Char *value, |
| int value_length, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId, |
| const XML_Char *notationName) |
| {} |
| |
| static void XMLCALL |
| dummy_notation_decl_handler(void *userData, |
| const XML_Char *notationName, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| {} |
| |
| static void XMLCALL |
| dummy_element_decl_handler(void *userData, |
| const XML_Char *name, |
| XML_Content *model) |
| {} |
| |
| static void XMLCALL |
| dummy_attlist_decl_handler(void *userData, |
| const XML_Char *elname, |
| const XML_Char *attname, |
| const XML_Char *att_type, |
| const XML_Char *dflt, |
| int isrequired) |
| {} |
| |
| static void XMLCALL |
| dummy_comment_handler(void *userData, const XML_Char *data) |
| {} |
| |
| static void XMLCALL |
| dummy_pi_handler(void *userData, const XML_Char *target, const XML_Char *data) |
| {} |
| |
| static void XMLCALL |
| dummy_start_element(void *userData, |
| const XML_Char *name, const XML_Char **atts) |
| {} |
| |
| |
| /* |
| * Character & encoding tests. |
| */ |
| |
| START_TEST(test_nul_byte) |
| { |
| char text[] = "<doc>\0</doc>"; |
| |
| /* test that a NUL byte (in US-ASCII data) is an error */ |
| if (XML_Parse(parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_OK) |
| fail("Parser did not report error on NUL-byte."); |
| if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| |
| START_TEST(test_u0000_char) |
| { |
| /* test that a NUL byte (in US-ASCII data) is an error */ |
| expect_failure("<doc>�</doc>", |
| XML_ERROR_BAD_CHAR_REF, |
| "Parser did not report error on NUL-byte."); |
| } |
| END_TEST |
| |
| START_TEST(test_bom_utf8) |
| { |
| /* This test is really just making sure we don't core on a UTF-8 BOM. */ |
| char *text = "\357\273\277<e/>"; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| START_TEST(test_bom_utf16_be) |
| { |
| char text[] = "\376\377\0<\0e\0/\0>"; |
| |
| if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| START_TEST(test_bom_utf16_le) |
| { |
| char text[] = "\377\376<\0e\0/\0>\0"; |
| |
| if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| static void XMLCALL |
| accumulate_characters(void *userData, const XML_Char *s, int len) |
| { |
| CharData_AppendXMLChars((CharData *)userData, s, len); |
| } |
| |
| static void XMLCALL |
| accumulate_attribute(void *userData, const XML_Char *name, |
| const XML_Char **atts) |
| { |
| CharData *storage = (CharData *)userData; |
| if (storage->count < 0 && atts != NULL && atts[0] != NULL) { |
| /* "accumulate" the value of the first attribute we see */ |
| CharData_AppendXMLChars(storage, atts[1], -1); |
| } |
| } |
| |
| |
| static void |
| _run_character_check(XML_Char *text, XML_Char *expected, |
| const char *file, int line) |
| { |
| CharData storage; |
| |
| CharData_Init(&storage); |
| XML_SetUserData(parser, &storage); |
| XML_SetCharacterDataHandler(parser, accumulate_characters); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| _xml_failure(parser, file, line); |
| CharData_CheckXMLChars(&storage, expected); |
| } |
| |
| #define run_character_check(text, expected) \ |
| _run_character_check(text, expected, __FILE__, __LINE__) |
| |
| static void |
| _run_attribute_check(XML_Char *text, XML_Char *expected, |
| const char *file, int line) |
| { |
| CharData storage; |
| |
| CharData_Init(&storage); |
| XML_SetUserData(parser, &storage); |
| XML_SetStartElementHandler(parser, accumulate_attribute); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| _xml_failure(parser, file, line); |
| CharData_CheckXMLChars(&storage, expected); |
| } |
| |
| #define run_attribute_check(text, expected) \ |
| _run_attribute_check(text, expected, __FILE__, __LINE__) |
| |
| /* Regression test for SF bug #491986. */ |
| START_TEST(test_danish_latin1) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='iso-8859-1'?>\n" |
| "<e>J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5</e>"; |
| run_character_check(text, |
| "J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85"); |
| } |
| END_TEST |
| |
| |
| /* Regression test for SF bug #514281. */ |
| START_TEST(test_french_charref_hexidecimal) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='iso-8859-1'?>\n" |
| "<doc>éèàçêÈ</doc>"; |
| run_character_check(text, |
| "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); |
| } |
| END_TEST |
| |
| START_TEST(test_french_charref_decimal) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='iso-8859-1'?>\n" |
| "<doc>éèàçêÈ</doc>"; |
| run_character_check(text, |
| "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); |
| } |
| END_TEST |
| |
| START_TEST(test_french_latin1) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='iso-8859-1'?>\n" |
| "<doc>\xE9\xE8\xE0\xE7\xEa\xC8</doc>"; |
| run_character_check(text, |
| "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); |
| } |
| END_TEST |
| |
| START_TEST(test_french_utf8) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='utf-8'?>\n" |
| "<doc>\xC3\xA9</doc>"; |
| run_character_check(text, "\xC3\xA9"); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #600479. |
| XXX There should be a test that exercises all legal XML Unicode |
| characters as PCDATA and attribute value content, and XML Name |
| characters as part of element and attribute names. |
| */ |
| START_TEST(test_utf8_false_rejection) |
| { |
| char *text = "<doc>\xEF\xBA\xBF</doc>"; |
| run_character_check(text, "\xEF\xBA\xBF"); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #477667. |
| This test assures that any 8-bit character followed by a 7-bit |
| character will not be mistakenly interpreted as a valid UTF-8 |
| sequence. |
| */ |
| START_TEST(test_illegal_utf8) |
| { |
| char text[100]; |
| int i; |
| |
| for (i = 128; i <= 255; ++i) { |
| sprintf(text, "<e>%ccd</e>", i); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) { |
| sprintf(text, |
| "expected token error for '%c' (ordinal %d) in UTF-8 text", |
| i, i); |
| fail(text); |
| } |
| else if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) |
| xml_failure(parser); |
| /* Reset the parser since we use the same parser repeatedly. */ |
| XML_ParserReset(parser, NULL); |
| } |
| } |
| END_TEST |
| |
| START_TEST(test_utf16) |
| { |
| /* <?xml version="1.0" encoding="UTF-16"?> |
| <doc a='123'>some text</doc> |
| */ |
| char text[] = |
| "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o" |
| "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o" |
| "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066" |
| "\000'\000?\000>\000\n" |
| "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'" |
| "\000>\000s\000o\000m\000e\000 \000t\000e\000x\000t\000<\000/" |
| "\000d\000o\000c\000>"; |
| if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| START_TEST(test_utf16_le_epilog_newline) |
| { |
| unsigned int first_chunk_bytes = 17; |
| char text[] = |
| "\xFF\xFE" /* BOM */ |
| "<\000e\000/\000>\000" /* document element */ |
| "\r\000\n\000\r\000\n\000"; /* epilog */ |
| |
| if (first_chunk_bytes >= sizeof(text) - 1) |
| fail("bad value of first_chunk_bytes"); |
| if ( XML_Parse(parser, text, first_chunk_bytes, XML_FALSE) |
| == XML_STATUS_ERROR) |
| xml_failure(parser); |
| else { |
| enum XML_Status rc; |
| rc = XML_Parse(parser, text + first_chunk_bytes, |
| sizeof(text) - first_chunk_bytes - 1, XML_TRUE); |
| if (rc == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #481609, #774028. */ |
| START_TEST(test_latin1_umlauts) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='iso-8859-1'?>\n" |
| "<e a='\xE4 \xF6 \xFC ä ö ü ä ö ü >'\n" |
| " >\xE4 \xF6 \xFC ä ö ü ä ö ü ></e>"; |
| char *utf8 = |
| "\xC3\xA4 \xC3\xB6 \xC3\xBC " |
| "\xC3\xA4 \xC3\xB6 \xC3\xBC " |
| "\xC3\xA4 \xC3\xB6 \xC3\xBC >"; |
| run_character_check(text, utf8); |
| XML_ParserReset(parser, NULL); |
| run_attribute_check(text, utf8); |
| } |
| END_TEST |
| |
| /* Regression test #1 for SF bug #653180. */ |
| START_TEST(test_line_number_after_parse) |
| { |
| char *text = |
| "<tag>\n" |
| "\n" |
| "\n</tag>"; |
| XML_Size lineno; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| lineno = XML_GetCurrentLineNumber(parser); |
| if (lineno != 4) { |
| char buffer[100]; |
| sprintf(buffer, |
| "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno); |
| fail(buffer); |
| } |
| } |
| END_TEST |
| |
| /* Regression test #2 for SF bug #653180. */ |
| START_TEST(test_column_number_after_parse) |
| { |
| char *text = "<tag></tag>"; |
| XML_Size colno; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| colno = XML_GetCurrentColumnNumber(parser); |
| if (colno != 11) { |
| char buffer[100]; |
| sprintf(buffer, |
| "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno); |
| fail(buffer); |
| } |
| } |
| END_TEST |
| |
| static void XMLCALL |
| start_element_event_handler2(void *userData, const XML_Char *name, |
| const XML_Char **attr) |
| { |
| CharData *storage = (CharData *) userData; |
| char buffer[100]; |
| |
| sprintf(buffer, |
| "<%s> at col:%" XML_FMT_INT_MOD "u line:%"\ |
| XML_FMT_INT_MOD "u\n", name, |
| XML_GetCurrentColumnNumber(parser), |
| XML_GetCurrentLineNumber(parser)); |
| CharData_AppendString(storage, buffer); |
| } |
| |
| static void XMLCALL |
| end_element_event_handler2(void *userData, const XML_Char *name) |
| { |
| CharData *storage = (CharData *) userData; |
| char buffer[100]; |
| |
| sprintf(buffer, |
| "</%s> at col:%" XML_FMT_INT_MOD "u line:%"\ |
| XML_FMT_INT_MOD "u\n", name, |
| XML_GetCurrentColumnNumber(parser), |
| XML_GetCurrentLineNumber(parser)); |
| CharData_AppendString(storage, buffer); |
| } |
| |
| /* Regression test #3 for SF bug #653180. */ |
| START_TEST(test_line_and_column_numbers_inside_handlers) |
| { |
| char *text = |
| "<a>\n" /* Unix end-of-line */ |
| " <b>\r\n" /* Windows end-of-line */ |
| " <c/>\r" /* Mac OS end-of-line */ |
| " </b>\n" |
| " <d>\n" |
| " <f/>\n" |
| " </d>\n" |
| "</a>"; |
| char *expected = |
| "<a> at col:0 line:1\n" |
| "<b> at col:2 line:2\n" |
| "<c> at col:4 line:3\n" |
| "</c> at col:8 line:3\n" |
| "</b> at col:2 line:4\n" |
| "<d> at col:2 line:5\n" |
| "<f> at col:4 line:6\n" |
| "</f> at col:8 line:6\n" |
| "</d> at col:2 line:7\n" |
| "</a> at col:0 line:8\n"; |
| CharData storage; |
| |
| CharData_Init(&storage); |
| XML_SetUserData(parser, &storage); |
| XML_SetStartElementHandler(parser, start_element_event_handler2); |
| XML_SetEndElementHandler(parser, end_element_event_handler2); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| |
| CharData_CheckString(&storage, expected); |
| } |
| END_TEST |
| |
| /* Regression test #4 for SF bug #653180. */ |
| START_TEST(test_line_number_after_error) |
| { |
| char *text = |
| "<a>\n" |
| " <b>\n" |
| " </a>"; /* missing </b> */ |
| XML_Size lineno; |
| if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) |
| fail("Expected a parse error"); |
| |
| lineno = XML_GetCurrentLineNumber(parser); |
| if (lineno != 3) { |
| char buffer[100]; |
| sprintf(buffer, "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno); |
| fail(buffer); |
| } |
| } |
| END_TEST |
| |
| /* Regression test #5 for SF bug #653180. */ |
| START_TEST(test_column_number_after_error) |
| { |
| char *text = |
| "<a>\n" |
| " <b>\n" |
| " </a>"; /* missing </b> */ |
| XML_Size colno; |
| if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) |
| fail("Expected a parse error"); |
| |
| colno = XML_GetCurrentColumnNumber(parser); |
| if (colno != 4) { |
| char buffer[100]; |
| sprintf(buffer, |
| "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno); |
| fail(buffer); |
| } |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #478332. */ |
| START_TEST(test_really_long_lines) |
| { |
| /* This parses an input line longer than INIT_DATA_BUF_SIZE |
| characters long (defined to be 1024 in xmlparse.c). We take a |
| really cheesy approach to building the input buffer, because |
| this avoids writing bugs in buffer-filling code. |
| */ |
| char *text = |
| "<e>" |
| /* 64 chars */ |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| /* until we have at least 1024 characters on the line: */ |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" |
| "</e>"; |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| |
| /* |
| * Element event tests. |
| */ |
| |
| static void XMLCALL |
| end_element_event_handler(void *userData, const XML_Char *name) |
| { |
| CharData *storage = (CharData *) userData; |
| CharData_AppendString(storage, "/"); |
| CharData_AppendXMLChars(storage, name, -1); |
| } |
| |
| START_TEST(test_end_element_events) |
| { |
| char *text = "<a><b><c/></b><d><f/></d></a>"; |
| char *expected = "/c/b/f/d/a"; |
| CharData storage; |
| |
| CharData_Init(&storage); |
| XML_SetUserData(parser, &storage); |
| XML_SetEndElementHandler(parser, end_element_event_handler); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| CharData_CheckString(&storage, expected); |
| } |
| END_TEST |
| |
| |
| /* |
| * Attribute tests. |
| */ |
| |
| /* Helpers used by the following test; this checks any "attr" and "refs" |
| attributes to make sure whitespace has been normalized. |
| |
| Return true if whitespace has been normalized in a string, using |
| the rules for attribute value normalization. The 'is_cdata' flag |
| is needed since CDATA attributes don't need to have multiple |
| whitespace characters collapsed to a single space, while other |
| attribute data types do. (Section 3.3.3 of the recommendation.) |
| */ |
| static int |
| is_whitespace_normalized(const XML_Char *s, int is_cdata) |
| { |
| int blanks = 0; |
| int at_start = 1; |
| while (*s) { |
| if (*s == ' ') |
| ++blanks; |
| else if (*s == '\t' || *s == '\n' || *s == '\r') |
| return 0; |
| else { |
| if (at_start) { |
| at_start = 0; |
| if (blanks && !is_cdata) |
| /* illegal leading blanks */ |
| return 0; |
| } |
| else if (blanks > 1 && !is_cdata) |
| return 0; |
| blanks = 0; |
| } |
| ++s; |
| } |
| if (blanks && !is_cdata) |
| return 0; |
| return 1; |
| } |
| |
| /* Check the attribute whitespace checker: */ |
| static void |
| testhelper_is_whitespace_normalized(void) |
| { |
| assert(is_whitespace_normalized("abc", 0)); |
| assert(is_whitespace_normalized("abc", 1)); |
| assert(is_whitespace_normalized("abc def ghi", 0)); |
| assert(is_whitespace_normalized("abc def ghi", 1)); |
| assert(!is_whitespace_normalized(" abc def ghi", 0)); |
| assert(is_whitespace_normalized(" abc def ghi", 1)); |
| assert(!is_whitespace_normalized("abc def ghi", 0)); |
| assert(is_whitespace_normalized("abc def ghi", 1)); |
| assert(!is_whitespace_normalized("abc def ghi ", 0)); |
| assert(is_whitespace_normalized("abc def ghi ", 1)); |
| assert(!is_whitespace_normalized(" ", 0)); |
| assert(is_whitespace_normalized(" ", 1)); |
| assert(!is_whitespace_normalized("\t", 0)); |
| assert(!is_whitespace_normalized("\t", 1)); |
| assert(!is_whitespace_normalized("\n", 0)); |
| assert(!is_whitespace_normalized("\n", 1)); |
| assert(!is_whitespace_normalized("\r", 0)); |
| assert(!is_whitespace_normalized("\r", 1)); |
| assert(!is_whitespace_normalized("abc\t def", 1)); |
| } |
| |
| static void XMLCALL |
| check_attr_contains_normalized_whitespace(void *userData, |
| const XML_Char *name, |
| const XML_Char **atts) |
| { |
| int i; |
| for (i = 0; atts[i] != NULL; i += 2) { |
| const XML_Char *attrname = atts[i]; |
| const XML_Char *value = atts[i + 1]; |
| if (strcmp("attr", attrname) == 0 |
| || strcmp("ents", attrname) == 0 |
| || strcmp("refs", attrname) == 0) { |
| if (!is_whitespace_normalized(value, 0)) { |
| char buffer[256]; |
| sprintf(buffer, "attribute value not normalized: %s='%s'", |
| attrname, value); |
| fail(buffer); |
| } |
| } |
| } |
| } |
| |
| START_TEST(test_attr_whitespace_normalization) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| " <!ATTLIST doc\n" |
| " attr NMTOKENS #REQUIRED\n" |
| " ents ENTITIES #REQUIRED\n" |
| " refs IDREFS #REQUIRED>\n" |
| "]>\n" |
| "<doc attr=' a b c\t\td\te\t' refs=' id-1 \t id-2\t\t' \n" |
| " ents=' ent-1 \t\r\n" |
| " ent-2 ' >\n" |
| " <e id='id-1'/>\n" |
| " <e id='id-2'/>\n" |
| "</doc>"; |
| |
| XML_SetStartElementHandler(parser, |
| check_attr_contains_normalized_whitespace); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| |
| /* |
| * XML declaration tests. |
| */ |
| |
| START_TEST(test_xmldecl_misplaced) |
| { |
| expect_failure("\n" |
| "<?xml version='1.0'?>\n" |
| "<a/>", |
| XML_ERROR_MISPLACED_XML_PI, |
| "failed to report misplaced XML declaration"); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #584832. */ |
| static int XMLCALL |
| UnknownEncodingHandler(void *data,const XML_Char *encoding,XML_Encoding *info) |
| { |
| if (strcmp(encoding,"unsupported-encoding") == 0) { |
| int i; |
| for (i = 0; i < 256; ++i) |
| info->map[i] = i; |
| info->data = NULL; |
| info->convert = NULL; |
| info->release = NULL; |
| return XML_STATUS_OK; |
| } |
| return XML_STATUS_ERROR; |
| } |
| |
| START_TEST(test_unknown_encoding_internal_entity) |
| { |
| char *text = |
| "<?xml version='1.0' encoding='unsupported-encoding'?>\n" |
| "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n" |
| "<test a='&foo;'/>"; |
| |
| XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, NULL); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #620106. */ |
| static int XMLCALL |
| external_entity_loader_set_encoding(XML_Parser parser, |
| const XML_Char *context, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| { |
| /* This text says it's an unsupported encoding, but it's really |
| UTF-8, which we tell Expat using XML_SetEncoding(). |
| */ |
| char *text = |
| "<?xml encoding='iso-8859-3'?>" |
| "\xC3\xA9"; |
| XML_Parser extparser; |
| |
| extparser = XML_ExternalEntityParserCreate(parser, context, NULL); |
| if (extparser == NULL) |
| fail("Could not create external entity parser."); |
| if (!XML_SetEncoding(extparser, "utf-8")) |
| fail("XML_SetEncoding() ignored for external entity"); |
| if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) |
| == XML_STATUS_ERROR) { |
| xml_failure(parser); |
| return 0; |
| } |
| return 1; |
| } |
| |
| START_TEST(test_ext_entity_set_encoding) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| " <!ENTITY en SYSTEM 'http://xml.libexpat.org/dummy.ent'>\n" |
| "]>\n" |
| "<doc>&en;</doc>"; |
| |
| XML_SetExternalEntityRefHandler(parser, |
| external_entity_loader_set_encoding); |
| run_character_check(text, "\xC3\xA9"); |
| } |
| END_TEST |
| |
| /* Test that no error is reported for unknown entities if we don't |
| read an external subset. This was fixed in Expat 1.95.5. |
| */ |
| START_TEST(test_wfc_undeclared_entity_unread_external_subset) { |
| char *text = |
| "<!DOCTYPE doc SYSTEM 'foo'>\n" |
| "<doc>&entity;</doc>"; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Test that an error is reported for unknown entities if we don't |
| have an external subset. |
| */ |
| START_TEST(test_wfc_undeclared_entity_no_external_subset) { |
| expect_failure("<doc>&entity;</doc>", |
| XML_ERROR_UNDEFINED_ENTITY, |
| "Parser did not report undefined entity w/out a DTD."); |
| } |
| END_TEST |
| |
| /* Test that an error is reported for unknown entities if we don't |
| read an external subset, but have been declared standalone. |
| */ |
| START_TEST(test_wfc_undeclared_entity_standalone) { |
| char *text = |
| "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n" |
| "<!DOCTYPE doc SYSTEM 'foo'>\n" |
| "<doc>&entity;</doc>"; |
| |
| expect_failure(text, |
| XML_ERROR_UNDEFINED_ENTITY, |
| "Parser did not report undefined entity (standalone)."); |
| } |
| END_TEST |
| |
| static int XMLCALL |
| external_entity_loader(XML_Parser parser, |
| const XML_Char *context, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| { |
| char *text = (char *)XML_GetUserData(parser); |
| XML_Parser extparser; |
| |
| extparser = XML_ExternalEntityParserCreate(parser, context, NULL); |
| if (extparser == NULL) |
| fail("Could not create external entity parser."); |
| if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) |
| == XML_STATUS_ERROR) { |
| xml_failure(parser); |
| return XML_STATUS_ERROR; |
| } |
| return XML_STATUS_OK; |
| } |
| |
| /* Test that an error is reported for unknown entities if we have read |
| an external subset, and standalone is true. |
| */ |
| START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) { |
| char *text = |
| "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n" |
| "<!DOCTYPE doc SYSTEM 'foo'>\n" |
| "<doc>&entity;</doc>"; |
| char *foo_text = |
| "<!ELEMENT doc (#PCDATA)*>"; |
| |
| XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); |
| XML_SetUserData(parser, foo_text); |
| XML_SetExternalEntityRefHandler(parser, external_entity_loader); |
| expect_failure(text, |
| XML_ERROR_UNDEFINED_ENTITY, |
| "Parser did not report undefined entity (external DTD)."); |
| } |
| END_TEST |
| |
| /* Test that no error is reported for unknown entities if we have read |
| an external subset, and standalone is false. |
| */ |
| START_TEST(test_wfc_undeclared_entity_with_external_subset) { |
| char *text = |
| "<?xml version='1.0' encoding='us-ascii'?>\n" |
| "<!DOCTYPE doc SYSTEM 'foo'>\n" |
| "<doc>&entity;</doc>"; |
| char *foo_text = |
| "<!ELEMENT doc (#PCDATA)*>"; |
| |
| XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); |
| XML_SetUserData(parser, foo_text); |
| XML_SetExternalEntityRefHandler(parser, external_entity_loader); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| START_TEST(test_wfc_no_recursive_entity_refs) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| " <!ENTITY entity '&entity;'>\n" |
| "]>\n" |
| "<doc>&entity;</doc>"; |
| |
| expect_failure(text, |
| XML_ERROR_RECURSIVE_ENTITY_REF, |
| "Parser did not report recursive entity reference."); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #483514. */ |
| START_TEST(test_dtd_default_handling) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| "<!ENTITY e SYSTEM 'http://xml.libexpat.org/e'>\n" |
| "<!NOTATION n SYSTEM 'http://xml.libexpat.org/n'>\n" |
| "<!ELEMENT doc EMPTY>\n" |
| "<!ATTLIST doc a CDATA #IMPLIED>\n" |
| "<?pi in dtd?>\n" |
| "<!--comment in dtd-->\n" |
| "]><doc/>"; |
| |
| XML_SetDefaultHandler(parser, accumulate_characters); |
| XML_SetDoctypeDeclHandler(parser, |
| dummy_start_doctype_handler, |
| dummy_end_doctype_handler); |
| XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); |
| XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); |
| XML_SetElementDeclHandler(parser, dummy_element_decl_handler); |
| XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); |
| XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); |
| XML_SetCommentHandler(parser, dummy_comment_handler); |
| run_character_check(text, "\n\n\n\n\n\n\n<doc/>"); |
| } |
| END_TEST |
| |
| /* See related SF bug #673791. |
| When namespace processing is enabled, setting the namespace URI for |
| a prefix is not allowed; this test ensures that it *is* allowed |
| when namespace processing is not enabled. |
| (See Namespaces in XML, section 2.) |
| */ |
| START_TEST(test_empty_ns_without_namespaces) |
| { |
| char *text = |
| "<doc xmlns:prefix='http://www.example.com/'>\n" |
| " <e xmlns:prefix=''/>\n" |
| "</doc>"; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #824420. |
| Checks that an xmlns:prefix attribute set in an attribute's default |
| value isn't misinterpreted. |
| */ |
| START_TEST(test_ns_in_attribute_default_without_namespaces) |
| { |
| char *text = |
| "<!DOCTYPE e:element [\n" |
| " <!ATTLIST e:element\n" |
| " xmlns:e CDATA 'http://example.com/'>\n" |
| " ]>\n" |
| "<e:element/>"; |
| |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| static char *long_character_data_text = |
| "<?xml version='1.0' encoding='iso-8859-1'?><s>" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "012345678901234567890123456789012345678901234567890123456789" |
| "</s>"; |
| |
| static XML_Bool resumable = XML_FALSE; |
| |
| static void |
| clearing_aborting_character_handler(void *userData, |
| const XML_Char *s, int len) |
| { |
| XML_StopParser(parser, resumable); |
| XML_SetCharacterDataHandler(parser, NULL); |
| } |
| |
| /* Regression test for SF bug #1515266: missing check of stopped |
| parser in doContext() 'for' loop. */ |
| START_TEST(test_stop_parser_between_char_data_calls) |
| { |
| /* The sample data must be big enough that there are two calls to |
| the character data handler from within the inner "for" loop of |
| the XML_TOK_DATA_CHARS case in doContent(), and the character |
| handler must stop the parser and clear the character data |
| handler. |
| */ |
| char *text = long_character_data_text; |
| |
| XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); |
| resumable = XML_FALSE; |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) |
| xml_failure(parser); |
| if (XML_GetErrorCode(parser) != XML_ERROR_ABORTED) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #1515266: missing check of stopped |
| parser in doContext() 'for' loop. */ |
| START_TEST(test_suspend_parser_between_char_data_calls) |
| { |
| /* The sample data must be big enough that there are two calls to |
| the character data handler from within the inner "for" loop of |
| the XML_TOK_DATA_CHARS case in doContent(), and the character |
| handler must stop the parser and clear the character data |
| handler. |
| */ |
| char *text = long_character_data_text; |
| |
| XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); |
| resumable = XML_TRUE; |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) |
| xml_failure(parser); |
| if (XML_GetErrorCode(parser) != XML_ERROR_NONE) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| |
| /* |
| * Namespaces tests. |
| */ |
| |
| static void |
| namespace_setup(void) |
| { |
| parser = XML_ParserCreateNS(NULL, ' '); |
| if (parser == NULL) |
| fail("Parser not created."); |
| } |
| |
| static void |
| namespace_teardown(void) |
| { |
| basic_teardown(); |
| } |
| |
| /* Check that an element name and attribute name match the expected values. |
| The expected values are passed as an array reference of string pointers |
| provided as the userData argument; the first is the expected |
| element name, and the second is the expected attribute name. |
| */ |
| static void XMLCALL |
| triplet_start_checker(void *userData, const XML_Char *name, |
| const XML_Char **atts) |
| { |
| char **elemstr = (char **)userData; |
| char buffer[1024]; |
| if (strcmp(elemstr[0], name) != 0) { |
| sprintf(buffer, "unexpected start string: '%s'", name); |
| fail(buffer); |
| } |
| if (strcmp(elemstr[1], atts[0]) != 0) { |
| sprintf(buffer, "unexpected attribute string: '%s'", atts[0]); |
| fail(buffer); |
| } |
| } |
| |
| /* Check that the element name passed to the end-element handler matches |
| the expected value. The expected value is passed as the first element |
| in an array of strings passed as the userData argument. |
| */ |
| static void XMLCALL |
| triplet_end_checker(void *userData, const XML_Char *name) |
| { |
| char **elemstr = (char **)userData; |
| if (strcmp(elemstr[0], name) != 0) { |
| char buffer[1024]; |
| sprintf(buffer, "unexpected end string: '%s'", name); |
| fail(buffer); |
| } |
| } |
| |
| START_TEST(test_return_ns_triplet) |
| { |
| char *text = |
| "<foo:e xmlns:foo='http://expat.sf.net/' bar:a='12'\n" |
| " xmlns:bar='http://expat.sf.net/'></foo:e>"; |
| char *elemstr[] = { |
| "http://expat.sf.net/ e foo", |
| "http://expat.sf.net/ a bar" |
| }; |
| XML_SetReturnNSTriplet(parser, XML_TRUE); |
| XML_SetUserData(parser, elemstr); |
| XML_SetElementHandler(parser, triplet_start_checker, triplet_end_checker); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| static void XMLCALL |
| overwrite_start_checker(void *userData, const XML_Char *name, |
| const XML_Char **atts) |
| { |
| CharData *storage = (CharData *) userData; |
| CharData_AppendString(storage, "start "); |
| CharData_AppendXMLChars(storage, name, -1); |
| while (*atts != NULL) { |
| CharData_AppendString(storage, "\nattribute "); |
| CharData_AppendXMLChars(storage, *atts, -1); |
| atts += 2; |
| } |
| CharData_AppendString(storage, "\n"); |
| } |
| |
| static void XMLCALL |
| overwrite_end_checker(void *userData, const XML_Char *name) |
| { |
| CharData *storage = (CharData *) userData; |
| CharData_AppendString(storage, "end "); |
| CharData_AppendXMLChars(storage, name, -1); |
| CharData_AppendString(storage, "\n"); |
| } |
| |
| static void |
| run_ns_tagname_overwrite_test(char *text, char *result) |
| { |
| CharData storage; |
| CharData_Init(&storage); |
| XML_SetUserData(parser, &storage); |
| XML_SetElementHandler(parser, |
| overwrite_start_checker, overwrite_end_checker); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| CharData_CheckString(&storage, result); |
| } |
| |
| /* Regression test for SF bug #566334. */ |
| START_TEST(test_ns_tagname_overwrite) |
| { |
| char *text = |
| "<n:e xmlns:n='http://xml.libexpat.org/'>\n" |
| " <n:f n:attr='foo'/>\n" |
| " <n:g n:attr2='bar'/>\n" |
| "</n:e>"; |
| char *result = |
| "start http://xml.libexpat.org/ e\n" |
| "start http://xml.libexpat.org/ f\n" |
| "attribute http://xml.libexpat.org/ attr\n" |
| "end http://xml.libexpat.org/ f\n" |
| "start http://xml.libexpat.org/ g\n" |
| "attribute http://xml.libexpat.org/ attr2\n" |
| "end http://xml.libexpat.org/ g\n" |
| "end http://xml.libexpat.org/ e\n"; |
| run_ns_tagname_overwrite_test(text, result); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #566334. */ |
| START_TEST(test_ns_tagname_overwrite_triplet) |
| { |
| char *text = |
| "<n:e xmlns:n='http://xml.libexpat.org/'>\n" |
| " <n:f n:attr='foo'/>\n" |
| " <n:g n:attr2='bar'/>\n" |
| "</n:e>"; |
| char *result = |
| "start http://xml.libexpat.org/ e n\n" |
| "start http://xml.libexpat.org/ f n\n" |
| "attribute http://xml.libexpat.org/ attr n\n" |
| "end http://xml.libexpat.org/ f n\n" |
| "start http://xml.libexpat.org/ g n\n" |
| "attribute http://xml.libexpat.org/ attr2 n\n" |
| "end http://xml.libexpat.org/ g n\n" |
| "end http://xml.libexpat.org/ e n\n"; |
| XML_SetReturnNSTriplet(parser, XML_TRUE); |
| run_ns_tagname_overwrite_test(text, result); |
| } |
| END_TEST |
| |
| |
| /* Regression test for SF bug #620343. */ |
| static void XMLCALL |
| start_element_fail(void *userData, |
| const XML_Char *name, const XML_Char **atts) |
| { |
| /* We should never get here. */ |
| fail("should never reach start_element_fail()"); |
| } |
| |
| static void XMLCALL |
| start_ns_clearing_start_element(void *userData, |
| const XML_Char *prefix, |
| const XML_Char *uri) |
| { |
| XML_SetStartElementHandler((XML_Parser) userData, NULL); |
| } |
| |
| START_TEST(test_start_ns_clears_start_element) |
| { |
| /* This needs to use separate start/end tags; using the empty tag |
| syntax doesn't cause the problematic path through Expat to be |
| taken. |
| */ |
| char *text = "<e xmlns='http://xml.libexpat.org/'></e>"; |
| |
| XML_SetStartElementHandler(parser, start_element_fail); |
| XML_SetStartNamespaceDeclHandler(parser, start_ns_clearing_start_element); |
| XML_UseParserAsHandlerArg(parser); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #616863. */ |
| static int XMLCALL |
| external_entity_handler(XML_Parser parser, |
| const XML_Char *context, |
| const XML_Char *base, |
| const XML_Char *systemId, |
| const XML_Char *publicId) |
| { |
| long callno = 1 + (long)XML_GetUserData(parser); |
| char *text; |
| XML_Parser p2; |
| |
| if (callno == 1) |
| text = ("<!ELEMENT doc (e+)>\n" |
| "<!ATTLIST doc xmlns CDATA #IMPLIED>\n" |
| "<!ELEMENT e EMPTY>\n"); |
| else |
| text = ("<?xml version='1.0' encoding='us-ascii'?>" |
| "<e/>"); |
| |
| XML_SetUserData(parser, (void *) callno); |
| p2 = XML_ExternalEntityParserCreate(parser, context, NULL); |
| if (XML_Parse(p2, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { |
| xml_failure(p2); |
| return 0; |
| } |
| XML_ParserFree(p2); |
| return 1; |
| } |
| |
| START_TEST(test_default_ns_from_ext_subset_and_ext_ge) |
| { |
| char *text = |
| "<?xml version='1.0'?>\n" |
| "<!DOCTYPE doc SYSTEM 'http://xml.libexpat.org/doc.dtd' [\n" |
| " <!ENTITY en SYSTEM 'http://xml.libexpat.org/entity.ent'>\n" |
| "]>\n" |
| "<doc xmlns='http://xml.libexpat.org/ns1'>\n" |
| "&en;\n" |
| "</doc>"; |
| |
| XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); |
| XML_SetExternalEntityRefHandler(parser, external_entity_handler); |
| /* We actually need to set this handler to tickle this bug. */ |
| XML_SetStartElementHandler(parser, dummy_start_element); |
| XML_SetUserData(parser, NULL); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test #1 for SF bug #673791. */ |
| START_TEST(test_ns_prefix_with_empty_uri_1) |
| { |
| char *text = |
| "<doc xmlns:prefix='http://xml.libexpat.org/'>\n" |
| " <e xmlns:prefix=''/>\n" |
| "</doc>"; |
| |
| expect_failure(text, |
| XML_ERROR_UNDECLARING_PREFIX, |
| "Did not report re-setting namespace" |
| " URI with prefix to ''."); |
| } |
| END_TEST |
| |
| /* Regression test #2 for SF bug #673791. */ |
| START_TEST(test_ns_prefix_with_empty_uri_2) |
| { |
| char *text = |
| "<?xml version='1.0'?>\n" |
| "<docelem xmlns:pre=''/>"; |
| |
| expect_failure(text, |
| XML_ERROR_UNDECLARING_PREFIX, |
| "Did not report setting namespace URI with prefix to ''."); |
| } |
| END_TEST |
| |
| /* Regression test #3 for SF bug #673791. */ |
| START_TEST(test_ns_prefix_with_empty_uri_3) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| " <!ELEMENT doc EMPTY>\n" |
| " <!ATTLIST doc\n" |
| " xmlns:prefix CDATA ''>\n" |
| "]>\n" |
| "<doc/>"; |
| |
| expect_failure(text, |
| XML_ERROR_UNDECLARING_PREFIX, |
| "Didn't report attr default setting NS w/ prefix to ''."); |
| } |
| END_TEST |
| |
| /* Regression test #4 for SF bug #673791. */ |
| START_TEST(test_ns_prefix_with_empty_uri_4) |
| { |
| char *text = |
| "<!DOCTYPE doc [\n" |
| " <!ELEMENT prefix:doc EMPTY>\n" |
| " <!ATTLIST prefix:doc\n" |
| " xmlns:prefix CDATA 'http://xml.libexpat.org/'>\n" |
| "]>\n" |
| "<prefix:doc/>"; |
| /* Packaged info expected by the end element handler; |
| the weird structuring lets us re-use the triplet_end_checker() |
| function also used for another test. */ |
| char *elemstr[] = { |
| "http://xml.libexpat.org/ doc prefix" |
| }; |
| XML_SetReturnNSTriplet(parser, XML_TRUE); |
| XML_SetUserData(parser, elemstr); |
| XML_SetEndElementHandler(parser, triplet_end_checker); |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| START_TEST(test_ns_default_with_empty_uri) |
| { |
| char *text = |
| "<doc xmlns='http://xml.libexpat.org/'>\n" |
| " <e xmlns=''/>\n" |
| "</doc>"; |
| if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) |
| xml_failure(parser); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #692964: two prefixes for one namespace. */ |
| START_TEST(test_ns_duplicate_attrs_diff_prefixes) |
| { |
| char *text = |
| "<doc xmlns:a='http://xml.libexpat.org/a'\n" |
| " xmlns:b='http://xml.libexpat.org/a'\n" |
| " a:a='v' b:a='v' />"; |
| expect_failure(text, |
| XML_ERROR_DUPLICATE_ATTRIBUTE, |
| "did not report multiple attributes with same URI+name"); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #695401: unbound prefix. */ |
| START_TEST(test_ns_unbound_prefix_on_attribute) |
| { |
| char *text = "<doc a:attr=''/>"; |
| expect_failure(text, |
| XML_ERROR_UNBOUND_PREFIX, |
| "did not report unbound prefix on attribute"); |
| } |
| END_TEST |
| |
| /* Regression test for SF bug #695401: unbound prefix. */ |
| START_TEST(test_ns_unbound_prefix_on_element) |
| { |
| char *text = "<a:doc/>"; |
| expect_failure(text, |
| XML_ERROR_UNBOUND_PREFIX, |
| "did not report unbound prefix on element"); |
| } |
| END_TEST |
| |
| static Suite * |
| make_suite(void) |
| { |
| Suite *s = suite_create("basic"); |
| TCase *tc_basic = tcase_create("basic tests"); |
| TCase *tc_namespace = tcase_create("XML namespaces"); |
| |
| suite_add_tcase(s, tc_basic); |
| tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown); |
| tcase_add_test(tc_basic, test_nul_byte); |
| tcase_add_test(tc_basic, test_u0000_char); |
| tcase_add_test(tc_basic, test_bom_utf8); |
| tcase_add_test(tc_basic, test_bom_utf16_be); |
| tcase_add_test(tc_basic, test_bom_utf16_le); |
| tcase_add_test(tc_basic, test_illegal_utf8); |
| tcase_add_test(tc_basic, test_utf16); |
| tcase_add_test(tc_basic, test_utf16_le_epilog_newline); |
| tcase_add_test(tc_basic, test_latin1_umlauts); |
| /* Regression test for SF bug #491986. */ |
| tcase_add_test(tc_basic, test_danish_latin1); |
| /* Regression test for SF bug #514281. */ |
| tcase_add_test(tc_basic, test_french_charref_hexidecimal); |
| tcase_add_test(tc_basic, test_french_charref_decimal); |
| tcase_add_test(tc_basic, test_french_latin1); |
| tcase_add_test(tc_basic, test_french_utf8); |
| tcase_add_test(tc_basic, test_utf8_false_rejection); |
| tcase_add_test(tc_basic, test_line_number_after_parse); |
| tcase_add_test(tc_basic, test_column_number_after_parse); |
| tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers); |
| tcase_add_test(tc_basic, test_line_number_after_error); |
| tcase_add_test(tc_basic, test_column_number_after_error); |
| tcase_add_test(tc_basic, test_really_long_lines); |
| tcase_add_test(tc_basic, test_end_element_events); |
| tcase_add_test(tc_basic, test_attr_whitespace_normalization); |
| tcase_add_test(tc_basic, test_xmldecl_misplaced); |
| tcase_add_test(tc_basic, test_unknown_encoding_internal_entity); |
| tcase_add_test(tc_basic, |
| test_wfc_undeclared_entity_unread_external_subset); |
| tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset); |
| tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone); |
| tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset); |
| tcase_add_test(tc_basic, |
| test_wfc_undeclared_entity_with_external_subset_standalone); |
| tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); |
| tcase_add_test(tc_basic, test_ext_entity_set_encoding); |
| tcase_add_test(tc_basic, test_dtd_default_handling); |
| tcase_add_test(tc_basic, test_empty_ns_without_namespaces); |
| tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces); |
| tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls); |
| tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls); |
| |
| suite_add_tcase(s, tc_namespace); |
| tcase_add_checked_fixture(tc_namespace, |
| namespace_setup, namespace_teardown); |
| tcase_add_test(tc_namespace, test_return_ns_triplet); |
| tcase_add_test(tc_namespace, test_ns_tagname_overwrite); |
| tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet); |
| tcase_add_test(tc_namespace, test_start_ns_clears_start_element); |
| tcase_add_test(tc_namespace, test_default_ns_from_ext_subset_and_ext_ge); |
| tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1); |
| tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2); |
| tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3); |
| tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4); |
| tcase_add_test(tc_namespace, test_ns_default_with_empty_uri); |
| tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes); |
| tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute); |
| tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element); |
| |
| return s; |
| } |
| |
| |
| int |
| main(int argc, char *argv[]) |
| { |
| int i, nf; |
| int verbosity = CK_NORMAL; |
| Suite *s = make_suite(); |
| SRunner *sr = srunner_create(s); |
| |
| /* run the tests for internal helper functions */ |
| testhelper_is_whitespace_normalized(); |
| |
| for (i = 1; i < argc; ++i) { |
| char *opt = argv[i]; |
| if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0) |
| verbosity = CK_VERBOSE; |
| else if (strcmp(opt, "-q") == 0 || strcmp(opt, "--quiet") == 0) |
| verbosity = CK_SILENT; |
| else { |
| fprintf(stderr, "runtests: unknown option '%s'\n", opt); |
| return 2; |
| } |
| } |
| if (verbosity != CK_SILENT) |
| printf("Expat version: %s\n", XML_ExpatVersion()); |
| srunner_run_all(sr, verbosity); |
| nf = srunner_ntests_failed(sr); |
| srunner_free(sr); |
| |
| return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; |
| } |