msxml3: Use provided qualified name length while writing end element tag.
[wine] / dlls / msxml3 / tests / saxreader.c
1 /*
2  * XML test
3  *
4  * Copyright 2008 Piotr Caban
5  * Copyright 2011 Thomas Mullaly
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23 #define CONST_VTABLE
24
25 #include <stdio.h>
26 #include <assert.h>
27
28 #include "windows.h"
29 #include "ole2.h"
30 #include "msxml2.h"
31 #include "ocidl.h"
32
33 #include "wine/test.h"
34
35 #define EXPECT_HR(hr,hr_exp) \
36     ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
37
38 #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
39 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
40 {
41     ULONG rc = IUnknown_AddRef(obj);
42     IUnknown_Release(obj);
43     ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1);
44 }
45
46 static BSTR alloc_str_from_narrow(const char *str)
47 {
48     int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
49     BSTR ret = SysAllocStringLen(NULL, len - 1);  /* NUL character added automatically */
50     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
51     return ret;
52 }
53
54 static BSTR alloced_bstrs[512];
55 static int alloced_bstrs_count;
56
57 static BSTR _bstr_(const char *str)
58 {
59     assert(alloced_bstrs_count < sizeof(alloced_bstrs)/sizeof(alloced_bstrs[0]));
60     alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str);
61     return alloced_bstrs[alloced_bstrs_count++];
62 }
63
64 static void free_bstrs(void)
65 {
66     int i;
67     for (i = 0; i < alloced_bstrs_count; i++)
68         SysFreeString(alloced_bstrs[i]);
69     alloced_bstrs_count = 0;
70 }
71
72 typedef enum _CH {
73     CH_ENDTEST,
74     CH_PUTDOCUMENTLOCATOR,
75     CH_STARTDOCUMENT,
76     CH_ENDDOCUMENT,
77     CH_STARTPREFIXMAPPING,
78     CH_ENDPREFIXMAPPING,
79     CH_STARTELEMENT,
80     CH_ENDELEMENT,
81     CH_CHARACTERS,
82     CH_IGNORABLEWHITESPACE,
83     CH_PROCESSINGINSTRUCTION,
84     CH_SKIPPEDENTITY
85 } CH;
86
87 static const WCHAR szSimpleXML[] = {
88 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n',
89 '<','B','a','n','k','A','c','c','o','u','n','t','>','\n',
90 ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n',
91 ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n',
92 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0'
93 };
94
95 static const WCHAR szCarriageRetTest[] = {
96 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n',
97 '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n',
98 '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n',
99 '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n',
100 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\0'
101 };
102
103 static const WCHAR szUtf16XML[] = {
104 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ',
105 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ',
106 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n'
107 };
108
109 static const CHAR szUtf16BOM[] = {0xff, 0xfe};
110
111 static const CHAR szUtf8XML[] =
112 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n";
113
114 static const CHAR szTestXML[] =
115 "<?xml version=\"1.0\" ?>\n"
116 "<BankAccount>\n"
117 "   <Number>1234</Number>\n"
118 "   <Name>Captain Ahab</Name>\n"
119 "</BankAccount>\n";
120
121 typedef struct _contenthandlercheck {
122     CH id;
123     int line;
124     int column;
125     const char *arg1;
126     const char *arg2;
127     const char *arg3;
128 } content_handler_test;
129
130 static content_handler_test contentHandlerTest1[] = {
131     { CH_PUTDOCUMENTLOCATOR, 0, 0 },
132     { CH_STARTDOCUMENT, 0, 0 },
133     { CH_STARTELEMENT, 2, 14, "", "BankAccount", "BankAccount" },
134     { CH_CHARACTERS, 2, 14, "\n   " },
135     { CH_STARTELEMENT, 3, 12, "", "Number", "Number" },
136     { CH_CHARACTERS, 3, 12, "1234" },
137     { CH_ENDELEMENT, 3, 18, "", "Number", "Number" },
138     { CH_CHARACTERS, 3, 25, "\n   " },
139     { CH_STARTELEMENT, 4, 10, "", "Name", "Name" },
140     { CH_CHARACTERS, 4, 10, "Captain Ahab" },
141     { CH_ENDELEMENT, 4, 24, "", "Name", "Name" },
142     { CH_CHARACTERS, 4, 29, "\n" },
143     { CH_ENDELEMENT, 5, 3, "", "BankAccount", "BankAccount" },
144     { CH_ENDDOCUMENT, 0, 0 },
145     { CH_ENDTEST }
146 };
147
148 static content_handler_test contentHandlerTest2[] = {
149     { CH_PUTDOCUMENTLOCATOR, 0, 0 },
150     { CH_STARTDOCUMENT, 0, 0 },
151     { CH_STARTELEMENT, 2, 14, "", "BankAccount", "BankAccount" },
152     { CH_CHARACTERS, 2, 14, "\n" },
153     { CH_CHARACTERS, 2, 16, "\t" },
154     { CH_STARTELEMENT, 3, 10, "", "Number", "Number" },
155     { CH_CHARACTERS, 3, 10, "1234" },
156     { CH_ENDELEMENT, 3, 16, "", "Number", "Number" },
157     { CH_CHARACTERS, 3, 23, "\n" },
158     { CH_CHARACTERS, 3, 25, "\t" },
159     { CH_STARTELEMENT, 4, 8, "", "Name", "Name" },
160     { CH_CHARACTERS, 4, 8, "Captain Ahab" },
161     { CH_ENDELEMENT, 4, 22, "", "Name", "Name" },
162     { CH_CHARACTERS, 4, 27, "\n" },
163     { CH_ENDELEMENT, 5, 3, "", "BankAccount", "BankAccount" },
164     { CH_ENDDOCUMENT, 0, 0 },
165     { CH_ENDTEST }
166 };
167
168 static content_handler_test *expectCall;
169 static ISAXLocator *locator;
170
171 static void test_saxstr(unsigned line, const WCHAR *szStr, int nStr, const char *szTest)
172 {
173     WCHAR buf[1024];
174     int len;
175
176     if(!szTest) {
177         ok_(__FILE__,line) (szStr == NULL, "szStr != NULL\n");
178         ok_(__FILE__,line) (nStr == 0, "nStr = %d, expected 0\n", nStr);
179         return;
180     }
181
182     len = strlen(szTest);
183     ok_(__FILE__,line) (len == nStr, "nStr = %d, expected %d (%s)\n", nStr, len, szTest);
184     if(len != nStr)
185         return;
186
187     MultiByteToWideChar(CP_ACP, 0, szTest, -1, buf, sizeof(buf)/sizeof(WCHAR));
188     ok_(__FILE__,line) (!memcmp(szStr, buf, len*sizeof(WCHAR)), "unexpected szStr %s, expected %s\n",
189                         wine_dbgstr_wn(szStr, nStr), szTest);
190 }
191
192 static BOOL test_expect_call(CH id)
193 {
194     ok(expectCall->id == id, "unexpected call %d, expected %d\n", id, expectCall->id);
195     return expectCall->id == id;
196 }
197
198 static void test_locator(unsigned line, int loc_line, int loc_column)
199 {
200     int rcolumn, rline;
201     ISAXLocator_getLineNumber(locator, &rline);
202     ISAXLocator_getColumnNumber(locator, &rcolumn);
203
204     ok_(__FILE__,line) (rline == loc_line,
205             "unexpected line %d, expected %d\n", rline, loc_line);
206     ok_(__FILE__,line) (rcolumn == loc_column,
207             "unexpected column %d, expected %d\n", rcolumn, loc_column);
208 }
209
210 static HRESULT WINAPI contentHandler_QueryInterface(
211         ISAXContentHandler* iface,
212         REFIID riid,
213         void **ppvObject)
214 {
215     *ppvObject = NULL;
216
217     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
218     {
219         *ppvObject = iface;
220     }
221     else
222     {
223         return E_NOINTERFACE;
224     }
225
226     return S_OK;
227 }
228
229 static ULONG WINAPI contentHandler_AddRef(
230         ISAXContentHandler* iface)
231 {
232     return 2;
233 }
234
235 static ULONG WINAPI contentHandler_Release(
236         ISAXContentHandler* iface)
237 {
238     return 1;
239 }
240
241 static HRESULT WINAPI contentHandler_putDocumentLocator(
242         ISAXContentHandler* iface,
243         ISAXLocator *pLocator)
244 {
245     if(!test_expect_call(CH_PUTDOCUMENTLOCATOR))
246         return E_FAIL;
247
248     locator = pLocator;
249     test_locator(__LINE__, expectCall->line, expectCall->column);
250
251     expectCall++;
252     return S_OK;
253 }
254
255 static HRESULT WINAPI contentHandler_startDocument(
256         ISAXContentHandler* iface)
257 {
258     if(!test_expect_call(CH_STARTDOCUMENT))
259         return E_FAIL;
260
261     test_locator(__LINE__, expectCall->line, expectCall->column);
262
263     expectCall++;
264     return S_OK;
265 }
266
267 static HRESULT WINAPI contentHandler_endDocument(
268         ISAXContentHandler* iface)
269 {
270     if(!test_expect_call(CH_ENDDOCUMENT))
271         return E_FAIL;
272
273     test_locator(__LINE__, expectCall->line, expectCall->column);
274
275     expectCall++;
276     return S_OK;
277 }
278
279 static HRESULT WINAPI contentHandler_startPrefixMapping(
280         ISAXContentHandler* iface,
281         const WCHAR *pPrefix,
282         int nPrefix,
283         const WCHAR *pUri,
284         int nUri)
285 {
286     if(!test_expect_call(CH_ENDDOCUMENT))
287         return E_FAIL;
288
289     test_saxstr(__LINE__, pPrefix, nPrefix, expectCall->arg1);
290     test_saxstr(__LINE__, pUri, nUri, expectCall->arg2);
291     test_locator(__LINE__, expectCall->line, expectCall->column);
292
293     expectCall++;
294     return S_OK;
295 }
296
297 static HRESULT WINAPI contentHandler_endPrefixMapping(
298         ISAXContentHandler* iface,
299         const WCHAR *pPrefix,
300         int nPrefix)
301 {
302     if(!test_expect_call(CH_ENDPREFIXMAPPING))
303         return E_FAIL;
304
305     test_saxstr(__LINE__, pPrefix, nPrefix, expectCall->arg1);
306     test_locator(__LINE__, expectCall->line, expectCall->column);
307
308     expectCall++;
309     return S_OK;
310 }
311
312 static HRESULT WINAPI contentHandler_startElement(
313         ISAXContentHandler* iface,
314         const WCHAR *pNamespaceUri,
315         int nNamespaceUri,
316         const WCHAR *pLocalName,
317         int nLocalName,
318         const WCHAR *pQName,
319         int nQName,
320         ISAXAttributes *pAttr)
321 {
322     if(!test_expect_call(CH_STARTELEMENT))
323         return E_FAIL;
324
325     test_saxstr(__LINE__, pNamespaceUri, nNamespaceUri, expectCall->arg1);
326     test_saxstr(__LINE__, pLocalName, nLocalName, expectCall->arg2);
327     test_saxstr(__LINE__, pQName, nQName, expectCall->arg3);
328     test_locator(__LINE__, expectCall->line, expectCall->column);
329
330     expectCall++;
331     return S_OK;
332 }
333
334 static HRESULT WINAPI contentHandler_endElement(
335         ISAXContentHandler* iface,
336         const WCHAR *pNamespaceUri,
337         int nNamespaceUri,
338         const WCHAR *pLocalName,
339         int nLocalName,
340         const WCHAR *pQName,
341         int nQName)
342 {
343     if(!test_expect_call(CH_ENDELEMENT))
344         return E_FAIL;
345
346     test_saxstr(__LINE__, pNamespaceUri, nNamespaceUri, expectCall->arg1);
347     test_saxstr(__LINE__, pLocalName, nLocalName, expectCall->arg2);
348     test_saxstr(__LINE__, pQName, nQName, expectCall->arg3);
349     test_locator(__LINE__, expectCall->line, expectCall->column);
350
351     expectCall++;
352     return S_OK;
353 }
354
355 static HRESULT WINAPI contentHandler_characters(
356         ISAXContentHandler* iface,
357         const WCHAR *pChars,
358         int nChars)
359 {
360     if(!test_expect_call(CH_CHARACTERS))
361         return E_FAIL;
362
363     test_saxstr(__LINE__, pChars, nChars, expectCall->arg1);
364     test_locator(__LINE__, expectCall->line, expectCall->column);
365
366     expectCall++;
367     return S_OK;
368 }
369
370 static HRESULT WINAPI contentHandler_ignorableWhitespace(
371         ISAXContentHandler* iface,
372         const WCHAR *pChars,
373         int nChars)
374 {
375     if(!test_expect_call(CH_IGNORABLEWHITESPACE))
376         return E_FAIL;
377
378     test_saxstr(__LINE__, pChars, nChars, expectCall->arg1);
379     test_locator(__LINE__, expectCall->line, expectCall->column);
380
381     expectCall++;
382     return S_OK;
383 }
384
385 static HRESULT WINAPI contentHandler_processingInstruction(
386         ISAXContentHandler* iface,
387         const WCHAR *pTarget,
388         int nTarget,
389         const WCHAR *pData,
390         int nData)
391 {
392     if(!test_expect_call(CH_PROCESSINGINSTRUCTION))
393         return E_FAIL;
394
395     test_saxstr(__LINE__, pTarget, nTarget, expectCall->arg1);
396     test_saxstr(__LINE__, pData, nData, expectCall->arg2);
397     test_locator(__LINE__, expectCall->line, expectCall->column);
398
399     expectCall++;
400     return S_OK;
401 }
402
403 static HRESULT WINAPI contentHandler_skippedEntity(
404         ISAXContentHandler* iface,
405         const WCHAR *pName,
406         int nName)
407 {
408     if(!test_expect_call(CH_SKIPPEDENTITY))
409         return E_FAIL;
410
411     test_saxstr(__LINE__, pName, nName, expectCall->arg1);
412     test_locator(__LINE__, expectCall->line, expectCall->column);
413
414     expectCall++;
415     return S_OK;
416 }
417
418
419 static const ISAXContentHandlerVtbl contentHandlerVtbl =
420 {
421     contentHandler_QueryInterface,
422     contentHandler_AddRef,
423     contentHandler_Release,
424     contentHandler_putDocumentLocator,
425     contentHandler_startDocument,
426     contentHandler_endDocument,
427     contentHandler_startPrefixMapping,
428     contentHandler_endPrefixMapping,
429     contentHandler_startElement,
430     contentHandler_endElement,
431     contentHandler_characters,
432     contentHandler_ignorableWhitespace,
433     contentHandler_processingInstruction,
434     contentHandler_skippedEntity
435 };
436
437 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
438
439 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
440         ISAXErrorHandler* iface,
441         REFIID riid,
442         void **ppvObject)
443 {
444     *ppvObject = NULL;
445
446     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
447     {
448         *ppvObject = iface;
449     }
450     else
451     {
452         return E_NOINTERFACE;
453     }
454
455     return S_OK;
456 }
457
458 static ULONG WINAPI isaxerrorHandler_AddRef(
459         ISAXErrorHandler* iface)
460 {
461     return 2;
462 }
463
464 static ULONG WINAPI isaxerrorHandler_Release(
465         ISAXErrorHandler* iface)
466 {
467     return 1;
468 }
469
470 static HRESULT WINAPI isaxerrorHandler_error(
471         ISAXErrorHandler* iface,
472         ISAXLocator *pLocator,
473         const WCHAR *pErrorMessage,
474         HRESULT hrErrorCode)
475 {
476     return S_OK;
477 }
478
479 static HRESULT WINAPI isaxerrorHandler_fatalError(
480         ISAXErrorHandler* iface,
481         ISAXLocator *pLocator,
482         const WCHAR *pErrorMessage,
483         HRESULT hrErrorCode)
484 {
485     return S_OK;
486 }
487
488 static HRESULT WINAPI isaxerrorHanddler_ignorableWarning(
489         ISAXErrorHandler* iface,
490         ISAXLocator *pLocator,
491         const WCHAR *pErrorMessage,
492         HRESULT hrErrorCode)
493 {
494     return S_OK;
495 }
496
497 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
498 {
499     isaxerrorHandler_QueryInterface,
500     isaxerrorHandler_AddRef,
501     isaxerrorHandler_Release,
502     isaxerrorHandler_error,
503     isaxerrorHandler_fatalError,
504     isaxerrorHanddler_ignorableWarning
505 };
506
507 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
508
509 static HRESULT WINAPI isaxattributes_QueryInterface(
510         ISAXAttributes* iface,
511         REFIID riid,
512         void **ppvObject)
513 {
514     *ppvObject = NULL;
515
516     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
517     {
518         *ppvObject = iface;
519     }
520     else
521     {
522         return E_NOINTERFACE;
523     }
524
525     return S_OK;
526 }
527
528 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
529 {
530     return 2;
531 }
532
533 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
534 {
535     return 1;
536 }
537
538 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
539 {
540     *length = 2;
541     return S_OK;
542 }
543
544 static HRESULT WINAPI isaxattributes_getURI(
545     ISAXAttributes* iface,
546     int nIndex,
547     const WCHAR **pUrl,
548     int *pUriSize)
549 {
550     ok(0, "unexpected call\n");
551     return E_NOTIMPL;
552 }
553
554 static HRESULT WINAPI isaxattributes_getLocalName(
555     ISAXAttributes* iface,
556     int nIndex,
557     const WCHAR **pLocalName,
558     int *pLocalNameLength)
559 {
560     ok(0, "unexpected call\n");
561     return E_NOTIMPL;
562 }
563
564 static HRESULT WINAPI isaxattributes_getQName(
565     ISAXAttributes* iface,
566     int nIndex,
567     const WCHAR **pQName,
568     int *pQNameLength)
569 {
570     static const WCHAR attr1W[] = {'a',':','a','t','t','r','1','j','u','n','k',0};
571     static const WCHAR attr2W[] = {'a','t','t','r','2','j','u','n','k',0};
572
573     ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
574
575     *pQName = (nIndex == 0) ? attr1W : attr2W;
576     *pQNameLength = (nIndex == 0) ? 7 : 5;
577
578     return S_OK;
579 }
580
581 static HRESULT WINAPI isaxattributes_getName(
582     ISAXAttributes* iface,
583     int nIndex,
584     const WCHAR **pUri,
585     int * pUriLength,
586     const WCHAR ** pLocalName,
587     int * pLocalNameSize,
588     const WCHAR ** pQName,
589     int * pQNameLength)
590 {
591     ok(0, "unexpected call\n");
592     return E_NOTIMPL;
593 }
594
595 static HRESULT WINAPI isaxattributes_getIndexFromName(
596     ISAXAttributes* iface,
597     const WCHAR * pUri,
598     int cUriLength,
599     const WCHAR * pLocalName,
600     int cocalNameLength,
601     int * index)
602 {
603     ok(0, "unexpected call\n");
604     return E_NOTIMPL;
605 }
606
607 static HRESULT WINAPI isaxattributes_getIndexFromQName(
608     ISAXAttributes* iface,
609     const WCHAR * pQName,
610     int nQNameLength,
611     int * index)
612 {
613     ok(0, "unexpected call\n");
614     return E_NOTIMPL;
615 }
616
617 static HRESULT WINAPI isaxattributes_getType(
618     ISAXAttributes* iface,
619     int nIndex,
620     const WCHAR ** pType,
621     int * pTypeLength)
622 {
623     ok(0, "unexpected call\n");
624     return E_NOTIMPL;
625 }
626
627 static HRESULT WINAPI isaxattributes_getTypeFromName(
628     ISAXAttributes* iface,
629     const WCHAR * pUri,
630     int nUri,
631     const WCHAR * pLocalName,
632     int nLocalName,
633     const WCHAR ** pType,
634     int * nType)
635 {
636     ok(0, "unexpected call\n");
637     return E_NOTIMPL;
638 }
639
640 static HRESULT WINAPI isaxattributes_getTypeFromQName(
641     ISAXAttributes* iface,
642     const WCHAR * pQName,
643     int nQName,
644     const WCHAR ** pType,
645     int * nType)
646 {
647     ok(0, "unexpected call\n");
648     return E_NOTIMPL;
649 }
650
651 static HRESULT WINAPI isaxattributes_getValue(
652     ISAXAttributes* iface,
653     int nIndex,
654     const WCHAR ** pValue,
655     int * nValue)
656 {
657     static const WCHAR attrval1W[] = {'a','1','j','u','n','k',0};
658     static const WCHAR attrval2W[] = {'a','2','j','u','n','k',0};
659
660     ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
661
662     *pValue = (nIndex == 0) ? attrval1W : attrval2W;
663     *nValue = 2;
664
665     return S_OK;
666 }
667
668 static HRESULT WINAPI isaxattributes_getValueFromName(
669     ISAXAttributes* iface,
670     const WCHAR * pUri,
671     int nUri,
672     const WCHAR * pLocalName,
673     int nLocalName,
674     const WCHAR ** pValue,
675     int * nValue)
676 {
677     ok(0, "unexpected call\n");
678     return E_NOTIMPL;
679 }
680
681 static HRESULT WINAPI isaxattributes_getValueFromQName(
682     ISAXAttributes* iface,
683     const WCHAR * pQName,
684     int nQName,
685     const WCHAR ** pValue,
686     int * nValue)
687 {
688     ok(0, "unexpected call\n");
689     return E_NOTIMPL;
690 }
691
692 static const ISAXAttributesVtbl SAXAttributesVtbl =
693 {
694     isaxattributes_QueryInterface,
695     isaxattributes_AddRef,
696     isaxattributes_Release,
697     isaxattributes_getLength,
698     isaxattributes_getURI,
699     isaxattributes_getLocalName,
700     isaxattributes_getQName,
701     isaxattributes_getName,
702     isaxattributes_getIndexFromName,
703     isaxattributes_getIndexFromQName,
704     isaxattributes_getType,
705     isaxattributes_getTypeFromName,
706     isaxattributes_getTypeFromQName,
707     isaxattributes_getValue,
708     isaxattributes_getValueFromName,
709     isaxattributes_getValueFromQName
710 };
711
712 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
713
714 static int handler_addrefcalled;
715
716 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **ppvObject)
717 {
718     *ppvObject = NULL;
719
720     if(IsEqualGUID(riid, &IID_IUnknown) ||
721        IsEqualGUID(riid, &IID_ISAXLexicalHandler))
722     {
723         *ppvObject = iface;
724     }
725     else
726     {
727         return E_NOINTERFACE;
728     }
729
730     return S_OK;
731 }
732
733 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
734 {
735     handler_addrefcalled++;
736     return 2;
737 }
738
739 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
740 {
741     return 1;
742 }
743
744 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
745     const WCHAR * pName, int nName, const WCHAR * pPublicId,
746     int nPublicId, const WCHAR * pSystemId, int nSystemId)
747 {
748     ok(0, "call not expected\n");
749     return E_NOTIMPL;
750 }
751
752 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
753 {
754     ok(0, "call not expected\n");
755     return E_NOTIMPL;
756 }
757
758 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
759     const WCHAR * pName, int nName)
760 {
761     ok(0, "call not expected\n");
762     return E_NOTIMPL;
763 }
764
765 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
766     const WCHAR * pName, int nName)
767 {
768     ok(0, "call not expected\n");
769     return E_NOTIMPL;
770 }
771
772 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
773 {
774     ok(0, "call not expected\n");
775     return E_NOTIMPL;
776 }
777
778 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
779 {
780     ok(0, "call not expected\n");
781     return E_NOTIMPL;
782 }
783
784 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
785     const WCHAR * pChars, int nChars)
786 {
787     ok(0, "call not expected\n");
788     return E_NOTIMPL;
789 }
790
791 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
792 {
793    isaxlexical_QueryInterface,
794    isaxlexical_AddRef,
795    isaxlexical_Release,
796    isaxlexical_startDTD,
797    isaxlexical_endDTD,
798    isaxlexical_startEntity,
799    isaxlexical_endEntity,
800    isaxlexical_startCDATA,
801    isaxlexical_endCDATA,
802    isaxlexical_comment
803 };
804
805 static ISAXLexicalHandler saxlexicalhandler = { &SAXLexicalHandlerVtbl };
806
807 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **ppvObject)
808 {
809     *ppvObject = NULL;
810
811     if(IsEqualGUID(riid, &IID_IUnknown) ||
812        IsEqualGUID(riid, &IID_ISAXDeclHandler))
813     {
814         *ppvObject = iface;
815     }
816     else
817     {
818         return E_NOINTERFACE;
819     }
820
821     return S_OK;
822 }
823
824 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
825 {
826     handler_addrefcalled++;
827     return 2;
828 }
829
830 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
831 {
832     return 1;
833 }
834
835 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
836     const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
837 {
838     ok(0, "call not expected\n");
839     return E_NOTIMPL;
840 }
841
842 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
843     const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
844     int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
845     int nValueDefault, const WCHAR * pValue, int nValue)
846 {
847     ok(0, "call not expected\n");
848     return E_NOTIMPL;
849 }
850
851 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
852     const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
853 {
854     ok(0, "call not expected\n");
855     return E_NOTIMPL;
856 }
857
858 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
859     const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
860     const WCHAR * pSystemId, int nSystemId)
861 {
862     ok(0, "call not expected\n");
863     return E_NOTIMPL;
864 }
865
866 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
867 {
868    isaxdecl_QueryInterface,
869    isaxdecl_AddRef,
870    isaxdecl_Release,
871    isaxdecl_elementDecl,
872    isaxdecl_attributeDecl,
873    isaxdecl_internalEntityDecl,
874    isaxdecl_externalEntityDecl
875 };
876
877 static ISAXDeclHandler saxdeclhandler = { &SAXDeclHandlerVtbl };
878
879 typedef struct mxwriter_write_test_t {
880     BOOL        last;
881     const BYTE  *data;
882     DWORD       cb;
883     BOOL        null_written;
884     BOOL        fail_write;
885 } mxwriter_write_test;
886
887 typedef struct mxwriter_stream_test_t {
888     VARIANT_BOOL        bom;
889     const char          *encoding;
890     mxwriter_write_test expected_writes[4];
891 } mxwriter_stream_test;
892
893 static const mxwriter_write_test *current_write_test;
894 static DWORD current_stream_test_index;
895
896 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
897 {
898     *ppvObject = NULL;
899
900     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
901         *ppvObject = iface;
902     else
903         return E_NOINTERFACE;
904
905     return S_OK;
906 }
907
908 static ULONG WINAPI istream_AddRef(IStream *iface)
909 {
910     return 2;
911 }
912
913 static ULONG WINAPI istream_Release(IStream *iface)
914 {
915     return 1;
916 }
917
918 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
919 {
920     ok(0, "unexpected call\n");
921     return E_NOTIMPL;
922 }
923
924 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
925 {
926     BOOL fail = FALSE;
927
928     ok(pv != NULL, "pv == NULL\n");
929
930     if(current_write_test->last) {
931         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
932         return E_FAIL;
933     }
934
935     fail = current_write_test->fail_write;
936
937     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
938         current_write_test->cb, cb, current_stream_test_index);
939
940     if(!pcbWritten)
941         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
942     else
943         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
944
945     ++current_write_test;
946
947     if(pcbWritten)
948         *pcbWritten = cb;
949
950     return fail ? E_FAIL : S_OK;
951 }
952
953 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
954         ULARGE_INTEGER *plibNewPosition)
955 {
956     ok(0, "unexpected call\n");
957     return E_NOTIMPL;
958 }
959
960 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
961 {
962     ok(0, "unexpected call\n");
963     return E_NOTIMPL;
964 }
965
966 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
967         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
968 {
969     ok(0, "unexpected call\n");
970     return E_NOTIMPL;
971 }
972
973 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
974 {
975     ok(0, "unexpected call\n");
976     return E_NOTIMPL;
977 }
978
979 static HRESULT WINAPI istream_Revert(IStream *iface)
980 {
981     ok(0, "unexpected call\n");
982     return E_NOTIMPL;
983 }
984
985 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
986         ULARGE_INTEGER cb, DWORD dwLockType)
987 {
988     ok(0, "unexpected call\n");
989     return E_NOTIMPL;
990 }
991
992 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
993         ULARGE_INTEGER cb, DWORD dwLockType)
994 {
995     ok(0, "unexpected call\n");
996     return E_NOTIMPL;
997 }
998
999 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
1000 {
1001     ok(0, "unexpected call\n");
1002     return E_NOTIMPL;
1003 }
1004
1005 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
1006 {
1007     ok(0, "unexpected call\n");
1008     return E_NOTIMPL;
1009 }
1010
1011 static const IStreamVtbl StreamVtbl = {
1012     istream_QueryInterface,
1013     istream_AddRef,
1014     istream_Release,
1015     istream_Read,
1016     istream_Write,
1017     istream_Seek,
1018     istream_SetSize,
1019     istream_CopyTo,
1020     istream_Commit,
1021     istream_Revert,
1022     istream_LockRegion,
1023     istream_UnlockRegion,
1024     istream_Stat,
1025     istream_Clone
1026 };
1027
1028 static IStream mxstream = { &StreamVtbl };
1029
1030 static void test_saxreader(void)
1031 {
1032     HRESULT hr;
1033     ISAXXMLReader *reader = NULL;
1034     VARIANT var;
1035     ISAXContentHandler *lpContentHandler;
1036     ISAXErrorHandler *lpErrorHandler;
1037     SAFEARRAY *pSA;
1038     SAFEARRAYBOUND SADim[1];
1039     char *pSAData = NULL;
1040     IStream *iStream;
1041     ULARGE_INTEGER liSize;
1042     LARGE_INTEGER liPos;
1043     ULONG bytesWritten;
1044     HANDLE file;
1045     static const CHAR testXmlA[] = "test.xml";
1046     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1047     IXMLDOMDocument *domDocument;
1048     BSTR bstrData;
1049     VARIANT_BOOL vBool;
1050
1051     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
1052             &IID_ISAXXMLReader, (LPVOID*)&reader);
1053     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1054
1055     hr = ISAXXMLReader_getContentHandler(reader, NULL);
1056     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
1057
1058     hr = ISAXXMLReader_getErrorHandler(reader, NULL);
1059     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
1060
1061     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
1062     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1063     ok(lpContentHandler == NULL, "Expected %p, got %p\n", NULL, lpContentHandler);
1064
1065     hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
1066     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1067     ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
1068
1069     hr = ISAXXMLReader_putContentHandler(reader, NULL);
1070     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1071
1072     hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
1073     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1074
1075     hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
1076     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1077
1078     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
1079     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1080     ok(lpContentHandler == &contentHandler, "Expected %p, got %p\n", &contentHandler, lpContentHandler);
1081
1082     V_VT(&var) = VT_BSTR;
1083     V_BSTR(&var) = SysAllocString(szSimpleXML);
1084
1085     expectCall = contentHandlerTest1;
1086     hr = ISAXXMLReader_parse(reader, var);
1087     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1088     test_expect_call(CH_ENDTEST);
1089
1090     VariantClear(&var);
1091
1092     SADim[0].lLbound= 0;
1093     SADim[0].cElements= sizeof(szTestXML)-1;
1094     pSA = SafeArrayCreate(VT_UI1, 1, SADim);
1095     SafeArrayAccessData(pSA, (void**)&pSAData);
1096     memcpy(pSAData, szTestXML, sizeof(szTestXML)-1);
1097     SafeArrayUnaccessData(pSA);
1098     V_VT(&var) = VT_ARRAY|VT_UI1;
1099     V_ARRAY(&var) = pSA;
1100
1101     expectCall = contentHandlerTest1;
1102     hr = ISAXXMLReader_parse(reader, var);
1103     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1104     test_expect_call(CH_ENDTEST);
1105
1106     SafeArrayDestroy(pSA);
1107
1108     CreateStreamOnHGlobal(NULL, TRUE, &iStream);
1109     liSize.QuadPart = strlen(szTestXML);
1110     IStream_SetSize(iStream, liSize);
1111     IStream_Write(iStream, szTestXML, strlen(szTestXML), &bytesWritten);
1112     liPos.QuadPart = 0;
1113     IStream_Seek(iStream, liPos, STREAM_SEEK_SET, NULL);
1114     V_VT(&var) = VT_UNKNOWN|VT_DISPATCH;
1115     V_UNKNOWN(&var) = (IUnknown*)iStream;
1116
1117     expectCall = contentHandlerTest1;
1118     hr = ISAXXMLReader_parse(reader, var);
1119     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1120     test_expect_call(CH_ENDTEST);
1121
1122     IStream_Release(iStream);
1123
1124     V_VT(&var) = VT_BSTR;
1125     V_BSTR(&var) = SysAllocString(szCarriageRetTest);
1126
1127     expectCall = contentHandlerTest2;
1128     hr = ISAXXMLReader_parse(reader, var);
1129     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1130     test_expect_call(CH_ENDTEST);
1131
1132     VariantClear(&var);
1133
1134     file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1135     ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
1136     WriteFile(file, szTestXML, sizeof(szTestXML)-1, &bytesWritten, NULL);
1137     CloseHandle(file);
1138
1139     expectCall = contentHandlerTest1;
1140     hr = ISAXXMLReader_parseURL(reader, testXmlW);
1141     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1142     test_expect_call(CH_ENDTEST);
1143
1144     DeleteFileA(testXmlA);
1145
1146     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
1147             &IID_IXMLDOMDocument, (LPVOID*)&domDocument);
1148     if(FAILED(hr))
1149     {
1150         skip("Failed to create DOMDocument instance\n");
1151         return;
1152     }
1153     bstrData = SysAllocString(szSimpleXML);
1154     hr = IXMLDOMDocument_loadXML(domDocument, bstrData, &vBool);
1155     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1156     V_VT(&var) = VT_UNKNOWN;
1157     V_UNKNOWN(&var) = (IUnknown*)domDocument;
1158
1159     expectCall = contentHandlerTest2;
1160     hr = ISAXXMLReader_parse(reader, var);
1161     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1162     test_expect_call(CH_ENDTEST);
1163     IXMLDOMDocument_Release(domDocument);
1164
1165     ISAXXMLReader_Release(reader);
1166     SysFreeString(bstrData);
1167 }
1168
1169 struct saxreader_props_test_t
1170 {
1171     const char *prop_name;
1172     IUnknown   *iface;
1173 };
1174
1175 static const struct saxreader_props_test_t props_test_data[] = {
1176     { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&saxlexicalhandler },
1177     { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&saxdeclhandler },
1178     { 0 }
1179 };
1180
1181 static void test_saxreader_properties(void)
1182 {
1183     const struct saxreader_props_test_t *ptr = props_test_data;
1184     ISAXXMLReader *reader;
1185     HRESULT hr;
1186
1187     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
1188             &IID_ISAXXMLReader, (void**)&reader);
1189     EXPECT_HR(hr, S_OK);
1190
1191     hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
1192     EXPECT_HR(hr, E_POINTER);
1193
1194     while (ptr->prop_name)
1195     {
1196         VARIANT v;
1197
1198         V_VT(&v) = VT_EMPTY;
1199         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1200         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
1201         EXPECT_HR(hr, S_OK);
1202         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
1203         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
1204
1205         V_VT(&v) = VT_UNKNOWN;
1206         V_UNKNOWN(&v) = ptr->iface;
1207         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
1208         EXPECT_HR(hr, S_OK);
1209
1210         V_VT(&v) = VT_EMPTY;
1211         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1212         handler_addrefcalled = 0;
1213         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
1214         EXPECT_HR(hr, S_OK);
1215         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
1216         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
1217         ok(handler_addrefcalled == 1, "AddRef called %d times\n", handler_addrefcalled);
1218         VariantClear(&v);
1219
1220         V_VT(&v) = VT_EMPTY;
1221         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1222         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
1223         EXPECT_HR(hr, S_OK);
1224
1225         V_VT(&v) = VT_EMPTY;
1226         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1227         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
1228         EXPECT_HR(hr, S_OK);
1229         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
1230         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
1231
1232         V_VT(&v) = VT_UNKNOWN;
1233         V_UNKNOWN(&v) = ptr->iface;
1234         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
1235         EXPECT_HR(hr, S_OK);
1236
1237         /* only VT_EMPTY seems to be valid to reset property */
1238         V_VT(&v) = VT_I4;
1239         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1240         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
1241         EXPECT_HR(hr, E_INVALIDARG);
1242
1243         V_VT(&v) = VT_EMPTY;
1244         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1245         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
1246         EXPECT_HR(hr, S_OK);
1247         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
1248         ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
1249         VariantClear(&v);
1250
1251         V_VT(&v) = VT_UNKNOWN;
1252         V_UNKNOWN(&v) = NULL;
1253         hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
1254         EXPECT_HR(hr, S_OK);
1255
1256         V_VT(&v) = VT_EMPTY;
1257         V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
1258         hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
1259         EXPECT_HR(hr, S_OK);
1260         ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
1261         ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
1262
1263         ptr++;
1264     }
1265
1266     ISAXXMLReader_Release(reader);
1267     free_bstrs();
1268 }
1269
1270 struct feature_ns_entry_t {
1271     const GUID *guid;
1272     const char *clsid;
1273     VARIANT_BOOL value;
1274 };
1275
1276 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
1277     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   VARIANT_TRUE },
1278     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE },
1279     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE },
1280     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE },
1281     { 0 }
1282 };
1283
1284 static void test_saxreader_features(void)
1285 {
1286     const struct feature_ns_entry_t *entry = feature_ns_entry_data;
1287     ISAXXMLReader *reader;
1288
1289     while (entry->guid)
1290     {
1291         VARIANT_BOOL value;
1292         HRESULT hr;
1293
1294         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1295         if (hr != S_OK)
1296         {
1297             win_skip("can't create %s instance\n", entry->clsid);
1298             entry++;
1299             continue;
1300         }
1301
1302         value = 0xc;
1303         hr = ISAXXMLReader_getFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), &value);
1304         EXPECT_HR(hr, S_OK);
1305
1306         ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
1307
1308         ISAXXMLReader_Release(reader);
1309
1310         entry++;
1311     }
1312 }
1313
1314 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
1315 static const CHAR UTF8BOMTest[] =
1316 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
1317 "<a></a>\n";
1318
1319 struct enc_test_entry_t {
1320     const GUID *guid;
1321     const char *clsid;
1322     const char *data;
1323     HRESULT hr;
1324     int todo;
1325 };
1326
1327 static const struct enc_test_entry_t encoding_test_data[] = {
1328     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
1329     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
1330     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
1331     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
1332     { 0 }
1333 };
1334
1335 static void test_encoding(void)
1336 {
1337     const struct enc_test_entry_t *entry = encoding_test_data;
1338     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1339     static const CHAR testXmlA[] = "test.xml";
1340     ISAXXMLReader *reader;
1341     DWORD written;
1342     HANDLE file;
1343     HRESULT hr;
1344
1345     while (entry->guid)
1346     {
1347         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1348         if (hr != S_OK)
1349         {
1350             win_skip("can't create %s instance\n", entry->clsid);
1351             entry++;
1352             continue;
1353         }
1354
1355         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1356         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
1357         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
1358         CloseHandle(file);
1359
1360         hr = ISAXXMLReader_parseURL(reader, testXmlW);
1361         if (entry->todo)
1362             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1363         else
1364             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1365
1366         DeleteFileA(testXmlA);
1367         ISAXXMLReader_Release(reader);
1368
1369         entry++;
1370     }
1371 }
1372
1373 static void test_mxwriter_contenthandler(void)
1374 {
1375     ISAXContentHandler *handler;
1376     IMXWriter *writer, *writer2;
1377     HRESULT hr;
1378
1379     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1380             &IID_IMXWriter, (void**)&writer);
1381     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1382
1383     EXPECT_REF(writer, 1);
1384
1385     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
1386     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1387     EXPECT_REF(writer, 2);
1388     EXPECT_REF(handler, 2);
1389
1390     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
1391     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1392     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
1393     EXPECT_REF(writer, 3);
1394     EXPECT_REF(writer2, 3);
1395     IMXWriter_Release(writer2);
1396
1397     ISAXContentHandler_Release(handler);
1398     IMXWriter_Release(writer);
1399 }
1400
1401 struct msxmlsupported_data_t
1402 {
1403     const GUID *clsid;
1404     const char *name;
1405     BOOL supported;
1406 };
1407
1408 static struct msxmlsupported_data_t msxmlsupported_data[] =
1409 {
1410     { &CLSID_MXXMLWriter,   "MXXMLWriter" },
1411     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
1412     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
1413     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
1414     { NULL }
1415 };
1416
1417 static BOOL is_mxwriter_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
1418 {
1419     while (table->clsid)
1420     {
1421         if (table->clsid == clsid) return table->supported;
1422         table++;
1423     }
1424     return FALSE;
1425 }
1426
1427 struct mxwriter_props_t
1428 {
1429     const GUID *clsid;
1430     VARIANT_BOOL bom;
1431     VARIANT_BOOL disable_escape;
1432     VARIANT_BOOL indent;
1433     VARIANT_BOOL omitdecl;
1434     VARIANT_BOOL standalone;
1435     const char *encoding;
1436 };
1437
1438 static const struct mxwriter_props_t mxwriter_default_props[] =
1439 {
1440     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1441     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1442     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1443     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1444     { NULL }
1445 };
1446
1447 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
1448 {
1449     int i = 0;
1450
1451     while (table->clsid)
1452     {
1453         IMXWriter *writer;
1454         VARIANT_BOOL b;
1455         BSTR encoding;
1456         HRESULT hr;
1457
1458         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1459         {
1460             table++;
1461             i++;
1462             continue;
1463         }
1464
1465         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1466             &IID_IMXWriter, (void**)&writer);
1467         EXPECT_HR(hr, S_OK);
1468
1469         b = !table->bom;
1470         hr = IMXWriter_get_byteOrderMark(writer, &b);
1471         EXPECT_HR(hr, S_OK);
1472         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
1473
1474         b = !table->disable_escape;
1475         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
1476         EXPECT_HR(hr, S_OK);
1477         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
1478            table->disable_escape);
1479
1480         b = !table->indent;
1481         hr = IMXWriter_get_indent(writer, &b);
1482         EXPECT_HR(hr, S_OK);
1483         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
1484
1485         b = !table->omitdecl;
1486         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
1487         EXPECT_HR(hr, S_OK);
1488         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
1489
1490         b = !table->standalone;
1491         hr = IMXWriter_get_standalone(writer, &b);
1492         EXPECT_HR(hr, S_OK);
1493         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
1494
1495         hr = IMXWriter_get_encoding(writer, &encoding);
1496         EXPECT_HR(hr, S_OK);
1497         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
1498             i, wine_dbgstr_w(encoding), table->encoding);
1499         SysFreeString(encoding);
1500
1501         IMXWriter_Release(writer);
1502
1503         table++;
1504         i++;
1505     }
1506 }
1507
1508 static void test_mxwriter_properties(void)
1509 {
1510     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
1511     static const WCHAR emptyW[] = {0};
1512     static const WCHAR testW[] = {'t','e','s','t',0};
1513     IMXWriter *writer;
1514     VARIANT_BOOL b;
1515     HRESULT hr;
1516     BSTR str, str2;
1517
1518     test_mxwriter_default_properties(mxwriter_default_props);
1519
1520     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1521             &IID_IMXWriter, (void**)&writer);
1522     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1523
1524     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
1525     ok(hr == E_POINTER, "got %08x\n", hr);
1526
1527     hr = IMXWriter_get_byteOrderMark(writer, NULL);
1528     ok(hr == E_POINTER, "got %08x\n", hr);
1529
1530     hr = IMXWriter_get_indent(writer, NULL);
1531     ok(hr == E_POINTER, "got %08x\n", hr);
1532
1533     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
1534     ok(hr == E_POINTER, "got %08x\n", hr);
1535
1536     hr = IMXWriter_get_standalone(writer, NULL);
1537     ok(hr == E_POINTER, "got %08x\n", hr);
1538
1539     /* set and check */
1540     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
1541     ok(hr == S_OK, "got %08x\n", hr);
1542
1543     b = VARIANT_FALSE;
1544     hr = IMXWriter_get_standalone(writer, &b);
1545     ok(hr == S_OK, "got %08x\n", hr);
1546     ok(b == VARIANT_TRUE, "got %d\n", b);
1547
1548     hr = IMXWriter_get_encoding(writer, NULL);
1549     ok(hr == E_POINTER, "got %08x\n", hr);
1550
1551     /* UTF-16 is a default setting apparently */
1552     str = (void*)0xdeadbeef;
1553     hr = IMXWriter_get_encoding(writer, &str);
1554     ok(hr == S_OK, "got %08x\n", hr);
1555     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1556
1557     str2 = (void*)0xdeadbeef;
1558     hr = IMXWriter_get_encoding(writer, &str2);
1559     ok(hr == S_OK, "got %08x\n", hr);
1560     ok(str != str2, "expected newly allocated, got same %p\n", str);
1561
1562     SysFreeString(str2);
1563     SysFreeString(str);
1564
1565     /* put empty string */
1566     str = SysAllocString(emptyW);
1567     hr = IMXWriter_put_encoding(writer, str);
1568     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1569     SysFreeString(str);
1570
1571     str = (void*)0xdeadbeef;
1572     hr = IMXWriter_get_encoding(writer, &str);
1573     ok(hr == S_OK, "got %08x\n", hr);
1574     ok(!lstrcmpW(str, emptyW) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1575     SysFreeString(str);
1576
1577     /* invalid encoding name */
1578     str = SysAllocString(testW);
1579     hr = IMXWriter_put_encoding(writer, str);
1580     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1581     SysFreeString(str);
1582
1583     hr = IMXWriter_get_version(writer, NULL);
1584     ok(hr == E_POINTER, "got %08x\n", hr);
1585     /* default version is 'surprisingly' 1.0 */
1586     hr = IMXWriter_get_version(writer, &str);
1587     ok(hr == S_OK, "got %08x\n", hr);
1588     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
1589     SysFreeString(str);
1590
1591     /* store version string as is */
1592     hr = IMXWriter_put_version(writer, NULL);
1593     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1594
1595     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
1596     ok(hr == S_OK, "got %08x\n", hr);
1597
1598     hr = IMXWriter_put_version(writer, _bstr_(""));
1599     ok(hr == S_OK, "got %08x\n", hr);
1600     hr = IMXWriter_get_version(writer, &str);
1601     ok(hr == S_OK, "got %08x\n", hr);
1602     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
1603     SysFreeString(str);
1604
1605     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
1606     ok(hr == S_OK, "got %08x\n", hr);
1607     hr = IMXWriter_get_version(writer, &str);
1608     ok(hr == S_OK, "got %08x\n", hr);
1609     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
1610     SysFreeString(str);
1611
1612     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
1613     ok(hr == S_OK, "got %08x\n", hr);
1614     hr = IMXWriter_get_version(writer, &str);
1615     ok(hr == S_OK, "got %08x\n", hr);
1616     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
1617     SysFreeString(str);
1618
1619     IMXWriter_Release(writer);
1620     free_bstrs();
1621 }
1622
1623 static void test_mxwriter_flush(void)
1624 {
1625     ISAXContentHandler *content;
1626     IMXWriter *writer;
1627     LARGE_INTEGER pos;
1628     ULARGE_INTEGER pos2;
1629     IStream *stream;
1630     VARIANT dest;
1631     HRESULT hr;
1632
1633     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1634             &IID_IMXWriter, (void**)&writer);
1635     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1636
1637     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1638     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1639     EXPECT_REF(stream, 1);
1640
1641     /* detach when nothing was attached */
1642     V_VT(&dest) = VT_EMPTY;
1643     hr = IMXWriter_put_output(writer, dest);
1644     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1645
1646     /* attach stream */
1647     V_VT(&dest) = VT_UNKNOWN;
1648     V_UNKNOWN(&dest) = (IUnknown*)stream;
1649     hr = IMXWriter_put_output(writer, dest);
1650     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1651     todo_wine EXPECT_REF(stream, 3);
1652
1653     /* detach setting VT_EMPTY destination */
1654     V_VT(&dest) = VT_EMPTY;
1655     hr = IMXWriter_put_output(writer, dest);
1656     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1657     EXPECT_REF(stream, 1);
1658
1659     V_VT(&dest) = VT_UNKNOWN;
1660     V_UNKNOWN(&dest) = (IUnknown*)stream;
1661     hr = IMXWriter_put_output(writer, dest);
1662     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1663
1664     /* flush() doesn't detach a stream */
1665     hr = IMXWriter_flush(writer);
1666     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1667     todo_wine EXPECT_REF(stream, 3);
1668
1669     pos.QuadPart = 0;
1670     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1671     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1672     ok(pos2.QuadPart == 0, "expected stream beginning\n");
1673
1674     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1675     ok(hr == S_OK, "got %08x\n", hr);
1676
1677     hr = ISAXContentHandler_startDocument(content);
1678     ok(hr == S_OK, "got %08x\n", hr);
1679
1680     pos.QuadPart = 0;
1681     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1682     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1683     ok(pos2.QuadPart != 0, "expected stream beginning\n");
1684
1685     /* already started */
1686     hr = ISAXContentHandler_startDocument(content);
1687     ok(hr == S_OK, "got %08x\n", hr);
1688
1689     hr = ISAXContentHandler_endDocument(content);
1690     ok(hr == S_OK, "got %08x\n", hr);
1691
1692     /* flushed on endDocument() */
1693     pos.QuadPart = 0;
1694     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1695     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1696     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1697
1698     ISAXContentHandler_Release(content);
1699     IStream_Release(stream);
1700     IMXWriter_Release(writer);
1701 }
1702
1703 static void test_mxwriter_startenddocument(void)
1704 {
1705     ISAXContentHandler *content;
1706     IMXWriter *writer;
1707     VARIANT dest;
1708     HRESULT hr;
1709
1710     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1711             &IID_IMXWriter, (void**)&writer);
1712     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1713
1714     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1715     ok(hr == S_OK, "got %08x\n", hr);
1716
1717     hr = ISAXContentHandler_startDocument(content);
1718     ok(hr == S_OK, "got %08x\n", hr);
1719
1720     hr = ISAXContentHandler_endDocument(content);
1721     ok(hr == S_OK, "got %08x\n", hr);
1722
1723     V_VT(&dest) = VT_EMPTY;
1724     hr = IMXWriter_get_output(writer, &dest);
1725     ok(hr == S_OK, "got %08x\n", hr);
1726     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1727     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1728         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1729     VariantClear(&dest);
1730
1731     /* now try another startDocument */
1732     hr = ISAXContentHandler_startDocument(content);
1733     ok(hr == S_OK, "got %08x\n", hr);
1734     /* and get duplicated prolog */
1735     V_VT(&dest) = VT_EMPTY;
1736     hr = IMXWriter_get_output(writer, &dest);
1737     ok(hr == S_OK, "got %08x\n", hr);
1738     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1739     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
1740                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1741         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1742     VariantClear(&dest);
1743
1744     ISAXContentHandler_Release(content);
1745     IMXWriter_Release(writer);
1746
1747     /* now with omitted declaration */
1748     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1749             &IID_IMXWriter, (void**)&writer);
1750     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1751
1752     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1753     ok(hr == S_OK, "got %08x\n", hr);
1754
1755     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1756     ok(hr == S_OK, "got %08x\n", hr);
1757
1758     hr = ISAXContentHandler_startDocument(content);
1759     ok(hr == S_OK, "got %08x\n", hr);
1760
1761     hr = ISAXContentHandler_endDocument(content);
1762     ok(hr == S_OK, "got %08x\n", hr);
1763
1764     V_VT(&dest) = VT_EMPTY;
1765     hr = IMXWriter_get_output(writer, &dest);
1766     ok(hr == S_OK, "got %08x\n", hr);
1767     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1768     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1769     VariantClear(&dest);
1770
1771     ISAXContentHandler_Release(content);
1772     IMXWriter_Release(writer);
1773
1774     free_bstrs();
1775 }
1776
1777 enum startendtype
1778 {
1779     StartElement,
1780     EndElement,
1781     StartEndElement
1782 };
1783
1784 struct writer_startendelement_t {
1785     const GUID *clsid;
1786     enum startendtype type;
1787     const char *uri;
1788     const char *local_name;
1789     const char *qname;
1790     const char *output;
1791     HRESULT hr;
1792     ISAXAttributes *attr;
1793 };
1794
1795 static const struct writer_startendelement_t writer_startendelement[] = {
1796     /* 0 */
1797     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1798     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1799     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1800     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
1801     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1802     /* 5 */
1803     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1804     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1805     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
1806     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1807     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1808     /* 10 */
1809     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1810     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
1811     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1812     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1813     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1814     /* 15 */
1815     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
1816     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
1817     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1818     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1819     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1820     /* 20 */
1821     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1822     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1823     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1824     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
1825     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1826     /* 25 */
1827     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1828     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1829     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1830     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1831     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1832     /* 30 */
1833     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1834     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1835     /* endElement tests */
1836     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1837     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1838     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1839     /* 35 */
1840     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
1841     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1842     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1843     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1844     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
1845     /* 40 */
1846     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1847     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1848     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1849     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
1850     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1851     /* 45 */
1852     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1853     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1854     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
1855     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
1856     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1857     /* 50 */
1858     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1859     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1860     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1861     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1862     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1863     /* 55 */
1864     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
1865     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1866     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1867     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1868     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1869     /* 60 */
1870     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1871     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1872     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1873     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1874
1875     /* with attributes */
1876     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1877     /* 65 */
1878     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1879     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1880     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1881     /* empty elements */
1882     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1883     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1884     /* 70 */
1885     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1886     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1887     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
1888     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
1889     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
1890     /* 75 */
1891     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
1892     { NULL }
1893 };
1894
1895 static void get_supported_mxwriter_data(struct msxmlsupported_data_t *table)
1896 {
1897     while (table->clsid)
1898     {
1899         IMXWriter *writer;
1900         HRESULT hr;
1901
1902         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1903             &IID_IMXWriter, (void**)&writer);
1904         if (hr == S_OK) IMXWriter_Release(writer);
1905
1906         table->supported = hr == S_OK;
1907         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
1908
1909         table++;
1910     }
1911 }
1912
1913 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
1914 {
1915     int i = 0;
1916
1917     while (table->clsid)
1918     {
1919         ISAXContentHandler *content;
1920         IMXWriter *writer;
1921         HRESULT hr;
1922
1923         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1924         {
1925             table++;
1926             i++;
1927             continue;
1928         }
1929
1930         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1931             &IID_IMXWriter, (void**)&writer);
1932         EXPECT_HR(hr, S_OK);
1933
1934         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1935         EXPECT_HR(hr, S_OK);
1936
1937         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1938         EXPECT_HR(hr, S_OK);
1939
1940         hr = ISAXContentHandler_startDocument(content);
1941         EXPECT_HR(hr, S_OK);
1942
1943         if (table->type == StartElement)
1944         {
1945             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1946                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1947             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1948         }
1949         else if (table->type == EndElement)
1950         {
1951             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1952                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1953             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1954         }
1955         else
1956         {
1957             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1958                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1959             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1960             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1961                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1962             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1963         }
1964
1965         /* test output */
1966         if (hr == S_OK)
1967         {
1968             VARIANT dest;
1969
1970             V_VT(&dest) = VT_EMPTY;
1971             hr = IMXWriter_get_output(writer, &dest);
1972             EXPECT_HR(hr, S_OK);
1973             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1974             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
1975                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
1976             VariantClear(&dest);
1977         }
1978
1979         ISAXContentHandler_Release(content);
1980         IMXWriter_Release(writer);
1981
1982         table++;
1983         i++;
1984     }
1985
1986     free_bstrs();
1987 }
1988
1989 static void test_mxwriter_startendelement(void)
1990 {
1991     ISAXContentHandler *content;
1992     IMXWriter *writer;
1993     VARIANT dest;
1994     HRESULT hr;
1995
1996     test_mxwriter_startendelement_batch(writer_startendelement);
1997
1998     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1999             &IID_IMXWriter, (void**)&writer);
2000     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2001
2002     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2003     ok(hr == S_OK, "got %08x\n", hr);
2004
2005     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2006     ok(hr == S_OK, "got %08x\n", hr);
2007
2008     hr = ISAXContentHandler_startDocument(content);
2009     ok(hr == S_OK, "got %08x\n", hr);
2010
2011     /* all string pointers should be not null */
2012     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
2013     ok(hr == S_OK, "got %08x\n", hr);
2014
2015     V_VT(&dest) = VT_EMPTY;
2016     hr = IMXWriter_get_output(writer, &dest);
2017     ok(hr == S_OK, "got %08x\n", hr);
2018     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2019     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2020     VariantClear(&dest);
2021
2022     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
2023     ok(hr == S_OK, "got %08x\n", hr);
2024
2025     V_VT(&dest) = VT_EMPTY;
2026     hr = IMXWriter_get_output(writer, &dest);
2027     ok(hr == S_OK, "got %08x\n", hr);
2028     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2029     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2030     VariantClear(&dest);
2031
2032     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
2033     EXPECT_HR(hr, E_INVALIDARG);
2034
2035     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
2036     EXPECT_HR(hr, E_INVALIDARG);
2037
2038     /* only local name is an error too */
2039     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
2040     EXPECT_HR(hr, E_INVALIDARG);
2041
2042     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
2043     EXPECT_HR(hr, S_OK);
2044
2045     V_VT(&dest) = VT_EMPTY;
2046     hr = IMXWriter_get_output(writer, &dest);
2047     EXPECT_HR(hr, S_OK);
2048     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2049     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2050     VariantClear(&dest);
2051
2052     hr = ISAXContentHandler_endDocument(content);
2053     EXPECT_HR(hr, S_OK);
2054
2055     V_VT(&dest) = VT_EMPTY;
2056     hr = IMXWriter_put_output(writer, dest);
2057     EXPECT_HR(hr, S_OK);
2058
2059     hr = ISAXContentHandler_startDocument(content);
2060     EXPECT_HR(hr, S_OK);
2061
2062     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
2063     EXPECT_HR(hr, S_OK);
2064
2065     V_VT(&dest) = VT_EMPTY;
2066     hr = IMXWriter_get_output(writer, &dest);
2067     EXPECT_HR(hr, S_OK);
2068     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2069     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2070     VariantClear(&dest);
2071
2072     ISAXContentHandler_endDocument(content);
2073     IMXWriter_flush(writer);
2074
2075     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
2076     EXPECT_HR(hr, S_OK);
2077     V_VT(&dest) = VT_EMPTY;
2078     hr = IMXWriter_get_output(writer, &dest);
2079     EXPECT_HR(hr, S_OK);
2080     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2081     ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2082     VariantClear(&dest);
2083
2084     ISAXContentHandler_Release(content);
2085     IMXWriter_Release(writer);
2086     free_bstrs();
2087 }
2088
2089 static void test_mxwriter_characters(void)
2090 {
2091     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
2092     ISAXContentHandler *content;
2093     IMXWriter *writer;
2094     VARIANT dest;
2095     HRESULT hr;
2096
2097     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2098             &IID_IMXWriter, (void**)&writer);
2099     EXPECT_HR(hr, S_OK);
2100
2101     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2102     EXPECT_HR(hr, S_OK);
2103
2104     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2105     EXPECT_HR(hr, S_OK);
2106
2107     hr = ISAXContentHandler_startDocument(content);
2108     EXPECT_HR(hr, S_OK);
2109
2110     hr = ISAXContentHandler_characters(content, NULL, 0);
2111     EXPECT_HR(hr, E_INVALIDARG);
2112
2113     hr = ISAXContentHandler_characters(content, chardataW, 0);
2114     EXPECT_HR(hr, S_OK);
2115
2116     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
2117     EXPECT_HR(hr, S_OK);
2118
2119     V_VT(&dest) = VT_EMPTY;
2120     hr = IMXWriter_get_output(writer, &dest);
2121     EXPECT_HR(hr, S_OK);
2122     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2123     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2124     VariantClear(&dest);
2125
2126     hr = ISAXContentHandler_endDocument(content);
2127     EXPECT_HR(hr, S_OK);
2128
2129     ISAXContentHandler_Release(content);
2130     IMXWriter_Release(writer);
2131
2132     /* try empty characters data to see if element is closed */
2133     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2134             &IID_IMXWriter, (void**)&writer);
2135     EXPECT_HR(hr, S_OK);
2136
2137     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2138     EXPECT_HR(hr, S_OK);
2139
2140     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2141     EXPECT_HR(hr, S_OK);
2142
2143     hr = ISAXContentHandler_startDocument(content);
2144     EXPECT_HR(hr, S_OK);
2145
2146     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
2147     EXPECT_HR(hr, S_OK);
2148
2149     hr = ISAXContentHandler_characters(content, chardataW, 0);
2150     EXPECT_HR(hr, S_OK);
2151
2152     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
2153     EXPECT_HR(hr, S_OK);
2154
2155     V_VT(&dest) = VT_EMPTY;
2156     hr = IMXWriter_get_output(writer, &dest);
2157     EXPECT_HR(hr, S_OK);
2158     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2159     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2160     VariantClear(&dest);
2161
2162     ISAXContentHandler_Release(content);
2163     IMXWriter_Release(writer);
2164
2165     free_bstrs();
2166 }
2167
2168 static const mxwriter_stream_test mxwriter_stream_tests[] = {
2169     {
2170         VARIANT_TRUE,"UTF-16",
2171         {
2172             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
2173             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2174             {TRUE}
2175         }
2176     },
2177     {
2178         VARIANT_FALSE,"UTF-16",
2179         {
2180             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2181             {TRUE}
2182         }
2183     },
2184     {
2185         VARIANT_TRUE,"UTF-8",
2186         {
2187             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
2188             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
2189              * and the writer is released.
2190              */
2191             {FALSE,NULL,0},
2192             {TRUE}
2193         }
2194     },
2195     {
2196         VARIANT_TRUE,"UTF-16",
2197         {
2198             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
2199             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2200             {TRUE}
2201         }
2202     },
2203     {
2204         VARIANT_TRUE,"UTF-16",
2205         {
2206             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
2207             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2208             {TRUE}
2209         }
2210     }
2211 };
2212
2213 static void test_mxwriter_stream(void)
2214 {
2215     IMXWriter *writer;
2216     ISAXContentHandler *content;
2217     HRESULT hr;
2218     VARIANT dest;
2219     IStream *stream;
2220     LARGE_INTEGER pos;
2221     ULARGE_INTEGER pos2;
2222     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
2223
2224     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
2225         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
2226
2227         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2228                 &IID_IMXWriter, (void**)&writer);
2229         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2230
2231         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2232         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2233
2234         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
2235         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
2236
2237         V_VT(&dest) = VT_UNKNOWN;
2238         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
2239         hr = IMXWriter_put_output(writer, dest);
2240         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
2241         VariantClear(&dest);
2242
2243         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
2244         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
2245
2246         current_write_test = test->expected_writes;
2247
2248         hr = ISAXContentHandler_startDocument(content);
2249         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
2250
2251         hr = ISAXContentHandler_endDocument(content);
2252         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
2253
2254         ISAXContentHandler_Release(content);
2255         IMXWriter_Release(writer);
2256
2257         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
2258             (int)(current_write_test-test->expected_writes), current_stream_test_index);
2259     }
2260
2261     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2262             &IID_IMXWriter, (void**)&writer);
2263     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2264
2265     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2266     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
2267
2268     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2269     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2270
2271     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
2272     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
2273
2274     V_VT(&dest) = VT_UNKNOWN;
2275     V_UNKNOWN(&dest) = (IUnknown*)stream;
2276     hr = IMXWriter_put_output(writer, dest);
2277     ok(hr == S_OK, "put_output failed: %08x\n", hr);
2278
2279     hr = ISAXContentHandler_startDocument(content);
2280     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2281
2282     /* Setting output of the mxwriter causes the current output to be flushed,
2283      * and the writer to start over.
2284      */
2285     V_VT(&dest) = VT_EMPTY;
2286     hr = IMXWriter_put_output(writer, dest);
2287     ok(hr == S_OK, "put_output failed: %08x\n", hr);
2288
2289     pos.QuadPart = 0;
2290     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2291     ok(hr == S_OK, "Seek failed: %08x\n", hr);
2292     ok(pos2.QuadPart != 0, "expected stream position moved\n");
2293
2294     hr = ISAXContentHandler_startDocument(content);
2295     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2296
2297     hr = ISAXContentHandler_endDocument(content);
2298     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
2299
2300     V_VT(&dest) = VT_EMPTY;
2301     hr = IMXWriter_get_output(writer, &dest);
2302     ok(hr == S_OK, "get_output failed: %08x\n", hr);
2303     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
2304     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
2305             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2306     VariantClear(&dest);
2307
2308     ISAXContentHandler_Release(content);
2309     IMXWriter_Release(writer);
2310
2311     free_bstrs();
2312 }
2313
2314 static void test_mxwriter_encoding(void)
2315 {
2316     IMXWriter *writer;
2317     ISAXContentHandler *content;
2318     HRESULT hr;
2319     VARIANT dest;
2320
2321     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2322             &IID_IMXWriter, (void**)&writer);
2323     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2324
2325     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2326     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2327
2328     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
2329     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
2330
2331     hr = ISAXContentHandler_startDocument(content);
2332     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2333
2334     hr = ISAXContentHandler_endDocument(content);
2335     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
2336
2337     /* The content is always re-encoded to UTF-16 when the output is
2338      * retrieved as a BSTR.
2339      */
2340     V_VT(&dest) = VT_EMPTY;
2341     hr = IMXWriter_get_output(writer, &dest);
2342     todo_wine ok(hr == S_OK, "get_output failed: %08x\n", hr);
2343     todo_wine ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
2344     if (V_VT(&dest) == VT_BSTR) todo_wine ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
2345             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2346     VariantClear(&dest);
2347
2348     ISAXContentHandler_Release(content);
2349     IMXWriter_Release(writer);
2350
2351     free_bstrs();
2352 }
2353
2354 START_TEST(saxreader)
2355 {
2356     ISAXXMLReader *reader;
2357     HRESULT hr;
2358
2359     hr = CoInitialize(NULL);
2360     ok(hr == S_OK, "failed to init com\n");
2361
2362     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2363             &IID_ISAXXMLReader, (void**)&reader);
2364
2365     if(FAILED(hr))
2366     {
2367         skip("Failed to create SAXXMLReader instance\n");
2368         CoUninitialize();
2369         return;
2370     }
2371     ISAXXMLReader_Release(reader);
2372
2373     test_saxreader();
2374     test_saxreader_properties();
2375     test_saxreader_features();
2376     test_encoding();
2377
2378     /* MXXMLWriter tests */
2379     get_supported_mxwriter_data(msxmlsupported_data);
2380     if (is_mxwriter_supported(&CLSID_MXXMLWriter, msxmlsupported_data))
2381     {
2382         test_mxwriter_contenthandler();
2383         test_mxwriter_startenddocument();
2384         test_mxwriter_startendelement();
2385         test_mxwriter_characters();
2386         test_mxwriter_properties();
2387         test_mxwriter_flush();
2388         test_mxwriter_stream();
2389         test_mxwriter_encoding();
2390     }
2391     else
2392         win_skip("MXXMLWriter not supported\n");
2393
2394     CoUninitialize();
2395 }