UrlEscape has different rules depending on the protocol.
[wine] / dlls / shlwapi / tests / path.c
1 /* Unit test suite for Path functions
2  *
3  * Copyright 2002 Matthew Mastracci
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  */
19
20 #include <assert.h>
21 #include <stdlib.h>
22 #include <stdarg.h>
23 #include <stdio.h>
24
25 #include "wine/test.h"
26 #include "windef.h"
27 #include "winbase.h"
28 #include "wine/unicode.h"
29 #include "winreg.h"
30 #include "shlwapi.h"
31 #include "wininet.h"
32
33 const char* TEST_URL_1 = "http://www.winehq.org/tests?date=10/10/1923";
34 const char* TEST_URL_2 = "http://localhost:8080/tests%2e.html?date=Mon%2010/10/1923";
35 const char* TEST_URL_3 = "http://foo:bar@localhost:21/internal.php?query=x&return=y";
36
37 typedef struct _TEST_URL_CANONICALIZE {
38     char *url;
39     DWORD flags;
40     HRESULT expectret;
41     char *expecturl;
42 } TEST_URL_CANONICALIZE;
43
44 const TEST_URL_CANONICALIZE TEST_CANONICALIZE[] = {
45     /*FIXME {"http://www.winehq.org/tests/../tests/../..", 0, S_OK, "http://www.winehq.org/"},*/
46     {"http://www.winehq.org/tests/../tests", 0, S_OK, "http://www.winehq.org/tests"},
47     {"http://www.winehq.org/tests\n", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
48     {"http://www.winehq.org/tests\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, S_OK, "http://www.winehq.org/tests"},
49     {"http://www.winehq.org/tests\r", 0, S_OK, "http://www.winehq.org/tests"},
50     {"http://www.winehq.org/tests\r", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests"},
51     {"http://www.winehq.org/tests/../tests/", 0, S_OK, "http://www.winehq.org/tests/"},
52     {"http://www.winehq.org/tests/../tests/..", 0, S_OK, "http://www.winehq.org/"},
53     {"http://www.winehq.org/tests/../tests/../", 0, S_OK, "http://www.winehq.org/"},
54     {"http://www.winehq.org/tests/..", 0, S_OK, "http://www.winehq.org/"},
55     {"http://www.winehq.org/tests/../", 0, S_OK, "http://www.winehq.org/"},
56     {"http://www.winehq.org/tests/..?query=x&return=y", 0, S_OK, "http://www.winehq.org/?query=x&return=y"},
57     {"http://www.winehq.org/tests/../?query=x&return=y", 0, S_OK, "http://www.winehq.org/?query=x&return=y"},
58     {"http://www.winehq.org/tests/..#example", 0, S_OK, "http://www.winehq.org/#example"},
59     {"http://www.winehq.org/tests/../#example", 0, S_OK, "http://www.winehq.org/#example"},
60     {"http://www.winehq.org/tests/../#example", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../#example"},
61 };
62
63 typedef struct _TEST_URL_ESCAPE {
64     char *url;
65     DWORD flags;
66     DWORD expectescaped;
67     HRESULT expectret;
68     char *expecturl;
69 } TEST_URL_ESCAPE;
70
71 const TEST_URL_ESCAPE TEST_ESCAPE[] = {
72     {"http://www.winehq.org/tests0", 0, 0, S_OK, "http://www.winehq.org/tests0"},
73     {"http://www.winehq.org/tests1\n", 0, 0, S_OK, "http://www.winehq.org/tests1%0A"},
74     {"http://www.winehq.org/tests2\r", 0, 0, S_OK, "http://www.winehq.org/tests2%0D"},
75     {"http://www.winehq.org/tests3\r", URL_ESCAPE_SPACES_ONLY|URL_ESCAPE_UNSAFE, 0, S_OK, "http://www.winehq.org/tests3\r"},
76     {"http://www.winehq.org/tests4\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests4\r"},
77     {"http://www.winehq.org/tests5\r", URL_WININET_COMPATIBILITY|URL_ESCAPE_SPACES_ONLY, 0, S_OK, "http://www.winehq.org/tests5\r"},
78     {"/direct/swhelp/series6/6.2i_latestservicepack.dat\r", URL_ESCAPE_SPACES_ONLY, 0, S_OK, "/direct/swhelp/series6/6.2i_latestservicepack.dat\r"},
79
80     {"file://////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
81     {"file://///foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
82     {"file:////foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
83     {"file:///localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
84     {"file:///foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
85     {"file://loCalHost/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
86     {"file://foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
87     {"file:/localhost/foo/bar\\baz", 0, 0, S_OK, "file:///localhost/foo/bar/baz"},
88     {"file:/foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
89     {"file:foo/bar\\baz", 0, 0, S_OK, "file:foo/bar/baz"},
90     {"file:\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
91     {"file:\\\\foo/bar\\baz", 0, 0, S_OK, "file://foo/bar/baz"},
92     {"file:\\\\\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
93     {"file:\\\\localhost\\foo/bar\\baz", 0, 0, S_OK, "file:///foo/bar/baz"},
94     {"file:///f oo/b?a r\\baz", 0, 0, S_OK, "file:///f%20oo/b?a r\\baz"},
95     {"file:///foo/b#a r\\baz", 0, 0, S_OK, "file:///foo/b%23a%20r/baz"},
96     {"file:///f o^&`{}|][\"<>\\%o/b#a r\\baz", 0, 0, S_OK, "file:///f%20o%5E%26%60%7B%7D%7C%5D%5B%22%3C%3E/%o/b%23a%20r/baz"},
97     {"file:///f o%o/b?a r\\b%az", URL_ESCAPE_PERCENT, 0, S_OK, "file:///f%20o%25o/b?a r\\b%az"},
98     {"file:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "file:%2Ffoo%2Fbar%5Cbaz"},
99
100     {"foo/b%ar\\ba?z\\", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%ar%5Cba%3Fz%5C"},
101     {"foo/b%ar\\ba?z\\", URL_ESCAPE_PERCENT | URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "foo%2Fb%25ar%5Cba%3Fz%5C"},
102     {"foo/bar\\ba?z\\", 0, 0, S_OK, "foo/bar%5Cba?z\\"},
103     {"/foo/bar\\ba?z\\", 0, 0, S_OK, "/foo/bar%5Cba?z\\"},
104     {"/foo/bar\\ba#z\\", 0, 0, S_OK, "/foo/bar%5Cba#z\\"},
105     {"/foo/%5C", 0, 0, S_OK, "/foo/%5C"},
106     {"/foo/%5C", URL_ESCAPE_PERCENT, 0, S_OK, "/foo/%255C"},
107
108     {"http://////foo/bar\\baz", 0, 0, S_OK, "http://////foo/bar/baz"},
109     {"http://///foo/bar\\baz", 0, 0, S_OK, "http://///foo/bar/baz"},
110     {"http:////foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
111     {"http:///foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
112     {"http://localhost/foo/bar\\baz", 0, 0, S_OK, "http://localhost/foo/bar/baz"},
113     {"http://foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
114     {"http:/foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
115     {"http:foo/bar\\ba?z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba?z\\"},
116     {"http:foo/bar\\ba#z\\", 0, 0, S_OK, "http:foo%2Fbar%2Fba#z\\"},
117     {"http:\\foo/bar\\baz", 0, 0, S_OK, "http:/foo/bar/baz"},
118     {"http:\\\\foo/bar\\baz", 0, 0, S_OK, "http://foo/bar/baz"},
119     {"http:\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:///foo/bar/baz"},
120     {"http:\\\\\\\\foo/bar\\baz", 0, 0, S_OK, "http:////foo/bar/baz"},
121     {"http:/fo ?o/b ar\\baz", 0, 0, S_OK, "http:/fo%20?o/b ar\\baz"},
122     {"http:fo ?o/b ar\\baz", 0, 0, S_OK, "http:fo%20?o/b ar\\baz"},
123     {"http:/foo/bar\\baz", URL_ESCAPE_SEGMENT_ONLY, 0, S_OK, "http:%2Ffoo%2Fbar%5Cbaz"},
124
125     {"https://foo/bar\\baz", 0, 0, S_OK, "https://foo/bar/baz"},
126     {"https:/foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
127     {"https:\\foo/bar\\baz", 0, 0, S_OK, "https:/foo/bar/baz"},
128
129     {"foo:////foo/bar\\baz", 0, 0, S_OK, "foo:////foo/bar%5Cbaz"},
130     {"foo:///foo/bar\\baz", 0, 0, S_OK, "foo:///foo/bar%5Cbaz"},
131     {"foo://localhost/foo/bar\\baz", 0, 0, S_OK, "foo://localhost/foo/bar%5Cbaz"},
132     {"foo://foo/bar\\baz", 0, 0, S_OK, "foo://foo/bar%5Cbaz"},
133     {"foo:/foo/bar\\baz", 0, 0, S_OK, "foo:/foo/bar%5Cbaz"},
134     {"foo:foo/bar\\baz", 0, 0, S_OK, "foo:foo%2Fbar%5Cbaz"},
135     {"foo:\\foo/bar\\baz", 0, 0, S_OK, "foo:%5Cfoo%2Fbar%5Cbaz"},
136     {"foo:/foo/bar\\ba?\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba?\\z"},
137     {"foo:/foo/bar\\ba#\\z", 0, 0, S_OK, "foo:/foo/bar%5Cba#\\z"},
138
139     {"mailto:/fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:%2Ffo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
140     {"mailto:fo/o@b\\%a?\\r.b#\\az", 0, 0, S_OK, "mailto:fo%2Fo@b%5C%a%3F%5Cr.b%23%5Caz"},
141     {"mailto:fo/o@b\\%a?\\r.b#\\az", URL_ESCAPE_PERCENT, 0, S_OK, "mailto:fo%2Fo@b%5C%25a%3F%5Cr.b%23%5Caz"},
142
143     {"ftp:fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:fo%2Fo@bar.baz%2Ffoo%2Fbar"},
144     {"ftp:/fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:/fo/o@bar.baz/foo/bar"},
145     {"ftp://fo/o@bar.baz/fo?o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo?o\\bar"},
146     {"ftp://fo/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://fo/o@bar.baz/fo#o\\bar"},
147     {"ftp://localhost/o@bar.baz/fo#o\\bar", 0, 0, S_OK, "ftp://localhost/o@bar.baz/fo#o\\bar"},
148     {"ftp:///fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:///fo/o@bar.baz/foo/bar"},
149     {"ftp:////fo/o@bar.baz/foo/bar", 0, 0, S_OK, "ftp:////fo/o@bar.baz/foo/bar"}
150 };
151
152 typedef struct _TEST_URL_COMBINE {
153     char *url1;
154     char *url2;
155     DWORD flags;
156     HRESULT expectret;
157     char *expecturl;
158 } TEST_URL_COMBINE;
159
160 const TEST_URL_COMBINE TEST_COMBINE[] = {
161     {"http://www.winehq.org/tests", "tests1", 0, S_OK, "http://www.winehq.org/tests1"},
162     /*FIXME {"http://www.winehq.org/tests", "../tests2", 0, S_OK, "http://www.winehq.org/tests2"},*/
163     {"http://www.winehq.org/tests/", "../tests3", 0, S_OK, "http://www.winehq.org/tests3"},
164     {"http://www.winehq.org/tests/../tests", "tests4", 0, S_OK, "http://www.winehq.org/tests4"},
165     {"http://www.winehq.org/tests/../tests/", "tests5", 0, S_OK, "http://www.winehq.org/tests/tests5"},
166     {"http://www.winehq.org/tests/../tests/", "/tests6/..", 0, S_OK, "http://www.winehq.org/"},
167     {"http://www.winehq.org/tests/../tests/..", "tests7/..", 0, S_OK, "http://www.winehq.org/"},
168     {"http://www.winehq.org/tests/?query=x&return=y", "tests8", 0, S_OK, "http://www.winehq.org/tests/tests8"},
169     {"http://www.winehq.org/tests/#example", "tests9", 0, S_OK, "http://www.winehq.org/tests/tests9"},
170     {"http://www.winehq.org/tests/../tests/", "/tests10/..", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests10/.."},
171     {"http://www.winehq.org/tests/../", "tests11", URL_DONT_SIMPLIFY, S_OK, "http://www.winehq.org/tests/../tests11"},
172 };
173
174 static LPWSTR GetWideString(const char* szString)
175 {
176   LPWSTR wszString = (LPWSTR) HeapAlloc(GetProcessHeap(), 0,
177                                          (2*INTERNET_MAX_URL_LENGTH) * sizeof(WCHAR));
178   
179   MultiByteToWideChar(0, 0, szString, -1, wszString, INTERNET_MAX_URL_LENGTH);
180
181   return wszString;
182 }
183
184 static void FreeWideString(LPWSTR wszString)
185 {
186    HeapFree(GetProcessHeap(), 0, wszString);
187 }
188
189 static void hash_url(const char* szUrl)
190 {
191   LPCSTR szTestUrl = szUrl;
192   LPWSTR wszTestUrl = GetWideString(szTestUrl);
193   
194   DWORD cbSize = sizeof(DWORD);
195   DWORD dwHash1, dwHash2;
196   ok(UrlHashA(szTestUrl, (LPBYTE)&dwHash1, cbSize) == S_OK, "UrlHashA didn't return S_OK\n");
197   ok(UrlHashW(wszTestUrl, (LPBYTE)&dwHash2, cbSize) == S_OK, "UrlHashW didn't return S_OK\n");
198
199   FreeWideString(wszTestUrl);
200
201   ok(dwHash1 == dwHash2, "Hashes didn't compare\n");
202 }
203
204 static void test_UrlHash(void)
205 {
206   hash_url(TEST_URL_1);
207   hash_url(TEST_URL_2);
208   hash_url(TEST_URL_3);
209 }
210
211 static void test_url_part(const char* szUrl, DWORD dwPart, DWORD dwFlags, const char* szExpected)
212 {
213   CHAR szPart[INTERNET_MAX_URL_LENGTH];
214   WCHAR wszPart[INTERNET_MAX_URL_LENGTH];
215   LPWSTR wszUrl = GetWideString(szUrl);
216   LPWSTR wszConvertedPart;
217
218   DWORD dwSize;
219
220   dwSize = INTERNET_MAX_URL_LENGTH;
221   ok( UrlGetPartA(szUrl, szPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartA for \"%s\" part 0x%08lx didn't return S_OK but \"%s\"\n", szUrl, dwPart, szPart);
222   dwSize = INTERNET_MAX_URL_LENGTH;
223   ok( UrlGetPartW(wszUrl, wszPart, &dwSize, dwPart, dwFlags) == S_OK, "UrlGetPartW didn't return S_OK\n" );
224
225   wszConvertedPart = GetWideString(szPart);
226
227   ok(strcmpW(wszPart,wszConvertedPart)==0, "Strings didn't match between ascii and unicode UrlGetPart!\n");
228
229   FreeWideString(wszUrl);
230   FreeWideString(wszConvertedPart);
231
232   /* Note that v6.0 and later don't return '?' with the query */
233   ok(strcmp(szPart,szExpected)==0 ||
234      (*szExpected=='?' && !strcmp(szPart,szExpected+1)),
235          "Expected %s, but got %s\n", szExpected, szPart);
236 }
237
238 static void test_UrlGetPart(void)
239 {
240   test_url_part(TEST_URL_3, URL_PART_HOSTNAME, 0, "localhost");
241   test_url_part(TEST_URL_3, URL_PART_PORT, 0, "21");
242   test_url_part(TEST_URL_3, URL_PART_USERNAME, 0, "foo");
243   test_url_part(TEST_URL_3, URL_PART_PASSWORD, 0, "bar");
244   test_url_part(TEST_URL_3, URL_PART_SCHEME, 0, "http");
245   test_url_part(TEST_URL_3, URL_PART_QUERY, 0, "?query=x&return=y");
246 }
247
248 static void test_url_escape(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
249 {
250     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
251     DWORD dwEscaped;
252     WCHAR ret_urlW[INTERNET_MAX_URL_LENGTH];
253     WCHAR *urlW, *expected_urlW;
254     dwEscaped=INTERNET_MAX_URL_LENGTH;
255
256     ok(UrlEscapeA(szUrl, szReturnUrl, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeA didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
257     ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected \"%s\", but got \"%s\" from \"%s\"\n", szExpectUrl, szReturnUrl, szUrl);
258     
259     dwEscaped = INTERNET_MAX_URL_LENGTH;
260     urlW = GetWideString(szUrl);
261     expected_urlW = GetWideString(szExpectUrl);
262     ok(UrlEscapeW(urlW, ret_urlW, &dwEscaped, dwFlags) == dwExpectReturn, "UrlEscapeW didn't return 0x%08lx from \"%s\"\n", dwExpectReturn, szUrl);
263     WideCharToMultiByte(CP_ACP,0,ret_urlW,-1,szReturnUrl,INTERNET_MAX_URL_LENGTH,0,0);
264     ok(strcmpW(ret_urlW, expected_urlW)==0, "Expected \"%s\", but got \"%s\" from \"%s\" flags %08lx\n", szExpectUrl, szReturnUrl, szUrl, dwFlags);
265     FreeWideString(urlW);
266     FreeWideString(expected_urlW);
267
268 }
269 static void test_url_canonicalize(const char *szUrl, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
270 {
271     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
272     WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
273     LPWSTR wszUrl = GetWideString(szUrl);
274     LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
275     LPWSTR wszConvertedUrl;
276     
277     DWORD dwSize;
278     
279     dwSize = INTERNET_MAX_URL_LENGTH;
280     ok(UrlCanonicalizeA(szUrl, szReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeA didn't return 0x%08lx\n", dwExpectReturn);
281     ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
282
283     dwSize = INTERNET_MAX_URL_LENGTH;
284     ok(UrlCanonicalizeW(wszUrl, wszReturnUrl, &dwSize, dwFlags) == dwExpectReturn, "UrlCanonicalizeW didn't return 0x%08lx\n", dwExpectReturn);
285     wszConvertedUrl = GetWideString(szReturnUrl);
286     ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCanonicalize!\n");
287     FreeWideString(wszConvertedUrl);
288     
289             
290     FreeWideString(wszUrl);
291     FreeWideString(wszExpectUrl);
292 }
293
294
295 static void test_UrlEscape(void)
296 {
297     unsigned int i;
298     for(i=0; i<sizeof(TEST_ESCAPE)/sizeof(TEST_ESCAPE[0]); i++) {
299         test_url_escape(TEST_ESCAPE[i].url, TEST_ESCAPE[i].flags,
300                               TEST_ESCAPE[i].expectret, TEST_ESCAPE[i].expecturl);
301     }
302 }
303
304 static void test_UrlCanonicalize(void)
305 {
306     unsigned int i;
307     for(i=0; i<sizeof(TEST_CANONICALIZE)/sizeof(TEST_CANONICALIZE[0]); i++) {
308         test_url_canonicalize(TEST_CANONICALIZE[i].url, TEST_CANONICALIZE[i].flags,
309                               TEST_CANONICALIZE[i].expectret, TEST_CANONICALIZE[i].expecturl);
310     }
311 }
312
313 static void test_url_combine(const char *szUrl1, const char *szUrl2, DWORD dwFlags, HRESULT dwExpectReturn, const char *szExpectUrl)
314 {
315     HRESULT hr;
316     CHAR szReturnUrl[INTERNET_MAX_URL_LENGTH];
317     WCHAR wszReturnUrl[INTERNET_MAX_URL_LENGTH];
318     LPWSTR wszUrl1 = GetWideString(szUrl1);
319     LPWSTR wszUrl2 = GetWideString(szUrl2);
320     LPWSTR wszExpectUrl = GetWideString(szExpectUrl);
321     LPWSTR wszConvertedUrl;
322
323     DWORD dwSize;
324     DWORD dwExpectLen = lstrlen(szExpectUrl);
325
326     hr = UrlCombineA(szUrl1, szUrl2, NULL, NULL, dwFlags);
327     ok(hr == E_INVALIDARG, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_INVALIDARG);
328     
329     dwSize = 0;
330     hr = UrlCombineA(szUrl1, szUrl2, NULL, &dwSize, dwFlags);
331     ok(hr == E_POINTER, "Checking length of string, return was 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
332     ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
333
334     dwSize--;
335     hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
336     ok(hr == E_POINTER, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
337     ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
338     
339     hr = UrlCombineA(szUrl1, szUrl2, szReturnUrl, &dwSize, dwFlags);
340     ok(hr == dwExpectReturn, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
341     ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
342     if(SUCCEEDED(hr)) {
343         ok(strcmp(szReturnUrl,szExpectUrl)==0, "Expected %s, but got %s\n", szExpectUrl, szReturnUrl);
344     }
345
346     dwSize = 0;
347     hr = UrlCombineW(wszUrl1, wszUrl2, NULL, &dwSize, dwFlags);
348     ok(hr == E_POINTER, "Checking length of string, return was 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
349     ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
350
351     dwSize--;
352     hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
353     ok(hr == E_POINTER, "UrlCombineA returned 0x%08lx, expected 0x%08lx\n", hr, E_POINTER);
354     ok(dwSize == dwExpectLen+1, "Got length %ld, expected %ld\n", dwSize, dwExpectLen+1);
355     
356     hr = UrlCombineW(wszUrl1, wszUrl2, wszReturnUrl, &dwSize, dwFlags);
357     ok(hr == dwExpectReturn, "UrlCombineW returned 0x%08lx, expected 0x%08lx\n", hr, dwExpectReturn);
358     ok(dwSize == dwExpectLen, "Got length %ld, expected %ld\n", dwSize, dwExpectLen);
359     if(SUCCEEDED(hr)) {
360         wszConvertedUrl = GetWideString(szReturnUrl);
361         ok(strcmpW(wszReturnUrl, wszConvertedUrl)==0, "Strings didn't match between ascii and unicode UrlCombine!\n");
362         FreeWideString(wszConvertedUrl);
363     }
364
365     FreeWideString(wszUrl1);
366     FreeWideString(wszUrl2);
367     FreeWideString(wszExpectUrl);
368 }
369
370 static void test_UrlCombine(void)
371 {
372     unsigned int i;
373     for(i=0; i<sizeof(TEST_COMBINE)/sizeof(TEST_COMBINE[0]); i++) {
374         test_url_combine(TEST_COMBINE[i].url1, TEST_COMBINE[i].url2, TEST_COMBINE[i].flags,
375                          TEST_COMBINE[i].expectret, TEST_COMBINE[i].expecturl);
376     }
377 }
378
379 static void test_PathSearchAndQualify(void)
380 {
381     WCHAR path1[] = {'c',':','\\','f','o','o',0};
382     WCHAR expect1[] = {'c',':','\\','f','o','o',0};
383     WCHAR path2[] = {'c',':','f','o','o',0};
384     WCHAR c_drive[] = {'c',':',0}; 
385     WCHAR foo[] = {'f','o','o',0}; 
386     WCHAR path3[] = {'\\','f','o','o',0};
387     WCHAR winini[] = {'w','i','n','.','i','n','i',0};
388     WCHAR out[MAX_PATH];
389     WCHAR cur_dir[MAX_PATH];
390     WCHAR dot[] = {'.',0};
391
392     /* c:\foo */
393     ok(PathSearchAndQualifyW(path1, out, MAX_PATH) != 0,
394        "PathSearchAndQualify rets 0\n");
395     ok(!lstrcmpiW(out, expect1), "strings don't match\n");
396
397     /* c:foo */
398     ok(PathSearchAndQualifyW(path2, out, MAX_PATH) != 0,
399        "PathSearchAndQualify rets 0\n");
400     GetFullPathNameW(c_drive, MAX_PATH, cur_dir, NULL);
401     PathAddBackslashW(cur_dir);
402     strcatW(cur_dir, foo);
403     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");    
404
405     /* foo */
406     ok(PathSearchAndQualifyW(foo, out, MAX_PATH) != 0,
407        "PathSearchAndQualify rets 0\n");
408     GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
409     PathAddBackslashW(cur_dir);
410     strcatW(cur_dir, foo);
411     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");    
412
413     /* \foo */
414     ok(PathSearchAndQualifyW(path3, out, MAX_PATH) != 0,
415        "PathSearchAndQualify rets 0\n");
416     GetFullPathNameW(dot, MAX_PATH, cur_dir, NULL);
417     strcpyW(cur_dir + 2, path3);
418     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
419
420     /* win.ini */
421     ok(PathSearchAndQualifyW(winini, out, MAX_PATH) != 0,
422        "PathSearchAndQualify rets 0\n");
423     if(!SearchPathW(NULL, winini, NULL, MAX_PATH, cur_dir, NULL))
424         GetFullPathNameW(winini, MAX_PATH, cur_dir, NULL);
425     ok(!lstrcmpiW(out, cur_dir), "strings don't match\n");
426
427 }
428
429 START_TEST(path)
430 {
431   test_UrlHash();
432   test_UrlGetPart();
433   test_UrlCanonicalize();
434   test_UrlEscape();
435   test_UrlCombine();
436
437   test_PathSearchAndQualify();
438 }