comctl32/tests: Use win_skip instead of trace.
[wine] / dlls / comctl32 / tests / dpa.c
1 /*
2  * Unit tests for DPA functions
3  *
4  * Copyright 2003 Uwe Bonnes
5  * Copyright 2005 Felix Nawothnig
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21
22 #define COBJMACROS
23
24 #include <stdarg.h>
25
26 #include "windef.h"
27 #include "winbase.h"
28 #include "winuser.h"
29 #include "commctrl.h"
30 #include "objidl.h"
31
32 #include "wine/test.h"
33
34 #define DPAM_NOSORT 0x1
35 #define DPAM_INSERT 0x4
36 #define DPAM_DELETE 0x8
37
38 typedef struct _ITEMDATA
39 {
40     INT   iPos;
41     PVOID pvData;
42 } ITEMDATA, *LPITEMDATA;
43
44 typedef PVOID   (CALLBACK *PFNDPAMERGE)(UINT,PVOID,PVOID,LPARAM);
45 typedef HRESULT (CALLBACK *PFNDPASTM)(LPITEMDATA,IStream*,LPARAM);
46
47 static HDPA    (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
48 static HDPA    (WINAPI *pDPA_Create)(INT);
49 static HDPA    (WINAPI *pDPA_CreateEx)(INT,HANDLE);
50 static PVOID   (WINAPI *pDPA_DeleteAllPtrs)(const HDPA);
51 static PVOID   (WINAPI *pDPA_DeletePtr)(const HDPA,INT);
52 static BOOL    (WINAPI *pDPA_Destroy)(const HDPA);
53 static VOID    (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
54 static VOID    (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); 
55 static INT     (WINAPI *pDPA_GetPtr)(const HDPA,INT);
56 static INT     (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
57 static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
58 static INT     (WINAPI *pDPA_InsertPtr)(const HDPA,INT,PVOID);
59 static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTM,IStream*,LPARAM);
60 static BOOL    (WINAPI *pDPA_Merge)(const HDPA,const HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
61 static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTM,IStream*,LPARAM);
62 static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
63 static BOOL    (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
64 static BOOL    (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);
65
66 #define COMCTL32_GET_PROC(func, ord) \
67   ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
68    : (trace( #func " not exported\n"), 0)) 
69
70 static BOOL InitFunctionPtrs(HMODULE hcomctl32)
71 {
72     /* 4.00+ */
73     if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
74        COMCTL32_GET_PROC(DPA_Create, 328) &&
75        COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
76        COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
77        COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
78        COMCTL32_GET_PROC(DPA_Destroy, 329) &&
79        COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
80        COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
81        COMCTL32_GET_PROC(DPA_Grow, 330) &&
82        COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
83        COMCTL32_GET_PROC(DPA_Search, 339) &&
84        COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
85        COMCTL32_GET_PROC(DPA_Sort, 338))
86     {
87         /* 4.71+ */
88         COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
89         COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
90         COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
91         COMCTL32_GET_PROC(DPA_Merge, 11) &&
92         COMCTL32_GET_PROC(DPA_SaveStream, 10);
93
94         return TRUE;
95     }
96
97     return FALSE;
98 }
99
100 /* Callbacks */
101 static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
102 {
103     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
104     return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
105 }
106
107 static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
108 {
109     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
110     return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
111 }
112
113 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
114 {
115     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
116     return p1;
117 }        
118
119 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
120 {
121     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
122     return ((PCHAR)p2)+1;
123 }
124
125 static INT nEnum;
126
127 static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
128 {   
129     INT i;
130
131     i = pDPA_GetPtrIndex(lp, pItem);
132     ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
133     nEnum++;
134     pDPA_SetPtr(lp, i, (PVOID)7);
135     return pItem != (PVOID)3;
136 }
137
138 static HRESULT CALLBACK CB_Save(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
139 {
140     HRESULT hRes;
141     
142     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
143     hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
144     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
145     hRes = IStream_Write(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
146     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
147     return S_OK;
148 }
149
150 static HRESULT CALLBACK CB_Load(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
151 {
152     HRESULT hRes;
153     INT iOldPos;
154     
155     iOldPos = pInfo->iPos;
156     ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
157     hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
158     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
159     ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
160     hRes = IStream_Read(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
161     ok(hRes == S_OK, "hRes=0x%x\n", hRes);
162     return S_OK;
163 }
164
165 static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
166 {
167     DWORD dwOut = 0;
168     INT i;
169
170     for(i = 0; i < 8;)
171     {
172         ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
173         if(!ulItem) break;
174         dwOut = dwOut << 4 | (ulItem & 0xf);
175     }
176     
177     *pdwOut = dwOut;
178
179     if(dwOut != dwIn)
180     {
181         pDPA_DeleteAllPtrs(dpa);
182         
183         do
184         {
185             pDPA_InsertPtr(dpa, 0, (PVOID)(dwIn & 0xf));
186             dwIn >>= 4;
187         }
188         while(dwIn);
189         
190         return FALSE;
191     }
192     
193     return TRUE;
194 }
195
196 static void test_dpa(void)
197 {
198     SYSTEM_INFO si;
199     HANDLE hHeap;
200     HDPA dpa, dpa2, dpa3;
201     INT ret, i;
202     PVOID p;
203     DWORD dw, dw2, dw3;
204     HRESULT hRes;
205     BOOL rc;
206     
207     GetSystemInfo(&si);
208     hHeap = HeapCreate(0, 1, 2);
209     ok(hHeap != NULL, "error=%d\n", GetLastError());
210     dpa3 = pDPA_CreateEx(0, hHeap);
211     ok(dpa3 != NULL, "\n");
212     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
213     todo_wine ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY, 
214        "ret=%d error=%d\n", ret, GetLastError());
215         
216     dpa = pDPA_Create(0);
217     ok(dpa != NULL, "\n");
218
219     /* Set item with out of bound index */
220     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
221     /* Fill the created gap */
222     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
223     rc=CheckDPA(dpa, 0x56, &dw);
224     ok(rc, "dw=0x%x\n", dw);
225     
226     /* Prepend item */
227     ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
228     ok(ret == 1, "ret=%d\n", ret);
229     /* Append item using correct index */
230     ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
231     ok(ret == 3, "ret=%d\n", ret);
232     /* Append item using out of bound index */
233     ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
234     ok(ret == 4, "ret=%d\n", ret);
235     /* Append item using DPA_APPEND */ 
236     ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
237     ok(ret == 5, "ret=%d\n", ret);
238
239     rc=CheckDPA(dpa, 0x516324, &dw);
240     ok(rc, "dw=0x%x\n", dw);
241
242     for(i = 1; i <= 6; i++)
243     {
244         INT j, k;
245         k = pDPA_GetPtrIndex(dpa, (PVOID)i);
246         /* Linear searches should work on unsorted DPAs */
247         j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, 0);
248         ok(j == k, "j=%d k=%d\n", j, k);
249     }
250
251     /* Sort DPA */
252     ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
253     rc=CheckDPA(dpa, 0x654321, &dw);
254     ok(rc, "dw=0x%x\n", dw);
255     
256     /* Clone into a new DPA */
257     dpa2 = pDPA_Clone(dpa, NULL);
258     ok(dpa2 != NULL, "\n");
259     /* The old data should have been preserved */
260     rc=CheckDPA(dpa2, 0x654321, &dw2);
261     ok(rc, "dw=0x%x\n", dw2);
262     ok(pDPA_Sort(dpa, CB_CmpLT, 0xdeadbeef), "\n");
263     
264     /* Test if the DPA itself was really copied */
265     rc=CheckDPA(dpa,  0x123456, &dw);
266     ok(rc, "dw=0x%x\n",  dw );
267     rc=CheckDPA(dpa2, 0x654321, &dw2);
268     ok(rc, "dw2=0x%x\n", dw2);
269
270     /* Clone into an old DPA */
271     p = NULL; SetLastError(ERROR_SUCCESS);
272     p = pDPA_Clone(dpa, dpa3);
273     ok(p == dpa3, "p=%p\n", p);
274     rc=CheckDPA(dpa3, 0x123456, &dw3);
275     ok(rc, "dw3=0x%x\n", dw3);
276
277     for(i = 1; i <= 6; i++)
278     {
279         INT j;
280
281         /* The array is in order so ptr == index+1 */
282         j = pDPA_GetPtrIndex(dpa, (PVOID)i);
283         ok(j+1 == i, "j=%d i=%d\n", j, i);
284         j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
285         ok(j+1 == i, "j=%d i=%d\n", j, i);
286
287         /* Linear searches respect iStart ... */
288         j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, 0);
289         ok(j == DPA_ERR, "j=%d\n", j);
290         /* ... but for a binary search it's ignored */
291         j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
292         todo_wine ok(j+1 == i, "j=%d i=%d\n", j, i);
293     }
294     
295     /* Try to get the index of a nonexistent item */
296     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
297     ok(i == DPA_ERR, "i=%d\n", i);
298     
299     /* Try to delete out of bound indexes */
300     p = pDPA_DeletePtr(dpa, -1);
301     ok(p == NULL, "p=%p\n", p);
302     p = pDPA_DeletePtr(dpa, 6);
303     ok(p == NULL, "p=%p\n", p);
304
305     /* Delete the third item */
306     p = pDPA_DeletePtr(dpa, 2);
307     ok(p == (PVOID)3, "p=%p\n", p);
308     rc=CheckDPA(dpa, 0x12456, &dw);
309     ok(rc, "dw=0x%x\n", dw);
310
311     /* Check where to re-insert the deleted item */
312     i = pDPA_Search(dpa, (PVOID)3, 0, 
313                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTAFTER);
314     ok(i == 2, "i=%d\n", i);
315     /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
316     i = pDPA_Search(dpa, (PVOID)3, 0,
317                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTBEFORE);
318     ok(i == 2, "i=%d\n", i);
319     /* without DPAS_INSERTBEFORE/AFTER */
320     i = pDPA_Search(dpa, (PVOID)3, 0,
321                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
322     ok(i == -1, "i=%d\n", i);
323
324     /* Re-insert the item */
325     ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
326     ok(ret == 2, "ret=%d i=%d\n", ret, 2);
327     rc=CheckDPA(dpa, 0x123456, &dw);
328     ok(rc, "dw=0x%x\n", dw);
329     
330     /* When doing a binary search while claiming reverse order all indexes
331      * should be bogus */
332     for(i = 0; i < 6; i++)
333     {
334         INT j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpGT, 0xdeadbeef,
335                             DPAS_SORTED|DPAS_INSERTBEFORE);
336         ok(j != i, "i=%d\n", i);
337     }
338
339     if(pDPA_Merge)
340     {
341         /* Delete all even entries from dpa */
342         p = pDPA_DeletePtr(dpa, 1);
343         p = pDPA_DeletePtr(dpa, 2);
344         p = pDPA_DeletePtr(dpa, 3);
345         rc=CheckDPA(dpa, 0x135, &dw);
346         ok(rc, "dw=0x%x\n", dw);
347     
348         /* Delete all odd entries from dpa2 */
349         pDPA_Merge(dpa2, dpa, DPAM_DELETE, 
350                    CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
351         todo_wine
352         {
353             rc=CheckDPA(dpa2, 0x246, &dw2);
354             ok(rc, "dw=0x%x\n", dw2);
355         }
356     
357         /* Merge dpa3 into dpa2 and dpa */
358         pDPA_Merge(dpa, dpa3, DPAM_INSERT|DPAM_NOSORT, 
359                    CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
360         pDPA_Merge(dpa2, dpa3, DPAM_INSERT|DPAM_NOSORT, 
361                    CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
362     
363         rc=CheckDPA(dpa,  0x123456, &dw);
364         ok(rc, "dw=0x%x\n",  dw);
365         rc=CheckDPA(dpa2, 0x123456, &dw2);
366         ok(rc ||
367            broken(!rc), /* win98 */
368            "dw2=0x%x\n", dw2);
369         rc=CheckDPA(dpa3, 0x123456, &dw3);
370         ok(rc, "dw3=0x%x\n", dw3);
371     }
372
373     if(pDPA_EnumCallback)
374     {
375         nEnum = 0;
376         pDPA_EnumCallback(dpa2, CB_EnumFirstThree, (PVOID)dpa2);
377         rc=CheckDPA(dpa2, 0x777456, &dw2);
378         ok(rc, "dw=0x%x\n", dw2);
379         ok(nEnum == 3, "nEnum=%d\n", nEnum);
380     }
381     
382     /* Setting item with huge index should work */
383     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
384     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
385     ok(ret == 0x12345, "ret=%d\n", ret);
386           
387     pDPA_DeleteAllPtrs(dpa2);
388     rc=CheckDPA(dpa2, 0, &dw2);
389     ok(rc, "dw2=0x%x\n", dw2);
390     pDPA_Destroy(dpa2);
391
392     if(pDPA_DestroyCallback)
393     {
394         nEnum = 0;
395         pDPA_DestroyCallback(dpa3, CB_EnumFirstThree, dpa3);
396         ok(nEnum == 3, "nEnum=%d\n", nEnum);
397     }
398     else pDPA_Destroy(dpa3);
399
400     if(!pDPA_SaveStream)
401         goto skip_stream_tests;
402
403     hRes = CoInitialize(NULL);
404     if(hRes == S_OK)
405     {
406         static const WCHAR szStg[] = { 'S','t','g',0 };
407         IStorage* pStg = NULL;
408         IStream* pStm = NULL;
409         LARGE_INTEGER liZero;
410         DWORD dwMode;
411         liZero.QuadPart = 0;
412
413         dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
414         hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
415         ok(hRes == S_OK, "hRes=0x%x\n", hRes);
416
417         hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
418         ok(hRes == S_OK, "hRes=0x%x\n", hRes);
419
420         hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
421         todo_wine ok(hRes == S_OK, "hRes=0x%x\n", hRes);
422         pDPA_Destroy(dpa);
423         
424         hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
425         ok(hRes == S_OK, "hRes=0x%x\n", hRes);
426         hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
427         todo_wine
428         {
429             ok(hRes == S_OK, "hRes=0x%x\n", hRes);
430             rc=CheckDPA(dpa, 0x123456, &dw);
431             ok(rc, "dw=0x%x\n", dw);
432         }
433
434         ret = IStream_Release(pStm);
435         ok(!ret, "ret=%d\n", ret);
436         
437         ret = IStorage_Release(pStg);
438         ok(!ret, "ret=%d\n", ret);
439
440         CoUninitialize();
441     }
442     else ok(0, "hResult: %d\n", hRes);
443
444 skip_stream_tests:
445     pDPA_Destroy(dpa);
446 }
447
448 START_TEST(dpa)
449 {
450     HMODULE hcomctl32;
451
452     hcomctl32 = GetModuleHandleA("comctl32.dll");
453
454     if(InitFunctionPtrs(hcomctl32))
455         test_dpa();
456     else
457         win_skip("Needed functions are not available\n");
458 }