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