dpa.c 21.1 KB
Newer Older
1 2 3 4
/*
 * Unit tests for DPA functions
 *
 * Copyright 2003 Uwe Bonnes
5
 * Copyright 2005 Felix Nawothnig
6 7 8 9 10 11 12 13 14 15 16 17 18
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 23
#define COBJMACROS

24 25 26
#include <stdarg.h>

#include "windef.h"
27
#include "winbase.h"
28
#include "winuser.h"
29
#include "commctrl.h"
30
#include "objidl.h"
31 32 33

#include "wine/test.h"

34 35
#define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got)

36 37 38 39 40 41 42
typedef struct _STREAMDATA
{
    DWORD dwSize;
    DWORD dwData2;
    DWORD dwItems;
} STREAMDATA, *PSTREAMDATA;

43 44 45 46 47 48 49 50 51 52 53 54
static HDPA    (WINAPI *pDPA_Clone)(const HDPA,const HDPA);
static HDPA    (WINAPI *pDPA_Create)(INT);
static HDPA    (WINAPI *pDPA_CreateEx)(INT,HANDLE);
static PVOID   (WINAPI *pDPA_DeleteAllPtrs)(const HDPA);
static PVOID   (WINAPI *pDPA_DeletePtr)(const HDPA,INT);
static BOOL    (WINAPI *pDPA_Destroy)(const HDPA);
static VOID    (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID);
static VOID    (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); 
static INT     (WINAPI *pDPA_GetPtr)(const HDPA,INT);
static INT     (WINAPI *pDPA_GetPtrIndex)(const HDPA,PVOID);
static BOOL    (WINAPI *pDPA_Grow)(HDPA,INT);
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 59 60 61 62 63 64 65 66
static INT     (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT);
static BOOL    (WINAPI *pDPA_SetPtr)(const HDPA,INT,PVOID);
static BOOL    (WINAPI *pDPA_Sort)(const HDPA,PFNDPACOMPARE,LPARAM);

#define COMCTL32_GET_PROC(func, ord) \
  ((p ## func = (PVOID)GetProcAddress(hcomctl32,(LPCSTR)ord)) ? 1 \
   : (trace( #func " not exported\n"), 0)) 

static BOOL InitFunctionPtrs(HMODULE hcomctl32)
67
{
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
    /* 4.00+ */
    if(COMCTL32_GET_PROC(DPA_Clone, 331) &&
       COMCTL32_GET_PROC(DPA_Create, 328) &&
       COMCTL32_GET_PROC(DPA_CreateEx, 340) &&
       COMCTL32_GET_PROC(DPA_DeleteAllPtrs, 337) &&
       COMCTL32_GET_PROC(DPA_DeletePtr, 336) &&
       COMCTL32_GET_PROC(DPA_Destroy, 329) &&
       COMCTL32_GET_PROC(DPA_GetPtr, 332) &&
       COMCTL32_GET_PROC(DPA_GetPtrIndex, 333) &&
       COMCTL32_GET_PROC(DPA_Grow, 330) &&
       COMCTL32_GET_PROC(DPA_InsertPtr, 334) &&
       COMCTL32_GET_PROC(DPA_Search, 339) &&
       COMCTL32_GET_PROC(DPA_SetPtr, 335) &&
       COMCTL32_GET_PROC(DPA_Sort, 338))
    {
        /* 4.71+ */
        COMCTL32_GET_PROC(DPA_DestroyCallback, 386) &&
        COMCTL32_GET_PROC(DPA_EnumCallback, 385) &&
86 87
        COMCTL32_GET_PROC(DPA_LoadStream, 9) &&
        COMCTL32_GET_PROC(DPA_Merge, 11) &&
88
        COMCTL32_GET_PROC(DPA_SaveStream, 10);
89

90 91 92 93
        return TRUE;
    }

    return FALSE;
94 95
}

96 97 98 99 100 101 102 103 104 105 106 107 108
/* Callbacks */
static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp)
{
    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
    return p1 < p2 ? -1 : p1 > p2 ? 1 : 0;
}

static INT CALLBACK CB_CmpGT(PVOID p1, PVOID p2, LPARAM lp)
{
    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
    return p1 > p2 ? -1 : p1 < p2 ? 1 : 0;
}

