123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592 |
- #include <wil/com.h>
- #include <wil/result.h>
- #if (NTDDI_VERSION >= NTDDI_WIN8)
- #include <wil/result_originate.h>
- #endif
- #include <roerrorapi.h>
- #include "common.h"
- static volatile long objectCount = 0;
- struct SharedObject
- {
- SharedObject()
- {
- ::InterlockedIncrement(&objectCount);
- }
- ~SharedObject()
- {
- ::InterlockedDecrement(&objectCount);
- }
- void ProcessShutdown()
- {
- }
- int value{};
- };
- TEST_CASE("ResultTests::SemaphoreValue", "[result]")
- {
- auto TestValue = [&](auto start, auto end)
- {
- wil::details_abi::SemaphoreValue semaphore;
- for (auto index = start; index <= end; index++)
- {
- semaphore.Destroy();
- REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index)));
- auto num1 = index;
- auto num2 = index;
- REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1)));
- REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2)));
- REQUIRE(num1 == index);
- REQUIRE(num2 == index);
- }
- };
- // Test 32-bit values (edge cases)
- TestValue(0u, 10u);
- TestValue(250u, 260u);
- TestValue(0x7FFFFFF0u, 0x7FFFFFFFu);
- // Test 64-bit values (edge cases)
- TestValue(0ull, 10ull);
- TestValue(250ull, 260ull);
- TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full);
- TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
- TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
- TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull);
- // Test pointer values
- wil::details_abi::SemaphoreValue semaphore;
- void* address = &semaphore;
- REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address)));
- void* ptr;
- REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr)));
- REQUIRE(ptr == address);
- }
- TEST_CASE("ResultTests::ProcessLocalStorage", "[result]")
- {
- // Test process local storage memory and ref-counting
- {
- wil::details_abi::ProcessLocalStorage<SharedObject> obj1("ver1");
- wil::details_abi::ProcessLocalStorage<SharedObject> obj2("ver1");
- auto& o1 = *obj1.GetShared();
- auto& o2 = *obj2.GetShared();
- REQUIRE(o1.value == 0);
- REQUIRE(o2.value == 0);
- o1.value = 42;
- REQUIRE(o2.value == 42);
- REQUIRE(objectCount == 1);
- wil::details_abi::ProcessLocalStorage<SharedObject> obj3("ver3");
- auto& o3 = *obj3.GetShared();
- REQUIRE(o3.value == 0);
- REQUIRE(objectCount == 2);
- }
- REQUIRE(objectCount == 0);
- }
- #ifdef WIL_ENABLE_EXCEPTIONS
- #pragma warning(push)
- #pragma warning(disable: 4702) // Unreachable code
- TEST_CASE("ResultTests::ExceptionHandling", "[result]")
- {
- witest::TestFailureCache failures;
- SECTION("Test 'what()' implementation on ResultException")
- {
- auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
- try
- {
- THROW_HR(E_INVALIDARG);
- FAIL("Expected an exception");
- }
- catch (const std::exception& exception)
- {
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == E_INVALIDARG);
- auto what = exception.what();
- REQUIRE((what && *what));
- REQUIRE(strstr(what, "Exception") != nullptr);
- }
- }
- failures.clear();
- SECTION("Test messaging from an unhandled std exception")
- {
- // #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue.
- auto hr = []()
- {
- try
- {
- throw std::runtime_error("runtime");
- }
- catch (...)
- {
- RETURN_CAUGHT_EXCEPTION();
- }
- }();
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
- REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string...
- REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
- }
- failures.clear();
- SECTION("Test messaging from bad_alloc")
- {
- auto hr = []() -> HRESULT
- {
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- RETURN_CAUGHT_EXCEPTION();
- }
- }();
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == E_OUTOFMEMORY);
- REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
- REQUIRE(hr == E_OUTOFMEMORY);
- }
- failures.clear();
- SECTION("Test messaging from a WIL exception")
- {
- auto hr = []() -> HRESULT
- {
- try
- {
- THROW_HR(E_INVALIDARG);
- }
- catch (...)
- {
- RETURN_CAUGHT_EXCEPTION();
- }
- return S_OK;
- }();
- REQUIRE(failures.size() == 2);
- REQUIRE(failures[0].hr == E_INVALIDARG);
- REQUIRE(failures[0].pszMessage == nullptr);
- REQUIRE(failures[1].hr == E_INVALIDARG);
- REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string...
- REQUIRE(hr == E_INVALIDARG);
- }
- failures.clear();
- SECTION("Fail fast an unknown exception")
- {
- REQUIRE(witest::DoesCodeCrash([]()
- {
- try
- {
- throw E_INVALIDARG; // bad throw... (long)
- }
- catch (...)
- {
- RETURN_CAUGHT_EXCEPTION();
- }
- }));
- }
- failures.clear();
- SECTION("Log test (returns hr)")
- {
- HRESULT hr = S_OK;
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- hr = LOG_CAUGHT_EXCEPTION();
- auto hrDirect = wil::ResultFromCaughtException();
- REQUIRE(hr == hrDirect);
- }
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == E_OUTOFMEMORY);
- REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
- REQUIRE(hr == E_OUTOFMEMORY);
- }
- failures.clear();
- SECTION("Fail-fast test")
- {
- REQUIRE_CRASH([]()
- {
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- FAIL_FAST_CAUGHT_EXCEPTION();
- }
- }());
- }
- failures.clear();
- SECTION("Exception test (different exception type thrown...)")
- {
- auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
- size_t line = 0;
- try
- {
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION();
- }
- }
- catch (const wil::ResultException& exception)
- {
- REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number
- REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY);
- }
- catch (...)
- {
- FAIL();
- }
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == E_OUTOFMEMORY);
- REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
- }
- failures.clear();
- SECTION("Exception test (rethrow same exception type...)")
- {
- auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
- size_t line = 0;
- try
- {
- try
- {
- line = __LINE__; THROW_HR(E_OUTOFMEMORY);
- }
- catch (...)
- {
- THROW_NORMALIZED_CAUGHT_EXCEPTION();
- }
- }
- catch (const wil::ResultException& exception)
- {
- REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number)
- }
- catch (...)
- {
- FAIL();
- }
- }
- failures.clear();
- SECTION("Test catch message")
- {
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42);
- }
- REQUIRE(failures.size() == 1);
- REQUIRE(failures[0].hr == E_OUTOFMEMORY);
- REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
- REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message...
- REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr);
- }
- failures.clear();
- SECTION("Test messaging from a WIL exception")
- {
- auto hr = []() -> HRESULT
- {
- try
- {
- throw std::bad_alloc();
- }
- catch (...)
- {
- RETURN_CAUGHT_EXCEPTION_EXPECTED();
- }
- }();
- REQUIRE(failures.empty());
- REQUIRE(hr == E_OUTOFMEMORY);
- }
- failures.clear();
- SECTION("Test ResultFromException...")
- {
- auto hrOk = wil::ResultFromException([&]
- {
- });
- REQUIRE(hrOk == S_OK);
- auto hr = wil::ResultFromException([&]
- {
- throw std::bad_alloc();
- });
- REQUIRE(failures.empty());
- REQUIRE(hr == E_OUTOFMEMORY);
- }
- failures.clear();
- SECTION("Explicit failfast for unrecognized")
- {
- REQUIRE_CRASH(wil::ResultFromException([&]
- {
- throw E_FAIL;
- }));
- }
- failures.clear();
- SECTION("Manual debug-only validation of the SEH failfast")
- {
- auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]()
- {
- // Uncomment to test SEH fail-fast
- // throw E_FAIL;
- });
- REQUIRE(hr1 == S_OK);
- auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&]
- {
- // Uncomment to test SEH fail-fast
- // throw std::range_error("range");
- });
- REQUIRE(hr2 == S_OK);
- wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
- {
- // Uncomment to test SEH fail-fast
- // THROW_HR(E_FAIL);
- });
- }
- failures.clear();
- SECTION("Standard")
- {
- auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
- {
- THROW_HR(E_INVALIDARG);
- });
- REQUIRE(failures.size() == 2);
- REQUIRE(static_cast<decltype(line)>(failures[1].uLineNumber) == line);
- REQUIRE(hr == E_INVALIDARG);
- }
- failures.clear();
- SECTION("bad_alloc")
- {
- auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
- {
- throw std::bad_alloc();
- });
- REQUIRE(failures.size() == 1);
- REQUIRE(hr == E_OUTOFMEMORY);
- }
- failures.clear();
- SECTION("std::exception")
- {
- auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
- {
- throw std::range_error("range");
- });
- REQUIRE(failures.size() == 1);
- REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr);
- REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
- }
- }
- void ExceptionHandlingCompilationTest()
- {
- []{ try { throw std::bad_alloc(); } CATCH_RETURN(); }();
- []{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }();
- []{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }();
- []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }();
- []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }();
- []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }();
- try { throw std::bad_alloc(); } CATCH_LOG();
- try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42);
- try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); }
- try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
- try { throw std::bad_alloc(); } CATCH_FAIL_FAST();
- try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42);
- try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); }
- try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
- try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {}
- try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } catch (...) {}
- try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
- try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
- wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
- {
- THROW_HR(E_FAIL);
- });
- wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
- {
- });
- wil::ResultFromException([&]
- {
- });
- wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
- {
- });
- }
- #pragma warning(pop)
- #endif
- TEST_CASE("ResultTests::ErrorMacros", "[result]")
- {
- REQUIRE_ERROR(FAIL_FAST());
- REQUIRE_ERROR(FAIL_FAST_IF(true));
- REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr));
- REQUIRE_NOERROR(FAIL_FAST_IF(false));
- REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress()));
- REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42));
- REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42));
- REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42));
- REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42));
- REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42));
- //wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;
- SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
- REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE));
- REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE));
- }
- // The originate helper isn't compatible with CX so don't test it in that mode.
- #if !defined(__cplusplus_winrt) && (NTDDI_VERSION >= NTDDI_WIN8)
- TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
- {
- ::wil::SetOriginateErrorCallback(nullptr);
- wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
- // We can't guarantee test order, so clear the error payload prior to starting
- SetRestrictedErrorInfo(nullptr);
- []() -> HRESULT
- {
- RETURN_HR(S_OK);
- }();
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- #ifdef WIL_ENABLE_EXCEPTIONS
- try
- {
- THROW_HR(E_FAIL);
- }
- catch (...) {}
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- #endif // WIL_ENABLE_EXCEPTIONS
- []() -> HRESULT
- {
- RETURN_HR(E_FAIL);
- }();
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- []() -> HRESULT
- {
- RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED);
- return S_OK;
- }();
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- }
- TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
- {
- ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
- wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
- // Make sure we don't start with an error payload
- SetRestrictedErrorInfo(nullptr);
- // Success codes shouldn't originate.
- []()
- {
- RETURN_HR(S_OK);
- }();
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- auto validateOriginatedError = [&](HRESULT hrExpected)
- {
- wil::unique_bstr descriptionUnused;
- HRESULT existingHr = S_OK;
- wil::unique_bstr restrictedDescriptionUnused;
- wil::unique_bstr capabilitySidUnused;
- REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused));
- REQUIRE(hrExpected == existingHr);
- };
- #ifdef WIL_ENABLE_EXCEPTIONS
- // Throwing an error should originate.
- constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND;
- try
- {
- THROW_HR(thrownErrorCode);
- }
- catch (...) {}
- REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
- validateOriginatedError(thrownErrorCode);
- #endif // WIL_ENABLE_EXCEPTIONS
- // Returning an error code should originate.
- static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG;
- []()
- {
- RETURN_HR(returnedErrorCode);
- }();
- REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
- validateOriginatedError(returnedErrorCode);
- // _EXPECTED errors should NOT originate.
- static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED;
- []()
- {
- RETURN_IF_FAILED_EXPECTED(expectedErrorCode);
- return S_OK;
- }();
- REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
- }
- #endif
- TEST_CASE("ResultTests::ReportDoesNotChangeLastError", "[result]")
- {
- decltype(wil::details::g_pfnLoggingCallback) oopsie = [](wil::FailureInfo const&) noexcept
- {
- ::SetLastError(ERROR_ABANDON_HIBERFILE);
- };
- auto swap = witest::AssignTemporaryValue(&wil::details::g_pfnLoggingCallback, oopsie);
- ::SetLastError(ERROR_ABIOS_ERROR);
- LOG_IF_WIN32_BOOL_FALSE(FALSE);
- REQUIRE(::GetLastError() == ERROR_ABIOS_ERROR);
- }
|