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