109 110 111 112 113 114
/* merge callback messages counter
   DPAMM_MERGE     1
   DPAMM_DELETE    2
   DPAMM_INSERT    3  */
static INT nMessages[4];

115 116
static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
{
117
    nMessages[op]++;
118 119 120 121 122
    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
    return p1;
}        

static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp)
123
{
124
    nMessages[op]++;
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    ok(lp == 0xdeadbeef, "lp=%ld\n", lp);
    return ((PCHAR)p2)+1;
}

static INT nEnum;

static INT CALLBACK CB_EnumFirstThree(PVOID pItem, PVOID lp)
{   
    INT i;

    i = pDPA_GetPtrIndex(lp, pItem);
    ok(i == nEnum, "i=%d nEnum=%d\n", i, nEnum);
    nEnum++;
    pDPA_SetPtr(lp, i, (PVOID)7);
    return pItem != (PVOID)3;
}

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 152 153
    return S_OK;
}

154
static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp)
155 156 157 158 159
{
    HRESULT hRes;
    INT iOldPos;
    
    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 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
    return S_OK;
}

static BOOL CheckDPA(HDPA dpa, DWORD dwIn, PDWORD pdwOut)
{
    DWORD dwOut = 0;
    INT i;

    for(i = 0; i < 8;)
    {
        ULONG_PTR ulItem = (ULONG_PTR)pDPA_GetPtr(dpa, i++);
        if(!ulItem) break;
        dwOut = dwOut << 4 | (ulItem & 0xf);
    }
    
    *pdwOut = dwOut;

    if(dwOut != dwIn)
    {
        pDPA_DeleteAllPtrs(dpa);
        
        do
        {
189
            pDPA_InsertPtr(dpa, 0, (PVOID)(ULONG_PTR)(dwIn & 0xf));
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
            dwIn >>= 4;
        }
        while(dwIn);
        
        return FALSE;
    }
    
    return TRUE;
}

static void test_dpa(void)
{
    SYSTEM_INFO si;
    HANDLE hHeap;
    HDPA dpa, dpa2, dpa3;
    INT ret, i;
    PVOID p;
    DWORD dw, dw2, dw3;
208
    BOOL rc;
209 210 211
    
    GetSystemInfo(&si);
    hHeap = HeapCreate(0, 1, 2);
212
    ok(hHeap != NULL, "error=%d\n", GetLastError());
213 214 215
    dpa3 = pDPA_CreateEx(0, hHeap);
    ok(dpa3 != NULL, "\n");
    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 220 221 222 223
    dpa = pDPA_Create(0);
    ok(dpa != NULL, "\n");

    /* Set item with out of bound index */
    ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n");
224
    /* Fill the created gap */
225
    ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n");
226 227
    rc=CheckDPA(dpa, 0x56, &dw);
    ok(rc, "dw=0x%x\n", dw);
228 229 230 231 232 233 234 235 236
    
    /* Prepend item */
    ret = pDPA_InsertPtr(dpa, 1, (PVOID)1);
    ok(ret == 1, "ret=%d\n", ret);
    /* Append item using correct index */
    ret = pDPA_InsertPtr(dpa, 3, (PVOID)3);
    ok(ret == 3, "ret=%d\n", ret);
    /* Append item using out of bound index */
    ret = pDPA_InsertPtr(dpa, 5, (PVOID)2);
237
    ok(ret == 4, "ret=%d\n", ret);
238 239
    /* Append item using DPA_APPEND */ 
    ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4);
240
    ok(ret == 5, "ret=%d\n", ret);
241

242 243
    rc=CheckDPA(dpa, 0x516324, &dw);
    ok(rc, "dw=0x%x\n", dw);
244 245 246 247

    for(i = 1; i <= 6; i++)
    {
        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, 0xdeadbeef, 0);
251 252 253 254 255
        ok(j == k, "j=%d k=%d\n", j, k);
    }

    /* Sort DPA */
    ok(pDPA_Sort(dpa, CB_CmpGT, 0xdeadbeef), "\n");
256 257
    rc=CheckDPA(dpa, 0x654321, &dw);
    ok(rc, "dw=0x%x\n", dw);
