strmbase: do not lock in BaseOutputPinImpl_GetDeliveryBuffer the MemInputPin will...
[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     "processingIntruction",
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     memset(&call, 0, sizeof(call));
960     init_call_entry(locator, &call);
961     call.id = CH_PUTDOCUMENTLOCATOR;
962     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
963
964     if (msxml_version >= 6) {
965         ISAXAttributes *attr, *attr1;
966         IMXAttributes *mxattr;
967
968         EXPECT_REF(pLocator, 1);
969         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr);
970         EXPECT_HR(hr, S_OK);
971         EXPECT_REF(pLocator, 2);
972         hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1);
973         EXPECT_HR(hr, S_OK);
974         EXPECT_REF(pLocator, 3);
975         ok(attr == attr1, "got %p, %p\n", attr, attr1);
976
977         hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
978         EXPECT_HR(hr, E_NOINTERFACE);
979
980         ISAXAttributes_Release(attr);
981         ISAXAttributes_Release(attr1);
982     }
983
984     return get_expected_ret();
985 }
986
987 static ISAXAttributes *test_attr_ptr;
988 static HRESULT WINAPI contentHandler_startDocument(
989         ISAXContentHandler* iface)
990 {
991     struct call_entry call;
992
993     init_call_entry(locator, &call);
994     call.id = CH_STARTDOCUMENT;
995     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
996
997     test_attr_ptr = NULL;
998
999     return get_expected_ret();
1000 }
1001
1002 static HRESULT WINAPI contentHandler_endDocument(
1003         ISAXContentHandler* iface)
1004 {
1005     struct call_entry call;
1006
1007     init_call_entry(locator, &call);
1008     call.id = CH_ENDDOCUMENT;
1009     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1010
1011     return get_expected_ret();
1012 }
1013
1014 static HRESULT WINAPI contentHandler_startPrefixMapping(
1015         ISAXContentHandler* iface,
1016         const WCHAR *prefix, int prefix_len,
1017         const WCHAR *uri, int uri_len)
1018 {
1019     struct call_entry call;
1020
1021     init_call_entry(locator, &call);
1022     call.id = CH_STARTPREFIXMAPPING;
1023     call.arg1W = SysAllocStringLen(prefix, prefix_len);
1024     call.arg2W = SysAllocStringLen(uri, uri_len);
1025     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1026
1027     return get_expected_ret();
1028 }
1029
1030 static HRESULT WINAPI contentHandler_endPrefixMapping(
1031         ISAXContentHandler* iface,
1032         const WCHAR *prefix, int len)
1033 {
1034     struct call_entry call;
1035
1036     init_call_entry(locator, &call);
1037     call.id = CH_ENDPREFIXMAPPING;
1038     call.arg1W = SysAllocStringLen(prefix, len);
1039     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1040
1041     return get_expected_ret();
1042 }
1043
1044 static HRESULT WINAPI contentHandler_startElement(
1045         ISAXContentHandler* iface,
1046         const WCHAR *uri, int uri_len,
1047         const WCHAR *localname, int local_len,
1048         const WCHAR *qname, int qname_len,
1049         ISAXAttributes *saxattr)
1050 {
1051     struct call_entry call;
1052     IMXAttributes *mxattr;
1053     HRESULT hr;
1054     int len;
1055
1056     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
1057     EXPECT_HR(hr, E_NOINTERFACE);
1058
1059     init_call_entry(locator, &call);
1060     call.id = CH_STARTELEMENT;
1061     call.arg1W = SysAllocStringLen(uri, uri_len);
1062     call.arg2W = SysAllocStringLen(localname, local_len);
1063     call.arg3W = SysAllocStringLen(qname, qname_len);
1064
1065     if(!test_attr_ptr)
1066         test_attr_ptr = saxattr;
1067     ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
1068
1069     /* store actual attributes */
1070     len = 0;
1071     hr = ISAXAttributes_getLength(saxattr, &len);
1072     EXPECT_HR(hr, S_OK);
1073
1074     if (len)
1075     {
1076         VARIANT_BOOL v;
1077         int i;
1078
1079         struct attribute_entry *attr;
1080         attr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(struct attribute_entry));
1081
1082         v = VARIANT_TRUE;
1083         hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
1084         EXPECT_HR(hr, S_OK);
1085
1086         for (i = 0; i < len; i++)
1087         {
1088             const WCHAR *value;
1089             int value_len;
1090
1091             hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
1092                 &localname, &local_len, &qname, &qname_len);
1093             EXPECT_HR(hr, S_OK);
1094
1095             hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
1096             EXPECT_HR(hr, S_OK);
1097
1098             /* if 'namespaces' switched off uri and local name contains garbage */
1099             if (v == VARIANT_FALSE && msxml_version > 0)
1100             {
1101                 attr[i].uriW   = SysAllocStringLen(NULL, 0);
1102                 attr[i].localW = SysAllocStringLen(NULL, 0);
1103             }
1104             else
1105             {
1106                 attr[i].uriW   = SysAllocStringLen(uri, uri_len);
1107                 attr[i].localW = SysAllocStringLen(localname, local_len);
1108             }
1109
1110             attr[i].qnameW = SysAllocStringLen(qname, qname_len);
1111             attr[i].valueW = SysAllocStringLen(value, value_len);
1112         }
1113
1114         call.attributes = attr;
1115         call.attr_count = len;
1116     }
1117
1118     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1119
1120     return get_expected_ret();
1121 }
1122
1123 static HRESULT WINAPI contentHandler_endElement(
1124         ISAXContentHandler* iface,
1125         const WCHAR *uri, int uri_len,
1126         const WCHAR *localname, int local_len,
1127         const WCHAR *qname, int qname_len)
1128 {
1129     struct call_entry call;
1130
1131     init_call_entry(locator, &call);
1132     call.id = CH_ENDELEMENT;
1133     call.arg1W = SysAllocStringLen(uri, uri_len);
1134     call.arg2W = SysAllocStringLen(localname, local_len);
1135     call.arg3W = SysAllocStringLen(qname, qname_len);
1136     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1137
1138     return get_expected_ret();
1139 }
1140
1141 static HRESULT WINAPI contentHandler_characters(
1142         ISAXContentHandler* iface,
1143         const WCHAR *chars,
1144         int len)
1145 {
1146     struct call_entry call;
1147
1148     init_call_entry(locator, &call);
1149     call.id = CH_CHARACTERS;
1150     call.arg1W = SysAllocStringLen(chars, len);
1151     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1152
1153     return get_expected_ret();
1154 }
1155
1156 static HRESULT WINAPI contentHandler_ignorableWhitespace(
1157         ISAXContentHandler* iface,
1158         const WCHAR *chars, int len)
1159 {
1160     struct call_entry call;
1161
1162     init_call_entry(locator, &call);
1163     call.id = CH_IGNORABLEWHITESPACE;
1164     call.arg1W = SysAllocStringLen(chars, len);
1165     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1166
1167     return get_expected_ret();
1168 }
1169
1170 static HRESULT WINAPI contentHandler_processingInstruction(
1171         ISAXContentHandler* iface,
1172         const WCHAR *target, int target_len,
1173         const WCHAR *data, int data_len)
1174 {
1175     struct call_entry call;
1176
1177     init_call_entry(locator, &call);
1178     call.id = CH_PROCESSINGINSTRUCTION;
1179     call.arg1W = SysAllocStringLen(target, target_len);
1180     call.arg2W = SysAllocStringLen(data, data_len);
1181     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1182
1183     return get_expected_ret();
1184 }
1185
1186 static HRESULT WINAPI contentHandler_skippedEntity(
1187         ISAXContentHandler* iface,
1188         const WCHAR *name, int len)
1189 {
1190     struct call_entry call;
1191
1192     init_call_entry(locator, &call);
1193     call.id = CH_SKIPPEDENTITY;
1194     call.arg1W = SysAllocStringLen(name, len);
1195
1196     return get_expected_ret();
1197 }
1198
1199 static const ISAXContentHandlerVtbl contentHandlerVtbl =
1200 {
1201     contentHandler_QueryInterface,
1202     contentHandler_AddRef,
1203     contentHandler_Release,
1204     contentHandler_putDocumentLocator,
1205     contentHandler_startDocument,
1206     contentHandler_endDocument,
1207     contentHandler_startPrefixMapping,
1208     contentHandler_endPrefixMapping,
1209     contentHandler_startElement,
1210     contentHandler_endElement,
1211     contentHandler_characters,
1212     contentHandler_ignorableWhitespace,
1213     contentHandler_processingInstruction,
1214     contentHandler_skippedEntity
1215 };
1216
1217 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
1218
1219 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
1220         ISAXErrorHandler* iface,
1221         REFIID riid,
1222         void **ppvObject)
1223 {
1224     *ppvObject = NULL;
1225
1226     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
1227     {
1228         *ppvObject = iface;
1229     }
1230     else
1231     {
1232         return E_NOINTERFACE;
1233     }
1234
1235     return S_OK;
1236 }
1237
1238 static ULONG WINAPI isaxerrorHandler_AddRef(
1239         ISAXErrorHandler* iface)
1240 {
1241     return 2;
1242 }
1243
1244 static ULONG WINAPI isaxerrorHandler_Release(
1245         ISAXErrorHandler* iface)
1246 {
1247     return 1;
1248 }
1249
1250 static HRESULT WINAPI isaxerrorHandler_error(
1251         ISAXErrorHandler* iface,
1252         ISAXLocator *pLocator,
1253         const WCHAR *pErrorMessage,
1254         HRESULT hrErrorCode)
1255 {
1256     ok(0, "unexpected call\n");
1257     return S_OK;
1258 }
1259
1260 static HRESULT WINAPI isaxerrorHandler_fatalError(
1261         ISAXErrorHandler* iface,
1262         ISAXLocator *pLocator,
1263         const WCHAR *message,
1264         HRESULT hr)
1265 {
1266     struct call_entry call;
1267
1268     init_call_entry(locator, &call);
1269     call.id  = EH_FATALERROR;
1270     call.ret = hr;
1271
1272     add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1273
1274     get_expected_ret();
1275     return S_OK;
1276 }
1277
1278 static HRESULT WINAPI isaxerrorHanddler_ignorableWarning(
1279         ISAXErrorHandler* iface,
1280         ISAXLocator *pLocator,
1281         const WCHAR *pErrorMessage,
1282         HRESULT hrErrorCode)
1283 {
1284     ok(0, "unexpected call\n");
1285     return S_OK;
1286 }
1287
1288 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
1289 {
1290     isaxerrorHandler_QueryInterface,
1291     isaxerrorHandler_AddRef,
1292     isaxerrorHandler_Release,
1293     isaxerrorHandler_error,
1294     isaxerrorHandler_fatalError,
1295     isaxerrorHanddler_ignorableWarning
1296 };
1297
1298 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
1299
1300 static HRESULT WINAPI isaxattributes_QueryInterface(
1301         ISAXAttributes* iface,
1302         REFIID riid,
1303         void **ppvObject)
1304 {
1305     *ppvObject = NULL;
1306
1307     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
1308     {
1309         *ppvObject = iface;
1310     }
1311     else
1312     {
1313         return E_NOINTERFACE;
1314     }
1315
1316     return S_OK;
1317 }
1318
1319 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
1320 {
1321     return 2;
1322 }
1323
1324 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
1325 {
1326     return 1;
1327 }
1328
1329 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
1330 {
1331     *length = 3;
1332     return S_OK;
1333 }
1334
1335 static HRESULT WINAPI isaxattributes_getURI(
1336     ISAXAttributes* iface,
1337     int nIndex,
1338     const WCHAR **pUrl,
1339     int *pUriSize)
1340 {
1341     ok(0, "unexpected call\n");
1342     return E_NOTIMPL;
1343 }
1344
1345 static HRESULT WINAPI isaxattributes_getLocalName(
1346     ISAXAttributes* iface,
1347     int nIndex,
1348     const WCHAR **pLocalName,
1349     int *pLocalNameLength)
1350 {
1351     ok(0, "unexpected call\n");
1352     return E_NOTIMPL;
1353 }
1354
1355 static HRESULT WINAPI isaxattributes_getQName(
1356     ISAXAttributes* iface,
1357     int index,
1358     const WCHAR **QName,
1359     int *QNameLength)
1360 {
1361     static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
1362                                             {'a','t','t','r','2','j','u','n','k',0},
1363                                             {'a','t','t','r','3',0}};
1364     static const int attrqnamelen[] = {7, 5, 5};
1365
1366     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1367
1368     *QName = attrqnamesW[index];
1369     *QNameLength = attrqnamelen[index];
1370
1371     return S_OK;
1372 }
1373
1374 static HRESULT WINAPI isaxattributes_getName(
1375     ISAXAttributes* iface,
1376     int nIndex,
1377     const WCHAR **pUri,
1378     int * pUriLength,
1379     const WCHAR ** pLocalName,
1380     int * pLocalNameSize,
1381     const WCHAR ** pQName,
1382     int * pQNameLength)
1383 {
1384     ok(0, "unexpected call\n");
1385     return E_NOTIMPL;
1386 }
1387
1388 static HRESULT WINAPI isaxattributes_getIndexFromName(
1389     ISAXAttributes* iface,
1390     const WCHAR * pUri,
1391     int cUriLength,
1392     const WCHAR * pLocalName,
1393     int cocalNameLength,
1394     int * index)
1395 {
1396     ok(0, "unexpected call\n");
1397     return E_NOTIMPL;
1398 }
1399
1400 static HRESULT WINAPI isaxattributes_getIndexFromQName(
1401     ISAXAttributes* iface,
1402     const WCHAR * pQName,
1403     int nQNameLength,
1404     int * index)
1405 {
1406     ok(0, "unexpected call\n");
1407     return E_NOTIMPL;
1408 }
1409
1410 static HRESULT WINAPI isaxattributes_getType(
1411     ISAXAttributes* iface,
1412     int nIndex,
1413     const WCHAR ** pType,
1414     int * pTypeLength)
1415 {
1416     ok(0, "unexpected call\n");
1417     return E_NOTIMPL;
1418 }
1419
1420 static HRESULT WINAPI isaxattributes_getTypeFromName(
1421     ISAXAttributes* iface,
1422     const WCHAR * pUri,
1423     int nUri,
1424     const WCHAR * pLocalName,
1425     int nLocalName,
1426     const WCHAR ** pType,
1427     int * nType)
1428 {
1429     ok(0, "unexpected call\n");
1430     return E_NOTIMPL;
1431 }
1432
1433 static HRESULT WINAPI isaxattributes_getTypeFromQName(
1434     ISAXAttributes* iface,
1435     const WCHAR * pQName,
1436     int nQName,
1437     const WCHAR ** pType,
1438     int * nType)
1439 {
1440     ok(0, "unexpected call\n");
1441     return E_NOTIMPL;
1442 }
1443
1444 static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
1445     const WCHAR **value, int *nValue)
1446 {
1447     static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
1448                                             {'a','2','j','u','n','k',0},
1449                                             {'<','&','"','>',0}};
1450     static const int attrvalueslen[] = {2, 2, 4};
1451
1452     ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1453
1454     *value = attrvaluesW[index];
1455     *nValue = attrvalueslen[index];
1456
1457     return S_OK;
1458 }
1459
1460 static HRESULT WINAPI isaxattributes_getValueFromName(
1461     ISAXAttributes* iface,
1462     const WCHAR * pUri,
1463     int nUri,
1464     const WCHAR * pLocalName,
1465     int nLocalName,
1466     const WCHAR ** pValue,
1467     int * nValue)
1468 {
1469     ok(0, "unexpected call\n");
1470     return E_NOTIMPL;
1471 }
1472
1473 static HRESULT WINAPI isaxattributes_getValueFromQName(
1474     ISAXAttributes* iface,
1475     const WCHAR * pQName,
1476     int nQName,
1477     const WCHAR ** pValue,
1478     int * nValue)
1479 {
1480     ok(0, "unexpected call\n");
1481     return E_NOTIMPL;
1482 }
1483
1484 static const ISAXAttributesVtbl SAXAttributesVtbl =
1485 {
1486     isaxattributes_QueryInterface,
1487     isaxattributes_AddRef,
1488     isaxattributes_Release,
1489     isaxattributes_getLength,
1490     isaxattributes_getURI,
1491     isaxattributes_getLocalName,
1492     isaxattributes_getQName,
1493     isaxattributes_getName,
1494     isaxattributes_getIndexFromName,
1495     isaxattributes_getIndexFromQName,
1496     isaxattributes_getType,
1497     isaxattributes_getTypeFromName,
1498     isaxattributes_getTypeFromQName,
1499     isaxattributes_getValue,
1500     isaxattributes_getValueFromName,
1501     isaxattributes_getValueFromQName
1502 };
1503
1504 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
1505
1506 struct saxlexicalhandler
1507 {
1508     ISAXLexicalHandler ISAXLexicalHandler_iface;
1509     LONG ref;
1510
1511     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1512 };
1513
1514 static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface )
1515 {
1516     return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
1517 }
1518
1519 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
1520 {
1521     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1522
1523     *out = NULL;
1524
1525     if (IsEqualGUID(riid, &IID_IUnknown))
1526     {
1527         *out = iface;
1528         ok(0, "got unexpected IID_IUnknown query\n");
1529     }
1530     else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
1531     {
1532         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1533         *out = iface;
1534     }
1535
1536     if (*out)
1537         ISAXLexicalHandler_AddRef(iface);
1538     else
1539         return E_NOINTERFACE;
1540
1541     return S_OK;
1542 }
1543
1544 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
1545 {
1546     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1547     return InterlockedIncrement(&handler->ref);
1548 }
1549
1550 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
1551 {
1552     struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1553     return InterlockedDecrement(&handler->ref);
1554 }
1555
1556 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
1557     const WCHAR * pName, int nName, const WCHAR * pPublicId,
1558     int nPublicId, const WCHAR * pSystemId, int nSystemId)
1559 {
1560     ok(0, "call not expected\n");
1561     return E_NOTIMPL;
1562 }
1563
1564 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
1565 {
1566     ok(0, "call not expected\n");
1567     return E_NOTIMPL;
1568 }
1569
1570 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
1571     const WCHAR * pName, int nName)
1572 {
1573     ok(0, "call not expected\n");
1574     return E_NOTIMPL;
1575 }
1576
1577 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
1578     const WCHAR * pName, int nName)
1579 {
1580     ok(0, "call not expected\n");
1581     return E_NOTIMPL;
1582 }
1583
1584 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
1585 {
1586     ok(0, "call not expected\n");
1587     return E_NOTIMPL;
1588 }
1589
1590 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
1591 {
1592     ok(0, "call not expected\n");
1593     return E_NOTIMPL;
1594 }
1595
1596 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
1597     const WCHAR * pChars, int nChars)
1598 {
1599     ok(0, "call not expected\n");
1600     return E_NOTIMPL;
1601 }
1602
1603 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1604 {
1605    isaxlexical_QueryInterface,
1606    isaxlexical_AddRef,
1607    isaxlexical_Release,
1608    isaxlexical_startDTD,
1609    isaxlexical_endDTD,
1610    isaxlexical_startEntity,
1611    isaxlexical_endEntity,
1612    isaxlexical_startCDATA,
1613    isaxlexical_endCDATA,
1614    isaxlexical_comment
1615 };
1616
1617 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
1618 {
1619     handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1620     handler->ref = 1;
1621     handler->qi_hr = hr;
1622 }
1623
1624 struct saxdeclhandler
1625 {
1626     ISAXDeclHandler ISAXDeclHandler_iface;
1627     LONG ref;
1628
1629     HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1630 };
1631
1632 static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface )
1633 {
1634     return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface);
1635 }
1636
1637 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out)
1638 {
1639     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1640
1641     *out = NULL;
1642
1643     if (IsEqualGUID(riid, &IID_IUnknown))
1644     {
1645         *out = iface;
1646         ok(0, "got unexpected IID_IUnknown query\n");
1647     }
1648     else if (IsEqualGUID(riid, &IID_ISAXDeclHandler))
1649     {
1650         if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1651         *out = iface;
1652     }
1653
1654     if (*out)
1655         ISAXDeclHandler_AddRef(iface);
1656     else
1657         return E_NOINTERFACE;
1658
1659     return S_OK;
1660 }
1661
1662 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
1663 {
1664     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1665     return InterlockedIncrement(&handler->ref);
1666 }
1667
1668 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
1669 {
1670     struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1671     return InterlockedDecrement(&handler->ref);
1672 }
1673
1674 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
1675     const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
1676 {
1677     ok(0, "call not expected\n");
1678     return E_NOTIMPL;
1679 }
1680
1681 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
1682     const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
1683     int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
1684     int nValueDefault, const WCHAR * pValue, int nValue)
1685 {
1686     ok(0, "call not expected\n");
1687     return E_NOTIMPL;
1688 }
1689
1690 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
1691     const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
1692 {
1693     ok(0, "call not expected\n");
1694     return E_NOTIMPL;
1695 }
1696
1697 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
1698     const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
1699     const WCHAR * pSystemId, int nSystemId)
1700 {
1701     ok(0, "call not expected\n");
1702     return E_NOTIMPL;
1703 }
1704
1705 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
1706 {
1707    isaxdecl_QueryInterface,
1708    isaxdecl_AddRef,
1709    isaxdecl_Release,
1710    isaxdecl_elementDecl,
1711    isaxdecl_attributeDecl,
1712    isaxdecl_internalEntityDecl,
1713    isaxdecl_externalEntityDecl
1714 };
1715
1716 static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr)
1717 {
1718     handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1719     handler->ref = 1;
1720     handler->qi_hr = hr;
1721 }
1722
1723 typedef struct mxwriter_write_test_t {
1724     BOOL        last;
1725     const BYTE  *data;
1726     DWORD       cb;
1727     BOOL        null_written;
1728     BOOL        fail_write;
1729 } mxwriter_write_test;
1730
1731 typedef struct mxwriter_stream_test_t {
1732     VARIANT_BOOL        bom;
1733     const char          *encoding;
1734     mxwriter_write_test expected_writes[4];
1735 } mxwriter_stream_test;
1736
1737 static const mxwriter_write_test *current_write_test;
1738 static DWORD current_stream_test_index;
1739
1740 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
1741 {
1742     *ppvObject = NULL;
1743
1744     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
1745         *ppvObject = iface;
1746     else
1747         return E_NOINTERFACE;
1748
1749     return S_OK;
1750 }
1751
1752 static ULONG WINAPI istream_AddRef(IStream *iface)
1753 {
1754     return 2;
1755 }
1756
1757 static ULONG WINAPI istream_Release(IStream *iface)
1758 {
1759     return 1;
1760 }
1761
1762 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
1763 {
1764     ok(0, "unexpected call\n");
1765     return E_NOTIMPL;
1766 }
1767
1768 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1769 {
1770     BOOL fail = FALSE;
1771
1772     ok(pv != NULL, "pv == NULL\n");
1773
1774     if(current_write_test->last) {
1775         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
1776         return E_FAIL;
1777     }
1778
1779     fail = current_write_test->fail_write;
1780
1781     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
1782         current_write_test->cb, cb, current_stream_test_index);
1783
1784     if(!pcbWritten)
1785         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
1786     else
1787         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
1788
1789     ++current_write_test;
1790
1791     if(pcbWritten)
1792         *pcbWritten = cb;
1793
1794     return fail ? E_FAIL : S_OK;
1795 }
1796
1797 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
1798         ULARGE_INTEGER *plibNewPosition)
1799 {
1800     ok(0, "unexpected call\n");
1801     return E_NOTIMPL;
1802 }
1803
1804 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
1805 {
1806     ok(0, "unexpected call\n");
1807     return E_NOTIMPL;
1808 }
1809
1810 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
1811         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
1812 {
1813     ok(0, "unexpected call\n");
1814     return E_NOTIMPL;
1815 }
1816
1817 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
1818 {
1819     ok(0, "unexpected call\n");
1820     return E_NOTIMPL;
1821 }
1822
1823 static HRESULT WINAPI istream_Revert(IStream *iface)
1824 {
1825     ok(0, "unexpected call\n");
1826     return E_NOTIMPL;
1827 }
1828
1829 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1830         ULARGE_INTEGER cb, DWORD dwLockType)
1831 {
1832     ok(0, "unexpected call\n");
1833     return E_NOTIMPL;
1834 }
1835
1836 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1837         ULARGE_INTEGER cb, DWORD dwLockType)
1838 {
1839     ok(0, "unexpected call\n");
1840     return E_NOTIMPL;
1841 }
1842
1843 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
1844 {
1845     ok(0, "unexpected call\n");
1846     return E_NOTIMPL;
1847 }
1848
1849 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
1850 {
1851     ok(0, "unexpected call\n");
1852     return E_NOTIMPL;
1853 }
1854
1855 static const IStreamVtbl StreamVtbl = {
1856     istream_QueryInterface,
1857     istream_AddRef,
1858     istream_Release,
1859     istream_Read,
1860     istream_Write,
1861     istream_Seek,
1862     istream_SetSize,
1863     istream_CopyTo,
1864     istream_Commit,
1865     istream_Revert,
1866     istream_LockRegion,
1867     istream_UnlockRegion,
1868     istream_Stat,
1869     istream_Clone
1870 };
1871
1872 static IStream mxstream = { &StreamVtbl };
1873
1874 static struct msxmlsupported_data_t reader_support_data[] =
1875 {
1876     { &CLSID_SAXXMLReader,   "SAXReader"   },
1877     { &CLSID_SAXXMLReader30, "SAXReader30" },
1878     { &CLSID_SAXXMLReader40, "SAXReader40" },
1879     { &CLSID_SAXXMLReader60, "SAXReader60" },
1880     { NULL }
1881 };
1882
1883 static void test_saxreader(void)
1884 {
1885     const struct msxmlsupported_data_t *table = reader_support_data;
1886     HRESULT hr;
1887     ISAXXMLReader *reader = NULL;
1888     VARIANT var;
1889     ISAXContentHandler *content;
1890     ISAXErrorHandler *lpErrorHandler;
1891     SAFEARRAY *sa;
1892     SAFEARRAYBOUND SADim[1];
1893     char *ptr = NULL;
1894     IStream *stream;
1895     ULARGE_INTEGER size;
1896     LARGE_INTEGER pos;
1897     ULONG written;
1898     HANDLE file;
1899     static const CHAR testXmlA[] = "test.xml";
1900     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1901     IXMLDOMDocument *doc;
1902     VARIANT_BOOL v;
1903
1904     while (table->clsid)
1905     {
1906         struct call_entry *test_seq;
1907         BSTR str;
1908
1909         if (!is_clsid_supported(table->clsid, reader_support_data))
1910         {
1911             table++;
1912             continue;
1913         }
1914
1915         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1916         EXPECT_HR(hr, S_OK);
1917         g_reader = reader;
1918
1919         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
1920             msxml_version = 4;
1921         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1922             msxml_version = 6;
1923         else
1924             msxml_version = 0;
1925
1926         /* crashes on old versions */
1927         if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
1928             !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1929         {
1930             hr = ISAXXMLReader_getContentHandler(reader, NULL);
1931             EXPECT_HR(hr, E_POINTER);
1932
1933             hr = ISAXXMLReader_getErrorHandler(reader, NULL);
1934             EXPECT_HR(hr, E_POINTER);
1935         }
1936
1937         hr = ISAXXMLReader_getContentHandler(reader, &content);
1938         EXPECT_HR(hr, S_OK);
1939         ok(content == NULL, "Expected %p, got %p\n", NULL, content);
1940
1941         hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
1942         EXPECT_HR(hr, S_OK);
1943         ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
1944
1945         hr = ISAXXMLReader_putContentHandler(reader, NULL);
1946         EXPECT_HR(hr, S_OK);
1947
1948         hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
1949         EXPECT_HR(hr, S_OK);
1950
1951         hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
1952         EXPECT_HR(hr, S_OK);
1953
1954         hr = ISAXXMLReader_getContentHandler(reader, &content);
1955         EXPECT_HR(hr, S_OK);
1956         ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
1957
1958         V_VT(&var) = VT_BSTR;
1959         V_BSTR(&var) = SysAllocString(szSimpleXML);
1960
1961         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
1962             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1963             test_seq = content_handler_test1_alternate;
1964         else
1965             test_seq = content_handler_test1;
1966         set_expected_seq(test_seq);
1967         hr = ISAXXMLReader_parse(reader, var);
1968         EXPECT_HR(hr, S_OK);
1969         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
1970
1971         VariantClear(&var);
1972
1973         SADim[0].lLbound = 0;
1974         SADim[0].cElements = sizeof(testXML)-1;
1975         sa = SafeArrayCreate(VT_UI1, 1, SADim);
1976         SafeArrayAccessData(sa, (void**)&ptr);
1977         memcpy(ptr, testXML, sizeof(testXML)-1);
1978         SafeArrayUnaccessData(sa);
1979         V_VT(&var) = VT_ARRAY|VT_UI1;
1980         V_ARRAY(&var) = sa;
1981
1982         set_expected_seq(test_seq);
1983         hr = ISAXXMLReader_parse(reader, var);
1984         EXPECT_HR(hr, S_OK);
1985         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
1986
1987         SafeArrayDestroy(sa);
1988
1989         CreateStreamOnHGlobal(NULL, TRUE, &stream);
1990         size.QuadPart = strlen(testXML);
1991         IStream_SetSize(stream, size);
1992         IStream_Write(stream, testXML, strlen(testXML), &written);
1993         pos.QuadPart = 0;
1994         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
1995         V_VT(&var) = VT_UNKNOWN;
1996         V_UNKNOWN(&var) = (IUnknown*)stream;
1997
1998         set_expected_seq(test_seq);
1999         hr = ISAXXMLReader_parse(reader, var);
2000         EXPECT_HR(hr, S_OK);
2001         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
2002
2003         IStream_Release(stream);
2004
2005         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2006         size.QuadPart = strlen(test_attributes);
2007         IStream_SetSize(stream, size);
2008         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2009         pos.QuadPart = 0;
2010         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2011         V_VT(&var) = VT_UNKNOWN;
2012         V_UNKNOWN(&var) = (IUnknown*)stream;
2013
2014         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2015             test_seq = content_handler_test_attributes_alternate_4;
2016         else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2017             test_seq = content_handler_test_attributes_alternate_6;
2018         else
2019             test_seq = content_handler_test_attributes;
2020
2021         set_expected_seq(test_seq);
2022         hr = ISAXXMLReader_parse(reader, var);
2023         EXPECT_HR(hr, S_OK);
2024
2025         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2026             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2027             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2028         else
2029             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2030
2031         IStream_Release(stream);
2032
2033         V_VT(&var) = VT_BSTR;
2034         V_BSTR(&var) = SysAllocString(carriage_ret_test);
2035
2036         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2037             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2038             test_seq = content_handler_test2_alternate;
2039         else
2040             test_seq = content_handler_test2;
2041
2042         set_expected_seq(test_seq);
2043         hr = ISAXXMLReader_parse(reader, var);
2044         EXPECT_HR(hr, S_OK);
2045         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
2046
2047         VariantClear(&var);
2048
2049         /* from file url */
2050         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2051         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2052         WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
2053         CloseHandle(file);
2054
2055         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2056             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2057             test_seq = content_handler_test1_alternate;
2058         else
2059             test_seq = content_handler_test1;
2060         set_expected_seq(test_seq);
2061         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2062         EXPECT_HR(hr, S_OK);
2063         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
2064
2065         /* error handler */
2066         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2067             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2068             test_seq = content_handler_testerror_alternate;
2069         else
2070             test_seq = content_handler_testerror;
2071         set_expected_seq(test_seq);
2072         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2073         EXPECT_HR(hr, E_FAIL);
2074         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
2075
2076         /* callback ret values */
2077         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2078             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2079         {
2080             test_seq = content_handler_test_callback_rets_alt;
2081             set_expected_seq(test_seq);
2082             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2083             EXPECT_HR(hr, S_OK);
2084         }
2085         else
2086         {
2087             test_seq = content_handler_test_callback_rets;
2088             set_expected_seq(test_seq);
2089             hr = ISAXXMLReader_parseURL(reader, testXmlW);
2090             EXPECT_HR(hr, S_FALSE);
2091         }
2092         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
2093
2094         DeleteFileA(testXmlA);
2095
2096         /* parse from IXMLDOMDocument */
2097         hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
2098                 &IID_IXMLDOMDocument, (void**)&doc);
2099         EXPECT_HR(hr, S_OK);
2100
2101         str = SysAllocString(szSimpleXML);
2102         hr = IXMLDOMDocument_loadXML(doc, str, &v);
2103         EXPECT_HR(hr, S_OK);
2104         SysFreeString(str);
2105
2106         V_VT(&var) = VT_UNKNOWN;
2107         V_UNKNOWN(&var) = (IUnknown*)doc;
2108
2109         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2110             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2111             test_seq = content_handler_test2_alternate;
2112         else
2113             test_seq = content_handler_test2;
2114
2115         set_expected_seq(test_seq);
2116         hr = ISAXXMLReader_parse(reader, var);
2117         EXPECT_HR(hr, S_OK);
2118         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
2119         IXMLDOMDocument_Release(doc);
2120
2121         /* xml:space test */
2122         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2123             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2124         {
2125             test_seq = xmlspaceattr_test_alternate;
2126         }
2127         else
2128             test_seq = xmlspaceattr_test;
2129
2130         set_expected_seq(test_seq);
2131         V_VT(&var) = VT_BSTR;
2132         V_BSTR(&var) = _bstr_(xmlspace_attr);
2133         hr = ISAXXMLReader_parse(reader, var);
2134         EXPECT_HR(hr, S_OK);
2135
2136         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2137             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2138         {
2139             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
2140         }
2141         else
2142             ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
2143
2144         /* switch off 'namespaces' feature */
2145         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
2146         EXPECT_HR(hr, S_OK);
2147
2148         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2149         size.QuadPart = strlen(test_attributes);
2150         IStream_SetSize(stream, size);
2151         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2152         pos.QuadPart = 0;
2153         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2154         V_VT(&var) = VT_UNKNOWN;
2155         V_UNKNOWN(&var) = (IUnknown*)stream;
2156
2157         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2158             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2159         {
2160             test_seq = content_handler_test_attributes_alt_no_ns;
2161         }
2162         else
2163             test_seq = content_handler_test_attributes;
2164
2165         set_expected_seq(test_seq);
2166         hr = ISAXXMLReader_parse(reader, var);
2167         EXPECT_HR(hr, S_OK);
2168         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2169         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
2170         EXPECT_HR(hr, S_OK);
2171
2172         /* switch off 'namespace-prefixes' feature */
2173         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
2174         EXPECT_HR(hr, S_OK);
2175
2176         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2177         size.QuadPart = strlen(test_attributes);
2178         IStream_SetSize(stream, size);
2179         IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2180         pos.QuadPart = 0;
2181         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2182         V_VT(&var) = VT_UNKNOWN;
2183         V_UNKNOWN(&var) = (IUnknown*)stream;
2184
2185         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2186             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2187         {
2188             test_seq = content_handler_test_attributes_alt_no_prefix;
2189         }
2190         else
2191             test_seq = content_handler_test_attributes_no_prefix;
2192
2193         set_expected_seq(test_seq);
2194         hr = ISAXXMLReader_parse(reader, var);
2195         EXPECT_HR(hr, S_OK);
2196         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2197
2198         hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
2199         EXPECT_HR(hr, S_OK);
2200
2201         /* attribute normalization */
2202         CreateStreamOnHGlobal(NULL, TRUE, &stream);
2203         size.QuadPart = strlen(attribute_normalize);
2204         IStream_SetSize(stream, size);
2205         IStream_Write(stream, attribute_normalize, strlen(attribute_normalize), &written);
2206         pos.QuadPart = 0;
2207         IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2208         V_VT(&var) = VT_UNKNOWN;
2209         V_UNKNOWN(&var) = (IUnknown*)stream;
2210
2211         if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2212             IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2213         {
2214             test_seq = attribute_norm_alt;
2215         }
2216         else
2217             test_seq = attribute_norm;
2218
2219         set_expected_seq(test_seq);
2220         hr = ISAXXMLReader_parse(reader, var);
2221         EXPECT_HR(hr, S_OK);
2222         ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
2223
2224         ISAXXMLReader_Release(reader);
2225         table++;
2226     }
2227
2228     free_bstrs();
2229 }
2230
2231 struct saxreader_props_test_t
2232 {
2233     const char *prop_name;
2234     IUnknown   *iface;
2235 };
2236
2237 static struct saxlexicalhandler lexicalhandler;
2238 static struct saxdeclhandler declhandler;
2239
2240 static const struct saxreader_props_test_t props_test_data[] = {
2241     { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface },
2242     { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface },
2243     { 0 }
2244 };
2245
2246 static void test_saxreader_properties(void)
2247 {
2248     const struct saxreader_props_test_t *ptr = props_test_data;
2249     ISAXXMLReader *reader;
2250     HRESULT hr;
2251
2252     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2253             &IID_ISAXXMLReader, (void**)&reader);
2254     EXPECT_HR(hr, S_OK);
2255
2256     hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
2257     EXPECT_HR(hr, E_POINTER);
2258
2259     while (ptr->prop_name)
2260     {
2261         VARIANT v;
2262         LONG ref;
2263
2264         init_saxlexicalhandler(&lexicalhandler, S_OK);
2265         init_saxdeclhandler(&declhandler, S_OK);
2266
2267         V_VT(&v) = VT_EMPTY;
2268         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2269         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2270         EXPECT_HR(hr, S_OK);
2271         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2272         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2273
2274         V_VT(&v) = VT_UNKNOWN;
2275         V_UNKNOWN(&v) = ptr->iface;
2276         ref = get_refcount(ptr->iface);
2277         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2278         EXPECT_HR(hr, S_OK);
2279         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2280
2281         V_VT(&v) = VT_EMPTY;
2282         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2283
2284         ref = get_refcount(ptr->iface);
2285         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2286         EXPECT_HR(hr, S_OK);
2287         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2288         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2289         ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2290         VariantClear(&v);
2291
2292         V_VT(&v) = VT_EMPTY;
2293         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2294         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2295         EXPECT_HR(hr, S_OK);
2296
2297         V_VT(&v) = VT_EMPTY;
2298         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2299         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2300         EXPECT_HR(hr, S_OK);
2301         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2302         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2303
2304         V_VT(&v) = VT_UNKNOWN;
2305         V_UNKNOWN(&v) = ptr->iface;
2306         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2307         EXPECT_HR(hr, S_OK);
2308
2309         /* only VT_EMPTY seems to be valid to reset property */
2310         V_VT(&v) = VT_I4;
2311         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2312         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2313         EXPECT_HR(hr, E_INVALIDARG);
2314
2315         V_VT(&v) = VT_EMPTY;
2316         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2317         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2318         EXPECT_HR(hr, S_OK);
2319         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2320         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2321         VariantClear(&v);
2322
2323         V_VT(&v) = VT_UNKNOWN;
2324         V_UNKNOWN(&v) = NULL;
2325         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2326         EXPECT_HR(hr, S_OK);
2327
2328         V_VT(&v) = VT_EMPTY;
2329         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2330         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2331         EXPECT_HR(hr, S_OK);
2332         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2333         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2334
2335         /* block QueryInterface on handler riid */
2336         init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE);
2337         init_saxdeclhandler(&declhandler, E_NOINTERFACE);
2338
2339         V_VT(&v) = VT_UNKNOWN;
2340         V_UNKNOWN(&v) = ptr->iface;
2341         EXPECT_REF(ptr->iface, 1);
2342         ref = get_refcount(ptr->iface);
2343         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2344         EXPECT_HR(hr, E_NOINTERFACE);
2345         EXPECT_REF(ptr->iface, 1);
2346
2347         ptr++;
2348     }
2349
2350     ISAXXMLReader_Release(reader);
2351     free_bstrs();
2352 }
2353
2354 struct feature_ns_entry_t {
2355     const GUID *guid;
2356     const char *clsid;
2357     VARIANT_BOOL value;
2358     VARIANT_BOOL value2; /* feature value after feature set to 0xc */
2359 };
2360
2361 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
2362     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   VARIANT_TRUE, VARIANT_FALSE },
2363     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE },
2364     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE },
2365     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE },
2366     { 0 }
2367 };
2368
2369 static const char *feature_names[] = {
2370     "http://xml.org/sax/features/namespaces",
2371     "http://xml.org/sax/features/namespace-prefixes",
2372     0
2373 };
2374
2375 static void test_saxreader_features(void)
2376 {
2377     const struct feature_ns_entry_t *entry = feature_ns_entry_data;
2378     ISAXXMLReader *reader;
2379
2380     while (entry->guid)
2381     {
2382         VARIANT_BOOL value;
2383         const char **name;
2384         HRESULT hr;
2385
2386         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2387         if (hr != S_OK)
2388         {
2389             win_skip("can't create %s instance\n", entry->clsid);
2390             entry++;
2391             continue;
2392         }
2393
2394         name = feature_names;
2395         while (*name)
2396         {
2397             value = 0xc;
2398             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2399             EXPECT_HR(hr, S_OK);
2400             ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
2401
2402             value = 0xc;
2403             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value);
2404             EXPECT_HR(hr, S_OK);
2405
2406             value = 0xd;
2407             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2408             EXPECT_HR(hr, S_OK);
2409             ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2);
2410
2411             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE);
2412             EXPECT_HR(hr, S_OK);
2413             value = 0xd;
2414             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2415             EXPECT_HR(hr, S_OK);
2416             ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value);
2417
2418             hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE);
2419             EXPECT_HR(hr, S_OK);
2420             value = 0xd;
2421             hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2422             EXPECT_HR(hr, S_OK);
2423             ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value);
2424
2425             name++;
2426         }
2427
2428         ISAXXMLReader_Release(reader);
2429
2430         entry++;
2431     }
2432 }
2433
2434 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
2435 static const CHAR UTF8BOMTest[] =
2436 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
2437 "<a></a>\n";
2438
2439 struct enc_test_entry_t {
2440     const GUID *guid;
2441     const char *clsid;
2442     const char *data;
2443     HRESULT hr;
2444     int todo;
2445 };
2446
2447 static const struct enc_test_entry_t encoding_test_data[] = {
2448     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
2449     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
2450     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
2451     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
2452     { 0 }
2453 };
2454
2455 static void test_saxreader_encoding(void)
2456 {
2457     const struct enc_test_entry_t *entry = encoding_test_data;
2458     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2459     static const CHAR testXmlA[] = "test.xml";
2460
2461     while (entry->guid)
2462     {
2463         ISAXXMLReader *reader;
2464         VARIANT input;
2465         DWORD written;
2466         HANDLE file;
2467         HRESULT hr;
2468
2469         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2470         if (hr != S_OK)
2471         {
2472             win_skip("can't create %s instance\n", entry->clsid);
2473             entry++;
2474             continue;
2475         }
2476
2477         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2478         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2479         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
2480         CloseHandle(file);
2481
2482         hr = ISAXXMLReader_parseURL(reader, testXmlW);
2483         if (entry->todo)
2484             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2485         else
2486             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2487
2488         DeleteFileA(testXmlA);
2489
2490         /* try BSTR input with no BOM or '<?xml' instruction */
2491         V_VT(&input) = VT_BSTR;
2492         V_BSTR(&input) = _bstr_("<element></element>");
2493         hr = ISAXXMLReader_parse(reader, input);
2494         EXPECT_HR(hr, S_OK);
2495
2496         ISAXXMLReader_Release(reader);
2497
2498         free_bstrs();
2499         entry++;
2500     }
2501 }
2502
2503 static void test_mxwriter_handlers(void)
2504 {
2505     ISAXContentHandler *handler;
2506     IMXWriter *writer, *writer2;
2507     ISAXDeclHandler *decl;
2508     ISAXLexicalHandler *lh;
2509     HRESULT hr;
2510
2511     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2512             &IID_IMXWriter, (void**)&writer);
2513     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2514
2515     EXPECT_REF(writer, 1);
2516
2517     /* ISAXContentHandler */
2518     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
2519     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2520     EXPECT_REF(writer, 2);
2521     EXPECT_REF(handler, 2);
2522
2523     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
2524     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2525     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2526     EXPECT_REF(writer, 3);
2527     EXPECT_REF(writer2, 3);
2528     IMXWriter_Release(writer2);
2529     ISAXContentHandler_Release(handler);
2530
2531     /* ISAXLexicalHandler */
2532     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lh);
2533     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2534     EXPECT_REF(writer, 2);
2535     EXPECT_REF(lh, 2);
2536
2537     hr = ISAXLexicalHandler_QueryInterface(lh, &IID_IMXWriter, (void**)&writer2);
2538     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2539     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2540     EXPECT_REF(writer, 3);
2541     EXPECT_REF(writer2, 3);
2542     IMXWriter_Release(writer2);
2543     ISAXLexicalHandler_Release(lh);
2544
2545     /* ISAXDeclHandler */
2546     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
2547     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2548     EXPECT_REF(writer, 2);
2549     EXPECT_REF(lh, 2);
2550
2551     hr = ISAXDeclHandler_QueryInterface(decl, &IID_IMXWriter, (void**)&writer2);
2552     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2553     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2554     EXPECT_REF(writer, 3);
2555     EXPECT_REF(writer2, 3);
2556     IMXWriter_Release(writer2);
2557     ISAXDeclHandler_Release(decl);
2558
2559     IMXWriter_Release(writer);
2560 }
2561
2562
2563 static struct msxmlsupported_data_t mxwriter_support_data[] =
2564 {
2565     { &CLSID_MXXMLWriter,   "MXXMLWriter"   },
2566     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
2567     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
2568     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
2569     { NULL }
2570 };
2571
2572 static struct msxmlsupported_data_t mxattributes_support_data[] =
2573 {
2574     { &CLSID_SAXAttributes,   "SAXAttributes"   },
2575     { &CLSID_SAXAttributes30, "SAXAttributes30" },
2576     { &CLSID_SAXAttributes40, "SAXAttributes40" },
2577     { &CLSID_SAXAttributes60, "SAXAttributes60" },
2578     { NULL }
2579 };
2580
2581 struct mxwriter_props_t
2582 {
2583     const GUID *clsid;
2584     VARIANT_BOOL bom;
2585     VARIANT_BOOL disable_escape;
2586     VARIANT_BOOL indent;
2587     VARIANT_BOOL omitdecl;
2588     VARIANT_BOOL standalone;
2589     const char *encoding;
2590 };
2591
2592 static const struct mxwriter_props_t mxwriter_default_props[] =
2593 {
2594     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2595     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2596     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2597     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2598     { NULL }
2599 };
2600
2601 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
2602 {
2603     int i = 0;
2604
2605     while (table->clsid)
2606     {
2607         IMXWriter *writer;
2608         VARIANT_BOOL b;
2609         BSTR encoding;
2610         HRESULT hr;
2611
2612         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
2613         {
2614             table++;
2615             i++;
2616             continue;
2617         }
2618
2619         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
2620             &IID_IMXWriter, (void**)&writer);
2621         EXPECT_HR(hr, S_OK);
2622
2623         b = !table->bom;
2624         hr = IMXWriter_get_byteOrderMark(writer, &b);
2625         EXPECT_HR(hr, S_OK);
2626         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
2627
2628         b = !table->disable_escape;
2629         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
2630         EXPECT_HR(hr, S_OK);
2631         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
2632            table->disable_escape);
2633
2634         b = !table->indent;
2635         hr = IMXWriter_get_indent(writer, &b);
2636         EXPECT_HR(hr, S_OK);
2637         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
2638
2639         b = !table->omitdecl;
2640         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
2641         EXPECT_HR(hr, S_OK);
2642         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
2643
2644         b = !table->standalone;
2645         hr = IMXWriter_get_standalone(writer, &b);
2646         EXPECT_HR(hr, S_OK);
2647         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
2648
2649         hr = IMXWriter_get_encoding(writer, &encoding);
2650         EXPECT_HR(hr, S_OK);
2651         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
2652             i, wine_dbgstr_w(encoding), table->encoding);
2653         SysFreeString(encoding);
2654
2655         IMXWriter_Release(writer);
2656
2657         table++;
2658         i++;
2659     }
2660 }
2661
2662 static void test_mxwriter_properties(void)
2663 {
2664     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
2665     static const WCHAR emptyW[] = {0};
2666     static const WCHAR testW[] = {'t','e','s','t',0};
2667     ISAXContentHandler *content;
2668     IMXWriter *writer;
2669     VARIANT_BOOL b;
2670     HRESULT hr;
2671     BSTR str, str2;
2672     VARIANT dest;
2673
2674     test_mxwriter_default_properties(mxwriter_default_props);
2675
2676     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2677             &IID_IMXWriter, (void**)&writer);
2678     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2679
2680     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
2681     ok(hr == E_POINTER, "got %08x\n", hr);
2682
2683     hr = IMXWriter_get_byteOrderMark(writer, NULL);
2684     ok(hr == E_POINTER, "got %08x\n", hr);
2685
2686     hr = IMXWriter_get_indent(writer, NULL);
2687     ok(hr == E_POINTER, "got %08x\n", hr);
2688
2689     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
2690     ok(hr == E_POINTER, "got %08x\n", hr);
2691
2692     hr = IMXWriter_get_standalone(writer, NULL);
2693     ok(hr == E_POINTER, "got %08x\n", hr);
2694
2695     /* set and check */
2696     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
2697     ok(hr == S_OK, "got %08x\n", hr);
2698
2699     b = VARIANT_FALSE;
2700     hr = IMXWriter_get_standalone(writer, &b);
2701     ok(hr == S_OK, "got %08x\n", hr);
2702     ok(b == VARIANT_TRUE, "got %d\n", b);
2703
2704     hr = IMXWriter_get_encoding(writer, NULL);
2705     EXPECT_HR(hr, E_POINTER);
2706
2707     /* UTF-16 is a default setting apparently */
2708     str = (void*)0xdeadbeef;
2709     hr = IMXWriter_get_encoding(writer, &str);
2710     EXPECT_HR(hr, S_OK);
2711     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
2712
2713     str2 = (void*)0xdeadbeef;
2714     hr = IMXWriter_get_encoding(writer, &str2);
2715     ok(hr == S_OK, "got %08x\n", hr);
2716     ok(str != str2, "expected newly allocated, got same %p\n", str);
2717
2718     SysFreeString(str2);
2719     SysFreeString(str);
2720
2721     /* put empty string */
2722     str = SysAllocString(emptyW);
2723     hr = IMXWriter_put_encoding(writer, str);
2724     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2725     SysFreeString(str);
2726
2727     str = (void*)0xdeadbeef;
2728     hr = IMXWriter_get_encoding(writer, &str);
2729     EXPECT_HR(hr, S_OK);
2730     ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str));
2731     SysFreeString(str);
2732
2733     /* invalid encoding name */
2734     str = SysAllocString(testW);
2735     hr = IMXWriter_put_encoding(writer, str);
2736     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2737     SysFreeString(str);
2738
2739     /* test case sensivity */
2740     hr = IMXWriter_put_encoding(writer, _bstr_("utf-8"));
2741     EXPECT_HR(hr, S_OK);
2742     str = (void*)0xdeadbeef;
2743     hr = IMXWriter_get_encoding(writer, &str);
2744     EXPECT_HR(hr, S_OK);
2745     ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str));
2746     SysFreeString(str);
2747
2748     hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16"));
2749     EXPECT_HR(hr, S_OK);
2750     str = (void*)0xdeadbeef;
2751     hr = IMXWriter_get_encoding(writer, &str);
2752     EXPECT_HR(hr, S_OK);
2753     ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str));
2754     SysFreeString(str);
2755
2756     /* how it affects document creation */
2757     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2758     EXPECT_HR(hr, S_OK);
2759
2760     hr = ISAXContentHandler_startDocument(content);
2761     EXPECT_HR(hr, S_OK);
2762     hr = ISAXContentHandler_endDocument(content);
2763     EXPECT_HR(hr, S_OK);
2764
2765     V_VT(&dest) = VT_EMPTY;
2766     hr = IMXWriter_get_output(writer, &dest);
2767     EXPECT_HR(hr, S_OK);
2768     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2769     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n"),
2770         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2771     VariantClear(&dest);
2772     ISAXContentHandler_Release(content);
2773
2774     hr = IMXWriter_get_version(writer, NULL);
2775     ok(hr == E_POINTER, "got %08x\n", hr);
2776     /* default version is 'surprisingly' 1.0 */
2777     hr = IMXWriter_get_version(writer, &str);
2778     ok(hr == S_OK, "got %08x\n", hr);
2779     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
2780     SysFreeString(str);
2781
2782     /* store version string as is */
2783     hr = IMXWriter_put_version(writer, NULL);
2784     ok(hr == E_INVALIDARG, "got %08x\n", hr);
2785
2786     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
2787     ok(hr == S_OK, "got %08x\n", hr);
2788
2789     hr = IMXWriter_put_version(writer, _bstr_(""));
2790     ok(hr == S_OK, "got %08x\n", hr);
2791     hr = IMXWriter_get_version(writer, &str);
2792     ok(hr == S_OK, "got %08x\n", hr);
2793     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
2794     SysFreeString(str);
2795
2796     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
2797     ok(hr == S_OK, "got %08x\n", hr);
2798     hr = IMXWriter_get_version(writer, &str);
2799     ok(hr == S_OK, "got %08x\n", hr);
2800     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
2801     SysFreeString(str);
2802
2803     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
2804     ok(hr == S_OK, "got %08x\n", hr);
2805     hr = IMXWriter_get_version(writer, &str);
2806     ok(hr == S_OK, "got %08x\n", hr);
2807     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
2808     SysFreeString(str);
2809
2810     IMXWriter_Release(writer);
2811     free_bstrs();
2812 }
2813
2814 static void test_mxwriter_flush(void)
2815 {
2816     ISAXContentHandler *content;
2817     IMXWriter *writer;
2818     LARGE_INTEGER pos;
2819     ULARGE_INTEGER pos2;
2820     IStream *stream;
2821     VARIANT dest;
2822     HRESULT hr;
2823
2824     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2825             &IID_IMXWriter, (void**)&writer);
2826     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2827
2828     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2829     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2830     EXPECT_REF(stream, 1);
2831
2832     /* detach when nothing was attached */
2833     V_VT(&dest) = VT_EMPTY;
2834     hr = IMXWriter_put_output(writer, dest);
2835     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2836
2837     /* attach stream */
2838     V_VT(&dest) = VT_UNKNOWN;
2839     V_UNKNOWN(&dest) = (IUnknown*)stream;
2840     hr = IMXWriter_put_output(writer, dest);
2841     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2842     todo_wine EXPECT_REF(stream, 3);
2843
2844     /* detach setting VT_EMPTY destination */
2845     V_VT(&dest) = VT_EMPTY;
2846     hr = IMXWriter_put_output(writer, dest);
2847     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2848     EXPECT_REF(stream, 1);
2849
2850     V_VT(&dest) = VT_UNKNOWN;
2851     V_UNKNOWN(&dest) = (IUnknown*)stream;
2852     hr = IMXWriter_put_output(writer, dest);
2853     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2854
2855     /* flush() doesn't detach a stream */
2856     hr = IMXWriter_flush(writer);
2857     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2858     todo_wine EXPECT_REF(stream, 3);
2859
2860     pos.QuadPart = 0;
2861     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2862     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2863     ok(pos2.QuadPart == 0, "expected stream beginning\n");
2864
2865     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2866     ok(hr == S_OK, "got %08x\n", hr);
2867
2868     hr = ISAXContentHandler_startDocument(content);
2869     ok(hr == S_OK, "got %08x\n", hr);
2870
2871     pos.QuadPart = 0;
2872     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2873     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2874     ok(pos2.QuadPart != 0, "expected stream beginning\n");
2875
2876     /* already started */
2877     hr = ISAXContentHandler_startDocument(content);
2878     ok(hr == S_OK, "got %08x\n", hr);
2879
2880     hr = ISAXContentHandler_endDocument(content);
2881     ok(hr == S_OK, "got %08x\n", hr);
2882
2883     /* flushed on endDocument() */
2884     pos.QuadPart = 0;
2885     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2886     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2887     ok(pos2.QuadPart != 0, "expected stream position moved\n");
2888
2889     ISAXContentHandler_Release(content);
2890     IStream_Release(stream);
2891     IMXWriter_Release(writer);
2892 }
2893
2894 static void test_mxwriter_startenddocument(void)
2895 {
2896     ISAXContentHandler *content;
2897     IMXWriter *writer;
2898     VARIANT dest;
2899     HRESULT hr;
2900
2901     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2902             &IID_IMXWriter, (void**)&writer);
2903     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2904
2905     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2906     ok(hr == S_OK, "got %08x\n", hr);
2907
2908     hr = ISAXContentHandler_startDocument(content);
2909     ok(hr == S_OK, "got %08x\n", hr);
2910
2911     hr = ISAXContentHandler_endDocument(content);
2912     ok(hr == S_OK, "got %08x\n", hr);
2913
2914     V_VT(&dest) = VT_EMPTY;
2915     hr = IMXWriter_get_output(writer, &dest);
2916     ok(hr == S_OK, "got %08x\n", hr);
2917     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2918     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
2919         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2920     VariantClear(&dest);
2921
2922     /* now try another startDocument */
2923     hr = ISAXContentHandler_startDocument(content);
2924     ok(hr == S_OK, "got %08x\n", hr);
2925     /* and get duplicated prolog */
2926     V_VT(&dest) = VT_EMPTY;
2927     hr = IMXWriter_get_output(writer, &dest);
2928     ok(hr == S_OK, "got %08x\n", hr);
2929     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2930     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
2931                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
2932         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2933     VariantClear(&dest);
2934
2935     ISAXContentHandler_Release(content);
2936     IMXWriter_Release(writer);
2937
2938     /* now with omitted declaration */
2939     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2940             &IID_IMXWriter, (void**)&writer);
2941     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2942
2943     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2944     ok(hr == S_OK, "got %08x\n", hr);
2945
2946     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2947     ok(hr == S_OK, "got %08x\n", hr);
2948
2949     hr = ISAXContentHandler_startDocument(content);
2950     ok(hr == S_OK, "got %08x\n", hr);
2951
2952     hr = ISAXContentHandler_endDocument(content);
2953     ok(hr == S_OK, "got %08x\n", hr);
2954
2955     V_VT(&dest) = VT_EMPTY;
2956     hr = IMXWriter_get_output(writer, &dest);
2957     ok(hr == S_OK, "got %08x\n", hr);
2958     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2959     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2960     VariantClear(&dest);
2961
2962     ISAXContentHandler_Release(content);
2963     IMXWriter_Release(writer);
2964
2965     free_bstrs();
2966 }
2967
2968 enum startendtype
2969 {
2970     StartElement    = 0x001,
2971     EndElement      = 0x010,
2972     StartEndElement = 0x011,
2973     DisableEscaping = 0x100
2974 };
2975
2976 struct writer_startendelement_t {
2977     const GUID *clsid;
2978     enum startendtype type;
2979     const char *uri;
2980     const char *local_name;
2981     const char *qname;
2982     const char *output;
2983     HRESULT hr;
2984     ISAXAttributes *attr;
2985 };
2986
2987 static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\">";
2988 static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\"/>";
2989 static const char startendelement_noescape_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&\">\"/>";
2990
2991 static const struct writer_startendelement_t writer_startendelement[] = {
2992     /* 0 */
2993     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
2994     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
2995     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
2996     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
2997     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
2998     /* 5 */
2999     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3000     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3001     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
3002     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3003     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3004     /* 10 */
3005     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3006     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
3007     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3008     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3009     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3010     /* 15 */
3011     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
3012     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
3013     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3014     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3015     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3016     /* 20 */
3017     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3018     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3019     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3020     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
3021     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3022     /* 25 */
3023     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3024     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3025     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3026     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3027     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3028     /* 30 */
3029     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3030     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3031     /* endElement tests */
3032     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3033     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3034     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3035     /* 35 */
3036     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
3037     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3038     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3039     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3040     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
3041     /* 40 */
3042     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3043     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3044     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3045     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
3046     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3047     /* 45 */
3048     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3049     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3050     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
3051     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
3052     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3053     /* 50 */
3054     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3055     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3056     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3057     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3058     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3059     /* 55 */
3060     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
3061     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3062     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3063     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3064     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3065     /* 60 */
3066     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3067     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3068     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3069     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3070
3071     /* with attributes */
3072     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3073     /* 65 */
3074     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3075     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3076     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3077     /* empty elements */
3078     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3079     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3080     /* 70 */
3081     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3082     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3083     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
3084     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
3085     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
3086     /* 75 */
3087     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
3088
3089     /* with disabled output escaping */
3090     { &CLSID_MXXMLWriter,   StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3091     { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3092     { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3093     { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3094
3095     { NULL }
3096 };
3097
3098 static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid)
3099 {
3100     while (table->clsid)
3101     {
3102         IUnknown *unk;
3103         HRESULT hr;
3104
3105         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk);
3106         if (hr == S_OK) IUnknown_Release(unk);
3107
3108         table->supported = hr == S_OK;
3109         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
3110
3111         table++;
3112     }
3113 }
3114
3115 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
3116 {
3117     int i = 0;
3118
3119     while (table->clsid)
3120     {
3121         ISAXContentHandler *content;
3122         IMXWriter *writer;
3123         HRESULT hr;
3124
3125         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3126         {
3127             table++;
3128             i++;
3129             continue;
3130         }
3131
3132         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3133             &IID_IMXWriter, (void**)&writer);
3134         EXPECT_HR(hr, S_OK);
3135
3136         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3137         EXPECT_HR(hr, S_OK);
3138
3139         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3140         EXPECT_HR(hr, S_OK);
3141
3142         hr = ISAXContentHandler_startDocument(content);
3143         EXPECT_HR(hr, S_OK);
3144
3145         if (table->type & DisableEscaping)
3146         {
3147             hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3148             EXPECT_HR(hr, S_OK);
3149         }
3150
3151         if (table->type & StartElement)
3152         {
3153             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
3154                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
3155             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3156         }
3157
3158         if (table->type & EndElement)
3159         {
3160             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
3161                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
3162             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3163         }
3164
3165         /* test output */
3166         if (hr == S_OK)
3167         {
3168             VARIANT dest;
3169
3170             V_VT(&dest) = VT_EMPTY;
3171             hr = IMXWriter_get_output(writer, &dest);
3172             EXPECT_HR(hr, S_OK);
3173             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3174             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3175                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3176             VariantClear(&dest);
3177         }
3178
3179         ISAXContentHandler_Release(content);
3180         IMXWriter_Release(writer);
3181
3182         table++;
3183         i++;
3184     }
3185
3186     free_bstrs();
3187 }
3188
3189 static void test_mxwriter_startendelement(void)
3190 {
3191     ISAXContentHandler *content;
3192     IMXWriter *writer;
3193     VARIANT dest;
3194     HRESULT hr;
3195
3196     test_mxwriter_startendelement_batch(writer_startendelement);
3197
3198     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3199             &IID_IMXWriter, (void**)&writer);
3200     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3201
3202     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3203     ok(hr == S_OK, "got %08x\n", hr);
3204
3205     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3206     ok(hr == S_OK, "got %08x\n", hr);
3207
3208     hr = ISAXContentHandler_startDocument(content);
3209     ok(hr == S_OK, "got %08x\n", hr);
3210
3211     /* all string pointers should be not null */
3212     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
3213     ok(hr == S_OK, "got %08x\n", hr);
3214
3215     V_VT(&dest) = VT_EMPTY;
3216     hr = IMXWriter_get_output(writer, &dest);
3217     ok(hr == S_OK, "got %08x\n", hr);
3218     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3219     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3220     VariantClear(&dest);
3221
3222     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
3223     ok(hr == S_OK, "got %08x\n", hr);
3224
3225     V_VT(&dest) = VT_EMPTY;
3226     hr = IMXWriter_get_output(writer, &dest);
3227     ok(hr == S_OK, "got %08x\n", hr);
3228     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3229     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3230     VariantClear(&dest);
3231
3232     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
3233     EXPECT_HR(hr, E_INVALIDARG);
3234
3235     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
3236     EXPECT_HR(hr, E_INVALIDARG);
3237
3238     /* only local name is an error too */
3239     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
3240     EXPECT_HR(hr, E_INVALIDARG);
3241
3242     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
3243     EXPECT_HR(hr, S_OK);
3244
3245     V_VT(&dest) = VT_EMPTY;
3246     hr = IMXWriter_get_output(writer, &dest);
3247     EXPECT_HR(hr, S_OK);
3248     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3249     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3250     VariantClear(&dest);
3251
3252     hr = ISAXContentHandler_endDocument(content);
3253     EXPECT_HR(hr, S_OK);
3254
3255     V_VT(&dest) = VT_EMPTY;
3256     hr = IMXWriter_put_output(writer, dest);
3257     EXPECT_HR(hr, S_OK);
3258
3259     V_VT(&dest) = VT_EMPTY;
3260     hr = IMXWriter_get_output(writer, &dest);
3261     EXPECT_HR(hr, S_OK);
3262     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3263     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3264     VariantClear(&dest);
3265
3266     hr = ISAXContentHandler_startDocument(content);
3267     EXPECT_HR(hr, S_OK);
3268
3269     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
3270     EXPECT_HR(hr, S_OK);
3271
3272     V_VT(&dest) = VT_EMPTY;
3273     hr = IMXWriter_get_output(writer, &dest);
3274     EXPECT_HR(hr, S_OK);
3275     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3276     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3277     VariantClear(&dest);
3278
3279     ISAXContentHandler_endDocument(content);
3280     IMXWriter_flush(writer);
3281
3282     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
3283     EXPECT_HR(hr, S_OK);
3284     V_VT(&dest) = VT_EMPTY;
3285     hr = IMXWriter_get_output(writer, &dest);
3286     EXPECT_HR(hr, S_OK);
3287     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3288     ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3289     VariantClear(&dest);
3290
3291     ISAXContentHandler_Release(content);
3292     IMXWriter_Release(writer);
3293     free_bstrs();
3294 }
3295
3296 struct writer_characters_t {
3297     const GUID *clsid;
3298     const char *data;
3299     const char *output;
3300 };
3301
3302 static const struct writer_characters_t writer_characters[] = {
3303     { &CLSID_MXXMLWriter,   "< > & \"", "&lt; &gt; &amp; \"" },
3304     { &CLSID_MXXMLWriter30, "< > & \"", "&lt; &gt; &amp; \"" },
3305     { &CLSID_MXXMLWriter40, "< > & \"", "&lt; &gt; &amp; \"" },
3306     { &CLSID_MXXMLWriter60, "< > & \"", "&lt; &gt; &amp; \"" },
3307     { NULL }
3308 };
3309
3310 static void test_mxwriter_characters(void)
3311 {
3312     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
3313     const struct writer_characters_t *table = writer_characters;
3314     ISAXContentHandler *content;
3315     IMXWriter *writer;
3316     VARIANT dest;
3317     HRESULT hr;
3318     int i = 0;
3319
3320     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3321             &IID_IMXWriter, (void**)&writer);
3322     EXPECT_HR(hr, S_OK);
3323
3324     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3325     EXPECT_HR(hr, S_OK);
3326
3327     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3328     EXPECT_HR(hr, S_OK);
3329
3330     hr = ISAXContentHandler_startDocument(content);
3331     EXPECT_HR(hr, S_OK);
3332
3333     hr = ISAXContentHandler_characters(content, NULL, 0);
3334     EXPECT_HR(hr, E_INVALIDARG);
3335
3336     hr = ISAXContentHandler_characters(content, chardataW, 0);
3337     EXPECT_HR(hr, S_OK);
3338
3339     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
3340     EXPECT_HR(hr, S_OK);
3341
3342     V_VT(&dest) = VT_EMPTY;
3343     hr = IMXWriter_get_output(writer, &dest);
3344     EXPECT_HR(hr, S_OK);
3345     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3346     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3347     VariantClear(&dest);
3348
3349     hr = ISAXContentHandler_endDocument(content);
3350     EXPECT_HR(hr, S_OK);
3351
3352     ISAXContentHandler_Release(content);
3353     IMXWriter_Release(writer);
3354
3355     /* try empty characters data to see if element is closed */
3356     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3357             &IID_IMXWriter, (void**)&writer);
3358     EXPECT_HR(hr, S_OK);
3359
3360     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3361     EXPECT_HR(hr, S_OK);
3362
3363     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3364     EXPECT_HR(hr, S_OK);
3365
3366     hr = ISAXContentHandler_startDocument(content);
3367     EXPECT_HR(hr, S_OK);
3368
3369     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3370     EXPECT_HR(hr, S_OK);
3371
3372     hr = ISAXContentHandler_characters(content, chardataW, 0);
3373     EXPECT_HR(hr, S_OK);
3374
3375     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3376     EXPECT_HR(hr, S_OK);
3377
3378     V_VT(&dest) = VT_EMPTY;
3379     hr = IMXWriter_get_output(writer, &dest);
3380     EXPECT_HR(hr, S_OK);
3381     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3382     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3383     VariantClear(&dest);
3384
3385     ISAXContentHandler_Release(content);
3386     IMXWriter_Release(writer);
3387
3388     /* batch tests */
3389     while (table->clsid)
3390     {
3391         ISAXContentHandler *content;
3392         IMXWriter *writer;
3393         VARIANT dest;
3394         HRESULT hr;
3395
3396         if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3397         {
3398             table++;
3399             i++;
3400             continue;
3401         }
3402
3403         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3404             &IID_IMXWriter, (void**)&writer);
3405         EXPECT_HR(hr, S_OK);
3406
3407         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3408         EXPECT_HR(hr, S_OK);
3409
3410         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3411         EXPECT_HR(hr, S_OK);
3412
3413         hr = ISAXContentHandler_startDocument(content);
3414         EXPECT_HR(hr, S_OK);
3415
3416         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3417         EXPECT_HR(hr, S_OK);
3418
3419         /* test output */
3420         if (hr == S_OK)
3421         {
3422             V_VT(&dest) = VT_EMPTY;
3423             hr = IMXWriter_get_output(writer, &dest);
3424             EXPECT_HR(hr, S_OK);
3425             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3426             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3427                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3428             VariantClear(&dest);
3429         }
3430
3431         /* with disabled escaping */
3432         V_VT(&dest) = VT_EMPTY;
3433         hr = IMXWriter_put_output(writer, dest);
3434         EXPECT_HR(hr, S_OK);
3435
3436         hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3437         EXPECT_HR(hr, S_OK);
3438
3439         hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3440         EXPECT_HR(hr, S_OK);
3441
3442         /* test output */
3443         if (hr == S_OK)
3444         {
3445             V_VT(&dest) = VT_EMPTY;
3446             hr = IMXWriter_get_output(writer, &dest);
3447             EXPECT_HR(hr, S_OK);
3448             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3449             ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)),
3450                 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data);
3451             VariantClear(&dest);
3452         }
3453
3454         ISAXContentHandler_Release(content);
3455         IMXWriter_Release(writer);
3456
3457         table++;
3458         i++;
3459     }
3460
3461     free_bstrs();
3462 }
3463
3464 static const mxwriter_stream_test mxwriter_stream_tests[] = {
3465     {
3466         VARIANT_TRUE,"UTF-16",
3467         {
3468             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3469             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3470             {TRUE}
3471         }
3472     },
3473     {
3474         VARIANT_FALSE,"UTF-16",
3475         {
3476             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3477             {TRUE}
3478         }
3479     },
3480     {
3481         VARIANT_TRUE,"UTF-8",
3482         {
3483             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
3484             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3485              * and the writer is released.
3486              */
3487             {FALSE,NULL,0},
3488             {TRUE}
3489         }
3490     },
3491     {
3492         VARIANT_TRUE,"utf-8",
3493         {
3494             {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1},
3495             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3496              * and the writer is released.
3497              */
3498             {FALSE,NULL,0},
3499             {TRUE}
3500         }
3501     },
3502     {
3503         VARIANT_TRUE,"UTF-16",
3504         {
3505             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3506             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3507             {TRUE}
3508         }
3509     },
3510     {
3511         VARIANT_TRUE,"UTF-16",
3512         {
3513             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
3514             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3515             {TRUE}
3516         }
3517     }
3518 };
3519
3520 static void test_mxwriter_stream(void)
3521 {
3522     IMXWriter *writer;
3523     ISAXContentHandler *content;
3524     HRESULT hr;
3525     VARIANT dest;
3526     IStream *stream;
3527     LARGE_INTEGER pos;
3528     ULARGE_INTEGER pos2;
3529     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
3530
3531     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
3532         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
3533
3534         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3535                 &IID_IMXWriter, (void**)&writer);
3536         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3537
3538         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3539         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3540
3541         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
3542         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
3543
3544         V_VT(&dest) = VT_UNKNOWN;
3545         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
3546         hr = IMXWriter_put_output(writer, dest);
3547         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
3548         VariantClear(&dest);
3549
3550         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
3551         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
3552
3553         current_write_test = test->expected_writes;
3554
3555         hr = ISAXContentHandler_startDocument(content);
3556         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3557
3558         hr = ISAXContentHandler_endDocument(content);
3559         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3560
3561         ISAXContentHandler_Release(content);
3562         IMXWriter_Release(writer);
3563
3564         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
3565             (int)(current_write_test-test->expected_writes), current_stream_test_index);
3566     }
3567
3568     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3569             &IID_IMXWriter, (void**)&writer);
3570     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3571
3572     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3573     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
3574
3575     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3576     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3577
3578     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3579     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
3580
3581     V_VT(&dest) = VT_UNKNOWN;
3582     V_UNKNOWN(&dest) = (IUnknown*)stream;
3583     hr = IMXWriter_put_output(writer, dest);
3584     ok(hr == S_OK, "put_output failed: %08x\n", hr);
3585
3586     hr = ISAXContentHandler_startDocument(content);
3587     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3588
3589     /* Setting output of the mxwriter causes the current output to be flushed,
3590      * and the writer to start over.
3591      */
3592     V_VT(&dest) = VT_EMPTY;
3593     hr = IMXWriter_put_output(writer, dest);
3594     ok(hr == S_OK, "put_output failed: %08x\n", hr);
3595
3596     pos.QuadPart = 0;
3597     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3598     ok(hr == S_OK, "Seek failed: %08x\n", hr);
3599     ok(pos2.QuadPart != 0, "expected stream position moved\n");
3600
3601     hr = ISAXContentHandler_startDocument(content);
3602     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3603
3604     hr = ISAXContentHandler_endDocument(content);
3605     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
3606
3607     V_VT(&dest) = VT_EMPTY;
3608     hr = IMXWriter_get_output(writer, &dest);
3609     ok(hr == S_OK, "get_output failed: %08x\n", hr);
3610     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3611     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3612             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3613     VariantClear(&dest);
3614
3615     /* test when BOM is written to output stream */
3616     V_VT(&dest) = VT_EMPTY;
3617     hr = IMXWriter_put_output(writer, dest);
3618     EXPECT_HR(hr, S_OK);
3619
3620     pos.QuadPart = 0;
3621     hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
3622     EXPECT_HR(hr, S_OK);
3623
3624     V_VT(&dest) = VT_UNKNOWN;
3625     V_UNKNOWN(&dest) = (IUnknown*)stream;
3626     hr = IMXWriter_put_output(writer, dest);
3627     EXPECT_HR(hr, S_OK);
3628
3629     hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE);
3630     EXPECT_HR(hr, S_OK);
3631
3632     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
3633     EXPECT_HR(hr, S_OK);
3634
3635     hr = ISAXContentHandler_startDocument(content);
3636     EXPECT_HR(hr, S_OK);
3637
3638     pos.QuadPart = 0;
3639     pos2.QuadPart = 0;
3640     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3641     EXPECT_HR(hr, S_OK);
3642     ok(pos2.QuadPart == 2, "got wrong position\n");
3643
3644     ISAXContentHandler_Release(content);
3645     IMXWriter_Release(writer);
3646
3647     free_bstrs();
3648 }
3649
3650 static void test_mxwriter_encoding(void)
3651 {
3652     ISAXContentHandler *content;
3653     IMXWriter *writer;
3654     IStream *stream;
3655     VARIANT dest;
3656     HRESULT hr;
3657     HGLOBAL g;
3658     char *ptr;
3659     BSTR s;
3660
3661     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3662             &IID_IMXWriter, (void**)&writer);
3663     EXPECT_HR(hr, S_OK);
3664
3665     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3666     EXPECT_HR(hr, S_OK);
3667
3668     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3669     EXPECT_HR(hr, S_OK);
3670
3671     hr = ISAXContentHandler_startDocument(content);
3672     EXPECT_HR(hr, S_OK);
3673
3674     hr = ISAXContentHandler_endDocument(content);
3675     EXPECT_HR(hr, S_OK);
3676
3677     /* The content is always re-encoded to UTF-16 when the output is
3678      * retrieved as a BSTR.
3679      */
3680     V_VT(&dest) = VT_EMPTY;
3681     hr = IMXWriter_get_output(writer, &dest);
3682     EXPECT_HR(hr, S_OK);
3683     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3684     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3685             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3686     VariantClear(&dest);
3687
3688     /* switch encoding when something is written already */
3689     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3690     EXPECT_HR(hr, S_OK);
3691
3692     V_VT(&dest) = VT_UNKNOWN;
3693     V_UNKNOWN(&dest) = (IUnknown*)stream;
3694     hr = IMXWriter_put_output(writer, dest);
3695     EXPECT_HR(hr, S_OK);
3696
3697     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3698     EXPECT_HR(hr, S_OK);
3699
3700     /* write empty element */
3701     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3702     EXPECT_HR(hr, S_OK);
3703
3704     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3705     EXPECT_HR(hr, S_OK);
3706
3707     /* switch */
3708     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
3709     EXPECT_HR(hr, S_OK);
3710
3711     hr = IMXWriter_flush(writer);
3712     EXPECT_HR(hr, S_OK);
3713
3714     hr = GetHGlobalFromStream(stream, &g);
3715     EXPECT_HR(hr, S_OK);
3716
3717     ptr = GlobalLock(g);
3718     ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]);
3719     GlobalUnlock(g);
3720
3721     /* so output is unaffected, encoding name is stored however */
3722     hr = IMXWriter_get_encoding(writer, &s);
3723     EXPECT_HR(hr, S_OK);
3724     ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s));
3725     SysFreeString(s);
3726
3727     IStream_Release(stream);
3728
3729     ISAXContentHandler_Release(content);
3730     IMXWriter_Release(writer);
3731
3732     free_bstrs();
3733 }
3734
3735 static void test_obj_dispex(IUnknown *obj)
3736 {
3737     static const WCHAR starW[] = {'*',0};
3738     DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE;
3739     IDispatchEx *dispex;
3740     IUnknown *unk;
3741     DWORD props;
3742     UINT ticnt;
3743     HRESULT hr;
3744     BSTR name;
3745
3746     hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
3747     EXPECT_HR(hr, S_OK);
3748     if (FAILED(hr)) return;
3749
3750     ticnt = 0;
3751     hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt);
3752     EXPECT_HR(hr, S_OK);
3753     ok(ticnt == 1, "ticnt=%u\n", ticnt);
3754
3755     name = SysAllocString(starW);
3756     hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive);
3757     EXPECT_HR(hr, E_NOTIMPL);
3758     SysFreeString(name);
3759
3760     hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid);
3761     EXPECT_HR(hr, E_NOTIMPL);
3762
3763     props = 0;
3764     hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props);
3765     EXPECT_HR(hr, E_NOTIMPL);
3766     ok(props == 0, "expected 0 got %d\n", props);
3767
3768     hr = IDispatchEx_GetMemberName(dispex, dispid, &name);
3769     EXPECT_HR(hr, E_NOTIMPL);
3770     if (SUCCEEDED(hr)) SysFreeString(name);
3771
3772     hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid);
3773     EXPECT_HR(hr, E_NOTIMPL);
3774
3775     hr = IDispatchEx_GetNameSpaceParent(dispex, &unk);
3776     EXPECT_HR(hr, E_NOTIMPL);
3777     if (hr == S_OK && unk) IUnknown_Release(unk);
3778
3779     IDispatchEx_Release(dispex);
3780 }
3781
3782 static void test_dispex(void)
3783 {
3784      IVBSAXXMLReader *vbreader;
3785      ISAXXMLReader *reader;
3786      IUnknown *unk;
3787      HRESULT hr;
3788
3789      hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
3790                 &IID_ISAXXMLReader, (void**)&reader);
3791      EXPECT_HR(hr, S_OK);
3792
3793      hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk);
3794      EXPECT_HR(hr, S_OK);
3795      test_obj_dispex(unk);
3796      IUnknown_Release(unk);
3797
3798      hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader);
3799      EXPECT_HR(hr, S_OK);
3800      hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk);
3801      EXPECT_HR(hr, S_OK);
3802      test_obj_dispex(unk);
3803      IUnknown_Release(unk);
3804      IVBSAXXMLReader_Release(vbreader);
3805
3806      ISAXXMLReader_Release(reader);
3807 }
3808
3809 static void test_mxwriter_dispex(void)
3810 {
3811     IDispatchEx *dispex;
3812     IMXWriter *writer;
3813     IUnknown *unk;
3814     HRESULT hr;
3815
3816     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3817             &IID_IMXWriter, (void**)&writer);
3818     EXPECT_HR(hr, S_OK);
3819
3820     hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex);
3821     EXPECT_HR(hr, S_OK);
3822     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
3823     test_obj_dispex(unk);
3824     IUnknown_Release(unk);
3825     IDispatchEx_Release(dispex);
3826
3827     IMXWriter_Release(writer);
3828 }
3829
3830 static void test_mxwriter_comment(void)
3831 {
3832     static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
3833     ISAXContentHandler *content;
3834     ISAXLexicalHandler *lexical;
3835     IMXWriter *writer;
3836     VARIANT dest;
3837     HRESULT hr;
3838
3839     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3840             &IID_IMXWriter, (void**)&writer);
3841     EXPECT_HR(hr, S_OK);
3842
3843     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3844     EXPECT_HR(hr, S_OK);
3845
3846     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
3847     EXPECT_HR(hr, S_OK);
3848
3849     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3850     EXPECT_HR(hr, S_OK);
3851
3852     hr = ISAXContentHandler_startDocument(content);
3853     EXPECT_HR(hr, S_OK);
3854
3855     hr = ISAXLexicalHandler_comment(lexical, NULL, 0);
3856     EXPECT_HR(hr, E_INVALIDARG);
3857
3858     hr = ISAXLexicalHandler_comment(lexical, commentW, 0);
3859     EXPECT_HR(hr, S_OK);
3860
3861     V_VT(&dest) = VT_EMPTY;
3862     hr = IMXWriter_get_output(writer, &dest);
3863     EXPECT_HR(hr, S_OK);
3864     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3865     ok(!lstrcmpW(_bstr_("<!---->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3866     VariantClear(&dest);
3867
3868     hr = ISAXLexicalHandler_comment(lexical, commentW, sizeof(commentW)/sizeof(WCHAR)-1);
3869     EXPECT_HR(hr, S_OK);
3870
3871     V_VT(&dest) = VT_EMPTY;
3872     hr = IMXWriter_get_output(writer, &dest);
3873     EXPECT_HR(hr, S_OK);
3874     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3875     ok(!lstrcmpW(_bstr_("<!---->\r\n<!--comment-->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3876     VariantClear(&dest);
3877
3878     ISAXContentHandler_Release(content);
3879     ISAXLexicalHandler_Release(lexical);
3880     IMXWriter_Release(writer);
3881     free_bstrs();
3882 }
3883
3884 static void test_mxwriter_cdata(void)
3885 {
3886     ISAXContentHandler *content;
3887     ISAXLexicalHandler *lexical;
3888     IMXWriter *writer;
3889     VARIANT dest;
3890     HRESULT hr;
3891
3892     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3893             &IID_IMXWriter, (void**)&writer);
3894     EXPECT_HR(hr, S_OK);
3895
3896     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3897     EXPECT_HR(hr, S_OK);
3898
3899     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
3900     EXPECT_HR(hr, S_OK);
3901
3902     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3903     EXPECT_HR(hr, S_OK);
3904
3905     hr = ISAXContentHandler_startDocument(content);
3906     EXPECT_HR(hr, S_OK);
3907
3908     hr = ISAXLexicalHandler_startCDATA(lexical);
3909     EXPECT_HR(hr, S_OK);
3910
3911     V_VT(&dest) = VT_EMPTY;
3912     hr = IMXWriter_get_output(writer, &dest);
3913     EXPECT_HR(hr, S_OK);
3914     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3915     ok(!lstrcmpW(_bstr_("<![CDATA["), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3916     VariantClear(&dest);
3917
3918     hr = ISAXLexicalHandler_startCDATA(lexical);
3919     EXPECT_HR(hr, S_OK);
3920
3921     /* all these are escaped for text nodes */
3922     hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
3923     EXPECT_HR(hr, S_OK);
3924
3925     hr = ISAXLexicalHandler_endCDATA(lexical);
3926     EXPECT_HR(hr, S_OK);
3927
3928     V_VT(&dest) = VT_EMPTY;
3929     hr = IMXWriter_get_output(writer, &dest);
3930     EXPECT_HR(hr, S_OK);
3931     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3932     ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3933     VariantClear(&dest);
3934
3935     ISAXContentHandler_Release(content);
3936     ISAXLexicalHandler_Release(lexical);
3937     IMXWriter_Release(writer);
3938     free_bstrs();
3939 }
3940
3941 static void test_mxwriter_pi(void)
3942 {
3943     static const WCHAR targetW[] = {'t','a','r','g','e','t',0};
3944     static const WCHAR dataW[] = {'d','a','t','a',0};
3945     ISAXContentHandler *content;
3946     IMXWriter *writer;
3947     VARIANT dest;
3948     HRESULT hr;
3949
3950     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3951             &IID_IMXWriter, (void**)&writer);
3952     EXPECT_HR(hr, S_OK);
3953
3954     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3955     EXPECT_HR(hr, S_OK);
3956
3957     hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0);
3958     EXPECT_HR(hr, E_INVALIDARG);
3959
3960     hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0);
3961     EXPECT_HR(hr, S_OK);
3962
3963     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0);
3964     EXPECT_HR(hr, S_OK);
3965
3966     V_VT(&dest) = VT_EMPTY;
3967     hr = IMXWriter_get_output(writer, &dest);
3968     EXPECT_HR(hr, S_OK);
3969     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3970     ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3971     VariantClear(&dest);
3972
3973     hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4);
3974     EXPECT_HR(hr, S_OK);
3975
3976     V_VT(&dest) = VT_EMPTY;
3977     hr = IMXWriter_get_output(writer, &dest);
3978     EXPECT_HR(hr, S_OK);
3979     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3980     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)));
3981     VariantClear(&dest);
3982
3983     V_VT(&dest) = VT_EMPTY;
3984     hr = IMXWriter_put_output(writer, dest);
3985     EXPECT_HR(hr, S_OK);
3986
3987     hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0);
3988     EXPECT_HR(hr, S_OK);
3989
3990     V_VT(&dest) = VT_EMPTY;
3991     hr = IMXWriter_get_output(writer, &dest);
3992     EXPECT_HR(hr, S_OK);
3993     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3994     ok(!lstrcmpW(_bstr_("<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3995     VariantClear(&dest);
3996
3997
3998     ISAXContentHandler_Release(content);
3999     IMXWriter_Release(writer);
4000 }
4001
4002 static void test_mxwriter_ignorablespaces(void)
4003 {
4004     static const WCHAR dataW[] = {'d','a','t','a',0};
4005     ISAXContentHandler *content;
4006     IMXWriter *writer;
4007     VARIANT dest;
4008     HRESULT hr;
4009
4010     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4011             &IID_IMXWriter, (void**)&writer);
4012     EXPECT_HR(hr, S_OK);
4013
4014     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4015     EXPECT_HR(hr, S_OK);
4016
4017     hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0);
4018     EXPECT_HR(hr, E_INVALIDARG);
4019
4020     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0);
4021     EXPECT_HR(hr, S_OK);
4022
4023     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4);
4024     EXPECT_HR(hr, S_OK);
4025
4026     hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1);
4027     EXPECT_HR(hr, S_OK);
4028
4029     V_VT(&dest) = VT_EMPTY;
4030     hr = IMXWriter_get_output(writer, &dest);
4031     EXPECT_HR(hr, S_OK);
4032     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4033     ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4034     VariantClear(&dest);
4035
4036     ISAXContentHandler_Release(content);
4037     IMXWriter_Release(writer);
4038 }
4039
4040 static void test_mxwriter_dtd(void)
4041 {
4042     static const WCHAR contentW[] = {'c','o','n','t','e','n','t'};
4043     static const WCHAR nameW[] = {'n','a','m','e'};
4044     static const WCHAR pubW[] = {'p','u','b'};
4045     static const WCHAR sysW[] = {'s','y','s'};
4046     ISAXContentHandler *content;
4047     ISAXLexicalHandler *lexical;
4048     ISAXDeclHandler *decl;
4049     IMXWriter *writer;
4050     VARIANT dest;
4051     HRESULT hr;
4052
4053     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4054             &IID_IMXWriter, (void**)&writer);
4055     EXPECT_HR(hr, S_OK);
4056
4057     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4058     EXPECT_HR(hr, S_OK);
4059
4060     hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4061     EXPECT_HR(hr, S_OK);
4062
4063     hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
4064     EXPECT_HR(hr, S_OK);
4065
4066     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4067     EXPECT_HR(hr, S_OK);
4068
4069     hr = ISAXContentHandler_startDocument(content);
4070     EXPECT_HR(hr, S_OK);
4071
4072     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
4073     EXPECT_HR(hr, E_INVALIDARG);
4074
4075     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4076     EXPECT_HR(hr, E_INVALIDARG);
4077
4078     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, sizeof(sysW)/sizeof(WCHAR));
4079     EXPECT_HR(hr, E_INVALIDARG);
4080
4081     hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4082     EXPECT_HR(hr, E_INVALIDARG);
4083
4084     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0, NULL, 0);
4085     EXPECT_HR(hr, S_OK);
4086
4087     V_VT(&dest) = VT_EMPTY;
4088     hr = IMXWriter_get_output(writer, &dest);
4089     EXPECT_HR(hr, S_OK);
4090     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4091     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4092     VariantClear(&dest);
4093
4094     /* system id is required if public is present */
4095     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4096     EXPECT_HR(hr, E_INVALIDARG);
4097
4098     hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR),
4099         pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4100     EXPECT_HR(hr, S_OK);
4101
4102     V_VT(&dest) = VT_EMPTY;
4103     hr = IMXWriter_get_output(writer, &dest);
4104     EXPECT_HR(hr, S_OK);
4105     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4106     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4107         "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4108     VariantClear(&dest);
4109
4110     hr = ISAXLexicalHandler_endDTD(lexical);
4111     EXPECT_HR(hr, S_OK);
4112
4113     hr = ISAXLexicalHandler_endDTD(lexical);
4114     EXPECT_HR(hr, S_OK);
4115
4116     V_VT(&dest) = VT_EMPTY;
4117     hr = IMXWriter_get_output(writer, &dest);
4118     EXPECT_HR(hr, S_OK);
4119     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4120     ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4121          "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
4122         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4123     VariantClear(&dest);
4124
4125     /* element declaration */
4126     V_VT(&dest) = VT_EMPTY;
4127     hr = IMXWriter_put_output(writer, dest);
4128     EXPECT_HR(hr, S_OK);
4129
4130     hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0);
4131     EXPECT_HR(hr, E_INVALIDARG);
4132
4133     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0);
4134     EXPECT_HR(hr, E_INVALIDARG);
4135
4136     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, sizeof(contentW)/sizeof(WCHAR));
4137     EXPECT_HR(hr, S_OK);
4138
4139     V_VT(&dest) = VT_EMPTY;
4140     hr = IMXWriter_get_output(writer, &dest);
4141     EXPECT_HR(hr, S_OK);
4142     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4143     ok(!lstrcmpW(_bstr_("<!ELEMENT name content>\r\n"),
4144         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4145     VariantClear(&dest);
4146
4147     V_VT(&dest) = VT_EMPTY;
4148     hr = IMXWriter_put_output(writer, dest);
4149     EXPECT_HR(hr, S_OK);
4150
4151     hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, 0);
4152     EXPECT_HR(hr, S_OK);
4153
4154     V_VT(&dest) = VT_EMPTY;
4155     hr = IMXWriter_get_output(writer, &dest);
4156     EXPECT_HR(hr, S_OK);
4157     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4158     ok(!lstrcmpW(_bstr_("<!ELEMENT name >\r\n"),
4159         V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4160     VariantClear(&dest);
4161
4162     ISAXContentHandler_Release(content);
4163     ISAXLexicalHandler_Release(lexical);
4164     ISAXDeclHandler_Release(decl);
4165     IMXWriter_Release(writer);
4166     free_bstrs();
4167 }
4168
4169 typedef struct {
4170     const CLSID *clsid;
4171     const char *uri;
4172     const char *local;
4173     const char *qname;
4174     const char *type;
4175     const char *value;
4176     HRESULT hr;
4177 } addattribute_test_t;
4178
4179 static const addattribute_test_t addattribute_data[] = {
4180     { &CLSID_SAXAttributes,   NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4181     { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4182     { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4183     { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK },
4184
4185     { &CLSID_SAXAttributes,   NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4186     { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4187     { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4188     { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK },
4189
4190     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4191     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4192     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4193     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK },
4194
4195     { &CLSID_SAXAttributes,   "uri", "qname", "ns:qname", "type", "value", S_OK },
4196     { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK },
4197     { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK },
4198     { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK },
4199
4200     { NULL }
4201 };
4202
4203 static void test_mxattr_addAttribute(void)
4204 {
4205     const addattribute_test_t *table = addattribute_data;
4206     int i = 0;
4207
4208     while (table->clsid)
4209     {
4210         ISAXAttributes *saxattr;
4211         IMXAttributes *mxattr;
4212         const WCHAR *value;
4213         int len, index;
4214         HRESULT hr;
4215
4216         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
4217         {
4218             table++;
4219             i++;
4220             continue;
4221         }
4222
4223         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4224             &IID_IMXAttributes, (void**)&mxattr);
4225         EXPECT_HR(hr, S_OK);
4226
4227         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4228         EXPECT_HR(hr, S_OK);
4229
4230         /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4231         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4232             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4233         {
4234             hr = ISAXAttributes_getLength(saxattr, NULL);
4235             EXPECT_HR(hr, E_POINTER);
4236         }
4237
4238         len = -1;
4239         hr = ISAXAttributes_getLength(saxattr, &len);
4240         EXPECT_HR(hr, S_OK);
4241         ok(len == 0, "got %d\n", len);
4242
4243         hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4244         EXPECT_HR(hr, E_INVALIDARG);
4245
4246         hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4247         EXPECT_HR(hr, E_INVALIDARG);
4248
4249         hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4250         EXPECT_HR(hr, E_INVALIDARG);
4251
4252         hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4253         EXPECT_HR(hr, E_INVALIDARG);
4254
4255         hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4256         EXPECT_HR(hr, E_INVALIDARG);
4257
4258         hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4259         EXPECT_HR(hr, E_INVALIDARG);
4260
4261         hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4262         EXPECT_HR(hr, E_INVALIDARG);
4263
4264         hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4265         EXPECT_HR(hr, E_INVALIDARG);
4266
4267         hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local),
4268             _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value));
4269         ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
4270
4271         if (hr == S_OK)
4272         {
4273             /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4274             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4275                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4276             {
4277                hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4278                EXPECT_HR(hr, E_POINTER);
4279
4280                hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4281                EXPECT_HR(hr, E_POINTER);
4282
4283                hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4284                EXPECT_HR(hr, E_POINTER);
4285
4286                hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4287                EXPECT_HR(hr, E_POINTER);
4288
4289                hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4290                EXPECT_HR(hr, E_POINTER);
4291
4292                hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4293                EXPECT_HR(hr, E_POINTER);
4294             }
4295
4296             len = -1;
4297             hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4298             EXPECT_HR(hr, S_OK);
4299             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4300                 table->value);
4301             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4302
4303             len = -1;
4304             value = (void*)0xdeadbeef;
4305             hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4306             EXPECT_HR(hr, S_OK);
4307
4308             if (table->type)
4309             {
4310                 ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4311                     table->type);
4312                 ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len);
4313             }
4314             else
4315             {
4316                 ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value));
4317                 ok(len == 0, "%d: got wrong type value length %d\n", i, len);
4318             }
4319
4320             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL);
4321             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4322                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4323             {
4324                 EXPECT_HR(hr, E_POINTER);
4325             }
4326             else
4327                 EXPECT_HR(hr, E_INVALIDARG);
4328
4329             hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index);
4330             EXPECT_HR(hr, E_INVALIDARG);
4331
4332             index = -1;
4333             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index);
4334             EXPECT_HR(hr, E_INVALIDARG);
4335             ok(index == -1, "%d: got wrong index %d\n", i, index);
4336
4337             index = -1;
4338             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index);
4339             EXPECT_HR(hr, E_INVALIDARG);
4340             ok(index == -1, "%d: got wrong index %d\n", i, index);
4341
4342             index = -1;
4343             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index);
4344             EXPECT_HR(hr, S_OK);
4345             ok(index == 0, "%d: got wrong index %d\n", i, index);
4346
4347             index = -1;
4348             hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index);
4349             EXPECT_HR(hr, E_INVALIDARG);
4350             ok(index == -1, "%d: got wrong index %d\n", i, index);
4351
4352             if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) ||
4353                 IsEqualGUID(table->clsid, &CLSID_SAXAttributes60))
4354             {
4355                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4356                 EXPECT_HR(hr, E_INVALIDARG);
4357
4358                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4359                 EXPECT_HR(hr, E_INVALIDARG);
4360
4361                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4362                 EXPECT_HR(hr, E_INVALIDARG);
4363
4364                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4365                 EXPECT_HR(hr, E_INVALIDARG);
4366
4367                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4368                 EXPECT_HR(hr, E_INVALIDARG);
4369
4370                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4371                 EXPECT_HR(hr, E_INVALIDARG);
4372             }
4373             else
4374             {
4375                 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4376                 EXPECT_HR(hr, E_POINTER);
4377
4378                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4379                 EXPECT_HR(hr, E_POINTER);
4380
4381                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4382                 EXPECT_HR(hr, E_POINTER);
4383
4384                 /* versions 4 and 6 crash */
4385                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL);
4386                 EXPECT_HR(hr, E_POINTER);
4387
4388                 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len);
4389                 EXPECT_HR(hr, E_POINTER);
4390
4391                 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4392                 EXPECT_HR(hr, E_POINTER);
4393
4394                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4395                 EXPECT_HR(hr, E_POINTER);
4396
4397                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4398                 EXPECT_HR(hr, E_POINTER);
4399
4400                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL);
4401                 EXPECT_HR(hr, E_POINTER);
4402
4403                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len);
4404                 EXPECT_HR(hr, E_POINTER);
4405
4406                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local),
4407                     strlen(table->local), NULL, NULL);
4408                 EXPECT_HR(hr, E_POINTER);
4409             }
4410
4411             hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len);
4412             EXPECT_HR(hr, S_OK);
4413             ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4414                 table->value);
4415             ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4416
4417             if (table->uri) {
4418                 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri),
4419                     _bstr_(table->local), strlen(table->local), &value, &len);
4420                 EXPECT_HR(hr, S_OK);
4421                 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4422                     table->value);
4423                 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4424             }
4425         }
4426
4427         len = -1;
4428         hr = ISAXAttributes_getLength(saxattr, &len);
4429         EXPECT_HR(hr, S_OK);
4430         if (table->hr == S_OK)
4431             ok(len == 1, "%d: got %d length, expected 0\n", i, len);
4432         else
4433             ok(len == 0, "%d: got %d length, expected 1\n", i, len);
4434
4435         ISAXAttributes_Release(saxattr);
4436         IMXAttributes_Release(mxattr);
4437
4438         table++;
4439         i++;
4440     }
4441
4442     free_bstrs();
4443 }
4444
4445 static void test_mxattr_clear(void)
4446 {
4447     ISAXAttributes *saxattr;
4448     IMXAttributes *mxattr;
4449     const WCHAR *ptr;
4450     HRESULT hr;
4451     int len;
4452
4453     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4454         &IID_IMXAttributes, (void**)&mxattr);
4455     EXPECT_HR(hr, S_OK);
4456
4457     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4458     EXPECT_HR(hr, S_OK);
4459
4460     hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL);
4461     EXPECT_HR(hr, E_INVALIDARG);
4462
4463     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4464     EXPECT_HR(hr, E_INVALIDARG);
4465
4466     hr = IMXAttributes_clear(mxattr);
4467     EXPECT_HR(hr, S_OK);
4468
4469     hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"),
4470         _bstr_("qname"), _bstr_("type"), _bstr_("value"));
4471     EXPECT_HR(hr, S_OK);
4472
4473     len = -1;
4474     hr = ISAXAttributes_getLength(saxattr, &len);
4475     EXPECT_HR(hr, S_OK);
4476     ok(len == 1, "got %d\n", len);
4477
4478     len = -1;
4479     hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len);
4480     EXPECT_HR(hr, E_POINTER);
4481     ok(len == -1, "got %d\n", len);
4482
4483     ptr = (void*)0xdeadbeef;
4484     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL);
4485     EXPECT_HR(hr, E_POINTER);
4486     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4487
4488     len = 0;
4489     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4490     EXPECT_HR(hr, S_OK);
4491     ok(len == 5, "got %d\n", len);
4492     ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr));
4493
4494     hr = IMXAttributes_clear(mxattr);
4495     EXPECT_HR(hr, S_OK);
4496
4497     len = -1;
4498     hr = ISAXAttributes_getLength(saxattr, &len);
4499     EXPECT_HR(hr, S_OK);
4500     ok(len == 0, "got %d\n", len);
4501
4502     len = -1;
4503     ptr = (void*)0xdeadbeef;
4504     hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4505     EXPECT_HR(hr, E_INVALIDARG);
4506     ok(len == -1, "got %d\n", len);
4507     ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4508
4509     IMXAttributes_Release(mxattr);
4510     ISAXAttributes_Release(saxattr);
4511     free_bstrs();
4512 }
4513
4514 static void test_mxattr_dispex(void)
4515 {
4516     IMXAttributes *mxattr;
4517     IDispatchEx *dispex;
4518     IUnknown *unk;
4519     HRESULT hr;
4520
4521     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4522             &IID_IMXAttributes, (void**)&mxattr);
4523     EXPECT_HR(hr, S_OK);
4524
4525     hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex);
4526     EXPECT_HR(hr, S_OK);
4527     hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4528     test_obj_dispex(unk);
4529     IUnknown_Release(unk);
4530     IDispatchEx_Release(dispex);
4531
4532     IMXAttributes_Release(mxattr);
4533 }
4534
4535 static void test_mxattr_qi(void)
4536 {
4537     IVBSAXAttributes *vbsaxattr, *vbsaxattr2;
4538     ISAXAttributes *saxattr;
4539     IMXAttributes *mxattr;
4540     HRESULT hr;
4541
4542     hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4543             &IID_IMXAttributes, (void**)&mxattr);
4544     EXPECT_HR(hr, S_OK);
4545
4546     EXPECT_REF(mxattr, 1);
4547     hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4548     EXPECT_HR(hr, S_OK);
4549
4550     EXPECT_REF(mxattr, 2);
4551     EXPECT_REF(saxattr, 2);
4552
4553     hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr);
4554     EXPECT_HR(hr, S_OK);
4555
4556     EXPECT_REF(vbsaxattr, 3);
4557     EXPECT_REF(mxattr, 3);
4558     EXPECT_REF(saxattr, 3);
4559
4560     hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2);
4561     EXPECT_HR(hr, S_OK);
4562
4563     EXPECT_REF(vbsaxattr, 4);
4564     EXPECT_REF(mxattr, 4);
4565     EXPECT_REF(saxattr, 4);
4566
4567     IMXAttributes_Release(mxattr);
4568     ISAXAttributes_Release(saxattr);
4569     IVBSAXAttributes_Release(vbsaxattr);
4570     IVBSAXAttributes_Release(vbsaxattr2);
4571 }
4572
4573 static struct msxmlsupported_data_t saxattr_support_data[] =
4574 {
4575     { &CLSID_SAXAttributes,   "SAXAttributes"   },
4576     { &CLSID_SAXAttributes30, "SAXAttributes30" },
4577     { &CLSID_SAXAttributes40, "SAXAttributes40" },
4578     { &CLSID_SAXAttributes60, "SAXAttributes60" },
4579     { NULL }
4580 };
4581
4582 static void test_mxattr_localname(void)
4583 {
4584     static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0};
4585     static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0};
4586     static const WCHAR uri1W[] = {'u','r','i','1',0};
4587     static const WCHAR uriW[] = {'u','r','i',0};
4588
4589     const struct msxmlsupported_data_t *table = saxattr_support_data;
4590
4591     while (table->clsid)
4592     {
4593         ISAXAttributes *saxattr;
4594         IMXAttributes *mxattr;
4595         HRESULT hr;
4596         int index;
4597
4598         if (!is_clsid_supported(table->clsid, mxattributes_support_data))
4599         {
4600             table++;
4601             continue;
4602         }
4603
4604         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4605             &IID_IMXAttributes, (void**)&mxattr);
4606         EXPECT_HR(hr, S_OK);
4607
4608         hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4609         EXPECT_HR(hr, S_OK);
4610
4611         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index);
4612         EXPECT_HR(hr, E_INVALIDARG);
4613
4614         /* add some ambiguos attribute names */
4615         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
4616             _bstr_("a:localname"), _bstr_(""), _bstr_("value"));
4617         EXPECT_HR(hr, S_OK);
4618         hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
4619             _bstr_("b:localname"), _bstr_(""), _bstr_("value"));
4620         EXPECT_HR(hr, S_OK);
4621
4622         index = -1;
4623         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index);
4624         EXPECT_HR(hr, S_OK);
4625         ok(index == 0, "%s: got index %d\n", table->name, index);
4626
4627         index = -1;
4628         hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index);
4629         EXPECT_HR(hr, E_INVALIDARG);
4630         ok(index == -1, "%s: got index %d\n", table->name, index);
4631
4632         index = -1;
4633         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index);
4634         EXPECT_HR(hr, E_INVALIDARG);
4635         ok(index == -1, "%s: got index %d\n", table->name, index);
4636
4637         if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4638             IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4639         {
4640             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
4641             EXPECT_HR(hr, E_POINTER);
4642
4643             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
4644             EXPECT_HR(hr, E_POINTER);
4645         }
4646         else
4647         {
4648             hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
4649             EXPECT_HR(hr, E_INVALIDARG);
4650
4651             hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
4652             EXPECT_HR(hr, E_INVALIDARG);
4653         }
4654
4655         hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index);
4656         EXPECT_HR(hr, E_INVALIDARG);
4657
4658         hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index);
4659         EXPECT_HR(hr, E_INVALIDARG);
4660
4661         table++;
4662
4663         ISAXAttributes_Release(saxattr);
4664         IMXAttributes_Release(mxattr);
4665     }
4666 }
4667
4668 START_TEST(saxreader)
4669 {
4670     ISAXXMLReader *reader;
4671     HRESULT hr;
4672
4673     hr = CoInitialize(NULL);
4674     ok(hr == S_OK, "failed to init com\n");
4675
4676     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
4677             &IID_ISAXXMLReader, (void**)&reader);
4678
4679     if(FAILED(hr))
4680     {
4681         skip("Failed to create SAXXMLReader instance\n");
4682         CoUninitialize();
4683         return;
4684     }
4685     ISAXXMLReader_Release(reader);
4686
4687     init_call_sequences(sequences, NUM_CALL_SEQUENCES);
4688
4689     get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
4690
4691     test_saxreader();
4692     test_saxreader_properties();
4693     test_saxreader_features();
4694     test_saxreader_encoding();
4695     test_dispex();
4696
4697     /* MXXMLWriter tests */
4698     get_class_support_data(mxwriter_support_data, &IID_IMXWriter);
4699     if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data))
4700     {
4701         test_mxwriter_handlers();
4702         test_mxwriter_startenddocument();
4703         test_mxwriter_startendelement();
4704         test_mxwriter_characters();
4705         test_mxwriter_comment();
4706         test_mxwriter_cdata();
4707         test_mxwriter_pi();
4708         test_mxwriter_ignorablespaces();
4709         test_mxwriter_dtd();
4710         test_mxwriter_properties();
4711         test_mxwriter_flush();
4712         test_mxwriter_stream();
4713         test_mxwriter_encoding();
4714         test_mxwriter_dispex();
4715     }
4716     else
4717         win_skip("MXXMLWriter not supported\n");
4718
4719     /* SAXAttributes tests */
4720     get_class_support_data(mxattributes_support_data, &IID_IMXAttributes);
4721     if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data))
4722     {
4723         test_mxattr_qi();
4724         test_mxattr_addAttribute();
4725         test_mxattr_clear();
4726         test_mxattr_localname();
4727         test_mxattr_dispex();
4728     }
4729     else
4730         skip("SAXAttributes not supported\n");
4731
4732     CoUninitialize();
4733 }