2 * Unit tests for DPA functions
4 * Copyright 2003 Uwe Bonnes
5 * Copyright 2005 Felix Nawothnig
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.
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.
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
32 #include "wine/test.h"
34 #define DPAM_NOSORT 0x1
35 #define DPAM_INSERT 0x4
36 #define DPAM_DELETE 0x8
38 typedef struct _ITEMDATA
42 } ITEMDATA, *LPITEMDATA;
44 typedef PVOID (CALLBACK *PFNDPAMERGE)(UINT,PVOID,PVOID,LPARAM);
45 typedef HRESULT (CALLBACK *PFNDPASTM)(LPITEMDATA,IStream*,LPARAM);
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);
66 #define COMCTL32_GET_PROC(func, ord) \
67 ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
68 : (trace( #func " not exported\n"), 0))
70 static BOOL InitFunctionPtrs(HMODULE hcomctl32)
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))
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);
101 static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
103 ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
104 return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
107 static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
109 ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
110 return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
113 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
115 ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
119 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
121 ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
122 return ((PCHAR)p2)+1;
127 static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
131 i = pDPA_GetPtrIndex(lp, pItem);
132 ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
134 pDPA_SetPtr(lp, i, (PVOID)7);
135 return pItem != (PVOID)3;
138 static HRESULT CALLBACK CB_Save(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
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);
150 static HRESULT CALLBACK CB_Load(LPITEMDATA pInfo, IStream *pStm, LPARAM lp)
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);
165 static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
172 ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
174 dwOut = dwOut << 4 | (ulItem & 0xf);
181 pDPA_DeleteAllPtrs(dpa);
185 pDPA_InsertPtr(dpa, 0, (PVOID)(dwIn & 0xf));
196 static void test_dpa(void)
200 HDPA dpa, dpa2, dpa3;
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());
216 dpa = pDPA_Create(0);
217 ok(dpa != NULL, "\n");
219 /* Set item with out of bound index */
220 ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
221 /* Fill the greated gap */
222 ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
223 rc=CheckDPA(dpa, 0x56, &dw);
224 ok(rc, "dw=0x%x\n", dw);
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);
239 rc=CheckDPA(dpa, 0x516324, &dw);
240 ok(rc, "dw=0x%x\n", dw);
242 for(i = 1; i <= 6; i++)
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);
252 ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
253 rc=CheckDPA(dpa, 0x654321, &dw);
254 ok(rc, "dw=0x%x\n", dw);
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");
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);
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);
277 for(i = 1; i <= 6; i++)
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);
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);
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);
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);
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);
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);
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);
330 /* When doing a binary search while claiming reverse order all indexes
332 for(i = 0; i < 6; i++)
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);
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);
348 /* Delete all odd entries from dpa2 */
349 pDPA_Merge(dpa2, dpa, DPAM_DELETE,
350 CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
353 rc=CheckDPA(dpa2, 0x246, &dw2);
354 ok(rc, "dw=0x%x\n", dw2);
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);
363 rc=CheckDPA(dpa, 0x123456, &dw);
364 ok(rc, "dw=0x%x\n", dw);
365 rc=CheckDPA(dpa2, 0x123456, &dw2);
366 ok(rc, "dw2=0x%x\n", dw2);
367 rc=CheckDPA(dpa3, 0x123456, &dw3);
368 ok(rc, "dw3=0x%x\n", dw3);
371 if(pDPA_EnumCallback)
374 pDPA_EnumCallback(dpa2, CB_EnumFirstThree, (PVOID)dpa2);
375 rc=CheckDPA(dpa2, 0x777456, &dw2);
376 ok(rc, "dw=0x%x\n", dw2);
377 ok(nEnum == 3, "nEnum=%d\n", nEnum);
380 /* Setting item with huge index should work */
381 ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
382 ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
383 ok(ret == 0x12345, "ret=%d\n", ret);
385 pDPA_DeleteAllPtrs(dpa2);
386 rc=CheckDPA(dpa2, 0, &dw2);
387 ok(rc, "dw2=0x%x\n", dw2);
390 if(pDPA_DestroyCallback)
393 pDPA_DestroyCallback(dpa3, CB_EnumFirstThree, dpa3);
394 ok(nEnum == 3, "nEnum=%d\n", nEnum);
396 else pDPA_Destroy(dpa3);
399 goto skip_stream_tests;
401 hRes = CoInitialize(NULL);
404 static const WCHAR szStg[] = { 'S','t','g',0 };
405 IStorage* pStg = NULL;
406 IStream* pStm = NULL;
407 LARGE_INTEGER liZero;
411 dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
412 hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
413 ok(hRes == S_OK, "hRes=0x%x\n", hRes);
415 hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
416 ok(hRes == S_OK, "hRes=0x%x\n", hRes);
418 hRes = pDPA_SaveStream(dpa, CB_Save, pStm, 0xdeadbeef);
419 todo_wine ok(hRes == S_OK, "hRes=0x%x\n", hRes);
422 hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
423 ok(hRes == S_OK, "hRes=0x%x\n", hRes);
424 hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, 0xdeadbeef);
427 ok(hRes == S_OK, "hRes=0x%x\n", hRes);
428 rc=CheckDPA(dpa, 0x123456, &dw);
429 ok(rc, "dw=0x%x\n", dw);
433 ret = IStream_Release(pStm);
434 ok(!ret, "ret=%d\n", ret);
436 ret = IStorage_Release(pStg);
437 ok(!ret, "ret=%d\n", ret);
441 else ok(0, "hResult: %d\n", hRes);
451 hcomctl32 = GetModuleHandleA("comctl32.dll");
453 if(InitFunctionPtrs(hcomctl32))
456 trace("skipping tests\n");
458 FreeLibrary(hcomctl32);