[SHLWAPI_WINETEST] Sync with Wine Staging 2.2. CORE-12823
[reactos.git] / rostests / winetests / shlwapi / url.c
index abdf880..f3756e4 100644 (file)
@@ -198,9 +198,26 @@ static const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
     {"res://c:\\tests/res\\foo%20bar/strange\\sth", 0, S_OK, "res://c:\\tests/res\\foo%20bar/strange\\sth", FALSE},
     {"res://c:\\tests/res\\foo%20bar/strange\\sth", URL_FILE_USE_PATHURL, S_OK, "res://c:\\tests/res\\foo%20bar/strange\\sth", FALSE},
     {"res://c:\\tests/res\\foo%20bar/strange\\sth", URL_UNESCAPE, S_OK, "res://c:\\tests/res\\foo bar/strange\\sth", FALSE},
+    {"/A/../B/./C/../../test_remove_dot_segments", 0, S_OK, "/test_remove_dot_segments", FALSE},
+    {"/A/../B/./C/../../test_remove_dot_segments", URL_FILE_USE_PATHURL, S_OK, "/test_remove_dot_segments", FALSE},
+    {"/A/../B/./C/../../test_remove_dot_segments", URL_WININET_COMPATIBILITY, S_OK, "/test_remove_dot_segments", FALSE},
+    {"/A/B\\C/D\\E", 0, S_OK, "/A/B\\C/D\\E", FALSE},
+    {"/A/B\\C/D\\E", URL_FILE_USE_PATHURL, S_OK, "/A/B\\C/D\\E", FALSE},
+    {"/A/B\\C/D\\E", URL_WININET_COMPATIBILITY, S_OK, "/A/B\\C/D\\E", FALSE},
+    {"///A/../B", 0, S_OK, "///B", FALSE},
+    {"///A/../B", URL_FILE_USE_PATHURL, S_OK, "///B", FALSE},
+    {"///A/../B", URL_WININET_COMPATIBILITY, S_OK, "///B", FALSE},
     {"A", 0, S_OK, "A", FALSE},
     {"../A", 0, S_OK, "../A", FALSE},
+    {".\\A", 0, S_OK, ".\\A", FALSE},
+    {"A\\.\\B", 0, S_OK, "A\\.\\B", FALSE},
     {"A/../B", 0, S_OK, "B", TRUE},
+    {"A/../B/./../C", 0, S_OK, "C", TRUE},
+    {"A/../B/./../C", URL_DONT_SIMPLIFY, S_OK, "A/../B/./../C", FALSE},
+    {".", 0, S_OK, "/", TRUE},
+    {"./A", 0, S_OK, "A", TRUE},
+    {"A/./B", 0, S_OK, "A/B", TRUE},
+    {"/:test\\", 0, S_OK, "/:test\\", TRUE},
     {"/uri-res/N2R?urn:sha1:B3K", URL_DONT_ESCAPE_EXTRA_INFO | URL_WININET_COMPATIBILITY /*0x82000000*/, S_OK, "/uri-res/N2R?urn:sha1:B3K", FALSE} /*LimeWire online installer calls this*/,
     {"http:www.winehq.org/dir/../index.html", 0, S_OK, "http:www.winehq.org/index.html"},
     {"http://localhost/test.html", URL_FILE_USE_PATHURL, S_OK, "http://localhost/test.html"},
