From 01fe6475f4e450e255e7ba89857af9f867963eb8 Mon Sep 17 00:00:00 2001 From: Timo Kreuzer Date: Mon, 1 Dec 2025 11:17:46 +0200 Subject: [PATCH] [MSXML6_WINETEST] Import from wine-10.0 --- modules/rostests/winetests/CMakeLists.txt | 1 + .../rostests/winetests/msxml6/CMakeLists.txt | 27 + modules/rostests/winetests/msxml6/domdoc.c | 344 ++ modules/rostests/winetests/msxml6/guid.c | 11 + modules/rostests/winetests/msxml6/saxreader.c | 2871 +++++++++++++++++ modules/rostests/winetests/msxml6/schema.c | 447 +++ modules/rostests/winetests/msxml6/testlist.c | 16 + 7 files changed, 3717 insertions(+) create mode 100644 modules/rostests/winetests/msxml6/CMakeLists.txt create mode 100644 modules/rostests/winetests/msxml6/domdoc.c create mode 100644 modules/rostests/winetests/msxml6/guid.c create mode 100644 modules/rostests/winetests/msxml6/saxreader.c create mode 100644 modules/rostests/winetests/msxml6/schema.c create mode 100644 modules/rostests/winetests/msxml6/testlist.c diff --git a/modules/rostests/winetests/CMakeLists.txt b/modules/rostests/winetests/CMakeLists.txt index 9b07246507e..98310436d2b 100644 --- a/modules/rostests/winetests/CMakeLists.txt +++ b/modules/rostests/winetests/CMakeLists.txt @@ -77,6 +77,7 @@ add_subdirectory(msvcrtd) add_subdirectory(msvfw32) add_subdirectory(msxml3) add_subdirectory(msxml4) +add_subdirectory(msxml6) add_subdirectory(netapi32) add_subdirectory(netcfgx) add_subdirectory(ntdll) diff --git a/modules/rostests/winetests/msxml6/CMakeLists.txt b/modules/rostests/winetests/msxml6/CMakeLists.txt new file mode 100644 index 00000000000..17d8e5f20ff --- /dev/null +++ b/modules/rostests/winetests/msxml6/CMakeLists.txt @@ -0,0 +1,27 @@ + +remove_definitions(-D_CRT_NON_CONFORMING_SWPRINTFS) + +add_definitions(-DUSE_WINE_TODOS) + +list(APPEND SOURCE + domdoc.c + saxreader.c + schema.c) + +list(APPEND PCH_SKIP_SOURCE + guid.c + testlist.c) + +add_executable(msxml6_winetest + ${SOURCE} + ${PCH_SKIP_SOURCE}) + +add_dependencies(msxml6_winetest xmlparser_idlheader_test) +set_module_type(msxml6_winetest win32cui) +add_importlibs(msxml6_winetest ole32 oleaut32 msvcrt kernel32) + +if(MSVC) + add_importlibs(msxml6_winetest ntdll) +endif() + +add_rostests_file(TARGET msxml6_winetest) diff --git a/modules/rostests/winetests/msxml6/domdoc.c b/modules/rostests/winetests/msxml6/domdoc.c new file mode 100644 index 00000000000..d451d0dd50c --- /dev/null +++ b/modules/rostests/winetests/msxml6/domdoc.c @@ -0,0 +1,344 @@ +/* + * XML test + * + * Copyright 2005 Mike McCormack for CodeWeavers + * Copyright 2007-2008 Alistair Leslie-Hughes + * Copyright 2010-2011 Adam Martinson for CodeWeavers + * Copyright 2010-2013 Nikolay Sivov for CodeWeavers + * Copyright 2023 Daniel Lehman + * + * 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 "initguid.h" +#include "msxml6.h" + +#include "wine/test.h" + +static BSTR alloced_bstrs[256]; +static int alloced_bstrs_count; + +static BSTR _bstr_(const WCHAR *str) +{ + assert(alloced_bstrs_count < ARRAY_SIZE(alloced_bstrs)); + alloced_bstrs[alloced_bstrs_count] = SysAllocString(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; +} + +struct attrtest_t { + const WCHAR *name; + const WCHAR *uri; + const WCHAR *prefix; + const WCHAR *href; +}; + +static struct attrtest_t attrtests[] = { + { L"xmlns", L"http://www.w3.org/2000/xmlns/", NULL, L"http://www.w3.org/2000/xmlns/" }, + { L"xmlns", L"nondefaulturi", NULL, L"http://www.w3.org/2000/xmlns/" }, + { L"c", L"http://www.w3.org/2000/xmlns/", NULL, L"http://www.w3.org/2000/xmlns/" }, + { L"c", L"nsref1", NULL, L"nsref1" }, + { L"ns:c", L"nsref1", L"ns", L"nsref1" }, + { L"xmlns:c", L"http://www.w3.org/2000/xmlns/", L"xmlns", L"http://www.w3.org/2000/xmlns/" }, + { L"xmlns:c", L"nondefaulturi", L"xmlns", L"http://www.w3.org/2000/xmlns/" }, + { 0 } +}; + +/* see dlls/msxml[34]/tests/domdoc.c */ +static void test_create_attribute(void) +{ + struct attrtest_t *ptr = attrtests; + IXMLDOMElement *el; + IXMLDOMDocument2 *doc; + IXMLDOMNode *node; + VARIANT var; + HRESULT hr; + int i = 0; + BSTR str; + + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&doc); + ok(hr == S_OK, "Failed to create DOMDocument60, hr %#lx.\n", hr); + + while (ptr->name) + { + V_VT(&var) = VT_I1; + V_I1(&var) = NODE_ATTRIBUTE; + hr = IXMLDOMDocument2_createNode(doc, var, _bstr_(ptr->name), _bstr_(ptr->uri), &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = NULL; + hr = IXMLDOMNode_get_prefix(node, &str); + if (ptr->prefix) + { + ok(hr == S_OK, "%d: unexpected hr %#lx\n", i, hr); + ok(!lstrcmpW(str, _bstr_(ptr->prefix)), "%d: got prefix %s, expected %s\n", + i, wine_dbgstr_w(str), wine_dbgstr_w(ptr->prefix)); + } + else + { + ok(hr == S_FALSE, "%d: unexpected hr %#lx\n", i, hr); + ok(str == NULL, "%d: got prefix %s\n", i, wine_dbgstr_w(str)); + } + SysFreeString(str); + + str = NULL; + hr = IXMLDOMNode_get_namespaceURI(node, &str); + ok(hr == S_OK, "%d: unexpected hr %#lx\n", i, hr); + ok(!lstrcmpW(str, _bstr_(ptr->href)) || + broken(!ptr->prefix && !lstrcmpW(str, L"xmlns")), /* win7 msxml6 */ + "%d: got uri %s, expected %s\n", i, wine_dbgstr_w(str), wine_dbgstr_w(ptr->href)); + SysFreeString(str); + + IXMLDOMNode_Release(node); + free_bstrs(); + + i++; + ptr++; + } + + V_VT(&var) = VT_I1; + V_I1(&var) = NODE_ELEMENT; + hr = IXMLDOMDocument2_createNode(doc, var, _bstr_(L"e"), NULL, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_QueryInterface(node, &IID_IXMLDOMElement, (void**)&el); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IXMLDOMNode_Release(node); + + V_VT(&var) = VT_I1; + V_I1(&var) = NODE_ATTRIBUTE; + hr = IXMLDOMDocument2_createNode(doc, var, _bstr_(L"xmlns:a"), + _bstr_(L"http://www.w3.org/2000/xmlns/"), &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMElement_setAttributeNode(el, (IXMLDOMAttribute*)node, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* for some reason default namespace uri is not reported */ + hr = IXMLDOMNode_get_namespaceURI(node, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"http://www.w3.org/2000/xmlns/") || + broken(!ptr->prefix && !lstrcmpW(str, L"xmlns")), /* win7 msxml6 */ + "got uri %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + IXMLDOMNode_Release(node); + IXMLDOMElement_Release(el); + IXMLDOMDocument2_Release(doc); + free_bstrs(); +} + +/* see dlls/msxml[34]/tests/domdoc.c */ +static void test_namespaces_as_attributes(void) +{ + struct test + { + const WCHAR *xml; + int explen; + const WCHAR *names[3]; + const WCHAR *prefixes[3]; + const WCHAR *basenames[3]; + const WCHAR *uris[3]; + const WCHAR *texts[3]; + const WCHAR *xmls[3]; + }; + static const struct test tests[] = + { + { + L"", 3, + { L"ns:b", L"d", L"xmlns:ns" }, /* nodeName */ + { L"ns", NULL, L"xmlns" }, /* prefix */ + { L"b", L"d", L"ns" }, /* baseName */ + { L"nshref", NULL, L"" }, /* namespaceURI */ + { L"b attr", L"d attr", L"nshref" }, /* text */ + { L"ns:b=\"b attr\"", L"d=\"d attr\"", L"xmlns:ns=\"nshref\"" }, /* xml */ + }, + /* property only */ + { + L"", 1, + { L"d" }, /* nodeName */ + { NULL }, /* prefix */ + { L"d" }, /* baseName */ + { NULL }, /* namespaceURI */ + { L"d attr" }, /* text */ + { L"d=\"d attr\"" }, /* xml */ + }, + /* namespace only */ + { + L"", 1, + { L"xmlns:ns" }, /* nodeName */ + { L"xmlns" }, /* prefix */ + { L"ns" }, /* baseName */ + { L"" }, /* namespaceURI */ + { L"nshref" }, /* text */ + { L"xmlns:ns=\"nshref\"" }, /* xml */ + }, + /* default namespace */ + { + L"", 1, + { L"xmlns" }, /* nodeName */ + { NULL }, /* prefix */ + { L"xmlns" }, /* baseName */ + { L"http://www.w3.org/2000/xmlns/" }, /* namespaceURI */ + { L"nshref" }, /* text */ + { L"xmlns=\"nshref\"" }, /* xml */ + }, + /* no properties or namespaces */ + { + L"", 0, + }, + + { NULL } + }; + const struct test *test; + IXMLDOMNamedNodeMap *map; + IXMLDOMNode *node, *item; + IXMLDOMDocument2 *doc; + VARIANT_BOOL b; + LONG len, i; + HRESULT hr; + BSTR str; + + test = tests; + while (test->xml) + { + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&doc); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMDocument2_loadXML(doc, _bstr_(test->xml), &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + node = NULL; + hr = IXMLDOMDocument2_get_firstChild(doc, &node); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_get_attributes(node, &map); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = IXMLDOMNamedNodeMap_get_length(map, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == test->explen, "got %ld\n", len); + + item = NULL; + hr = IXMLDOMNamedNodeMap_get_item(map, test->explen+1, &item); + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + ok(!item, "Item should be NULL\n"); + + for (i = 0; i < len; i++) + { + item = NULL; + hr = IXMLDOMNamedNodeMap_get_item(map, i, &item); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = NULL; + hr = IXMLDOMNode_get_nodeName(item, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, test->names[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + str = NULL; + hr = IXMLDOMNode_get_prefix(item, &str); + if (test->prefixes[i]) + { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, test->prefixes[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + } + else + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr ); + + str = NULL; + hr = IXMLDOMNode_get_baseName(item, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, test->basenames[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + str = NULL; + hr = IXMLDOMNode_get_namespaceURI(item, &str); + if (test->uris[i]) + { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (test->prefixes[i] && !lstrcmpW(test->prefixes[i], L"xmlns")) + ok(!lstrcmpW(str, L"http://www.w3.org/2000/xmlns/"), + "got %s\n", wine_dbgstr_w(str)); + else + ok(!lstrcmpW(str, test->uris[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + } + else + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr ); + + str = NULL; + hr = IXMLDOMNode_get_text(item, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, test->texts[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + str = NULL; + hr = IXMLDOMNode_get_xml(item, &str); + ok(SUCCEEDED(hr), "Failed to get node xml, hr %#lx.\n", hr); + ok(!lstrcmpW(str, test->xmls[i]), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + IXMLDOMNode_Release(item); + } + + IXMLDOMNamedNodeMap_Release(map); + IXMLDOMNode_Release(node); + IXMLDOMDocument2_Release(doc); + + test++; + } + free_bstrs(); +} + +START_TEST(domdoc) +{ + HRESULT hr; + IXMLDOMDocument2 *doc; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "failed to init com\n"); + + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&doc); + if (hr != S_OK) + { + win_skip("class &CLSID_DOMDocument60 not supported\n"); + return; + } + IXMLDOMDocument2_Release(doc); + + test_namespaces_as_attributes(); + test_create_attribute(); + + CoUninitialize(); +} diff --git a/modules/rostests/winetests/msxml6/guid.c b/modules/rostests/winetests/msxml6/guid.c new file mode 100644 index 00000000000..a66c206038d --- /dev/null +++ b/modules/rostests/winetests/msxml6/guid.c @@ -0,0 +1,11 @@ +/* DO NOT USE THE PRECOMPILED HEADER FOR THIS FILE! */ + +#define WIN32_NO_STATUS +#define _INC_WINDOWS + +#include +#include +#include +#include + +/* NO CODE HERE, THIS IS JUST REQUIRED FOR THE GUID DEFINITIONS */ diff --git a/modules/rostests/winetests/msxml6/saxreader.c b/modules/rostests/winetests/msxml6/saxreader.c new file mode 100644 index 00000000000..45587d79e67 --- /dev/null +++ b/modules/rostests/winetests/msxml6/saxreader.c @@ -0,0 +1,2871 @@ +/* + * 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 "msxml6.h" +#include "msxml6did.h" +#include "ocidl.h" +#include "initguid.h" +#include "dispex.h" + +#include "wine/test.h" + +struct class_support +{ + const GUID *clsid; + const char *name; + const IID *iid; + BOOL supported; +}; + +static struct class_support class_support[] = +{ + { &CLSID_MXXMLWriter60, "MXXMLWriter60", &IID_IMXWriter }, + { &CLSID_SAXAttributes60, "SAXAttributes60", &IID_IMXAttributes }, + { NULL } +}; + +static void get_class_support_data(void) +{ + struct class_support *table = class_support; + + while (table->clsid) + { + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(table->clsid, NULL, CLSCTX_INPROC_SERVER, table->iid, (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 BOOL is_class_supported(const CLSID *clsid) +{ + struct class_support *table = class_support; + + while (table->clsid) + { + if (table->clsid == clsid) return table->supported; + table++; + } + return FALSE; +} + +#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 %ld, got %ld.\n", ref, rc); +} + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static LONG get_refcount(void *iface) +{ + IUnknown *unk = iface; + LONG ref; + + ref = IUnknown_AddRef(unk); + IUnknown_Release(unk); + return ref-1; +} + +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 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] = {L"a:attr1junk", + L"attr2junk", + L"attr3"}; + 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] = {L"a1junk", + L"a2junk", + L"<&\">'"}; + 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 }; + +static void test_mxwriter_handlers(void) +{ + IMXWriter *writer; + HRESULT hr; + int i; + + static const IID *riids[] = + { + &IID_ISAXContentHandler, + &IID_ISAXLexicalHandler, + &IID_ISAXDeclHandler, + &IID_ISAXDTDHandler, + &IID_ISAXErrorHandler, + &IID_IVBSAXDeclHandler, + &IID_IVBSAXLexicalHandler, + &IID_IVBSAXContentHandler, + &IID_IVBSAXDTDHandler, + &IID_IVBSAXErrorHandler + }; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\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, unexpected hr %#lx.\n", wine_dbgstr_guid(riids[i]), hr); + EXPECT_REF(writer, 2); + + /* IMXWriter from a handler */ + hr = IUnknown_QueryInterface(handler, &IID_IMXWriter, (void**)&writer2); + ok(hr == S_OK, "%s, unexpected hr %#lx.\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 void test_mxwriter_default_properties(void) +{ + IMXWriter *writer; + VARIANT_BOOL b; + BSTR encoding; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + b = VARIANT_FALSE; + hr = IMXWriter_get_byteOrderMark(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "Unexpected value %d.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_disableOutputEscaping(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %d.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_indent(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %d.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_omitXMLDeclaration(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %d.\n", b); + + b = VARIANT_TRUE; + hr = IMXWriter_get_standalone(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_FALSE, "Unexpected value %d.\n", b); + + hr = IMXWriter_get_encoding(writer, &encoding); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(encoding, L"UTF-16"), "Unexpected value %s.\n", wine_dbgstr_w(encoding)); + SysFreeString(encoding); + + IMXWriter_Release(writer); +} + +static void test_mxwriter_properties(void) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT_BOOL b; + HRESULT hr; + BSTR str, str2; + VARIANT dest; + + test_mxwriter_default_properties(); + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_disableOutputEscaping(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_byteOrderMark(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_indent(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_omitXMLDeclaration(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_get_standalone(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + /* set and check */ + hr = IMXWriter_put_standalone(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + b = VARIANT_FALSE; + hr = IMXWriter_get_standalone(writer, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "got %d\n", b); + + hr = IMXWriter_get_encoding(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"UTF-16"), "Unexpected string %s.\n", wine_dbgstr_w(str)); + + str2 = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(str != str2, "expected newly allocated, got same %p\n", str); + + SysFreeString(str2); + SysFreeString(str); + + /* put empty string */ + str = SysAllocString(L""); + hr = IMXWriter_put_encoding(writer, str); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"UTF-16"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* invalid encoding name */ + str = SysAllocString(L"test"); + hr = IMXWriter_put_encoding(writer, str); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + /* test case sensitivity */ + hr = IMXWriter_put_encoding(writer, _bstr_("utf-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"utf-8"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_encoding(writer, _bstr_("uTf-16")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + str = (void*)0xdeadbeef; + hr = IMXWriter_get_encoding(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"uTf-16"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* how it affects document creation */ + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\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, "Unexpected hr %#lx.\n", hr); + /* default version is 'surprisingly' 1.0 */ + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"1.0"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* store version string as is */ + hr = IMXWriter_put_version(writer, NULL); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_version(writer, _bstr_("1.0")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_version(writer, _bstr_("")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!lstrcmpW(str, L"1.0"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_version(writer, _bstr_("a.b")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"a.b"), "got %s\n", wine_dbgstr_w(str)); + SysFreeString(str); + + hr = IMXWriter_put_version(writer, _bstr_("2.0")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXWriter_get_version(writer, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(str, L"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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\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, "Unexpected hr %#lx.\n", hr); + + /* attach stream */ + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\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, "Unexpected hr %#lx.\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, "Unexpected hr %#lx.\n", hr); + + /* flush() doesn't detach a stream */ + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine EXPECT_REF(stream, 3); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "expected stream beginning\n"); + + /* already started */ + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* flushed on endDocument() */ + pos.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "expected stream position moved\n"); + + IStream_Release(stream); + + /* auto-flush feature */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\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, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_byteOrderMark(writer, VARIANT_FALSE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* internal buffer is flushed automatically on certain threshold */ + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + len = 2048; + buff = malloc(len + 1); + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 0; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart != 0, "unexpected stream beginning\n"); + + hr = IMXWriter_get_output(writer, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + ref = get_refcount(stream); + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IStream_Release(stream); + + /* test char count lower than threshold */ + hr = CreateStreamOnHGlobal(NULL, TRUE, &stream); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + EXPECT_REF(stream, 1); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len - 8); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + pos.QuadPart = 0; + pos2.QuadPart = 1; + hr = IStream_Seek(stream, pos, STREAM_SEEK_CUR, &pos2); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(pos2.QuadPart == 0, "expected stream beginning\n"); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test auto-flush function when stream is not set */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + memset(buff, 'A', len); + buff[len] = 0; + hr = ISAXContentHandler_characters(content, _bstr_(buff), len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + len += strlen(""); + ok(SysStringLen(V_BSTR(&dest)) == len, "got len=%d, expected %d\n", SysStringLen(V_BSTR(&dest)), len); + VariantClear(&dest); + + 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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"\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, "Unexpected hr %#lx.\n", hr); + /* and get duplicated prolog */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", 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 +{ + 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 struct writer_startendelement_t writer_startendelement[] = +{ + { StartElement, NULL, NULL, NULL, "<>", S_OK }, + { StartElement, "uri", NULL, NULL, "<>", S_OK }, + { StartElement, NULL, "local", NULL, "<>", S_OK }, + { StartElement, NULL, NULL, "qname", "", S_OK }, + { StartElement, "uri", "local", "qname", "", S_OK }, + { StartElement, "uri", "local", NULL, "<>", S_OK }, + { StartElement, "uri", "local", "uri:local", "", S_OK }, + { StartElement, "uri", "local", "uri:local2", "", S_OK }, + { EndElement, NULL, NULL, NULL, "", S_OK }, + { EndElement, "uri", NULL, NULL, "", S_OK }, + { EndElement, NULL, "local", NULL, "", S_OK }, + { EndElement, NULL, NULL, "qname", "", S_OK }, + { EndElement, "uri", "local", "qname", "", S_OK }, + { EndElement, "uri", "local", NULL, "", S_OK }, + { EndElement, "uri", "local", "uri:local", "", S_OK }, + { EndElement, "uri", "local", "uri:local2", "", S_OK }, + { StartElement, "uri", "local", "uri:local", startelement_xml, S_OK, &saxattributes }, + { StartEndElement, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, + { StartEndElement, "", "", "", "", S_OK }, + { StartEndElement | DisableEscaping, "uri", "local", "uri:local", startendelement_xml, S_OK, &saxattributes }, +}; + +static void test_mxwriter_startendelement_batch(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(writer_startendelement); ++i) + { + const struct writer_startendelement_t *table = &writer_startendelement[i]; + ISAXContentHandler *content; + IMXWriter *writer; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (table->type & DisableEscaping) + { + hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + } + + 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 %#lx, expected %#lx\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 %#lx, expected %#lx\n", i, hr, table->hr); + } + + /* test output */ + if (hr == S_OK) + { + VARIANT dest; + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + } + + free_bstrs(); +} + +/* point of these test is to start/end element with different names and name lengths */ +struct writer_startendelement2_t +{ + 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[] = +{ + { "a", -1, "b", -1, "", E_INVALIDARG }, + { "a", 1, "b", 1, "", S_OK }, +}; + +static void test_mxwriter_startendelement_batch2(void) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(writer_startendelement2); ++i) + { + const struct writer_startendelement2_t *table = &writer_startendelement2[i]; + ISAXContentHandler *content; + IMXWriter *writer; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, + _bstr_(table->qnamestart), table->qnamestart_len, NULL); + ok(hr == table->hr, "test %d: got %#lx, expected %#lx\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 %#lx, expected %#lx\n", i, hr, table->hr); + + /* test output */ + if (hr == S_OK) + { + VARIANT dest; + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + + free_bstrs(); + } +} + +static void test_mxwriter_startendelement(void) +{ + ISAXContentHandler *content; + IVBSAXContentHandler *vb_content; + IMXWriter *writer; + VARIANT dest; + BSTR bstr_null = NULL, bstr_empty, bstr_a, bstr_b, bstr_ab; + HRESULT hr; + + test_mxwriter_startendelement_batch(); + test_mxwriter_startendelement_batch2(); + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + bstr_empty = SysAllocString(L""); + bstr_a = SysAllocString(L"a"); + bstr_b = SysAllocString(L"b"); + bstr_ab = SysAllocString(L"a:b"); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_null, &bstr_empty, &bstr_b, NULL); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_b, &bstr_empty, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXContentHandler_startElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_null, &bstr_null, &bstr_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_null, &bstr_a, &bstr_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_a, &bstr_b, &bstr_null); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_null, &bstr_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_b, &bstr_null); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_endElement(vb_content, &bstr_empty, &bstr_empty, &bstr_b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + SysFreeString(bstr_empty); + SysFreeString(bstr_a); + SysFreeString(bstr_b); + SysFreeString(bstr_ab); + + hr = IVBSAXContentHandler_endDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\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, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<>", 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, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<>", 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, _bstr_("a:b"), 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* only local name is an error too */ + hr = ISAXContentHandler_endElement(content, NULL, 0, _bstr_("b"), 1, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("b"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"<>", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abcdef"), 3, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IMXWriter_flush(writer); + + hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("abdcdef"), 3); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* length -1 */ + hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), -1, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + free_bstrs(); +} + +struct writer_characters_t +{ + const char *data; + const char *output; +}; + +static const struct writer_characters_t writer_characters[] = +{ + { "< > & \" \'", "< > & \" \'" }, +}; + +static void test_mxwriter_characters(void) +{ + static const WCHAR embedded_nullbytes[] = L"a\0b\0\0\0c"; + IVBSAXContentHandler *vb_content; + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + BSTR str; + HRESULT hr; + int i = 0; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = _bstr_("VbChars"); + hr = IVBSAXContentHandler_characters(vb_content, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 14); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"VbCharsTESTCHARDATA .", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + ISAXContentHandler_Release(content); + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + /* try empty characters data to see if element is closed */ + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"TESTCHARDATA .", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + /* test embedded null bytes */ + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(SysStringLen(V_BSTR(&dest)) == ARRAY_SIZE(embedded_nullbytes), "unexpected len %d\n", SysStringLen(V_BSTR(&dest))); + ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXContentHandler, (void**)&vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXContentHandler_startDocument(vb_content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + str = SysAllocStringLen(embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)); + hr = IVBSAXContentHandler_characters(vb_content, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(str); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(SysStringLen(V_BSTR(&dest)) == 1, "Unexpected length %d.\n", SysStringLen(V_BSTR(&dest))); + ok(!memcmp(V_BSTR(&dest), embedded_nullbytes, ARRAY_SIZE(embedded_nullbytes)), + "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + IVBSAXContentHandler_Release(vb_content); + IMXWriter_Release(writer); + + /* batch tests */ + for (i = 0; i < ARRAY_SIZE(writer_characters); ++i) + { + const struct writer_characters_t *table = &writer_characters[i]; + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test output */ + if (hr == S_OK) + { + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_disableOutputEscaping(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, _bstr_(table->data), strlen(table->data)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* test output */ + if (hr == S_OK) + { + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + } + + free_bstrs(); +} + +static void test_mxwriter_domdoc(void) +{ + ISAXContentHandler *content; + IXMLDOMDocument *domdoc; + IMXWriter *writer; + HRESULT hr; + VARIANT dest; + IXMLDOMElement *root = NULL; + IXMLDOMNodeList *node_list = NULL; + IXMLDOMNode *node = NULL; + LONG list_length = 0; + BSTR str; + + /* Create writer and attach DOMDocument output */ + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void**)&writer); + ok(hr == S_OK, "Failed to create a writer, hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument, (void **)&domdoc); + ok(hr == S_OK, "Failed to create a document, hr %#lx.\n", hr); + + V_VT(&dest) = VT_DISPATCH; + V_DISPATCH(&dest) = (IDispatch *)domdoc; + + hr = IMXWriter_put_output(writer, dest); + todo_wine + ok(hr == S_OK, "Failed to set writer output, hr %#lx.\n", hr); + if (FAILED(hr)) + { + IXMLDOMDocument_Release(domdoc); + IMXWriter_Release(writer); + return; + } + + /* Add root element to document. */ + hr = IXMLDOMDocument_createElement(domdoc, _bstr_("TestElement"), &root); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IXMLDOMDocument_appendChild(domdoc, (IXMLDOMNode *)root, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IXMLDOMElement_Release(root); + + hr = IXMLDOMDocument_get_documentElement(domdoc, &root); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(root != NULL, "Unexpected document root.\n"); + IXMLDOMElement_Release(root); + + /* startDocument clears root element and disables methods. */ + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMDocument_get_documentElement(domdoc, &root); + todo_wine + ok(hr == S_FALSE, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMDocument_createElement(domdoc, _bstr_("TestElement"), &root); + todo_wine + ok(hr == E_FAIL, "Unexpected hr %#lx.\n", hr); + + /* startElement allows document root node to be accessed. */ + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"BankAccount", 11, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMDocument_get_documentElement(domdoc, &root); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(root != NULL, "Unexpected document root.\n"); + + hr = IXMLDOMElement_get_nodeName(root, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!lstrcmpW(L"BankAccount", str), "Unexpected name %s.\n", wine_dbgstr_w(str)); + SysFreeString(str); + + /* startElement immediately updates previous node. */ + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"Number", 6, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMElement_get_childNodes(root, &node_list); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNodeList_get_length(node_list, &list_length); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(list_length == 1, "list length %ld, expected 1\n", list_length); + + hr = IXMLDOMNodeList_get_item(node_list, 0, &node); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_get_nodeName(node, &str); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(L"Number", str), "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + /* characters not immediately visible. */ + hr = ISAXContentHandler_characters(content, L"12345", 5); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_get_text(node, &str); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(L"", str), "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + /* characters visible after endElement. */ + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"Number", 6); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_get_text(node, &str); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(L"12345", str), "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + IXMLDOMNode_Release(node); + + /* second startElement updates the existing node list. */ + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, L"Name", 4, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, L"Captain Ahab", 12); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"Name", 4); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, L"BankAccount", 11); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNodeList_get_length(node_list, &list_length); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(2 == list_length, "list length %ld, expected 2\n", list_length); +} + hr = IXMLDOMNodeList_get_item(node_list, 1, &node); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMNode_get_nodeName(node, &str); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(L"Name", str), "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + hr = IXMLDOMNode_get_text(node, &str); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(L"Captain Ahab", str), "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + IXMLDOMNode_Release(node); + IXMLDOMNodeList_Release(node_list); + IXMLDOMElement_Release(root); + + /* endDocument makes document modifiable again. */ + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMDocument_createElement(domdoc, _bstr_("TestElement"), &root); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + IXMLDOMElement_Release(root); + + /* finally check doc output */ + hr = IXMLDOMDocument_get_xml(domdoc, &str); +todo_wine { + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW( + L"" + "12345" + "Captain Ahab" + "\r\n", + str), + "got %s\n", wine_dbgstr_w(str)); +} + SysFreeString(str); + + IXMLDOMDocument_Release(domdoc); + 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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "Expected VT_BSTR, got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-8")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* write empty element */ + hr = ISAXContentHandler_startElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, _bstr_(""), 0, _bstr_(""), 0, _bstr_("a"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* switch */ + hr = IMXWriter_put_encoding(writer, _bstr_("UTF-16")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = GetHGlobalFromStream(stream, &g); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!lstrcmpW(s, L"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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_UNKNOWN; + V_UNKNOWN(&dest) = (IUnknown*)stream; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_flush(writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* prepare expected string */ + *expectedA = 0; + strcat(expectedA, "\r\n"); + + hr = GetHGlobalFromStream(stream, &g); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + IStream_Release(stream); + + enc = encoding_names[++i]; + } + + ISAXContentHandler_Release(content); + IMXWriter_Release(writer); + + free_bstrs(); +} + +static void test_obj_dispex(IUnknown *obj) +{ + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) return; + + ticnt = 0; + hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ticnt == 1, "ticnt=%u\n", ticnt); + + name = SysAllocString(L"*"); + hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + props = 0; + hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(props == 0, "Unexpected value %ld.\n", props); + + hr = IDispatchEx_GetMemberName(dispex, dispid, &name); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) SysFreeString(name); + + hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_SAX_XMLREADER_GETFEATURE, &dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + unk = (IUnknown*)0xdeadbeef; + hr = IDispatchEx_GetNameSpaceParent(dispex, &unk); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk); + + name = SysAllocString(L"testprop"); + hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &did); + ok(hr == DISP_E_UNKNOWNNAME, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + IDispatchEx_Release(dispex); +} + +static void test_mxwriter_dispex(void) +{ + IDispatchEx *dispex; + IMXWriter *writer; + IUnknown *unk; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IDispatchEx, (void**)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IDispatchEx_QueryInterface(dispex, &IID_IUnknown, (void**)&unk); + test_obj_dispex(unk); + IUnknown_Release(unk); + IDispatchEx_Release(dispex); + IMXWriter_Release(writer); + + if (is_class_supported(&CLSID_MXXMLWriter60)) + { + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IUnknown, (void**)&unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_obj_dispex(unk); + IUnknown_Release(unk); + } +} + +static void test_mxwriter_comment(void) +{ + IVBSAXLexicalHandler *vblexical; + ISAXContentHandler *content; + ISAXLexicalHandler *lexical; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_comment(lexical, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXLexicalHandler_comment(vblexical, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_comment(lexical, L"comment", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n\r\n", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXLexicalHandler_comment(lexical, L"comment", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n\r\n\r\n", V_BSTR(&dest)), "Unexpected 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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startCDATA(lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L" & \""), 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_endCDATA(lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L" & \"]]>", 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) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, NULL, 0, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 0, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 6, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n\r\n\r\n", V_BSTR(&dest)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 4, L"data", 4); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n\r\n\r\n\r\n", V_BSTR(&dest)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_processingInstruction(content, L"target", 6, L"data", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"\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) +{ + ISAXContentHandler *content; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 4); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_ignorableWhitespace(content, L"data", 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"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) +{ + IVBSAXLexicalHandler *vblexical; + ISAXContentHandler *content; + ISAXLexicalHandler *lexical; + IVBSAXDeclHandler *vbdecl; + ISAXDeclHandler *decl; + ISAXDTDHandler *dtd; + IMXWriter *writer; + VARIANT dest; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXLexicalHandler, (void**)&lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXDeclHandler, (void**)&decl); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXDeclHandler, (void**)&vbdecl); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_IVBSAXLexicalHandler, (void**)&vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_omitXMLDeclaration(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXLexicalHandler_startDTD(vblexical, NULL, NULL, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, L"pub", 3, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, NULL, 0, L"sys", 3); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, NULL, 0, L"pub", 3, L"sys", 3); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, NULL, 0, NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n]>\r\n", + V_BSTR(&dest)), "Unexpected content %s.\n", debugstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* element declaration */ + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_put_output(writer, dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, NULL, 0, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXDeclHandler_elementDecl(vbdecl, NULL, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 7); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_elementDecl(decl, L"name", 4, L"content", 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"\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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element"), strlen("element"), + _bstr_("attribute"), strlen("attribute"), _bstr_("CDATA"), strlen("CDATA"), + _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value"), strlen("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\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")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_attributeDecl(decl, _bstr_("element2"), strlen("element2"), + _bstr_("attribute3"), strlen("attribute3"), _bstr_("CDATA"), strlen("CDATA"), + _bstr_("#REQUIRED"), strlen("#REQUIRED"), _bstr_("value3"), strlen("value3")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_internalEntityDecl(decl, NULL, 0, NULL, 0); + todo_wine + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXDeclHandler_internalEntityDecl(vbdecl, NULL, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), -1, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_internalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("value"), strlen("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n\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, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, NULL, 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXDeclHandler_externalEntityDecl(vbdecl, NULL, NULL, NULL); + todo_wine + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), -1, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), + _bstr_("sysid"), strlen("sysid")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDeclHandler_externalEntityDecl(decl, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), + NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + todo_wine + ok(!lstrcmpW(L"\r\n" + "\r\n\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 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[] = +{ + { NULL, NULL, "ns:qname", NULL, "value", S_OK }, + { NULL, "qname", "ns:qname", NULL, "value", S_OK }, + { "uri", "qname", "ns:qname", NULL, "value", S_OK }, + { "uri", "qname", "ns:qname", "type", "value", S_OK }, +}; + +static void test_mxattr_addAttribute(void) +{ + int i = 0; + + for (i = 0; i < ARRAY_SIZE(addattribute_data); ++i) + { + const addattribute_test_t *table = &addattribute_data[i]; + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + const WCHAR *value; + int len, index; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 0, "got %d\n", len); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\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 %#lx, expected %#lx.\n", i, hr, table->hr); + + if (hr == S_OK) + { + /* Crashes. */ + if (0) + { + hr = ISAXAttributes_getValue(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValue(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, &value, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getType(saxattr, 0, NULL, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + } + + len = -1; + hr = ISAXAttributes_getValue(saxattr, 0, &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromQName(saxattr, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_("nonexistent"), 11, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + index = -1; + hr = ISAXAttributes_getIndexFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &index); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "%d: got wrong index %d\n", i, index); + + hr = ISAXAttributes_getValueFromQName(saxattr, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, NULL, 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, NULL, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromName(saxattr, _bstr_(table->uri), 0, NULL, 0, &value, NULL); + ok(hr == E_POINTER /* win8 */ || hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getValueFromQName(saxattr, _bstr_(table->qname), strlen(table->qname), &value, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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); + } + + free_bstrs(); +} + +static void test_mxattr_clear(void) +{ + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + const WCHAR *ptr; + HRESULT hr; + int len; + + hr = CoCreateInstance(&CLSID_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getQName(saxattr, 0, NULL, NULL); + todo_wine + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_clear(mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("local"), + _bstr_("qname"), _bstr_("type"), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 1, "got %d\n", len); + + len = -1; + hr = ISAXAttributes_getQName(saxattr, 0, NULL, &len); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + ok(len == -1, "got %d\n", len); + + ptr = (void*)0xdeadbeef; + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!ptr, "Unexpected pointer %p.\n", ptr); + + len = 0; + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 5, "got %d\n", len); + ok(!lstrcmpW(ptr, L"qname"), "got %s\n", wine_dbgstr_w(ptr)); + + hr = IMXAttributes_clear(mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = ISAXAttributes_getLength(saxattr, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 0, "got %d\n", len); + + len = -1; + ptr = (void*)0xdeadbeef; + hr = ISAXAttributes_getQName(saxattr, 0, &ptr, &len); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + todo_wine + ok(!len, "Unexpected length %d.\n", len); + todo_wine + ok(!ptr, "Unexpected pointer %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_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_IDispatchEx, (void**)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + 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) +{ + IMXAttributes *mxattr; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + check_interface(mxattr, &IID_IMXAttributes, TRUE); + check_interface(mxattr, &IID_ISAXAttributes, TRUE); + check_interface(mxattr, &IID_IVBSAXAttributes, TRUE); + check_interface(mxattr, &IID_IDispatch, TRUE); + check_interface(mxattr, &IID_IDispatchEx, TRUE); + + IMXAttributes_Release(mxattr); +} + +static void test_mxattr_localname(void) +{ + ISAXAttributes *saxattr; + IMXAttributes *mxattr; + HRESULT hr; + int index; + + hr = CoCreateInstance(&CLSID_SAXAttributes60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXAttributes, (void **)&mxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXAttributes_QueryInterface(mxattr, &IID_ISAXAttributes, (void**)&saxattr); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + /* add some ambiguous attribute names */ + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), + _bstr_("a:localname"), _bstr_(""), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + hr = IMXAttributes_addAttribute(mxattr, _bstr_("uri"), _bstr_("localname"), + _bstr_("b:localname"), _bstr_(""), _bstr_("value")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname", 9, &index); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(!index, "Got index %d.\n", index); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri1", 4, L"localname", 9, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "Got index %d.\n", index); + + index = -1; + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname1", 10, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + ok(index == -1, "Got index %d.\n", index); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, NULL, 0, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, L"localname1", 10, NULL); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, L"uri", 3, NULL, 0, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXAttributes_getIndexFromName(saxattr, NULL, 0, L"localname1", 10, &index); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + 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_MXXMLWriter60, NULL, CLSCTX_INPROC_SERVER, &IID_IMXWriter, (void **)&writer); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_put_indent(writer, VARIANT_TRUE); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IMXWriter_QueryInterface(writer, &IID_ISAXContentHandler, (void**)&content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("a"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_characters(content, _bstr_(""), 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("b"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_startElement(content, L"", 0, L"", 0, _bstr_("c"), 1, NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, _bstr_("c"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, _bstr_("b"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endElement(content, L"", 0, L"", 0, _bstr_("a"), 1); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXContentHandler_endDocument(content); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&dest) = VT_EMPTY; + hr = IMXWriter_get_output(writer, &dest); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_VT(&dest) == VT_BSTR, "got %d\n", V_VT(&dest)); + ok(!lstrcmpW(L"\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) +{ + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "Failed to initialize COM, hr %#lx.\n", hr); + + get_class_support_data(); + + if (is_class_supported(&CLSID_MXXMLWriter60)) + { + 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_domdoc(); + test_mxwriter_encoding(); + test_mxwriter_dispex(); + test_mxwriter_indent(); + } + + if (is_class_supported(&CLSID_SAXAttributes60)) + { + test_mxattr_qi(); + test_mxattr_addAttribute(); + test_mxattr_clear(); + test_mxattr_localname(); + test_mxattr_dispex(); + } + + CoUninitialize(); +} diff --git a/modules/rostests/winetests/msxml6/schema.c b/modules/rostests/winetests/msxml6/schema.c new file mode 100644 index 00000000000..c8458f397c3 --- /dev/null +++ b/modules/rostests/winetests/msxml6/schema.c @@ -0,0 +1,447 @@ +/* + * Schema test + * + * Copyright 2007 Huw Davies + * Copyright 2010 Adam Martinson for CodeWeavers + * + * 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 + */ + +#include +#include +#define COBJMACROS + +#include "ole2.h" +#include "msxml6.h" +#include "msxml6did.h" +#include "dispex.h" + +#include "wine/test.h" + +#include "initguid.h" + +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + +static const WCHAR xsd_schema1_uri[] = L"x-schema:test1.xsd"; +static const WCHAR xsd_schema1_xml[] = +L"" +"" +" " +" " +" " +" " +" " +" " +" " +""; + +#define check_interface(a, b, c) check_interface_(__LINE__, a, b, c) +static void check_interface_(unsigned int line, void *iface_ptr, REFIID iid, BOOL supported) +{ + IUnknown *iface = iface_ptr; + HRESULT hr, expected_hr; + IUnknown *unk; + + expected_hr = supported ? S_OK : E_NOINTERFACE; + + hr = IUnknown_QueryInterface(iface, iid, (void **)&unk); + ok_(__FILE__, line)(hr == expected_hr, "Got hr %#lx, expected %#lx.\n", hr, expected_hr); + if (SUCCEEDED(hr)) + IUnknown_Release(unk); +} + +static BSTR alloced_bstrs[256]; +static int alloced_bstrs_count; + +static BSTR _bstr_(const WCHAR *str) +{ + assert(alloced_bstrs_count < ARRAY_SIZE(alloced_bstrs)); + alloced_bstrs[alloced_bstrs_count] = SysAllocString(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 IXMLDOMDocument2 *create_document(void) +{ + IXMLDOMDocument2 *obj = NULL; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&obj); + ok(hr == S_OK, "Failed to create a document object, hr %#lx.\n", hr); + + return obj; +} + +static void *create_cache(REFIID riid) +{ + void *obj = NULL; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_XMLSchemaCache60, NULL, CLSCTX_INPROC_SERVER, riid, &obj); + ok(hr == S_OK, "Failed to create a document object, hr %#lx.\n", hr); + + return obj; +} + +static HRESULT validate_regex_document(IXMLDOMDocument2 *doc, IXMLDOMDocument2 *schema, IXMLDOMSchemaCollection* cache, + const WCHAR *regex, const WCHAR *input) +{ + static const WCHAR regex_doc[] = +L"" +"" +"%s"; + + static const WCHAR regex_schema[] = +L"" +"" +" " +" " +" " +" " +" " +" " +" " +""; + + WCHAR buffer[1024]; + IXMLDOMParseError* err; + BSTR namespace, bstr; + VARIANT_BOOL b; + HRESULT hr; + VARIANT v; + + VariantInit(&v); + + swprintf(buffer, ARRAY_SIZE(buffer), regex_doc, input); + bstr = SysAllocString(buffer); + hr = IXMLDOMDocument2_loadXML(doc, bstr, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "failed to load XML\n"); + SysFreeString(bstr); + + swprintf(buffer, ARRAY_SIZE(buffer), regex_schema, regex); + bstr = SysAllocString(buffer); + hr = IXMLDOMDocument2_loadXML(schema, bstr, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "failed to load XML\n"); + SysFreeString(bstr); + + /* add the schema to the cache */ + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = NULL; + hr = IXMLDOMDocument2_QueryInterface(schema, &IID_IDispatch, (void**)&V_DISPATCH(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_DISPATCH(&v) != NULL, "failed to get IDispatch interface\n"); + namespace = SysAllocString(L"urn:test"); + hr = IXMLDOMSchemaCollection_add(cache, namespace, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + SysFreeString(namespace); + VariantClear(&v); +/* + if (FAILED(hr)) + return hr; +*/ + /* associate the cache to the doc */ + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = NULL; + hr = IXMLDOMSchemaCollection_QueryInterface(cache, &IID_IDispatch, (void**)&V_DISPATCH(&v)); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(V_DISPATCH(&v) != NULL, "failed to get IDispatch interface\n"); + hr = IXMLDOMDocument2_putref_schemas(doc, v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + VariantClear(&v); + + /* validate the doc + * only declared elements in the declared order + * this is fine */ + err = NULL; + bstr = NULL; + hr = IXMLDOMDocument2_validate(doc, &err); + ok(err != NULL, "domdoc_validate() should always set err\n"); + if (IXMLDOMParseError_get_reason(err, &bstr) != S_FALSE) + trace("got error: %s\n", wine_dbgstr_w(bstr)); + SysFreeString(bstr); + IXMLDOMParseError_Release(err); + + return hr; +} + +static void test_regex(void) +{ + static const struct regex_test + { + const WCHAR *regex; + const WCHAR *input; + } + tests[] = + { + { L"\\!", L"!" }, + { L"\\\"", L"\"" }, + { L"\\#", L"#" }, + { L"\\$", L"$" }, + { L"\\%", L"%" }, + { L"\\,", L"," }, + { L"\\/", L"/" }, + { L"\\:", L":" }, + { L"\\;", L";" }, + { L"\\=", L"=" }, + { L"\\>", L">" }, + { L"\\@", L"@" }, + { L"\\`", L"`" }, + { L"\\~", L"~" }, + { L"\\uCAFE", L"\xCAFE" }, + /* non-BMP character in surrogate pairs: */ + { L"\\uD83D\\uDE00", L"\xD83D\xDE00" }, + /* "x{,2}" is non-standard and only works on libxml2 <= v2.9.10 */ + { L"x{0,2}", L"x" } + }; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(tests); ++i) + { + const struct regex_test *test = &tests[i]; + IXMLDOMDocument2 *doc, *schema; + IXMLDOMSchemaCollection *cache; + HRESULT hr; + + winetest_push_context("Test %s", wine_dbgstr_w(test->regex)); + + doc = create_document(); + schema = create_document(); + cache = create_cache(&IID_IXMLDOMSchemaCollection); + + hr = validate_regex_document(doc, schema, cache, tests->regex, tests->input); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + if (doc) + IXMLDOMDocument2_Release(doc); + if (schema) + IXMLDOMDocument2_Release(schema); + if (cache) + IXMLDOMSchemaCollection_Release(cache); + + winetest_pop_context(); + } +} + +static void test_get(void) +{ + IXMLDOMSchemaCollection2 *cache; + IXMLDOMNode *node; + HRESULT hr; + + cache = create_cache(&IID_IXMLDOMSchemaCollection2); + + hr = IXMLDOMSchemaCollection2_get(cache, NULL, NULL); + ok(hr == E_NOTIMPL || hr == E_POINTER /* win8 */, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMSchemaCollection2_get(cache, _bstr_(L"uri"), &node); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + IXMLDOMSchemaCollection2_Release(cache); + free_bstrs(); +} + +static void test_ifaces(void) +{ + IXMLDOMSchemaCollection2 *cache; + IUnknown *unk; + HRESULT hr; + + cache = create_cache(&IID_IXMLDOMSchemaCollection2); + + /* CLSID_XMLSchemaCache60 is returned as an interface (the same as IXMLDOMSchemaCollection2). */ + hr = IXMLDOMSchemaCollection2_QueryInterface(cache, &CLSID_XMLSchemaCache60, (void**)&unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(unk == (IUnknown *)cache, "Unexpected pointer %p.\n", unk); + IUnknown_Release(unk); + + check_interface(cache, &IID_IXMLDOMSchemaCollection, TRUE); + check_interface(cache, &IID_IXMLDOMSchemaCollection2, TRUE); + check_interface(cache, &IID_IDispatch, TRUE); + check_interface(cache, &IID_IDispatchEx, TRUE); + + IXMLDOMSchemaCollection2_Release(cache); +} + +static void test_remove(void) +{ + IXMLDOMSchemaCollection2 *cache; + IXMLDOMDocument2 *doc; + VARIANT_BOOL b; + HRESULT hr; + VARIANT v; + LONG len; + + cache = create_cache(&IID_IXMLDOMSchemaCollection2); + + doc = create_document(); + ok(doc != NULL, "got %p\n", doc); + + hr = IXMLDOMDocument2_loadXML(doc, _bstr_(xsd_schema1_xml), &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)doc; + hr = IXMLDOMSchemaCollection2_add(cache, _bstr_(xsd_schema1_uri), v); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = IXMLDOMSchemaCollection2_get_length(cache, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 1, "Unexpected length %ld.\n", len); + + /* ::remove() is a stub for version 6 */ + hr = IXMLDOMSchemaCollection2_remove(cache, NULL); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMSchemaCollection2_remove(cache, _bstr_(L"invaliduri")); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + hr = IXMLDOMSchemaCollection2_remove(cache, _bstr_(xsd_schema1_uri)); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + len = -1; + hr = IXMLDOMSchemaCollection2_get_length(cache, &len); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(len == 1, "Unexpected length %ld.\n", len); + + IXMLDOMDocument2_Release(doc); + IXMLDOMSchemaCollection2_Release(cache); + free_bstrs(); +} + +static void test_obj_dispex(IUnknown *obj) +{ + DISPID dispid = DISPID_SAX_XMLREADER_GETFEATURE; + IDispatchEx *dispex; + IUnknown *unk; + DWORD props; + UINT ticnt; + HRESULT hr; + BSTR name; + + hr = IUnknown_QueryInterface(obj, &IID_IDispatchEx, (void**)&dispex); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + if (FAILED(hr)) return; + + ticnt = 0; + hr = IDispatchEx_GetTypeInfoCount(dispex, &ticnt); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(ticnt == 1, "ticnt=%u\n", ticnt); + + name = SysAllocString(L"*"); + hr = IDispatchEx_DeleteMemberByName(dispex, name, fdexNameCaseSensitive); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + hr = IDispatchEx_DeleteMemberByDispID(dispex, dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + props = 0; + hr = IDispatchEx_GetMemberProperties(dispex, dispid, grfdexPropCanAll, &props); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(props == 0, "expected 0 got %ld\n", props); + + hr = IDispatchEx_GetMemberName(dispex, dispid, &name); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + if (SUCCEEDED(hr)) SysFreeString(name); + + hr = IDispatchEx_GetNextDispID(dispex, fdexEnumDefault, DISPID_XMLDOM_SCHEMACOLLECTION_ADD, &dispid); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + + unk = (IUnknown*)0xdeadbeef; + hr = IDispatchEx_GetNameSpaceParent(dispex, &unk); + ok(hr == E_NOTIMPL, "Unexpected hr %#lx.\n", hr); + ok(unk == (IUnknown*)0xdeadbeef, "got %p\n", unk); + + name = SysAllocString(L"testprop"); + hr = IDispatchEx_GetDispID(dispex, name, fdexNameEnsure, &dispid); + ok(hr == DISP_E_UNKNOWNNAME, "Unexpected hr %#lx.\n", hr); + SysFreeString(name); + + IDispatchEx_Release(dispex); +} + +static void test_dispex(void) +{ + IXMLDOMSchemaCollection *cache; + IUnknown *unk; + HRESULT hr; + + cache = create_cache(&IID_IXMLDOMSchemaCollection); + + hr = IXMLDOMSchemaCollection_QueryInterface(cache, &IID_IUnknown, (void**)&unk); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + test_obj_dispex(unk); + IUnknown_Release(unk); + + IXMLDOMSchemaCollection_Release(cache); +} + +static void test_validate_on_load(void) +{ + IXMLDOMSchemaCollection2 *cache; + VARIANT_BOOL b; + HRESULT hr; + + cache = create_cache(&IID_IXMLDOMSchemaCollection2); + + hr = IXMLDOMSchemaCollection2_get_validateOnLoad(cache, NULL); + ok(hr == E_POINTER, "Unexpected hr %#lx.\n", hr); + + b = VARIANT_FALSE; + hr = IXMLDOMSchemaCollection2_get_validateOnLoad(cache, &b); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + ok(b == VARIANT_TRUE, "got %d\n", b); + + IXMLDOMSchemaCollection2_Release(cache); +} + +START_TEST(schema) +{ + IUnknown *obj; + HRESULT hr; + + hr = CoInitialize(NULL); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = CoCreateInstance(&CLSID_DOMDocument60, NULL, CLSCTX_INPROC_SERVER, &IID_IXMLDOMDocument2, (void **)&obj); + if (FAILED(hr)) + { + win_skip("DOMDocument60 is not supported.\n"); + CoUninitialize(); + return; + } + IUnknown_Release(obj); + + test_regex(); + test_get(); + test_ifaces(); + test_remove(); + test_dispex(); + test_validate_on_load(); + + CoUninitialize(); +} diff --git a/modules/rostests/winetests/msxml6/testlist.c b/modules/rostests/winetests/msxml6/testlist.c new file mode 100644 index 00000000000..57fce1a9c16 --- /dev/null +++ b/modules/rostests/winetests/msxml6/testlist.c @@ -0,0 +1,16 @@ +/* Automatically generated file; DO NOT EDIT!! */ + +#define STANDALONE +#include + +extern void func_domdoc(void); +extern void func_saxreader(void); +extern void func_schema(void); + +const struct test winetest_testlist[] = +{ + { "domdoc", func_domdoc }, + { "saxreader", func_saxreader }, + { "schema", func_schema }, + { 0, 0 } +};