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