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