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