diff --git a/dlls/msxml3/tests/schema.c b/dlls/msxml3/tests/schema.c
index d1622ffc7da..b66be76c159 100644
--- a/dlls/msxml3/tests/schema.c
+++ b/dlls/msxml3/tests/schema.c
@@ -1012,6 +1012,159 @@ static void test_collection_content(void)
free_bstrs();
}
+static HRESULT validate_regex_document(IXMLDOMDocument2 *doc, IXMLDOMDocument2 *schema, IXMLDOMSchemaCollection* cache,
+ const WCHAR *regex, const WCHAR *input)
+{
+ static const WCHAR regex_doc[] =
+L""
+""
+"%s";
+
+ static const WCHAR regex_schema[] =
+L""
+""
+" "
+" "
+" "
+" "
+" "
+" "
+" "
+"";
+
+ WCHAR buffer[1024];
+ IXMLDOMParseError* err;
+ VARIANT v;
+ VARIANT_BOOL b;
+ BSTR namespace;
+ BSTR bstr;
+ HRESULT hr;
+
+ VariantInit(&v);
+
+ swprintf(buffer, ARRAY_SIZE(buffer), regex_doc, input);
+ bstr = SysAllocString(buffer);
+ ole_check(IXMLDOMDocument2_loadXML(doc, bstr, &b));
+ ok(b == VARIANT_TRUE, "failed to load XML\n");
+ SysFreeString(bstr);
+
+ swprintf(buffer, ARRAY_SIZE(buffer), regex_schema, regex);
+ bstr = SysAllocString(buffer);
+ ole_check(IXMLDOMDocument2_loadXML(schema, bstr, &b));
+ ok(b == VARIANT_TRUE, "failed to load XML\n");
+ SysFreeString(bstr);
+
+ /* add the schema to the cache */
+ V_VT(&v) = VT_DISPATCH;
+ V_DISPATCH(&v) = NULL;
+ ole_check(IXMLDOMDocument2_QueryInterface(schema, &IID_IDispatch, (void**)&V_DISPATCH(&v)));
+ ok(V_DISPATCH(&v) != NULL, "failed to get IDispatch interface\n");
+ namespace = alloc_str_from_narrow("urn:test");
+ hr = IXMLDOMSchemaCollection_add(cache, namespace, v);
+ SysFreeString(namespace);
+ VariantClear(&v);
+ if (FAILED(hr))
+ return hr;
+
+ /* associate the cache to the doc */
+ V_VT(&v) = VT_DISPATCH;
+ V_DISPATCH(&v) = NULL;
+ ole_check(IXMLDOMSchemaCollection_QueryInterface(cache, &IID_IDispatch, (void**)&V_DISPATCH(&v)));
+ ok(V_DISPATCH(&v) != NULL, "failed to get IDispatch interface\n");
+ ole_check(IXMLDOMDocument2_putref_schemas(doc, v));
+ VariantClear(&v);
+
+ /* validate the doc
+ * only declared elements in the declared order
+ * this is fine */
+ err = NULL;
+ bstr = NULL;
+ hr = IXMLDOMDocument2_validate(doc, &err);
+ ok(err != NULL, "domdoc_validate() should always set err\n");
+ if (IXMLDOMParseError_get_reason(err, &bstr) != S_FALSE)
+ trace("got error: %s\n", wine_dbgstr_w(bstr));
+ SysFreeString(bstr);
+ IXMLDOMParseError_Release(err);
+
+ return hr;
+}
+
+static void test_regex(void)
+{
+ struct regex_test {
+ const WCHAR *regex;
+ const WCHAR *input;
+ BOOL todo;
+ };
+
+ struct regex_test tests[] = {
+ { L"\\!", L"!", TRUE },
+ { L"\\\"", L"\"", TRUE },
+ { L"\\#", L"#", TRUE },
+ { L"\\$", L"$", TRUE },
+ { L"\\%", L"%", TRUE },
+ { L"\\,", L",", TRUE },
+ { L"\\/", L"/", TRUE },
+ { L"\\:", L":", TRUE },
+ { L"\\;", L";", TRUE },
+ { L"\\=", L"=", TRUE },
+ { L"\\>", L">", TRUE },
+ { L"\\@", L"@", TRUE },
+ { L"\\`", L"`", TRUE },
+ { L"\\~", L"~", TRUE },
+ { L"\\uCAFE", L"\xCAFE", TRUE },
+ /* non-BMP character in surrogate pairs: */
+ { L"\\uD83D\\uDE00", L"\xD83D\xDE00", TRUE }
+ };
+
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(tests); i++)
+ {
+ IXMLDOMDocument2 *doc40, *doc60;
+ IXMLDOMDocument2 *schema40, *schema60;
+ IXMLDOMSchemaCollection *cache40, *cache60;
+
+ doc40 = create_document_version(40, &IID_IXMLDOMDocument2);
+ doc60 = create_document_version(60, &IID_IXMLDOMDocument2);
+ schema40 = create_document_version(40, &IID_IXMLDOMDocument2);
+ schema60 = create_document_version(60, &IID_IXMLDOMDocument2);
+ cache40 = create_cache_version(40, &IID_IXMLDOMSchemaCollection);
+ cache60 = create_cache_version(60, &IID_IXMLDOMSchemaCollection);
+
+ if (doc60 && schema60 && cache60)
+ {
+ HRESULT hr = validate_regex_document(doc60, schema60, cache60, tests[i].regex, tests[i].input);
+ todo_wine_if(tests[i].todo)
+ ok(hr == S_OK, "got 0x%08x for version 60 regex %s input %s\n",
+ hr, wine_dbgstr_w(tests[i].regex), wine_dbgstr_w(tests[i].input));
+ if (doc40 && schema40 && cache40)
+ {
+ hr = validate_regex_document(doc40, schema40, cache40, tests[i].regex, tests[i].input);
+ todo_wine_if(tests[i].todo)
+ ok(hr == S_OK, "got 0x%08x for version 40 regex %s input %s\n",
+ hr, wine_dbgstr_w(tests[i].regex), wine_dbgstr_w(tests[i].input));
+ }
+ }
+ else
+ ok(0, "out of memory\n");
+
+ if (doc40)
+ IXMLDOMDocument2_Release(doc40);
+ if (doc60)
+ IXMLDOMDocument2_Release(doc60);
+ if (schema40)
+ IXMLDOMDocument2_Release(schema40);
+ if (schema60)
+ IXMLDOMDocument2_Release(schema60);
+ if (cache40)
+ IXMLDOMSchemaCollection_Release(cache40);
+ if (cache60)
+ IXMLDOMSchemaCollection_Release(cache60);
+ }
+}
+
static void test_XDR_schemas(void)
{
IXMLDOMDocument2 *doc, *schema;
@@ -1669,6 +1822,7 @@ START_TEST(schema)
test_collection_refs();
test_length();
test_collection_content();
+ test_regex();
test_XDR_schemas();
test_XDR_datatypes();
test_validate_on_load();