258 259 260 261 262
    
    /* Clone into a new DPA */
    dpa2 = pDPA_Clone(dpa, NULL);
    ok(dpa2 != NULL, "\n");
    /* The old data should have been preserved */
263 264
    rc=CheckDPA(dpa2, 0x654321, &dw2);
    ok(rc, "dw=0x%x\n", dw2);
265 266 267
    ok(pDPA_Sort(dpa, CB_CmpLT, 0xdeadbeef), "\n");
    
    /* Test if the DPA itself was really copied */
268 269 270 271
    rc=CheckDPA(dpa,  0x123456, &dw);
    ok(rc, "dw=0x%x\n",  dw );
    rc=CheckDPA(dpa2, 0x654321, &dw2);
    ok(rc, "dw2=0x%x\n", dw2);
272 273 274 275 276

    /* Clone into an old DPA */
    p = NULL; SetLastError(ERROR_SUCCESS);
    p = pDPA_Clone(dpa, dpa3);
    ok(p == dpa3, "p=%p\n", p);
277 278
    rc=CheckDPA(dpa3, 0x123456, &dw3);
    ok(rc, "dw3=0x%x\n", dw3);
279 280 281 282 283 284

    for(i = 1; i <= 6; i++)
    {
        INT j;

        /* 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, 0xdeadbeef, 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, 0xdeadbeef, 0);
292 293
        ok(j == DPA_ERR, "j=%d\n", j);
        /* ... but for a binary search it's ignored */
294
        j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0xdeadbeef, 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 300 301 302 303 304 305 306 307 308 309 310
    i = pDPA_GetPtrIndex(dpa, (PVOID)7);
    ok(i == DPA_ERR, "i=%d\n", i);
    
    /* Try to delete out of bound indexes */
    p = pDPA_DeletePtr(dpa, -1);
    ok(p == NULL, "p=%p\n", p);
    p = pDPA_DeletePtr(dpa, 6);
    ok(p == NULL, "p=%p\n", p);

    /* Delete the third item */
    p = pDPA_DeletePtr(dpa, 2);
    ok(p == (PVOID)3, "p=%p\n", p);
311 312
    rc=CheckDPA(dpa, 0x12456, &dw);
    ok(rc, "dw=0x%x\n", dw);
313 314 315 316 317 318 319 320

    /* Check where to re-insert the deleted item */
    i = pDPA_Search(dpa, (PVOID)3, 0, 
                    CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTAFTER);
    ok(i == 2, "i=%d\n", i);
    /* DPAS_INSERTBEFORE works just like DPAS_INSERTAFTER */
    i = pDPA_Search(dpa, (PVOID)3, 0,
                    CB_CmpLT, 0xdeadbeef, DPAS_SORTED|DPAS_INSERTBEFORE);
321
    ok(i == 2, "i=%d\n", i);
322 323 324 325
    /* without DPAS_INSERTBEFORE/AFTER */
    i = pDPA_Search(dpa, (PVOID)3, 0,
                    CB_CmpLT, 0xdeadbeef, DPAS_SORTED);
    ok(i == -1, "i=%d\n", i);
326 327 328 329

    /* Re-insert the item */
    ret = pDPA_InsertPtr(dpa, 2, (PVOID)3);
    ok(ret == 2, "ret=%d i=%d\n", ret, 2);
330 331
    rc=CheckDPA(dpa, 0x123456, &dw);
    ok(rc, "dw=0x%x\n", dw);
332 333 334 335 336
    
    /* When doing a binary search while claiming reverse order all indexes
     * should be bogus */
    for(i = 0; i < 6; i++)
    {
337
        INT j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpGT, 0xdeadbeef,
338 339 340 341
                            DPAS_SORTED|DPAS_INSERTBEFORE);
        ok(j != i, "i=%d\n", i);
    }

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 349
    rc=CheckDPA(dpa2, 0, &dw2);
    ok(rc, "dw2=0x%x\n", dw2);
350 351

    pDPA_Destroy(dpa);
352
    pDPA_Destroy(dpa2);
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    pDPA_Destroy(dpa3);
}

