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