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