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