]>
Commit | Line | Data |
---|---|---|
1 | /* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd | |
2 | See the file COPYING for copying permission. | |
3 | ||
4 | runtest.c : run the Expat test suite | |
5 | */ | |
6 | ||
7 | #ifdef HAVE_EXPAT_CONFIG_H | |
8 | #include <expat_config.h> | |
9 | #endif | |
10 | ||
11 | #include <assert.h> | |
12 | #include <stdlib.h> | |
13 | #include <stdio.h> | |
14 | #include <string.h> | |
15 | #include <stdint.h> | |
16 | ||
17 | #include "expat.h" | |
18 | #include "chardata.h" | |
19 | #include "minicheck.h" | |
20 | ||
21 | #if defined(__amigaos__) && defined(__USE_INLINE__) | |
22 | #include <proto/expat.h> | |
23 | #endif | |
24 | ||
25 | #ifdef XML_LARGE_SIZE | |
26 | #define XML_FMT_INT_MOD "ll" | |
27 | #else | |
28 | #define XML_FMT_INT_MOD "l" | |
29 | #endif | |
30 | ||
31 | static XML_Parser parser; | |
32 | ||
33 | ||
34 | static void | |
35 | basic_setup(void) | |
36 | { | |
37 | parser = XML_ParserCreate(NULL); | |
38 | if (parser == NULL) | |
39 | fail("Parser not created."); | |
40 | } | |
41 | ||
42 | static void | |
43 | basic_teardown(void) | |
44 | { | |
45 | if (parser != NULL) | |
46 | XML_ParserFree(parser); | |
47 | } | |
48 | ||
49 | /* Generate a failure using the parser state to create an error message; | |
50 | this should be used when the parser reports an error we weren't | |
51 | expecting. | |
52 | */ | |
53 | static void | |
54 | _xml_failure(XML_Parser parser, const char *file, int line) | |
55 | { | |
56 | char buffer[1024]; | |
57 | enum XML_Error err = XML_GetErrorCode(parser); | |
58 | sprintf(buffer, | |
59 | " %d: %s (line %" XML_FMT_INT_MOD "u, offset %"\ | |
60 | XML_FMT_INT_MOD "u)\n reported from %s, line %d\n", | |
61 | err, | |
62 | XML_ErrorString(err), | |
63 | XML_GetCurrentLineNumber(parser), | |
64 | XML_GetCurrentColumnNumber(parser), | |
65 | file, line); | |
66 | _fail_unless(0, file, line, buffer); | |
67 | } | |
68 | ||
69 | #define xml_failure(parser) _xml_failure((parser), __FILE__, __LINE__) | |
70 | ||
71 | static void | |
72 | _expect_failure(char *text, enum XML_Error errorCode, char *errorMessage, | |
73 | char *file, int lineno) | |
74 | { | |
75 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) | |
76 | /* Hackish use of _fail_unless() macro, but let's us report | |
77 | the right filename and line number. */ | |
78 | _fail_unless(0, file, lineno, errorMessage); | |
79 | if (XML_GetErrorCode(parser) != errorCode) | |
80 | _xml_failure(parser, file, lineno); | |
81 | } | |
82 | ||
83 | #define expect_failure(text, errorCode, errorMessage) \ | |
84 | _expect_failure((text), (errorCode), (errorMessage), \ | |
85 | __FILE__, __LINE__) | |
86 | ||
87 | /* Dummy handlers for when we need to set a handler to tickle a bug, | |
88 | but it doesn't need to do anything. | |
89 | */ | |
90 | ||
91 | static void XMLCALL | |
92 | dummy_start_doctype_handler(void *userData, | |
93 | const XML_Char *doctypeName, | |
94 | const XML_Char *sysid, | |
95 | const XML_Char *pubid, | |
96 | int has_internal_subset) | |
97 | {} | |
98 | ||
99 | static void XMLCALL | |
100 | dummy_end_doctype_handler(void *userData) | |
101 | {} | |
102 | ||
103 | static void XMLCALL | |
104 | dummy_entity_decl_handler(void *userData, | |
105 | const XML_Char *entityName, | |
106 | int is_parameter_entity, | |
107 | const XML_Char *value, | |
108 | int value_length, | |
109 | const XML_Char *base, | |
110 | const XML_Char *systemId, | |
111 | const XML_Char *publicId, | |
112 | const XML_Char *notationName) | |
113 | {} | |
114 | ||
115 | static void XMLCALL | |
116 | dummy_notation_decl_handler(void *userData, | |
117 | const XML_Char *notationName, | |
118 | const XML_Char *base, | |
119 | const XML_Char *systemId, | |
120 | const XML_Char *publicId) | |
121 | {} | |
122 | ||
123 | static void XMLCALL | |
124 | dummy_element_decl_handler(void *userData, | |
125 | const XML_Char *name, | |
126 | XML_Content *model) | |
127 | {} | |
128 | ||
129 | static void XMLCALL | |
130 | dummy_attlist_decl_handler(void *userData, | |
131 | const XML_Char *elname, | |
132 | const XML_Char *attname, | |
133 | const XML_Char *att_type, | |
134 | const XML_Char *dflt, | |
135 | int isrequired) | |
136 | {} | |
137 | ||
138 | static void XMLCALL | |
139 | dummy_comment_handler(void *userData, const XML_Char *data) | |
140 | {} | |
141 | ||
142 | static void XMLCALL | |
143 | dummy_pi_handler(void *userData, const XML_Char *target, const XML_Char *data) | |
144 | {} | |
145 | ||
146 | static void XMLCALL | |
147 | dummy_start_element(void *userData, | |
148 | const XML_Char *name, const XML_Char **atts) | |
149 | {} | |
150 | ||
151 | ||
152 | /* | |
153 | * Character & encoding tests. | |
154 | */ | |
155 | ||
156 | START_TEST(test_nul_byte) | |
157 | { | |
158 | char text[] = "<doc>\0</doc>"; | |
159 | ||
160 | /* test that a NUL byte (in US-ASCII data) is an error */ | |
161 | if (XML_Parse(parser, text, sizeof(text) - 1, XML_TRUE) == XML_STATUS_OK) | |
162 | fail("Parser did not report error on NUL-byte."); | |
163 | if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) | |
164 | xml_failure(parser); | |
165 | } | |
166 | END_TEST | |
167 | ||
168 | ||
169 | START_TEST(test_u0000_char) | |
170 | { | |
171 | /* test that a NUL byte (in US-ASCII data) is an error */ | |
172 | expect_failure("<doc>�</doc>", | |
173 | XML_ERROR_BAD_CHAR_REF, | |
174 | "Parser did not report error on NUL-byte."); | |
175 | } | |
176 | END_TEST | |
177 | ||
178 | START_TEST(test_bom_utf8) | |
179 | { | |
180 | /* This test is really just making sure we don't core on a UTF-8 BOM. */ | |
181 | char *text = "\357\273\277<e/>"; | |
182 | ||
183 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
184 | xml_failure(parser); | |
185 | } | |
186 | END_TEST | |
187 | ||
188 | START_TEST(test_bom_utf16_be) | |
189 | { | |
190 | char text[] = "\376\377\0<\0e\0/\0>"; | |
191 | ||
192 | if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) | |
193 | xml_failure(parser); | |
194 | } | |
195 | END_TEST | |
196 | ||
197 | START_TEST(test_bom_utf16_le) | |
198 | { | |
199 | char text[] = "\377\376<\0e\0/\0>\0"; | |
200 | ||
201 | if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) | |
202 | xml_failure(parser); | |
203 | } | |
204 | END_TEST | |
205 | ||
206 | static void XMLCALL | |
207 | accumulate_characters(void *userData, const XML_Char *s, int len) | |
208 | { | |
209 | CharData_AppendXMLChars((CharData *)userData, s, len); | |
210 | } | |
211 | ||
212 | static void XMLCALL | |
213 | accumulate_attribute(void *userData, const XML_Char *name, | |
214 | const XML_Char **atts) | |
215 | { | |
216 | CharData *storage = (CharData *)userData; | |
217 | if (storage->count < 0 && atts != NULL && atts[0] != NULL) { | |
218 | /* "accumulate" the value of the first attribute we see */ | |
219 | CharData_AppendXMLChars(storage, atts[1], -1); | |
220 | } | |
221 | } | |
222 | ||
223 | ||
224 | static void | |
225 | _run_character_check(XML_Char *text, XML_Char *expected, | |
226 | const char *file, int line) | |
227 | { | |
228 | CharData storage; | |
229 | ||
230 | CharData_Init(&storage); | |
231 | XML_SetUserData(parser, &storage); | |
232 | XML_SetCharacterDataHandler(parser, accumulate_characters); | |
233 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
234 | _xml_failure(parser, file, line); | |
235 | CharData_CheckXMLChars(&storage, expected); | |
236 | } | |
237 | ||
238 | #define run_character_check(text, expected) \ | |
239 | _run_character_check(text, expected, __FILE__, __LINE__) | |
240 | ||
241 | static void | |
242 | _run_attribute_check(XML_Char *text, XML_Char *expected, | |
243 | const char *file, int line) | |
244 | { | |
245 | CharData storage; | |
246 | ||
247 | CharData_Init(&storage); | |
248 | XML_SetUserData(parser, &storage); | |
249 | XML_SetStartElementHandler(parser, accumulate_attribute); | |
250 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
251 | _xml_failure(parser, file, line); | |
252 | CharData_CheckXMLChars(&storage, expected); | |
253 | } | |
254 | ||
255 | #define run_attribute_check(text, expected) \ | |
256 | _run_attribute_check(text, expected, __FILE__, __LINE__) | |
257 | ||
258 | /* Regression test for SF bug #491986. */ | |
259 | START_TEST(test_danish_latin1) | |
260 | { | |
261 | char *text = | |
262 | "<?xml version='1.0' encoding='iso-8859-1'?>\n" | |
263 | "<e>J\xF8rgen \xE6\xF8\xE5\xC6\xD8\xC5</e>"; | |
264 | run_character_check(text, | |
265 | "J\xC3\xB8rgen \xC3\xA6\xC3\xB8\xC3\xA5\xC3\x86\xC3\x98\xC3\x85"); | |
266 | } | |
267 | END_TEST | |
268 | ||
269 | ||
270 | /* Regression test for SF bug #514281. */ | |
271 | START_TEST(test_french_charref_hexidecimal) | |
272 | { | |
273 | char *text = | |
274 | "<?xml version='1.0' encoding='iso-8859-1'?>\n" | |
275 | "<doc>éèàçêÈ</doc>"; | |
276 | run_character_check(text, | |
277 | "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); | |
278 | } | |
279 | END_TEST | |
280 | ||
281 | START_TEST(test_french_charref_decimal) | |
282 | { | |
283 | char *text = | |
284 | "<?xml version='1.0' encoding='iso-8859-1'?>\n" | |
285 | "<doc>éèàçêÈ</doc>"; | |
286 | run_character_check(text, | |
287 | "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); | |
288 | } | |
289 | END_TEST | |
290 | ||
291 | START_TEST(test_french_latin1) | |
292 | { | |
293 | char *text = | |
294 | "<?xml version='1.0' encoding='iso-8859-1'?>\n" | |
295 | "<doc>\xE9\xE8\xE0\xE7\xEa\xC8</doc>"; | |
296 | run_character_check(text, | |
297 | "\xC3\xA9\xC3\xA8\xC3\xA0\xC3\xA7\xC3\xAA\xC3\x88"); | |
298 | } | |
299 | END_TEST | |
300 | ||
301 | START_TEST(test_french_utf8) | |
302 | { | |
303 | char *text = | |
304 | "<?xml version='1.0' encoding='utf-8'?>\n" | |
305 | "<doc>\xC3\xA9</doc>"; | |
306 | run_character_check(text, "\xC3\xA9"); | |
307 | } | |
308 | END_TEST | |
309 | ||
310 | /* Regression test for SF bug #600479. | |
311 | XXX There should be a test that exercises all legal XML Unicode | |
312 | characters as PCDATA and attribute value content, and XML Name | |
313 | characters as part of element and attribute names. | |
314 | */ | |
315 | START_TEST(test_utf8_false_rejection) | |
316 | { | |
317 | char *text = "<doc>\xEF\xBA\xBF</doc>"; | |
318 | run_character_check(text, "\xEF\xBA\xBF"); | |
319 | } | |
320 | END_TEST | |
321 | ||
322 | /* Regression test for SF bug #477667. | |
323 | This test assures that any 8-bit character followed by a 7-bit | |
324 | character will not be mistakenly interpreted as a valid UTF-8 | |
325 | sequence. | |
326 | */ | |
327 | START_TEST(test_illegal_utf8) | |
328 | { | |
329 | char text[100]; | |
330 | int i; | |
331 | ||
332 | for (i = 128; i <= 255; ++i) { | |
333 | sprintf(text, "<e>%ccd</e>", i); | |
334 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_OK) { | |
335 | sprintf(text, | |
336 | "expected token error for '%c' (ordinal %d) in UTF-8 text", | |
337 | i, i); | |
338 | fail(text); | |
339 | } | |
340 | else if (XML_GetErrorCode(parser) != XML_ERROR_INVALID_TOKEN) | |
341 | xml_failure(parser); | |
342 | /* Reset the parser since we use the same parser repeatedly. */ | |
343 | XML_ParserReset(parser, NULL); | |
344 | } | |
345 | } | |
346 | END_TEST | |
347 | ||
348 | START_TEST(test_utf16) | |
349 | { | |
350 | /* <?xml version="1.0" encoding="UTF-16"?> | |
351 | <doc a='123'>some text</doc> | |
352 | */ | |
353 | char text[] = | |
354 | "\000<\000?\000x\000m\000\154\000 \000v\000e\000r\000s\000i\000o" | |
355 | "\000n\000=\000'\0001\000.\000\060\000'\000 \000e\000n\000c\000o" | |
356 | "\000d\000i\000n\000g\000=\000'\000U\000T\000F\000-\0001\000\066" | |
357 | "\000'\000?\000>\000\n" | |
358 | "\000<\000d\000o\000c\000 \000a\000=\000'\0001\0002\0003\000'" | |
359 | "\000>\000s\000o\000m\000e\000 \000t\000e\000x\000t\000<\000/" | |
360 | "\000d\000o\000c\000>"; | |
361 | if (XML_Parse(parser, text, sizeof(text)-1, XML_TRUE) == XML_STATUS_ERROR) | |
362 | xml_failure(parser); | |
363 | } | |
364 | END_TEST | |
365 | ||
366 | START_TEST(test_utf16_le_epilog_newline) | |
367 | { | |
368 | unsigned int first_chunk_bytes = 17; | |
369 | char text[] = | |
370 | "\xFF\xFE" /* BOM */ | |
371 | "<\000e\000/\000>\000" /* document element */ | |
372 | "\r\000\n\000\r\000\n\000"; /* epilog */ | |
373 | ||
374 | if (first_chunk_bytes >= sizeof(text) - 1) | |
375 | fail("bad value of first_chunk_bytes"); | |
376 | if ( XML_Parse(parser, text, first_chunk_bytes, XML_FALSE) | |
377 | == XML_STATUS_ERROR) | |
378 | xml_failure(parser); | |
379 | else { | |
380 | enum XML_Status rc; | |
381 | rc = XML_Parse(parser, text + first_chunk_bytes, | |
382 | sizeof(text) - first_chunk_bytes - 1, XML_TRUE); | |
383 | if (rc == XML_STATUS_ERROR) | |
384 | xml_failure(parser); | |
385 | } | |
386 | } | |
387 | END_TEST | |
388 | ||
389 | /* Regression test for SF bug #481609, #774028. */ | |
390 | START_TEST(test_latin1_umlauts) | |
391 | { | |
392 | char *text = | |
393 | "<?xml version='1.0' encoding='iso-8859-1'?>\n" | |
394 | "<e a='\xE4 \xF6 \xFC ä ö ü ä ö ü >'\n" | |
395 | " >\xE4 \xF6 \xFC ä ö ü ä ö ü ></e>"; | |
396 | char *utf8 = | |
397 | "\xC3\xA4 \xC3\xB6 \xC3\xBC " | |
398 | "\xC3\xA4 \xC3\xB6 \xC3\xBC " | |
399 | "\xC3\xA4 \xC3\xB6 \xC3\xBC >"; | |
400 | run_character_check(text, utf8); | |
401 | XML_ParserReset(parser, NULL); | |
402 | run_attribute_check(text, utf8); | |
403 | } | |
404 | END_TEST | |
405 | ||
406 | /* Regression test #1 for SF bug #653180. */ | |
407 | START_TEST(test_line_number_after_parse) | |
408 | { | |
409 | char *text = | |
410 | "<tag>\n" | |
411 | "\n" | |
412 | "\n</tag>"; | |
413 | XML_Size lineno; | |
414 | ||
415 | if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) | |
416 | xml_failure(parser); | |
417 | lineno = XML_GetCurrentLineNumber(parser); | |
418 | if (lineno != 4) { | |
419 | char buffer[100]; | |
420 | sprintf(buffer, | |
421 | "expected 4 lines, saw %" XML_FMT_INT_MOD "u", lineno); | |
422 | fail(buffer); | |
423 | } | |
424 | } | |
425 | END_TEST | |
426 | ||
427 | /* Regression test #2 for SF bug #653180. */ | |
428 | START_TEST(test_column_number_after_parse) | |
429 | { | |
430 | char *text = "<tag></tag>"; | |
431 | XML_Size colno; | |
432 | ||
433 | if (XML_Parse(parser, text, strlen(text), XML_FALSE) == XML_STATUS_ERROR) | |
434 | xml_failure(parser); | |
435 | colno = XML_GetCurrentColumnNumber(parser); | |
436 | if (colno != 11) { | |
437 | char buffer[100]; | |
438 | sprintf(buffer, | |
439 | "expected 11 columns, saw %" XML_FMT_INT_MOD "u", colno); | |
440 | fail(buffer); | |
441 | } | |
442 | } | |
443 | END_TEST | |
444 | ||
445 | static void XMLCALL | |
446 | start_element_event_handler2(void *userData, const XML_Char *name, | |
447 | const XML_Char **attr) | |
448 | { | |
449 | CharData *storage = (CharData *) userData; | |
450 | char buffer[100]; | |
451 | ||
452 | sprintf(buffer, | |
453 | "<%s> at col:%" XML_FMT_INT_MOD "u line:%"\ | |
454 | XML_FMT_INT_MOD "u\n", name, | |
455 | XML_GetCurrentColumnNumber(parser), | |
456 | XML_GetCurrentLineNumber(parser)); | |
457 | CharData_AppendString(storage, buffer); | |
458 | } | |
459 | ||
460 | static void XMLCALL | |
461 | end_element_event_handler2(void *userData, const XML_Char *name) | |
462 | { | |
463 | CharData *storage = (CharData *) userData; | |
464 | char buffer[100]; | |
465 | ||
466 | sprintf(buffer, | |
467 | "</%s> at col:%" XML_FMT_INT_MOD "u line:%"\ | |
468 | XML_FMT_INT_MOD "u\n", name, | |
469 | XML_GetCurrentColumnNumber(parser), | |
470 | XML_GetCurrentLineNumber(parser)); | |
471 | CharData_AppendString(storage, buffer); | |
472 | } | |
473 | ||
474 | /* Regression test #3 for SF bug #653180. */ | |
475 | START_TEST(test_line_and_column_numbers_inside_handlers) | |
476 | { | |
477 | char *text = | |
478 | "<a>\n" /* Unix end-of-line */ | |
479 | " <b>\r\n" /* Windows end-of-line */ | |
480 | " <c/>\r" /* Mac OS end-of-line */ | |
481 | " </b>\n" | |
482 | " <d>\n" | |
483 | " <f/>\n" | |
484 | " </d>\n" | |
485 | "</a>"; | |
486 | char *expected = | |
487 | "<a> at col:0 line:1\n" | |
488 | "<b> at col:2 line:2\n" | |
489 | "<c> at col:4 line:3\n" | |
490 | "</c> at col:8 line:3\n" | |
491 | "</b> at col:2 line:4\n" | |
492 | "<d> at col:2 line:5\n" | |
493 | "<f> at col:4 line:6\n" | |
494 | "</f> at col:8 line:6\n" | |
495 | "</d> at col:2 line:7\n" | |
496 | "</a> at col:0 line:8\n"; | |
497 | CharData storage; | |
498 | ||
499 | CharData_Init(&storage); | |
500 | XML_SetUserData(parser, &storage); | |
501 | XML_SetStartElementHandler(parser, start_element_event_handler2); | |
502 | XML_SetEndElementHandler(parser, end_element_event_handler2); | |
503 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
504 | xml_failure(parser); | |
505 | ||
506 | CharData_CheckString(&storage, expected); | |
507 | } | |
508 | END_TEST | |
509 | ||
510 | /* Regression test #4 for SF bug #653180. */ | |
511 | START_TEST(test_line_number_after_error) | |
512 | { | |
513 | char *text = | |
514 | "<a>\n" | |
515 | " <b>\n" | |
516 | " </a>"; /* missing </b> */ | |
517 | XML_Size lineno; | |
518 | if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) | |
519 | fail("Expected a parse error"); | |
520 | ||
521 | lineno = XML_GetCurrentLineNumber(parser); | |
522 | if (lineno != 3) { | |
523 | char buffer[100]; | |
524 | sprintf(buffer, "expected 3 lines, saw %" XML_FMT_INT_MOD "u", lineno); | |
525 | fail(buffer); | |
526 | } | |
527 | } | |
528 | END_TEST | |
529 | ||
530 | /* Regression test #5 for SF bug #653180. */ | |
531 | START_TEST(test_column_number_after_error) | |
532 | { | |
533 | char *text = | |
534 | "<a>\n" | |
535 | " <b>\n" | |
536 | " </a>"; /* missing </b> */ | |
537 | XML_Size colno; | |
538 | if (XML_Parse(parser, text, strlen(text), XML_FALSE) != XML_STATUS_ERROR) | |
539 | fail("Expected a parse error"); | |
540 | ||
541 | colno = XML_GetCurrentColumnNumber(parser); | |
542 | if (colno != 4) { | |
543 | char buffer[100]; | |
544 | sprintf(buffer, | |
545 | "expected 4 columns, saw %" XML_FMT_INT_MOD "u", colno); | |
546 | fail(buffer); | |
547 | } | |
548 | } | |
549 | END_TEST | |
550 | ||
551 | /* Regression test for SF bug #478332. */ | |
552 | START_TEST(test_really_long_lines) | |
553 | { | |
554 | /* This parses an input line longer than INIT_DATA_BUF_SIZE | |
555 | characters long (defined to be 1024 in xmlparse.c). We take a | |
556 | really cheesy approach to building the input buffer, because | |
557 | this avoids writing bugs in buffer-filling code. | |
558 | */ | |
559 | char *text = | |
560 | "<e>" | |
561 | /* 64 chars */ | |
562 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
563 | /* until we have at least 1024 characters on the line: */ | |
564 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
565 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
566 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
567 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
568 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
569 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
570 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
571 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
572 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
573 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
574 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
575 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
576 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
577 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
578 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
579 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-+" | |
580 | "</e>"; | |
581 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
582 | xml_failure(parser); | |
583 | } | |
584 | END_TEST | |
585 | ||
586 | ||
587 | /* | |
588 | * Element event tests. | |
589 | */ | |
590 | ||
591 | static void XMLCALL | |
592 | end_element_event_handler(void *userData, const XML_Char *name) | |
593 | { | |
594 | CharData *storage = (CharData *) userData; | |
595 | CharData_AppendString(storage, "/"); | |
596 | CharData_AppendXMLChars(storage, name, -1); | |
597 | } | |
598 | ||
599 | START_TEST(test_end_element_events) | |
600 | { | |
601 | char *text = "<a><b><c/></b><d><f/></d></a>"; | |
602 | char *expected = "/c/b/f/d/a"; | |
603 | CharData storage; | |
604 | ||
605 | CharData_Init(&storage); | |
606 | XML_SetUserData(parser, &storage); | |
607 | XML_SetEndElementHandler(parser, end_element_event_handler); | |
608 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
609 | xml_failure(parser); | |
610 | CharData_CheckString(&storage, expected); | |
611 | } | |
612 | END_TEST | |
613 | ||
614 | ||
615 | /* | |
616 | * Attribute tests. | |
617 | */ | |
618 | ||
619 | /* Helpers used by the following test; this checks any "attr" and "refs" | |
620 | attributes to make sure whitespace has been normalized. | |
621 | ||
622 | Return true if whitespace has been normalized in a string, using | |
623 | the rules for attribute value normalization. The 'is_cdata' flag | |
624 | is needed since CDATA attributes don't need to have multiple | |
625 | whitespace characters collapsed to a single space, while other | |
626 | attribute data types do. (Section 3.3.3 of the recommendation.) | |
627 | */ | |
628 | static int | |
629 | is_whitespace_normalized(const XML_Char *s, int is_cdata) | |
630 | { | |
631 | int blanks = 0; | |
632 | int at_start = 1; | |
633 | while (*s) { | |
634 | if (*s == ' ') | |
635 | ++blanks; | |
636 | else if (*s == '\t' || *s == '\n' || *s == '\r') | |
637 | return 0; | |
638 | else { | |
639 | if (at_start) { | |
640 | at_start = 0; | |
641 | if (blanks && !is_cdata) | |
642 | /* illegal leading blanks */ | |
643 | return 0; | |
644 | } | |
645 | else if (blanks > 1 && !is_cdata) | |
646 | return 0; | |
647 | blanks = 0; | |
648 | } | |
649 | ++s; | |
650 | } | |
651 | if (blanks && !is_cdata) | |
652 | return 0; | |
653 | return 1; | |
654 | } | |
655 | ||
656 | /* Check the attribute whitespace checker: */ | |
657 | static void | |
658 | testhelper_is_whitespace_normalized(void) | |
659 | { | |
660 | assert(is_whitespace_normalized("abc", 0)); | |
661 | assert(is_whitespace_normalized("abc", 1)); | |
662 | assert(is_whitespace_normalized("abc def ghi", 0)); | |
663 | assert(is_whitespace_normalized("abc def ghi", 1)); | |
664 | assert(!is_whitespace_normalized(" abc def ghi", 0)); | |
665 | assert(is_whitespace_normalized(" abc def ghi", 1)); | |
666 | assert(!is_whitespace_normalized("abc def ghi", 0)); | |
667 | assert(is_whitespace_normalized("abc def ghi", 1)); | |
668 | assert(!is_whitespace_normalized("abc def ghi ", 0)); | |
669 | assert(is_whitespace_normalized("abc def ghi ", 1)); | |
670 | assert(!is_whitespace_normalized(" ", 0)); | |
671 | assert(is_whitespace_normalized(" ", 1)); | |
672 | assert(!is_whitespace_normalized("\t", 0)); | |
673 | assert(!is_whitespace_normalized("\t", 1)); | |
674 | assert(!is_whitespace_normalized("\n", 0)); | |
675 | assert(!is_whitespace_normalized("\n", 1)); | |
676 | assert(!is_whitespace_normalized("\r", 0)); | |
677 | assert(!is_whitespace_normalized("\r", 1)); | |
678 | assert(!is_whitespace_normalized("abc\t def", 1)); | |
679 | } | |
680 | ||
681 | static void XMLCALL | |
682 | check_attr_contains_normalized_whitespace(void *userData, | |
683 | const XML_Char *name, | |
684 | const XML_Char **atts) | |
685 | { | |
686 | int i; | |
687 | for (i = 0; atts[i] != NULL; i += 2) { | |
688 | const XML_Char *attrname = atts[i]; | |
689 | const XML_Char *value = atts[i + 1]; | |
690 | if (strcmp("attr", attrname) == 0 | |
691 | || strcmp("ents", attrname) == 0 | |
692 | || strcmp("refs", attrname) == 0) { | |
693 | if (!is_whitespace_normalized(value, 0)) { | |
694 | char buffer[256]; | |
695 | sprintf(buffer, "attribute value not normalized: %s='%s'", | |
696 | attrname, value); | |
697 | fail(buffer); | |
698 | } | |
699 | } | |
700 | } | |
701 | } | |
702 | ||
703 | START_TEST(test_attr_whitespace_normalization) | |
704 | { | |
705 | char *text = | |
706 | "<!DOCTYPE doc [\n" | |
707 | " <!ATTLIST doc\n" | |
708 | " attr NMTOKENS #REQUIRED\n" | |
709 | " ents ENTITIES #REQUIRED\n" | |
710 | " refs IDREFS #REQUIRED>\n" | |
711 | "]>\n" | |
712 | "<doc attr=' a b c\t\td\te\t' refs=' id-1 \t id-2\t\t' \n" | |
713 | " ents=' ent-1 \t\r\n" | |
714 | " ent-2 ' >\n" | |
715 | " <e id='id-1'/>\n" | |
716 | " <e id='id-2'/>\n" | |
717 | "</doc>"; | |
718 | ||
719 | XML_SetStartElementHandler(parser, | |
720 | check_attr_contains_normalized_whitespace); | |
721 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
722 | xml_failure(parser); | |
723 | } | |
724 | END_TEST | |
725 | ||
726 | ||
727 | /* | |
728 | * XML declaration tests. | |
729 | */ | |
730 | ||
731 | START_TEST(test_xmldecl_misplaced) | |
732 | { | |
733 | expect_failure("\n" | |
734 | "<?xml version='1.0'?>\n" | |
735 | "<a/>", | |
736 | XML_ERROR_MISPLACED_XML_PI, | |
737 | "failed to report misplaced XML declaration"); | |
738 | } | |
739 | END_TEST | |
740 | ||
741 | /* Regression test for SF bug #584832. */ | |
742 | static int XMLCALL | |
743 | UnknownEncodingHandler(void *data,const XML_Char *encoding,XML_Encoding *info) | |
744 | { | |
745 | if (strcmp(encoding,"unsupported-encoding") == 0) { | |
746 | int i; | |
747 | for (i = 0; i < 256; ++i) | |
748 | info->map[i] = i; | |
749 | info->data = NULL; | |
750 | info->convert = NULL; | |
751 | info->release = NULL; | |
752 | return XML_STATUS_OK; | |
753 | } | |
754 | return XML_STATUS_ERROR; | |
755 | } | |
756 | ||
757 | START_TEST(test_unknown_encoding_internal_entity) | |
758 | { | |
759 | char *text = | |
760 | "<?xml version='1.0' encoding='unsupported-encoding'?>\n" | |
761 | "<!DOCTYPE test [<!ENTITY foo 'bar'>]>\n" | |
762 | "<test a='&foo;'/>"; | |
763 | ||
764 | XML_SetUnknownEncodingHandler(parser, UnknownEncodingHandler, NULL); | |
765 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
766 | xml_failure(parser); | |
767 | } | |
768 | END_TEST | |
769 | ||
770 | /* Regression test for SF bug #620106. */ | |
771 | static int XMLCALL | |
772 | external_entity_loader_set_encoding(XML_Parser parser, | |
773 | const XML_Char *context, | |
774 | const XML_Char *base, | |
775 | const XML_Char *systemId, | |
776 | const XML_Char *publicId) | |
777 | { | |
778 | /* This text says it's an unsupported encoding, but it's really | |
779 | UTF-8, which we tell Expat using XML_SetEncoding(). | |
780 | */ | |
781 | char *text = | |
782 | "<?xml encoding='iso-8859-3'?>" | |
783 | "\xC3\xA9"; | |
784 | XML_Parser extparser; | |
785 | ||
786 | extparser = XML_ExternalEntityParserCreate(parser, context, NULL); | |
787 | if (extparser == NULL) | |
788 | fail("Could not create external entity parser."); | |
789 | if (!XML_SetEncoding(extparser, "utf-8")) | |
790 | fail("XML_SetEncoding() ignored for external entity"); | |
791 | if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) | |
792 | == XML_STATUS_ERROR) { | |
793 | xml_failure(parser); | |
794 | return 0; | |
795 | } | |
796 | return 1; | |
797 | } | |
798 | ||
799 | START_TEST(test_ext_entity_set_encoding) | |
800 | { | |
801 | char *text = | |
802 | "<!DOCTYPE doc [\n" | |
803 | " <!ENTITY en SYSTEM 'http://xml.libexpat.org/dummy.ent'>\n" | |
804 | "]>\n" | |
805 | "<doc>&en;</doc>"; | |
806 | ||
807 | XML_SetExternalEntityRefHandler(parser, | |
808 | external_entity_loader_set_encoding); | |
809 | run_character_check(text, "\xC3\xA9"); | |
810 | } | |
811 | END_TEST | |
812 | ||
813 | /* Test that no error is reported for unknown entities if we don't | |
814 | read an external subset. This was fixed in Expat 1.95.5. | |
815 | */ | |
816 | START_TEST(test_wfc_undeclared_entity_unread_external_subset) { | |
817 | char *text = | |
818 | "<!DOCTYPE doc SYSTEM 'foo'>\n" | |
819 | "<doc>&entity;</doc>"; | |
820 | ||
821 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
822 | xml_failure(parser); | |
823 | } | |
824 | END_TEST | |
825 | ||
826 | /* Test that an error is reported for unknown entities if we don't | |
827 | have an external subset. | |
828 | */ | |
829 | START_TEST(test_wfc_undeclared_entity_no_external_subset) { | |
830 | expect_failure("<doc>&entity;</doc>", | |
831 | XML_ERROR_UNDEFINED_ENTITY, | |
832 | "Parser did not report undefined entity w/out a DTD."); | |
833 | } | |
834 | END_TEST | |
835 | ||
836 | /* Test that an error is reported for unknown entities if we don't | |
837 | read an external subset, but have been declared standalone. | |
838 | */ | |
839 | START_TEST(test_wfc_undeclared_entity_standalone) { | |
840 | char *text = | |
841 | "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n" | |
842 | "<!DOCTYPE doc SYSTEM 'foo'>\n" | |
843 | "<doc>&entity;</doc>"; | |
844 | ||
845 | expect_failure(text, | |
846 | XML_ERROR_UNDEFINED_ENTITY, | |
847 | "Parser did not report undefined entity (standalone)."); | |
848 | } | |
849 | END_TEST | |
850 | ||
851 | static int XMLCALL | |
852 | external_entity_loader(XML_Parser parser, | |
853 | const XML_Char *context, | |
854 | const XML_Char *base, | |
855 | const XML_Char *systemId, | |
856 | const XML_Char *publicId) | |
857 | { | |
858 | char *text = (char *)XML_GetUserData(parser); | |
859 | XML_Parser extparser; | |
860 | ||
861 | extparser = XML_ExternalEntityParserCreate(parser, context, NULL); | |
862 | if (extparser == NULL) | |
863 | fail("Could not create external entity parser."); | |
864 | if ( XML_Parse(extparser, text, strlen(text), XML_TRUE) | |
865 | == XML_STATUS_ERROR) { | |
866 | xml_failure(parser); | |
867 | return XML_STATUS_ERROR; | |
868 | } | |
869 | return XML_STATUS_OK; | |
870 | } | |
871 | ||
872 | /* Test that an error is reported for unknown entities if we have read | |
873 | an external subset, and standalone is true. | |
874 | */ | |
875 | START_TEST(test_wfc_undeclared_entity_with_external_subset_standalone) { | |
876 | char *text = | |
877 | "<?xml version='1.0' encoding='us-ascii' standalone='yes'?>\n" | |
878 | "<!DOCTYPE doc SYSTEM 'foo'>\n" | |
879 | "<doc>&entity;</doc>"; | |
880 | char *foo_text = | |
881 | "<!ELEMENT doc (#PCDATA)*>"; | |
882 | ||
883 | XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); | |
884 | XML_SetUserData(parser, foo_text); | |
885 | XML_SetExternalEntityRefHandler(parser, external_entity_loader); | |
886 | expect_failure(text, | |
887 | XML_ERROR_UNDEFINED_ENTITY, | |
888 | "Parser did not report undefined entity (external DTD)."); | |
889 | } | |
890 | END_TEST | |
891 | ||
892 | /* Test that no error is reported for unknown entities if we have read | |
893 | an external subset, and standalone is false. | |
894 | */ | |
895 | START_TEST(test_wfc_undeclared_entity_with_external_subset) { | |
896 | char *text = | |
897 | "<?xml version='1.0' encoding='us-ascii'?>\n" | |
898 | "<!DOCTYPE doc SYSTEM 'foo'>\n" | |
899 | "<doc>&entity;</doc>"; | |
900 | char *foo_text = | |
901 | "<!ELEMENT doc (#PCDATA)*>"; | |
902 | ||
903 | XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); | |
904 | XML_SetUserData(parser, foo_text); | |
905 | XML_SetExternalEntityRefHandler(parser, external_entity_loader); | |
906 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
907 | xml_failure(parser); | |
908 | } | |
909 | END_TEST | |
910 | ||
911 | START_TEST(test_wfc_no_recursive_entity_refs) | |
912 | { | |
913 | char *text = | |
914 | "<!DOCTYPE doc [\n" | |
915 | " <!ENTITY entity '&entity;'>\n" | |
916 | "]>\n" | |
917 | "<doc>&entity;</doc>"; | |
918 | ||
919 | expect_failure(text, | |
920 | XML_ERROR_RECURSIVE_ENTITY_REF, | |
921 | "Parser did not report recursive entity reference."); | |
922 | } | |
923 | END_TEST | |
924 | ||
925 | /* Regression test for SF bug #483514. */ | |
926 | START_TEST(test_dtd_default_handling) | |
927 | { | |
928 | char *text = | |
929 | "<!DOCTYPE doc [\n" | |
930 | "<!ENTITY e SYSTEM 'http://xml.libexpat.org/e'>\n" | |
931 | "<!NOTATION n SYSTEM 'http://xml.libexpat.org/n'>\n" | |
932 | "<!ELEMENT doc EMPTY>\n" | |
933 | "<!ATTLIST doc a CDATA #IMPLIED>\n" | |
934 | "<?pi in dtd?>\n" | |
935 | "<!--comment in dtd-->\n" | |
936 | "]><doc/>"; | |
937 | ||
938 | XML_SetDefaultHandler(parser, accumulate_characters); | |
939 | XML_SetDoctypeDeclHandler(parser, | |
940 | dummy_start_doctype_handler, | |
941 | dummy_end_doctype_handler); | |
942 | XML_SetEntityDeclHandler(parser, dummy_entity_decl_handler); | |
943 | XML_SetNotationDeclHandler(parser, dummy_notation_decl_handler); | |
944 | XML_SetElementDeclHandler(parser, dummy_element_decl_handler); | |
945 | XML_SetAttlistDeclHandler(parser, dummy_attlist_decl_handler); | |
946 | XML_SetProcessingInstructionHandler(parser, dummy_pi_handler); | |
947 | XML_SetCommentHandler(parser, dummy_comment_handler); | |
948 | run_character_check(text, "\n\n\n\n\n\n\n<doc/>"); | |
949 | } | |
950 | END_TEST | |
951 | ||
952 | /* See related SF bug #673791. | |
953 | When namespace processing is enabled, setting the namespace URI for | |
954 | a prefix is not allowed; this test ensures that it *is* allowed | |
955 | when namespace processing is not enabled. | |
956 | (See Namespaces in XML, section 2.) | |
957 | */ | |
958 | START_TEST(test_empty_ns_without_namespaces) | |
959 | { | |
960 | char *text = | |
961 | "<doc xmlns:prefix='http://www.example.com/'>\n" | |
962 | " <e xmlns:prefix=''/>\n" | |
963 | "</doc>"; | |
964 | ||
965 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
966 | xml_failure(parser); | |
967 | } | |
968 | END_TEST | |
969 | ||
970 | /* Regression test for SF bug #824420. | |
971 | Checks that an xmlns:prefix attribute set in an attribute's default | |
972 | value isn't misinterpreted. | |
973 | */ | |
974 | START_TEST(test_ns_in_attribute_default_without_namespaces) | |
975 | { | |
976 | char *text = | |
977 | "<!DOCTYPE e:element [\n" | |
978 | " <!ATTLIST e:element\n" | |
979 | " xmlns:e CDATA 'http://example.com/'>\n" | |
980 | " ]>\n" | |
981 | "<e:element/>"; | |
982 | ||
983 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
984 | xml_failure(parser); | |
985 | } | |
986 | END_TEST | |
987 | ||
988 | static char *long_character_data_text = | |
989 | "<?xml version='1.0' encoding='iso-8859-1'?><s>" | |
990 | "012345678901234567890123456789012345678901234567890123456789" | |
991 | "012345678901234567890123456789012345678901234567890123456789" | |
992 | "012345678901234567890123456789012345678901234567890123456789" | |
993 | "012345678901234567890123456789012345678901234567890123456789" | |
994 | "012345678901234567890123456789012345678901234567890123456789" | |
995 | "012345678901234567890123456789012345678901234567890123456789" | |
996 | "012345678901234567890123456789012345678901234567890123456789" | |
997 | "012345678901234567890123456789012345678901234567890123456789" | |
998 | "012345678901234567890123456789012345678901234567890123456789" | |
999 | "012345678901234567890123456789012345678901234567890123456789" | |
1000 | "012345678901234567890123456789012345678901234567890123456789" | |
1001 | "012345678901234567890123456789012345678901234567890123456789" | |
1002 | "012345678901234567890123456789012345678901234567890123456789" | |
1003 | "012345678901234567890123456789012345678901234567890123456789" | |
1004 | "012345678901234567890123456789012345678901234567890123456789" | |
1005 | "012345678901234567890123456789012345678901234567890123456789" | |
1006 | "012345678901234567890123456789012345678901234567890123456789" | |
1007 | "012345678901234567890123456789012345678901234567890123456789" | |
1008 | "012345678901234567890123456789012345678901234567890123456789" | |
1009 | "012345678901234567890123456789012345678901234567890123456789" | |
1010 | "</s>"; | |
1011 | ||
1012 | static XML_Bool resumable = XML_FALSE; | |
1013 | ||
1014 | static void | |
1015 | clearing_aborting_character_handler(void *userData, | |
1016 | const XML_Char *s, int len) | |
1017 | { | |
1018 | XML_StopParser(parser, resumable); | |
1019 | XML_SetCharacterDataHandler(parser, NULL); | |
1020 | } | |
1021 | ||
1022 | /* Regression test for SF bug #1515266: missing check of stopped | |
1023 | parser in doContext() 'for' loop. */ | |
1024 | START_TEST(test_stop_parser_between_char_data_calls) | |
1025 | { | |
1026 | /* The sample data must be big enough that there are two calls to | |
1027 | the character data handler from within the inner "for" loop of | |
1028 | the XML_TOK_DATA_CHARS case in doContent(), and the character | |
1029 | handler must stop the parser and clear the character data | |
1030 | handler. | |
1031 | */ | |
1032 | char *text = long_character_data_text; | |
1033 | ||
1034 | XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); | |
1035 | resumable = XML_FALSE; | |
1036 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_ERROR) | |
1037 | xml_failure(parser); | |
1038 | if (XML_GetErrorCode(parser) != XML_ERROR_ABORTED) | |
1039 | xml_failure(parser); | |
1040 | } | |
1041 | END_TEST | |
1042 | ||
1043 | /* Regression test for SF bug #1515266: missing check of stopped | |
1044 | parser in doContext() 'for' loop. */ | |
1045 | START_TEST(test_suspend_parser_between_char_data_calls) | |
1046 | { | |
1047 | /* The sample data must be big enough that there are two calls to | |
1048 | the character data handler from within the inner "for" loop of | |
1049 | the XML_TOK_DATA_CHARS case in doContent(), and the character | |
1050 | handler must stop the parser and clear the character data | |
1051 | handler. | |
1052 | */ | |
1053 | char *text = long_character_data_text; | |
1054 | ||
1055 | XML_SetCharacterDataHandler(parser, clearing_aborting_character_handler); | |
1056 | resumable = XML_TRUE; | |
1057 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) != XML_STATUS_SUSPENDED) | |
1058 | xml_failure(parser); | |
1059 | if (XML_GetErrorCode(parser) != XML_ERROR_NONE) | |
1060 | xml_failure(parser); | |
1061 | } | |
1062 | END_TEST | |
1063 | ||
1064 | ||
1065 | /* | |
1066 | * Namespaces tests. | |
1067 | */ | |
1068 | ||
1069 | static void | |
1070 | namespace_setup(void) | |
1071 | { | |
1072 | parser = XML_ParserCreateNS(NULL, ' '); | |
1073 | if (parser == NULL) | |
1074 | fail("Parser not created."); | |
1075 | } | |
1076 | ||
1077 | static void | |
1078 | namespace_teardown(void) | |
1079 | { | |
1080 | basic_teardown(); | |
1081 | } | |
1082 | ||
1083 | /* Check that an element name and attribute name match the expected values. | |
1084 | The expected values are passed as an array reference of string pointers | |
1085 | provided as the userData argument; the first is the expected | |
1086 | element name, and the second is the expected attribute name. | |
1087 | */ | |
1088 | static void XMLCALL | |
1089 | triplet_start_checker(void *userData, const XML_Char *name, | |
1090 | const XML_Char **atts) | |
1091 | { | |
1092 | char **elemstr = (char **)userData; | |
1093 | char buffer[1024]; | |
1094 | if (strcmp(elemstr[0], name) != 0) { | |
1095 | sprintf(buffer, "unexpected start string: '%s'", name); | |
1096 | fail(buffer); | |
1097 | } | |
1098 | if (strcmp(elemstr[1], atts[0]) != 0) { | |
1099 | sprintf(buffer, "unexpected attribute string: '%s'", atts[0]); | |
1100 | fail(buffer); | |
1101 | } | |
1102 | } | |
1103 | ||
1104 | /* Check that the element name passed to the end-element handler matches | |
1105 | the expected value. The expected value is passed as the first element | |
1106 | in an array of strings passed as the userData argument. | |
1107 | */ | |
1108 | static void XMLCALL | |
1109 | triplet_end_checker(void *userData, const XML_Char *name) | |
1110 | { | |
1111 | char **elemstr = (char **)userData; | |
1112 | if (strcmp(elemstr[0], name) != 0) { | |
1113 | char buffer[1024]; | |
1114 | sprintf(buffer, "unexpected end string: '%s'", name); | |
1115 | fail(buffer); | |
1116 | } | |
1117 | } | |
1118 | ||
1119 | START_TEST(test_return_ns_triplet) | |
1120 | { | |
1121 | char *text = | |
1122 | "<foo:e xmlns:foo='http://expat.sf.net/' bar:a='12'\n" | |
1123 | " xmlns:bar='http://expat.sf.net/'></foo:e>"; | |
1124 | char *elemstr[] = { | |
1125 | "http://expat.sf.net/ e foo", | |
1126 | "http://expat.sf.net/ a bar" | |
1127 | }; | |
1128 | XML_SetReturnNSTriplet(parser, XML_TRUE); | |
1129 | XML_SetUserData(parser, elemstr); | |
1130 | XML_SetElementHandler(parser, triplet_start_checker, triplet_end_checker); | |
1131 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1132 | xml_failure(parser); | |
1133 | } | |
1134 | END_TEST | |
1135 | ||
1136 | static void XMLCALL | |
1137 | overwrite_start_checker(void *userData, const XML_Char *name, | |
1138 | const XML_Char **atts) | |
1139 | { | |
1140 | CharData *storage = (CharData *) userData; | |
1141 | CharData_AppendString(storage, "start "); | |
1142 | CharData_AppendXMLChars(storage, name, -1); | |
1143 | while (*atts != NULL) { | |
1144 | CharData_AppendString(storage, "\nattribute "); | |
1145 | CharData_AppendXMLChars(storage, *atts, -1); | |
1146 | atts += 2; | |
1147 | } | |
1148 | CharData_AppendString(storage, "\n"); | |
1149 | } | |
1150 | ||
1151 | static void XMLCALL | |
1152 | overwrite_end_checker(void *userData, const XML_Char *name) | |
1153 | { | |
1154 | CharData *storage = (CharData *) userData; | |
1155 | CharData_AppendString(storage, "end "); | |
1156 | CharData_AppendXMLChars(storage, name, -1); | |
1157 | CharData_AppendString(storage, "\n"); | |
1158 | } | |
1159 | ||
1160 | static void | |
1161 | run_ns_tagname_overwrite_test(char *text, char *result) | |
1162 | { | |
1163 | CharData storage; | |
1164 | CharData_Init(&storage); | |
1165 | XML_SetUserData(parser, &storage); | |
1166 | XML_SetElementHandler(parser, | |
1167 | overwrite_start_checker, overwrite_end_checker); | |
1168 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1169 | xml_failure(parser); | |
1170 | CharData_CheckString(&storage, result); | |
1171 | } | |
1172 | ||
1173 | /* Regression test for SF bug #566334. */ | |
1174 | START_TEST(test_ns_tagname_overwrite) | |
1175 | { | |
1176 | char *text = | |
1177 | "<n:e xmlns:n='http://xml.libexpat.org/'>\n" | |
1178 | " <n:f n:attr='foo'/>\n" | |
1179 | " <n:g n:attr2='bar'/>\n" | |
1180 | "</n:e>"; | |
1181 | char *result = | |
1182 | "start http://xml.libexpat.org/ e\n" | |
1183 | "start http://xml.libexpat.org/ f\n" | |
1184 | "attribute http://xml.libexpat.org/ attr\n" | |
1185 | "end http://xml.libexpat.org/ f\n" | |
1186 | "start http://xml.libexpat.org/ g\n" | |
1187 | "attribute http://xml.libexpat.org/ attr2\n" | |
1188 | "end http://xml.libexpat.org/ g\n" | |
1189 | "end http://xml.libexpat.org/ e\n"; | |
1190 | run_ns_tagname_overwrite_test(text, result); | |
1191 | } | |
1192 | END_TEST | |
1193 | ||
1194 | /* Regression test for SF bug #566334. */ | |
1195 | START_TEST(test_ns_tagname_overwrite_triplet) | |
1196 | { | |
1197 | char *text = | |
1198 | "<n:e xmlns:n='http://xml.libexpat.org/'>\n" | |
1199 | " <n:f n:attr='foo'/>\n" | |
1200 | " <n:g n:attr2='bar'/>\n" | |
1201 | "</n:e>"; | |
1202 | char *result = | |
1203 | "start http://xml.libexpat.org/ e n\n" | |
1204 | "start http://xml.libexpat.org/ f n\n" | |
1205 | "attribute http://xml.libexpat.org/ attr n\n" | |
1206 | "end http://xml.libexpat.org/ f n\n" | |
1207 | "start http://xml.libexpat.org/ g n\n" | |
1208 | "attribute http://xml.libexpat.org/ attr2 n\n" | |
1209 | "end http://xml.libexpat.org/ g n\n" | |
1210 | "end http://xml.libexpat.org/ e n\n"; | |
1211 | XML_SetReturnNSTriplet(parser, XML_TRUE); | |
1212 | run_ns_tagname_overwrite_test(text, result); | |
1213 | } | |
1214 | END_TEST | |
1215 | ||
1216 | ||
1217 | /* Regression test for SF bug #620343. */ | |
1218 | static void XMLCALL | |
1219 | start_element_fail(void *userData, | |
1220 | const XML_Char *name, const XML_Char **atts) | |
1221 | { | |
1222 | /* We should never get here. */ | |
1223 | fail("should never reach start_element_fail()"); | |
1224 | } | |
1225 | ||
1226 | static void XMLCALL | |
1227 | start_ns_clearing_start_element(void *userData, | |
1228 | const XML_Char *prefix, | |
1229 | const XML_Char *uri) | |
1230 | { | |
1231 | XML_SetStartElementHandler((XML_Parser) userData, NULL); | |
1232 | } | |
1233 | ||
1234 | START_TEST(test_start_ns_clears_start_element) | |
1235 | { | |
1236 | /* This needs to use separate start/end tags; using the empty tag | |
1237 | syntax doesn't cause the problematic path through Expat to be | |
1238 | taken. | |
1239 | */ | |
1240 | char *text = "<e xmlns='http://xml.libexpat.org/'></e>"; | |
1241 | ||
1242 | XML_SetStartElementHandler(parser, start_element_fail); | |
1243 | XML_SetStartNamespaceDeclHandler(parser, start_ns_clearing_start_element); | |
1244 | XML_UseParserAsHandlerArg(parser); | |
1245 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1246 | xml_failure(parser); | |
1247 | } | |
1248 | END_TEST | |
1249 | ||
1250 | /* Regression test for SF bug #616863. */ | |
1251 | static int XMLCALL | |
1252 | external_entity_handler(XML_Parser parser, | |
1253 | const XML_Char *context, | |
1254 | const XML_Char *base, | |
1255 | const XML_Char *systemId, | |
1256 | const XML_Char *publicId) | |
1257 | { | |
1258 | intptr_t callno = 1 + (intptr_t)XML_GetUserData(parser); | |
1259 | char *text; | |
1260 | XML_Parser p2; | |
1261 | ||
1262 | if (callno == 1) | |
1263 | text = ("<!ELEMENT doc (e+)>\n" | |
1264 | "<!ATTLIST doc xmlns CDATA #IMPLIED>\n" | |
1265 | "<!ELEMENT e EMPTY>\n"); | |
1266 | else | |
1267 | text = ("<?xml version='1.0' encoding='us-ascii'?>" | |
1268 | "<e/>"); | |
1269 | ||
1270 | XML_SetUserData(parser, (void *) callno); | |
1271 | p2 = XML_ExternalEntityParserCreate(parser, context, NULL); | |
1272 | if (XML_Parse(p2, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) { | |
1273 | xml_failure(p2); | |
1274 | return 0; | |
1275 | } | |
1276 | XML_ParserFree(p2); | |
1277 | return 1; | |
1278 | } | |
1279 | ||
1280 | START_TEST(test_default_ns_from_ext_subset_and_ext_ge) | |
1281 | { | |
1282 | char *text = | |
1283 | "<?xml version='1.0'?>\n" | |
1284 | "<!DOCTYPE doc SYSTEM 'http://xml.libexpat.org/doc.dtd' [\n" | |
1285 | " <!ENTITY en SYSTEM 'http://xml.libexpat.org/entity.ent'>\n" | |
1286 | "]>\n" | |
1287 | "<doc xmlns='http://xml.libexpat.org/ns1'>\n" | |
1288 | "&en;\n" | |
1289 | "</doc>"; | |
1290 | ||
1291 | XML_SetParamEntityParsing(parser, XML_PARAM_ENTITY_PARSING_ALWAYS); | |
1292 | XML_SetExternalEntityRefHandler(parser, external_entity_handler); | |
1293 | /* We actually need to set this handler to tickle this bug. */ | |
1294 | XML_SetStartElementHandler(parser, dummy_start_element); | |
1295 | XML_SetUserData(parser, NULL); | |
1296 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1297 | xml_failure(parser); | |
1298 | } | |
1299 | END_TEST | |
1300 | ||
1301 | /* Regression test #1 for SF bug #673791. */ | |
1302 | START_TEST(test_ns_prefix_with_empty_uri_1) | |
1303 | { | |
1304 | char *text = | |
1305 | "<doc xmlns:prefix='http://xml.libexpat.org/'>\n" | |
1306 | " <e xmlns:prefix=''/>\n" | |
1307 | "</doc>"; | |
1308 | ||
1309 | expect_failure(text, | |
1310 | XML_ERROR_UNDECLARING_PREFIX, | |
1311 | "Did not report re-setting namespace" | |
1312 | " URI with prefix to ''."); | |
1313 | } | |
1314 | END_TEST | |
1315 | ||
1316 | /* Regression test #2 for SF bug #673791. */ | |
1317 | START_TEST(test_ns_prefix_with_empty_uri_2) | |
1318 | { | |
1319 | char *text = | |
1320 | "<?xml version='1.0'?>\n" | |
1321 | "<docelem xmlns:pre=''/>"; | |
1322 | ||
1323 | expect_failure(text, | |
1324 | XML_ERROR_UNDECLARING_PREFIX, | |
1325 | "Did not report setting namespace URI with prefix to ''."); | |
1326 | } | |
1327 | END_TEST | |
1328 | ||
1329 | /* Regression test #3 for SF bug #673791. */ | |
1330 | START_TEST(test_ns_prefix_with_empty_uri_3) | |
1331 | { | |
1332 | char *text = | |
1333 | "<!DOCTYPE doc [\n" | |
1334 | " <!ELEMENT doc EMPTY>\n" | |
1335 | " <!ATTLIST doc\n" | |
1336 | " xmlns:prefix CDATA ''>\n" | |
1337 | "]>\n" | |
1338 | "<doc/>"; | |
1339 | ||
1340 | expect_failure(text, | |
1341 | XML_ERROR_UNDECLARING_PREFIX, | |
1342 | "Didn't report attr default setting NS w/ prefix to ''."); | |
1343 | } | |
1344 | END_TEST | |
1345 | ||
1346 | /* Regression test #4 for SF bug #673791. */ | |
1347 | START_TEST(test_ns_prefix_with_empty_uri_4) | |
1348 | { | |
1349 | char *text = | |
1350 | "<!DOCTYPE doc [\n" | |
1351 | " <!ELEMENT prefix:doc EMPTY>\n" | |
1352 | " <!ATTLIST prefix:doc\n" | |
1353 | " xmlns:prefix CDATA 'http://xml.libexpat.org/'>\n" | |
1354 | "]>\n" | |
1355 | "<prefix:doc/>"; | |
1356 | /* Packaged info expected by the end element handler; | |
1357 | the weird structuring lets us re-use the triplet_end_checker() | |
1358 | function also used for another test. */ | |
1359 | char *elemstr[] = { | |
1360 | "http://xml.libexpat.org/ doc prefix" | |
1361 | }; | |
1362 | XML_SetReturnNSTriplet(parser, XML_TRUE); | |
1363 | XML_SetUserData(parser, elemstr); | |
1364 | XML_SetEndElementHandler(parser, triplet_end_checker); | |
1365 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1366 | xml_failure(parser); | |
1367 | } | |
1368 | END_TEST | |
1369 | ||
1370 | START_TEST(test_ns_default_with_empty_uri) | |
1371 | { | |
1372 | char *text = | |
1373 | "<doc xmlns='http://xml.libexpat.org/'>\n" | |
1374 | " <e xmlns=''/>\n" | |
1375 | "</doc>"; | |
1376 | if (XML_Parse(parser, text, strlen(text), XML_TRUE) == XML_STATUS_ERROR) | |
1377 | xml_failure(parser); | |
1378 | } | |
1379 | END_TEST | |
1380 | ||
1381 | /* Regression test for SF bug #692964: two prefixes for one namespace. */ | |
1382 | START_TEST(test_ns_duplicate_attrs_diff_prefixes) | |
1383 | { | |
1384 | char *text = | |
1385 | "<doc xmlns:a='http://xml.libexpat.org/a'\n" | |
1386 | " xmlns:b='http://xml.libexpat.org/a'\n" | |
1387 | " a:a='v' b:a='v' />"; | |
1388 | expect_failure(text, | |
1389 | XML_ERROR_DUPLICATE_ATTRIBUTE, | |
1390 | "did not report multiple attributes with same URI+name"); | |
1391 | } | |
1392 | END_TEST | |
1393 | ||
1394 | /* Regression test for SF bug #695401: unbound prefix. */ | |
1395 | START_TEST(test_ns_unbound_prefix_on_attribute) | |
1396 | { | |
1397 | char *text = "<doc a:attr=''/>"; | |
1398 | expect_failure(text, | |
1399 | XML_ERROR_UNBOUND_PREFIX, | |
1400 | "did not report unbound prefix on attribute"); | |
1401 | } | |
1402 | END_TEST | |
1403 | ||
1404 | /* Regression test for SF bug #695401: unbound prefix. */ | |
1405 | START_TEST(test_ns_unbound_prefix_on_element) | |
1406 | { | |
1407 | char *text = "<a:doc/>"; | |
1408 | expect_failure(text, | |
1409 | XML_ERROR_UNBOUND_PREFIX, | |
1410 | "did not report unbound prefix on element"); | |
1411 | } | |
1412 | END_TEST | |
1413 | ||
1414 | static Suite * | |
1415 | make_suite(void) | |
1416 | { | |
1417 | Suite *s = suite_create("basic"); | |
1418 | TCase *tc_basic = tcase_create("basic tests"); | |
1419 | TCase *tc_namespace = tcase_create("XML namespaces"); | |
1420 | ||
1421 | suite_add_tcase(s, tc_basic); | |
1422 | tcase_add_checked_fixture(tc_basic, basic_setup, basic_teardown); | |
1423 | tcase_add_test(tc_basic, test_nul_byte); | |
1424 | tcase_add_test(tc_basic, test_u0000_char); | |
1425 | tcase_add_test(tc_basic, test_bom_utf8); | |
1426 | tcase_add_test(tc_basic, test_bom_utf16_be); | |
1427 | tcase_add_test(tc_basic, test_bom_utf16_le); | |
1428 | tcase_add_test(tc_basic, test_illegal_utf8); | |
1429 | tcase_add_test(tc_basic, test_utf16); | |
1430 | tcase_add_test(tc_basic, test_utf16_le_epilog_newline); | |
1431 | tcase_add_test(tc_basic, test_latin1_umlauts); | |
1432 | /* Regression test for SF bug #491986. */ | |
1433 | tcase_add_test(tc_basic, test_danish_latin1); | |
1434 | /* Regression test for SF bug #514281. */ | |
1435 | tcase_add_test(tc_basic, test_french_charref_hexidecimal); | |
1436 | tcase_add_test(tc_basic, test_french_charref_decimal); | |
1437 | tcase_add_test(tc_basic, test_french_latin1); | |
1438 | tcase_add_test(tc_basic, test_french_utf8); | |
1439 | tcase_add_test(tc_basic, test_utf8_false_rejection); | |
1440 | tcase_add_test(tc_basic, test_line_number_after_parse); | |
1441 | tcase_add_test(tc_basic, test_column_number_after_parse); | |
1442 | tcase_add_test(tc_basic, test_line_and_column_numbers_inside_handlers); | |
1443 | tcase_add_test(tc_basic, test_line_number_after_error); | |
1444 | tcase_add_test(tc_basic, test_column_number_after_error); | |
1445 | tcase_add_test(tc_basic, test_really_long_lines); | |
1446 | tcase_add_test(tc_basic, test_end_element_events); | |
1447 | tcase_add_test(tc_basic, test_attr_whitespace_normalization); | |
1448 | tcase_add_test(tc_basic, test_xmldecl_misplaced); | |
1449 | tcase_add_test(tc_basic, test_unknown_encoding_internal_entity); | |
1450 | tcase_add_test(tc_basic, | |
1451 | test_wfc_undeclared_entity_unread_external_subset); | |
1452 | tcase_add_test(tc_basic, test_wfc_undeclared_entity_no_external_subset); | |
1453 | tcase_add_test(tc_basic, test_wfc_undeclared_entity_standalone); | |
1454 | tcase_add_test(tc_basic, test_wfc_undeclared_entity_with_external_subset); | |
1455 | tcase_add_test(tc_basic, | |
1456 | test_wfc_undeclared_entity_with_external_subset_standalone); | |
1457 | tcase_add_test(tc_basic, test_wfc_no_recursive_entity_refs); | |
1458 | tcase_add_test(tc_basic, test_ext_entity_set_encoding); | |
1459 | tcase_add_test(tc_basic, test_dtd_default_handling); | |
1460 | tcase_add_test(tc_basic, test_empty_ns_without_namespaces); | |
1461 | tcase_add_test(tc_basic, test_ns_in_attribute_default_without_namespaces); | |
1462 | tcase_add_test(tc_basic, test_stop_parser_between_char_data_calls); | |
1463 | tcase_add_test(tc_basic, test_suspend_parser_between_char_data_calls); | |
1464 | ||
1465 | suite_add_tcase(s, tc_namespace); | |
1466 | tcase_add_checked_fixture(tc_namespace, | |
1467 | namespace_setup, namespace_teardown); | |
1468 | tcase_add_test(tc_namespace, test_return_ns_triplet); | |
1469 | tcase_add_test(tc_namespace, test_ns_tagname_overwrite); | |
1470 | tcase_add_test(tc_namespace, test_ns_tagname_overwrite_triplet); | |
1471 | tcase_add_test(tc_namespace, test_start_ns_clears_start_element); | |
1472 | tcase_add_test(tc_namespace, test_default_ns_from_ext_subset_and_ext_ge); | |
1473 | tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_1); | |
1474 | tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_2); | |
1475 | tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_3); | |
1476 | tcase_add_test(tc_namespace, test_ns_prefix_with_empty_uri_4); | |
1477 | tcase_add_test(tc_namespace, test_ns_default_with_empty_uri); | |
1478 | tcase_add_test(tc_namespace, test_ns_duplicate_attrs_diff_prefixes); | |
1479 | tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_attribute); | |
1480 | tcase_add_test(tc_namespace, test_ns_unbound_prefix_on_element); | |
1481 | ||
1482 | return s; | |
1483 | } | |
1484 | ||
1485 | ||
1486 | int | |
1487 | main(int argc, char *argv[]) | |
1488 | { | |
1489 | int i, nf; | |
1490 | int verbosity = CK_NORMAL; | |
1491 | Suite *s = make_suite(); | |
1492 | SRunner *sr = srunner_create(s); | |
1493 | ||
1494 | /* run the tests for internal helper functions */ | |
1495 | testhelper_is_whitespace_normalized(); | |
1496 | ||
1497 | for (i = 1; i < argc; ++i) { | |
1498 | char *opt = argv[i]; | |
1499 | if (strcmp(opt, "-v") == 0 || strcmp(opt, "--verbose") == 0) | |
1500 | verbosity = CK_VERBOSE; | |
1501 | else if (strcmp(opt, "-q") == 0 || strcmp(opt, "--quiet") == 0) | |
1502 | verbosity = CK_SILENT; | |
1503 | else { | |
1504 | fprintf(stderr, "runtests: unknown option '%s'\n", opt); | |
1505 | return 2; | |
1506 | } | |
1507 | } | |
1508 | if (verbosity != CK_SILENT) | |
1509 | printf("Expat version: %s\n", XML_ExpatVersion()); | |
1510 | srunner_run_all(sr, verbosity); | |
1511 | nf = srunner_ntests_failed(sr); | |
1512 | srunner_free(sr); | |
1513 | ||
1514 | return (nf == 0) ? EXIT_SUCCESS : EXIT_FAILURE; | |
1515 | } |