comctl32: header: Copy some fields on INSERTITEM even if they are not in the mask.
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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%lx\n", hRes);
145     hRes = IStream_Write(pStm, &pInfo->pvData, sizeof(PVOID), NULL);
146     ok(hRes == S_OK, "hRes=0x%lx\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%lx\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%lx\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     
206     GetSystemInfo(&si);
207     hHeap = HeapCreate(0, 1, 2);
208     ok(hHeap != NULL, "error=%ld\n", GetLastError());
209     dpa3 = pDPA_CreateEx(0, hHeap);
210     ok(dpa3 != NULL, "\n");
211     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
212     todo_wine ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY, 
213        "ret=%d error=%ld\n", ret, GetLastError());
214         
215     dpa = pDPA_Create(0);
216     ok(dpa != NULL, "\n");
217
218     /* Set item with out of bound index */
219     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
220     /* Fill the greated gap */
221     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
222     ok(CheckDPA(dpa, 0x56, &dw), "dw=0x%lx\n", dw);
223     
224     /* Prepend item */
225     ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
226     ok(ret == 1, "ret=%d\n", ret);
227     /* Append item using correct index */
228     ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
229     ok(ret == 3, "ret=%d\n", ret);
230     /* Append item using out of bound index */
231     ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
232     ok(ret == 4, "ret=%d\n", ret);
233     /* Append item using DPA_APPEND */ 
234     ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
235     ok(ret == 5, "ret=%d\n", ret);
236
237     ok(CheckDPA(dpa, 0x516324, &dw), "dw=0x%lx\n", dw);
238
239     for(i = 1; i <= 6; i++)
240     {
241         INT j, k;
242         k = pDPA_GetPtrIndex(dpa, (PVOID)i);
243         /* Linear searches should work on unsorted DPAs */
244         j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, 0);
245         ok(j == k, "j=%d k=%d\n", j, k);
246     }
247
248     /* Sort DPA */
249     ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
250     ok(CheckDPA(dpa, 0x654321, &dw), "dw=0x%lx\n", dw);
251     
252     /* Clone into a new DPA */
253     dpa2 = pDPA_Clone(dpa, NULL);
254     ok(dpa2 != NULL, "\n");
255     /* The old data should have been preserved */
256     ok(CheckDPA(dpa2, 0x654321, &dw2), "dw=0x%lx\n", dw2);
257     ok(pDPA_Sort(dpa, CB_CmpLT, 0xdeadbeef), "\n");
258     
259     /* Test if the DPA itself was really copied */
260     ok(CheckDPA(dpa,  0x123456, &dw),  "dw=0x%lx\n",  dw );
261     ok(CheckDPA(dpa2, 0x654321, &dw2), "dw2=0x%lx\n", dw2);
262
263     /* Clone into an old DPA */
264     p = NULL; SetLastError(ERROR_SUCCESS);
265     p = pDPA_Clone(dpa, dpa3);
266     ok(p == dpa3, "p=%p\n", p);
267     ok(CheckDPA(dpa3, 0x123456, &dw3), "dw3=0x%lx\n", dw3);
268
269     for(i = 1; i <= 6; i++)
270     {
271         INT j;
272
273         /* The array is in order so ptr == index+1 */
274         j = pDPA_GetPtrIndex(dpa, (PVOID)i);
275         ok(j+1 == i, "j=%d i=%d\n", j, i);
276         j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
277         ok(j+1 == i, "j=%d i=%d\n", j, i);
278
279         /* Linear searches respect iStart ... */
280         j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, 0);
281         ok(j == DPA_ERR, "j=%d\n", j);
282         /* ... but for a binary search it's ignored */
283         j = pDPA_Search(dpa, (PVOID)i, i+1, CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
284         todo_wine ok(j+1 == i, "j=%d i=%d\n", j, i);
285     }
286     
287     /* Try to get the index of a nonexistent item */
288     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
289     ok(i == DPA_ERR, "i=%d\n", i);
290     
291     /* Try to delete out of bound indexes */
292     p = pDPA_DeletePtr(dpa, -1);
293     ok(p == NULL, "p=%p\n", p);
294     p = pDPA_DeletePtr(dpa, 6);
295     ok(p == NULL, "p=%p\n", p);
296
297     /* Delete the third item */
298     p = pDPA_DeletePtr(dpa, 2);
299     ok(p == (PVOID)3, "p=%p\n", p);
300     ok(CheckDPA(dpa, 0x12456, &dw), "dw=0x%lx\n", dw);
301
302     /* Check where to re-insert the deleted item */
303     i = pDPA_Search(dpa, (PVOID)3, 0, 
304                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTAFTER);
305     ok(i == 2, "i=%d\n", i);
306     /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
307     i = pDPA_Search(dpa, (PVOID)3, 0,
308                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTBEFORE);
309     ok(i == 2, "i=%d\n", i);
310     /* without DPAS_INSERTBEFORE/AFTER */
311     i = pDPA_Search(dpa, (PVOID)3, 0,
312                     CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
313     ok(i == -1, "i=%d\n", i);
314
315     /* Re-insert the item */
316     ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
317     ok(ret == 2, "ret=%d i=%d\n", ret, 2);
318     ok(CheckDPA(dpa, 0x123456, &dw), "dw=0x%lx\n", dw);
319     
320     /* When doing a binary search while claiming reverse order all indexes
321      * should be bogus */
322     for(i = 0; i < 6; i++)
323     {
324         INT j = pDPA_Search(dpa, (PVOID)i, 0, CB_CmpGT, 0xdeadbeef,
325                             DPAS_SORTED|DPAS_INSERTBEFORE);
326         ok(j != i, "i=%d\n", i);
327     }
328
329     if(pDPA_Merge)
330     {
331         /* Delete all even entries from dpa */
332         p = pDPA_DeletePtr(dpa, 1);
333         p = pDPA_DeletePtr(dpa, 2);
334         p = pDPA_DeletePtr(dpa, 3);
335         ok(CheckDPA(dpa, 0x135, &dw), "dw=0x%lx\n", dw);
336     
337         /* Delete all odd entries from dpa2 */
338         pDPA_Merge(dpa2, dpa, DPAM_DELETE, 
339                    CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
340         todo_wine ok(CheckDPA(dpa2, 0x246, &dw2), "dw=0x%lx\n", dw2);
341     
342         /* Merge dpa3 into dpa2 and dpa */
343         pDPA_Merge(dpa, dpa3, DPAM_INSERT|DPAM_NOSORT, 
344                    CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
345         pDPA_Merge(dpa2, dpa3, DPAM_INSERT|DPAM_NOSORT, 
346                    CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
347     
348         ok(CheckDPA(dpa,  0x123456, &dw ), "dw=0x%lx\n",  dw);
349         ok(CheckDPA(dpa2, 0x123456, &dw2), "dw2=0x%lx\n", dw2);
350         ok(CheckDPA(dpa3, 0x123456, &dw3), "dw3=0x%lx\n", dw3);
351     }
352
353     if(pDPA_EnumCallback)
354     {
355         nEnum = 0;
356         pDPA_EnumCallback(dpa2, CB_EnumFirstThree, (PVOID)dpa2);
357         ok(CheckDPA(dpa2, 0x777456, &dw2), "dw=0x%lx\n", dw2);
358         ok(nEnum == 3, "nEnum=%d\n", nEnum);
359     }
360     
361     /* Setting item with huge index should work */
362     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
363     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
364     ok(ret == 0x12345, "ret=%d\n", ret);
365           
366     pDPA_DeleteAllPtrs(dpa2);
367     ok(CheckDPA(dpa2, 0, &dw2), "dw2=0x%lx\n", dw2);
368     pDPA_Destroy(dpa2);
369
370     if(pDPA_DestroyCallback)
371     {
372         nEnum = 0;
373         pDPA_DestroyCallback(dpa3, CB_EnumFirstThree, dpa3);
374         ok(nEnum == 3, "nEnum=%d\n", nEnum);
375     }
376     else pDPA_Destroy(dpa3);
377
378     if(!pDPA_SaveStream)
379         goto skip_stream_tests;
380
381     hRes = CoInitialize(NULL);
382     if(hRes == S_OK)
383     {
384         static const WCHAR szStg[] = { 'S','t','g',0 };
385         IStorage* pStg = NULL;
386         IStream* pStm = NULL;
387         LARGE_INTEGER liZero;
388         DWORD dwMode;
389         liZero.QuadPart = 0;
390
391         dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
392         hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
393         ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
394
395         hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
396         ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
397
398         hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
399         todo_wine ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
400         pDPA_Destroy(dpa);
401         
402         hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
403         ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
404         hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
405         todo_wine ok(hRes == S_OK, "hRes=0x%lx\n", hRes);
406         todo_wine ok(CheckDPA(dpa, 0x123456, &dw), "dw=0x%lx\n", dw);
407         pDPA_Destroy(dpa);
408
409         ret = IStream_Release(pStm);
410         ok(!ret, "ret=%d\n", ret);
411         
412         ret = IStorage_Release(pStg);
413         ok(!ret, "ret=%d\n", ret);
414
415         CoUninitialize();
416     }
417     else ok(0, "hResult: %ld\n", hRes);
418
419 skip_stream_tests:
420     pDPA_Destroy(dpa);
421 }
422
423 START_TEST(dpa)
424 {
425     HMODULE hcomctl32;
426
427     hcomctl32 = GetModuleHandleA("comctl32.dll");
428
429     if(!hcomctl32)
430     {
431         ok(0, "error=%ld\n", GetLastError());
432         return;
433     }
434
435     if(InitFunctionPtrs(hcomctl32))
436         test_dpa();
437     else
438         trace("skipping tests\n");
439
440     FreeLibrary(hcomctl32);
441 }