comctl32/listview: Use LVM_SETITEMSTATE directly in tests.
[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., 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 "winuser.h"
29 #include "commctrl.h"
30 #include "objidl.h"
31
32 #include "wine/test.h"
33
34 #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)
35
36 typedef struct _STREAMDATA
37 {
38     DWORD dwSize;
39     DWORD dwData2;
40     DWORD dwItems;
41 } STREAMDATA, *PSTREAMDATA;
42
43 static HDPA    (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
44 static HDPA    (WINAPI *pDPA_Create)(INT);
45 static HDPA    (WINAPI *pDPA_CreateEx)(INT,HANDLE);
46 static PVOID   (WINAPI *pDPA_DeleteAllPtrs)(const HDPA);
47 static PVOID   (WINAPI *pDPA_DeletePtr)(const HDPA,INT);
48 static BOOL    (WINAPI *pDPA_Destroy)(const HDPA);
49 static VOID    (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
50 static VOID    (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); 
51 static INT     (WINAPI *pDPA_GetPtr)(const HDPA,INT);
52 static INT     (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
53 static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
54 static INT     (WINAPI *pDPA_InsertPtr)(const HDPA,INT,PVOID);
55 static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTREAM,IStream*,LPVOID);
56 static BOOL    (WINAPI *pDPA_Merge)(const HDPA,const HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM);
57 static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTREAM,IStream*,LPVOID);
58 static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
59 static BOOL    (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
60 static BOOL    (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);
61
62 #define COMCTL32_GET_PROC(func, ord) \
63   ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
64    : (trace( #func " not exported\n"), 0)) 
65
66 static BOOL InitFunctionPtrs(HMODULE hcomctl32)
67 {
68     /* 4.00+ */
69     if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
70        COMCTL32_GET_PROC(DPA_Create, 328) &&
71        COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
72        COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
73        COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
74        COMCTL32_GET_PROC(DPA_Destroy, 329) &&
75        COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
76        COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
77        COMCTL32_GET_PROC(DPA_Grow, 330) &&
78        COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
79        COMCTL32_GET_PROC(DPA_Search, 339) &&
80        COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
81        COMCTL32_GET_PROC(DPA_Sort, 338))
82     {
83         /* 4.71+ */
84         COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
85         COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
86         COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
87         COMCTL32_GET_PROC(DPA_Merge, 11) &&
88         COMCTL32_GET_PROC(DPA_SaveStream, 10);
89
90         return TRUE;
91     }
92
93     return FALSE;
94 }
95
96 /* Callbacks */
97 static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
98 {
99     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
100     return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
101 }
102
103 static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
104 {
105     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
106     return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
107 }
108
109 /* merge callback messages counter
110    DPAMM_MERGE     1
111    DPAMM_DELETE    2
112    DPAMM_INSERT    3  */
113 static INT nMessages[4];
114
115 static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
116 {
117     nMessages[op]++;
118     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
119     return p1;
120 }        
121
122 static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
123 {
124     nMessages[op]++;
125     ok(lp == 0x1abe11ed, "lp=%ld\n", lp);
126     return ((PCHAR)p2)+1;
127 }
128
129 static INT nEnum;
130
131 static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
132 {   
133     INT i;
134
135     i = pDPA_GetPtrIndex(lp, pItem);
136     ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
137     nEnum++;
138     pDPA_SetPtr(lp, i, (PVOID)7);
139     return pItem != (PVOID)3;
140 }
141
142 static HRESULT CALLBACK CB_Save(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
143 {
144     HRESULT hRes;
145
146     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
147     hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL);
148     expect(S_OK, hRes);
149     hRes = IStream_Write(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
150     expect(S_OK, hRes);
151     return S_OK;
152 }
153
154 static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
155 {
156     HRESULT hRes;
157     INT iOldPos;
158     
159     iOldPos = pInfo->iPos;
160     ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp);
161     hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL);
162     expect(S_OK, hRes);
163     ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos);
164     hRes = IStream_Read(pStm, &pInfo->pvItem, sizeof(PVOID), NULL);
165     expect(S_OK, hRes);
166     return S_OK;
167 }
168
169 static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
170 {
171     DWORD dwOut = 0;
172     INT i;
173
174     for(i = 0; i < 8;)
175     {
176         ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
177         if(!ulItem) break;
178         dwOut = dwOut << 4 | (ulItem & 0xf);
179     }
180     
181     *pdwOut = dwOut;
182
183     if(dwOut != dwIn)
184     {
185         pDPA_DeleteAllPtrs(dpa);
186         
187         do
188         {
189             pDPA_InsertPtr(dpa, 0, (PVOID)(ULONG_PTR)(dwIn & 0xf));
190             dwIn >>= 4;
191         }
192         while(dwIn);
193         
194         return FALSE;
195     }
196     
197     return TRUE;
198 }
199
200 static void test_dpa(void)
201 {
202     SYSTEM_INFO si;
203     HANDLE hHeap;
204     HDPA dpa, dpa2, dpa3;
205     INT ret, i;
206     PVOID p;
207     DWORD dw, dw2, dw3;
208     BOOL rc;
209     
210     GetSystemInfo(&si);
211     hHeap = HeapCreate(0, 1, 2);
212     ok(hHeap != NULL, "error=%d\n", GetLastError());
213     dpa3 = pDPA_CreateEx(0, hHeap);
214     ok(dpa3 != NULL, "\n");
215     ret = pDPA_Grow(dpa3, si.dwPageSize + 1);
216     ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY,
217        "ret=%d error=%d\n", ret, GetLastError());
218
219     dpa = pDPA_Create(0);
220     ok(dpa != NULL, "\n");
221
222     /* Set item with out of bound index */
223     ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
224     /* Fill the created gap */
225     ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
226     rc=CheckDPA(dpa, 0x56, &dw);
227     ok(rc, "dw=0x%x\n", dw);
228     
229     /* Prepend item */
230     ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
231     ok(ret == 1, "ret=%d\n", ret);
232     /* Append item using correct index */
233     ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
234     ok(ret == 3, "ret=%d\n", ret);
235     /* Append item using out of bound index */
236     ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
237     ok(ret == 4, "ret=%d\n", ret);
238     /* Append item using DPA_APPEND */ 
239     ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
240     ok(ret == 5, "ret=%d\n", ret);
241
242     rc=CheckDPA(dpa, 0x516324, &dw);
243     ok(rc, "dw=0x%x\n", dw);
244
245     for(i = 1; i <= 6; i++)
246     {
247         INT j, k;
248         k = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
249         /* Linear searches should work on unsorted DPAs */
250         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, 0);
251         ok(j == k, "j=%d k=%d\n", j, k);
252     }
253
254     /* Sort DPA */
255     ok(pDPA_Sort(dpa, CB_CmpGT, 0x1abe11ed), "\n");
256     rc=CheckDPA(dpa, 0x654321, &dw);
257     ok(rc, "dw=0x%x\n", dw);
258     
259     /* Clone into a new DPA */
260     dpa2 = pDPA_Clone(dpa, NULL);
261     ok(dpa2 != NULL, "\n");
262     /* The old data should have been preserved */
263     rc=CheckDPA(dpa2, 0x654321, &dw2);
264     ok(rc, "dw=0x%x\n", dw2);
265     ok(pDPA_Sort(dpa, CB_CmpLT, 0x1abe11ed), "\n");
266     
267     /* Test if the DPA itself was really copied */
268     rc=CheckDPA(dpa,  0x123456, &dw);
269     ok(rc, "dw=0x%x\n",  dw );
270     rc=CheckDPA(dpa2, 0x654321, &dw2);
271     ok(rc, "dw2=0x%x\n", dw2);
272
273     /* Clone into an old DPA */
274     p = NULL; SetLastError(ERROR_SUCCESS);
275     p = pDPA_Clone(dpa, dpa3);
276     ok(p == dpa3, "p=%p\n", p);
277     rc=CheckDPA(dpa3, 0x123456, &dw3);
278     ok(rc, "dw3=0x%x\n", dw3);
279
280     for(i = 1; i <= 6; i++)
281     {
282         INT j;
283
284         /* The array is in order so ptr == index+1 */
285         j = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i);
286         ok(j+1 == i, "j=%d i=%d\n", j, i);
287         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
288         ok(j+1 == i, "j=%d i=%d\n", j, i);
289
290         /* Linear searches respect iStart ... */
291         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, 0);
292         ok(j == DPA_ERR, "j=%d\n", j);
293         /* ... but for a binary search it's ignored */
294         j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
295         ok(j+1 == i, "j=%d i=%d\n", j, i);
296     }
297
298     /* Try to get the index of a nonexistent item */
299     i = pDPA_GetPtrIndex(dpa, (PVOID)7);
300     ok(i == DPA_ERR, "i=%d\n", i);
301     
302     /* Try to delete out of bound indexes */
303     p = pDPA_DeletePtr(dpa, -1);
304     ok(p == NULL, "p=%p\n", p);
305     p = pDPA_DeletePtr(dpa, 6);
306     ok(p == NULL, "p=%p\n", p);
307
308     /* Delete the third item */
309     p = pDPA_DeletePtr(dpa, 2);
310     ok(p == (PVOID)3, "p=%p\n", p);
311     rc=CheckDPA(dpa, 0x12456, &dw);
312     ok(rc, "dw=0x%x\n", dw);
313
314     /* Check where to re-insert the deleted item */
315     i = pDPA_Search(dpa, (PVOID)3, 0, 
316                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED|DPAS_INSERTAFTER);
317     ok(i == 2, "i=%d\n", i);
318     /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
319     i = pDPA_Search(dpa, (PVOID)3, 0,
320                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED|DPAS_INSERTBEFORE);
321     ok(i == 2, "i=%d\n", i);
322     /* without DPAS_INSERTBEFORE/AFTER */
323     i = pDPA_Search(dpa, (PVOID)3, 0,
324                     CB_CmpLT, 0x1abe11ed, DPAS_SORTED);
325     ok(i == -1, "i=%d\n", i);
326
327     /* Re-insert the item */
328     ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
329     ok(ret == 2, "ret=%d i=%d\n", ret, 2);
330     rc=CheckDPA(dpa, 0x123456, &dw);
331     ok(rc, "dw=0x%x\n", dw);
332     
333     /* When doing a binary search while claiming reverse order all indexes
334      * should be bogus */
335     for(i = 0; i < 6; i++)
336     {
337         INT j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpGT, 0x1abe11ed,
338                             DPAS_SORTED|DPAS_INSERTBEFORE);
339         ok(j != i, "i=%d\n", i);
340     }
341
342     /* Setting item with huge index should work */
343     ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n");
344     ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef);
345     ok(ret == 0x12345, "ret=%d\n", ret);
346           
347     pDPA_DeleteAllPtrs(dpa2);
348     rc=CheckDPA(dpa2, 0, &dw2);
349     ok(rc, "dw2=0x%x\n", dw2);
350
351     pDPA_Destroy(dpa);
352     pDPA_Destroy(dpa2);
353     pDPA_Destroy(dpa3);
354 }
355
356 static void test_DPA_Merge(void)
357 {
358     HDPA dpa, dpa2, dpa3;
359     INT ret, i;
360     DWORD dw;
361     BOOL rc;
362
363     if(!pDPA_Merge)
364     {
365         win_skip("DPA_Merge() not available\n");
366         return;
367     }
368
369     dpa  = pDPA_Create(0);
370     dpa2 = pDPA_Create(0);
371     dpa3 = pDPA_Create(0);
372
373     ret = pDPA_InsertPtr(dpa, 0, (PVOID)1);
374     ok(ret == 0, "ret=%d\n", ret);
375     ret = pDPA_InsertPtr(dpa, 1, (PVOID)3);
376     ok(ret == 1, "ret=%d\n", ret);
377     ret = pDPA_InsertPtr(dpa, 2, (PVOID)5);
378     ok(ret == 2, "ret=%d\n", ret);
379
380     rc = CheckDPA(dpa, 0x135, &dw);
381     ok(rc, "dw=0x%x\n", dw);
382
383     for (i = 0; i < 6; i++)
384     {
385         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
386         ok(ret == i, "ret=%d\n", ret);
387         ret = pDPA_InsertPtr(dpa3, i, (PVOID)(INT_PTR)(i+1));
388         ok(ret == i, "ret=%d\n", ret);
389     }
390
391     rc = CheckDPA(dpa2, 0x654321, &dw);
392     ok(rc, "dw=0x%x\n", dw);
393     rc = CheckDPA(dpa3, 0x123456, &dw);
394     ok(rc, "dw=0x%x\n", dw);
395
396     /* Delete all odd entries from dpa2 */
397     memset(nMessages, 0, sizeof(nMessages));
398     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
399                CB_CmpLT, CB_MergeDeleteOddSrc, 0x1abe11ed);
400     rc = CheckDPA(dpa2, 0x246, &dw);
401     ok(rc, "dw=0x%x\n", dw);
402
403     expect(3, nMessages[DPAMM_MERGE]);
404     expect(3, nMessages[DPAMM_DELETE]);
405     expect(0, nMessages[DPAMM_INSERT]);
406
407     for (i = 0; i < 6; i++)
408     {
409         ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
410         ok(ret == i, "ret=%d\n", ret);
411     }
412
413     /* DPAM_INTERSECT - returning source while merging */
414     memset(nMessages, 0, sizeof(nMessages));
415     pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
416                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
417     rc = CheckDPA(dpa2, 0x135, &dw);
418     ok(rc, "dw=0x%x\n", dw);
419
420     expect(3, nMessages[DPAMM_MERGE]);
421     expect(6, nMessages[DPAMM_DELETE]);
422     expect(0, nMessages[DPAMM_INSERT]);
423
424     /* DPAM_UNION */
425     pDPA_DeleteAllPtrs(dpa);
426     pDPA_InsertPtr(dpa, 0, (PVOID)1);
427     pDPA_InsertPtr(dpa, 1, (PVOID)3);
428     pDPA_InsertPtr(dpa, 2, (PVOID)5);
429     pDPA_DeleteAllPtrs(dpa2);
430     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
431     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
432     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
433
434     memset(nMessages, 0, sizeof(nMessages));
435     pDPA_Merge(dpa2, dpa, DPAM_UNION,
436                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
437     rc = CheckDPA(dpa2, 0x123456, &dw);
438     ok(rc ||
439        broken(!rc && dw == 0x23456), /* 4.7x */
440        "dw=0x%x\n", dw);
441
442     expect(0, nMessages[DPAMM_MERGE]);
443     expect(0, nMessages[DPAMM_DELETE]);
444     ok(nMessages[DPAMM_INSERT] == 3 ||
445        broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
446        "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
447
448     /* Merge dpa3 into dpa2 and dpa */
449     memset(nMessages, 0, sizeof(nMessages));
450     pDPA_Merge(dpa, dpa3, DPAM_UNION|DPAM_SORTED,
451                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
452     expect(3, nMessages[DPAMM_MERGE]);
453     expect(0, nMessages[DPAMM_DELETE]);
454     expect(3, nMessages[DPAMM_INSERT]);
455
456
457     pDPA_DeleteAllPtrs(dpa2);
458     pDPA_InsertPtr(dpa2, 0, (PVOID)2);
459     pDPA_InsertPtr(dpa2, 1, (PVOID)4);
460     pDPA_InsertPtr(dpa2, 2, (PVOID)6);
461
462     memset(nMessages, 0, sizeof(nMessages));
463     pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED,
464                CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed);
465     expect(3, nMessages[DPAMM_MERGE]);
466     expect(0, nMessages[DPAMM_DELETE]);
467     ok(nMessages[DPAMM_INSERT] == 3 ||
468        broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
469        "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
470
471     rc = CheckDPA(dpa,  0x123456, &dw);
472     ok(rc, "dw=0x%x\n",  dw);
473     rc = CheckDPA(dpa2, 0x123456, &dw);
474     ok(rc ||
475        broken(!rc), /* win98 */
476        "dw=0x%x\n", dw);
477     rc = CheckDPA(dpa3, 0x123456, &dw);
478     ok(rc, "dw=0x%x\n", dw);
479
480     pDPA_Destroy(dpa);
481     pDPA_Destroy(dpa2);
482     pDPA_Destroy(dpa3);
483 }
484
485 static void test_DPA_EnumCallback(void)
486 {
487     HDPA dpa;
488     BOOL rc;
489     DWORD dw;
490     INT i, ret;
491
492     if(!pDPA_EnumCallback)
493     {
494         win_skip("DPA_EnumCallback() not available\n");
495         return;
496     }
497
498     dpa = pDPA_Create(0);
499
500     for (i = 0; i < 6; i++)
501     {
502         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
503         ok(ret == i, "ret=%d\n", ret);
504     }
505
506     rc = CheckDPA(dpa, 0x123456, &dw);
507     ok(rc, "dw=0x%x\n", dw);
508
509     nEnum = 0;
510     /* test callback sets first 3 items to 7 */
511     pDPA_EnumCallback(dpa, CB_EnumFirstThree, dpa);
512     rc = CheckDPA(dpa, 0x777456, &dw);
513     ok(rc, "dw=0x%x\n", dw);
514     ok(nEnum == 3, "nEnum=%d\n", nEnum);
515
516     pDPA_Destroy(dpa);
517 }
518
519 static void test_DPA_DestroyCallback(void)
520 {
521     HDPA dpa;
522     INT i, ret;
523
524     if(!pDPA_DestroyCallback)
525     {
526         win_skip("DPA_DestroyCallback() not available\n");
527         return;
528     }
529
530     dpa = pDPA_Create(0);
531
532     for (i = 0; i < 3; i++)
533     {
534         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
535         ok(ret == i, "ret=%d\n", ret);
536     }
537
538     nEnum = 0;
539     pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa);
540     ok(nEnum == 3, "nEnum=%d\n", nEnum);
541 }
542
543 static void test_DPA_LoadStream(void)
544 {
545     static const WCHAR szStg[] = { 'S','t','g',0 };
546     IStorage* pStg = NULL;
547     IStream* pStm = NULL;
548     LARGE_INTEGER li;
549     ULARGE_INTEGER uli;
550     DWORD dwMode;
551     HRESULT hRes;
552     STREAMDATA header;
553     ULONG written, ret;
554     HDPA dpa;
555
556     if(!pDPA_LoadStream)
557     {
558         win_skip("DPA_LoadStream() not available. Skipping stream tests.\n");
559         return;
560     }
561
562     hRes = CoInitialize(NULL);
563     if (hRes != S_OK)
564     {
565         ok(0, "hResult: %d\n", hRes);
566         return;
567     }
568
569     dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
570     hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
571     expect(S_OK, hRes);
572
573     hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
574     expect(S_OK, hRes);
575
576     /* write less than header size */
577     li.QuadPart = 0;
578     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
579     expect(S_OK, hRes);
580
581     memset(&header, 0, sizeof(header));
582     written = 0;
583     uli.QuadPart = sizeof(header)-1;
584     hRes = IStream_SetSize(pStm, uli);
585     expect(S_OK, hRes);
586     hRes = IStream_Write(pStm, &header, sizeof(header)-1, &written);
587     expect(S_OK, hRes);
588     written -= sizeof(header)-1;
589     expect(0, written);
590
591     li.QuadPart = 0;
592     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
593     expect(S_OK, hRes);
594
595     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
596     expect(E_FAIL, hRes);
597
598     /* check stream position after header read failed */
599     li.QuadPart = 0;
600     uli.QuadPart = 1;
601     hRes = IStream_Seek(pStm, li, STREAM_SEEK_CUR, &uli);
602     expect(S_OK, hRes);
603     ok(uli.QuadPart == 0, "Expected to position reset\n");
604
605     /* write valid header for empty DPA */
606     header.dwSize = sizeof(header);
607     header.dwData2 = 1;
608     header.dwItems = 0;
609     written = 0;
610
611     li.QuadPart = 0;
612     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
613     expect(S_OK, hRes);
614
615     uli.QuadPart = sizeof(header);
616     hRes = IStream_SetSize(pStm, uli);
617     expect(S_OK, hRes);
618
619     hRes = IStream_Write(pStm, &header, sizeof(header), &written);
620     expect(S_OK, hRes);
621     written -= sizeof(header);
622     expect(0, written);
623
624     li.QuadPart = 0;
625     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
626     expect(S_OK, hRes);
627
628     dpa = NULL;
629     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
630     expect(S_OK, hRes);
631     DPA_Destroy(dpa);
632
633     /* try with altered dwData2 field */
634     header.dwSize = sizeof(header);
635     header.dwData2 = 2;
636     header.dwItems = 0;
637
638     li.QuadPart = 0;
639     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
640     expect(S_OK, hRes);
641     hRes = IStream_Write(pStm, &header, sizeof(header), &written);
642     expect(S_OK, hRes);
643     written -= sizeof(header);
644     expect(0, written);
645
646     li.QuadPart = 0;
647     hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
648     expect(S_OK, hRes);
649
650     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
651     expect(E_FAIL, hRes);
652
653     ret = IStream_Release(pStm);
654     ok(!ret, "ret=%d\n", ret);
655
656     ret = IStorage_Release(pStg);
657     ok(!ret, "ret=%d\n", ret);
658
659     CoUninitialize();
660 }
661
662 static void test_DPA_SaveStream(void)
663 {
664     HDPA dpa;
665     static const WCHAR szStg[] = { 'S','t','g',0 };
666     IStorage* pStg = NULL;
667     IStream* pStm = NULL;
668     DWORD dwMode, dw;
669     HRESULT hRes;
670     INT ret;
671     INT i;
672     BOOL rc;
673     LARGE_INTEGER liZero;
674
675     if(!pDPA_SaveStream)
676     {
677         win_skip("DPA_SaveStream() not available. Skipping stream tests.\n");
678         return;
679     }
680
681     hRes = CoInitialize(NULL);
682     if (hRes != S_OK)
683     {
684         ok(0, "hResult: %d\n", hRes);
685         return;
686     }
687
688     dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
689     hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
690     expect(S_OK, hRes);
691
692     hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
693     expect(S_OK, hRes);
694
695     dpa = pDPA_Create(0);
696
697     /* simple parameter check */
698     hRes = pDPA_SaveStream(dpa, NULL, pStm, NULL);
699     ok(hRes == E_INVALIDARG ||
700        broken(hRes == S_OK) /* XP and below */, "Wrong result, %d\n", hRes);
701 if (0) {
702     /* crashes on XP */
703     hRes = pDPA_SaveStream(NULL, CB_Save, pStm, NULL);
704     expect(E_INVALIDARG, hRes);
705
706     hRes = pDPA_SaveStream(dpa, CB_Save, NULL, NULL);
707     expect(E_INVALIDARG, hRes);
708 }
709
710     /* saving/loading */
711     for (i = 0; i < 6; i++)
712     {
713         ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
714         ok(ret == i, "ret=%d\n", ret);
715     }
716
717     liZero.QuadPart = 0;
718     hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
719     expect(S_OK, hRes);
720
721     hRes = pDPA_SaveStream(dpa, CB_Save, pStm, (void*)0xdeadbeef);
722     expect(S_OK, hRes);
723     pDPA_Destroy(dpa);
724
725     liZero.QuadPart = 0;
726     hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL);
727     expect(S_OK, hRes);
728     hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
729     expect(S_OK, hRes);
730     rc = CheckDPA(dpa, 0x123456, &dw);
731     ok(rc, "dw=0x%x\n", dw);
732     pDPA_Destroy(dpa);
733
734     ret = IStream_Release(pStm);
735     ok(!ret, "ret=%d\n", ret);
736
737     ret = IStorage_Release(pStg);
738     ok(!ret, "ret=%d\n", ret);
739
740     CoUninitialize();
741 }
742
743 START_TEST(dpa)
744 {
745     HMODULE hcomctl32;
746
747     hcomctl32 = GetModuleHandleA("comctl32.dll");
748
749     if(!InitFunctionPtrs(hcomctl32))
750     {
751         win_skip("Needed functions are not available\n");
752         return;
753     }
754
755     test_dpa();
756     test_DPA_Merge();
757     test_DPA_EnumCallback();
758     test_DPA_DestroyCallback();
759     test_DPA_LoadStream();
760     test_DPA_SaveStream();
761 }