xmllite/tests: Add test for XML declaration parsing.
[wine] / dlls / xmllite / tests / reader.c
1 /*
2  * XMLLite IXmlReader tests
3  *
4  * Copyright 2010 (C) Nikolay Sivov
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 COBJMACROS
22
23 #include <stdarg.h>
24 #include <stdio.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "initguid.h"
29 #include "ole2.h"
30 #include "xmllite.h"
31 #include "wine/test.h"
32
33 DEFINE_GUID(IID_IXmlReaderInput, 0x0b3ccc9b, 0x9214, 0x428b, 0xa2, 0xae, 0xef, 0x3a, 0xa8, 0x71, 0xaf, 0xda);
34
35 HRESULT WINAPI (*pCreateXmlReader)(REFIID riid, void **ppvObject, IMalloc *pMalloc);
36 HRESULT WINAPI (*pCreateXmlReaderInputWithEncodingName)(IUnknown *stream,
37                                                         IMalloc *pMalloc,
38                                                         LPCWSTR encoding,
39                                                         BOOL hint,
40                                                         LPCWSTR base_uri,
41                                                         IXmlReaderInput **ppInput);
42 static const char *debugstr_guid(REFIID riid)
43 {
44     static char buf[50];
45
46     sprintf(buf, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
47             riid->Data1, riid->Data2, riid->Data3, riid->Data4[0],
48             riid->Data4[1], riid->Data4[2], riid->Data4[3], riid->Data4[4],
49             riid->Data4[5], riid->Data4[6], riid->Data4[7]);
50
51     return buf;
52 }
53
54 static const char xmldecl_full[] = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n";
55
56 static IStream *create_stream_on_data(const char *data, int size)
57 {
58     IStream *stream = NULL;
59     HGLOBAL hglobal;
60     void *ptr;
61     HRESULT hr;
62
63     hglobal = GlobalAlloc(GHND, size);
64     ptr = GlobalLock(hglobal);
65
66     memcpy(ptr, data, size);
67
68     hr = CreateStreamOnHGlobal(hglobal, TRUE, &stream);
69     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
70     ok(stream != NULL, "Expected non-NULL stream\n");
71
72     GlobalUnlock(hglobal);
73
74     return stream;
75 }
76
77 static void ok_pos_(IXmlReader *reader, int line, int pos, int todo, int _line_)
78 {
79     UINT l, p;
80     HRESULT hr;
81
82     hr = IXmlReader_GetLineNumber(reader, &l);
83     ok_(__FILE__, _line_)(hr == S_OK, "Expected S_OK, got %08x\n", hr);
84     hr = IXmlReader_GetLinePosition(reader, &p);
85     ok_(__FILE__, _line_)(hr == S_OK, "Expected S_OK, got %08x\n", hr);
86
87     if (todo)
88         todo_wine
89         ok_(__FILE__, _line_)(l == line && pos == p,
90                             "Expected (%d,%d), got (%d,%d)\n", line, pos, l, p);
91     else
92     {
93         ok_(__FILE__, _line_)(l == line && pos == p,
94                             "Expected (%d,%d), got (%d,%d)\n", line, pos, l, p);
95     }
96 }
97 #define ok_pos(reader, l, p, todo) ok_pos_(reader, l, p, todo, __LINE__)
98
99 typedef struct input_iids_t {
100     IID iids[10];
101     int count;
102 } input_iids_t;
103
104 static const IID *setinput_full[] = {
105     &IID_IXmlReaderInput,
106     &IID_IStream,
107     &IID_ISequentialStream,
108     NULL
109 };
110
111 /* this applies to early xmllite versions */
112 static const IID *setinput_full_old[] = {
113     &IID_IXmlReaderInput,
114     &IID_ISequentialStream,
115     &IID_IStream,
116     NULL
117 };
118
119 /* after ::SetInput(IXmlReaderInput*) */
120 static const IID *setinput_readerinput[] = {
121     &IID_IStream,
122     &IID_ISequentialStream,
123     NULL
124 };
125
126 static const IID *empty_seq[] = {
127     NULL
128 };
129
130 static input_iids_t input_iids;
131
132 static void ok_iids_(const input_iids_t *iids, const IID **expected, const IID **exp_broken, int todo, int line)
133 {
134     int i = 0, size = 0;
135
136     while (expected[i++]) size++;
137
138     if (todo) {
139         todo_wine
140             ok_(__FILE__, line)(iids->count == size, "Sequence size mismatch (%d), got (%d)\n", size, iids->count);
141     }
142     else
143        ok_(__FILE__, line)(iids->count == size, "Sequence size mismatch (%d), got (%d)\n", size, iids->count);
144
145     if (iids->count != size) return;
146
147     for (i = 0; i < size; i++) {
148         ok_(__FILE__, line)(IsEqualGUID(&iids->iids[i], expected[i]) ||
149             (exp_broken ? broken(IsEqualGUID(&iids->iids[i], exp_broken[i])) : FALSE),
150             "Wrong IID(%d), got (%s)\n", i, debugstr_guid(&iids->iids[i]));
151     }
152 }
153 #define ok_iids(got, exp, brk, todo) ok_iids_(got, exp, brk, todo, __LINE__)
154
155 static const char *state_to_str(XmlReadState state)
156 {
157     static const char* state_names[] = {
158         "XmlReadState_Initial",
159         "XmlReadState_Interactive",
160         "XmlReadState_Error",
161         "XmlReadState_EndOfFile",
162         "XmlReadState_Closed"
163     };
164
165     static const char unknown[] = "unknown";
166
167     switch (state)
168     {
169     case XmlReadState_Initial:
170     case XmlReadState_Interactive:
171     case XmlReadState_Error:
172     case XmlReadState_EndOfFile:
173     case XmlReadState_Closed:
174         return state_names[state];
175     default:
176         return unknown;
177     }
178 }
179
180 static const char *type_to_str(XmlNodeType type)
181 {
182     static const char* type_names[] = {
183         "XmlNodeType_None",
184         "XmlNodeType_Element",
185         "XmlNodeType_Attribute",
186         "XmlNodeType_Text",
187         "XmlNodeType_CDATA",
188         "XmlNodeType_ProcessingInstruction",
189         "XmlNodeType_Comment",
190         "XmlNodeType_DocumentType",
191         "XmlNodeType_Whitespace",
192         "XmlNodeType_EndElement",
193         "XmlNodeType_XmlDeclaration"
194     };
195
196     static const char unknown[] = "unknown";
197
198     switch (type)
199     {
200     case XmlNodeType_None:
201     case XmlNodeType_Element:
202     case XmlNodeType_Attribute:
203     case XmlNodeType_Text:
204     case XmlNodeType_CDATA:
205     case XmlNodeType_ProcessingInstruction:
206     case XmlNodeType_Comment:
207     case XmlNodeType_DocumentType:
208     case XmlNodeType_Whitespace:
209     case XmlNodeType_EndElement:
210     case XmlNodeType_XmlDeclaration:
211         return type_names[type];
212     default:
213         return unknown;
214     }
215 }
216
217 static void test_read_state_(IXmlReader *reader, XmlReadState expected, int todo, int line)
218 {
219     XmlReadState state;
220     HRESULT hr;
221
222     state = -1; /* invalid value */
223     hr = IXmlReader_GetProperty(reader, XmlReaderProperty_ReadState, (LONG_PTR*)&state);
224     ok_(__FILE__, line)(hr == S_OK, "Expected S_OK, got %08x\n", hr);
225
226     if (todo)
227     {
228     todo_wine
229         ok_(__FILE__, line)(state == expected, "Expected (%s), got (%s)\n",
230                                    state_to_str(expected), state_to_str(state));
231     }
232     else
233         ok_(__FILE__, line)(state == expected, "Expected (%s), got (%s)\n",
234                                    state_to_str(expected), state_to_str(state));
235 }
236
237 #define test_read_sate(reader, exp, todo) test_read_state_(reader, exp, todo, __LINE__)
238
239 typedef struct _testinput
240 {
241     const IUnknownVtbl *lpVtbl;
242     LONG ref;
243 } testinput;
244
245 static inline testinput *impl_from_IUnknown(IUnknown *iface)
246 {
247     return (testinput *)((char*)iface - FIELD_OFFSET(testinput, lpVtbl));
248 }
249
250 static HRESULT WINAPI testinput_QueryInterface(IUnknown *iface, REFIID riid, void** ppvObj)
251 {
252     if (IsEqualGUID( riid, &IID_IUnknown ))
253     {
254         *ppvObj = iface;
255         IUnknown_AddRef(iface);
256         return S_OK;
257     }
258
259     input_iids.iids[input_iids.count++] = *riid;
260
261     *ppvObj = NULL;
262
263     return E_NOINTERFACE;
264 }
265
266 static ULONG WINAPI testinput_AddRef(IUnknown *iface)
267 {
268     testinput *This = impl_from_IUnknown(iface);
269     return InterlockedIncrement(&This->ref);
270 }
271
272 static ULONG WINAPI testinput_Release(IUnknown *iface)
273 {
274     testinput *This = impl_from_IUnknown(iface);
275     LONG ref;
276
277     ref = InterlockedDecrement(&This->ref);
278     if (ref == 0)
279     {
280         HeapFree(GetProcessHeap(), 0, This);
281     }
282
283     return ref;
284 }
285
286 static const struct IUnknownVtbl testinput_vtbl =
287 {
288     testinput_QueryInterface,
289     testinput_AddRef,
290     testinput_Release
291 };
292
293 static HRESULT testinput_createinstance(void **ppObj)
294 {
295     testinput *input;
296
297     input = HeapAlloc(GetProcessHeap(), 0, sizeof (*input));
298     if(!input) return E_OUTOFMEMORY;
299
300     input->lpVtbl = &testinput_vtbl;
301     input->ref = 1;
302
303     *ppObj = &input->lpVtbl;
304
305     return S_OK;
306 }
307
308 static BOOL init_pointers(void)
309 {
310     /* don't free module here, it's to be unloaded on exit */
311     HMODULE mod = LoadLibraryA("xmllite.dll");
312
313     if (!mod)
314     {
315         win_skip("xmllite library not available\n");
316         return FALSE;
317     }
318
319 #define MAKEFUNC(f) if (!(p##f = (void*)GetProcAddress(mod, #f))) return FALSE;
320     MAKEFUNC(CreateXmlReader);
321     MAKEFUNC(CreateXmlReaderInputWithEncodingName);
322 #undef MAKEFUNC
323
324     return TRUE;
325 }
326
327 static void test_reader_create(void)
328 {
329     HRESULT hr;
330     IXmlReader *reader;
331     IUnknown *input;
332
333     /* crashes native */
334     if (0)
335     {
336         hr = pCreateXmlReader(&IID_IXmlReader, NULL, NULL);
337         hr = pCreateXmlReader(NULL, (LPVOID*)&reader, NULL);
338     }
339
340     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader, NULL);
341     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
342
343     test_read_sate(reader, XmlReadState_Closed, FALSE);
344
345     /* Null input pointer, releases previous input */
346     hr = IXmlReader_SetInput(reader, NULL);
347     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
348
349     test_read_sate(reader, XmlReadState_Closed, FALSE);
350
351     /* test input interface selection sequence */
352     hr = testinput_createinstance((void**)&input);
353     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
354
355     input_iids.count = 0;
356     hr = IXmlReader_SetInput(reader, input);
357     ok(hr == E_NOINTERFACE, "Expected E_NOINTERFACE, got %08x\n", hr);
358     ok_iids(&input_iids, setinput_full, setinput_full_old, FALSE);
359
360     IUnknown_Release(input);
361
362     IXmlReader_Release(reader);
363 }
364
365 static void test_readerinput(void)
366 {
367     IXmlReaderInput *reader_input;
368     IXmlReader *reader, *reader2;
369     IUnknown *obj, *input;
370     IStream *stream;
371     HRESULT hr;
372     LONG ref;
373
374     hr = pCreateXmlReaderInputWithEncodingName(NULL, NULL, NULL, FALSE, NULL, NULL);
375     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
376     hr = pCreateXmlReaderInputWithEncodingName(NULL, NULL, NULL, FALSE, NULL, &reader_input);
377     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
378
379     hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
380     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
381
382     ref = IStream_AddRef(stream);
383     ok(ref == 2, "Expected 2, got %d\n", ref);
384     IStream_Release(stream);
385     hr = pCreateXmlReaderInputWithEncodingName((IUnknown*)stream, NULL, NULL, FALSE, NULL, &reader_input);
386     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
387
388     /* IXmlReaderInput grabs a stream reference */
389     ref = IStream_AddRef(stream);
390     ok(ref == 3, "Expected 3, got %d\n", ref);
391     IStream_Release(stream);
392
393     /* try ::SetInput() with valid IXmlReaderInput */
394     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader, NULL);
395     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
396
397     ref = IUnknown_AddRef(reader_input);
398     ok(ref == 2, "Expected 2, got %d\n", ref);
399     IUnknown_Release(reader_input);
400
401     hr = IXmlReader_SetInput(reader, reader_input);
402     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
403
404     test_read_sate(reader, XmlReadState_Initial, FALSE);
405
406     /* IXmlReader grabs a IXmlReaderInput reference */
407     ref = IUnknown_AddRef(reader_input);
408     ok(ref == 3, "Expected 3, got %d\n", ref);
409     IUnknown_Release(reader_input);
410
411     ref = IStream_AddRef(stream);
412     ok(ref == 4, "Expected 4, got %d\n", ref);
413     IStream_Release(stream);
414
415     /* reset input and check state */
416     hr = IXmlReader_SetInput(reader, NULL);
417     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
418
419     test_read_sate(reader, XmlReadState_Closed, FALSE);
420
421     IXmlReader_Release(reader);
422
423     ref = IStream_AddRef(stream);
424     ok(ref == 3, "Expected 3, got %d\n", ref);
425     IStream_Release(stream);
426
427     ref = IUnknown_AddRef(reader_input);
428     ok(ref == 2, "Expected 2, got %d\n", ref);
429     IUnknown_Release(reader_input);
430
431     /* IID_IXmlReaderInput */
432     /* it returns a kind of private undocumented vtable incompatible with IUnknown,
433        so it's not a COM interface actually.
434        Such query will be used only to check if input is really IXmlReaderInput */
435     obj = (IUnknown*)0xdeadbeef;
436     hr = IUnknown_QueryInterface(reader_input, &IID_IXmlReaderInput, (void**)&obj);
437     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
438     ref = IUnknown_AddRef(reader_input);
439     ok(ref == 3, "Expected 3, got %d\n", ref);
440     IUnknown_Release(reader_input);
441
442     IUnknown_Release(reader_input);
443     IStream_Release(stream);
444
445     /* test input interface selection sequence */
446     hr = testinput_createinstance((void**)&input);
447     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
448
449     input_iids.count = 0;
450     ref = IUnknown_AddRef(input);
451     ok(ref == 2, "Expected 2, got %d\n", ref);
452     IUnknown_Release(input);
453     hr = pCreateXmlReaderInputWithEncodingName(input, NULL, NULL, FALSE, NULL, &reader_input);
454     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
455     ok_iids(&input_iids, empty_seq, NULL, FALSE);
456     /* IXmlReaderInput stores stream interface as IUnknown */
457     ref = IUnknown_AddRef(input);
458     ok(ref == 3, "Expected 3, got %d\n", ref);
459     IUnknown_Release(input);
460
461     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader, NULL);
462     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
463
464     input_iids.count = 0;
465     ref = IUnknown_AddRef(reader_input);
466     ok(ref == 2, "Expected 2, got %d\n", ref);
467     IUnknown_Release(reader_input);
468     ref = IUnknown_AddRef(input);
469     ok(ref == 3, "Expected 3, got %d\n", ref);
470     IUnknown_Release(input);
471     hr = IXmlReader_SetInput(reader, reader_input);
472     ok(hr == E_NOINTERFACE, "Expected E_NOINTERFACE, got %08x\n", hr);
473     ok_iids(&input_iids, setinput_readerinput, NULL, FALSE);
474
475     test_read_sate(reader, XmlReadState_Closed, FALSE);
476
477     ref = IUnknown_AddRef(input);
478     ok(ref == 3, "Expected 3, got %d\n", ref);
479     IUnknown_Release(input);
480
481     ref = IUnknown_AddRef(reader_input);
482     ok(ref == 2, "Expected 2, got %d\n", ref);
483     IUnknown_Release(reader_input);
484     /* repeat another time, no check or caching here */
485     input_iids.count = 0;
486     hr = IXmlReader_SetInput(reader, reader_input);
487     ok(hr == E_NOINTERFACE, "Expected E_NOINTERFACE, got %08x\n", hr);
488     ok_iids(&input_iids, setinput_readerinput, NULL, FALSE);
489
490     /* another reader */
491     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader2, NULL);
492     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
493
494     /* resolving from IXmlReaderInput to IStream/ISequentialStream is done at
495        ::SetInput() level, each time it's called */
496     input_iids.count = 0;
497     hr = IXmlReader_SetInput(reader2, reader_input);
498     ok(hr == E_NOINTERFACE, "Expected E_NOINTERFACE, got %08x\n", hr);
499     ok_iids(&input_iids, setinput_readerinput, NULL, FALSE);
500
501     IXmlReader_Release(reader2);
502     IXmlReader_Release(reader);
503
504     IUnknown_Release(reader_input);
505     IUnknown_Release(input);
506 }
507
508 static void test_reader_state(void)
509 {
510     IXmlReader *reader;
511     HRESULT hr;
512
513     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader, NULL);
514     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
515
516     /* invalid arguments */
517     hr = IXmlReader_GetProperty(reader, XmlReaderProperty_ReadState, NULL);
518     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
519
520     IXmlReader_Release(reader);
521 }
522
523 static void test_read_xmldeclaration(void)
524 {
525     IXmlReader *reader;
526     IStream *stream;
527     HRESULT hr;
528     XmlNodeType type;
529     UINT count = 0;
530
531     hr = pCreateXmlReader(&IID_IXmlReader, (LPVOID*)&reader, NULL);
532     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
533
534     /* position methods with Null args */
535     hr = IXmlReader_GetLineNumber(reader, NULL);
536     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
537
538     hr = IXmlReader_GetLinePosition(reader, NULL);
539     ok(hr == E_INVALIDARG, "Expected E_INVALIDARG, got %08x\n", hr);
540
541     stream = create_stream_on_data(xmldecl_full, sizeof(xmldecl_full));
542
543     hr = IXmlReader_SetInput(reader, (IUnknown*)stream);
544     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
545
546     ok_pos(reader, 0, 0, FALSE);
547
548     type = -1;
549     hr = IXmlReader_Read(reader, &type);
550 todo_wine {
551     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
552     ok(type == XmlNodeType_XmlDeclaration,
553                      "Expected XmlNodeType_XmlDeclaration, got %s\n", type_to_str(type));
554 }
555     ok_pos(reader, 1, 55, TRUE);
556
557     /* check attributes */
558     hr = IXmlReader_MoveToNextAttribute(reader);
559     todo_wine ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
560     ok_pos(reader, 1, 55, TRUE);
561
562     hr = IXmlReader_MoveToFirstAttribute(reader);
563     todo_wine ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
564     ok_pos(reader, 1, 55, TRUE);
565
566     hr = IXmlReader_GetAttributeCount(reader, &count);
567 todo_wine {
568     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
569     ok(count == 3, "Expected 3, got %d\n", count);
570 }
571     hr = IXmlReader_GetDepth(reader, &count);
572 todo_wine {
573     ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
574     ok(count == 1, "Expected 1, got %d\n", count);
575 }
576
577     IStream_Release(stream);
578     IXmlReader_Release(reader);
579 }
580
581 START_TEST(reader)
582 {
583     HRESULT r;
584
585     r = CoInitialize( NULL );
586     ok( r == S_OK, "failed to init com\n");
587
588     if (!init_pointers())
589     {
590        CoUninitialize();
591        return;
592     }
593
594     test_reader_create();
595     test_readerinput();
596     test_reader_state();
597     test_read_xmldeclaration();
598
599     CoUninitialize();
600 }