1 /* Unit test suite for SHLWAPI Compact List and IStream ordinal functions
3 * Copyright 2002 Jon Griffiths
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/test.h"
30 static const DATABLOCK_HEADER clist_items
[] =
46 /* Dummy IStream object for testing calls */
49 IStream IStream_iface
;
62 const DATABLOCK_HEADER
*item
;
66 static inline struct dummystream
*impl_from_IStream(IStream
*iface
)
68 return CONTAINING_RECORD(iface
, struct dummystream
, IStream_iface
);
71 static HRESULT WINAPI
QueryInterface(IStream
*iface
, REFIID riid
, void **ret_iface
)
73 if (IsEqualGUID(&IID_IUnknown
, riid
) || IsEqualGUID(&IID_IStream
, riid
)) {
75 IStream_AddRef(iface
);
78 trace("Unexpected REFIID %s\n", wine_dbgstr_guid(riid
));
83 static ULONG WINAPI
AddRef(IStream
*iface
)
85 struct dummystream
*This
= impl_from_IStream(iface
);
87 return InterlockedIncrement(&This
->ref
);
90 static ULONG WINAPI
Release(IStream
*iface
)
92 struct dummystream
*This
= impl_from_IStream(iface
);
94 return InterlockedDecrement(&This
->ref
);
97 static HRESULT WINAPI
Read(IStream
*iface
, void *lpMem
, ULONG ulSize
, ULONG
*lpRead
)
99 struct dummystream
*This
= impl_from_IStream(iface
);
103 if (This
->failreadcall
)
105 return STG_E_ACCESSDENIED
;
107 else if (This
->failreadsize
)
109 *lpRead
= ulSize
+ 8;
112 else if (This
->readreturnlarge
)
114 *((ULONG
*)lpMem
) = 0xffff01;
116 This
->readreturnlarge
= FALSE
;
119 if (ulSize
== sizeof(ULONG
))
121 /* Read size of item */
122 *((ULONG
*)lpMem
) = This
->item
->cbSize
? This
->item
->cbSize
+ sizeof(DATABLOCK_HEADER
) : 0;
131 if (!This
->item
->cbSize
)
133 This
->readbeyondend
= TRUE
;
135 return E_FAIL
; /* Should never happen */
137 *((ULONG
*)lpMem
) = This
->item
->dwSignature
;
140 for (i
= 0; i
< This
->item
->cbSize
; i
++)
148 static HRESULT WINAPI
Write(IStream
*iface
, const void *lpMem
, ULONG ulSize
, ULONG
*lpWritten
)
150 struct dummystream
*This
= impl_from_IStream(iface
);
154 if (This
->failwritecall
)
156 return STG_E_ACCESSDENIED
;
158 else if (This
->failwritesize
)
167 static HRESULT WINAPI
Seek(IStream
*iface
, LARGE_INTEGER dlibMove
, DWORD dwOrigin
,
168 ULARGE_INTEGER
*plibNewPosition
)
170 struct dummystream
*This
= impl_from_IStream(iface
);
173 This
->pos
.QuadPart
= dlibMove
.QuadPart
;
175 plibNewPosition
->QuadPart
= dlibMove
.QuadPart
;
179 static HRESULT WINAPI
Stat(IStream
*iface
, STATSTG
*pstatstg
, DWORD grfStatFlag
)
181 struct dummystream
*This
= impl_from_IStream(iface
);
184 if (This
->failstatcall
)
187 pstatstg
->cbSize
.QuadPart
= This
->pos
.QuadPart
;
192 static IStreamVtbl iclvt
=
204 NULL
, /* LockRegion */
205 NULL
, /* UnlockRegion */
210 /* Function ptrs for ordinal calls */
211 static HMODULE SHLWAPI_hshlwapi
= 0;
213 static void (WINAPI
*pSHFreeDataBlockList
)(DATABLOCK_HEADER
*);
214 static HRESULT (WINAPI
*pSHAddDataBlock
)(DATABLOCK_HEADER
**, DATABLOCK_HEADER
*);
215 static BOOL (WINAPI
*pSHRemoveDataBlock
)(DATABLOCK_HEADER
**,ULONG
);
216 static DATABLOCK_HEADER
*(WINAPI
*pSHFindDataBlock
)(DATABLOCK_HEADER
*,ULONG
);
217 static HRESULT (WINAPI
*pSHWriteDataBlockList
)(IStream
*, DATABLOCK_HEADER
*);
218 static HRESULT (WINAPI
*pSHReadDataBlockList
)(IStream
*, DATABLOCK_HEADER
**);
220 static BOOL (WINAPI
*pSHIsEmptyStream
)(IStream
*);
221 static HRESULT (WINAPI
*pIStream_Read
)(IStream
*, void *, ULONG
);
222 static HRESULT (WINAPI
*pIStream_Write
)(IStream
*, const void *, ULONG
);
223 static HRESULT (WINAPI
*pIStream_Reset
)(IStream
*);
224 static HRESULT (WINAPI
*pIStream_Size
)(IStream
*, ULARGE_INTEGER
*);
227 static BOOL
InitFunctionPtrs(void)
229 SHLWAPI_hshlwapi
= GetModuleHandleA("shlwapi.dll");
231 /* SHCreateStreamOnFileEx was introduced in shlwapi v6.0 */
232 if(!GetProcAddress(SHLWAPI_hshlwapi
, "SHCreateStreamOnFileEx")){
233 win_skip("Too old shlwapi version\n");
237 pSHWriteDataBlockList
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)17);
238 ok(pSHWriteDataBlockList
!= 0, "No Ordinal 17\n");
239 pSHReadDataBlockList
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)18);
240 ok(pSHReadDataBlockList
!= 0, "No Ordinal 18\n");
241 pSHFreeDataBlockList
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)19);
242 ok(pSHFreeDataBlockList
!= 0, "No Ordinal 19\n");
243 pSHAddDataBlock
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)20);
244 ok(pSHAddDataBlock
!= 0, "No Ordinal 20\n");
245 pSHRemoveDataBlock
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)21);
246 ok(pSHRemoveDataBlock
!= 0, "No Ordinal 21\n");
247 pSHFindDataBlock
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)22);
248 ok(pSHFindDataBlock
!= 0, "No Ordinal 22\n");
249 pSHIsEmptyStream
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)166);
250 ok(pSHIsEmptyStream
!= 0, "No Ordinal 166\n");
251 pIStream_Read
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)184);
252 ok(pIStream_Read
!= 0, "No Ordinal 184\n");
253 pIStream_Write
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)212);
254 ok(pIStream_Write
!= 0, "No Ordinal 212\n");
255 pIStream_Reset
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)213);
256 ok(pIStream_Reset
!= 0, "No Ordinal 213\n");
257 pIStream_Size
= (void *)GetProcAddress( SHLWAPI_hshlwapi
, (LPSTR
)214);
258 ok(pIStream_Size
!= 0, "No Ordinal 214\n");
263 static void InitDummyStream(struct dummystream
*obj
)
265 obj
->IStream_iface
.lpVtbl
= &iclvt
;
268 obj
->failreadcall
= FALSE
;
269 obj
->failreadsize
= FALSE
;
270 obj
->readbeyondend
= FALSE
;
271 obj
->readreturnlarge
= FALSE
;
273 obj
->failwritecall
= FALSE
;
274 obj
->failwritesize
= FALSE
;
277 obj
->failstatcall
= FALSE
;
278 obj
->item
= clist_items
;
279 obj
->pos
.QuadPart
= 0;
283 static void test_CList(void)
285 struct dummystream streamobj
;
286 DATABLOCK_HEADER
*list
= NULL
;
287 const DATABLOCK_HEADER
*item
= clist_items
;
289 DATABLOCK_HEADER
*inserted
;
294 if (!pSHWriteDataBlockList
|| !pSHReadDataBlockList
|| !pSHFreeDataBlockList
|| !pSHAddDataBlock
||
295 !pSHRemoveDataBlock
|| !pSHFindDataBlock
)
298 /* Populate a list and test the items are added correctly */
301 /* Create item and fill with data */
302 inserted
= (DATABLOCK_HEADER
*)buff
;
303 inserted
->cbSize
= item
->cbSize
+ sizeof(DATABLOCK_HEADER
);
304 inserted
->dwSignature
= item
->dwSignature
;
305 for (i
= 0; i
< item
->cbSize
; i
++)
306 buff
[sizeof(DATABLOCK_HEADER
) + i
] = i
* 2;
308 ret
= pSHAddDataBlock(&list
, inserted
);
309 ok(ret
== TRUE
, "got %d\n", ret
);
313 ok(list
&& list
->cbSize
, "item not added\n");
315 inserted
= pSHFindDataBlock(list
, item
->dwSignature
);
316 ok(inserted
!= NULL
, "lost after adding\n");
318 ok(!inserted
|| inserted
->dwSignature
!= ~0U, "find returned a container\n");
321 if (inserted
&& (inserted
->cbSize
& 0x3))
324 ok(inserted
[-1].dwSignature
== ~0U, "invalid size is not countained\n");
325 ok(inserted
[-1].cbSize
> inserted
->cbSize
+ sizeof(DATABLOCK_HEADER
),
326 "container too small\n");
330 ok(inserted
->cbSize
== item
->cbSize
+ sizeof(DATABLOCK_HEADER
),
331 "id %ld wrong size %ld\n", inserted
->dwSignature
, inserted
->cbSize
);
336 LPBYTE bufftest
= (LPBYTE
)inserted
;
338 for (i
= 0; i
< inserted
->cbSize
- sizeof(DATABLOCK_HEADER
); i
++)
339 if (bufftest
[sizeof(DATABLOCK_HEADER
) + i
] != i
* 2)
342 ok(bDataOK
== TRUE
, "data corrupted on insert\n");
344 ok(!inserted
|| inserted
->dwSignature
== item
->dwSignature
, "find got wrong item\n");
350 InitDummyStream(&streamobj
);
352 hRet
= pSHWriteDataBlockList(&streamobj
.IStream_iface
, list
);
353 ok(hRet
== S_OK
, "write failed\n");
356 /* 1 call for each element, + 1 for OK (use our null element for this) */
357 ok(streamobj
.writecalls
== ARRAY_SIZE(clist_items
), "wrong call count\n");
358 ok(streamobj
.readcalls
== 0,"called Read() in write\n");
359 ok(streamobj
.seekcalls
== 0,"called Seek() in write\n");
362 /* Failure cases for writing */
363 InitDummyStream(&streamobj
);
364 streamobj
.failwritecall
= TRUE
;
365 hRet
= pSHWriteDataBlockList(&streamobj
.IStream_iface
, list
);
366 ok(hRet
== STG_E_ACCESSDENIED
, "changed object failure return\n");
367 ok(streamobj
.writecalls
== 1, "called object after failure\n");
368 ok(streamobj
.readcalls
== 0,"called Read() after failure\n");
369 ok(streamobj
.seekcalls
== 0,"called Seek() after failure\n");
371 InitDummyStream(&streamobj
);
372 streamobj
.failwritesize
= TRUE
;
373 hRet
= pSHWriteDataBlockList(&streamobj
.IStream_iface
, list
);
374 ok(hRet
== STG_E_MEDIUMFULL
|| broken(hRet
== E_FAIL
) /* Win7 */,
375 "changed size failure return\n");
376 ok(streamobj
.writecalls
== 1, "called object after size failure\n");
377 ok(streamobj
.readcalls
== 0,"called Read() after failure\n");
378 ok(streamobj
.seekcalls
== 0,"called Seek() after failure\n");
380 /* Invalid inputs for adding */
381 inserted
= (DATABLOCK_HEADER
*)buff
;
382 inserted
->cbSize
= sizeof(DATABLOCK_HEADER
) - 1;
383 inserted
->dwSignature
= 33;
385 ret
= pSHAddDataBlock(NULL
, inserted
);
386 ok(!ret
, "got %d\n", ret
);
388 ret
= pSHAddDataBlock(&list
, inserted
);
389 ok(!ret
, "got %d\n", ret
);
391 inserted
= pSHFindDataBlock(list
, 33);
392 ok(inserted
== NULL
, "inserted bad element size\n");
394 inserted
= (DATABLOCK_HEADER
*)buff
;
395 inserted
->cbSize
= 44;
396 inserted
->dwSignature
= ~0U;
398 ret
= pSHAddDataBlock(&list
, inserted
);
399 ok(!ret
, "got %d\n", ret
);
403 /* Look for nonexistent item in populated list */
404 inserted
= pSHFindDataBlock(list
, 99999999);
405 ok(inserted
== NULL
, "found a nonexistent item\n");
409 BOOL bRet
= pSHRemoveDataBlock(&list
, item
->dwSignature
);
410 ok(bRet
== TRUE
, "couldn't find item to delete\n");
414 /* Look for nonexistent item in empty list */
415 inserted
= pSHFindDataBlock(list
, 99999999);
416 ok(inserted
== NULL
, "found an item in empty list\n");
418 /* Create a list by reading in data */
419 InitDummyStream(&streamobj
);
421 hRet
= pSHReadDataBlockList(&streamobj
.IStream_iface
, &list
);
422 ok(hRet
== S_OK
, "failed create from Read()\n");
425 ok(streamobj
.readbeyondend
== FALSE
, "read beyond end\n");
426 /* 2 calls per item, but only 1 for the terminator */
427 ok(streamobj
.readcalls
== ARRAY_SIZE(clist_items
) * 2 - 1, "wrong call count\n");
428 ok(streamobj
.writecalls
== 0, "called Write() from create\n");
429 ok(streamobj
.seekcalls
== 0,"called Seek() from create\n");
433 /* Check the items were added correctly */
436 inserted
= pSHFindDataBlock(list
, item
->dwSignature
);
437 ok(inserted
!= NULL
, "lost after adding\n");
439 ok(!inserted
|| inserted
->dwSignature
!= ~0U, "find returned a container\n");
442 if (inserted
&& inserted
->cbSize
& 0x3)
445 ok(inserted
[-1].dwSignature
== ~0U, "invalid size is not countained\n");
446 ok(inserted
[-1].cbSize
> inserted
->cbSize
+ sizeof(DATABLOCK_HEADER
),
447 "container too small\n");
451 ok(inserted
->cbSize
== item
->cbSize
+ sizeof(DATABLOCK_HEADER
),
452 "id %ld wrong size %ld\n", inserted
->dwSignature
, inserted
->cbSize
);
454 ok(!inserted
|| inserted
->dwSignature
== item
->dwSignature
, "find got wrong item\n");
458 LPBYTE bufftest
= (LPBYTE
)inserted
;
460 for (i
= 0; i
< inserted
->cbSize
- sizeof(DATABLOCK_HEADER
); i
++)
461 if (bufftest
[sizeof(DATABLOCK_HEADER
) + i
] != i
* 2)
464 ok(bDataOK
== TRUE
, "data corrupted on insert\n");
470 /* Failure cases for reading */
471 InitDummyStream(&streamobj
);
472 streamobj
.failreadcall
= TRUE
;
473 hRet
= pSHReadDataBlockList(&streamobj
.IStream_iface
, &list
);
474 ok(hRet
== STG_E_ACCESSDENIED
, "changed object failure return\n");
475 ok(streamobj
.readbeyondend
== FALSE
, "read beyond end\n");
476 ok(streamobj
.readcalls
== 1, "called object after read failure\n");
477 ok(streamobj
.writecalls
== 0,"called Write() after read failure\n");
478 ok(streamobj
.seekcalls
== 0,"called Seek() after read failure\n");
480 /* Read returns large object */
481 InitDummyStream(&streamobj
);
482 streamobj
.readreturnlarge
= TRUE
;
483 hRet
= pSHReadDataBlockList(&streamobj
.IStream_iface
, &list
);
484 ok(hRet
== S_OK
, "failed create from Read() with large item\n");
485 ok(streamobj
.readbeyondend
== FALSE
, "read beyond end\n");
486 ok(streamobj
.readcalls
== 1,"wrong call count\n");
487 ok(streamobj
.writecalls
== 0,"called Write() after read failure\n");
488 ok(streamobj
.seekcalls
== 2,"wrong Seek() call count (%d)\n", streamobj
.seekcalls
);
490 pSHFreeDataBlockList(list
);
493 static BOOL
test_SHIsEmptyStream(void)
495 struct dummystream streamobj
;
498 if (!pSHIsEmptyStream
)
501 InitDummyStream(&streamobj
);
502 bRet
= pSHIsEmptyStream(&streamobj
.IStream_iface
);
505 return FALSE
; /* This version doesn't support stream ops on clists */
507 ok(streamobj
.readcalls
== 0, "called Read()\n");
508 ok(streamobj
.writecalls
== 0, "called Write()\n");
509 ok(streamobj
.seekcalls
== 0, "called Seek()\n");
510 ok(streamobj
.statcalls
== 1, "wrong call count\n");
512 streamobj
.statcalls
= 0;
513 streamobj
.pos
.QuadPart
= 50001;
515 bRet
= pSHIsEmptyStream(&streamobj
.IStream_iface
);
517 ok(bRet
== FALSE
, "failed after seek adjusted\n");
518 ok(streamobj
.readcalls
== 0, "called Read()\n");
519 ok(streamobj
.writecalls
== 0, "called Write()\n");
520 ok(streamobj
.seekcalls
== 0, "called Seek()\n");
521 ok(streamobj
.statcalls
== 1, "wrong call count\n");
524 InitDummyStream(&streamobj
);
525 streamobj
.pos
.QuadPart
= 50001;
526 streamobj
.failstatcall
= TRUE
; /* 1: Stat() Bad, Read() OK */
527 bRet
= pSHIsEmptyStream(&streamobj
.IStream_iface
);
528 ok(bRet
== FALSE
, "should be FALSE after read is OK\n");
529 ok(streamobj
.readcalls
== 1, "wrong call count\n");
530 ok(streamobj
.writecalls
== 0, "called Write()\n");
531 ok(streamobj
.seekcalls
== 1, "wrong call count\n");
532 ok(streamobj
.statcalls
== 1, "wrong call count\n");
533 ok(streamobj
.pos
.QuadPart
== 0, "Didn't seek to start\n");
535 InitDummyStream(&streamobj
);
536 streamobj
.pos
.QuadPart
= 50001;
537 streamobj
.failstatcall
= TRUE
;
538 streamobj
.failreadcall
= TRUE
; /* 2: Stat() Bad, Read() Bad Also */
539 bRet
= pSHIsEmptyStream(&streamobj
.IStream_iface
);
540 ok(bRet
== TRUE
, "Should be true after read fails\n");
541 ok(streamobj
.readcalls
== 1, "wrong call count\n");
542 ok(streamobj
.writecalls
== 0, "called Write()\n");
543 ok(streamobj
.seekcalls
== 0, "Called Seek()\n");
544 ok(streamobj
.statcalls
== 1, "wrong call count\n");
545 ok(streamobj
.pos
.QuadPart
== 50001, "called Seek() after read failed\n");
549 static void test_IStream_Read(void)
551 struct dummystream streamobj
;
558 InitDummyStream(&streamobj
);
559 hRet
= pIStream_Read(&streamobj
.IStream_iface
, buff
, sizeof(buff
));
561 ok(hRet
== S_OK
, "failed Read()\n");
562 ok(streamobj
.readcalls
== 1, "wrong call count\n");
563 ok(streamobj
.writecalls
== 0, "called Write()\n");
564 ok(streamobj
.seekcalls
== 0, "called Seek()\n");
567 static void test_IStream_Write(void)
569 struct dummystream streamobj
;
570 char buff
[256] = {0};
576 InitDummyStream(&streamobj
);
577 hRet
= pIStream_Write(&streamobj
.IStream_iface
, buff
, sizeof(buff
));
579 ok(hRet
== S_OK
, "failed Write()\n");
580 ok(streamobj
.readcalls
== 0, "called Read()\n");
581 ok(streamobj
.writecalls
== 1, "wrong call count\n");
582 ok(streamobj
.seekcalls
== 0, "called Seek()\n");
585 static void test_IStream_Reset(void)
587 struct dummystream streamobj
;
592 if (!pIStream_Reset
|| !pIStream_Size
)
595 InitDummyStream(&streamobj
);
597 Seek(&streamobj
.IStream_iface
, ll
, 0, NULL
); /* Seek to 5000l */
599 streamobj
.seekcalls
= 0;
600 pIStream_Reset(&streamobj
.IStream_iface
);
601 ok(streamobj
.statcalls
== 0, "called Stat()\n");
602 ok(streamobj
.readcalls
== 0, "called Read()\n");
603 ok(streamobj
.writecalls
== 0, "called Write()\n");
604 ok(streamobj
.seekcalls
== 1, "wrong call count\n");
607 hRet
= pIStream_Size(&streamobj
.IStream_iface
, &ul
);
608 ok(hRet
== S_OK
, "failed Stat()\n");
609 ok(ul
.QuadPart
== 0, "213 didn't rewind stream\n");
612 static void test_IStream_Size(void)
614 struct dummystream streamobj
;
622 InitDummyStream(&streamobj
);
624 Seek(&streamobj
.IStream_iface
, ll
, 0, NULL
);
626 streamobj
.seekcalls
= 0;
627 hRet
= pIStream_Size(&streamobj
.IStream_iface
, &ul
);
629 ok(hRet
== S_OK
, "failed Stat()\n");
630 ok(streamobj
.statcalls
== 1, "wrong call count\n");
631 ok(streamobj
.readcalls
== 0, "called Read()\n");
632 ok(streamobj
.writecalls
== 0, "called Write()\n");
633 ok(streamobj
.seekcalls
== 0, "called Seek()\n");
634 ok(ul
.QuadPart
== 5000l, "Stat gave wrong size\n");
639 if(!InitFunctionPtrs())
644 /* Test streaming if this version supports it */
645 if (test_SHIsEmptyStream())
648 test_IStream_Write();
649 test_IStream_Reset();