/* * SAXReader/MXWriter tests * * Copyright 2008 Piotr Caban * Copyright 2011 Thomas Mullaly * Copyright 2012 Nikolay Sivov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #define COBJMACROS #define CONST_VTABLE #include #include #include "windows.h" #include "ole2.h" #include "msxml2.h" #include "msxml2did.h" #include "ocidl.h" #include "dispex.h" #include "wine/heap.h" #include "wine/test.h" static const WCHAR emptyW[] = {0}; #define EXPECT_HR(hr,hr_exp) \ ok(hr == hr_exp, "got 0x%08x, expected 0x%08x\n", hr, hr_exp) #define EXPECT_REF(obj,ref) _expect_ref((IUnknown*)obj, ref, __LINE__) static void _expect_ref(IUnknown* obj, ULONG ref, int line) { ULONG rc; IUnknown_AddRef(obj); rc = IUnknown_Release(obj); ok_(__FILE__, line)(rc == ref, "expected refcount %d, got %d\n", ref, rc); } static LONG get_refcount(void *iface) { IUnknown *unk = iface; LONG ref; ref = IUnknown_AddRef(unk); IUnknown_Release(unk); return ref-1; } struct msxmlsupported_data_t { const GUID *clsid; const char *name; BOOL supported; }; static BOOL is_clsid_supported(const GUID *clsid, const struct msxmlsupported_data_t *table) { while (table->clsid) { if (table->clsid == clsid) return table->supported; table++; } return FALSE; } static BSTR alloc_str_from_narrow(const char *str) { int len = MultiByteToWideChar(CP_ACP, 0, str, -1, NULL, 0); BSTR ret = SysAllocStringLen(NULL, len - 1); /* NUL character added automatically */ MultiByteToWideChar(CP_ACP, 0, str, -1, ret, len); return ret; } static BSTR alloced_bstrs[512]; static int alloced_bstrs_count; static BSTR _bstr_(const char *str) { assert(alloced_bstrs_count < ARRAY_SIZE(alloced_bstrs)); alloced_bstrs[alloced_bstrs_count] = alloc_str_from_narrow(str); return alloced_bstrs[alloced_bstrs_count++]; } static void free_bstrs(void) { int i; for (i = 0; i < alloced_bstrs_count; i++) SysFreeString(alloced_bstrs[i]); alloced_bstrs_count = 0; } static void test_saxstr(const char *file, unsigned line, BSTR str, const char *expected, BOOL todo, int *failcount) { int len, lenexp, cmp; WCHAR buf[1024]; len = SysStringLen(str); if (!expected) { if (str && todo) { (*failcount)++; todo_wine ok_(file, line) (!str, "got %p, expected null str\n", str); } else ok_(file, line) (!str, "got %p, expected null str\n", str); if (len && todo) { (*failcount)++; todo_wine ok_(file, line) (len == 0, "got len %d, expected 0\n", len); } else ok_(file, line) (len == 0, "got len %d, expected 0\n", len); return; } lenexp = strlen(expected); if (lenexp != len && todo) { (*failcount)++; todo_wine ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected); } else ok_(file, line) (lenexp == len, "len %d (%s), expected %d (%s)\n", len, wine_dbgstr_wn(str, len), lenexp, expected); /* exit earlier on length mismatch */ if (lenexp != len) return; MultiByteToWideChar(CP_ACP, 0, expected, -1, buf, ARRAY_SIZE(buf)); cmp = memcmp(str, buf, lenexp*sizeof(WCHAR)); if (cmp && todo) { (*failcount)++; todo_wine ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", wine_dbgstr_wn(str, len), expected); } else ok_(file, line) (!cmp, "unexpected str %s, expected %s\n", wine_dbgstr_wn(str, len), expected); } typedef enum _CH { CH_ENDTEST, CH_PUTDOCUMENTLOCATOR, CH_STARTDOCUMENT, CH_ENDDOCUMENT, CH_STARTPREFIXMAPPING, CH_ENDPREFIXMAPPING, CH_STARTELEMENT, CH_ENDELEMENT, CH_CHARACTERS, CH_IGNORABLEWHITESPACE, CH_PROCESSINGINSTRUCTION, CH_SKIPPEDENTITY, LH_STARTCDATA, LH_ENDCDATA, EH_ERROR, EH_FATALERROR, EH_IGNORABLEWARNING, EVENT_LAST } CH; static const char *event_names[EVENT_LAST] = { "endtest", "putDocumentLocator", "startDocument", "endDocument", "startPrefixMapping", "endPrefixMapping", "startElement", "endElement", "characters", "ignorableWhitespace", "processingInstruction", "skippedEntity", "startCDATA", "endCDATA", "error", "fatalError", "ignorableWarning" }; struct attribute_entry { const char *uri; const char *local; const char *qname; const char *value; /* used for actual call data only, null for expected call data */ BSTR uriW; BSTR localW; BSTR qnameW; BSTR valueW; }; struct call_entry { CH id; int line; int column; HRESULT ret; const char *arg1; const char *arg2; const char *arg3; /* allocated once at startElement callback */ struct attribute_entry *attributes; int attr_count; /* used for actual call data only, null for expected call data */ BSTR arg1W; BSTR arg2W; BSTR arg3W; }; struct call_sequence { int count; int size; struct call_entry *sequence; }; #define CONTENT_HANDLER_INDEX 0 #define NUM_CALL_SEQUENCES 1 static struct call_sequence *sequences[NUM_CALL_SEQUENCES]; static void init_call_entry(ISAXLocator *locator, struct call_entry *call) { memset(call, 0, sizeof(*call)); ISAXLocator_getLineNumber(locator, &call->line); ISAXLocator_getColumnNumber(locator, &call->column); } static void add_call(struct call_sequence **seq, int sequence_index, const struct call_entry *call) { struct call_sequence *call_seq = seq[sequence_index]; if (!call_seq->sequence) { call_seq->size = 10; call_seq->sequence = heap_alloc(call_seq->size * sizeof (struct call_entry)); } if (call_seq->count == call_seq->size) { call_seq->size *= 2; call_seq->sequence = heap_realloc(call_seq->sequence, call_seq->size * sizeof (struct call_entry)); } assert(call_seq->sequence); call_seq->sequence[call_seq->count].id = call->id; call_seq->sequence[call_seq->count].line = call->line; call_seq->sequence[call_seq->count].column = call->column; call_seq->sequence[call_seq->count].arg1W = call->arg1W; call_seq->sequence[call_seq->count].arg2W = call->arg2W; call_seq->sequence[call_seq->count].arg3W = call->arg3W; call_seq->sequence[call_seq->count].ret = call->ret; call_seq->sequence[call_seq->count].attr_count = call->attr_count; call_seq->sequence[call_seq->count].attributes = call->attributes; call_seq->count++; } static inline void flush_sequence(struct call_sequence **seg, int sequence_index) { int i; struct call_sequence *call_seq = seg[sequence_index]; for (i = 0; i < call_seq->count; i++) { int j; for (j = 0; j < call_seq->sequence[i].attr_count; j++) { SysFreeString(call_seq->sequence[i].attributes[j].uriW); SysFreeString(call_seq->sequence[i].attributes[j].localW); SysFreeString(call_seq->sequence[i].attributes[j].qnameW); SysFreeString(call_seq->sequence[i].attributes[j].valueW); } heap_free(call_seq->sequence[i].attributes); call_seq->sequence[i].attr_count = 0; SysFreeString(call_seq->sequence[i].arg1W); SysFreeString(call_seq->sequence[i].arg2W); SysFreeString(call_seq->sequence[i].arg3W); } heap_free(call_seq->sequence); call_seq->sequence = NULL; call_seq->count = call_seq->size = 0; } static const char *get_event_name(CH event) { return event_names[event]; } static void compare_attributes(const struct call_entry *actual, const struct call_entry *expected, const char *context, BOOL todo, const char *file, int line, int *failcount) { int i, lenexp = 0; /* attribute count is not stored for expected data */ if (expected->attributes) { struct attribute_entry *ptr = expected->attributes; while (ptr->uri) { lenexp++; ptr++; }; } /* check count first and exit earlier */ if (actual->attr_count != lenexp && todo) { (*failcount)++; todo_wine ok_(file, line) (FALSE, "%s: in event %s expecting attr count %d got %d\n", context, get_event_name(actual->id), lenexp, actual->attr_count); } else ok_(file, line) (actual->attr_count == lenexp, "%s: in event %s expecting attr count %d got %d\n", context, get_event_name(actual->id), lenexp, actual->attr_count); if (actual->attr_count != lenexp) return; /* now compare all attributes strings */ for (i = 0; i < actual->attr_count; i++) { test_saxstr(file, line, actual->attributes[i].uriW, expected->attributes[i].uri, todo, failcount); test_saxstr(file, line, actual->attributes[i].localW, expected->attributes[i].local, todo, failcount); test_saxstr(file, line, actual->attributes[i].qnameW, expected->attributes[i].qname, todo, failcount); test_saxstr(file, line, actual->attributes[i].valueW, expected->attributes[i].value, todo, failcount); } } static void ok_sequence_(struct call_sequence **seq, int sequence_index, const struct call_entry *expected, const char *context, BOOL todo, const char *file, int line) { struct call_sequence *call_seq = seq[sequence_index]; static const struct call_entry end_of_sequence = { CH_ENDTEST }; const struct call_entry *actual, *sequence; int failcount = 0; add_call(seq, sequence_index, &end_of_sequence); sequence = call_seq->sequence; actual = sequence; while (expected->id != CH_ENDTEST && actual->id != CH_ENDTEST) { if (expected->id == actual->id) { if (expected->line != -1) { /* always test position data */ if (expected->line != actual->line && todo) { todo_wine { failcount++; ok_(file, line) (FALSE, "%s: in event %s expecting line %d got %d\n", context, get_event_name(actual->id), expected->line, actual->line); } } else { ok_(file, line) (expected->line == actual->line, "%s: in event %s expecting line %d got %d\n", context, get_event_name(actual->id), expected->line, actual->line); } } if (expected->column != -1) { if (expected->column != actual->column && todo) { todo_wine { failcount++; ok_(file, line) (FALSE, "%s: in event %s expecting column %d got %d\n", context, get_event_name(actual->id), expected->column, actual->column); } } else { ok_(file, line) (expected->column == actual->column, "%s: in event %s expecting column %d got %d\n", context, get_event_name(actual->id), expected->column, actual->column); } } switch (actual->id) { case CH_PUTDOCUMENTLOCATOR: case CH_STARTDOCUMENT: case CH_ENDDOCUMENT: case LH_STARTCDATA: case LH_ENDCDATA: break; case CH_STARTPREFIXMAPPING: /* prefix, uri */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); break; case CH_ENDPREFIXMAPPING: /* prefix */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); break; case CH_STARTELEMENT: /* compare attributes */ compare_attributes(actual, expected, context, todo, file, line, &failcount); /* fallthrough */ case CH_ENDELEMENT: /* uri, localname, qname */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); test_saxstr(file, line, actual->arg3W, expected->arg3, todo, &failcount); break; case CH_CHARACTERS: case CH_IGNORABLEWHITESPACE: /* char data */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); break; case CH_PROCESSINGINSTRUCTION: /* target, data */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); test_saxstr(file, line, actual->arg2W, expected->arg2, todo, &failcount); break; case CH_SKIPPEDENTITY: /* name */ test_saxstr(file, line, actual->arg1W, expected->arg1, todo, &failcount); break; case EH_FATALERROR: /* test return value only */ if (expected->ret != actual->ret && todo) { failcount++; ok_(file, line) (FALSE, "%s: in event %s expecting ret 0x%08x got 0x%08x\n", context, get_event_name(actual->id), expected->ret, actual->ret); } else ok_(file, line) (expected->ret == actual->ret, "%s: in event %s expecting ret 0x%08x got 0x%08x\n", context, get_event_name(actual->id), expected->ret, actual->ret); break; case EH_ERROR: case EH_IGNORABLEWARNING: default: ok(0, "%s: callback not handled, %s\n", context, get_event_name(actual->id)); } expected++; actual++; } else if (todo) { failcount++; todo_wine { ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n", context, get_event_name(expected->id), get_event_name(actual->id)); } flush_sequence(seq, sequence_index); return; } else { ok_(file, line) (FALSE, "%s: call %s was expected, but got call %s instead\n", context, get_event_name(expected->id), get_event_name(actual->id)); expected++; actual++; } } if (todo) { todo_wine { if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST) { failcount++; ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n", context, get_event_name(expected->id), get_event_name(actual->id)); } } } else if (expected->id != CH_ENDTEST || actual->id != CH_ENDTEST) { ok_(file, line) (FALSE, "%s: the call sequence is not complete: expected %s - actual %s\n", context, get_event_name(expected->id), get_event_name(actual->id)); } if (todo && !failcount) /* succeeded yet marked todo */ { todo_wine { ok_(file, line)(TRUE, "%s: marked \"todo_wine\" but succeeds\n", context); } } flush_sequence(seq, sequence_index); } #define ok_sequence(seq, index, exp, contx, todo) \ ok_sequence_(seq, index, (exp), (contx), (todo), __FILE__, __LINE__) static void init_call_sequences(struct call_sequence **seq, int n) { int i; for (i = 0; i < n; i++) seq[i] = heap_alloc_zero(sizeof(struct call_sequence)); } static const WCHAR szSimpleXML[] = { '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','\"','1','.','0','\"',' ','?','>','\n', '<','B','a','n','k','A','c','c','o','u','n','t','>','\n', ' ',' ',' ','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\n', ' ',' ',' ','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\n', '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\n','\0' }; static const WCHAR carriage_ret_test[] = { '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"','?','>','\r','\n', '<','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n', '\t','<','N','u','m','b','e','r','>','1','2','3','4','<','/','N','u','m','b','e','r','>','\r','\n', '\t','<','N','a','m','e','>','C','a','p','t','a','i','n',' ','A','h','a','b','<','/','N','a','m','e','>','\r','\n', '<','/','B','a','n','k','A','c','c','o','u','n','t','>','\r','\n','\0' }; static const WCHAR szUtf16XML[] = { '<','?','x','m','l',' ','v','e','r','s','i','o','n','=','"','1','.','0','"',' ', 'e','n','c','o','d','i','n','g','=','"','U','T','F','-','1','6','"',' ', 's','t','a','n','d','a','l','o','n','e','=','"','n','o','"','?','>','\r','\n' }; static const CHAR szUtf16BOM[] = {0xff, 0xfe}; static const CHAR szUtf8XML[] = "\r\n"; static const char utf8xml2[] = "\r\n"; static const char testXML[] = "\n" "\n" " 1234\n" " Captain Ahab\n" "\n"; static const char test_attributes[] = "\n" "\n" "" "\n"; static const char test_cdata_xml[] = "" ""; static const char test2_cdata_xml[] = "" ""; static const char test3_cdata_xml[] = ""; static struct call_entry content_handler_test1[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" }, { CH_CHARACTERS, 2, 14, S_OK, "\n " }, { CH_STARTELEMENT, 3, 12, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 12, S_OK, "1234" }, { CH_ENDELEMENT, 3, 18, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 25, S_OK, "\n " }, { CH_STARTELEMENT, 4, 10, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 10, S_OK, "Captain Ahab" }, { CH_ENDELEMENT, 4, 24, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 29, S_OK, "\n" }, { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" }, { CH_ENDDOCUMENT, 0, 0, S_OK}, { CH_ENDTEST } }; /* applies to versions 4 and 6 */ static struct call_entry content_handler_test1_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" }, { CH_CHARACTERS, 3, 4, S_OK, "\n " }, { CH_STARTELEMENT, 3, 11, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 16, S_OK, "1234" }, { CH_ENDELEMENT, 3, 24, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 4, 4, S_OK, "\n " }, { CH_STARTELEMENT, 4, 9, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 22, S_OK, "Captain Ahab" }, { CH_ENDELEMENT, 4, 28, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 5, 1, S_OK, "\n" }, { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry content_handler_test2[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 2, 14, S_OK, "", "BankAccount", "BankAccount" }, { CH_CHARACTERS, 2, 14, S_OK, "\n" }, { CH_CHARACTERS, 2, 16, S_OK, "\t" }, { CH_STARTELEMENT, 3, 10, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 10, S_OK, "1234" }, { CH_ENDELEMENT, 3, 16, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 23, S_OK, "\n" }, { CH_CHARACTERS, 3, 25, S_OK, "\t" }, { CH_STARTELEMENT, 4, 8, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 8, S_OK, "Captain Ahab" }, { CH_ENDELEMENT, 4, 22, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 27, S_OK, "\n" }, { CH_ENDELEMENT, 5, 3, S_OK, "", "BankAccount", "BankAccount" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry content_handler_test2_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 21, S_OK }, { CH_STARTELEMENT, 2, 13, S_OK, "", "BankAccount", "BankAccount" }, { CH_CHARACTERS, 3, 0, S_OK, "\n" }, { CH_CHARACTERS, 3, 2, S_OK, "\t" }, { CH_STARTELEMENT, 3, 9, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 3, 14, S_OK, "1234" }, { CH_ENDELEMENT, 3, 22, S_OK, "", "Number", "Number" }, { CH_CHARACTERS, 4, 0, S_OK, "\n" }, { CH_CHARACTERS, 4, 2, S_OK, "\t" }, { CH_STARTELEMENT, 4, 7, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 4, 20, S_OK, "Captain Ahab" }, { CH_ENDELEMENT, 4, 26, S_OK, "", "Name", "Name" }, { CH_CHARACTERS, 5, 0, S_OK, "\n" }, { CH_ENDELEMENT, 5, 14, S_OK, "", "BankAccount", "BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry content_handler_testerror[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, E_FAIL }, { EH_FATALERROR, 0, 0, E_FAIL }, { CH_ENDTEST } }; static struct call_entry content_handler_testerror_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, E_FAIL }, { EH_FATALERROR, 1, 0, E_FAIL }, { CH_ENDTEST } }; static struct call_entry content_handler_test_callback_rets[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_FALSE }, { CH_STARTDOCUMENT, 0, 0, S_FALSE }, { EH_FATALERROR, 0, 0, S_FALSE }, { CH_ENDTEST } }; static struct call_entry content_handler_test_callback_rets_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_FALSE }, { CH_STARTDOCUMENT, 1, 22, S_FALSE }, { CH_STARTELEMENT, 2, 13, S_FALSE, "", "BankAccount", "BankAccount" }, { CH_CHARACTERS, 3, 4, S_FALSE, "\n " }, { CH_STARTELEMENT, 3, 11, S_FALSE, "", "Number", "Number" }, { CH_CHARACTERS, 3, 16, S_FALSE, "1234" }, { CH_ENDELEMENT, 3, 24, S_FALSE, "", "Number", "Number" }, { CH_CHARACTERS, 4, 4, S_FALSE, "\n " }, { CH_STARTELEMENT, 4, 9, S_FALSE, "", "Name", "Name" }, { CH_CHARACTERS, 4, 22, S_FALSE, "Captain Ahab" }, { CH_ENDELEMENT, 4, 28, S_FALSE, "", "Name", "Name" }, { CH_CHARACTERS, 5, 1, S_FALSE, "\n" }, { CH_ENDELEMENT, 5, 14, S_FALSE, "", "BankAccount", "BankAccount" }, { CH_ENDDOCUMENT, 6, 0, S_FALSE }, { CH_ENDTEST } }; static struct attribute_entry ch_attributes1[] = { { "", "", "xmlns:test", "prefix_test" }, { "", "", "xmlns", "prefix" }, { "prefix_test", "arg1", "test:arg1", "arg1" }, { "", "arg2", "arg2", "arg2" }, { "prefix_test", "ar3", "test:ar3", "arg3" }, { NULL } }; static struct attribute_entry ch_attributes2[] = { { "", "", "xmlns:p", "test" }, { NULL } }; static struct call_entry content_handler_test_attributes[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" }, { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" }, { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes1 }, { CH_CHARACTERS, 2, 96, S_OK, "\n" }, { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" }, { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", ch_attributes2 }, { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" }, { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" }, { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" }, { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" }, { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" }, { CH_ENDDOCUMENT, 0, 0 }, { CH_ENDTEST } }; static struct attribute_entry ch_attributes_alt_4[] = { { "prefix_test", "arg1", "test:arg1", "arg1" }, { "", "arg2", "arg2", "arg2" }, { "prefix_test", "ar3", "test:ar3", "arg3" }, { "", "", "xmlns:test", "prefix_test" }, { "", "", "xmlns", "prefix" }, { NULL } }; static struct call_entry content_handler_test_attributes_alternate_4[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" }, { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_4 }, { CH_CHARACTERS, 3, 1, S_OK, "\n" }, { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" }, { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2 }, { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" }, { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" }, { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; /* 'namespace' feature switched off */ static struct attribute_entry ch_attributes_alt_no_ns[] = { { "", "", "xmlns:test", "prefix_test" }, { "", "", "xmlns", "prefix" }, { "", "", "test:arg1", "arg1" }, { "", "", "arg2", "arg2" }, { "", "", "test:ar3", "arg3" }, { NULL } }; static struct call_entry content_handler_test_attributes_alt_no_ns[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 2, 95, S_OK, "", "", "document", ch_attributes_alt_no_ns }, { CH_CHARACTERS, 3, 1, S_OK, "\n" }, { CH_STARTELEMENT, 3, 24, S_OK, "", "", "node1", ch_attributes2 }, { CH_ENDELEMENT, 3, 24, S_OK, "", "", "node1" }, { CH_ENDELEMENT, 3, 35, S_OK, "", "", "document" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; static struct attribute_entry ch_attributes_alt_6[] = { { "prefix_test", "arg1", "test:arg1", "arg1" }, { "", "arg2", "arg2", "arg2" }, { "prefix_test", "ar3", "test:ar3", "arg3" }, { "http://www.w3.org/2000/xmlns/", "", "xmlns:test", "prefix_test" }, { "http://www.w3.org/2000/xmlns/", "", "xmlns", "prefix" }, { NULL } }; static struct attribute_entry ch_attributes2_6[] = { { "http://www.w3.org/2000/xmlns/", "", "xmlns:p", "test" }, { NULL } }; static struct call_entry content_handler_test_attributes_alternate_6[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" }, { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_alt_6 }, { CH_CHARACTERS, 3, 1, S_OK, "\n" }, { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" }, { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", ch_attributes2_6 }, { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" }, { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" }, { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; /* 'namespaces' is on, 'namespace-prefixes' if off */ static struct attribute_entry ch_attributes_no_prefix[] = { { "prefix_test", "arg1", "test:arg1", "arg1" }, { "", "arg2", "arg2", "arg2" }, { "prefix_test", "ar3", "test:ar3", "arg3" }, { NULL } }; static struct call_entry content_handler_test_attributes_alt_no_prefix[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "test", "prefix_test" }, { CH_STARTPREFIXMAPPING, 2, 95, S_OK, "", "prefix" }, { CH_STARTELEMENT, 2, 95, S_OK, "prefix", "document", "document", ch_attributes_no_prefix }, { CH_CHARACTERS, 3, 1, S_OK, "\n" }, { CH_STARTPREFIXMAPPING, 3, 24, S_OK, "p", "test" }, { CH_STARTELEMENT, 3, 24, S_OK, "prefix", "node1", "node1", NULL }, { CH_ENDELEMENT, 3, 24, S_OK, "prefix", "node1", "node1" }, { CH_ENDPREFIXMAPPING, 3, 24, S_OK, "p" }, { CH_ENDELEMENT, 3, 35, S_OK, "prefix", "document", "document" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "test" }, { CH_ENDPREFIXMAPPING, 3, 35, S_OK, "" }, { CH_ENDDOCUMENT, 4, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry content_handler_test_attributes_no_prefix[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "test", "prefix_test" }, { CH_STARTPREFIXMAPPING, 2, 96, S_OK, "", "prefix" }, { CH_STARTELEMENT, 2, 96, S_OK, "prefix", "document", "document", ch_attributes_no_prefix }, { CH_CHARACTERS, 2, 96, S_OK, "\n" }, { CH_STARTPREFIXMAPPING, 3, 25, S_OK, "p", "test" }, { CH_STARTELEMENT, 3, 25, S_OK, "prefix", "node1", "node1", NULL }, { CH_ENDELEMENT, 3, 25, S_OK, "prefix", "node1", "node1" }, { CH_ENDPREFIXMAPPING, 3, 25, S_OK, "p" }, { CH_ENDELEMENT, 3, 27, S_OK, "prefix", "document", "document" }, { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "" }, { CH_ENDPREFIXMAPPING, 3, 27, S_OK, "test" }, { CH_ENDDOCUMENT, 0, 0 }, { CH_ENDTEST } }; static struct attribute_entry xmlspace_attrs[] = { { "http://www.w3.org/XML/1998/namespace", "space", "xml:space", "preserve" }, { NULL } }; static struct call_entry xmlspaceattr_test[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 1, 64, S_OK, "", "a", "a", xmlspace_attrs }, { CH_CHARACTERS, 1, 64, S_OK, " Some text data " }, { CH_ENDELEMENT, 1, 82, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry xmlspaceattr_test_alternate[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 39, S_OK }, { CH_STARTELEMENT, 1, 63, S_OK, "", "a", "a", xmlspace_attrs }, { CH_CHARACTERS, 1, 80, S_OK, " Some text data " }, { CH_ENDELEMENT, 1, 83, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 1, 83, S_OK }, { CH_ENDTEST } }; /* attribute value normalization test */ static const char attribute_normalize[] = "\n" "\n"; static struct attribute_entry attribute_norm_attrs[] = { { "", "attr1", "attr1", " attr_value A & & " }, { NULL } }; static struct call_entry attribute_norm[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 6, 4, S_OK, "", "a", "a", attribute_norm_attrs }, { CH_ENDELEMENT, 6, 4, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry attribute_norm_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 8, 3, S_OK, "", "a", "a", attribute_norm_attrs }, { CH_ENDELEMENT, 8, 3, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 9, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry cdata_test[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 35, S_OK }, { CH_CHARACTERS, 1, 35, S_OK, "Some \n" }, { CH_CHARACTERS, 1, 42, S_OK, "text\n\n" }, { CH_CHARACTERS, 1, 49, S_OK, "data\n\n" }, { LH_ENDCDATA, 1, 49, S_OK }, { CH_ENDELEMENT, 6, 6, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry cdata_test2[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 35, S_OK }, { CH_CHARACTERS, 1, 35, S_OK, "\n\n" }, { CH_CHARACTERS, 1, 38, S_OK, "Some \n" }, { CH_CHARACTERS, 1, 45, S_OK, "text\n\n" }, { CH_CHARACTERS, 1, 52, S_OK, "data\n\n" }, { LH_ENDCDATA, 1, 52, S_OK }, { CH_ENDELEMENT, 8, 6, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; static struct call_entry cdata_test3[] = { { CH_PUTDOCUMENTLOCATOR, 0, 0, S_OK }, { CH_STARTDOCUMENT, 0, 0, S_OK }, { CH_STARTELEMENT, 1, 26, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 35, S_OK }, { CH_CHARACTERS, 1, 35, S_OK, "Some text data" }, { LH_ENDCDATA, 1, 35, S_OK }, { CH_ENDELEMENT, 1, 54, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 0, 0, S_OK }, { CH_ENDTEST } }; /* this is what MSXML6 does */ static struct call_entry cdata_test_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 34, S_OK }, { CH_CHARACTERS, 1, 40, S_OK, "Some " }, { CH_CHARACTERS, 2, 0, S_OK, "\n" }, { CH_CHARACTERS, 3, 1, S_OK, "text\n" }, { CH_CHARACTERS, 4, 0, S_OK, "\n" }, { CH_CHARACTERS, 6, 3, S_OK, "data\n\n" }, { LH_ENDCDATA, 6, 3, S_OK }, { CH_ENDELEMENT, 6, 7, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 6, 7, S_OK }, { CH_ENDTEST } }; static struct call_entry cdata_test2_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 34, S_OK }, { CH_CHARACTERS, 2, 1, S_OK, "\n" }, { CH_CHARACTERS, 3, 0, S_OK, "\n" }, { CH_CHARACTERS, 3, 6, S_OK, "Some " }, { CH_CHARACTERS, 4, 0, S_OK, "\n" }, { CH_CHARACTERS, 5, 1, S_OK, "text\n" }, { CH_CHARACTERS, 6, 0, S_OK, "\n" }, { CH_CHARACTERS, 8, 3, S_OK, "data\n\n" }, { LH_ENDCDATA, 8, 3, S_OK }, { CH_ENDELEMENT, 8, 7, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 8, 7, S_OK }, { CH_ENDTEST } }; static struct call_entry cdata_test3_alt[] = { { CH_PUTDOCUMENTLOCATOR, 1, 0, S_OK }, { CH_STARTDOCUMENT, 1, 22, S_OK }, { CH_STARTELEMENT, 1, 25, S_OK, "", "a", "a" }, { LH_STARTCDATA, 1, 34, S_OK }, { CH_CHARACTERS, 1, 51, S_OK, "Some text data" }, { LH_ENDCDATA, 1, 51, S_OK }, { CH_ENDELEMENT, 1, 55, S_OK, "", "a", "a" }, { CH_ENDDOCUMENT, 1, 55, S_OK }, { CH_ENDTEST } }; static struct attribute_entry read_test_attrs[] = { { "", "attr", "attr", "val" }, { NULL } }; static struct call_entry read_test_seq[] = { { CH_PUTDOCUMENTLOCATOR, -1, 0, S_OK }, { CH_STARTDOCUMENT, -1, -1, S_OK }, { CH_STARTELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" }, { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, { CH_CHARACTERS, -1, -1, S_OK, "text" }, { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, { CH_CHARACTERS, -1, -1, S_OK, "text" }, { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, { CH_CHARACTERS, -1, -1, S_OK, "text" }, { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, { CH_STARTELEMENT, -1, -1, S_OK, "", "elem", "elem", read_test_attrs }, { CH_CHARACTERS, -1, -1, S_OK, "text" }, { CH_ENDELEMENT, -1, -1, S_OK, "", "elem", "elem" }, { CH_ENDELEMENT, -1, -1, S_OK, "", "rootelem", "rootelem" }, { CH_ENDDOCUMENT, -1, -1, S_OK}, { CH_ENDTEST } }; static const char xmlspace_attr[] = "" " Some text data "; static struct call_entry *expectCall; static ISAXLocator *locator; static ISAXXMLReader *g_reader; int msxml_version; static void set_expected_seq(struct call_entry *expected) { expectCall = expected; } /* to be called once on each tested callback return */ static HRESULT get_expected_ret(void) { HRESULT hr = expectCall->ret; if (expectCall->id != CH_ENDTEST) expectCall++; return hr; } static HRESULT WINAPI contentHandler_QueryInterface( ISAXContentHandler* iface, REFIID riid, void **ppvObject) { *ppvObject = NULL; if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXContentHandler)) { *ppvObject = iface; } else { return E_NOINTERFACE; } return S_OK; } static ULONG WINAPI contentHandler_AddRef( ISAXContentHandler* iface) { return 2; } static ULONG WINAPI contentHandler_Release( ISAXContentHandler* iface) { return 1; } static HRESULT WINAPI contentHandler_putDocumentLocator( ISAXContentHandler* iface, ISAXLocator *pLocator) { struct call_entry call; IUnknown *unk; HRESULT hr; locator = pLocator; init_call_entry(locator, &call); call.id = CH_PUTDOCUMENTLOCATOR; add_call(sequences, CONTENT_HANDLER_INDEX, &call); hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXLocator, (void**)&unk); EXPECT_HR(hr, E_NOINTERFACE); if (msxml_version >= 6) { ISAXAttributes *attr, *attr1; IMXAttributes *mxattr; EXPECT_REF(pLocator, 1); hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr); EXPECT_HR(hr, S_OK); EXPECT_REF(pLocator, 2); hr = ISAXLocator_QueryInterface(pLocator, &IID_ISAXAttributes, (void**)&attr1); EXPECT_HR(hr, S_OK); EXPECT_REF(pLocator, 3); ok(attr == attr1, "got %p, %p\n", attr, attr1); hr = ISAXAttributes_QueryInterface(attr, &IID_IVBSAXAttributes, (void**)&unk); EXPECT_HR(hr, E_NOINTERFACE); hr = ISAXLocator_QueryInterface(pLocator, &IID_IVBSAXAttributes, (void**)&unk); EXPECT_HR(hr, E_NOINTERFACE); hr = ISAXAttributes_QueryInterface(attr, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, E_NOINTERFACE); ISAXAttributes_Release(attr); ISAXAttributes_Release(attr1); } return get_expected_ret(); } static ISAXAttributes *test_attr_ptr; static HRESULT WINAPI contentHandler_startDocument( ISAXContentHandler* iface) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_STARTDOCUMENT; add_call(sequences, CONTENT_HANDLER_INDEX, &call); test_attr_ptr = NULL; return get_expected_ret(); } static HRESULT WINAPI contentHandler_endDocument( ISAXContentHandler* iface) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_ENDDOCUMENT; add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_startPrefixMapping( ISAXContentHandler* iface, const WCHAR *prefix, int prefix_len, const WCHAR *uri, int uri_len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_STARTPREFIXMAPPING; call.arg1W = SysAllocStringLen(prefix, prefix_len); call.arg2W = SysAllocStringLen(uri, uri_len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_endPrefixMapping( ISAXContentHandler* iface, const WCHAR *prefix, int len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_ENDPREFIXMAPPING; call.arg1W = SysAllocStringLen(prefix, len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_startElement( ISAXContentHandler* iface, const WCHAR *uri, int uri_len, const WCHAR *localname, int local_len, const WCHAR *qname, int qname_len, ISAXAttributes *saxattr) { struct call_entry call; IMXAttributes *mxattr; HRESULT hr; int len; hr = ISAXAttributes_QueryInterface(saxattr, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, E_NOINTERFACE); init_call_entry(locator, &call); call.id = CH_STARTELEMENT; call.arg1W = SysAllocStringLen(uri, uri_len); call.arg2W = SysAllocStringLen(localname, local_len); call.arg3W = SysAllocStringLen(qname, qname_len); if(!test_attr_ptr) test_attr_ptr = saxattr; ok(test_attr_ptr == saxattr, "Multiple ISAXAttributes instances are used (%p %p)\n", test_attr_ptr, saxattr); /* store actual attributes */ len = 0; hr = ISAXAttributes_getLength(saxattr, &len); EXPECT_HR(hr, S_OK); if (len) { VARIANT_BOOL v; int i; struct attribute_entry *attr; attr = heap_alloc_zero(len * sizeof(*attr)); v = VARIANT_TRUE; hr = ISAXXMLReader_getFeature(g_reader, _bstr_("http://xml.org/sax/features/namespaces"), &v); EXPECT_HR(hr, S_OK); for (i = 0; i < len; i++) { const WCHAR *value; int value_len; hr = ISAXAttributes_getName(saxattr, i, &uri, &uri_len, &localname, &local_len, &qname, &qname_len); EXPECT_HR(hr, S_OK); hr = ISAXAttributes_getValue(saxattr, i, &value, &value_len); EXPECT_HR(hr, S_OK); /* if 'namespaces' switched off uri and local name contains garbage */ if (v == VARIANT_FALSE && msxml_version > 0) { attr[i].uriW = SysAllocStringLen(NULL, 0); attr[i].localW = SysAllocStringLen(NULL, 0); } else { attr[i].uriW = SysAllocStringLen(uri, uri_len); attr[i].localW = SysAllocStringLen(localname, local_len); } attr[i].qnameW = SysAllocStringLen(qname, qname_len); attr[i].valueW = SysAllocStringLen(value, value_len); } call.attributes = attr; call.attr_count = len; } add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_endElement( ISAXContentHandler* iface, const WCHAR *uri, int uri_len, const WCHAR *localname, int local_len, const WCHAR *qname, int qname_len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_ENDELEMENT; call.arg1W = SysAllocStringLen(uri, uri_len); call.arg2W = SysAllocStringLen(localname, local_len); call.arg3W = SysAllocStringLen(qname, qname_len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_characters( ISAXContentHandler* iface, const WCHAR *chars, int len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_CHARACTERS; call.arg1W = SysAllocStringLen(chars, len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_ignorableWhitespace( ISAXContentHandler* iface, const WCHAR *chars, int len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_IGNORABLEWHITESPACE; call.arg1W = SysAllocStringLen(chars, len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_processingInstruction( ISAXContentHandler* iface, const WCHAR *target, int target_len, const WCHAR *data, int data_len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_PROCESSINGINSTRUCTION; call.arg1W = SysAllocStringLen(target, target_len); call.arg2W = SysAllocStringLen(data, data_len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI contentHandler_skippedEntity( ISAXContentHandler* iface, const WCHAR *name, int len) { struct call_entry call; init_call_entry(locator, &call); call.id = CH_SKIPPEDENTITY; call.arg1W = SysAllocStringLen(name, len); add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static const ISAXContentHandlerVtbl contentHandlerVtbl = { contentHandler_QueryInterface, contentHandler_AddRef, contentHandler_Release, contentHandler_putDocumentLocator, contentHandler_startDocument, contentHandler_endDocument, contentHandler_startPrefixMapping, contentHandler_endPrefixMapping, contentHandler_startElement, contentHandler_endElement, contentHandler_characters, contentHandler_ignorableWhitespace, contentHandler_processingInstruction, contentHandler_skippedEntity }; static ISAXContentHandler contentHandler = { &contentHandlerVtbl }; static HRESULT WINAPI isaxerrorHandler_QueryInterface( ISAXErrorHandler* iface, REFIID riid, void **ppvObject) { *ppvObject = NULL; if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXErrorHandler)) { *ppvObject = iface; } else { return E_NOINTERFACE; } return S_OK; } static ULONG WINAPI isaxerrorHandler_AddRef( ISAXErrorHandler* iface) { return 2; } static ULONG WINAPI isaxerrorHandler_Release( ISAXErrorHandler* iface) { return 1; } static HRESULT WINAPI isaxerrorHandler_error( ISAXErrorHandler* iface, ISAXLocator *pLocator, const WCHAR *pErrorMessage, HRESULT hrErrorCode) { ok(0, "unexpected call\n"); return S_OK; } static HRESULT WINAPI isaxerrorHandler_fatalError( ISAXErrorHandler* iface, ISAXLocator *pLocator, const WCHAR *message, HRESULT hr) { struct call_entry call; init_call_entry(locator, &call); call.id = EH_FATALERROR; call.ret = hr; add_call(sequences, CONTENT_HANDLER_INDEX, &call); get_expected_ret(); return S_OK; } static HRESULT WINAPI isaxerrorHandler_ignorableWarning( ISAXErrorHandler* iface, ISAXLocator *pLocator, const WCHAR *pErrorMessage, HRESULT hrErrorCode) { ok(0, "unexpected call\n"); return S_OK; } static const ISAXErrorHandlerVtbl errorHandlerVtbl = { isaxerrorHandler_QueryInterface, isaxerrorHandler_AddRef, isaxerrorHandler_Release, isaxerrorHandler_error, isaxerrorHandler_fatalError, isaxerrorHandler_ignorableWarning }; static ISAXErrorHandler errorHandler = { &errorHandlerVtbl }; static HRESULT WINAPI isaxattributes_QueryInterface( ISAXAttributes* iface, REFIID riid, void **ppvObject) { *ppvObject = NULL; if(IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_ISAXAttributes)) { *ppvObject = iface; } else { return E_NOINTERFACE; } return S_OK; } static ULONG WINAPI isaxattributes_AddRef(ISAXAttributes* iface) { return 2; } static ULONG WINAPI isaxattributes_Release(ISAXAttributes* iface) { return 1; } static HRESULT WINAPI isaxattributes_getLength(ISAXAttributes* iface, int *length) { *length = 3; return S_OK; } static HRESULT WINAPI isaxattributes_getURI( ISAXAttributes* iface, int nIndex, const WCHAR **pUrl, int *pUriSize) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getLocalName( ISAXAttributes* iface, int nIndex, const WCHAR **pLocalName, int *pLocalNameLength) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getQName( ISAXAttributes* iface, int index, const WCHAR **QName, int *QNameLength) { static const WCHAR attrqnamesW[][15] = {{'a',':','a','t','t','r','1','j','u','n','k',0}, {'a','t','t','r','2','j','u','n','k',0}, {'a','t','t','r','3',0}}; static const int attrqnamelen[] = {7, 5, 5}; ok(index >= 0 && index <= 2, "invalid index received %d\n", index); if (index >= 0 && index <= 2) { *QName = attrqnamesW[index]; *QNameLength = attrqnamelen[index]; } else { *QName = NULL; *QNameLength = 0; } return S_OK; } static HRESULT WINAPI isaxattributes_getName( ISAXAttributes* iface, int nIndex, const WCHAR **pUri, int * pUriLength, const WCHAR ** pLocalName, int * pLocalNameSize, const WCHAR ** pQName, int * pQNameLength) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getIndexFromName( ISAXAttributes* iface, const WCHAR * pUri, int cUriLength, const WCHAR * pLocalName, int cocalNameLength, int * index) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getIndexFromQName( ISAXAttributes* iface, const WCHAR * pQName, int nQNameLength, int * index) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getType( ISAXAttributes* iface, int nIndex, const WCHAR ** pType, int * pTypeLength) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getTypeFromName( ISAXAttributes* iface, const WCHAR * pUri, int nUri, const WCHAR * pLocalName, int nLocalName, const WCHAR ** pType, int * nType) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getTypeFromQName( ISAXAttributes* iface, const WCHAR * pQName, int nQName, const WCHAR ** pType, int * nType) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getValue(ISAXAttributes* iface, int index, const WCHAR **value, int *nValue) { static const WCHAR attrvaluesW[][10] = {{'a','1','j','u','n','k',0}, {'a','2','j','u','n','k',0}, {'<','&','"','>','\'',0}}; static const int attrvalueslen[] = {2, 2, 5}; ok(index >= 0 && index <= 2, "invalid index received %d\n", index); if (index >= 0 && index <= 2) { *value = attrvaluesW[index]; *nValue = attrvalueslen[index]; } else { *value = NULL; *nValue = 0; } return S_OK; } static HRESULT WINAPI isaxattributes_getValueFromName( ISAXAttributes* iface, const WCHAR * pUri, int nUri, const WCHAR * pLocalName, int nLocalName, const WCHAR ** pValue, int * nValue) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxattributes_getValueFromQName( ISAXAttributes* iface, const WCHAR * pQName, int nQName, const WCHAR ** pValue, int * nValue) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static const ISAXAttributesVtbl SAXAttributesVtbl = { isaxattributes_QueryInterface, isaxattributes_AddRef, isaxattributes_Release, isaxattributes_getLength, isaxattributes_getURI, isaxattributes_getLocalName, isaxattributes_getQName, isaxattributes_getName, isaxattributes_getIndexFromName, isaxattributes_getIndexFromQName, isaxattributes_getType, isaxattributes_getTypeFromName, isaxattributes_getTypeFromQName, isaxattributes_getValue, isaxattributes_getValueFromName, isaxattributes_getValueFromQName }; static ISAXAttributes saxattributes = { &SAXAttributesVtbl }; struct saxlexicalhandler { ISAXLexicalHandler ISAXLexicalHandler_iface; LONG ref; HRESULT qi_hr; /* ret value for QueryInterface for handler riid */ }; static inline struct saxlexicalhandler *impl_from_ISAXLexicalHandler( ISAXLexicalHandler *iface ) { return CONTAINING_RECORD(iface, struct saxlexicalhandler, ISAXLexicalHandler_iface); } static HRESULT WINAPI isaxlexical_QueryInterface(ISAXLexicalHandler* iface, REFIID riid, void **out) { struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); *out = NULL; if (IsEqualGUID(riid, &IID_IUnknown)) { *out = iface; ok(0, "got unexpected IID_IUnknown query\n"); } else if (IsEqualGUID(riid, &IID_ISAXLexicalHandler)) { if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr; *out = iface; } if (*out) ISAXLexicalHandler_AddRef(iface); else return E_NOINTERFACE; return S_OK; } static ULONG WINAPI isaxlexical_AddRef(ISAXLexicalHandler* iface) { struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); return InterlockedIncrement(&handler->ref); } static ULONG WINAPI isaxlexical_Release(ISAXLexicalHandler* iface) { struct saxlexicalhandler *handler = impl_from_ISAXLexicalHandler(iface); return InterlockedDecrement(&handler->ref); } static HRESULT WINAPI isaxlexical_startDTD(ISAXLexicalHandler* iface, const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId, const WCHAR * pSystemId, int nSystemId) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxlexical_endDTD(ISAXLexicalHandler* iface) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxlexical_startEntity(ISAXLexicalHandler *iface, const WCHAR * pName, int nName) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxlexical_endEntity(ISAXLexicalHandler *iface, const WCHAR * pName, int nName) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxlexical_startCDATA(ISAXLexicalHandler *iface) { struct call_entry call; init_call_entry(locator, &call); call.id = LH_STARTCDATA; add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI isaxlexical_endCDATA(ISAXLexicalHandler *iface) { struct call_entry call; init_call_entry(locator, &call); call.id = LH_ENDCDATA; add_call(sequences, CONTENT_HANDLER_INDEX, &call); return get_expected_ret(); } static HRESULT WINAPI isaxlexical_comment(ISAXLexicalHandler *iface, const WCHAR * pChars, int nChars) { ok(0, "call not expected\n"); return E_NOTIMPL; } static const ISAXLexicalHandlerVtbl SAXLexicalHandlerVtbl = { isaxlexical_QueryInterface, isaxlexical_AddRef, isaxlexical_Release, isaxlexical_startDTD, isaxlexical_endDTD, isaxlexical_startEntity, isaxlexical_endEntity, isaxlexical_startCDATA, isaxlexical_endCDATA, isaxlexical_comment }; static void init_saxlexicalhandler(struct saxlexicalhandler *handler, HRESULT hr) { handler->ISAXLexicalHandler_iface.lpVtbl = &SAXLexicalHandlerVtbl; handler->ref = 1; handler->qi_hr = hr; } struct saxdeclhandler { ISAXDeclHandler ISAXDeclHandler_iface; LONG ref; HRESULT qi_hr; /* ret value for QueryInterface for handler riid */ }; static inline struct saxdeclhandler *impl_from_ISAXDeclHandler( ISAXDeclHandler *iface ) { return CONTAINING_RECORD(iface, struct saxdeclhandler, ISAXDeclHandler_iface); } static HRESULT WINAPI isaxdecl_QueryInterface(ISAXDeclHandler* iface, REFIID riid, void **out) { struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); *out = NULL; if (IsEqualGUID(riid, &IID_IUnknown)) { *out = iface; ok(0, "got unexpected IID_IUnknown query\n"); } else if (IsEqualGUID(riid, &IID_ISAXDeclHandler)) { if (handler->qi_hr == E_NOINTERFACE) return handler->qi_hr; *out = iface; } if (*out) ISAXDeclHandler_AddRef(iface); else return E_NOINTERFACE; return S_OK; } static ULONG WINAPI isaxdecl_AddRef(ISAXDeclHandler* iface) { struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); return InterlockedIncrement(&handler->ref); } static ULONG WINAPI isaxdecl_Release(ISAXDeclHandler* iface) { struct saxdeclhandler *handler = impl_from_ISAXDeclHandler(iface); return InterlockedDecrement(&handler->ref); } static HRESULT WINAPI isaxdecl_elementDecl(ISAXDeclHandler* iface, const WCHAR * pName, int nName, const WCHAR * pModel, int nModel) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxdecl_attributeDecl(ISAXDeclHandler* iface, const WCHAR * pElementName, int nElementName, const WCHAR * pAttributeName, int nAttributeName, const WCHAR * pType, int nType, const WCHAR * pValueDefault, int nValueDefault, const WCHAR * pValue, int nValue) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxdecl_internalEntityDecl(ISAXDeclHandler* iface, const WCHAR * pName, int nName, const WCHAR * pValue, int nValue) { ok(0, "call not expected\n"); return E_NOTIMPL; } static HRESULT WINAPI isaxdecl_externalEntityDecl(ISAXDeclHandler* iface, const WCHAR * pName, int nName, const WCHAR * pPublicId, int nPublicId, const WCHAR * pSystemId, int nSystemId) { ok(0, "call not expected\n"); return E_NOTIMPL; } static const ISAXDeclHandlerVtbl SAXDeclHandlerVtbl = { isaxdecl_QueryInterface, isaxdecl_AddRef, isaxdecl_Release, isaxdecl_elementDecl, isaxdecl_attributeDecl, isaxdecl_internalEntityDecl, isaxdecl_externalEntityDecl }; static void init_saxdeclhandler(struct saxdeclhandler *handler, HRESULT hr) { handler->ISAXDeclHandler_iface.lpVtbl = &SAXDeclHandlerVtbl; handler->ref = 1; handler->qi_hr = hr; } typedef struct mxwriter_write_test_t { BOOL last; const BYTE *data; DWORD cb; BOOL null_written; BOOL fail_write; } mxwriter_write_test; typedef struct mxwriter_stream_test_t { VARIANT_BOOL bom; const char *encoding; mxwriter_write_test expected_writes[4]; } mxwriter_stream_test; static const mxwriter_write_test *current_write_test; static DWORD current_stream_test_index; static HRESULT WINAPI istream_QueryInterface(IStream *iface, REFIID riid, void **ppvObject) { *ppvObject = NULL; ok(!IsEqualGUID(riid, &IID_IPersistStream), "Did not expect QI for IPersistStream\n"); if(IsEqualGUID(riid, &IID_IStream) || IsEqualGUID(riid, &IID_IUnknown)) *ppvObject = iface; else return E_NOINTERFACE; return S_OK; } static ULONG WINAPI istream_AddRef(IStream *iface) { return 2; } static ULONG WINAPI istream_Release(IStream *iface) { return 1; } static HRESULT WINAPI istream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_Seek(IStream *iface, LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_SetSize(IStream *iface, ULARGE_INTEGER libNewSize) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_CopyTo(IStream *iface, IStream *pstm, ULARGE_INTEGER cb, ULARGE_INTEGER *pcbRead, ULARGE_INTEGER *plibWritten) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_Commit(IStream *iface, DWORD grfCommitFlags) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_Revert(IStream *iface) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_LockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_UnlockRegion(IStream *iface, ULARGE_INTEGER libOffset, ULARGE_INTEGER cb, DWORD dwLockType) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI istream_Stat(IStream *iface, STATSTG *pstatstg, DWORD grfStatFlag) { return E_NOTIMPL; } static HRESULT WINAPI istream_Clone(IStream *iface, IStream **ppstm) { ok(0, "unexpected call\n"); return E_NOTIMPL; } static HRESULT WINAPI mxstream_Write(IStream *iface, const void *pv, ULONG cb, ULONG *pcbWritten) { BOOL fail = FALSE; ok(pv != NULL, "pv == NULL\n"); if(current_write_test->last) { ok(0, "Too many Write calls made on test %d\n", current_stream_test_index); return E_FAIL; } fail = current_write_test->fail_write; ok(current_write_test->cb == cb, "Expected %d, but got %d on test %d\n", current_write_test->cb, cb, current_stream_test_index); if(!pcbWritten) ok(current_write_test->null_written, "pcbWritten was NULL on test %d\n", current_stream_test_index); else ok(!memcmp(current_write_test->data, pv, cb), "Unexpected data on test %d\n", current_stream_test_index); ++current_write_test; if(pcbWritten) *pcbWritten = cb; return fail ? E_FAIL : S_OK; } static const IStreamVtbl mxstreamVtbl = { istream_QueryInterface, istream_AddRef, istream_Release, istream_Read, mxstream_Write, istream_Seek, istream_SetSize, istream_CopyTo, istream_Commit, istream_Revert, istream_LockRegion, istream_UnlockRegion, istream_Stat, istream_Clone }; static IStream mxstream = { &mxstreamVtbl }; static int read_cnt; static HRESULT WINAPI instream_Read(IStream *iface, void *pv, ULONG cb, ULONG *pcbRead) { static const char *ret_str; if(!read_cnt) ret_str = "\n"; else if(read_cnt < 5) ret_str = "text"; else if(read_cnt == 5) ret_str = "\n"; else ret_str = ""; read_cnt++; strcpy(pv, ret_str); *pcbRead = strlen(ret_str); return S_OK; } static const IStreamVtbl instreamVtbl = { istream_QueryInterface, istream_AddRef, istream_Release, instream_Read, istream_Write, istream_Seek, istream_SetSize, istream_CopyTo, istream_Commit, istream_Revert, istream_LockRegion, istream_UnlockRegion, istream_Stat, istream_Clone }; static IStream instream = { &instreamVtbl }; static struct msxmlsupported_data_t reader_support_data[] = { { &CLSID_SAXXMLReader, "SAXReader" }, { &CLSID_SAXXMLReader30, "SAXReader30" }, { &CLSID_SAXXMLReader40, "SAXReader40" }, { &CLSID_SAXXMLReader60, "SAXReader60" }, { NULL } }; static struct saxlexicalhandler lexicalhandler; static struct saxdeclhandler declhandler; static IStream *create_test_stream(const char *data, int len) { ULARGE_INTEGER size; LARGE_INTEGER pos; IStream *stream; ULONG written; if (len == -1) len = strlen(data); CreateStreamOnHGlobal(NULL, TRUE, &stream); size.QuadPart = len; IStream_SetSize(stream, size); IStream_Write(stream, data, len, &written); pos.QuadPart = 0; IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); return stream; } static void test_saxreader(void) { const struct msxmlsupported_data_t *table = reader_support_data; HRESULT hr; ISAXXMLReader *reader = NULL; VARIANT var; ISAXContentHandler *content; ISAXErrorHandler *lpErrorHandler; SAFEARRAY *sa; SAFEARRAYBOUND SADim[1]; char *ptr = NULL; IStream *stream; ULONG written; HANDLE file; static const CHAR testXmlA[] = "test.xml"; static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0}; IXMLDOMDocument *doc; char seqname[50]; VARIANT_BOOL v; while (table->clsid) { struct call_entry *test_seq; ISAXEntityResolver *resolver; BSTR str; if (!is_clsid_supported(table->clsid, reader_support_data)) { table++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); EXPECT_HR(hr, S_OK); g_reader = reader; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) msxml_version = 4; else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) msxml_version = 6; else msxml_version = 0; /* crashes on old versions */ if (!IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) && !IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { hr = ISAXXMLReader_getContentHandler(reader, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXXMLReader_getErrorHandler(reader, NULL); EXPECT_HR(hr, E_POINTER); } hr = ISAXXMLReader_getContentHandler(reader, &content); EXPECT_HR(hr, S_OK); ok(content == NULL, "Expected %p, got %p\n", NULL, content); hr = ISAXXMLReader_getErrorHandler(reader, &lpErrorHandler); EXPECT_HR(hr, S_OK); ok(lpErrorHandler == NULL, "Expected %p, got %p\n", NULL, lpErrorHandler); hr = ISAXXMLReader_putContentHandler(reader, NULL); EXPECT_HR(hr, S_OK); hr = ISAXXMLReader_putContentHandler(reader, &contentHandler); EXPECT_HR(hr, S_OK); hr = ISAXXMLReader_putErrorHandler(reader, &errorHandler); EXPECT_HR(hr, S_OK); hr = ISAXXMLReader_getContentHandler(reader, &content); EXPECT_HR(hr, S_OK); ok(content == &contentHandler, "Expected %p, got %p\n", &contentHandler, content); V_VT(&var) = VT_BSTR; V_BSTR(&var) = SysAllocString(szSimpleXML); if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_test1_alternate; else test_seq = content_handler_test1; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1", FALSE); VariantClear(&var); SADim[0].lLbound = 0; SADim[0].cElements = sizeof(testXML)-1; sa = SafeArrayCreate(VT_UI1, 1, SADim); SafeArrayAccessData(sa, (void**)&ptr); memcpy(ptr, testXML, sizeof(testXML)-1); SafeArrayUnaccessData(sa); V_VT(&var) = VT_ARRAY|VT_UI1; V_ARRAY(&var) = sa; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from safe array", FALSE); SafeArrayDestroy(sa); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = NULL; hr = ISAXXMLReader_parse(reader, var); ok(hr == E_INVALIDARG, "got %#x\n", hr); V_VT(&var) = VT_DISPATCH; V_DISPATCH(&var) = NULL; hr = ISAXXMLReader_parse(reader, var); ok(hr == E_INVALIDARG, "got %#x\n", hr); stream = create_test_stream(testXML, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from stream", FALSE); IStream_Release(stream); stream = create_test_stream(test_attributes, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = content_handler_test_attributes_alternate_4; else if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_test_attributes_alternate_6; else test_seq = content_handler_test_attributes; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); else ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE); IStream_Release(stream); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)&instream; test_seq = read_test_seq; read_cnt = 0; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok(read_cnt == 7, "read_cnt = %d\n", read_cnt); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "Read call test", FALSE); V_VT(&var) = VT_BSTR; V_BSTR(&var) = SysAllocString(carriage_ret_test); if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_test2_alternate; else test_seq = content_handler_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 2", FALSE); VariantClear(&var); /* from file url */ file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError()); WriteFile(file, testXML, sizeof(testXML)-1, &written, NULL); CloseHandle(file); if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_test1_alternate; else test_seq = content_handler_test1; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, testXmlW); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test 1: from file url", FALSE); /* error handler */ if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_testerror_alternate; else test_seq = content_handler_testerror; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, testXmlW); EXPECT_HR(hr, E_FAIL); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test error", FALSE); /* callback ret values */ if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { test_seq = content_handler_test_callback_rets_alt; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, testXmlW); EXPECT_HR(hr, S_OK); } else { test_seq = content_handler_test_callback_rets; set_expected_seq(test_seq); hr = ISAXXMLReader_parseURL(reader, testXmlW); EXPECT_HR(hr, S_FALSE); } ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content callback ret values", FALSE); DeleteFileA(testXmlA); /* parse from IXMLDOMDocument */ hr = CoCreateInstance(&CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void**)&doc); EXPECT_HR(hr, S_OK); str = SysAllocString(szSimpleXML); hr = IXMLDOMDocument_loadXML(doc, str, &v); EXPECT_HR(hr, S_OK); SysFreeString(str); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)doc; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) test_seq = content_handler_test2_alternate; else test_seq = content_handler_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "parse from IXMLDOMDocument", FALSE); IXMLDOMDocument_Release(doc); /* xml:space test */ if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { test_seq = xmlspaceattr_test_alternate; } else test_seq = xmlspaceattr_test; set_expected_seq(test_seq); V_VT(&var) = VT_BSTR; V_BSTR(&var) = _bstr_(xmlspace_attr); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", TRUE); } else ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "xml:space handling", FALSE); /* switch off 'namespaces' feature */ hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_FALSE); EXPECT_HR(hr, S_OK); stream = create_test_stream(test_attributes, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { test_seq = content_handler_test_attributes_alt_no_ns; } else test_seq = content_handler_test_attributes; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", TRUE); IStream_Release(stream); hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespaces"), VARIANT_TRUE); EXPECT_HR(hr, S_OK); /* switch off 'namespace-prefixes' feature */ hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_FALSE); EXPECT_HR(hr, S_OK); stream = create_test_stream(test_attributes, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { test_seq = content_handler_test_attributes_alt_no_prefix; } else test_seq = content_handler_test_attributes_no_prefix; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "content test attributes", FALSE); IStream_Release(stream); hr = ISAXXMLReader_putFeature(reader, _bstr_("http://xml.org/sax/features/namespace-prefixes"), VARIANT_TRUE); EXPECT_HR(hr, S_OK); /* attribute normalization */ stream = create_test_stream(attribute_normalize, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60)) { test_seq = attribute_norm_alt; } else test_seq = attribute_norm; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); EXPECT_HR(hr, S_OK); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, "attribute value normalization", TRUE); IStream_Release(stream); resolver = (void*)0xdeadbeef; hr = ISAXXMLReader_getEntityResolver(reader, &resolver); ok(hr == S_OK, "got 0x%08x\n", hr); ok(resolver == NULL, "got %p\n", resolver); hr = ISAXXMLReader_putEntityResolver(reader, NULL); ok(hr == S_OK || broken(hr == E_FAIL), "got 0x%08x\n", hr); /* CDATA sections */ init_saxlexicalhandler(&lexicalhandler, S_OK); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface; hr = ISAXXMLReader_putProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), var); ok(hr == S_OK, "got 0x%08x\n", hr); stream = create_test_stream(test_cdata_xml, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = cdata_test_alt; else test_seq = cdata_test; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "got 0x%08x\n", hr); sprintf(seqname, "%s: cdata test", table->name); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); IStream_Release(stream); /* 2. CDATA sections */ stream = create_test_stream(test2_cdata_xml, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = cdata_test2_alt; else test_seq = cdata_test2; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "got 0x%08x\n", hr); sprintf(seqname, "%s: cdata test 2", table->name); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); IStream_Release(stream); /* 3. CDATA sections */ stream = create_test_stream(test3_cdata_xml, -1); V_VT(&var) = VT_UNKNOWN; V_UNKNOWN(&var) = (IUnknown*)stream; if (IsEqualGUID(table->clsid, &CLSID_SAXXMLReader60) || IsEqualGUID(table->clsid, &CLSID_SAXXMLReader40)) test_seq = cdata_test3_alt; else test_seq = cdata_test3; set_expected_seq(test_seq); hr = ISAXXMLReader_parse(reader, var); ok(hr == S_OK, "got 0x%08x\n", hr); sprintf(seqname, "%s: cdata test 3", table->name); ok_sequence(sequences, CONTENT_HANDLER_INDEX, test_seq, seqname, TRUE); IStream_Release(stream); ISAXXMLReader_Release(reader); table++; } free_bstrs(); } struct saxreader_props_test_t { const char *prop_name; IUnknown *iface; }; static const struct saxreader_props_test_t props_test_data[] = { { "http://xml.org/sax/properties/lexical-handler", (IUnknown*)&lexicalhandler.ISAXLexicalHandler_iface }, { "http://xml.org/sax/properties/declaration-handler", (IUnknown*)&declhandler.ISAXDeclHandler_iface }, { 0 } }; static void test_saxreader_properties(void) { const struct saxreader_props_test_t *ptr = props_test_data; ISAXXMLReader *reader; HRESULT hr; VARIANT v; BSTR str; hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); EXPECT_HR(hr, S_OK); hr = ISAXXMLReader_getProperty(reader, _bstr_("http://xml.org/sax/properties/lexical-handler"), NULL); EXPECT_HR(hr, E_POINTER); while (ptr->prop_name) { VARIANT varref; LONG ref; init_saxlexicalhandler(&lexicalhandler, S_OK); init_saxdeclhandler(&declhandler, S_OK); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); /* VT_UNKNOWN */ V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = ptr->iface; ref = get_refcount(ptr->iface); hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n"); /* VT_DISPATCH */ V_VT(&v) = VT_DISPATCH; V_UNKNOWN(&v) = ptr->iface; ref = get_refcount(ptr->iface); hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref); /* VT_VARIANT|VT_BYREF with VT_UNKNOWN in referenced variant */ V_VT(&varref) = VT_UNKNOWN; V_UNKNOWN(&varref) = ptr->iface; V_VT(&v) = VT_VARIANT|VT_BYREF; V_VARIANTREF(&v) = &varref; ref = get_refcount(ptr->iface); hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref); /* VT_VARIANT|VT_BYREF with VT_DISPATCH in referenced variant */ V_VT(&varref) = VT_DISPATCH; V_UNKNOWN(&varref) = ptr->iface; V_VT(&v) = VT_VARIANT|VT_BYREF; V_VARIANTREF(&v) = &varref; ref = get_refcount(ptr->iface); hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); ok(ref == get_refcount(ptr->iface), "got wrong refcount %d, expected %d\n", get_refcount(ptr->iface), ref); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; ref = get_refcount(ptr->iface); hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v)); ok(ref < get_refcount(ptr->iface), "expected inreased refcount\n"); VariantClear(&v); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = ptr->iface; hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); /* only VT_EMPTY seems to be valid to reset property */ V_VT(&v) = VT_I4; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, E_INVALIDARG); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == ptr->iface, "got %p\n", V_UNKNOWN(&v)); VariantClear(&v); V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = NULL; hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) == NULL, "got %p\n", V_UNKNOWN(&v)); /* block QueryInterface on handler riid */ V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = ptr->iface; hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, S_OK); init_saxlexicalhandler(&lexicalhandler, E_NOINTERFACE); init_saxdeclhandler(&declhandler, E_NOINTERFACE); V_VT(&v) = VT_UNKNOWN; V_UNKNOWN(&v) = ptr->iface; EXPECT_REF(ptr->iface, 1); ref = get_refcount(ptr->iface); hr = ISAXXMLReader_putProperty(reader, _bstr_(ptr->prop_name), v); EXPECT_HR(hr, E_NOINTERFACE); EXPECT_REF(ptr->iface, 1); V_VT(&v) = VT_EMPTY; V_UNKNOWN(&v) = (IUnknown*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_(ptr->prop_name), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_UNKNOWN, "got %d\n", V_VT(&v)); ok(V_UNKNOWN(&v) != NULL, "got %p\n", V_UNKNOWN(&v)); ptr++; free_bstrs(); } ISAXXMLReader_Release(reader); if (!is_clsid_supported(&CLSID_SAXXMLReader40, reader_support_data)) return; hr = CoCreateInstance(&CLSID_SAXXMLReader40, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); EXPECT_HR(hr, S_OK); /* xmldecl-version property */ V_VT(&v) = VT_EMPTY; V_BSTR(&v) = (void*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); /* stream without declaration */ V_VT(&v) = VT_BSTR; V_BSTR(&v) = _bstr_(""); hr = ISAXXMLReader_parse(reader, v); EXPECT_HR(hr, S_OK); V_VT(&v) = VT_EMPTY; V_BSTR(&v) = (void*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); ok(V_BSTR(&v) == NULL, "got %s\n", wine_dbgstr_w(V_BSTR(&v))); /* stream with declaration */ V_VT(&v) = VT_BSTR; V_BSTR(&v) = _bstr_(""); hr = ISAXXMLReader_parse(reader, v); EXPECT_HR(hr, S_OK); /* VT_BSTR|VT_BYREF input type */ str = _bstr_(""); V_VT(&v) = VT_BSTR|VT_BYREF; V_BSTRREF(&v) = &str; hr = ISAXXMLReader_parse(reader, v); EXPECT_HR(hr, S_OK); V_VT(&v) = VT_EMPTY; V_BSTR(&v) = (void*)0xdeadbeef; hr = ISAXXMLReader_getProperty(reader, _bstr_("xmldecl-version"), &v); EXPECT_HR(hr, S_OK); ok(V_VT(&v) == VT_BSTR, "got %d\n", V_VT(&v)); ok(!lstrcmpW(V_BSTR(&v), _bstr_("1.0")), "got %s\n", wine_dbgstr_w(V_BSTR(&v))); VariantClear(&v); ISAXXMLReader_Release(reader); free_bstrs(); } struct feature_ns_entry_t { const GUID *guid; const char *clsid; VARIANT_BOOL value; VARIANT_BOOL value2; /* feature value after feature set to 0xc */ }; static const struct feature_ns_entry_t feature_ns_entry_data[] = { { &CLSID_SAXXMLReader, "CLSID_SAXXMLReader", VARIANT_TRUE, VARIANT_FALSE }, { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", VARIANT_TRUE, VARIANT_FALSE }, { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", VARIANT_TRUE, VARIANT_TRUE }, { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", VARIANT_TRUE, VARIANT_TRUE }, { 0 } }; static const char *feature_names[] = { "http://xml.org/sax/features/namespaces", "http://xml.org/sax/features/namespace-prefixes", 0 }; static void test_saxreader_features(void) { const struct feature_ns_entry_t *entry = feature_ns_entry_data; ISAXXMLReader *reader; while (entry->guid) { VARIANT_BOOL value; const char **name; HRESULT hr; hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); if (hr != S_OK) { win_skip("can't create %s instance\n", entry->clsid); entry++; continue; } if (IsEqualGUID(entry->guid, &CLSID_SAXXMLReader40) || IsEqualGUID(entry->guid, &CLSID_SAXXMLReader60)) { value = VARIANT_TRUE; hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value); ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr); ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE); ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr); value = VARIANT_TRUE; hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value); ok(hr == S_OK, "Failed to get feature value, hr %#x.\n", hr); ok(value == VARIANT_FALSE, "Unexpected default feature value.\n"); hr = ISAXXMLReader_putFeature(reader, _bstr_("exhaustive-errors"), VARIANT_FALSE); ok(hr == S_OK, "Failed to put feature value, hr %#x.\n", hr); } else { value = 123; hr = ISAXXMLReader_getFeature(reader, _bstr_("exhaustive-errors"), &value); ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr); ok(value == 123, "Unexpected value %d.\n", value); value = 123; hr = ISAXXMLReader_getFeature(reader, _bstr_("schema-validation"), &value); ok(hr == E_INVALIDARG, "Failed to get feature value, hr %#x.\n", hr); ok(value == 123, "Unexpected value %d.\n", value); } name = feature_names; while (*name) { value = 0xc; hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value); EXPECT_HR(hr, S_OK); ok(entry->value == value, "%s: got wrong default value %x, expected %x\n", entry->clsid, value, entry->value); value = 0xc; hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), value); EXPECT_HR(hr, S_OK); value = 0xd; hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value); EXPECT_HR(hr, S_OK); ok(entry->value2 == value, "%s: got wrong value %x, expected %x\n", entry->clsid, value, entry->value2); hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_FALSE); EXPECT_HR(hr, S_OK); value = 0xd; hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value); EXPECT_HR(hr, S_OK); ok(value == VARIANT_FALSE, "%s: got wrong value %x, expected VARIANT_FALSE\n", entry->clsid, value); hr = ISAXXMLReader_putFeature(reader, _bstr_(*name), VARIANT_TRUE); EXPECT_HR(hr, S_OK); value = 0xd; hr = ISAXXMLReader_getFeature(reader, _bstr_(*name), &value); EXPECT_HR(hr, S_OK); ok(value == VARIANT_TRUE, "%s: got wrong value %x, expected VARIANT_TRUE\n", entry->clsid, value); name++; } ISAXXMLReader_Release(reader); entry++; } } /* UTF-8 data with UTF-8 BOM and UTF-16 in prolog */ static const CHAR UTF8BOMTest[] = "\xEF\xBB\xBF\n" "\n"; struct enc_test_entry_t { const GUID *guid; const char *clsid; const char *data; HRESULT hr; BOOL todo; }; static const struct enc_test_entry_t encoding_test_data[] = { { &CLSID_SAXXMLReader, "CLSID_SAXXMLReader", UTF8BOMTest, 0xc00ce56f, TRUE }, { &CLSID_SAXXMLReader30, "CLSID_SAXXMLReader30", UTF8BOMTest, 0xc00ce56f, TRUE }, { &CLSID_SAXXMLReader40, "CLSID_SAXXMLReader40", UTF8BOMTest, S_OK, FALSE }, { &CLSID_SAXXMLReader60, "CLSID_SAXXMLReader60", UTF8BOMTest, S_OK, FALSE }, { 0 } }; static void test_saxreader_encoding(void) { const struct enc_test_entry_t *entry = encoding_test_data; static const WCHAR testXmlW[] = {'t','e','s','t','.','x','m','l',0}; static const CHAR testXmlA[] = "test.xml"; while (entry->guid) { ISAXXMLReader *reader; VARIANT input; DWORD written; HANDLE file; HRESULT hr; hr = CoCreateInstance(entry->guid, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); if (hr != S_OK) { win_skip("can't create %s instance\n", entry->clsid); entry++; continue; } file = CreateFileA(testXmlA, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); ok(file != INVALID_HANDLE_VALUE, "Could not create file: %u\n", GetLastError()); WriteFile(file, UTF8BOMTest, sizeof(UTF8BOMTest)-1, &written, NULL); CloseHandle(file); hr = ISAXXMLReader_parseURL(reader, testXmlW); todo_wine_if(entry->todo) ok(hr == entry->hr, "Expected 0x%08x, got 0x%08x. CLSID %s\n", entry->hr, hr, entry->clsid); DeleteFileA(testXmlA); /* try BSTR input with no BOM or '"); hr = ISAXXMLReader_parse(reader, input); EXPECT_HR(hr, S_OK); ISAXXMLReader_Release(reader); free_bstrs(); entry++; } } static void test_mxwriter_handlers(void) { IMXWriter *writer; HRESULT hr; int i; static REFIID riids[] = { &IID_ISAXContentHandler, &IID_ISAXLexicalHandler, &IID_ISAXDeclHandler, &IID_ISAXDTDHandler, &IID_ISAXErrorHandler, &IID_IVBSAXDeclHandler, &IID_IVBSAXLexicalHandler, &IID_IVBSAXContentHandler, &IID_IVBSAXDTDHandler, &IID_IVBSAXErrorHandler }; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); EXPECT_REF(writer, 1); for (i = 0; i < ARRAY_SIZE(riids); i++) { IUnknown *handler; IMXWriter *writer2; /* handler from IMXWriter */ hr = IMXWriter_QueryInterface(writer, riids[i], (void**)&handler); ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr); EXPECT_REF(writer, 2); EXPECT_REF(handler, 2); /* IMXWriter from a handler */ hr = IUnknown_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2); ok(hr == S_OK, "%s, expected S_OK, got %08x\n", wine_dbgstr_guid(riids[i]), hr); ok(writer2 == writer, "got %p, expected %p\n", writer2, writer); EXPECT_REF(writer, 3); IMXWriter_Release(writer2); IUnknown_Release(handler); } IMXWriter_Release(writer); } static struct msxmlsupported_data_t mxwriter_support_data[] = { { &CLSID_MXXMLWriter, "MXXMLWriter" }, { &CLSID_MXXMLWriter30, "MXXMLWriter30" }, { &CLSID_MXXMLWriter40, "MXXMLWriter40" }, { &CLSID_MXXMLWriter60, "MXXMLWriter60" }, { NULL } }; static struct msxmlsupported_data_t mxattributes_support_data[] = { { &CLSID_SAXAttributes, "SAXAttributes" }, { &CLSID_SAXAttributes30, "SAXAttributes30" }, { &CLSID_SAXAttributes40, "SAXAttributes40" }, { &CLSID_SAXAttributes60, "SAXAttributes60" }, { NULL } }; struct mxwriter_props_t { const GUID *clsid; VARIANT_BOOL bom; VARIANT_BOOL disable_escape; VARIANT_BOOL indent; VARIANT_BOOL omitdecl; VARIANT_BOOL standalone; const char *encoding; }; static const struct mxwriter_props_t mxwriter_default_props[] = { { &CLSID_MXXMLWriter, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" }, { &CLSID_MXXMLWriter30, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" }, { &CLSID_MXXMLWriter40, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" }, { &CLSID_MXXMLWriter60, VARIANT_TRUE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, VARIANT_FALSE, "UTF-16" }, { NULL } }; static void test_mxwriter_default_properties(const struct mxwriter_props_t *table) { int i = 0; while (table->clsid) { IMXWriter *writer; VARIANT_BOOL b; BSTR encoding; HRESULT hr; if (!is_clsid_supported(table->clsid, mxwriter_support_data)) { table++; i++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); b = !table->bom; hr = IMXWriter_get_byteOrderMark(writer, &b); EXPECT_HR(hr, S_OK); ok(table->bom == b, "test %d: got BOM %d, expected %d\n", i, b, table->bom); b = !table->disable_escape; hr = IMXWriter_get_disableOutputEscaping(writer, &b); EXPECT_HR(hr, S_OK); ok(table->disable_escape == b, "test %d: got disable escape %d, expected %d\n", i, b, table->disable_escape); b = !table->indent; hr = IMXWriter_get_indent(writer, &b); EXPECT_HR(hr, S_OK); ok(table->indent == b, "test %d: got indent %d, expected %d\n", i, b, table->indent); b = !table->omitdecl; hr = IMXWriter_get_omitXMLDeclaration(writer, &b); EXPECT_HR(hr, S_OK); ok(table->omitdecl == b, "test %d: got omitdecl %d, expected %d\n", i, b, table->omitdecl); b = !table->standalone; hr = IMXWriter_get_standalone(writer, &b); EXPECT_HR(hr, S_OK); ok(table->standalone == b, "test %d: got standalone %d, expected %d\n", i, b, table->standalone); hr = IMXWriter_get_encoding(writer, &encoding); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(encoding, _bstr_(table->encoding)), "test %d: got encoding %s, expected %s\n", i, wine_dbgstr_w(encoding), table->encoding); SysFreeString(encoding); IMXWriter_Release(writer); table++; i++; } } static void test_mxwriter_properties(void) { static const WCHAR utf16W[] = {'U','T','F','-','1','6',0}; static const WCHAR testW[] = {'t','e','s','t',0}; ISAXContentHandler *content; IMXWriter *writer; VARIANT_BOOL b; HRESULT hr; BSTR str, str2; VARIANT dest; test_mxwriter_default_properties(mxwriter_default_props); hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = IMXWriter_get_disableOutputEscaping(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); hr = IMXWriter_get_byteOrderMark(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); hr = IMXWriter_get_indent(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); hr = IMXWriter_get_omitXMLDeclaration(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); hr = IMXWriter_get_standalone(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); /* set and check */ hr = IMXWriter_put_standalone(writer, VARIANT_TRUE); ok(hr == S_OK, "got %08x\n", hr); b = VARIANT_FALSE; hr = IMXWriter_get_standalone(writer, &b); ok(hr == S_OK, "got %08x\n", hr); ok(b == VARIANT_TRUE, "got %d\n", b); hr = IMXWriter_get_encoding(writer, NULL); EXPECT_HR(hr, E_POINTER); /* UTF-16 is a default setting apparently */ str = (void*)0xdeadbeef; hr = IMXWriter_get_encoding(writer, &str); EXPECT_HR(hr, S_OK); ok(lstrcmpW(str, utf16W) == 0, "expected empty string, got %s\n", wine_dbgstr_w(str)); str2 = (void*)0xdeadbeef; hr = IMXWriter_get_encoding(writer, &str2); ok(hr == S_OK, "got %08x\n", hr); ok(str != str2, "expected newly allocated, got same %p\n", str); SysFreeString(str2); SysFreeString(str); /* put empty string */ str = SysAllocString(emptyW); hr = IMXWriter_put_encoding(writer, str); ok(hr == E_INVALIDARG, "got %08x\n", hr); SysFreeString(str); str = (void*)0xdeadbeef; hr = IMXWriter_get_encoding(writer, &str); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(str, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); /* invalid encoding name */ str = SysAllocString(testW); hr = IMXWriter_put_encoding(writer, str); ok(hr == E_INVALIDARG, "got %08x\n", hr); SysFreeString(str); /* test case sensivity */ hr = IMXWriter_put_encoding(writer, _bstr_("utf-8")); EXPECT_HR(hr, S_OK); str = (void*)0xdeadbeef; hr = IMXWriter_get_encoding(writer, &str); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(str, _bstr_("utf-8")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16")); EXPECT_HR(hr, S_OK); str = (void*)0xdeadbeef; hr = IMXWriter_get_encoding(writer, &str); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(str, _bstr_("uTf-16")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); /* how it affects document creation */ hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); hr = IMXWriter_get_version(writer, NULL); ok(hr == E_POINTER, "got %08x\n", hr); /* default version is 'surprisingly' 1.0 */ hr = IMXWriter_get_version(writer, &str); ok(hr == S_OK, "got %08x\n", hr); ok(!lstrcmpW(str, _bstr_("1.0")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); /* store version string as is */ hr = IMXWriter_put_version(writer, NULL); ok(hr == E_INVALIDARG, "got %08x\n", hr); hr = IMXWriter_put_version(writer, _bstr_("1.0")); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_put_version(writer, _bstr_("")); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_get_version(writer, &str); ok(hr == S_OK, "got %08x\n", hr); ok(!lstrcmpW(str, _bstr_("")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); hr = IMXWriter_put_version(writer, _bstr_("a.b")); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_get_version(writer, &str); ok(hr == S_OK, "got %08x\n", hr); ok(!lstrcmpW(str, _bstr_("a.b")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); hr = IMXWriter_put_version(writer, _bstr_("2.0")); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_get_version(writer, &str); ok(hr == S_OK, "got %08x\n", hr); ok(!lstrcmpW(str, _bstr_("2.0")), "got %s\n", wine_dbgstr_w(str)); SysFreeString(str); IMXWriter_Release(writer); free_bstrs(); } static void test_mxwriter_flush(void) { ISAXContentHandler *content; IMXWriter *writer; LARGE_INTEGER pos; ULARGE_INTEGER pos2; IStream *stream; VARIANT dest; HRESULT hr; char *buff; LONG ref; int len; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); EXPECT_REF(stream, 1); /* detach when nothing was attached */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); /* attach stream */ V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); todo_wine EXPECT_REF(stream, 3); /* detach setting VT_EMPTY destination */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); EXPECT_REF(stream, 1); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); /* flush() doesn't detach a stream */ hr = IMXWriter_flush(writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); todo_wine EXPECT_REF(stream, 3); pos.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); ok(pos2.QuadPart == 0, "expected stream beginning\n"); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); pos.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); ok(pos2.QuadPart != 0, "expected stream beginning\n"); /* already started */ hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "got %08x\n", hr); /* flushed on endDocument() */ pos.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); ok(pos2.QuadPart != 0, "expected stream position moved\n"); IStream_Release(stream); /* auto-flush feature */ hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); EXPECT_HR(hr, S_OK); EXPECT_REF(stream, 1); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL); EXPECT_HR(hr, S_OK); /* internal buffer is flushed automatically on certain threshold */ pos.QuadPart = 0; pos2.QuadPart = 1; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); EXPECT_HR(hr, S_OK); ok(pos2.QuadPart == 0, "expected stream beginning\n"); len = 2048; buff = heap_alloc(len + 1); memset(buff, 'A', len); buff[len] = 0; hr = ISAXContentHandler_characters(content, _bstr_(buff), len); EXPECT_HR(hr, S_OK); pos.QuadPart = 0; pos2.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); EXPECT_HR(hr, S_OK); ok(pos2.QuadPart != 0, "unexpected stream beginning\n"); hr = IMXWriter_get_output(writer, NULL); EXPECT_HR(hr, E_POINTER); ref = get_refcount(stream); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_UNKNOWN, "got vt type %d\n", V_VT(&dest)); ok(V_UNKNOWN(&dest) == (IUnknown*)stream, "got pointer %p\n", V_UNKNOWN(&dest)); ok(ref+1 == get_refcount(stream), "expected increased refcount\n"); VariantClear(&dest); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); IStream_Release(stream); /* test char count lower than threshold */ hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); EXPECT_HR(hr, S_OK); EXPECT_REF(stream, 1); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL); EXPECT_HR(hr, S_OK); pos.QuadPart = 0; pos2.QuadPart = 1; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); EXPECT_HR(hr, S_OK); ok(pos2.QuadPart == 0, "expected stream beginning\n"); memset(buff, 'A', len); buff[len] = 0; hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8); EXPECT_HR(hr, S_OK); pos.QuadPart = 0; pos2.QuadPart = 1; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); EXPECT_HR(hr, S_OK); ok(pos2.QuadPart == 0, "expected stream beginning\n"); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); /* test auto-flush function when stream is not set */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL); EXPECT_HR(hr, S_OK); memset(buff, 'A', len); buff[len] = 0; hr = ISAXContentHandler_characters(content, _bstr_(buff), len); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); len += strlen(""); ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len); VariantClear(&dest); heap_free(buff); ISAXContentHandler_Release(content); IStream_Release(stream); IMXWriter_Release(writer); free_bstrs(); } static void test_mxwriter_startenddocument(void) { ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "got %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* now try another startDocument */ hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); /* and get duplicated prolog */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n" "\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); /* now with omitted declaration */ hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "got %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); free_bstrs(); } enum startendtype { StartElement = 0x001, EndElement = 0x010, StartEndElement = 0x011, DisableEscaping = 0x100 }; struct writer_startendelement_t { const GUID *clsid; enum startendtype type; const char *uri; const char *local_name; const char *qname; const char *output; HRESULT hr; ISAXAttributes *attr; }; static const char startelement_xml[] = ""; static const char startendelement_xml[] = ""; static const char startendelement_noescape_xml[] = "\'\"/>"; static const struct writer_startendelement_t writer_startendelement[] = { /* 0 */ { &CLSID_MXXMLWriter, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, NULL, "<>", S_OK }, { &CLSID_MXXMLWriter, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, /* 5 */ { &CLSID_MXXMLWriter30, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, StartElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, StartElement, "uri", NULL, NULL, "<>", S_OK }, { &CLSID_MXXMLWriter, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG }, /* 10 */ { &CLSID_MXXMLWriter40, StartElement, NULL, "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, StartElement, NULL, "local", NULL, "<>", S_OK }, { &CLSID_MXXMLWriter, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, StartElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, /* 15 */ { &CLSID_MXXMLWriter60, StartElement, NULL, NULL, "qname", "", S_OK }, { &CLSID_MXXMLWriter, StartElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "qname", "", S_OK }, /* 20 */ { &CLSID_MXXMLWriter, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, StartElement, "uri", "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, StartElement, "uri", "local", NULL, "<>", S_OK }, { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local", "", S_OK }, /* 25 */ { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local2", "", S_OK }, { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local2", "", S_OK }, /* 30 */ { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local2", "", S_OK }, { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local2", "", S_OK }, /* endElement tests */ { &CLSID_MXXMLWriter, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, NULL, NULL, E_INVALIDARG }, /* 35 */ { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, NULL, "", S_OK }, { &CLSID_MXXMLWriter, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, EndElement, "uri", NULL, NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, EndElement, "uri", NULL, NULL, "", S_OK }, /* 40 */ { &CLSID_MXXMLWriter, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, EndElement, NULL, "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, EndElement, NULL, "local", NULL, "", S_OK }, { &CLSID_MXXMLWriter, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, /* 45 */ { &CLSID_MXXMLWriter30, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, EndElement, NULL, NULL, "qname", NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter60, EndElement, NULL, NULL, "qname", "", S_OK }, { &CLSID_MXXMLWriter, EndElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "qname", "", S_OK }, /* 50 */ { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "qname", "", S_OK }, { &CLSID_MXXMLWriter, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter30, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG }, { &CLSID_MXXMLWriter40, EndElement, "uri", "local", NULL, NULL, E_INVALIDARG }, /* 55 */ { &CLSID_MXXMLWriter60, EndElement, "uri", "local", NULL, "", S_OK }, { &CLSID_MXXMLWriter, EndElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local", "", S_OK }, { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local", "", S_OK }, /* 60 */ { &CLSID_MXXMLWriter, EndElement, "uri", "local", "uri:local2", "", S_OK }, { &CLSID_MXXMLWriter30, EndElement, "uri", "local", "uri:local2", "", S_OK }, { &CLSID_MXXMLWriter40, EndElement, "uri", "local", "uri:local2", "", S_OK }, { &CLSID_MXXMLWriter60, EndElement, "uri", "local", "uri:local2", "", S_OK }, /* with attributes */ { &CLSID_MXXMLWriter, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes }, /* 65 */ { &CLSID_MXXMLWriter30, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter40, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter60, StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes }, /* empty elements */ { &CLSID_MXXMLWriter, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter30, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, /* 70 */ { &CLSID_MXXMLWriter40, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter60, StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter, StartEndElement, "", "", "", "", S_OK }, { &CLSID_MXXMLWriter30, StartEndElement, "", "", "", "", S_OK }, { &CLSID_MXXMLWriter40, StartEndElement, "", "", "", "", S_OK }, /* 75 */ { &CLSID_MXXMLWriter60, StartEndElement, "", "", "", "", S_OK }, /* with disabled output escaping */ { &CLSID_MXXMLWriter, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter30, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_noescape_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter40, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, { &CLSID_MXXMLWriter60, StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, { NULL } }; static void get_class_support_data(struct msxmlsupported_data_t *table, REFIID riid) { while (table->clsid) { IUnknown *unk; HRESULT hr; hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, riid, (void**)&unk); if (hr == S_OK) IUnknown_Release(unk); table->supported = hr == S_OK; if (hr != S_OK) win_skip("class %s not supported\n", table->name); table++; } } static void test_mxwriter_startendelement_batch(const struct writer_startendelement_t *table) { int i = 0; while (table->clsid) { ISAXContentHandler *content; IMXWriter *writer; HRESULT hr; if (!is_clsid_supported(table->clsid, mxwriter_support_data)) { table++; i++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); if (table->type & DisableEscaping) { hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); } if (table->type & StartElement) { hr = ISAXContentHandler_startElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0, _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname), table->qname ? strlen(table->qname) : 0, table->attr); ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr); } if (table->type & EndElement) { hr = ISAXContentHandler_endElement(content, _bstr_(table->uri), table->uri ? strlen(table->uri) : 0, _bstr_(table->local_name), table->local_name ? strlen(table->local_name) : 0, _bstr_(table->qname), table->qname ? strlen(table->qname) : 0); ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr); } /* test output */ if (hr == S_OK) { VARIANT dest; V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)), "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output); VariantClear(&dest); } ISAXContentHandler_Release(content); IMXWriter_Release(writer); table++; i++; } free_bstrs(); } /* point of these test is to start/end element with different names and name lengths */ struct writer_startendelement2_t { const GUID *clsid; const char *qnamestart; int qnamestart_len; const char *qnameend; int qnameend_len; const char *output; HRESULT hr; }; static const struct writer_startendelement2_t writer_startendelement2[] = { { &CLSID_MXXMLWriter, "a", -1, "b", -1, "", S_OK }, { &CLSID_MXXMLWriter30, "a", -1, "b", -1, "", S_OK }, { &CLSID_MXXMLWriter40, "a", -1, "b", -1, "", S_OK }, /* -1 length is not allowed for version 6 */ { &CLSID_MXXMLWriter60, "a", -1, "b", -1, "", E_INVALIDARG }, { &CLSID_MXXMLWriter, "a", 1, "b", 1, "", S_OK }, { &CLSID_MXXMLWriter30, "a", 1, "b", 1, "", S_OK }, { &CLSID_MXXMLWriter40, "a", 1, "b", 1, "", S_OK }, { &CLSID_MXXMLWriter60, "a", 1, "b", 1, "", S_OK }, { NULL } }; static void test_mxwriter_startendelement_batch2(const struct writer_startendelement2_t *table) { int i = 0; while (table->clsid) { ISAXContentHandler *content; IMXWriter *writer; HRESULT hr; if (!is_clsid_supported(table->clsid, mxwriter_support_data)) { table++; i++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_(table->qnamestart), table->qnamestart_len, NULL); ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr); hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_(table->qnameend), table->qnameend_len); ok(hr == table->hr, "test %d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr); /* test output */ if (hr == S_OK) { VARIANT dest; V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)), "test %d: got wrong content %s, expected %s\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output); VariantClear(&dest); } ISAXContentHandler_Release(content); IMXWriter_Release(writer); table++; i++; free_bstrs(); } } static void test_mxwriter_startendelement(void) { ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; test_mxwriter_startendelement_batch(writer_startendelement); test_mxwriter_startendelement_batch2(writer_startendelement2); hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); /* all string pointers should be not null */ hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_("b"), 1, _bstr_(""), 0, NULL); ok(hr == S_OK, "got %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1, NULL); ok(hr == S_OK, "got %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_endElement(content, NULL, 0, NULL, 0, _bstr_("a:b"), 3); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3); EXPECT_HR(hr, E_INVALIDARG); /* only local name is an error too */ hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("<>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); IMXWriter_flush(writer); hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); /* length -1 */ hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); free_bstrs(); } struct writer_characters_t { const GUID *clsid; const char *data; const char *output; }; static const struct writer_characters_t writer_characters[] = { { &CLSID_MXXMLWriter, "< > & \" \'", "< > & \" \'" }, { &CLSID_MXXMLWriter30, "< > & \" \'", "< > & \" \'" }, { &CLSID_MXXMLWriter40, "< > & \" \'", "< > & \" \'" }, { &CLSID_MXXMLWriter60, "< > & \" \'", "< > & \" \'" }, { NULL } }; static void test_mxwriter_characters(void) { static const WCHAR chardataW[] = {'T','E','S','T','C','H','A','R','D','A','T','A',' ','.',0}; const struct writer_characters_t *table = writer_characters; ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; int i = 0; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_characters(content, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXContentHandler_characters(content, chardataW, 0); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_characters(content, chardataW, ARRAY_SIZE(chardataW) - 1); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("TESTCHARDATA ."), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); ISAXContentHandler_Release(content); IMXWriter_Release(writer); /* try empty characters data to see if element is closed */ hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_characters(content, chardataW, 0); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(""), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); /* batch tests */ while (table->clsid) { ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; if (!is_clsid_supported(table->clsid, mxwriter_support_data)) { table++; i++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data)); EXPECT_HR(hr, S_OK); /* test output */ if (hr == S_OK) { V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(table->output), V_BSTR(&dest)), "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->output); VariantClear(&dest); } /* with disabled escaping */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data)); EXPECT_HR(hr, S_OK); /* test output */ if (hr == S_OK) { V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(table->data), V_BSTR(&dest)), "test %d: got wrong content %s, expected \"%s\"\n", i, wine_dbgstr_w(V_BSTR(&dest)), table->data); VariantClear(&dest); } ISAXContentHandler_Release(content); IMXWriter_Release(writer); table++; i++; } free_bstrs(); } static const mxwriter_stream_test mxwriter_stream_tests[] = { { VARIANT_TRUE,"UTF-16", { {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE}, {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)}, {TRUE} } }, { VARIANT_FALSE,"UTF-16", { {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)}, {TRUE} } }, { VARIANT_TRUE,"UTF-8", { {FALSE,(const BYTE*)szUtf8XML,sizeof(szUtf8XML)-1}, /* For some reason Windows makes an empty write call when UTF-8 encoding is used * and the writer is released. */ {FALSE,NULL,0}, {TRUE} } }, { VARIANT_TRUE,"utf-8", { {FALSE,(const BYTE*)utf8xml2,sizeof(utf8xml2)-1}, /* For some reason Windows makes an empty write call when UTF-8 encoding is used * and the writer is released. */ {FALSE,NULL,0}, {TRUE} } }, { VARIANT_TRUE,"UTF-16", { {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE}, {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)}, {TRUE} } }, { VARIANT_TRUE,"UTF-16", { {FALSE,(const BYTE*)szUtf16BOM,sizeof(szUtf16BOM),TRUE,TRUE}, {FALSE,(const BYTE*)szUtf16XML,sizeof(szUtf16XML)}, {TRUE} } } }; static void test_mxwriter_stream(void) { IMXWriter *writer; ISAXContentHandler *content; HRESULT hr; VARIANT dest; IStream *stream; LARGE_INTEGER pos; ULARGE_INTEGER pos2; DWORD test_count = ARRAY_SIZE(mxwriter_stream_tests); for(current_stream_test_index = 0; current_stream_test_index < test_count; ++current_stream_test_index) { const mxwriter_stream_test *test = mxwriter_stream_tests+current_stream_test_index; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr); hr = IMXWriter_put_encoding(writer, _bstr_(test->encoding)); ok(hr == S_OK, "put_encoding failed with %08x on test %d\n", hr, current_stream_test_index); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)&mxstream; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "put_output failed with %08x on test %d\n", hr, current_stream_test_index); hr = IMXWriter_put_byteOrderMark(writer, test->bom); ok(hr == S_OK, "put_byteOrderMark failed with %08x on test %d\n", hr, current_stream_test_index); current_write_test = test->expected_writes; hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "startDocument failed with %08x on test %d\n", hr, current_stream_test_index); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "endDocument failed with %08x on test %d\n", hr, current_stream_test_index); ISAXContentHandler_Release(content); IMXWriter_Release(writer); ok(current_write_test->last, "The last %d write calls on test %d were missed\n", (int)(current_write_test-test->expected_writes), current_stream_test_index); } hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "CoCreateInstance failed: %08x\n", hr); hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); ok(hr == S_OK, "CreateStreamOnHGlobal failed: %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "QueryInterface(ISAXContentHandler) failed: %08x\n", hr); hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); ok(hr == S_OK, "put_encoding failed: %08x\n", hr); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "put_output failed: %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "startDocument failed: %08x\n", hr); /* Setting output of the mxwriter causes the current output to be flushed, * and the writer to start over. */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "put_output failed: %08x\n", hr); pos.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); ok(hr == S_OK, "Seek failed: %08x\n", hr); ok(pos2.QuadPart != 0, "expected stream position moved\n"); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "startDocument failed: %08x\n", hr); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "endDocument failed: %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "get_output failed: %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "Got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* test when BOM is written to output stream */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); pos.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_SET, NULL); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_byteOrderMark(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16")); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); pos.QuadPart = 0; pos2.QuadPart = 0; hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); EXPECT_HR(hr, S_OK); ok(pos2.QuadPart == 2, "got wrong position\n"); IStream_Release(stream); ISAXContentHandler_Release(content); IMXWriter_Release(writer); free_bstrs(); } static const char *encoding_names[] = { "iso-8859-1", "iso-8859-2", "iso-8859-3", "iso-8859-4", "iso-8859-5", "iso-8859-7", "iso-8859-9", "iso-8859-13", "iso-8859-15", NULL }; static void test_mxwriter_encoding(void) { ISAXContentHandler *content; IMXWriter *writer; IStream *stream; const char *enc; VARIANT dest; HRESULT hr; HGLOBAL g; char *ptr; BSTR s; int i; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); /* The content is always re-encoded to UTF-16 when the output is * retrieved as a BSTR. */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content: %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* switch encoding when something is written already */ hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); EXPECT_HR(hr, S_OK); /* write empty element */ hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1); EXPECT_HR(hr, S_OK); /* switch */ hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16")); EXPECT_HR(hr, S_OK); hr = IMXWriter_flush(writer); EXPECT_HR(hr, S_OK); hr = GetHGlobalFromStream(stream, &g); EXPECT_HR(hr, S_OK); ptr = GlobalLock(g); ok(!strncmp(ptr, "", 4), "got %c%c%c%c\n", ptr[0],ptr[1],ptr[2],ptr[3]); GlobalUnlock(g); /* so output is unaffected, encoding name is stored however */ hr = IMXWriter_get_encoding(writer, &s); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(s, _bstr_("UTF-16")), "got %s\n", wine_dbgstr_w(s)); SysFreeString(s); IStream_Release(stream); i = 0; enc = encoding_names[i]; while (enc) { char expectedA[200]; hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_UNKNOWN; V_UNKNOWN(&dest) = (IUnknown*)stream; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_encoding(writer, _bstr_(enc)); ok(hr == S_OK || broken(hr != S_OK) /* old win versions do not support certain encodings */, "%s: encoding not accepted\n", enc); if (hr != S_OK) { enc = encoding_names[++i]; IStream_Release(stream); continue; } hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_endDocument(content); EXPECT_HR(hr, S_OK); hr = IMXWriter_flush(writer); EXPECT_HR(hr, S_OK); /* prepare expected string */ *expectedA = 0; strcat(expectedA, "\r\n"); hr = GetHGlobalFromStream(stream, &g); EXPECT_HR(hr, S_OK); ptr = GlobalLock(g); ok(!strncmp(ptr, expectedA, strlen(expectedA)), "%s: got %s, expected %.50s\n", enc, ptr, expectedA); GlobalUnlock(g); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); IStream_Release(stream); enc = encoding_names[++i]; } ISAXContentHandler_Release(content); IMXWriter_Release(writer); free_bstrs(); } static void test_obj_dispex(IUnknown *obj) { static const WCHAR testW[] = {'t','e','s','t','p','r','o','p',0}; static const WCHAR starW[] = {'*',0}; DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE; IDispatchEx *dispex; IUnknown *unk; DWORD props; UINT ticnt; HRESULT hr; BSTR name; DISPID did; hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); EXPECT_HR(hr, S_OK); if (FAILED(hr)) return; ticnt = 0; hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt); EXPECT_HR(hr, S_OK); ok(ticnt == 1, "ticnt=%u\n", ticnt); name = SysAllocString(starW); hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive); EXPECT_HR(hr, E_NOTIMPL); SysFreeString(name); hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid); EXPECT_HR(hr, E_NOTIMPL); props = 0; hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props); EXPECT_HR(hr, E_NOTIMPL); ok(props == 0, "expected 0 got %d\n", props); hr = IDispatchEx_GetMemberName(dispex, dispid, &name); EXPECT_HR(hr, E_NOTIMPL); if (SUCCEEDED(hr)) SysFreeString(name); hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid); EXPECT_HR(hr, E_NOTIMPL); unk = (IUnknown*)0xdeadbeef; hr = IDispatchEx_GetNameSpaceParent(dispex, &unk); EXPECT_HR(hr, E_NOTIMPL); ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk); name = SysAllocString(testW); hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &did); ok(hr == DISP_E_UNKNOWNNAME, "got 0x%08x\n", hr); SysFreeString(name); IDispatchEx_Release(dispex); } static void test_saxreader_dispex(void) { IVBSAXXMLReader *vbreader; ISAXXMLReader *reader; DISPPARAMS dispparams; DISPID dispid; IUnknown *unk; VARIANT arg; HRESULT hr; hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); EXPECT_HR(hr, S_OK); hr = ISAXXMLReader_QueryInterface(reader, &IID_IUnknown, (void**)&unk); EXPECT_HR(hr, S_OK); test_obj_dispex(unk); IUnknown_Release(unk); hr = ISAXXMLReader_QueryInterface(reader, &IID_IVBSAXXMLReader, (void**)&vbreader); EXPECT_HR(hr, S_OK); hr = IVBSAXXMLReader_QueryInterface(vbreader, &IID_IUnknown, (void**)&unk); EXPECT_HR(hr, S_OK); test_obj_dispex(unk); IUnknown_Release(unk); dispid = DISPID_PROPERTYPUT; dispparams.cArgs = 1; dispparams.cNamedArgs = 1; dispparams.rgdispidNamedArgs = &dispid; dispparams.rgvarg = &arg; V_VT(&arg) = VT_DISPATCH; V_DISPATCH(&arg) = NULL; /* propputref is callable as PROPERTYPUT and PROPERTYPUTREF */ hr = IVBSAXXMLReader_Invoke(vbreader, DISPID_SAX_XMLREADER_CONTENTHANDLER, &IID_NULL, 0, DISPATCH_PROPERTYPUT, &dispparams, NULL, NULL, NULL); ok(hr == S_OK, "got 0x%08x\n", hr); hr = IVBSAXXMLReader_Invoke(vbreader, DISPID_SAX_XMLREADER_CONTENTHANDLER, &IID_NULL, 0, DISPATCH_PROPERTYPUTREF, &dispparams, NULL, NULL, NULL); ok(hr == S_OK, "got 0x%08x\n", hr); IVBSAXXMLReader_Release(vbreader); ISAXXMLReader_Release(reader); if (is_clsid_supported(&CLSID_SAXXMLReader60, reader_support_data)) { hr = CoCreateInstance(&CLSID_SAXXMLReader60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk); ok(hr == S_OK, "got 0x%08x\n", hr); test_obj_dispex(unk); IUnknown_Release(unk); } } static void test_mxwriter_dispex(void) { IDispatchEx *dispex; IMXWriter *writer; IUnknown *unk; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex); EXPECT_HR(hr, S_OK); hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk); test_obj_dispex(unk); IUnknown_Release(unk); IDispatchEx_Release(dispex); IMXWriter_Release(writer); if (is_clsid_supported(&CLSID_MXXMLWriter60, mxwriter_support_data)) { hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk); ok(hr == S_OK, "got 0x%08x\n", hr); test_obj_dispex(unk); IUnknown_Release(unk); } } static void test_mxwriter_comment(void) { static const WCHAR commentW[] = {'c','o','m','m','e','n','t',0}; IVBSAXLexicalHandler *vblexical; ISAXContentHandler *content; ISAXLexicalHandler *lexical; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXLexicalHandler_comment(lexical, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = IVBSAXLexicalHandler_comment(vblexical, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXLexicalHandler_comment(lexical, commentW, 0); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXLexicalHandler_comment(lexical, commentW, ARRAY_SIZE(commentW) - 1); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); ISAXLexicalHandler_Release(lexical); IVBSAXLexicalHandler_Release(vblexical); IMXWriter_Release(writer); free_bstrs(); } static void test_mxwriter_cdata(void) { IVBSAXLexicalHandler *vblexical; ISAXContentHandler *content; ISAXLexicalHandler *lexical; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXLexicalHandler_startCDATA(lexical); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(" & \""), 7); EXPECT_HR(hr, S_OK); hr = ISAXLexicalHandler_endCDATA(lexical); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_(" & \"]]>"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); ISAXLexicalHandler_Release(lexical); IVBSAXLexicalHandler_Release(vblexical); IMXWriter_Release(writer); free_bstrs(); } static void test_mxwriter_pi(void) { static const WCHAR targetW[] = {'t','a','r','g','e','t',0}; static const WCHAR dataW[] = {'d','a','t','a',0}; ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXContentHandler_processingInstruction(content, targetW, 0, NULL, 0); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_processingInstruction(content, targetW, 6, NULL, 0); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXContentHandler_processingInstruction(content, targetW, 4, dataW, 4); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n\r\n\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_processingInstruction(content, targetW, 6, dataW, 0); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); } static void test_mxwriter_ignorablespaces(void) { static const WCHAR dataW[] = {'d','a','t','a',0}; ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 0); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 4); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_ignorableWhitespace(content, dataW, 1); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("datad"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); } static void test_mxwriter_dtd(void) { static const WCHAR contentW[] = {'c','o','n','t','e','n','t'}; static const WCHAR nameW[] = {'n','a','m','e'}; static const WCHAR pubW[] = {'p','u','b'}; static const WCHAR sysW[] = {'s','y','s'}; IVBSAXLexicalHandler *vblexical; ISAXContentHandler *content; ISAXLexicalHandler *lexical; IVBSAXDeclHandler *vbdecl; ISAXDeclHandler *decl; ISAXDTDHandler *dtd; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXDeclHandler, (void**)&vbdecl); EXPECT_HR(hr, S_OK); hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); EXPECT_HR(hr, S_OK); hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); EXPECT_HR(hr, S_OK); hr = ISAXContentHandler_startDocument(content); EXPECT_HR(hr, S_OK); hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = IVBSAXLexicalHandler_startDTD(vblexical, NULL, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, sysW, ARRAY_SIZE(sysW)); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, pubW, ARRAY_SIZE(pubW), sysW, ARRAY_SIZE(sysW)); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXLexicalHandler_startDTD(lexical, nameW, ARRAY_SIZE(nameW), NULL, 0, NULL, 0); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n]>\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* element declaration */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = IVBSAXDeclHandler_elementDecl(vbdecl, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, ARRAY_SIZE(contentW)); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXDeclHandler_elementDecl(decl, nameW, ARRAY_SIZE(nameW), contentW, 0); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* attribute declaration */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"), _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"), _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value")); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"), _bstr_("attribute2"), strlen("attribute2"), _bstr_("CDATA"), strlen("CDATA"), _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value2"), strlen("value2")); EXPECT_HR(hr, S_OK); hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"), _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"), _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3")); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n" "\r\n" "\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* internal entities */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); EXPECT_HR(hr, S_OK); hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = IVBSAXDeclHandler_internalEntityDecl(vbdecl, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), -1, NULL, 0); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("value"), strlen("value")); EXPECT_HR(hr, S_OK); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); EXPECT_HR(hr, S_OK); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* external entities */ V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, NULL, 0, NULL, 0, NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = IVBSAXDeclHandler_externalEntityDecl(vbdecl, NULL, NULL, NULL); ok(hr == E_POINTER, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), 0, NULL, 0, NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), -1, NULL, 0, NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), _bstr_("sysid"), strlen("sysid")); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid")); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got 0x%08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_( "\r\n" "\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); /* notation declaration */ hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd); ok(hr == S_OK, "got 0x%08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_put_output(writer, dest); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDTDHandler_notationDecl(dtd, NULL, 0, NULL, 0, NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, NULL, 0); ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), NULL, 0); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), _bstr_("sysid"), strlen("sysid")); ok(hr == S_OK, "got 0x%08x\n", hr); hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid")); ok(hr == S_OK, "got 0x%08x\n", hr); hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got 0x%08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_( "\r\n" "\r\n" "\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXDTDHandler_Release(dtd); ISAXContentHandler_Release(content); ISAXLexicalHandler_Release(lexical); IVBSAXLexicalHandler_Release(vblexical); IVBSAXDeclHandler_Release(vbdecl); ISAXDeclHandler_Release(decl); IMXWriter_Release(writer); free_bstrs(); } typedef struct { const CLSID *clsid; const char *uri; const char *local; const char *qname; const char *type; const char *value; HRESULT hr; } addattribute_test_t; static const addattribute_test_t addattribute_data[] = { { &CLSID_SAXAttributes, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes40, NULL, NULL, "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes60, NULL, NULL, "ns:qname", NULL, "value", S_OK }, { &CLSID_SAXAttributes, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes40, NULL, "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes60, NULL, "qname", "ns:qname", NULL, "value", S_OK }, { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", NULL, "value", E_INVALIDARG }, { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", NULL, "value", S_OK }, { &CLSID_SAXAttributes, "uri", "qname", "ns:qname", "type", "value", S_OK }, { &CLSID_SAXAttributes30, "uri", "qname", "ns:qname", "type", "value", S_OK }, { &CLSID_SAXAttributes40, "uri", "qname", "ns:qname", "type", "value", S_OK }, { &CLSID_SAXAttributes60, "uri", "qname", "ns:qname", "type", "value", S_OK }, { NULL } }; static void test_mxattr_addAttribute(void) { const addattribute_test_t *table = addattribute_data; int i = 0; while (table->clsid) { ISAXAttributes *saxattr; IMXAttributes *mxattr; const WCHAR *value; int len, index; HRESULT hr; if (!is_clsid_supported(table->clsid, mxattributes_support_data)) { table++; i++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, S_OK); hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); EXPECT_HR(hr, S_OK); /* SAXAttributes40 and SAXAttributes60 both crash on this test */ if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) { hr = ISAXAttributes_getLength(saxattr, NULL); EXPECT_HR(hr, E_POINTER); } len = -1; hr = ISAXAttributes_getLength(saxattr, &len); EXPECT_HR(hr, S_OK); ok(len == 0, "got %d\n", len); hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getType(saxattr, 0, &value, &len); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = IMXAttributes_addAttribute(mxattr, _bstr_(table->uri), _bstr_(table->local), _bstr_(table->qname), _bstr_(table->type), _bstr_(table->value)); ok(hr == table->hr, "%d: got 0x%08x, expected 0x%08x\n", i, hr, table->hr); if (hr == S_OK) { /* SAXAttributes40 and SAXAttributes60 both crash on this test */ if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) { hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); } len = -1; hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), table->value); ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); len = -1; value = (void*)0xdeadbeef; hr = ISAXAttributes_getType(saxattr, 0, &value, &len); EXPECT_HR(hr, S_OK); if (table->type) { ok(!lstrcmpW(_bstr_(table->type), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), table->type); ok(lstrlenW(value) == len, "%d: got wrong type value length %d\n", i, len); } else { ok(*value == 0, "%d: got type value %s\n", i, wine_dbgstr_w(value)); ok(len == 0, "%d: got wrong type value length %d\n", i, len); } hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, NULL); if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) { EXPECT_HR(hr, E_POINTER); } else EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index); EXPECT_HR(hr, E_INVALIDARG); index = -1; hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index); EXPECT_HR(hr, E_INVALIDARG); ok(index == -1, "%d: got wrong index %d\n", i, index); index = -1; hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index); EXPECT_HR(hr, E_INVALIDARG); ok(index == -1, "%d: got wrong index %d\n", i, index); index = -1; hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index); EXPECT_HR(hr, S_OK); ok(index == 0, "%d: got wrong index %d\n", i, index); index = -1; hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname)-1, &index); EXPECT_HR(hr, E_INVALIDARG); ok(index == -1, "%d: got wrong index %d\n", i, index); if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes40) || IsEqualGUID(table->clsid, &CLSID_SAXAttributes60)) { hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "got 0x%08x\n", hr); } else { hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); EXPECT_HR(hr, E_POINTER); /* versions 4 and 6 crash */ hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), NULL, &len); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, &value, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, _bstr_(table->local), 0, NULL, &len); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local), strlen(table->local), NULL, NULL); EXPECT_HR(hr, E_POINTER); } hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), table->value); ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); if (table->uri) { hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), strlen(table->uri), _bstr_(table->local), strlen(table->local), &value, &len); EXPECT_HR(hr, S_OK); ok(!lstrcmpW(_bstr_(table->value), value), "%d: got %s, expected %s\n", i, wine_dbgstr_w(value), table->value); ok(lstrlenW(value) == len, "%d: got wrong value length %d\n", i, len); } } len = -1; hr = ISAXAttributes_getLength(saxattr, &len); EXPECT_HR(hr, S_OK); if (table->hr == S_OK) ok(len == 1, "%d: got %d length, expected 1\n", i, len); else ok(len == 0, "%d: got %d length, expected 0\n", i, len); ISAXAttributes_Release(saxattr); IMXAttributes_Release(mxattr); table++; i++; } free_bstrs(); } static void test_mxattr_clear(void) { ISAXAttributes *saxattr; IMXAttributes *mxattr; const WCHAR *ptr; HRESULT hr; int len; hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, S_OK); hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); EXPECT_HR(hr, S_OK); hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); EXPECT_HR(hr, E_INVALIDARG); hr = IMXAttributes_clear(mxattr); EXPECT_HR(hr, S_OK); hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"), _bstr_("qname"), _bstr_("type"), _bstr_("value")); EXPECT_HR(hr, S_OK); len = -1; hr = ISAXAttributes_getLength(saxattr, &len); EXPECT_HR(hr, S_OK); ok(len == 1, "got %d\n", len); len = -1; hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len); EXPECT_HR(hr, E_POINTER); ok(len == -1, "got %d\n", len); ptr = (void*)0xdeadbeef; hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL); EXPECT_HR(hr, E_POINTER); ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr); len = 0; hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); EXPECT_HR(hr, S_OK); ok(len == 5, "got %d\n", len); ok(!lstrcmpW(ptr, _bstr_("qname")), "got %s\n", wine_dbgstr_w(ptr)); hr = IMXAttributes_clear(mxattr); EXPECT_HR(hr, S_OK); len = -1; hr = ISAXAttributes_getLength(saxattr, &len); EXPECT_HR(hr, S_OK); ok(len == 0, "got %d\n", len); len = -1; ptr = (void*)0xdeadbeef; hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); EXPECT_HR(hr, E_INVALIDARG); ok(len == -1, "got %d\n", len); ok(ptr == (void*)0xdeadbeef, "got %p\n", ptr); IMXAttributes_Release(mxattr); ISAXAttributes_Release(saxattr); free_bstrs(); } static void test_mxattr_dispex(void) { IMXAttributes *mxattr; IDispatchEx *dispex; IUnknown *unk; HRESULT hr; hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, S_OK); hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex); EXPECT_HR(hr, S_OK); hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk); test_obj_dispex(unk); IUnknown_Release(unk); IDispatchEx_Release(dispex); IMXAttributes_Release(mxattr); } static void test_mxattr_qi(void) { IVBSAXAttributes *vbsaxattr, *vbsaxattr2; ISAXAttributes *saxattr; IMXAttributes *mxattr; HRESULT hr; hr = CoCreateInstance(&CLSID_SAXAttributes, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, S_OK); EXPECT_REF(mxattr, 1); hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); EXPECT_HR(hr, S_OK); EXPECT_REF(mxattr, 2); EXPECT_REF(saxattr, 2); hr = IMXAttributes_QueryInterface(mxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr); EXPECT_HR(hr, S_OK); EXPECT_REF(vbsaxattr, 3); EXPECT_REF(mxattr, 3); EXPECT_REF(saxattr, 3); hr = ISAXAttributes_QueryInterface(saxattr, &IID_IVBSAXAttributes, (void**)&vbsaxattr2); EXPECT_HR(hr, S_OK); EXPECT_REF(vbsaxattr, 4); EXPECT_REF(mxattr, 4); EXPECT_REF(saxattr, 4); IMXAttributes_Release(mxattr); ISAXAttributes_Release(saxattr); IVBSAXAttributes_Release(vbsaxattr); IVBSAXAttributes_Release(vbsaxattr2); } static struct msxmlsupported_data_t saxattr_support_data[] = { { &CLSID_SAXAttributes, "SAXAttributes" }, { &CLSID_SAXAttributes30, "SAXAttributes30" }, { &CLSID_SAXAttributes40, "SAXAttributes40" }, { &CLSID_SAXAttributes60, "SAXAttributes60" }, { NULL } }; static void test_mxattr_localname(void) { static const WCHAR localname1W[] = {'l','o','c','a','l','n','a','m','e','1',0}; static const WCHAR localnameW[] = {'l','o','c','a','l','n','a','m','e',0}; static const WCHAR uri1W[] = {'u','r','i','1',0}; static const WCHAR uriW[] = {'u','r','i',0}; const struct msxmlsupported_data_t *table = saxattr_support_data; while (table->clsid) { ISAXAttributes *saxattr; IMXAttributes *mxattr; HRESULT hr; int index; if (!is_clsid_supported(table->clsid, mxattributes_support_data)) { table++; continue; } hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void**)&mxattr); EXPECT_HR(hr, S_OK); hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); EXPECT_HR(hr, S_OK); hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index); EXPECT_HR(hr, E_INVALIDARG); /* add some ambiguos attribute names */ hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), _bstr_("a:localname"), _bstr_(""), _bstr_("value")); EXPECT_HR(hr, S_OK); hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), _bstr_("b:localname"), _bstr_(""), _bstr_("value")); EXPECT_HR(hr, S_OK); index = -1; hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localnameW, lstrlenW(localnameW), &index); EXPECT_HR(hr, S_OK); ok(index == 0, "%s: got index %d\n", table->name, index); index = -1; hr = ISAXAttributes_getIndexFromName(saxattr, uri1W, lstrlenW(uri1W), localnameW, lstrlenW(localnameW), &index); EXPECT_HR(hr, E_INVALIDARG); ok(index == -1, "%s: got index %d\n", table->name, index); index = -1; hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), &index); EXPECT_HR(hr, E_INVALIDARG); ok(index == -1, "%s: got index %d\n", table->name, index); if (IsEqualGUID(table->clsid, &CLSID_SAXAttributes) || IsEqualGUID(table->clsid, &CLSID_SAXAttributes30)) { hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL); EXPECT_HR(hr, E_POINTER); hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL); EXPECT_HR(hr, E_POINTER); } else { hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), localname1W, lstrlenW(localname1W), NULL); EXPECT_HR(hr, E_INVALIDARG); } hr = ISAXAttributes_getIndexFromName(saxattr, uriW, lstrlenW(uriW), NULL, 0, &index); EXPECT_HR(hr, E_INVALIDARG); hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, localname1W, lstrlenW(localname1W), &index); EXPECT_HR(hr, E_INVALIDARG); table++; ISAXAttributes_Release(saxattr); IMXAttributes_Release(mxattr); free_bstrs(); } } static void test_mxwriter_indent(void) { ISAXContentHandler *content; IMXWriter *writer; VARIANT dest; HRESULT hr; hr = CoCreateInstance(&CLSID_MXXMLWriter, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); ok(hr == S_OK, "Expected S_OK, got %08x\n", hr); hr = IMXWriter_put_indent(writer, VARIANT_TRUE); ok(hr == S_OK, "got %08x\n", hr); hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startDocument(content); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1, NULL); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_characters(content, _bstr_(""), 0); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1, NULL); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_startElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1, NULL); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("c"), -1); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("b"), -1); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endElement(content, emptyW, 0, emptyW, 0, _bstr_("a"), -1); ok(hr == S_OK, "got %08x\n", hr); hr = ISAXContentHandler_endDocument(content); ok(hr == S_OK, "got %08x\n", hr); V_VT(&dest) = VT_EMPTY; hr = IMXWriter_get_output(writer, &dest); ok(hr == S_OK, "got %08x\n", hr); ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); ok(!lstrcmpW(_bstr_("\r\n\r\n\t\t\r\n\t\r\n"), V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); VariantClear(&dest); ISAXContentHandler_Release(content); IMXWriter_Release(writer); free_bstrs(); } START_TEST(saxreader) { ISAXXMLReader *reader; HRESULT hr; hr = CoInitialize(NULL); ok(hr == S_OK, "failed to init com\n"); hr = CoCreateInstance(&CLSID_SAXXMLReader, NULL, CLSCTX_INPROC_SERVER, &IID_ISAXXMLReader, (void**)&reader); if(FAILED(hr)) { win_skip("Failed to create SAXXMLReader instance\n"); CoUninitialize(); return; } ISAXXMLReader_Release(reader); init_call_sequences(sequences, NUM_CALL_SEQUENCES); get_class_support_data(reader_support_data, &IID_ISAXXMLReader); test_saxreader(); test_saxreader_properties(); test_saxreader_features(); test_saxreader_encoding(); test_saxreader_dispex(); /* MXXMLWriter tests */ get_class_support_data(mxwriter_support_data, &IID_IMXWriter); if (is_clsid_supported(&CLSID_MXXMLWriter, mxwriter_support_data)) { test_mxwriter_handlers(); test_mxwriter_startenddocument(); test_mxwriter_startendelement(); test_mxwriter_characters(); test_mxwriter_comment(); test_mxwriter_cdata(); test_mxwriter_pi(); test_mxwriter_ignorablespaces(); test_mxwriter_dtd(); test_mxwriter_properties(); test_mxwriter_flush(); test_mxwriter_stream(); test_mxwriter_encoding(); test_mxwriter_dispex(); test_mxwriter_indent(); } else win_skip("MXXMLWriter not supported\n"); /* SAXAttributes tests */ get_class_support_data(mxattributes_support_data, &IID_IMXAttributes); if (is_clsid_supported(&CLSID_SAXAttributes, mxattributes_support_data)) { test_mxattr_qi(); test_mxattr_addAttribute(); test_mxattr_clear(); test_mxattr_localname(); test_mxattr_dispex(); } else win_skip("SAXAttributes not supported\n"); CoUninitialize(); }