[MSXML3_WINETEST]
[reactos.git] / rostests / winetests / msxml3 / saxreader.c
1 /*
2 * SAXReader/MXWriter tests
3 *
4 * Copyright 2008 Piotr Caban
5 * Copyright 2011 Thomas Mullaly
6 * Copyright 2012 Nikolay Sivov
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 */
22
23 #define COBJMACROS
24 #define CONST_VTABLE
25
26 #include <stdio.h>
27 #include <assert.h>
28
29 #include "windows.h"
30 #include "ole2.h"
31 #include "msxml2.h"
32 #include "msxml2did.h"
33 #include "ocidl.h"
34 #include "dispex.h"
35
36 #include "wine/test.h"
37
38 #define EXPECT_HR(hr,hr_exp) \
39 ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp)
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 rc = IUnknown_AddRef(obj);
45 IUnknown_Release(obj);
46 ok_(__FILE__,line)(rc-1 == ref, "expected refcount %d, got %d\n", ref, rc-1);
47 }
48
49 static LONG get_refcount(void *iface)
50 {
51 IUnknown *unk = iface;
52 LONG ref;
53
54 ref = IUnknown_AddRef(unk);
55 IUnknown_Release(unk);
56 return ref-1;
57 }
58
59 struct msxmlsupported_data_t
60 {
61 const GUID *clsid;
62 const char *name;
63 BOOL supported;
64 };
65
66 static BOOL is_clsid_supported(const GUID *clsid, const struct msxmlsupported_data_t *table)
67 {
68 while (table->clsid)
69 {
70 if (table->clsid == clsid) return table->supported;
71 table++;
72 }
73 return FALSE;
74 }
75
76 static BSTR alloc_str_from_narrow(const char *str)
77 {
78 int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0);
79 BSTR ret = SysAllocStringLen(NULL, len - 1); /* NUL character added automatically */
80 MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len);
81 return ret;
82 }
83
84 static BSTR alloced_bstrs[512];
85 static int alloced_bstrs_count;
86
87 static BSTR _bstr_(const char *str)
88 {
89 assert(alloced_bstrs_count < sizeof(alloced_bstrs)/sizeof(alloced_bstrs[0]));
90 alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str);
91 return alloced_bstrs[alloced_bstrs_count++];
92 }
93
94 static void free_bstrs(void)
95 {
96 int i;
97 for (i = 0; i < alloced_bstrs_count; i++)
98 SysFreeString(alloced_bstrs[i]);
99 alloced_bstrs_count = 0;
100 }
101
102 static void test_saxstr(const char *file, unsigned line, BSTR str, const char *expected, int todo, int *failcount)
103 {
104 int len, lenexp, cmp;
105 WCHAR buf[1024];
106
107 len = SysStringLen(str);
108
109 if (!expected) {
110 if (str && todo)
111 {
112 (*failcount)++;
113 todo_wine
114 ok_(file, line) (!str, "got %p, expected null str\n", str);
115 }
116 else
117 ok_(file, line) (!str, "got %p, expected null str\n", str);
118
119 if (len && todo)
120 {
121 (*failcount)++;
122 todo_wine
123 ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
124 }
125 else
126 ok_(file, line) (len == 0, "got len %d, expected 0\n", len);
127 return;
128 }
129
130 lenexp = strlen(expected);
131 if (lenexp != len && todo)
132 {
133 (*failcount)++;
134 todo_wine
135 ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
136 }
137 else
138 ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected);
139
140 /* exit earlier on length mismatch */
141 if (lenexp != len) return;
142
143 MultiByteToWideChar(CP_ACP, 0, expected, -1, buf, sizeof(buf)/sizeof(WCHAR));
144
145 cmp = memcmp(str, buf, lenexp*sizeof(WCHAR));
146 if (cmp && todo)
147 {
148 (*failcount)++;
149 todo_wine
150 ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
151 wine_dbgstr_wn(str, len), expected);
152 }
153 else
154 ok_(file, line) (!cmp, "unexpected str %s, expected %s\n",
155 wine_dbgstr_wn(str, len), expected);
156 }
157
158 typedef enum _CH {
159 CH_ENDTEST,
160 CH_PUTDOCUMENTLOCATOR,
161 CH_STARTDOCUMENT,
162 CH_ENDDOCUMENT,
163 CH_STARTPREFIXMAPPING,
164 CH_ENDPREFIXMAPPING,
165 CH_STARTELEMENT,
166 CH_ENDELEMENT,
167 CH_CHARACTERS,
168 CH_IGNORABLEWHITESPACE,
169 CH_PROCESSINGINSTRUCTION,
170 CH_SKIPPEDENTITY,
171 EH_ERROR,
172 EH_FATALERROR,
173 EG_IGNORABLEWARNING,
174 EVENT_LAST
175 } CH;
176
177 static const char *event_names[EVENT_LAST] = {
178 "endtest",
179 "putDocumentLocator",
180 "startDocument",
181 "endDocument",
182 "startPrefixMapping",
183 "endPrefixMapping",
184 "startElement",
185 "endElement",
186 "characters",
187 "ignorableWhitespace",
188 "processingInstruction",
189 "skippedEntity",
190 "error",
191 "fatalError",
192 "ignorableWarning"
193 };
194
195 struct attribute_entry {
196 const char *uri;
197 const char *local;
198 const char *qname;
199 const char *value;
200
201 /* used for actual call data only, null for expected call data */
202 BSTR uriW;
203 BSTR localW;
204 BSTR qnameW;
205 BSTR valueW;
206 };
207
208 struct call_entry {
209 CH id;
210 int line;
211 int column;
212 HRESULT ret;
213 const char *arg1;
214 const char *arg2;
215 const char *arg3;
216
217 /* allocated once at startElement callback */
218 struct attribute_entry *attributes;
219 int attr_count;
220
221 /* used for actual call data only, null for expected call data */
222 BSTR arg1W;
223 BSTR arg2W;
224 BSTR arg3W;
225 };
226
227 struct call_sequence
228 {
229 int count;
230 int size;
231 struct call_entry *sequence;
232 };
233
234 #define CONTENT_HANDLER_INDEX 0
235 #define NUM_CALL_SEQUENCES 1
236 static struct call_sequence *sequences[NUM_CALL_SEQUENCES];
237
238 static void init_call_entry(ISAXLocator *locator, struct call_entry *call)
239 {
240 memset(call, 0, sizeof(*call));
241 ISAXLocator_getLineNumber(locator, &call->line);
242 ISAXLocator_getColumnNumber(locator, &call->column);
243 }
244
245 static void add_call(struct call_sequence **seq, int sequence_index,
246 const struct call_entry *call)
247 {
248 struct call_sequence *call_seq = seq[sequence_index];
249
250 if (!call_seq->sequence)
251 {
252 call_seq->size = 10;
253 call_seq->sequence = HeapAlloc(GetProcessHeap(), 0,
254 call_seq->size * sizeof (struct call_entry));
255 }
256
257 if (call_seq->count == call_seq->size)
258 {
259 call_seq->size *= 2;
260 call_seq->sequence = HeapReAlloc(GetProcessHeap(), 0,
261 call_seq->sequence,
262 call_seq->size * sizeof (struct call_entry));
263 }
264
265 assert(call_seq->sequence);
266
267 call_seq->sequence[call_seq->count].id = call->id;
268 call_seq->sequence[call_seq->count].line = call->line;
269 call_seq->sequence[call_seq->count].column = call->column;
270 call_seq->sequence[call_seq->count].arg1W = call->arg1W;
271 call_seq->sequence[call_seq->count].arg2W = call->arg2W;
272 call_seq->sequence[call_seq->count].arg3W = call->arg3W;
273 call_seq->sequence[call_seq->count].ret = call->ret;
274 call_seq->sequence[call_seq->count].attr_count = call->attr_count;
275 call_seq->sequence[call_seq->count].attributes = call->attributes;
276
277 call_seq->count++;
278 }
279
280 static inline void flush_sequence(struct call_sequence **seg, int sequence_index)
281 {
282 int i;
283
284 struct call_sequence *call_seq = seg[sequence_index];
285
286 for (i = 0; i < call_seq->count; i++)
287 {
288 int j;
289
290 for (j = 0; j < call_seq->sequence[i].attr_count; j++)
291 {
292 SysFreeString(call_seq->sequence[i].attributes[j].uriW);
293 SysFreeString(call_seq->sequence[i].attributes[j].localW);
294 SysFreeString(call_seq->sequence[i].attributes[j].qnameW);
295 }
296
297 SysFreeString(call_seq->sequence[i].arg1W);
298 SysFreeString(call_seq->sequence[i].arg2W);
299 SysFreeString(call_seq->sequence[i].arg3W);
300 }
301
302 HeapFree(GetProcessHeap(), 0, call_seq->sequence);
303 call_seq->sequence = NULL;
304 call_seq->count = call_seq->size = 0;
305 }
306
307 static inline void flush_sequences(struct call_sequence **seq, int n)
308 {
309 int i;
310 for (i = 0; i < n; i++)
311 flush_sequence(seq, i);
312 }
313
314 static const char *get_event_name(CH event)
315 {
316 return event_names[event];
317 }
318
319 static void compare_attributes(const struct call_entry *actual, const struct call_entry *expected, const char *context,
320 int todo, const char *file, int line, int *failcount)
321 {
322 int i, lenexp = 0;
323
324 /* attribute count is not stored for expected data */
325 if (expected->attributes)
326 {
327 struct attribute_entry *ptr = expected->attributes;
328 while (ptr->uri) { lenexp++; ptr++; };
329 }
330
331 /* check count first and exit earlier */
332 if (actual->attr_count != lenexp && todo)
333 {
334 (*failcount)++;
335 todo_wine
336 ok_(file, line) (FALSE, "%s: in event %s expecting attr count %d got %d\n",
337 context, get_event_name(actual->id), lenexp, actual->attr_count);
338 }
339 else
340 ok_(file, line) (actual->attr_count == lenexp, "%s: in event %s expecting attr count %d got %d\n",
341 context, get_event_name(actual->id), lenexp, actual->attr_count);
342
343 if (actual->attr_count != lenexp) return;
344
345 /* now compare all attributes strings */
346 for (i = 0; i < actual->attr_count; i++)
347 {
348 test_saxstr(file, line, actual->attributes[i].uriW, expected->attributes[i].uri, todo, failcount);
349 test_saxstr(file, line, actual->attributes[i].localW, expected->attributes[i].local, todo, failcount);
350 test_saxstr(file, line, actual->attributes[i].qnameW, expected->attributes[i].qname, todo, failcount);
351 test_saxstr(file, line, actual->attributes[i].valueW, expected->attributes[i].value, todo, failcount);
352 }
353 }
354
355 static void ok_sequence_(struct call_sequence **seq, int sequence_index,
356 const struct call_entry *expected, const char *context, int todo,
357 const char *file, int line)
358 {
359 struct call_sequence *call_seq = seq[sequence_index];
360 static const struct call_entry end_of_sequence = { CH_ENDTEST };
361 const struct call_entry *actual, *sequence;
362 int failcount = 0;
363
364 add_call(seq, sequence_index, &end_of_sequence);
365
366 sequence = call_seq->sequence;
367 actual = sequence;
368
369 while (expected->id != CH_ENDTEST && actual->id != CH_ENDTEST)
370 {
371 if (expected->id == actual->id)
372 {
373 /* always test position data */
374 if (expected->line != actual->line && todo)
375 {
376 todo_wine
377 {
378 failcount++;
379 ok_(file, line) (FALSE,
380 "%s: in event %s expecting line %d got %d\n",
381 context, get_event_name(actual->id), expected->line, actual->line);
382 }
383 }
384 else
385 {
386 ok_(file, line) (expected->line == actual->line,
387 "%s: in event %s expecting line %d got %d\n",
388 context, get_event_name(actual->id), expected->line, actual->line);
389 }
390
391 if (expected->column != actual->column && todo)
392 {
393 todo_wine
394 {
395 failcount++;
396 ok_(file, line) (FALSE,
397 "%s: in event %s expecting column %d got %d\n",
398 context, get_event_name(actual->id), expected->column, actual->column);
399 }
400 }
401 else
402 {
403 ok_(file, line) (expected->column == actual->column,
404 "%s: in event %s expecting column %d got %d\n",
405 context, get_event_name(actual->id), expected->column, actual->column);
406 }
407
408 switch (actual->id)
409 {
410 case CH_PUTDOCUMENTLOCATOR:
411 case CH_STARTDOCUMENT:
412 case CH_ENDDOCUMENT:
413 break;
414 case CH_STARTPREFIXMAPPING:
415 /* prefix, uri */
416 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
417 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
418 break;
419 case CH_ENDPREFIXMAPPING:
420 /* prefix */
421 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
422 break;
423 case CH_STARTELEMENT:
424 /* compare attributes */
425 compare_attributes(actual, expected, context, todo, file, line, &failcount);
426 /* fallthrough */
427 case CH_ENDELEMENT:
428 /* uri, localname, qname */
429 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
430 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
431 test_saxstr(file, line, actual->arg3W, expected->arg3, todo, &failcount);
432 break;
433 case CH_CHARACTERS:
434 case CH_IGNORABLEWHITESPACE:
435 /* char data */
436 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
437 break;
438 case CH_PROCESSINGINSTRUCTION:
439 /* target, data */
440 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
441 test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount);
442 break;
443 case CH_SKIPPEDENTITY:
444 /* name */
445 test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount);
446 break;
447 case EH_FATALERROR:
448 /* test return value only */
449 if (expected->ret != actual->ret && todo)
450 {
451 failcount++;
452 ok_(file, line) (FALSE,
453 "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
454 context, get_event_name(actual->id), expected->ret, actual->ret);
455 }
456 else
457 ok_(file, line) (expected->ret == actual->ret,
458 "%s: in event %s expecting ret 0x%08x got 0x%08x\n",
459 context, get_event_name(actual->id), expected->ret, actual->ret);
460 break;
461 case EH_ERROR:
462 case EG_IGNORABLEWARNING:
463 default:
464 ok(0, "%s: callback not handled, %s\n", context, get_event_name(actual->id));
465 }
466 expected++;
467 actual++;
468 }
469 else if (todo)
470 {
471 failcount++;
472 todo_wine
473 {
474 ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
475 context, get_event_name(expected->id), get_event_name(actual->id));
476 }
477
478 flush_sequence(seq, sequence_index);
479 return;
480 }
481 else
482 {
483 ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n",
484 context, get_event_name(expected->id), get_event_name(actual->id));
485 expected++;
486 actual++;
487 }
488 }
489
490 if (todo)
491 {
492 todo_wine
493 {
494 if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
495 {
496 failcount++;
497 ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
498 context, get_event_name(expected->id), get_event_name(actual->id));
499 }
500 }
501 }
502 else if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST)
503 {
504 ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n",
505 context, get_event_name(expected->id), get_event_name(actual->id));
506 }
507
508 if (todo && !failcount) /* succeeded yet marked todo */
509 {
510 todo_wine
511 {
512 ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context);
513 }
514 }
515
516 flush_sequence(seq, sequence_index);
517 }
518
519 #define ok_sequence(seq, index, exp, contx, todo) \
520 ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__)
521
522 static void init_call_sequences(struct call_sequence **seq, int n)
523 {
524 int i;
525
526 for (i = 0; i < n; i++)
527 seq[i] = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct call_sequence));
528 }
529
530 static const WCHAR szSimpleXML[] = {
531 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n',
532 '<','B','a','n','k','A','c','c','o','u','n','t','>','\n',
533 ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n',
534 ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n',
535 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0'
536 };
537
538 static const WCHAR carriage_ret_test[] = {
539 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n',
540 '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n',
541 '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n',
542 '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n',
543 '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n','\0'
544 };
545
546 static const WCHAR szUtf16XML[] = {
547 '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ',
548 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ',
549 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n'
550 };
551
552 static const CHAR szUtf16BOM[] = {0xff, 0xfe};
553
554 static const CHAR szUtf8XML[] =
555 "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\r\n";
556
557 static const char utf8xml2[] =
558 "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\r\n";
559
560 static const char testXML[] =
561 "<?xml version=\"1.0\" ?>\n"
562 "<BankAccount>\n"
563 " <Number>1234</Number>\n"
564 " <Name>Captain Ahab</Name>\n"
565 "</BankAccount>\n";
566
567 static const char test_attributes[] =
568 "<?xml version=\"1.0\" ?>\n"
569 "<document xmlns:test=\"prefix_test\" xmlns=\"prefix\" test:arg1=\"arg1\" arg2=\"arg2\" test:ar3=\"arg3\">\n"
570 "<node1 xmlns:p=\"test\" />"
571 "</document>\n";
572
573 static struct call_entry content_handler_test1[] = {
574 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
575 { CH_STARTDOCUMENT, 0, 0, S_OK },
576 { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
577 { CH_CHARACTERS, 2, 14, S_OK, "\n " },
578 { CH_STARTELEMENT, 3, 12, S_OK, "", "Number", "Number" },
579 { CH_CHARACTERS, 3, 12, S_OK, "1234" },
580 { CH_ENDELEMENT, 3, 18, S_OK, "", "Number", "Number" },
581 { CH_CHARACTERS, 3, 25, S_OK, "\n " },
582 { CH_STARTELEMENT, 4, 10, S_OK, "", "Name", "Name" },
583 { CH_CHARACTERS, 4, 10, S_OK, "Captain Ahab" },
584 { CH_ENDELEMENT, 4, 24, S_OK, "", "Name", "Name" },
585 { CH_CHARACTERS, 4, 29, S_OK, "\n" },
586 { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
587 { CH_ENDDOCUMENT, 0, 0, S_OK},
588 { CH_ENDTEST }
589 };
590
591 /* applies to versions 4 and 6 */
592 static struct call_entry content_handler_test1_alternate[] = {
593 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
594 { CH_STARTDOCUMENT, 1, 22, S_OK },
595 { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
596 { CH_CHARACTERS, 3, 4, S_OK, "\n " },
597 { CH_STARTELEMENT, 3, 11, S_OK, "", "Number", "Number" },
598 { CH_CHARACTERS, 3, 16, S_OK, "1234" },
599 { CH_ENDELEMENT, 3, 24, S_OK, "", "Number", "Number" },
600 { CH_CHARACTERS, 4, 4, S_OK, "\n " },
601 { CH_STARTELEMENT, 4, 9, S_OK, "", "Name", "Name" },
602 { CH_CHARACTERS, 4, 22, S_OK, "Captain Ahab" },
603 { CH_ENDELEMENT, 4, 28, S_OK, "", "Name", "Name" },
604 { CH_CHARACTERS, 5, 1, S_OK, "\n" },
605 { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
606 { CH_ENDDOCUMENT, 6, 0, S_OK },
607 { CH_ENDTEST }
608 };
609
610 static struct call_entry content_handler_test2[] = {
611 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
612 { CH_STARTDOCUMENT, 0, 0, S_OK },
613 { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" },
614 { CH_CHARACTERS, 2, 14, S_OK, "\n" },
615 { CH_CHARACTERS, 2, 16, S_OK, "\t" },
616 { CH_STARTELEMENT, 3, 10, S_OK, "", "Number", "Number" },
617 { CH_CHARACTERS, 3, 10, S_OK, "1234" },
618 { CH_ENDELEMENT, 3, 16, S_OK, "", "Number", "Number" },
619 { CH_CHARACTERS, 3, 23, S_OK, "\n" },
620 { CH_CHARACTERS, 3, 25, S_OK, "\t" },
621 { CH_STARTELEMENT, 4, 8, S_OK, "", "Name", "Name" },
622 { CH_CHARACTERS, 4, 8, S_OK, "Captain Ahab" },
623 { CH_ENDELEMENT, 4, 22, S_OK, "", "Name", "Name" },
624 { CH_CHARACTERS, 4, 27, S_OK, "\n" },
625 { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" },
626 { CH_ENDDOCUMENT, 0, 0, S_OK },
627 { CH_ENDTEST }
628 };
629
630 static struct call_entry content_handler_test2_alternate[] = {
631 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
632 { CH_STARTDOCUMENT, 1, 21, S_OK },
633 { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" },
634 { CH_CHARACTERS, 3, 0, S_OK, "\n" },
635 { CH_CHARACTERS, 3, 2, S_OK, "\t" },
636 { CH_STARTELEMENT, 3, 9, S_OK, "", "Number", "Number" },
637 { CH_CHARACTERS, 3, 14, S_OK, "1234" },
638 { CH_ENDELEMENT, 3, 22, S_OK, "", "Number", "Number" },
639 { CH_CHARACTERS, 4, 0, S_OK, "\n" },
640 { CH_CHARACTERS, 4, 2, S_OK, "\t" },
641 { CH_STARTELEMENT, 4, 7, S_OK, "", "Name", "Name" },
642 { CH_CHARACTERS, 4, 20, S_OK, "Captain Ahab" },
643 { CH_ENDELEMENT, 4, 26, S_OK, "", "Name", "Name" },
644 { CH_CHARACTERS, 5, 0, S_OK, "\n" },
645 { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" },
646 { CH_ENDDOCUMENT, 6, 0, S_OK },
647 { CH_ENDTEST }
648 };
649
650 static struct call_entry content_handler_testerror[] = {
651 { CH_PUTDOCUMENTLOCATOR, 0, 0, E_FAIL },
652 { EH_FATALERROR, 0, 0, E_FAIL },
653 { CH_ENDTEST }
654 };
655
656 static struct call_entry content_handler_testerror_alternate[] = {
657 { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL },
658 { EH_FATALERROR, 1, 0, E_FAIL },
659 { CH_ENDTEST }
660 };
661
662 static struct call_entry content_handler_test_callback_rets[] = {
663 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_FALSE },
664 { CH_STARTDOCUMENT, 0, 0, S_FALSE },
665 { EH_FATALERROR, 0, 0, S_FALSE },
666 { CH_ENDTEST }
667 };
668
669 static struct call_entry content_handler_test_callback_rets_alt[] = {
670 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE },
671 { CH_STARTDOCUMENT, 1, 22, S_FALSE },
672 { CH_STARTELEMENT, 2, 13, S_FALSE, "", "BankAccount", "BankAccount" },
673 { CH_CHARACTERS, 3, 4, S_FALSE, "\n " },
674 { CH_STARTELEMENT, 3, 11, S_FALSE, "", "Number", "Number" },
675 { CH_CHARACTERS, 3, 16, S_FALSE, "1234" },
676 { CH_ENDELEMENT, 3, 24, S_FALSE, "", "Number", "Number" },
677 { CH_CHARACTERS, 4, 4, S_FALSE, "\n " },
678 { CH_STARTELEMENT, 4, 9, S_FALSE, "", "Name", "Name" },
679 { CH_CHARACTERS, 4, 22, S_FALSE, "Captain Ahab" },
680 { CH_ENDELEMENT, 4, 28, S_FALSE, "", "Name", "Name" },
681 { CH_CHARACTERS, 5, 1, S_FALSE, "\n" },
682 { CH_ENDELEMENT, 5, 14, S_FALSE, "", "BankAccount", "BankAccount" },
683 { CH_ENDDOCUMENT, 6, 0, S_FALSE },
684 { CH_ENDTEST }
685 };
686
687 static struct attribute_entry ch_attributes1[] = {
688 { "", "", "xmlns:test", "prefix_test" },
689 { "", "", "xmlns", "prefix" },
690 { "prefix_test", "arg1", "test:arg1", "arg1" },
691 { "", "arg2", "arg2", "arg2" },
692 { "prefix_test", "ar3", "test:ar3", "arg3" },
693 { NULL }
694 };
695
696 static struct attribute_entry ch_attributes2[] = {
697 { "", "", "xmlns:p", "test" },
698 { NULL }
699 };
700
701 static struct call_entry content_handler_test_attributes[] = {
702 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
703 { CH_STARTDOCUMENT, 0, 0, S_OK },
704 { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
705 { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
706 { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes1 },
707 { CH_CHARACTERS, 2, 96, S_OK, "\n" },
708 { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
709 { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", ch_attributes2 },
710 { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
711 { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
712 { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
713 { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
714 { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
715 { CH_ENDDOCUMENT, 0, 0 },
716 { CH_ENDTEST }
717 };
718
719 static struct attribute_entry ch_attributes_alt_4[] = {
720 { "prefix_test", "arg1", "test:arg1", "arg1" },
721 { "", "arg2", "arg2", "arg2" },
722 { "prefix_test", "ar3", "test:ar3", "arg3" },
723 { "", "", "xmlns:test", "prefix_test" },
724 { "", "", "xmlns", "prefix" },
725 { NULL }
726 };
727
728 static struct call_entry content_handler_test_attributes_alternate_4[] = {
729 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
730 { CH_STARTDOCUMENT, 1, 22, S_OK },
731 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
732 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
733 { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_4 },
734 { CH_CHARACTERS, 3, 1, S_OK, "\n" },
735 { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
736 { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2 },
737 { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
738 { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
739 { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
740 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
741 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
742 { CH_ENDDOCUMENT, 4, 0, S_OK },
743 { CH_ENDTEST }
744 };
745
746 /* 'namespace' feature switched off */
747 static struct attribute_entry ch_attributes_alt_no_ns[] = {
748 { "", "", "xmlns:test", "prefix_test" },
749 { "", "", "xmlns", "prefix" },
750 { "", "", "test:arg1", "arg1" },
751 { "", "", "arg2", "arg2" },
752 { "", "", "test:ar3", "arg3" },
753 { NULL }
754 };
755
756 static struct call_entry content_handler_test_attributes_alt_no_ns[] = {
757 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
758 { CH_STARTDOCUMENT, 1, 22, S_OK },
759 { CH_STARTELEMENT, 2, 95, S_OK, "", "", "document", ch_attributes_alt_no_ns },
760 { CH_CHARACTERS, 3, 1, S_OK, "\n" },
761 { CH_STARTELEMENT, 3, 24, S_OK, "", "", "node1", ch_attributes2 },
762 { CH_ENDELEMENT, 3, 24, S_OK, "", "", "node1" },
763 { CH_ENDELEMENT, 3, 35, S_OK, "", "", "document" },
764 { CH_ENDDOCUMENT, 4, 0, S_OK },
765 { CH_ENDTEST }
766 };
767
768 static struct attribute_entry ch_attributes_alt_6[] = {
769 { "prefix_test", "arg1", "test:arg1", "arg1" },
770 { "", "arg2", "arg2", "arg2" },
771 { "prefix_test", "ar3", "test:ar3", "arg3" },
772 { "http://www.w3.org/2000/xmlns/", "", "xmlns:test", "prefix_test" },
773 { "http://www.w3.org/2000/xmlns/", "", "xmlns", "prefix" },
774 { NULL }
775 };
776
777 static struct attribute_entry ch_attributes2_6[] = {
778 { "http://www.w3.org/2000/xmlns/", "", "xmlns:p", "test" },
779 { NULL }
780 };
781
782 static struct call_entry content_handler_test_attributes_alternate_6[] = {
783 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
784 { CH_STARTDOCUMENT, 1, 22, S_OK },
785 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
786 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
787 { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_6 },
788 { CH_CHARACTERS, 3, 1, S_OK, "\n" },
789 { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
790 { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2_6 },
791 { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
792 { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
793 { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
794 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
795 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
796 { CH_ENDDOCUMENT, 4, 0, S_OK },
797 { CH_ENDTEST }
798 };
799
800 /* 'namespaces' is on, 'namespace-prefixes' if off */
801 static struct attribute_entry ch_attributes_no_prefix[] = {
802 { "prefix_test", "arg1", "test:arg1", "arg1" },
803 { "", "arg2", "arg2", "arg2" },
804 { "prefix_test", "ar3", "test:ar3", "arg3" },
805 { NULL }
806 };
807
808 static struct call_entry content_handler_test_attributes_alt_no_prefix[] = {
809 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
810 { CH_STARTDOCUMENT, 1, 22, S_OK },
811 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" },
812 { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" },
813 { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
814 { CH_CHARACTERS, 3, 1, S_OK, "\n" },
815 { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" },
816 { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", NULL },
817 { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" },
818 { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" },
819 { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" },
820 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" },
821 { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" },
822 { CH_ENDDOCUMENT, 4, 0, S_OK },
823 { CH_ENDTEST }
824 };
825
826 static struct call_entry content_handler_test_attributes_no_prefix[] = {
827 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
828 { CH_STARTDOCUMENT, 0, 0, S_OK },
829 { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" },
830 { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" },
831 { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes_no_prefix },
832 { CH_CHARACTERS, 2, 96, S_OK, "\n" },
833 { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" },
834 { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", NULL },
835 { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" },
836 { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" },
837 { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" },
838 { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" },
839 { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" },
840 { CH_ENDDOCUMENT, 0, 0 },
841 { CH_ENDTEST }
842 };
843
844 static struct attribute_entry xmlspace_attrs[] = {
845 { "http://www.w3.org/XML/1998/namespace", "space", "xml:space", "preserve" },
846 { NULL }
847 };
848
849 static struct call_entry xmlspaceattr_test[] = {
850 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
851 { CH_STARTDOCUMENT, 0, 0, S_OK },
852 { CH_STARTELEMENT, 1, 64, S_OK, "", "a", "a", xmlspace_attrs },
853 { CH_CHARACTERS, 1, 64, S_OK, " Some text data " },
854 { CH_ENDELEMENT, 1, 82, S_OK, "", "a", "a" },
855 { CH_ENDDOCUMENT, 0, 0, S_OK },
856 { CH_ENDTEST }
857 };
858
859 static struct call_entry xmlspaceattr_test_alternate[] = {
860 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
861 { CH_STARTDOCUMENT, 1, 39, S_OK },
862 { CH_STARTELEMENT, 1, 63, S_OK, "", "a", "a", xmlspace_attrs },
863 { CH_CHARACTERS, 1, 80, S_OK, " Some text data " },
864 { CH_ENDELEMENT, 1, 83, S_OK, "", "a", "a" },
865 { CH_ENDDOCUMENT, 1, 83, S_OK },
866 { CH_ENDTEST }
867 };
868
869 /* attribute value normalization test */
870 static const char attribute_normalize[] =
871 "<?xml version=\"1.0\" ?>\n"
872 "<a attr1=\" \r \n \tattr_value &#65; \t \r \n\r\n \n\"/>\n";
873
874 static struct attribute_entry attribute_norm_attrs[] = {
875 { "", "attr1", "attr1", " attr_value A " },
876 { NULL }
877 };
878
879 static struct call_entry attribute_norm[] = {
880 { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK },
881 { CH_STARTDOCUMENT, 0, 0, S_OK },
882 { CH_STARTELEMENT, 6, 4, S_OK, "", "a", "a", attribute_norm_attrs },
883 { CH_ENDELEMENT, 6, 4, S_OK, "", "a", "a" },
884 { CH_ENDDOCUMENT, 0, 0, S_OK },
885 { CH_ENDTEST }
886 };
887
888 static struct call_entry attribute_norm_alt[] = {
889 { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK },
890 { CH_STARTDOCUMENT, 1, 22, S_OK },
891 { CH_STARTELEMENT, 8, 3, S_OK, "", "a", "a", attribute_norm_attrs },
892 { CH_ENDELEMENT, 8, 3, S_OK, "", "a", "a" },
893 { CH_ENDDOCUMENT, 9, 0, S_OK },
894 { CH_ENDTEST }
895 };
896
897 static const char xmlspace_attr[] =
898 "<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
899 "<a xml:space=\"preserve\"> Some text data </a>";
900
901 static struct call_entry *expectCall;
902 static ISAXLocator *locator;
903 static ISAXXMLReader *g_reader;
904 int msxml_version;
905
906 static void set_expected_seq(struct call_entry *expected)
907 {
908 expectCall = expected;
909 }
910
911 /* to be called once on each tested callback return */
912 static HRESULT get_expected_ret(void)
913 {
914 HRESULT hr = expectCall->ret;
915 if (expectCall->id != CH_ENDTEST) expectCall++;
916 return hr;
917 }
918
919 static HRESULT WINAPI contentHandler_QueryInterface(
920 ISAXContentHandler* iface,
921 REFIID riid,
922 void **ppvObject)
923 {
924 *ppvObject = NULL;
925
926 if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler))
927 {
928 *ppvObject = iface;
929 }
930 else
931 {
932 return E_NOINTERFACE;
933 }
934
935 return S_OK;
936 }
937
938 static ULONG WINAPI contentHandler_AddRef(
939 ISAXContentHandler* iface)
940 {
941 return 2;
942 }
943
944 static ULONG WINAPI contentHandler_Release(
945 ISAXContentHandler* iface)
946 {
947 return 1;
948 }
949
950 static HRESULT WINAPI contentHandler_putDocumentLocator(
951 ISAXContentHandler* iface,
952 ISAXLocator *pLocator)
953 {
954 struct call_entry call;
955 HRESULT hr;
956
957 locator = pLocator;
958
959 init_call_entry(locator, &call);
960 call.id = CH_PUTDOCUMENTLOCATOR;
961 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
962
963 if (msxml_version >= 6) {
964 ISAXAttributes *attr, *attr1;
965 IMXAttributes *mxattr;
966
967 EXPECT_REF(pLocator, 1);
968 hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr);
969 EXPECT_HR(hr, S_OK);
970 EXPECT_REF(pLocator, 2);
971 hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1);
972 EXPECT_HR(hr, S_OK);
973 EXPECT_REF(pLocator, 3);
974 ok(attr == attr1, "got %p, %p\n", attr, attr1);
975
976 hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr);
977 EXPECT_HR(hr, E_NOINTERFACE);
978
979 ISAXAttributes_Release(attr);
980 ISAXAttributes_Release(attr1);
981 }
982
983 return get_expected_ret();
984 }
985
986 static ISAXAttributes *test_attr_ptr;
987 static HRESULT WINAPI contentHandler_startDocument(
988 ISAXContentHandler* iface)
989 {
990 struct call_entry call;
991
992 init_call_entry(locator, &call);
993 call.id = CH_STARTDOCUMENT;
994 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
995
996 test_attr_ptr = NULL;
997
998 return get_expected_ret();
999 }
1000
1001 static HRESULT WINAPI contentHandler_endDocument(
1002 ISAXContentHandler* iface)
1003 {
1004 struct call_entry call;
1005
1006 init_call_entry(locator, &call);
1007 call.id = CH_ENDDOCUMENT;
1008 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1009
1010 return get_expected_ret();
1011 }
1012
1013 static HRESULT WINAPI contentHandler_startPrefixMapping(
1014 ISAXContentHandler* iface,
1015 const WCHAR *prefix, int prefix_len,
1016 const WCHAR *uri, int uri_len)
1017 {
1018 struct call_entry call;
1019
1020 init_call_entry(locator, &call);
1021 call.id = CH_STARTPREFIXMAPPING;
1022 call.arg1W = SysAllocStringLen(prefix, prefix_len);
1023 call.arg2W = SysAllocStringLen(uri, uri_len);
1024 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1025
1026 return get_expected_ret();
1027 }
1028
1029 static HRESULT WINAPI contentHandler_endPrefixMapping(
1030 ISAXContentHandler* iface,
1031 const WCHAR *prefix, int len)
1032 {
1033 struct call_entry call;
1034
1035 init_call_entry(locator, &call);
1036 call.id = CH_ENDPREFIXMAPPING;
1037 call.arg1W = SysAllocStringLen(prefix, len);
1038 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1039
1040 return get_expected_ret();
1041 }
1042
1043 static HRESULT WINAPI contentHandler_startElement(
1044 ISAXContentHandler* iface,
1045 const WCHAR *uri, int uri_len,
1046 const WCHAR *localname, int local_len,
1047 const WCHAR *qname, int qname_len,
1048 ISAXAttributes *saxattr)
1049 {
1050 struct call_entry call;
1051 IMXAttributes *mxattr;
1052 HRESULT hr;
1053 int len;
1054
1055 hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr);
1056 EXPECT_HR(hr, E_NOINTERFACE);
1057
1058 init_call_entry(locator, &call);
1059 call.id = CH_STARTELEMENT;
1060 call.arg1W = SysAllocStringLen(uri, uri_len);
1061 call.arg2W = SysAllocStringLen(localname, local_len);
1062 call.arg3W = SysAllocStringLen(qname, qname_len);
1063
1064 if(!test_attr_ptr)
1065 test_attr_ptr = saxattr;
1066 ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr);
1067
1068 /* store actual attributes */
1069 len = 0;
1070 hr = ISAXAttributes_getLength(saxattr, &len);
1071 EXPECT_HR(hr, S_OK);
1072
1073 if (len)
1074 {
1075 VARIANT_BOOL v;
1076 int i;
1077
1078 struct attribute_entry *attr;
1079 attr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len*sizeof(struct attribute_entry));
1080
1081 v = VARIANT_TRUE;
1082 hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v);
1083 EXPECT_HR(hr, S_OK);
1084
1085 for (i = 0; i < len; i++)
1086 {
1087 const WCHAR *value;
1088 int value_len;
1089
1090 hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len,
1091 &localname, &local_len, &qname, &qname_len);
1092 EXPECT_HR(hr, S_OK);
1093
1094 hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len);
1095 EXPECT_HR(hr, S_OK);
1096
1097 /* if 'namespaces' switched off uri and local name contains garbage */
1098 if (v == VARIANT_FALSE && msxml_version > 0)
1099 {
1100 attr[i].uriW = SysAllocStringLen(NULL, 0);
1101 attr[i].localW = SysAllocStringLen(NULL, 0);
1102 }
1103 else
1104 {
1105 attr[i].uriW = SysAllocStringLen(uri, uri_len);
1106 attr[i].localW = SysAllocStringLen(localname, local_len);
1107 }
1108
1109 attr[i].qnameW = SysAllocStringLen(qname, qname_len);
1110 attr[i].valueW = SysAllocStringLen(value, value_len);
1111 }
1112
1113 call.attributes = attr;
1114 call.attr_count = len;
1115 }
1116
1117 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1118
1119 return get_expected_ret();
1120 }
1121
1122 static HRESULT WINAPI contentHandler_endElement(
1123 ISAXContentHandler* iface,
1124 const WCHAR *uri, int uri_len,
1125 const WCHAR *localname, int local_len,
1126 const WCHAR *qname, int qname_len)
1127 {
1128 struct call_entry call;
1129
1130 init_call_entry(locator, &call);
1131 call.id = CH_ENDELEMENT;
1132 call.arg1W = SysAllocStringLen(uri, uri_len);
1133 call.arg2W = SysAllocStringLen(localname, local_len);
1134 call.arg3W = SysAllocStringLen(qname, qname_len);
1135 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1136
1137 return get_expected_ret();
1138 }
1139
1140 static HRESULT WINAPI contentHandler_characters(
1141 ISAXContentHandler* iface,
1142 const WCHAR *chars,
1143 int len)
1144 {
1145 struct call_entry call;
1146
1147 init_call_entry(locator, &call);
1148 call.id = CH_CHARACTERS;
1149 call.arg1W = SysAllocStringLen(chars, len);
1150 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1151
1152 return get_expected_ret();
1153 }
1154
1155 static HRESULT WINAPI contentHandler_ignorableWhitespace(
1156 ISAXContentHandler* iface,
1157 const WCHAR *chars, int len)
1158 {
1159 struct call_entry call;
1160
1161 init_call_entry(locator, &call);
1162 call.id = CH_IGNORABLEWHITESPACE;
1163 call.arg1W = SysAllocStringLen(chars, len);
1164 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1165
1166 return get_expected_ret();
1167 }
1168
1169 static HRESULT WINAPI contentHandler_processingInstruction(
1170 ISAXContentHandler* iface,
1171 const WCHAR *target, int target_len,
1172 const WCHAR *data, int data_len)
1173 {
1174 struct call_entry call;
1175
1176 init_call_entry(locator, &call);
1177 call.id = CH_PROCESSINGINSTRUCTION;
1178 call.arg1W = SysAllocStringLen(target, target_len);
1179 call.arg2W = SysAllocStringLen(data, data_len);
1180 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1181
1182 return get_expected_ret();
1183 }
1184
1185 static HRESULT WINAPI contentHandler_skippedEntity(
1186 ISAXContentHandler* iface,
1187 const WCHAR *name, int len)
1188 {
1189 struct call_entry call;
1190
1191 init_call_entry(locator, &call);
1192 call.id = CH_SKIPPEDENTITY;
1193 call.arg1W = SysAllocStringLen(name, len);
1194
1195 return get_expected_ret();
1196 }
1197
1198 static const ISAXContentHandlerVtbl contentHandlerVtbl =
1199 {
1200 contentHandler_QueryInterface,
1201 contentHandler_AddRef,
1202 contentHandler_Release,
1203 contentHandler_putDocumentLocator,
1204 contentHandler_startDocument,
1205 contentHandler_endDocument,
1206 contentHandler_startPrefixMapping,
1207 contentHandler_endPrefixMapping,
1208 contentHandler_startElement,
1209 contentHandler_endElement,
1210 contentHandler_characters,
1211 contentHandler_ignorableWhitespace,
1212 contentHandler_processingInstruction,
1213 contentHandler_skippedEntity
1214 };
1215
1216 static ISAXContentHandler contentHandler = { &contentHandlerVtbl };
1217
1218 static HRESULT WINAPI isaxerrorHandler_QueryInterface(
1219 ISAXErrorHandler* iface,
1220 REFIID riid,
1221 void **ppvObject)
1222 {
1223 *ppvObject = NULL;
1224
1225 if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler))
1226 {
1227 *ppvObject = iface;
1228 }
1229 else
1230 {
1231 return E_NOINTERFACE;
1232 }
1233
1234 return S_OK;
1235 }
1236
1237 static ULONG WINAPI isaxerrorHandler_AddRef(
1238 ISAXErrorHandler* iface)
1239 {
1240 return 2;
1241 }
1242
1243 static ULONG WINAPI isaxerrorHandler_Release(
1244 ISAXErrorHandler* iface)
1245 {
1246 return 1;
1247 }
1248
1249 static HRESULT WINAPI isaxerrorHandler_error(
1250 ISAXErrorHandler* iface,
1251 ISAXLocator *pLocator,
1252 const WCHAR *pErrorMessage,
1253 HRESULT hrErrorCode)
1254 {
1255 ok(0, "unexpected call\n");
1256 return S_OK;
1257 }
1258
1259 static HRESULT WINAPI isaxerrorHandler_fatalError(
1260 ISAXErrorHandler* iface,
1261 ISAXLocator *pLocator,
1262 const WCHAR *message,
1263 HRESULT hr)
1264 {
1265 struct call_entry call;
1266
1267 init_call_entry(locator, &call);
1268 call.id = EH_FATALERROR;
1269 call.ret = hr;
1270
1271 add_call(sequences, CONTENT_HANDLER_INDEX, &call);
1272
1273 get_expected_ret();
1274 return S_OK;
1275 }
1276
1277 static HRESULT WINAPI isaxerrorHanddler_ignorableWarning(
1278 ISAXErrorHandler* iface,
1279 ISAXLocator *pLocator,
1280 const WCHAR *pErrorMessage,
1281 HRESULT hrErrorCode)
1282 {
1283 ok(0, "unexpected call\n");
1284 return S_OK;
1285 }
1286
1287 static const ISAXErrorHandlerVtbl errorHandlerVtbl =
1288 {
1289 isaxerrorHandler_QueryInterface,
1290 isaxerrorHandler_AddRef,
1291 isaxerrorHandler_Release,
1292 isaxerrorHandler_error,
1293 isaxerrorHandler_fatalError,
1294 isaxerrorHanddler_ignorableWarning
1295 };
1296
1297 static ISAXErrorHandler errorHandler = { &errorHandlerVtbl };
1298
1299 static HRESULT WINAPI isaxattributes_QueryInterface(
1300 ISAXAttributes* iface,
1301 REFIID riid,
1302 void **ppvObject)
1303 {
1304 *ppvObject = NULL;
1305
1306 if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes))
1307 {
1308 *ppvObject = iface;
1309 }
1310 else
1311 {
1312 return E_NOINTERFACE;
1313 }
1314
1315 return S_OK;
1316 }
1317
1318 static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface)
1319 {
1320 return 2;
1321 }
1322
1323 static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface)
1324 {
1325 return 1;
1326 }
1327
1328 static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length)
1329 {
1330 *length = 3;
1331 return S_OK;
1332 }
1333
1334 static HRESULT WINAPI isaxattributes_getURI(
1335 ISAXAttributes* iface,
1336 int nIndex,
1337 const WCHAR **pUrl,
1338 int *pUriSize)
1339 {
1340 ok(0, "unexpected call\n");
1341 return E_NOTIMPL;
1342 }
1343
1344 static HRESULT WINAPI isaxattributes_getLocalName(
1345 ISAXAttributes* iface,
1346 int nIndex,
1347 const WCHAR **pLocalName,
1348 int *pLocalNameLength)
1349 {
1350 ok(0, "unexpected call\n");
1351 return E_NOTIMPL;
1352 }
1353
1354 static HRESULT WINAPI isaxattributes_getQName(
1355 ISAXAttributes* iface,
1356 int index,
1357 const WCHAR **QName,
1358 int *QNameLength)
1359 {
1360 static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0},
1361 {'a','t','t','r','2','j','u','n','k',0},
1362 {'a','t','t','r','3',0}};
1363 static const int attrqnamelen[] = {7, 5, 5};
1364
1365 ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1366
1367 *QName = attrqnamesW[index];
1368 *QNameLength = attrqnamelen[index];
1369
1370 return S_OK;
1371 }
1372
1373 static HRESULT WINAPI isaxattributes_getName(
1374 ISAXAttributes* iface,
1375 int nIndex,
1376 const WCHAR **pUri,
1377 int * pUriLength,
1378 const WCHAR ** pLocalName,
1379 int * pLocalNameSize,
1380 const WCHAR ** pQName,
1381 int * pQNameLength)
1382 {
1383 ok(0, "unexpected call\n");
1384 return E_NOTIMPL;
1385 }
1386
1387 static HRESULT WINAPI isaxattributes_getIndexFromName(
1388 ISAXAttributes* iface,
1389 const WCHAR * pUri,
1390 int cUriLength,
1391 const WCHAR * pLocalName,
1392 int cocalNameLength,
1393 int * index)
1394 {
1395 ok(0, "unexpected call\n");
1396 return E_NOTIMPL;
1397 }
1398
1399 static HRESULT WINAPI isaxattributes_getIndexFromQName(
1400 ISAXAttributes* iface,
1401 const WCHAR * pQName,
1402 int nQNameLength,
1403 int * index)
1404 {
1405 ok(0, "unexpected call\n");
1406 return E_NOTIMPL;
1407 }
1408
1409 static HRESULT WINAPI isaxattributes_getType(
1410 ISAXAttributes* iface,
1411 int nIndex,
1412 const WCHAR ** pType,
1413 int * pTypeLength)
1414 {
1415 ok(0, "unexpected call\n");
1416 return E_NOTIMPL;
1417 }
1418
1419 static HRESULT WINAPI isaxattributes_getTypeFromName(
1420 ISAXAttributes* iface,
1421 const WCHAR * pUri,
1422 int nUri,
1423 const WCHAR * pLocalName,
1424 int nLocalName,
1425 const WCHAR ** pType,
1426 int * nType)
1427 {
1428 ok(0, "unexpected call\n");
1429 return E_NOTIMPL;
1430 }
1431
1432 static HRESULT WINAPI isaxattributes_getTypeFromQName(
1433 ISAXAttributes* iface,
1434 const WCHAR * pQName,
1435 int nQName,
1436 const WCHAR ** pType,
1437 int * nType)
1438 {
1439 ok(0, "unexpected call\n");
1440 return E_NOTIMPL;
1441 }
1442
1443 static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index,
1444 const WCHAR **value, int *nValue)
1445 {
1446 static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0},
1447 {'a','2','j','u','n','k',0},
1448 {'<','&','"','>',0}};
1449 static const int attrvalueslen[] = {2, 2, 4};
1450
1451 ok(index >= 0 && index <= 2, "invalid index received %d\n", index);
1452
1453 *value = attrvaluesW[index];
1454 *nValue = attrvalueslen[index];
1455
1456 return S_OK;
1457 }
1458
1459 static HRESULT WINAPI isaxattributes_getValueFromName(
1460 ISAXAttributes* iface,
1461 const WCHAR * pUri,
1462 int nUri,
1463 const WCHAR * pLocalName,
1464 int nLocalName,
1465 const WCHAR ** pValue,
1466 int * nValue)
1467 {
1468 ok(0, "unexpected call\n");
1469 return E_NOTIMPL;
1470 }
1471
1472 static HRESULT WINAPI isaxattributes_getValueFromQName(
1473 ISAXAttributes* iface,
1474 const WCHAR * pQName,
1475 int nQName,
1476 const WCHAR ** pValue,
1477 int * nValue)
1478 {
1479 ok(0, "unexpected call\n");
1480 return E_NOTIMPL;
1481 }
1482
1483 static const ISAXAttributesVtbl SAXAttributesVtbl =
1484 {
1485 isaxattributes_QueryInterface,
1486 isaxattributes_AddRef,
1487 isaxattributes_Release,
1488 isaxattributes_getLength,
1489 isaxattributes_getURI,
1490 isaxattributes_getLocalName,
1491 isaxattributes_getQName,
1492 isaxattributes_getName,
1493 isaxattributes_getIndexFromName,
1494 isaxattributes_getIndexFromQName,
1495 isaxattributes_getType,
1496 isaxattributes_getTypeFromName,
1497 isaxattributes_getTypeFromQName,
1498 isaxattributes_getValue,
1499 isaxattributes_getValueFromName,
1500 isaxattributes_getValueFromQName
1501 };
1502
1503 static ISAXAttributes saxattributes = { &SAXAttributesVtbl };
1504
1505 struct saxlexicalhandler
1506 {
1507 ISAXLexicalHandler ISAXLexicalHandler_iface;
1508 LONG ref;
1509
1510 HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1511 };
1512
1513 static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface )
1514 {
1515 return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface);
1516 }
1517
1518 static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out)
1519 {
1520 struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1521
1522 *out = NULL;
1523
1524 if (IsEqualGUID(riid, &IID_IUnknown))
1525 {
1526 *out = iface;
1527 ok(0, "got unexpected IID_IUnknown query\n");
1528 }
1529 else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler))
1530 {
1531 if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1532 *out = iface;
1533 }
1534
1535 if (*out)
1536 ISAXLexicalHandler_AddRef(iface);
1537 else
1538 return E_NOINTERFACE;
1539
1540 return S_OK;
1541 }
1542
1543 static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface)
1544 {
1545 struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1546 return InterlockedIncrement(&handler->ref);
1547 }
1548
1549 static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface)
1550 {
1551 struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface);
1552 return InterlockedDecrement(&handler->ref);
1553 }
1554
1555 static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface,
1556 const WCHAR * pName, int nName, const WCHAR * pPublicId,
1557 int nPublicId, const WCHAR * pSystemId, int nSystemId)
1558 {
1559 ok(0, "call not expected\n");
1560 return E_NOTIMPL;
1561 }
1562
1563 static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface)
1564 {
1565 ok(0, "call not expected\n");
1566 return E_NOTIMPL;
1567 }
1568
1569 static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface,
1570 const WCHAR * pName, int nName)
1571 {
1572 ok(0, "call not expected\n");
1573 return E_NOTIMPL;
1574 }
1575
1576 static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface,
1577 const WCHAR * pName, int nName)
1578 {
1579 ok(0, "call not expected\n");
1580 return E_NOTIMPL;
1581 }
1582
1583 static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface)
1584 {
1585 ok(0, "call not expected\n");
1586 return E_NOTIMPL;
1587 }
1588
1589 static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface)
1590 {
1591 ok(0, "call not expected\n");
1592 return E_NOTIMPL;
1593 }
1594
1595 static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface,
1596 const WCHAR * pChars, int nChars)
1597 {
1598 ok(0, "call not expected\n");
1599 return E_NOTIMPL;
1600 }
1601
1602 static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl =
1603 {
1604 isaxlexical_QueryInterface,
1605 isaxlexical_AddRef,
1606 isaxlexical_Release,
1607 isaxlexical_startDTD,
1608 isaxlexical_endDTD,
1609 isaxlexical_startEntity,
1610 isaxlexical_endEntity,
1611 isaxlexical_startCDATA,
1612 isaxlexical_endCDATA,
1613 isaxlexical_comment
1614 };
1615
1616 static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr)
1617 {
1618 handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl;
1619 handler->ref = 1;
1620 handler->qi_hr = hr;
1621 }
1622
1623 struct saxdeclhandler
1624 {
1625 ISAXDeclHandler ISAXDeclHandler_iface;
1626 LONG ref;
1627
1628 HRESULT qi_hr; /* ret value for QueryInterface for handler riid */
1629 };
1630
1631 static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface )
1632 {
1633 return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface);
1634 }
1635
1636 static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out)
1637 {
1638 struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1639
1640 *out = NULL;
1641
1642 if (IsEqualGUID(riid, &IID_IUnknown))
1643 {
1644 *out = iface;
1645 ok(0, "got unexpected IID_IUnknown query\n");
1646 }
1647 else if (IsEqualGUID(riid, &IID_ISAXDeclHandler))
1648 {
1649 if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr;
1650 *out = iface;
1651 }
1652
1653 if (*out)
1654 ISAXDeclHandler_AddRef(iface);
1655 else
1656 return E_NOINTERFACE;
1657
1658 return S_OK;
1659 }
1660
1661 static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface)
1662 {
1663 struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1664 return InterlockedIncrement(&handler->ref);
1665 }
1666
1667 static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface)
1668 {
1669 struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface);
1670 return InterlockedDecrement(&handler->ref);
1671 }
1672
1673 static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface,
1674 const WCHAR * pName, int nName, const WCHAR * pModel, int nModel)
1675 {
1676 ok(0, "call not expected\n");
1677 return E_NOTIMPL;
1678 }
1679
1680 static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface,
1681 const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName,
1682 int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault,
1683 int nValueDefault, const WCHAR * pValue, int nValue)
1684 {
1685 ok(0, "call not expected\n");
1686 return E_NOTIMPL;
1687 }
1688
1689 static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface,
1690 const WCHAR * pName, int nName, const WCHAR * pValue, int nValue)
1691 {
1692 ok(0, "call not expected\n");
1693 return E_NOTIMPL;
1694 }
1695
1696 static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface,
1697 const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId,
1698 const WCHAR * pSystemId, int nSystemId)
1699 {
1700 ok(0, "call not expected\n");
1701 return E_NOTIMPL;
1702 }
1703
1704 static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl =
1705 {
1706 isaxdecl_QueryInterface,
1707 isaxdecl_AddRef,
1708 isaxdecl_Release,
1709 isaxdecl_elementDecl,
1710 isaxdecl_attributeDecl,
1711 isaxdecl_internalEntityDecl,
1712 isaxdecl_externalEntityDecl
1713 };
1714
1715 static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr)
1716 {
1717 handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl;
1718 handler->ref = 1;
1719 handler->qi_hr = hr;
1720 }
1721
1722 typedef struct mxwriter_write_test_t {
1723 BOOL last;
1724 const BYTE *data;
1725 DWORD cb;
1726 BOOL null_written;
1727 BOOL fail_write;
1728 } mxwriter_write_test;
1729
1730 typedef struct mxwriter_stream_test_t {
1731 VARIANT_BOOL bom;
1732 const char *encoding;
1733 mxwriter_write_test expected_writes[4];
1734 } mxwriter_stream_test;
1735
1736 static const mxwriter_write_test *current_write_test;
1737 static DWORD current_stream_test_index;
1738
1739 static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject)
1740 {
1741 *ppvObject = NULL;
1742
1743 if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown))
1744 *ppvObject = iface;
1745 else
1746 return E_NOINTERFACE;
1747
1748 return S_OK;
1749 }
1750
1751 static ULONG WINAPI istream_AddRef(IStream *iface)
1752 {
1753 return 2;
1754 }
1755
1756 static ULONG WINAPI istream_Release(IStream *iface)
1757 {
1758 return 1;
1759 }
1760
1761 static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead)
1762 {
1763 ok(0, "unexpected call\n");
1764 return E_NOTIMPL;
1765 }
1766
1767 static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten)
1768 {
1769 BOOL fail = FALSE;
1770
1771 ok(pv != NULL, "pv == NULL\n");
1772
1773 if(current_write_test->last) {
1774 ok(0, "Too many Write calls made on test %d\n", current_stream_test_index);
1775 return E_FAIL;
1776 }
1777
1778 fail = current_write_test->fail_write;
1779
1780 ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n",
1781 current_write_test->cb, cb, current_stream_test_index);
1782
1783 if(!pcbWritten)
1784 ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index);
1785 else
1786 ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index);
1787
1788 ++current_write_test;
1789
1790 if(pcbWritten)
1791 *pcbWritten = cb;
1792
1793 return fail ? E_FAIL : S_OK;
1794 }
1795
1796 static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin,
1797 ULARGE_INTEGER *plibNewPosition)
1798 {
1799 ok(0, "unexpected call\n");
1800 return E_NOTIMPL;
1801 }
1802
1803 static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize)
1804 {
1805 ok(0, "unexpected call\n");
1806 return E_NOTIMPL;
1807 }
1808
1809 static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb,
1810 ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten)
1811 {
1812 ok(0, "unexpected call\n");
1813 return E_NOTIMPL;
1814 }
1815
1816 static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags)
1817 {
1818 ok(0, "unexpected call\n");
1819 return E_NOTIMPL;
1820 }
1821
1822 static HRESULT WINAPI istream_Revert(IStream *iface)
1823 {
1824 ok(0, "unexpected call\n");
1825 return E_NOTIMPL;
1826 }
1827
1828 static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1829 ULARGE_INTEGER cb, DWORD dwLockType)
1830 {
1831 ok(0, "unexpected call\n");
1832 return E_NOTIMPL;
1833 }
1834
1835 static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset,
1836 ULARGE_INTEGER cb, DWORD dwLockType)
1837 {
1838 ok(0, "unexpected call\n");
1839 return E_NOTIMPL;
1840 }
1841
1842 static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag)
1843 {
1844 ok(0, "unexpected call\n");
1845 return E_NOTIMPL;
1846 }
1847
1848 static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm)
1849 {
1850 ok(0, "unexpected call\n");
1851 return E_NOTIMPL;
1852 }
1853
1854 static const IStreamVtbl StreamVtbl = {
1855 istream_QueryInterface,
1856 istream_AddRef,
1857 istream_Release,
1858 istream_Read,
1859 istream_Write,
1860 istream_Seek,
1861 istream_SetSize,
1862 istream_CopyTo,
1863 istream_Commit,
1864 istream_Revert,
1865 istream_LockRegion,
1866 istream_UnlockRegion,
1867 istream_Stat,
1868 istream_Clone
1869 };
1870
1871 static IStream mxstream = { &StreamVtbl };
1872
1873 static struct msxmlsupported_data_t reader_support_data[] =
1874 {
1875 { &CLSID_SAXXMLReader, "SAXReader" },
1876 { &CLSID_SAXXMLReader30, "SAXReader30" },
1877 { &CLSID_SAXXMLReader40, "SAXReader40" },
1878 { &CLSID_SAXXMLReader60, "SAXReader60" },
1879 { NULL }
1880 };
1881
1882 static void test_saxreader(void)
1883 {
1884 const struct msxmlsupported_data_t *table = reader_support_data;
1885 HRESULT hr;
1886 ISAXXMLReader *reader = NULL;
1887 VARIANT var;
1888 ISAXContentHandler *content;
1889 ISAXErrorHandler *lpErrorHandler;
1890 SAFEARRAY *sa;
1891 SAFEARRAYBOUND SADim[1];
1892 char *ptr = NULL;
1893 IStream *stream;
1894 ULARGE_INTEGER size;
1895 LARGE_INTEGER pos;
1896 ULONG written;
1897 HANDLE file;
1898 static const CHAR testXmlA[] = "test.xml";
1899 static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
1900 IXMLDOMDocument *doc;
1901 VARIANT_BOOL v;
1902
1903 while (table->clsid)
1904 {
1905 struct call_entry *test_seq;
1906 BSTR str;
1907
1908 if (!is_clsid_supported(table->clsid, reader_support_data))
1909 {
1910 table++;
1911 continue;
1912 }
1913
1914 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
1915 EXPECT_HR(hr, S_OK);
1916 g_reader = reader;
1917
1918 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
1919 msxml_version = 4;
1920 else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1921 msxml_version = 6;
1922 else
1923 msxml_version = 0;
1924
1925 /* crashes on old versions */
1926 if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) &&
1927 !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1928 {
1929 hr = ISAXXMLReader_getContentHandler(reader, NULL);
1930 EXPECT_HR(hr, E_POINTER);
1931
1932 hr = ISAXXMLReader_getErrorHandler(reader, NULL);
1933 EXPECT_HR(hr, E_POINTER);
1934 }
1935
1936 hr = ISAXXMLReader_getContentHandler(reader, &content);
1937 EXPECT_HR(hr, S_OK);
1938 ok(content == NULL, "Expected %p, got %p\n", NULL, content);
1939
1940 hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler);
1941 EXPECT_HR(hr, S_OK);
1942 ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler);
1943
1944 hr = ISAXXMLReader_putContentHandler(reader, NULL);
1945 EXPECT_HR(hr, S_OK);
1946
1947 hr = ISAXXMLReader_putContentHandler(reader, &contentHandler);
1948 EXPECT_HR(hr, S_OK);
1949
1950 hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler);
1951 EXPECT_HR(hr, S_OK);
1952
1953 hr = ISAXXMLReader_getContentHandler(reader, &content);
1954 EXPECT_HR(hr, S_OK);
1955 ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content);
1956
1957 V_VT(&var) = VT_BSTR;
1958 V_BSTR(&var) = SysAllocString(szSimpleXML);
1959
1960 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
1961 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
1962 test_seq = content_handler_test1_alternate;
1963 else
1964 test_seq = content_handler_test1;
1965 set_expected_seq(test_seq);
1966 hr = ISAXXMLReader_parse(reader, var);
1967 EXPECT_HR(hr, S_OK);
1968 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE);
1969
1970 VariantClear(&var);
1971
1972 SADim[0].lLbound = 0;
1973 SADim[0].cElements = sizeof(testXML)-1;
1974 sa = SafeArrayCreate(VT_UI1, 1, SADim);
1975 SafeArrayAccessData(sa, (void**)&ptr);
1976 memcpy(ptr, testXML, sizeof(testXML)-1);
1977 SafeArrayUnaccessData(sa);
1978 V_VT(&var) = VT_ARRAY|VT_UI1;
1979 V_ARRAY(&var) = sa;
1980
1981 set_expected_seq(test_seq);
1982 hr = ISAXXMLReader_parse(reader, var);
1983 EXPECT_HR(hr, S_OK);
1984 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE);
1985
1986 SafeArrayDestroy(sa);
1987
1988 CreateStreamOnHGlobal(NULL, TRUE, &stream);
1989 size.QuadPart = strlen(testXML);
1990 IStream_SetSize(stream, size);
1991 IStream_Write(stream, testXML, strlen(testXML), &written);
1992 pos.QuadPart = 0;
1993 IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
1994 V_VT(&var) = VT_UNKNOWN;
1995 V_UNKNOWN(&var) = (IUnknown*)stream;
1996
1997 set_expected_seq(test_seq);
1998 hr = ISAXXMLReader_parse(reader, var);
1999 EXPECT_HR(hr, S_OK);
2000 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE);
2001
2002 IStream_Release(stream);
2003
2004 CreateStreamOnHGlobal(NULL, TRUE, &stream);
2005 size.QuadPart = strlen(test_attributes);
2006 IStream_SetSize(stream, size);
2007 IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2008 pos.QuadPart = 0;
2009 IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2010 V_VT(&var) = VT_UNKNOWN;
2011 V_UNKNOWN(&var) = (IUnknown*)stream;
2012
2013 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40))
2014 test_seq = content_handler_test_attributes_alternate_4;
2015 else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2016 test_seq = content_handler_test_attributes_alternate_6;
2017 else
2018 test_seq = content_handler_test_attributes;
2019
2020 set_expected_seq(test_seq);
2021 hr = ISAXXMLReader_parse(reader, var);
2022 EXPECT_HR(hr, S_OK);
2023
2024 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2025 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2026 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2027 else
2028 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2029
2030 IStream_Release(stream);
2031
2032 V_VT(&var) = VT_BSTR;
2033 V_BSTR(&var) = SysAllocString(carriage_ret_test);
2034
2035 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2036 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2037 test_seq = content_handler_test2_alternate;
2038 else
2039 test_seq = content_handler_test2;
2040
2041 set_expected_seq(test_seq);
2042 hr = ISAXXMLReader_parse(reader, var);
2043 EXPECT_HR(hr, S_OK);
2044 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE);
2045
2046 VariantClear(&var);
2047
2048 /* from file url */
2049 file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2050 ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2051 WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL);
2052 CloseHandle(file);
2053
2054 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2055 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2056 test_seq = content_handler_test1_alternate;
2057 else
2058 test_seq = content_handler_test1;
2059 set_expected_seq(test_seq);
2060 hr = ISAXXMLReader_parseURL(reader, testXmlW);
2061 EXPECT_HR(hr, S_OK);
2062 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE);
2063
2064 /* error handler */
2065 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2066 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2067 test_seq = content_handler_testerror_alternate;
2068 else
2069 test_seq = content_handler_testerror;
2070 set_expected_seq(test_seq);
2071 hr = ISAXXMLReader_parseURL(reader, testXmlW);
2072 EXPECT_HR(hr, E_FAIL);
2073 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE);
2074
2075 /* callback ret values */
2076 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2077 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2078 {
2079 test_seq = content_handler_test_callback_rets_alt;
2080 set_expected_seq(test_seq);
2081 hr = ISAXXMLReader_parseURL(reader, testXmlW);
2082 EXPECT_HR(hr, S_OK);
2083 }
2084 else
2085 {
2086 test_seq = content_handler_test_callback_rets;
2087 set_expected_seq(test_seq);
2088 hr = ISAXXMLReader_parseURL(reader, testXmlW);
2089 EXPECT_HR(hr, S_FALSE);
2090 }
2091 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE);
2092
2093 DeleteFileA(testXmlA);
2094
2095 /* parse from IXMLDOMDocument */
2096 hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
2097 &IID_IXMLDOMDocument, (void**)&doc);
2098 EXPECT_HR(hr, S_OK);
2099
2100 str = SysAllocString(szSimpleXML);
2101 hr = IXMLDOMDocument_loadXML(doc, str, &v);
2102 EXPECT_HR(hr, S_OK);
2103 SysFreeString(str);
2104
2105 V_VT(&var) = VT_UNKNOWN;
2106 V_UNKNOWN(&var) = (IUnknown*)doc;
2107
2108 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2109 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2110 test_seq = content_handler_test2_alternate;
2111 else
2112 test_seq = content_handler_test2;
2113
2114 set_expected_seq(test_seq);
2115 hr = ISAXXMLReader_parse(reader, var);
2116 EXPECT_HR(hr, S_OK);
2117 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE);
2118 IXMLDOMDocument_Release(doc);
2119
2120 /* xml:space test */
2121 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2122 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2123 {
2124 test_seq = xmlspaceattr_test_alternate;
2125 }
2126 else
2127 test_seq = xmlspaceattr_test;
2128
2129 set_expected_seq(test_seq);
2130 V_VT(&var) = VT_BSTR;
2131 V_BSTR(&var) = _bstr_(xmlspace_attr);
2132 hr = ISAXXMLReader_parse(reader, var);
2133 EXPECT_HR(hr, S_OK);
2134
2135 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2136 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2137 {
2138 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE);
2139 }
2140 else
2141 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE);
2142
2143 /* switch off 'namespaces' feature */
2144 hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE);
2145 EXPECT_HR(hr, S_OK);
2146
2147 CreateStreamOnHGlobal(NULL, TRUE, &stream);
2148 size.QuadPart = strlen(test_attributes);
2149 IStream_SetSize(stream, size);
2150 IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2151 pos.QuadPart = 0;
2152 IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2153 V_VT(&var) = VT_UNKNOWN;
2154 V_UNKNOWN(&var) = (IUnknown*)stream;
2155
2156 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2157 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2158 {
2159 test_seq = content_handler_test_attributes_alt_no_ns;
2160 }
2161 else
2162 test_seq = content_handler_test_attributes;
2163
2164 set_expected_seq(test_seq);
2165 hr = ISAXXMLReader_parse(reader, var);
2166 EXPECT_HR(hr, S_OK);
2167 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE);
2168 hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE);
2169 EXPECT_HR(hr, S_OK);
2170
2171 /* switch off 'namespace-prefixes' feature */
2172 hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE);
2173 EXPECT_HR(hr, S_OK);
2174
2175 CreateStreamOnHGlobal(NULL, TRUE, &stream);
2176 size.QuadPart = strlen(test_attributes);
2177 IStream_SetSize(stream, size);
2178 IStream_Write(stream, test_attributes, strlen(test_attributes), &written);
2179 pos.QuadPart = 0;
2180 IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2181 V_VT(&var) = VT_UNKNOWN;
2182 V_UNKNOWN(&var) = (IUnknown*)stream;
2183
2184 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2185 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2186 {
2187 test_seq = content_handler_test_attributes_alt_no_prefix;
2188 }
2189 else
2190 test_seq = content_handler_test_attributes_no_prefix;
2191
2192 set_expected_seq(test_seq);
2193 hr = ISAXXMLReader_parse(reader, var);
2194 EXPECT_HR(hr, S_OK);
2195 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE);
2196
2197 hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE);
2198 EXPECT_HR(hr, S_OK);
2199
2200 /* attribute normalization */
2201 CreateStreamOnHGlobal(NULL, TRUE, &stream);
2202 size.QuadPart = strlen(attribute_normalize);
2203 IStream_SetSize(stream, size);
2204 IStream_Write(stream, attribute_normalize, strlen(attribute_normalize), &written);
2205 pos.QuadPart = 0;
2206 IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
2207 V_VT(&var) = VT_UNKNOWN;
2208 V_UNKNOWN(&var) = (IUnknown*)stream;
2209
2210 if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) ||
2211 IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60))
2212 {
2213 test_seq = attribute_norm_alt;
2214 }
2215 else
2216 test_seq = attribute_norm;
2217
2218 set_expected_seq(test_seq);
2219 hr = ISAXXMLReader_parse(reader, var);
2220 EXPECT_HR(hr, S_OK);
2221 ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE);
2222
2223 ISAXXMLReader_Release(reader);
2224 table++;
2225 }
2226
2227 free_bstrs();
2228 }
2229
2230 struct saxreader_props_test_t
2231 {
2232 const char *prop_name;
2233 IUnknown *iface;
2234 };
2235
2236 static struct saxlexicalhandler lexicalhandler;
2237 static struct saxdeclhandler declhandler;
2238
2239 static const struct saxreader_props_test_t props_test_data[] = {
2240 { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface },
2241 { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface },
2242 { 0 }
2243 };
2244
2245 static void test_saxreader_properties(void)
2246 {
2247 const struct saxreader_props_test_t *ptr = props_test_data;
2248 ISAXXMLReader *reader;
2249 HRESULT hr;
2250 VARIANT v;
2251
2252 hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
2253 &IID_ISAXXMLReader, (void**)&reader);
2254 EXPECT_HR(hr, S_OK);
2255
2256 hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL);
2257 EXPECT_HR(hr, E_POINTER);
2258
2259 while (ptr->prop_name)
2260 {
2261 LONG ref;
2262
2263 init_saxlexicalhandler(&lexicalhandler, S_OK);
2264 init_saxdeclhandler(&declhandler, S_OK);
2265
2266 V_VT(&v) = VT_EMPTY;
2267 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2268 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2269 EXPECT_HR(hr, S_OK);
2270 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2271 ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2272
2273 V_VT(&v) = VT_UNKNOWN;
2274 V_UNKNOWN(&v) = ptr->iface;
2275 ref = get_refcount(ptr->iface);
2276 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2277 EXPECT_HR(hr, S_OK);
2278 ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2279
2280 V_VT(&v) = VT_EMPTY;
2281 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2282
2283 ref = get_refcount(ptr->iface);
2284 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2285 EXPECT_HR(hr, S_OK);
2286 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2287 ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2288 ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n");
2289 VariantClear(&v);
2290
2291 V_VT(&v) = VT_EMPTY;
2292 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2293 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2294 EXPECT_HR(hr, S_OK);
2295
2296 V_VT(&v) = VT_EMPTY;
2297 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2298 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2299 EXPECT_HR(hr, S_OK);
2300 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2301 ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2302
2303 V_VT(&v) = VT_UNKNOWN;
2304 V_UNKNOWN(&v) = ptr->iface;
2305 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2306 EXPECT_HR(hr, S_OK);
2307
2308 /* only VT_EMPTY seems to be valid to reset property */
2309 V_VT(&v) = VT_I4;
2310 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2311 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2312 EXPECT_HR(hr, E_INVALIDARG);
2313
2314 V_VT(&v) = VT_EMPTY;
2315 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2316 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2317 EXPECT_HR(hr, S_OK);
2318 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2319 ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v));
2320 VariantClear(&v);
2321
2322 V_VT(&v) = VT_UNKNOWN;
2323 V_UNKNOWN(&v) = NULL;
2324 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2325 EXPECT_HR(hr, S_OK);
2326
2327 V_VT(&v) = VT_EMPTY;
2328 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2329 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2330 EXPECT_HR(hr, S_OK);
2331 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2332 ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v));
2333
2334 /* block QueryInterface on handler riid */
2335 V_VT(&v) = VT_UNKNOWN;
2336 V_UNKNOWN(&v) = ptr->iface;
2337 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2338 EXPECT_HR(hr, S_OK);
2339
2340 init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE);
2341 init_saxdeclhandler(&declhandler, E_NOINTERFACE);
2342
2343 V_VT(&v) = VT_UNKNOWN;
2344 V_UNKNOWN(&v) = ptr->iface;
2345 EXPECT_REF(ptr->iface, 1);
2346 ref = get_refcount(ptr->iface);
2347 hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v);
2348 EXPECT_HR(hr, E_NOINTERFACE);
2349 EXPECT_REF(ptr->iface, 1);
2350
2351 V_VT(&v) = VT_EMPTY;
2352 V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef;
2353 hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v);
2354 EXPECT_HR(hr, S_OK);
2355 ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v));
2356 ok(V_UNKNOWN(&v) != NULL, "got %p\n", V_UNKNOWN(&v));
2357
2358 ptr++;
2359 free_bstrs();
2360 }
2361
2362 ISAXXMLReader_Release(reader);
2363
2364 if (!is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data))
2365 return;
2366
2367 hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER,
2368 &IID_ISAXXMLReader, (void**)&reader);
2369 EXPECT_HR(hr, S_OK);
2370
2371 /* xmldecl-version property */
2372 V_VT(&v) = VT_EMPTY;
2373 V_BSTR(&v) = (void*)0xdeadbeef;
2374 hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2375 EXPECT_HR(hr, S_OK);
2376 ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2377 ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2378
2379 /* stream without declaration */
2380 V_VT(&v) = VT_BSTR;
2381 V_BSTR(&v) = _bstr_("<element></element>");
2382 hr = ISAXXMLReader_parse(reader, v);
2383 EXPECT_HR(hr, S_OK);
2384
2385 V_VT(&v) = VT_EMPTY;
2386 V_BSTR(&v) = (void*)0xdeadbeef;
2387 hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2388 EXPECT_HR(hr, S_OK);
2389 ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2390 ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2391
2392 /* stream with declaration */
2393 V_VT(&v) = VT_BSTR;
2394 V_BSTR(&v) = _bstr_("<?xml version=\"1.0\"?><element></element>");
2395 hr = ISAXXMLReader_parse(reader, v);
2396 EXPECT_HR(hr, S_OK);
2397
2398 V_VT(&v) = VT_EMPTY;
2399 V_BSTR(&v) = (void*)0xdeadbeef;
2400 hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v);
2401 EXPECT_HR(hr, S_OK);
2402 ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v));
2403 ok(!lstrcmpW(V_BSTR(&v), _bstr_("1.0")), "got %s\n", wine_dbgstr_w(V_BSTR(&v)));
2404 VariantClear(&v);
2405
2406 ISAXXMLReader_Release(reader);
2407 free_bstrs();
2408 }
2409
2410 struct feature_ns_entry_t {
2411 const GUID *guid;
2412 const char *clsid;
2413 VARIANT_BOOL value;
2414 VARIANT_BOOL value2; /* feature value after feature set to 0xc */
2415 };
2416
2417 static const struct feature_ns_entry_t feature_ns_entry_data[] = {
2418 { &CLSID_SAXXMLReader, "CLSID_SAXXMLReader", VARIANT_TRUE, VARIANT_FALSE },
2419 { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE },
2420 { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE },
2421 { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE },
2422 { 0 }
2423 };
2424
2425 static const char *feature_names[] = {
2426 "http://xml.org/sax/features/namespaces",
2427 "http://xml.org/sax/features/namespace-prefixes",
2428 0
2429 };
2430
2431 static void test_saxreader_features(void)
2432 {
2433 const struct feature_ns_entry_t *entry = feature_ns_entry_data;
2434 ISAXXMLReader *reader;
2435
2436 while (entry->guid)
2437 {
2438 VARIANT_BOOL value;
2439 const char **name;
2440 HRESULT hr;
2441
2442 hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2443 if (hr != S_OK)
2444 {
2445 win_skip("can't create %s instance\n", entry->clsid);
2446 entry++;
2447 continue;
2448 }
2449
2450 name = feature_names;
2451 while (*name)
2452 {
2453 value = 0xc;
2454 hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2455 EXPECT_HR(hr, S_OK);
2456 ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value);
2457
2458 value = 0xc;
2459 hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value);
2460 EXPECT_HR(hr, S_OK);
2461
2462 value = 0xd;
2463 hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2464 EXPECT_HR(hr, S_OK);
2465 ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2);
2466
2467 hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE);
2468 EXPECT_HR(hr, S_OK);
2469 value = 0xd;
2470 hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2471 EXPECT_HR(hr, S_OK);
2472 ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value);
2473
2474 hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE);
2475 EXPECT_HR(hr, S_OK);
2476 value = 0xd;
2477 hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value);
2478 EXPECT_HR(hr, S_OK);
2479 ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value);
2480
2481 name++;
2482 }
2483
2484 ISAXXMLReader_Release(reader);
2485
2486 entry++;
2487 }
2488 }
2489
2490 /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */
2491 static const CHAR UTF8BOMTest[] =
2492 "\xEF\xBB\xBF<?xml version = \"1.0\" encoding = \"UTF-16\"?>\n"
2493 "<a></a>\n";
2494
2495 struct enc_test_entry_t {
2496 const GUID *guid;
2497 const char *clsid;
2498 const char *data;
2499 HRESULT hr;
2500 int todo;
2501 };
2502
2503 static const struct enc_test_entry_t encoding_test_data[] = {
2504 { &CLSID_SAXXMLReader, "CLSID_SAXXMLReader", UTF8BOMTest, 0xc00ce56f, 1 },
2505 { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, 1 },
2506 { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, 0 },
2507 { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, 0 },
2508 { 0 }
2509 };
2510
2511 static void test_saxreader_encoding(void)
2512 {
2513 const struct enc_test_entry_t *entry = encoding_test_data;
2514 static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0};
2515 static const CHAR testXmlA[] = "test.xml";
2516
2517 while (entry->guid)
2518 {
2519 ISAXXMLReader *reader;
2520 VARIANT input;
2521 DWORD written;
2522 HANDLE file;
2523 HRESULT hr;
2524
2525 hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader);
2526 if (hr != S_OK)
2527 {
2528 win_skip("can't create %s instance\n", entry->clsid);
2529 entry++;
2530 continue;
2531 }
2532
2533 file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
2534 ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError());
2535 WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL);
2536 CloseHandle(file);
2537
2538 hr = ISAXXMLReader_parseURL(reader, testXmlW);
2539 if (entry->todo)
2540 todo_wine ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2541 else
2542 ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid);
2543
2544 DeleteFileA(testXmlA);
2545
2546 /* try BSTR input with no BOM or '<?xml' instruction */
2547 V_VT(&input) = VT_BSTR;
2548 V_BSTR(&input) = _bstr_("<element></element>");
2549 hr = ISAXXMLReader_parse(reader, input);
2550 EXPECT_HR(hr, S_OK);
2551
2552 ISAXXMLReader_Release(reader);
2553
2554 free_bstrs();
2555 entry++;
2556 }
2557 }
2558
2559 static void test_mxwriter_handlers(void)
2560 {
2561 ISAXContentHandler *handler;
2562 IMXWriter *writer, *writer2;
2563 ISAXDeclHandler *decl;
2564 ISAXLexicalHandler *lh;
2565 HRESULT hr;
2566
2567 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2568 &IID_IMXWriter, (void**)&writer);
2569 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2570
2571 EXPECT_REF(writer, 1);
2572
2573 /* ISAXContentHandler */
2574 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&handler);
2575 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2576 EXPECT_REF(writer, 2);
2577 EXPECT_REF(handler, 2);
2578
2579 hr = ISAXContentHandler_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2);
2580 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2581 ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2582 EXPECT_REF(writer, 3);
2583 EXPECT_REF(writer2, 3);
2584 IMXWriter_Release(writer2);
2585 ISAXContentHandler_Release(handler);
2586
2587 /* ISAXLexicalHandler */
2588 hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lh);
2589 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2590 EXPECT_REF(writer, 2);
2591 EXPECT_REF(lh, 2);
2592
2593 hr = ISAXLexicalHandler_QueryInterface(lh, &IID_IMXWriter, (void**)&writer2);
2594 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2595 ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2596 EXPECT_REF(writer, 3);
2597 EXPECT_REF(writer2, 3);
2598 IMXWriter_Release(writer2);
2599 ISAXLexicalHandler_Release(lh);
2600
2601 /* ISAXDeclHandler */
2602 hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
2603 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2604 EXPECT_REF(writer, 2);
2605 EXPECT_REF(lh, 2);
2606
2607 hr = ISAXDeclHandler_QueryInterface(decl, &IID_IMXWriter, (void**)&writer2);
2608 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2609 ok(writer2 == writer, "got %p, expected %p\n", writer2, writer);
2610 EXPECT_REF(writer, 3);
2611 EXPECT_REF(writer2, 3);
2612 IMXWriter_Release(writer2);
2613 ISAXDeclHandler_Release(decl);
2614
2615 IMXWriter_Release(writer);
2616 }
2617
2618
2619 static struct msxmlsupported_data_t mxwriter_support_data[] =
2620 {
2621 { &CLSID_MXXMLWriter, "MXXMLWriter" },
2622 { &CLSID_MXXMLWriter30, "MXXMLWriter30" },
2623 { &CLSID_MXXMLWriter40, "MXXMLWriter40" },
2624 { &CLSID_MXXMLWriter60, "MXXMLWriter60" },
2625 { NULL }
2626 };
2627
2628 static struct msxmlsupported_data_t mxattributes_support_data[] =
2629 {
2630 { &CLSID_SAXAttributes, "SAXAttributes" },
2631 { &CLSID_SAXAttributes30, "SAXAttributes30" },
2632 { &CLSID_SAXAttributes40, "SAXAttributes40" },
2633 { &CLSID_SAXAttributes60, "SAXAttributes60" },
2634 { NULL }
2635 };
2636
2637 struct mxwriter_props_t
2638 {
2639 const GUID *clsid;
2640 VARIANT_BOOL bom;
2641 VARIANT_BOOL disable_escape;
2642 VARIANT_BOOL indent;
2643 VARIANT_BOOL omitdecl;
2644 VARIANT_BOOL standalone;
2645 const char *encoding;
2646 };
2647
2648 static const struct mxwriter_props_t mxwriter_default_props[] =
2649 {
2650 { &CLSID_MXXMLWriter, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2651 { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2652 { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2653 { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" },
2654 { NULL }
2655 };
2656
2657 static void test_mxwriter_default_properties(const struct mxwriter_props_t *table)
2658 {
2659 int i = 0;
2660
2661 while (table->clsid)
2662 {
2663 IMXWriter *writer;
2664 VARIANT_BOOL b;
2665 BSTR encoding;
2666 HRESULT hr;
2667
2668 if (!is_clsid_supported(table->clsid, mxwriter_support_data))
2669 {
2670 table++;
2671 i++;
2672 continue;
2673 }
2674
2675 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
2676 &IID_IMXWriter, (void**)&writer);
2677 EXPECT_HR(hr, S_OK);
2678
2679 b = !table->bom;
2680 hr = IMXWriter_get_byteOrderMark(writer, &b);
2681 EXPECT_HR(hr, S_OK);
2682 ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom);
2683
2684 b = !table->disable_escape;
2685 hr = IMXWriter_get_disableOutputEscaping(writer, &b);
2686 EXPECT_HR(hr, S_OK);
2687 ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b,
2688 table->disable_escape);
2689
2690 b = !table->indent;
2691 hr = IMXWriter_get_indent(writer, &b);
2692 EXPECT_HR(hr, S_OK);
2693 ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent);
2694
2695 b = !table->omitdecl;
2696 hr = IMXWriter_get_omitXMLDeclaration(writer, &b);
2697 EXPECT_HR(hr, S_OK);
2698 ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl);
2699
2700 b = !table->standalone;
2701 hr = IMXWriter_get_standalone(writer, &b);
2702 EXPECT_HR(hr, S_OK);
2703 ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone);
2704
2705 hr = IMXWriter_get_encoding(writer, &encoding);
2706 EXPECT_HR(hr, S_OK);
2707 ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n",
2708 i, wine_dbgstr_w(encoding), table->encoding);
2709 SysFreeString(encoding);
2710
2711 IMXWriter_Release(writer);
2712
2713 table++;
2714 i++;
2715 }
2716 }
2717
2718 static void test_mxwriter_properties(void)
2719 {
2720 static const WCHAR utf16W[] = {'U','T','F','-','1','6',0};
2721 static const WCHAR emptyW[] = {0};
2722 static const WCHAR testW[] = {'t','e','s','t',0};
2723 ISAXContentHandler *content;
2724 IMXWriter *writer;
2725 VARIANT_BOOL b;
2726 HRESULT hr;
2727 BSTR str, str2;
2728 VARIANT dest;
2729
2730 test_mxwriter_default_properties(mxwriter_default_props);
2731
2732 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2733 &IID_IMXWriter, (void**)&writer);
2734 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2735
2736 hr = IMXWriter_get_disableOutputEscaping(writer, NULL);
2737 ok(hr == E_POINTER, "got %08x\n", hr);
2738
2739 hr = IMXWriter_get_byteOrderMark(writer, NULL);
2740 ok(hr == E_POINTER, "got %08x\n", hr);
2741
2742 hr = IMXWriter_get_indent(writer, NULL);
2743 ok(hr == E_POINTER, "got %08x\n", hr);
2744
2745 hr = IMXWriter_get_omitXMLDeclaration(writer, NULL);
2746 ok(hr == E_POINTER, "got %08x\n", hr);
2747
2748 hr = IMXWriter_get_standalone(writer, NULL);
2749 ok(hr == E_POINTER, "got %08x\n", hr);
2750
2751 /* set and check */
2752 hr = IMXWriter_put_standalone(writer, VARIANT_TRUE);
2753 ok(hr == S_OK, "got %08x\n", hr);
2754
2755 b = VARIANT_FALSE;
2756 hr = IMXWriter_get_standalone(writer, &b);
2757 ok(hr == S_OK, "got %08x\n", hr);
2758 ok(b == VARIANT_TRUE, "got %d\n", b);
2759
2760 hr = IMXWriter_get_encoding(writer, NULL);
2761 EXPECT_HR(hr, E_POINTER);
2762
2763 /* UTF-16 is a default setting apparently */
2764 str = (void*)0xdeadbeef;
2765 hr = IMXWriter_get_encoding(writer, &str);
2766 EXPECT_HR(hr, S_OK);
2767 ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str));
2768
2769 str2 = (void*)0xdeadbeef;
2770 hr = IMXWriter_get_encoding(writer, &str2);
2771 ok(hr == S_OK, "got %08x\n", hr);
2772 ok(str != str2, "expected newly allocated, got same %p\n", str);
2773
2774 SysFreeString(str2);
2775 SysFreeString(str);
2776
2777 /* put empty string */
2778 str = SysAllocString(emptyW);
2779 hr = IMXWriter_put_encoding(writer, str);
2780 ok(hr == E_INVALIDARG, "got %08x\n", hr);
2781 SysFreeString(str);
2782
2783 str = (void*)0xdeadbeef;
2784 hr = IMXWriter_get_encoding(writer, &str);
2785 EXPECT_HR(hr, S_OK);
2786 ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str));
2787 SysFreeString(str);
2788
2789 /* invalid encoding name */
2790 str = SysAllocString(testW);
2791 hr = IMXWriter_put_encoding(writer, str);
2792 ok(hr == E_INVALIDARG, "got %08x\n", hr);
2793 SysFreeString(str);
2794
2795 /* test case sensivity */
2796 hr = IMXWriter_put_encoding(writer, _bstr_("utf-8"));
2797 EXPECT_HR(hr, S_OK);
2798 str = (void*)0xdeadbeef;
2799 hr = IMXWriter_get_encoding(writer, &str);
2800 EXPECT_HR(hr, S_OK);
2801 ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str));
2802 SysFreeString(str);
2803
2804 hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16"));
2805 EXPECT_HR(hr, S_OK);
2806 str = (void*)0xdeadbeef;
2807 hr = IMXWriter_get_encoding(writer, &str);
2808 EXPECT_HR(hr, S_OK);
2809 ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str));
2810 SysFreeString(str);
2811
2812 /* how it affects document creation */
2813 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2814 EXPECT_HR(hr, S_OK);
2815
2816 hr = ISAXContentHandler_startDocument(content);
2817 EXPECT_HR(hr, S_OK);
2818 hr = ISAXContentHandler_endDocument(content);
2819 EXPECT_HR(hr, S_OK);
2820
2821 V_VT(&dest) = VT_EMPTY;
2822 hr = IMXWriter_get_output(writer, &dest);
2823 EXPECT_HR(hr, S_OK);
2824 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
2825 ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"yes\"?>\r\n"),
2826 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
2827 VariantClear(&dest);
2828 ISAXContentHandler_Release(content);
2829
2830 hr = IMXWriter_get_version(writer, NULL);
2831 ok(hr == E_POINTER, "got %08x\n", hr);
2832 /* default version is 'surprisingly' 1.0 */
2833 hr = IMXWriter_get_version(writer, &str);
2834 ok(hr == S_OK, "got %08x\n", hr);
2835 ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str));
2836 SysFreeString(str);
2837
2838 /* store version string as is */
2839 hr = IMXWriter_put_version(writer, NULL);
2840 ok(hr == E_INVALIDARG, "got %08x\n", hr);
2841
2842 hr = IMXWriter_put_version(writer, _bstr_("1.0"));
2843 ok(hr == S_OK, "got %08x\n", hr);
2844
2845 hr = IMXWriter_put_version(writer, _bstr_(""));
2846 ok(hr == S_OK, "got %08x\n", hr);
2847 hr = IMXWriter_get_version(writer, &str);
2848 ok(hr == S_OK, "got %08x\n", hr);
2849 ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str));
2850 SysFreeString(str);
2851
2852 hr = IMXWriter_put_version(writer, _bstr_("a.b"));
2853 ok(hr == S_OK, "got %08x\n", hr);
2854 hr = IMXWriter_get_version(writer, &str);
2855 ok(hr == S_OK, "got %08x\n", hr);
2856 ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str));
2857 SysFreeString(str);
2858
2859 hr = IMXWriter_put_version(writer, _bstr_("2.0"));
2860 ok(hr == S_OK, "got %08x\n", hr);
2861 hr = IMXWriter_get_version(writer, &str);
2862 ok(hr == S_OK, "got %08x\n", hr);
2863 ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str));
2864 SysFreeString(str);
2865
2866 IMXWriter_Release(writer);
2867 free_bstrs();
2868 }
2869
2870 static void test_mxwriter_flush(void)
2871 {
2872 static const WCHAR emptyW[] = {0};
2873 ISAXContentHandler *content;
2874 IMXWriter *writer;
2875 LARGE_INTEGER pos;
2876 ULARGE_INTEGER pos2;
2877 IStream *stream;
2878 VARIANT dest;
2879 HRESULT hr;
2880 char *buff;
2881 LONG ref;
2882 int len;
2883
2884 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
2885 &IID_IMXWriter, (void**)&writer);
2886 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2887
2888 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2889 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2890 EXPECT_REF(stream, 1);
2891
2892 /* detach when nothing was attached */
2893 V_VT(&dest) = VT_EMPTY;
2894 hr = IMXWriter_put_output(writer, dest);
2895 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2896
2897 /* attach stream */
2898 V_VT(&dest) = VT_UNKNOWN;
2899 V_UNKNOWN(&dest) = (IUnknown*)stream;
2900 hr = IMXWriter_put_output(writer, dest);
2901 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2902 todo_wine EXPECT_REF(stream, 3);
2903
2904 /* detach setting VT_EMPTY destination */
2905 V_VT(&dest) = VT_EMPTY;
2906 hr = IMXWriter_put_output(writer, dest);
2907 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2908 EXPECT_REF(stream, 1);
2909
2910 V_VT(&dest) = VT_UNKNOWN;
2911 V_UNKNOWN(&dest) = (IUnknown*)stream;
2912 hr = IMXWriter_put_output(writer, dest);
2913 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2914
2915 /* flush() doesn't detach a stream */
2916 hr = IMXWriter_flush(writer);
2917 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2918 todo_wine EXPECT_REF(stream, 3);
2919
2920 pos.QuadPart = 0;
2921 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2922 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2923 ok(pos2.QuadPart == 0, "expected stream beginning\n");
2924
2925 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
2926 ok(hr == S_OK, "got %08x\n", hr);
2927
2928 hr = ISAXContentHandler_startDocument(content);
2929 ok(hr == S_OK, "got %08x\n", hr);
2930
2931 pos.QuadPart = 0;
2932 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2933 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2934 ok(pos2.QuadPart != 0, "expected stream beginning\n");
2935
2936 /* already started */
2937 hr = ISAXContentHandler_startDocument(content);
2938 ok(hr == S_OK, "got %08x\n", hr);
2939
2940 hr = ISAXContentHandler_endDocument(content);
2941 ok(hr == S_OK, "got %08x\n", hr);
2942
2943 /* flushed on endDocument() */
2944 pos.QuadPart = 0;
2945 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2946 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
2947 ok(pos2.QuadPart != 0, "expected stream position moved\n");
2948
2949 IStream_Release(stream);
2950
2951 /* auto-flush feature */
2952 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
2953 EXPECT_HR(hr, S_OK);
2954 EXPECT_REF(stream, 1);
2955
2956 V_VT(&dest) = VT_UNKNOWN;
2957 V_UNKNOWN(&dest) = (IUnknown*)stream;
2958 hr = IMXWriter_put_output(writer, dest);
2959 EXPECT_HR(hr, S_OK);
2960
2961 hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE);
2962 EXPECT_HR(hr, S_OK);
2963
2964 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
2965 EXPECT_HR(hr, S_OK);
2966
2967 hr = ISAXContentHandler_startDocument(content);
2968 EXPECT_HR(hr, S_OK);
2969
2970 hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
2971 EXPECT_HR(hr, S_OK);
2972
2973 /* internal buffer is flushed automatically on certain threshold */
2974 pos.QuadPart = 0;
2975 pos2.QuadPart = 1;
2976 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2977 EXPECT_HR(hr, S_OK);
2978 ok(pos2.QuadPart == 0, "expected stream beginning\n");
2979
2980 len = 2048;
2981 buff = HeapAlloc(GetProcessHeap(), 0, len);
2982 memset(buff, 'A', len);
2983 hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
2984 EXPECT_HR(hr, S_OK);
2985
2986 pos.QuadPart = 0;
2987 pos2.QuadPart = 0;
2988 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
2989 EXPECT_HR(hr, S_OK);
2990 todo_wine
2991 ok(pos2.QuadPart != 0, "unexpected stream beginning\n");
2992
2993 hr = IMXWriter_get_output(writer, NULL);
2994 EXPECT_HR(hr, E_POINTER);
2995
2996 ref = get_refcount(stream);
2997 V_VT(&dest) = VT_EMPTY;
2998 hr = IMXWriter_get_output(writer, &dest);
2999 EXPECT_HR(hr, S_OK);
3000 ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest));
3001 ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest));
3002 ok(ref+1 == get_refcount(stream), "expected increased refcount\n");
3003 VariantClear(&dest);
3004
3005 hr = ISAXContentHandler_endDocument(content);
3006 EXPECT_HR(hr, S_OK);
3007
3008 IStream_Release(stream);
3009
3010 /* test char count lower than threshold */
3011 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3012 EXPECT_HR(hr, S_OK);
3013 EXPECT_REF(stream, 1);
3014
3015 hr = ISAXContentHandler_startDocument(content);
3016 EXPECT_HR(hr, S_OK);
3017
3018 hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3019 EXPECT_HR(hr, S_OK);
3020
3021 pos.QuadPart = 0;
3022 pos2.QuadPart = 1;
3023 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3024 EXPECT_HR(hr, S_OK);
3025 ok(pos2.QuadPart == 0, "expected stream beginning\n");
3026
3027 memset(buff, 'A', len);
3028 hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8);
3029 EXPECT_HR(hr, S_OK);
3030
3031 pos.QuadPart = 0;
3032 pos2.QuadPart = 1;
3033 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3034 EXPECT_HR(hr, S_OK);
3035 ok(pos2.QuadPart == 0, "expected stream beginning\n");
3036
3037 hr = ISAXContentHandler_endDocument(content);
3038 EXPECT_HR(hr, S_OK);
3039
3040 /* test auto-flush function when stream is not set */
3041 V_VT(&dest) = VT_EMPTY;
3042 hr = IMXWriter_put_output(writer, dest);
3043 EXPECT_HR(hr, S_OK);
3044
3045 hr = ISAXContentHandler_startDocument(content);
3046 EXPECT_HR(hr, S_OK);
3047
3048 hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL);
3049 EXPECT_HR(hr, S_OK);
3050
3051 memset(buff, 'A', len);
3052 hr = ISAXContentHandler_characters(content, _bstr_(buff), len);
3053 EXPECT_HR(hr, S_OK);
3054
3055 V_VT(&dest) = VT_EMPTY;
3056 hr = IMXWriter_get_output(writer, &dest);
3057 EXPECT_HR(hr, S_OK);
3058 len += strlen("<a>");
3059 ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len);
3060 VariantClear(&dest);
3061
3062 HeapFree(GetProcessHeap(), 0, buff);
3063 ISAXContentHandler_Release(content);
3064 IStream_Release(stream);
3065 IMXWriter_Release(writer);
3066 free_bstrs();
3067 }
3068
3069 static void test_mxwriter_startenddocument(void)
3070 {
3071 ISAXContentHandler *content;
3072 IMXWriter *writer;
3073 VARIANT dest;
3074 HRESULT hr;
3075
3076 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3077 &IID_IMXWriter, (void**)&writer);
3078 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3079
3080 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3081 ok(hr == S_OK, "got %08x\n", hr);
3082
3083 hr = ISAXContentHandler_startDocument(content);
3084 ok(hr == S_OK, "got %08x\n", hr);
3085
3086 hr = ISAXContentHandler_endDocument(content);
3087 ok(hr == S_OK, "got %08x\n", hr);
3088
3089 V_VT(&dest) = VT_EMPTY;
3090 hr = IMXWriter_get_output(writer, &dest);
3091 ok(hr == S_OK, "got %08x\n", hr);
3092 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3093 ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3094 "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3095 VariantClear(&dest);
3096
3097 /* now try another startDocument */
3098 hr = ISAXContentHandler_startDocument(content);
3099 ok(hr == S_OK, "got %08x\n", hr);
3100 /* and get duplicated prolog */
3101 V_VT(&dest) = VT_EMPTY;
3102 hr = IMXWriter_get_output(writer, &dest);
3103 ok(hr == S_OK, "got %08x\n", hr);
3104 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3105 ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"
3106 "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3107 "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3108 VariantClear(&dest);
3109
3110 ISAXContentHandler_Release(content);
3111 IMXWriter_Release(writer);
3112
3113 /* now with omitted declaration */
3114 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3115 &IID_IMXWriter, (void**)&writer);
3116 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3117
3118 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3119 ok(hr == S_OK, "got %08x\n", hr);
3120
3121 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3122 ok(hr == S_OK, "got %08x\n", hr);
3123
3124 hr = ISAXContentHandler_startDocument(content);
3125 ok(hr == S_OK, "got %08x\n", hr);
3126
3127 hr = ISAXContentHandler_endDocument(content);
3128 ok(hr == S_OK, "got %08x\n", hr);
3129
3130 V_VT(&dest) = VT_EMPTY;
3131 hr = IMXWriter_get_output(writer, &dest);
3132 ok(hr == S_OK, "got %08x\n", hr);
3133 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3134 ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3135 VariantClear(&dest);
3136
3137 ISAXContentHandler_Release(content);
3138 IMXWriter_Release(writer);
3139
3140 free_bstrs();
3141 }
3142
3143 enum startendtype
3144 {
3145 StartElement = 0x001,
3146 EndElement = 0x010,
3147 StartEndElement = 0x011,
3148 DisableEscaping = 0x100
3149 };
3150
3151 struct writer_startendelement_t {
3152 const GUID *clsid;
3153 enum startendtype type;
3154 const char *uri;
3155 const char *local_name;
3156 const char *qname;
3157 const char *output;
3158 HRESULT hr;
3159 ISAXAttributes *attr;
3160 };
3161
3162 static const char startelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\">";
3163 static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"&lt;&amp;&quot;&gt;\"/>";
3164 static const char startendelement_noescape_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&\">\"/>";
3165
3166 static const struct writer_startendelement_t writer_startendelement[] = {
3167 /* 0 */
3168 { &CLSID_MXXMLWriter, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3169 { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3170 { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3171 { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK },
3172 { &CLSID_MXXMLWriter, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3173 /* 5 */
3174 { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3175 { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3176 { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK },
3177 { &CLSID_MXXMLWriter, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3178 { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3179 /* 10 */
3180 { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3181 { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK },
3182 { &CLSID_MXXMLWriter, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3183 { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3184 { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3185 /* 15 */
3186 { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "<qname>", S_OK },
3187 { &CLSID_MXXMLWriter, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3188 { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3189 { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3190 { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "<qname>", S_OK },
3191 /* 20 */
3192 { &CLSID_MXXMLWriter, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3193 { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3194 { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3195 { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK },
3196 { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3197 /* 25 */
3198 { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3199 { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3200 { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK },
3201 { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3202 { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3203 /* 30 */
3204 { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3205 { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "<uri:local2>", S_OK },
3206 /* endElement tests */
3207 { &CLSID_MXXMLWriter, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3208 { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3209 { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG },
3210 /* 35 */
3211 { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "</>", S_OK },
3212 { &CLSID_MXXMLWriter, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3213 { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3214 { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG },
3215 { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "</>", S_OK },
3216 /* 40 */
3217 { &CLSID_MXXMLWriter, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3218 { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3219 { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG },
3220 { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "</>", S_OK },
3221 { &CLSID_MXXMLWriter, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3222 /* 45 */
3223 { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3224 { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG },
3225 { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "</qname>", S_OK },
3226 { &CLSID_MXXMLWriter, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3227 { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3228 /* 50 */
3229 { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3230 { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "</qname>", S_OK },
3231 { &CLSID_MXXMLWriter, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3232 { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3233 { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG },
3234 /* 55 */
3235 { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "</>", S_OK },
3236 { &CLSID_MXXMLWriter, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3237 { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3238 { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3239 { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK },
3240 /* 60 */
3241 { &CLSID_MXXMLWriter, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3242 { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3243 { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3244 { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "</uri:local2>", S_OK },
3245
3246 /* with attributes */
3247 { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3248 /* 65 */
3249 { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3250 { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3251 { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes },
3252 /* empty elements */
3253 { &CLSID_MXXMLWriter, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3254 { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3255 /* 70 */
3256 { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3257 { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3258 { &CLSID_MXXMLWriter, StartEndElement, "", "", "", "</>", S_OK },
3259 { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "</>", S_OK },
3260 { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "</>", S_OK },
3261 /* 75 */
3262 { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "</>", S_OK },
3263
3264 /* with disabled output escaping */
3265 { &CLSID_MXXMLWriter, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3266 { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes },
3267 { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3268 { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes },
3269
3270 { NULL }
3271 };
3272
3273 static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid)
3274 {
3275 while (table->clsid)
3276 {
3277 IUnknown *unk;
3278 HRESULT hr;
3279
3280 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk);
3281 if (hr == S_OK) IUnknown_Release(unk);
3282
3283 table->supported = hr == S_OK;
3284 if (hr != S_OK) win_skip("class %s not supported\n", table->name);
3285
3286 table++;
3287 }
3288 }
3289
3290 static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table)
3291 {
3292 int i = 0;
3293
3294 while (table->clsid)
3295 {
3296 ISAXContentHandler *content;
3297 IMXWriter *writer;
3298 HRESULT hr;
3299
3300 if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3301 {
3302 table++;
3303 i++;
3304 continue;
3305 }
3306
3307 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3308 &IID_IMXWriter, (void**)&writer);
3309 EXPECT_HR(hr, S_OK);
3310
3311 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3312 EXPECT_HR(hr, S_OK);
3313
3314 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3315 EXPECT_HR(hr, S_OK);
3316
3317 hr = ISAXContentHandler_startDocument(content);
3318 EXPECT_HR(hr, S_OK);
3319
3320 if (table->type & DisableEscaping)
3321 {
3322 hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3323 EXPECT_HR(hr, S_OK);
3324 }
3325
3326 if (table->type & StartElement)
3327 {
3328 hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), lstrlen(table->uri),
3329 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname), table->attr);
3330 ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3331 }
3332
3333 if (table->type & EndElement)
3334 {
3335 hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), lstrlen(table->uri),
3336 _bstr_(table->local_name), lstrlen(table->local_name), _bstr_(table->qname), lstrlen(table->qname));
3337 ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3338 }
3339
3340 /* test output */
3341 if (hr == S_OK)
3342 {
3343 VARIANT dest;
3344
3345 V_VT(&dest) = VT_EMPTY;
3346 hr = IMXWriter_get_output(writer, &dest);
3347 EXPECT_HR(hr, S_OK);
3348 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3349 ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3350 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3351 VariantClear(&dest);
3352 }
3353
3354 ISAXContentHandler_Release(content);
3355 IMXWriter_Release(writer);
3356
3357 table++;
3358 i++;
3359 }
3360
3361 free_bstrs();
3362 }
3363
3364 /* point of these test is to start/end element with different names and name lengths */
3365 struct writer_startendelement2_t {
3366 const GUID *clsid;
3367 const char *qnamestart;
3368 int qnamestart_len;
3369 const char *qnameend;
3370 int qnameend_len;
3371 const char *output;
3372 HRESULT hr;
3373 };
3374
3375 static const struct writer_startendelement2_t writer_startendelement2[] = {
3376 { &CLSID_MXXMLWriter, "a", -1, "b", -1, "<a/>", S_OK },
3377 { &CLSID_MXXMLWriter30, "a", -1, "b", -1, "<a/>", S_OK },
3378 { &CLSID_MXXMLWriter40, "a", -1, "b", -1, "<a/>", S_OK },
3379 /* -1 length is not allowed for version 6 */
3380 { &CLSID_MXXMLWriter60, "a", -1, "b", -1, "<a/>", E_INVALIDARG },
3381
3382 { &CLSID_MXXMLWriter, "a", 1, "b", 1, "<a/>", S_OK },
3383 { &CLSID_MXXMLWriter30, "a", 1, "b", 1, "<a/>", S_OK },
3384 { &CLSID_MXXMLWriter40, "a", 1, "b", 1, "<a/>", S_OK },
3385 { &CLSID_MXXMLWriter60, "a", 1, "b", 1, "<a/>", S_OK },
3386 { NULL }
3387 };
3388
3389 static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table)
3390 {
3391 int i = 0;
3392
3393 while (table->clsid)
3394 {
3395 ISAXContentHandler *content;
3396 IMXWriter *writer;
3397 HRESULT hr;
3398
3399 if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3400 {
3401 table++;
3402 i++;
3403 continue;
3404 }
3405
3406 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3407 &IID_IMXWriter, (void**)&writer);
3408 EXPECT_HR(hr, S_OK);
3409
3410 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3411 EXPECT_HR(hr, S_OK);
3412
3413 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3414 EXPECT_HR(hr, S_OK);
3415
3416 hr = ISAXContentHandler_startDocument(content);
3417 EXPECT_HR(hr, S_OK);
3418
3419 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0,
3420 _bstr_(table->qnamestart), table->qnamestart_len, NULL);
3421 ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3422
3423 hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0,
3424 _bstr_(table->qnameend), table->qnameend_len);
3425 ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
3426
3427 /* test output */
3428 if (hr == S_OK)
3429 {
3430 VARIANT dest;
3431
3432 V_VT(&dest) = VT_EMPTY;
3433 hr = IMXWriter_get_output(writer, &dest);
3434 EXPECT_HR(hr, S_OK);
3435 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3436 ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3437 "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3438 VariantClear(&dest);
3439 }
3440
3441 ISAXContentHandler_Release(content);
3442 IMXWriter_Release(writer);
3443
3444 table++;
3445 i++;
3446
3447 free_bstrs();
3448 }
3449 }
3450
3451
3452 static void test_mxwriter_startendelement(void)
3453 {
3454 ISAXContentHandler *content;
3455 IMXWriter *writer;
3456 VARIANT dest;
3457 HRESULT hr;
3458
3459 test_mxwriter_startendelement_batch(writer_startendelement);
3460 test_mxwriter_startendelement_batch2(writer_startendelement2);
3461
3462 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3463 &IID_IMXWriter, (void**)&writer);
3464 ok(hr == S_OK, "Expected S_OK, got %08x\n", hr);
3465
3466 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3467 ok(hr == S_OK, "got %08x\n", hr);
3468
3469 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3470 ok(hr == S_OK, "got %08x\n", hr);
3471
3472 hr = ISAXContentHandler_startDocument(content);
3473 ok(hr == S_OK, "got %08x\n", hr);
3474
3475 /* all string pointers should be not null */
3476 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL);
3477 ok(hr == S_OK, "got %08x\n", hr);
3478
3479 V_VT(&dest) = VT_EMPTY;
3480 hr = IMXWriter_get_output(writer, &dest);
3481 ok(hr == S_OK, "got %08x\n", hr);
3482 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3483 ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3484 VariantClear(&dest);
3485
3486 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL);
3487 ok(hr == S_OK, "got %08x\n", hr);
3488
3489 V_VT(&dest) = VT_EMPTY;
3490 hr = IMXWriter_get_output(writer, &dest);
3491 ok(hr == S_OK, "got %08x\n", hr);
3492 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3493 ok(!lstrcmpW(_bstr_("<><b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3494 VariantClear(&dest);
3495
3496 hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3);
3497 EXPECT_HR(hr, E_INVALIDARG);
3498
3499 hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3);
3500 EXPECT_HR(hr, E_INVALIDARG);
3501
3502 /* only local name is an error too */
3503 hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0);
3504 EXPECT_HR(hr, E_INVALIDARG);
3505
3506 hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1);
3507 EXPECT_HR(hr, S_OK);
3508
3509 V_VT(&dest) = VT_EMPTY;
3510 hr = IMXWriter_get_output(writer, &dest);
3511 EXPECT_HR(hr, S_OK);
3512 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3513 ok(!lstrcmpW(_bstr_("<><b></b>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3514 VariantClear(&dest);
3515
3516 hr = ISAXContentHandler_endDocument(content);
3517 EXPECT_HR(hr, S_OK);
3518
3519 V_VT(&dest) = VT_EMPTY;
3520 hr = IMXWriter_put_output(writer, dest);
3521 EXPECT_HR(hr, S_OK);
3522
3523 V_VT(&dest) = VT_EMPTY;
3524 hr = IMXWriter_get_output(writer, &dest);
3525 EXPECT_HR(hr, S_OK);
3526 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3527 ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3528 VariantClear(&dest);
3529
3530 hr = ISAXContentHandler_startDocument(content);
3531 EXPECT_HR(hr, S_OK);
3532
3533 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL);
3534 EXPECT_HR(hr, S_OK);
3535
3536 V_VT(&dest) = VT_EMPTY;
3537 hr = IMXWriter_get_output(writer, &dest);
3538 EXPECT_HR(hr, S_OK);
3539 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3540 ok(!lstrcmpW(_bstr_("<abc>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3541 VariantClear(&dest);
3542
3543 ISAXContentHandler_endDocument(content);
3544 IMXWriter_flush(writer);
3545
3546 hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3);
3547 EXPECT_HR(hr, S_OK);
3548 V_VT(&dest) = VT_EMPTY;
3549 hr = IMXWriter_get_output(writer, &dest);
3550 EXPECT_HR(hr, S_OK);
3551 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3552 ok(!lstrcmpW(_bstr_("<abc></abd>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3553 VariantClear(&dest);
3554
3555 V_VT(&dest) = VT_EMPTY;
3556 hr = IMXWriter_put_output(writer, dest);
3557 EXPECT_HR(hr, S_OK);
3558
3559 /* length -1 */
3560 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL);
3561 EXPECT_HR(hr, S_OK);
3562 V_VT(&dest) = VT_EMPTY;
3563 hr = IMXWriter_get_output(writer, &dest);
3564 EXPECT_HR(hr, S_OK);
3565 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3566 ok(!lstrcmpW(_bstr_("<a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3567 VariantClear(&dest);
3568
3569 ISAXContentHandler_Release(content);
3570 IMXWriter_Release(writer);
3571 free_bstrs();
3572 }
3573
3574 struct writer_characters_t {
3575 const GUID *clsid;
3576 const char *data;
3577 const char *output;
3578 };
3579
3580 static const struct writer_characters_t writer_characters[] = {
3581 { &CLSID_MXXMLWriter, "< > & \"", "&lt; &gt; &amp; \"" },
3582 { &CLSID_MXXMLWriter30, "< > & \"", "&lt; &gt; &amp; \"" },
3583 { &CLSID_MXXMLWriter40, "< > & \"", "&lt; &gt; &amp; \"" },
3584 { &CLSID_MXXMLWriter60, "< > & \"", "&lt; &gt; &amp; \"" },
3585 { NULL }
3586 };
3587
3588 static void test_mxwriter_characters(void)
3589 {
3590 static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0};
3591 const struct writer_characters_t *table = writer_characters;
3592 ISAXContentHandler *content;
3593 IMXWriter *writer;
3594 VARIANT dest;
3595 HRESULT hr;
3596 int i = 0;
3597
3598 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3599 &IID_IMXWriter, (void**)&writer);
3600 EXPECT_HR(hr, S_OK);
3601
3602 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3603 EXPECT_HR(hr, S_OK);
3604
3605 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3606 EXPECT_HR(hr, S_OK);
3607
3608 hr = ISAXContentHandler_startDocument(content);
3609 EXPECT_HR(hr, S_OK);
3610
3611 hr = ISAXContentHandler_characters(content, NULL, 0);
3612 EXPECT_HR(hr, E_INVALIDARG);
3613
3614 hr = ISAXContentHandler_characters(content, chardataW, 0);
3615 EXPECT_HR(hr, S_OK);
3616
3617 hr = ISAXContentHandler_characters(content, chardataW, sizeof(chardataW)/sizeof(WCHAR) - 1);
3618 EXPECT_HR(hr, S_OK);
3619
3620 V_VT(&dest) = VT_EMPTY;
3621 hr = IMXWriter_get_output(writer, &dest);
3622 EXPECT_HR(hr, S_OK);
3623 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3624 ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3625 VariantClear(&dest);
3626
3627 hr = ISAXContentHandler_endDocument(content);
3628 EXPECT_HR(hr, S_OK);
3629
3630 ISAXContentHandler_Release(content);
3631 IMXWriter_Release(writer);
3632
3633 /* try empty characters data to see if element is closed */
3634 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3635 &IID_IMXWriter, (void**)&writer);
3636 EXPECT_HR(hr, S_OK);
3637
3638 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3639 EXPECT_HR(hr, S_OK);
3640
3641 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3642 EXPECT_HR(hr, S_OK);
3643
3644 hr = ISAXContentHandler_startDocument(content);
3645 EXPECT_HR(hr, S_OK);
3646
3647 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3648 EXPECT_HR(hr, S_OK);
3649
3650 hr = ISAXContentHandler_characters(content, chardataW, 0);
3651 EXPECT_HR(hr, S_OK);
3652
3653 hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3654 EXPECT_HR(hr, S_OK);
3655
3656 V_VT(&dest) = VT_EMPTY;
3657 hr = IMXWriter_get_output(writer, &dest);
3658 EXPECT_HR(hr, S_OK);
3659 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3660 ok(!lstrcmpW(_bstr_("<a></a>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3661 VariantClear(&dest);
3662
3663 ISAXContentHandler_Release(content);
3664 IMXWriter_Release(writer);
3665
3666 /* batch tests */
3667 while (table->clsid)
3668 {
3669 ISAXContentHandler *content;
3670 IMXWriter *writer;
3671 VARIANT dest;
3672 HRESULT hr;
3673
3674 if (!is_clsid_supported(table->clsid, mxwriter_support_data))
3675 {
3676 table++;
3677 i++;
3678 continue;
3679 }
3680
3681 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
3682 &IID_IMXWriter, (void**)&writer);
3683 EXPECT_HR(hr, S_OK);
3684
3685 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3686 EXPECT_HR(hr, S_OK);
3687
3688 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
3689 EXPECT_HR(hr, S_OK);
3690
3691 hr = ISAXContentHandler_startDocument(content);
3692 EXPECT_HR(hr, S_OK);
3693
3694 hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3695 EXPECT_HR(hr, S_OK);
3696
3697 /* test output */
3698 if (hr == S_OK)
3699 {
3700 V_VT(&dest) = VT_EMPTY;
3701 hr = IMXWriter_get_output(writer, &dest);
3702 EXPECT_HR(hr, S_OK);
3703 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3704 ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)),
3705 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output);
3706 VariantClear(&dest);
3707 }
3708
3709 /* with disabled escaping */
3710 V_VT(&dest) = VT_EMPTY;
3711 hr = IMXWriter_put_output(writer, dest);
3712 EXPECT_HR(hr, S_OK);
3713
3714 hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE);
3715 EXPECT_HR(hr, S_OK);
3716
3717 hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data));
3718 EXPECT_HR(hr, S_OK);
3719
3720 /* test output */
3721 if (hr == S_OK)
3722 {
3723 V_VT(&dest) = VT_EMPTY;
3724 hr = IMXWriter_get_output(writer, &dest);
3725 EXPECT_HR(hr, S_OK);
3726 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
3727 ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)),
3728 "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data);
3729 VariantClear(&dest);
3730 }
3731
3732 ISAXContentHandler_Release(content);
3733 IMXWriter_Release(writer);
3734
3735 table++;
3736 i++;
3737 }
3738
3739 free_bstrs();
3740 }
3741
3742 static const mxwriter_stream_test mxwriter_stream_tests[] = {
3743 {
3744 VARIANT_TRUE,"UTF-16",
3745 {
3746 {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3747 {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3748 {TRUE}
3749 }
3750 },
3751 {
3752 VARIANT_FALSE,"UTF-16",
3753 {
3754 {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3755 {TRUE}
3756 }
3757 },
3758 {
3759 VARIANT_TRUE,"UTF-8",
3760 {
3761 {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1},
3762 /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3763 * and the writer is released.
3764 */
3765 {FALSE,NULL,0},
3766 {TRUE}
3767 }
3768 },
3769 {
3770 VARIANT_TRUE,"utf-8",
3771 {
3772 {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1},
3773 /* For some reason Windows makes an empty write call when UTF-8 encoding is used
3774 * and the writer is released.
3775 */
3776 {FALSE,NULL,0},
3777 {TRUE}
3778 }
3779 },
3780 {
3781 VARIANT_TRUE,"UTF-16",
3782 {
3783 {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE},
3784 {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3785 {TRUE}
3786 }
3787 },
3788 {
3789 VARIANT_TRUE,"UTF-16",
3790 {
3791 {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE},
3792 {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)},
3793 {TRUE}
3794 }
3795 }
3796 };
3797
3798 static void test_mxwriter_stream(void)
3799 {
3800 IMXWriter *writer;
3801 ISAXContentHandler *content;
3802 HRESULT hr;
3803 VARIANT dest;
3804 IStream *stream;
3805 LARGE_INTEGER pos;
3806 ULARGE_INTEGER pos2;
3807 DWORD test_count = sizeof(mxwriter_stream_tests)/sizeof(mxwriter_stream_tests[0]);
3808
3809 for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) {
3810 const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index;
3811
3812 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3813 &IID_IMXWriter, (void**)&writer);
3814 ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3815
3816 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3817 ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3818
3819 hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding));
3820 ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index);
3821
3822 V_VT(&dest) = VT_UNKNOWN;
3823 V_UNKNOWN(&dest) = (IUnknown*)&mxstream;
3824 hr = IMXWriter_put_output(writer, dest);
3825 ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index);
3826 VariantClear(&dest);
3827
3828 hr = IMXWriter_put_byteOrderMark(writer, test->bom);
3829 ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index);
3830
3831 current_write_test = test->expected_writes;
3832
3833 hr = ISAXContentHandler_startDocument(content);
3834 ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3835
3836 hr = ISAXContentHandler_endDocument(content);
3837 ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index);
3838
3839 ISAXContentHandler_Release(content);
3840 IMXWriter_Release(writer);
3841
3842 ok(current_write_test->last, "The last %d write calls on test %d were missed\n",
3843 (int)(current_write_test-test->expected_writes), current_stream_test_index);
3844 }
3845
3846 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3847 &IID_IMXWriter, (void**)&writer);
3848 ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr);
3849
3850 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3851 ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr);
3852
3853 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3854 ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr);
3855
3856 hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3857 ok(hr == S_OK, "put_encoding failed: %08x\n", hr);
3858
3859 V_VT(&dest) = VT_UNKNOWN;
3860 V_UNKNOWN(&dest) = (IUnknown*)stream;
3861 hr = IMXWriter_put_output(writer, dest);
3862 ok(hr == S_OK, "put_output failed: %08x\n", hr);
3863
3864 hr = ISAXContentHandler_startDocument(content);
3865 ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3866
3867 /* Setting output of the mxwriter causes the current output to be flushed,
3868 * and the writer to start over.
3869 */
3870 V_VT(&dest) = VT_EMPTY;
3871 hr = IMXWriter_put_output(writer, dest);
3872 ok(hr == S_OK, "put_output failed: %08x\n", hr);
3873
3874 pos.QuadPart = 0;
3875 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3876 ok(hr == S_OK, "Seek failed: %08x\n", hr);
3877 ok(pos2.QuadPart != 0, "expected stream position moved\n");
3878
3879 hr = ISAXContentHandler_startDocument(content);
3880 ok(hr == S_OK, "startDocument failed: %08x\n", hr);
3881
3882 hr = ISAXContentHandler_endDocument(content);
3883 ok(hr == S_OK, "endDocument failed: %08x\n", hr);
3884
3885 V_VT(&dest) = VT_EMPTY;
3886 hr = IMXWriter_get_output(writer, &dest);
3887 ok(hr == S_OK, "get_output failed: %08x\n", hr);
3888 ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3889 ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3890 "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3891 VariantClear(&dest);
3892
3893 /* test when BOM is written to output stream */
3894 V_VT(&dest) = VT_EMPTY;
3895 hr = IMXWriter_put_output(writer, dest);
3896 EXPECT_HR(hr, S_OK);
3897
3898 pos.QuadPart = 0;
3899 hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL);
3900 EXPECT_HR(hr, S_OK);
3901
3902 V_VT(&dest) = VT_UNKNOWN;
3903 V_UNKNOWN(&dest) = (IUnknown*)stream;
3904 hr = IMXWriter_put_output(writer, dest);
3905 EXPECT_HR(hr, S_OK);
3906
3907 hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE);
3908 EXPECT_HR(hr, S_OK);
3909
3910 hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
3911 EXPECT_HR(hr, S_OK);
3912
3913 hr = ISAXContentHandler_startDocument(content);
3914 EXPECT_HR(hr, S_OK);
3915
3916 pos.QuadPart = 0;
3917 pos2.QuadPart = 0;
3918 hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2);
3919 EXPECT_HR(hr, S_OK);
3920 ok(pos2.QuadPart == 2, "got wrong position\n");
3921
3922 ISAXContentHandler_Release(content);
3923 IMXWriter_Release(writer);
3924
3925 free_bstrs();
3926 }
3927
3928 static const char *encoding_names[] = {
3929 "iso-8859-1",
3930 "iso-8859-2",
3931 "iso-8859-3",
3932 "iso-8859-4",
3933 "iso-8859-5",
3934 "iso-8859-7",
3935 "iso-8859-9",
3936 "iso-8859-13",
3937 "iso-8859-15",
3938 NULL
3939 };
3940
3941 static void test_mxwriter_encoding(void)
3942 {
3943 ISAXContentHandler *content;
3944 IMXWriter *writer;
3945 IStream *stream;
3946 const char *enc;
3947 VARIANT dest;
3948 HRESULT hr;
3949 HGLOBAL g;
3950 char *ptr;
3951 BSTR s;
3952 int i;
3953
3954 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
3955 &IID_IMXWriter, (void**)&writer);
3956 EXPECT_HR(hr, S_OK);
3957
3958 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
3959 EXPECT_HR(hr, S_OK);
3960
3961 hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3962 EXPECT_HR(hr, S_OK);
3963
3964 hr = ISAXContentHandler_startDocument(content);
3965 EXPECT_HR(hr, S_OK);
3966
3967 hr = ISAXContentHandler_endDocument(content);
3968 EXPECT_HR(hr, S_OK);
3969
3970 /* The content is always re-encoded to UTF-16 when the output is
3971 * retrieved as a BSTR.
3972 */
3973 V_VT(&dest) = VT_EMPTY;
3974 hr = IMXWriter_get_output(writer, &dest);
3975 EXPECT_HR(hr, S_OK);
3976 ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest));
3977 ok(!lstrcmpW(_bstr_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n"), V_BSTR(&dest)),
3978 "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest)));
3979 VariantClear(&dest);
3980
3981 /* switch encoding when something is written already */
3982 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
3983 EXPECT_HR(hr, S_OK);
3984
3985 V_VT(&dest) = VT_UNKNOWN;
3986 V_UNKNOWN(&dest) = (IUnknown*)stream;
3987 hr = IMXWriter_put_output(writer, dest);
3988 EXPECT_HR(hr, S_OK);
3989
3990 hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8"));
3991 EXPECT_HR(hr, S_OK);
3992
3993 /* write empty element */
3994 hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL);
3995 EXPECT_HR(hr, S_OK);
3996
3997 hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1);
3998 EXPECT_HR(hr, S_OK);
3999
4000 /* switch */
4001 hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16"));
4002 EXPECT_HR(hr, S_OK);
4003
4004 hr = IMXWriter_flush(writer);
4005 EXPECT_HR(hr, S_OK);
4006
4007 hr = GetHGlobalFromStream(stream, &g);
4008 EXPECT_HR(hr, S_OK);
4009
4010 ptr = GlobalLock(g);
4011 ok(!strncmp(ptr, "<a/>", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]);
4012 GlobalUnlock(g);
4013
4014 /* so output is unaffected, encoding name is stored however */
4015 hr = IMXWriter_get_encoding(writer, &s);
4016 EXPECT_HR(hr, S_OK);
4017 ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s));
4018 SysFreeString(s);
4019
4020 IStream_Release(stream);
4021
4022 i = 0;
4023 enc = encoding_names[i];
4024 while (enc)
4025 {
4026 char expectedA[200];
4027
4028 hr = CreateStreamOnHGlobal(NULL, TRUE, &stream);
4029 EXPECT_HR(hr, S_OK);
4030
4031 V_VT(&dest) = VT_UNKNOWN;
4032 V_UNKNOWN(&dest) = (IUnknown*)stream;
4033 hr = IMXWriter_put_output(writer, dest);
4034 EXPECT_HR(hr, S_OK);
4035
4036 hr = IMXWriter_put_encoding(writer, _bstr_(enc));
4037 ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */,
4038 "%s: encoding not accepted\n", enc);
4039 if (hr != S_OK)
4040 {
4041 enc = encoding_names[++i];
4042 IStream_Release(stream);
4043 continue;
4044 }
4045
4046 hr = ISAXContentHandler_startDocument(content);
4047 EXPECT_HR(hr, S_OK);
4048
4049 hr = ISAXContentHandler_endDocument(content);
4050 EXPECT_HR(hr, S_OK);
4051
4052 hr = IMXWriter_flush(writer);
4053 EXPECT_HR(hr, S_OK);
4054
4055 /* prepare expected string */
4056 *expectedA = 0;
4057 strcat(expectedA, "<?xml version=\"1.0\" encoding=\"");
4058 strcat(expectedA, enc);
4059 strcat(expectedA, "\" standalone=\"no\"?>\r\n");
4060
4061 hr = GetHGlobalFromStream(stream, &g);
4062 EXPECT_HR(hr, S_OK);
4063
4064 ptr = GlobalLock(g);
4065 ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA);
4066 GlobalUnlock(g);
4067
4068 V_VT(&dest) = VT_EMPTY;
4069 hr = IMXWriter_put_output(writer, dest);
4070 EXPECT_HR(hr, S_OK);
4071
4072 IStream_Release(stream);
4073
4074 enc = encoding_names[++i];
4075 }
4076
4077 ISAXContentHandler_Release(content);
4078 IMXWriter_Release(writer);
4079
4080 free_bstrs();
4081 }
4082
4083 static void test_obj_dispex(IUnknown *obj)
4084 {
4085 static const WCHAR starW[] = {'*',0};
4086 DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE;
4087 IDispatchEx *dispex;
4088 IUnknown *unk;
4089 DWORD props;
4090 UINT ticnt;
4091 HRESULT hr;
4092 BSTR name;
4093
4094 hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex);
4095 EXPECT_HR(hr, S_OK);
4096 if (FAILED(hr)) return;
4097
4098 ticnt = 0;
4099 hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt);
4100 EXPECT_HR(hr, S_OK);
4101 ok(ticnt == 1, "ticnt=%u\n", ticnt);
4102
4103 name = SysAllocString(starW);
4104 hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive);
4105 EXPECT_HR(hr, E_NOTIMPL);
4106 SysFreeString(name);
4107
4108 hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid);
4109 EXPECT_HR(hr, E_NOTIMPL);
4110
4111 props = 0;
4112 hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props);
4113 EXPECT_HR(hr, E_NOTIMPL);
4114 ok(props == 0, "expected 0 got %d\n", props);
4115
4116 hr = IDispatchEx_GetMemberName(dispex, dispid, &name);
4117 EXPECT_HR(hr, E_NOTIMPL);
4118 if (SUCCEEDED(hr)) SysFreeString(name);
4119
4120 hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid);
4121 EXPECT_HR(hr, E_NOTIMPL);
4122
4123 hr = IDispatchEx_GetNameSpaceParent(dispex, &unk);
4124 EXPECT_HR(hr, E_NOTIMPL);
4125 if (hr == S_OK && unk) IUnknown_Release(unk);
4126
4127 IDispatchEx_Release(dispex);
4128 }
4129
4130 static void test_dispex(void)
4131 {
4132 IVBSAXXMLReader *vbreader;
4133 ISAXXMLReader *reader;
4134 IUnknown *unk;
4135 HRESULT hr;
4136
4137 hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
4138 &IID_ISAXXMLReader, (void**)&reader);
4139 EXPECT_HR(hr, S_OK);
4140
4141 hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk);
4142 EXPECT_HR(hr, S_OK);
4143 test_obj_dispex(unk);
4144 IUnknown_Release(unk);
4145
4146 hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader);
4147 EXPECT_HR(hr, S_OK);
4148 hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk);
4149 EXPECT_HR(hr, S_OK);
4150 test_obj_dispex(unk);
4151 IUnknown_Release(unk);
4152 IVBSAXXMLReader_Release(vbreader);
4153
4154 ISAXXMLReader_Release(reader);
4155 }
4156
4157 static void test_mxwriter_dispex(void)
4158 {
4159 IDispatchEx *dispex;
4160 IMXWriter *writer;
4161 IUnknown *unk;
4162 HRESULT hr;
4163
4164 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4165 &IID_IMXWriter, (void**)&writer);
4166 EXPECT_HR(hr, S_OK);
4167
4168 hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex);
4169 EXPECT_HR(hr, S_OK);
4170 hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4171 test_obj_dispex(unk);
4172 IUnknown_Release(unk);
4173 IDispatchEx_Release(dispex);
4174
4175 IMXWriter_Release(writer);
4176 }
4177
4178 static void test_mxwriter_comment(void)
4179 {
4180 static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0};
4181 ISAXContentHandler *content;
4182 ISAXLexicalHandler *lexical;
4183 IMXWriter *writer;
4184 VARIANT dest;
4185 HRESULT hr;
4186
4187 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4188 &IID_IMXWriter, (void**)&writer);
4189 EXPECT_HR(hr, S_OK);
4190
4191 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4192 EXPECT_HR(hr, S_OK);
4193
4194 hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4195 EXPECT_HR(hr, S_OK);
4196
4197 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4198 EXPECT_HR(hr, S_OK);
4199
4200 hr = ISAXContentHandler_startDocument(content);
4201 EXPECT_HR(hr, S_OK);
4202
4203 hr = ISAXLexicalHandler_comment(lexical, NULL, 0);
4204 EXPECT_HR(hr, E_INVALIDARG);
4205
4206 hr = ISAXLexicalHandler_comment(lexical, commentW, 0);
4207 EXPECT_HR(hr, S_OK);
4208
4209 V_VT(&dest) = VT_EMPTY;
4210 hr = IMXWriter_get_output(writer, &dest);
4211 EXPECT_HR(hr, S_OK);
4212 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4213 ok(!lstrcmpW(_bstr_("<!---->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4214 VariantClear(&dest);
4215
4216 hr = ISAXLexicalHandler_comment(lexical, commentW, sizeof(commentW)/sizeof(WCHAR)-1);
4217 EXPECT_HR(hr, S_OK);
4218
4219 V_VT(&dest) = VT_EMPTY;
4220 hr = IMXWriter_get_output(writer, &dest);
4221 EXPECT_HR(hr, S_OK);
4222 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4223 ok(!lstrcmpW(_bstr_("<!---->\r\n<!--comment-->\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4224 VariantClear(&dest);
4225
4226 ISAXContentHandler_Release(content);
4227 ISAXLexicalHandler_Release(lexical);
4228 IMXWriter_Release(writer);
4229 free_bstrs();
4230 }
4231
4232 static void test_mxwriter_cdata(void)
4233 {
4234 ISAXContentHandler *content;
4235 ISAXLexicalHandler *lexical;
4236 IMXWriter *writer;
4237 VARIANT dest;
4238 HRESULT hr;
4239
4240 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4241 &IID_IMXWriter, (void**)&writer);
4242 EXPECT_HR(hr, S_OK);
4243
4244 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4245 EXPECT_HR(hr, S_OK);
4246
4247 hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4248 EXPECT_HR(hr, S_OK);
4249
4250 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4251 EXPECT_HR(hr, S_OK);
4252
4253 hr = ISAXContentHandler_startDocument(content);
4254 EXPECT_HR(hr, S_OK);
4255
4256 hr = ISAXLexicalHandler_startCDATA(lexical);
4257 EXPECT_HR(hr, S_OK);
4258
4259 V_VT(&dest) = VT_EMPTY;
4260 hr = IMXWriter_get_output(writer, &dest);
4261 EXPECT_HR(hr, S_OK);
4262 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4263 ok(!lstrcmpW(_bstr_("<![CDATA["), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4264 VariantClear(&dest);
4265
4266 hr = ISAXLexicalHandler_startCDATA(lexical);
4267 EXPECT_HR(hr, S_OK);
4268
4269 /* all these are escaped for text nodes */
4270 hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 7);
4271 EXPECT_HR(hr, S_OK);
4272
4273 hr = ISAXLexicalHandler_endCDATA(lexical);
4274 EXPECT_HR(hr, S_OK);
4275
4276 V_VT(&dest) = VT_EMPTY;
4277 hr = IMXWriter_get_output(writer, &dest);
4278 EXPECT_HR(hr, S_OK);
4279 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4280 ok(!lstrcmpW(_bstr_("<![CDATA[<![CDATA[< > & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4281 VariantClear(&dest);
4282
4283 ISAXContentHandler_Release(content);
4284 ISAXLexicalHandler_Release(lexical);
4285 IMXWriter_Release(writer);
4286 free_bstrs();
4287 }
4288
4289 static void test_mxwriter_pi(void)
4290 {
4291 static const WCHAR targetW[] = {'t','a','r','g','e','t',0};
4292 static const WCHAR dataW[] = {'d','a','t','a',0};
4293 ISAXContentHandler *content;
4294 IMXWriter *writer;
4295 VARIANT dest;
4296 HRESULT hr;
4297
4298 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4299 &IID_IMXWriter, (void**)&writer);
4300 EXPECT_HR(hr, S_OK);
4301
4302 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4303 EXPECT_HR(hr, S_OK);
4304
4305 hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0);
4306 EXPECT_HR(hr, E_INVALIDARG);
4307
4308 hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0);
4309 EXPECT_HR(hr, S_OK);
4310
4311 hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0);
4312 EXPECT_HR(hr, S_OK);
4313
4314 V_VT(&dest) = VT_EMPTY;
4315 hr = IMXWriter_get_output(writer, &dest);
4316 EXPECT_HR(hr, S_OK);
4317 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4318 ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4319 VariantClear(&dest);
4320
4321 hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4);
4322 EXPECT_HR(hr, S_OK);
4323
4324 V_VT(&dest) = VT_EMPTY;
4325 hr = IMXWriter_get_output(writer, &dest);
4326 EXPECT_HR(hr, S_OK);
4327 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4328 ok(!lstrcmpW(_bstr_("<?\?>\r\n<?target?>\r\n<?targ data?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4329 VariantClear(&dest);
4330
4331 V_VT(&dest) = VT_EMPTY;
4332 hr = IMXWriter_put_output(writer, dest);
4333 EXPECT_HR(hr, S_OK);
4334
4335 hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0);
4336 EXPECT_HR(hr, S_OK);
4337
4338 V_VT(&dest) = VT_EMPTY;
4339 hr = IMXWriter_get_output(writer, &dest);
4340 EXPECT_HR(hr, S_OK);
4341 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4342 ok(!lstrcmpW(_bstr_("<?target?>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4343 VariantClear(&dest);
4344
4345
4346 ISAXContentHandler_Release(content);
4347 IMXWriter_Release(writer);
4348 }
4349
4350 static void test_mxwriter_ignorablespaces(void)
4351 {
4352 static const WCHAR dataW[] = {'d','a','t','a',0};
4353 ISAXContentHandler *content;
4354 IMXWriter *writer;
4355 VARIANT dest;
4356 HRESULT hr;
4357
4358 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4359 &IID_IMXWriter, (void**)&writer);
4360 EXPECT_HR(hr, S_OK);
4361
4362 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4363 EXPECT_HR(hr, S_OK);
4364
4365 hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0);
4366 EXPECT_HR(hr, E_INVALIDARG);
4367
4368 hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0);
4369 EXPECT_HR(hr, S_OK);
4370
4371 hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4);
4372 EXPECT_HR(hr, S_OK);
4373
4374 hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1);
4375 EXPECT_HR(hr, S_OK);
4376
4377 V_VT(&dest) = VT_EMPTY;
4378 hr = IMXWriter_get_output(writer, &dest);
4379 EXPECT_HR(hr, S_OK);
4380 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4381 ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4382 VariantClear(&dest);
4383
4384 ISAXContentHandler_Release(content);
4385 IMXWriter_Release(writer);
4386 }
4387
4388 static void test_mxwriter_dtd(void)
4389 {
4390 static const WCHAR contentW[] = {'c','o','n','t','e','n','t'};
4391 static const WCHAR nameW[] = {'n','a','m','e'};
4392 static const WCHAR pubW[] = {'p','u','b'};
4393 static const WCHAR sysW[] = {'s','y','s'};
4394 ISAXContentHandler *content;
4395 ISAXLexicalHandler *lexical;
4396 ISAXDeclHandler *decl;
4397 IMXWriter *writer;
4398 VARIANT dest;
4399 HRESULT hr;
4400
4401 hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER,
4402 &IID_IMXWriter, (void**)&writer);
4403 EXPECT_HR(hr, S_OK);
4404
4405 hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content);
4406 EXPECT_HR(hr, S_OK);
4407
4408 hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical);
4409 EXPECT_HR(hr, S_OK);
4410
4411 hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl);
4412 EXPECT_HR(hr, S_OK);
4413
4414 hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE);
4415 EXPECT_HR(hr, S_OK);
4416
4417 hr = ISAXContentHandler_startDocument(content);
4418 EXPECT_HR(hr, S_OK);
4419
4420 hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0);
4421 EXPECT_HR(hr, E_INVALIDARG);
4422
4423 hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4424 EXPECT_HR(hr, E_INVALIDARG);
4425
4426 hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, sizeof(sysW)/sizeof(WCHAR));
4427 EXPECT_HR(hr, E_INVALIDARG);
4428
4429 hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4430 EXPECT_HR(hr, E_INVALIDARG);
4431
4432 hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0, NULL, 0);
4433 EXPECT_HR(hr, S_OK);
4434
4435 V_VT(&dest) = VT_EMPTY;
4436 hr = IMXWriter_get_output(writer, &dest);
4437 EXPECT_HR(hr, S_OK);
4438 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4439 ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4440 VariantClear(&dest);
4441
4442 /* system id is required if public is present */
4443 hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR), pubW, sizeof(pubW)/sizeof(WCHAR), NULL, 0);
4444 EXPECT_HR(hr, E_INVALIDARG);
4445
4446 hr = ISAXLexicalHandler_startDTD(lexical, nameW, sizeof(nameW)/sizeof(WCHAR),
4447 pubW, sizeof(pubW)/sizeof(WCHAR), sysW, sizeof(sysW)/sizeof(WCHAR));
4448 EXPECT_HR(hr, S_OK);
4449
4450 V_VT(&dest) = VT_EMPTY;
4451 hr = IMXWriter_get_output(writer, &dest);
4452 EXPECT_HR(hr, S_OK);
4453 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4454 ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4455 "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4456 VariantClear(&dest);
4457
4458 hr = ISAXLexicalHandler_endDTD(lexical);
4459 EXPECT_HR(hr, S_OK);
4460
4461 hr = ISAXLexicalHandler_endDTD(lexical);
4462 EXPECT_HR(hr, S_OK);
4463
4464 V_VT(&dest) = VT_EMPTY;
4465 hr = IMXWriter_get_output(writer, &dest);
4466 EXPECT_HR(hr, S_OK);
4467 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4468 ok(!lstrcmpW(_bstr_("<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\""
4469 "<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\r\n]>\r\n"),
4470 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4471 VariantClear(&dest);
4472
4473 /* element declaration */
4474 V_VT(&dest) = VT_EMPTY;
4475 hr = IMXWriter_put_output(writer, dest);
4476 EXPECT_HR(hr, S_OK);
4477
4478 hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0);
4479 EXPECT_HR(hr, E_INVALIDARG);
4480
4481 hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), NULL, 0);
4482 EXPECT_HR(hr, E_INVALIDARG);
4483
4484 hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, sizeof(contentW)/sizeof(WCHAR));
4485 EXPECT_HR(hr, S_OK);
4486
4487 V_VT(&dest) = VT_EMPTY;
4488 hr = IMXWriter_get_output(writer, &dest);
4489 EXPECT_HR(hr, S_OK);
4490 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4491 ok(!lstrcmpW(_bstr_("<!ELEMENT name content>\r\n"),
4492 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4493 VariantClear(&dest);
4494
4495 V_VT(&dest) = VT_EMPTY;
4496 hr = IMXWriter_put_output(writer, dest);
4497 EXPECT_HR(hr, S_OK);
4498
4499 hr = ISAXDeclHandler_elementDecl(decl, nameW, sizeof(nameW)/sizeof(WCHAR), contentW, 0);
4500 EXPECT_HR(hr, S_OK);
4501
4502 V_VT(&dest) = VT_EMPTY;
4503 hr = IMXWriter_get_output(writer, &dest);
4504 EXPECT_HR(hr, S_OK);
4505 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4506 ok(!lstrcmpW(_bstr_("<!ELEMENT name >\r\n"),
4507 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4508 VariantClear(&dest);
4509
4510 /* attribute declaration */
4511 V_VT(&dest) = VT_EMPTY;
4512 hr = IMXWriter_put_output(writer, dest);
4513 EXPECT_HR(hr, S_OK);
4514
4515 hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4516 _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"),
4517 _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value"));
4518 EXPECT_HR(hr, S_OK);
4519
4520 V_VT(&dest) = VT_EMPTY;
4521 hr = IMXWriter_get_output(writer, &dest);
4522 EXPECT_HR(hr, S_OK);
4523 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4524 ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"),
4525 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4526 VariantClear(&dest);
4527
4528 hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"),
4529 _bstr_("attribute2"), strlen("attribute2"), _bstr_("CDATA"), strlen("CDATA"),
4530 _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value2"), strlen("value2"));
4531 EXPECT_HR(hr, S_OK);
4532
4533 hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"),
4534 _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"),
4535 _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3"));
4536 EXPECT_HR(hr, S_OK);
4537
4538 V_VT(&dest) = VT_EMPTY;
4539 hr = IMXWriter_get_output(writer, &dest);
4540 EXPECT_HR(hr, S_OK);
4541 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4542 ok(!lstrcmpW(_bstr_("<!ATTLIST element attribute CDATA #REQUIRED \"value\">\r\n"
4543 "<!ATTLIST element attribute2 CDATA #REQUIRED \"value2\">\r\n"
4544 "<!ATTLIST element2 attribute3 CDATA #REQUIRED \"value3\">\r\n"),
4545 V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4546 VariantClear(&dest);
4547
4548 /* internal entities */
4549 V_VT(&dest) = VT_EMPTY;
4550 hr = IMXWriter_put_output(writer, dest);
4551 EXPECT_HR(hr, S_OK);
4552
4553 hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0);
4554 EXPECT_HR(hr, E_INVALIDARG);
4555
4556 hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), -1, NULL, 0);
4557 EXPECT_HR(hr, E_INVALIDARG);
4558
4559 hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("value"), strlen("value"));
4560 EXPECT_HR(hr, S_OK);
4561
4562 V_VT(&dest) = VT_EMPTY;
4563 hr = IMXWriter_get_output(writer, &dest);
4564 EXPECT_HR(hr, S_OK);
4565 ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest));
4566 ok(!lstrcmpW(_bstr_("<!ENTITY name \"value\">\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest)));
4567 VariantClear(&dest);
4568
4569 ISAXContentHandler_Release(content);
4570 ISAXLexicalHandler_Release(lexical);
4571 ISAXDeclHandler_Release(decl);
4572 IMXWriter_Release(writer);
4573 free_bstrs();
4574 }
4575
4576 typedef struct {
4577 const CLSID *clsid;
4578 const char *uri;
4579 const char *local;
4580 const char *qname;
4581 const char *type;
4582 const char *value;
4583 HRESULT hr;
4584 } addattribute_test_t;
4585
4586 static const addattribute_test_t addattribute_data[] = {
4587 { &CLSID_SAXAttributes, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4588 { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4589 { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG },
4590 { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK },
4591
4592 { &CLSID_SAXAttributes, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4593 { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4594 { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4595 { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK },
4596
4597 { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4598 { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4599 { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG },
4600 { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK },
4601
4602 { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", "type", "value", S_OK },
4603 { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK },
4604 { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK },
4605 { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK },
4606
4607 { NULL }
4608 };
4609
4610 static void test_mxattr_addAttribute(void)
4611 {
4612 const addattribute_test_t *table = addattribute_data;
4613 int i = 0;
4614
4615 while (table->clsid)
4616 {
4617 ISAXAttributes *saxattr;
4618 IMXAttributes *mxattr;
4619 const WCHAR *value;
4620 int len, index;
4621 HRESULT hr;
4622
4623 if (!is_clsid_supported(table->clsid, mxattributes_support_data))
4624 {
4625 table++;
4626 i++;
4627 continue;
4628 }
4629
4630 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
4631 &IID_IMXAttributes, (void**)&mxattr);
4632 EXPECT_HR(hr, S_OK);
4633
4634 hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4635 EXPECT_HR(hr, S_OK);
4636
4637 /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4638 if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4639 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4640 {
4641 hr = ISAXAttributes_getLength(saxattr, NULL);
4642 EXPECT_HR(hr, E_POINTER);
4643 }
4644
4645 len = -1;
4646 hr = ISAXAttributes_getLength(saxattr, &len);
4647 EXPECT_HR(hr, S_OK);
4648 ok(len == 0, "got %d\n", len);
4649
4650 hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4651 EXPECT_HR(hr, E_INVALIDARG);
4652
4653 hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4654 EXPECT_HR(hr, E_INVALIDARG);
4655
4656 hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4657 EXPECT_HR(hr, E_INVALIDARG);
4658
4659 hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4660 EXPECT_HR(hr, E_INVALIDARG);
4661
4662 hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4663 EXPECT_HR(hr, E_INVALIDARG);
4664
4665 hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4666 EXPECT_HR(hr, E_INVALIDARG);
4667
4668 hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4669 EXPECT_HR(hr, E_INVALIDARG);
4670
4671 hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4672 EXPECT_HR(hr, E_INVALIDARG);
4673
4674 hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local),
4675 _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value));
4676 ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr);
4677
4678 if (hr == S_OK)
4679 {
4680 /* SAXAttributes40 and SAXAttributes60 both crash on this test */
4681 if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4682 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4683 {
4684 hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len);
4685 EXPECT_HR(hr, E_POINTER);
4686
4687 hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL);
4688 EXPECT_HR(hr, E_POINTER);
4689
4690 hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL);
4691 EXPECT_HR(hr, E_POINTER);
4692
4693 hr = ISAXAttributes_getType(saxattr, 0, NULL, &len);
4694 EXPECT_HR(hr, E_POINTER);
4695
4696 hr = ISAXAttributes_getType(saxattr, 0, &value, NULL);
4697 EXPECT_HR(hr, E_POINTER);
4698
4699 hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL);
4700 EXPECT_HR(hr, E_POINTER);
4701 }
4702
4703 len = -1;
4704 hr = ISAXAttributes_getValue(saxattr, 0, &value, &len);
4705 EXPECT_HR(hr, S_OK);
4706 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4707 table->value);
4708 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4709
4710 len = -1;
4711 value = (void*)0xdeadbeef;
4712 hr = ISAXAttributes_getType(saxattr, 0, &value, &len);
4713 EXPECT_HR(hr, S_OK);
4714
4715 if (table->type)
4716 {
4717 ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4718 table->type);
4719 ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len);
4720 }
4721 else
4722 {
4723 ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value));
4724 ok(len == 0, "%d: got wrong type value length %d\n", i, len);
4725 }
4726
4727 hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL);
4728 if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
4729 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
4730 {
4731 EXPECT_HR(hr, E_POINTER);
4732 }
4733 else
4734 EXPECT_HR(hr, E_INVALIDARG);
4735
4736 hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index);
4737 EXPECT_HR(hr, E_INVALIDARG);
4738
4739 index = -1;
4740 hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index);
4741 EXPECT_HR(hr, E_INVALIDARG);
4742 ok(index == -1, "%d: got wrong index %d\n", i, index);
4743
4744 index = -1;
4745 hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index);
4746 EXPECT_HR(hr, E_INVALIDARG);
4747 ok(index == -1, "%d: got wrong index %d\n", i, index);
4748
4749 index = -1;
4750 hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index);
4751 EXPECT_HR(hr, S_OK);
4752 ok(index == 0, "%d: got wrong index %d\n", i, index);
4753
4754 index = -1;
4755 hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index);
4756 EXPECT_HR(hr, E_INVALIDARG);
4757 ok(index == -1, "%d: got wrong index %d\n", i, index);
4758
4759 if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) ||
4760 IsEqualGUID(table->clsid, &CLSID_SAXAttributes60))
4761 {
4762 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4763 EXPECT_HR(hr, E_INVALIDARG);
4764
4765 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4766 EXPECT_HR(hr, E_INVALIDARG);
4767
4768 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4769 EXPECT_HR(hr, E_INVALIDARG);
4770
4771 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4772 EXPECT_HR(hr, E_INVALIDARG);
4773
4774 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4775 EXPECT_HR(hr, E_INVALIDARG);
4776
4777 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4778 EXPECT_HR(hr, E_INVALIDARG);
4779 }
4780 else
4781 {
4782 hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL);
4783 EXPECT_HR(hr, E_POINTER);
4784
4785 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL);
4786 EXPECT_HR(hr, E_POINTER);
4787
4788 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL);
4789 EXPECT_HR(hr, E_POINTER);
4790
4791 /* versions 4 and 6 crash */
4792 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL);
4793 EXPECT_HR(hr, E_POINTER);
4794
4795 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len);
4796 EXPECT_HR(hr, E_POINTER);
4797
4798 hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL);
4799 EXPECT_HR(hr, E_POINTER);
4800
4801 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL);
4802 EXPECT_HR(hr, E_POINTER);
4803
4804 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL);
4805 EXPECT_HR(hr, E_POINTER);
4806
4807 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL);
4808 EXPECT_HR(hr, E_POINTER);
4809
4810 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len);
4811 EXPECT_HR(hr, E_POINTER);
4812
4813 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local),
4814 strlen(table->local), NULL, NULL);
4815 EXPECT_HR(hr, E_POINTER);
4816 }
4817
4818 hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len);
4819 EXPECT_HR(hr, S_OK);
4820 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4821 table->value);
4822 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4823
4824 if (table->uri) {
4825 hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri),
4826 _bstr_(table->local), strlen(table->local), &value, &len);
4827 EXPECT_HR(hr, S_OK);
4828 ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value),
4829 table->value);
4830 ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len);
4831 }
4832 }
4833
4834 len = -1;
4835 hr = ISAXAttributes_getLength(saxattr, &len);
4836 EXPECT_HR(hr, S_OK);
4837 if (table->hr == S_OK)
4838 ok(len == 1, "%d: got %d length, expected 1\n", i, len);
4839 else
4840 ok(len == 0, "%d: got %d length, expected 0\n", i, len);
4841
4842 ISAXAttributes_Release(saxattr);
4843 IMXAttributes_Release(mxattr);
4844
4845 table++;
4846 i++;
4847 }
4848
4849 free_bstrs();
4850 }
4851
4852 static void test_mxattr_clear(void)
4853 {
4854 ISAXAttributes *saxattr;
4855 IMXAttributes *mxattr;
4856 const WCHAR *ptr;
4857 HRESULT hr;
4858 int len;
4859
4860 hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4861 &IID_IMXAttributes, (void**)&mxattr);
4862 EXPECT_HR(hr, S_OK);
4863
4864 hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4865 EXPECT_HR(hr, S_OK);
4866
4867 hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL);
4868 EXPECT_HR(hr, E_INVALIDARG);
4869
4870 hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4871 EXPECT_HR(hr, E_INVALIDARG);
4872
4873 hr = IMXAttributes_clear(mxattr);
4874 EXPECT_HR(hr, S_OK);
4875
4876 hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"),
4877 _bstr_("qname"), _bstr_("type"), _bstr_("value"));
4878 EXPECT_HR(hr, S_OK);
4879
4880 len = -1;
4881 hr = ISAXAttributes_getLength(saxattr, &len);
4882 EXPECT_HR(hr, S_OK);
4883 ok(len == 1, "got %d\n", len);
4884
4885 len = -1;
4886 hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len);
4887 EXPECT_HR(hr, E_POINTER);
4888 ok(len == -1, "got %d\n", len);
4889
4890 ptr = (void*)0xdeadbeef;
4891 hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL);
4892 EXPECT_HR(hr, E_POINTER);
4893 ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4894
4895 len = 0;
4896 hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4897 EXPECT_HR(hr, S_OK);
4898 ok(len == 5, "got %d\n", len);
4899 ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr));
4900
4901 hr = IMXAttributes_clear(mxattr);
4902 EXPECT_HR(hr, S_OK);
4903
4904 len = -1;
4905 hr = ISAXAttributes_getLength(saxattr, &len);
4906 EXPECT_HR(hr, S_OK);
4907 ok(len == 0, "got %d\n", len);
4908
4909 len = -1;
4910 ptr = (void*)0xdeadbeef;
4911 hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len);
4912 EXPECT_HR(hr, E_INVALIDARG);
4913 ok(len == -1, "got %d\n", len);
4914 ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr);
4915
4916 IMXAttributes_Release(mxattr);
4917 ISAXAttributes_Release(saxattr);
4918 free_bstrs();
4919 }
4920
4921 static void test_mxattr_dispex(void)
4922 {
4923 IMXAttributes *mxattr;
4924 IDispatchEx *dispex;
4925 IUnknown *unk;
4926 HRESULT hr;
4927
4928 hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4929 &IID_IMXAttributes, (void**)&mxattr);
4930 EXPECT_HR(hr, S_OK);
4931
4932 hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex);
4933 EXPECT_HR(hr, S_OK);
4934 hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk);
4935 test_obj_dispex(unk);
4936 IUnknown_Release(unk);
4937 IDispatchEx_Release(dispex);
4938
4939 IMXAttributes_Release(mxattr);
4940 }
4941
4942 static void test_mxattr_qi(void)
4943 {
4944 IVBSAXAttributes *vbsaxattr, *vbsaxattr2;
4945 ISAXAttributes *saxattr;
4946 IMXAttributes *mxattr;
4947 HRESULT hr;
4948
4949 hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER,
4950 &IID_IMXAttributes, (void**)&mxattr);
4951 EXPECT_HR(hr, S_OK);
4952
4953 EXPECT_REF(mxattr, 1);
4954 hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
4955 EXPECT_HR(hr, S_OK);
4956
4957 EXPECT_REF(mxattr, 2);
4958 EXPECT_REF(saxattr, 2);
4959
4960 hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr);
4961 EXPECT_HR(hr, S_OK);
4962
4963 EXPECT_REF(vbsaxattr, 3);
4964 EXPECT_REF(mxattr, 3);
4965 EXPECT_REF(saxattr, 3);
4966
4967 hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2);
4968 EXPECT_HR(hr, S_OK);
4969
4970 EXPECT_REF(vbsaxattr, 4);
4971 EXPECT_REF(mxattr, 4);
4972 EXPECT_REF(saxattr, 4);
4973
4974 IMXAttributes_Release(mxattr);
4975 ISAXAttributes_Release(saxattr);
4976 IVBSAXAttributes_Release(vbsaxattr);
4977 IVBSAXAttributes_Release(vbsaxattr2);
4978 }
4979
4980 static struct msxmlsupported_data_t saxattr_support_data[] =
4981 {
4982 { &CLSID_SAXAttributes, "SAXAttributes" },
4983 { &CLSID_SAXAttributes30, "SAXAttributes30" },
4984 { &CLSID_SAXAttributes40, "SAXAttributes40" },
4985 { &CLSID_SAXAttributes60, "SAXAttributes60" },
4986 { NULL }
4987 };
4988
4989 static void test_mxattr_localname(void)
4990 {
4991 static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0};
4992 static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0};
4993 static const WCHAR uri1W[] = {'u','r','i','1',0};
4994 static const WCHAR uriW[] = {'u','r','i',0};
4995
4996 const struct msxmlsupported_data_t *table = saxattr_support_data;
4997
4998 while (table->clsid)
4999 {
5000 ISAXAttributes *saxattr;
5001 IMXAttributes *mxattr;
5002 HRESULT hr;
5003 int index;
5004
5005 if (!is_clsid_supported(table->clsid, mxattributes_support_data))
5006 {
5007 table++;
5008 continue;
5009 }
5010
5011 hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER,
5012 &IID_IMXAttributes, (void**)&mxattr);
5013 EXPECT_HR(hr, S_OK);
5014
5015 hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr);
5016 EXPECT_HR(hr, S_OK);
5017
5018 hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index);
5019 EXPECT_HR(hr, E_INVALIDARG);
5020
5021 /* add some ambiguos attribute names */
5022 hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5023 _bstr_("a:localname"), _bstr_(""), _bstr_("value"));
5024 EXPECT_HR(hr, S_OK);
5025 hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"),
5026 _bstr_("b:localname"), _bstr_(""), _bstr_("value"));
5027 EXPECT_HR(hr, S_OK);
5028
5029 index = -1;
5030 hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index);
5031 EXPECT_HR(hr, S_OK);
5032 ok(index == 0, "%s: got index %d\n", table->name, index);
5033
5034 index = -1;
5035 hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index);
5036 EXPECT_HR(hr, E_INVALIDARG);
5037 ok(index == -1, "%s: got index %d\n", table->name, index);
5038
5039 index = -1;
5040 hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index);
5041 EXPECT_HR(hr, E_INVALIDARG);
5042 ok(index == -1, "%s: got index %d\n", table->name, index);
5043
5044 if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) ||
5045 IsEqualGUID(table->clsid, &CLSID_SAXAttributes30))
5046 {
5047 hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5048 EXPECT_HR(hr, E_POINTER);
5049
5050 hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5051 EXPECT_HR(hr, E_POINTER);
5052 }
5053 else
5054 {
5055 hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL);
5056 EXPECT_HR(hr, E_INVALIDARG);
5057
5058 hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL);
5059 EXPECT_HR(hr, E_INVALIDARG);
5060 }
5061
5062 hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index);
5063 EXPECT_HR(hr, E_INVALIDARG);
5064
5065 hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index);
5066 EXPECT_HR(hr, E_INVALIDARG);
5067
5068 table++;
5069
5070 ISAXAttributes_Release(saxattr);
5071 IMXAttributes_Release(mxattr);
5072 }
5073 }
5074
5075 START_TEST(saxreader)
5076 {
5077 ISAXXMLReader *reader;
5078 HRESULT hr;
5079
5080 hr = CoInitialize(NULL);
5081 ok(hr == S_OK, "failed to init com\n");
5082
5083 hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER,
5084 &IID_ISAXXMLReader, (void**)&reader);
5085
5086 if(FAILED(hr))
5087 {
5088 skip("Failed to create SAXXMLReader instance\n");
5089 CoUninitialize();
5090 return;
5091 }
5092 ISAXXMLReader_Release(reader);
5093
5094 init_call_sequences(sequences, NUM_CALL_SEQUENCES);
5095
5096 get_class_support_data(reader_support_data, &IID_ISAXXMLReader);
5097
5098 test_saxreader();
5099 test_saxreader_properties();
5100 test_saxreader_features();
5101 test_saxreader_encoding();
5102 test_dispex();
5103
5104 /* MXXMLWriter tests */
5105 get_class_support_data(mxwriter_support_data, &IID_IMXWriter);
5106 if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data))
5107 {
5108 test_mxwriter_handlers();
5109 test_mxwriter_startenddocument();
5110 test_mxwriter_startendelement();
5111 test_mxwriter_characters();
5112 test_mxwriter_comment();
5113 test_mxwriter_cdata();
5114 test_mxwriter_pi();
5115 test_mxwriter_ignorablespaces();
5116 test_mxwriter_dtd();
5117 test_mxwriter_properties();
5118 test_mxwriter_flush();
5119 test_mxwriter_stream();
5120 test_mxwriter_encoding();
5121 test_mxwriter_dispex();
5122 }
5123 else
5124 win_skip("MXXMLWriter not supported\n");
5125
5126 /* SAXAttributes tests */
5127 get_class_support_data(mxattributes_support_data, &IID_IMXAttributes);
5128 if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data))
5129 {
5130 test_mxattr_qi();
5131 test_mxattr_addAttribute();
5132 test_mxattr_clear();
5133 test_mxattr_localname();
5134 test_mxattr_dispex();
5135 }
5136 else
5137 skip("SAXAttributes not supported\n");
5138
5139 CoUninitialize();
5140 }