@@ -302,7 +319,48 @@ static const TEST_URL_ESCAPE TEST_ESCAPE[] = {
     {"ftp://fo/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo#o\\bar"},
     {"ftp://localhost/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://localhost/o@bar.baz/fo#o\\bar"},
     {"ftp:///fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:///fo/o@bar.baz/foo/bar"},
-    {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"}
+    {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"},
+
+    {"ftp\x1f\1end/", 0, 0, S_OK, "ftp%1F%01end/"}
+};
+
+typedef struct _TEST_URL_ESCAPEW {
+    const WCHAR url[INTERNET_MAX_URL_LENGTH];
+    DWORD flags;
+    HRESULT expectret;
+    const WCHAR expecturl[INTERNET_MAX_URL_LENGTH];
+    const WCHAR win7url[INTERNET_MAX_URL_LENGTH];  /* <= Win7 */
+    const WCHAR vistaurl[INTERNET_MAX_URL_LENGTH]; /* <= Vista/2k8 */
+} TEST_URL_ESCAPEW;
+
+static const TEST_URL_ESCAPEW TEST_ESCAPEW[] = {
+    {{' ','<','>','"',0},  URL_ESCAPE_AS_UTF8, S_OK, {'%','2','0','%','3','C','%','3','E','%','2','2',0}},
+    {{'{','}','|','\\',0}, URL_ESCAPE_AS_UTF8, S_OK, {'%','7','B','%','7','D','%','7','C','%','5','C',0}},
+    {{'^',']','[','`',0},  URL_ESCAPE_AS_UTF8, S_OK, {'%','5','E','%','5','D','%','5','B','%','6','0',0}},
+    {{'&','/','?','#',0},  URL_ESCAPE_AS_UTF8, S_OK, {'%','2','6','/','?','#',0}},
+    {{'M','a','s','s',0},  URL_ESCAPE_AS_UTF8, S_OK, {'M','a','s','s',0}},
+
+    /* broken < Win8/10 */
+
+    {{'M','a',0xdf,0},  URL_ESCAPE_AS_UTF8, S_OK, {'M','a','%','C','3','%','9','F',0},
+                                                  {'M','a','%','D','F',0}},
+    /* 0x2070E */
+    {{0xd841,0xdf0e,0}, URL_ESCAPE_AS_UTF8, S_OK, {'%','F','0','%','A','0','%','9','C','%','8','E',0},
+                                                  {'%','E','F','%','B','F','%','B','D','%','E','F','%','B','F','%','B','D',0},
+                                                  {0xd841,0xdf0e,0}},
+    /* 0x27A3E */
+    {{0xd85e,0xde3e,0}, URL_ESCAPE_AS_UTF8, S_OK, {'%','F','0','%','A','7','%','A','8','%','B','E',0},
+                                                  {'%','E','F','%','B','F','%','B','D','%','E','F','%','B','F','%','B','D',0},
+                                                  {0xd85e,0xde3e,0}},
+
+    {{0xd85e,0},        URL_ESCAPE_AS_UTF8, S_OK, {'%','E','F','%','B','F','%','B','D',0},
+                                                  {0xd85e,0}},
+    {{0xd85e,0x41},     URL_ESCAPE_AS_UTF8, S_OK, {'%','E','F','%','B','F','%','B','D','A',0},
+                                                  {0xd85e,'A',0}},
+    {{0xdc00,0},        URL_ESCAPE_AS_UTF8, S_OK, {'%','E','F','%','B','F','%','B','D',0},
+                                                  {0xdc00,0}},
+    {{0xffff,0},        URL_ESCAPE_AS_UTF8, S_OK, {'%','E','F','%','B','F','%','B','F',0},
+                                                  {0xffff,0}},
 };
 
 /* ################ */
@@ -313,6 +371,7 @@ typedef struct _TEST_URL_COMBINE {
     DWORD flags;
     HRESULT expectret;
     const char *expecturl;
+    BOOL todo;
 } TEST_URL_COMBINE;
 
 static const TEST_URL_COMBINE TEST_COMBINE[] = {
@@ -332,6 +391,17 @@ static const TEST_URL_COMBINE TEST_COMBINE[] = {
     {"http://www.winehq.org/test12", "#", 0, S_OK, "http://www.winehq.org/test12#"},
     {"http://www.winehq.org/test13#aaa", "#bbb", 0, S_OK, "http://www.winehq.org/test13#bbb"},
     {"http://www.winehq.org/test14#aaa/bbb#ccc", "#", 0, S_OK, "http://www.winehq.org/test14#"},
+    {"http://www.winehq.org/tests/?query=x/y/z", "tests15", 0, S_OK, "http://www.winehq.org/tests/tests15"},
+    {"http://www.winehq.org/tests/?query=x/y/z#example", "tests16", 0, S_OK, "http://www.winehq.org/tests/tests16"},
+    {"http://www.winehq.org/tests17", ".", 0, S_OK, "http://www.winehq.org/"},
+    {"http://www.winehq.org/tests18/test", ".", 0, S_OK, "http://www.winehq.org/tests18/"},
+    {"http://www.winehq.org/tests19/test", "./", 0, S_OK, "http://www.winehq.org/tests19/", FALSE},
+    {"http://www.winehq.org/tests20/test", "/", 0, S_OK, "http://www.winehq.org/", FALSE},
+    {"http://www.winehq.org/tests/test", "./test21", 0, S_OK, "http://www.winehq.org/tests/test21", FALSE},
+    {"http://www.winehq.org/tests/test", "./test22/../test", 0, S_OK, "http://www.winehq.org/tests/test", FALSE},
+    {"http://www.winehq.org/tests/", "http://www.winehq.org:80/tests23", 0, S_OK, "http://www.winehq.org/tests23", TRUE},
+    {"http://www.winehq.org/tests/", "tests24/./test/../test", 0, S_OK, "http://www.winehq.org/tests/tests24/test", FALSE},
+    {"http://www.winehq.org/tests/./tests25", "./", 0, S_OK, "http://www.winehq.org/tests/", FALSE},
     {"file:///C:\\dir\\file.txt", "test.txt", 0, S_OK, "file:///C:/dir/test.txt"},
     {"file:///C:\\dir\\file.txt#hash\\hash", "test.txt", 0, S_OK, "file:///C:/dir/file.txt#hash/test.txt"},
     {"file:///C:\\dir\\file.html#hash\\hash", "test.html", 0, S_OK, "file:///C:/dir/test.html"},
@@ -782,34 +852,6 @@ static void test_UrlGetPart(void)
 }
 
 /* ########################### */
-
-static void test_url_escape(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
-{
-    CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
-    DWORD dwEscaped;
-    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
-    WCHAR *urlW, *expected_urlW;
-    dwEscaped=INTERNET_MAX_URL_LENGTH;
-
-    ok(pUrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn,
-        "UrlEscapeA didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
-    ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", szExpectUrl, szReturnUrl, szUrl);
-
-    if (pUrlEscapeW) {
-        dwEscaped = INTERNET_MAX_URL_LENGTH;
-        urlW = GetWideString(szUrl);
-        expected_urlW = GetWideString(szExpectUrl);
-        ok(pUrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn,
-            "UrlEscapeW didn't return 0x%08x from \"%s\"\n", dwExpectReturn, szUrl);
-        WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
-        ok(lstrcmpW(ret_urlW, expected_urlW)==0,
-            "Expected \"%s\", but got \"%s\" from \"%s\" flags %08x\n",
-            szExpectUrl, szReturnUrl, szUrl, dwFlags);
-        FreeWideString(urlW);
-        FreeWideString(expected_urlW);
-    }
-}
-
 static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, HRESULT dwExpectReturnAlt, const char *szExpectUrl, BOOL todo)
 {
     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
@@ -828,10 +870,7 @@ static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, H
     ok(ret == dwExpectReturn || ret == dwExpectReturnAlt,
        "UrlCanonicalizeA failed: expected=0x%08x or 0x%08x, got=0x%08x, index %d\n",
        dwExpectReturn, dwExpectReturnAlt, ret, index);
-    if (todo)
-        todo_wine
-        ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x url '%s' Expected \"%s\", but got \"%s\", index %d\n", dwFlags, szUrl, szExpectUrl, szReturnUrl, index);
-    else
+    todo_wine_if (todo)
         ok(strcmp(szReturnUrl,szExpectUrl)==0, "UrlCanonicalizeA dwFlags 0x%08x url '%s' Expected \"%s\", but got \"%s\", index %d\n", dwFlags, szUrl, szExpectUrl, szReturnUrl, index);
 
     if (pUrlCanonicalizeW) {
@@ -853,18 +892,15 @@ static void test_url_canonicalize(int index, const char *szUrl, DWORD dwFlags, H
 }
 
 
-static void test_UrlEscape(void)
+static void test_UrlEscapeA(void)
 {
-    static const WCHAR out[] = { 'f','o','o','%','2','0','b','a','r',0 };
-
     DWORD size = 0;
     HRESULT ret;
     unsigned int i;
     char empty_string[] = "";
-    WCHAR overwrite[] = { 'f','o','o',' ','b','a','r',0,0,0 };
 
     if (!pUrlEscapeA) {
-        win_skip("UrlEscapeA noz found\n");
+        win_skip("UrlEscapeA not found\n");
         return;
     }
 
@@ -894,26 +930,130 @@ static void test_UrlEscape(void)
     ok(size == 34, "got %d, expected %d\n", size, 34);
     ok(empty_string[0] == 127, "String has changed, empty_string[0] = %d\n", empty_string[0]);
 
-    if(pUrlEscapeW) {
-        WCHAR wc;
-
-        size = sizeof(overwrite)/sizeof(WCHAR);
-        ret = pUrlEscapeW(overwrite, overwrite, &size, URL_ESCAPE_SPACES_ONLY);
-        ok(ret == S_OK, "got %x, expected S_OK\n", ret);
-        ok(size == 9, "got %d, expected 9\n", size);
-        ok(!lstrcmpW(overwrite, out), "got %s, expected %s\n", wine_dbgstr_w(overwrite), wine_dbgstr_w(out));
-
-        size = 1;
-        wc = 127;
-        ret = pUrlEscapeW(overwrite, &wc, &size, URL_ESCAPE_SPACES_ONLY);
-        ok(ret == E_POINTER, "got %x, expected %x\n", ret, E_POINTER);
-        ok(size == 10, "got %d, expected 10\n", size);
-        ok(wc == 127, "String has changed, wc = %d\n", wc);
-    }
+    size = 1;
+    empty_string[0] = 127;
+    ret = pUrlEscapeA("/woningplan/woonkamer basis.swf", empty_string, &size, URL_ESCAPE_AS_UTF8);
+    ok(ret == E_NOTIMPL || broken(ret == E_POINTER), /* < Win7/Win2k8 */
+        "got %x, expected %x\n", ret, E_NOTIMPL);
+    ok(size == 1 || broken(size == 34), /* < Win7/Win2k8 */
+        "got %d, expected %d\n", size, 1);
+    ok(empty_string[0] == 127, "String has changed, empty_string[0] = %d\n", empty_string[0]);
 
     for(i=0; i<sizeof(TEST_ESCAPE)/sizeof(TEST_ESCAPE[0]); i++) {
-        test_url_escape(TEST_ESCAPE[i].url, TEST_ESCAPE[i].flags,
-                              TEST_ESCAPE[i].expectret, TEST_ESCAPE[i].expecturl);
+        CHAR ret_url[INTERNET_MAX_URL_LENGTH];
+
+        size = INTERNET_MAX_URL_LENGTH;
+        ret = pUrlEscapeA(TEST_ESCAPE[i].url, ret_url, &size, TEST_ESCAPE[i].flags);
+        ok(ret == TEST_ESCAPE[i].expectret, "UrlEscapeA returned 0x%08x instead of 0x%08x for \"%s\"\n",
+            ret, TEST_ESCAPE[i].expectret, TEST_ESCAPE[i].url);
+        ok(!strcmp(ret_url, TEST_ESCAPE[i].expecturl), "Expected \"%s\", but got \"%s\" for \"%s\"\n",
+            TEST_ESCAPE[i].expecturl, ret_url, TEST_ESCAPE[i].url);
+    }
+}
+
+static void test_UrlEscapeW(void)
+{
+    static const WCHAR path_test[] = {'/','t','e','s','t',0};
+    static const WCHAR naW[] = {'f','t','p',31,255,250,0x2122,'e','n','d','/',0};
+    static const WCHAR naescapedW[] = {'f','t','p','%','1','F','%','F','F','%','F','A',0x2122,'e','n','d','/',0};
+    static const WCHAR out[] = {'f','o','o','%','2','0','b','a','r',0};
+    WCHAR overwrite[] = {'f','o','o',' ','b','a','r',0,0,0};
+    WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
+    WCHAR empty_string[] = {0};
+    DWORD size;
+    HRESULT ret;
+    WCHAR wc;
+    int i;
+
+    if (!pUrlEscapeW) {
+        win_skip("UrlEscapeW not found\n");
+        return;
+    }
+
+    /* Check error paths */
+
+    ret = UrlEscapeW(path_test, NULL, NULL, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+
+    size = 0;
+    ret = UrlEscapeW(path_test, NULL, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+    ok(size == 0, "got %d, expected %d\n", size, 0);
+
+    ret = UrlEscapeW(path_test, empty_string, NULL, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+
+    size = 0;
+    ret = UrlEscapeW(path_test, empty_string, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+    ok(size == 0, "got %d, expected %d\n", size, 0);
+
+    ret = UrlEscapeW(path_test, NULL, NULL, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+
+    size = 1;
+    ret = UrlEscapeW(path_test, NULL, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+    ok(size == 1, "got %d, expected %d\n", size, 1);
+
+    ret = UrlEscapeW(path_test, empty_string, NULL, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_INVALIDARG, "got %x, expected %x\n", ret, E_INVALIDARG);
+
+    size = 1;
+    ret = UrlEscapeW(path_test, empty_string, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_POINTER, "got %x, expected %x\n", ret, E_POINTER);
+    ok(size == 6, "got %d, expected %d\n", size, 6);
+
+    /* Check actual escaping */
+
+    size = sizeof(overwrite)/sizeof(WCHAR);
+    ret = pUrlEscapeW(overwrite, overwrite, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == S_OK, "got %x, expected S_OK\n", ret);
+    ok(size == 9, "got %d, expected 9\n", size);
+    ok(!lstrcmpW(overwrite, out), "got %s, expected %s\n", wine_dbgstr_w(overwrite), wine_dbgstr_w(out));
+
+    size = 1;
+    wc = 127;
+    ret = pUrlEscapeW(overwrite, &wc, &size, URL_ESCAPE_SPACES_ONLY);
+    ok(ret == E_POINTER, "got %x, expected %x\n", ret, E_POINTER);
+    ok(size == 10, "got %d, expected 10\n", size);
+    ok(wc == 127, "String has changed, wc = %d\n", wc);
+
+    /* non-ASCII range */
+    size = sizeof(ret_urlW)/sizeof(WCHAR);
+    ret = pUrlEscapeW(naW, ret_urlW, &size, 0);
+    ok(ret == S_OK, "got %x, expected S_OK\n", ret);
+    ok(!lstrcmpW(naescapedW, ret_urlW), "got %s, expected %s\n", wine_dbgstr_w(ret_urlW), wine_dbgstr_w(naescapedW));
+
+    for (i = 0; i < sizeof(TEST_ESCAPE)/sizeof(TEST_ESCAPE[0]); i++) {
+
+        WCHAR *urlW, *expected_urlW;
+
+        size = INTERNET_MAX_URL_LENGTH;
+        urlW = GetWideString(TEST_ESCAPE[i].url);
+        expected_urlW = GetWideString(TEST_ESCAPE[i].expecturl);
+        ret = pUrlEscapeW(urlW, ret_urlW, &size, TEST_ESCAPE[i].flags);
+        ok(ret == TEST_ESCAPE[i].expectret, "UrlEscapeW returned 0x%08x instead of 0x%08x for %s\n",
+           ret, TEST_ESCAPE[i].expectret, wine_dbgstr_w(urlW));
+        ok(!lstrcmpW(ret_urlW, expected_urlW), "Expected %s, but got %s for %s flags %08x\n",
+            wine_dbgstr_w(expected_urlW), wine_dbgstr_w(ret_urlW), wine_dbgstr_w(urlW), TEST_ESCAPE[i].flags);
+        FreeWideString(urlW);
+        FreeWideString(expected_urlW);
+    }
+
+    for(i=0; i<sizeof(TEST_ESCAPEW)/sizeof(TEST_ESCAPEW[0]); i++) {
+        WCHAR ret_url[INTERNET_MAX_URL_LENGTH];
+
+        size = INTERNET_MAX_URL_LENGTH;
+        ret = pUrlEscapeW(TEST_ESCAPEW[i].url, ret_url, &size, TEST_ESCAPEW[i].flags);
+        ok(ret == TEST_ESCAPEW[i].expectret, "UrlEscapeW returned 0x%08x instead of 0x%08x for \"%s\"\n",
+           ret, TEST_ESCAPEW[i].expectret, wine_dbgstr_w(TEST_ESCAPEW[i].url));
+        ok(!lstrcmpW(ret_url, TEST_ESCAPEW[i].expecturl) ||
+           broken(!lstrcmpW(ret_url, TEST_ESCAPEW[i].vistaurl)) ||
+           broken(!lstrcmpW(ret_url, TEST_ESCAPEW[i].win7url)),
+            "Expected \"%s\" or \"%s\" or \"%s\", but got \"%s\" for \"%s\"\n",
+            wine_dbgstr_w(TEST_ESCAPEW[i].expecturl), wine_dbgstr_w(TEST_ESCAPEW[i].vistaurl),
+            wine_dbgstr_w(TEST_ESCAPEW[i].win7url), wine_dbgstr_w(ret_url), wine_dbgstr_w(TEST_ESCAPEW[i].url));
     }
 }
 
@@ -1080,58 +1220,67 @@ static void test_UrlCanonicalizeW(void)
 
 /* ########################### */
 
-static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
+static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl, BOOL todo)
 {
     HRESULT hr;
     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
     WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
-    LPWSTR wszUrl1 = GetWideString(szUrl1);
-    LPWSTR wszUrl2 = GetWideString(szUrl2);
-    LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
-    LPWSTR wszConvertedUrl;
+    LPWSTR wszUrl1, wszUrl2, wszExpectUrl, wszConvertedUrl;
 
     DWORD dwSize;
-    DWORD dwExpectLen = lstrlen(szExpectUrl);
+    DWORD dwExpectLen = lstrlenA(szExpectUrl);
 
     if (!pUrlCombineA) {
         win_skip("UrlCombineA not found\n");
         return;
     }
 
+    wszUrl1 = GetWideString(szUrl1);
+    wszUrl2 = GetWideString(szUrl2);
+    wszExpectUrl = GetWideString(szExpectUrl);
+
     hr = pUrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
     ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_INVALIDARG);
 
     dwSize = 0;
     hr = pUrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
     ok(hr == E_POINTER, "Checking length of string, return was 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+    ok(todo || dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
     dwSize--;
     hr = pUrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
     ok(hr == E_POINTER, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-    ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+    ok(todo || dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
     hr = pUrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
     ok(hr == dwExpectReturn, "UrlCombineA returned 0x%08x, expected 0x%08x\n", hr, dwExpectReturn);
-    ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
-    if(SUCCEEDED(hr)) {
-        ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
+
+    if (todo)
+    {
+        todo_wine ok(dwSize == dwExpectLen && (FAILED(hr) || strcmp(szReturnUrl, szExpectUrl)==0),
+                "Expected %s (len=%d), but got %s (len=%d)\n", szExpectUrl, dwExpectLen, SUCCEEDED(hr) ? szReturnUrl : "(null)", dwSize);
+    }
+    else
+    {
+        ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
+        if (SUCCEEDED(hr))
+            ok(strcmp(szReturnUrl, szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
     }
 
     if (pUrlCombineW) {
         dwSize = 0;
         hr = pUrlCombineW(wszUrl1, wszUrl2, NULL, &dwSize, dwFlags);
         ok(hr == E_POINTER, "Checking length of string, return was 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-        ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+        ok(todo || dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
         dwSize--;
         hr = pUrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
         ok(hr == E_POINTER, "UrlCombineW returned 0x%08x, expected 0x%08x\n", hr, E_POINTER);
-        ok(dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
+        ok(todo || dwSize == dwExpectLen+1, "Got length %d, expected %d\n", dwSize, dwExpectLen+1);
 
         hr = pUrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
         ok(hr == dwExpectReturn, "UrlCombineW returned 0x%08x, expected 0x%08x\n", hr, dwExpectReturn);
-        ok(dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
+        ok(todo || dwSize == dwExpectLen, "Got length %d, expected %d\n", dwSize, dwExpectLen);
         if(SUCCEEDED(hr)) {
             wszConvertedUrl = GetWideString(szReturnUrl);
             ok(lstrcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
@@ -1151,7 +1300,7 @@ static void test_UrlCombine(void)
     unsigned int i;
     for(i=0; i<sizeof(TEST_COMBINE)/sizeof(TEST_COMBINE[0]); i++) {
         test_url_combine(TEST_COMBINE[i].url1, TEST_COMBINE[i].url2, TEST_COMBINE[i].flags,
-                         TEST_COMBINE[i].expectret, TEST_COMBINE[i].expecturl);
+                         TEST_COMBINE[i].expectret, TEST_COMBINE[i].expecturl, TEST_COMBINE[i].todo);
     }
 }
 
@@ -1174,7 +1323,7 @@ static void test_UrlCreateFromPath(void)
         len = INTERNET_MAX_URL_LENGTH;
         ret = pUrlCreateFromPathA(TEST_URLFROMPATH[i].path, ret_url, &len, 0);
         ok(ret == TEST_URLFROMPATH[i].ret, "ret %08x from path %s\n", ret, TEST_URLFROMPATH[i].path);
-        ok(!lstrcmpi(ret_url, TEST_URLFROMPATH[i].url), "url %s from path %s\n", ret_url, TEST_URLFROMPATH[i].path);
+        ok(!lstrcmpiA(ret_url, TEST_URLFROMPATH[i].url), "url %s from path %s\n", ret_url, TEST_URLFROMPATH[i].path);
         ok(len == strlen(ret_url), "ret len %d from path %s\n", len, TEST_URLFROMPATH[i].path);
 
         if (pUrlCreateFromPathW) {
@@ -1364,6 +1513,7 @@ static const struct parse_url_test_t {
     {"htt?p://www.winehq.org/",URL_E_INVALID_SYNTAX},
     {"ab-://www.winehq.org/",S_OK,3,URL_SCHEME_UNKNOWN},
     {" http://www.winehq.org/",URL_E_INVALID_SYNTAX},
+    {"HTTP://www.winehq.org/",S_OK,4,URL_SCHEME_HTTP},
 };
 
 static void test_ParseURL(void)
@@ -1550,7 +1700,8 @@ START_TEST(url)
   test_UrlGetPart();
   test_UrlCanonicalizeA();
   test_UrlCanonicalizeW();
-  test_UrlEscape();
+  test_UrlEscapeA();
+  test_UrlEscapeW();
   test_UrlCombine();
   test_UrlCreateFromPath();
   test_UrlIs();