uxtheme: Remove unused variable.
[wine] / dlls / itss / tests / protocol.c
1 /*
2  * Copyright 2006 Jacek Caban for CodeWeavers
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #define COBJMACROS
20
21 #include <wine/test.h>
22 #include <stdarg.h>
23
24 #include "windef.h"
25 #include "winbase.h"
26 #include "initguid.h"
27 #include "ole2.h"
28 #include "urlmon.h"
29 #include "shlwapi.h"
30
31 #define DEFINE_EXPECT(func) \
32     static BOOL expect_ ## func = FALSE, called_ ## func = FALSE
33
34 #define SET_EXPECT(func) \
35     expect_ ## func = TRUE
36
37 #define CHECK_EXPECT(func) \
38     do { \
39         ok(expect_ ##func, "unexpected call " #func "\n"); \
40         expect_ ## func = FALSE; \
41         called_ ## func = TRUE; \
42     }while(0)
43
44 #define CHECK_EXPECT2(func) \
45     do { \
46         ok(expect_ ##func, "unexpected call " #func  "\n"); \
47         called_ ## func = TRUE; \
48     }while(0)
49
50 #define SET_CALLED(func) \
51     expect_ ## func = called_ ## func = FALSE
52
53 #define CHECK_CALLED(func) \
54     do { \
55         ok(called_ ## func, "expected " #func "\n"); \
56         SET_CALLED(func); \
57     }while(0)
58
59 DEFINE_GUID(CLSID_ITSProtocol,0x9d148291,0xb9c8,0x11d0,0xa4,0xcc,0x00,0x00,0xf8,0x01,0x49,0xf6);
60
61 DEFINE_EXPECT(GetBindInfo);
62 DEFINE_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
63 DEFINE_EXPECT(ReportProgress_SENDINGREQUEST);
64 DEFINE_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
65 DEFINE_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
66 DEFINE_EXPECT(ReportProgress_DIRECTBIND);
67 DEFINE_EXPECT(ReportData);
68 DEFINE_EXPECT(ReportResult);
69
70 static HRESULT expect_hrResult;
71 static IInternetProtocol *read_protocol = NULL;
72 static DWORD bindf;
73
74 static const WCHAR blank_url1[] = {'i','t','s',':',
75     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
76 static const WCHAR blank_url2[] = {'m','S','-','i','T','s',':',
77     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
78 static const WCHAR blank_url3[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
79     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
80 static const WCHAR blank_url4[] = {'i','t','s',':',
81     't','e','s','t','.','c','h','m',':',':','b','l','a','n','k','.','h','t','m','l',0};
82 static const WCHAR blank_url5[] = {'i','t','s',':',
83     't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
84 static const WCHAR blank_url6[] = {'i','t','s',':',
85     't','e','s','t','.','c','h','m',':',':','/','%','6','2','l','a','n','k','.','h','t','m','l',0};
86 static const WCHAR blank_url7[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
87     't','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
88 static const WCHAR blank_url8[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
89     't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
90 static const WCHAR blank_url9[] = {'i','t','s',':',
91     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','.','.','/','b','l','a','n','k','.','h','t','m','l',0};
92
93 static enum {
94     ITS_PROTOCOL,
95     MK_PROTOCOL
96 } test_protocol;
97
98 static const WCHAR cache_file1[] =
99     {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
100 static const WCHAR cache_file2[] =
101     {'t','e','s','t','.','c','h','m',':',':','\\','b','l','a','n','k','.','h','t','m','l',0};
102 static const WCHAR cache_file3[] =
103     {'t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l','/',0};
104 static const WCHAR *cache_file = cache_file1;
105
106 static HRESULT WINAPI ProtocolSink_QueryInterface(IInternetProtocolSink *iface, REFIID riid, void **ppv)
107 {
108     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetProtocolSink, riid)) {
109         *ppv = iface;
110         return S_OK;
111     }
112     return E_NOINTERFACE;
113 }
114
115 static ULONG WINAPI ProtocolSink_AddRef(IInternetProtocolSink *iface)
116 {
117     return 2;
118 }
119
120 static ULONG WINAPI ProtocolSink_Release(IInternetProtocolSink *iface)
121 {
122     return 1;
123 }
124
125 static HRESULT WINAPI ProtocolSink_Switch(IInternetProtocolSink *iface, PROTOCOLDATA *pProtocolData)
126 {
127     ok(0, "unexpected call\n");
128     return E_NOTIMPL;
129 }
130
131 static HRESULT WINAPI ProtocolSink_ReportProgress(IInternetProtocolSink *iface, ULONG ulStatusCode,
132         LPCWSTR szStatusText)
133 {
134     static const WCHAR blank_html[] = {'b','l','a','n','k','.','h','t','m','l',0};
135     static const WCHAR text_html[] = {'t','e','x','t','/','h','t','m','l',0};
136
137     switch(ulStatusCode) {
138     case BINDSTATUS_BEGINDOWNLOADDATA:
139         CHECK_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
140         ok(!szStatusText, "szStatusText != NULL\n");
141         break;
142     case BINDSTATUS_SENDINGREQUEST:
143         CHECK_EXPECT(ReportProgress_SENDINGREQUEST);
144         if(test_protocol == ITS_PROTOCOL)
145             ok(!lstrcmpW(szStatusText, blank_html), "unexpected szStatusText\n");
146         else
147             ok(szStatusText == NULL, "szStatusText != NULL\n");
148         break;
149     case BINDSTATUS_MIMETYPEAVAILABLE:
150         CHECK_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
151         ok(!lstrcmpW(szStatusText, text_html), "unexpected szStatusText\n");
152         break;
153     case BINDSTATUS_CACHEFILENAMEAVAILABLE:
154         CHECK_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
155         ok(!lstrcmpW(szStatusText, cache_file), "unexpected szStatusText\n");
156         break;
157     case BINDSTATUS_DIRECTBIND:
158         CHECK_EXPECT(ReportProgress_DIRECTBIND);
159         ok(!szStatusText, "szStatusText != NULL\n");
160         break;
161     default:
162         ok(0, "unexpected ulStatusCode %d\n", ulStatusCode);
163         break;
164     }
165
166     return S_OK;
167 }
168
169 static HRESULT WINAPI ProtocolSink_ReportData(IInternetProtocolSink *iface, DWORD grfBSCF, ULONG ulProgress,
170         ULONG ulProgressMax)
171 {
172     CHECK_EXPECT(ReportData);
173
174     ok(ulProgress == ulProgressMax, "ulProgress != ulProgressMax\n");
175     if(test_protocol == ITS_PROTOCOL)
176         ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE), "grcf = %08x\n", grfBSCF);
177     else
178         ok(grfBSCF == (BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION), "grcf = %08x\n", grfBSCF);
179
180     if(read_protocol) {
181         BYTE buf[100];
182         DWORD cb = 0xdeadbeef;
183         HRESULT hres;
184
185         hres = IInternetProtocol_Read(read_protocol, buf, sizeof(buf), &cb);
186         ok(hres == S_OK, "Read failed: %08x\n", hres);
187         ok(cb == 13, "cb=%u expected 13\n", cb);
188         ok(!memcmp(buf, "<html></html>", 13), "unexpected data\n");
189     }
190
191     return S_OK;
192 }
193
194 static HRESULT WINAPI ProtocolSink_ReportResult(IInternetProtocolSink *iface, HRESULT hrResult,
195         DWORD dwError, LPCWSTR szResult)
196 {
197     CHECK_EXPECT(ReportResult);
198
199     ok(hrResult == expect_hrResult, "expected: %08x got: %08x\n", expect_hrResult, hrResult);
200     ok(dwError == 0, "dwError = %d\n", dwError);
201     ok(!szResult, "szResult != NULL\n");
202
203     return S_OK;
204 }
205
206 static IInternetProtocolSinkVtbl protocol_sink_vtbl = {
207     ProtocolSink_QueryInterface,
208     ProtocolSink_AddRef,
209     ProtocolSink_Release,
210     ProtocolSink_Switch,
211     ProtocolSink_ReportProgress,
212     ProtocolSink_ReportData,
213     ProtocolSink_ReportResult
214 };
215
216 static IInternetProtocolSink protocol_sink = {
217     &protocol_sink_vtbl
218 };
219
220 static HRESULT WINAPI BindInfo_QueryInterface(IInternetBindInfo *iface, REFIID riid, void **ppv)
221 {
222     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IInternetBindInfo, riid)) {
223         *ppv = iface;
224         return S_OK;
225     }
226     return E_NOINTERFACE;
227 }
228
229 static ULONG WINAPI BindInfo_AddRef(IInternetBindInfo *iface)
230 {
231     return 2;
232 }
233
234 static ULONG WINAPI BindInfo_Release(IInternetBindInfo *iface)
235 {
236     return 1;
237 }
238
239 static HRESULT WINAPI BindInfo_GetBindInfo(IInternetBindInfo *iface, DWORD *grfBINDF, BINDINFO *pbindinfo)
240 {
241     CHECK_EXPECT(GetBindInfo);
242
243     ok(grfBINDF != NULL, "grfBINDF == NULL\n");
244     if(grfBINDF)
245         ok(!*grfBINDF, "*grfBINDF != 0\n");
246     ok(pbindinfo != NULL, "pbindinfo == NULL\n");
247     ok(pbindinfo->cbSize == sizeof(BINDINFO), "wrong size of pbindinfo: %d\n", pbindinfo->cbSize);
248
249     *grfBINDF = bindf;
250     return S_OK;
251 }
252
253 static HRESULT WINAPI BindInfo_GetBindString(IInternetBindInfo *iface, ULONG ulStringType, LPOLESTR *ppwzStr,
254         ULONG cEl, ULONG *pcElFetched)
255 {
256     ok(0, "unexpected call\n");
257     return E_NOTIMPL;
258 }
259
260 static IInternetBindInfoVtbl bind_info_vtbl = {
261     BindInfo_QueryInterface,
262     BindInfo_AddRef,
263     BindInfo_Release,
264     BindInfo_GetBindInfo,
265     BindInfo_GetBindString
266 };
267
268 static IInternetBindInfo bind_info = {
269     &bind_info_vtbl
270 };
271
272 static void test_protocol_fail(IInternetProtocol *protocol, LPCWSTR url, HRESULT expected_hres)
273 {
274     HRESULT hres;
275
276     SET_EXPECT(GetBindInfo);
277     SET_EXPECT(ReportResult);
278
279     expect_hrResult = expected_hres;
280     hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
281     ok(hres == expected_hres, "expected: %08x got: %08x\n", expected_hres, hres);
282
283     CHECK_CALLED(GetBindInfo);
284     CHECK_CALLED(ReportResult);
285 }
286
287 #define protocol_start(p,u,e) _protocol_start(__LINE__,p,u,e)
288 static HRESULT _protocol_start(unsigned line, IInternetProtocol *protocol, LPCWSTR url, BOOL expect_mime)
289 {
290     HRESULT hres;
291
292     SET_EXPECT(GetBindInfo);
293     if(test_protocol == MK_PROTOCOL)
294         SET_EXPECT(ReportProgress_DIRECTBIND);
295     SET_EXPECT(ReportProgress_SENDINGREQUEST);
296     if(expect_mime)
297         SET_EXPECT(ReportProgress_MIMETYPEAVAILABLE);
298     if(test_protocol == MK_PROTOCOL)
299         SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
300     SET_EXPECT(ReportData);
301     if(test_protocol == ITS_PROTOCOL)
302         SET_EXPECT(ReportProgress_BEGINDOWNLOADDATA);
303     SET_EXPECT(ReportResult);
304     expect_hrResult = S_OK;
305
306     hres = IInternetProtocol_Start(protocol, url, &protocol_sink, &bind_info, 0, 0);
307
308     if(FAILED(hres)) {
309         SET_CALLED(GetBindInfo);
310         if(test_protocol == MK_PROTOCOL)
311             SET_CALLED(ReportProgress_DIRECTBIND);
312         SET_CALLED(ReportProgress_SENDINGREQUEST);
313         if(expect_mime)
314             SET_CALLED(ReportProgress_MIMETYPEAVAILABLE);
315         if(test_protocol == MK_PROTOCOL)
316             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
317         SET_CALLED(ReportData);
318         if(test_protocol == ITS_PROTOCOL)
319             SET_CALLED(ReportProgress_BEGINDOWNLOADDATA);
320         SET_CALLED(ReportResult);
321     }else {
322         CHECK_CALLED(GetBindInfo);
323         if(test_protocol == MK_PROTOCOL)
324             SET_CALLED(ReportProgress_DIRECTBIND);
325         CHECK_CALLED(ReportProgress_SENDINGREQUEST);
326         if(expect_mime)
327             CHECK_CALLED(ReportProgress_MIMETYPEAVAILABLE);
328         if(test_protocol == MK_PROTOCOL)
329             SET_EXPECT(ReportProgress_CACHEFILENAMEAVAILABLE);
330         CHECK_CALLED(ReportData);
331         if(test_protocol == ITS_PROTOCOL)
332             CHECK_CALLED(ReportProgress_BEGINDOWNLOADDATA);
333         CHECK_CALLED(ReportResult);
334     }
335
336     return hres;
337 }
338
339 static void test_protocol_url(IClassFactory *factory, LPCWSTR url, BOOL expect_mime)
340 {
341     IInternetProtocol *protocol;
342     BYTE buf[512];
343     ULONG cb, ref;
344     HRESULT hres;
345
346     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
347     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
348     if(FAILED(hres))
349         return;
350
351     hres = protocol_start(protocol, url, expect_mime);
352     if(FAILED(hres)) {
353         IInternetProtocol_Release(protocol);
354         return;
355     }
356
357     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
358     ok(hres == S_OK, "Read failed: %08x\n", hres);
359     ok(cb == 13, "cb=%u expected 13\n", cb);
360     ok(!memcmp(buf, "<html></html>", 13), "unexpected data\n");
361     ref = IInternetProtocol_Release(protocol);
362     ok(!ref, "protocol ref=%d\n", ref);
363
364     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
365     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
366     if(FAILED(hres))
367         return;
368
369     cb = 0xdeadbeef;
370     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
371     ok(hres == (test_protocol == ITS_PROTOCOL ? INET_E_DATA_NOT_AVAILABLE : E_FAIL),
372        "Read returned %08x\n", hres);
373     ok(cb == 0xdeadbeef, "cb=%u expected 0xdeadbeef\n", cb);
374
375     protocol_start(protocol, url, expect_mime);
376     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
377     ok(hres == S_OK, "Read failed: %08x\n", hres);
378     ok(cb == 2, "cb=%u expected 2\n", cb);
379     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
380     ok(hres == S_OK, "Read failed: %08x\n", hres);
381     ok(cb == 11, "cb=%u, expected 11\n", cb);
382     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
383     ok(hres == S_FALSE, "Read failed: %08x expected S_FALSE\n", hres);
384     ok(cb == 0, "cb=%u expected 0\n", cb);
385     hres = IInternetProtocol_UnlockRequest(protocol);
386     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
387     ref = IInternetProtocol_Release(protocol);
388     ok(!ref, "protocol ref=%d\n", ref);
389
390     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
391     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
392     if(FAILED(hres))
393         return;
394
395     protocol_start(protocol, url, expect_mime);
396     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
397     ok(hres == S_OK, "Read failed: %08x\n", hres);
398     hres = IInternetProtocol_LockRequest(protocol, 0);
399     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
400     hres = IInternetProtocol_UnlockRequest(protocol);
401     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
402     hres = IInternetProtocol_Read(protocol, buf, sizeof(buf), &cb);
403     ok(hres == S_OK, "Read failed: %08x\n", hres);
404     ok(cb == 11, "cb=%u, expected 11\n", cb);
405     ref = IInternetProtocol_Release(protocol);
406     ok(!ref, "protocol ref=%d\n", ref);
407
408     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
409     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
410     if(FAILED(hres))
411         return;
412
413     protocol_start(protocol, url, expect_mime);
414     hres = IInternetProtocol_LockRequest(protocol, 0);
415     ok(hres == S_OK, "LockRequest failed: %08x\n", hres);
416     hres = IInternetProtocol_Terminate(protocol, 0);
417     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
418     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
419     ok(hres == S_OK, "Read failed: %08x\n", hres);
420     ok(cb == 2, "cb=%u, expected 2\n", cb);
421     hres = IInternetProtocol_UnlockRequest(protocol);
422     ok(hres == S_OK, "UnlockRequest failed: %08x\n", hres);
423     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
424     ok(hres == S_OK, "Read failed: %08x\n", hres);
425     ok(cb == 2, "cb=%u, expected 2\n", cb);
426     hres = IInternetProtocol_Terminate(protocol, 0);
427     ok(hres == S_OK, "Terminate failed: %08x\n", hres);
428     hres = IInternetProtocol_Read(protocol, buf, 2, &cb);
429     ok(hres == S_OK, "Read failed: %08x\n", hres);
430     ok(cb == 2, "cb=%u expected 2\n", cb);
431     ref = IInternetProtocol_Release(protocol);
432     ok(!ref, "protocol ref=%d\n", ref);
433
434     hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&read_protocol);
435     ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
436     if(FAILED(hres))
437         return;
438
439     protocol_start(read_protocol, url, expect_mime);
440     ref = IInternetProtocol_Release(read_protocol);
441     ok(!ref, "protocol ref=%d\n", ref);
442     read_protocol = NULL;
443 }
444
445 static const WCHAR rel_url1[] =
446     {'t','e','s','t','.','h','t','m','l',0};
447 static const WCHAR rel_url2[] =
448     {'t','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
449 static const WCHAR rel_url3[] =
450     {'/','t','e','s','t','.','h','t','m','l',0};
451 static const WCHAR rel_url4[] =
452     {'t','e',':','t','.','h','t','m','l',0};
453 static const WCHAR rel_url5[] =
454     {'d','i','r','/','t','e','s','t','.','h','t','m','l',0};
455
456 static const WCHAR base_url1[] = {'i','t','s',':',
457     't','e','s','t',':','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
458 static const WCHAR base_url2[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
459     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
460 static const WCHAR base_url3[] = {'m','s','-','i','t','s',':','t','e','s','t','.','c','h','m',
461     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
462 static const WCHAR base_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
463     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/',
464     'b','l','a','n','k','.','h','t','m','l',0};
465 static const WCHAR base_url5[] = {'x','x','x',':','t','e','s','t','.','c','h','m',
466     ':',':','/','d','i','r','/','b','l','a','n','k','.','h','t','m','l',0};
467
468 static const WCHAR combined_url1[] = {'i','t','s',':',
469     't','e','s','t','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
470 static const WCHAR combined_url2[] = {'i','t','s',':',
471     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
472 static const WCHAR combined_url3[] = {'i','t','s',':',
473     't','e','s','t',':','.','c','h','m',':',':','/','t','e','s','t','.','h','t','m','l',0};
474 static const WCHAR combined_url4[] = {'i','t','s',':','t','e','s','t','.','c','h','m',
475     ':',':','b','l','a','n','k','.','h','t','m','l','t','e','s','t','.','h','t','m','l',0};
476 static const WCHAR combined_url5[] = {'m','s','-','i','t','s',':',
477     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
478 static const WCHAR combined_url6[] = {'m','k',':','@','M','S','I','T','S','t','o','r','e',':',
479     't','e','s','t','.','c','h','m',':',':','/','d','i','r','/','t','e','s','t','.','h','t','m','l',0};
480
481 static const struct {
482     LPCWSTR base_url;
483     LPCWSTR rel_url;
484     DWORD flags;
485     HRESULT hres;
486     LPCWSTR combined_url;
487 } combine_tests[] = {
488     {blank_url1, blank_url1, 0, STG_E_INVALIDNAME, NULL},
489     {blank_url2, blank_url2, 0, STG_E_INVALIDNAME, NULL},
490     {blank_url1, rel_url1, 0, S_OK, combined_url1},
491     {blank_url1, rel_url2, 0, STG_E_INVALIDNAME, NULL},
492     {blank_url1, rel_url3, 0, S_OK, combined_url1},
493     {blank_url1, rel_url4, 0, STG_E_INVALIDNAME, NULL},
494     {blank_url1, rel_url3, URL_ESCAPE_SPACES_ONLY|URL_DONT_ESCAPE_EXTRA_INFO, S_OK, combined_url1},
495     {blank_url1, rel_url5, 0, S_OK, combined_url2},
496     {rel_url1, rel_url2, 0, 0x80041001, NULL},
497     {base_url1, rel_url1, 0, S_OK, combined_url3},
498     {base_url2, rel_url1, 0, S_OK, combined_url2},
499     {blank_url4, rel_url1, 0, S_OK, combined_url4},
500     {base_url3, rel_url1, 0, S_OK, combined_url5},
501     {base_url4, rel_url1, 0, S_OK, combined_url6},
502     {base_url5, rel_url1, 0, INET_E_USE_DEFAULT_PROTOCOLHANDLER, NULL},
503     {base_url2, rel_url3, 0, S_OK, combined_url1},
504 };
505
506 static void test_its_protocol_info(IInternetProtocol *protocol)
507 {
508     IInternetProtocolInfo *info;
509     WCHAR buf[1024];
510     DWORD size, i;
511     HRESULT hres;
512
513     hres = IInternetProtocol_QueryInterface(protocol, &IID_IInternetProtocolInfo, (void**)&info);
514     ok(hres == S_OK, "Could not get IInternetProtocolInfo interface: %08x\n", hres);
515     if(FAILED(hres))
516         return;
517
518     for(i = PARSE_CANONICALIZE; i <= PARSE_UNESCAPE; i++) {
519         if(i != PARSE_CANONICALIZE && i != PARSE_SECURITY_URL) {
520             hres = IInternetProtocolInfo_ParseUrl(info, blank_url1, i, 0, buf,
521                     sizeof(buf)/sizeof(buf[0]), &size, 0);
522             ok(hres == INET_E_DEFAULT_ACTION,
523                "[%d] failed: %08x, expected INET_E_DEFAULT_ACTION\n", i, hres);
524         }
525     }
526
527     for(i=0; i < sizeof(combine_tests)/sizeof(combine_tests[0]); i++) {
528         size = 0xdeadbeef;
529         memset(buf, 0xfe, sizeof(buf));
530         hres = IInternetProtocolInfo_CombineUrl(info, combine_tests[i].base_url,
531                 combine_tests[i].rel_url, combine_tests[i].flags, buf,
532                 sizeof(buf)/sizeof(WCHAR), &size, 0);
533         ok(hres == combine_tests[i].hres, "[%d] CombineUrl returned %08x, expected %08x\n",
534            i, hres, combine_tests[i].hres);
535         ok(size == (combine_tests[i].combined_url ? lstrlenW(combine_tests[i].combined_url)+1
536            : 0xdeadbeef), "[%d] unexpected size=%d\n", i, size);
537         if(combine_tests[i].combined_url)
538             ok(!lstrcmpW(combine_tests[i].combined_url, buf), "[%d] unexpected result\n", i);
539         else
540             ok(buf[0] == 0xfefe, "buf changed\n");
541     }
542
543     size = 0xdeadbeef;
544     memset(buf, 0xfe, sizeof(buf));
545     hres = IInternetProtocolInfo_CombineUrl(info, blank_url1, rel_url1, 0, buf,
546             1, &size, 0);
547     ok(hres == E_OUTOFMEMORY, "CombineUrl failed: %08x\n", hres);
548     ok(size == sizeof(combined_url1)/sizeof(WCHAR), "size=%d\n", size);
549     ok(buf[0] == 0xfefe, "buf changed\n");
550
551     IInternetProtocolInfo_Release(info);
552 }
553
554 static void test_its_protocol(void)
555 {
556     IInternetProtocolInfo *info;
557     IClassFactory *factory;
558     IUnknown *unk;
559     ULONG ref;
560     HRESULT hres;
561
562     static const WCHAR wrong_url1[] =
563         {'i','t','s',':','t','e','s','t','.','c','h','m',':',':','/','b','l','a','n','.','h','t','m','l',0};
564     static const WCHAR wrong_url2[] =
565         {'i','t','s',':','t','e','s','.','c','h','m',':',':','b','/','l','a','n','k','.','h','t','m','l',0};
566     static const WCHAR wrong_url3[] =
567         {'i','t','s',':','t','e','s','t','.','c','h','m','/','b','l','a','n','k','.','h','t','m','l',0};
568     static const WCHAR wrong_url4[] = {'m','k',':','@','M','S','I','T','S','t','o','r',':',
569          't','e','s','t','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
570     static const WCHAR wrong_url5[] = {'f','i','l','e',':',
571         't','e','s','.','c','h','m',':',':','/','b','l','a','n','k','.','h','t','m','l',0};
572
573     test_protocol = ITS_PROTOCOL;
574
575     hres = CoGetClassObject(&CLSID_ITSProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IUnknown, (void**)&unk);
576     ok(hres == S_OK ||
577        broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
578        "CoGetClassObject failed: %08x\n", hres);
579     if(FAILED(hres))
580         return;
581
582     hres = IUnknown_QueryInterface(unk, &IID_IInternetProtocolInfo, (void**)&info);
583     ok(hres == E_NOINTERFACE, "Could not get IInternetProtocolInfo: %08x\n", hres);
584
585     hres = IUnknown_QueryInterface(unk, &IID_IClassFactory, (void**)&factory);
586     ok(hres == S_OK, "Could not get IClassFactory interface\n");
587     if(SUCCEEDED(hres)) {
588         IInternetProtocol *protocol;
589
590         hres = IClassFactory_CreateInstance(factory, NULL, &IID_IInternetProtocol, (void**)&protocol);
591         ok(hres == S_OK, "Could not get IInternetProtocol: %08x\n", hres);
592         if(SUCCEEDED(hres)) {
593             test_its_protocol_info(protocol);
594
595             test_protocol_fail(protocol, wrong_url1, STG_E_FILENOTFOUND);
596             test_protocol_fail(protocol, wrong_url2, STG_E_FILENOTFOUND);
597             test_protocol_fail(protocol, wrong_url3, STG_E_FILENOTFOUND);
598
599             hres = IInternetProtocol_Start(protocol, wrong_url4, &protocol_sink, &bind_info, 0, 0);
600             ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
601                "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
602
603             hres = IInternetProtocol_Start(protocol, wrong_url5, &protocol_sink, &bind_info, 0, 0);
604             ok(hres == INET_E_USE_DEFAULT_PROTOCOLHANDLER,
605                "Start failed: %08x, expected INET_E_USE_DEFAULT_PROTOCOLHANDLER\n", hres);
606
607             ref = IInternetProtocol_Release(protocol);
608             ok(!ref, "protocol ref=%d\n", ref);
609
610             test_protocol_url(factory, blank_url1, TRUE);
611             test_protocol_url(factory, blank_url2, TRUE);
612             test_protocol_url(factory, blank_url3, TRUE);
613             test_protocol_url(factory, blank_url4, TRUE);
614             test_protocol_url(factory, blank_url5, TRUE);
615             test_protocol_url(factory, blank_url6, TRUE);
616             test_protocol_url(factory, blank_url8, TRUE);
617             test_protocol_url(factory, blank_url9, TRUE);
618             bindf = BINDF_FROMURLMON | BINDF_NEEDFILE;
619             test_protocol_url(factory, blank_url1, TRUE);
620         }
621
622         IClassFactory_Release(factory);
623     }
624
625     IUnknown_Release(unk);
626 }
627
628 static void test_mk_protocol(void)
629 {
630     IClassFactory *cf;
631     HRESULT hres;
632
633     test_protocol = MK_PROTOCOL;
634
635     hres = CoGetClassObject(&CLSID_MkProtocol, CLSCTX_INPROC_SERVER, NULL, &IID_IClassFactory,
636                             (void**)&cf);
637     ok(hres == S_OK ||
638        broken(hres == REGDB_E_CLASSNOTREG), /* Some W95 and NT4 */
639        "CoGetClassObject failed: %08x\n", hres);
640     if(FAILED(hres))
641         return;
642
643     cache_file = cache_file1;
644     test_protocol_url(cf, blank_url3, TRUE);
645     cache_file = cache_file2;
646     test_protocol_url(cf, blank_url7, TRUE);
647     cache_file = cache_file3;
648     test_protocol_url(cf, blank_url8, FALSE);
649
650     IClassFactory_Release(cf);
651 }
652
653 static BOOL create_chm(void)
654 {
655     HANDLE file;
656     HRSRC src;
657     DWORD size;
658
659     file = CreateFileA("test.chm", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
660             FILE_ATTRIBUTE_NORMAL, NULL);
661     ok(file != INVALID_HANDLE_VALUE, "Could not create test.chm file\n");
662     if(file == INVALID_HANDLE_VALUE)
663         return FALSE;
664
665     src = FindResourceA(NULL, MAKEINTRESOURCEA(60), MAKEINTRESOURCEA(60));
666
667     WriteFile(file, LoadResource(NULL, src), SizeofResource(NULL, src), &size, NULL);
668     CloseHandle(file);
669
670     return TRUE;
671 }
672
673 static void delete_chm(void)
674 {
675     BOOL ret;
676
677     ret = DeleteFileA("test.chm");
678     ok(ret, "DeleteFileA failed: %d\n", GetLastError());
679 }
680
681 START_TEST(protocol)
682 {
683     OleInitialize(NULL);
684
685     if(!create_chm())
686         return;
687
688     test_its_protocol();
689     test_mk_protocol();
690
691     delete_chm();
692     OleUninitialize();
693 }