1 /*
2  * XMLLite IXmlWriter tests
3  *
4  * Copyright 2011 (C) Alistair Leslie-Hughes
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #define WIN32_NO_STATUS
22 #define _INC_WINDOWS
23 #define COM_NO_WINDOWS_H
24 
25 #define CONST_VTABLE
26 #define COBJMACROS
27 
28 #include <stdarg.h>
29 //#include <stdio.h>
30 
31 #include <windef.h>
32 #include <winbase.h>
33 #include <winnls.h>
34 #include <objbase.h>
35 #include <ole2.h>
36 #include <xmllite.h>
37 #include <wine/test.h>
38 
39 #include <initguid.h>
40 DEFINE_GUID(IID_IXmlWriterOutput, 0xc1131708, 0x0f59, 0x477f, 0x93, 0x59, 0x7d, 0x33, 0x24, 0x51, 0xbc, 0x1a);
41 
42 static const WCHAR aW[] = {'a',0};
43 
44 #define EXPECT_REF(obj, ref) _expect_ref((IUnknown *)obj, ref, __LINE__)
45 static void _expect_ref(IUnknown *obj, ULONG ref, int line)
46 {
47     ULONG refcount;
48     IUnknown_AddRef(obj);
49     refcount = IUnknown_Release(obj);
50     ok_(__FILE__, line)(refcount == ref, "expected refcount %d, got %d\n", ref, refcount);
51 }
52 
53 static void check_output_raw(IStream *stream, const void *expected, SIZE_T size, int line)
54 {
55     SIZE_T content_size;
56     HGLOBAL hglobal;
57     HRESULT hr;
58     char *ptr;
59 
60     hr = GetHGlobalFromStream(stream, &hglobal);
61     ok_(__FILE__, line)(hr == S_OK, "Failed to get the stream handle, hr %#x.\n", hr);
62 
63     content_size = GlobalSize(hglobal);
64     ok_(__FILE__, line)(size <= content_size, "Unexpected test output size.\n");
65     ptr = GlobalLock(hglobal);
66     if (size <= content_size)
67         ok_(__FILE__, line)(!memcmp(expected, ptr, size), "Unexpected output content.\n");
68 
69     GlobalUnlock(hglobal);
70 }
71 
72 static void check_output(IStream *stream, const char *expected, BOOL todo, int line)
73 {
74     int len = strlen(expected), size;
75     HGLOBAL hglobal;
76     HRESULT hr;
77     char *ptr;
78 
79     hr = GetHGlobalFromStream(stream, &hglobal);
80     ok_(__FILE__, line)(hr == S_OK, "got 0x%08x\n", hr);
81 
82     size = GlobalSize(hglobal);
83     ptr = GlobalLock(hglobal);
84     todo_wine_if(todo)
85     {
86         if (size != len)
87         {
88             ok_(__FILE__, line)(0, "data size mismatch, expected %u, got %u\n", len, size);
89             ok_(__FILE__, line)(0, "got |%s|, expected |%s|\n", ptr, expected);
90         }
91         else
92             ok_(__FILE__, line)(!strncmp(ptr, expected, len), "got |%s|, expected |%s|\n", ptr, expected);
93     }
94     GlobalUnlock(hglobal);
95 }
96 #define CHECK_OUTPUT(stream, expected) check_output(stream, expected, FALSE, __LINE__)
97 #define CHECK_OUTPUT_TODO(stream, expected) check_output(stream, expected, TRUE, __LINE__)
98 #define CHECK_OUTPUT_RAW(stream, expected, size) check_output_raw(stream, expected, size, __LINE__)
99 
100 static void writer_set_property(IXmlWriter *writer, XmlWriterProperty property)
101 {
102     HRESULT hr;
103 
104     hr = IXmlWriter_SetProperty(writer, property, TRUE);
105     ok(hr == S_OK, "Failed to set writer property, hr %#x.\n", hr);
106 }
107 
108 /* used to test all Write* methods for consistent error state */
109 static void check_writer_state(IXmlWriter *writer, HRESULT exp_hr)
110 {
111     static const WCHAR aW[] = {'a',0};
112     HRESULT hr;
113 
114     /* FIXME: add WriteAttributes */
115 
116     hr = IXmlWriter_WriteAttributeString(writer, NULL, aW, NULL, aW);
117     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
118 
119     hr = IXmlWriter_WriteCData(writer, aW);
120     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
121 
122     hr = IXmlWriter_WriteCharEntity(writer, aW[0]);
123     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
124 
125     hr = IXmlWriter_WriteChars(writer, aW, 1);
126     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
127 
128     hr = IXmlWriter_WriteComment(writer, aW);
129     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
130 
131     /* FIXME: add WriteDocType */
132 
133     hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, aW);
134     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
135 
136     hr = IXmlWriter_WriteEndDocument(writer);
137     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
138 
139     hr = IXmlWriter_WriteEndElement(writer);
140     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
141 
142     hr = IXmlWriter_WriteEntityRef(writer, aW);
143     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
144 
145     hr = IXmlWriter_WriteFullEndElement(writer);
146     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
147 
148     hr = IXmlWriter_WriteName(writer, aW);
149     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
150 
151     hr = IXmlWriter_WriteNmToken(writer, aW);
152     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
153 
154     /* FIXME: add WriteNode */
155     /* FIXME: add WriteNodeShallow */
156 
157     hr = IXmlWriter_WriteProcessingInstruction(writer, aW, aW);
158     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
159 
160     hr = IXmlWriter_WriteQualifiedName(writer, aW, NULL);
161     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
162 
163     hr = IXmlWriter_WriteRaw(writer, aW);
164     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
165 
166     hr = IXmlWriter_WriteRawChars(writer, aW, 1);
167     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
168 
169     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
170     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
171 
172     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
173     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
174 
175     hr = IXmlWriter_WriteString(writer, aW);
176     ok(hr == exp_hr, "got 0x%08x, expected 0x%08x\n", hr, exp_hr);
177 
178     /* FIXME: add WriteSurrogateCharEntity */
179     /* FIXME: add WriteWhitespace */
180 }
181 
182 static IStream *writer_set_output(IXmlWriter *writer)
183 {
184     IStream *stream;
185     HRESULT hr;
186 
187     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
188     ok(hr == S_OK, "got 0x%08x\n", hr);
189 
190     hr = IXmlWriter_SetOutput(writer, (IUnknown*)stream);
191     ok(hr == S_OK, "got 0x%08x\n", hr);
192 
193     return stream;
194 }
195 
196 static HRESULT WINAPI testoutput_QueryInterface(IUnknown *iface, REFIID riid, void **obj)
197 {
198     if (IsEqualGUID(riid, &IID_IUnknown)) {
199         *obj = iface;
200         return S_OK;
201     }
202     else {
203         ok(0, "unknown riid=%s\n", wine_dbgstr_guid(riid));
204         return E_NOINTERFACE;
205     }
206 }
207 
208 static ULONG WINAPI testoutput_AddRef(IUnknown *iface)
209 {
210     return 2;
211 }
212 
213 static ULONG WINAPI testoutput_Release(IUnknown *iface)
214 {
215     return 1;
216 }
217 
218 static const IUnknownVtbl testoutputvtbl = {
219     testoutput_QueryInterface,
220     testoutput_AddRef,
221     testoutput_Release
222 };
223 
224 static IUnknown testoutput = { &testoutputvtbl };
225 
226 static HRESULT WINAPI teststream_QueryInterface(ISequentialStream *iface, REFIID riid, void **obj)
227 {
228     if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_ISequentialStream))
229     {
230         *obj = iface;
231         return S_OK;
232     }
233 
234     *obj = NULL;
235     return E_NOINTERFACE;
236 }
237 
238 static ULONG WINAPI teststream_AddRef(ISequentialStream *iface)
239 {
240     return 2;
241 }
242 
243 static ULONG WINAPI teststream_Release(ISequentialStream *iface)
244 {
245     return 1;
246 }
247 
248 static HRESULT WINAPI teststream_Read(ISequentialStream *iface, void *pv, ULONG cb, ULONG *pread)
249 {
250     ok(0, "unexpected call\n");
251     return E_NOTIMPL;
252 }
253 
254 static ULONG g_write_len;
255 static HRESULT WINAPI teststream_Write(ISequentialStream *iface, const void *pv, ULONG cb, ULONG *written)
256 {
257     g_write_len = cb;
258     *written = cb;
259     return S_OK;
260 }
261 
262 static const ISequentialStreamVtbl teststreamvtbl =
263 {
264     teststream_QueryInterface,
265     teststream_AddRef,
266     teststream_Release,
267     teststream_Read,
268     teststream_Write
269 };
270 
271 static ISequentialStream teststream = { &teststreamvtbl };
272 
273 static void test_writer_create(void)
274 {
275     HRESULT hr;
276     IXmlWriter *writer;
277     LONG_PTR value;
278     IUnknown *unk;
279 
280     /* crashes native */
281     if (0)
282     {
283         CreateXmlWriter(&IID_IXmlWriter, NULL, NULL);
284         CreateXmlWriter(NULL, (void**)&writer, NULL);
285     }
286 
287     hr = CreateXmlWriter(&IID_IStream, (void **)&unk, NULL);
288     ok(hr == E_NOINTERFACE, "got %08x\n", hr);
289 
290     hr = CreateXmlWriter(&IID_IUnknown, (void **)&unk, NULL);
291     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
292     hr = IUnknown_QueryInterface(unk, &IID_IXmlWriter, (void **)&writer);
293     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
294     ok(unk == (IUnknown *)writer, "unexpected interface pointer\n");
295     IUnknown_Release(unk);
296     IXmlWriter_Release(writer);
297 
298     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
299     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
300 
301     /* check default properties values */
302     value = 0;
303     hr = IXmlWriter_GetProperty(writer, XmlWriterProperty_ByteOrderMark, &value);
304     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
305     ok(value == TRUE, "got %ld\n", value);
306 
307     value = TRUE;
308     hr = IXmlWriter_GetProperty(writer, XmlWriterProperty_Indent, &value);
309     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
310     ok(value == FALSE, "got %ld\n", value);
311 
312     value = TRUE;
313     hr = IXmlWriter_GetProperty(writer, XmlWriterProperty_OmitXmlDeclaration, &value);
314     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
315     ok(value == FALSE, "got %ld\n", value);
316 
317     value = XmlConformanceLevel_Auto;
318     hr = IXmlWriter_GetProperty(writer, XmlWriterProperty_ConformanceLevel, &value);
319     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
320     ok(value == XmlConformanceLevel_Document, "got %ld\n", value);
321 
322     IXmlWriter_Release(writer);
323 }
324 
325 static void test_invalid_output_encoding(IXmlWriter *writer, IUnknown *output)
326 {
327     HRESULT hr;
328 
329     hr = IXmlWriter_SetOutput(writer, output);
330     ok(hr == S_OK, "Failed to set output, hr %#x.\n", hr);
331 
332     /* TODO: WriteAttributes */
333 
334     hr = IXmlWriter_WriteAttributeString(writer, NULL, aW, NULL, aW);
335     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
336 
337     hr = IXmlWriter_WriteCData(writer, aW);
338     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
339 
340     hr = IXmlWriter_WriteCharEntity(writer, 0x100);
341     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
342 
343     hr = IXmlWriter_WriteChars(writer, aW, 1);
344     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
345 
346     hr = IXmlWriter_WriteComment(writer, aW);
347     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
348 
349     /* TODO: WriteDocType */
350 
351     hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, NULL);
352     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
353 
354     hr = IXmlWriter_WriteEndDocument(writer);
355     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
356 
357     hr = IXmlWriter_WriteEndElement(writer);
358     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
359 
360     hr = IXmlWriter_WriteEntityRef(writer, aW);
361     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
362 
363     hr = IXmlWriter_WriteFullEndElement(writer);
364     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
365 
366     hr = IXmlWriter_WriteName(writer, aW);
367     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
368 
369     hr = IXmlWriter_WriteNmToken(writer, aW);
370     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
371 
372     /* TODO: WriteNode */
373     /* TODO: WriteNodeShallow */
374 
375     hr = IXmlWriter_WriteProcessingInstruction(writer, aW, aW);
376     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
377 
378     hr = IXmlWriter_WriteQualifiedName(writer, aW, NULL);
379     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
380 
381     hr = IXmlWriter_WriteRaw(writer, aW);
382     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
383 
384     hr = IXmlWriter_WriteRawChars(writer, aW, 1);
385     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
386 
387     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
388     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
389 
390     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
391     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
392 
393     hr = IXmlWriter_WriteString(writer, aW);
394     ok(hr == MX_E_ENCODING, "Unexpected hr %#x.\n", hr);
395 
396     /* TODO: WriteSurrogateCharEntity */
397     /* ًُُTODO: WriteWhitespace */
398 
399     hr = IXmlWriter_Flush(writer);
400     ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
401 }
402 
403 static void test_writeroutput(void)
404 {
405     static const WCHAR utf16W[] = {'u','t','f','-','1','6',0};
406     static const WCHAR usasciiW[] = {'u','s','-','a','s','c','i','i',0};
407     static const WCHAR dummyW[] = {'d','u','m','m','y',0};
408     static const WCHAR utf16_outputW[] = {0xfeff,'<','a'};
409     IXmlWriterOutput *output;
410     IXmlWriter *writer;
411     IStream *stream;
412     IUnknown *unk;
413     HRESULT hr;
414 
415     output = NULL;
416     hr = CreateXmlWriterOutputWithEncodingName(&testoutput, NULL, NULL, &output);
417     ok(hr == S_OK, "got %08x\n", hr);
418     EXPECT_REF(output, 1);
419     IUnknown_Release(output);
420 
421     hr = CreateXmlWriterOutputWithEncodingName(&testoutput, NULL, utf16W, &output);
422     ok(hr == S_OK, "got %08x\n", hr);
423     unk = NULL;
424     hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&unk);
425     ok(hr == S_OK, "got %08x\n", hr);
426 todo_wine
427     ok(unk != NULL && unk != output, "got %p, output %p\n", unk, output);
428     EXPECT_REF(output, 2);
429     /* releasing 'unk' crashes on native */
430     IUnknown_Release(output);
431     EXPECT_REF(output, 1);
432     IUnknown_Release(output);
433 
434     output = NULL;
435     hr = CreateXmlWriterOutputWithEncodingCodePage(&testoutput, NULL, ~0u, &output);
436     ok(hr == S_OK, "got %08x\n", hr);
437     IUnknown_Release(output);
438 
439     hr = CreateXmlWriterOutputWithEncodingCodePage(&testoutput, NULL, CP_UTF8, &output);
440     ok(hr == S_OK, "got %08x\n", hr);
441     unk = NULL;
442     hr = IUnknown_QueryInterface(output, &IID_IXmlWriterOutput, (void**)&unk);
443     ok(hr == S_OK, "got %08x\n", hr);
444     ok(unk != NULL, "got %p\n", unk);
445     /* releasing 'unk' crashes on native */
446     IUnknown_Release(output);
447     IUnknown_Release(output);
448 
449     /* create with us-ascii */
450     output = NULL;
451     hr = CreateXmlWriterOutputWithEncodingName(&testoutput, NULL, usasciiW, &output);
452     ok(hr == S_OK, "got %08x\n", hr);
453     IUnknown_Release(output);
454 
455     /* Output with codepage 1200. */
456     hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&writer, NULL);
457     ok(hr == S_OK, "Failed to create writer, hr %#x.\n", hr);
458 
459     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
460     ok(hr == S_OK, "Failed to create stream, hr %#x.\n", hr);
461 
462     hr = CreateXmlWriterOutputWithEncodingCodePage((IUnknown *)stream, NULL, 1200, &output);
463     ok(hr == S_OK, "Failed to create writer output, hr %#x.\n", hr);
464 
465     hr = IXmlWriter_SetOutput(writer, output);
466     ok(hr == S_OK, "Failed to set writer output, hr %#x.\n", hr);
467 
468     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
469     ok(hr == S_OK, "Write failed, hr %#x.\n", hr);
470 
471     hr = IXmlWriter_Flush(writer);
472     ok(hr == S_OK, "Failed to flush, hr %#x.\n", hr);
473 
474     CHECK_OUTPUT_RAW(stream, utf16_outputW, sizeof(utf16_outputW));
475 
476     IStream_Release(stream);
477 
478     /* Create output with meaningless code page value. */
479     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
480     ok(hr == S_OK, "Failed to create stream, hr %#x.\n", hr);
481 
482     output = NULL;
483     hr = CreateXmlWriterOutputWithEncodingCodePage((IUnknown *)stream, NULL, ~0u, &output);
484     ok(hr == S_OK, "Failed to create writer output, hr %#x.\n", hr);
485 
486     test_invalid_output_encoding(writer, output);
487     CHECK_OUTPUT(stream, "");
488 
489     IStream_Release(stream);
490     IUnknown_Release(output);
491 
492     /* Same, with invalid encoding name. */
493     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
494     ok(hr == S_OK, "got 0x%08x\n", hr);
495 
496     output = NULL;
497     hr = CreateXmlWriterOutputWithEncodingName((IUnknown *)stream, NULL, dummyW, &output);
498     ok(hr == S_OK, "got %08x\n", hr);
499 
500     test_invalid_output_encoding(writer, output);
501     CHECK_OUTPUT(stream, "");
502 
503     IStream_Release(stream);
504     IUnknown_Release(output);
505 
506     IXmlWriter_Release(writer);
507 }
508 
509 static void test_writestartdocument(void)
510 {
511     static const char fullprolog[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
512     static const char *prologversion2 = "<?xml version=\"1.0\" encoding=\"uS-asCii\"?>";
513     static const char prologversion[] = "<?xml version=\"1.0\"?>";
514     static const WCHAR versionW[] = {'v','e','r','s','i','o','n','=','"','1','.','0','"',0};
515     static const WCHAR usasciiW[] = {'u','S','-','a','s','C','i','i',0};
516     static const WCHAR xmlW[] = {'x','m','l',0};
517     IXmlWriterOutput *output;
518     IXmlWriter *writer;
519     IStream *stream;
520     HRESULT hr;
521 
522     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
523     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
524 
525     /* output not set */
526     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
527     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
528 
529     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
530     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
531 
532     hr = IXmlWriter_Flush(writer);
533     ok(hr == S_OK, "got 0x%08x\n", hr);
534 
535     stream = writer_set_output(writer);
536 
537     /* nothing written yet */
538     hr = IXmlWriter_Flush(writer);
539     ok(hr == S_OK, "got 0x%08x\n", hr);
540 
541     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
542     ok(hr == S_OK, "got 0x%08x\n", hr);
543 
544     hr = IXmlWriter_Flush(writer);
545     ok(hr == S_OK, "got 0x%08x\n", hr);
546 
547     CHECK_OUTPUT(stream, fullprolog);
548 
549     /* one more time */
550     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
551     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
552     IStream_Release(stream);
553 
554     /* now add PI manually, and try to start a document */
555     stream = writer_set_output(writer);
556 
557     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
558     ok(hr == S_OK, "got 0x%08x\n", hr);
559 
560     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
561     ok(hr == S_OK, "got 0x%08x\n", hr);
562 
563     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
564     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
565 
566     /* another attempt to add 'xml' PI */
567     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
568     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
569 
570     hr = IXmlWriter_Flush(writer);
571     ok(hr == S_OK, "got 0x%08x\n", hr);
572 
573     CHECK_OUTPUT(stream, prologversion);
574 
575     IStream_Release(stream);
576     IXmlWriter_Release(writer);
577 
578     /* create with us-ascii */
579     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
580     ok(hr == S_OK, "got 0x%08x\n", hr);
581 
582     output = NULL;
583     hr = CreateXmlWriterOutputWithEncodingName((IUnknown *)stream, NULL, usasciiW, &output);
584     ok(hr == S_OK, "got %08x\n", hr);
585 
586     hr = CreateXmlWriter(&IID_IXmlWriter, (void **)&writer, NULL);
587     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
588 
589     hr = IXmlWriter_SetOutput(writer, output);
590     ok(hr == S_OK, "got %08x\n", hr);
591 
592     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
593     ok(hr == S_OK, "got 0x%08x\n", hr);
594 
595     hr = IXmlWriter_Flush(writer);
596     ok(hr == S_OK, "got 0x%08x\n", hr);
597 
598     CHECK_OUTPUT(stream, prologversion2);
599 
600     IStream_Release(stream);
601     IXmlWriter_Release(writer);
602     IUnknown_Release(output);
603 }
604 
605 static void test_flush(void)
606 {
607     IXmlWriter *writer;
608     HRESULT hr;
609 
610     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
611     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
612 
613     hr = IXmlWriter_SetOutput(writer, (IUnknown*)&teststream);
614     ok(hr == S_OK, "got 0x%08x\n", hr);
615 
616     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
617     ok(hr == S_OK, "got 0x%08x\n", hr);
618 
619     g_write_len = 0;
620     hr = IXmlWriter_Flush(writer);
621     ok(hr == S_OK, "got 0x%08x\n", hr);
622     ok(g_write_len > 0, "got %d\n", g_write_len);
623 
624     g_write_len = 1;
625     hr = IXmlWriter_Flush(writer);
626     ok(hr == S_OK, "got 0x%08x\n", hr);
627     ok(g_write_len == 0, "got %d\n", g_write_len);
628 
629     /* Release() flushes too */
630     g_write_len = 1;
631     IXmlWriter_Release(writer);
632     ok(g_write_len == 0, "got %d\n", g_write_len);
633 }
634 
635 static void test_omitxmldeclaration(void)
636 {
637     static const char prologversion[] = "<?xml version=\"1.0\"?>";
638     static const WCHAR versionW[] = {'v','e','r','s','i','o','n','=','"','1','.','0','"',0};
639     static const WCHAR xmlW[] = {'x','m','l',0};
640     IXmlWriter *writer;
641     HGLOBAL hglobal;
642     IStream *stream;
643     HRESULT hr;
644     char *ptr;
645 
646     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
647     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
648 
649     stream = writer_set_output(writer);
650 
651     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
652 
653     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
654     ok(hr == S_OK, "got 0x%08x\n", hr);
655 
656     hr = IXmlWriter_Flush(writer);
657     ok(hr == S_OK, "got 0x%08x\n", hr);
658 
659     hr = GetHGlobalFromStream(stream, &hglobal);
660     ok(hr == S_OK, "got 0x%08x\n", hr);
661 
662     ptr = GlobalLock(hglobal);
663     ok(!ptr, "got %p\n", ptr);
664     GlobalUnlock(hglobal);
665 
666     /* one more time */
667     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
668     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
669 
670     IStream_Release(stream);
671 
672     /* now add PI manually, and try to start a document */
673     stream = writer_set_output(writer);
674 
675     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
676     ok(hr == S_OK, "got 0x%08x\n", hr);
677 
678     hr = IXmlWriter_Flush(writer);
679     ok(hr == S_OK, "got 0x%08x\n", hr);
680 
681     CHECK_OUTPUT(stream, prologversion);
682 
683     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
684     ok(hr == S_OK, "got 0x%08x\n", hr);
685 
686     hr = IXmlWriter_Flush(writer);
687     ok(hr == S_OK, "got 0x%08x\n", hr);
688 
689     CHECK_OUTPUT(stream, prologversion);
690 
691     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
692     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
693 
694     hr = IXmlWriter_Flush(writer);
695     ok(hr == S_OK, "got 0x%08x\n", hr);
696 
697     CHECK_OUTPUT(stream, prologversion);
698 
699     /* another attempt to add 'xml' PI */
700     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
701     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
702 
703     hr = IXmlWriter_Flush(writer);
704     ok(hr == S_OK, "got 0x%08x\n", hr);
705 
706     IStream_Release(stream);
707     IXmlWriter_Release(writer);
708 }
709 
710 static void test_bom(void)
711 {
712     static const WCHAR versionW[] = {'v','e','r','s','i','o','n','=','"','1','.','0','"',0};
713     static const WCHAR utf16W[] = {'u','t','f','-','1','6',0};
714     static const WCHAR xmlW[] = {'x','m','l',0};
715     IXmlWriterOutput *output;
716     unsigned char *ptr;
717     IXmlWriter *writer;
718     IStream *stream;
719     HGLOBAL hglobal;
720     HRESULT hr;
721 
722     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
723     ok(hr == S_OK, "got 0x%08x\n", hr);
724 
725     hr = CreateXmlWriterOutputWithEncodingName((IUnknown*)stream, NULL, utf16W, &output);
726     ok(hr == S_OK, "got %08x\n", hr);
727 
728     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
729     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
730 
731     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
732 
733     hr = IXmlWriter_SetOutput(writer, output);
734     ok(hr == S_OK, "got 0x%08x\n", hr);
735 
736     /* BOM is on by default */
737     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
738     ok(hr == S_OK, "got 0x%08x\n", hr);
739 
740     hr = IXmlWriter_Flush(writer);
741     ok(hr == S_OK, "got 0x%08x\n", hr);
742 
743     hr = GetHGlobalFromStream(stream, &hglobal);
744     ok(hr == S_OK, "got 0x%08x\n", hr);
745 
746     ptr = GlobalLock(hglobal);
747     ok(ptr[0] == 0xff && ptr[1] == 0xfe, "got %x,%x\n", ptr[0], ptr[1]);
748     GlobalUnlock(hglobal);
749 
750     IStream_Release(stream);
751     IUnknown_Release(output);
752 
753     /* start with PI */
754     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
755     ok(hr == S_OK, "got 0x%08x\n", hr);
756 
757     hr = CreateXmlWriterOutputWithEncodingName((IUnknown*)stream, NULL, utf16W, &output);
758     ok(hr == S_OK, "got %08x\n", hr);
759 
760     hr = IXmlWriter_SetOutput(writer, output);
761     ok(hr == S_OK, "got 0x%08x\n", hr);
762 
763     hr = IXmlWriter_WriteProcessingInstruction(writer, xmlW, versionW);
764     ok(hr == S_OK, "got 0x%08x\n", hr);
765 
766     hr = IXmlWriter_Flush(writer);
767     ok(hr == S_OK, "got 0x%08x\n", hr);
768 
769     hr = GetHGlobalFromStream(stream, &hglobal);
770     ok(hr == S_OK, "got 0x%08x\n", hr);
771 
772     ptr = GlobalLock(hglobal);
773     ok(ptr[0] == 0xff && ptr[1] == 0xfe, "got %x,%x\n", ptr[0], ptr[1]);
774     GlobalUnlock(hglobal);
775 
776     IUnknown_Release(output);
777     IStream_Release(stream);
778 
779     /* start with element */
780     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
781     ok(hr == S_OK, "got 0x%08x\n", hr);
782 
783     hr = CreateXmlWriterOutputWithEncodingName((IUnknown*)stream, NULL, utf16W, &output);
784     ok(hr == S_OK, "got %08x\n", hr);
785 
786     hr = IXmlWriter_SetOutput(writer, output);
787     ok(hr == S_OK, "got 0x%08x\n", hr);
788 
789     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
790     ok(hr == S_OK, "got 0x%08x\n", hr);
791 
792     hr = IXmlWriter_Flush(writer);
793     ok(hr == S_OK, "got 0x%08x\n", hr);
794 
795     hr = GetHGlobalFromStream(stream, &hglobal);
796     ok(hr == S_OK, "got 0x%08x\n", hr);
797 
798     ptr = GlobalLock(hglobal);
799     ok(ptr[0] == 0xff && ptr[1] == 0xfe, "got %x,%x\n", ptr[0], ptr[1]);
800     GlobalUnlock(hglobal);
801 
802     IUnknown_Release(output);
803     IStream_Release(stream);
804 
805     /* WriteElementString */
806     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
807     ok(hr == S_OK, "got 0x%08x\n", hr);
808 
809     hr = CreateXmlWriterOutputWithEncodingName((IUnknown*)stream, NULL, utf16W, &output);
810     ok(hr == S_OK, "got %08x\n", hr);
811 
812     hr = IXmlWriter_SetOutput(writer, output);
813     ok(hr == S_OK, "got 0x%08x\n", hr);
814 
815     writer_set_property(writer, XmlWriterProperty_Indent);
816 
817     hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, NULL);
818     ok(hr == S_OK, "got 0x%08x\n", hr);
819 
820     hr = IXmlWriter_Flush(writer);
821     ok(hr == S_OK, "got 0x%08x\n", hr);
822 
823     hr = GetHGlobalFromStream(stream, &hglobal);
824     ok(hr == S_OK, "got 0x%08x\n", hr);
825 
826     ptr = GlobalLock(hglobal);
827     ok(ptr[0] == 0xff && ptr[1] == 0xfe && ptr[2] == '<', "Unexpected output: %#x,%#x,%#x\n",
828             ptr[0], ptr[1], ptr[2]);
829     GlobalUnlock(hglobal);
830 
831     IUnknown_Release(output);
832     IStream_Release(stream);
833 
834     IXmlWriter_Release(writer);
835 }
836 
837 static void test_writestartelement(void)
838 {
839     static const WCHAR valueW[] = {'v','a','l','u','e',0};
840     static const WCHAR aW[] = {'a',0};
841     static const WCHAR bW[] = {'b',0};
842     IXmlWriter *writer;
843     IStream *stream;
844     HRESULT hr;
845 
846     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
847     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
848 
849     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
850     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
851 
852     stream = writer_set_output(writer);
853 
854     hr = IXmlWriter_WriteStartElement(writer, aW, NULL, NULL);
855     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
856 
857     hr = IXmlWriter_WriteStartElement(writer, NULL, NULL, NULL);
858     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
859 
860     hr = IXmlWriter_WriteStartElement(writer, NULL, NULL, aW);
861     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
862 
863     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
864     ok(hr == S_OK, "got 0x%08x\n", hr);
865 
866     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
867     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
868 
869     hr = IXmlWriter_Flush(writer);
870     ok(hr == S_OK, "got 0x%08x\n", hr);
871 
872     CHECK_OUTPUT(stream, "<a");
873 
874     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
875     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
876 
877     hr = IXmlWriter_WriteStartElement(writer, NULL, NULL, NULL);
878     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
879 
880     hr = IXmlWriter_WriteProcessingInstruction(writer, aW, aW);
881     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
882 
883     IStream_Release(stream);
884     IXmlWriter_Release(writer);
885 
886     /* WriteElementString */
887     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
888     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
889 
890     hr = IXmlWriter_WriteElementString(writer, NULL, bW, NULL, valueW);
891     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
892 
893     stream = writer_set_output(writer);
894 
895     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
896     ok(hr == S_OK, "got 0x%08x\n", hr);
897 
898     hr = IXmlWriter_WriteElementString(writer, NULL, bW, NULL, valueW);
899     ok(hr == S_OK, "got 0x%08x\n", hr);
900 
901     hr = IXmlWriter_WriteElementString(writer, NULL, bW, NULL, NULL);
902     ok(hr == S_OK, "got 0x%08x\n", hr);
903 
904     hr = IXmlWriter_Flush(writer);
905     ok(hr == S_OK, "got 0x%08x\n", hr);
906 
907     CHECK_OUTPUT(stream,
908         "<a><b>value</b><b />");
909 
910     IStream_Release(stream);
911     IXmlWriter_Release(writer);
912 }
913 
914 static void test_writeendelement(void)
915 {
916     static const WCHAR aW[] = {'a',0};
917     static const WCHAR bW[] = {'b',0};
918     IXmlWriter *writer;
919     IStream *stream;
920     HRESULT hr;
921 
922     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
923     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
924 
925     stream = writer_set_output(writer);
926 
927     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
928     ok(hr == S_OK, "got 0x%08x\n", hr);
929 
930     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
931     ok(hr == S_OK, "got 0x%08x\n", hr);
932 
933     hr = IXmlWriter_WriteEndElement(writer);
934     ok(hr == S_OK, "got 0x%08x\n", hr);
935 
936     hr = IXmlWriter_WriteEndElement(writer);
937     ok(hr == S_OK, "got 0x%08x\n", hr);
938 
939     hr = IXmlWriter_Flush(writer);
940     ok(hr == S_OK, "got 0x%08x\n", hr);
941 
942     CHECK_OUTPUT(stream, "<a><b /></a>");
943 
944     IXmlWriter_Release(writer);
945     IStream_Release(stream);
946 }
947 
948 static void test_writeenddocument(void)
949 {
950     static const WCHAR aW[] = {'a',0};
951     static const WCHAR bW[] = {'b',0};
952     IXmlWriter *writer;
953     IStream *stream;
954     HGLOBAL hglobal;
955     HRESULT hr;
956     char *ptr;
957 
958     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
959     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
960 
961     hr = IXmlWriter_WriteEndDocument(writer);
962     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
963 
964     stream = writer_set_output(writer);
965 
966     /* WriteEndDocument resets it to initial state */
967     hr = IXmlWriter_WriteEndDocument(writer);
968     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
969 
970     hr = IXmlWriter_WriteEndDocument(writer);
971     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
972 
973     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
974     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
975 
976     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
977     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
978 
979     hr = IXmlWriter_SetOutput(writer, (IUnknown*)stream);
980     ok(hr == S_OK, "got 0x%08x\n", hr);
981 
982     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
983     ok(hr == S_OK, "got 0x%08x\n", hr);
984 
985     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
986     ok(hr == S_OK, "got 0x%08x\n", hr);
987 
988     hr = IXmlWriter_WriteEndDocument(writer);
989     ok(hr == S_OK, "got 0x%08x\n", hr);
990 
991     hr = GetHGlobalFromStream(stream, &hglobal);
992     ok(hr == S_OK, "got 0x%08x\n", hr);
993 
994     ptr = GlobalLock(hglobal);
995     ok(ptr == NULL, "got %p\n", ptr);
996 
997     /* we still need to flush manually, WriteEndDocument doesn't do that */
998     hr = IXmlWriter_Flush(writer);
999     ok(hr == S_OK, "got 0x%08x\n", hr);
1000 
1001     CHECK_OUTPUT(stream, "<a><b /></a>");
1002 
1003     IXmlWriter_Release(writer);
1004     IStream_Release(stream);
1005 }
1006 
1007 static void test_WriteComment(void)
1008 {
1009     static const WCHAR closeW[] = {'-','-','>',0};
1010     static const WCHAR aW[] = {'a',0};
1011     static const WCHAR bW[] = {'b',0};
1012     IXmlWriter *writer;
1013     IStream *stream;
1014     HRESULT hr;
1015 
1016     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1017     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1018 
1019     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1020 
1021     hr = IXmlWriter_WriteComment(writer, aW);
1022     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
1023 
1024     stream = writer_set_output(writer);
1025 
1026     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1027     ok(hr == S_OK, "got 0x%08x\n", hr);
1028 
1029     hr = IXmlWriter_WriteComment(writer, aW);
1030     ok(hr == S_OK, "got 0x%08x\n", hr);
1031 
1032     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
1033     ok(hr == S_OK, "got 0x%08x\n", hr);
1034 
1035     hr = IXmlWriter_WriteComment(writer, aW);
1036     ok(hr == S_OK, "got 0x%08x\n", hr);
1037 
1038     hr = IXmlWriter_WriteComment(writer, NULL);
1039     ok(hr == S_OK, "got 0x%08x\n", hr);
1040 
1041     hr = IXmlWriter_WriteComment(writer, closeW);
1042     ok(hr == S_OK, "got 0x%08x\n", hr);
1043 
1044     hr = IXmlWriter_Flush(writer);
1045     ok(hr == S_OK, "got 0x%08x\n", hr);
1046 
1047     CHECK_OUTPUT(stream, "<!--a--><b><!--a--><!----><!--- ->-->");
1048 
1049     IXmlWriter_Release(writer);
1050     IStream_Release(stream);
1051 }
1052 
1053 static void test_WriteCData(void)
1054 {
1055     static const WCHAR closeW[] = {']',']','>',0};
1056     static const WCHAR close2W[] = {'a',']',']','>','b',0};
1057     static const WCHAR aW[] = {'a',0};
1058     static const WCHAR bW[] = {'b',0};
1059     IXmlWriter *writer;
1060     IStream *stream;
1061     HRESULT hr;
1062 
1063     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1064     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1065 
1066     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1067 
1068     hr = IXmlWriter_WriteCData(writer, aW);
1069     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
1070 
1071     stream = writer_set_output(writer);
1072 
1073     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
1074     ok(hr == S_OK, "got 0x%08x\n", hr);
1075 
1076     hr = IXmlWriter_WriteCData(writer, aW);
1077     ok(hr == S_OK, "got 0x%08x\n", hr);
1078 
1079     hr = IXmlWriter_WriteCData(writer, NULL);
1080     ok(hr == S_OK, "got 0x%08x\n", hr);
1081 
1082     hr = IXmlWriter_WriteCData(writer, closeW);
1083     ok(hr == S_OK, "got 0x%08x\n", hr);
1084 
1085     hr = IXmlWriter_WriteCData(writer, close2W);
1086     ok(hr == S_OK, "got 0x%08x\n", hr);
1087 
1088     hr = IXmlWriter_Flush(writer);
1089     ok(hr == S_OK, "got 0x%08x\n", hr);
1090 
1091     CHECK_OUTPUT(stream,
1092         "<b>"
1093         "<![CDATA[a]]>"
1094         "<![CDATA[]]>"
1095         "<![CDATA[]]]]>"
1096         "<![CDATA[>]]>"
1097         "<![CDATA[a]]]]>"
1098         "<![CDATA[>b]]>");
1099 
1100     IXmlWriter_Release(writer);
1101     IStream_Release(stream);
1102 }
1103 
1104 static void test_WriteRaw(void)
1105 {
1106     static const WCHAR rawW[] = {'a','<',':',0};
1107     static const WCHAR aW[] = {'a',0};
1108     IXmlWriter *writer;
1109     IStream *stream;
1110     HRESULT hr;
1111 
1112     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1113     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1114 
1115     hr = IXmlWriter_WriteRaw(writer, NULL);
1116     ok(hr == S_OK, "got 0x%08x\n", hr);
1117 
1118     hr = IXmlWriter_WriteRaw(writer, rawW);
1119     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
1120 
1121     stream = writer_set_output(writer);
1122 
1123     hr = IXmlWriter_WriteRaw(writer, NULL);
1124     ok(hr == S_OK, "got 0x%08x\n", hr);
1125 
1126     hr = IXmlWriter_WriteRaw(writer, rawW);
1127     ok(hr == S_OK, "got 0x%08x\n", hr);
1128 
1129     hr = IXmlWriter_WriteRaw(writer, rawW);
1130     ok(hr == S_OK, "got 0x%08x\n", hr);
1131 
1132     hr = IXmlWriter_WriteComment(writer, rawW);
1133     ok(hr == S_OK, "got 0x%08x\n", hr);
1134 
1135     hr = IXmlWriter_WriteRaw(writer, rawW);
1136     ok(hr == S_OK, "got 0x%08x\n", hr);
1137 
1138     hr = IXmlWriter_WriteElementString(writer, NULL, aW, NULL, aW);
1139     ok(hr == S_OK, "got 0x%08x\n", hr);
1140 
1141     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Yes);
1142     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1143 
1144     hr = IXmlWriter_WriteComment(writer, rawW);
1145     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1146 
1147     hr = IXmlWriter_WriteEndDocument(writer);
1148     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1149 
1150     hr = IXmlWriter_WriteRaw(writer, rawW);
1151     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1152 
1153     hr = IXmlWriter_Flush(writer);
1154     ok(hr == S_OK, "got 0x%08x\n", hr);
1155 
1156     CHECK_OUTPUT(stream, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>a<:a<:<!--a<:-->a<:<a>a</a>");
1157 
1158     IXmlWriter_Release(writer);
1159     IStream_Release(stream);
1160 }
1161 
1162 static void test_writer_state(void)
1163 {
1164     static const WCHAR aW[] = {'a',0};
1165     IXmlWriter *writer;
1166     IStream *stream;
1167     HRESULT hr;
1168 
1169     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1170     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1171 
1172     /* initial state */
1173     check_writer_state(writer, E_UNEXPECTED);
1174 
1175     /* set output and call 'wrong' method, WriteEndElement */
1176     stream = writer_set_output(writer);
1177 
1178     hr = IXmlWriter_WriteEndElement(writer);
1179     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1180 
1181     check_writer_state(writer, WR_E_INVALIDACTION);
1182     IStream_Release(stream);
1183 
1184     /* WriteAttributeString */
1185     stream = writer_set_output(writer);
1186 
1187     hr = IXmlWriter_WriteAttributeString(writer, NULL, aW, NULL, aW);
1188     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1189 
1190     check_writer_state(writer, WR_E_INVALIDACTION);
1191     IStream_Release(stream);
1192 
1193     /* WriteEndDocument */
1194     stream = writer_set_output(writer);
1195 
1196     hr = IXmlWriter_WriteEndDocument(writer);
1197     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1198 
1199     check_writer_state(writer, WR_E_INVALIDACTION);
1200     IStream_Release(stream);
1201 
1202     /* WriteFullEndElement */
1203     stream = writer_set_output(writer);
1204 
1205     hr = IXmlWriter_WriteFullEndElement(writer);
1206     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1207 
1208     check_writer_state(writer, WR_E_INVALIDACTION);
1209     IStream_Release(stream);
1210 
1211     /* WriteCData */
1212     stream = writer_set_output(writer);
1213 
1214     hr = IXmlWriter_WriteCData(writer, aW);
1215     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1216 
1217     check_writer_state(writer, WR_E_INVALIDACTION);
1218     IStream_Release(stream);
1219 
1220     /* WriteName */
1221     stream = writer_set_output(writer);
1222 
1223     hr = IXmlWriter_WriteName(writer, aW);
1224     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1225 
1226     check_writer_state(writer, WR_E_INVALIDACTION);
1227     IStream_Release(stream);
1228 
1229     /* WriteNmToken */
1230     stream = writer_set_output(writer);
1231 
1232     hr = IXmlWriter_WriteNmToken(writer, aW);
1233     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1234 
1235     check_writer_state(writer, WR_E_INVALIDACTION);
1236     IStream_Release(stream);
1237 
1238     /* WriteString */
1239     stream = writer_set_output(writer);
1240 
1241     hr = IXmlWriter_WriteString(writer, aW);
1242     ok(hr == WR_E_INVALIDACTION, "got 0x%08x\n", hr);
1243 
1244     check_writer_state(writer, WR_E_INVALIDACTION);
1245     IStream_Release(stream);
1246 
1247     IXmlWriter_Release(writer);
1248 }
1249 
1250 static void test_indentation(void)
1251 {
1252     static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
1253     static const WCHAR aW[] = {'a',0};
1254     static const WCHAR bW[] = {'b',0};
1255     IXmlWriter *writer;
1256     IStream *stream;
1257     HRESULT hr;
1258 
1259     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1260     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1261 
1262     stream = writer_set_output(writer);
1263 
1264     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1265     writer_set_property(writer, XmlWriterProperty_Indent);
1266 
1267     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1268     ok(hr == S_OK, "got 0x%08x\n", hr);
1269 
1270     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1271     ok(hr == S_OK, "got 0x%08x\n", hr);
1272 
1273     hr = IXmlWriter_WriteComment(writer, commentW);
1274     ok(hr == S_OK, "got 0x%08x\n", hr);
1275 
1276     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
1277     ok(hr == S_OK, "got 0x%08x\n", hr);
1278 
1279     hr = IXmlWriter_WriteEndDocument(writer);
1280     ok(hr == S_OK, "got 0x%08x\n", hr);
1281 
1282     hr = IXmlWriter_Flush(writer);
1283     ok(hr == S_OK, "got 0x%08x\n", hr);
1284 
1285     CHECK_OUTPUT(stream,
1286         "<a>\r\n"
1287         "  <!--comment-->\r\n"
1288         "  <b />\r\n"
1289         "</a>");
1290 
1291     IStream_Release(stream);
1292 
1293     /* WriteElementString */
1294     stream = writer_set_output(writer);
1295 
1296     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1297     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1298 
1299     hr = IXmlWriter_WriteElementString(writer, NULL, bW, NULL, NULL);
1300     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1301 
1302     hr = IXmlWriter_WriteElementString(writer, NULL, bW, NULL, NULL);
1303     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1304 
1305     hr = IXmlWriter_WriteEndElement(writer);
1306     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1307 
1308     hr = IXmlWriter_Flush(writer);
1309     ok(hr == S_OK, "Unexpected hr %#x.\n", hr);
1310 
1311     CHECK_OUTPUT(stream,
1312         "<a>\r\n"
1313         "  <b />\r\n"
1314         "  <b />\r\n"
1315         "</a>");
1316 
1317     IStream_Release(stream);
1318 
1319     IXmlWriter_Release(writer);
1320 }
1321 
1322 static void test_WriteAttributeString(void)
1323 {
1324     static const WCHAR prefixW[] = {'p','r','e','f','i','x',0};
1325     static const WCHAR localW[] = {'l','o','c','a','l',0};
1326     static const WCHAR uriW[] = {'u','r','i',0};
1327     static const WCHAR uri2W[] = {'u','r','i','2',0};
1328     static const WCHAR xmlnsW[] = {'x','m','l','n','s',0};
1329     static const WCHAR aW[] = {'a',0};
1330     static const WCHAR bW[] = {'b',0};
1331     IXmlWriter *writer;
1332     IStream *stream;
1333     HRESULT hr;
1334 
1335     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1336     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1337 
1338     stream = writer_set_output(writer);
1339 
1340     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1341 
1342     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1343     ok(hr == S_OK, "got 0x%08x\n", hr);
1344 
1345     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1346     ok(hr == S_OK, "got 0x%08x\n", hr);
1347 
1348     hr = IXmlWriter_WriteAttributeString(writer, NULL, aW, NULL, bW);
1349     ok(hr == S_OK, "got 0x%08x\n", hr);
1350 
1351     hr = IXmlWriter_WriteEndDocument(writer);
1352     ok(hr == S_OK, "got 0x%08x\n", hr);
1353 
1354     hr = IXmlWriter_Flush(writer);
1355     ok(hr == S_OK, "got 0x%08x\n", hr);
1356 
1357     CHECK_OUTPUT(stream,
1358         "<a a=\"b\" />");
1359     IStream_Release(stream);
1360 
1361     /* with namespaces */
1362     stream = writer_set_output(writer);
1363 
1364     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1365     ok(hr == S_OK, "got 0x%08x\n", hr);
1366 
1367     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1368     ok(hr == S_OK, "got 0x%08x\n", hr);
1369 
1370     hr = IXmlWriter_WriteAttributeString(writer, aW, NULL, NULL, bW);
1371 todo_wine
1372     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
1373 
1374     hr = IXmlWriter_WriteAttributeString(writer, prefixW, localW, uriW, bW);
1375 todo_wine
1376     ok(hr == S_OK, "got 0x%08x\n", hr);
1377 
1378     hr = IXmlWriter_WriteAttributeString(writer, NULL, aW, NULL, bW);
1379     ok(hr == S_OK, "got 0x%08x\n", hr);
1380 
1381     hr = IXmlWriter_WriteAttributeString(writer, NULL, xmlnsW, uri2W, NULL);
1382 todo_wine
1383     ok(hr == WR_E_XMLNSPREFIXDECLARATION, "got 0x%08x\n", hr);
1384 
1385     hr = IXmlWriter_WriteAttributeString(writer, NULL, xmlnsW, NULL, uri2W);
1386 todo_wine
1387     ok(hr == WR_E_NSPREFIXDECLARED, "got 0x%08x\n", hr);
1388 
1389     hr = IXmlWriter_WriteAttributeString(writer, prefixW, localW, NULL, bW);
1390 todo_wine
1391     ok(hr == WR_E_DUPLICATEATTRIBUTE, "got 0x%08x\n", hr);
1392 
1393     hr = IXmlWriter_WriteEndDocument(writer);
1394     ok(hr == S_OK, "got 0x%08x\n", hr);
1395 
1396     hr = IXmlWriter_Flush(writer);
1397     ok(hr == S_OK, "got 0x%08x\n", hr);
1398 
1399     CHECK_OUTPUT_TODO(stream,
1400         "<a prefix:local=\"b\" a=\"b\" xmlns:prefix=\"uri\" />");
1401 
1402     IXmlWriter_Release(writer);
1403     IStream_Release(stream);
1404 }
1405 
1406 static void test_WriteFullEndElement(void)
1407 {
1408     static const WCHAR aW[] = {'a',0};
1409     IXmlWriter *writer;
1410     IStream *stream;
1411     HRESULT hr;
1412 
1413     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1414     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1415 
1416     /* standalone element */
1417     stream = writer_set_output(writer);
1418 
1419     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1420     writer_set_property(writer, XmlWriterProperty_Indent);
1421 
1422     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1423     ok(hr == S_OK, "got 0x%08x\n", hr);
1424 
1425     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1426     ok(hr == S_OK, "got 0x%08x\n", hr);
1427 
1428     hr = IXmlWriter_WriteFullEndElement(writer);
1429     ok(hr == S_OK, "got 0x%08x\n", hr);
1430 
1431     hr = IXmlWriter_WriteEndDocument(writer);
1432     ok(hr == S_OK, "got 0x%08x\n", hr);
1433 
1434     hr = IXmlWriter_Flush(writer);
1435     ok(hr == S_OK, "got 0x%08x\n", hr);
1436 
1437     CHECK_OUTPUT(stream,
1438         "<a></a>");
1439     IStream_Release(stream);
1440 
1441     /* nested elements */
1442     stream = writer_set_output(writer);
1443 
1444     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1445     writer_set_property(writer, XmlWriterProperty_Indent);
1446 
1447     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1448     ok(hr == S_OK, "got 0x%08x\n", hr);
1449 
1450     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1451     ok(hr == S_OK, "got 0x%08x\n", hr);
1452 
1453     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1454     ok(hr == S_OK, "got 0x%08x\n", hr);
1455 
1456     hr = IXmlWriter_WriteFullEndElement(writer);
1457     ok(hr == S_OK, "got 0x%08x\n", hr);
1458 
1459     hr = IXmlWriter_WriteEndDocument(writer);
1460     ok(hr == S_OK, "got 0x%08x\n", hr);
1461 
1462     hr = IXmlWriter_Flush(writer);
1463     ok(hr == S_OK, "got 0x%08x\n", hr);
1464 
1465     CHECK_OUTPUT(stream,
1466         "<a>\r\n"
1467         "  <a></a>\r\n"
1468         "</a>");
1469 
1470     IXmlWriter_Release(writer);
1471     IStream_Release(stream);
1472 }
1473 
1474 static void test_WriteCharEntity(void)
1475 {
1476     static const WCHAR aW[] = {'a',0};
1477     IXmlWriter *writer;
1478     IStream *stream;
1479     HRESULT hr;
1480 
1481     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1482     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1483 
1484     /* without indentation */
1485     stream = writer_set_output(writer);
1486 
1487     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1488 
1489     hr = IXmlWriter_WriteStartDocument(writer, XmlStandalone_Omit);
1490     ok(hr == S_OK, "got 0x%08x\n", hr);
1491 
1492     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1493     ok(hr == S_OK, "got 0x%08x\n", hr);
1494 
1495     hr = IXmlWriter_WriteCharEntity(writer, 0x100);
1496     ok(hr == S_OK, "got 0x%08x\n", hr);
1497 
1498     hr = IXmlWriter_WriteStartElement(writer, NULL, aW, NULL);
1499     ok(hr == S_OK, "got 0x%08x\n", hr);
1500 
1501     hr = IXmlWriter_WriteEndDocument(writer);
1502     ok(hr == S_OK, "got 0x%08x\n", hr);
1503 
1504     hr = IXmlWriter_Flush(writer);
1505     ok(hr == S_OK, "got 0x%08x\n", hr);
1506 
1507     CHECK_OUTPUT(stream,
1508         "<a>&#x100;<a /></a>");
1509 
1510     IXmlWriter_Release(writer);
1511     IStream_Release(stream);
1512 }
1513 
1514 static void test_WriteString(void)
1515 {
1516     static const WCHAR markupW[] = {'<','&','"','>','=',0};
1517     static const WCHAR aW[] = {'a',0};
1518     static const WCHAR bW[] = {'b',0};
1519     static const WCHAR emptyW[] = {0};
1520     IXmlWriter *writer;
1521     IStream *stream;
1522     HRESULT hr;
1523 
1524     hr = CreateXmlWriter(&IID_IXmlWriter, (void**)&writer, NULL);
1525     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
1526 
1527     writer_set_property(writer, XmlWriterProperty_OmitXmlDeclaration);
1528 
1529     hr = IXmlWriter_WriteString(writer, aW);
1530     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
1531 
1532     hr = IXmlWriter_WriteString(writer, NULL);
1533     ok(hr == S_OK, "got 0x%08x\n", hr);
1534 
1535     hr = IXmlWriter_WriteString(writer, emptyW);
1536     ok(hr == E_UNEXPECTED, "got 0x%08x\n", hr);
1537 
1538     stream = writer_set_output(writer);
1539 
1540     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
1541     ok(hr == S_OK, "got 0x%08x\n", hr);
1542 
1543     hr = IXmlWriter_WriteString(writer, NULL);
1544     ok(hr == S_OK, "got 0x%08x\n", hr);
1545 
1546     hr = IXmlWriter_WriteString(writer, emptyW);
1547     ok(hr == S_OK, "got 0x%08x\n", hr);
1548 
1549     hr = IXmlWriter_WriteString(writer, aW);
1550     ok(hr == S_OK, "got 0x%08x\n", hr);
1551 
1552     /* WriteString automatically escapes markup characters */
1553     hr = IXmlWriter_WriteString(writer, markupW);
1554     ok(hr == S_OK, "got 0x%08x\n", hr);
1555 
1556     hr = IXmlWriter_Flush(writer);
1557     ok(hr == S_OK, "got 0x%08x\n", hr);
1558 
1559     CHECK_OUTPUT(stream,
1560         "<b>a&lt;&amp;\"&gt;=");
1561     IStream_Release(stream);
1562 
1563     stream = writer_set_output(writer);
1564 
1565     hr = IXmlWriter_WriteStartElement(writer, NULL, bW, NULL);
1566     ok(hr == S_OK, "got 0x%08x\n", hr);
1567 
1568     hr = IXmlWriter_WriteString(writer, NULL);
1569     ok(hr == S_OK, "got 0x%08x\n", hr);
1570 
1571     hr = IXmlWriter_Flush(writer);
1572     ok(hr == S_OK, "got 0x%08x\n", hr);
1573 
1574     CHECK_OUTPUT(stream,
1575         "<b");
1576 
1577     hr = IXmlWriter_WriteString(writer, emptyW);
1578     ok(hr == S_OK, "got 0x%08x\n", hr);
1579 
1580     hr = IXmlWriter_Flush(writer);
1581     ok(hr == S_OK, "got 0x%08x\n", hr);
1582 
1583     CHECK_OUTPUT(stream,
1584         "<b>");
1585 
1586     IXmlWriter_Release(writer);
1587     IStream_Release(stream);
1588 }
1589 
1590 START_TEST(writer)
1591 {
1592     test_writer_create();
1593     test_writer_state();
1594     test_writeroutput();
1595     test_writestartdocument();
1596     test_writestartelement();
1597     test_writeendelement();
1598     test_flush();
1599     test_omitxmldeclaration();
1600     test_bom();
1601     test_writeenddocument();
1602     test_WriteComment();
1603     test_WriteCData();
1604     test_WriteRaw();
1605     test_indentation();
1606     test_WriteAttributeString();
1607     test_WriteFullEndElement();
1608     test_WriteCharEntity();
1609     test_WriteString();
1610 }
1611