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