| /* |
| * 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(); |
| } |