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