gdiplus: Fix a memory leak in GdipCreateMetafileFromWmf.
[wine] / dlls / cabinet / tests / fdi.c
1 /*
2  * Unit tests for the File Decompression Interface
3  *
4  * Copyright (C) 2006 James Hawkins
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include <stdio.h>
22 #include <windows.h>
23 #include "fci.h"
24 #include "fdi.h"
25 #include "wine/test.h"
26
27 /* make the max size large so there is only one cab file */
28 #define MEDIA_SIZE          999999999
29 #define FOLDER_THRESHOLD    900000
30
31 CHAR CURR_DIR[MAX_PATH];
32
33 /* FDI callbacks */
34
35 static void * CDECL fdi_alloc(ULONG cb)
36 {
37     return HeapAlloc(GetProcessHeap(), 0, cb);
38 }
39
40 static void * CDECL fdi_alloc_bad(ULONG cb)
41 {
42     return NULL;
43 }
44
45 static void CDECL fdi_free(void *pv)
46 {
47     HeapFree(GetProcessHeap(), 0, pv);
48 }
49
50 static INT_PTR CDECL fdi_open(char *pszFile, int oflag, int pmode)
51 {
52     HANDLE handle;
53     handle = CreateFileA(pszFile, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
54                           OPEN_EXISTING, 0, NULL );
55     if (handle == INVALID_HANDLE_VALUE)
56         return 0;
57     return (INT_PTR) handle;
58 }
59
60 static UINT CDECL fdi_read(INT_PTR hf, void *pv, UINT cb)
61 {
62     HANDLE handle = (HANDLE) hf;
63     DWORD dwRead;
64     if (ReadFile(handle, pv, cb, &dwRead, NULL))
65         return dwRead;
66     return 0;
67 }
68
69 static UINT CDECL fdi_write(INT_PTR hf, void *pv, UINT cb)
70 {
71     HANDLE handle = (HANDLE) hf;
72     DWORD dwWritten;
73     if (WriteFile(handle, pv, cb, &dwWritten, NULL))
74         return dwWritten;
75     return 0;
76 }
77
78 static int CDECL fdi_close(INT_PTR hf)
79 {
80     HANDLE handle = (HANDLE) hf;
81     return CloseHandle(handle) ? 0 : -1;
82 }
83
84 static LONG CDECL fdi_seek(INT_PTR hf, LONG dist, int seektype)
85 {
86     HANDLE handle = (HANDLE) hf;
87     return SetFilePointer(handle, dist, NULL, seektype);
88 }
89
90 static void test_FDICreate(void)
91 {
92     HFDI hfdi;
93     ERF erf;
94
95     /* native crashes if pfnalloc is NULL */
96
97     /* FDICreate does not crash with a NULL pfnfree,
98      * but FDIDestroy will crash when it tries to access it.
99      */
100     if (0)
101     {
102         SetLastError(0xdeadbeef);
103         erf.erfOper = 0xcafefeed;
104         erf.erfType = 0xdeadbabe;
105         erf.fError = 0xdecaface;
106         hfdi = FDICreate(fdi_alloc, NULL, fdi_open, fdi_read,
107                          fdi_write, fdi_close, fdi_seek,
108                          cpuUNKNOWN, &erf);
109         ok(hfdi != NULL, "Expected non-NULL context\n");
110         ok(GetLastError() == 0xdeadbeef,
111            "Expected 0xdeadbeef, got %d\n", GetLastError());
112         ok(erf.erfOper == 0xcafefeed, "Expected 0xcafefeed, got %d\n", erf.erfOper);
113         ok(erf.erfType == 0xdeadbabe, "Expected 0xdeadbabe, got %d\n", erf.erfType);
114         ok(erf.fError == 0xdecaface, "Expected 0xdecaface, got %d\n", erf.fError);
115
116         FDIDestroy(hfdi);
117     }
118
119     SetLastError(0xdeadbeef);
120     erf.erfOper = 0xcafefeed;
121     erf.erfType = 0xdeadbabe;
122     erf.fError = 0xdecaface;
123     hfdi = FDICreate(fdi_alloc, fdi_free, NULL, fdi_read,
124                      fdi_write, fdi_close, fdi_seek,
125                      cpuUNKNOWN, &erf);
126     ok(hfdi != NULL, "Expected non-NULL context\n");
127     ok(GetLastError() == 0xdeadbeef,
128        "Expected 0xdeadbeef, got %d\n", GetLastError());
129     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
130     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
131     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
132
133     FDIDestroy(hfdi);
134
135     SetLastError(0xdeadbeef);
136     erf.erfOper = 0xcafefeed;
137     erf.erfType = 0xdeadbabe;
138     erf.fError = 0xdecaface;
139     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, NULL,
140                      fdi_write, fdi_close, fdi_seek,
141                      cpuUNKNOWN, &erf);
142     ok(hfdi != NULL, "Expected non-NULL context\n");
143     ok(GetLastError() == 0xdeadbeef,
144        "Expected 0xdeadbeef, got %d\n", GetLastError());
145     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
146     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
147     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
148
149     FDIDestroy(hfdi);
150
151     SetLastError(0xdeadbeef);
152     erf.erfOper = 0xcafefeed;
153     erf.erfType = 0xdeadbabe;
154     erf.fError = 0xdecaface;
155     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
156                      NULL, fdi_close, fdi_seek,
157                      cpuUNKNOWN, &erf);
158     ok(hfdi != NULL, "Expected non-NULL context\n");
159     ok(GetLastError() == 0xdeadbeef,
160        "Expected 0xdeadbeef, got %d\n", GetLastError());
161     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
162     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
163     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
164
165     FDIDestroy(hfdi);
166
167     SetLastError(0xdeadbeef);
168     erf.erfOper = 0xcafefeed;
169     erf.erfType = 0xdeadbabe;
170     erf.fError = 0xdecaface;
171     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
172                      fdi_write, NULL, fdi_seek,
173                      cpuUNKNOWN, &erf);
174     ok(hfdi != NULL, "Expected non-NULL context\n");
175     ok(GetLastError() == 0xdeadbeef,
176        "Expected 0xdeadbeef, got %d\n", GetLastError());
177     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
178     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
179     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
180
181     FDIDestroy(hfdi);
182
183     SetLastError(0xdeadbeef);
184     erf.erfOper = 0xcafefeed;
185     erf.erfType = 0xdeadbabe;
186     erf.fError = 0xdecaface;
187     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
188                      fdi_write, fdi_close, NULL,
189                      cpuUNKNOWN, &erf);
190     ok(hfdi != NULL, "Expected non-NULL context\n");
191     ok(GetLastError() == 0xdeadbeef,
192        "Expected 0xdeadbeef, got %d\n", GetLastError());
193     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
194     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
195     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
196
197     FDIDestroy(hfdi);
198
199     SetLastError(0xdeadbeef);
200     erf.erfOper = 0xcafefeed;
201     erf.erfType = 0xdeadbabe;
202     erf.fError = 0xdecaface;
203     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
204                      fdi_write, fdi_close, fdi_seek,
205                      cpuUNKNOWN, NULL);
206     /* XP sets hfdi to a non-NULL value, but Vista sets it to NULL! */
207     ok(GetLastError() == 0xdeadbeef,
208        "Expected 0xdeadbeef, got %d\n", GetLastError());
209     /* NULL is passed to FDICreate instead of &erf, so don't retest the erf member values. */
210
211     FDIDestroy(hfdi);
212
213     /* bad cpu type */
214     SetLastError(0xdeadbeef);
215     erf.erfOper = 0xcafefeed;
216     erf.erfType = 0xdeadbabe;
217     erf.fError = 0xdecaface;
218     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
219                      fdi_write, fdi_close, fdi_seek,
220                      0xcafebabe, &erf);
221     ok(hfdi != NULL, "Expected non-NULL context\n");
222     ok(GetLastError() == 0xdeadbeef,
223        "Expected 0xdeadbeef, got %d\n", GetLastError());
224     ok((erf.erfOper == 0xcafefeed || erf.erfOper == 0 /* Vista */), "Expected 0xcafefeed or 0, got %d\n", erf.erfOper);
225     ok((erf.erfType == 0xdeadbabe || erf.erfType == 0 /* Vista */), "Expected 0xdeadbabe or 0, got %d\n", erf.erfType);
226     ok((erf.fError == 0xdecaface || erf.fError == 0 /* Vista */), "Expected 0xdecaface or 0, got %d\n", erf.fError);
227
228     FDIDestroy(hfdi);
229
230     /* pfnalloc fails */
231     SetLastError(0xdeadbeef);
232     erf.erfOper = 0xcafefeed;
233     erf.erfType = 0xdeadbabe;
234     erf.fError = 0xdecaface;
235     hfdi = FDICreate(fdi_alloc_bad, fdi_free, fdi_open, fdi_read,
236                      fdi_write, fdi_close, fdi_seek,
237                      cpuUNKNOWN, &erf);
238     ok(hfdi == NULL, "Expected NULL context, got %p\n", hfdi);
239     ok(erf.erfOper == FDIERROR_ALLOC_FAIL,
240        "Expected FDIERROR_ALLOC_FAIL, got %d\n", erf.erfOper);
241     ok(erf.fError == TRUE, "Expected TRUE, got %d\n", erf.fError);
242     todo_wine
243     {
244         ok(GetLastError() == 0xdeadbeef,
245            "Expected 0xdeadbeef, got %d\n", GetLastError());
246         ok(erf.erfType == 0, "Expected 0, got %d\n", erf.erfType);
247     }
248 }
249
250 static void test_FDIDestroy(void)
251 {
252     HFDI hfdi;
253     ERF erf;
254     BOOL ret;
255
256     /* native crashes if hfdi is NULL or invalid */
257
258     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
259                      fdi_write, fdi_close, fdi_seek,
260                      cpuUNKNOWN, &erf);
261     ok(hfdi != NULL, "Expected non-NULL context\n");
262
263     /* successfully destroy hfdi */
264     ret = FDIDestroy(hfdi);
265     ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
266
267     /* native crashes if you try to destroy hfdi twice */
268     if (0)
269     {
270         /* try to destroy hfdi again */
271         ret = FDIDestroy(hfdi);
272         ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
273     }
274 }
275
276 static void createTestFile(const CHAR *name)
277 {
278     HANDLE file;
279     DWORD written;
280
281     file = CreateFileA(name, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
282     ok(file != INVALID_HANDLE_VALUE, "Failure to open file %s\n", name);
283     WriteFile(file, name, strlen(name), &written, NULL);
284     WriteFile(file, "\n", strlen("\n"), &written, NULL);
285     CloseHandle(file);
286 }
287
288 static void create_test_files(void)
289 {
290     DWORD len;
291
292     len = GetCurrentDirectoryA(MAX_PATH, CURR_DIR);
293
294     if(len && (CURR_DIR[len-1] == '\\'))
295         CURR_DIR[len-1] = 0;
296
297     createTestFile("a.txt");
298     createTestFile("b.txt");
299     CreateDirectoryA("testdir", NULL);
300     createTestFile("testdir\\c.txt");
301     createTestFile("testdir\\d.txt");
302 }
303
304 static void delete_test_files(void)
305 {
306     DeleteFileA("a.txt");
307     DeleteFileA("b.txt");
308     DeleteFileA("testdir\\c.txt");
309     DeleteFileA("testdir\\d.txt");
310     RemoveDirectoryA("testdir");
311
312     DeleteFileA("extract.cab");
313 }
314
315 /* FCI callbacks */
316
317 static void * CDECL mem_alloc(ULONG cb)
318 {
319     return HeapAlloc(GetProcessHeap(), 0, cb);
320 }
321
322 static void CDECL mem_free(void *memory)
323 {
324     HeapFree(GetProcessHeap(), 0, memory);
325 }
326
327 static BOOL CDECL get_next_cabinet(PCCAB pccab, ULONG  cbPrevCab, void *pv)
328 {
329     return TRUE;
330 }
331
332 static LONG CDECL progress(UINT typeStatus, ULONG cb1, ULONG cb2, void *pv)
333 {
334     return 0;
335 }
336
337 static int CDECL file_placed(PCCAB pccab, char *pszFile, LONG cbFile,
338                              BOOL fContinuation, void *pv)
339 {
340     return 0;
341 }
342
343 static INT_PTR CDECL fci_open(char *pszFile, int oflag, int pmode, int *err, void *pv)
344 {
345     HANDLE handle;
346     DWORD dwAccess = 0;
347     DWORD dwShareMode = 0;
348     DWORD dwCreateDisposition = OPEN_EXISTING;
349
350     dwAccess = GENERIC_READ | GENERIC_WRITE;
351     /* FILE_SHARE_DELETE is not supported by Windows Me/98/95 */
352     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
353
354     if (GetFileAttributesA(pszFile) != INVALID_FILE_ATTRIBUTES)
355         dwCreateDisposition = OPEN_EXISTING;
356     else
357         dwCreateDisposition = CREATE_NEW;
358
359     handle = CreateFileA(pszFile, dwAccess, dwShareMode, NULL,
360                          dwCreateDisposition, 0, NULL);
361
362     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszFile);
363
364     return (INT_PTR)handle;
365 }
366
367 static UINT CDECL fci_read(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
368 {
369     HANDLE handle = (HANDLE)hf;
370     DWORD dwRead;
371     BOOL res;
372
373     res = ReadFile(handle, memory, cb, &dwRead, NULL);
374     ok(res, "Failed to ReadFile\n");
375
376     return dwRead;
377 }
378
379 static UINT CDECL fci_write(INT_PTR hf, void *memory, UINT cb, int *err, void *pv)
380 {
381     HANDLE handle = (HANDLE)hf;
382     DWORD dwWritten;
383     BOOL res;
384
385     res = WriteFile(handle, memory, cb, &dwWritten, NULL);
386     ok(res, "Failed to WriteFile\n");
387
388     return dwWritten;
389 }
390
391 static int CDECL fci_close(INT_PTR hf, int *err, void *pv)
392 {
393     HANDLE handle = (HANDLE)hf;
394     ok(CloseHandle(handle), "Failed to CloseHandle\n");
395
396     return 0;
397 }
398
399 static LONG CDECL fci_seek(INT_PTR hf, LONG dist, int seektype, int *err, void *pv)
400 {
401     HANDLE handle = (HANDLE)hf;
402     DWORD ret;
403
404     ret = SetFilePointer(handle, dist, NULL, seektype);
405     ok(ret != INVALID_SET_FILE_POINTER, "Failed to SetFilePointer\n");
406
407     return ret;
408 }
409
410 static int CDECL fci_delete(char *pszFile, int *err, void *pv)
411 {
412     BOOL ret = DeleteFileA(pszFile);
413     ok(ret, "Failed to DeleteFile %s\n", pszFile);
414
415     return 0;
416 }
417
418 static BOOL CDECL get_temp_file(char *pszTempName, int cbTempName, void *pv)
419 {
420     LPSTR tempname;
421
422     tempname = HeapAlloc(GetProcessHeap(), 0, MAX_PATH);
423     GetTempFileNameA(".", "xx", 0, tempname);
424
425     if (tempname && (strlen(tempname) < (unsigned)cbTempName))
426     {
427         lstrcpyA(pszTempName, tempname);
428         HeapFree(GetProcessHeap(), 0, tempname);
429         return TRUE;
430     }
431
432     HeapFree(GetProcessHeap(), 0, tempname);
433
434     return FALSE;
435 }
436
437 static INT_PTR CDECL get_open_info(char *pszName, USHORT *pdate, USHORT *ptime,
438                                    USHORT *pattribs, int *err, void *pv)
439 {
440     BY_HANDLE_FILE_INFORMATION finfo;
441     FILETIME filetime;
442     HANDLE handle;
443     DWORD attrs;
444     BOOL res;
445
446     handle = CreateFile(pszName, GENERIC_READ, FILE_SHARE_READ, NULL,
447                         OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
448
449     ok(handle != INVALID_HANDLE_VALUE, "Failed to CreateFile %s\n", pszName);
450
451     res = GetFileInformationByHandle(handle, &finfo);
452     ok(res, "Expected GetFileInformationByHandle to succeed\n");
453
454     FileTimeToLocalFileTime(&finfo.ftLastWriteTime, &filetime);
455     FileTimeToDosDateTime(&filetime, pdate, ptime);
456
457     attrs = GetFileAttributes(pszName);
458     ok(attrs != INVALID_FILE_ATTRIBUTES, "Failed to GetFileAttributes\n");
459     /* fixme: should convert attrs to *pattribs, make sure
460      * have a test that catches the fact that we don't?
461      */
462
463     return (INT_PTR)handle;
464 }
465
466 static void add_file(HFCI hfci, char *file)
467 {
468     char path[MAX_PATH];
469     BOOL res;
470
471     lstrcpyA(path, CURR_DIR);
472     lstrcatA(path, "\\");
473     lstrcatA(path, file);
474
475     res = FCIAddFile(hfci, path, file, FALSE, get_next_cabinet, progress,
476                      get_open_info, tcompTYPE_MSZIP);
477     ok(res, "Expected FCIAddFile to succeed\n");
478 }
479
480 static void set_cab_parameters(PCCAB pCabParams)
481 {
482     ZeroMemory(pCabParams, sizeof(CCAB));
483
484     pCabParams->cb = MEDIA_SIZE;
485     pCabParams->cbFolderThresh = FOLDER_THRESHOLD;
486     pCabParams->setID = 0xbeef;
487     lstrcpyA(pCabParams->szCabPath, CURR_DIR);
488     lstrcatA(pCabParams->szCabPath, "\\");
489     lstrcpyA(pCabParams->szCab, "extract.cab");
490 }
491
492 static void create_cab_file(void)
493 {
494     CCAB cabParams;
495     HFCI hfci;
496     ERF erf;
497     static CHAR a_txt[]         = "a.txt",
498                 b_txt[]         = "b.txt",
499                 testdir_c_txt[] = "testdir\\c.txt",
500                 testdir_d_txt[] = "testdir\\d.txt";
501     BOOL res;
502
503     set_cab_parameters(&cabParams);
504
505     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
506                       fci_read, fci_write, fci_close, fci_seek, fci_delete,
507                       get_temp_file, &cabParams, NULL);
508
509     ok(hfci != NULL, "Failed to create an FCI context\n");
510
511     add_file(hfci, a_txt);
512     add_file(hfci, b_txt);
513     add_file(hfci, testdir_c_txt);
514     add_file(hfci, testdir_d_txt);
515
516     res = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
517     ok(res, "Failed to flush the cabinet\n");
518
519     res = FCIDestroy(hfci);
520     ok(res, "Failed to destroy the cabinet\n");
521 }
522
523 static void test_FDIIsCabinet(void)
524 {
525     ERF erf;
526     BOOL ret;
527     HFDI hfdi;
528     INT_PTR fd;
529     FDICABINETINFO cabinfo;
530     char temp[] = "temp.txt";
531     char extract[] = "extract.cab";
532
533     create_test_files();
534     create_cab_file();
535
536     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
537                      fdi_write, fdi_close, fdi_seek,
538                      cpuUNKNOWN, &erf);
539     ok(hfdi != NULL, "Expected non-NULL context\n");
540
541     /* native crashes if hfdi or cabinfo are NULL or invalid */
542
543     /* invalid file handle */
544     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
545     SetLastError(0xdeadbeef);
546     ret = FDIIsCabinet(hfdi, -1, &cabinfo);
547     ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
548     ok(GetLastError() == ERROR_INVALID_HANDLE,
549        "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
550     ok(cabinfo.cbCabinet == 0, "Expected 0, got %d\n", cabinfo.cbCabinet);
551     ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles);
552     ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders);
553     ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
554     ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID);
555
556     createTestFile("temp.txt");
557     fd = fdi_open(temp, 0, 0);
558
559     /* file handle doesn't point to a cabinet */
560     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
561     SetLastError(0xdeadbeef);
562     ret = FDIIsCabinet(hfdi, fd, &cabinfo);
563     ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
564     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
565     ok(cabinfo.cbCabinet == 0, "Expected 0, got %d\n", cabinfo.cbCabinet);
566     ok(cabinfo.cFiles == 0, "Expected 0, got %d\n", cabinfo.cFiles);
567     ok(cabinfo.cFolders == 0, "Expected 0, got %d\n", cabinfo.cFolders);
568     ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
569     ok(cabinfo.setID == 0, "Expected 0, got %d\n", cabinfo.setID);
570
571     fdi_close(fd);
572     DeleteFileA("temp.txt");
573
574     /* try a real cab */
575     fd = fdi_open(extract, 0, 0);
576     ZeroMemory(&cabinfo, sizeof(FDICABINETINFO));
577     SetLastError(0xdeadbeef);
578     ret = FDIIsCabinet(hfdi, fd, &cabinfo);
579     ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
580     ok(GetLastError() == 0xdeadbeef, "Expected 0xdeadbeef, got %d\n", GetLastError());
581     ok(cabinfo.cFiles == 4, "Expected 4, got %d\n", cabinfo.cFiles);
582     ok(cabinfo.cFolders == 1, "Expected 1, got %d\n", cabinfo.cFolders);
583     ok(cabinfo.setID == 0xbeef, "Expected 0xbeef, got %d\n", cabinfo.setID);
584     todo_wine
585     {
586         ok(cabinfo.cbCabinet == 182, "Expected 182, got %d\n", cabinfo.cbCabinet);
587         ok(cabinfo.iCabinet == 0, "Expected 0, got %d\n", cabinfo.iCabinet);
588     }
589
590     fdi_close(fd);
591     FDIDestroy(hfdi);
592     delete_test_files();
593 }
594
595
596 static INT_PTR __cdecl CopyProgress(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
597 {
598     return 0;
599 }
600
601 static void test_FDICopy(void)
602 {
603     CCAB cabParams;
604     HFDI hfdi;
605     HFCI hfci;
606     ERF erf;
607     BOOL ret;
608     char name[] = "extract.cab";
609     char path[MAX_PATH + 1];
610
611     set_cab_parameters(&cabParams);
612
613     hfci = FCICreate(&erf, file_placed, mem_alloc, mem_free, fci_open,
614                      fci_read, fci_write, fci_close, fci_seek,
615                      fci_delete, get_temp_file, &cabParams, NULL);
616
617     ret = FCIFlushCabinet(hfci, FALSE, get_next_cabinet, progress);
618     ok(ret, "Failed to flush the cabinet\n");
619
620     FCIDestroy(hfci);
621
622     lstrcpyA(path, CURR_DIR);
623
624     /* path doesn't have a trailing backslash */
625     if (lstrlenA(path) > 2)
626     {
627         hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
628                          fdi_write, fdi_close, fdi_seek,
629                          cpuUNKNOWN, &erf);
630
631         SetLastError(0xdeadbeef);
632         ret = FDICopy(hfdi, name, path, 0, CopyProgress, NULL, 0);
633         ok(ret == FALSE, "Expected FALSE, got %d\n", ret);
634         todo_wine
635         {
636             ok(GetLastError() == ERROR_INVALID_HANDLE,
637                "Expected ERROR_INVALID_HANDLE, got %d\n", GetLastError());
638         }
639
640         FDIDestroy(hfdi);
641     }
642     else
643         skip("Running on a root drive directory.\n");
644
645     lstrcatA(path, "\\");
646
647     hfdi = FDICreate(fdi_alloc, fdi_free, fdi_open, fdi_read,
648                      fdi_write, fdi_close, fdi_seek,
649                      cpuUNKNOWN, &erf);
650
651     /* cabinet with no files or folders */
652     SetLastError(0xdeadbeef);
653     ret = FDICopy(hfdi, name, path, 0, CopyProgress, NULL, 0);
654     todo_wine
655     ok(ret == TRUE, "Expected TRUE, got %d\n", ret);
656     ok(GetLastError() == 0, "Expected 0f, got %d\n", GetLastError());
657
658     FDIDestroy(hfdi);
659
660     DeleteFileA(name);
661 }
662
663
664 START_TEST(fdi)
665 {
666     test_FDICreate();
667     test_FDIDestroy();
668     test_FDIIsCabinet();
669     test_FDICopy();
670 }