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