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