msxml3/tests: Add a trailing '\n' to an ok() call.
[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_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__)
36 static void _expect_ref(IUnknown* obj, ULONG ref, int line)
37 {
38     ULONG rc = IUnknown_AddRef(obj);
39     IUnknown_Release(obj);
40     ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1);
41 }
42
43 static BSTR alloc_str_from_narrow(const char *str)
44 {
45     int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
46     BSTR ret = SysAllocStringLen(NULL, len - 1);  /* NUL character added automatically */
47     MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
48     return ret;
49 }
50
51 static BSTR alloced_bstrs[256];
52 static int alloced_bstrs_count;
53
54 static BSTR _bstr_(const char *str)
55 {
56     assert(alloced_bstrs_count < sizeof(alloced_bstrs)/sizeof(alloced_bstrs[0]));
57     alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str);
58     return alloced_bstrs[alloced_bstrs_count++];
59 }
60
61 static void free_bstrs(void)
62 {
63     int i;
64     for (i = 0; i < alloced_bstrs_count; i++)
65         SysFreeString(alloced_bstrs[i]);
66     alloced_bstrs_count = 0;
67 }
68
69 typedef enum _CH {
70     CH_ENDTEST,
71     CH_PUTDOCUMENTLOCATOR,
72     CH_STARTDOCUMENT,
73     CH_ENDDOCUMENT,
74     CH_STARTPREFIXMAPPING,
75     CH_ENDPREFIXMAPPING,
76     CH_STARTELEMENT,
77     CH_ENDELEMENT,
78     CH_CHARACTERS,
79     CH_IGNORABLEWHITESPACE,
80     CH_PROCESSINGINSTRUCTION,
81     CH_SKIPPEDENTITY
82 } CH;
83
84 static const WCHAR szSimpleXML[] = {
85 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n',
86 '<','B','a','n','k','A','c','c','o','u','n','t','>','\n',
87 ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n',
88 ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n',
89 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0'
90 };
91
92 static const WCHAR szCarriageRetTest[] = {
93 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n',
94 '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n',
95 '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n',
96 '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n',
97 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\0'
98 };
99
100 static const WCHAR szUtf16XML[] = {
101 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ',
102 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ',
103 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n'
104 };
105
106 static const CHAR szUtf16BOM[] = {0xff, 0xfe};
107
108 static const CHAR szUtf8XML[] =
109 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n";
110
111 static const CHAR szTestXML[] =
112 "<?xml version=\"1.0\" ?>\n"
113 "<BankAccount>\n"
114 "   <Number>1234</Number>\n"
115 "   <Name>Captain Ahab</Name>\n"
116 "</BankAccount>\n";
117
118 typedef struct _contenthandlercheck {
119     CH id;
120     int line;
121     int column;
122     const char *arg1;
123     const char *arg2;
124     const char *arg3;
125 } content_handler_test;
126
127 static content_handler_test contentHandlerTest1[] = {
128     { CH_PUTDOCUMENTLOCATOR, 0, 0 },
129     { CH_STARTDOCUMENT, 0, 0 },
130     { CH_STARTELEMENT, 2, 14, "", "BankAccount", "BankAccount" },
131     { CH_CHARACTERS, 2, 14, "\n   " },
132     { CH_STARTELEMENT, 3, 12, "", "Number", "Number" },
133     { CH_CHARACTERS, 3, 12, "1234" },
134     { CH_ENDELEMENT, 3, 18, "", "Number", "Number" },
135     { CH_CHARACTERS, 3, 25, "\n   " },
136     { CH_STARTELEMENT, 4, 10, "", "Name", "Name" },
137     { CH_CHARACTERS, 4, 10, "Captain Ahab" },
138     { CH_ENDELEMENT, 4, 24, "", "Name", "Name" },
139     { CH_CHARACTERS, 4, 29, "\n" },
140     { CH_ENDELEMENT, 5, 3, "", "BankAccount", "BankAccount" },
141     { CH_ENDDOCUMENT, 0, 0 },
142     { CH_ENDTEST }
143 };
144
145 static content_handler_test contentHandlerTest2[] = {
146     { CH_PUTDOCUMENTLOCATOR, 0, 0 },
147     { CH_STARTDOCUMENT, 0, 0 },
148     { CH_STARTELEMENT, 2, 14, "", "BankAccount", "BankAccount" },
149     { CH_CHARACTERS, 2, 14, "\n" },
150     { CH_CHARACTERS, 2, 16, "\t" },
151     { CH_STARTELEMENT, 3, 10, "", "Number", "Number" },
152     { CH_CHARACTERS, 3, 10, "1234" },
153     { CH_ENDELEMENT, 3, 16, "", "Number", "Number" },
154     { CH_CHARACTERS, 3, 23, "\n" },
155     { CH_CHARACTERS, 3, 25, "\t" },
156     { CH_STARTELEMENT, 4, 8, "", "Name", "Name" },
157     { CH_CHARACTERS, 4, 8, "Captain Ahab" },
158     { CH_ENDELEMENT, 4, 22, "", "Name", "Name" },
159     { CH_CHARACTERS, 4, 27, "\n" },
160     { CH_ENDELEMENT, 5, 3, "", "BankAccount", "BankAccount" },
161     { CH_ENDDOCUMENT, 0, 0 },
162     { CH_ENDTEST }
163 };
164
165 static content_handler_test *expectCall;
166 static ISAXLocator *locator;
167
168 static void test_saxstr(unsigned line, const WCHAR *szStr, int nStr, const char *szTest)
169 {
170     WCHAR buf[1024];
171     int len;
172
173     if(!szTest) {
174         ok_(__FILE__,line) (szStr == NULL, "szStr != NULL\n");
175         ok_(__FILE__,line) (nStr == 0, "nStr = %d, expected 0\n", nStr);
176         return;
177     }
178
179     len = strlen(szTest);
180     ok_(__FILE__,line) (len == nStr, "nStr = %d, expected %d (%s)\n", nStr, len, szTest);
181     if(len != nStr)
182         return;
183
184     MultiByteToWideChar(CP_ACP, 0, szTest, -1, buf, sizeof(buf)/sizeof(WCHAR));
185     ok_(__FILE__,line) (!memcmp(szStr, buf, len*sizeof(WCHAR)), "unexpected szStr %s, expected %s\n",
186                         wine_dbgstr_wn(szStr, nStr), szTest);
187 }
188
189 static BOOL test_expect_call(CH id)
190 {
191     ok(expectCall->id == id, "unexpected call %d, expected %d\n", id, expectCall->id);
192     return expectCall->id == id;
193 }
194
195 static void test_locator(unsigned line, int loc_line, int loc_column)
196 {
197     int rcolumn, rline;
198     ISAXLocator_getLineNumber(locator, &rline);
199     ISAXLocator_getColumnNumber(locator, &rcolumn);
200
201     ok_(__FILE__,line) (rline == loc_line,
202             "unexpected line %d, expected %d\n", rline, loc_line);
203     ok_(__FILE__,line) (rcolumn == loc_column,
204             "unexpected column %d, expected %d\n", rcolumn, loc_column);
205 }
206
207 static HRESULT WINAPI contentHandler_QueryInterface(
208         ISAXContentHandler* iface,
209         REFIID riid,
210         void **ppvObject)
211 {
212     *ppvObject = NULL;
213
214     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
215     {
216         *ppvObject = iface;
217     }
218     else
219     {
220         return E_NOINTERFACE;
221     }
222
223     return S_OK;
224 }
225
226 static ULONG WINAPI contentHandler_AddRef(
227         ISAXContentHandler* iface)
228 {
229     return 2;
230 }
231
232 static ULONG WINAPI contentHandler_Release(
233         ISAXContentHandler* iface)
234 {
235     return 1;
236 }
237
238 static HRESULT WINAPI contentHandler_putDocumentLocator(
239         ISAXContentHandler* iface,
240         ISAXLocator *pLocator)
241 {
242     if(!test_expect_call(CH_PUTDOCUMENTLOCATOR))
243         return E_FAIL;
244
245     locator = pLocator;
246     test_locator(__LINE__, expectCall->line, expectCall->column);
247
248     expectCall++;
249     return S_OK;
250 }
251
252 static HRESULT WINAPI contentHandler_startDocument(
253         ISAXContentHandler* iface)
254 {
255     if(!test_expect_call(CH_STARTDOCUMENT))
256         return E_FAIL;
257
258     test_locator(__LINE__, expectCall->line, expectCall->column);
259
260     expectCall++;
261     return S_OK;
262 }
263
264 static HRESULT WINAPI contentHandler_endDocument(
265         ISAXContentHandler* iface)
266 {
267     if(!test_expect_call(CH_ENDDOCUMENT))
268         return E_FAIL;
269
270     test_locator(__LINE__, expectCall->line, expectCall->column);
271
272     expectCall++;
273     return S_OK;
274 }
275
276 static HRESULT WINAPI contentHandler_startPrefixMapping(
277         ISAXContentHandler* iface,
278         const WCHAR *pPrefix,
279         int nPrefix,
280         const WCHAR *pUri,
281         int nUri)
282 {
283     if(!test_expect_call(CH_ENDDOCUMENT))
284         return E_FAIL;
285
286     test_saxstr(__LINE__, pPrefix, nPrefix, expectCall->arg1);
287     test_saxstr(__LINE__, pUri, nUri, expectCall->arg2);
288     test_locator(__LINE__, expectCall->line, expectCall->column);
289
290     expectCall++;
291     return S_OK;
292 }
293
294 static HRESULT WINAPI contentHandler_endPrefixMapping(
295         ISAXContentHandler* iface,
296         const WCHAR *pPrefix,
297         int nPrefix)
298 {
299     if(!test_expect_call(CH_ENDPREFIXMAPPING))
300         return E_FAIL;
301
302     test_saxstr(__LINE__, pPrefix, nPrefix, expectCall->arg1);
303     test_locator(__LINE__, expectCall->line, expectCall->column);
304
305     expectCall++;
306     return S_OK;
307 }
308
309 static HRESULT WINAPI contentHandler_startElement(
310         ISAXContentHandler* iface,
311         const WCHAR *pNamespaceUri,
312         int nNamespaceUri,
313         const WCHAR *pLocalName,
314         int nLocalName,
315         const WCHAR *pQName,
316         int nQName,
317         ISAXAttributes *pAttr)
318 {
319     if(!test_expect_call(CH_STARTELEMENT))
320         return E_FAIL;
321
322     test_saxstr(__LINE__, pNamespaceUri, nNamespaceUri, expectCall->arg1);
323     test_saxstr(__LINE__, pLocalName, nLocalName, expectCall->arg2);
324     test_saxstr(__LINE__, pQName, nQName, expectCall->arg3);
325     test_locator(__LINE__, expectCall->line, expectCall->column);
326
327     expectCall++;
328     return S_OK;
329 }
330
331 static HRESULT WINAPI contentHandler_endElement(
332         ISAXContentHandler* iface,
333         const WCHAR *pNamespaceUri,
334         int nNamespaceUri,
335         const WCHAR *pLocalName,
336         int nLocalName,
337         const WCHAR *pQName,
338         int nQName)
339 {
340     if(!test_expect_call(CH_ENDELEMENT))
341         return E_FAIL;
342
343     test_saxstr(__LINE__, pNamespaceUri, nNamespaceUri, expectCall->arg1);
344     test_saxstr(__LINE__, pLocalName, nLocalName, expectCall->arg2);
345     test_saxstr(__LINE__, pQName, nQName, expectCall->arg3);
346     test_locator(__LINE__, expectCall->line, expectCall->column);
347
348     expectCall++;
349     return S_OK;
350 }
351
352 static HRESULT WINAPI contentHandler_characters(
353         ISAXContentHandler* iface,
354         const WCHAR *pChars,
355         int nChars)
356 {
357     if(!test_expect_call(CH_CHARACTERS))
358         return E_FAIL;
359
360     test_saxstr(__LINE__, pChars, nChars, expectCall->arg1);
361     test_locator(__LINE__, expectCall->line, expectCall->column);
362
363     expectCall++;
364     return S_OK;
365 }
366
367 static HRESULT WINAPI contentHandler_ignorableWhitespace(
368         ISAXContentHandler* iface,
369         const WCHAR *pChars,
370         int nChars)
371 {
372     if(!test_expect_call(CH_IGNORABLEWHITESPACE))
373         return E_FAIL;
374
375     test_saxstr(__LINE__, pChars, nChars, expectCall->arg1);
376     test_locator(__LINE__, expectCall->line, expectCall->column);
377
378     expectCall++;
379     return S_OK;
380 }
381
382 static HRESULT WINAPI contentHandler_processingInstruction(
383         ISAXContentHandler* iface,
384         const WCHAR *pTarget,
385         int nTarget,
386         const WCHAR *pData,
387         int nData)
388 {
389     if(!test_expect_call(CH_PROCESSINGINSTRUCTION))
390         return E_FAIL;
391
392     test_saxstr(__LINE__, pTarget, nTarget, expectCall->arg1);
393     test_saxstr(__LINE__, pData, nData, expectCall->arg2);
394     test_locator(__LINE__, expectCall->line, expectCall->column);
395
396     expectCall++;
397     return S_OK;
398 }
399
400 static HRESULT WINAPI contentHandler_skippedEntity(
401         ISAXContentHandler* iface,
402         const WCHAR *pName,
403         int nName)
404 {
405     if(!test_expect_call(CH_SKIPPEDENTITY))
406         return E_FAIL;
407
408     test_saxstr(__LINE__, pName, nName, expectCall->arg1);
409     test_locator(__LINE__, expectCall->line, expectCall->column);
410
411     expectCall++;
412     return S_OK;
413 }
414
415
416 static const ISAXContentHandlerVtbl contentHandlerVtbl =
417 {
418     contentHandler_QueryInterface,
419     contentHandler_AddRef,
420     contentHandler_Release,
421     contentHandler_putDocumentLocator,
422     contentHandler_startDocument,
423     contentHandler_endDocument,
424     contentHandler_startPrefixMapping,
425     contentHandler_endPrefixMapping,
426     contentHandler_startElement,
427     contentHandler_endElement,
428     contentHandler_characters,
429     contentHandler_ignorableWhitespace,
430     contentHandler_processingInstruction,
431     contentHandler_skippedEntity
432 };
433
434 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
435
436 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
437         ISAXErrorHandler* iface,
438         REFIID riid,
439         void **ppvObject)
440 {
441     *ppvObject = NULL;
442
443     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
444     {
445         *ppvObject = iface;
446     }
447     else
448     {
449         return E_NOINTERFACE;
450     }
451
452     return S_OK;
453 }
454
455 static ULONG WINAPI isaxerrorHandler_AddRef(
456         ISAXErrorHandler* iface)
457 {
458     return 2;
459 }
460
461 static ULONG WINAPI isaxerrorHandler_Release(
462         ISAXErrorHandler* iface)
463 {
464     return 1;
465 }
466
467 static HRESULT WINAPI isaxerrorHandler_error(
468         ISAXErrorHandler* iface,
469         ISAXLocator *pLocator,
470         const WCHAR *pErrorMessage,
471         HRESULT hrErrorCode)
472 {
473     return S_OK;
474 }
475
476 static HRESULT WINAPI isaxerrorHandler_fatalError(
477         ISAXErrorHandler* iface,
478         ISAXLocator *pLocator,
479         const WCHAR *pErrorMessage,
480         HRESULT hrErrorCode)
481 {
482     return S_OK;
483 }
484
485 static HRESULT WINAPI isaxerrorHanddler_ignorableWarning(
486         ISAXErrorHandler* iface,
487         ISAXLocator *pLocator,
488         const WCHAR *pErrorMessage,
489         HRESULT hrErrorCode)
490 {
491     return S_OK;
492 }
493
494 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
495 {
496     isaxerrorHandler_QueryInterface,
497     isaxerrorHandler_AddRef,
498     isaxerrorHandler_Release,
499     isaxerrorHandler_error,
500     isaxerrorHandler_fatalError,
501     isaxerrorHanddler_ignorableWarning
502 };
503
504 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
505
506 static HRESULT WINAPI isaxattributes_QueryInterface(
507         ISAXAttributes* iface,
508         REFIID riid,
509         void **ppvObject)
510 {
511     *ppvObject = NULL;
512
513     if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
514     {
515         *ppvObject = iface;
516     }
517     else
518     {
519         return E_NOINTERFACE;
520     }
521
522     return S_OK;
523 }
524
525 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
526 {
527     return 2;
528 }
529
530 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
531 {
532     return 1;
533 }
534
535 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
536 {
537     *length = 2;
538     return S_OK;
539 }
540
541 static HRESULT WINAPI isaxattributes_getURI(
542     ISAXAttributes* iface,
543     int nIndex,
544     const WCHAR **pUrl,
545     int *pUriSize)
546 {
547     ok(0, "unexpected call\n");
548     return E_NOTIMPL;
549 }
550
551 static HRESULT WINAPI isaxattributes_getLocalName(
552     ISAXAttributes* iface,
553     int nIndex,
554     const WCHAR **pLocalName,
555     int *pLocalNameLength)
556 {
557     ok(0, "unexpected call\n");
558     return E_NOTIMPL;
559 }
560
561 static HRESULT WINAPI isaxattributes_getQName(
562     ISAXAttributes* iface,
563     int nIndex,
564     const WCHAR **pQName,
565     int *pQNameLength)
566 {
567     static const WCHAR attr1W[] = {'a',':','a','t','t','r','1',0};
568     static const WCHAR attr2W[] = {'a','t','t','r','2',0};
569
570     ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
571
572     *pQName = (nIndex == 0) ? attr1W : attr2W;
573     *pQNameLength = lstrlenW(*pQName);
574
575     return S_OK;
576 }
577
578 static HRESULT WINAPI isaxattributes_getName(
579     ISAXAttributes* iface,
580     int nIndex,
581     const WCHAR **pUri,
582     int * pUriLength,
583     const WCHAR ** pLocalName,
584     int * pLocalNameSize,
585     const WCHAR ** pQName,
586     int * pQNameLength)
587 {
588     ok(0, "unexpected call\n");
589     return E_NOTIMPL;
590 }
591
592 static HRESULT WINAPI isaxattributes_getIndexFromName(
593     ISAXAttributes* iface,
594     const WCHAR * pUri,
595     int cUriLength,
596     const WCHAR * pLocalName,
597     int cocalNameLength,
598     int * index)
599 {
600     ok(0, "unexpected call\n");
601     return E_NOTIMPL;
602 }
603
604 static HRESULT WINAPI isaxattributes_getIndexFromQName(
605     ISAXAttributes* iface,
606     const WCHAR * pQName,
607     int nQNameLength,
608     int * index)
609 {
610     ok(0, "unexpected call\n");
611     return E_NOTIMPL;
612 }
613
614 static HRESULT WINAPI isaxattributes_getType(
615     ISAXAttributes* iface,
616     int nIndex,
617     const WCHAR ** pType,
618     int * pTypeLength)
619 {
620     ok(0, "unexpected call\n");
621     return E_NOTIMPL;
622 }
623
624 static HRESULT WINAPI isaxattributes_getTypeFromName(
625     ISAXAttributes* iface,
626     const WCHAR * pUri,
627     int nUri,
628     const WCHAR * pLocalName,
629     int nLocalName,
630     const WCHAR ** pType,
631     int * nType)
632 {
633     ok(0, "unexpected call\n");
634     return E_NOTIMPL;
635 }
636
637 static HRESULT WINAPI isaxattributes_getTypeFromQName(
638     ISAXAttributes* iface,
639     const WCHAR * pQName,
640     int nQName,
641     const WCHAR ** pType,
642     int * nType)
643 {
644     ok(0, "unexpected call\n");
645     return E_NOTIMPL;
646 }
647
648 static HRESULT WINAPI isaxattributes_getValue(
649     ISAXAttributes* iface,
650     int nIndex,
651     const WCHAR ** pValue,
652     int * nValue)
653 {
654     static const WCHAR attrval1W[] = {'a','1',0};
655     static const WCHAR attrval2W[] = {'a','2',0};
656
657     ok(nIndex == 0 || nIndex == 1, "invalid index received %d\n", nIndex);
658
659     *pValue = (nIndex == 0) ? attrval1W : attrval2W;
660     *nValue = lstrlenW(*pValue);
661
662     return S_OK;
663 }
664
665 static HRESULT WINAPI isaxattributes_getValueFromName(
666     ISAXAttributes* iface,
667     const WCHAR * pUri,
668     int nUri,
669     const WCHAR * pLocalName,
670     int nLocalName,
671     const WCHAR ** pValue,
672     int * nValue)
673 {
674     ok(0, "unexpected call\n");
675     return E_NOTIMPL;
676 }
677
678 static HRESULT WINAPI isaxattributes_getValueFromQName(
679     ISAXAttributes* iface,
680     const WCHAR * pQName,
681     int nQName,
682     const WCHAR ** pValue,
683     int * nValue)
684 {
685     ok(0, "unexpected call\n");
686     return E_NOTIMPL;
687 }
688
689 static const ISAXAttributesVtbl SAXAttributesVtbl =
690 {
691     isaxattributes_QueryInterface,
692     isaxattributes_AddRef,
693     isaxattributes_Release,
694     isaxattributes_getLength,
695     isaxattributes_getURI,
696     isaxattributes_getLocalName,
697     isaxattributes_getQName,
698     isaxattributes_getName,
699     isaxattributes_getIndexFromName,
700     isaxattributes_getIndexFromQName,
701     isaxattributes_getType,
702     isaxattributes_getTypeFromName,
703     isaxattributes_getTypeFromQName,
704     isaxattributes_getValue,
705     isaxattributes_getValueFromName,
706     isaxattributes_getValueFromQName
707 };
708
709 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
710
711 typedef struct mxwriter_write_test_t {
712     BOOL        last;
713     const BYTE  *data;
714     DWORD       cb;
715     BOOL        null_written;
716     BOOL        fail_write;
717 } mxwriter_write_test;
718
719 typedef struct mxwriter_stream_test_t {
720     VARIANT_BOOL        bom;
721     const char          *encoding;
722     mxwriter_write_test expected_writes[4];
723 } mxwriter_stream_test;
724
725 static const mxwriter_write_test *current_write_test;
726 static DWORD current_stream_test_index;
727
728 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
729 {
730     *ppvObject = NULL;
731
732     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
733         *ppvObject = iface;
734     else
735         return E_NOINTERFACE;
736
737     return S_OK;
738 }
739
740 static ULONG WINAPI istream_AddRef(IStream *iface)
741 {
742     return 2;
743 }
744
745 static ULONG WINAPI istream_Release(IStream *iface)
746 {
747     return 1;
748 }
749
750 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
751 {
752     ok(0, "unexpected call\n");
753     return E_NOTIMPL;
754 }
755
756 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
757 {
758     BOOL fail = FALSE;
759
760     ok(pv != NULL, "pv == NULL\n");
761
762     if(current_write_test->last) {
763         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
764         return E_FAIL;
765     }
766
767     fail = current_write_test->fail_write;
768
769     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
770         current_write_test->cb, cb, current_stream_test_index);
771
772     if(!pcbWritten)
773         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
774     else
775         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
776
777     ++current_write_test;
778
779     if(pcbWritten)
780         *pcbWritten = cb;
781
782     return fail ? E_FAIL : S_OK;
783 }
784
785 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
786         ULARGE_INTEGER *plibNewPosition)
787 {
788     ok(0, "unexpected call\n");
789     return E_NOTIMPL;
790 }
791
792 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
793 {
794     ok(0, "unexpected call\n");
795     return E_NOTIMPL;
796 }
797
798 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
799         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
800 {
801     ok(0, "unexpected call\n");
802     return E_NOTIMPL;
803 }
804
805 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
806 {
807     ok(0, "unexpected call\n");
808     return E_NOTIMPL;
809 }
810
811 static HRESULT WINAPI istream_Revert(IStream *iface)
812 {
813     ok(0, "unexpected call\n");
814     return E_NOTIMPL;
815 }
816
817 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
818         ULARGE_INTEGER cb, DWORD dwLockType)
819 {
820     ok(0, "unexpected call\n");
821     return E_NOTIMPL;
822 }
823
824 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
825         ULARGE_INTEGER cb, DWORD dwLockType)
826 {
827     ok(0, "unexpected call\n");
828     return E_NOTIMPL;
829 }
830
831 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
832 {
833     ok(0, "unexpected call\n");
834     return E_NOTIMPL;
835 }
836
837 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
838 {
839     ok(0, "unexpected call\n");
840     return E_NOTIMPL;
841 }
842
843 static const IStreamVtbl StreamVtbl = {
844     istream_QueryInterface,
845     istream_AddRef,
846     istream_Release,
847     istream_Read,
848     istream_Write,
849     istream_Seek,
850     istream_SetSize,
851     istream_CopyTo,
852     istream_Commit,
853     istream_Revert,
854     istream_LockRegion,
855     istream_UnlockRegion,
856     istream_Stat,
857     istream_Clone
858 };
859
860 static IStream mxstream = { &StreamVtbl };
861
862 static void test_saxreader(void)
863 {
864     HRESULT hr;
865     ISAXXMLReader *reader = NULL;
866     VARIANT var;
867     ISAXContentHandler *lpContentHandler;
868     ISAXErrorHandler *lpErrorHandler;
869     SAFEARRAY *pSA;
870     SAFEARRAYBOUND SADim[1];
871     char *pSAData = NULL;
872     IStream *iStream;
873     ULARGE_INTEGER liSize;
874     LARGE_INTEGER liPos;
875     ULONG bytesWritten;
876     HANDLE file;
877     static const CHAR testXmlA[] = "test.xml";
878     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
879     IXMLDOMDocument *domDocument;
880     BSTR bstrData;
881     VARIANT_BOOL vBool;
882
883     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
884             &IID_ISAXXMLReader, (LPVOID*)&reader);
885     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
886
887     hr = ISAXXMLReader_getContentHandler(reader, NULL);
888     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
889
890     hr = ISAXXMLReader_getErrorHandler(reader, NULL);
891     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
892
893     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
894     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
895     ok(lpContentHandler == NULL, "Expected %p, got %p\n", NULL, lpContentHandler);
896
897     hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
898     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
899     ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
900
901     hr = ISAXXMLReader_putContentHandler(reader, NULL);
902     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
903
904     hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
905     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
906
907     hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
908     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
909
910     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
911     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
912     ok(lpContentHandler == &contentHandler, "Expected %p, got %p\n", &contentHandler, lpContentHandler);
913
914     V_VT(&var) = VT_BSTR;
915     V_BSTR(&var) = SysAllocString(szSimpleXML);
916
917     expectCall = contentHandlerTest1;
918     hr = ISAXXMLReader_parse(reader, var);
919     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
920     test_expect_call(CH_ENDTEST);
921
922     VariantClear(&var);
923
924     SADim[0].lLbound= 0;
925     SADim[0].cElements= sizeof(szTestXML)-1;
926     pSA = SafeArrayCreate(VT_UI1, 1, SADim);
927     SafeArrayAccessData(pSA, (void**)&pSAData);
928     memcpy(pSAData, szTestXML, sizeof(szTestXML)-1);
929     SafeArrayUnaccessData(pSA);
930     V_VT(&var) = VT_ARRAY|VT_UI1;
931     V_ARRAY(&var) = pSA;
932
933     expectCall = contentHandlerTest1;
934     hr = ISAXXMLReader_parse(reader, var);
935     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
936     test_expect_call(CH_ENDTEST);
937
938     SafeArrayDestroy(pSA);
939
940     CreateStreamOnHGlobal(NULL, TRUE, &iStream);
941     liSize.QuadPart = strlen(szTestXML);
942     IStream_SetSize(iStream, liSize);
943     IStream_Write(iStream, szTestXML, strlen(szTestXML), &bytesWritten);
944     liPos.QuadPart = 0;
945     IStream_Seek(iStream, liPos, STREAM_SEEK_SET, NULL);
946     V_VT(&var) = VT_UNKNOWN|VT_DISPATCH;
947     V_UNKNOWN(&var) = (IUnknown*)iStream;
948
949     expectCall = contentHandlerTest1;
950     hr = ISAXXMLReader_parse(reader, var);
951     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
952     test_expect_call(CH_ENDTEST);
953
954     IStream_Release(iStream);
955
956     V_VT(&var) = VT_BSTR;
957     V_BSTR(&var) = SysAllocString(szCarriageRetTest);
958
959     expectCall = contentHandlerTest2;
960     hr = ISAXXMLReader_parse(reader, var);
961     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
962     test_expect_call(CH_ENDTEST);
963
964     VariantClear(&var);
965
966     file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
967     ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
968     WriteFile(file, szTestXML, sizeof(szTestXML)-1, &bytesWritten, NULL);
969     CloseHandle(file);
970
971     expectCall = contentHandlerTest1;
972     hr = ISAXXMLReader_parseURL(reader, testXmlW);
973     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
974     test_expect_call(CH_ENDTEST);
975
976     DeleteFileA(testXmlA);
977
978     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
979             &IID_IXMLDOMDocument, (LPVOID*)&domDocument);
980     if(FAILED(hr))
981     {
982         skip("Failed to create DOMDocument instance\n");
983         return;
984     }
985     bstrData = SysAllocString(szSimpleXML);
986     hr = IXMLDOMDocument_loadXML(domDocument, bstrData, &vBool);
987     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
988     V_VT(&var) = VT_UNKNOWN;
989     V_UNKNOWN(&var) = (IUnknown*)domDocument;
990
991     expectCall = contentHandlerTest2;
992     hr = ISAXXMLReader_parse(reader, var);
993     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
994     test_expect_call(CH_ENDTEST);
995     IXMLDOMDocument_Release(domDocument);
996
997     ISAXXMLReader_Release(reader);
998     SysFreeString(bstrData);
999 }
1000
1001 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
1002 static const CHAR UTF8BOMTest[] =
1003 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
1004 "<a></a>\n";
1005
1006 struct enc_test_entry_t {
1007     const GUID *guid;
1008     const char *clsid;
1009     const char *data;
1010     HRESULT hr;
1011     int todo;
1012 };
1013
1014 static const struct enc_test_entry_t encoding_test_data[] = {
1015     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
1016     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
1017     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
1018     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
1019     { 0 }
1020 };
1021
1022 static void test_encoding(void)
1023 {
1024     const struct enc_test_entry_t *entry = encoding_test_data;
1025     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1026     static const CHAR testXmlA[] = "test.xml";
1027     ISAXXMLReader *reader;
1028     DWORD written;
1029     HANDLE file;
1030     HRESULT hr;
1031
1032     while (entry->guid)
1033     {
1034         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1035         if (hr != S_OK)
1036         {
1037             win_skip("can't create %s instance\n", entry->clsid);
1038             entry++;
1039             continue;
1040         }
1041
1042         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1043         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
1044         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
1045         CloseHandle(file);
1046
1047         hr = ISAXXMLReader_parseURL(reader, testXmlW);
1048         if (entry->todo)
1049             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1050         else
1051             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1052
1053         DeleteFileA(testXmlA);
1054         ISAXXMLReader_Release(reader);
1055
1056         entry++;
1057     }
1058 }
1059
1060 static void test_mxwriter_contenthandler(void)
1061 {
1062     ISAXContentHandler *handler;
1063     IMXWriter *writer, *writer2;
1064     HRESULT hr;
1065
1066     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1067             &IID_IMXWriter, (void**)&writer);
1068     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1069
1070     EXPECT_REF(writer, 1);
1071
1072     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
1073     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1074     EXPECT_REF(writer, 2);
1075     EXPECT_REF(handler, 2);
1076
1077     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
1078     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1079     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
1080     EXPECT_REF(writer, 3);
1081     EXPECT_REF(writer2, 3);
1082     IMXWriter_Release(writer2);
1083
1084     ISAXContentHandler_Release(handler);
1085     IMXWriter_Release(writer);
1086 }
1087
1088 static void test_mxwriter_properties(void)
1089 {
1090     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
1091     static const WCHAR emptyW[] = {0};
1092     static const WCHAR testW[] = {'t','e','s','t',0};
1093     IMXWriter *writer;
1094     VARIANT_BOOL b;
1095     HRESULT hr;
1096     BSTR str, str2;
1097
1098     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1099             &IID_IMXWriter, (void**)&writer);
1100     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1101
1102     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
1103     ok(hr == E_POINTER, "got %08x\n", hr);
1104
1105     b = VARIANT_TRUE;
1106     hr = IMXWriter_get_disableOutputEscaping(writer, &b);
1107     ok(hr == S_OK, "got %08x\n", hr);
1108     ok(b == VARIANT_FALSE, "got %d\n", b);
1109
1110     hr = IMXWriter_get_byteOrderMark(writer, NULL);
1111     ok(hr == E_POINTER, "got %08x\n", hr);
1112
1113     b = VARIANT_FALSE;
1114     hr = IMXWriter_get_byteOrderMark(writer, &b);
1115     ok(hr == S_OK, "got %08x\n", hr);
1116     ok(b == VARIANT_TRUE, "got %d\n", b);
1117
1118     hr = IMXWriter_get_indent(writer, NULL);
1119     ok(hr == E_POINTER, "got %08x\n", hr);
1120
1121     b = VARIANT_TRUE;
1122     hr = IMXWriter_get_indent(writer, &b);
1123     ok(hr == S_OK, "got %08x\n", hr);
1124     ok(b == VARIANT_FALSE, "got %d\n", b);
1125
1126     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
1127     ok(hr == E_POINTER, "got %08x\n", hr);
1128
1129     b = VARIANT_TRUE;
1130     hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
1131     ok(hr == S_OK, "got %08x\n", hr);
1132     ok(b == VARIANT_FALSE, "got %d\n", b);
1133
1134     hr = IMXWriter_get_standalone(writer, NULL);
1135     ok(hr == E_POINTER, "got %08x\n", hr);
1136
1137     b = VARIANT_TRUE;
1138     hr = IMXWriter_get_standalone(writer, &b);
1139     ok(hr == S_OK, "got %08x\n", hr);
1140     ok(b == VARIANT_FALSE, "got %d\n", b);
1141
1142     /* set and check */
1143     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
1144     ok(hr == S_OK, "got %08x\n", hr);
1145
1146     b = VARIANT_FALSE;
1147     hr = IMXWriter_get_standalone(writer, &b);
1148     ok(hr == S_OK, "got %08x\n", hr);
1149     ok(b == VARIANT_TRUE, "got %d\n", b);
1150
1151     hr = IMXWriter_get_encoding(writer, NULL);
1152     ok(hr == E_POINTER, "got %08x\n", hr);
1153
1154     /* UTF-16 is a default setting apparently */
1155     str = (void*)0xdeadbeef;
1156     hr = IMXWriter_get_encoding(writer, &str);
1157     ok(hr == S_OK, "got %08x\n", hr);
1158     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1159
1160     str2 = (void*)0xdeadbeef;
1161     hr = IMXWriter_get_encoding(writer, &str2);
1162     ok(hr == S_OK, "got %08x\n", hr);
1163     ok(str != str2, "expected newly allocated, got same %p\n", str);
1164
1165     SysFreeString(str2);
1166     SysFreeString(str);
1167
1168     /* put empty string */
1169     str = SysAllocString(emptyW);
1170     hr = IMXWriter_put_encoding(writer, str);
1171     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1172     SysFreeString(str);
1173
1174     str = (void*)0xdeadbeef;
1175     hr = IMXWriter_get_encoding(writer, &str);
1176     ok(hr == S_OK, "got %08x\n", hr);
1177     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1178     SysFreeString(str);
1179
1180     /* invalid encoding name */
1181     str = SysAllocString(testW);
1182     hr = IMXWriter_put_encoding(writer, str);
1183     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1184     SysFreeString(str);
1185
1186     hr = IMXWriter_get_version(writer, NULL);
1187     ok(hr == E_POINTER, "got %08x\n", hr);
1188     /* default version is 'surprisingly' 1.0 */
1189     hr = IMXWriter_get_version(writer, &str);
1190     ok(hr == S_OK, "got %08x\n", hr);
1191     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
1192     SysFreeString(str);
1193
1194     /* store version string as is */
1195     hr = IMXWriter_put_version(writer, NULL);
1196     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1197
1198     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
1199     ok(hr == S_OK, "got %08x\n", hr);
1200
1201     hr = IMXWriter_put_version(writer, _bstr_(""));
1202     ok(hr == S_OK, "got %08x\n", hr);
1203     hr = IMXWriter_get_version(writer, &str);
1204     ok(hr == S_OK, "got %08x\n", hr);
1205     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
1206     SysFreeString(str);
1207
1208     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
1209     ok(hr == S_OK, "got %08x\n", hr);
1210     hr = IMXWriter_get_version(writer, &str);
1211     ok(hr == S_OK, "got %08x\n", hr);
1212     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
1213     SysFreeString(str);
1214
1215     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
1216     ok(hr == S_OK, "got %08x\n", hr);
1217     hr = IMXWriter_get_version(writer, &str);
1218     ok(hr == S_OK, "got %08x\n", hr);
1219     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
1220     SysFreeString(str);
1221
1222     IMXWriter_Release(writer);
1223     free_bstrs();
1224 }
1225
1226 static void test_mxwriter_flush(void)
1227 {
1228     ISAXContentHandler *content;
1229     IMXWriter *writer;
1230     LARGE_INTEGER pos;
1231     ULARGE_INTEGER pos2;
1232     IStream *stream;
1233     VARIANT dest;
1234     HRESULT hr;
1235
1236     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1237             &IID_IMXWriter, (void**)&writer);
1238     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1239
1240     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1241     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1242     EXPECT_REF(stream, 1);
1243
1244     /* detach when nothing was attached */
1245     V_VT(&dest) = VT_EMPTY;
1246     hr = IMXWriter_put_output(writer, dest);
1247     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1248
1249     /* attach stream */
1250     V_VT(&dest) = VT_UNKNOWN;
1251     V_UNKNOWN(&dest) = (IUnknown*)stream;
1252     hr = IMXWriter_put_output(writer, dest);
1253     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1254     todo_wine EXPECT_REF(stream, 3);
1255
1256     /* detach setting VT_EMPTY destination */
1257     V_VT(&dest) = VT_EMPTY;
1258     hr = IMXWriter_put_output(writer, dest);
1259     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1260     EXPECT_REF(stream, 1);
1261
1262     V_VT(&dest) = VT_UNKNOWN;
1263     V_UNKNOWN(&dest) = (IUnknown*)stream;
1264     hr = IMXWriter_put_output(writer, dest);
1265     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1266
1267     /* flush() doesn't detach a stream */
1268     hr = IMXWriter_flush(writer);
1269     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1270     todo_wine EXPECT_REF(stream, 3);
1271
1272     pos.QuadPart = 0;
1273     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1274     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1275     ok(pos2.QuadPart == 0, "expected stream beginning\n");
1276
1277     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1278     ok(hr == S_OK, "got %08x\n", hr);
1279
1280     hr = ISAXContentHandler_startDocument(content);
1281     ok(hr == S_OK, "got %08x\n", hr);
1282
1283     pos.QuadPart = 0;
1284     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1285     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1286     ok(pos2.QuadPart != 0, "expected stream beginning\n");
1287
1288     /* already started */
1289     hr = ISAXContentHandler_startDocument(content);
1290     ok(hr == S_OK, "got %08x\n", hr);
1291
1292     hr = ISAXContentHandler_endDocument(content);
1293     ok(hr == S_OK, "got %08x\n", hr);
1294
1295     /* flushed on endDocument() */
1296     pos.QuadPart = 0;
1297     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1298     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1299     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1300
1301     ISAXContentHandler_Release(content);
1302     IStream_Release(stream);
1303     IMXWriter_Release(writer);
1304 }
1305
1306 static void test_mxwriter_startenddocument(void)
1307 {
1308     ISAXContentHandler *content;
1309     IMXWriter *writer;
1310     VARIANT dest;
1311     HRESULT hr;
1312
1313     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1314             &IID_IMXWriter, (void**)&writer);
1315     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1316
1317     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1318     ok(hr == S_OK, "got %08x\n", hr);
1319
1320     hr = ISAXContentHandler_startDocument(content);
1321     ok(hr == S_OK, "got %08x\n", hr);
1322
1323     hr = ISAXContentHandler_endDocument(content);
1324     ok(hr == S_OK, "got %08x\n", hr);
1325
1326     V_VT(&dest) = VT_EMPTY;
1327     hr = IMXWriter_get_output(writer, &dest);
1328     ok(hr == S_OK, "got %08x\n", hr);
1329     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1330     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1331         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1332     VariantClear(&dest);
1333
1334     /* now try another startDocument */
1335     hr = ISAXContentHandler_startDocument(content);
1336     ok(hr == S_OK, "got %08x\n", hr);
1337     /* and get duplicated prolog */
1338     V_VT(&dest) = VT_EMPTY;
1339     hr = IMXWriter_get_output(writer, &dest);
1340     ok(hr == S_OK, "got %08x\n", hr);
1341     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1342     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
1343                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1344         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1345     VariantClear(&dest);
1346
1347     ISAXContentHandler_Release(content);
1348     IMXWriter_Release(writer);
1349
1350     /* now with omitted declaration */
1351     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1352             &IID_IMXWriter, (void**)&writer);
1353     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1354
1355     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1356     ok(hr == S_OK, "got %08x\n", hr);
1357
1358     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1359     ok(hr == S_OK, "got %08x\n", hr);
1360
1361     hr = ISAXContentHandler_startDocument(content);
1362     ok(hr == S_OK, "got %08x\n", hr);
1363
1364     hr = ISAXContentHandler_endDocument(content);
1365     ok(hr == S_OK, "got %08x\n", hr);
1366
1367     V_VT(&dest) = VT_EMPTY;
1368     hr = IMXWriter_get_output(writer, &dest);
1369     ok(hr == S_OK, "got %08x\n", hr);
1370     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1371     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1372     VariantClear(&dest);
1373
1374     ISAXContentHandler_Release(content);
1375     IMXWriter_Release(writer);
1376
1377     free_bstrs();
1378 }
1379
1380 static void test_mxwriter_startendelement(void)
1381 {
1382     static const char winehqA[] = "http://winehq.org";
1383     ISAXContentHandler *content;
1384     IMXWriter *writer;
1385     VARIANT dest;
1386     HRESULT hr;
1387
1388     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1389             &IID_IMXWriter, (void**)&writer);
1390     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1391
1392     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1393     ok(hr == S_OK, "got %08x\n", hr);
1394
1395     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1396     ok(hr == S_OK, "got %08x\n", hr);
1397
1398     hr = ISAXContentHandler_startDocument(content);
1399     ok(hr == S_OK, "got %08x\n", hr);
1400
1401     /* qualified name without defined namespace */
1402     hr = ISAXContentHandler_startElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3, NULL);
1403     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1404
1405     hr = ISAXContentHandler_startElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3, NULL);
1406     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1407
1408     /* only local name is an error too */
1409     hr = ISAXContentHandler_startElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0, NULL);
1410     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1411
1412     /* only local name is an error too */
1413     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, NULL, 0, NULL);
1414     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1415
1416     /* all string pointers should be not null */
1417     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
1418     ok(hr == S_OK, "got %08x\n", hr);
1419
1420     V_VT(&dest) = VT_EMPTY;
1421     hr = IMXWriter_get_output(writer, &dest);
1422     ok(hr == S_OK, "got %08x\n", hr);
1423     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1424     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1425     VariantClear(&dest);
1426
1427     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
1428     ok(hr == S_OK, "got %08x\n", hr);
1429
1430     V_VT(&dest) = VT_EMPTY;
1431     hr = IMXWriter_get_output(writer, &dest);
1432     ok(hr == S_OK, "got %08x\n", hr);
1433     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1434     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1435     VariantClear(&dest);
1436
1437     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
1438     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1439
1440     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
1441     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1442
1443     /* only local name is an error too */
1444     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
1445     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1446
1447     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
1448     ok(hr == S_OK, "got %08x\n", hr);
1449
1450     V_VT(&dest) = VT_EMPTY;
1451     hr = IMXWriter_get_output(writer, &dest);
1452     ok(hr == S_OK, "got %08x\n", hr);
1453     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1454     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1455     VariantClear(&dest);
1456
1457     /* some with namespace URI */
1458     hr = ISAXContentHandler_startElement(content, _bstr_(winehqA), sizeof(winehqA), _bstr_(""), 0, _bstr_("nspace:c"), 8, NULL);
1459     ok(hr == S_OK, "got %08x\n", hr);
1460
1461     hr = ISAXContentHandler_endElement(content, _bstr_(winehqA), sizeof(winehqA), _bstr_(""), 0, _bstr_("nspace:c"), 8);
1462     ok(hr == S_OK, "got %08x\n", hr);
1463
1464     V_VT(&dest) = VT_EMPTY;
1465     hr = IMXWriter_get_output(writer, &dest);
1466     ok(hr == S_OK, "got %08x\n", hr);
1467     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1468     todo_wine ok(!lstrcmpW(_bstr_("<><b></b><nspace:c/>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1469     VariantClear(&dest);
1470
1471     /* try to end element that wasn't open */
1472     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
1473     ok(hr == S_OK, "got %08x\n", hr);
1474
1475     V_VT(&dest) = VT_EMPTY;
1476     hr = IMXWriter_get_output(writer, &dest);
1477     ok(hr == S_OK, "got %08x\n", hr);
1478     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1479     todo_wine ok(!lstrcmpW(_bstr_("<><b></b><nspace:c/></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1480     VariantClear(&dest);
1481
1482     /* try with attributes */
1483     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, &saxattributes);
1484     ok(hr == S_OK, "got %08x\n", hr);
1485
1486     hr = ISAXContentHandler_endDocument(content);
1487     ok(hr == S_OK, "got %08x\n", hr);
1488
1489     ISAXContentHandler_Release(content);
1490     IMXWriter_Release(writer);
1491
1492     free_bstrs();
1493 }
1494
1495 static void test_mxwriter_characters(void)
1496 {
1497     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
1498     ISAXContentHandler *content;
1499     IMXWriter *writer;
1500     VARIANT dest;
1501     HRESULT hr;
1502
1503     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1504             &IID_IMXWriter, (void**)&writer);
1505     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1506
1507     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1508     ok(hr == S_OK, "got %08x\n", hr);
1509
1510     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1511     ok(hr == S_OK, "got %08x\n", hr);
1512
1513     hr = ISAXContentHandler_startDocument(content);
1514     ok(hr == S_OK, "got %08x\n", hr);
1515
1516     hr = ISAXContentHandler_characters(content, NULL, 0);
1517     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1518
1519     hr = ISAXContentHandler_characters(content, chardataW, 0);
1520     ok(hr == S_OK, "got %08x\n", hr);
1521
1522     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
1523     ok(hr == S_OK, "got %08x\n", hr);
1524
1525     V_VT(&dest) = VT_EMPTY;
1526     hr = IMXWriter_get_output(writer, &dest);
1527     ok(hr == S_OK, "got %08x\n", hr);
1528     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1529     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1530     VariantClear(&dest);
1531
1532     hr = ISAXContentHandler_endDocument(content);
1533     ok(hr == S_OK, "got %08x\n", hr);
1534
1535     ISAXContentHandler_Release(content);
1536     IMXWriter_Release(writer);
1537
1538     free_bstrs();
1539 }
1540
1541 static const mxwriter_stream_test mxwriter_stream_tests[] = {
1542     {
1543         VARIANT_TRUE,"UTF-16",
1544         {
1545             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
1546             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1547             {TRUE}
1548         }
1549     },
1550     {
1551         VARIANT_FALSE,"UTF-16",
1552         {
1553             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1554             {TRUE}
1555         }
1556     },
1557     {
1558         VARIANT_TRUE,"UTF-8",
1559         {
1560             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
1561             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
1562              * and the writer is released.
1563              */
1564             {FALSE,NULL,0},
1565             {TRUE}
1566         }
1567     },
1568     {
1569         VARIANT_TRUE,"UTF-16",
1570         {
1571             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
1572             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1573             {TRUE}
1574         }
1575     },
1576     {
1577         VARIANT_TRUE,"UTF-16",
1578         {
1579             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
1580             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1581             {TRUE}
1582         }
1583     }
1584 };
1585
1586 static void test_mxwriter_stream(void)
1587 {
1588     IMXWriter *writer;
1589     ISAXContentHandler *content;
1590     HRESULT hr;
1591     VARIANT dest;
1592     IStream *stream;
1593     LARGE_INTEGER pos;
1594     ULARGE_INTEGER pos2;
1595     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
1596
1597     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
1598         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
1599
1600         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1601                 &IID_IMXWriter, (void**)&writer);
1602         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
1603
1604         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1605         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
1606
1607         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
1608         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
1609
1610         V_VT(&dest) = VT_UNKNOWN;
1611         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
1612         hr = IMXWriter_put_output(writer, dest);
1613         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
1614         VariantClear(&dest);
1615
1616         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
1617         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
1618
1619         current_write_test = test->expected_writes;
1620
1621         hr = ISAXContentHandler_startDocument(content);
1622         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
1623
1624         hr = ISAXContentHandler_endDocument(content);
1625         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
1626
1627         ISAXContentHandler_Release(content);
1628         IMXWriter_Release(writer);
1629
1630         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
1631             current_write_test-test->expected_writes, current_stream_test_index);
1632     }
1633
1634     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1635             &IID_IMXWriter, (void**)&writer);
1636     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
1637
1638     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1639     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
1640
1641     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1642     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
1643
1644     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
1645     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
1646
1647     V_VT(&dest) = VT_UNKNOWN;
1648     V_UNKNOWN(&dest) = (IUnknown*)stream;
1649     hr = IMXWriter_put_output(writer, dest);
1650     ok(hr == S_OK, "put_output failed: %08x\n", hr);
1651
1652     hr = ISAXContentHandler_startDocument(content);
1653     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
1654
1655     /* Setting output of the mxwriter causes the current output to be flushed,
1656      * and the writer to start over.
1657      */
1658     V_VT(&dest) = VT_EMPTY;
1659     hr = IMXWriter_put_output(writer, dest);
1660     ok(hr == S_OK, "put_output failed: %08x\n", hr);
1661
1662     pos.QuadPart = 0;
1663     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1664     ok(hr == S_OK, "Seek failed: %08x\n", hr);
1665     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1666
1667     hr = ISAXContentHandler_startDocument(content);
1668     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
1669
1670     hr = ISAXContentHandler_endDocument(content);
1671     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
1672
1673     V_VT(&dest) = VT_EMPTY;
1674     hr = IMXWriter_get_output(writer, &dest);
1675     ok(hr == S_OK, "get_output failed: %08x\n", hr);
1676     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
1677     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1678             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1679     VariantClear(&dest);
1680
1681     ISAXContentHandler_Release(content);
1682     IMXWriter_Release(writer);
1683
1684     free_bstrs();
1685 }
1686
1687 static void test_mxwriter_encoding(void)
1688 {
1689     IMXWriter *writer;
1690     ISAXContentHandler *content;
1691     HRESULT hr;
1692     VARIANT dest;
1693
1694     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1695             &IID_IMXWriter, (void**)&writer);
1696     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
1697
1698     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1699     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
1700
1701     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
1702     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
1703
1704     hr = ISAXContentHandler_startDocument(content);
1705     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
1706
1707     hr = ISAXContentHandler_endDocument(content);
1708     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
1709
1710     /* The content is always re-encoded to UTF-16 when the output is
1711      * retrieved as a BSTR.
1712      */
1713     V_VT(&dest) = VT_EMPTY;
1714     hr = IMXWriter_get_output(writer, &dest);
1715     todo_wine ok(hr == S_OK, "get_output failed: %08x\n", hr);
1716     todo_wine ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
1717     todo_wine ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1718             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1719     VariantClear(&dest);
1720
1721     ISAXContentHandler_Release(content);
1722     IMXWriter_Release(writer);
1723
1724     free_bstrs();
1725 }
1726
1727 START_TEST(saxreader)
1728 {
1729     ISAXXMLReader *reader;
1730     IMXWriter *writer;
1731     HRESULT hr;
1732
1733     hr = CoInitialize(NULL);
1734     ok(hr == S_OK, "failed to init com\n");
1735
1736     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
1737             &IID_ISAXXMLReader, (void**)&reader);
1738
1739     if(FAILED(hr))
1740     {
1741         skip("Failed to create SAXXMLReader instance\n");
1742         CoUninitialize();
1743         return;
1744     }
1745     ISAXXMLReader_Release(reader);
1746
1747     test_saxreader();
1748     test_encoding();
1749
1750     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1751             &IID_IMXWriter, (void**)&writer);
1752     if (hr == S_OK)
1753     {
1754         IMXWriter_Release(writer);
1755
1756         test_mxwriter_contenthandler();
1757         test_mxwriter_startenddocument();
1758         test_mxwriter_startendelement();
1759         test_mxwriter_characters();
1760         test_mxwriter_properties();
1761         test_mxwriter_flush();
1762         test_mxwriter_stream();
1763         test_mxwriter_encoding();
1764     }
1765     else
1766         win_skip("MXXMLWriter not supported\n");
1767
1768     CoUninitialize();
1769 }