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