static void test_DPA_Merge(void)
{
    HDPA dpa, dpa2, dpa3;
    INT ret, i;
    DWORD dw;
    BOOL rc;

    if(!pDPA_Merge)
    {
        win_skip("DPA_Merge() not available\n");
        return;
    }

    dpa  = pDPA_Create(0);
    dpa2 = pDPA_Create(0);
    dpa3 = pDPA_Create(0);

    ret = pDPA_InsertPtr(dpa, 0, (PVOID)1);
    ok(ret == 0, "ret=%d\n", ret);
    ret = pDPA_InsertPtr(dpa, 1, (PVOID)3);
    ok(ret == 1, "ret=%d\n", ret);
    ret = pDPA_InsertPtr(dpa, 2, (PVOID)5);
    ok(ret == 2, "ret=%d\n", ret);

    rc = CheckDPA(dpa, 0x135, &dw);
    ok(rc, "dw=0x%x\n", dw);

    for (i = 0; i < 6; i++)
    {
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 389 390 391 392 393 394 395 396
        ok(ret == i, "ret=%d\n", ret);
    }

    rc = CheckDPA(dpa2, 0x654321, &dw);
    ok(rc, "dw=0x%x\n", dw);
    rc = CheckDPA(dpa3, 0x123456, &dw);
    ok(rc, "dw=0x%x\n", dw);

    /* Delete all odd entries from dpa2 */
397
    memset(nMessages, 0, sizeof(nMessages));
398
    pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
399
               CB_CmpLT, CB_MergeDeleteOddSrc, 0xdeadbeef);
400 401 402
    rc = CheckDPA(dpa2, 0x246, &dw);
    ok(rc, "dw=0x%x\n", dw);

403 404 405 406 407 408
    expect(3, nMessages[DPAMM_MERGE]);
    expect(3, nMessages[DPAMM_DELETE]);
    expect(0, nMessages[DPAMM_INSERT]);

    for (i = 0; i < 6; i++)
    {
409
        ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i));
410 411 412 413 414 415 416
        ok(ret == i, "ret=%d\n", ret);
    }

    /* DPAM_INTERSECT - returning source while merging */
    memset(nMessages, 0, sizeof(nMessages));
    pDPA_Merge(dpa2, dpa, DPAM_INTERSECT,
               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
417 418 419
    rc = CheckDPA(dpa2, 0x135, &dw);
    ok(rc, "dw=0x%x\n", dw);

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437
    expect(3, nMessages[DPAMM_MERGE]);
    expect(6, nMessages[DPAMM_DELETE]);
    expect(0, nMessages[DPAMM_INSERT]);

    /* DPAM_UNION */
    pDPA_DeleteAllPtrs(dpa);
    pDPA_InsertPtr(dpa, 0, (PVOID)1);
    pDPA_InsertPtr(dpa, 1, (PVOID)3);
    pDPA_InsertPtr(dpa, 2, (PVOID)5);
    pDPA_DeleteAllPtrs(dpa2);
    pDPA_InsertPtr(dpa2, 0, (PVOID)2);
    pDPA_InsertPtr(dpa2, 1, (PVOID)4);
    pDPA_InsertPtr(dpa2, 2, (PVOID)6);

    memset(nMessages, 0, sizeof(nMessages));
    pDPA_Merge(dpa2, dpa, DPAM_UNION,
               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
    rc = CheckDPA(dpa2, 0x123456, &dw);
438 439 440
    ok(rc ||
       broken(!rc && dw == 0x23456), /* 4.7x */
       "dw=0x%x\n", dw);
441 442 443

    expect(0, nMessages[DPAMM_MERGE]);
    expect(0, nMessages[DPAMM_DELETE]);
444 445 446
    ok(nMessages[DPAMM_INSERT] == 3 ||
       broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
       "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, 0xdeadbeef);
452 453 454 455 456 457 458 459 460 461 462
    expect(3, nMessages[DPAMM_MERGE]);
    expect(0, nMessages[DPAMM_DELETE]);
    expect(3, nMessages[DPAMM_INSERT]);


    pDPA_DeleteAllPtrs(dpa2);
    pDPA_InsertPtr(dpa2, 0, (PVOID)2);
    pDPA_InsertPtr(dpa2, 1, (PVOID)4);
    pDPA_InsertPtr(dpa2, 2, (PVOID)6);

    memset(nMessages, 0, sizeof(nMessages));
463
    pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED,
464
               CB_CmpLT, CB_MergeInsertSrc, 0xdeadbeef);
465 466
    expect(3, nMessages[DPAMM_MERGE]);
    expect(0, nMessages[DPAMM_DELETE]);
467 468 469
    ok(nMessages[DPAMM_INSERT] == 3 ||
       broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */
       "Expected 3, got %d\n", nMessages[DPAMM_INSERT]);
470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501

    rc = CheckDPA(dpa,  0x123456, &dw);
    ok(rc, "dw=0x%x\n",  dw);
    rc = CheckDPA(dpa2, 0x123456, &dw);
    ok(rc ||
       broken(!rc), /* win98 */
       "dw=0x%x\n", dw);
    rc = CheckDPA(dpa3, 0x123456, &dw);
    ok(rc, "dw=0x%x\n", dw);

    pDPA_Destroy(dpa);
    pDPA_Destroy(dpa2);
    pDPA_Destroy(dpa3);
}

static void test_DPA_EnumCallback(void)
{
    HDPA dpa;
    BOOL rc;
    DWORD dw;
    INT i, ret;

    if(!pDPA_EnumCallback)
    {
        win_skip("DPA_EnumCallback() not available\n");
        return;
    }

    dpa = pDPA_Create(0);

    for (i = 0; i < 6; i++)
    {
502
        ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530
        ok(ret == i, "ret=%d\n", ret);
    }

    rc = CheckDPA(dpa, 0x123456, &dw);
    ok(rc, "dw=0x%x\n", dw);

    nEnum = 0;
    /* test callback sets first 3 items to 7 */
    pDPA_EnumCallback(dpa, CB_EnumFirstThree, dpa);
    rc = CheckDPA(dpa, 0x777456, &dw);
    ok(rc, "dw=0x%x\n", dw);
    ok(nEnum == 3, "nEnum=%d\n", nEnum);

    pDPA_Destroy(dpa);
}

static void test_DPA_DestroyCallback(void)
{
    HDPA dpa;
    INT i, ret;

    if(!pDPA_DestroyCallback)
    {
        win_skip("DPA_DestroyCallback() not available\n");
        return;
    }

    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 539 540 541 542

    nEnum = 0;
    pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa);
    ok(nEnum == 3, "nEnum=%d\n", nEnum);
}

