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