[PATCH 0/1] MR5334: msxml6/tests: Add some tests for MXWriter60.
Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> -- https://gitlab.winehq.org/wine/wine/-/merge_requests/5334
From: Nikolay Sivov <nsivov(a)codeweavers.com> Signed-off-by: Nikolay Sivov <nsivov(a)codeweavers.com> --- dlls/msxml6/tests/Makefile.in | 3 +- dlls/msxml6/tests/saxreader.c | 2871 +++++++++++++++++++++++++++++++++ 2 files changed, 2873 insertions(+), 1 deletion(-) create mode 100644 dlls/msxml6/tests/saxreader.c diff --git a/dlls/msxml6/tests/Makefile.in b/dlls/msxml6/tests/Makefile.in index 0dc61bd2019..6f70a0494ec 100644 --- a/dlls/msxml6/tests/Makefile.in +++ b/dlls/msxml6/tests/Makefile.in @@ -2,4 +2,5 @@ TESTDLL = msxml6.dll IMPORTS = oleaut32 ole32 SOURCES = \ - domdoc.c + domdoc.c \ + saxreader.c diff --git a/dlls/msxml6/tests/saxreader.c b/dlls/msxml6/tests/saxreader.c new file mode 100644 index 00000000000..de49f9890b0 --- /dev/null +++ b/dlls/msxml6/tests/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 <stdio.h> +#include <assert.h> + +#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 REFIID riids[] = + { + &IID_ISAXContentHandler, + &IID_ISAXLexicalHandler, + &IID_ISAXDeclHandler, + &IID_ISAXDTDHandler, + &IID_ISAXErrorHandler, + &IID_IVBSAXDeclHandler, + &IID_IVBSAXLexicalHandler, + &IID_IVBSAXContentHandler, + &IID_IVBSAXDTDHandler, + &IID_IVBSAXErrorHandler + }; + + hr = CoCreateInstance(&CLSID_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"<?xml version=\"1.0\" standalone=\"yes\"?>\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("<a>"); + 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"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\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_("<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n" + "<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\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[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&">\'\">"; +static const char startendelement_xml[] = "<uri:local a:attr1=\"a1\" attr2=\"a2\" attr3=\"<&">\'\"/>"; + +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", "<qname>", S_OK }, + { StartElement, "uri", "local", "qname", "<qname>", S_OK }, + { StartElement, "uri", "local", NULL, "<>", S_OK }, + { StartElement, "uri", "local", "uri:local", "<uri:local>", S_OK }, + { StartElement, "uri", "local", "uri:local2", "<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", "</qname>", S_OK }, + { EndElement, "uri", "local", "qname", "</qname>", S_OK }, + { EndElement, "uri", "local", NULL, "</>", S_OK }, + { EndElement, "uri", "local", "uri:local", "</uri:local>", S_OK }, + { EndElement, "uri", "local", "uri:local2", "</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, "<a/>", E_INVALIDARG }, + { "a", 1, "b", 1, "<a/>", 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"<b><>", 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"<b><><b>", 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"<b><><b></b></b></></b></></b>", 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"<><b>", 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"<><b></a:b></a:b></></b>", 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"<abc>", 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"<abc></abd>", 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"<a></a>", 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"<BankAccount>" + "<Number>12345</Number>" + "<Name>Captain Ahab</Name>" + "</BankAccount>\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"<?xml version=\"1.0\" standalone=\"no\"?>\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, "<a/>", 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, "<?xml version=\"1.0\" encoding=\""); + strcat(expectedA, enc); + strcat(expectedA, "\" standalone=\"no\"?>\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<!--comment-->\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"<![CDATA[", V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = IVBSAXLexicalHandler_startCDATA(vblexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + /* all these are escaped for text nodes */ + hr = ISAXContentHandler_characters(content, _bstr_("< > & \""), 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"<![CDATA[<![CDATA[< > & \"]]>", 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<?target?>\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<?target?>\r\n<?targ data?>\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"<?target?>\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"<!DOCTYPE [\r\n<!DOCTYPE PUBLIC \"pub\"<!DOCTYPE SYSTEM \"sys\" [\r\n" + "<!DOCTYPE PUBLIC \"pub\" \"sys\" [\r\n<!DOCTYPE name [\r\n", V_BSTR(&dest)), "Unexpected content %s.\n", wine_dbgstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + /* system id is required if public is present */ + hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, L"pub", 3, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXLexicalHandler_startDTD(lexical, L"name", 4, L"pub", 3, L"sys", 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)); + todo_wine + ok(!lstrcmpW(L"<!DOCTYPE [\r\n<!DOCTYPE PUBLIC \"pub\"<!DOCTYPE SYSTEM \"sys\" [\r\n" + "<!DOCTYPE PUBLIC \"pub\" \"sys\" [\r\n<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\"<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n", + V_BSTR(&dest)), "Unexpected content %s.\n", debugstr_w(V_BSTR(&dest))); + VariantClear(&dest); + + hr = ISAXLexicalHandler_endDTD(lexical); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = IVBSAXLexicalHandler_endDTD(vblexical); + 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"<!DOCTYPE [\r\n<!DOCTYPE PUBLIC \"pub\"<!DOCTYPE SYSTEM \"sys\" [\r\n" + "<!DOCTYPE PUBLIC \"pub\" \"sys\" [\r\n<!DOCTYPE name [\r\n<!DOCTYPE name PUBLIC \"pub\"<!DOCTYPE name PUBLIC \"pub\" \"sys\" [\r\n]>\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"<!ELEMENT >\r\n<!ELEMENT name >\r\n<!ELEMENT name content>\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"<!ELEMENT name >\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"<!ATTLIST element attribute CDATA #REQUIRED>\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"<!ATTLIST element attribute CDATA #REQUIRED>\r\n" + "<!ATTLIST element attribute2 CDATA #REQUIRED>\r\n<!ATTLIST element2 attribute3 CDATA #REQUIRED>\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"<!ENTITY \"\">\r\n<!ENTITY name \"value\">\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"<!ENTITY <!ENTITY <!ENTITY name PUBLIC \"pubid\" \"sysid\">\r\n" + "<!ENTITY name SYSTEM \"sysid\">\r\n<!ENTITY name PUBLIC \"pubid\"", + V_BSTR(&dest)), "got wrong content %s\n", wine_dbgstr_w(V_BSTR(&dest))); + + VariantClear(&dest); + + /* notation declaration */ + hr = IMXWriter_QueryInterface(writer, &IID_ISAXDTDHandler, (void**)&dtd); + ok(hr == S_OK, "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); + + hr = ISAXDTDHandler_notationDecl(dtd, NULL, 0, NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, NULL, 0); + ok(hr == E_INVALIDARG, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), NULL, 0); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), _bstr_("pubid"), strlen("pubid"), _bstr_("sysid"), strlen("sysid")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + hr = ISAXDTDHandler_notationDecl(dtd, _bstr_("name"), strlen("name"), NULL, 0, _bstr_("sysid"), strlen("sysid")); + ok(hr == S_OK, "Unexpected hr %#lx.\n", hr); + + 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"<!NOTATION <!NOTATION name<!NOTATION name PUBLIC \"pubid\">\r\n" + "<!NOTATION name PUBLIC \"pubid\" \"sysid\">\r\n" + "<!NOTATION name SYSTEM \"sysid\">\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"<?xml version=\"1.0\" encoding=\"UTF-16\" standalone=\"no\"?>\r\n<a><b>\r\n\t\t<c/>\r\n\t</b>\r\n</a>", 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(); +} -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/5334
Hi, It looks like your patch introduced the new failures shown below. Please investigate and fix them before resubmitting your patch. If they are not new, fixing them anyway would help a lot. Otherwise please ask for the known failures list to be updated. The full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=144124 Your paranoid android. === w7u_2qxl (32 bit report) === msxml6: 0784:saxreader: unhandled exception c0000005 at 7143C7F5 === w7u_adm (32 bit report) === msxml6: 0874:saxreader: unhandled exception c0000005 at 71CCC7F5 === w7u_el (32 bit report) === msxml6: 0a5c:saxreader: unhandled exception c0000005 at 70AFC7F5 === w7pro64 (64 bit report) === msxml6: 0928:saxreader: unhandled exception c0000005 at 000007FEF93529F4
participants (3)
-
Marvin -
Nikolay Sivov -
Nikolay Sivov (@nsivov)