543 544 545 546 547 548 549 550 551 552 553 554 555
static void test_DPA_LoadStream(void)
{
    static const WCHAR szStg[] = { 'S','t','g',0 };
    IStorage* pStg = NULL;
    IStream* pStm = NULL;
    LARGE_INTEGER li;
    ULARGE_INTEGER uli;
    DWORD dwMode;
    HRESULT hRes;
    STREAMDATA header;
    ULONG written, ret;
    HDPA dpa;

556 557 558 559 560 561
    if(!pDPA_LoadStream)
    {
        win_skip("DPA_LoadStream() not available. Skipping stream tests.\n");
        return;
    }

562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602
    hRes = CoInitialize(NULL);
    if (hRes != S_OK)
    {
        ok(0, "hResult: %d\n", hRes);
        return;
    }

    dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
    hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
    expect(S_OK, hRes);

    hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
    expect(S_OK, hRes);

    /* write less than header size */
    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);

    memset(&header, 0, sizeof(header));
    written = 0;
    uli.QuadPart = sizeof(header)-1;
    hRes = IStream_SetSize(pStm, uli);
    expect(S_OK, hRes);
    hRes = IStream_Write(pStm, &header, sizeof(header)-1, &written);
    expect(S_OK, hRes);
    written -= sizeof(header)-1;
    expect(0, written);

    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);

    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
    expect(E_FAIL, hRes);

    /* check stream position after header read failed */
    li.QuadPart = 0;
    uli.QuadPart = 1;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_CUR, &uli);
    expect(S_OK, hRes);
603
    ok(uli.QuadPart == 0, "Expected to position reset\n");
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627

    /* write valid header for empty DPA */
    header.dwSize = sizeof(header);
    header.dwData2 = 1;
    header.dwItems = 0;
    written = 0;

    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);

    uli.QuadPart = sizeof(header);
    hRes = IStream_SetSize(pStm, uli);
    expect(S_OK, hRes);

    hRes = IStream_Write(pStm, &header, sizeof(header), &written);
    expect(S_OK, hRes);
    written -= sizeof(header);
    expect(0, written);

    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);

