msxml3: Initial implementation of attributeDecl() in writer.
[wine] / dlls / msxml3 / tests / saxreader.c
1 /*
2  * SAXReader/MXWriter tests
3  *
4  * Copyright 2008 Piotr Caban
5  * Copyright 2011 Thomas Mullaly
6  * Copyright 2012 Nikolay Sivov
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #define COBJMACROS
24 #define CONST_VTABLE
25
26 #include <stdio.h>
27 #include <assert.h>
28
29 #include "windows.h"
30 #include "ole2.h"
31 #include "msxml2.h"
32 #include "msxml2did.h"
33 #include "ocidl.h"
34 #include "dispex.h"
35
36 #include "wine/test.h"
37
38 #define EXPECT_HR(hr,hr_exp) \
39     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
40
41 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
42 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
43 {
44     ULONG rc = IUnknown_AddRef(obj);
45     IUnknown_Release(obj);
46     ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1);
47 }
48
49 static LONG get_refcount(void *iface)
50 {
51     IUnknown *unk = iface;
52     LONG ref;
53
54     ref = IUnknown_AddRef(unk);
55     IUnknown_Release(unk);
56     return ref-1;
57 }
58
59 struct msxmlsupported_data_t
60 {
61     const GUID *clsid;
62     const char *name;
63     BOOL supported;
64 };
65
66 static BOOL is_clsid_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
67 {
68     while (table->clsid)
69     {
70         if (table->clsid == clsid) return table->supported;
71         table++;
72     }
73     return FALSE;
74 }
75
76 static BSTR alloc_str_from_narrow(const char *str)
77 {
78     int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
79     BSTR ret = SysAllocStringLen(NULL, len - 1);  /* NUL character added automatically */
80     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
81     return ret;
82 }
83
84 static BSTR alloced_bstrs[512];
85 static int alloced_bstrs_count;
86
87 static BSTR _bstr_(const char *str)
88 {
89     assert(alloced_bstrs_count < sizeof(alloced_bstrs)/sizeof(alloced_bstrs[0]));
90     alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str);
91     return alloced_bstrs[alloced_bstrs_count++];
92 }
93
94 static void free_bstrs(void)
95 {
96     int i;
97     for (i = 0; i < alloced_bstrs_count; i++)
98         SysFreeString(alloced_bstrs[i]);
99     alloced_bstrs_count = 0;
100 }
101
102 static void test_saxstr(const char *file, unsigned line, BSTR str, const char *expected, int todo, int *failcount)
103 {
104     int len, lenexp, cmp;
105     WCHAR buf[1024];
106
107     len = SysStringLen(str);
108
109     if (!expected) {
110         if (str && todo)
111         {
112             (*failcount)++;
113             todo_wine
114             ok_(file, line) (!str, "got %p, expected null str\n", str);
115         }
116         else
117             ok_(file, line) (!str, "got %p, expected null str\n", str);
118
119         if (len && todo)
120         {
121             (*failcount)++;
122             todo_wine
123             ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
124         }
125         else
126             ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
127         return;
128     }
129
130     lenexp = strlen(expected);
131     if (lenexp != len && todo)
132     {
133         (*failcount)++;
134         todo_wine
135         ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
136     }
137     else
138         ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
139
140     /* exit earlier on length mismatch */
141     if (lenexp != len) return;
142
143     MultiByteToWideChar(CP_ACP, 0, expected, -1, buf, sizeof(buf)/sizeof(WCHAR));
144
145     cmp = memcmp(str, buf, lenexp*sizeof(WCHAR));
146     if (cmp && todo)
147     {
148         (*failcount)++;
149         todo_wine
150         ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
151                          wine_dbgstr_wn(str, len), expected);
152     }
153     else
154         ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
155                              wine_dbgstr_wn(str, len), expected);
156 }
157
158 typedef enum _CH {
159     CH_ENDTEST,
160     CH_PUTDOCUMENTLOCATOR,
161     CH_STARTDOCUMENT,
162     CH_ENDDOCUMENT,
163     CH_STARTPREFIXMAPPING,
164     CH_ENDPREFIXMAPPING,
165     CH_STARTELEMENT,
166     CH_ENDELEMENT,
167     CH_CHARACTERS,
168     CH_IGNORABLEWHITESPACE,
169     CH_PROCESSINGINSTRUCTION,
170     CH_SKIPPEDENTITY,
171     EH_ERROR,
172     EH_FATALERROR,
173     EG_IGNORABLEWARNING,
174     EVENT_LAST
175 } CH;
176
177 static const char *event_names[EVENT_LAST] = {
178     "endtest",
179     "putDocumentLocator",
180     "startDocument",
181     "endDocument",
182     "startPrefixMapping",
183     "endPrefixMapping",
184     "startElement",
185     "endElement",
186     "characters",
187     "ignorableWhitespace",
188     "processingInstruction",
189     "skippedEntity",
190     "error",
191     "fatalError",
192     "ignorableWarning"
193 };
194
195 struct attribute_entry {
196     const char *uri;
197     const char *local;
198     const char *qname;
199     const char *value;
200
201     /* used for actual call data only, null for expected call data */
202     BSTR uriW;
203     BSTR localW;
204     BSTR qnameW;
205     BSTR valueW;
206 };
207
208 struct call_entry {
209     CH id;
210     int line;
211     int column;
212     HRESULT ret;
213     const char *arg1;
214     const char *arg2;
215     const char *arg3;
216
217     /* allocated once at startElement callback */
218     struct attribute_entry *attributes;
219     int attr_count;
220
221     /* used for actual call data only, null for expected call data */
222     BSTR arg1W;
223     BSTR arg2W;
224     BSTR arg3W;
225 };
226
227 struct call_sequence
228 {
229     int count;
230     int size;
231     struct call_entry *sequence;
232 };
233
234 #define CONTENT_HANDLER_INDEX 0
235 #define NUM_CALL_SEQUENCES    1
236 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
237
238 static void init_call_entry(ISAXLocator *locator, struct call_entry *call)
239 {
240     memset(call, 0, sizeof(*call));
241     ISAXLocator_getLineNumber(locator, &call->line);
242     ISAXLocator_getColumnNumber(locator, &call->column);
243 }
244
245 static void add_call(struct call_sequence **seq, int sequence_index,
246     const struct call_entry *call)
247 {
248     struct call_sequence *call_seq = seq[sequence_index];
249
250     if (!call_seq->sequence)
251     {
252         call_seq->size = 10;
253         call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
254                                       call_seq->size * sizeof (struct call_entry));
255     }
256
257     if (call_seq->count == call_seq->size)
258     {
259         call_seq->size *= 2;
260         call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
261                                         call_seq->sequence,
262                                         call_seq->size * sizeof (struct call_entry));
263     }
264
265     assert(call_seq->sequence);
266
267     call_seq->sequence[call_seq->count].id     = call->id;
268     call_seq->sequence[call_seq->count].line   = call->line;
269     call_seq->sequence[call_seq->count].column = call->column;
270     call_seq->sequence[call_seq->count].arg1W  = call->arg1W;
271     call_seq->sequence[call_seq->count].arg2W  = call->arg2W;
272     call_seq->sequence[call_seq->count].arg3W  = call->arg3W;
273     call_seq->sequence[call_seq->count].ret    = call->ret;
274     call_seq->sequence[call_seq->count].attr_count = call->attr_count;
275     call_seq->sequence[call_seq->count].attributes = call->attributes;
276
277     call_seq->count++;
278 }
279
280 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
281 {
282     int i;
283
284     struct call_sequence *call_seq = seg[sequence_index];
285
286     for (i = 0; i < call_seq->count; i++)
287     {
288         int j;
289
290         for (j = 0; j < call_seq->sequence[i].attr_count; j++)
291         {
292             SysFreeString(call_seq->sequence[i].attributes[j].uriW);
293             SysFreeString(call_seq->sequence[i].attributes[j].localW);
294             SysFreeString(call_seq->sequence[i].attributes[j].qnameW);
295         }
296
297         SysFreeString(call_seq->sequence[i].arg1W);
298         SysFreeString(call_seq->sequence[i].arg2W);
299         SysFreeString(call_seq->sequence[i].arg3W);
300     }
301
302     HeapFree(GetProcessHeap(), 0, call_seq->sequence);
303     call_seq->sequence = NULL;
304     call_seq->count = call_seq->size = 0;
305 }
306
307 static inline void flush_sequences(struct call_sequence **seq, int n)
308 {
309     int i;
310     for (i = 0; i < n; i++)
311         flush_sequence(seq, i);
312 }
313
314 static const char *get_event_name(CH event)
315 {
316     return event_names[event];
317 }
318
319 static void compare_attributes(const struct call_entry *actual, const struct call_entry *expected, const char *context,
320     int todo, const char *file, int line, int *failcount)
321 {
322     int i, lenexp = 0;
323
324     /* attribute count is not stored for expected data */
325     if (expected->attributes)
326     {
327         struct attribute_entry *ptr = expected->attributes;
328         while (ptr->uri) { lenexp++; ptr++; };
329     }
330
331     /* check count first and exit earlier */
332     if (actual->attr_count != lenexp && todo)
333     {
334         (*failcount)++;
335         todo_wine
336             ok_(file, line) (FALSE, "%s: in event %s expecting attr count %d got %d\n",
337                 context, get_event_name(actual->id), lenexp, actual->attr_count);
338     }
339     else
340         ok_(file, line) (actual->attr_count == lenexp, "%s: in event %s expecting attr count %d got %d\n",
341             context, get_event_name(actual->id), lenexp, actual->attr_count);
342
343     if (actual->attr_count != lenexp) return;
344
345     /* now compare all attributes strings */
346     for (i = 0; i < actual->attr_count; i++)
347     {
348         test_saxstr(file, line, actual->attributes[i].uriW,   expected->attributes[i].uri, todo, failcount);
349         test_saxstr(file, line, actual->attributes[i].localW, expected->attributes[i].local, todo, failcount);
350         test_saxstr(file, line, actual->attributes[i].qnameW, expected->attributes[i].qname, todo, failcount);
351         test_saxstr(file, line, actual->attributes[i].valueW, expected->attributes[i].value, todo, failcount);
352     }
353 }
354
355 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
356     const struct call_entry *expected, const char *context, int todo,
357     const char *file, int line)
358 {
359     struct call_sequence *call_seq = seq[sequence_index];
360     static const struct call_entry end_of_sequence = { CH_ENDTEST };
361     const struct call_entry *actual, *sequence;
362     int failcount = 0;
363
364     add_call(seq, sequence_index, &end_of_sequence);
365
366     sequence = call_seq->sequence;
367     actual = sequence;
368
369     while (expected->id != CH_ENDTEST && actual->id != CH_ENDTEST)
370     {
371         if (expected->id == actual->id)
372         {
373             /* always test position data */
374             if (expected->line != actual->line && todo)
375             {
376                 todo_wine
377                 {
378                     failcount++;
379                     ok_(file, line) (FALSE,
380                         "%s: in event %s expecting line %d got %d\n",
381                         context, get_event_name(actual->id), expected->line, actual->line);
382                 }
383             }
384             else
385             {
386                 ok_(file, line) (expected->line == actual->line,
387                    "%s: in event %s expecting line %d got %d\n",
388                    context, get_event_name(actual->id), expected->line, actual->line);
389             }
390
391             if (expected->column != actual->column && todo)
392             {
393                 todo_wine
394                 {
395                     failcount++;
396                     ok_(file, line) (FALSE,
397                         "%s: in event %s expecting column %d got %d\n",
398                         context, get_event_name(actual->id), expected->column, actual->column);
399                 }
400             }
401             else
402             {
403                 ok_(file, line) (expected->column == actual->column,
404                    "%s: in event %s expecting column %d got %d\n",
405                    context, get_event_name(actual->id), expected->column, actual->column);
406             }
407
408             switch (actual->id)
409             {
410             case CH_PUTDOCUMENTLOCATOR:
411             case CH_STARTDOCUMENT:
412             case CH_ENDDOCUMENT:
413                 break;
414             case CH_STARTPREFIXMAPPING:
415                 /* prefix, uri */
416                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
417                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
418                 break;
419             case CH_ENDPREFIXMAPPING:
420                 /* prefix */
421                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
422                 break;
423             case CH_STARTELEMENT:
424                 /* compare attributes */
425                 compare_attributes(actual, expected, context, todo, file, line, &failcount);
426                 /* fallthrough */
427             case CH_ENDELEMENT:
428                 /* uri, localname, qname */
429                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
430                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
431                 test_saxstr(file, line, actual->arg3W, expected->arg3, todo, &failcount);
432                 break;
433             case CH_CHARACTERS:
434             case CH_IGNORABLEWHITESPACE:
435                 /* char data */
436                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
437                 break;
438             case CH_PROCESSINGINSTRUCTION:
439                 /* target, data */
440                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
441                 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
442                 break;
443             case CH_SKIPPEDENTITY:
444                 /* name */
445                 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
446                 break;
447             case EH_FATALERROR:
448                 /* test return value only */
449                 if (expected->ret != actual->ret && todo)
450                 {
451                      failcount++;
452                      ok_(file, line) (FALSE,
453                          "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
454                          context, get_event_name(actual->id), expected->ret, actual->ret);
455                 }
456                 else
457                      ok_(file, line) (expected->ret == actual->ret,
458                          "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
459                          context, get_event_name(actual->id), expected->ret, actual->ret);
460                 break;
461             case EH_ERROR:
462             case EG_IGNORABLEWARNING:
463             default:
464                 ok(0, "%s: callback not handled, %s\n", context, get_event_name(actual->id));
465             }
466             expected++;
467             actual++;
468         }
469         else if (todo)
470         {
471             failcount++;
472             todo_wine
473             {
474                 ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
475                     context, get_event_name(expected->id), get_event_name(actual->id));
476             }
477
478             flush_sequence(seq, sequence_index);
479             return;
480         }
481         else
482         {
483             ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
484                 context, get_event_name(expected->id), get_event_name(actual->id));
485             expected++;
486             actual++;
487         }
488     }
489
490     if (todo)
491     {
492         todo_wine
493         {
494             if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
495             {
496                 failcount++;
497                 ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
498                     context, get_event_name(expected->id), get_event_name(actual->id));
499             }
500         }
501     }
502     else if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
503     {
504         ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
505             context, get_event_name(expected->id), get_event_name(actual->id));
506     }
507
508     if (todo && !failcount) /* succeeded yet marked todo */
509     {
510         todo_wine
511         {
512             ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
513         }
514     }
515
516     flush_sequence(seq, sequence_index);
517 }
518
519 #define ok_sequence(seq, index, exp, contx, todo) \
520         ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
521
522 static void init_call_sequences(struct call_sequence **seq, int n)
523 {
524     int i;
525
526     for (i = 0; i < n; i++)
527         seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
528 }
529
530 static const WCHAR szSimpleXML[] = {
531 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n',
532 '<','B','a','n','k','A','c','c','o','u','n','t','>','\n',
533 ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n',
534 ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n',
535 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0'
536 };
537
538 static const WCHAR carriage_ret_test[] = {
539 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n',
540 '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n',
541 '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n',
542 '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n',
543 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n','\0'
544 };
545
546 static const WCHAR szUtf16XML[] = {
547 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ',
548 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ',
549 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n'
550 };
551
552 static const CHAR szUtf16BOM[] = {0xff, 0xfe};
553
554 static const CHAR szUtf8XML[] =
555 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n";
556
557 static const char utf8xml2[] =
558 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\r\n";
559
560 static const CHAR testXML[] =
561 "<?xml version=\"1.0\" ?>\n"
562 "<BankAccount>\n"
563 "   <Number>1234</Number>\n"
564 "   <Name>Captain Ahab</Name>\n"
565 "</BankAccount>\n";
566
567 static const char test_attributes[] =
568 "<?xml version=\"1.0\" ?>\n"
569 "<document xmlns:test=\"prefix_test\" xmlns=\"prefix\" test:arg1=\"arg1\" arg2=\"arg2\" test:ar3=\"arg3\">\n"
570 "<node1 xmlns:p=\"test\" />"
571 "</document>\n";
572
573 static struct call_entry content_handler_test1[] = {
574     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
575     { CH_STARTDOCUMENT, 0, 0, S_OK },
576     { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
577     { CH_CHARACTERS, 2, 14, S_OK, "\n   " },
578     { CH_STARTELEMENT, 3, 12, S_OK, "", "Number", "Number" },
579     { CH_CHARACTERS, 3, 12, S_OK, "1234" },
580     { CH_ENDELEMENT, 3, 18, S_OK, "", "Number", "Number" },
581     { CH_CHARACTERS, 3, 25, S_OK, "\n   " },
582     { CH_STARTELEMENT, 4, 10, S_OK, "", "Name", "Name" },
583     { CH_CHARACTERS, 4, 10, S_OK, "Captain Ahab" },
584     { CH_ENDELEMENT, 4, 24, S_OK, "", "Name", "Name" },
585     { CH_CHARACTERS, 4, 29, S_OK, "\n" },
586     { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
587     { CH_ENDDOCUMENT, 0, 0, S_OK},
588     { CH_ENDTEST }
589 };
590
591 /* applies to versions 4 and 6 */
592 static struct call_entry content_handler_test1_alternate[] = {
593     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
594     { CH_STARTDOCUMENT, 1, 22, S_OK },
595     { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
596     { CH_CHARACTERS, 3, 4, S_OK, "\n   " },
597     { CH_STARTELEMENT, 3, 11, S_OK, "", "Number", "Number" },
598     { CH_CHARACTERS, 3, 16, S_OK, "1234" },
599     { CH_ENDELEMENT, 3, 24, S_OK, "", "Number", "Number" },
600     { CH_CHARACTERS, 4, 4, S_OK, "\n   " },
601     { CH_STARTELEMENT, 4, 9, S_OK, "", "Name", "Name" },
602     { CH_CHARACTERS, 4, 22, S_OK, "Captain Ahab" },
603     { CH_ENDELEMENT, 4, 28, S_OK, "", "Name", "Name" },
604     { CH_CHARACTERS, 5, 1, S_OK, "\n" },
605     { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
606     { CH_ENDDOCUMENT, 6, 0, S_OK },
607     { CH_ENDTEST }
608 };
609
610 static struct call_entry content_handler_test2[] = {
611     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
612     { CH_STARTDOCUMENT, 0, 0, S_OK },
613     { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
614     { CH_CHARACTERS, 2, 14, S_OK, "\n" },
615     { CH_CHARACTERS, 2, 16, S_OK, "\t" },
616     { CH_STARTELEMENT, 3, 10, S_OK, "", "Number", "Number" },
617     { CH_CHARACTERS, 3, 10, S_OK, "1234" },
618     { CH_ENDELEMENT, 3, 16, S_OK, "", "Number", "Number" },
619     { CH_CHARACTERS, 3, 23, S_OK, "\n" },
620     { CH_CHARACTERS, 3, 25, S_OK, "\t" },
621     { CH_STARTELEMENT, 4, 8, S_OK, "", "Name", "Name" },
622     { CH_CHARACTERS, 4, 8, S_OK, "Captain Ahab" },
623     { CH_ENDELEMENT, 4, 22, S_OK, "", "Name", "Name" },
624     { CH_CHARACTERS, 4, 27, S_OK, "\n" },
625     { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
626     { CH_ENDDOCUMENT, 0, 0, S_OK },
627     { CH_ENDTEST }
628 };
629
630 static struct call_entry content_handler_test2_alternate[] = {
631     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
632     { CH_STARTDOCUMENT, 1, 21, S_OK },
633     { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
634     { CH_CHARACTERS, 3, 0, S_OK, "\n" },
635     { CH_CHARACTERS, 3, 2, S_OK, "\t" },
636     { CH_STARTELEMENT, 3, 9, S_OK, "", "Number", "Number" },
637     { CH_CHARACTERS, 3, 14, S_OK, "1234" },
638     { CH_ENDELEMENT, 3, 22, S_OK, "", "Number", "Number" },
639     { CH_CHARACTERS, 4, 0, S_OK, "\n" },
640     { CH_CHARACTERS, 4, 2, S_OK, "\t" },
641     { CH_STARTELEMENT, 4, 7, S_OK, "", "Name", "Name" },
642     { CH_CHARACTERS, 4, 20, S_OK, "Captain Ahab" },
643     { CH_ENDELEMENT, 4, 26, S_OK, "", "Name", "Name" },
644     { CH_CHARACTERS, 5, 0, S_OK, "\n" },
645     { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
646     { CH_ENDDOCUMENT, 6, 0, S_OK },
647     { CH_ENDTEST }
648 };
649
650 static struct call_entry content_handler_testerror[] = {
651     { CH_PUTDOCUMENTLOCATOR, 0, 0, E_FAIL },
652     { EH_FATALERROR, 0, 0, E_FAIL },
653     { CH_ENDTEST }
654 };
655
656 static struct call_entry content_handler_testerror_alternate[] = {
657     { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL },
658     { EH_FATALERROR, 1, 0, E_FAIL },
659     { CH_ENDTEST }
660 };
661
662 static struct call_entry content_handler_test_callback_rets[] = {
663     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_FALSE },
664     { CH_STARTDOCUMENT, 0, 0, S_FALSE },
665     { EH_FATALERROR, 0, 0, S_FALSE },
666     { CH_ENDTEST }
667 };
668
669 static struct call_entry content_handler_test_callback_rets_alt[] = {
670     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE },
671     { CH_STARTDOCUMENT, 1, 22, S_FALSE },
672     { CH_STARTELEMENT, 2, 13, S_FALSE, "", "BankAccount", "BankAccount" },
673     { CH_CHARACTERS, 3, 4, S_FALSE, "\n   " },
674     { CH_STARTELEMENT, 3, 11, S_FALSE, "", "Number", "Number" },
675     { CH_CHARACTERS, 3, 16, S_FALSE, "1234" },
676     { CH_ENDELEMENT, 3, 24, S_FALSE, "", "Number", "Number" },
677     { CH_CHARACTERS, 4, 4, S_FALSE, "\n   " },
678     { CH_STARTELEMENT, 4, 9, S_FALSE, "", "Name", "Name" },
679     { CH_CHARACTERS, 4, 22, S_FALSE, "Captain Ahab" },
680     { CH_ENDELEMENT, 4, 28, S_FALSE, "", "Name", "Name" },
681     { CH_CHARACTERS, 5, 1, S_FALSE, "\n" },
682     { CH_ENDELEMENT, 5, 14, S_FALSE, "", "BankAccount", "BankAccount" },
683     { CH_ENDDOCUMENT, 6, 0, S_FALSE },
684     { CH_ENDTEST }
685 };
686
687 static struct attribute_entry ch_attributes1[] = {
688     { "", "", "xmlns:test", "prefix_test" },
689     { "", "", "xmlns", "prefix" },
690     { "prefix_test", "arg1", "test:arg1", "arg1" },
691     { "", "arg2", "arg2", "arg2" },
692     { "prefix_test", "ar3", "test:ar3", "arg3" },
693     { NULL }
694 };
695
696 static struct attribute_entry ch_attributes2[] = {
697     { "", "", "xmlns:p", "test" },
698     { NULL }
699 };
700
701 static struct call_entry content_handler_test_attributes[] = {
702     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
703     { CH_STARTDOCUMENT, 0, 0, S_OK },
704     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
705     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
706     { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes1 },
707     { CH_CHARACTERS, 2, 96, S_OK, "\n" },
708     { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
709     { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", ch_attributes2 },
710     { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
711     { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
712     { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
713     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
714     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
715     { CH_ENDDOCUMENT, 0, 0 },
716     { CH_ENDTEST }
717 };
718
719 static struct attribute_entry ch_attributes_alt_4[] = {
720     { "prefix_test", "arg1", "test:arg1", "arg1" },
721     { "", "arg2", "arg2", "arg2" },
722     { "prefix_test", "ar3", "test:ar3", "arg3" },
723     { "", "", "xmlns:test", "prefix_test" },
724     { "", "", "xmlns", "prefix" },
725     { NULL }
726 };
727
728 static struct call_entry content_handler_test_attributes_alternate_4[] = {
729     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
730     { CH_STARTDOCUMENT, 1, 22, S_OK },
731     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
732     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
733     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_4 },
734     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
735     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
736     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2 },
737     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
738     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
739     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
740     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
741     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
742     { CH_ENDDOCUMENT, 4, 0, S_OK },
743     { CH_ENDTEST }
744 };
745
746 /* 'namespace' feature switched off */
747 static struct attribute_entry ch_attributes_alt_no_ns[] = {
748     { "", "", "xmlns:test", "prefix_test" },
749     { "", "", "xmlns", "prefix" },
750     { "", "", "test:arg1", "arg1" },
751     { "", "", "arg2", "arg2" },
752     { "", "", "test:ar3", "arg3" },
753     { NULL }
754 };
755
756 static struct call_entry content_handler_test_attributes_alt_no_ns[] = {
757     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
758     { CH_STARTDOCUMENT, 1, 22, S_OK },
759     { CH_STARTELEMENT, 2, 95, S_OK, "", "", "document", ch_attributes_alt_no_ns },
760     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
761     { CH_STARTELEMENT, 3, 24, S_OK, "", "", "node1", ch_attributes2 },
762     { CH_ENDELEMENT, 3, 24, S_OK, "", "", "node1" },
763     { CH_ENDELEMENT, 3, 35, S_OK, "", "", "document" },
764     { CH_ENDDOCUMENT, 4, 0, S_OK },
765     { CH_ENDTEST }
766 };
767
768 static struct attribute_entry ch_attributes_alt_6[] = {
769     { "prefix_test", "arg1", "test:arg1", "arg1" },
770     { "", "arg2", "arg2", "arg2" },
771     { "prefix_test", "ar3", "test:ar3", "arg3" },
772     { "http://www.w3.org/2000/xmlns/", "", "xmlns:test", "prefix_test" },
773     { "http://www.w3.org/2000/xmlns/", "", "xmlns", "prefix" },
774     { NULL }
775 };
776
777 static struct attribute_entry ch_attributes2_6[] = {
778     { "http://www.w3.org/2000/xmlns/", "", "xmlns:p", "test" },
779     { NULL }
780 };
781
782 static struct call_entry content_handler_test_attributes_alternate_6[] = {
783     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
784     { CH_STARTDOCUMENT, 1, 22, S_OK },
785     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
786     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
787     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_6 },
788     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
789     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
790     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2_6 },
791     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
792     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
793     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
794     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
795     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
796     { CH_ENDDOCUMENT, 4, 0, S_OK },
797     { CH_ENDTEST }
798 };
799
800 /* 'namespaces' is on, 'namespace-prefixes' if off */
801 static struct attribute_entry ch_attributes_no_prefix[] = {
802     { "prefix_test", "arg1", "test:arg1", "arg1" },
803     { "", "arg2", "arg2", "arg2" },
804     { "prefix_test", "ar3", "test:ar3", "arg3" },
805     { NULL }
806 };
807
808 static struct call_entry content_handler_test_attributes_alt_no_prefix[] = {
809     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
810     { CH_STARTDOCUMENT, 1, 22, S_OK },
811     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
812     { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
813     { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
814     { CH_CHARACTERS, 3, 1, S_OK, "\n" },
815     { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
816     { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", NULL },
817     { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
818     { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
819     { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
820     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
821     { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
822     { CH_ENDDOCUMENT, 4, 0, S_OK },
823     { CH_ENDTEST }
824 };
825
826 static struct call_entry content_handler_test_attributes_no_prefix[] = {
827     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
828     { CH_STARTDOCUMENT, 0, 0, S_OK },
829     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
830     { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
831     { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
832     { CH_CHARACTERS, 2, 96, S_OK, "\n" },
833     { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
834     { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", NULL },
835     { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
836     { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
837     { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
838     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
839     { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
840     { CH_ENDDOCUMENT, 0, 0 },
841     { CH_ENDTEST }
842 };
843
844 static struct attribute_entry xmlspace_attrs[] = {
845     { "http://www.w3.org/XML/1998/namespace", "space", "xml:space", "preserve" },
846     { NULL }
847 };
848
849 static struct call_entry xmlspaceattr_test[] = {
850     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
851     { CH_STARTDOCUMENT, 0, 0, S_OK },
852     { CH_STARTELEMENT, 1, 64, S_OK, "", "a", "a", xmlspace_attrs },
853     { CH_CHARACTERS, 1, 64, S_OK, " Some text data " },
854     { CH_ENDELEMENT, 1, 82, S_OK, "", "a", "a" },
855     { CH_ENDDOCUMENT, 0, 0, S_OK },
856     { CH_ENDTEST }
857 };
858
859 static struct call_entry xmlspaceattr_test_alternate[] = {
860     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
861     { CH_STARTDOCUMENT, 1, 39, S_OK },
862     { CH_STARTELEMENT, 1, 63, S_OK, "", "a", "a", xmlspace_attrs },
863     { CH_CHARACTERS, 1, 80, S_OK, " Some text data " },
864     { CH_ENDELEMENT, 1, 83, S_OK, "", "a", "a" },
865     { CH_ENDDOCUMENT, 1, 83, S_OK },
866     { CH_ENDTEST }
867 };
868
869 /* attribute value normalization test */
870 static const char attribute_normalize[] =
871     "<?xml version=\"1.0\" ?>\n"
872     "<a attr1=\" \r \n \tattr_value &#65; \t \r \n\r\n \n\"/>\n";
873
874 static struct attribute_entry attribute_norm_attrs[] = {
875     { "", "attr1", "attr1", "      attr_value A         " },
876     { NULL }
877 };
878
879 static struct call_entry attribute_norm[] = {
880     { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
881     { CH_STARTDOCUMENT, 0, 0, S_OK },
882     { CH_STARTELEMENT, 6, 4, S_OK, "", "a", "a", attribute_norm_attrs },
883     { CH_ENDELEMENT, 6, 4, S_OK, "", "a", "a" },
884     { CH_ENDDOCUMENT, 0, 0, S_OK },
885     { CH_ENDTEST }
886 };
887
888 static struct call_entry attribute_norm_alt[] = {
889     { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
890     { CH_STARTDOCUMENT, 1, 22, S_OK },
891     { CH_STARTELEMENT, 8, 3, S_OK, "", "a", "a", attribute_norm_attrs },
892     { CH_ENDELEMENT, 8, 3, S_OK, "", "a", "a" },
893     { CH_ENDDOCUMENT, 9, 0, S_OK },
894     { CH_ENDTEST }
895 };
896
897 static const char xmlspace_attr[] =
898     "<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
899     "<a xml:space=\"preserve\"> Some text data </a>";
900
901 static struct call_entry *expectCall;
902 static ISAXLocator *locator;
903 static ISAXXMLReader *g_reader;
904 int msxml_version;
905
906 static void set_expected_seq(struct call_entry *expected)
907 {
908     expectCall = expected;
909 }
910
911 /* to be called once on each tested callback return */
912 static HRESULT get_expected_ret(void)
913 {
914     HRESULT hr = expectCall->ret;
915     if (expectCall->id != CH_ENDTEST) expectCall++;
916     return hr;
917 }
918
919 static HRESULT WINAPI contentHandler_QueryInterface(
920         ISAXContentHandler* iface,
921         REFIID riid,
922         void **ppvObject)
923 {
924     *ppvObject = NULL;
925
926     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
927     {
928         *ppvObject = iface;
929     }
930     else
931     {
932         return E_NOINTERFACE;
933     }
934
935     return S_OK;
936 }
937
938 static ULONG WINAPI contentHandler_AddRef(
939         ISAXContentHandler* iface)
940 {
941     return 2;
942 }
943
944 static ULONG WINAPI contentHandler_Release(
945         ISAXContentHandler* iface)
946 {
947     return 1;
948 }
949
950 static HRESULT WINAPI contentHandler_putDocumentLocator(
951         ISAXContentHandler* iface,
952         ISAXLocator *pLocator)
953 {
954     struct call_entry call;
955     HRESULT hr;
956
957     locator = pLocator;
958
959     init_call_entry(locator, &call);
960     call.id = CH_PUTDOCUMENTLOCATOR;
961     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
962
963     if (msxml_version >= 6) {
964         ISAXAttributes *attr, *attr1;
965         IMXAttributes *mxattr;
966
967         EXPECT_REF(pLocator, 1);
968         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr);
969         EXPECT_HR(hr, S_OK);
970         EXPECT_REF(pLocator, 2);
971         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1);
972         EXPECT_HR(hr, S_OK);
973         EXPECT_REF(pLocator, 3);
974         ok(attr == attr1, "got %p, %p\n", attr, attr1);
975
976         hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
977         EXPECT_HR(hr, E_NOINTERFACE);
978
979         ISAXAttributes_Release(attr);
980         ISAXAttributes_Release(attr1);
981     }
982
983     return get_expected_ret();
984 }
985
986 static ISAXAttributes *test_attr_ptr;
987 static HRESULT WINAPI contentHandler_startDocument(
988         ISAXContentHandler* iface)
989 {
990     struct call_entry call;
991
992     init_call_entry(locator, &call);
993     call.id = CH_STARTDOCUMENT;
994     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
995
996     test_attr_ptr = NULL;
997
998     return get_expected_ret();
999 }
1000
1001 static HRESULT WINAPI contentHandler_endDocument(
1002         ISAXContentHandler* iface)
1003 {
1004     struct call_entry call;
1005
1006     init_call_entry(locator, &call);
1007     call.id = CH_ENDDOCUMENT;
1008     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1009
1010     return get_expected_ret();
1011 }
1012
1013 static HRESULT WINAPI contentHandler_startPrefixMapping(
1014         ISAXContentHandler* iface,
1015         const WCHAR *prefix, int prefix_len,
1016         const WCHAR *uri, int uri_len)
1017 {
1018     struct call_entry call;
1019
1020     init_call_entry(locator, &call);
1021     call.id = CH_STARTPREFIXMAPPING;
1022     call.arg1W = SysAllocStringLen(prefix, prefix_len);
1023     call.arg2W = SysAllocStringLen(uri, uri_len);
1024     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1025
1026     return get_expected_ret();
1027 }
1028
1029 static HRESULT WINAPI contentHandler_endPrefixMapping(
1030         ISAXContentHandler* iface,
1031         const WCHAR *prefix, int len)
1032 {
1033     struct call_entry call;
1034
1035     init_call_entry(locator, &call);
1036     call.id = CH_ENDPREFIXMAPPING;
1037     call.arg1W = SysAllocStringLen(prefix, len);
1038     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1039
1040     return get_expected_ret();
1041 }
1042
1043 static HRESULT WINAPI contentHandler_startElement(
1044         ISAXContentHandler* iface,
1045         const WCHAR *uri, int uri_len,
1046         const WCHAR *localname, int local_len,
1047         const WCHAR *qname, int qname_len,
1048         ISAXAttributes *saxattr)
1049 {
1050     struct call_entry call;
1051     IMXAttributes *mxattr;
1052     HRESULT hr;
1053     int len;
1054
1055     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
1056     EXPECT_HR(hr, E_NOINTERFACE);
1057
1058     init_call_entry(locator, &call);
1059     call.id = CH_STARTELEMENT;
1060     call.arg1W = SysAllocStringLen(uri, uri_len);
1061     call.arg2W = SysAllocStringLen(localname, local_len);
1062     call.arg3W = SysAllocStringLen(qname, qname_len);
1063
1064     if(!test_attr_ptr)
1065         test_attr_ptr = saxattr;
1066     ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
1067
1068     /* store actual attributes */
1069     len = 0;
1070     hr = ISAXAttributes_getLength(saxattr, &len);
1071     EXPECT_HR(hr, S_OK);
1072
1073     if (len)
1074     {
1075         VARIANT_BOOL v;
1076         int i;
1077
1078         struct attribute_entry *attr;
1079         attr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(struct attribute_entry));
1080
1081         v = VARIANT_TRUE;
1082         hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
1083         EXPECT_HR(hr, S_OK);
1084
1085         for (i = 0; i < len; i++)
1086         {
1087             const WCHAR *value;
1088             int value_len;
1089
1090             hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
1091                 &localname, &local_len, &qname, &qname_len);
1092             EXPECT_HR(hr, S_OK);
1093
1094             hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
1095             EXPECT_HR(hr, S_OK);
1096
1097             /* if 'namespaces' switched off uri and local name contains garbage */
1098             if (v == VARIANT_FALSE && msxml_version > 0)
1099             {
1100                 attr[i].uriW   = SysAllocStringLen(NULL, 0);
1101                 attr[i].localW = SysAllocStringLen(NULL, 0);
1102             }
1103             else
1104             {
1105                 attr[i].uriW   = SysAllocStringLen(uri, uri_len);
1106                 attr[i].localW = SysAllocStringLen(localname, local_len);
1107             }
1108
1109             attr[i].qnameW = SysAllocStringLen(qname, qname_len);
1110             attr[i].valueW = SysAllocStringLen(value, value_len);
1111         }
1112
1113         call.attributes = attr;
1114         call.attr_count = len;
1115     }
1116
1117     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1118
1119     return get_expected_ret();
1120 }
1121
1122 static HRESULT WINAPI contentHandler_endElement(
1123         ISAXContentHandler* iface,
1124         const WCHAR *uri, int uri_len,
1125         const WCHAR *localname, int local_len,
1126         const WCHAR *qname, int qname_len)
1127 {
1128     struct call_entry call;
1129
1130     init_call_entry(locator, &call);
1131     call.id = CH_ENDELEMENT;
1132     call.arg1W = SysAllocStringLen(uri, uri_len);
1133     call.arg2W = SysAllocStringLen(localname, local_len);
1134     call.arg3W = SysAllocStringLen(qname, qname_len);
1135     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1136
1137     return get_expected_ret();
1138 }
1139
1140 static HRESULT WINAPI contentHandler_characters(
1141         ISAXContentHandler* iface,
1142         const WCHAR *chars,
1143         int len)
1144 {
1145     struct call_entry call;
1146
1147     init_call_entry(locator, &call);
1148     call.id = CH_CHARACTERS;
1149     call.arg1W = SysAllocStringLen(chars, len);
1150     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1151
1152     return get_expected_ret();
1153 }
1154
1155 static HRESULT WINAPI contentHandler_ignorableWhitespace(
1156         ISAXContentHandler* iface,
1157         const WCHAR *chars, int len)
1158 {
1159     struct call_entry call;
1160
1161     init_call_entry(locator, &call);
1162     call.id = CH_IGNORABLEWHITESPACE;
1163     call.arg1W = SysAllocStringLen(chars, len);
1164     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1165
1166     return get_expected_ret();
1167 }
1168
1169 static HRESULT WINAPI contentHandler_processingInstruction(
1170         ISAXContentHandler* iface,
1171         const WCHAR *target, int target_len,
1172         const WCHAR *data, int data_len)
1173 {
1174     struct call_entry call;
1175
1176     init_call_entry(locator, &call);
1177     call.id = CH_PROCESSINGINSTRUCTION;
1178     call.arg1W = SysAllocStringLen(target, target_len);
1179     call.arg2W = SysAllocStringLen(data, data_len);
1180     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1181
1182     return get_expected_ret();
1183 }
1184
1185 static HRESULT WINAPI contentHandler_skippedEntity(
1186         ISAXContentHandler* iface,
1187         const WCHAR *name, int len)
1188 {
1189     struct call_entry call;
1190
1191     init_call_entry(locator, &call);
1192     call.id = CH_SKIPPEDENTITY;
1193     call.arg1W = SysAllocStringLen(name, len);
1194
1195     return get_expected_ret();
1196 }
1197
1198 static const ISAXContentHandlerVtbl contentHandlerVtbl =
1199 {
1200     contentHandler_QueryInterface,
1201     contentHandler_AddRef,
1202     contentHandler_Release,
1203     contentHandler_putDocumentLocator,
1204     contentHandler_startDocument,
1205     contentHandler_endDocument,
1206     contentHandler_startPrefixMapping,
1207     contentHandler_endPrefixMapping,
1208     contentHandler_startElement,
1209     contentHandler_endElement,
1210     contentHandler_characters,
1211     contentHandler_ignorableWhitespace,
1212     contentHandler_processingInstruction,
1213     contentHandler_skippedEntity
1214 };
1215
1216 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
1217
1218 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
1219         ISAXErrorHandler* iface,
1220         REFIID riid,
1221         void **ppvObject)
1222 {
1223     *ppvObject = NULL;
1224
1225     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
1226     {
1227         *ppvObject = iface;
1228     }
1229     else
1230     {
1231         return E_NOINTERFACE;
1232     }
1233
1234     return S_OK;
1235 }
1236
1237 static ULONG WINAPI isaxerrorHandler_AddRef(
1238         ISAXErrorHandler* iface)
1239 {
1240     return 2;
1241 }
1242
1243 static ULONG WINAPI isaxerrorHandler_Release(
1244         ISAXErrorHandler* iface)
1245 {
1246     return 1;
1247 }
1248
1249 static HRESULT WINAPI isaxerrorHandler_error(
1250         ISAXErrorHandler* iface,
1251         ISAXLocator *pLocator,
1252         const WCHAR *pErrorMessage,
1253         HRESULT hrErrorCode)
1254 {
1255     ok(0, "unexpected call\n");
1256     return S_OK;
1257 }
1258
1259 static HRESULT WINAPI isaxerrorHandler_fatalError(
1260         ISAXErrorHandler* iface,
1261         ISAXLocator *pLocator,
1262         const WCHAR *message,
1263         HRESULT hr)
1264 {
1265     struct call_entry call;
1266
1267     init_call_entry(locator, &call);
1268     call.id  = EH_FATALERROR;
1269     call.ret = hr;
1270
1271     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1272
1273     get_expected_ret();
1274     return S_OK;
1275 }
1276
1277 static HRESULT WINAPI isaxerrorHanddler_ignorableWarning(
1278         ISAXErrorHandler* iface,
1279         ISAXLocator *pLocator,
1280         const WCHAR *pErrorMessage,
1281         HRESULT hrErrorCode)
1282 {
1283     ok(0, "unexpected call\n");
1284     return S_OK;
1285 }
1286
1287 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
1288 {
1289     isaxerrorHandler_QueryInterface,
1290     isaxerrorHandler_AddRef,
1291     isaxerrorHandler_Release,
1292     isaxerrorHandler_error,
1293     isaxerrorHandler_fatalError,
1294     isaxerrorHanddler_ignorableWarning
1295 };
1296
1297 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
1298
1299 static HRESULT WINAPI isaxattributes_QueryInterface(
1300         ISAXAttributes* iface,
1301         REFIID riid,
1302         void **ppvObject)
1303 {
1304     *ppvObject = NULL;
1305
1306     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
1307     {
1308         *ppvObject = iface;
1309     }
1310     else
1311     {
1312         return E_NOINTERFACE;
1313     }
1314
1315     return S_OK;
1316 }
1317
1318 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
1319 {
1320     return 2;
1321 }
1322
1323 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
1324 {
1325     return 1;
1326 }
1327
1328 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
1329 {
1330     *length = 3;
1331     return S_OK;
1332 }
1333
1334 static HRESULT WINAPI isaxattributes_getURI(
1335     ISAXAttributes* iface,
1336     int nIndex,
1337     const WCHAR **pUrl,
1338     int *pUriSize)
1339 {
1340     ok(0, "unexpected call\n");
1341     return E_NOTIMPL;
1342 }
1343
1344 static HRESULT WINAPI isaxattributes_getLocalName(
1345     ISAXAttributes* iface,
1346     int nIndex,
1347     const WCHAR **pLocalName,
1348     int *pLocalNameLength)
1349 {
1350     ok(0, "unexpected call\n");
1351     return E_NOTIMPL;
1352 }
1353
1354 static HRESULT WINAPI isaxattributes_getQName(
1355     ISAXAttributes* iface,
1356     int index,
1357     const WCHAR **QName,
1358     int *QNameLength)
1359 {
1360     static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
1361                                             {'a','t','t','r','2','j','u','n','k',0},
1362                                             {'a','t','t','r','3',0}};
1363     static const int attrqnamelen[] = {7, 5, 5};
1364
1365     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1366
1367     *QName = attrqnamesW[index];
1368     *QNameLength = attrqnamelen[index];
1369
1370     return S_OK;
1371 }
1372
1373 static HRESULT WINAPI isaxattributes_getName(
1374     ISAXAttributes* iface,
1375     int nIndex,
1376     const WCHAR **pUri,
1377     int * pUriLength,
1378     const WCHAR ** pLocalName,
1379     int * pLocalNameSize,
1380     const WCHAR ** pQName,
1381     int * pQNameLength)
1382 {
1383     ok(0, "unexpected call\n");
1384     return E_NOTIMPL;
1385 }
1386
1387 static HRESULT WINAPI isaxattributes_getIndexFromName(
1388     ISAXAttributes* iface,
1389     const WCHAR * pUri,
1390     int cUriLength,
1391     const WCHAR * pLocalName,
1392     int cocalNameLength,
1393     int * index)
1394 {
1395     ok(0, "unexpected call\n");
1396     return E_NOTIMPL;
1397 }
1398
1399 static HRESULT WINAPI isaxattributes_getIndexFromQName(
1400     ISAXAttributes* iface,
1401     const WCHAR * pQName,
1402     int nQNameLength,
1403     int * index)
1404 {
1405     ok(0, "unexpected call\n");
1406     return E_NOTIMPL;
1407 }
1408
1409 static HRESULT WINAPI isaxattributes_getType(
1410     ISAXAttributes* iface,
1411     int nIndex,
1412     const WCHAR ** pType,
1413     int * pTypeLength)
1414 {
1415     ok(0, "unexpected call\n");
1416     return E_NOTIMPL;
1417 }
1418
1419 static HRESULT WINAPI isaxattributes_getTypeFromName(
1420     ISAXAttributes* iface,
1421     const WCHAR * pUri,
1422     int nUri,
1423     const WCHAR * pLocalName,
1424     int nLocalName,
1425     const WCHAR ** pType,
1426     int * nType)
1427 {
1428     ok(0, "unexpected call\n");
1429     return E_NOTIMPL;
1430 }
1431
1432 static HRESULT WINAPI isaxattributes_getTypeFromQName(
1433     ISAXAttributes* iface,
1434     const WCHAR * pQName,
1435     int nQName,
1436     const WCHAR ** pType,
1437     int * nType)
1438 {
1439     ok(0, "unexpected call\n");
1440     return E_NOTIMPL;
1441 }
1442
1443 static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
1444     const WCHAR **value, int *nValue)
1445 {
1446     static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
1447                                             {'a','2','j','u','n','k',0},
1448                                             {'<','&','"','>',0}};
1449     static const int attrvalueslen[] = {2, 2, 4};
1450
1451     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1452
1453     *value = attrvaluesW[index];
1454     *nValue = attrvalueslen[index];
1455
1456     return S_OK;
1457 }
1458
1459 static HRESULT WINAPI isaxattributes_getValueFromName(
1460     ISAXAttributes* iface,
1461     const WCHAR * pUri,
1462     int nUri,
1463     const WCHAR * pLocalName,
1464     int nLocalName,
1465     const WCHAR ** pValue,
1466     int * nValue)
1467 {
1468     ok(0, "unexpected call\n");
1469     return E_NOTIMPL;
1470 }
1471
1472 static HRESULT WINAPI isaxattributes_getValueFromQName(
1473     ISAXAttributes* iface,
1474     const WCHAR * pQName,
1475     int nQName,
1476     const WCHAR ** pValue,
1477     int * nValue)
1478 {
1479     ok(0, "unexpected call\n");
1480     return E_NOTIMPL;
1481 }
1482
1483 static const ISAXAttributesVtbl SAXAttributesVtbl =
1484 {
1485     isaxattributes_QueryInterface,
1486     isaxattributes_AddRef,
1487     isaxattributes_Release,
1488     isaxattributes_getLength,
1489     isaxattributes_getURI,
1490     isaxattributes_getLocalName,
1491     isaxattributes_getQName,
1492     isaxattributes_getName,
1493     isaxattributes_getIndexFromName,
1494     isaxattributes_getIndexFromQName,
1495     isaxattributes_getType,
1496     isaxattributes_getTypeFromName,
1497     isaxattributes_getTypeFromQName,
1498     isaxattributes_getValue,
1499     isaxattributes_getValueFromName,
1500     isaxattributes_getValueFromQName
1501 };
1502
1503 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
1504
1505 struct saxlexicalhandler
1506 {
1507     ISAXLexicalHandler ISAXLexicalHandler_iface;
1508     LONG ref;
1509
1510     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1511 };
1512
1513 static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface )
1514 {
1515     return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
1516 }
1517
1518 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
1519 {
1520     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1521
1522     *out = NULL;
1523
1524     if (IsEqualGUID(riid, &IID_IUnknown))
1525     {
1526         *out = iface;
1527         ok(0, "got unexpected IID_IUnknown query\n");
1528     }
1529     else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
1530     {
1531         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1532         *out = iface;
1533     }
1534
1535     if (*out)
1536         ISAXLexicalHandler_AddRef(iface);
1537     else
1538         return E_NOINTERFACE;
1539
1540     return S_OK;
1541 }
1542
1543 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
1544 {
1545     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1546     return InterlockedIncrement(&handler->ref);
1547 }
1548
1549 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
1550 {
1551     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1552     return InterlockedDecrement(&handler->ref);
1553 }
1554
1555 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
1556     const WCHAR * pName, int nName, const WCHAR * pPublicId,
1557     int nPublicId, const WCHAR * pSystemId, int nSystemId)
1558 {
1559     ok(0, "call not expected\n");
1560     return E_NOTIMPL;
1561 }
1562
1563 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
1564 {
1565     ok(0, "call not expected\n");
1566     return E_NOTIMPL;
1567 }
1568
1569 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
1570     const WCHAR * pName, int nName)
1571 {
1572     ok(0, "call not expected\n");
1573     return E_NOTIMPL;
1574 }
1575
1576 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
1577     const WCHAR * pName, int nName)
1578 {
1579     ok(0, "call not expected\n");
1580     return E_NOTIMPL;
1581 }
1582
1583 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
1584 {
1585     ok(0, "call not expected\n");
1586     return E_NOTIMPL;
1587 }
1588
1589 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
1590 {
1591     ok(0, "call not expected\n");
1592     return E_NOTIMPL;
1593 }
1594
1595 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
1596     const WCHAR * pChars, int nChars)
1597 {
1598     ok(0, "call not expected\n");
1599     return E_NOTIMPL;
1600 }
1601
1602 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1603 {
1604    isaxlexical_QueryInterface,
1605    isaxlexical_AddRef,
1606    isaxlexical_Release,
1607    isaxlexical_startDTD,
1608    isaxlexical_endDTD,
1609    isaxlexical_startEntity,
1610    isaxlexical_endEntity,
1611    isaxlexical_startCDATA,
1612    isaxlexical_endCDATA,
1613    isaxlexical_comment
1614 };
1615
1616 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
1617 {
1618     handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1619     handler->ref = 1;
1620     handler->qi_hr = hr;
1621 }
1622
1623 struct saxdeclhandler
1624 {
1625     ISAXDeclHandler ISAXDeclHandler_iface;
1626     LONG ref;
1627
1628     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1629 };
1630
1631 static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface )
1632 {
1633     return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface);
1634 }
1635
1636 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out)
1637 {
1638     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1639
1640     *out = NULL;
1641
1642     if (IsEqualGUID(riid, &IID_IUnknown))
1643     {
1644         *out = iface;
1645         ok(0, "got unexpected IID_IUnknown query\n");
1646     }
1647     else if (IsEqualGUID(riid, &IID_ISAXDeclHandler))
1648     {
1649         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1650         *out = iface;
1651     }
1652
1653     if (*out)
1654         ISAXDeclHandler_AddRef(iface);
1655     else
1656         return E_NOINTERFACE;
1657
1658     return S_OK;
1659 }
1660
1661 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
1662 {
1663     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1664     return InterlockedIncrement(&handler->ref);
1665 }
1666
1667 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
1668 {
1669     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1670     return InterlockedDecrement(&handler->ref);
1671 }
1672
1673 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
1674     const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
1675 {
1676     ok(0, "call not expected\n");
1677     return E_NOTIMPL;
1678 }
1679
1680 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
1681     const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
1682     int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
1683     int nValueDefault, const WCHAR * pValue, int nValue)
1684 {
1685     ok(0, "call not expected\n");
1686     return E_NOTIMPL;
1687 }
1688
1689 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
1690     const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
1691 {
1692     ok(0, "call not expected\n");
1693     return E_NOTIMPL;
1694 }
1695
1696 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
1697     const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
1698     const WCHAR * pSystemId, int nSystemId)
1699 {
1700     ok(0, "call not expected\n");
1701     return E_NOTIMPL;
1702 }
1703
1704 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
1705 {
1706    isaxdecl_QueryInterface,
1707    isaxdecl_AddRef,
1708    isaxdecl_Release,
1709    isaxdecl_elementDecl,
1710    isaxdecl_attributeDecl,
1711    isaxdecl_internalEntityDecl,
1712    isaxdecl_externalEntityDecl
1713 };
1714
1715 static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr)
1716 {
1717     handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1718     handler->ref = 1;
1719     handler->qi_hr = hr;
1720 }
1721
1722 typedef struct mxwriter_write_test_t {
1723     BOOL        last;
1724     const BYTE  *data;
1725     DWORD       cb;
1726     BOOL        null_written;
1727     BOOL        fail_write;
1728 } mxwriter_write_test;
1729
1730 typedef struct mxwriter_stream_test_t {
1731     VARIANT_BOOL        bom;
1732     const char          *encoding;
1733     mxwriter_write_test expected_writes[4];
1734 } mxwriter_stream_test;
1735
1736 static const mxwriter_write_test *current_write_test;
1737 static DWORD current_stream_test_index;
1738
1739 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
1740 {
1741     *ppvObject = NULL;
1742
1743     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
1744         *ppvObject = iface;
1745     else
1746         return E_NOINTERFACE;
1747
1748     return S_OK;
1749 }
1750
1751 static ULONG WINAPI istream_AddRef(IStream *iface)
1752 {
1753     return 2;
1754 }
1755
1756 static ULONG WINAPI istream_Release(IStream *iface)
1757 {
1758     return 1;
1759 }
1760
1761 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
1762 {
1763     ok(0, "unexpected call\n");
1764     return E_NOTIMPL;
1765 }
1766
1767 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1768 {
1769     BOOL fail = FALSE;
1770
1771     ok(pv != NULL, "pv == NULL\n");
1772
1773     if(current_write_test->last) {
1774         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
1775         return E_FAIL;
1776     }
1777
1778     fail = current_write_test->fail_write;
1779
1780     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
1781         current_write_test->cb, cb, current_stream_test_index);
1782
1783     if(!pcbWritten)
1784         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
1785     else
1786         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
1787
1788     ++current_write_test;
1789
1790     if(pcbWritten)
1791         *pcbWritten = cb;
1792
1793     return fail ? E_FAIL : S_OK;
1794 }
1795
1796 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
1797         ULARGE_INTEGER *plibNewPosition)
1798 {
1799     ok(0, "unexpected call\n");
1800     return E_NOTIMPL;
1801 }
1802
1803 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
1804 {
1805     ok(0, "unexpected call\n");
1806     return E_NOTIMPL;
1807 }
1808
1809 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
1810         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
1811 {
1812     ok(0, "unexpected call\n");
1813     return E_NOTIMPL;
1814 }
1815
1816 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
1817 {
1818     ok(0, "unexpected call\n");
1819     return E_NOTIMPL;
1820 }
1821
1822 static HRESULT WINAPI istream_Revert(IStream *iface)
1823 {
1824     ok(0, "unexpected call\n");
1825     return E_NOTIMPL;
1826 }
1827
1828 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1829         ULARGE_INTEGER cb, DWORD dwLockType)
1830 {
1831     ok(0, "unexpected call\n");
1832     return E_NOTIMPL;
1833 }
1834
1835 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1836         ULARGE_INTEGER cb, DWORD dwLockType)
1837 {
1838     ok(0, "unexpected call\n");
1839     return E_NOTIMPL;
1840 }
1841
1842 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
1843 {
1844     ok(0, "unexpected call\n");
1845     return E_NOTIMPL;
1846 }
1847
1848 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
1849 {
1850     ok(0, "unexpected call\n");
1851     return E_NOTIMPL;
1852 }
1853
1854 static const IStreamVtbl StreamVtbl = {
1855     istream_QueryInterface,
1856     istream_AddRef,
1857     istream_Release,
1858     istream_Read,
1859     istream_Write,
1860     istream_Seek,
1861     istream_SetSize,
1862     istream_CopyTo,
1863     istream_Commit,
1864     istream_Revert,
1865     istream_LockRegion,
1866     istream_UnlockRegion,
1867     istream_Stat,
1868     istream_Clone
1869 };
1870
1871 static IStream mxstream = { &StreamVtbl };
1872
1873 static struct msxmlsupported_data_t reader_support_data[] =
1874 {
1875     { &CLSID_SAXXMLReader,   "SAXReader"   },
1876     { &CLSID_SAXXMLReader30, "SAXReader30" },
1877     { &CLSID_SAXXMLReader40, "SAXReader40" },
1878     { &CLSID_SAXXMLReader60, "SAXReader60" },
1879     { NULL }
1880 };
1881
1882 static void test_saxreader(void)
1883 {
1884     const struct msxmlsupported_data_t *table = reader_support_data;
1885     HRESULT hr;
1886     ISAXXMLReader *reader = NULL;
1887     VARIANT var;
1888     ISAXContentHandler *content;
1889     ISAXErrorHandler *lpErrorHandler;
1890     SAFEARRAY *sa;
1891     SAFEARRAYBOUND SADim[1];
1892     char *ptr = NULL;
1893     IStream *stream;
1894     ULARGE_INTEGER size;
1895     LARGE_INTEGER pos;
1896     ULONG written;
1897     HANDLE file;
1898     static const CHAR testXmlA[] = "test.xml";
1899     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1900     IXMLDOMDocument *doc;
1901     VARIANT_BOOL v;
1902
1903     while (table->clsid)
1904     {
1905         struct call_entry *test_seq;
1906         BSTR str;
1907
1908         if (!is_clsid_supported(table->clsid, reader_support_data))
1909         {
1910             table++;
1911             continue;
1912         }
1913
1914         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1915         EXPECT_HR(hr, S_OK);
1916         g_reader = reader;
1917
1918         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
1919             msxml_version = 4;
1920         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1921             msxml_version = 6;
1922         else
1923             msxml_version = 0;
1924
1925         /* crashes on old versions */
1926         if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
1927             !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1928         {
1929             hr = ISAXXMLReader_getContentHandler(reader, NULL);
1930             EXPECT_HR(hr, E_POINTER);
1931
1932             hr = ISAXXMLReader_getErrorHandler(reader, NULL);
1933             EXPECT_HR(hr, E_POINTER);
1934         }
1935
1936         hr = ISAXXMLReader_getContentHandler(reader, &content);
1937         EXPECT_HR(hr, S_OK);
1938         ok(content == NULL, "Expected %p, got %p\n", NULL, content);
1939
1940         hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
1941         EXPECT_HR(hr, S_OK);
1942         ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
1943
1944         hr = ISAXXMLReader_putContentHandler(reader, NULL);
1945         EXPECT_HR(hr, S_OK);
1946
1947         hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
1948         EXPECT_HR(hr, S_OK);
1949
1950         hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
1951         EXPECT_HR(hr, S_OK);
1952
1953         hr = ISAXXMLReader_getContentHandler(reader, &content);
1954         EXPECT_HR(hr, S_OK);
1955         ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
1956
1957         V_VT(&var) = VT_BSTR;
1958         V_BSTR(&var) = SysAllocString(szSimpleXML);
1959
1960         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
1961             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1962             test_seq = content_handler_test1_alternate;
1963         else
1964             test_seq = content_handler_test1;
1965         set_expected_seq(test_seq);
1966         hr = ISAXXMLReader_parse(reader, var);
1967         EXPECT_HR(hr, S_OK);
1968         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
1969
1970         VariantClear(&var);
1971
1972         SADim[0].lLbound = 0;
1973         SADim[0].cElements = sizeof(testXML)-1;
1974         sa = SafeArrayCreate(VT_UI1, 1, SADim);
1975         SafeArrayAccessData(sa, (void**)&ptr);
1976         memcpy(ptr, testXML, sizeof(testXML)-1);
1977         SafeArrayUnaccessData(sa);
1978         V_VT(&var) = VT_ARRAY|VT_UI1;
1979         V_ARRAY(&var) = sa;
1980
1981         set_expected_seq(test_seq);
1982         hr = ISAXXMLReader_parse(reader, var);
1983         EXPECT_HR(hr, S_OK);
1984         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
1985
1986         SafeArrayDestroy(sa);
1987
1988         CreateStreamOnHGlobal(NULL, TRUE, &stream);
1989         size.QuadPart = strlen(testXML);
1990         IStream_SetSize(stream, size);
1991         IStream_Write(stream, testXML, strlen(testXML), &written);
1992         pos.QuadPart = 0;
1993         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
1994         V_VT(&var) = VT_UNKNOWN;
1995         V_UNKNOWN(&var) = (IUnknown*)stream;
1996
1997         set_expected_seq(test_seq);
1998         hr = ISAXXMLReader_parse(reader, var);
1999         EXPECT_HR(hr, S_OK);
2000         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
2001
2002         IStream_Release(stream);
2003
2004         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2005         size.QuadPart = strlen(test_attributes);
2006         IStream_SetSize(stream, size);
2007         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2008         pos.QuadPart = 0;
2009         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2010         V_VT(&var) = VT_UNKNOWN;
2011         V_UNKNOWN(&var) = (IUnknown*)stream;
2012
2013         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2014             test_seq = content_handler_test_attributes_alternate_4;
2015         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2016             test_seq = content_handler_test_attributes_alternate_6;
2017         else
2018             test_seq = content_handler_test_attributes;
2019
2020         set_expected_seq(test_seq);
2021         hr = ISAXXMLReader_parse(reader, var);
2022         EXPECT_HR(hr, S_OK);
2023
2024         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2025             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2026             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2027         else
2028             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2029
2030         IStream_Release(stream);
2031
2032         V_VT(&var) = VT_BSTR;
2033         V_BSTR(&var) = SysAllocString(carriage_ret_test);
2034
2035         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2036             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2037             test_seq = content_handler_test2_alternate;
2038         else
2039             test_seq = content_handler_test2;
2040
2041         set_expected_seq(test_seq);
2042         hr = ISAXXMLReader_parse(reader, var);
2043         EXPECT_HR(hr, S_OK);
2044         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
2045
2046         VariantClear(&var);
2047
2048         /* from file url */
2049         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2050         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2051         WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
2052         CloseHandle(file);
2053
2054         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2055             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2056             test_seq = content_handler_test1_alternate;
2057         else
2058             test_seq = content_handler_test1;
2059         set_expected_seq(test_seq);
2060         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2061         EXPECT_HR(hr, S_OK);
2062         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
2063
2064         /* error handler */
2065         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2066             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2067             test_seq = content_handler_testerror_alternate;
2068         else
2069             test_seq = content_handler_testerror;
2070         set_expected_seq(test_seq);
2071         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2072         EXPECT_HR(hr, E_FAIL);
2073         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
2074
2075         /* callback ret values */
2076         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2077             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2078         {
2079             test_seq = content_handler_test_callback_rets_alt;
2080             set_expected_seq(test_seq);
2081             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2082             EXPECT_HR(hr, S_OK);
2083         }
2084         else
2085         {
2086             test_seq = content_handler_test_callback_rets;
2087             set_expected_seq(test_seq);
2088             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2089             EXPECT_HR(hr, S_FALSE);
2090         }
2091         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
2092
2093         DeleteFileA(testXmlA);
2094
2095         /* parse from IXMLDOMDocument */
2096         hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
2097                 &IID_IXMLDOMDocument, (void**)&doc);
2098         EXPECT_HR(hr, S_OK);
2099
2100         str = SysAllocString(szSimpleXML);
2101         hr = IXMLDOMDocument_loadXML(doc, str, &v);
2102         EXPECT_HR(hr, S_OK);
2103         SysFreeString(str);
2104
2105         V_VT(&var) = VT_UNKNOWN;
2106         V_UNKNOWN(&var) = (IUnknown*)doc;
2107
2108         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2109             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2110             test_seq = content_handler_test2_alternate;
2111         else
2112             test_seq = content_handler_test2;
2113
2114         set_expected_seq(test_seq);
2115         hr = ISAXXMLReader_parse(reader, var);
2116         EXPECT_HR(hr, S_OK);
2117         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
2118         IXMLDOMDocument_Release(doc);
2119
2120         /* xml:space test */
2121         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2122             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2123         {
2124             test_seq = xmlspaceattr_test_alternate;
2125         }
2126         else
2127             test_seq = xmlspaceattr_test;
2128
2129         set_expected_seq(test_seq);
2130         V_VT(&var) = VT_BSTR;
2131         V_BSTR(&var) = _bstr_(xmlspace_attr);
2132         hr = ISAXXMLReader_parse(reader, var);
2133         EXPECT_HR(hr, S_OK);
2134
2135         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2136             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2137         {
2138             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
2139         }
2140         else
2141             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
2142
2143         /* switch off 'namespaces' feature */
2144         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
2145         EXPECT_HR(hr, S_OK);
2146
2147         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2148         size.QuadPart = strlen(test_attributes);
2149         IStream_SetSize(stream, size);
2150         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2151         pos.QuadPart = 0;
2152         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2153         V_VT(&var) = VT_UNKNOWN;
2154         V_UNKNOWN(&var) = (IUnknown*)stream;
2155
2156         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2157             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2158         {
2159             test_seq = content_handler_test_attributes_alt_no_ns;
2160         }
2161         else
2162             test_seq = content_handler_test_attributes;
2163
2164         set_expected_seq(test_seq);
2165         hr = ISAXXMLReader_parse(reader, var);
2166         EXPECT_HR(hr, S_OK);
2167         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2168         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
2169         EXPECT_HR(hr, S_OK);
2170
2171         /* switch off 'namespace-prefixes' feature */
2172         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
2173         EXPECT_HR(hr, S_OK);
2174
2175         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2176         size.QuadPart = strlen(test_attributes);
2177         IStream_SetSize(stream, size);
2178         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2179         pos.QuadPart = 0;
2180         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2181         V_VT(&var) = VT_UNKNOWN;
2182         V_UNKNOWN(&var) = (IUnknown*)stream;
2183
2184         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2185             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2186         {
2187             test_seq = content_handler_test_attributes_alt_no_prefix;
2188         }
2189         else
2190             test_seq = content_handler_test_attributes_no_prefix;
2191
2192         set_expected_seq(test_seq);
2193         hr = ISAXXMLReader_parse(reader, var);
2194         EXPECT_HR(hr, S_OK);
2195         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2196
2197         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
2198         EXPECT_HR(hr, S_OK);
2199
2200         /* attribute normalization */
2201         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2202         size.QuadPart = strlen(attribute_normalize);
2203         IStream_SetSize(stream, size);
2204         IStream_Write(stream, attribute_normalize, strlen(attribute_normalize), &written);
2205         pos.QuadPart = 0;
2206         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2207         V_VT(&var) = VT_UNKNOWN;
2208         V_UNKNOWN(&var) = (IUnknown*)stream;
2209
2210         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2211             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2212         {
2213             test_seq = attribute_norm_alt;
2214         }
2215         else
2216             test_seq = attribute_norm;
2217
2218         set_expected_seq(test_seq);
2219         hr = ISAXXMLReader_parse(reader, var);
2220         EXPECT_HR(hr, S_OK);
2221         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
2222
2223         ISAXXMLReader_Release(reader);
2224         table++;
2225     }
2226
2227     free_bstrs();
2228 }
2229
2230 struct saxreader_props_test_t
2231 {
2232     const char *prop_name;
2233     IUnknown   *iface;
2234 };
2235
2236 static struct saxlexicalhandler lexicalhandler;
2237 static struct saxdeclhandler declhandler;
2238
2239 static const struct saxreader_props_test_t props_test_data[] = {
2240     { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface },
2241     { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface },
2242     { 0 }
2243 };
2244
2245 static void test_saxreader_properties(void)
2246 {
2247     const struct saxreader_props_test_t *ptr = props_test_data;
2248     ISAXXMLReader *reader;
2249     HRESULT hr;
2250
2251     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2252             &IID_ISAXXMLReader, (void**)&reader);
2253     EXPECT_HR(hr, S_OK);
2254
2255     hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
2256     EXPECT_HR(hr, E_POINTER);
2257
2258     while (ptr->prop_name)
2259     {
2260         VARIANT v;
2261         LONG ref;
2262
2263         init_saxlexicalhandler(&lexicalhandler, S_OK);
2264         init_saxdeclhandler(&declhandler, S_OK);
2265
2266         V_VT(&v) = VT_EMPTY;
2267         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2268         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2269         EXPECT_HR(hr, S_OK);
2270         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2271         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2272
2273         V_VT(&v) = VT_UNKNOWN;
2274         V_UNKNOWN(&v) = ptr->iface;
2275         ref = get_refcount(ptr->iface);
2276         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2277         EXPECT_HR(hr, S_OK);
2278         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2279
2280         V_VT(&v) = VT_EMPTY;
2281         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2282
2283         ref = get_refcount(ptr->iface);
2284         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2285         EXPECT_HR(hr, S_OK);
2286         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2287         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2288         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2289         VariantClear(&v);
2290
2291         V_VT(&v) = VT_EMPTY;
2292         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2293         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2294         EXPECT_HR(hr, S_OK);
2295
2296         V_VT(&v) = VT_EMPTY;
2297         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2298         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2299         EXPECT_HR(hr, S_OK);
2300         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2301         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2302
2303         V_VT(&v) = VT_UNKNOWN;
2304         V_UNKNOWN(&v) = ptr->iface;
2305         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2306         EXPECT_HR(hr, S_OK);
2307
2308         /* only VT_EMPTY seems to be valid to reset property */
2309         V_VT(&v) = VT_I4;
2310         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2311         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2312         EXPECT_HR(hr, E_INVALIDARG);
2313
2314         V_VT(&v) = VT_EMPTY;
2315         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2316         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2317         EXPECT_HR(hr, S_OK);
2318         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2319         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2320         VariantClear(&v);
2321
2322         V_VT(&v) = VT_UNKNOWN;
2323         V_UNKNOWN(&v) = NULL;
2324         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2325         EXPECT_HR(hr, S_OK);
2326
2327         V_VT(&v) = VT_EMPTY;
2328         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2329         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2330         EXPECT_HR(hr, S_OK);
2331         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2332         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2333
2334         /* block QueryInterface on handler riid */
2335         init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE);
2336         init_saxdeclhandler(&declhandler, E_NOINTERFACE);
2337
2338         V_VT(&v) = VT_UNKNOWN;
2339         V_UNKNOWN(&v) = ptr->iface;
2340         EXPECT_REF(ptr->iface, 1);
2341         ref = get_refcount(ptr->iface);
2342         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2343         EXPECT_HR(hr, E_NOINTERFACE);
2344         EXPECT_REF(ptr->iface, 1);
2345
2346         ptr++;
2347     }
2348
2349     ISAXXMLReader_Release(reader);
2350     free_bstrs();
2351 }
2352
2353 struct feature_ns_entry_t {
2354     const GUID *guid;
2355     const char *clsid;
2356     VARIANT_BOOL value;
2357     VARIANT_BOOL value2; /* feature value after feature set to 0xc */
2358 };
2359
2360 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
2361     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   VARIANT_TRUE, VARIANT_FALSE },
2362     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE },
2363     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE },
2364     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE },
2365     { 0 }
2366 };
2367
2368 static const char *feature_names[] = {
2369     "http://xml.org/sax/features/namespaces",
2370     "http://xml.org/sax/features/namespace-prefixes",
2371     0
2372 };
2373
2374 static void test_saxreader_features(void)
2375 {
2376     const struct feature_ns_entry_t *entry = feature_ns_entry_data;
2377     ISAXXMLReader *reader;
2378
2379     while (entry->guid)
2380     {
2381         VARIANT_BOOL value;
2382         const char **name;
2383         HRESULT hr;
2384
2385         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2386         if (hr != S_OK)
2387         {
2388             win_skip("can't create %s instance\n", entry->clsid);
2389             entry++;
2390             continue;
2391         }
2392
2393         name = feature_names;
2394         while (*name)
2395         {
2396             value = 0xc;
2397             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2398             EXPECT_HR(hr, S_OK);
2399             ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
2400
2401             value = 0xc;
2402             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value);
2403             EXPECT_HR(hr, S_OK);
2404
2405             value = 0xd;
2406             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2407             EXPECT_HR(hr, S_OK);
2408             ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2);
2409
2410             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE);
2411             EXPECT_HR(hr, S_OK);
2412             value = 0xd;
2413             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2414             EXPECT_HR(hr, S_OK);
2415             ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value);
2416
2417             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE);
2418             EXPECT_HR(hr, S_OK);
2419             value = 0xd;
2420             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2421             EXPECT_HR(hr, S_OK);
2422             ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value);
2423
2424             name++;
2425         }
2426
2427         ISAXXMLReader_Release(reader);
2428
2429         entry++;
2430     }
2431 }
2432
2433 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
2434 static const CHAR UTF8BOMTest[] =
2435 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
2436 "<a></a>\n";
2437
2438 struct enc_test_entry_t {
2439     const GUID *guid;
2440     const char *clsid;
2441     const char *data;
2442     HRESULT hr;
2443     int todo;
2444 };
2445
2446 static const struct enc_test_entry_t encoding_test_data[] = {
2447     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
2448     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
2449     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
2450     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
2451     { 0 }
2452 };
2453
2454 static void test_saxreader_encoding(void)
2455 {
2456     const struct enc_test_entry_t *entry = encoding_test_data;
2457     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2458     static const CHAR testXmlA[] = "test.xml";
2459
2460     while (entry->guid)
2461     {
2462         ISAXXMLReader *reader;
2463         VARIANT input;
2464         DWORD written;
2465         HANDLE file;
2466         HRESULT hr;
2467
2468         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2469         if (hr != S_OK)
2470         {
2471             win_skip("can't create %s instance\n", entry->clsid);
2472             entry++;
2473             continue;
2474         }
2475
2476         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2477         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2478         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
2479         CloseHandle(file);
2480
2481         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2482         if (entry->todo)
2483             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2484         else
2485             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2486
2487         DeleteFileA(testXmlA);
2488
2489         /* try BSTR input with no BOM or '<?xml' instruction */
2490         V_VT(&input) = VT_BSTR;
2491         V_BSTR(&input) = _bstr_("<element></element>");
2492         hr = ISAXXMLReader_parse(reader, input);
2493         EXPECT_HR(hr, S_OK);
2494
2495         ISAXXMLReader_Release(reader);
2496
2497         free_bstrs();
2498         entry++;
2499     }
2500 }
2501
2502 static void test_mxwriter_handlers(void)
2503 {
2504     ISAXContentHandler *handler;
2505     IMXWriter *writer, *writer2;
2506     ISAXDeclHandler *decl;
2507     ISAXLexicalHandler *lh;
2508     HRESULT hr;
2509
2510     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2511             &IID_IMXWriter, (void**)&writer);
2512     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2513
2514     EXPECT_REF(writer, 1);
2515
2516     /* ISAXContentHandler */
2517     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
2518     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2519     EXPECT_REF(writer, 2);
2520     EXPECT_REF(handler, 2);
2521
2522     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
2523     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2524     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2525     EXPECT_REF(writer, 3);
2526     EXPECT_REF(writer2, 3);
2527     IMXWriter_Release(writer2);
2528     ISAXContentHandler_Release(handler);
2529
2530     /* ISAXLexicalHandler */
2531     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lh);
2532     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2533     EXPECT_REF(writer, 2);
2534     EXPECT_REF(lh, 2);
2535
2536     hr = ISAXLexicalHandler_QueryInterface(lh, &IID_IMXWriter, (void**)&writer2);
2537     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2538     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2539     EXPECT_REF(writer, 3);
2540     EXPECT_REF(writer2, 3);
2541     IMXWriter_Release(writer2);
2542     ISAXLexicalHandler_Release(lh);
2543
2544     /* ISAXDeclHandler */
2545     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
2546     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2547     EXPECT_REF(writer, 2);
2548     EXPECT_REF(lh, 2);
2549
2550     hr = ISAXDeclHandler_QueryInterface(decl, &IID_IMXWriter, (void**)&writer2);
2551     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2552     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2553     EXPECT_REF(writer, 3);
2554     EXPECT_REF(writer2, 3);
2555     IMXWriter_Release(writer2);
2556     ISAXDeclHandler_Release(decl);
2557
2558     IMXWriter_Release(writer);
2559 }
2560
2561
2562 static struct msxmlsupported_data_t mxwriter_support_data[] =
2563 {
2564     { &CLSID_MXXMLWriter,   "MXXMLWriter"   },
2565     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
2566     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
2567     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
2568     { NULL }
2569 };
2570
2571 static struct msxmlsupported_data_t mxattributes_support_data[] =
2572 {
2573     { &CLSID_SAXAttributes,   "SAXAttributes"   },
2574     { &CLSID_SAXAttributes30, "SAXAttributes30" },
2575     { &CLSID_SAXAttributes40, "SAXAttributes40" },
2576     { &CLSID_SAXAttributes60, "SAXAttributes60" },
2577     { NULL }
2578 };
2579
2580 struct mxwriter_props_t
2581 {
2582     const GUID *clsid;
2583     VARIANT_BOOL bom;
2584     VARIANT_BOOL disable_escape;
2585     VARIANT_BOOL indent;
2586     VARIANT_BOOL omitdecl;
2587     VARIANT_BOOL standalone;
2588     const char *encoding;
2589 };
2590
2591 static const struct mxwriter_props_t mxwriter_default_props[] =
2592 {
2593     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2594     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2595     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2596     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2597     { NULL }
2598 };
2599
2600 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
2601 {
2602     int i = 0;
2603
2604     while (table->clsid)
2605     {
2606         IMXWriter *writer;
2607         VARIANT_BOOL b;
2608         BSTR encoding;
2609         HRESULT hr;
2610
2611         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
2612         {
2613             table++;
2614             i++;
2615             continue;
2616         }
2617
2618         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
2619             &IID_IMXWriter, (void**)&writer);
2620         EXPECT_HR(hr, S_OK);
2621
2622         b = !table->bom;
2623         hr = IMXWriter_get_byteOrderMark(writer, &b);
2624         EXPECT_HR(hr, S_OK);
2625         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
2626
2627         b = !table->disable_escape;
2628         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
2629         EXPECT_HR(hr, S_OK);
2630         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
2631            table->disable_escape);
2632
2633         b = !table->indent;
2634         hr = IMXWriter_get_indent(writer, &b);
2635         EXPECT_HR(hr, S_OK);
2636         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
2637
2638         b = !table->omitdecl;
2639         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
2640         EXPECT_HR(hr, S_OK);
2641         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
2642
2643         b = !table->standalone;
2644         hr = IMXWriter_get_standalone(writer, &b);
2645         EXPECT_HR(hr, S_OK);
2646         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
2647
2648         hr = IMXWriter_get_encoding(writer, &encoding);
2649         EXPECT_HR(hr, S_OK);
2650         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
2651             i, wine_dbgstr_w(encoding), table->encoding);
2652         SysFreeString(encoding);
2653
2654         IMXWriter_Release(writer);
2655
2656         table++;
2657         i++;
2658     }
2659 }
2660
2661 static void test_mxwriter_properties(void)
2662 {
2663     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
2664     static const WCHAR emptyW[] = {0};
2665     static const WCHAR testW[] = {'t','e','s','t',0};
2666     ISAXContentHandler *content;
2667     IMXWriter *writer;
2668     VARIANT_BOOL b;
2669     HRESULT hr;
2670     BSTR str, str2;
2671     VARIANT dest;
2672
2673     test_mxwriter_default_properties(mxwriter_default_props);
2674
2675     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2676             &IID_IMXWriter, (void**)&writer);
2677     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2678
2679     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
2680     ok(hr == E_POINTER, "got %08x\n", hr);
2681
2682     hr = IMXWriter_get_byteOrderMark(writer, NULL);
2683     ok(hr == E_POINTER, "got %08x\n", hr);
2684
2685     hr = IMXWriter_get_indent(writer, NULL);
2686     ok(hr == E_POINTER, "got %08x\n", hr);
2687
2688     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
2689     ok(hr == E_POINTER, "got %08x\n", hr);
2690
2691     hr = IMXWriter_get_standalone(writer, NULL);
2692     ok(hr == E_POINTER, "got %08x\n", hr);
2693
2694     /* set and check */
2695     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
2696     ok(hr == S_OK, "got %08x\n", hr);
2697
2698     b = VARIANT_FALSE;
2699     hr = IMXWriter_get_standalone(writer, &b);
2700     ok(hr == S_OK, "got %08x\n", hr);
2701     ok(b == VARIANT_TRUE, "got %d\n", b);
2702
2703     hr = IMXWriter_get_encoding(writer, NULL);
2704     EXPECT_HR(hr, E_POINTER);
2705
2706     /* UTF-16 is a default setting apparently */
2707     str = (void*)0xdeadbeef;
2708     hr = IMXWriter_get_encoding(writer, &str);
2709     EXPECT_HR(hr, S_OK);
2710     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
2711
2712     str2 = (void*)0xdeadbeef;
2713     hr = IMXWriter_get_encoding(writer, &str2);
2714     ok(hr == S_OK, "got %08x\n", hr);
2715     ok(str != str2, "expected newly allocated, got same %p\n", str);
2716
2717     SysFreeString(str2);
2718     SysFreeString(str);
2719
2720     /* put empty string */
2721     str = SysAllocString(emptyW);
2722     hr = IMXWriter_put_encoding(writer, str);
2723     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2724     SysFreeString(str);
2725
2726     str = (void*)0xdeadbeef;
2727     hr = IMXWriter_get_encoding(writer, &str);
2728     EXPECT_HR(hr, S_OK);
2729     ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str));
2730     SysFreeString(str);
2731
2732     /* invalid encoding name */
2733     str = SysAllocString(testW);
2734     hr = IMXWriter_put_encoding(writer, str);
2735     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2736     SysFreeString(str);
2737
2738     /* test case sensivity */
2739     hr = IMXWriter_put_encoding(writer, _bstr_("utf-8"));
2740     EXPECT_HR(hr, S_OK);
2741     str = (void*)0xdeadbeef;
2742     hr = IMXWriter_get_encoding(writer, &str);
2743     EXPECT_HR(hr, S_OK);
2744     ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str));
2745     SysFreeString(str);
2746
2747     hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16"));
2748     EXPECT_HR(hr, S_OK);
2749     str = (void*)0xdeadbeef;
2750     hr = IMXWriter_get_encoding(writer, &str);
2751     EXPECT_HR(hr, S_OK);
2752     ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str));
2753     SysFreeString(str);
2754
2755     /* how it affects document creation */
2756     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2757     EXPECT_HR(hr, S_OK);
2758
2759     hr = ISAXContentHandler_startDocument(content);
2760     EXPECT_HR(hr, S_OK);
2761     hr = ISAXContentHandler_endDocument(content);
2762     EXPECT_HR(hr, S_OK);
2763
2764     V_VT(&dest) = VT_EMPTY;
2765     hr = IMXWriter_get_output(writer, &dest);
2766     EXPECT_HR(hr, S_OK);
2767     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2768     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n"),
2769         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2770     VariantClear(&dest);
2771     ISAXContentHandler_Release(content);
2772
2773     hr = IMXWriter_get_version(writer, NULL);
2774     ok(hr == E_POINTER, "got %08x\n", hr);
2775     /* default version is 'surprisingly' 1.0 */
2776     hr = IMXWriter_get_version(writer, &str);
2777     ok(hr == S_OK, "got %08x\n", hr);
2778     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
2779     SysFreeString(str);
2780
2781     /* store version string as is */
2782     hr = IMXWriter_put_version(writer, NULL);
2783     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2784
2785     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
2786     ok(hr == S_OK, "got %08x\n", hr);
2787
2788     hr = IMXWriter_put_version(writer, _bstr_(""));
2789     ok(hr == S_OK, "got %08x\n", hr);
2790     hr = IMXWriter_get_version(writer, &str);
2791     ok(hr == S_OK, "got %08x\n", hr);
2792     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
2793     SysFreeString(str);
2794
2795     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
2796     ok(hr == S_OK, "got %08x\n", hr);
2797     hr = IMXWriter_get_version(writer, &str);
2798     ok(hr == S_OK, "got %08x\n", hr);
2799     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
2800     SysFreeString(str);
2801
2802     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
2803     ok(hr == S_OK, "got %08x\n", hr);
2804     hr = IMXWriter_get_version(writer, &str);
2805     ok(hr == S_OK, "got %08x\n", hr);
2806     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
2807     SysFreeString(str);
2808
2809     IMXWriter_Release(writer);
2810     free_bstrs();
2811 }
2812
2813 static void test_mxwriter_flush(void)
2814 {
2815     static const WCHAR emptyW[] = {0};
2816     ISAXContentHandler *content;
2817     IMXWriter *writer;
2818     LARGE_INTEGER pos;
2819     ULARGE_INTEGER pos2;
2820     IStream *stream;
2821     VARIANT dest;
2822     HRESULT hr;
2823     char *buff;
2824     LONG ref;
2825     int len;
2826
2827     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2828             &IID_IMXWriter, (void**)&writer);
2829     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2830
2831     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2832     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2833     EXPECT_REF(stream, 1);
2834
2835     /* detach when nothing was attached */
2836     V_VT(&dest) = VT_EMPTY;
2837     hr = IMXWriter_put_output(writer, dest);
2838     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2839
2840     /* attach stream */
2841     V_VT(&dest) = VT_UNKNOWN;
2842     V_UNKNOWN(&dest) = (IUnknown*)stream;
2843     hr = IMXWriter_put_output(writer, dest);
2844     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2845     todo_wine EXPECT_REF(stream, 3);
2846
2847     /* detach setting VT_EMPTY destination */
2848     V_VT(&dest) = VT_EMPTY;
2849     hr = IMXWriter_put_output(writer, dest);
2850     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2851     EXPECT_REF(stream, 1);
2852
2853     V_VT(&dest) = VT_UNKNOWN;
2854     V_UNKNOWN(&dest) = (IUnknown*)stream;
2855     hr = IMXWriter_put_output(writer, dest);
2856     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2857
2858     /* flush() doesn't detach a stream */
2859     hr = IMXWriter_flush(writer);
2860     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2861     todo_wine EXPECT_REF(stream, 3);
2862
2863     pos.QuadPart = 0;
2864     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2865     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2866     ok(pos2.QuadPart == 0, "expected stream beginning\n");
2867
2868     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2869     ok(hr == S_OK, "got %08x\n", hr);
2870
2871     hr = ISAXContentHandler_startDocument(content);
2872     ok(hr == S_OK, "got %08x\n", hr);
2873
2874     pos.QuadPart = 0;
2875     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2876     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2877     ok(pos2.QuadPart != 0, "expected stream beginning\n");
2878
2879     /* already started */
2880     hr = ISAXContentHandler_startDocument(content);
2881     ok(hr == S_OK, "got %08x\n", hr);
2882
2883     hr = ISAXContentHandler_endDocument(content);
2884     ok(hr == S_OK, "got %08x\n", hr);
2885
2886     /* flushed on endDocument() */
2887     pos.QuadPart = 0;
2888     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2889     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2890     ok(pos2.QuadPart != 0, "expected stream position moved\n");
2891
2892     IStream_Release(stream);
2893
2894     /* auto-flush feature */
2895     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2896     EXPECT_HR(hr, S_OK);
2897     EXPECT_REF(stream, 1);
2898
2899     V_VT(&dest) = VT_UNKNOWN;
2900     V_UNKNOWN(&dest) = (IUnknown*)stream;
2901     hr = IMXWriter_put_output(writer, dest);
2902     EXPECT_HR(hr, S_OK);
2903
2904     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE);
2905     EXPECT_HR(hr, S_OK);
2906
2907     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2908     EXPECT_HR(hr, S_OK);
2909
2910     hr = ISAXContentHandler_startDocument(content);
2911     EXPECT_HR(hr, S_OK);
2912
2913     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
2914     EXPECT_HR(hr, S_OK);
2915
2916     /* internal buffer is flushed automatically on certain threshold */
2917     pos.QuadPart = 0;
2918     pos2.QuadPart = 1;
2919     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2920     EXPECT_HR(hr, S_OK);
2921     ok(pos2.QuadPart == 0, "expected stream beginning\n");
2922
2923     len = 2048;
2924     buff = HeapAlloc(GetProcessHeap(), 0, len);
2925     memset(buff, 'A', len);
2926     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
2927     EXPECT_HR(hr, S_OK);
2928
2929     pos.QuadPart = 0;
2930     pos2.QuadPart = 0;
2931     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2932     EXPECT_HR(hr, S_OK);
2933 todo_wine
2934     ok(pos2.QuadPart != 0, "unexpected stream beginning\n");
2935
2936     hr = IMXWriter_get_output(writer, NULL);
2937     EXPECT_HR(hr, E_POINTER);
2938
2939     ref = get_refcount(stream);
2940     V_VT(&dest) = VT_EMPTY;
2941     hr = IMXWriter_get_output(writer, &dest);
2942     EXPECT_HR(hr, S_OK);
2943     ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest));
2944     ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest));
2945     ok(ref+1 == get_refcount(stream), "expected increased refcount\n");
2946     VariantClear(&dest);
2947
2948     hr = ISAXContentHandler_endDocument(content);
2949     EXPECT_HR(hr, S_OK);
2950
2951     IStream_Release(stream);
2952
2953     /* test char count lower than threshold */
2954     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2955     EXPECT_HR(hr, S_OK);
2956     EXPECT_REF(stream, 1);
2957
2958     hr = ISAXContentHandler_startDocument(content);
2959     EXPECT_HR(hr, S_OK);
2960
2961     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
2962     EXPECT_HR(hr, S_OK);
2963
2964     pos.QuadPart = 0;
2965     pos2.QuadPart = 1;
2966     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2967     EXPECT_HR(hr, S_OK);
2968     ok(pos2.QuadPart == 0, "expected stream beginning\n");
2969
2970     memset(buff, 'A', len);
2971     hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8);
2972     EXPECT_HR(hr, S_OK);
2973
2974     pos.QuadPart = 0;
2975     pos2.QuadPart = 1;
2976     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2977     EXPECT_HR(hr, S_OK);
2978     ok(pos2.QuadPart == 0, "expected stream beginning\n");
2979
2980     hr = ISAXContentHandler_endDocument(content);
2981     EXPECT_HR(hr, S_OK);
2982
2983     /* test auto-flush function when stream is not set */
2984     V_VT(&dest) = VT_EMPTY;
2985     hr = IMXWriter_put_output(writer, dest);
2986     EXPECT_HR(hr, S_OK);
2987
2988     hr = ISAXContentHandler_startDocument(content);
2989     EXPECT_HR(hr, S_OK);
2990
2991     hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
2992     EXPECT_HR(hr, S_OK);
2993
2994     memset(buff, 'A', len);
2995     hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
2996     EXPECT_HR(hr, S_OK);
2997
2998     V_VT(&dest) = VT_EMPTY;
2999     hr = IMXWriter_get_output(writer, &dest);
3000     EXPECT_HR(hr, S_OK);
3001     len += strlen("<a>");
3002     ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len);
3003     VariantClear(&dest);
3004
3005     HeapFree(GetProcessHeap(), 0, buff);
3006     ISAXContentHandler_Release(content);
3007     IStream_Release(stream);
3008     IMXWriter_Release(writer);
3009     free_bstrs();
3010 }
3011
3012 static void test_mxwriter_startenddocument(void)
3013 {
3014     ISAXContentHandler *content;
3015     IMXWriter *writer;
3016     VARIANT dest;
3017     HRESULT hr;
3018
3019     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3020             &IID_IMXWriter, (void**)&writer);
3021     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3022
3023     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3024     ok(hr == S_OK, "got %08x\n", hr);
3025
3026     hr = ISAXContentHandler_startDocument(content);
3027     ok(hr == S_OK, "got %08x\n", hr);
3028
3029     hr = ISAXContentHandler_endDocument(content);
3030     ok(hr == S_OK, "got %08x\n", hr);
3031
3032     V_VT(&dest) = VT_EMPTY;
3033     hr = IMXWriter_get_output(writer, &dest);
3034     ok(hr == S_OK, "got %08x\n", hr);
3035     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3036     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3037         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3038     VariantClear(&dest);
3039
3040     /* now try another startDocument */
3041     hr = ISAXContentHandler_startDocument(content);
3042     ok(hr == S_OK, "got %08x\n", hr);
3043     /* and get duplicated prolog */
3044     V_VT(&dest) = VT_EMPTY;
3045     hr = IMXWriter_get_output(writer, &dest);
3046     ok(hr == S_OK, "got %08x\n", hr);
3047     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3048     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
3049                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3050         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3051     VariantClear(&dest);
3052
3053     ISAXContentHandler_Release(content);
3054     IMXWriter_Release(writer);
3055
3056     /* now with omitted declaration */
3057     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3058             &IID_IMXWriter, (void**)&writer);
3059     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3060
3061     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3062     ok(hr == S_OK, "got %08x\n", hr);
3063
3064     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3065     ok(hr == S_OK, "got %08x\n", hr);
3066
3067     hr = ISAXContentHandler_startDocument(content);
3068     ok(hr == S_OK, "got %08x\n", hr);
3069
3070     hr = ISAXContentHandler_endDocument(content);
3071     ok(hr == S_OK, "got %08x\n", hr);
3072
3073     V_VT(&dest) = VT_EMPTY;
3074     hr = IMXWriter_get_output(writer, &dest);
3075     ok(hr == S_OK, "got %08x\n", hr);
3076     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3077     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3078     VariantClear(&dest);
3079
3080     ISAXContentHandler_Release(content);
3081     IMXWriter_Release(writer);
3082
3083     free_bstrs();
3084 }
3085
3086 enum startendtype
3087 {
3088     StartElement    = 0x001,
3089     EndElement      = 0x010,
3090     StartEndElement = 0x011,
3091     DisableEscaping = 0x100
3092 };
3093
3094 struct writer_startendelement_t {
3095     const GUID *clsid;
3096     enum startendtype type;
3097     const char *uri;
3098     const char *local_name;
3099     const char *qname;
3100     const char *output;
3101     HRESULT hr;
3102     ISAXAttributes *attr;
3103 };
3104
3105 static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\">";
3106 static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\"/>";
3107 static const char startendelement_noescape_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&\">\"/>";
3108
3109 static const struct writer_startendelement_t writer_startendelement[] = {
3110     /* 0 */
3111     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3112     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3113     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3114     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
3115     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3116     /* 5 */
3117     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3118     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3119     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
3120     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3121     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3122     /* 10 */
3123     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3124     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
3125     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3126     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3127     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3128     /* 15 */
3129     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
3130     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
3131     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3132     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3133     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3134     /* 20 */
3135     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3136     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3137     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3138     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
3139     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3140     /* 25 */
3141     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3142     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3143     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3144     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3145     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3146     /* 30 */
3147     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3148     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3149     /* endElement tests */
3150     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3151     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3152     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3153     /* 35 */
3154     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
3155     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3156     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3157     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3158     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
3159     /* 40 */
3160     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3161     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3162     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3163     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
3164     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3165     /* 45 */
3166     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3167     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3168     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
3169     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
3170     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3171     /* 50 */
3172     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3173     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3174     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3175     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3176     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3177     /* 55 */
3178     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
3179     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3180     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3181     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3182     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3183     /* 60 */
3184     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3185     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3186     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3187     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3188
3189     /* with attributes */
3190     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3191     /* 65 */
3192     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3193     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3194     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3195     /* empty elements */
3196     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3197     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3198     /* 70 */
3199     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3200     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3201     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
3202     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
3203     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
3204     /* 75 */
3205     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
3206
3207     /* with disabled output escaping */
3208     { &CLSID_MXXMLWriter,   StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3209     { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3210     { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3211     { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3212
3213     { NULL }
3214 };
3215
3216 static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid)
3217 {
3218     while (table->clsid)
3219     {
3220         IUnknown *unk;
3221         HRESULT hr;
3222
3223         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk);
3224         if (hr == S_OK) IUnknown_Release(unk);
3225
3226         table->supported = hr == S_OK;
3227         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
3228
3229         table++;
3230     }
3231 }
3232
3233 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
3234 {
3235     int i = 0;
3236
3237     while (table->clsid)
3238     {
3239         ISAXContentHandler *content;
3240         IMXWriter *writer;
3241         HRESULT hr;
3242
3243         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3244         {
3245             table++;
3246             i++;
3247             continue;
3248         }
3249
3250         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3251             &IID_IMXWriter, (void**)&writer);
3252         EXPECT_HR(hr, S_OK);
3253
3254         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3255         EXPECT_HR(hr, S_OK);
3256
3257         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3258         EXPECT_HR(hr, S_OK);
3259
3260         hr = ISAXContentHandler_startDocument(content);
3261         EXPECT_HR(hr, S_OK);
3262
3263         if (table->type & DisableEscaping)
3264         {
3265             hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3266             EXPECT_HR(hr, S_OK);
3267         }
3268
3269         if (table->type & StartElement)
3270         {
3271             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
3272                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
3273             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3274         }
3275
3276         if (table->type & EndElement)
3277         {
3278             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
3279                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
3280             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3281         }
3282
3283         /* test output */
3284         if (hr == S_OK)
3285         {
3286             VARIANT dest;
3287
3288             V_VT(&dest) = VT_EMPTY;
3289             hr = IMXWriter_get_output(writer, &dest);
3290             EXPECT_HR(hr, S_OK);
3291             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3292             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3293                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3294             VariantClear(&dest);
3295         }
3296
3297         ISAXContentHandler_Release(content);
3298         IMXWriter_Release(writer);
3299
3300         table++;
3301         i++;
3302     }
3303
3304     free_bstrs();
3305 }
3306
3307 /* point of these test is to start/end element with different names and name lengths */
3308 struct writer_startendelement2_t {
3309     const GUID *clsid;
3310     const char *qnamestart;
3311     int qnamestart_len;
3312     const char *qnameend;
3313     int qnameend_len;
3314     const char *output;
3315     HRESULT hr;
3316 };
3317
3318 static const struct writer_startendelement2_t writer_startendelement2[] = {
3319     { &CLSID_MXXMLWriter,   "a", -1, "b", -1, "<a/>", S_OK },
3320     { &CLSID_MXXMLWriter30, "a", -1, "b", -1, "<a/>", S_OK },
3321     { &CLSID_MXXMLWriter40, "a", -1, "b", -1, "<a/>", S_OK },
3322     /* -1 length is not allowed for version 6 */
3323     { &CLSID_MXXMLWriter60, "a", -1, "b", -1, "<a/>", E_INVALIDARG },
3324
3325     { &CLSID_MXXMLWriter,   "a", 1, "b", 1, "<a/>", S_OK },
3326     { &CLSID_MXXMLWriter30, "a", 1, "b", 1, "<a/>", S_OK },
3327     { &CLSID_MXXMLWriter40, "a", 1, "b", 1, "<a/>", S_OK },
3328     { &CLSID_MXXMLWriter60, "a", 1, "b", 1, "<a/>", S_OK },
3329     { NULL }
3330 };
3331
3332 static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table)
3333 {
3334     int i = 0;
3335
3336     while (table->clsid)
3337     {
3338         ISAXContentHandler *content;
3339         IMXWriter *writer;
3340         HRESULT hr;
3341
3342         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3343         {
3344             table++;
3345             i++;
3346             continue;
3347         }
3348
3349         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3350             &IID_IMXWriter, (void**)&writer);
3351         EXPECT_HR(hr, S_OK);
3352
3353         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3354         EXPECT_HR(hr, S_OK);
3355
3356         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3357         EXPECT_HR(hr, S_OK);
3358
3359         hr = ISAXContentHandler_startDocument(content);
3360         EXPECT_HR(hr, S_OK);
3361
3362         hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0,
3363             _bstr_(table->qnamestart), table->qnamestart_len, NULL);
3364         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3365
3366         hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0,
3367             _bstr_(table->qnameend), table->qnameend_len);
3368         ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3369
3370         /* test output */
3371         if (hr == S_OK)
3372         {
3373             VARIANT dest;
3374
3375             V_VT(&dest) = VT_EMPTY;
3376             hr = IMXWriter_get_output(writer, &dest);
3377             EXPECT_HR(hr, S_OK);
3378             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3379             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3380                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3381             VariantClear(&dest);
3382         }
3383
3384         ISAXContentHandler_Release(content);
3385         IMXWriter_Release(writer);
3386
3387         table++;
3388         i++;
3389
3390         free_bstrs();
3391     }
3392 }
3393
3394
3395 static void test_mxwriter_startendelement(void)
3396 {
3397     ISAXContentHandler *content;
3398     IMXWriter *writer;
3399     VARIANT dest;
3400     HRESULT hr;
3401
3402     test_mxwriter_startendelement_batch(writer_startendelement);
3403     test_mxwriter_startendelement_batch2(writer_startendelement2);
3404
3405     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3406             &IID_IMXWriter, (void**)&writer);
3407     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3408
3409     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3410     ok(hr == S_OK, "got %08x\n", hr);
3411
3412     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3413     ok(hr == S_OK, "got %08x\n", hr);
3414
3415     hr = ISAXContentHandler_startDocument(content);
3416     ok(hr == S_OK, "got %08x\n", hr);
3417
3418     /* all string pointers should be not null */
3419     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
3420     ok(hr == S_OK, "got %08x\n", hr);
3421
3422     V_VT(&dest) = VT_EMPTY;
3423     hr = IMXWriter_get_output(writer, &dest);
3424     ok(hr == S_OK, "got %08x\n", hr);
3425     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3426     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3427     VariantClear(&dest);
3428
3429     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
3430     ok(hr == S_OK, "got %08x\n", hr);
3431
3432     V_VT(&dest) = VT_EMPTY;
3433     hr = IMXWriter_get_output(writer, &dest);
3434     ok(hr == S_OK, "got %08x\n", hr);
3435     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3436     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3437     VariantClear(&dest);
3438
3439     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
3440     EXPECT_HR(hr, E_INVALIDARG);
3441
3442     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
3443     EXPECT_HR(hr, E_INVALIDARG);
3444
3445     /* only local name is an error too */
3446     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
3447     EXPECT_HR(hr, E_INVALIDARG);
3448
3449     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
3450     EXPECT_HR(hr, S_OK);
3451
3452     V_VT(&dest) = VT_EMPTY;
3453     hr = IMXWriter_get_output(writer, &dest);
3454     EXPECT_HR(hr, S_OK);
3455     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3456     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3457     VariantClear(&dest);
3458
3459     hr = ISAXContentHandler_endDocument(content);
3460     EXPECT_HR(hr, S_OK);
3461
3462     V_VT(&dest) = VT_EMPTY;
3463     hr = IMXWriter_put_output(writer, dest);
3464     EXPECT_HR(hr, S_OK);
3465
3466     V_VT(&dest) = VT_EMPTY;
3467     hr = IMXWriter_get_output(writer, &dest);
3468     EXPECT_HR(hr, S_OK);
3469     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3470     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3471     VariantClear(&dest);
3472
3473     hr = ISAXContentHandler_startDocument(content);
3474     EXPECT_HR(hr, S_OK);
3475
3476     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
3477     EXPECT_HR(hr, S_OK);
3478
3479     V_VT(&dest) = VT_EMPTY;
3480     hr = IMXWriter_get_output(writer, &dest);
3481     EXPECT_HR(hr, S_OK);
3482     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3483     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3484     VariantClear(&dest);
3485
3486     ISAXContentHandler_endDocument(content);
3487     IMXWriter_flush(writer);
3488
3489     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
3490     EXPECT_HR(hr, S_OK);
3491     V_VT(&dest) = VT_EMPTY;
3492     hr = IMXWriter_get_output(writer, &dest);
3493     EXPECT_HR(hr, S_OK);
3494     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3495     ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3496     VariantClear(&dest);
3497
3498     V_VT(&dest) = VT_EMPTY;
3499     hr = IMXWriter_put_output(writer, dest);
3500     EXPECT_HR(hr, S_OK);
3501
3502     /* length -1 */
3503     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL);
3504     EXPECT_HR(hr, S_OK);
3505     V_VT(&dest) = VT_EMPTY;
3506     hr = IMXWriter_get_output(writer, &dest);
3507     EXPECT_HR(hr, S_OK);
3508     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3509     ok(!lstrcmpW(_bstr_("<a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3510     VariantClear(&dest);
3511
3512     ISAXContentHandler_Release(content);
3513     IMXWriter_Release(writer);
3514     free_bstrs();
3515 }
3516
3517 struct writer_characters_t {
3518     const GUID *clsid;
3519     const char *data;
3520     const char *output;
3521 };
3522
3523 static const struct writer_characters_t writer_characters[] = {
3524     { &CLSID_MXXMLWriter,   "< > & \"", "&lt; &gt; &amp; \"" },
3525     { &CLSID_MXXMLWriter30, "< > & \"", "&lt; &gt; &amp; \"" },
3526     { &CLSID_MXXMLWriter40, "< > & \"", "&lt; &gt; &amp; \"" },
3527     { &CLSID_MXXMLWriter60, "< > & \"", "&lt; &gt; &amp; \"" },
3528     { NULL }
3529 };
3530
3531 static void test_mxwriter_characters(void)
3532 {
3533     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
3534     const struct writer_characters_t *table = writer_characters;
3535     ISAXContentHandler *content;
3536     IMXWriter *writer;
3537     VARIANT dest;
3538     HRESULT hr;
3539     int i = 0;
3540
3541     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3542             &IID_IMXWriter, (void**)&writer);
3543     EXPECT_HR(hr, S_OK);
3544
3545     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3546     EXPECT_HR(hr, S_OK);
3547
3548     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3549     EXPECT_HR(hr, S_OK);
3550
3551     hr = ISAXContentHandler_startDocument(content);
3552     EXPECT_HR(hr, S_OK);
3553
3554     hr = ISAXContentHandler_characters(content, NULL, 0);
3555     EXPECT_HR(hr, E_INVALIDARG);
3556
3557     hr = ISAXContentHandler_characters(content, chardataW, 0);
3558     EXPECT_HR(hr, S_OK);
3559
3560     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
3561     EXPECT_HR(hr, S_OK);
3562
3563     V_VT(&dest) = VT_EMPTY;
3564     hr = IMXWriter_get_output(writer, &dest);
3565     EXPECT_HR(hr, S_OK);
3566     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3567     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3568     VariantClear(&dest);
3569
3570     hr = ISAXContentHandler_endDocument(content);
3571     EXPECT_HR(hr, S_OK);
3572
3573     ISAXContentHandler_Release(content);
3574     IMXWriter_Release(writer);
3575
3576     /* try empty characters data to see if element is closed */
3577     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3578             &IID_IMXWriter, (void**)&writer);
3579     EXPECT_HR(hr, S_OK);
3580
3581     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3582     EXPECT_HR(hr, S_OK);
3583
3584     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3585     EXPECT_HR(hr, S_OK);
3586
3587     hr = ISAXContentHandler_startDocument(content);
3588     EXPECT_HR(hr, S_OK);
3589
3590     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3591     EXPECT_HR(hr, S_OK);
3592
3593     hr = ISAXContentHandler_characters(content, chardataW, 0);
3594     EXPECT_HR(hr, S_OK);
3595
3596     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3597     EXPECT_HR(hr, S_OK);
3598
3599     V_VT(&dest) = VT_EMPTY;
3600     hr = IMXWriter_get_output(writer, &dest);
3601     EXPECT_HR(hr, S_OK);
3602     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3603     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3604     VariantClear(&dest);
3605
3606     ISAXContentHandler_Release(content);
3607     IMXWriter_Release(writer);
3608
3609     /* batch tests */
3610     while (table->clsid)
3611     {
3612         ISAXContentHandler *content;
3613         IMXWriter *writer;
3614         VARIANT dest;
3615         HRESULT hr;
3616
3617         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3618         {
3619             table++;
3620             i++;
3621             continue;
3622         }
3623
3624         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3625             &IID_IMXWriter, (void**)&writer);
3626         EXPECT_HR(hr, S_OK);
3627
3628         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3629         EXPECT_HR(hr, S_OK);
3630
3631         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3632         EXPECT_HR(hr, S_OK);
3633
3634         hr = ISAXContentHandler_startDocument(content);
3635         EXPECT_HR(hr, S_OK);
3636
3637         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3638         EXPECT_HR(hr, S_OK);
3639
3640         /* test output */
3641         if (hr == S_OK)
3642         {
3643             V_VT(&dest) = VT_EMPTY;
3644             hr = IMXWriter_get_output(writer, &dest);
3645             EXPECT_HR(hr, S_OK);
3646             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3647             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3648                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3649             VariantClear(&dest);
3650         }
3651
3652         /* with disabled escaping */
3653         V_VT(&dest) = VT_EMPTY;
3654         hr = IMXWriter_put_output(writer, dest);
3655         EXPECT_HR(hr, S_OK);
3656
3657         hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3658         EXPECT_HR(hr, S_OK);
3659
3660         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3661         EXPECT_HR(hr, S_OK);
3662
3663         /* test output */
3664         if (hr == S_OK)
3665         {
3666             V_VT(&dest) = VT_EMPTY;
3667             hr = IMXWriter_get_output(writer, &dest);
3668             EXPECT_HR(hr, S_OK);
3669             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3670             ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)),
3671                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data);
3672             VariantClear(&dest);
3673         }
3674
3675         ISAXContentHandler_Release(content);
3676         IMXWriter_Release(writer);
3677
3678         table++;
3679         i++;
3680     }
3681
3682     free_bstrs();
3683 }
3684
3685 static const mxwriter_stream_test mxwriter_stream_tests[] = {
3686     {
3687         VARIANT_TRUE,"UTF-16",
3688         {
3689             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3690             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3691             {TRUE}
3692         }
3693     },
3694     {
3695         VARIANT_FALSE,"UTF-16",
3696         {
3697             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3698             {TRUE}
3699         }
3700     },
3701     {
3702         VARIANT_TRUE,"UTF-8",
3703         {
3704             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
3705             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3706              * and the writer is released.
3707              */
3708             {FALSE,NULL,0},
3709             {TRUE}
3710         }
3711     },
3712     {
3713         VARIANT_TRUE,"utf-8",
3714         {
3715             {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1},
3716             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3717              * and the writer is released.
3718              */
3719             {FALSE,NULL,0},
3720             {TRUE}
3721         }
3722     },
3723     {
3724         VARIANT_TRUE,"UTF-16",
3725         {
3726             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3727             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3728             {TRUE}
3729         }
3730     },
3731     {
3732         VARIANT_TRUE,"UTF-16",
3733         {
3734             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
3735             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3736             {TRUE}
3737         }
3738     }
3739 };
3740
3741 static void test_mxwriter_stream(void)
3742 {
3743     IMXWriter *writer;
3744     ISAXContentHandler *content;
3745     HRESULT hr;
3746     VARIANT dest;
3747     IStream *stream;
3748     LARGE_INTEGER pos;
3749     ULARGE_INTEGER pos2;
3750     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
3751
3752     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
3753         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
3754
3755         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3756                 &IID_IMXWriter, (void**)&writer);
3757         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3758
3759         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3760         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3761
3762         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
3763         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
3764
3765         V_VT(&dest) = VT_UNKNOWN;
3766         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
3767         hr = IMXWriter_put_output(writer, dest);
3768         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
3769         VariantClear(&dest);
3770
3771         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
3772         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
3773
3774         current_write_test = test->expected_writes;
3775
3776         hr = ISAXContentHandler_startDocument(content);
3777         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3778
3779         hr = ISAXContentHandler_endDocument(content);
3780         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3781
3782         ISAXContentHandler_Release(content);
3783         IMXWriter_Release(writer);
3784
3785         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
3786             (int)(current_write_test-test->expected_writes), current_stream_test_index);
3787     }
3788
3789     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3790             &IID_IMXWriter, (void**)&writer);
3791     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3792
3793     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3794     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
3795
3796     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3797     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3798
3799     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3800     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
3801
3802     V_VT(&dest) = VT_UNKNOWN;
3803     V_UNKNOWN(&dest) = (IUnknown*)stream;
3804     hr = IMXWriter_put_output(writer, dest);
3805     ok(hr == S_OK, "put_output failed: %08x\n", hr);
3806
3807     hr = ISAXContentHandler_startDocument(content);
3808     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3809
3810     /* Setting output of the mxwriter causes the current output to be flushed,
3811      * and the writer to start over.
3812      */
3813     V_VT(&dest) = VT_EMPTY;
3814     hr = IMXWriter_put_output(writer, dest);
3815     ok(hr == S_OK, "put_output failed: %08x\n", hr);
3816
3817     pos.QuadPart = 0;
3818     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3819     ok(hr == S_OK, "Seek failed: %08x\n", hr);
3820     ok(pos2.QuadPart != 0, "expected stream position moved\n");
3821
3822     hr = ISAXContentHandler_startDocument(content);
3823     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3824
3825     hr = ISAXContentHandler_endDocument(content);
3826     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
3827
3828     V_VT(&dest) = VT_EMPTY;
3829     hr = IMXWriter_get_output(writer, &dest);
3830     ok(hr == S_OK, "get_output failed: %08x\n", hr);
3831     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3832     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3833             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3834     VariantClear(&dest);
3835
3836     /* test when BOM is written to output stream */
3837     V_VT(&dest) = VT_EMPTY;
3838     hr = IMXWriter_put_output(writer, dest);
3839     EXPECT_HR(hr, S_OK);
3840
3841     pos.QuadPart = 0;
3842     hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
3843     EXPECT_HR(hr, S_OK);
3844
3845     V_VT(&dest) = VT_UNKNOWN;
3846     V_UNKNOWN(&dest) = (IUnknown*)stream;
3847     hr = IMXWriter_put_output(writer, dest);
3848     EXPECT_HR(hr, S_OK);
3849
3850     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE);
3851     EXPECT_HR(hr, S_OK);
3852
3853     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
3854     EXPECT_HR(hr, S_OK);
3855
3856     hr = ISAXContentHandler_startDocument(content);
3857     EXPECT_HR(hr, S_OK);
3858
3859     pos.QuadPart = 0;
3860     pos2.QuadPart = 0;
3861     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3862     EXPECT_HR(hr, S_OK);
3863     ok(pos2.QuadPart == 2, "got wrong position\n");
3864
3865     ISAXContentHandler_Release(content);
3866     IMXWriter_Release(writer);
3867
3868     free_bstrs();
3869 }
3870
3871 static const char *encoding_names[] = {
3872     "iso-8859-1",
3873     "iso-8859-2",
3874     "iso-8859-3",
3875     "iso-8859-4",
3876     "iso-8859-5",
3877     "iso-8859-7",
3878     "iso-8859-9",
3879     "iso-8859-13",
3880     "iso-8859-15",
3881     NULL
3882 };
3883
3884 static void test_mxwriter_encoding(void)
3885 {
3886     ISAXContentHandler *content;
3887     IMXWriter *writer;
3888     IStream *stream;
3889     const char *enc;
3890     VARIANT dest;
3891     HRESULT hr;
3892     HGLOBAL g;
3893     char *ptr;
3894     BSTR s;
3895     int i;
3896
3897     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3898             &IID_IMXWriter, (void**)&writer);
3899     EXPECT_HR(hr, S_OK);
3900
3901     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3902     EXPECT_HR(hr, S_OK);
3903
3904     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3905     EXPECT_HR(hr, S_OK);
3906
3907     hr = ISAXContentHandler_startDocument(content);
3908     EXPECT_HR(hr, S_OK);
3909
3910     hr = ISAXContentHandler_endDocument(content);
3911     EXPECT_HR(hr, S_OK);
3912
3913     /* The content is always re-encoded to UTF-16 when the output is
3914      * retrieved as a BSTR.
3915      */
3916     V_VT(&dest) = VT_EMPTY;
3917     hr = IMXWriter_get_output(writer, &dest);
3918     EXPECT_HR(hr, S_OK);
3919     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3920     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3921             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3922     VariantClear(&dest);
3923
3924     /* switch encoding when something is written already */
3925     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3926     EXPECT_HR(hr, S_OK);
3927
3928     V_VT(&dest) = VT_UNKNOWN;
3929     V_UNKNOWN(&dest) = (IUnknown*)stream;
3930     hr = IMXWriter_put_output(writer, dest);
3931     EXPECT_HR(hr, S_OK);
3932
3933     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3934     EXPECT_HR(hr, S_OK);
3935
3936     /* write empty element */
3937     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3938     EXPECT_HR(hr, S_OK);
3939
3940     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3941     EXPECT_HR(hr, S_OK);
3942
3943     /* switch */
3944     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
3945     EXPECT_HR(hr, S_OK);
3946
3947     hr = IMXWriter_flush(writer);
3948     EXPECT_HR(hr, S_OK);
3949
3950     hr = GetHGlobalFromStream(stream, &g);
3951     EXPECT_HR(hr, S_OK);
3952
3953     ptr = GlobalLock(g);
3954     ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]);
3955     GlobalUnlock(g);
3956
3957     /* so output is unaffected, encoding name is stored however */
3958     hr = IMXWriter_get_encoding(writer, &s);
3959     EXPECT_HR(hr, S_OK);
3960     ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s));
3961     SysFreeString(s);
3962
3963     IStream_Release(stream);
3964
3965     i = 0;
3966     enc = encoding_names[i];
3967     while (enc)
3968     {
3969         char expectedA[200];
3970
3971         hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3972         EXPECT_HR(hr, S_OK);
3973
3974         V_VT(&dest) = VT_UNKNOWN;
3975         V_UNKNOWN(&dest) = (IUnknown*)stream;
3976         hr = IMXWriter_put_output(writer, dest);
3977         EXPECT_HR(hr, S_OK);
3978
3979         hr = IMXWriter_put_encoding(writer, _bstr_(enc));
3980         ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */,
3981             "%s: encoding not accepted\n", enc);
3982         if (hr != S_OK)
3983         {
3984             enc = encoding_names[++i];
3985             IStream_Release(stream);
3986             continue;
3987         }
3988
3989         hr = ISAXContentHandler_startDocument(content);
3990         EXPECT_HR(hr, S_OK);
3991
3992         hr = ISAXContentHandler_endDocument(content);
3993         EXPECT_HR(hr, S_OK);
3994
3995         hr = IMXWriter_flush(writer);
3996         EXPECT_HR(hr, S_OK);
3997
3998         /* prepare expected string */
3999         *expectedA = 0;
4000         strcat(expectedA, "<?xml version=\"1.0\" encoding=\"");
4001         strcat(expectedA, enc);
4002         strcat(expectedA, "\" standalone=\"no\"?>\r\n");
4003
4004         hr = GetHGlobalFromStream(stream, &g);
4005         EXPECT_HR(hr, S_OK);
4006
4007         ptr = GlobalLock(g);
4008         ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA);
4009         GlobalUnlock(g);
4010
4011         V_VT(&dest) = VT_EMPTY;
4012         hr = IMXWriter_put_output(writer, dest);
4013         EXPECT_HR(hr, S_OK);
4014
4015         IStream_Release(stream);
4016
4017         enc = encoding_names[++i];
4018     }
4019
4020     ISAXContentHandler_Release(content);
4021     IMXWriter_Release(writer);
4022
4023     free_bstrs();
4024 }
4025
4026 static void test_obj_dispex(IUnknown *obj)
4027 {
4028     static const WCHAR starW[] = {'*',0};
4029     DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE;
4030     IDispatchEx *dispex;
4031     IUnknown *unk;
4032     DWORD props;
4033     UINT ticnt;
4034     HRESULT hr;
4035     BSTR name;
4036
4037     hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
4038     EXPECT_HR(hr, S_OK);
4039     if (FAILED(hr)) return;
4040
4041     ticnt = 0;
4042     hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt);
4043     EXPECT_HR(hr, S_OK);
4044     ok(ticnt == 1, "ticnt=%u\n", ticnt);
4045
4046     name = SysAllocString(starW);
4047     hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive);
4048     EXPECT_HR(hr, E_NOTIMPL);
4049     SysFreeString(name);
4050
4051     hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid);
4052     EXPECT_HR(hr, E_NOTIMPL);
4053
4054     props = 0;
4055     hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props);
4056     EXPECT_HR(hr, E_NOTIMPL);
4057     ok(props == 0, "expected 0 got %d\n", props);
4058
4059     hr = IDispatchEx_GetMemberName(dispex, dispid, &name);
4060     EXPECT_HR(hr, E_NOTIMPL);
4061     if (SUCCEEDED(hr)) SysFreeString(name);
4062
4063     hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid);
4064     EXPECT_HR(hr, E_NOTIMPL);
4065
4066     hr = IDispatchEx_GetNameSpaceParent(dispex, &unk);
4067     EXPECT_HR(hr, E_NOTIMPL);
4068     if (hr == S_OK && unk) IUnknown_Release(unk);
4069
4070     IDispatchEx_Release(dispex);
4071 }
4072
4073 static void test_dispex(void)
4074 {
4075      IVBSAXXMLReader *vbreader;
4076      ISAXXMLReader *reader;
4077      IUnknown *unk;
4078      HRESULT hr;
4079
4080      hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
4081                 &IID_ISAXXMLReader, (void**)&reader);
4082      EXPECT_HR(hr, S_OK);
4083
4084      hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk);
4085      EXPECT_HR(hr, S_OK);
4086      test_obj_dispex(unk);
4087      IUnknown_Release(unk);
4088
4089      hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader);
4090      EXPECT_HR(hr, S_OK);
4091      hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk);
4092      EXPECT_HR(hr, S_OK);
4093      test_obj_dispex(unk);
4094      IUnknown_Release(unk);
4095      IVBSAXXMLReader_Release(vbreader);
4096
4097      ISAXXMLReader_Release(reader);
4098 }
4099
4100 static void test_mxwriter_dispex(void)
4101 {
4102     IDispatchEx *dispex;
4103     IMXWriter *writer;
4104     IUnknown *unk;
4105     HRESULT hr;
4106
4107     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4108             &IID_IMXWriter, (void**)&writer);
4109     EXPECT_HR(hr, S_OK);
4110
4111     hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex);
4112     EXPECT_HR(hr, S_OK);
4113     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4114     test_obj_dispex(unk);
4115     IUnknown_Release(unk);
4116     IDispatchEx_Release(dispex);
4117
4118     IMXWriter_Release(writer);
4119 }
4120
4121 static void test_mxwriter_comment(void)
4122 {
4123     static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
4124     ISAXContentHandler *content;
4125     ISAXLexicalHandler *lexical;
4126     IMXWriter *writer;
4127     VARIANT dest;
4128     HRESULT hr;
4129
4130     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4131             &IID_IMXWriter, (void**)&writer);
4132     EXPECT_HR(hr, S_OK);
4133
4134     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4135     EXPECT_HR(hr, S_OK);
4136
4137     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4138     EXPECT_HR(hr, S_OK);
4139
4140     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4141     EXPECT_HR(hr, S_OK);
4142
4143     hr = ISAXContentHandler_startDocument(content);
4144     EXPECT_HR(hr, S_OK);
4145
4146     hr = ISAXLexicalHandler_comment(lexical, NULL, 0);
4147     EXPECT_HR(hr, E_INVALIDARG);
4148
4149     hr = ISAXLexicalHandler_comment(lexical, commentW, 0);
4150     EXPECT_HR(hr, S_OK);
4151
4152     V_VT(&dest) = VT_EMPTY;
4153     hr = IMXWriter_get_output(writer, &dest);
4154     EXPECT_HR(hr, S_OK);
4155     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4156     ok(!lstrcmpW(_bstr_("<!---->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4157     VariantClear(&dest);
4158
4159     hr = ISAXLexicalHandler_comment(lexical, commentW, sizeof(commentW)/sizeof(WCHAR)-1);
4160     EXPECT_HR(hr, S_OK);
4161
4162     V_VT(&dest) = VT_EMPTY;
4163     hr = IMXWriter_get_output(writer, &dest);
4164     EXPECT_HR(hr, S_OK);
4165     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4166     ok(!lstrcmpW(_bstr_("<!---->\r\n<!--comment-->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4167     VariantClear(&dest);
4168
4169     ISAXContentHandler_Release(content);
4170     ISAXLexicalHandler_Release(lexical);
4171     IMXWriter_Release(writer);
4172     free_bstrs();
4173 }
4174
4175 static void test_mxwriter_cdata(void)
4176 {
4177     ISAXContentHandler *content;
4178     ISAXLexicalHandler *lexical;
4179     IMXWriter *writer;
4180     VARIANT dest;
4181     HRESULT hr;
4182
4183     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4184             &IID_IMXWriter, (void**)&writer);
4185     EXPECT_HR(hr, S_OK);
4186
4187     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4188     EXPECT_HR(hr, S_OK);
4189
4190     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4191     EXPECT_HR(hr, S_OK);
4192
4193     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4194     EXPECT_HR(hr, S_OK);
4195
4196     hr = ISAXContentHandler_startDocument(content);
4197     EXPECT_HR(hr, S_OK);
4198
4199     hr = ISAXLexicalHandler_startCDATA(lexical);
4200     EXPECT_HR(hr, S_OK);
4201
4202     V_VT(&dest) = VT_EMPTY;
4203     hr = IMXWriter_get_output(writer, &dest);
4204     EXPECT_HR(hr, S_OK);
4205     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4206     ok(!lstrcmpW(_bstr_("<![CDATA["), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4207     VariantClear(&dest);
4208
4209     hr = ISAXLexicalHandler_startCDATA(lexical);
4210     EXPECT_HR(hr, S_OK);
4211
4212     /* all these are escaped for text nodes */
4213     hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
4214     EXPECT_HR(hr, S_OK);
4215
4216     hr = ISAXLexicalHandler_endCDATA(lexical);
4217     EXPECT_HR(hr, S_OK);
4218
4219     V_VT(&dest) = VT_EMPTY;
4220     hr = IMXWriter_get_output(writer, &dest);
4221     EXPECT_HR(hr, S_OK);
4222     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4223     ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4224     VariantClear(&dest);
4225
4226     ISAXContentHandler_Release(content);
4227     ISAXLexicalHandler_Release(lexical);
4228     IMXWriter_Release(writer);
4229     free_bstrs();
4230 }
4231
4232 static void test_mxwriter_pi(void)
4233 {
4234     static const WCHAR targetW[] = {'t','a','r','g','e','t',0};
4235     static const WCHAR dataW[] = {'d','a','t','a',0};
4236     ISAXContentHandler *content;
4237     IMXWriter *writer;
4238     VARIANT dest;
4239     HRESULT hr;
4240
4241     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4242             &IID_IMXWriter, (void**)&writer);
4243     EXPECT_HR(hr, S_OK);
4244
4245     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4246     EXPECT_HR(hr, S_OK);
4247
4248     hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0);
4249     EXPECT_HR(hr, E_INVALIDARG);
4250
4251     hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0);
4252     EXPECT_HR(hr, S_OK);
4253
4254     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0);
4255     EXPECT_HR(hr, S_OK);
4256
4257     V_VT(&dest) = VT_EMPTY;
4258     hr = IMXWriter_get_output(writer, &dest);
4259     EXPECT_HR(hr, S_OK);
4260     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4261     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4262     VariantClear(&dest);
4263
4264     hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4);
4265     EXPECT_HR(hr, S_OK);
4266
4267     V_VT(&dest) = VT_EMPTY;
4268     hr = IMXWriter_get_output(writer, &dest);
4269     EXPECT_HR(hr, S_OK);
4270     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4271     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n<?targ data?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4272     VariantClear(&dest);
4273
4274     V_VT(&dest) = VT_EMPTY;
4275     hr = IMXWriter_put_output(writer, dest);
4276     EXPECT_HR(hr, S_OK);
4277
4278     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0);
4279     EXPECT_HR(hr, S_OK);
4280
4281     V_VT(&dest) = VT_EMPTY;
4282     hr = IMXWriter_get_output(writer, &dest);
4283     EXPECT_HR(hr, S_OK);
4284     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4285     ok(!lstrcmpW(_bstr_("<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4286     VariantClear(&dest);
4287
4288
4289     ISAXContentHandler_Release(content);
4290     IMXWriter_Release(writer);
4291 }
4292
4293 static void test_mxwriter_ignorablespaces(void)
4294 {
4295     static const WCHAR dataW[] = {'d','a','t','a',0};
4296     ISAXContentHandler *content;
4297     IMXWriter *writer;
4298     VARIANT dest;
4299     HRESULT hr;
4300
4301     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4302             &IID_IMXWriter, (void**)&writer);
4303     EXPECT_HR(hr, S_OK);
4304
4305     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4306     EXPECT_HR(hr, S_OK);
4307
4308     hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0);
4309     EXPECT_HR(hr, E_INVALIDARG);
4310
4311     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0);
4312     EXPECT_HR(hr, S_OK);
4313
4314     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4);
4315     EXPECT_HR(hr, S_OK);
4316
4317     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1);
4318     EXPECT_HR(hr, S_OK);
4319
4320     V_VT(&dest) = VT_EMPTY;
4321     hr = IMXWriter_get_output(writer, &dest);
4322     EXPECT_HR(hr, S_OK);
4323     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4324     ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4325     VariantClear(&dest);
4326
4327     ISAXContentHandler_Release(content);
4328     IMXWriter_Release(writer);
4329 }
4330
4331 static void test_mxwriter_dtd(void)
4332 {
4333     static const WCHAR contentW[] = {'c','o','n','t','e','n','t'};
4334     static const WCHAR nameW[] = {'n','a','m','e'};
4335     static const WCHAR pubW[] = {'p','u','b'};
4336     static const WCHAR sysW[] = {'s','y','s'};
4337     ISAXContentHandler *content;
4338     ISAXLexicalHandler *lexical;
4339     ISAXDeclHandler *decl;
4340     IMXWriter *writer;
4341     VARIANT dest;
4342     HRESULT hr;
4343
4344     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4345             &IID_IMXWriter, (void**)&writer);
4346     EXPECT_HR(hr, S_OK);
4347
4348     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4349     EXPECT_HR(hr, S_OK);
4350
4351     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4352     EXPECT_HR(hr, S_OK);
4353
4354     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
4355     EXPECT_HR(hr, S_OK);
4356
4357     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4358     EXPECT_HR(hr, S_OK);
4359
4360     hr = ISAXContentHandler_startDocument(content);
4361     EXPECT_HR(hr, S_OK);
4362
4363     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
4364     EXPECT_HR(hr, E_INVALIDARG);
4365
4366     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4367     EXPECT_HR(hr, E_INVALIDARG);
4368
4369     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, sizeof(sysW)/sizeof(WCHAR));
4370     EXPECT_HR(hr, E_INVALIDARG);
4371
4372     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4373     EXPECT_HR(hr, E_INVALIDARG);
4374
4375     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0, NULL, 0);
4376     EXPECT_HR(hr, S_OK);
4377
4378     V_VT(&dest) = VT_EMPTY;
4379     hr = IMXWriter_get_output(writer, &dest);
4380     EXPECT_HR(hr, S_OK);
4381     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4382     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4383     VariantClear(&dest);
4384
4385     /* system id is required if public is present */
4386     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4387     EXPECT_HR(hr, E_INVALIDARG);
4388
4389     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR),
4390         pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4391     EXPECT_HR(hr, S_OK);
4392
4393     V_VT(&dest) = VT_EMPTY;
4394     hr = IMXWriter_get_output(writer, &dest);
4395     EXPECT_HR(hr, S_OK);
4396     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4397     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4398         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4399     VariantClear(&dest);
4400
4401     hr = ISAXLexicalHandler_endDTD(lexical);
4402     EXPECT_HR(hr, S_OK);
4403
4404     hr = ISAXLexicalHandler_endDTD(lexical);
4405     EXPECT_HR(hr, S_OK);
4406
4407     V_VT(&dest) = VT_EMPTY;
4408     hr = IMXWriter_get_output(writer, &dest);
4409     EXPECT_HR(hr, S_OK);
4410     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4411     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4412          "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
4413         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4414     VariantClear(&dest);
4415
4416     /* element declaration */
4417     V_VT(&dest) = VT_EMPTY;
4418     hr = IMXWriter_put_output(writer, dest);
4419     EXPECT_HR(hr, S_OK);
4420
4421     hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0);
4422     EXPECT_HR(hr, E_INVALIDARG);
4423
4424     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0);
4425     EXPECT_HR(hr, E_INVALIDARG);
4426
4427     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, sizeof(contentW)/sizeof(WCHAR));
4428     EXPECT_HR(hr, S_OK);
4429
4430     V_VT(&dest) = VT_EMPTY;
4431     hr = IMXWriter_get_output(writer, &dest);
4432     EXPECT_HR(hr, S_OK);
4433     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4434     ok(!lstrcmpW(_bstr_("<!ELEMENT name content>\r\n"),
4435         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4436     VariantClear(&dest);
4437
4438     V_VT(&dest) = VT_EMPTY;
4439     hr = IMXWriter_put_output(writer, dest);
4440     EXPECT_HR(hr, S_OK);
4441
4442     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, 0);
4443     EXPECT_HR(hr, S_OK);
4444
4445     V_VT(&dest) = VT_EMPTY;
4446     hr = IMXWriter_get_output(writer, &dest);
4447     EXPECT_HR(hr, S_OK);
4448     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4449     ok(!lstrcmpW(_bstr_("<!ELEMENT name >\r\n"),
4450         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4451     VariantClear(&dest);
4452
4453     /* attribute declaration */
4454     V_VT(&dest) = VT_EMPTY;
4455     hr = IMXWriter_put_output(writer, dest);
4456     EXPECT_HR(hr, S_OK);
4457
4458     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4459         _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"),
4460         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value"));
4461     EXPECT_HR(hr, S_OK);
4462
4463     V_VT(&dest) = VT_EMPTY;
4464     hr = IMXWriter_get_output(writer, &dest);
4465     EXPECT_HR(hr, S_OK);
4466     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4467     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"),
4468         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4469     VariantClear(&dest);
4470
4471     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4472         _bstr_("attribute2"), strlen("attribute2"), _bstr_("CDATA"), strlen("CDATA"),
4473         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value2"), strlen("value2"));
4474     EXPECT_HR(hr, S_OK);
4475
4476     hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"),
4477         _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"),
4478         _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3"));
4479     EXPECT_HR(hr, S_OK);
4480
4481     V_VT(&dest) = VT_EMPTY;
4482     hr = IMXWriter_get_output(writer, &dest);
4483     EXPECT_HR(hr, S_OK);
4484     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4485     ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
4486                         "<!ATTLIST element attribute2 CDATA #REQUIRED \"value2\">\r\n"
4487                         "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n"),
4488         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4489     VariantClear(&dest);
4490
4491     ISAXContentHandler_Release(content);
4492     ISAXLexicalHandler_Release(lexical);
4493     ISAXDeclHandler_Release(decl);
4494     IMXWriter_Release(writer);
4495     free_bstrs();
4496 }
4497
4498 typedef struct {
4499     const CLSID *clsid;
4500     const char *uri;
4501     const char *local;
4502     const char *qname;
4503     const char *type;
4504     const char *value;
4505     HRESULT hr;
4506 } addattribute_test_t;
4507
4508 static const addattribute_test_t addattribute_data[] = {
4509     { &CLSID_SAXAttributes,   NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4510     { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4511     { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4512     { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK },
4513
4514     { &CLSID_SAXAttributes,   NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4515     { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4516     { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4517     { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK },
4518
4519     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4520     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4521     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4522     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK },
4523
4524     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", "type", "value", S_OK },
4525     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK },
4526     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK },
4527     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK },
4528
4529     { NULL }
4530 };
4531
4532 static void test_mxattr_addAttribute(void)
4533 {
4534     const addattribute_test_t *table = addattribute_data;
4535     int i = 0;
4536
4537     while (table->clsid)
4538     {
4539         ISAXAttributes *saxattr;
4540         IMXAttributes *mxattr;
4541         const WCHAR *value;
4542         int len, index;
4543         HRESULT hr;
4544
4545         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
4546         {
4547             table++;
4548             i++;
4549             continue;
4550         }
4551
4552         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4553             &IID_IMXAttributes, (void**)&mxattr);
4554         EXPECT_HR(hr, S_OK);
4555
4556         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4557         EXPECT_HR(hr, S_OK);
4558
4559         /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4560         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4561             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4562         {
4563             hr = ISAXAttributes_getLength(saxattr, NULL);
4564             EXPECT_HR(hr, E_POINTER);
4565         }
4566
4567         len = -1;
4568         hr = ISAXAttributes_getLength(saxattr, &len);
4569         EXPECT_HR(hr, S_OK);
4570         ok(len == 0, "got %d\n", len);
4571
4572         hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4573         EXPECT_HR(hr, E_INVALIDARG);
4574
4575         hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4576         EXPECT_HR(hr, E_INVALIDARG);
4577
4578         hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4579         EXPECT_HR(hr, E_INVALIDARG);
4580
4581         hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4582         EXPECT_HR(hr, E_INVALIDARG);
4583
4584         hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4585         EXPECT_HR(hr, E_INVALIDARG);
4586
4587         hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4588         EXPECT_HR(hr, E_INVALIDARG);
4589
4590         hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4591         EXPECT_HR(hr, E_INVALIDARG);
4592
4593         hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4594         EXPECT_HR(hr, E_INVALIDARG);
4595
4596         hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local),
4597             _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value));
4598         ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
4599
4600         if (hr == S_OK)
4601         {
4602             /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4603             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4604                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4605             {
4606                hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4607                EXPECT_HR(hr, E_POINTER);
4608
4609                hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4610                EXPECT_HR(hr, E_POINTER);
4611
4612                hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4613                EXPECT_HR(hr, E_POINTER);
4614
4615                hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4616                EXPECT_HR(hr, E_POINTER);
4617
4618                hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4619                EXPECT_HR(hr, E_POINTER);
4620
4621                hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4622                EXPECT_HR(hr, E_POINTER);
4623             }
4624
4625             len = -1;
4626             hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4627             EXPECT_HR(hr, S_OK);
4628             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4629                 table->value);
4630             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4631
4632             len = -1;
4633             value = (void*)0xdeadbeef;
4634             hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4635             EXPECT_HR(hr, S_OK);
4636
4637             if (table->type)
4638             {
4639                 ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4640                     table->type);
4641                 ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len);
4642             }
4643             else
4644             {
4645                 ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value));
4646                 ok(len == 0, "%d: got wrong type value length %d\n", i, len);
4647             }
4648
4649             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL);
4650             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4651                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4652             {
4653                 EXPECT_HR(hr, E_POINTER);
4654             }
4655             else
4656                 EXPECT_HR(hr, E_INVALIDARG);
4657
4658             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index);
4659             EXPECT_HR(hr, E_INVALIDARG);
4660
4661             index = -1;
4662             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index);
4663             EXPECT_HR(hr, E_INVALIDARG);
4664             ok(index == -1, "%d: got wrong index %d\n", i, index);
4665
4666             index = -1;
4667             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index);
4668             EXPECT_HR(hr, E_INVALIDARG);
4669             ok(index == -1, "%d: got wrong index %d\n", i, index);
4670
4671             index = -1;
4672             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index);
4673             EXPECT_HR(hr, S_OK);
4674             ok(index == 0, "%d: got wrong index %d\n", i, index);
4675
4676             index = -1;
4677             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index);
4678             EXPECT_HR(hr, E_INVALIDARG);
4679             ok(index == -1, "%d: got wrong index %d\n", i, index);
4680
4681             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) ||
4682                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes60))
4683             {
4684                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4685                 EXPECT_HR(hr, E_INVALIDARG);
4686
4687                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4688                 EXPECT_HR(hr, E_INVALIDARG);
4689
4690                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4691                 EXPECT_HR(hr, E_INVALIDARG);
4692
4693                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4694                 EXPECT_HR(hr, E_INVALIDARG);
4695
4696                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4697                 EXPECT_HR(hr, E_INVALIDARG);
4698
4699                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4700                 EXPECT_HR(hr, E_INVALIDARG);
4701             }
4702             else
4703             {
4704                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4705                 EXPECT_HR(hr, E_POINTER);
4706
4707                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4708                 EXPECT_HR(hr, E_POINTER);
4709
4710                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4711                 EXPECT_HR(hr, E_POINTER);
4712
4713                 /* versions 4 and 6 crash */
4714                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL);
4715                 EXPECT_HR(hr, E_POINTER);
4716
4717                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len);
4718                 EXPECT_HR(hr, E_POINTER);
4719
4720                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4721                 EXPECT_HR(hr, E_POINTER);
4722
4723                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4724                 EXPECT_HR(hr, E_POINTER);
4725
4726                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4727                 EXPECT_HR(hr, E_POINTER);
4728
4729                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL);
4730                 EXPECT_HR(hr, E_POINTER);
4731
4732                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len);
4733                 EXPECT_HR(hr, E_POINTER);
4734
4735                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local),
4736                     strlen(table->local), NULL, NULL);
4737                 EXPECT_HR(hr, E_POINTER);
4738             }
4739
4740             hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len);
4741             EXPECT_HR(hr, S_OK);
4742             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4743                 table->value);
4744             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4745
4746             if (table->uri) {
4747                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri),
4748                     _bstr_(table->local), strlen(table->local), &value, &len);
4749                 EXPECT_HR(hr, S_OK);
4750                 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4751                     table->value);
4752                 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4753             }
4754         }
4755
4756         len = -1;
4757         hr = ISAXAttributes_getLength(saxattr, &len);
4758         EXPECT_HR(hr, S_OK);
4759         if (table->hr == S_OK)
4760             ok(len == 1, "%d: got %d length, expected 1\n", i, len);
4761         else
4762             ok(len == 0, "%d: got %d length, expected 0\n", i, len);
4763
4764         ISAXAttributes_Release(saxattr);
4765         IMXAttributes_Release(mxattr);
4766
4767         table++;
4768         i++;
4769     }
4770
4771     free_bstrs();
4772 }
4773
4774 static void test_mxattr_clear(void)
4775 {
4776     ISAXAttributes *saxattr;
4777     IMXAttributes *mxattr;
4778     const WCHAR *ptr;
4779     HRESULT hr;
4780     int len;
4781
4782     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4783         &IID_IMXAttributes, (void**)&mxattr);
4784     EXPECT_HR(hr, S_OK);
4785
4786     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4787     EXPECT_HR(hr, S_OK);
4788
4789     hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL);
4790     EXPECT_HR(hr, E_INVALIDARG);
4791
4792     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4793     EXPECT_HR(hr, E_INVALIDARG);
4794
4795     hr = IMXAttributes_clear(mxattr);
4796     EXPECT_HR(hr, S_OK);
4797
4798     hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"),
4799         _bstr_("qname"), _bstr_("type"), _bstr_("value"));
4800     EXPECT_HR(hr, S_OK);
4801
4802     len = -1;
4803     hr = ISAXAttributes_getLength(saxattr, &len);
4804     EXPECT_HR(hr, S_OK);
4805     ok(len == 1, "got %d\n", len);
4806
4807     len = -1;
4808     hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len);
4809     EXPECT_HR(hr, E_POINTER);
4810     ok(len == -1, "got %d\n", len);
4811
4812     ptr = (void*)0xdeadbeef;
4813     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL);
4814     EXPECT_HR(hr, E_POINTER);
4815     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4816
4817     len = 0;
4818     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4819     EXPECT_HR(hr, S_OK);
4820     ok(len == 5, "got %d\n", len);
4821     ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr));
4822
4823     hr = IMXAttributes_clear(mxattr);
4824     EXPECT_HR(hr, S_OK);
4825
4826     len = -1;
4827     hr = ISAXAttributes_getLength(saxattr, &len);
4828     EXPECT_HR(hr, S_OK);
4829     ok(len == 0, "got %d\n", len);
4830
4831     len = -1;
4832     ptr = (void*)0xdeadbeef;
4833     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4834     EXPECT_HR(hr, E_INVALIDARG);
4835     ok(len == -1, "got %d\n", len);
4836     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4837
4838     IMXAttributes_Release(mxattr);
4839     ISAXAttributes_Release(saxattr);
4840     free_bstrs();
4841 }
4842
4843 static void test_mxattr_dispex(void)
4844 {
4845     IMXAttributes *mxattr;
4846     IDispatchEx *dispex;
4847     IUnknown *unk;
4848     HRESULT hr;
4849
4850     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4851             &IID_IMXAttributes, (void**)&mxattr);
4852     EXPECT_HR(hr, S_OK);
4853
4854     hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex);
4855     EXPECT_HR(hr, S_OK);
4856     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4857     test_obj_dispex(unk);
4858     IUnknown_Release(unk);
4859     IDispatchEx_Release(dispex);
4860
4861     IMXAttributes_Release(mxattr);
4862 }
4863
4864 static void test_mxattr_qi(void)
4865 {
4866     IVBSAXAttributes *vbsaxattr, *vbsaxattr2;
4867     ISAXAttributes *saxattr;
4868     IMXAttributes *mxattr;
4869     HRESULT hr;
4870
4871     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4872             &IID_IMXAttributes, (void**)&mxattr);
4873     EXPECT_HR(hr, S_OK);
4874
4875     EXPECT_REF(mxattr, 1);
4876     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4877     EXPECT_HR(hr, S_OK);
4878
4879     EXPECT_REF(mxattr, 2);
4880     EXPECT_REF(saxattr, 2);
4881
4882     hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr);
4883     EXPECT_HR(hr, S_OK);
4884
4885     EXPECT_REF(vbsaxattr, 3);
4886     EXPECT_REF(mxattr, 3);
4887     EXPECT_REF(saxattr, 3);
4888
4889     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2);
4890     EXPECT_HR(hr, S_OK);
4891
4892     EXPECT_REF(vbsaxattr, 4);
4893     EXPECT_REF(mxattr, 4);
4894     EXPECT_REF(saxattr, 4);
4895
4896     IMXAttributes_Release(mxattr);
4897     ISAXAttributes_Release(saxattr);
4898     IVBSAXAttributes_Release(vbsaxattr);
4899     IVBSAXAttributes_Release(vbsaxattr2);
4900 }
4901
4902 static struct msxmlsupported_data_t saxattr_support_data[] =
4903 {
4904     { &CLSID_SAXAttributes,   "SAXAttributes"   },
4905     { &CLSID_SAXAttributes30, "SAXAttributes30" },
4906     { &CLSID_SAXAttributes40, "SAXAttributes40" },
4907     { &CLSID_SAXAttributes60, "SAXAttributes60" },
4908     { NULL }
4909 };
4910
4911 static void test_mxattr_localname(void)
4912 {
4913     static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0};
4914     static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0};
4915     static const WCHAR uri1W[] = {'u','r','i','1',0};
4916     static const WCHAR uriW[] = {'u','r','i',0};
4917
4918     const struct msxmlsupported_data_t *table = saxattr_support_data;
4919
4920     while (table->clsid)
4921     {
4922         ISAXAttributes *saxattr;
4923         IMXAttributes *mxattr;
4924         HRESULT hr;
4925         int index;
4926
4927         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
4928         {
4929             table++;
4930             continue;
4931         }
4932
4933         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4934             &IID_IMXAttributes, (void**)&mxattr);
4935         EXPECT_HR(hr, S_OK);
4936
4937         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4938         EXPECT_HR(hr, S_OK);
4939
4940         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index);
4941         EXPECT_HR(hr, E_INVALIDARG);
4942
4943         /* add some ambiguos attribute names */
4944         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
4945             _bstr_("a:localname"), _bstr_(""), _bstr_("value"));
4946         EXPECT_HR(hr, S_OK);
4947         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
4948             _bstr_("b:localname"), _bstr_(""), _bstr_("value"));
4949         EXPECT_HR(hr, S_OK);
4950
4951         index = -1;
4952         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index);
4953         EXPECT_HR(hr, S_OK);
4954         ok(index == 0, "%s: got index %d\n", table->name, index);
4955
4956         index = -1;
4957         hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index);
4958         EXPECT_HR(hr, E_INVALIDARG);
4959         ok(index == -1, "%s: got index %d\n", table->name, index);
4960
4961         index = -1;
4962         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index);
4963         EXPECT_HR(hr, E_INVALIDARG);
4964         ok(index == -1, "%s: got index %d\n", table->name, index);
4965
4966         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4967             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4968         {
4969             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
4970             EXPECT_HR(hr, E_POINTER);
4971
4972             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
4973             EXPECT_HR(hr, E_POINTER);
4974         }
4975         else
4976         {
4977             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
4978             EXPECT_HR(hr, E_INVALIDARG);
4979
4980             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
4981             EXPECT_HR(hr, E_INVALIDARG);
4982         }
4983
4984         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index);
4985         EXPECT_HR(hr, E_INVALIDARG);
4986
4987         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index);
4988         EXPECT_HR(hr, E_INVALIDARG);
4989
4990         table++;
4991
4992         ISAXAttributes_Release(saxattr);
4993         IMXAttributes_Release(mxattr);
4994     }
4995 }
4996
4997 START_TEST(saxreader)
4998 {
4999     ISAXXMLReader *reader;
5000     HRESULT hr;
5001
5002     hr = CoInitialize(NULL);
5003     ok(hr == S_OK, "failed to init com\n");
5004
5005     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
5006             &IID_ISAXXMLReader, (void**)&reader);
5007
5008     if(FAILED(hr))
5009     {
5010         skip("Failed to create SAXXMLReader instance\n");
5011         CoUninitialize();
5012         return;
5013     }
5014     ISAXXMLReader_Release(reader);
5015
5016     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
5017
5018     get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
5019
5020     test_saxreader();
5021     test_saxreader_properties();
5022     test_saxreader_features();
5023     test_saxreader_encoding();
5024     test_dispex();
5025
5026     /* MXXMLWriter tests */
5027     get_class_support_data(mxwriter_support_data, &IID_IMXWriter);
5028     if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data))
5029     {
5030         test_mxwriter_handlers();
5031         test_mxwriter_startenddocument();
5032         test_mxwriter_startendelement();
5033         test_mxwriter_characters();
5034         test_mxwriter_comment();
5035         test_mxwriter_cdata();
5036         test_mxwriter_pi();
5037         test_mxwriter_ignorablespaces();
5038         test_mxwriter_dtd();
5039         test_mxwriter_properties();
5040         test_mxwriter_flush();
5041         test_mxwriter_stream();
5042         test_mxwriter_encoding();
5043         test_mxwriter_dispex();
5044     }
5045     else
5046         win_skip("MXXMLWriter not supported\n");
5047
5048     /* SAXAttributes tests */
5049     get_class_support_data(mxattributes_support_data, &IID_IMXAttributes);
5050     if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data))
5051     {
5052         test_mxattr_qi();
5053         test_mxattr_addAttribute();
5054         test_mxattr_clear();
5055         test_mxattr_localname();
5056         test_mxattr_dispex();
5057     }
5058     else
5059         skip("SAXAttributes not supported\n");
5060
5061     CoUninitialize();
5062 }