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
)(ULONG_PTR
)(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 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
);
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
)(INT_PTR
)i
);
246 /* Linear searches should work on unsorted DPAs */
247 j
= pDPA_Search(dpa
, (PVOID
)(INT_PTR
)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
)(INT_PTR
)i
);
283 ok(j
+1 == i
, "j=%d i=%d\n", j
, i
);
284 j
= pDPA_Search(dpa
, (PVOID
)(INT_PTR
)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
)(INT_PTR
)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
)(INT_PTR
)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
)(INT_PTR
)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
);
367 broken(!rc
), /* win98 */
369 rc
=CheckDPA(dpa3
, 0x123456, &dw3
);
370 ok(rc
, "dw3=0x%x\n", dw3
);
373 if(pDPA_EnumCallback
)
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
);
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
);
387 pDPA_DeleteAllPtrs(dpa2
);
388 rc
=CheckDPA(dpa2
, 0, &dw2
);
389 ok(rc
, "dw2=0x%x\n", dw2
);
392 if(pDPA_DestroyCallback
)
395 pDPA_DestroyCallback(dpa3
, CB_EnumFirstThree
, dpa3
);
396 ok(nEnum
== 3, "nEnum=%d\n", nEnum
);
398 else pDPA_Destroy(dpa3
);
401 goto skip_stream_tests
;
403 hRes
= CoInitialize(NULL
);
406 static const WCHAR szStg
[] = { 'S','t','g',0 };
407 IStorage
* pStg
= NULL
;
408 IStream
* pStm
= NULL
;
409 LARGE_INTEGER liZero
;
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
);
417 hRes
= IStorage_CreateStream(pStg
, szStg
, dwMode
, 0, 0, &pStm
);
418 ok(hRes
== S_OK
, "hRes=0x%x\n", hRes
);
420 hRes
= pDPA_SaveStream(dpa
, CB_Save
, pStm
, 0xdeadbeef);
421 todo_wine
ok(hRes
== S_OK
, "hRes=0x%x\n", hRes
);
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);
429 ok(hRes
== S_OK
, "hRes=0x%x\n", hRes
);
430 rc
=CheckDPA(dpa
, 0x123456, &dw
);
431 ok(rc
, "dw=0x%x\n", dw
);
434 ret
= IStream_Release(pStm
);
435 ok(!ret
, "ret=%d\n", ret
);
437 ret
= IStorage_Release(pStg
);
438 ok(!ret
, "ret=%d\n", ret
);
442 else ok(0, "hResult: %d\n", hRes
);
452 hcomctl32
= GetModuleHandleA("comctl32.dll");
454 if(InitFunctionPtrs(hcomctl32
))
457 win_skip("Needed functions are not available\n");