628
    dpa = NULL;
629
    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL);
630
    expect(S_OK, hRes);
631
    DPA_Destroy(dpa);
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650

    /* try with altered dwData2 field */
    header.dwSize = sizeof(header);
    header.dwData2 = 2;
    header.dwItems = 0;

    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);
    hRes = IStream_Write(pStm, &header, sizeof(header), &written);
    expect(S_OK, hRes);
    written -= sizeof(header);
    expect(0, written);

    li.QuadPart = 0;
    hRes = IStream_Seek(pStm, li, STREAM_SEEK_SET, NULL);
    expect(S_OK, hRes);

    hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef);
651
    expect(E_FAIL, hRes);
652 653 654 655 656 657 658 659 660 661

    ret = IStream_Release(pStm);
    ok(!ret, "ret=%d\n", ret);

    ret = IStorage_Release(pStg);
    ok(!ret, "ret=%d\n", ret);

    CoUninitialize();
}

662 663 664 665 666 667
static void test_DPA_SaveStream(void)
{
    HDPA dpa;
    static const WCHAR szStg[] = { 'S','t','g',0 };
    IStorage* pStg = NULL;
    IStream* pStm = NULL;
668
    DWORD dwMode, dw;
669 670
    HRESULT hRes;
    ULONG ret;
671 672 673
    INT i;
    BOOL rc;
    LARGE_INTEGER liZero;
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

    if(!pDPA_SaveStream)
    {
        win_skip("DPA_SaveStream() not available. Skipping stream tests.\n");
        return;
    }

    hRes = CoInitialize(NULL);
    if (hRes != S_OK)
    {
        ok(0, "hResult: %d\n", hRes);
        return;
    }

    dwMode = STGM_DIRECT|STGM_CREATE|STGM_READWRITE|STGM_SHARE_EXCLUSIVE;
    hRes = StgCreateDocfile(NULL, dwMode|STGM_DELETEONRELEASE, 0, &pStg);
    expect(S_OK, hRes);

    hRes = IStorage_CreateStream(pStg, szStg, dwMode, 0, 0, &pStm);
    expect(S_OK, hRes);

    dpa = pDPA_Create(0);

    /* simple parameter check */
    hRes = pDPA_SaveStream(dpa, NULL, pStm, NULL);
    ok(hRes == E_INVALIDARG ||
       broken(hRes == S_OK) /* XP and below */, "Wrong result, %d\n", hRes);
if (0) {
    /* crashes on XP */
    hRes = pDPA_SaveStream(NULL, CB_Save, pStm, NULL);
    expect(E_INVALIDARG, hRes);

    hRes = pDPA_SaveStream(dpa, CB_Save, NULL, NULL);
    expect(E_INVALIDARG, hRes);
}

710
    /* saving/loading */
711 712
    for (i = 0; i < 6; i++)
    {
713
        ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1));
714 715
        ok(ret == i, "ret=%d\n", ret);
    }
716

717 718
    liZero.QuadPart = 0;
    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 726

    liZero.QuadPart = 0;
    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 730 731
    expect(S_OK, hRes);
    rc = CheckDPA(dpa, 0x123456, &dw);
    ok(rc, "dw=0x%x\n", dw);
732 733 734 735 736 737 738 739 740
    pDPA_Destroy(dpa);

    ret = IStream_Release(pStm);
    ok(!ret, "ret=%d\n", ret);

    ret = IStorage_Release(pStg);
    ok(!ret, "ret=%d\n", ret);

    CoUninitialize();
741 742 743 744
}

START_TEST(dpa)
{
745 746 747 748
    HMODULE hcomctl32;

    hcomctl32 = GetModuleHandleA("comctl32.dll");

749 750
    if(!InitFunctionPtrs(hcomctl32))
    {
751
        win_skip("Needed functions are not available\n");
752 753 754 755 756 757 758
        return;
    }

    test_dpa();
    test_DPA_Merge();
    test_DPA_EnumCallback();
    test_DPA_DestroyCallback();
759
    test_DPA_LoadStream();
760
    test_DPA_SaveStream();
761
}