2019-11-01 00:09:52 +01:00
// Included first and then again later to ensure that we're able to "light up" new functionality based off new includes
#include <wil/resource.h>
#include <wil/com.h>
#include <wil/stl.h>
// Headers to "light up" functionality in resource.h
#include <memory>
#include <roapi.h>
#include <winstring.h>
2023-02-22 13:12:56 -08:00
#include <WinUser.h>
2019-11-01 00:09:52 +01:00
#include <wil/resource.h>
#include <wrl/implements.h>
#include "common.h"
TEST_CASE ( "ResourceTests::TestLastErrorContext" , "[resource][last_error_context]" )
{
// Destructing the last_error_context restores the error.
{
SetLastError ( 42 );
auto error42 = wil :: last_error_context ();
SetLastError ( 0 );
}
REQUIRE ( GetLastError () == 42 );
// The context can be moved.
{
SetLastError ( 42 );
auto error42 = wil :: last_error_context ();
SetLastError ( 0 );
{
auto another_error42 = wil :: last_error_context ( std :: move ( error42 ));
SetLastError ( 1 );
}
REQUIRE ( GetLastError () == 42 );
SetLastError ( 0 );
// error42 has been moved-from and should not do anything at destruction.
}
REQUIRE ( GetLastError () == 0 );
// The context can be self-assigned, which has no effect.
{
SetLastError ( 42 );
auto error42 = wil :: last_error_context ();
SetLastError ( 0 );
error42 = std :: move ( error42 );
SetLastError ( 1 );
}
REQUIRE ( GetLastError () == 42 );
// The context can be dismissed, which cause it to do nothing at destruction.
{
SetLastError ( 42 );
auto error42 = wil :: last_error_context ();
SetLastError ( 0 );
error42 . release ();
SetLastError ( 1 );
}
REQUIRE ( GetLastError () == 1 );
2023-02-22 13:12:56 -08:00
// The value in the context is unimpacted by other things changing the last error
{
SetLastError ( 42 );
auto error42 = wil :: last_error_context ();
SetLastError ( 1 );
REQUIRE ( error42 . value () == 42 );
}
2019-11-01 00:09:52 +01:00
}
TEST_CASE ( "ResourceTests::TestScopeExit" , "[resource][scope_exit]" )
{
int count = 0 ;
auto validate = [ & ]( int expected ) { REQUIRE ( count == expected ); count = 0 ; };
{
auto foo = wil :: scope_exit ([ & ] { count ++ ; });
}
validate ( 1 );
{
auto foo = wil :: scope_exit ([ & ] { count ++ ; });
foo . release ();
foo . reset ();
}
validate ( 0 );
{
auto foo = wil :: scope_exit ([ & ] { count ++ ; });
foo . reset ();
foo . reset ();
validate ( 1 );
}
validate ( 0 );
#ifdef WIL_ENABLE_EXCEPTIONS
{
auto foo = wil :: scope_exit_log ( WI_DIAGNOSTICS_INFO , [ & ] { count ++ ; THROW_HR ( E_FAIL ); });
}
validate ( 1 );
{
auto foo = wil :: scope_exit_log ( WI_DIAGNOSTICS_INFO , [ & ] { count ++ ; THROW_HR ( E_FAIL ); });
foo . release ();
foo . reset ();
}
validate ( 0 );
{
auto foo = wil :: scope_exit_log ( WI_DIAGNOSTICS_INFO , [ & ] { count ++ ; THROW_HR ( E_FAIL ); });
foo . reset ();
foo . reset ();
validate ( 1 );
}
validate ( 0 );
#endif // WIL_ENABLE_EXCEPTIONS
}
interface __declspec ( uuid ( "ececcc6a-5193-4d14-b38e-ed1460c20b00" ))
ITest : public IUnknown
{
STDMETHOD_ ( void , Test )() = 0 ;
};
class PointerTestObject : witest :: AllocatedObject ,
public Microsoft :: WRL :: RuntimeClass < Microsoft :: WRL :: RuntimeClassFlags < Microsoft :: WRL :: RuntimeClassType :: ClassicCom > , ITest >
{
public :
STDMETHOD_ ( void , Test )() {};
};
TEST_CASE ( "ResourceTests::TestOperationsOnGenericSmartPointerClasses" , "[resource]" )
{
#ifdef WIL_ENABLE_EXCEPTIONS
{
// wil::unique_any_t example
wil :: unique_event ptr2 ( wil :: EventOptions :: ManualReset );
// wil::com_ptr
wil :: com_ptr < PointerTestObject > ptr3 = Microsoft :: WRL :: Make < PointerTestObject > ();
// wil::shared_any_t example
wil :: shared_event ptr4 ( wil :: EventOptions :: ManualReset );
// wistd::unique_ptr example
auto ptr5 = wil :: make_unique_failfast < POINT > ();
static_assert ( wistd :: is_same < typename wil :: smart_pointer_details < decltype ( ptr2 ) >:: pointer , HANDLE >:: value , "type-mismatch" );
static_assert ( wistd :: is_same < typename wil :: smart_pointer_details < decltype ( ptr3 ) >:: pointer , PointerTestObject *>:: value , "type-mismatch" );
auto p2 = wil :: detach_from_smart_pointer ( ptr2 );
auto p3 = wil :: detach_from_smart_pointer ( ptr3 );
// auto p4 = wil::detach_from_smart_pointer(ptr4); // wil::shared_any_t and std::shared_ptr do not support release().
HANDLE p4 {};
auto p5 = wil :: detach_from_smart_pointer ( ptr5 );
REQUIRE (( ! ptr2 && ! ptr3 ));
REQUIRE (( p2 && p3 ));
wil :: attach_to_smart_pointer ( ptr2 , p2 );
wil :: attach_to_smart_pointer ( ptr3 , p3 );
wil :: attach_to_smart_pointer ( ptr4 , p4 );
wil :: attach_to_smart_pointer ( ptr5 , p5 );
p2 = nullptr ;
p3 = nullptr ;
p4 = nullptr ;
p5 = nullptr ;
wil :: detach_to_opt_param ( & p2 , ptr2 );
wil :: detach_to_opt_param ( & p3 , ptr3 );
REQUIRE (( ! ptr2 && ! ptr3 ));
REQUIRE (( p2 && p3 ));
wil :: attach_to_smart_pointer ( ptr2 , p2 );
wil :: attach_to_smart_pointer ( ptr3 , p3 );
p2 = nullptr ;
p3 = nullptr ;
wil :: detach_to_opt_param ( & p2 , ptr2 );
wil :: detach_to_opt_param ( & p3 , ptr3 );
REQUIRE (( ! ptr2 && ! ptr3 ));
REQUIRE (( p2 && p3 ));
[ & ]( decltype ( p2 ) * ptr ) { * ptr = p2 ; } ( wil :: out_param ( ptr2 ));
[ & ]( decltype ( p3 ) * ptr ) { * ptr = p3 ; } ( wil :: out_param ( ptr3 ));
[ & ]( decltype ( p4 ) * ptr ) { * ptr = p4 ; } ( wil :: out_param ( ptr4 ));
[ & ]( decltype ( p5 ) * ptr ) { * ptr = p5 ; } ( wil :: out_param ( ptr5 ));
REQUIRE (( ptr2 && ptr3 ));
// Validate R-Value compilation
wil :: detach_to_opt_param ( & p2 , decltype ( ptr2 ){});
wil :: detach_to_opt_param ( & p3 , decltype ( ptr3 ){});
}
#endif
std :: unique_ptr < int > ptr1 ( new int ( 1 ));
Microsoft :: WRL :: ComPtr < PointerTestObject > ptr4 = Microsoft :: WRL :: Make < PointerTestObject > ();
static_assert ( wistd :: is_same < typename wil :: smart_pointer_details < decltype ( ptr1 ) >:: pointer , int *>:: value , "type-mismatch" );
static_assert ( wistd :: is_same < typename wil :: smart_pointer_details < decltype ( ptr4 ) >:: pointer , PointerTestObject *>:: value , "type-mismatch" );
auto p1 = wil :: detach_from_smart_pointer ( ptr1 );
auto p4 = wil :: detach_from_smart_pointer ( ptr4 );
REQUIRE (( ! ptr1 && ! ptr4 ));
REQUIRE (( p1 && p4 ));
wil :: attach_to_smart_pointer ( ptr1 , p1 );
wil :: attach_to_smart_pointer ( ptr4 , p4 );
REQUIRE (( ptr1 && ptr4 ));
p1 = nullptr ;
p4 = nullptr ;
int ** pNull = nullptr ;
wil :: detach_to_opt_param ( pNull , ptr1 );
REQUIRE ( ptr1 );
wil :: detach_to_opt_param ( & p1 , ptr1 );
wil :: detach_to_opt_param ( & p4 , ptr4 );
REQUIRE (( ! ptr1 && ! ptr4 ));
REQUIRE (( p1 && p4 ));
[ & ]( decltype ( p1 ) * ptr ) { * ptr = p1 ; } ( wil :: out_param ( ptr1 ));
[ & ]( decltype ( p4 ) * ptr ) { * ptr = p4 ; } ( wil :: out_param ( ptr4 ));
REQUIRE (( ptr1 && ptr4 ));
p1 = wil :: detach_from_smart_pointer ( ptr1 );
[ & ]( int ** ptr ) { * ptr = p1 ; } ( wil :: out_param_ptr < int **> ( ptr1 ));
REQUIRE ( ptr1 );
}
// Compilation only test...
void StlAdlTest ()
{
// This test has exposed some Argument Dependent Lookup issues in wistd / stl. Primarily we're
// just looking for clean compilation.
std :: vector < wistd :: unique_ptr < int >> v ;
v . emplace_back ( new int { 1 });
v . emplace_back ( new int { 2 });
v . emplace_back ( new int { 3 });
std :: rotate ( begin ( v ), begin ( v ) + 1 , end ( v ));
REQUIRE ( * v [ 0 ] == 1 );
REQUIRE ( * v [ 1 ] == 3 );
REQUIRE ( * v [ 2 ] == 2 );
decltype ( v ) v2 ;
v2 = std :: move ( v );
REQUIRE ( * v2 [ 0 ] == 1 );
REQUIRE ( * v2 [ 1 ] == 3 );
REQUIRE ( * v2 [ 2 ] == 2 );
decltype ( v ) v3 ;
std :: swap ( v2 , v3 );
REQUIRE ( * v3 [ 0 ] == 1 );
REQUIRE ( * v3 [ 1 ] == 3 );
REQUIRE ( * v3 [ 2 ] == 2 );
}
// Compilation only test...
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
void UniqueProcessInfo ()
{
wil :: unique_process_information process ;
CreateProcessW ( nullptr , nullptr , nullptr , nullptr , FALSE , 0 , nullptr , nullptr , nullptr , & process );
ResumeThread ( process . hThread );
WaitForSingleObject ( process . hProcess , INFINITE );
wil :: unique_process_information other ( wistd :: move ( process ));
}
#endif
2023-02-22 13:12:56 -08:00
// Compilation only test...
#ifdef WIL_ENABLE_EXCEPTIONS
void NoexceptConstructibleTest ()
{
using BaseStorage = wil :: details :: unique_storage < wil :: details :: handle_resource_policy > ;
struct ThrowingConstructor : BaseStorage
{
ThrowingConstructor () = default ;
explicit ThrowingConstructor ( HANDLE ) __WI_NOEXCEPT_ ( false ) {}
};
struct ProtectedConstructor : BaseStorage
{
protected :
ProtectedConstructor () = default ;
explicit ProtectedConstructor ( HANDLE ) WI_NOEXCEPT {}
};
// wil::unique_handle is one of the many types which are expected to be noexcept
// constructible since they don't perform any "advanced" initialization.
static_assert ( wistd :: is_nothrow_default_constructible_v < wil :: unique_handle > , "wil::unique_any_t should always be nothrow default constructible" );
static_assert ( wistd :: is_nothrow_constructible_v < wil :: unique_handle , HANDLE > , "wil::unique_any_t should be noexcept if the storage is" );
// The inverse: A throwing storage constructor.
static_assert ( wistd :: is_nothrow_default_constructible_v < wil :: unique_any_t < ThrowingConstructor >> , "wil::unique_any_t should always be nothrow default constructible" );
static_assert ( ! wistd :: is_nothrow_constructible_v < wil :: unique_any_t < ThrowingConstructor > , HANDLE > , "wil::unique_any_t shouldn't be noexcept if the storage isn't" );
// With a protected constructor wil::unique_any_t will be unable to correctly
// "forward" the noexcept attribute, but the code should still compile.
wil :: unique_any_t < ProtectedConstructor > p { INVALID_HANDLE_VALUE };
}
#endif
2019-11-01 00:09:52 +01:00
struct FakeComInterface
{
void AddRef ()
{
refs ++ ;
}
void Release ()
{
refs -- ;
}
HRESULT __stdcall Close ()
{
closes ++ ;
return S_OK ;
}
size_t refs = 0 ;
size_t closes = 0 ;
bool called ()
{
auto old = closes ;
closes = 0 ;
return ( old > 0 );
}
bool has_ref ()
{
return ( refs > 0 );
}
};
static void __stdcall CloseFakeComInterface ( FakeComInterface * fake )
{
fake -> Close ();
}
using unique_fakeclose_call = wil :: unique_com_call < FakeComInterface , decltype ( & CloseFakeComInterface ), CloseFakeComInterface > ;
TEST_CASE ( "ResourceTests::VerifyUniqueComCall" , "[resource][unique_com_call]" )
{
unique_fakeclose_call call1 ;
unique_fakeclose_call call2 ;
// intentional compilation errors
// unique_fakeclose_call call3 = call1;
// call2 = call1;
FakeComInterface fake1 ;
unique_fakeclose_call call4 ( & fake1 );
REQUIRE ( fake1 . has_ref ());
unique_fakeclose_call call5 ( wistd :: move ( call4 ));
REQUIRE ( ! call4 );
REQUIRE ( call5 );
REQUIRE ( fake1 . has_ref ());
call4 = wistd :: move ( call5 );
REQUIRE ( call4 );
REQUIRE ( ! call5 );
REQUIRE ( fake1 . has_ref ());
REQUIRE ( ! fake1 . called ());
FakeComInterface fake2 ;
{
unique_fakeclose_call scoped ( & fake2 );
}
REQUIRE ( ! fake2 . has_ref ());
REQUIRE ( fake2 . called ());
call4 . reset ( & fake2 );
REQUIRE ( fake1 . called ());
REQUIRE ( ! fake1 . has_ref ());
call4 . reset ();
REQUIRE ( ! fake2 . has_ref ());
REQUIRE ( fake2 . called ());
call1 . reset ( & fake1 );
call2 . swap ( call1 );
REQUIRE (( call2 && ! call1 ));
call2 . release ();
REQUIRE ( ! fake1 . called ());
REQUIRE ( ! fake1 . has_ref ());
REQUIRE ( ! call2 );
REQUIRE ( * call1 . addressof () == nullptr );
call1 . reset ( & fake1 );
fake2 . closes = 0 ;
fake2 . refs = 1 ;
* ( & call1 ) = & fake2 ;
REQUIRE ( ! fake1 . has_ref ());
REQUIRE ( fake1 . called ());
REQUIRE ( fake2 . has_ref ());
call1 . reset ( & fake1 );
fake2 . closes = 0 ;
fake2 . refs = 1 ;
* call1 . put () = & fake2 ;
REQUIRE ( ! fake1 . has_ref ());
REQUIRE ( fake1 . called ());
REQUIRE ( fake2 . has_ref ());
call1 . reset ();
REQUIRE ( ! fake2 . has_ref ());
REQUIRE ( fake2 . called ());
}
static bool g_called = false ;
static bool called ()
{
auto call = g_called ;
g_called = false ;
return ( call );
}
static void __stdcall FakeCall ()
{
g_called = true ;
}
using unique_fake_call = wil :: unique_call < decltype ( & FakeCall ), FakeCall > ;
TEST_CASE ( "ResourceTests::VerifyUniqueCall" , "[resource][unique_call]" )
{
unique_fake_call call1 ;
unique_fake_call call2 ;
// intentional compilation errors
// unique_fake_call call3 = call1;
// call2 = call1;
unique_fake_call call4 ;
REQUIRE ( ! called ());
unique_fake_call call5 ( wistd :: move ( call4 ));
REQUIRE ( ! call4 );
REQUIRE ( call5 );
call4 = wistd :: move ( call5 );
REQUIRE ( call4 );
REQUIRE ( ! call5 );
REQUIRE ( ! called ());
{
unique_fake_call scoped ;
}
REQUIRE ( called ());
call4 . reset ();
REQUIRE ( called ());
call4 . reset ();
REQUIRE ( ! called ());
call1 . release ();
REQUIRE (( ! call1 && call2 ));
call2 . swap ( call1 );
REQUIRE (( call1 && ! call2 ));
call2 . release ();
REQUIRE ( ! called ());
REQUIRE ( ! call2 );
#ifdef __WIL__ROAPI_H_APPEXCEPTIONAL
{
auto call = wil :: RoInitialize ();
}
#endif
#ifdef __WIL__ROAPI_H_APP
{
wil :: unique_rouninitialize_call uninit ;
uninit . release ();
auto call = wil :: RoInitialize_failfast ();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APPEXCEPTIONAL
{
auto call = wil :: CoInitializeEx ();
}
#endif
#ifdef __WIL__COMBASEAPI_H_APP
{
wil :: unique_couninitialize_call uninit ;
uninit . release ();
auto call = wil :: CoInitializeEx_failfast ();
}
#endif
}
void UniqueCallCompilationTest ()
{
#ifdef __WIL__COMBASEAPI_H_EXCEPTIONAL
{
auto call = wil :: CoImpersonateClient ();
}
#endif
#ifdef __WIL__COMBASEAPI_H_
{
wil :: unique_coreverttoself_call uninit ;
uninit . release ();
auto call = wil :: CoImpersonateClient_failfast ();
}
#endif
}
template < typename StringType , typename VerifyContents >
static void TestStringMaker ( VerifyContents && verifyContents )
{
PCWSTR values [] =
{
L "" ,
L "value" ,
// 300 chars
L "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
L "0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
};
for ( const auto & value : values )
{
auto const valueLength = wcslen ( value );
// Direct construction case.
wil :: details :: string_maker < StringType > maker ;
THROW_IF_FAILED ( maker . make ( value , valueLength ));
auto result = maker . release ();
verifyContents ( value , valueLength , result );
// Two phase construction case.
THROW_IF_FAILED ( maker . make ( nullptr , valueLength ));
REQUIRE ( maker . buffer () != nullptr );
// In the case of the wil::unique_hstring and the empty string the buffer is in a read only
// section and can't be written to, so StringCchCopy(maker.buffer(), valueLength + 1, value) will fault adding the nul terminator.
// Use memcpy_s specifying exact size that will be zero in this case instead.
memcpy_s ( maker . buffer (), valueLength * sizeof ( * value ), value , valueLength * sizeof ( * value ));
result = maker . release ();
verifyContents ( value , valueLength , result );
{
// no promote, ensure no leaks (not tested here, inspect in the debugger)
wil :: details :: string_maker < StringType > maker2 ;
THROW_IF_FAILED ( maker2 . make ( value , valueLength ));
}
}
}
#ifdef WIL_ENABLE_EXCEPTIONS
template < typename StringType >
static void VerifyMakeUniqueString ( bool nullValueSupported = true )
{
if ( nullValueSupported )
{
auto value0 = wil :: make_unique_string < StringType > ( nullptr , 5 );
}
struct
{
PCWSTR expectedValue ;
PCWSTR testValue ;
// this is an optional parameter
size_t testLength = static_cast < size_t > ( - 1 );
}
const testCaseEntries [] =
{
{ L "value" , L "value" , 5 },
{ L "value" , L "value" },
{ L "va" , L "va \0 ue" , 5 },
{ L "v" , L "value" , 1 },
{ L " \0 " , L "" , 5 },
{ L " \0 " , nullptr , 5 },
};
using maker = wil :: details :: string_maker < StringType > ;
for ( auto const & entry : testCaseEntries )
{
bool shouldSkipNullString = (( wcscmp ( entry . expectedValue , L " \0 " ) == 0 ) && ! nullValueSupported );
if ( ! shouldSkipNullString )
{
auto desiredValue = wil :: make_unique_string < StringType > ( entry . expectedValue );
auto stringValue = wil :: make_unique_string < StringType > ( entry . testValue , entry . testLength );
auto stringValueNoThrow = wil :: make_unique_string_nothrow < StringType > ( entry . testValue , entry . testLength );
auto stringValueFailFast = wil :: make_unique_string_failfast < StringType > ( entry . testValue , entry . testLength );
REQUIRE ( wcscmp ( maker :: get ( desiredValue ), maker :: get ( stringValue )) == 0 );
REQUIRE ( wcscmp ( maker :: get ( desiredValue ), maker :: get ( stringValueNoThrow )) == 0 );
REQUIRE ( wcscmp ( maker :: get ( desiredValue ), maker :: get ( stringValueFailFast )) == 0 );
}
}
}
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerCoTaskMem" , "[resource][string_maker]" )
{
VerifyMakeUniqueString < wil :: unique_cotaskmem_string > ();
TestStringMaker < wil :: unique_cotaskmem_string > (
[]( PCWSTR value , size_t /*valueLength*/ , const wil :: unique_cotaskmem_string & result )
{
REQUIRE ( wcscmp ( value , result . get ()) == 0 );
});
}
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerLocalAlloc" , "[resource][string_maker]" )
{
VerifyMakeUniqueString < wil :: unique_hlocal_string > ();
TestStringMaker < wil :: unique_hlocal_string > (
[]( PCWSTR value , size_t /*valueLength*/ , const wil :: unique_hlocal_string & result )
{
REQUIRE ( wcscmp ( value , result . get ()) == 0 );
});
}
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerGlobalAlloc" , "[resource][string_maker]" )
{
VerifyMakeUniqueString < wil :: unique_hglobal_string > ();
TestStringMaker < wil :: unique_hglobal_string > (
[]( PCWSTR value , size_t /*valueLength*/ , const wil :: unique_hglobal_string & result )
{
REQUIRE ( wcscmp ( value , result . get ()) == 0 );
});
}
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerProcessHeap" , "[resource][string_maker]" )
{
VerifyMakeUniqueString < wil :: unique_process_heap_string > ();
TestStringMaker < wil :: unique_process_heap_string > (
[]( PCWSTR value , size_t /*valueLength*/ , const wil :: unique_process_heap_string & result )
{
REQUIRE ( wcscmp ( value , result . get ()) == 0 );
});
}
#endif
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerMidl" , "[resource][string_maker]" )
{
VerifyMakeUniqueString < wil :: unique_midl_string > ();
TestStringMaker < wil :: unique_midl_string > (
[]( PCWSTR value , size_t /*valueLength*/ , const wil :: unique_midl_string & result )
{
REQUIRE ( wcscmp ( value , result . get ()) == 0 );
});
}
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerHString" , "[resource][string_maker]" )
{
wil :: unique_hstring value ;
value . reset ( static_cast < HSTRING > ( nullptr ));
VerifyMakeUniqueString < wil :: unique_hstring > ( false );
TestStringMaker < wil :: unique_hstring > (
[]( PCWSTR value , size_t valueLength , const wil :: unique_hstring & result )
{
UINT32 length ;
REQUIRE ( wcscmp ( value , WindowsGetStringRawBuffer ( result . get (), & length )) == 0 );
REQUIRE ( valueLength == length );
});
}
#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyStringMakerStdWString" , "[resource][string_maker]" )
{
std :: string s ;
wil :: details :: string_maker < std :: wstring > maker ;
TestStringMaker < std :: wstring > (
[]( PCWSTR value , size_t valueLength , const std :: wstring & result )
{
REQUIRE ( wcscmp ( value , result . c_str ()) == 0 );
REQUIRE ( result == value );
REQUIRE ( result . size () == valueLength );
});
}
#endif
TEST_CASE ( "UniqueStringAndStringMakerTests::VerifyLegacySTringMakers" , "[resource][string_maker]" )
{
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
auto l = wil :: make_hlocal_string ( L "value" );
l = wil :: make_hlocal_string_nothrow ( L "value" );
l = wil :: make_hlocal_string_failfast ( L "value" );
auto p = wil :: make_process_heap_string ( L "value" );
p = wil :: make_process_heap_string_nothrow ( L "value" );
p = wil :: make_process_heap_string_failfast ( L "value" );
#endif
auto c = wil :: make_cotaskmem_string ( L "value" );
c = wil :: make_cotaskmem_string_nothrow ( L "value" );
c = wil :: make_cotaskmem_string_failfast ( L "value" );
}
#endif
_Use_decl_annotations_ void * __RPC_USER MIDL_user_allocate ( size_t size )
{
return :: HeapAlloc ( GetProcessHeap (), 0 , size );
}
_Use_decl_annotations_ void __RPC_USER MIDL_user_free ( void * p )
{
:: HeapFree ( GetProcessHeap (), 0 , p );
}
TEST_CASE ( "UniqueMidlStringTests" , "[resource][rpc]" )
{
wil :: unique_midl_ptr < int [] > intArray { reinterpret_cast < int *> ( :: MIDL_user_allocate ( sizeof ( int ) * 10 )) };
intArray [ 2 ] = 1 ;
wil :: unique_midl_ptr < int > intSingle { reinterpret_cast < int *> ( :: MIDL_user_allocate ( sizeof ( int ) * 1 )) };
}
TEST_CASE ( "UniqueEnvironmentStrings" , "[resource][win32]" )
{
wil :: unique_environstrings_ptr env { :: GetEnvironmentStringsW () };
const wchar_t * nextVar = env . get ();
while ( nextVar &&* nextVar )
{
// consume 'nextVar'
nextVar += wcslen ( nextVar ) + 1 ;
}
wil :: unique_environansistrings_ptr envAnsi { :: GetEnvironmentStringsA () };
const char * nextVarAnsi = envAnsi . get ();
while ( nextVarAnsi && * nextVarAnsi )
{
// consume 'nextVar'
nextVarAnsi += strlen ( nextVarAnsi ) + 1 ;
}
}
TEST_CASE ( "UniqueVariant" , "[resource][com]" )
{
wil :: unique_variant var ;
var . vt = VT_BSTR ;
var . bstrVal = :: SysAllocString ( L "25" );
REQUIRE ( var . bstrVal != nullptr );
auto call = []( const VARIANT & ) {};
call ( var );
VARIANT weakVar = var ;
( void ) weakVar ;
wil :: unique_variant var2 ;
REQUIRE_SUCCEEDED ( VariantChangeType ( & var2 , & var , 0 , VT_UI4 ));
REQUIRE ( var2 . vt == VT_UI4 );
REQUIRE ( var2 . uiVal == 25 );
}
TEST_CASE ( "DefaultTemplateParamCompiles" , "[resource]" )
{
wil :: unique_process_heap_ptr <> a ;
wil :: unique_virtualalloc_ptr <> b ;
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
wil :: unique_hlocal_ptr <> c ;
wil :: unique_hlocal_secure_ptr <> d ;
wil :: unique_hglobal_ptr <> e ;
wil :: unique_cotaskmem_secure_ptr <> f ;
#endif
wil :: unique_midl_ptr <> g ;
wil :: unique_cotaskmem_ptr <> h ;
2023-02-22 13:12:56 -08:00
wil :: unique_mapview_ptr <> i ;
}
TEST_CASE ( "UniqueInvokeCleanupMembers" , "[resource]" )
{
// Case 1 - unique_ptr<> for a T* that has a "destroy" member
struct ThingWithDestroy
{
bool destroyed = false ;
void destroy () { destroyed = true ; };
};
ThingWithDestroy toDestroy ;
wil :: unique_any < ThingWithDestroy * , decltype ( & ThingWithDestroy :: destroy ), & ThingWithDestroy :: destroy > p ( & toDestroy );
p . reset ();
REQUIRE ( ! p );
REQUIRE ( toDestroy . destroyed );
// Case 2 - unique_struct calling a member, like above
struct ThingToDestroy2
{
bool * destroyed ;
void destroy () { * destroyed = true ; };
};
bool structDestroyed = false ;
{
wil :: unique_struct < ThingToDestroy2 , decltype ( & ThingToDestroy2 :: destroy ), & ThingToDestroy2 :: destroy > other ;
other . destroyed = & structDestroyed ;
REQUIRE ( ! structDestroyed );
}
REQUIRE ( structDestroyed );
}
struct ITokenTester : IUnknown
{
virtual void DirectClose ( DWORD_PTR token ) = 0 ;
};
struct TokenTester : ITokenTester
{
IFACEMETHOD_ ( ULONG , AddRef )() override { return 2 ; }
IFACEMETHOD_ ( ULONG , Release )() override { return 1 ; }
IFACEMETHOD ( QueryInterface )( REFIID , void ** ) { return E_NOINTERFACE ; }
void DirectClose ( DWORD_PTR token ) override {
m_closed = ( token == m_closeToken );
}
bool m_closed = false ;
DWORD_PTR m_closeToken ;
};
void MyTokenTesterCloser ( ITokenTester * tt , DWORD_PTR token )
{
tt -> DirectClose ( token );
}
TEST_CASE ( "ComTokenCloser" , "[resource]" )
{
using token_tester_t = wil :: unique_com_token < ITokenTester , DWORD_PTR , decltype ( MyTokenTesterCloser ), & MyTokenTesterCloser > ;
TokenTester tt ;
tt . m_closeToken = 4 ;
{
token_tester_t tmp { & tt , 4 };
}
REQUIRE ( tt . m_closed );
}
TEST_CASE ( "ComTokenDirectCloser" , "[resource]" )
{
using token_tester_t = wil :: unique_com_token < ITokenTester , DWORD_PTR , decltype ( & ITokenTester :: DirectClose ), & ITokenTester :: DirectClose > ;
TokenTester tt ;
tt . m_closeToken = 4 ;
{
token_tester_t tmp { & tt , 4 };
}
REQUIRE ( tt . m_closed );
}
TEST_CASE ( "UniqueCloseClipboardCall" , "[resource]" )
{
#if defined(__WIL__WINUSER_) && !defined(NOCLIPBOARD)
if ( auto clip = wil :: open_clipboard ( nullptr ))
{
REQUIRE ( :: EmptyClipboard ());
}
#endif
2019-11-01 00:09:52 +01:00
}