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