dinput: Fix printing NULL strings.
[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 typedef struct mxwriter_write_test_t {
715     BOOL        last;
716     const BYTE  *data;
717     DWORD       cb;
718     BOOL        null_written;
719     BOOL        fail_write;
720 } mxwriter_write_test;
721
722 typedef struct mxwriter_stream_test_t {
723     VARIANT_BOOL        bom;
724     const char          *encoding;
725     mxwriter_write_test expected_writes[4];
726 } mxwriter_stream_test;
727
728 static const mxwriter_write_test *current_write_test;
729 static DWORD current_stream_test_index;
730
731 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
732 {
733     *ppvObject = NULL;
734
735     if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
736         *ppvObject = iface;
737     else
738         return E_NOINTERFACE;
739
740     return S_OK;
741 }
742
743 static ULONG WINAPI istream_AddRef(IStream *iface)
744 {
745     return 2;
746 }
747
748 static ULONG WINAPI istream_Release(IStream *iface)
749 {
750     return 1;
751 }
752
753 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
754 {
755     ok(0, "unexpected call\n");
756     return E_NOTIMPL;
757 }
758
759 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
760 {
761     BOOL fail = FALSE;
762
763     ok(pv != NULL, "pv == NULL\n");
764
765     if(current_write_test->last) {
766         ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
767         return E_FAIL;
768     }
769
770     fail = current_write_test->fail_write;
771
772     ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
773         current_write_test->cb, cb, current_stream_test_index);
774
775     if(!pcbWritten)
776         ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
777     else
778         ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
779
780     ++current_write_test;
781
782     if(pcbWritten)
783         *pcbWritten = cb;
784
785     return fail ? E_FAIL : S_OK;
786 }
787
788 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
789         ULARGE_INTEGER *plibNewPosition)
790 {
791     ok(0, "unexpected call\n");
792     return E_NOTIMPL;
793 }
794
795 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
796 {
797     ok(0, "unexpected call\n");
798     return E_NOTIMPL;
799 }
800
801 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
802         ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
803 {
804     ok(0, "unexpected call\n");
805     return E_NOTIMPL;
806 }
807
808 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
809 {
810     ok(0, "unexpected call\n");
811     return E_NOTIMPL;
812 }
813
814 static HRESULT WINAPI istream_Revert(IStream *iface)
815 {
816     ok(0, "unexpected call\n");
817     return E_NOTIMPL;
818 }
819
820 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
821         ULARGE_INTEGER cb, DWORD dwLockType)
822 {
823     ok(0, "unexpected call\n");
824     return E_NOTIMPL;
825 }
826
827 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
828         ULARGE_INTEGER cb, DWORD dwLockType)
829 {
830     ok(0, "unexpected call\n");
831     return E_NOTIMPL;
832 }
833
834 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
835 {
836     ok(0, "unexpected call\n");
837     return E_NOTIMPL;
838 }
839
840 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
841 {
842     ok(0, "unexpected call\n");
843     return E_NOTIMPL;
844 }
845
846 static const IStreamVtbl StreamVtbl = {
847     istream_QueryInterface,
848     istream_AddRef,
849     istream_Release,
850     istream_Read,
851     istream_Write,
852     istream_Seek,
853     istream_SetSize,
854     istream_CopyTo,
855     istream_Commit,
856     istream_Revert,
857     istream_LockRegion,
858     istream_UnlockRegion,
859     istream_Stat,
860     istream_Clone
861 };
862
863 static IStream mxstream = { &StreamVtbl };
864
865 static void test_saxreader(void)
866 {
867     HRESULT hr;
868     ISAXXMLReader *reader = NULL;
869     VARIANT var;
870     ISAXContentHandler *lpContentHandler;
871     ISAXErrorHandler *lpErrorHandler;
872     SAFEARRAY *pSA;
873     SAFEARRAYBOUND SADim[1];
874     char *pSAData = NULL;
875     IStream *iStream;
876     ULARGE_INTEGER liSize;
877     LARGE_INTEGER liPos;
878     ULONG bytesWritten;
879     HANDLE file;
880     static const CHAR testXmlA[] = "test.xml";
881     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
882     IXMLDOMDocument *domDocument;
883     BSTR bstrData;
884     VARIANT_BOOL vBool;
885
886     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
887             &IID_ISAXXMLReader, (LPVOID*)&reader);
888     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
889
890     hr = ISAXXMLReader_getContentHandler(reader, NULL);
891     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
892
893     hr = ISAXXMLReader_getErrorHandler(reader, NULL);
894     ok(hr == E_POINTER, "Expected E_POINTER, got %08x\n", hr);
895
896     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
897     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
898     ok(lpContentHandler == NULL, "Expected %p, got %p\n", NULL, lpContentHandler);
899
900     hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
901     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
902     ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
903
904     hr = ISAXXMLReader_putContentHandler(reader, NULL);
905     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
906
907     hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
908     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
909
910     hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
911     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
912
913     hr = ISAXXMLReader_getContentHandler(reader, &lpContentHandler);
914     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
915     ok(lpContentHandler == &contentHandler, "Expected %p, got %p\n", &contentHandler, lpContentHandler);
916
917     V_VT(&var) = VT_BSTR;
918     V_BSTR(&var) = SysAllocString(szSimpleXML);
919
920     expectCall = contentHandlerTest1;
921     hr = ISAXXMLReader_parse(reader, var);
922     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
923     test_expect_call(CH_ENDTEST);
924
925     VariantClear(&var);
926
927     SADim[0].lLbound= 0;
928     SADim[0].cElements= sizeof(szTestXML)-1;
929     pSA = SafeArrayCreate(VT_UI1, 1, SADim);
930     SafeArrayAccessData(pSA, (void**)&pSAData);
931     memcpy(pSAData, szTestXML, sizeof(szTestXML)-1);
932     SafeArrayUnaccessData(pSA);
933     V_VT(&var) = VT_ARRAY|VT_UI1;
934     V_ARRAY(&var) = pSA;
935
936     expectCall = contentHandlerTest1;
937     hr = ISAXXMLReader_parse(reader, var);
938     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
939     test_expect_call(CH_ENDTEST);
940
941     SafeArrayDestroy(pSA);
942
943     CreateStreamOnHGlobal(NULL, TRUE, &iStream);
944     liSize.QuadPart = strlen(szTestXML);
945     IStream_SetSize(iStream, liSize);
946     IStream_Write(iStream, szTestXML, strlen(szTestXML), &bytesWritten);
947     liPos.QuadPart = 0;
948     IStream_Seek(iStream, liPos, STREAM_SEEK_SET, NULL);
949     V_VT(&var) = VT_UNKNOWN|VT_DISPATCH;
950     V_UNKNOWN(&var) = (IUnknown*)iStream;
951
952     expectCall = contentHandlerTest1;
953     hr = ISAXXMLReader_parse(reader, var);
954     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
955     test_expect_call(CH_ENDTEST);
956
957     IStream_Release(iStream);
958
959     V_VT(&var) = VT_BSTR;
960     V_BSTR(&var) = SysAllocString(szCarriageRetTest);
961
962     expectCall = contentHandlerTest2;
963     hr = ISAXXMLReader_parse(reader, var);
964     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
965     test_expect_call(CH_ENDTEST);
966
967     VariantClear(&var);
968
969     file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
970     ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
971     WriteFile(file, szTestXML, sizeof(szTestXML)-1, &bytesWritten, NULL);
972     CloseHandle(file);
973
974     expectCall = contentHandlerTest1;
975     hr = ISAXXMLReader_parseURL(reader, testXmlW);
976     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
977     test_expect_call(CH_ENDTEST);
978
979     DeleteFileA(testXmlA);
980
981     hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
982             &IID_IXMLDOMDocument, (LPVOID*)&domDocument);
983     if(FAILED(hr))
984     {
985         skip("Failed to create DOMDocument instance\n");
986         return;
987     }
988     bstrData = SysAllocString(szSimpleXML);
989     hr = IXMLDOMDocument_loadXML(domDocument, bstrData, &vBool);
990     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
991     V_VT(&var) = VT_UNKNOWN;
992     V_UNKNOWN(&var) = (IUnknown*)domDocument;
993
994     expectCall = contentHandlerTest2;
995     hr = ISAXXMLReader_parse(reader, var);
996     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
997     test_expect_call(CH_ENDTEST);
998     IXMLDOMDocument_Release(domDocument);
999
1000     ISAXXMLReader_Release(reader);
1001     SysFreeString(bstrData);
1002 }
1003
1004 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
1005 static const CHAR UTF8BOMTest[] =
1006 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
1007 "<a></a>\n";
1008
1009 struct enc_test_entry_t {
1010     const GUID *guid;
1011     const char *clsid;
1012     const char *data;
1013     HRESULT hr;
1014     int todo;
1015 };
1016
1017 static const struct enc_test_entry_t encoding_test_data[] = {
1018     { &CLSID_SAXXMLReader,   "CLSID_SAXXMLReader",   UTF8BOMTest, 0xc00ce56f, 1 },
1019     { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
1020     { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
1021     { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
1022     { 0 }
1023 };
1024
1025 static void test_encoding(void)
1026 {
1027     const struct enc_test_entry_t *entry = encoding_test_data;
1028     static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1029     static const CHAR testXmlA[] = "test.xml";
1030     ISAXXMLReader *reader;
1031     DWORD written;
1032     HANDLE file;
1033     HRESULT hr;
1034
1035     while (entry->guid)
1036     {
1037         hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1038         if (hr != S_OK)
1039         {
1040             win_skip("can't create %s instance\n", entry->clsid);
1041             entry++;
1042             continue;
1043         }
1044
1045         file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
1046         ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
1047         WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
1048         CloseHandle(file);
1049
1050         hr = ISAXXMLReader_parseURL(reader, testXmlW);
1051         if (entry->todo)
1052             todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1053         else
1054             ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
1055
1056         DeleteFileA(testXmlA);
1057         ISAXXMLReader_Release(reader);
1058
1059         entry++;
1060     }
1061 }
1062
1063 static void test_mxwriter_contenthandler(void)
1064 {
1065     ISAXContentHandler *handler;
1066     IMXWriter *writer, *writer2;
1067     HRESULT hr;
1068
1069     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1070             &IID_IMXWriter, (void**)&writer);
1071     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1072
1073     EXPECT_REF(writer, 1);
1074
1075     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
1076     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1077     EXPECT_REF(writer, 2);
1078     EXPECT_REF(handler, 2);
1079
1080     hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
1081     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1082     ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
1083     EXPECT_REF(writer, 3);
1084     EXPECT_REF(writer2, 3);
1085     IMXWriter_Release(writer2);
1086
1087     ISAXContentHandler_Release(handler);
1088     IMXWriter_Release(writer);
1089 }
1090
1091 struct msxmlsupported_data_t
1092 {
1093     const GUID *clsid;
1094     const char *name;
1095     BOOL supported;
1096 };
1097
1098 static struct msxmlsupported_data_t msxmlsupported_data[] =
1099 {
1100     { &CLSID_MXXMLWriter,   "MXXMLWriter" },
1101     { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
1102     { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
1103     { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
1104     { NULL }
1105 };
1106
1107 static BOOL is_mxwriter_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
1108 {
1109     while (table->clsid)
1110     {
1111         if (table->clsid == clsid) return table->supported;
1112         table++;
1113     }
1114     return FALSE;
1115 }
1116
1117 struct mxwriter_props_t
1118 {
1119     const GUID *clsid;
1120     VARIANT_BOOL bom;
1121     VARIANT_BOOL disable_escape;
1122     VARIANT_BOOL indent;
1123     VARIANT_BOOL omitdecl;
1124     VARIANT_BOOL standalone;
1125     const char *encoding;
1126 };
1127
1128 static const struct mxwriter_props_t mxwriter_default_props[] =
1129 {
1130     { &CLSID_MXXMLWriter,   VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1131     { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1132     { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1133     { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
1134     { NULL }
1135 };
1136
1137 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
1138 {
1139     int i = 0;
1140
1141     while (table->clsid)
1142     {
1143         IMXWriter *writer;
1144         VARIANT_BOOL b;
1145         BSTR encoding;
1146         HRESULT hr;
1147
1148         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1149         {
1150             table++;
1151             i++;
1152             continue;
1153         }
1154
1155         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1156             &IID_IMXWriter, (void**)&writer);
1157         EXPECT_HR(hr, S_OK);
1158
1159         b = !table->bom;
1160         hr = IMXWriter_get_byteOrderMark(writer, &b);
1161         EXPECT_HR(hr, S_OK);
1162         ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
1163
1164         b = !table->disable_escape;
1165         hr = IMXWriter_get_disableOutputEscaping(writer, &b);
1166         EXPECT_HR(hr, S_OK);
1167         ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
1168            table->disable_escape);
1169
1170         b = !table->indent;
1171         hr = IMXWriter_get_indent(writer, &b);
1172         EXPECT_HR(hr, S_OK);
1173         ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
1174
1175         b = !table->omitdecl;
1176         hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
1177         EXPECT_HR(hr, S_OK);
1178         ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
1179
1180         b = !table->standalone;
1181         hr = IMXWriter_get_standalone(writer, &b);
1182         EXPECT_HR(hr, S_OK);
1183         ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
1184
1185         hr = IMXWriter_get_encoding(writer, &encoding);
1186         EXPECT_HR(hr, S_OK);
1187         ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
1188             i, wine_dbgstr_w(encoding), table->encoding);
1189         SysFreeString(encoding);
1190
1191         IMXWriter_Release(writer);
1192
1193         table++;
1194         i++;
1195     }
1196 }
1197
1198 static void test_mxwriter_properties(void)
1199 {
1200     static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
1201     static const WCHAR emptyW[] = {0};
1202     static const WCHAR testW[] = {'t','e','s','t',0};
1203     IMXWriter *writer;
1204     VARIANT_BOOL b;
1205     HRESULT hr;
1206     BSTR str, str2;
1207
1208     test_mxwriter_default_properties(mxwriter_default_props);
1209
1210     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1211             &IID_IMXWriter, (void**)&writer);
1212     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1213
1214     hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
1215     ok(hr == E_POINTER, "got %08x\n", hr);
1216
1217     hr = IMXWriter_get_byteOrderMark(writer, NULL);
1218     ok(hr == E_POINTER, "got %08x\n", hr);
1219
1220     hr = IMXWriter_get_indent(writer, NULL);
1221     ok(hr == E_POINTER, "got %08x\n", hr);
1222
1223     hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
1224     ok(hr == E_POINTER, "got %08x\n", hr);
1225
1226     hr = IMXWriter_get_standalone(writer, NULL);
1227     ok(hr == E_POINTER, "got %08x\n", hr);
1228
1229     /* set and check */
1230     hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
1231     ok(hr == S_OK, "got %08x\n", hr);
1232
1233     b = VARIANT_FALSE;
1234     hr = IMXWriter_get_standalone(writer, &b);
1235     ok(hr == S_OK, "got %08x\n", hr);
1236     ok(b == VARIANT_TRUE, "got %d\n", b);
1237
1238     hr = IMXWriter_get_encoding(writer, NULL);
1239     ok(hr == E_POINTER, "got %08x\n", hr);
1240
1241     /* UTF-16 is a default setting apparently */
1242     str = (void*)0xdeadbeef;
1243     hr = IMXWriter_get_encoding(writer, &str);
1244     ok(hr == S_OK, "got %08x\n", hr);
1245     ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1246
1247     str2 = (void*)0xdeadbeef;
1248     hr = IMXWriter_get_encoding(writer, &str2);
1249     ok(hr == S_OK, "got %08x\n", hr);
1250     ok(str != str2, "expected newly allocated, got same %p\n", str);
1251
1252     SysFreeString(str2);
1253     SysFreeString(str);
1254
1255     /* put empty string */
1256     str = SysAllocString(emptyW);
1257     hr = IMXWriter_put_encoding(writer, str);
1258     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1259     SysFreeString(str);
1260
1261     str = (void*)0xdeadbeef;
1262     hr = IMXWriter_get_encoding(writer, &str);
1263     ok(hr == S_OK, "got %08x\n", hr);
1264     ok(!lstrcmpW(str, emptyW) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
1265     SysFreeString(str);
1266
1267     /* invalid encoding name */
1268     str = SysAllocString(testW);
1269     hr = IMXWriter_put_encoding(writer, str);
1270     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1271     SysFreeString(str);
1272
1273     hr = IMXWriter_get_version(writer, NULL);
1274     ok(hr == E_POINTER, "got %08x\n", hr);
1275     /* default version is 'surprisingly' 1.0 */
1276     hr = IMXWriter_get_version(writer, &str);
1277     ok(hr == S_OK, "got %08x\n", hr);
1278     ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
1279     SysFreeString(str);
1280
1281     /* store version string as is */
1282     hr = IMXWriter_put_version(writer, NULL);
1283     ok(hr == E_INVALIDARG, "got %08x\n", hr);
1284
1285     hr = IMXWriter_put_version(writer, _bstr_("1.0"));
1286     ok(hr == S_OK, "got %08x\n", hr);
1287
1288     hr = IMXWriter_put_version(writer, _bstr_(""));
1289     ok(hr == S_OK, "got %08x\n", hr);
1290     hr = IMXWriter_get_version(writer, &str);
1291     ok(hr == S_OK, "got %08x\n", hr);
1292     ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
1293     SysFreeString(str);
1294
1295     hr = IMXWriter_put_version(writer, _bstr_("a.b"));
1296     ok(hr == S_OK, "got %08x\n", hr);
1297     hr = IMXWriter_get_version(writer, &str);
1298     ok(hr == S_OK, "got %08x\n", hr);
1299     ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
1300     SysFreeString(str);
1301
1302     hr = IMXWriter_put_version(writer, _bstr_("2.0"));
1303     ok(hr == S_OK, "got %08x\n", hr);
1304     hr = IMXWriter_get_version(writer, &str);
1305     ok(hr == S_OK, "got %08x\n", hr);
1306     ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
1307     SysFreeString(str);
1308
1309     IMXWriter_Release(writer);
1310     free_bstrs();
1311 }
1312
1313 static void test_mxwriter_flush(void)
1314 {
1315     ISAXContentHandler *content;
1316     IMXWriter *writer;
1317     LARGE_INTEGER pos;
1318     ULARGE_INTEGER pos2;
1319     IStream *stream;
1320     VARIANT dest;
1321     HRESULT hr;
1322
1323     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1324             &IID_IMXWriter, (void**)&writer);
1325     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1326
1327     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1328     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1329     EXPECT_REF(stream, 1);
1330
1331     /* detach when nothing was attached */
1332     V_VT(&dest) = VT_EMPTY;
1333     hr = IMXWriter_put_output(writer, dest);
1334     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1335
1336     /* attach stream */
1337     V_VT(&dest) = VT_UNKNOWN;
1338     V_UNKNOWN(&dest) = (IUnknown*)stream;
1339     hr = IMXWriter_put_output(writer, dest);
1340     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1341     todo_wine EXPECT_REF(stream, 3);
1342
1343     /* detach setting VT_EMPTY destination */
1344     V_VT(&dest) = VT_EMPTY;
1345     hr = IMXWriter_put_output(writer, dest);
1346     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1347     EXPECT_REF(stream, 1);
1348
1349     V_VT(&dest) = VT_UNKNOWN;
1350     V_UNKNOWN(&dest) = (IUnknown*)stream;
1351     hr = IMXWriter_put_output(writer, dest);
1352     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1353
1354     /* flush() doesn't detach a stream */
1355     hr = IMXWriter_flush(writer);
1356     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1357     todo_wine EXPECT_REF(stream, 3);
1358
1359     pos.QuadPart = 0;
1360     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1361     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1362     ok(pos2.QuadPart == 0, "expected stream beginning\n");
1363
1364     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1365     ok(hr == S_OK, "got %08x\n", hr);
1366
1367     hr = ISAXContentHandler_startDocument(content);
1368     ok(hr == S_OK, "got %08x\n", hr);
1369
1370     pos.QuadPart = 0;
1371     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1372     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1373     ok(pos2.QuadPart != 0, "expected stream beginning\n");
1374
1375     /* already started */
1376     hr = ISAXContentHandler_startDocument(content);
1377     ok(hr == S_OK, "got %08x\n", hr);
1378
1379     hr = ISAXContentHandler_endDocument(content);
1380     ok(hr == S_OK, "got %08x\n", hr);
1381
1382     /* flushed on endDocument() */
1383     pos.QuadPart = 0;
1384     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1385     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1386     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1387
1388     ISAXContentHandler_Release(content);
1389     IStream_Release(stream);
1390     IMXWriter_Release(writer);
1391 }
1392
1393 static void test_mxwriter_startenddocument(void)
1394 {
1395     ISAXContentHandler *content;
1396     IMXWriter *writer;
1397     VARIANT dest;
1398     HRESULT hr;
1399
1400     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1401             &IID_IMXWriter, (void**)&writer);
1402     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1403
1404     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1405     ok(hr == S_OK, "got %08x\n", hr);
1406
1407     hr = ISAXContentHandler_startDocument(content);
1408     ok(hr == S_OK, "got %08x\n", hr);
1409
1410     hr = ISAXContentHandler_endDocument(content);
1411     ok(hr == S_OK, "got %08x\n", hr);
1412
1413     V_VT(&dest) = VT_EMPTY;
1414     hr = IMXWriter_get_output(writer, &dest);
1415     ok(hr == S_OK, "got %08x\n", hr);
1416     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1417     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1418         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1419     VariantClear(&dest);
1420
1421     /* now try another startDocument */
1422     hr = ISAXContentHandler_startDocument(content);
1423     ok(hr == S_OK, "got %08x\n", hr);
1424     /* and get duplicated prolog */
1425     V_VT(&dest) = VT_EMPTY;
1426     hr = IMXWriter_get_output(writer, &dest);
1427     ok(hr == S_OK, "got %08x\n", hr);
1428     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1429     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
1430                         "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1431         "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1432     VariantClear(&dest);
1433
1434     ISAXContentHandler_Release(content);
1435     IMXWriter_Release(writer);
1436
1437     /* now with omitted declaration */
1438     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1439             &IID_IMXWriter, (void**)&writer);
1440     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1441
1442     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1443     ok(hr == S_OK, "got %08x\n", hr);
1444
1445     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1446     ok(hr == S_OK, "got %08x\n", hr);
1447
1448     hr = ISAXContentHandler_startDocument(content);
1449     ok(hr == S_OK, "got %08x\n", hr);
1450
1451     hr = ISAXContentHandler_endDocument(content);
1452     ok(hr == S_OK, "got %08x\n", hr);
1453
1454     V_VT(&dest) = VT_EMPTY;
1455     hr = IMXWriter_get_output(writer, &dest);
1456     ok(hr == S_OK, "got %08x\n", hr);
1457     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1458     ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1459     VariantClear(&dest);
1460
1461     ISAXContentHandler_Release(content);
1462     IMXWriter_Release(writer);
1463
1464     free_bstrs();
1465 }
1466
1467 enum startendtype
1468 {
1469     StartElement,
1470     EndElement,
1471     StartEndElement
1472 };
1473
1474 struct writer_startendelement_t {
1475     const GUID *clsid;
1476     enum startendtype type;
1477     const char *uri;
1478     const char *local_name;
1479     const char *qname;
1480     const char *output;
1481     HRESULT hr;
1482     ISAXAttributes *attr;
1483 };
1484
1485 static const struct writer_startendelement_t writer_startendelement[] = {
1486     /* 0 */
1487     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1488     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1489     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1490     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
1491     { &CLSID_MXXMLWriter,   StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1492     /* 5 */
1493     { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1494     { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1495     { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
1496     { &CLSID_MXXMLWriter,   StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1497     { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1498     /* 10 */
1499     { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1500     { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
1501     { &CLSID_MXXMLWriter,   StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1502     { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1503     { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1504     /* 15 */
1505     { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
1506     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "qname", "<qname>", S_OK },
1507     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1508     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1509     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
1510     /* 20 */
1511     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1512     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1513     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1514     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
1515     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1516     /* 25 */
1517     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1518     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1519     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
1520     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1521     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1522     /* 30 */
1523     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1524     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
1525     /* endElement tests */
1526     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1527     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1528     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
1529     /* 35 */
1530     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
1531     { &CLSID_MXXMLWriter,   EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1532     { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1533     { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
1534     { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
1535     /* 40 */
1536     { &CLSID_MXXMLWriter,   EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1537     { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1538     { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
1539     { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
1540     { &CLSID_MXXMLWriter,   EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1541     /* 45 */
1542     { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1543     { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
1544     { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
1545     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "qname", "</qname>", S_OK },
1546     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1547     /* 50 */
1548     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1549     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
1550     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1551     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1552     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
1553     /* 55 */
1554     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
1555     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1556     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1557     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1558     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
1559     /* 60 */
1560     { &CLSID_MXXMLWriter,   EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1561     { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1562     { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1563     { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
1564
1565     /* with attributes */
1566     { &CLSID_MXXMLWriter,   StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1567     /* 65 */
1568     { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1569     { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1570     { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\">", S_OK, &saxattributes },
1571     /* empty elements */
1572     { &CLSID_MXXMLWriter,   StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1573     { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1574     /* 70 */
1575     { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1576     { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", "<uri:local a:attr1=\"a1\" attr2=\"a2\"/>", S_OK, &saxattributes },
1577     { &CLSID_MXXMLWriter,   StartEndElement, "", "", "", "</>", S_OK },
1578     { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
1579     { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
1580     /* 75 */
1581     { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
1582     { NULL }
1583 };
1584
1585 static void get_supported_mxwriter_data(struct msxmlsupported_data_t *table)
1586 {
1587     while (table->clsid)
1588     {
1589         IMXWriter *writer;
1590         HRESULT hr;
1591
1592         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1593             &IID_IMXWriter, (void**)&writer);
1594         if (hr == S_OK) IMXWriter_Release(writer);
1595
1596         table->supported = hr == S_OK;
1597         if (hr != S_OK) win_skip("class %s not supported\n", table->name);
1598
1599         table++;
1600     }
1601 }
1602
1603 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
1604 {
1605     int i = 0;
1606
1607     while (table->clsid)
1608     {
1609         ISAXContentHandler *content;
1610         IMXWriter *writer;
1611         HRESULT hr;
1612
1613         if (!is_mxwriter_supported(table->clsid, msxmlsupported_data))
1614         {
1615             table++;
1616             i++;
1617             continue;
1618         }
1619
1620         hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
1621             &IID_IMXWriter, (void**)&writer);
1622         EXPECT_HR(hr, S_OK);
1623
1624         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1625         EXPECT_HR(hr, S_OK);
1626
1627         hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1628         EXPECT_HR(hr, S_OK);
1629
1630         hr = ISAXContentHandler_startDocument(content);
1631         EXPECT_HR(hr, S_OK);
1632
1633         if (table->type == StartElement)
1634         {
1635             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1636                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1637             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1638         }
1639         else if (table->type == EndElement)
1640         {
1641             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1642                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1643             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1644         }
1645         else
1646         {
1647             hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
1648                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
1649             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1650             hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
1651                 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
1652             ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
1653         }
1654
1655         /* test output */
1656         if (hr == S_OK)
1657         {
1658             VARIANT dest;
1659
1660             V_VT(&dest) = VT_EMPTY;
1661             hr = IMXWriter_get_output(writer, &dest);
1662             EXPECT_HR(hr, S_OK);
1663             ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1664             ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
1665                 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
1666             VariantClear(&dest);
1667         }
1668
1669         ISAXContentHandler_Release(content);
1670         IMXWriter_Release(writer);
1671
1672         table++;
1673         i++;
1674     }
1675
1676     free_bstrs();
1677 }
1678
1679 static void test_mxwriter_startendelement(void)
1680 {
1681     ISAXContentHandler *content;
1682     IMXWriter *writer;
1683     VARIANT dest;
1684     HRESULT hr;
1685
1686     test_mxwriter_startendelement_batch(writer_startendelement);
1687
1688     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1689             &IID_IMXWriter, (void**)&writer);
1690     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1691
1692     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1693     ok(hr == S_OK, "got %08x\n", hr);
1694
1695     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1696     ok(hr == S_OK, "got %08x\n", hr);
1697
1698     hr = ISAXContentHandler_startDocument(content);
1699     ok(hr == S_OK, "got %08x\n", hr);
1700
1701     /* all string pointers should be not null */
1702     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
1703     ok(hr == S_OK, "got %08x\n", hr);
1704
1705     V_VT(&dest) = VT_EMPTY;
1706     hr = IMXWriter_get_output(writer, &dest);
1707     ok(hr == S_OK, "got %08x\n", hr);
1708     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1709     ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1710     VariantClear(&dest);
1711
1712     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
1713     ok(hr == S_OK, "got %08x\n", hr);
1714
1715     V_VT(&dest) = VT_EMPTY;
1716     hr = IMXWriter_get_output(writer, &dest);
1717     ok(hr == S_OK, "got %08x\n", hr);
1718     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1719     ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1720     VariantClear(&dest);
1721
1722     hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
1723     EXPECT_HR(hr, E_INVALIDARG);
1724
1725     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
1726     EXPECT_HR(hr, E_INVALIDARG);
1727
1728     /* only local name is an error too */
1729     hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
1730     EXPECT_HR(hr, E_INVALIDARG);
1731
1732     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
1733     EXPECT_HR(hr, S_OK);
1734
1735     V_VT(&dest) = VT_EMPTY;
1736     hr = IMXWriter_get_output(writer, &dest);
1737     EXPECT_HR(hr, S_OK);
1738     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1739     ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1740     VariantClear(&dest);
1741
1742     hr = ISAXContentHandler_endDocument(content);
1743     EXPECT_HR(hr, S_OK);
1744
1745     V_VT(&dest) = VT_EMPTY;
1746     hr = IMXWriter_put_output(writer, dest);
1747     EXPECT_HR(hr, S_OK);
1748
1749     hr = ISAXContentHandler_startDocument(content);
1750     EXPECT_HR(hr, S_OK);
1751
1752     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
1753     EXPECT_HR(hr, S_OK);
1754
1755     V_VT(&dest) = VT_EMPTY;
1756     hr = IMXWriter_get_output(writer, &dest);
1757     EXPECT_HR(hr, S_OK);
1758     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1759     ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1760     VariantClear(&dest);
1761
1762     ISAXContentHandler_Release(content);
1763     IMXWriter_Release(writer);
1764
1765     free_bstrs();
1766 }
1767
1768 static void test_mxwriter_characters(void)
1769 {
1770     static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
1771     ISAXContentHandler *content;
1772     IMXWriter *writer;
1773     VARIANT dest;
1774     HRESULT hr;
1775
1776     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1777             &IID_IMXWriter, (void**)&writer);
1778     EXPECT_HR(hr, S_OK);
1779
1780     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1781     EXPECT_HR(hr, S_OK);
1782
1783     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1784     EXPECT_HR(hr, S_OK);
1785
1786     hr = ISAXContentHandler_startDocument(content);
1787     EXPECT_HR(hr, S_OK);
1788
1789     hr = ISAXContentHandler_characters(content, NULL, 0);
1790     EXPECT_HR(hr, E_INVALIDARG);
1791
1792     hr = ISAXContentHandler_characters(content, chardataW, 0);
1793     EXPECT_HR(hr, S_OK);
1794
1795     hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
1796     EXPECT_HR(hr, S_OK);
1797
1798     V_VT(&dest) = VT_EMPTY;
1799     hr = IMXWriter_get_output(writer, &dest);
1800     EXPECT_HR(hr, S_OK);
1801     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1802     ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1803     VariantClear(&dest);
1804
1805     hr = ISAXContentHandler_endDocument(content);
1806     EXPECT_HR(hr, S_OK);
1807
1808     ISAXContentHandler_Release(content);
1809     IMXWriter_Release(writer);
1810
1811     /* try empty characters data to see if element is closed */
1812     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1813             &IID_IMXWriter, (void**)&writer);
1814     EXPECT_HR(hr, S_OK);
1815
1816     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1817     EXPECT_HR(hr, S_OK);
1818
1819     hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
1820     EXPECT_HR(hr, S_OK);
1821
1822     hr = ISAXContentHandler_startDocument(content);
1823     EXPECT_HR(hr, S_OK);
1824
1825     hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
1826     EXPECT_HR(hr, S_OK);
1827
1828     hr = ISAXContentHandler_characters(content, chardataW, 0);
1829     EXPECT_HR(hr, S_OK);
1830
1831     hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
1832     EXPECT_HR(hr, S_OK);
1833
1834     V_VT(&dest) = VT_EMPTY;
1835     hr = IMXWriter_get_output(writer, &dest);
1836     EXPECT_HR(hr, S_OK);
1837     ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
1838     ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1839     VariantClear(&dest);
1840
1841     ISAXContentHandler_Release(content);
1842     IMXWriter_Release(writer);
1843
1844     free_bstrs();
1845 }
1846
1847 static const mxwriter_stream_test mxwriter_stream_tests[] = {
1848     {
1849         VARIANT_TRUE,"UTF-16",
1850         {
1851             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
1852             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1853             {TRUE}
1854         }
1855     },
1856     {
1857         VARIANT_FALSE,"UTF-16",
1858         {
1859             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1860             {TRUE}
1861         }
1862     },
1863     {
1864         VARIANT_TRUE,"UTF-8",
1865         {
1866             {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
1867             /* For some reason Windows makes an empty write call when UTF-8 encoding is used
1868              * and the writer is released.
1869              */
1870             {FALSE,NULL,0},
1871             {TRUE}
1872         }
1873     },
1874     {
1875         VARIANT_TRUE,"UTF-16",
1876         {
1877             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
1878             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1879             {TRUE}
1880         }
1881     },
1882     {
1883         VARIANT_TRUE,"UTF-16",
1884         {
1885             {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
1886             {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
1887             {TRUE}
1888         }
1889     }
1890 };
1891
1892 static void test_mxwriter_stream(void)
1893 {
1894     IMXWriter *writer;
1895     ISAXContentHandler *content;
1896     HRESULT hr;
1897     VARIANT dest;
1898     IStream *stream;
1899     LARGE_INTEGER pos;
1900     ULARGE_INTEGER pos2;
1901     DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
1902
1903     for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
1904         const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
1905
1906         hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1907                 &IID_IMXWriter, (void**)&writer);
1908         ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
1909
1910         hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1911         ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
1912
1913         hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
1914         ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
1915
1916         V_VT(&dest) = VT_UNKNOWN;
1917         V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
1918         hr = IMXWriter_put_output(writer, dest);
1919         ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
1920         VariantClear(&dest);
1921
1922         hr = IMXWriter_put_byteOrderMark(writer, test->bom);
1923         ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
1924
1925         current_write_test = test->expected_writes;
1926
1927         hr = ISAXContentHandler_startDocument(content);
1928         ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
1929
1930         hr = ISAXContentHandler_endDocument(content);
1931         ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
1932
1933         ISAXContentHandler_Release(content);
1934         IMXWriter_Release(writer);
1935
1936         ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
1937             (int)(current_write_test-test->expected_writes), current_stream_test_index);
1938     }
1939
1940     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
1941             &IID_IMXWriter, (void**)&writer);
1942     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
1943
1944     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
1945     ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
1946
1947     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
1948     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
1949
1950     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
1951     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
1952
1953     V_VT(&dest) = VT_UNKNOWN;
1954     V_UNKNOWN(&dest) = (IUnknown*)stream;
1955     hr = IMXWriter_put_output(writer, dest);
1956     ok(hr == S_OK, "put_output failed: %08x\n", hr);
1957
1958     hr = ISAXContentHandler_startDocument(content);
1959     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
1960
1961     /* Setting output of the mxwriter causes the current output to be flushed,
1962      * and the writer to start over.
1963      */
1964     V_VT(&dest) = VT_EMPTY;
1965     hr = IMXWriter_put_output(writer, dest);
1966     ok(hr == S_OK, "put_output failed: %08x\n", hr);
1967
1968     pos.QuadPart = 0;
1969     hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
1970     ok(hr == S_OK, "Seek failed: %08x\n", hr);
1971     ok(pos2.QuadPart != 0, "expected stream position moved\n");
1972
1973     hr = ISAXContentHandler_startDocument(content);
1974     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
1975
1976     hr = ISAXContentHandler_endDocument(content);
1977     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
1978
1979     V_VT(&dest) = VT_EMPTY;
1980     hr = IMXWriter_get_output(writer, &dest);
1981     ok(hr == S_OK, "get_output failed: %08x\n", hr);
1982     ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
1983     ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
1984             "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
1985     VariantClear(&dest);
1986
1987     ISAXContentHandler_Release(content);
1988     IMXWriter_Release(writer);
1989
1990     free_bstrs();
1991 }
1992
1993 static void test_mxwriter_encoding(void)
1994 {
1995     IMXWriter *writer;
1996     ISAXContentHandler *content;
1997     HRESULT hr;
1998     VARIANT dest;
1999
2000     hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2001             &IID_IMXWriter, (void**)&writer);
2002     ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
2003
2004     hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2005     ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
2006
2007     hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
2008     ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
2009
2010     hr = ISAXContentHandler_startDocument(content);
2011     ok(hr == S_OK, "startDocument failed: %08x\n", hr);
2012
2013     hr = ISAXContentHandler_endDocument(content);
2014     ok(hr == S_OK, "endDocument failed: %08x\n", hr);
2015
2016     /* The content is always re-encoded to UTF-16 when the output is
2017      * retrieved as a BSTR.
2018      */
2019     V_VT(&dest) = VT_EMPTY;
2020     hr = IMXWriter_get_output(writer, &dest);
2021     todo_wine ok(hr == S_OK, "get_output failed: %08x\n", hr);
2022     todo_wine ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
2023     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)),
2024             "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2025     VariantClear(&dest);
2026
2027     ISAXContentHandler_Release(content);
2028     IMXWriter_Release(writer);
2029
2030     free_bstrs();
2031 }
2032
2033 START_TEST(saxreader)
2034 {
2035     ISAXXMLReader *reader;
2036     HRESULT hr;
2037
2038     hr = CoInitialize(NULL);
2039     ok(hr == S_OK, "failed to init com\n");
2040
2041     hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2042             &IID_ISAXXMLReader, (void**)&reader);
2043
2044     if(FAILED(hr))
2045     {
2046         skip("Failed to create SAXXMLReader instance\n");
2047         CoUninitialize();
2048         return;
2049     }
2050     ISAXXMLReader_Release(reader);
2051
2052     test_saxreader();
2053     test_encoding();
2054
2055     /* MXXMLWriter tests */
2056     get_supported_mxwriter_data(msxmlsupported_data);
2057     if (is_mxwriter_supported(&CLSID_MXXMLWriter, msxmlsupported_data))
2058     {
2059         test_mxwriter_contenthandler();
2060         test_mxwriter_startenddocument();
2061         test_mxwriter_startendelement();
2062         test_mxwriter_characters();
2063         test_mxwriter_properties();
2064         test_mxwriter_flush();
2065         test_mxwriter_stream();
2066         test_mxwriter_encoding();
2067     }
2068     else
2069         win_skip("MXXMLWriter not supported\n");
2070
2071     CoUninitialize();
2072 }