| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #pragma once | ||
|
|
||
| #include "catch.hpp" | ||
| #include <objbase.h> | ||
| #include <wil/wistd_functional.h> | ||
| #include <wrl/implements.h> | ||
|
|
||
| #if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP | WINAPI_PARTITION_SYSTEM) | ||
|
|
||
| // IMallocSpy requires you to implement all methods, but we often only want one or two... | ||
| struct MallocSpy : Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, IMallocSpy> | ||
| { | ||
| wistd::function<SIZE_T(SIZE_T)> PreAllocCallback; | ||
| virtual SIZE_T STDMETHODCALLTYPE PreAlloc(SIZE_T requestSize) override | ||
| { | ||
| if (PreAllocCallback) | ||
| { | ||
| return PreAllocCallback(requestSize); | ||
| } | ||
|
|
||
| return requestSize; | ||
| } | ||
|
|
||
| wistd::function<void*(void*)> PostAllocCallback; | ||
| virtual void* STDMETHODCALLTYPE PostAlloc(void* ptr) override | ||
| { | ||
| if (PostAllocCallback) | ||
| { | ||
| return PostAllocCallback(ptr); | ||
| } | ||
|
|
||
| return ptr; | ||
| } | ||
|
|
||
| wistd::function<void*(void*)> PreFreeCallback; | ||
| virtual void* STDMETHODCALLTYPE PreFree(void* ptr, BOOL wasSpyed) override | ||
| { | ||
| if (wasSpyed && PreFreeCallback) | ||
| { | ||
| return PreFreeCallback(ptr); | ||
| } | ||
|
|
||
| return ptr; | ||
| } | ||
|
|
||
| virtual void STDMETHODCALLTYPE PostFree(BOOL /*wasSpyed*/) override | ||
| { | ||
| } | ||
|
|
||
| wistd::function<SIZE_T(void*, SIZE_T, void**)> PreReallocCallback; | ||
| virtual SIZE_T STDMETHODCALLTYPE PreRealloc(void* ptr, SIZE_T requestSize, void** newPtr, BOOL wasSpyed) override | ||
| { | ||
| *newPtr = ptr; | ||
| if (wasSpyed && PreReallocCallback) | ||
| { | ||
| return PreReallocCallback(ptr, requestSize, newPtr); | ||
| } | ||
|
|
||
| return requestSize; | ||
| } | ||
|
|
||
| wistd::function<void*(void*)> PostReallocCallback; | ||
| virtual void* STDMETHODCALLTYPE PostRealloc(void* ptr, BOOL wasSpyed) override | ||
| { | ||
| if (wasSpyed && PostReallocCallback) | ||
| { | ||
| return PostReallocCallback(ptr); | ||
| } | ||
|
|
||
| return ptr; | ||
| } | ||
|
|
||
| wistd::function<void*(void*)> PreGetSizeCallback; | ||
| virtual void* STDMETHODCALLTYPE PreGetSize(void* ptr, BOOL wasSpyed) override | ||
| { | ||
| if (wasSpyed && PreGetSizeCallback) | ||
| { | ||
| return PreGetSizeCallback(ptr); | ||
| } | ||
|
|
||
| return ptr; | ||
| } | ||
|
|
||
| wistd::function<SIZE_T(SIZE_T)> PostGetSizeCallback; | ||
| virtual SIZE_T STDMETHODCALLTYPE PostGetSize(SIZE_T size, BOOL wasSpyed) override | ||
| { | ||
| if (wasSpyed && PostGetSizeCallback) | ||
| { | ||
| return PostGetSizeCallback(size); | ||
| } | ||
|
|
||
| return size; | ||
| } | ||
|
|
||
| wistd::function<void*(void*)> PreDidAllocCallback; | ||
| virtual void* STDMETHODCALLTYPE PreDidAlloc(void* ptr, BOOL wasSpyed) override | ||
| { | ||
| if (wasSpyed && PreDidAllocCallback) | ||
| { | ||
| return PreDidAllocCallback(ptr); | ||
| } | ||
|
|
||
| return ptr; | ||
| } | ||
|
|
||
| virtual int STDMETHODCALLTYPE PostDidAlloc(void* /*ptr*/, BOOL /*wasSpyed*/, int result) override | ||
| { | ||
| return result; | ||
| } | ||
|
|
||
| virtual void STDMETHODCALLTYPE PreHeapMinimize() override | ||
| { | ||
| } | ||
|
|
||
| virtual void STDMETHODCALLTYPE PostHeapMinimize() override | ||
| { | ||
| } | ||
| }; | ||
|
|
||
| Microsoft::WRL::ComPtr<MallocSpy> MakeSecureDeleterMallocSpy() | ||
| { | ||
| using namespace Microsoft::WRL; | ||
| auto result = Make<MallocSpy>(); | ||
| REQUIRE(result); | ||
|
|
||
| result->PreFreeCallback = [](void* ptr) | ||
| { | ||
| ComPtr<IMalloc> malloc; | ||
| if (SUCCEEDED(::CoGetMalloc(1, &malloc))) | ||
| { | ||
| auto size = malloc->GetSize(ptr); | ||
| auto buffer = static_cast<byte*>(ptr); | ||
| for (size_t i = 0; i < size; ++i) | ||
| { | ||
| REQUIRE(buffer[i] == 0); | ||
| } | ||
| } | ||
|
|
||
| return ptr; | ||
| }; | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,137 @@ | ||
| #include "common.h" | ||
|
|
||
| #include <wil/rpc_helpers.h> | ||
|
|
||
| void RpcMethodReturnsVoid(ULONG toRaise) | ||
| { | ||
| if (toRaise) | ||
| { | ||
| RaiseException(toRaise, 0, 0, nullptr); | ||
| } | ||
| } | ||
|
|
||
| struct FOO_CONTEXT_T {}; | ||
| typedef FOO_CONTEXT_T* FOO_CONTEXT; | ||
| typedef FOO_CONTEXT* PFOO_CONTEXT; | ||
|
|
||
| void CloseContextHandle(_Inout_ PFOO_CONTEXT) | ||
| { | ||
| } | ||
|
|
||
| void CloseContextHandleRaise(_Inout_ PFOO_CONTEXT) | ||
| { | ||
| return RpcMethodReturnsVoid(RPC_X_BAD_STUB_DATA); | ||
| } | ||
|
|
||
| HRESULT AcquireContextHandle(_In_ handle_t binding, _Out_ PFOO_CONTEXT context) | ||
| { | ||
| *context = reinterpret_cast<FOO_CONTEXT>(binding); | ||
| return S_OK; | ||
| } | ||
|
|
||
| HRESULT RpcMethodReturnsHResult(HRESULT toReturn, ULONG toRaise) | ||
| { | ||
| RpcMethodReturnsVoid(toRaise); | ||
| return toReturn; | ||
| } | ||
|
|
||
| GUID RpcMethodReturnsGuid(ULONG toRaise) | ||
| { | ||
| RpcMethodReturnsVoid(toRaise); | ||
| return __uuidof(IUnknown); | ||
| } | ||
|
|
||
| TEST_CASE("Rpc::NonThrowing", "[rpc]") | ||
| { | ||
| SECTION("Success paths") | ||
| { | ||
| REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, 0UL)); | ||
| REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, S_OK, 0UL)); | ||
|
|
||
| GUID tmp{}; | ||
| REQUIRE_SUCCEEDED(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, 0UL)); | ||
| REQUIRE(tmp == __uuidof(IUnknown)); | ||
| } | ||
|
|
||
| SECTION("Failures in the method") | ||
| { | ||
| REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, 0) == E_CHANGED_STATE); | ||
| } | ||
|
|
||
| SECTION("Failures in the fabric") | ||
| { | ||
| REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsVoid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); | ||
| REQUIRE(wil::invoke_rpc_nothrow(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); | ||
|
|
||
| GUID tmp{}; | ||
| REQUIRE(wil::invoke_rpc_result_nothrow(tmp, RpcMethodReturnsGuid, RPC_S_CALL_FAILED) == HRESULT_FROM_WIN32(RPC_S_CALL_FAILED)); | ||
| } | ||
|
|
||
| SECTION("Context Handles") | ||
| { | ||
| using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandle), CloseContextHandle>; | ||
| foo_context_t ctx; | ||
| auto tempBinding = reinterpret_cast<handle_t>(-5); | ||
| REQUIRE_SUCCEEDED(wil::invoke_rpc_nothrow(AcquireContextHandle, tempBinding, ctx.put())); | ||
| REQUIRE(ctx.get() == reinterpret_cast<FOO_CONTEXT>(tempBinding)); | ||
| ctx.reset(); | ||
| } | ||
|
|
||
| SECTION("Context Handles Close Raised") | ||
| { | ||
| using foo_context_t = wil::unique_rpc_context_handle<FOO_CONTEXT, decltype(&CloseContextHandleRaise), CloseContextHandleRaise>; | ||
| foo_context_t ctx{ reinterpret_cast<FOO_CONTEXT>(42) }; | ||
| ctx.reset(); | ||
| } | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
|
|
||
| #include <sstream> | ||
|
|
||
| class WilExceptionMatcher : public Catch::MatcherBase<wil::ResultException> | ||
| { | ||
| HRESULT m_expected; | ||
| public: | ||
| WilExceptionMatcher(HRESULT ex) : m_expected(ex) { } | ||
|
|
||
| bool match(wil::ResultException const& ex) const override { | ||
| return ex.GetErrorCode() == m_expected; | ||
| } | ||
|
|
||
| std::string describe() const override { | ||
| std::ostringstream ss; | ||
| ss << "wil::ResultException expects code 0x%08lx" << std::hex << m_expected; | ||
| return ss.str(); | ||
| } | ||
| }; | ||
|
|
||
| #define REQUIRE_THROWS_WIL_HR(hr, expr) REQUIRE_THROWS_MATCHES(expr, wil::ResultException, WilExceptionMatcher(hr)) | ||
|
|
||
| TEST_CASE("Rpc::Throwing", "[rpc]") | ||
| { | ||
| SECTION("Success paths") | ||
| { | ||
| REQUIRE_NOTHROW(wil::invoke_rpc(RpcMethodReturnsVoid, 0UL)); | ||
|
|
||
| GUID tmp{}; | ||
| REQUIRE_NOTHROW(tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, 0UL)); | ||
| REQUIRE(tmp == __uuidof(IUnknown)); | ||
| } | ||
|
|
||
| SECTION("Failures in the method") | ||
| { | ||
| REQUIRE_THROWS_WIL_HR(E_CHANGED_STATE, wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, 0UL)); | ||
| } | ||
|
|
||
| SECTION("Failures in the fabric") | ||
| { | ||
| REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsVoid, RPC_S_CALL_FAILED)); | ||
| REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), wil::invoke_rpc(RpcMethodReturnsHResult, E_CHANGED_STATE, RPC_S_CALL_FAILED)); | ||
|
|
||
| GUID tmp{}; | ||
| REQUIRE_THROWS_WIL_HR(HRESULT_FROM_WIN32(RPC_S_CALL_FAILED), tmp = wil::invoke_rpc_result(RpcMethodReturnsGuid, RPC_S_CALL_FAILED)); | ||
| REQUIRE(tmp == GUID{}); | ||
| } | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
|
|
||
| #include <wil/stl.h> | ||
|
|
||
| #include "common.h" | ||
|
|
||
| #ifndef WIL_ENABLE_EXCEPTIONS | ||
| #error STL tests require exceptions | ||
| #endif | ||
|
|
||
| struct dummy | ||
| { | ||
| char value; | ||
| }; | ||
|
|
||
| // Specialize std::allocator<> so that we don't actually allocate/deallocate memory | ||
| dummy g_memoryBuffer[256]; | ||
| namespace std | ||
| { | ||
| template <> | ||
| struct allocator<dummy> | ||
| { | ||
| using value_type = dummy; | ||
| using size_type = std::size_t; | ||
| using difference_type = std::ptrdiff_t; | ||
|
|
||
| dummy* allocate(std::size_t count) | ||
| { | ||
| REQUIRE(count <= std::size(g_memoryBuffer)); | ||
| return g_memoryBuffer; | ||
| } | ||
|
|
||
| void deallocate(dummy* ptr, std::size_t count) | ||
| { | ||
| for (std::size_t i = 0; i < count; ++i) | ||
| { | ||
| REQUIRE(ptr[i].value == 0); | ||
| } | ||
| } | ||
| }; | ||
| } | ||
|
|
||
| TEST_CASE("StlTests::TestSecureAllocator", "[stl][secure_allocator]") | ||
| { | ||
| { | ||
| wil::secure_vector<dummy> sensitiveBytes(32, dummy{ 'a' }); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,320 @@ | ||
|
|
||
| #include <wil/token_helpers.h> | ||
|
|
||
| #include "common.h" | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessTokenNoThrow", "[token_helpers]") | ||
| { | ||
| // Open current thread/process access token | ||
| wil::unique_handle token; | ||
| REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token)); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ)); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Current)); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| REQUIRE_SUCCEEDED(wil::open_current_access_token_nothrow(&token, TOKEN_READ, wil::OpenThreadTokenAs::Self)); | ||
| REQUIRE(token != nullptr); | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| TEST_CASE("TokenHelpersTests::VerifyOpenCurrentAccessToken", "[token_helpers]") | ||
| { | ||
| // Open current thread/process access token | ||
| wil::unique_handle token(wil::open_current_access_token()); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| token = wil::open_current_access_token(TOKEN_READ); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Current); | ||
| REQUIRE(token != nullptr); | ||
|
|
||
| token = wil::open_current_access_token(TOKEN_READ, wil::OpenThreadTokenAs::Self); | ||
| REQUIRE(token != nullptr); | ||
| } | ||
| #endif | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationNoThrow", "[token_helpers]") | ||
| { | ||
| SECTION("Passing a null token") | ||
| { | ||
| wistd::unique_ptr<TOKEN_USER> tokenInfo; | ||
| REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, nullptr)); | ||
| REQUIRE(tokenInfo != nullptr); | ||
| } | ||
|
|
||
| SECTION("Passing a non null token, since it a fake token there is no tokenInfo and hence should fail, code path is correct") | ||
| { | ||
| HANDLE faketoken = GetStdHandle(STD_INPUT_HANDLE); | ||
| wistd::unique_ptr<TOKEN_USER> tokenInfo; | ||
| REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, faketoken)); | ||
| } | ||
| } | ||
|
|
||
| // Pseudo tokens can be passed to token APIs and avoid the handle allocations | ||
| // making use more efficient. | ||
| TEST_CASE("TokenHelpersTests::DemonstrateUseWithPseudoTokens", "[token_helpers]") | ||
| { | ||
| wistd::unique_ptr<TOKEN_USER> tokenInfo; | ||
| REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentProcessToken())); | ||
| REQUIRE(tokenInfo != nullptr); | ||
|
|
||
| REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadEffectiveToken())); | ||
| REQUIRE(tokenInfo != nullptr); | ||
|
|
||
| // No thread token by default, this should fail | ||
| REQUIRE_FAILED(wil::get_token_information_nothrow(tokenInfo, GetCurrentThreadToken())); | ||
| REQUIRE(tokenInfo == nullptr); | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation", "[token_helpers]") | ||
| { | ||
| // Passing a null token | ||
| wistd::unique_ptr<TOKEN_USER> tokenInfo(wil::get_token_information<TOKEN_USER>(nullptr)); | ||
| REQUIRE(tokenInfo != nullptr); | ||
| } | ||
| #endif | ||
|
|
||
| // This fails with 'ERROR_NO_SUCH_LOGON_SESSION' on the CI machines, so disable | ||
| #ifndef WIL_FAST_BUILD | ||
| TEST_CASE("TokenHelpersTests::VerifyLinkedToken", "[token_helpers]") | ||
| { | ||
| wil::unique_token_linked_token theToken; | ||
| REQUIRE_SUCCEEDED(wil::get_token_information_nothrow(theToken, nullptr)); | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| REQUIRE_NOTHROW(wil::get_linked_token_information()); | ||
| #endif | ||
| } | ||
| #endif | ||
|
|
||
| bool IsImpersonating() | ||
| { | ||
| wil::unique_handle token; | ||
| if (!::OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token)) | ||
| { | ||
| WI_ASSERT(::GetLastError() == ERROR_NO_TOKEN); | ||
| return false; | ||
| } | ||
|
|
||
| return true; | ||
| } | ||
|
|
||
| wil::unique_handle GetTokenToImpersonate() | ||
| { | ||
| wil::unique_handle processToken; | ||
| FAIL_FAST_IF_WIN32_BOOL_FALSE(::OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &processToken)); | ||
|
|
||
| wil::unique_handle impersonateToken; | ||
| FAIL_FAST_IF_WIN32_BOOL_FALSE(::DuplicateToken(processToken.get(), SecurityImpersonation, &impersonateToken)); | ||
|
|
||
| return impersonateToken; | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyResetThreadTokenNoThrow", "[token_helpers]") | ||
| { | ||
| auto impersonateToken = GetTokenToImpersonate(); | ||
|
|
||
| // Set the thread into a known state - no token. | ||
| wil::unique_token_reverter clearThreadToken; | ||
| REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadToken)); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
|
|
||
| // Set a token on the thread - the process token, guaranteed to be friendly | ||
| wil::unique_token_reverter setThreadToken1; | ||
| REQUIRE_SUCCEEDED(wil::impersonate_token_nothrow(impersonateToken.get(), setThreadToken1)); | ||
| REQUIRE(IsImpersonating()); | ||
|
|
||
| SECTION("Clear the token again, should be not impersonating, explicit reset") | ||
| { | ||
| wil::unique_token_reverter clearThreadAgain; | ||
| REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain)); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| clearThreadAgain.reset(); | ||
| REQUIRE(IsImpersonating()); | ||
| } | ||
|
|
||
| SECTION("Clear the token again, should be not impersonating, dtor reset") | ||
| { | ||
| wil::unique_token_reverter clearThreadAgain; | ||
| REQUIRE_SUCCEEDED(wil::run_as_self_nothrow(clearThreadAgain)); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| } | ||
| REQUIRE(IsImpersonating()); | ||
|
|
||
| // Clear what we were impersonating | ||
| setThreadToken1.reset(); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| TEST_CASE("TokenHelpersTests::VerifyResetThreadToken", "[token_helpers]") | ||
| { | ||
| auto impersonateToken = GetTokenToImpersonate(); | ||
|
|
||
| // Set the thread into a known state - no token. | ||
| auto clearThreadToken = wil::run_as_self(); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
|
|
||
| // Set a token on the thread - the process token, guaranteed to be friendly | ||
| auto setThreadToken1 = wil::impersonate_token(impersonateToken.get()); | ||
| REQUIRE(IsImpersonating()); | ||
|
|
||
| SECTION("Clear the token again, should be not impersonating, explicit reset") | ||
| { | ||
| auto clearThreadAgain = wil::run_as_self(); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| clearThreadAgain.reset(); | ||
| REQUIRE(IsImpersonating()); | ||
| } | ||
|
|
||
| SECTION("Clear the token again, should be not impersonating, dtor reset") | ||
| { | ||
| auto clearThreadAgain = wil::run_as_self(); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| } | ||
| REQUIRE(IsImpersonating()); | ||
|
|
||
| // Clear what we were impersonating | ||
| setThreadToken1.reset(); | ||
| REQUIRE_FALSE(IsImpersonating()); | ||
| } | ||
| #endif // WIL_ENABLE_EXCEPTIONS | ||
|
|
||
| template <typename T, wistd::enable_if_t<!wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> | ||
| void TestGetTokenInfoForCurrentThread() | ||
| { | ||
| wistd::unique_ptr<T> tokenInfo; | ||
| const auto hr = wil::get_token_information_nothrow(tokenInfo, nullptr); | ||
| REQUIRE(S_OK == hr); | ||
| } | ||
|
|
||
| template <typename T, wistd::enable_if_t<wil::details::MapTokenStructToInfoClass<T>::FixedSize>* = nullptr> | ||
| void TestGetTokenInfoForCurrentThread() | ||
| { | ||
| T tokenInfo{}; | ||
| const auto hr = wil::get_token_information_nothrow(&tokenInfo, nullptr); | ||
| REQUIRE(S_OK == hr); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInformation2", "[token_helpers]") | ||
| { | ||
| // Variable sized cases | ||
| TestGetTokenInfoForCurrentThread<TOKEN_ACCESS_INFORMATION>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_APPCONTAINER_INFORMATION>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_DEFAULT_DACL>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_GROUPS_AND_PRIVILEGES>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_LABEL>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_OWNER>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_PRIMARY_GROUP>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_PRIVILEGES>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_USER>(); | ||
|
|
||
| // Fixed size and reports size using ERROR_INSUFFICIENT_BUFFER (perf opportunity, ignore second allocation) | ||
| TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION_TYPE>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_MANDATORY_POLICY>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_ORIGIN>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_SOURCE>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_STATISTICS>(); | ||
| TestGetTokenInfoForCurrentThread<TOKEN_TYPE>(); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationBadLength", "[token_helpers]") | ||
| { | ||
| // Fixed size and reports size using ERROR_BAD_LENGTH (bug) | ||
| TestGetTokenInfoForCurrentThread<TOKEN_ELEVATION>(); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInformationSecurityImpersonationLevelErrorCases", "[token_helpers]") | ||
| { | ||
| SECURITY_IMPERSONATION_LEVEL tokenInfo{}; | ||
|
|
||
| // SECURITY_IMPERSONATION_LEVEL does not support the effective token when it is implicit. | ||
| // Demonstrate the error return in that case. | ||
| REQUIRE(E_INVALIDARG == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadEffectiveToken())); | ||
|
|
||
| // Using an explicit token is supported but returns ERROR_NO_TOKEN when there is no | ||
| // impersonation token be sure to use RETURN_IF_FAILED_EXPECTED() and don't use | ||
| // the exception forms if this case is not expected. | ||
| REQUIRE(HRESULT_FROM_WIN32(ERROR_NO_TOKEN) == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken())); | ||
|
|
||
| // Setup the impersonation token that SECURITY_IMPERSONATION_LEVEL requires. | ||
| FAIL_FAST_IF_WIN32_BOOL_FALSE(ImpersonateSelf(SecurityIdentification)); | ||
| TestGetTokenInfoForCurrentThread<SECURITY_IMPERSONATION_LEVEL>(); | ||
|
|
||
| REQUIRE(S_OK == wil::get_token_information_nothrow(&tokenInfo, GetCurrentThreadToken())); | ||
|
|
||
| RevertToSelf(); | ||
| } | ||
|
|
||
| bool operator==(const SID_IDENTIFIER_AUTHORITY& left, const SID_IDENTIFIER_AUTHORITY& right) | ||
| { | ||
| return memcmp(&left, &right, sizeof(left)) == 0; | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::StaticSid", "[token_helpers]") | ||
| { | ||
| SID_IDENTIFIER_AUTHORITY ntAuthority = SECURITY_NT_AUTHORITY; | ||
| auto staticSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS); | ||
| auto largerSid = wil::make_static_sid(SECURITY_NT_AUTHORITY, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_GUESTS, DOMAIN_ALIAS_RID_BACKUP_OPS); | ||
|
|
||
| largerSid = staticSid; | ||
| largerSid = largerSid; | ||
| // staticSid = largerSid; // Uncommenting this correctly fails to compile. | ||
|
|
||
| REQUIRE(IsValidSid(staticSid.get())); | ||
| REQUIRE(*GetSidSubAuthorityCount(staticSid.get()) == 2); | ||
| REQUIRE(*GetSidIdentifierAuthority(staticSid.get()) == ntAuthority); | ||
| REQUIRE(*GetSidSubAuthority(staticSid.get(), 0) == SECURITY_BUILTIN_DOMAIN_RID); | ||
| REQUIRE(*GetSidSubAuthority(staticSid.get(), 1) == DOMAIN_ALIAS_RID_GUESTS); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::TestMembership", "[token_helpers]") | ||
| { | ||
| bool member; | ||
| REQUIRE_SUCCEEDED(wil::test_token_membership_nothrow( | ||
| &member, | ||
| GetCurrentThreadEffectiveToken(), | ||
| SECURITY_NT_AUTHORITY, | ||
| SECURITY_AUTHENTICATED_USER_RID)); | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInfo", "[token_helpers]") | ||
| { | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_APPCONTAINER_INFORMATION>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION_TYPE>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_MANDATORY_POLICY>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ORIGIN>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_STATISTICS>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>()); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_ELEVATION>()); | ||
|
|
||
| // check a non-pointer size value to make sure the whole struct is returned. | ||
| DWORD resultSize{}; | ||
| TOKEN_SOURCE ts{}; | ||
| auto tokenSource = wil::get_token_information<TOKEN_SOURCE>(); | ||
| GetTokenInformation(GetCurrentThreadEffectiveToken(), TokenSource, &ts, sizeof(ts), &resultSize); | ||
| REQUIRE(memcmp(&ts, &tokenSource, sizeof(ts)) == 0); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::VerifyGetTokenInfoFailFast", "[token_helpers]") | ||
| { | ||
| // fixed size | ||
| REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_APPCONTAINER_INFORMATION>()); | ||
| // variable size | ||
| REQUIRE_NOTHROW(wil::get_token_information_failfast<TOKEN_OWNER>()); | ||
| } | ||
|
|
||
| TEST_CASE("TokenHelpersTests::Verify_impersonate_token", "[token_helpers]") | ||
| { | ||
| auto impersonationToken = wil::impersonate_token(); | ||
| REQUIRE_NOTHROW(wil::get_token_information<TOKEN_TYPE>()); | ||
| } | ||
| #endif // WIL_ENABLE_EXCEPTIONS |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,266 @@ | ||
|
|
||
| #include <wil/winrt.h> | ||
| #include <wrl/implements.h> | ||
|
|
||
| #include "common.h" | ||
|
|
||
| using namespace ABI::Windows::Foundation; | ||
| using namespace Microsoft::WRL; | ||
|
|
||
| namespace wiltest | ||
| { | ||
| class AbiTestEventSender WrlFinal : public RuntimeClass< | ||
| RuntimeClassFlags<RuntimeClassType::WinRtClassicComMix>, | ||
| IClosable, | ||
| IMemoryBufferReference, | ||
| FtmBase> | ||
| { | ||
| public: | ||
|
|
||
| // IMemoryBufferReference | ||
| IFACEMETHODIMP get_Capacity(_Out_ UINT32* value) | ||
| { | ||
| *value = 0; | ||
| return S_OK; | ||
| } | ||
|
|
||
| IFACEMETHODIMP add_Closed( | ||
| _In_ ITypedEventHandler<IMemoryBufferReference*, IInspectable*>* handler, | ||
| _Out_ ::EventRegistrationToken* token) | ||
| { | ||
| return m_closedEvent.Add(handler, token); | ||
| } | ||
|
|
||
| IFACEMETHODIMP remove_Closed(::EventRegistrationToken token) | ||
| { | ||
| return m_closedEvent.Remove(token); | ||
| } | ||
|
|
||
| // IClosable | ||
| IFACEMETHODIMP Close() | ||
| { | ||
| RETURN_IF_FAILED(m_closedEvent.InvokeAll(this, nullptr)); | ||
| return S_OK; | ||
| } | ||
|
|
||
| private: | ||
| Microsoft::WRL::EventSource<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>> m_closedEvent; | ||
| }; | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEventSubscribe", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| { | ||
| wil::unique_winrt_event_token<IMemoryBufferReference> token; | ||
| REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); | ||
| REQUIRE(static_cast<bool>(token)); | ||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
| } | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenEarlyReset", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> token; | ||
| REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); | ||
| REQUIRE(static_cast<bool>(token)); | ||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
|
|
||
| token.reset(); | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveTokenToDifferentScope", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> outerToken; | ||
| REQUIRE_FALSE(static_cast<bool>(outerToken)); | ||
| { | ||
| wil::unique_winrt_event_token<IMemoryBufferReference> token; | ||
| REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &token)); | ||
| REQUIRE(static_cast<bool>(token)); | ||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
|
|
||
| outerToken = std::move(token); | ||
| REQUIRE_FALSE(static_cast<bool>(token)); | ||
| REQUIRE(static_cast<bool>(outerToken)); | ||
| } | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 2); | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenMoveConstructor", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> firstToken; | ||
| REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); | ||
| REQUIRE(static_cast<bool>(firstToken)); | ||
| closable->Close(); | ||
| REQUIRE(timesInvoked == 1); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> secondToken(std::move(firstToken)); | ||
| REQUIRE_FALSE(static_cast<bool>(firstToken)); | ||
| REQUIRE(static_cast<bool>(secondToken)); | ||
|
|
||
| closable->Close(); | ||
| REQUIRE(timesInvoked == 2); | ||
|
|
||
| firstToken.reset(); | ||
| closable->Close(); | ||
| REQUIRE(timesInvoked == 3); | ||
|
|
||
| secondToken.reset(); | ||
| closable->Close(); | ||
| REQUIRE(timesInvoked == 3); | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenReleaseAndReattachToNewWrapper", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> firstToken; | ||
| REQUIRE_SUCCEEDED(WI_MakeUniqueWinRtEventTokenNoThrow(Closed, testEventSender.Get(), handler.Get(), &firstToken)); | ||
| REQUIRE(static_cast<bool>(firstToken)); | ||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 1); | ||
|
|
||
| ::EventRegistrationToken rawToken = firstToken.release(); | ||
| REQUIRE_FALSE(static_cast<bool>(firstToken)); | ||
| REQUIRE(rawToken.value != 0); | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 2); | ||
|
|
||
| wil::unique_winrt_event_token<IMemoryBufferReference> secondToken( | ||
| rawToken, testEventSender.Get(), &IMemoryBufferReference::remove_Closed); | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 3); | ||
|
|
||
| secondToken.reset(); | ||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
| REQUIRE(timesInvoked == 3); | ||
| } | ||
|
|
||
| TEST_CASE("UniqueWinRTEventTokenTests::AbiUniqueWinrtEventTokenPolicyVariants", "[winrt][unique_winrt_event_token]") | ||
| { | ||
| ComPtr<IMemoryBufferReference> testEventSender; | ||
| REQUIRE_SUCCEEDED(MakeAndInitialize<wiltest::AbiTestEventSender>(&testEventSender)); | ||
| ComPtr<IClosable> closable; | ||
| testEventSender.As(&closable); | ||
|
|
||
| int timesInvoked = 0; | ||
| auto handler = Callback<Implements<RuntimeClassFlags<ClassicCom>, | ||
| ITypedEventHandler<IMemoryBufferReference*, IInspectable*>, FtmBase>> | ||
| ([×Invoked](IInspectable*, IInspectable*) | ||
| { | ||
| timesInvoked++; | ||
| return S_OK; | ||
| }); | ||
| REQUIRE(timesInvoked == 0); | ||
|
|
||
| { | ||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| auto exceptionPolicyToken = WI_MakeUniqueWinRtEventToken(Closed, testEventSender.Get(), handler.Get()); | ||
| REQUIRE(static_cast<bool>(exceptionPolicyToken)); | ||
| #endif | ||
|
|
||
| auto failFastPolicyToken = WI_MakeUniqueWinRtEventTokenFailFast(Closed, testEventSender.Get(), handler.Get()); | ||
| REQUIRE(static_cast<bool>(failFastPolicyToken)); | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| REQUIRE(timesInvoked == 2); | ||
| #else | ||
| REQUIRE(timesInvoked == 1); | ||
| #endif | ||
| } | ||
|
|
||
| REQUIRE_SUCCEEDED(closable->Close()); | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| REQUIRE(timesInvoked == 2); | ||
| #else | ||
| REQUIRE(timesInvoked == 1); | ||
| #endif | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,209 @@ | ||
|
|
||
| #include <wil/wistd_functional.h> | ||
|
|
||
| #include "common.h" | ||
| #include "test_objects.h" | ||
|
|
||
| // Test methods/objects | ||
| int GetValue() | ||
| { | ||
| return 42; | ||
| } | ||
|
|
||
| int GetOtherValue() | ||
| { | ||
| return 8; | ||
| } | ||
|
|
||
| int Negate(int value) | ||
| { | ||
| return -value; | ||
| } | ||
|
|
||
| int Add(int lhs, int rhs) | ||
| { | ||
| return lhs + rhs; | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::CallOperatorTest", "[wistd]") | ||
| { | ||
| wistd::function<int()> getValue = GetValue; | ||
| REQUIRE(GetValue() == getValue()); | ||
|
|
||
| wistd::function<int(int)> negate = Negate; | ||
| REQUIRE(Negate(42) == negate(42)); | ||
|
|
||
| wistd::function<int(int, int)> add = Add; | ||
| REQUIRE(Add(42, 8) == add(42, 8)); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::AssignmentOperatorTest", "[wistd]") | ||
| { | ||
| wistd::function<int()> fn = GetValue; | ||
| REQUIRE(GetValue() == fn()); | ||
|
|
||
| fn = GetOtherValue; | ||
| REQUIRE(GetOtherValue() == fn()); | ||
| } | ||
|
|
||
| #ifdef WIL_ENABLE_EXCEPTIONS | ||
| TEST_CASE("WistdFunctionTests::StdFunctionConstructionTest", "[wistd]") | ||
| { | ||
| // We should be able to capture a std::function in a wistd::function | ||
| wistd::function<int()> fn; | ||
|
|
||
| { | ||
| value_holder holder{ 42 }; | ||
| std::function<int()> stdFn = [holder]() | ||
| { | ||
| return holder.value; | ||
| }; | ||
|
|
||
| fn = stdFn; | ||
| } | ||
|
|
||
| REQUIRE(42 == fn()); | ||
| } | ||
| #endif | ||
|
|
||
| TEST_CASE("WistdFunctionTests::CopyConstructionTest", "[wistd]") | ||
| { | ||
| object_counter_state state; | ||
| { | ||
| wistd::function<int()> copyFrom = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
| REQUIRE(0 == copyFrom()); | ||
|
|
||
| auto copyTo = copyFrom; | ||
| REQUIRE(1 == copyTo()); | ||
| } | ||
|
|
||
| REQUIRE(0 == state.instance_count()); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::CopyAssignmentTest", "[wistd]") | ||
| { | ||
| object_counter_state state; | ||
| { | ||
| wistd::function<int()> copyTo; | ||
| { | ||
| wistd::function<int()> copyFrom = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
| REQUIRE(0 == copyFrom()); | ||
|
|
||
| copyTo = copyFrom; | ||
| } | ||
|
|
||
| REQUIRE(1 == copyTo()); | ||
| } | ||
|
|
||
| REQUIRE(0 == state.instance_count()); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::MoveConstructionTest", "[wistd]") | ||
| { | ||
| object_counter_state state; | ||
| { | ||
| wistd::function<int()> moveFrom = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
| REQUIRE(0 == moveFrom()); | ||
|
|
||
| auto moveTo = std::move(moveFrom); | ||
| REQUIRE(0 == moveTo()); | ||
|
|
||
| // Because we move the underlying function object, we _must_ invalidate the moved from function | ||
| REQUIRE_FALSE(moveFrom != nullptr); | ||
| } | ||
|
|
||
| REQUIRE(0 == state.instance_count()); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::MoveAssignmentTest", "[wistd]") | ||
| { | ||
| object_counter_state state; | ||
| { | ||
| wistd::function<int()> moveTo; | ||
| { | ||
| wistd::function<int()> moveFrom = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
| REQUIRE(0 == moveFrom()); | ||
|
|
||
| moveTo = std::move(moveFrom); | ||
| } | ||
|
|
||
| REQUIRE(0 == moveTo()); | ||
| } | ||
|
|
||
| REQUIRE(0 == state.instance_count()); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::SwapTest", "[wistd]") | ||
| { | ||
| object_counter_state state; | ||
| { | ||
| wistd::function<int()> first; | ||
| wistd::function<int()> second; | ||
|
|
||
| first.swap(second); | ||
| REQUIRE_FALSE(first != nullptr); | ||
| REQUIRE_FALSE(second != nullptr); | ||
|
|
||
| first = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
|
|
||
| first.swap(second); | ||
| REQUIRE_FALSE(first != nullptr); | ||
| REQUIRE(second != nullptr); | ||
| REQUIRE(0 == second()); | ||
|
|
||
| first.swap(second); | ||
| REQUIRE(first != nullptr); | ||
| REQUIRE_FALSE(second != nullptr); | ||
| REQUIRE(0 == first()); | ||
|
|
||
| second = [counter = object_counter{ state }]() | ||
| { | ||
| return counter.state->copy_count; | ||
| }; | ||
|
|
||
| first.swap(second); | ||
| REQUIRE(first != nullptr); | ||
| REQUIRE(second != nullptr); | ||
| REQUIRE(0 == first()); | ||
| } | ||
|
|
||
| REQUIRE(0 == state.instance_count()); | ||
| } | ||
|
|
||
| // MSVC's optimizer has had issues with wistd::function in the past when forwarding wistd::function objects to a | ||
| // function that accepts the arguments by value. This test exercises the workaround that we have in place. Note | ||
| // that this of course requires building with optimizations enabled | ||
| void ForwardingTest(wistd::function<int()> getValue, wistd::function<int(int)> negate, wistd::function<int(int, int)> add) | ||
| { | ||
| // Previously, this would cause a runtime crash | ||
| REQUIRE(Add(GetValue(), Negate(8)) == add(getValue(), negate(8))); | ||
| } | ||
|
|
||
| template <typename... Args> | ||
| void CallForwardingTest(Args&&... args) | ||
| { | ||
| ForwardingTest(wistd::forward<Args>(args)...); | ||
| } | ||
|
|
||
| TEST_CASE("WistdFunctionTests::OptimizationRegressionTest", "[wistd]") | ||
| { | ||
| CallForwardingTest( | ||
| wistd::function<int()>(GetValue), | ||
| wistd::function<int(int)>(Negate), | ||
| wistd::function<int(int, int)>(Add)); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
|
|
||
| project(witest.app) | ||
| add_executable(witest.app) | ||
|
|
||
| add_definitions(-DWINAPI_FAMILY=WINAPI_FAMILY_PC_APP) | ||
|
|
||
| target_sources(witest.app PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,31 @@ | ||
|
|
||
| # Compilers often don't use the latest C++ standard as the default. Periodically update this value (possibly conditioned | ||
| # on compiler) as new standards are ratified/support is available | ||
| set(CMAKE_CXX_STANDARD 17) | ||
|
|
||
| project(witest.cpplatest) | ||
| add_executable(witest.cpplatest) | ||
|
|
||
| # Semi-arbitrary insiders SDK version selected that uses C++/WinRT "2.0" | ||
| if ("${WIL_WINDOWS_SDK_VERSION}" VERSION_GREATER_EQUAL "10.0.18878.0") | ||
| target_sources(witest.cpplatest PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRT20Tests.cpp) | ||
| endif() | ||
|
|
||
| target_sources(witest.cpplatest PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../CppWinRTTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
|
|
||
| #pragma comment(lib, "Pathcch.lib") | ||
| #pragma comment(lib, "RuntimeObject.lib") | ||
| #pragma comment(lib, "Synchronization.lib") | ||
| #pragma comment(lib, "RpcRt4.lib") | ||
|
|
||
| #define CATCH_CONFIG_MAIN | ||
| #include "catch.hpp" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
|
|
||
| project(witest.noexcept) | ||
| add_executable(witest.noexcept) | ||
|
|
||
| # Turn off exceptions for this test | ||
| replace_cxx_flag("/EHsc" "") | ||
| add_definitions(-DCATCH_CONFIG_DISABLE_EXCEPTIONS) | ||
|
|
||
| # Catch2 has a no exceptions mode (configured above), however still includes STL headers which contain try...catch | ||
| # statements... Thankfully MSVC just gives us a warning that we can disable | ||
| append_cxx_flag("/wd4530") | ||
|
|
||
| target_sources(witest.noexcept PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
|
|
||
| project(witest) | ||
| add_executable(witest) | ||
|
|
||
| target_sources(witest PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../main.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../CommonTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ComTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../FileSystemTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResourceTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../ResultTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../Rpc.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../SafeCastTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../StlTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../TokenHelpersTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../UniqueWinRTEventTokenTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WatcherTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WinRTTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../WistdTests.cpp | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/../wiTest.cpp | ||
| ) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| #pragma once | ||
|
|
||
| #include "catch.hpp" | ||
|
|
||
| // Useful for validating that the copy constructor is never called (e.g. to validate perfect forwarding). Note that | ||
| // the copy constructor/assignment operator are not deleted since we want to be able to validate in scenarios that | ||
| // require CopyConstructible (e.g. for wistd::function) | ||
| struct fail_on_copy | ||
| { | ||
| fail_on_copy() = default; | ||
|
|
||
| fail_on_copy(const fail_on_copy&) | ||
| { | ||
| FAIL("Copy constructor invoked for fail_on_copy type"); | ||
| } | ||
|
|
||
| fail_on_copy(fail_on_copy&&) = default; | ||
|
|
||
| fail_on_copy& operator=(const fail_on_copy&) | ||
| { | ||
| FAIL("Copy assignment operator invoked for fail_on_copy type"); | ||
| return *this; | ||
| } | ||
|
|
||
| fail_on_copy& operator=(fail_on_copy&&) = default; | ||
| }; | ||
|
|
||
| // Useful for validating that objects get copied e.g. as opposed to capturing a reference | ||
| struct value_holder | ||
| { | ||
| int value = 0xbadf00d; | ||
|
|
||
| ~value_holder() | ||
| { | ||
| value = 0xbadf00d; | ||
| } | ||
| }; | ||
|
|
||
| // Useful for validating that functions, etc. are callable with move-only types | ||
| // Example real type that is move only is Microsoft::WRL::Wrappers::HString | ||
| struct cannot_copy | ||
| { | ||
| cannot_copy() = default; | ||
| cannot_copy(const cannot_copy&) = delete; | ||
| cannot_copy& operator=(const cannot_copy&) = delete; | ||
|
|
||
| cannot_copy(cannot_copy&&) = default; | ||
| cannot_copy& operator=(cannot_copy&&) = default; | ||
| }; | ||
|
|
||
| // State for object_counter type. This has the unfortunate side effect that the object_counter type cannot be used in | ||
| // contexts that require a default constructible type, but has the nice property that it allows for tests to run | ||
| // concurrently | ||
| struct object_counter_state | ||
| { | ||
| volatile LONG constructed_count = 0; | ||
| volatile LONG destructed_count = 0; | ||
| volatile LONG copy_count = 0; | ||
| volatile LONG move_count = 0; | ||
|
|
||
| LONG instance_count() | ||
| { | ||
| return constructed_count - destructed_count; | ||
| } | ||
| }; | ||
|
|
||
| struct object_counter | ||
| { | ||
| object_counter_state* state; | ||
|
|
||
| object_counter(object_counter_state& s) : | ||
| state(&s) | ||
| { | ||
| ::InterlockedIncrement(&state->constructed_count); | ||
| } | ||
|
|
||
| object_counter(const object_counter& other) : | ||
| state(other.state) | ||
| { | ||
| ::InterlockedIncrement(&state->constructed_count); | ||
| ::InterlockedIncrement(&state->copy_count); | ||
| } | ||
|
|
||
| object_counter(object_counter&& other) WI_NOEXCEPT : | ||
| state(other.state) | ||
| { | ||
| ::InterlockedIncrement(&state->constructed_count); | ||
| ::InterlockedIncrement(&state->move_count); | ||
| } | ||
|
|
||
| ~object_counter() | ||
| { | ||
| ::InterlockedIncrement(&state->destructed_count); | ||
| state = nullptr; | ||
| } | ||
|
|
||
| object_counter& operator=(const object_counter&) | ||
| { | ||
| ::InterlockedIncrement(&state->copy_count); | ||
| return *this; | ||
| } | ||
|
|
||
| object_counter& operator=(object_counter&&) WI_NOEXCEPT | ||
| { | ||
| ::InterlockedIncrement(&state->move_count); | ||
| return *this; | ||
| } | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
|
|
||
| We try and be as conformant as possible, but sometimes dependencies make that difficult. For example, WRL has had a number of conformance issues that keep getting uncovered. The files here are fixed up copies of those files and the include path is modified such that these directories appear first. |