msxml3: Fix putProperty() for declaration handler.
[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 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
1271 static const CHAR UTF8BOMTest[] =
1272 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
1273 "<a></a>\n";
1274
1275 struct enc_test_entry_t {
1276     const GUID *guid;
1277     const char *clsid;
1278     const char *data;
1279     HRESULT hr;
1280     int todo;
1281 };
1282
1283 static const struct enc_test_entry_t encoding_test_data[] = {
1284     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
1285     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
1286     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
1287     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
1288     { 0 }
1289 };
1290
1291 static void test_encoding(void)
1292 {
1293     const struct enc_test_entry_t *entry = encoding_test_data;
1294     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1295     static const CHAR testXmlA[] = "test.xml";
1296     ISAXXMLReader *reader;
1297     DWORD written;
1298     HANDLE file;
1299     HRESULT hr;
1300
1301     while (entry->guid)
1302     {
1303         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1304         if (hr != S_OK)
1305         {
1306             win_skip("can't create %s instance\n", entry->clsid);
1307             entry++;
1308             continue;
1309         }
1310
1311         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1312         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
1313         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
1314         CloseHandle(file);
1315
1316         hr = ISAXXMLReader_parseURL(reader, testXmlW);
1317         if (entry->todo)
1318             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1319         else
1320             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1321
1322         DeleteFileA(testXmlA);
1323         ISAXXMLReader_Release(reader);
1324
1325         entry++;
1326     }
1327 }
1328
1329 static void test_mxwriter_contenthandler(void)
1330 {
1331     ISAXContentHandler *handler;
1332     IMXWriter *writer, *writer2;
1333     HRESULT hr;
1334
1335     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1336             &IID_IMXWriter, (void**)&writer);
1337     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1338
1339     EXPECT_REF(writer, 1);
1340
1341     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
1342     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1343     EXPECT_REF(writer, 2);
1344     EXPECT_REF(handler, 2);
1345
1346     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
1347     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1348     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
1349     EXPECT_REF(writer, 3);
1350     EXPECT_REF(writer2, 3);
1351     IMXWriter_Release(writer2);
1352
1353     ISAXContentHandler_Release(handler);
1354     IMXWriter_Release(writer);
1355 }
1356
1357 struct msxmlsupported_data_t
1358 {
1359     const GUID *clsid;
1360     const char *name;
1361     BOOL supported;
1362 };
1363
1364 static struct msxmlsupported_data_t msxmlsupported_data[] =
1365 {
1366     { &CLSID_MXXMLWriter,   "MXXMLWriter" },
1367     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
1368     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
1369     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
1370     { NULL }
1371 };
1372
1373 static BOOL is_mxwriter_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
1374 {
1375     while (table->clsid)
1376     {
1377         if (table->clsid == clsid) return table->supported;
1378         table++;
1379     }
1380     return FALSE;
1381 }
1382
1383 struct mxwriter_props_t
1384 {
1385     const GUID *clsid;
1386     VARIANT_BOOL bom;
1387     VARIANT_BOOL disable_escape;
1388     VARIANT_BOOL indent;
1389     VARIANT_BOOL omitdecl;
1390     VARIANT_BOOL standalone;
1391     const char *encoding;
1392 };
1393
1394 static const struct mxwriter_props_t mxwriter_default_props[] =
1395 {
1396     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1397     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1398     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1399     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1400     { NULL }
1401 };
1402
1403 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
1404 {
1405     int i = 0;
1406
1407     while (table->clsid)
1408     {
1409         IMXWriter *writer;
1410         VARIANT_BOOL b;
1411         BSTR encoding;
1412         HRESULT hr;
1413
1414         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1415         {
1416             table++;
1417             i++;
1418             continue;
1419         }
1420
1421         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1422             &IID_IMXWriter, (void**)&writer);
1423         EXPECT_HR(hr, S_OK);
1424
1425         b = !table->bom;
1426         hr = IMXWriter_get_byteOrderMark(writer, &b);
1427         EXPECT_HR(hr, S_OK);
1428         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
1429
1430         b = !table->disable_escape;
1431         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
1432         EXPECT_HR(hr, S_OK);
1433         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
1434            table->disable_escape);
1435
1436         b = !table->indent;
1437         hr = IMXWriter_get_indent(writer, &b);
1438         EXPECT_HR(hr, S_OK);
1439         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
1440
1441         b = !table->omitdecl;
1442         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
1443         EXPECT_HR(hr, S_OK);
1444         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
1445
1446         b = !table->standalone;
1447         hr = IMXWriter_get_standalone(writer, &b);
1448         EXPECT_HR(hr, S_OK);
1449         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
1450
1451         hr = IMXWriter_get_encoding(writer, &encoding);
1452         EXPECT_HR(hr, S_OK);
1453         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
1454             i, wine_dbgstr_w(encoding), table->encoding);
1455         SysFreeString(encoding);
1456
1457         IMXWriter_Release(writer);
1458
1459         table++;
1460         i++;
1461     }
1462 }
1463
1464 static void test_mxwriter_properties(void)
1465 {
1466     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
1467     static const WCHAR emptyW[] = {0};
1468     static const WCHAR testW[] = {'t','e','s','t',0};
1469     IMXWriter *writer;
1470     VARIANT_BOOL b;
1471     HRESULT hr;
1472     BSTR str, str2;
1473
1474     test_mxwriter_default_properties(mxwriter_default_props);
1475
1476     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1477             &IID_IMXWriter, (void**)&writer);
1478     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1479
1480     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
1481     ok(hr == E_POINTER, "got %08x\n", hr);
1482
1483     hr = IMXWriter_get_byteOrderMark(writer, NULL);
1484     ok(hr == E_POINTER, "got %08x\n", hr);
1485
1486     hr = IMXWriter_get_indent(writer, NULL);
1487     ok(hr == E_POINTER, "got %08x\n", hr);
1488
1489     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
1490     ok(hr == E_POINTER, "got %08x\n", hr);
1491
1492     hr = IMXWriter_get_standalone(writer, NULL);
1493     ok(hr == E_POINTER, "got %08x\n", hr);
1494
1495     /* set and check */
1496     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
1497     ok(hr == S_OK, "got %08x\n", hr);
1498
1499     b = VARIANT_FALSE;
1500     hr = IMXWriter_get_standalone(writer, &b);
1501     ok(hr == S_OK, "got %08x\n", hr);
1502     ok(b == VARIANT_TRUE, "got %d\n", b);
1503
1504     hr = IMXWriter_get_encoding(writer, NULL);
1505     ok(hr == E_POINTER, "got %08x\n", hr);
1506
1507     /* UTF-16 is a default setting apparently */
1508     str = (void*)0xdeadbeef;
1509     hr = IMXWriter_get_encoding(writer, &str);
1510     ok(hr == S_OK, "got %08x\n", hr);
1511     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1512
1513     str2 = (void*)0xdeadbeef;
1514     hr = IMXWriter_get_encoding(writer, &str2);
1515     ok(hr == S_OK, "got %08x\n", hr);
1516     ok(str != str2, "expected newly allocated, got same %p\n", str);
1517
1518     SysFreeString(str2);
1519     SysFreeString(str);
1520
1521     /* put empty string */
1522     str = SysAllocString(emptyW);
1523     hr = IMXWriter_put_encoding(writer, str);
1524     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1525     SysFreeString(str);
1526
1527     str = (void*)0xdeadbeef;
1528     hr = IMXWriter_get_encoding(writer, &str);
1529     ok(hr == S_OK, "got %08x\n", hr);
1530     ok(!lstrcmpW(str, emptyW) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1531     SysFreeString(str);
1532
1533     /* invalid encoding name */
1534     str = SysAllocString(testW);
1535     hr = IMXWriter_put_encoding(writer, str);
1536     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1537     SysFreeString(str);
1538
1539     hr = IMXWriter_get_version(writer, NULL);
1540     ok(hr == E_POINTER, "got %08x\n", hr);
1541     /* default version is 'surprisingly' 1.0 */
1542     hr = IMXWriter_get_version(writer, &str);
1543     ok(hr == S_OK, "got %08x\n", hr);
1544     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
1545     SysFreeString(str);
1546
1547     /* store version string as is */
1548     hr = IMXWriter_put_version(writer, NULL);
1549     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1550
1551     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
1552     ok(hr == S_OK, "got %08x\n", hr);
1553
1554     hr = IMXWriter_put_version(writer, _bstr_(""));
1555     ok(hr == S_OK, "got %08x\n", hr);
1556     hr = IMXWriter_get_version(writer, &str);
1557     ok(hr == S_OK, "got %08x\n", hr);
1558     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
1559     SysFreeString(str);
1560
1561     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
1562     ok(hr == S_OK, "got %08x\n", hr);
1563     hr = IMXWriter_get_version(writer, &str);
1564     ok(hr == S_OK, "got %08x\n", hr);
1565     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
1566     SysFreeString(str);
1567
1568     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
1569     ok(hr == S_OK, "got %08x\n", hr);
1570     hr = IMXWriter_get_version(writer, &str);
1571     ok(hr == S_OK, "got %08x\n", hr);
1572     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
1573     SysFreeString(str);
1574
1575     IMXWriter_Release(writer);
1576     free_bstrs();
1577 }
1578
1579 static void test_mxwriter_flush(void)
1580 {
1581     ISAXContentHandler *content;
1582     IMXWriter *writer;
1583     LARGE_INTEGER pos;
1584     ULARGE_INTEGER pos2;
1585     IStream *stream;
1586     VARIANT dest;
1587     HRESULT hr;
1588
1589     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1590             &IID_IMXWriter, (void**)&writer);
1591     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1592
1593     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1594     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1595     EXPECT_REF(stream, 1);
1596
1597     /* detach when nothing was attached */
1598     V_VT(&dest) = VT_EMPTY;
1599     hr = IMXWriter_put_output(writer, dest);
1600     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1601
1602     /* attach stream */
1603     V_VT(&dest) = VT_UNKNOWN;
1604     V_UNKNOWN(&dest) = (IUnknown*)stream;
1605     hr = IMXWriter_put_output(writer, dest);
1606     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1607     todo_wine EXPECT_REF(stream, 3);
1608
1609     /* detach setting VT_EMPTY destination */
1610     V_VT(&dest) = VT_EMPTY;
1611     hr = IMXWriter_put_output(writer, dest);
1612     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1613     EXPECT_REF(stream, 1);
1614
1615     V_VT(&dest) = VT_UNKNOWN;
1616     V_UNKNOWN(&dest) = (IUnknown*)stream;
1617     hr = IMXWriter_put_output(writer, dest);
1618     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1619
1620     /* flush() doesn't detach a stream */
1621     hr = IMXWriter_flush(writer);
1622     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1623     todo_wine EXPECT_REF(stream, 3);
1624
1625     pos.QuadPart = 0;
1626     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1627     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1628     ok(pos2.QuadPart == 0, "expected stream beginning\n");
1629
1630     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1631     ok(hr == S_OK, "got %08x\n", hr);
1632
1633     hr = ISAXContentHandler_startDocument(content);
1634     ok(hr == S_OK, "got %08x\n", hr);
1635
1636     pos.QuadPart = 0;
1637     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1638     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1639     ok(pos2.QuadPart != 0, "expected stream beginning\n");
1640
1641     /* already started */
1642     hr = ISAXContentHandler_startDocument(content);
1643     ok(hr == S_OK, "got %08x\n", hr);
1644
1645     hr = ISAXContentHandler_endDocument(content);
1646     ok(hr == S_OK, "got %08x\n", hr);
1647
1648     /* flushed on endDocument() */
1649     pos.QuadPart = 0;
1650     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1651     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1652     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1653
1654     ISAXContentHandler_Release(content);
1655     IStream_Release(stream);
1656     IMXWriter_Release(writer);
1657 }
1658
1659 static void test_mxwriter_startenddocument(void)
1660 {
1661     ISAXContentHandler *content;
1662     IMXWriter *writer;
1663     VARIANT dest;
1664     HRESULT hr;
1665
1666     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1667             &IID_IMXWriter, (void**)&writer);
1668     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1669
1670     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1671     ok(hr == S_OK, "got %08x\n", hr);
1672
1673     hr = ISAXContentHandler_startDocument(content);
1674     ok(hr == S_OK, "got %08x\n", hr);
1675
1676     hr = ISAXContentHandler_endDocument(content);
1677     ok(hr == S_OK, "got %08x\n", hr);
1678
1679     V_VT(&dest) = VT_EMPTY;
1680     hr = IMXWriter_get_output(writer, &dest);
1681     ok(hr == S_OK, "got %08x\n", hr);
1682     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1683     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1684         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1685     VariantClear(&dest);
1686
1687     /* now try another startDocument */
1688     hr = ISAXContentHandler_startDocument(content);
1689     ok(hr == S_OK, "got %08x\n", hr);
1690     /* and get duplicated prolog */
1691     V_VT(&dest) = VT_EMPTY;
1692     hr = IMXWriter_get_output(writer, &dest);
1693     ok(hr == S_OK, "got %08x\n", hr);
1694     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1695     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
1696                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1697         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1698     VariantClear(&dest);
1699
1700     ISAXContentHandler_Release(content);
1701     IMXWriter_Release(writer);
1702
1703     /* now with omitted declaration */
1704     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1705             &IID_IMXWriter, (void**)&writer);
1706     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1707
1708     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1709     ok(hr == S_OK, "got %08x\n", hr);
1710
1711     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1712     ok(hr == S_OK, "got %08x\n", hr);
1713
1714     hr = ISAXContentHandler_startDocument(content);
1715     ok(hr == S_OK, "got %08x\n", hr);
1716
1717     hr = ISAXContentHandler_endDocument(content);
1718     ok(hr == S_OK, "got %08x\n", hr);
1719
1720     V_VT(&dest) = VT_EMPTY;
1721     hr = IMXWriter_get_output(writer, &dest);
1722     ok(hr == S_OK, "got %08x\n", hr);
1723     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1724     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1725     VariantClear(&dest);
1726
1727     ISAXContentHandler_Release(content);
1728     IMXWriter_Release(writer);
1729
1730     free_bstrs();
1731 }
1732
1733 enum startendtype
1734 {
1735     StartElement,
1736     EndElement,
1737     StartEndElement
1738 };
1739
1740 struct writer_startendelement_t {
1741     const GUID *clsid;
1742     enum startendtype type;
1743     const char *uri;
1744     const char *local_name;
1745     const char *qname;
1746     const char *output;
1747     HRESULT hr;
1748     ISAXAttributes *attr;
1749 };
1750
1751 static const struct writer_startendelement_t writer_startendelement[] = {
1752     /* 0 */
1753     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1754     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1755     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1756     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
1757     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1758     /* 5 */
1759     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1760     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1761     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
1762     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1763     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1764     /* 10 */
1765     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1766     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
1767     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1768     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1769     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1770     /* 15 */
1771     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
1772     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
1773     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1774     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1775     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1776     /* 20 */
1777     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1778     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1779     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1780     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
1781     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1782     /* 25 */
1783     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1784     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1785     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1786     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1787     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1788     /* 30 */
1789     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1790     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1791     /* endElement tests */
1792     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1793     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1794     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1795     /* 35 */
1796     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
1797     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1798     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1799     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1800     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
1801     /* 40 */
1802     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1803     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1804     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1805     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
1806     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1807     /* 45 */
1808     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1809     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1810     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
1811     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
1812     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1813     /* 50 */
1814     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1815     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1816     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1817     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1818     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1819     /* 55 */
1820     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
1821     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1822     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1823     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1824     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1825     /* 60 */
1826     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1827     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1828     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1829     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1830
1831     /* with attributes */
1832     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1833     /* 65 */
1834     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1835     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1836     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1837     /* empty elements */
1838     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1839     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1840     /* 70 */
1841     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1842     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1843     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
1844     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
1845     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
1846     /* 75 */
1847     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
1848     { NULL }
1849 };
1850
1851 static void get_supported_mxwriter_data(struct msxmlsupported_data_t *table)
1852 {
1853     while (table->clsid)
1854     {
1855         IMXWriter *writer;
1856         HRESULT hr;
1857
1858         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1859             &IID_IMXWriter, (void**)&writer);
1860         if (hr == S_OK) IMXWriter_Release(writer);
1861
1862         table->supported = hr == S_OK;
1863         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
1864
1865         table++;
1866     }
1867 }
1868
1869 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
1870 {
1871     int i = 0;
1872
1873     while (table->clsid)
1874     {
1875         ISAXContentHandler *content;
1876         IMXWriter *writer;
1877         HRESULT hr;
1878
1879         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1880         {
1881             table++;
1882             i++;
1883             continue;
1884         }
1885
1886         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1887             &IID_IMXWriter, (void**)&writer);
1888         EXPECT_HR(hr, S_OK);
1889
1890         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1891         EXPECT_HR(hr, S_OK);
1892
1893         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1894         EXPECT_HR(hr, S_OK);
1895
1896         hr = ISAXContentHandler_startDocument(content);
1897         EXPECT_HR(hr, S_OK);
1898
1899         if (table->type == StartElement)
1900         {
1901             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1902                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1903             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1904         }
1905         else if (table->type == EndElement)
1906         {
1907             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1908                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1909             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1910         }
1911         else
1912         {
1913             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1914                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1915             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1916             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1917                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1918             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1919         }
1920
1921         /* test output */
1922         if (hr == S_OK)
1923         {
1924             VARIANT dest;
1925
1926             V_VT(&dest) = VT_EMPTY;
1927             hr = IMXWriter_get_output(writer, &dest);
1928             EXPECT_HR(hr, S_OK);
1929             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1930             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
1931                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
1932             VariantClear(&dest);
1933         }
1934
1935         ISAXContentHandler_Release(content);
1936         IMXWriter_Release(writer);
1937
1938         table++;
1939         i++;
1940     }
1941
1942     free_bstrs();
1943 }
1944
1945 static void test_mxwriter_startendelement(void)
1946 {
1947     ISAXContentHandler *content;
1948     IMXWriter *writer;
1949     VARIANT dest;
1950     HRESULT hr;
1951
1952     test_mxwriter_startendelement_batch(writer_startendelement);
1953
1954     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1955             &IID_IMXWriter, (void**)&writer);
1956     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1957
1958     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1959     ok(hr == S_OK, "got %08x\n", hr);
1960
1961     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1962     ok(hr == S_OK, "got %08x\n", hr);
1963
1964     hr = ISAXContentHandler_startDocument(content);
1965     ok(hr == S_OK, "got %08x\n", hr);
1966
1967     /* all string pointers should be not null */
1968     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
1969     ok(hr == S_OK, "got %08x\n", hr);
1970
1971     V_VT(&dest) = VT_EMPTY;
1972     hr = IMXWriter_get_output(writer, &dest);
1973     ok(hr == S_OK, "got %08x\n", hr);
1974     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1975     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1976     VariantClear(&dest);
1977
1978     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
1979     ok(hr == S_OK, "got %08x\n", hr);
1980
1981     V_VT(&dest) = VT_EMPTY;
1982     hr = IMXWriter_get_output(writer, &dest);
1983     ok(hr == S_OK, "got %08x\n", hr);
1984     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1985     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1986     VariantClear(&dest);
1987
1988     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
1989     EXPECT_HR(hr, E_INVALIDARG);
1990
1991     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
1992     EXPECT_HR(hr, E_INVALIDARG);
1993
1994     /* only local name is an error too */
1995     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
1996     EXPECT_HR(hr, E_INVALIDARG);
1997
1998     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
1999     EXPECT_HR(hr, S_OK);
2000
2001     V_VT(&dest) = VT_EMPTY;
2002     hr = IMXWriter_get_output(writer, &dest);
2003     EXPECT_HR(hr, S_OK);
2004     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2005     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2006     VariantClear(&dest);
2007
2008     hr = ISAXContentHandler_endDocument(content);
2009     EXPECT_HR(hr, S_OK);
2010
2011     V_VT(&dest) = VT_EMPTY;
2012     hr = IMXWriter_put_output(writer, dest);
2013     EXPECT_HR(hr, S_OK);
2014
2015     hr = ISAXContentHandler_startDocument(content);
2016     EXPECT_HR(hr, S_OK);
2017
2018     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
2019     EXPECT_HR(hr, S_OK);
2020
2021     V_VT(&dest) = VT_EMPTY;
2022     hr = IMXWriter_get_output(writer, &dest);
2023     EXPECT_HR(hr, S_OK);
2024     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2025     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2026     VariantClear(&dest);
2027
2028     ISAXContentHandler_Release(content);
2029     IMXWriter_Release(writer);
2030
2031     free_bstrs();
2032 }
2033
2034 static void test_mxwriter_characters(void)
2035 {
2036     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
2037     ISAXContentHandler *content;
2038     IMXWriter *writer;
2039     VARIANT dest;
2040     HRESULT hr;
2041
2042     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2043             &IID_IMXWriter, (void**)&writer);
2044     EXPECT_HR(hr, S_OK);
2045
2046     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2047     EXPECT_HR(hr, S_OK);
2048
2049     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2050     EXPECT_HR(hr, S_OK);
2051
2052     hr = ISAXContentHandler_startDocument(content);
2053     EXPECT_HR(hr, S_OK);
2054
2055     hr = ISAXContentHandler_characters(content, NULL, 0);
2056     EXPECT_HR(hr, E_INVALIDARG);
2057
2058     hr = ISAXContentHandler_characters(content, chardataW, 0);
2059     EXPECT_HR(hr, S_OK);
2060
2061     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
2062     EXPECT_HR(hr, S_OK);
2063
2064     V_VT(&dest) = VT_EMPTY;
2065     hr = IMXWriter_get_output(writer, &dest);
2066     EXPECT_HR(hr, S_OK);
2067     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2068     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2069     VariantClear(&dest);
2070
2071     hr = ISAXContentHandler_endDocument(content);
2072     EXPECT_HR(hr, S_OK);
2073
2074     ISAXContentHandler_Release(content);
2075     IMXWriter_Release(writer);
2076
2077     /* try empty characters data to see if element is closed */
2078     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2079             &IID_IMXWriter, (void**)&writer);
2080     EXPECT_HR(hr, S_OK);
2081
2082     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2083     EXPECT_HR(hr, S_OK);
2084
2085     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2086     EXPECT_HR(hr, S_OK);
2087
2088     hr = ISAXContentHandler_startDocument(content);
2089     EXPECT_HR(hr, S_OK);
2090
2091     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
2092     EXPECT_HR(hr, S_OK);
2093
2094     hr = ISAXContentHandler_characters(content, chardataW, 0);
2095     EXPECT_HR(hr, S_OK);
2096
2097     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
2098     EXPECT_HR(hr, S_OK);
2099
2100     V_VT(&dest) = VT_EMPTY;
2101     hr = IMXWriter_get_output(writer, &dest);
2102     EXPECT_HR(hr, S_OK);
2103     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2104     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2105     VariantClear(&dest);
2106
2107     ISAXContentHandler_Release(content);
2108     IMXWriter_Release(writer);
2109
2110     free_bstrs();
2111 }
2112
2113 static const mxwriter_stream_test mxwriter_stream_tests[] = {
2114     {
2115         VARIANT_TRUE,"UTF-16",
2116         {
2117             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
2118             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2119             {TRUE}
2120         }
2121     },
2122     {
2123         VARIANT_FALSE,"UTF-16",
2124         {
2125             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2126             {TRUE}
2127         }
2128     },
2129     {
2130         VARIANT_TRUE,"UTF-8",
2131         {
2132             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
2133             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
2134              * and the writer is released.
2135              */
2136             {FALSE,NULL,0},
2137             {TRUE}
2138         }
2139     },
2140     {
2141         VARIANT_TRUE,"UTF-16",
2142         {
2143             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
2144             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2145             {TRUE}
2146         }
2147     },
2148     {
2149         VARIANT_TRUE,"UTF-16",
2150         {
2151             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
2152             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
2153             {TRUE}
2154         }
2155     }
2156 };
2157
2158 static void test_mxwriter_stream(void)
2159 {
2160     IMXWriter *writer;
2161     ISAXContentHandler *content;
2162     HRESULT hr;
2163     VARIANT dest;
2164     IStream *stream;
2165     LARGE_INTEGER pos;
2166     ULARGE_INTEGER pos2;
2167     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
2168
2169     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
2170         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
2171
2172         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2173                 &IID_IMXWriter, (void**)&writer);
2174         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2175
2176         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2177         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2178
2179         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
2180         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
2181
2182         V_VT(&dest) = VT_UNKNOWN;
2183         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
2184         hr = IMXWriter_put_output(writer, dest);
2185         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
2186         VariantClear(&dest);
2187
2188         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
2189         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
2190
2191         current_write_test = test->expected_writes;
2192
2193         hr = ISAXContentHandler_startDocument(content);
2194         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
2195
2196         hr = ISAXContentHandler_endDocument(content);
2197         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
2198
2199         ISAXContentHandler_Release(content);
2200         IMXWriter_Release(writer);
2201
2202         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
2203             (int)(current_write_test-test->expected_writes), current_stream_test_index);
2204     }
2205
2206     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2207             &IID_IMXWriter, (void**)&writer);
2208     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2209
2210     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2211     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
2212
2213     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2214     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2215
2216     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
2217     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
2218
2219     V_VT(&dest) = VT_UNKNOWN;
2220     V_UNKNOWN(&dest) = (IUnknown*)stream;
2221     hr = IMXWriter_put_output(writer, dest);
2222     ok(hr == S_OK, "put_output failed: %08x\n", hr);
2223
2224     hr = ISAXContentHandler_startDocument(content);
2225     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2226
2227     /* Setting output of the mxwriter causes the current output to be flushed,
2228      * and the writer to start over.
2229      */
2230     V_VT(&dest) = VT_EMPTY;
2231     hr = IMXWriter_put_output(writer, dest);
2232     ok(hr == S_OK, "put_output failed: %08x\n", hr);
2233
2234     pos.QuadPart = 0;
2235     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2236     ok(hr == S_OK, "Seek failed: %08x\n", hr);
2237     ok(pos2.QuadPart != 0, "expected stream position moved\n");
2238
2239     hr = ISAXContentHandler_startDocument(content);
2240     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2241
2242     hr = ISAXContentHandler_endDocument(content);
2243     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
2244
2245     V_VT(&dest) = VT_EMPTY;
2246     hr = IMXWriter_get_output(writer, &dest);
2247     ok(hr == S_OK, "get_output failed: %08x\n", hr);
2248     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
2249     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
2250             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2251     VariantClear(&dest);
2252
2253     ISAXContentHandler_Release(content);
2254     IMXWriter_Release(writer);
2255
2256     free_bstrs();
2257 }
2258
2259 static void test_mxwriter_encoding(void)
2260 {
2261     IMXWriter *writer;
2262     ISAXContentHandler *content;
2263     HRESULT hr;
2264     VARIANT dest;
2265
2266     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2267             &IID_IMXWriter, (void**)&writer);
2268     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2269
2270     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2271     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2272
2273     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
2274     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
2275
2276     hr = ISAXContentHandler_startDocument(content);
2277     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2278
2279     hr = ISAXContentHandler_endDocument(content);
2280     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
2281
2282     /* The content is always re-encoded to UTF-16 when the output is
2283      * retrieved as a BSTR.
2284      */
2285     V_VT(&dest) = VT_EMPTY;
2286     hr = IMXWriter_get_output(writer, &dest);
2287     todo_wine ok(hr == S_OK, "get_output failed: %08x\n", hr);
2288     todo_wine ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
2289     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)),
2290             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2291     VariantClear(&dest);
2292
2293     ISAXContentHandler_Release(content);
2294     IMXWriter_Release(writer);
2295
2296     free_bstrs();
2297 }
2298
2299 START_TEST(saxreader)
2300 {
2301     ISAXXMLReader *reader;
2302     HRESULT hr;
2303
2304     hr = CoInitialize(NULL);
2305     ok(hr == S_OK, "failed to init com\n");
2306
2307     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2308             &IID_ISAXXMLReader, (void**)&reader);
2309
2310     if(FAILED(hr))
2311     {
2312         skip("Failed to create SAXXMLReader instance\n");
2313         CoUninitialize();
2314         return;
2315     }
2316     ISAXXMLReader_Release(reader);
2317
2318     test_saxreader();
2319     test_saxreader_properties();
2320     test_encoding();
2321
2322     /* MXXMLWriter tests */
2323     get_supported_mxwriter_data(msxmlsupported_data);
2324     if (is_mxwriter_supported(&CLSID_MXXMLWriter, msxmlsupported_data))
2325     {
2326         test_mxwriter_contenthandler();
2327         test_mxwriter_startenddocument();
2328         test_mxwriter_startendelement();
2329         test_mxwriter_characters();
2330         test_mxwriter_properties();
2331         test_mxwriter_flush();
2332         test_mxwriter_stream();
2333         test_mxwriter_encoding();
2334     }
2335     else
2336         win_skip("MXXMLWriter not supported\n");
2337
2338     CoUninitialize();
2339 }