| /* | |
| * Unit tests for DPA functions | |
| * | |
| * Copyright 2003 Uwe Bonnes | |
| * Copyright 2005 Felix Nawothnig | |
| * | |
| * 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 | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #define COBJMACROS | |
| #include <stdarg.h> | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winuser.h" | |
| #include "commctrl.h" | |
| #include "objidl.h" | |
| #include "wine/test.h" | |
| #define expect(expected, got) ok(got == expected, "Expected %d, got %d\n", expected, got) | |
| typedef struct _STREAMDATA | |
| { | |
| DWORD dwSize; | |
| DWORD dwData2; | |
| DWORD dwItems; | |
| } STREAMDATA, *PSTREAMDATA; | |
| static HDPA (WINAPI *pDPA_Clone)(const HDPA,HDPA); | |
| static HDPA (WINAPI *pDPA_Create)(INT); | |
| static HDPA (WINAPI *pDPA_CreateEx)(INT,HANDLE); | |
| static PVOID (WINAPI *pDPA_DeleteAllPtrs)(HDPA); | |
| static PVOID (WINAPI *pDPA_DeletePtr)(HDPA,INT); | |
| static BOOL (WINAPI *pDPA_Destroy)(HDPA); | |
| static VOID (WINAPI *pDPA_DestroyCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); | |
| static VOID (WINAPI *pDPA_EnumCallback)(HDPA,PFNDPAENUMCALLBACK,PVOID); | |
| static INT (WINAPI *pDPA_GetPtr)(HDPA,INT); | |
| static INT (WINAPI *pDPA_GetPtrIndex)(HDPA,PVOID); | |
| static BOOL (WINAPI *pDPA_Grow)(HDPA,INT); | |
| static INT (WINAPI *pDPA_InsertPtr)(HDPA,INT,PVOID); | |
| static HRESULT (WINAPI *pDPA_LoadStream)(HDPA*,PFNDPASTREAM,IStream*,LPVOID); | |
| static BOOL (WINAPI *pDPA_Merge)(HDPA,HDPA,DWORD,PFNDPACOMPARE,PFNDPAMERGE,LPARAM); | |
| static HRESULT (WINAPI *pDPA_SaveStream)(HDPA,PFNDPASTREAM,IStream*,LPVOID); | |
| static INT (WINAPI *pDPA_Search)(HDPA,PVOID,INT,PFNDPACOMPARE,LPARAM,UINT); | |
| static BOOL (WINAPI *pDPA_SetPtr)(HDPA,INT,PVOID); | |
| static BOOL (WINAPI *pDPA_Sort)(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) | |
| { | |
| /* 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) && | |
| COMCTL32_GET_PROC(DPA_LoadStream, 9) && | |
| COMCTL32_GET_PROC(DPA_Merge, 11) && | |
| COMCTL32_GET_PROC(DPA_SaveStream, 10); | |
| return TRUE; | |
| } | |
| return FALSE; | |
| } | |
| /* Callbacks */ | |
| static INT CALLBACK CB_CmpLT(PVOID p1, PVOID p2, LPARAM lp) | |
| { | |
| ok(lp == 0x1abe11ed, "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 == 0x1abe11ed, "lp=%ld\n", lp); | |
| return p1 > p2 ? -1 : p1 < p2 ? 1 : 0; | |
| } | |
| /* merge callback messages counter | |
| DPAMM_MERGE 1 | |
| DPAMM_DELETE 2 | |
| DPAMM_INSERT 3 */ | |
| static INT nMessages[4]; | |
| static PVOID CALLBACK CB_MergeInsertSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp) | |
| { | |
| nMessages[op]++; | |
| ok(lp == 0x1abe11ed, "lp=%ld\n", lp); | |
| return p1; | |
| } | |
| static PVOID CALLBACK CB_MergeDeleteOddSrc(UINT op, PVOID p1, PVOID p2, LPARAM lp) | |
| { | |
| nMessages[op]++; | |
| ok(lp == 0x1abe11ed, "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; | |
| } | |
| static HRESULT CALLBACK CB_Save(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp) | |
| { | |
| HRESULT hRes; | |
| ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp); | |
| hRes = IStream_Write(pStm, &pInfo->iPos, sizeof(INT), NULL); | |
| expect(S_OK, hRes); | |
| hRes = IStream_Write(pStm, &pInfo->pvItem, sizeof(PVOID), NULL); | |
| expect(S_OK, hRes); | |
| return S_OK; | |
| } | |
| static HRESULT CALLBACK CB_Load(DPASTREAMINFO *pInfo, IStream *pStm, LPVOID lp) | |
| { | |
| HRESULT hRes; | |
| INT iOldPos; | |
| iOldPos = pInfo->iPos; | |
| ok(lp == (LPVOID)0xdeadbeef, "lp=%p\n", lp); | |
| hRes = IStream_Read(pStm, &pInfo->iPos, sizeof(INT), NULL); | |
| expect(S_OK, hRes); | |
| ok(pInfo->iPos == iOldPos, "iPos=%d iOldPos=%d\n", pInfo->iPos, iOldPos); | |
| hRes = IStream_Read(pStm, &pInfo->pvItem, sizeof(PVOID), NULL); | |
| expect(S_OK, hRes); | |
| 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 | |
| { | |
| pDPA_InsertPtr(dpa, 0, (PVOID)(ULONG_PTR)(dwIn & 0xf)); | |
| 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; | |
| BOOL rc; | |
| GetSystemInfo(&si); | |
| hHeap = HeapCreate(0, 1, 2); | |
| ok(hHeap != NULL, "error=%d\n", GetLastError()); | |
| dpa3 = pDPA_CreateEx(0, hHeap); | |
| ok(dpa3 != NULL, "\n"); | |
| ret = pDPA_Grow(dpa3, si.dwPageSize + 1); | |
| ok(!ret && GetLastError() == ERROR_NOT_ENOUGH_MEMORY, | |
| "ret=%d error=%d\n", ret, GetLastError()); | |
| dpa = pDPA_Create(0); | |
| ok(dpa != NULL, "\n"); | |
| /* Set item with out of bound index */ | |
| ok(pDPA_SetPtr(dpa, 1, (PVOID)6), "\n"); | |
| /* Fill the created gap */ | |
| ok(pDPA_SetPtr(dpa, 0, (PVOID)5), "\n"); | |
| rc=CheckDPA(dpa, 0x56, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| /* 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); | |
| ok(ret == 4, "ret=%d\n", ret); | |
| /* Append item using DPA_APPEND */ | |
| ret = pDPA_InsertPtr(dpa, DPA_APPEND, (PVOID)4); | |
| ok(ret == 5, "ret=%d\n", ret); | |
| rc=CheckDPA(dpa, 0x516324, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| for(i = 1; i <= 6; i++) | |
| { | |
| INT j, k; | |
| k = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i); | |
| /* Linear searches should work on unsorted DPAs */ | |
| j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, 0); | |
| ok(j == k, "j=%d k=%d\n", j, k); | |
| } | |
| /* Sort DPA */ | |
| ok(pDPA_Sort(dpa, CB_CmpGT, 0x1abe11ed), "\n"); | |
| rc=CheckDPA(dpa, 0x654321, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| /* Clone into a new DPA */ | |
| dpa2 = pDPA_Clone(dpa, NULL); | |
| ok(dpa2 != NULL, "\n"); | |
| /* The old data should have been preserved */ | |
| rc=CheckDPA(dpa2, 0x654321, &dw2); | |
| ok(rc, "dw=0x%x\n", dw2); | |
| ok(pDPA_Sort(dpa, CB_CmpLT, 0x1abe11ed), "\n"); | |
| /* Test if the DPA itself was really copied */ | |
| rc=CheckDPA(dpa, 0x123456, &dw); | |
| ok(rc, "dw=0x%x\n", dw ); | |
| rc=CheckDPA(dpa2, 0x654321, &dw2); | |
| ok(rc, "dw2=0x%x\n", dw2); | |
| /* Clone into an old DPA */ | |
| SetLastError(ERROR_SUCCESS); | |
| p = pDPA_Clone(dpa, dpa3); | |
| ok(p == dpa3, "p=%p\n", p); | |
| rc=CheckDPA(dpa3, 0x123456, &dw3); | |
| ok(rc, "dw3=0x%x\n", dw3); | |
| for(i = 1; i <= 6; i++) | |
| { | |
| INT j; | |
| /* The array is in order so ptr == index+1 */ | |
| j = pDPA_GetPtrIndex(dpa, (PVOID)(INT_PTR)i); | |
| ok(j+1 == i, "j=%d i=%d\n", j, i); | |
| j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpLT, 0x1abe11ed, DPAS_SORTED); | |
| ok(j+1 == i, "j=%d i=%d\n", j, i); | |
| /* Linear searches respect iStart ... */ | |
| j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, 0); | |
| ok(j == DPA_ERR, "j=%d\n", j); | |
| /* ... but for a binary search it's ignored */ | |
| j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, i+1, CB_CmpLT, 0x1abe11ed, DPAS_SORTED); | |
| ok(j+1 == i, "j=%d i=%d\n", j, i); | |
| } | |
| /* Try to get the index of a nonexistent item */ | |
| 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); | |
| rc=CheckDPA(dpa, 0x12456, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| /* Check where to re-insert the deleted item */ | |
| i = pDPA_Search(dpa, (PVOID)3, 0, | |
| CB_CmpLT, 0x1abe11ed, 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, 0x1abe11ed, DPAS_SORTED|DPAS_INSERTBEFORE); | |
| ok(i == 2, "i=%d\n", i); | |
| /* without DPAS_INSERTBEFORE/AFTER */ | |
| i = pDPA_Search(dpa, (PVOID)3, 0, | |
| CB_CmpLT, 0x1abe11ed, DPAS_SORTED); | |
| ok(i == -1, "i=%d\n", i); | |
| /* Re-insert the item */ | |
| ret = pDPA_InsertPtr(dpa, 2, (PVOID)3); | |
| ok(ret == 2, "ret=%d i=%d\n", ret, 2); | |
| rc=CheckDPA(dpa, 0x123456, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| /* When doing a binary search while claiming reverse order all indexes | |
| * should be bogus */ | |
| for(i = 0; i < 6; i++) | |
| { | |
| INT j = pDPA_Search(dpa, (PVOID)(INT_PTR)i, 0, CB_CmpGT, 0x1abe11ed, | |
| DPAS_SORTED|DPAS_INSERTBEFORE); | |
| ok(j != i, "i=%d\n", i); | |
| } | |
| /* Setting item with huge index should work */ | |
| ok(pDPA_SetPtr(dpa2, 0x12345, (PVOID)0xdeadbeef), "\n"); | |
| ret = pDPA_GetPtrIndex(dpa2, (PVOID)0xdeadbeef); | |
| ok(ret == 0x12345, "ret=%d\n", ret); | |
| pDPA_DeleteAllPtrs(dpa2); | |
| rc=CheckDPA(dpa2, 0, &dw2); | |
| ok(rc, "dw2=0x%x\n", dw2); | |
| pDPA_Destroy(dpa); | |
| pDPA_Destroy(dpa2); | |
| 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++) | |
| { | |
| ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i)); | |
| ok(ret == i, "ret=%d\n", ret); | |
| ret = pDPA_InsertPtr(dpa3, i, (PVOID)(INT_PTR)(i+1)); | |
| 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 */ | |
| memset(nMessages, 0, sizeof(nMessages)); | |
| pDPA_Merge(dpa2, dpa, DPAM_INTERSECT, | |
| CB_CmpLT, CB_MergeDeleteOddSrc, 0x1abe11ed); | |
| rc = CheckDPA(dpa2, 0x246, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| expect(3, nMessages[DPAMM_MERGE]); | |
| expect(3, nMessages[DPAMM_DELETE]); | |
| expect(0, nMessages[DPAMM_INSERT]); | |
| for (i = 0; i < 6; i++) | |
| { | |
| ret = pDPA_InsertPtr(dpa2, i, (PVOID)(INT_PTR)(6-i)); | |
| 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, 0x1abe11ed); | |
| rc = CheckDPA(dpa2, 0x135, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| 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, 0x1abe11ed); | |
| rc = CheckDPA(dpa2, 0x123456, &dw); | |
| ok(rc || | |
| broken(!rc && dw == 0x23456), /* 4.7x */ | |
| "dw=0x%x\n", dw); | |
| expect(0, nMessages[DPAMM_MERGE]); | |
| expect(0, nMessages[DPAMM_DELETE]); | |
| ok(nMessages[DPAMM_INSERT] == 3 || | |
| broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */ | |
| "Expected 3, got %d\n", nMessages[DPAMM_INSERT]); | |
| /* Merge dpa3 into dpa2 and dpa */ | |
| memset(nMessages, 0, sizeof(nMessages)); | |
| pDPA_Merge(dpa, dpa3, DPAM_UNION|DPAM_SORTED, | |
| CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed); | |
| 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)); | |
| pDPA_Merge(dpa2, dpa3, DPAM_UNION|DPAM_SORTED, | |
| CB_CmpLT, CB_MergeInsertSrc, 0x1abe11ed); | |
| expect(3, nMessages[DPAMM_MERGE]); | |
| expect(0, nMessages[DPAMM_DELETE]); | |
| ok(nMessages[DPAMM_INSERT] == 3 || | |
| broken(nMessages[DPAMM_INSERT] == 2), /* 4.7x */ | |
| "Expected 3, got %d\n", nMessages[DPAMM_INSERT]); | |
| 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++) | |
| { | |
| ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1)); | |
| 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); | |
| for (i = 0; i < 3; i++) | |
| { | |
| ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1)); | |
| ok(ret == i, "ret=%d\n", ret); | |
| } | |
| nEnum = 0; | |
| pDPA_DestroyCallback(dpa, CB_EnumFirstThree, dpa); | |
| ok(nEnum == 3, "nEnum=%d\n", nEnum); | |
| } | |
| 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; | |
| if(!pDPA_LoadStream) | |
| { | |
| win_skip("DPA_LoadStream() 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); | |
| /* 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); | |
| ok(uli.QuadPart == 0, "Expected to position reset\n"); | |
| /* 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); | |
| dpa = NULL; | |
| hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, NULL); | |
| expect(S_OK, hRes); | |
| DPA_Destroy(dpa); | |
| /* 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); | |
| expect(E_FAIL, hRes); | |
| ret = IStream_Release(pStm); | |
| ok(!ret, "ret=%d\n", ret); | |
| ret = IStorage_Release(pStg); | |
| ok(!ret, "ret=%d\n", ret); | |
| CoUninitialize(); | |
| } | |
| static void test_DPA_SaveStream(void) | |
| { | |
| HDPA dpa; | |
| static const WCHAR szStg[] = { 'S','t','g',0 }; | |
| IStorage* pStg = NULL; | |
| IStream* pStm = NULL; | |
| DWORD dwMode, dw; | |
| HRESULT hRes; | |
| INT ret; | |
| INT i; | |
| BOOL rc; | |
| LARGE_INTEGER liZero; | |
| 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); | |
| } | |
| /* saving/loading */ | |
| for (i = 0; i < 6; i++) | |
| { | |
| ret = pDPA_InsertPtr(dpa, i, (PVOID)(INT_PTR)(i+1)); | |
| ok(ret == i, "ret=%d\n", ret); | |
| } | |
| liZero.QuadPart = 0; | |
| hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL); | |
| expect(S_OK, hRes); | |
| hRes = pDPA_SaveStream(dpa, CB_Save, pStm, (void*)0xdeadbeef); | |
| expect(S_OK, hRes); | |
| pDPA_Destroy(dpa); | |
| liZero.QuadPart = 0; | |
| hRes = IStream_Seek(pStm, liZero, STREAM_SEEK_SET, NULL); | |
| expect(S_OK, hRes); | |
| hRes = pDPA_LoadStream(&dpa, CB_Load, pStm, (void*)0xdeadbeef); | |
| expect(S_OK, hRes); | |
| rc = CheckDPA(dpa, 0x123456, &dw); | |
| ok(rc, "dw=0x%x\n", dw); | |
| pDPA_Destroy(dpa); | |
| ret = IStream_Release(pStm); | |
| ok(!ret, "ret=%d\n", ret); | |
| ret = IStorage_Release(pStg); | |
| ok(!ret, "ret=%d\n", ret); | |
| CoUninitialize(); | |
| } | |
| START_TEST(dpa) | |
| { | |
| HMODULE hcomctl32; | |
| hcomctl32 = GetModuleHandleA("comctl32.dll"); | |
| if(!InitFunctionPtrs(hcomctl32)) | |
| { | |
| win_skip("Needed functions are not available\n"); | |
| return; | |
| } | |
| test_dpa(); | |
| test_DPA_Merge(); | |
| test_DPA_EnumCallback(); | |
| test_DPA_DestroyCallback(); | |
| test_DPA_LoadStream(); | |
| test_DPA_SaveStream(); | |
| } |