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