#include <wil/result.h>
#include <wil/resource.h>
#include <wil/win32_helpers.h>
#include <wil/filesystem.h>
#include <wil/wrl.h>
#include <wil/com.h>

#ifdef WIL_ENABLE_EXCEPTIONS
#include <memory>
#include <set>
#include <unordered_set>
#endif

// Do not include most headers until after the WIL headers to ensure that we're not inadvertently adding any unnecessary
// dependencies to STL, WRL, or indirectly retrieved headers

#ifndef __cplusplus_winrt
#include <windows.foundation.collections.h>
#include <windows.foundation.h>
#endif

// Include Resource.h a second time after including other headers
#include <wil/resource.h>

#include "common.h"
#include "MallocSpy.h"
#include "test_objects.h"

#pragma warning(push)
#pragma warning(disable: 4702) // Unreachable code

TEST_CASE("WindowsInternalTests::CommonHelpers", "[resource]")
{
    {
        wil::unique_handle spHandle;
        REQUIRE(spHandle == nullptr);
        REQUIRE(nullptr == spHandle);
        REQUIRE_FALSE(spHandle != nullptr);
        REQUIRE_FALSE(nullptr != spHandle);

        //equivalence check will static_assert because spMutex does not allow pointer access
        wil::mutex_release_scope_exit spMutex;
        //REQUIRE(spMutex == nullptr);
        //REQUIRE(nullptr == spMutex);

        //equivalence check will static_assert because spFile does not use nullptr_t as a invalid value
        wil::unique_hfile spFile;
        //REQUIRE(spFile == nullptr);
    }
#ifdef __WIL_WINBASE_STL
    {
        wil::shared_handle spHandle;
        REQUIRE(spHandle == nullptr);
        REQUIRE(nullptr == spHandle);
        REQUIRE_FALSE(spHandle != nullptr);
        REQUIRE_FALSE(nullptr != spHandle);
    }
#endif
}

TEST_CASE("WindowsInternalTests::AssertMacros", "[result_macros]")
{
    //WI_ASSERT macros are all no-ops if in retail
#ifndef RESULT_DEBUG
    WI_ASSERT(false);
    WI_ASSERT_MSG(false, "WI_ASSERT_MSG");
    WI_ASSERT_NOASSUME(false);
    WI_ASSERT_MSG_NOASSUME(false, "WI_ASSERT_MSG_NOASSUME");
    WI_VERIFY(false);
    WI_VERIFY_MSG(false, "WI_VERIFY_MSG");
#endif

    WI_ASSERT(true);
    WI_ASSERT_MSG(true, "WI_ASSERT_MSG");
    WI_ASSERT_NOASSUME(true);
    WI_ASSERT_MSG_NOASSUME(true, "WI_ASSERT_MSG_NOASSUME");
    WI_VERIFY(true);
    WI_VERIFY_MSG(true, "WI_VERIFY_MSG");
}

void __stdcall EmptyResultMacrosLoggingCallback(wil::FailureInfo*, PWSTR, size_t) WI_NOEXCEPT
{
}

#ifdef WIL_ENABLE_EXCEPTIONS
// Test Result Macros
void TestErrorCallbacks()
{
    {
        size_t callbackCount = 0;
        auto monitor = wil::ThreadFailureCallback([&](wil::FailureInfo const &failure) -> bool
        {
            REQUIRE(failure.hr == E_ACCESSDENIED);
            callbackCount++;
            return false;
        });

        size_t const depthCount = 10;
        for (size_t index = 0; index < depthCount; index++)
        {
            LOG_HR(E_ACCESSDENIED);
        }
        REQUIRE(callbackCount == depthCount);
    }
    {
        wil::ThreadFailureCache cache;

        LOG_HR(E_ACCESSDENIED);
        REQUIRE(cache.GetFailure() != nullptr);
        REQUIRE(cache.GetFailure()->hr == E_ACCESSDENIED);

        wil::ThreadFailureCache cacheNested;

        LOG_HR(E_FAIL); unsigned long errorLine = __LINE__;
        LOG_HR(E_FAIL);
        LOG_HR(E_FAIL);
        REQUIRE(cache.GetFailure()->hr == E_FAIL);
        REQUIRE(cache.GetFailure()->uLineNumber == errorLine);
        REQUIRE(cacheNested.GetFailure()->hr == E_FAIL);
        REQUIRE(cacheNested.GetFailure()->uLineNumber == errorLine);
    }
}

DWORD WINAPI ErrorCallbackThreadTest(_In_ LPVOID lpParameter)
{
    try
    {
        HANDLE hEvent = reinterpret_cast<HANDLE>(lpParameter);

        for (size_t stress = 0; stress < 200; stress++)
        {
            Sleep(1); // allow the threadpool to saturate the thread count...
            TestErrorCallbacks();
        }
        THROW_IF_WIN32_BOOL_FALSE(::SetEvent(hEvent));
    }
    catch (...)
    {
        FAIL();
    }
    return 1;
}

void StressErrorCallbacks()
{
    auto restore = witest::AssignTemporaryValue(&wil::g_fResultOutputDebugString, false);

    size_t const threadCount = 20;
    wil::unique_event eventArray[threadCount];

    for (size_t index = 0; index < threadCount; index++)
    {
        eventArray[index].create();
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
        THROW_IF_WIN32_BOOL_FALSE(::QueueUserWorkItem(ErrorCallbackThreadTest, eventArray[index].get(), 0));
#else
        ErrorCallbackThreadTest(eventArray[index].get());
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */
    }
    for (size_t index = 0; index < threadCount; index++)
    {
        eventArray[index].wait();
    }
}

TEST_CASE("WindowsInternalTests::ResultMacrosStress", "[!hide][result_macros][stress]")
{
    auto restore = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, EmptyResultMacrosLoggingCallback);
    StressErrorCallbacks();
}
#endif

#define E_AD HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED)
void SetAD()
{
    ::SetLastError(ERROR_ACCESS_DENIED);
}

class AlternateAccessDeniedException
{
};

#ifdef WIL_ENABLE_EXCEPTIONS
class DerivedAccessDeniedException : public wil::ResultException
{
public:
    DerivedAccessDeniedException() : ResultException(E_AD) {}
};

HRESULT __stdcall TestResultCaughtFromException() WI_NOEXCEPT
{
    try
    {
        throw;
    }
    catch (AlternateAccessDeniedException)
    {
        return E_AD;
    }
    catch (...)
    {
    }
    return S_OK;
}
#endif

HANDLE hValid = reinterpret_cast<HANDLE>(1);
HANDLE& hValidRef() { return hValid; }
HANDLE hNull = NULL;
HANDLE hInvalid = INVALID_HANDLE_VALUE;
void* pValid = reinterpret_cast<void *>(1);
void*& pValidRef() { return pValid; }
void* pNull = nullptr;
void*& pNullRef() { return pNull; }
bool fTrue = true;
bool& fTrueRef() { return fTrue; }
bool fFalse = false;
bool& fFalseRef() { return fFalse; }
BOOL fTRUE = TRUE;
BOOL& fTRUERef() { return fTRUE; }
BOOL fFALSE = FALSE;
DWORD errSuccess = ERROR_SUCCESS;
DWORD& errSuccessRef() { return errSuccess; }
HRESULT hrOK = S_OK;
HRESULT& hrOKRef() { return hrOK; }
HRESULT hrFAIL = E_FAIL;
HRESULT& hrFAILRef() { return hrFAIL; }
const HRESULT E_hrOutOfPaper = HRESULT_FROM_WIN32(ERROR_OUT_OF_PAPER);
NTSTATUS ntOK = STATUS_SUCCESS;
NTSTATUS& ntOKRef() { return ntOK; }
NTSTATUS ntFAIL = STATUS_NO_MEMORY;
NTSTATUS& ntFAILRef() { return ntFAIL; }
const HRESULT S_hrNtOkay = wil::details::NtStatusToHr(STATUS_SUCCESS);
const HRESULT E_hrNtAssertionFailure = wil::details::NtStatusToHr(STATUS_ASSERTION_FAILURE);

wil::StoredFailureInfo g_log;

void __stdcall ResultMacrosLoggingCallback(wil::FailureInfo *pFailure, PWSTR, size_t) WI_NOEXCEPT
{
    g_log = *pFailure;
}

enum class EType
{
    None = 0x00,
    Expected = 0x02,
    Msg = 0x04,
    FailFast = 0x08,        // overall fail fast (throw exception on successful result code, for example)
    FailFastMacro = 0x10,   // explicit use of fast fail fast (FAIL_FAST_IF...)
    NoContext = 0x20        // file and line info can be wrong (throw does not happen in context to code)
};
DEFINE_ENUM_FLAG_OPERATORS(EType);

template <typename TLambda>
bool VerifyResult(unsigned int lineNumber, EType type, HRESULT hr, TLambda&& lambda)
{
    bool succeeded = true;
#ifdef WIL_ENABLE_EXCEPTIONS
    try
    {
#endif
        HRESULT lambdaResult = E_FAIL;
        bool didFailFast = true;
        {
            didFailFast = witest::DoesCodeCrash([&]()
            {
                lambdaResult = lambda();
            });
        }
        if (WI_IsFlagSet(type, EType::FailFast))
        {
            REQUIRE(didFailFast);
        }
        else
        {
            if (WI_IsFlagClear(type, EType::Expected))
            {
                if (SUCCEEDED(hr))
                {
                    REQUIRE(hr == lambdaResult);
                    REQUIRE(lineNumber != g_log.GetFailureInfo().uLineNumber);
                    REQUIRE(!didFailFast);
                }
                else
                {
                    REQUIRE((WI_IsFlagSet(type, EType::NoContext) || (g_log.GetFailureInfo().uLineNumber == lineNumber)));
                    REQUIRE(g_log.GetFailureInfo().hr == hr);
                    REQUIRE((WI_IsFlagClear(type, EType::Msg) || (nullptr != wcsstr(g_log.GetFailureInfo().pszMessage, L"msg"))));
                    REQUIRE((WI_IsFlagClear(type, EType::FailFastMacro) || (didFailFast)));
                    REQUIRE((WI_IsFlagSet(type, EType::FailFastMacro) || (!didFailFast)));
                }
            }
        }
#ifdef WIL_ENABLE_EXCEPTIONS
    }
    catch (...)
    {
        succeeded = false;
    }
#endif

    // Ensure we come out clean...
    ::SetLastError(ERROR_SUCCESS);
    return succeeded;
}

#ifdef WIL_ENABLE_EXCEPTIONS
template <typename TLambda>
HRESULT TranslateException(TLambda&& lambda)
{
    try
    {
        lambda();
    }
    catch (wil::ResultException &re)
    {
        return re.GetErrorCode();
    }
#ifdef __cplusplus_winrt
    catch (Platform::Exception ^pe)
    {
        return wil::details::GetErrorCode(pe);
    }
#endif
    catch (...)
    {
        FAIL();
    }
    return S_OK;
}
#endif

#define REQUIRE_RETURNS(hr, lambda)             REQUIRE(VerifyResult(__LINE__, EType::None, hr, lambda))
#define REQUIRE_RETURNS_MSG(hr, lambda)         REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, lambda))
#define REQUIRE_RETURNS_EXPECTED(hr, lambda)    REQUIRE(VerifyResult(__LINE__, EType::Expected, hr, lambda))

#ifdef WIL_ENABLE_EXCEPTIONS
#define REQUIRE_THROWS_RESULT(hr, lambda)       REQUIRE(VerifyResult(__LINE__, EType::None, hr, [&] { return TranslateException(lambda); }))
#define REQUIRE_THROWS_MSG(hr, lambda)          REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, [&] { return TranslateException(lambda); }))
#else
#define REQUIRE_THROWS_RESULT(hr, lambda)
#define REQUIRE_THROWS_MSG(hr, lambda)
#endif

#define REQUIRE_LOG(hr, lambda)                 REQUIRE(VerifyResult(__LINE__, EType::None, hr, [&] { auto fn = (lambda); fn(); return hr; }))
#define REQUIRE_LOG_MSG(hr, lambda)             REQUIRE(VerifyResult(__LINE__, EType::Msg, hr, [&] { auto fn = (lambda); fn(); return hr; }))

#define REQUIRE_FAILFAST(hr, lambda)            REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro, hr, [&] { auto fn = (lambda); fn(); return hr; }))
#define REQUIRE_FAILFAST_MSG(hr, lambda)        REQUIRE(VerifyResult(__LINE__, EType::FailFastMacro | EType::Msg, hr, [&] { auto fn = (lambda); fn(); return hr; }))
#define REQUIRE_FAILFAST_UNSPECIFIED(lambda)    REQUIRE(VerifyResult(__LINE__, EType::FailFast, S_OK, [&] { auto fn = (lambda); fn(); return S_OK; }))

TEST_CASE("WindowsInternalTests::ResultMacros", "[result_macros]")
{
    auto restoreLoggingCallback = witest::AssignTemporaryValue(&wil::g_pfnResultLoggingCallback, ResultMacrosLoggingCallback);
#ifdef WIL_ENABLE_EXCEPTIONS
    auto restoreExceptionCallback = witest::AssignTemporaryValue(&wil::g_pfnResultFromCaughtException, TestResultCaughtFromException);
#endif

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR(MDEC(hrOKRef())); });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR(MDEC(hrOKRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR(E_FAIL); });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_MSG(E_FAIL, "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR(E_FAIL); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_MSG(E_FAIL, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_FAIL, [] { LOG_HR(E_FAIL); });
    REQUIRE_LOG_MSG(E_FAIL, [] { LOG_HR_MSG(E_FAIL, "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR(E_FAIL); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_MSG(E_FAIL, "msg: %d", __LINE__); });

    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR(); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_MSG("msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR(); });
    REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_MSG("msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR(); });
    REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_MSG("msg: %d", __LINE__); });
    REQUIRE_LOG(E_AD, [] { SetAD(); LOG_LAST_ERROR(); });
    REQUIRE_LOG_MSG(E_AD, [] { SetAD(); LOG_LAST_ERROR_MSG("msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR(); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_MSG("msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_WIN32(MDEC(errSuccessRef())); });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_WIN32_MSG(MDEC(errSuccessRef()), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_WIN32(MDEC(errSuccessRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_WIN32_MSG(MDEC(errSuccessRef()), "msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_AD, [] { RETURN_WIN32(ERROR_ACCESS_DENIED); });
    REQUIRE_RETURNS_MSG(E_AD, [] { RETURN_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_AD, [] { THROW_WIN32(ERROR_ACCESS_DENIED); });
    REQUIRE_THROWS_MSG(E_AD, [] { THROW_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_AD, [] { LOG_WIN32(ERROR_ACCESS_DENIED); });
    REQUIRE_LOG_MSG(E_AD, [] { LOG_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_AD, [] { FAIL_FAST_WIN32(ERROR_ACCESS_DENIED); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { FAIL_FAST_WIN32_MSG(ERROR_ACCESS_DENIED, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_IF_FAILED(MDEC(hrOKRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_FAILED_EXPECTED(MDEC(hrOKRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(S_OK == THROW_IF_FAILED(MDEC(hrOKRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(S_OK == THROW_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED(MDEC(hrOKRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_FAILED(MDEC(hrOKRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_FAILED_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(E_FAIL, [] { RETURN_IF_FAILED(E_FAIL); return S_OK; });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_EXPECTED(E_FAIL); return S_OK; });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_IF_FAILED(E_FAIL); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED(E_FAIL)); });
    REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_IF_FAILED(E_FAIL); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_IF_FAILED_MSG(E_FAIL, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(MDEC(fTRUERef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fTRUE == THROW_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fTRUE == THROW_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fTRUE == LOG_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fTRUE == LOG_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fTRUE == FAIL_FAST_IF_WIN32_BOOL_FALSE(MDEC(fTRUERef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fTRUE == FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(MDEC(fTRUERef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE(fFALSE); return S_OK; });
    REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fFALSE); return S_OK; });
    REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_IF_WIN32_BOOL_FALSE(fFALSE); });
    REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(fFALSE == LOG_IF_WIN32_BOOL_FALSE(fFALSE)); });
    REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(fFALSE == LOG_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_IF_WIN32_BOOL_FALSE(fFALSE); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_IF_WIN32_BOOL_FALSE_MSG(fFALSE, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_IF_WIN32_ERROR(MDEC(hrOKRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_WIN32_ERROR_EXPECTED(MDEC(hrOKRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(S_OK == THROW_IF_WIN32_ERROR(MDEC(hrOKRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(S_OK == THROW_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_WIN32_ERROR(MDEC(hrOKRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(S_OK == LOG_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_WIN32_ERROR(MDEC(hrOKRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IF_WIN32_ERROR_MSG(MDEC(hrOKRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); return S_OK; });
    REQUIRE_RETURNS_MSG(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_hrOutOfPaper, [] { RETURN_IF_WIN32_ERROR_EXPECTED(ERROR_OUT_OF_PAPER); return S_OK; });
    REQUIRE_THROWS_RESULT(E_hrOutOfPaper, [] { THROW_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); });
    REQUIRE_THROWS_MSG(E_hrOutOfPaper, [] { THROW_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_hrOutOfPaper, [] { REQUIRE(ERROR_OUT_OF_PAPER == LOG_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER)); });
    REQUIRE_LOG_MSG(E_hrOutOfPaper, [] { REQUIRE(ERROR_OUT_OF_PAPER == LOG_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_hrOutOfPaper, [] { FAIL_FAST_IF_WIN32_ERROR(ERROR_OUT_OF_PAPER); });
    REQUIRE_FAILFAST_MSG(E_hrOutOfPaper, [] { FAIL_FAST_IF_WIN32_ERROR_MSG(ERROR_OUT_OF_PAPER, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_hrNtOkay, [] { RETURN_NTSTATUS(MDEC(ntOKRef())); });
    REQUIRE_RETURNS_MSG(S_hrNtOkay, [] { RETURN_NTSTATUS_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_NTSTATUS(MDEC(ntOKRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_NTSTATUS_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_hrNtAssertionFailure, [] { RETURN_NTSTATUS(STATUS_ASSERTION_FAILURE); });
    REQUIRE_RETURNS_MSG(E_hrNtAssertionFailure, [] { RETURN_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_NTSTATUS(STATUS_ASSERTION_FAILURE); });
    REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_hrNtAssertionFailure, [] { LOG_NTSTATUS(STATUS_ASSERTION_FAILURE); });
    REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { LOG_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_NTSTATUS(STATUS_ASSERTION_FAILURE); });
    REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_NTSTATUS_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });

    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(MDEC(ntOKRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(STATUS_WAIT_0 == THROW_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == THROW_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == LOG_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == LOG_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(STATUS_WAIT_0 == FAIL_FAST_IF_NTSTATUS_FAILED(MDEC(ntOKRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(STATUS_WAIT_0 == FAIL_FAST_IF_NTSTATUS_FAILED_MSG(MDEC(ntOKRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); return S_OK; });
    REQUIRE_RETURNS_MSG(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_hrNtAssertionFailure, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_ASSERTION_FAILURE); return S_OK; });
    REQUIRE_THROWS_RESULT(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
    REQUIRE_THROWS_MSG(E_hrNtAssertionFailure, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE)); });
    REQUIRE_LOG_MSG(E_hrNtAssertionFailure, [] { REQUIRE(STATUS_ASSERTION_FAILURE == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_ASSERTION_FAILURE); });
    REQUIRE_FAILFAST_MSG(E_hrNtAssertionFailure, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_ASSERTION_FAILURE, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); return S_OK; });
    REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NTSTATUS_FAILED_EXPECTED(STATUS_NO_MEMORY); return S_OK; });
    REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
    REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY)); });
    REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(STATUS_NO_MEMORY == LOG_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED(STATUS_NO_MEMORY); });
    REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NTSTATUS_FAILED_MSG(STATUS_NO_MEMORY, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_IF_NULL_ALLOC(MDEC(pValidRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pValidRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_IF_NULL_ALLOC(MDEC(pValidRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_IF_NULL_ALLOC(MDEC(pValidRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IF_NULL_ALLOC(MDEC(pValidRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC(pNull); return S_OK; });
    REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { RETURN_IF_NULL_ALLOC_EXPECTED(pNull); return S_OK; });
    REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { THROW_IF_NULL_ALLOC(pNull); });
    REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { THROW_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { REQUIRE(pNull == LOG_IF_NULL_ALLOC(pNull)); });
    REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { REQUIRE(pNull == LOG_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NULL_ALLOC(pNull); });
    REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { FAIL_FAST_IF_NULL_ALLOC_MSG(pNull, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); });
    REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF(E_FAIL, fTrue); return S_OK; });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_EXPECTED(E_FAIL, fTrue); return S_OK; });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF(E_FAIL, fTrue); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF(E_FAIL, fTrue)); });
    REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF(E_FAIL, fTrue); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fTrueRef())); return S_OK; });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fTrueRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fTrueRef()), "msg: %d", __LINE__); });
    REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF(E_FAIL, fTrue); return S_OK; });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_EXPECTED(E_FAIL, fTrue); return S_OK; });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF(E_FAIL, fTrue); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF(E_FAIL, fTrue)); });
    REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(fTrue == LOG_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF(E_FAIL, fTrue); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_MSG(E_FAIL, fTrue, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(E_FAIL, MDEC(fFalseRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(E_FAIL, MDEC(fFalseRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(MDEC(S_OK), MDEC(fFalseRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(MDEC(S_OK), MDEC(fFalseRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(MDEC(S_OK), MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF(E_FAIL, MDEC(fFalseRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_EXPECTED(E_FAIL, MDEC(fFalseRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF(E_FAIL, MDEC(fFalseRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_HR_IF_MSG(E_FAIL, MDEC(fFalseRef()), "msg: %d", __LINE__)); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(S_OK, pNull); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(S_OK, pNull, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(S_OK, pNull); return S_OK; });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_NULL(S_OK, pNull); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_HR_IF_NULL_MSG(S_OK, pNull, "msg: %d", __LINE__); });
    REQUIRE_RETURNS(E_FAIL, [] { RETURN_HR_IF_NULL(E_FAIL, pNull); return S_OK; });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { RETURN_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_HR_IF_NULL_EXPECTED(E_FAIL, pNull); return S_OK; });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { THROW_HR_IF_NULL(E_FAIL, pNull); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { THROW_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(pNull == LOG_HR_IF_NULL(E_FAIL, pNull)); });
    REQUIRE_LOG_MSG(E_FAIL, [] { REQUIRE(pNull == LOG_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_FAIL, [] { FAIL_FAST_HR_IF_NULL(E_FAIL, pNull); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { FAIL_FAST_HR_IF_NULL_MSG(E_FAIL, pNull, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(MDEC(S_OK), MDEC(pValidRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL(MDEC(S_OK), MDEC(pValidRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL_MSG(MDEC(S_OK), MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_RETURNS(S_OK, [] { RETURN_HR_IF_NULL(E_FAIL, MDEC(pValidRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_HR_IF_NULL_EXPECTED(E_FAIL, MDEC(pValidRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pValid == THROW_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pValid == LOG_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL(E_FAIL, MDEC(pValidRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pValid == FAIL_FAST_HR_IF_NULL_MSG(E_FAIL, MDEC(pValidRef()), "msg: %d", __LINE__)); });

    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF(fTrue); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); });
    REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF(fTrue); return S_OK; });
    REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_EXPECTED(fTrue); return S_OK; });
    REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF(fTrue); });
    REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(fTrue == LOG_LAST_ERROR_IF(fTrue)); });
    REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(fTrue == LOG_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF(fTrue); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_MSG(fTrue, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_LAST_ERROR_IF(MDEC(fFalseRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_LAST_ERROR_IF_EXPECTED(MDEC(fFalseRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(fFalse == THROW_LAST_ERROR_IF(MDEC(fFalseRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(fFalse == THROW_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(fFalse == LOG_LAST_ERROR_IF(MDEC(fFalseRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(fFalse == LOG_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_LAST_ERROR_IF(MDEC(fFalseRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_LAST_ERROR_IF_MSG(MDEC(fFalseRef()), "msg: %d", __LINE__)); });

    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_NULL(pNull); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { ::SetLastError(0); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); });
    REQUIRE_RETURNS(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL(pNull); return S_OK; });
    REQUIRE_RETURNS_MSG(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_AD, [] { SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(pNull); return S_OK; });
    REQUIRE_THROWS_RESULT(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_NULL(pNull); });
    REQUIRE_THROWS_MSG(E_AD, [] { SetAD(); THROW_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); });
    REQUIRE_LOG(E_AD, [] { SetAD(); REQUIRE(pNull == LOG_LAST_ERROR_IF_NULL(pNull)); });
    REQUIRE_LOG_MSG(E_AD, [] { SetAD(); REQUIRE(pNull == LOG_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL(pNull); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pNull, "msg: %d", __LINE__); });

    REQUIRE_RETURNS(S_OK, [] { RETURN_LAST_ERROR_IF_NULL(MDEC(pValidRef())); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pValidRef())); return S_OK; });
    REQUIRE_THROWS_RESULT(S_OK, [] { REQUIRE(pNull != THROW_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); });
    REQUIRE_THROWS_MSG(S_OK, [] { REQUIRE(pNull != THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(pNull != LOG_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); });
    REQUIRE_LOG_MSG(S_OK, [] { REQUIRE(pNull != LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pNull != FAIL_FAST_LAST_ERROR_IF_NULL(MDEC(pValidRef()))); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { REQUIRE(pNull != FAIL_FAST_LAST_ERROR_IF_NULL_MSG(MDEC(pValidRef()), "msg: %d", __LINE__)); });

    REQUIRE_LOG(S_OK, [] { REQUIRE(true == SUCCEEDED_LOG(MDEC(S_OK))); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(false == SUCCEEDED_LOG(E_FAIL)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(false == FAILED_LOG(MDEC(S_OK))); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(true == FAILED_LOG(E_FAIL)); });

    REQUIRE_LOG(ERROR_SUCCESS, [] { REQUIRE(true == SUCCEEDED_WIN32_LOG(MDEC(ERROR_SUCCESS))); });
    REQUIRE_LOG(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), [] { REQUIRE(false == SUCCEEDED_WIN32_LOG(ERROR_ACCESS_DENIED)); });
    REQUIRE_LOG(ERROR_SUCCESS, [] { REQUIRE(false == FAILED_WIN32_LOG(MDEC(ERROR_SUCCESS))); });
    REQUIRE_LOG(HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), [] { REQUIRE(true == FAILED_WIN32_LOG(ERROR_ACCESS_DENIED)); });

    REQUIRE_LOG(ntOK, [] { REQUIRE(true == SUCCEEDED_NTSTATUS_LOG(MDEC(ntOK))); });
    REQUIRE_LOG(wil::details::NtStatusToHr(ntFAIL), [] { REQUIRE(false == SUCCEEDED_NTSTATUS_LOG(ntFAIL)); });
    REQUIRE_LOG(ntOK, [] { REQUIRE(false == FAILED_NTSTATUS_LOG(MDEC(ntOK))); });
    REQUIRE_LOG(wil::details::NtStatusToHr(ntFAIL), [] { REQUIRE(true == FAILED_NTSTATUS_LOG(ntFAIL)); });

    // FAIL_FAST_IMMEDIATE* directly invokes __fastfail, which we can't catch, so disabled for now
    // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE(); });
    // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF_FAILED(E_FAIL); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(S_OK == FAIL_FAST_IMMEDIATE_IF_FAILED(MDEC(S_OK))); });
    // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF(fTrue); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(fFalse == FAIL_FAST_IMMEDIATE_IF(MDEC(fFalseRef()))); });
    // REQUIRE_FAILFAST_UNSPECIFIED([] { FAIL_FAST_IMMEDIATE_IF_NULL(pNull); });
    REQUIRE_FAILFAST(S_OK, [] { REQUIRE(pValid == FAIL_FAST_IMMEDIATE_IF_NULL(MDEC(pValidRef()))); });

#ifdef WIL_ENABLE_EXCEPTIONS
    REQUIRE_RETURNS(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN(); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN_MSG("msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_RETURN_EXPECTED(); return S_OK; });
    REQUIRE_LOG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_LOG(); });
    REQUIRE_LOG_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_LOG_MSG("msg: %d", __LINE__); });
    REQUIRE_FAILFAST(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_FAIL_FAST(); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_THROW_NORMALIZED(); });
    REQUIRE_THROWS_MSG(S_OK, [] { try { THROW_IF_FAILED(hrOK); } CATCH_THROW_NORMALIZED_MSG("msg: %d", __LINE__); });

    REQUIRE_RETURNS(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN(); return S_OK; });
    REQUIRE_RETURNS_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN_MSG("msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_RETURN_EXPECTED(); return S_OK; });
    REQUIRE_LOG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG(); });
    REQUIRE_LOG_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG_MSG("msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_FAIL_FAST(); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_THROW_NORMALIZED(); });
    REQUIRE_THROWS_MSG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_THROW_NORMALIZED_MSG("msg: %d", __LINE__); });

    REQUIRE_FAILFAST_UNSPECIFIED([] { try { if (FAILED(hrFAIL)) { throw E_FAIL; } } CATCH_FAIL_FAST(); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { try { if (FAILED(hrFAIL)) { throw E_FAIL; } } CATCH_FAIL_FAST_MSG("msg: %d", __LINE__); });

    REQUIRE_THROWS_RESULT(E_AD, [] { THROW_EXCEPTION(MDEC(DerivedAccessDeniedException())); });
    REQUIRE_THROWS_MSG(E_AD, [] { THROW_EXCEPTION_MSG(MDEC(DerivedAccessDeniedException()), "msg: %d", __LINE__); });

    REQUIRE_LOG(E_AD, [] { try { throw AlternateAccessDeniedException(); } CATCH_LOG(); });
    REQUIRE_THROWS_RESULT(E_AD, [] { try { throw AlternateAccessDeniedException(); } CATCH_THROW_NORMALIZED(); });

    REQUIRE_RETURNS(S_OK, [] { return wil::ResultFromException([] { THROW_IF_FAILED(hrOK); }); });
    REQUIRE_RETURNS(E_FAIL, [] { return wil::ResultFromException([] { THROW_IF_FAILED(hrFAIL); }); });
    REQUIRE(E_AD == wil::ResultFromException([] { throw AlternateAccessDeniedException(); }));

    try { THROW_HR(E_FAIL); }
    catch (...) { REQUIRE(E_FAIL == wil::ResultFromCaughtException()); };
#endif

#ifdef WIL_ENABLE_EXCEPTIONS
    REQUIRE_LOG(E_FAIL, [] { try { THROW_IF_FAILED(hrFAIL); } CATCH_LOG(); });
#endif

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_IF_NULL_ALLOC(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_IF_NULL_ALLOC(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_IF_NULL_ALLOC_EXPECTED(MDEC(pInt)); return S_OK; });

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_HR_IF_NULL(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_HR_IF_NULL_MSG(E_OUTOFMEMORY, pInt, "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; RETURN_HR_IF_NULL_EXPECTED(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_HR_IF_NULL(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_HR_IF_NULL_MSG(E_OUTOFMEMORY, MDEC(pInt), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); RETURN_HR_IF_NULL_EXPECTED(E_OUTOFMEMORY, MDEC(pInt)); return S_OK; });

    REQUIRE_RETURNS(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL(MDEC(pInt)); return S_OK; });
    REQUIRE_RETURNS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); SetAD(); RETURN_LAST_ERROR_IF_NULL_EXPECTED(MDEC(pInt)); return S_OK; });

    REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; THROW_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; THROW_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; LOG_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; LOG_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; FAIL_FAST_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_FAILFAST_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_LOG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_IF_NULL_ALLOC(MDEC(pInt)); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_IF_NULL_ALLOC_MSG(MDEC(pInt), "msg: %d", __LINE__); });

    REQUIRE_LOG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); });
    REQUIRE_LOG_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; LOG_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_FAIL, [] { std::unique_ptr<int> pInt; FAIL_FAST_HR_IF_NULL(MDEC(E_FAIL), MDEC(pInt)); });
    REQUIRE_FAILFAST_MSG(E_FAIL, [] { std::unique_ptr<int> pInt; FAIL_FAST_HR_IF_NULL_MSG(MDEC(E_FAIL), MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; THROW_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); });
    REQUIRE_THROWS_MSG(E_OUTOFMEMORY, [] { std::unique_ptr<int> pInt; THROW_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_LOG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); });
    REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_HR_IF_NULL(MDEC(E_FAIL), MDEC(pInt)); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_HR_IF_NULL_MSG(MDEC(E_FAIL), MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(pInt)); });
    REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_HR_IF_NULL_MSG(MDEC(E_OUTOFMEMORY), MDEC(pInt), "msg: %d", __LINE__); });

    REQUIRE_LOG(E_AD, [] {  std::unique_ptr<int> pInt; SetAD(); LOG_LAST_ERROR_IF_NULL(MDEC(pInt)); });
    REQUIRE_LOG_MSG(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL(pInt); });
    REQUIRE_FAILFAST_MSG(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pInt, "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); THROW_LAST_ERROR_IF_NULL(MDEC(pInt)); });
    REQUIRE_THROWS_MSG(E_AD, [] { std::unique_ptr<int> pInt; SetAD(); THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_LOG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_LAST_ERROR_IF_NULL(MDEC(pInt)); });
    REQUIRE_LOG_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); LOG_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); });
    REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr<int> pInt(new int(5));  FAIL_FAST_LAST_ERROR_IF_NULL(pInt); });
    REQUIRE_FAILFAST_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5));  FAIL_FAST_LAST_ERROR_IF_NULL_MSG(pInt, "msg: %d", __LINE__); });
    REQUIRE_THROWS_RESULT(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_LAST_ERROR_IF_NULL(MDEC(pInt)); });
    REQUIRE_THROWS_MSG(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); THROW_LAST_ERROR_IF_NULL_MSG(MDEC(pInt), "msg: %d", __LINE__); });

    // REQUIRE_FAILFAST_UNSPECIFIED([] { std::unique_ptr<int> pInt; FAIL_FAST_IMMEDIATE_IF_NULL(pNull); });
    REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_IMMEDIATE_IF_NULL(MDEC(pValidRef())); });
    REQUIRE_FAILFAST_UNSPECIFIED([] { std::unique_ptr<int> pInt; FAIL_FAST_IF_NULL(pNull); });
    REQUIRE_FAILFAST(S_OK, [] { std::unique_ptr<int> pInt(new int(5)); FAIL_FAST_IF_NULL(MDEC(pInt)); });

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { Microsoft::WRL::ComPtr<IUnknown> ptr; RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { Microsoft::WRL::ComPtr<IUnknown> ptr; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); });

    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { std::shared_ptr<int> ptr; RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { std::shared_ptr<int> ptr; LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); });
    REQUIRE_RETURNS(S_OK, [] { std::shared_ptr<int> ptr(new int(5)); RETURN_IF_NULL_ALLOC(MDEC(ptr)); return S_OK; });
    REQUIRE_LOG(S_OK, [] { std::shared_ptr<int> ptr(new int(5)); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(ptr)); });

#ifdef __cplusplus_winrt
    REQUIRE_RETURNS(E_OUTOFMEMORY, [] { Platform::String^ str(nullptr); RETURN_IF_NULL_ALLOC(MDEC(str)); return S_OK; });
    REQUIRE_LOG(E_OUTOFMEMORY, [] { Platform::String^ str(nullptr); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(str)); });
    REQUIRE_RETURNS(S_OK, [] { Platform::String^ str(L"a"); RETURN_IF_NULL_ALLOC(MDEC(str)); return S_OK; });
    REQUIRE_LOG(S_OK, [] { Platform::String^ str(L"a"); LOG_HR_IF_NULL(MDEC(E_OUTOFMEMORY), MDEC(str)); });
#endif
}

#define WRAP_LAMBDA(code)           [&] {code;};

//these macros should all have compile errors due to use of an invalid type
void InvalidTypeChecks()
{
    std::unique_ptr<int> boolCastClass;
    std::vector<int> noBoolCastClass;

    //WRAP_LAMBDA(RETURN_IF_FAILED(fTrue));
    //WRAP_LAMBDA(RETURN_IF_FAILED(fTRUE));
    //WRAP_LAMBDA(RETURN_IF_FAILED(boolCastClass));
    //WRAP_LAMBDA(RETURN_IF_FAILED(noBoolCastClass));
    //WRAP_LAMBDA(RETURN_IF_FAILED(errSuccess));

    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(fTrue));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(noBoolCastClass));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(hrOK));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE(errSuccess));

    //WRAP_LAMBDA(RETURN_HR_IF(errSuccess, false));
    //WRAP_LAMBDA(RETURN_HR_IF(errSuccess, true));
    //WRAP_LAMBDA(RETURN_HR_IF(hrOK, noBoolCastClass));
    //WRAP_LAMBDA(RETURN_HR_IF(hrOK, hrOK));
    //WRAP_LAMBDA(RETURN_HR_IF(hrOK, errSuccess));

    //WRAP_LAMBDA(RETURN_HR_IF_NULL(errSuccess, nullptr));
    //WRAP_LAMBDA(RETURN_HR_IF_NULL(errSuccess, pValid));

    //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(noBoolCastClass));
    //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(errSuccess));
    //WRAP_LAMBDA(RETURN_LAST_ERROR_IF(hrOK));

    //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(fTrue));
    //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(fTRUE));
    //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(boolCastClass));
    //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(noBoolCastClass));
    //WRAP_LAMBDA(RETURN_IF_FAILED_EXPECTED(errSuccess));

    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(fTrue));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(noBoolCastClass));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(hrOK));
    //WRAP_LAMBDA(RETURN_IF_WIN32_BOOL_FALSE_EXPECTED(errSuccess));

    //LOG_IF_FAILED(fTrue);
    //LOG_IF_FAILED(fTRUE);
    //LOG_IF_FAILED(boolCastClass);
    //LOG_IF_FAILED(noBoolCastClass);
    //LOG_IF_FAILED(errSuccess);

    //LOG_IF_WIN32_BOOL_FALSE(fTrue);
    //LOG_IF_WIN32_BOOL_FALSE(noBoolCastClass);
    //LOG_IF_WIN32_BOOL_FALSE(hrOK);
    //LOG_IF_WIN32_BOOL_FALSE(errSuccess);

    //LOG_HR_IF(errSuccess, false);
    //LOG_HR_IF(errSuccess, true);
    //LOG_HR_IF(hrOK, noBoolCastClass);
    //LOG_HR_IF(hrOK, hrOK);
    //LOG_HR_IF(hrOK, errSuccess);

    //FAIL_FAST_IF_FAILED(fTrue);
    //FAIL_FAST_IF_FAILED(fTRUE);
    //FAIL_FAST_IF_FAILED(boolCastClass);
    //FAIL_FAST_IF_FAILED(noBoolCastClass);
    //FAIL_FAST_IF_FAILED(errSuccess);

    //FAIL_FAST_IF_WIN32_BOOL_FALSE(fTrue);
    //FAIL_FAST_IF_WIN32_BOOL_FALSE(noBoolCastClass);
    //FAIL_FAST_IF_WIN32_BOOL_FALSE(hrOK);
    //FAIL_FAST_IF_WIN32_BOOL_FALSE(errSuccess);

    //FAIL_FAST_HR_IF(errSuccess, false);
    //FAIL_FAST_HR_IF(errSuccess, true);
    //FAIL_FAST_HR_IF(hrOK, noBoolCastClass);
    //FAIL_FAST_HR_IF(hrOK, hrOK);
    //FAIL_FAST_HR_IF(hrOK, errSuccess);

    //THROW_IF_FAILED(fTrue);
    //THROW_IF_FAILED(fTRUE);
    //THROW_IF_FAILED(boolCastClass);
    //THROW_IF_FAILED(noBoolCastClass);
    //THROW_IF_FAILED(errSuccess);

    //THROW_IF_WIN32_BOOL_FALSE(fTrue);
    //THROW_IF_WIN32_BOOL_FALSE(noBoolCastClass);
    //THROW_IF_WIN32_BOOL_FALSE(hrOK);
    //THROW_IF_WIN32_BOOL_FALSE(errSuccess);

    //THROW_HR_IF(errSuccess, false);
    //THROW_HR_IF(errSuccess, true);
    //THROW_HR_IF(hrOK, noBoolCastClass);
    //THROW_HR_IF(hrOK, hrOK);
    //THROW_HR_IF(hrOK, errSuccess);

    //FAIL_FAST_IF(noBoolCastClass);
    //FAIL_FAST_IF(hrOK);
    //FAIL_FAST_IF(errSuccess);

    //FAIL_FAST_IMMEDIATE_IF_FAILED(fTrue);
    //FAIL_FAST_IMMEDIATE_IF_FAILED(fTRUE);
    //FAIL_FAST_IMMEDIATE_IF_FAILED(boolCastClass);
    //FAIL_FAST_IMMEDIATE_IF_FAILED(noBoolCastClass);
    //FAIL_FAST_IMMEDIATE_IF_FAILED(errSuccess);

    //FAIL_FAST_IMMEDIATE_IF(noBoolCastClass);
    //FAIL_FAST_IMMEDIATE_IF(hrOK);
    //FAIL_FAST_IMMEDIATE_IF(errSuccess);
}

TEST_CASE("WindowsInternalTests::UniqueHandle", "[resource][unique_any]")
{
    {
        // default construction test
        wil::unique_handle spHandle;
        REQUIRE(spHandle.get() == nullptr);

        // null ptr assignment creation
        wil::unique_handle spNullHandle = nullptr;
        REQUIRE(spNullHandle.get() == nullptr);

        // explicit construction from the invalid value
        wil::unique_handle spInvalidHandle(nullptr);
        REQUIRE(spInvalidHandle.get() == nullptr);

        // valid handle creation
        wil::unique_handle spValidHandle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
        REQUIRE(spValidHandle.get() != nullptr);
        auto const handleValue = spValidHandle.get();

        // r-value construction
        wil::unique_handle spMoveHandle = wistd::move(spValidHandle);
        REQUIRE(spValidHandle.get() == nullptr);
        REQUIRE(spMoveHandle.get() == handleValue);

        // nullptr-assignment
        spNullHandle = nullptr;
        REQUIRE(spNullHandle.get() == nullptr);

        // r-value assignment
        spValidHandle = wistd::move(spMoveHandle);
        REQUIRE(spValidHandle.get() == handleValue);
        REQUIRE(spMoveHandle.get() == nullptr);

        // swap
        spValidHandle.swap(spMoveHandle);
        REQUIRE(spValidHandle.get() == nullptr);
        REQUIRE(spMoveHandle.get() == handleValue);

        // operator bool
        REQUIRE_FALSE(spValidHandle);
        REQUIRE(spMoveHandle);

        // release
        auto ptrValidHandle = spValidHandle.release();
        auto ptrMoveHandle = spMoveHandle.release();
        REQUIRE(ptrValidHandle == nullptr);
        REQUIRE(ptrMoveHandle == handleValue);
        REQUIRE(spValidHandle.get() == nullptr);
        REQUIRE(spMoveHandle.get() == nullptr);

        // reset
        spValidHandle.reset();
        spMoveHandle.reset();
        REQUIRE(spValidHandle.get() == nullptr);
        REQUIRE(spMoveHandle.get() == nullptr);
        spValidHandle.reset(ptrValidHandle);
        spMoveHandle.reset(ptrMoveHandle);
        REQUIRE(spValidHandle.get() == nullptr);
        REQUIRE(spMoveHandle.get() == handleValue);
        spNullHandle.reset(nullptr);
        REQUIRE(spNullHandle.get() == nullptr);

        // address
        REQUIRE(*spMoveHandle.addressof() == handleValue);
        REQUIRE(*spMoveHandle.put() == nullptr);
        *spMoveHandle.put() = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);
        REQUIRE(spMoveHandle);
        REQUIRE(*(&spMoveHandle) == nullptr);
        *(&spMoveHandle) = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);
        REQUIRE(spMoveHandle);
    }
    {
        // default construction test
        wil::unique_hfile spHandle;
        REQUIRE(spHandle.get() == INVALID_HANDLE_VALUE);

        // implicit construction from the invalid value
        wil::unique_hfile spNullHandle; // = nullptr;       // method explicitly disabled as nullptr isn't the invalid value
        REQUIRE(spNullHandle.get() == INVALID_HANDLE_VALUE);

        // assignment from the invalid value
        // spNullHandle = nullptr;                          // method explicitly disabled as nullptr isn't the invalid value
        REQUIRE(spNullHandle.get() == INVALID_HANDLE_VALUE);

        // explicit construction from the invalid value
        wil::unique_hfile spInvalidHandle(INVALID_HANDLE_VALUE);
        REQUIRE(spInvalidHandle.get() == INVALID_HANDLE_VALUE);

        // valid handle creation
        wchar_t tempFileName[MAX_PATH];
        REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName));

        CREATEFILE2_EXTENDED_PARAMETERS params = { sizeof(params) };
        params.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
        wil::unique_hfile spValidHandle(::CreateFile2(tempFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, &params));

        ::DeleteFileW(tempFileName);
        REQUIRE(spValidHandle.get() != INVALID_HANDLE_VALUE);
        auto const handleValue = spValidHandle.get();

        // r-value construction
        wil::unique_hfile spMoveHandle = wistd::move(spValidHandle);
        REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE);
        REQUIRE(spMoveHandle.get() == handleValue);

        // nullptr-assignment -- uncomment to check intentional compilation error
        // spNullHandle = nullptr;

        // r-value assignment
        spValidHandle = wistd::move(spMoveHandle);
        REQUIRE(spValidHandle.get() == handleValue);
        REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE);

        // swap
        spValidHandle.swap(spMoveHandle);
        REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE);
        REQUIRE(spMoveHandle.get() == handleValue);

        // operator bool
        REQUIRE_FALSE(spValidHandle);
        REQUIRE(spMoveHandle);

        // release
        auto ptrValidHandle = spValidHandle.release();
        auto ptrMoveHandle = spMoveHandle.release();
        REQUIRE(ptrValidHandle == INVALID_HANDLE_VALUE);
        REQUIRE(ptrMoveHandle == handleValue);
        REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE);
        REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE);

        // reset
        spValidHandle.reset();
        spMoveHandle.reset();
        REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE);
        REQUIRE(spMoveHandle.get() == INVALID_HANDLE_VALUE);
        spValidHandle.reset(ptrValidHandle);
        spMoveHandle.reset(ptrMoveHandle);
        REQUIRE(spValidHandle.get() == INVALID_HANDLE_VALUE);
        REQUIRE(spMoveHandle.get() == handleValue);
        // uncomment to test intentional compilation error due to conflict with INVALID_HANDLE_VALUE
        // spNullHandle.reset(nullptr);

        // address
        REQUIRE(*spMoveHandle.addressof() == handleValue);
        REQUIRE(*(&spMoveHandle) == INVALID_HANDLE_VALUE);

        wchar_t tempFileName2[MAX_PATH];
        REQUIRE_SUCCEEDED(witest::GetTempFileName(tempFileName2));

        CREATEFILE2_EXTENDED_PARAMETERS params2 = { sizeof(params2) };
        params2.dwFileAttributes = FILE_ATTRIBUTE_TEMPORARY;
        *(&spMoveHandle) = ::CreateFile2(tempFileName2, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, CREATE_ALWAYS, &params2);

        ::DeleteFileW(tempFileName2);
        REQUIRE(spMoveHandle);

        // ensure that mistaken nullptr usage is not valid...
        spMoveHandle.reset();
        *(&spMoveHandle) = nullptr;
        REQUIRE_FALSE(spMoveHandle);
    }

    auto hFirst = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);
    auto hSecond= ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);

    wil::unique_handle spLeft(hFirst);
    wil::unique_handle spRight(hSecond);

    REQUIRE(spRight.get() == hSecond);
    REQUIRE(spLeft.get() == hFirst);
    swap(spLeft, spRight);
    REQUIRE(spLeft.get() == hSecond);
    REQUIRE(spRight.get() == hFirst);
    swap(spLeft, spRight);

    REQUIRE((spLeft.get() == spRight.get()) == (spLeft == spRight));
    REQUIRE((spLeft.get() != spRight.get()) == (spLeft != spRight));
    REQUIRE((spLeft.get() < spRight.get()) == (spLeft < spRight));
    REQUIRE((spLeft.get() <= spRight.get()) == (spLeft <= spRight));
    REQUIRE((spLeft.get() >= spRight.get()) == (spLeft >= spRight));
    REQUIRE((spLeft.get() > spRight.get()) == (spLeft > spRight));

    // test stl container use (hash & std::less)
#ifdef WIL_ENABLE_EXCEPTIONS
    std::unordered_set<wil::unique_handle> hashSet;
    hashSet.insert(std::move(spLeft));
    hashSet.insert(std::move(spRight));
    std::multiset<wil::unique_handle> set;
    set.insert(std::move(spLeft));
    set.insert(std::move(spRight));
#endif
}

#ifdef WIL_ENABLE_EXCEPTIONS
TEST_CASE("WindowsInternalTests::SharedHandle", "[resource][shared_any]")
{
    // default construction
    wil::shared_handle spHandle;
    REQUIRE(spHandle.get() == nullptr);

    // pointer construction
    wil::shared_handle spValid(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    auto ptr = spValid.get();
    REQUIRE(spValid.get() != nullptr);

    // null construction
    wil::shared_handle spNull = nullptr;
    REQUIRE(spNull.get() == nullptr);

    // Present to verify that it doesn't compile (disabled)
    // wil::shared_hfile spFile = nullptr;

    // copy construction
    wil::shared_handle spCopy = spValid;
    REQUIRE(spCopy.get() == ptr);

    // r-value construction
    wil::shared_handle spMove = wistd::move(spCopy);
    REQUIRE(spMove.get() == ptr);
    REQUIRE(spCopy.get() == nullptr);

    // unique handle construction
    wil::shared_handle spFromUnique = wil::unique_handle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    REQUIRE(spFromUnique.get() != nullptr);

    // direct assignment
    wil::shared_handle spAssign;
    spAssign = spValid;
    REQUIRE(spAssign.get() == ptr);

    // empty reset
    spFromUnique.reset();
    REQUIRE(spFromUnique.get() == nullptr);

    // reset against unique ptr
    spFromUnique.reset(wil::unique_handle(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0)));
    REQUIRE(spFromUnique.get() != nullptr);

    // reset against raw pointer
    spAssign.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    REQUIRE(spAssign.get() != nullptr);
    REQUIRE(spAssign.get() != ptr);

    // ref-count checks
    REQUIRE(spAssign.use_count() == 1);

    // bool operator
    REQUIRE(spAssign);
    spAssign.reset();
    REQUIRE_FALSE(spAssign);

    // swap and compare
    wil::shared_handle sp1(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    wil::shared_handle sp2(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    auto ptr1 = sp1.get();
    auto ptr2 = sp2.get();
    sp1.swap(sp2);
    REQUIRE(sp1.get() == ptr2);
    REQUIRE(sp2.get() == ptr1);
    swap(sp1, sp2);
    REQUIRE(sp1.get() == ptr1);
    REQUIRE(sp2.get() == ptr2);
    REQUIRE((ptr1 == ptr2) == (sp1 == sp2));
    REQUIRE((ptr1 != ptr2) == (sp1 != sp2));
    REQUIRE((ptr1 < ptr2) == (sp1 < sp2));
    REQUIRE((ptr1 <= ptr2) == (sp1 <= sp2));
    REQUIRE((ptr1 > ptr2) == (sp1 > sp2));
    REQUIRE((ptr1 >= ptr2) == (sp1 >= sp2));

    // construction
    wil::weak_handle wh;
    REQUIRE_FALSE(wh.lock());
    wil::weak_handle wh1 = sp1;
    REQUIRE(wh1.lock());
    REQUIRE(wh1.lock().get() == ptr1);
    wil::weak_handle wh1copy = wh1;
    REQUIRE(wh1copy.lock());

    // assignment
    wh = wh1;
    REQUIRE(wh.lock().get() == ptr1);
    wh = sp2;
    REQUIRE(wh.lock().get() == ptr2);

    // reset
    wh.reset();
    REQUIRE_FALSE(wh.lock());

    // expiration
    wh = sp1;
    sp1.reset();
    REQUIRE(wh.expired());
    REQUIRE_FALSE(wh.lock());

    // swap
    wh1 = sp1;
    wil::weak_handle wh2 = sp2;
    ptr1 = sp1.get();
    ptr2 = sp2.get();
    REQUIRE(wh1.lock().get() == ptr1);
    REQUIRE(wh2.lock().get() == ptr2);
    wh1.swap(wh2);
    REQUIRE(wh1.lock().get() == ptr2);
    REQUIRE(wh2.lock().get() == ptr1);
    swap(wh1, wh2);
    REQUIRE(wh1.lock().get() == ptr1);
    REQUIRE(wh2.lock().get() == ptr2);

    // put
    sp1.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    REQUIRE(sp1);
    sp1.put();   // frees the pointer...
    REQUIRE_FALSE(sp1);
    sp2 = sp1;
    REQUIRE_FALSE(sp2);
    *sp1.put() = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);
    REQUIRE(sp1);
    REQUIRE_FALSE(sp2);

    // address
    sp1.reset(::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0));
    REQUIRE(sp1);
    &sp1;   // frees the pointer...
    REQUIRE_FALSE(sp1);
    sp2 = sp1;
    REQUIRE_FALSE(sp2);
    *(&sp1) = ::CreateEventEx(nullptr, nullptr, CREATE_EVENT_INITIAL_SET, 0);
    REQUIRE(sp1);
    REQUIRE_FALSE(sp2);

    // test stl container use (hash & std::less)
    std::unordered_set<wil::shared_handle> hashSet;
    hashSet.insert(sp1);
    hashSet.insert(sp2);
    std::set<wil::shared_handle> set;
    set.insert(sp1);
    set.insert(sp2);
}
#endif

template <typename event_t>
void EventTestCommon()
{
    // Constructor tests...
    event_t e1;
    REQUIRE_FALSE(e1);
    event_t e2(::CreateEventEx(nullptr, nullptr, 0, 0));
    REQUIRE(e2);
    wil::unique_handle h1(::CreateEventEx(nullptr, nullptr, 0, 0));
    REQUIRE(h1);
    event_t e3(h1.release());
    REQUIRE(e3);
    REQUIRE_FALSE(h1);
    event_t e4(std::move(e2));
    REQUIRE(e4);
    REQUIRE_FALSE(e2);

    // inherited address tests...
    REQUIRE(e4);
    &e4;
    REQUIRE_FALSE(e4);
    auto hFill = ::CreateEventEx(nullptr, nullptr, 0, 0);
    *(&e4) = hFill;
    REQUIRE(e4);
    REQUIRE(*e4.addressof() == hFill);
    REQUIRE(e4);

    // assignment...
    event_t e5;
    e5 = std::move(e4);
    REQUIRE(e5);
    REQUIRE_FALSE(e4);

    // various event-based tests
    event_t eManual;
    eManual.create(wil::EventOptions::ManualReset);
    REQUIRE_FALSE(eManual.is_signaled());
    eManual.SetEvent();
    REQUIRE(eManual.is_signaled());
    eManual.ResetEvent();
    REQUIRE_FALSE(eManual.is_signaled());
    {
        auto exit = eManual.SetEvent_scope_exit();
        REQUIRE_FALSE(eManual.is_signaled());
    }
    REQUIRE(eManual.is_signaled());
    {
        auto exit = eManual.ResetEvent_scope_exit();
        REQUIRE(eManual.is_signaled());
    }
    REQUIRE_FALSE(eManual.is_signaled());
    REQUIRE_FALSE(eManual.wait(50));
    REQUIRE_FALSE(wil::handle_wait(eManual.get(), 50));
    eManual.SetEvent();
    REQUIRE(eManual.wait(50));
    REQUIRE(wil::handle_wait(eManual.get(), 50));

    REQUIRE(eManual.wait(50));

    REQUIRE(eManual.try_create(wil::EventOptions::ManualReset, L"IExist"));
    REQUIRE_FALSE(eManual.try_open(L"IDontExist"));
}

template <typename mutex_t>
void MutexTestCommon()
{
    // Constructor tests...
    mutex_t m1;
    REQUIRE_FALSE(m1);
    mutex_t m2(::CreateMutexEx(nullptr, nullptr, 0, 0));
    REQUIRE(m2);
    wil::unique_handle h1(::CreateMutexEx(nullptr, nullptr, 0, 0));
    REQUIRE(h1);
    mutex_t m3(h1.release());
    REQUIRE(m3);
    REQUIRE_FALSE(h1);
    mutex_t m4(std::move(m2));
    REQUIRE(m4);
    REQUIRE_FALSE(m2);

    // inherited address tests...
    REQUIRE(m4);
    &m4;
    REQUIRE_FALSE(m4);
    auto hFill = ::CreateMutexEx(nullptr, nullptr, 0, 0);
    *(&m4) = hFill;
    REQUIRE(m4);
    REQUIRE(*m4.addressof() == hFill);
    REQUIRE(m4);

    // assignment...
    mutex_t m5;
    m5 = std::move(m4);
    REQUIRE(m5);
    REQUIRE_FALSE(m4);

    // various mutex-based tests
    mutex_t eManual;
    eManual.create(nullptr, CREATE_MUTEX_INITIAL_OWNER);
    eManual.ReleaseMutex();
    eManual.create(nullptr, CREATE_MUTEX_INITIAL_OWNER);
    {
        auto release = eManual.ReleaseMutex_scope_exit();
    }
    {
        DWORD dwStatus;
        auto release = eManual.acquire(&dwStatus);
        REQUIRE(release);
        REQUIRE(dwStatus == WAIT_OBJECT_0);
    }

    // pass-through methods -- test compilation;
    REQUIRE(eManual.try_create(L"FOO-TEST"));
    REQUIRE(eManual.try_open(L"FOO-TEST"));
}

template <typename semaphore_t>
void SemaphoreTestCommon()
{
    // Constructor tests...
    semaphore_t m1;
    REQUIRE_FALSE(m1);
    semaphore_t m2(::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0));
    REQUIRE(m2);
    wil::unique_handle h1(::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0));
    REQUIRE(h1);
    semaphore_t m3(h1.release());
    REQUIRE(m3);
    REQUIRE_FALSE(h1);
    semaphore_t m4(std::move(m2));
    REQUIRE(m4);
    REQUIRE_FALSE(m2);

    // inherited address tests...
    REQUIRE(m4);
    &m4;
    REQUIRE_FALSE(m4);
    auto hFill = ::CreateSemaphoreEx(nullptr, 1, 1, nullptr, 0, 0);
    *(&m4) = hFill;
    REQUIRE(m4);
    REQUIRE(*m4.addressof() == hFill);
    REQUIRE(m4);

    // assignment...
    semaphore_t m5;
    m5 = std::move(m4);
    REQUIRE(m5);
    REQUIRE_FALSE(m4);

    // various semaphore-based tests
    semaphore_t eManual;
    eManual.create(1, 1);
    WaitForSingleObjectEx(eManual.get(), INFINITE, true);
    eManual.ReleaseSemaphore();
    eManual.create(1, 1);
    WaitForSingleObjectEx(eManual.get(), INFINITE, true);
    {
        auto release = eManual.ReleaseSemaphore_scope_exit();
    }
    {
        DWORD dwStatus;
        auto release = eManual.acquire(&dwStatus);
        REQUIRE(release);
        REQUIRE(dwStatus == WAIT_OBJECT_0);
    }

    // pass-through methods -- test compilation;
    REQUIRE(eManual.try_create(1, 1, L"BAR-TEST"));
    REQUIRE(eManual.try_open(L"BAR-TEST"));
}

TEST_CASE("WindowsInternalTests::HandleWrappers", "[resource][unique_any]")
{
    EventTestCommon<wil::unique_event_nothrow>();
    EventTestCommon<wil::unique_event_failfast>();

    // intentionally disabled in the non-exception version...
    // wil::unique_event_nothrow testEvent2(wil::EventOptions::ManualReset);
    wil::unique_event_failfast testEvent3(wil::EventOptions::ManualReset);
#ifdef WIL_ENABLE_EXCEPTIONS
    EventTestCommon<wil::unique_event>();

    wil::unique_event testEvent(wil::EventOptions::ManualReset);
    {
        REQUIRE_FALSE(wil::event_is_signaled(testEvent.get()));
        auto eventSet = wil::SetEvent_scope_exit(testEvent.get());
        REQUIRE_FALSE(wil::event_is_signaled(testEvent.get()));
    }
    {
        REQUIRE(wil::event_is_signaled(testEvent.get()));
        auto eventSet = wil::ResetEvent_scope_exit(testEvent.get());
        REQUIRE(wil::event_is_signaled(testEvent.get()));
    }
    REQUIRE_FALSE(wil::event_is_signaled(testEvent.get()));
    REQUIRE_FALSE(wil::handle_wait(testEvent.get(), 0));

    // Exception-based - no return
    testEvent.create(wil::EventOptions::ManualReset);
#endif

    // Error-code based -- returns HR
    wil::unique_event_nothrow testEventNoExcept;
    REQUIRE(SUCCEEDED(testEventNoExcept.create(wil::EventOptions::ManualReset)));


    MutexTestCommon<wil::unique_mutex_nothrow>();
    MutexTestCommon<wil::unique_mutex_failfast>();

    // intentionally disabled in the non-exception version...
    // wil::unique_mutex_nothrow testMutex2(L"FOO-TEST-2");
    wil::unique_mutex_failfast testMutex3(L"FOO-TEST-3");
#ifdef WIL_ENABLE_EXCEPTIONS
    MutexTestCommon<wil::unique_mutex>();

    wil::unique_mutex testMutex(L"FOO-TEST");
    WaitForSingleObjectEx(testMutex.get(), INFINITE, TRUE);
    {
        auto release = wil::ReleaseMutex_scope_exit(testMutex.get());
    }

    // Exception-based - no return
    testMutex.create(nullptr);
#endif

    // Error-code based -- returns HR
    wil::unique_mutex_nothrow testMutexNoExcept;
    REQUIRE(SUCCEEDED(testMutexNoExcept.create(nullptr)));


    SemaphoreTestCommon<wil::unique_semaphore_nothrow>();
    SemaphoreTestCommon<wil::unique_semaphore_failfast>();

    // intentionally disabled in the non-exception version...
    // wil::unique_semaphore_nothrow testSemaphore2(1, 1);
    wil::unique_semaphore_failfast testSemaphore3(1, 1);
#ifdef WIL_ENABLE_EXCEPTIONS
    SemaphoreTestCommon<wil::unique_semaphore>();

    wil::unique_semaphore testSemaphore(1, 1);
    WaitForSingleObjectEx(testSemaphore.get(), INFINITE, true);
    {
        auto release = wil::ReleaseSemaphore_scope_exit(testSemaphore.get());
    }

    // Exception-based - no return
    testSemaphore.create(1, 1);
#endif

    // Error-code based -- returns HR
    wil::unique_semaphore_nothrow testSemaphoreNoExcept;
    REQUIRE(SUCCEEDED(testSemaphoreNoExcept.create(1, 1)));

    auto unique_cotaskmem_string_failfast1 = wil::make_cotaskmem_string_failfast(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_failfast1.get()) == 0);

    auto unique_cotaskmem_string_nothrow1 = wil::make_cotaskmem_string_nothrow(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_nothrow1.get()) == 0);

    auto unique_cotaskmem_string_nothrow2 = wil::make_cotaskmem_string_nothrow(L"");
    REQUIRE(wcscmp(L"", unique_cotaskmem_string_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
    auto unique_cotaskmem_string_te1 = wil::make_cotaskmem_string(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_te1.get()) == 0);

    auto unique_cotaskmem_string_te2 = wil::make_cotaskmem_string(L"");
    REQUIRE(wcscmp(L"", unique_cotaskmem_string_te2.get()) == 0);

    auto unique_cotaskmem_string_range1 = wil::make_cotaskmem_string(L"Foo", 2);
    REQUIRE(wcscmp(L"Fo", unique_cotaskmem_string_range1.get()) == 0);

    auto unique_cotaskmem_string_range2 = wil::make_cotaskmem_string(nullptr, 2);
    unique_cotaskmem_string_range2.get()[0] = L'F';
    unique_cotaskmem_string_range2.get()[1] = L'o';
    REQUIRE(wcscmp(L"Fo", unique_cotaskmem_string_range2.get()) == 0);

    auto unique_cotaskmem_string_range3 = wil::make_cotaskmem_string(nullptr, 0);
    REQUIRE(wcscmp(L"", unique_cotaskmem_string_range3.get()) == 0);
#endif

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    {
        auto verify = MakeSecureDeleterMallocSpy();
        REQUIRE_SUCCEEDED(::CoRegisterMallocSpy(verify.Get()));
        auto removeSpy = wil::scope_exit([&] { ::CoRevokeMallocSpy(); });

        auto unique_cotaskmem_string_secure_failfast1 = wil::make_cotaskmem_string_secure_failfast(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_failfast1.get()) == 0);

        auto unique_cotaskmem_string_secure_nothrow1 = wil::make_cotaskmem_string_secure_nothrow(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_nothrow1.get()) == 0);

        auto unique_cotaskmem_string_secure_nothrow2 = wil::make_cotaskmem_string_secure_nothrow(L"");
        REQUIRE(wcscmp(L"", unique_cotaskmem_string_secure_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
        auto unique_cotaskmem_string_secure_te1 = wil::make_cotaskmem_string_secure(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_cotaskmem_string_secure_te1.get()) == 0);

        auto unique_cotaskmem_string_secure_te2 = wil::make_cotaskmem_string_secure(L"");
        REQUIRE(wcscmp(L"", unique_cotaskmem_string_secure_te2.get()) == 0);
#endif
    }

    auto unique_hlocal_string_failfast1 = wil::make_hlocal_string_failfast(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_hlocal_string_failfast1.get()) == 0);

    auto unique_hlocal_string_nothrow1 = wil::make_hlocal_string_nothrow(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_hlocal_string_nothrow1.get()) == 0);

    auto unique_hlocal_string_nothrow2 = wil::make_hlocal_string_nothrow(L"");
    REQUIRE(wcscmp(L"", unique_hlocal_string_nothrow2.get()) == 0);

    auto unique_hlocal_ansistring_failfast1 = wil::make_hlocal_ansistring_failfast("Foo");
    REQUIRE(strcmp("Foo", unique_hlocal_ansistring_failfast1.get()) == 0);

    auto unique_hlocal_ansistring_nothrow1 = wil::make_hlocal_ansistring_nothrow("Foo");
    REQUIRE(strcmp("Foo", unique_hlocal_ansistring_nothrow1.get()) == 0);

    auto unique_hlocal_ansistring_nothrow2 = wil::make_hlocal_ansistring_nothrow("");
    REQUIRE(strcmp("", unique_hlocal_ansistring_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
    auto unique_hlocal_string_te1 = wil::make_hlocal_string(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_hlocal_string_te1.get()) == 0);

    auto unique_hlocal_string_te2 = wil::make_hlocal_string(L"");
    REQUIRE(wcscmp(L"", unique_hlocal_string_te2.get()) == 0);

    auto unique_hlocal_string_range1 = wil::make_hlocal_string(L"Foo", 2);
    REQUIRE(wcscmp(L"Fo", unique_hlocal_string_range1.get()) == 0);

    auto unique_hlocal_string_range2 = wil::make_hlocal_string(nullptr, 2);
    unique_hlocal_string_range2.get()[0] = L'F';
    unique_hlocal_string_range2.get()[1] = L'o';
    REQUIRE(wcscmp(L"Fo", unique_hlocal_string_range2.get()) == 0);

    auto unique_hlocal_string_range3 = wil::make_hlocal_string(nullptr, 0);
    REQUIRE(wcscmp(L"", unique_hlocal_string_range3.get()) == 0);

    auto unique_hlocal_ansistring_te1 = wil::make_hlocal_ansistring("Foo");
    REQUIRE(strcmp("Foo", unique_hlocal_ansistring_te1.get()) == 0);

    auto unique_hlocal_ansistring_te2 = wil::make_hlocal_ansistring("");
    REQUIRE(strcmp("", unique_hlocal_ansistring_te2.get()) == 0);

    auto unique_hlocal_ansistring_range1 = wil::make_hlocal_ansistring("Foo", 2);
    REQUIRE(strcmp("Fo", unique_hlocal_ansistring_range1.get()) == 0);

    auto unique_hlocal_ansistring_range2 = wil::make_hlocal_ansistring(nullptr, 2);
    unique_hlocal_ansistring_range2.get()[0] = L'F';
    unique_hlocal_ansistring_range2.get()[1] = L'o';
    REQUIRE(strcmp("Fo", unique_hlocal_ansistring_range2.get()) == 0);

    auto unique_hlocal_ansistring_range3 = wil::make_hlocal_ansistring(nullptr, 0);
    REQUIRE(strcmp("", unique_hlocal_ansistring_range3.get()) == 0);
#endif

    {
        auto verify = MakeSecureDeleterMallocSpy();
        REQUIRE_SUCCEEDED(::CoRegisterMallocSpy(verify.Get()));
        auto removeSpy = wil::scope_exit([&] { ::CoRevokeMallocSpy(); });

        auto unique_hlocal_string_secure_failfast1 = wil::make_hlocal_string_secure_failfast(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_failfast1.get()) == 0);

        auto unique_hlocal_string_secure_nothrow1 = wil::make_hlocal_string_secure_nothrow(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_nothrow1.get()) == 0);

        auto unique_hlocal_string_secure_nothrow2 = wil::make_hlocal_string_secure_nothrow(L"");
        REQUIRE(wcscmp(L"", unique_hlocal_string_secure_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
        auto unique_hlocal_string_secure_te1 = wil::make_hlocal_string_secure(L"Foo");
        REQUIRE(wcscmp(L"Foo", unique_hlocal_string_secure_te1.get()) == 0);

        auto unique_hlocal_string_secure_te2 = wil::make_hlocal_string_secure(L"");
        REQUIRE(wcscmp(L"", unique_hlocal_string_secure_te2.get()) == 0);
#endif
    }

    auto unique_process_heap_string_failfast1 = wil::make_process_heap_string_failfast(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_process_heap_string_failfast1.get()) == 0);

    auto unique_process_heap_string_nothrow1 = wil::make_process_heap_string_nothrow(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_process_heap_string_nothrow1.get()) == 0);

    auto unique_process_heap_string_nothrow2 = wil::make_process_heap_string_nothrow(L"");
    REQUIRE(wcscmp(L"", unique_process_heap_string_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
    auto unique_process_heap_string_te1 = wil::make_process_heap_string(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_process_heap_string_te1.get()) == 0);

    auto unique_process_heap_string_te2 = wil::make_process_heap_string(L"");
    REQUIRE(wcscmp(L"", unique_process_heap_string_te2.get()) == 0);

    auto unique_process_heap_string_range1 = wil::make_process_heap_string(L"Foo", 2);
    REQUIRE(wcscmp(L"Fo", unique_process_heap_string_range1.get()) == 0);

    auto unique_process_heap_string_range2 = wil::make_process_heap_string(nullptr, 2);
    unique_process_heap_string_range2.get()[0] = L'F';
    unique_process_heap_string_range2.get()[1] = L'o';
    REQUIRE(wcscmp(L"Fo", unique_process_heap_string_range2.get()) == 0);

    auto unique_process_heap_string_range3 = wil::make_process_heap_string(nullptr, 0);
    REQUIRE(wcscmp(L"", unique_process_heap_string_range3.get()) == 0);
#endif

#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */

    auto unique_bstr_failfast1 = wil::make_bstr_failfast(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_bstr_failfast1.get()) == 0);

    auto unique_bstr_nothrow1 = wil::make_bstr_nothrow(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_bstr_nothrow1.get()) == 0);

    auto unique_bstr_nothrow2 = wil::make_bstr_nothrow(L"");
    REQUIRE(wcscmp(L"", unique_bstr_nothrow2.get()) == 0);

#ifdef WIL_ENABLE_EXCEPTIONS
    auto unique_bstr_te1 = wil::make_bstr(L"Foo");
    REQUIRE(wcscmp(L"Foo", unique_bstr_te1.get()) == 0);

    auto unique_bstr_te2 = wil::make_bstr(L"");
    REQUIRE(wcscmp(L"", unique_bstr_te2.get()) == 0);


    auto testString = wil::make_cotaskmem_string(L"Foo");
    {
        auto cleanupMemory = wil::SecureZeroMemory_scope_exit(testString.get());
    }
    REQUIRE(0 == testString.get()[0]);

    auto testString2 = wil::make_cotaskmem_string(L"Bar");
    {
        auto cleanupMemory = wil::SecureZeroMemory_scope_exit(testString2.get(), wcslen(testString2.get()) * sizeof(testString2.get()[0]));
    }
    REQUIRE(0 == testString2.get()[0]);
#endif
}

TEST_CASE("WindowsInternalTests::Locking", "[resource]")
{
    {
        SRWLOCK rwlock = SRWLOCK_INIT;
        {
            auto lock = wil::AcquireSRWLockExclusive(&rwlock);
            REQUIRE(lock);

            auto lockRecursive = wil::TryAcquireSRWLockExclusive(&rwlock);
            REQUIRE_FALSE(lockRecursive);

            auto lockRecursiveShared = wil::TryAcquireSRWLockShared(&rwlock);
            REQUIRE_FALSE(lockRecursiveShared);
        }
        {
            auto lock = wil::AcquireSRWLockShared(&rwlock);
            REQUIRE(lock);

            auto lockRecursive = wil::TryAcquireSRWLockShared(&rwlock);
            REQUIRE(lockRecursive);

            auto lockRecursiveExclusive = wil::TryAcquireSRWLockExclusive(&rwlock);
            REQUIRE_FALSE(lockRecursiveExclusive);
        }
        {
            auto lock = wil::TryAcquireSRWLockExclusive(&rwlock);
            REQUIRE(lock);
        }
        {
            auto lock = wil::TryAcquireSRWLockShared(&rwlock);
            REQUIRE(lock);
        }
    }

    {
        wil::srwlock rwlock;
        {
            auto lock = rwlock.lock_exclusive();
            REQUIRE(lock);

            auto lockRecursive = rwlock.try_lock_exclusive();
            REQUIRE_FALSE(lockRecursive);

            auto lockRecursiveShared = rwlock.try_lock_shared();
            REQUIRE_FALSE(lockRecursiveShared);
        }
        {
            auto lock = rwlock.lock_shared();
            REQUIRE(lock);

            auto lockRecursive = rwlock.try_lock_shared();
            REQUIRE(lockRecursive);

            auto lockRecursiveExclusive = rwlock.try_lock_exclusive();
            REQUIRE_FALSE(lockRecursiveExclusive);
        }
        {
            auto lock = rwlock.try_lock_exclusive();
            REQUIRE(lock);
        }
        {
            auto lock = rwlock.try_lock_shared();
            REQUIRE(lock);
        }
    }

    {
        CRITICAL_SECTION cs;
        ::InitializeCriticalSectionEx(&cs, 0, 0);
        auto lock = wil::EnterCriticalSection(&cs);
        REQUIRE(lock);
        auto tryLock = wil::TryEnterCriticalSection(&cs);
        REQUIRE(tryLock);
    }
    {
        wil::critical_section cs;
        auto lock = cs.lock();
        REQUIRE(lock);
        auto tryLock = cs.try_lock();
        REQUIRE(tryLock);
    }
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE("WindowsInternalTests::GDIWrappers", "[resource]")
{
    {
        auto dc = wil::GetDC(::GetDesktopWindow());
    }
    {
        auto dc = wil::GetWindowDC(::GetDesktopWindow());
    }
    {
        auto dc = wil::BeginPaint(::GetDesktopWindow());
        wil::unique_hbrush brush(::CreateSolidBrush(0xffffff));
        auto select = wil::SelectObject(dc.get(), brush.get());
    }
}
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */

void TestOutHandle(_Out_ HANDLE *pHandle)
{
    *pHandle = nullptr;
}

void TestOutAlloc(_Out_ int **ppInt)
{
    *ppInt = new int(5);
}

void TestCoTask(_Outptr_result_buffer_(*charCount) PWSTR *ppsz, size_t *charCount)
{
    *charCount = 0;
    PWSTR psz = static_cast<PWSTR>(::CoTaskMemAlloc(10));
    if (psz != nullptr)
    {
        *charCount = 5;
        *psz = L'\0';
    }
    *ppsz = psz;
}

void TestVoid(_Out_ void **ppv)
{
    *ppv = nullptr;
}

void TestByte(_Out_ BYTE **ppByte)
{
    *ppByte = nullptr;
}

struct my_deleter
{
    template <typename T>
    void operator()(T* p) const
    {
        delete p;
    }
};

TEST_CASE("WindowsInternalTests::WistdTests", "[resource][wistd]")
{
    wil::unique_handle spHandle;
    TestOutHandle(wil::out_param(spHandle));

    wistd::unique_ptr<int> spInt;
    TestOutAlloc(wil::out_param(spInt));

    std::unique_ptr<int> spIntStd;
    TestOutAlloc(wil::out_param(spIntStd));

    wil::unique_cotaskmem_string spsz0;
    size_t count;
    TestCoTask(wil::out_param(spsz0), &count);

    std::unique_ptr<wchar_t[], wil::cotaskmem_deleter> spsz1;
    TestCoTask(wil::out_param(spsz1), &count);

    wistd::unique_ptr<wchar_t[], wil::cotaskmem_deleter> spsz2;
    TestCoTask(wil::out_param(spsz2), &count);

    wil::unique_cotaskmem_ptr<wchar_t[]> spsz3;
    TestCoTask(wil::out_param(spsz3), &count);

    wil::unique_cotaskmem_ptr<void> spv;
    TestVoid(wil::out_param(spv));

    std::unique_ptr<int> spIntStd2;
    TestByte(wil::out_param_ptr<BYTE**>(spIntStd2));

    struct Nothing
    {
        int n;
        Nothing(int param) : n(param) {}
        void Method() {}
    };

    auto spff = wil::make_unique_failfast<Nothing>(3);
    auto sp = wil::make_unique_nothrow<Nothing>(3);
    REQUIRE(sp);
#ifdef WIL_ENABLE_EXCEPTIONS
    THROW_IF_NULL_ALLOC(sp.get());
    THROW_IF_NULL_ALLOC(sp);
#endif
    sp->Method();
    decltype(sp) sp2;
    sp2 = wistd::move(sp);
    sp2.get();

    wistd::unique_ptr<int> spConstruct;
    wistd::unique_ptr<int> spConstruct2 = nullptr;
    spConstruct = nullptr;
    wistd::unique_ptr<int> spConstruct3(new int(3));
    my_deleter d;
    wistd::unique_ptr<int, my_deleter> spConstruct4(new int(4), d);
    wistd::unique_ptr<int, my_deleter> spConstruct5(new int(5), my_deleter());
    wistd::unique_ptr<int> spConstruct6(wistd::unique_ptr<int>(new int(6)));
    spConstruct = std::move(spConstruct2);
    spConstruct.swap(spConstruct2);
    REQUIRE(*spConstruct4 == 4);
    spConstruct4.get();
    if (spConstruct4)
    {
    }
    spConstruct.reset();
    spConstruct.release();

    auto spTooBig = wil::make_unique_nothrow<int[]>(static_cast<size_t>(-1));
    REQUIRE_FALSE(spTooBig);
    // REQUIRE_FAILFAST_UNSPECIFIED([]{ auto spTooBigFF = wil::make_unique_failfast<int[]>(static_cast<size_t>(-1)); });

    object_counter_state state;
    count = 0;
    {
        object_counter c{ state };
        REQUIRE(state.instance_count() == 1);

        wistd::function<void(int)> fn = [&count, c](int param)
        {
            count += param;
        };
        REQUIRE(state.instance_count() == 2);

        fn(3);
        REQUIRE(count == 3);
    }
    REQUIRE(state.instance_count() == 0);

    count = 0;
    {
        wistd::function<void(int)> fn;
        {
            object_counter c{ state };
            REQUIRE(state.instance_count() == 1);
            fn = [&count, c](int param)
            {
                count += param;
            };
            REQUIRE(state.instance_count() == 2);
        }
        REQUIRE(state.instance_count() == 1);
        fn(3);
        REQUIRE(count == 3);
    }

    {
        // Size Check -- the current implementation allows for 10 pointers to be passed through the lambda
        int a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12;
        (void)a11; (void)a12;

        wistd::function<void()> fn = [&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10]()
        {
            (void)a1; (void)a2; (void)a3; (void)a4; (void)a5; (void)a6; (void)a7; (void)a8; (void)a9; (void)a10;
        };
        auto fnCopy = fn;

        // Uncomment to double-check static assert.  Reports:
        // "The sizeof(wistd::function) has grown too large for the reserved buffer (10 pointers).  Refactor to reduce size of the capture."
        // wistd::function<void()> fn2 = [&a1, &a2, &a3, &a4, &a5, &a6, &a7, &a8, &a9, &a10, &a11]()
        // {
        //     a1; a2; a3; a4; a5; a6; a7; a8; a9; a10; a11;
        // };
    }
}

template <typename test_t, typename lambda_t>
void NullptrRaiiTests(lambda_t const &fnCreate)
{
    // nullptr_t construct
    test_t var1 = nullptr;      // implicit
    REQUIRE_FALSE(var1);
    test_t var2(nullptr);       // explicit
    REQUIRE_FALSE(var2);

    // nullptr_t assingment
    var1.reset(fnCreate());
    REQUIRE(var1);
    var1 = nullptr;
    REQUIRE_FALSE(var1);

    // nullptr_t reset
    var1.reset(fnCreate());
    REQUIRE(var1);
    var1.reset(nullptr);
    REQUIRE_FALSE(var1);
}

template <typename test_t, typename lambda_t>
void ReleaseRaiiTests(lambda_t const &fnCreate)
{
    test_t var1(fnCreate());
    REQUIRE(var1);
    auto ptr = var1.release();
    REQUIRE_FALSE(var1);
    REQUIRE(ptr != test_t::policy::invalid_value());
    REQUIRE(var1.get() == test_t::policy::invalid_value());

    var1.reset(ptr);
}

template <typename test_t, typename lambda_t>
void GetRaiiTests(lambda_t const &fnCreate)
{
    test_t var1;
    REQUIRE_FALSE(var1);
    REQUIRE(var1.get() == test_t::policy::invalid_value());

    var1.reset(fnCreate());
    REQUIRE(var1);
    REQUIRE(var1.get() != test_t::policy::invalid_value());
}

template <typename test_t, typename lambda_t>
void SharedRaiiTests(lambda_t const &fnCreate)
{
    // copy construction
    test_t var1(fnCreate());
    REQUIRE(var1);
    test_t var2 = var1;     // implicit
    REQUIRE(var1);
    REQUIRE(var2);
    test_t var3(var1);      // explicit

    // copy assignment
    test_t var4(fnCreate());
    test_t var5;
    var5 = var4;
    REQUIRE(var5);
    REQUIRE(var4);

    // r-value construction from unique_ptr
    typename test_t::unique_t unique1(fnCreate());
    test_t var7(std::move(unique1));    // explicit
    REQUIRE(var7);
    REQUIRE_FALSE(unique1);
    typename test_t::unique_t unique2(fnCreate());
    test_t var8 = std::move(unique2);   // implicit
    REQUIRE(var8);
    REQUIRE_FALSE(unique2);

    // r-value assignment from unique_ptr
    var8.reset();
    REQUIRE_FALSE(var8);
    unique2.reset(fnCreate());
    var8 = std::move(unique2);
    REQUIRE(var8);
    REQUIRE_FALSE(unique2);

    // use_count()
    REQUIRE(var8.use_count() == 1);
    auto var9 = var8;
    REQUIRE(var8.use_count() == 2);
}

template <typename test_t, typename lambda_t>
void WeakRaiiTests(lambda_t const &fnCreate)
{
    typedef typename test_t::shared_t shared_type;

    // base constructor
    test_t weak1;

    // construct from shared
    shared_type shared1(fnCreate());
    test_t weak2 = shared1;             // implicit
    test_t weak3(shared1);              // explicit

    // construct from weak
    test_t weak4 = weak2;               // implicit
    test_t weak5(weak2);                // explicit

    // assign from weak
    weak2 = weak5;

    // assign from shared
    weak2 = shared1;

    // reset
    weak2.reset();
    REQUIRE_FALSE(weak2.lock());

    // swap
    test_t swap1 = shared1;
    test_t swap2;
    REQUIRE(swap1.lock());
    REQUIRE_FALSE(swap2.lock());
    swap1.swap(swap2);
    REQUIRE_FALSE(swap1.lock());
    REQUIRE(swap2.lock());

    // expired
    REQUIRE_FALSE(swap2.expired());
    shared1.reset();
    REQUIRE(swap2.expired());

    // lock
    shared1.reset(fnCreate());
    weak1 = shared1;
    auto shared2 = weak1.lock();
    REQUIRE(shared2);
    shared2.reset();
    REQUIRE(weak1.lock());
    shared1.reset();
    shared2 = weak1.lock();
    REQUIRE_FALSE(shared2);
}

template <typename test_t, typename lambda_t>
void AddressRaiiTests(lambda_t const &fnCreate)
{
    test_t var1(fnCreate());
    REQUIRE(var1);

    &var1;
    REQUIRE_FALSE(var1);                              // the address operator does an auto-release

    *(&var1) = fnCreate();
    REQUIRE(var1);

    var1.put();
    REQUIRE_FALSE(var1);                              // verify that 'put()' does an auto-release

    *var1.put() = fnCreate();
    REQUIRE(var1);

    REQUIRE(var1.addressof() != nullptr);
    REQUIRE(var1);                               // verify that 'addressof()' does not auto-release
}

template <typename test_t, typename lambda_t>
void BasicRaiiTests(lambda_t const &fnCreate)
{
    auto invalidHandle = test_t::policy::invalid_value();

    // no-constructor construction
    test_t var1;
    REQUIRE_FALSE(var1);

    // construct from a given resource
    test_t var2(fnCreate());    // r-value
    REQUIRE(var2);
    test_t var3(invalidHandle);      // l-value
    REQUIRE_FALSE(var3);

    // r-value construct from the same type
    test_t var4(std::move(var2));                   // explicit
    REQUIRE(var4);
    REQUIRE_FALSE(var2);
    test_t varMove(fnCreate());
    test_t var4implicit = std::move(varMove);       // implicit
    REQUIRE(var4implicit);

    // move assignment
    var2 = std::move(var4);
    REQUIRE(var2);
    REQUIRE_FALSE(var4);

    // swap
    var2.swap(var4);
    REQUIRE(var4);
    REQUIRE_FALSE(var2);

    // explicit bool cast
    REQUIRE(static_cast<bool>(var4));
    REQUIRE_FALSE(static_cast<bool>(var2));

    // reset
    var4.reset();
    REQUIRE_FALSE(var4);
    var4.reset(fnCreate());     // r-value
    REQUIRE(var4);
    var4.reset(invalidHandle);       // l-value
    REQUIRE_FALSE(var4);
}

template <typename test_t>
void EventRaiiTests()
{
    test_t var1;
    var1.create(wil::EventOptions::ManualReset);
    REQUIRE_FALSE(wil::event_is_signaled(var1.get()));

    // SetEvent/ResetEvent
    var1.SetEvent();
    REQUIRE(wil::event_is_signaled(var1.get()));
    var1.ResetEvent();
    REQUIRE_FALSE(wil::event_is_signaled(var1.get()));

    // SetEvent/ResetEvent scope_exit
    {
        auto exit = var1.SetEvent_scope_exit();
        REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
    }
    REQUIRE(wil::event_is_signaled(var1.get()));
    {
        auto exit = var1.ResetEvent_scope_exit();
        REQUIRE(wil::event_is_signaled(var1.get()));
    }
    REQUIRE_FALSE(wil::event_is_signaled(var1.get()));

    // is_signaled
    REQUIRE_FALSE(var1.is_signaled());

    // wait
    REQUIRE_FALSE(var1.wait(50));

    // try_create
    bool exists = false;
    REQUIRE(var1.try_create(wil::EventOptions::ManualReset, L"wiltestevent", nullptr, &exists));
    REQUIRE_FALSE(exists);
    test_t var2;
    REQUIRE(var2.try_create(wil::EventOptions::ManualReset, L"wiltestevent", nullptr, &exists));
    REQUIRE(exists);
    test_t var3;
    REQUIRE_FALSE(var3.try_create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces", nullptr, &exists));
    REQUIRE(::GetLastError() != ERROR_SUCCESS);

    // try_open
    test_t var4;
    REQUIRE_FALSE(var4.try_open(L"\\illegal\\chars\\too\\\\many\\\\namespaces"));
    REQUIRE(::GetLastError() != ERROR_SUCCESS);
    REQUIRE(var4.try_open(L"wiltestevent"));
}

void EventTests()
{
    static_assert(sizeof(wil::unique_event_nothrow) == sizeof(HANDLE), "event_t should be sizeof(HANDLE) to allow for raw array utilization");

    auto fnCreate = []() { return CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, 0); };

    BasicRaiiTests<wil::unique_event_nothrow>(fnCreate);
    NullptrRaiiTests<wil::unique_event_nothrow>(fnCreate);
    GetRaiiTests<wil::unique_event_nothrow>(fnCreate);
    ReleaseRaiiTests<wil::unique_event_nothrow>(fnCreate);
    AddressRaiiTests<wil::unique_event_nothrow>(fnCreate);
    EventRaiiTests<wil::unique_event_nothrow>();

    BasicRaiiTests<wil::unique_event_failfast>(fnCreate);
    NullptrRaiiTests<wil::unique_event_failfast>(fnCreate);
    GetRaiiTests<wil::unique_event_failfast>(fnCreate);
    ReleaseRaiiTests<wil::unique_event_failfast>(fnCreate);
    AddressRaiiTests<wil::unique_event_failfast>(fnCreate);
    EventRaiiTests<wil::unique_event_failfast>();

    wil::unique_event_nothrow event4;
    REQUIRE(S_OK == event4.create(wil::EventOptions::ManualReset));
    REQUIRE(FAILED(event4.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces")));

#ifdef WIL_ENABLE_EXCEPTIONS
    static_assert(sizeof(wil::unique_event) == sizeof(HANDLE), "event_t should be sizeof(HANDLE) to allow for raw array utilization");

    BasicRaiiTests<wil::unique_event>(fnCreate);
    NullptrRaiiTests<wil::unique_event>(fnCreate);
    GetRaiiTests<wil::unique_event>(fnCreate);
    ReleaseRaiiTests<wil::unique_event>(fnCreate);
    AddressRaiiTests<wil::unique_event>(fnCreate);
    EventRaiiTests<wil::unique_event>();

    BasicRaiiTests<wil::shared_event>(fnCreate);
    NullptrRaiiTests<wil::shared_event>(fnCreate);
    GetRaiiTests<wil::shared_event>(fnCreate);
    AddressRaiiTests<wil::shared_event>(fnCreate);
    SharedRaiiTests<wil::shared_event>(fnCreate);
    EventRaiiTests<wil::shared_event>();

    WeakRaiiTests<wil::weak_event>(fnCreate);

    // explicitly disabled
    // wil::unique_event_nothrow event1(wil::EventOptions::ManualReset);
    wil::unique_event event2(wil::EventOptions::ManualReset);
    wil::shared_event event3(wil::EventOptions::ManualReset);

    event2.create(wil::EventOptions::ManualReset);
    REQUIRE(event2);
    event3.create(wil::EventOptions::ManualReset);
    REQUIRE(event3);
    REQUIRE_THROWS(event2.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces") );
    REQUIRE_THROWS(event3.create(wil::EventOptions::ManualReset, L"\\illegal\\chars\\too\\\\many\\\\namespaces") );

    wil::unique_event var1(wil::EventOptions::ManualReset);
    REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
    {
        auto autoset = wil::SetEvent_scope_exit(var1.get());
        REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
        REQUIRE(autoset.get() == var1.get());
        // &autoset;                // verified disabled
        // autoset.addressof();     // verified disabled
    }
    REQUIRE(wil::event_is_signaled(var1.get()));
    {
        auto autoreset = wil::ResetEvent_scope_exit(var1.get());
        REQUIRE(wil::event_is_signaled(var1.get()));
        autoreset.reset();
        REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
    }
    {
        auto autoset = wil::SetEvent_scope_exit(var1.get());
        REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
        autoset.release();
        REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
    }
    REQUIRE_FALSE(wil::event_is_signaled(var1.get()));
#endif
}

typedef wil::unique_struct<PROPVARIANT, decltype(&::PropVariantClear), ::PropVariantClear> unique_prop_variant_no_init;

void SetPropVariantValue(_In_ int intVal, _Out_ PROPVARIANT* ppropvar)
{
    ppropvar->intVal = intVal;
    ppropvar->vt = VT_INT;
}

template<typename T>
void TestUniquePropVariant()
{
    {
        wil::unique_prop_variant spPropVariant;
        REQUIRE(spPropVariant.vt == VT_EMPTY);
    }

    // constructor test
    {
        PROPVARIANT propVariant;
        SetPropVariantValue(12, &propVariant);
        T spPropVariant(propVariant);
        REQUIRE(((spPropVariant.intVal == 12) && (spPropVariant.vt == VT_INT)));

        T spPropVariant2(wistd::move(propVariant));
        REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT)));

        //spPropVariant = propVariant;    // deleted function
        //spPropVariant = wistd::move(propVariant);  // deleted function
        //spPropVariant.swap(propVariant);  //deleted function
    }

    // move constructor
    {
        T spPropVariant;
        SetPropVariantValue(12, &spPropVariant);
        REQUIRE(((spPropVariant.intVal == 12) && (spPropVariant.vt == VT_INT)));

        T spPropVariant2(wistd::move(spPropVariant));
        REQUIRE(spPropVariant.vt == VT_EMPTY);
        REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT)));

        //T spPropVariant3(spPropVariant);     // deleted function
        //spPropVariant2 = spPropVariant;     // deleted function
    }

    // move operator
    {
        T spPropVariant;
        SetPropVariantValue(12, &spPropVariant);
        T spPropVariant2 = wistd::move(spPropVariant);
        REQUIRE(spPropVariant.vt == VT_EMPTY);
        REQUIRE(((spPropVariant2.intVal == 12) && (spPropVariant2.vt == VT_INT)));
    }

    // reset
    {
        PROPVARIANT propVariant;
        SetPropVariantValue(22, &propVariant);
        T spPropVariant;
        SetPropVariantValue(12, &spPropVariant);
        T spPropVariant2;

        //spPropVariant2.reset(spPropVariant);    // deleted function
        spPropVariant.reset(propVariant);
        REQUIRE(spPropVariant.intVal == 22);
        REQUIRE(propVariant.intVal == 22);

        spPropVariant.reset();
        REQUIRE(spPropVariant.vt == VT_EMPTY);
    }

    // swap
    {
        T spPropVariant;
        SetPropVariantValue(12, &spPropVariant);
        T spPropVariant2;
        SetPropVariantValue(22, &spPropVariant2);

        spPropVariant.swap(spPropVariant2);
        REQUIRE(spPropVariant.intVal == 22);
        REQUIRE(spPropVariant2.intVal == 12);
    }

    // release, addressof, reset_and_addressof
    {
        T spPropVariant;
        SetPropVariantValue(12, &spPropVariant);

        [](PROPVARIANT* propVariant)
        {
            REQUIRE(propVariant->vt == VT_EMPTY);
        }(spPropVariant.reset_and_addressof());

        SetPropVariantValue(12, &spPropVariant);
        PROPVARIANT* pPropVariant = spPropVariant.addressof();
        REQUIRE(pPropVariant->intVal == 12);
        REQUIRE(spPropVariant.intVal == 12);

        PROPVARIANT propVariant = spPropVariant.release();
        REQUIRE(propVariant.intVal == 12);
        REQUIRE(spPropVariant.vt == VT_EMPTY);
    }
}

TEST_CASE("WindowsInternalTests::ResourceTemplateTests", "[resource]")
{
    EventTests();
    TestUniquePropVariant<wil::unique_prop_variant>();
    TestUniquePropVariant<unique_prop_variant_no_init>();
}

inline unsigned long long ToInt64(const FILETIME &ft)
{
    return (static_cast<unsigned long long>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
}

inline FILETIME FromInt64(unsigned long long i64)
{
    FILETIME ft = { static_cast<DWORD>(i64), static_cast<DWORD>(i64 >> 32) };
    return ft;
}

TEST_CASE("WindowsInternalTests::Win32HelperTests", "[win32_helpers]")
{
    auto systemTime = wil::filetime::get_system_time();
    REQUIRE(ToInt64(systemTime) == wil::filetime::to_int64(systemTime));
    auto systemTime64 = wil::filetime::to_int64(systemTime);
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
    auto ft1 = FromInt64(systemTime64);
    auto ft2 = wil::filetime::from_int64(systemTime64);
    REQUIRE(CompareFileTime(&ft1, &ft2) == 0);
#endif /* WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) */

    REQUIRE(systemTime64 == wil::filetime::to_int64(wil::filetime::from_int64(systemTime64)));
    REQUIRE((systemTime64 + wil::filetime_duration::one_hour) == (systemTime64 + (wil::filetime_duration::one_minute * 60)));
    auto systemTimePlusOneHour = wil::filetime::add(systemTime, wil::filetime_duration::one_hour);
    auto systemTimePlusOneHour64 = wil::filetime::to_int64(systemTimePlusOneHour);
    REQUIRE(systemTimePlusOneHour64 == (systemTime64 + wil::filetime_duration::one_hour));
}

TEST_CASE("WindowsInternalTests::InitOnceNonTests")
{
    bool called = false;
    bool winner = false;
    INIT_ONCE init{};
    REQUIRE_FALSE(wil::init_once_initialized(init));

    // Call, but fail. Should transport the HRESULT back, but mark us as not the winner
    called = false;
    winner = false;
    REQUIRE(E_FAIL == wil::init_once_nothrow(init, [&] { called = true; return E_FAIL; }, &winner));
    REQUIRE_FALSE(wil::init_once_initialized(init));
    REQUIRE(called);
    REQUIRE_FALSE(winner);

    // Call, succeed. Should mark us as the winner.
    called = false;
    winner = false;
    REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = true; return S_OK; }, &winner));
    REQUIRE(wil::init_once_initialized(init));
    REQUIRE(called);
    REQUIRE(winner);

    // Call again. Should not actually be invoked and should not be the winner
    called = false;
    winner = false;
    REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = false; return S_OK; }, &winner));
    REQUIRE(wil::init_once_initialized(init));
    REQUIRE_FALSE(called);
    REQUIRE_FALSE(winner);

    // Call again. Still not invoked, but we don't care if we're the winner
    called = false;
    REQUIRE_SUCCEEDED(wil::init_once_nothrow(init, [&] { called = false; return S_OK; }));
    REQUIRE(wil::init_once_initialized(init));
    REQUIRE_FALSE(called);

#ifdef WIL_ENABLE_EXCEPTIONS
    called = false;
    winner = false;
    init = {};

    // A thrown exception leaves the object un-initialized
    REQUIRE_THROWS_AS(winner = wil::init_once(init, [&] { called = true; throw wil::ResultException(E_FAIL); }), wil::ResultException);
    REQUIRE_FALSE(wil::init_once_initialized(init));
    REQUIRE(called);
    REQUIRE_FALSE(winner);

    // Success!
    called = false;
    winner = false;
    REQUIRE_NOTHROW(winner = wil::init_once(init, [&] { called = true; }));
    REQUIRE(wil::init_once_initialized(init));
    REQUIRE(called);
    REQUIRE(winner);

    // No-op success!
    called = false;
    winner = false;
    REQUIRE_NOTHROW(winner = wil::init_once(init, [&] { called = true; }));
    REQUIRE(wil::init_once_initialized(init));
    REQUIRE_FALSE(called);
    REQUIRE_FALSE(winner);
#endif // WIL_ENABLE_EXCEPTIONS
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE("WindowsInternalTests::TestUniquePointerCases", "[resource][unique_any]")
{
    // wil::unique_process_heap_ptr tests
    {
        wil::unique_process_heap_ptr<void> empty; // null case
    }
    {
        wil::unique_process_heap_ptr<void> heapMemory(::HeapAlloc(::GetProcessHeap(), 0, 100));
        REQUIRE(static_cast<bool>(heapMemory));
    }

    // wil::unique_cotaskmem_ptr tests
    {
        wil::unique_cotaskmem_ptr<void> empty; // null case
    }
    {
        wil::unique_cotaskmem_ptr<void> cotaskmemMemory(CoTaskMemAlloc(100));
        REQUIRE(static_cast<bool>(cotaskmemMemory));
    }
    {
        auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow<DWORD>(42);
        REQUIRE(static_cast<bool>(cotaskmemMemory));
        REQUIRE(*cotaskmemMemory == static_cast<DWORD>(42));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow<S>();
        REQUIRE(static_cast<bool>(cotaskmemMemory));
        REQUIRE(cotaskmemMemory->s == static_cast<size_t>(42));
    }
    {
        auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow<BYTE[]>(12);
        REQUIRE(static_cast<bool>(cotaskmemArrayMemory));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        const size_t size = 12;
        auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow<S[]>(size);
        REQUIRE(static_cast<bool>(cotaskmemArrayMemory));
        bool verified = true;
        for (auto& elem : wil::make_range(cotaskmemArrayMemory.get(), size)) if (elem.s != 42) verified = false;
        REQUIRE(verified);
    }

    // wil::unique_cotaskmem_secure_ptr tests
    {
        wil::unique_cotaskmem_secure_ptr<void> empty; // null case
    }
    {
        wil::unique_cotaskmem_secure_ptr<void> cotaskmemMemory(CoTaskMemAlloc(100));
        REQUIRE(static_cast<bool>(cotaskmemMemory));
    }
    {
        auto cotaskmemMemory = wil::make_unique_cotaskmem_secure_nothrow<DWORD>(42);
        REQUIRE(static_cast<bool>(cotaskmemMemory));
        REQUIRE(*cotaskmemMemory == static_cast<DWORD>(42));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        auto cotaskmemMemory = wil::make_unique_cotaskmem_secure_nothrow<S>();
        REQUIRE(static_cast<bool>(cotaskmemMemory));
        REQUIRE(cotaskmemMemory->s == static_cast<size_t>(42));
    }
    {
        auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_secure_nothrow<BYTE[]>(12);
        REQUIRE(static_cast<bool>(cotaskmemArrayMemory));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        const size_t size = 12;
        auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_secure_nothrow<S[]>(size);
        REQUIRE(static_cast<bool>(cotaskmemArrayMemory));
        bool verified = true;
        for (auto& elem : wil::make_range(cotaskmemArrayMemory.get(), size)) if (elem.s != 42) verified = false;
        REQUIRE(verified);
    }

    // wil::unique_hlocal_ptr tests
    {
        wil::unique_hlocal_ptr<void> empty; // null case
    }
    {
        wil::unique_hlocal_ptr<void> localMemory(LocalAlloc(LPTR, 100));
        REQUIRE(static_cast<bool>(localMemory));
    }
    {
        auto localMemory = wil::make_unique_hlocal_nothrow<DWORD>(42);
        REQUIRE(static_cast<bool>(localMemory));
        REQUIRE(*localMemory == static_cast<DWORD>(42));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        auto localMemory = wil::make_unique_hlocal_nothrow<S>();
        REQUIRE(static_cast<bool>(localMemory));
        REQUIRE(localMemory->s == static_cast<size_t>(42));
    }
    {
        auto localArrayMemory = wil::make_unique_hlocal_nothrow<BYTE[]>(12);
        REQUIRE(static_cast<bool>(localArrayMemory));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        const size_t size = 12;
        auto localArrayMemory = wil::make_unique_hlocal_nothrow<S[]>(size);
        REQUIRE(static_cast<bool>(localArrayMemory));
        bool verified = true;
        for (auto& elem : wil::make_range(localArrayMemory.get(), size)) if (elem.s != 42) verified = false;
        REQUIRE(verified);
    }

    // wil::unique_hlocal_secure_ptr tests
    {
        wil::unique_hlocal_secure_ptr<void> empty; // null case
    }
    {
        wil::unique_hlocal_secure_ptr<void> localMemory(LocalAlloc(LPTR, 100));
        REQUIRE(static_cast<bool>(localMemory));
    }
    {
        auto localMemory = wil::make_unique_hlocal_secure_nothrow<DWORD>(42);
        REQUIRE(static_cast<bool>(localMemory));
        REQUIRE(*localMemory == static_cast<DWORD>(42));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        auto localMemory = wil::make_unique_hlocal_secure_nothrow<S>();
        REQUIRE(static_cast<bool>(localMemory));
        REQUIRE(localMemory->s == static_cast<size_t>(42));
    }
    {
        auto localArrayMemory = wil::make_unique_hlocal_secure_nothrow<BYTE[]>(12);
        REQUIRE(static_cast<bool>(localArrayMemory));
    }
    {
        struct S { size_t s; S() : s(42) {} };
        const size_t size = 12;
        auto localArrayMemory = wil::make_unique_hlocal_secure_nothrow<S[]>(size);
        REQUIRE(static_cast<bool>(localArrayMemory));
        bool verified = true;
        for (auto& elem : wil::make_range(localArrayMemory.get(), size)) if (elem.s != 42) verified = false;
        REQUIRE(verified);
    }

    // wil::unique_hglobal_ptr tests
    {
        wil::unique_hglobal_ptr<void> empty; // null case
    }
    {
        wil::unique_hglobal_ptr<void> globalMemory(GlobalAlloc(GPTR, 100));
        REQUIRE(static_cast<bool>(globalMemory));
    }

    {
        // The following uses are blocked due to a static assert failure

        //struct S { ~S() {} };

        //auto cotaskmemMemory = wil::make_unique_cotaskmem_nothrow<S>();
        //auto cotaskmemArrayMemory = wil::make_unique_cotaskmem_nothrow<S[]>(1);
        //auto cotaskmemMemory2 = wil::make_unique_cotaskmem_secure_nothrow<S>();
        //auto cotaskmemArrayMemory2 = wil::make_unique_cotaskmem_secure_nothrow<S[]>(1);

        //auto localMemory = wil::make_unique_hlocal_nothrow<S>();
        //auto localArrayMemory = wil::make_unique_hlocal_nothrow<S[]>(1);
        //auto localMemory2 = wil::make_unique_hlocal_secure_nothrow<S>();
        //auto localArrayMemory2 = wil::make_unique_hlocal_secure_nothrow<S[]>(1);
    }
}
#endif

void GetDWORDArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) DWORD** numbers)
{
    const size_t size = 5;
    auto ptr = static_cast<DWORD*>(::CoTaskMemAlloc(sizeof(DWORD) * size));
    REQUIRE(ptr);
    ::ZeroMemory(ptr, sizeof(DWORD) * size);
    *numbers = ptr;
    *count = size;
}

void GetHSTRINGArray(_Out_ ULONG* count, _Outptr_result_buffer_(*count) HSTRING** strings)
{
    const size_t size = 5;
    auto ptr = static_cast<HSTRING*>(::CoTaskMemAlloc(sizeof(HSTRING) * size));
    REQUIRE(ptr);
    for (UINT i = 0; i < size; ++i)
    {
        REQUIRE_SUCCEEDED(WindowsCreateString(L"test", static_cast<UINT32>(wcslen(L"test")), &ptr[i]));
    }
    *strings = ptr;
    *count = static_cast<ULONG>(size);
}

void GetPOINTArray(_Out_ UINT32* count, _Outptr_result_buffer_(*count) POINT** points)
{
    const size_t size = 5;
    auto ptr = static_cast<POINT*>(::CoTaskMemAlloc(sizeof(POINT) * size));
    REQUIRE(ptr);
    for (UINT i = 0; i < size; ++i)
    {
        ptr[i].x = ptr[i].y = i;
    }
    *points = ptr;
    *count = static_cast<UINT32>(size);
}

#ifdef WIL_ENABLE_EXCEPTIONS
void GetHANDLEArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) HANDLE** events)
{
    const size_t size = 5;
    HANDLE* ptr = reinterpret_cast<HANDLE*>(::CoTaskMemAlloc(sizeof(HANDLE) * size));
    for (auto& val : wil::make_range(ptr, size))
    {
        val = wil::unique_event(wil::EventOptions::ManualReset).release();
    }
    *events = ptr;
    *count = size;
}
#endif

interface __declspec(uuid("EDCA4ADC-DF46-442A-A69D-FDFD8BC37B31")) IFakeObject : public IUnknown
{
   STDMETHOD_(void, DoStuff)() = 0;
};

class ArrayTestObject : witest::AllocatedObject,
    public Microsoft::WRL::RuntimeClass<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::RuntimeClassType::ClassicCom>, IFakeObject>
{
public:
    HRESULT RuntimeClassInitialize(UINT n) { m_number = n; return S_OK; };
    STDMETHOD_(void, DoStuff)() {}
private:
    UINT m_number{};
};

void GetUnknownArray(_Out_ size_t* count, _Outptr_result_buffer_(*count) IFakeObject*** objects)
{
    const size_t size = 5;
    auto ptr = reinterpret_cast<IFakeObject**>(::CoTaskMemAlloc(sizeof(IFakeObject*) * size));
    REQUIRE(ptr);
    for (UINT i = 0; i < size; ++i)
    {
        Microsoft::WRL::ComPtr<IFakeObject> obj;
        REQUIRE_SUCCEEDED(Microsoft::WRL::MakeAndInitialize<ArrayTestObject>(&obj, i));
        ptr[i] = obj.Detach();
    }
    *objects = ptr;
    *count = size;
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
TEST_CASE("WindowsInternalTests::TestUniqueArrayCases", "[resource]")
{
    // wil::unique_cotaskmem_array_ptr tests
    {
        wil::unique_cotaskmem_array_ptr<DWORD> values;
        GetDWORDArray(values.size_address(), &values);
    }
    {
        wil::unique_cotaskmem_array_ptr<wil::unique_hstring> strings;
        GetHSTRINGArray(strings.size_address<ULONG>(), &strings);
        for (ULONG i = 0; i < strings.size(); ++i)
        {
            REQUIRE(WindowsGetStringLen(strings[i]) == wcslen(L"test"));
        }
    }
    {
        wil::unique_cotaskmem_array_ptr<POINT> points;
        GetPOINTArray(points.size_address<UINT32>(), &points);
        for (ULONG i = 0; i < points.size(); ++i)
        {
            REQUIRE((ULONG)points[i].x == i);
        }
    }
#ifdef WIL_ENABLE_EXCEPTIONS
    {
        wil::unique_cotaskmem_array_ptr<wil::unique_event> events;
        GetHANDLEArray(events.size_address(), &events);
    }
    {
        wil::unique_cotaskmem_array_ptr<wil::com_ptr<IFakeObject>> objects;
        GetUnknownArray(objects.size_address(), &objects);
        for (ULONG i = 0; i < objects.size(); ++i)
        {
            objects[i]->DoStuff();
        }
    }
#endif
    {
        wil::unique_cotaskmem_array_ptr<DWORD> values = nullptr;
        REQUIRE(!values);
        REQUIRE(values.size() == 0);

        // move onto self
        values = wistd::move(values);
        REQUIRE(!values);

        // fetch
        GetDWORDArray(values.size_address(), &values);
        REQUIRE(!!values);
        REQUIRE(values.size() > 0);
        REQUIRE(!values.empty());

        // move onto self
        values = wistd::move(values);
        REQUIRE(!!values);

        decltype(values) values2(wistd::move(values));
        REQUIRE(!values);
        REQUIRE(!!values2);
        REQUIRE(values2.size() > 0);

        values = wistd::move(values2);
        REQUIRE(!!values);
        REQUIRE(!values2);

        values = nullptr;
        REQUIRE(!values);
        GetDWORDArray(values.size_address(), values.put());
        REQUIRE(!!values);

        values = nullptr;
        REQUIRE(!values);
        GetDWORDArray(values.size_address(), &values);
        REQUIRE(!!values);

        auto size = values.size();
        auto ptr = values.release();

        REQUIRE(!values);
        REQUIRE(values.empty());

        decltype(values) values3(ptr, size);
        REQUIRE(!!values3);
        REQUIRE(values3.size() == size);

        values3.swap(values);
        REQUIRE(!!values);
        REQUIRE(!values.empty());
        REQUIRE(!values3);
        REQUIRE(values3.empty());

        REQUIRE(!values.empty());
        size_t count = 0;
        for (auto it = values.begin(); it != values.end(); ++it)
        {
            ++count;
        }
        REQUIRE(count == values.size());

        count = 0;
        for (auto it = values.cbegin(); it != values.cend(); ++it)
        {
            ++count;
        }
        REQUIRE(count == values.size());

        for (size_t index = 0; index < values.size(); index++)
        {
            auto& val = values[index];
            REQUIRE(val == 0);
        }

        auto& front = values.front();
        REQUIRE(front == 0);
        auto& back = values.back();
        REQUIRE(back == 0);

        [](const wil::unique_cotaskmem_array_ptr<DWORD>& cvalues)
        {
            size_t count = 0;
            for (auto it = cvalues.begin(); it != cvalues.end(); ++it)
            {
                ++count;
            }
            REQUIRE(count == cvalues.size());
            for (size_t index = 0; index < cvalues.size(); index++)
            {
                auto& val = cvalues[index];
                REQUIRE(val == 0);
            }

            auto& front = cvalues.front();
            REQUIRE(front == 0);
            auto& back = cvalues.back();
            REQUIRE(back == 0);

            REQUIRE(cvalues.data() != nullptr);
        }(values);

        auto data1 = values.data();
        auto data2 = values.get();
        REQUIRE((data1 && (data1 == data2)));

        values.reset();
        REQUIRE(!values);
        REQUIRE(values.empty());

        GetDWORDArray(values2.size_address(), &values2);
        size = values2.size();
        ptr = values2.release();

        values.reset(ptr, size);
        REQUIRE(!!values);
        REQUIRE(!values.empty());

        REQUIRE(values2.put() == values2.addressof());
        REQUIRE(&values2 == values2.addressof());
    }
}
#endif

#ifndef __cplusplus_winrt
TEST_CASE("WindowsInternalTests::VerifyMakeAgileCallback", "[wrl]")
{
    using namespace ABI::Windows::Foundation;

    class CallbackClient
    {
    public:
        HRESULT On(IMemoryBufferReference*, IInspectable*)
        {
            return S_OK;
        }
    };
    CallbackClient callbackClient;

#ifdef WIL_ENABLE_EXCEPTIONS
    auto cbAgile = wil::MakeAgileCallback<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>>([](IMemoryBufferReference*, IInspectable*) -> HRESULT
    {
        return S_OK;
    });
    REQUIRE(wil::is_agile(cbAgile));

    auto cbAgileWithMember = wil::MakeAgileCallback<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>>(&callbackClient, &CallbackClient::On);
    REQUIRE(wil::is_agile(cbAgileWithMember));
#endif
    auto cbAgileNoThrow = wil::MakeAgileCallbackNoThrow<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>>([](IMemoryBufferReference*, IInspectable*) -> HRESULT
    {
        return S_OK;
    });
    REQUIRE(wil::is_agile(cbAgileNoThrow));

    auto cbAgileWithMemberNoThrow = wil::MakeAgileCallbackNoThrow<ITypedEventHandler<IMemoryBufferReference*, IInspectable*>>(&callbackClient, &CallbackClient::On);
    REQUIRE(wil::is_agile(cbAgileWithMemberNoThrow));
}
#endif

TEST_CASE("WindowsInternalTests::Ranges", "[common]")
{
    {
        int things[10]{};
        unsigned int count = 0;
        for (auto& m : wil::make_range(things, ARRAYSIZE(things)))
        {
            ++count;
            m = 1;
        }
        REQUIRE(ARRAYSIZE(things) == count);
        REQUIRE(1 == things[1]);
    }

    {
        int things[10]{};
        unsigned int count = 0;
        for (auto m : wil::make_range(things, ARRAYSIZE(things)))
        {
            ++count;
            m = 1;
        }
        REQUIRE(ARRAYSIZE(things) == count);
        REQUIRE(0 == things[0]);
    }

    {
        int things[10]{};
        unsigned int count = 0;
        auto range = wil::make_range(things, ARRAYSIZE(things));
        for (auto m : range)
        {
            (void)m;
            ++count;
        }
        REQUIRE(ARRAYSIZE(things) == count);
    }

    {
        int things[10]{};
        unsigned int count = 0;
        const auto range = wil::make_range(things, ARRAYSIZE(things));
        for (auto m : range)
        {
            (void)m;
            ++count;
        }
        REQUIRE(ARRAYSIZE(things) == count);
    }
}

TEST_CASE("WindowsInternalTests::HStringTests", "[resource][unique_any]")
{
    const wchar_t kittens[] = L"kittens";

    {
        wchar_t* bufferStorage = nullptr;
        wil::unique_hstring_buffer theBuffer;
        REQUIRE_SUCCEEDED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer));
        REQUIRE_SUCCEEDED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens));

        // Promote sets the promoted-to value but resets theBuffer
        wil::unique_hstring promoted;
        REQUIRE_SUCCEEDED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), &promoted));
        REQUIRE(static_cast<bool>(promoted));
        REQUIRE_FALSE(static_cast<bool>(theBuffer));
    }

    {
        wchar_t* bufferStorage = nullptr;
        wil::unique_hstring_buffer theBuffer;
        REQUIRE_SUCCEEDED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer));
        REQUIRE_SUCCEEDED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens));

        // Failure to promote retains the buffer state
        REQUIRE_FAILED(wil::make_hstring_from_buffer_nothrow(wistd::move(theBuffer), nullptr));
        REQUIRE(static_cast<bool>(theBuffer));
    }

#ifdef WIL_ENABLE_EXCEPTIONS
    {
        wchar_t* bufferStorage = nullptr;
        wil::unique_hstring_buffer theBuffer;
        THROW_IF_FAILED(::WindowsPreallocateStringBuffer(ARRAYSIZE(kittens), &bufferStorage, &theBuffer));
        THROW_IF_FAILED(StringCchCopyW(bufferStorage, ARRAYSIZE(kittens), kittens));

        wil::unique_hstring promoted;
        REQUIRE_NOTHROW(promoted = wil::make_hstring_from_buffer(wistd::move(theBuffer)));
        REQUIRE(static_cast<bool>(promoted));
        REQUIRE_FALSE(static_cast<bool>(theBuffer));
    }
#endif
}

struct ThreadPoolWaitTestContext
{
    volatile LONG Counter = 0;
    wil::unique_event_nothrow Event;
};

static void __stdcall ThreadPoolWaitTestCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
    _Inout_opt_ void* context,
    _Inout_ PTP_WAIT wait,
    _In_ TP_WAIT_RESULT /*waitResult*/)
{
    ThreadPoolWaitTestContext& myContext = *reinterpret_cast<ThreadPoolWaitTestContext*>(context);
    SetThreadpoolWait(wait, myContext.Event.get(), nullptr);
    ::InterlockedIncrement(&myContext.Counter);
}

template <typename WaitResourceT>
void ThreadPoolWaitTestHelper(bool requireExactCallbackCount)
{
    ThreadPoolWaitTestContext myContext;
    REQUIRE_SUCCEEDED(myContext.Event.create());

    WaitResourceT wait;
    wait.reset(CreateThreadpoolWait(ThreadPoolWaitTestCallback, &myContext, NULL));
    REQUIRE(wait);

    SetThreadpoolWait(wait.get(), myContext.Event.get(), nullptr);

    const int loopCount = 5;
    for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
    {
        // Signal event.
        myContext.Event.SetEvent();

        // Wait until 'myContext.Counter' increments by 1.
        for (int itr = 0; itr != 50 && currCallbackCount == myContext.Counter; ++itr)
        {
            Sleep(10);
        }

        // Ensure we didn't timeout
        REQUIRE(currCallbackCount + 1 == myContext.Counter);
    }

    // Signal one last event.
    myContext.Event.SetEvent();

    // Close thread-pool wait.
    wait.reset();
    myContext.Event.reset();

    // Verify counter.
    if (requireExactCallbackCount)
    {
        REQUIRE(loopCount + 1 == myContext.Counter);
    }
    else
    {
        REQUIRE((loopCount + 1 == myContext.Counter || loopCount == myContext.Counter));
    }
}

TEST_CASE("WindowsInternalTests::ThreadPoolWaitTest", "[resource][unique_threadpool_wait]")
{
    ThreadPoolWaitTestHelper<wil::unique_threadpool_wait>(false);
    ThreadPoolWaitTestHelper<wil::unique_threadpool_wait_nocancel>(true);
}

struct ThreadPoolWaitWorkContext
{
    volatile LONG Counter = 0;
};

static void __stdcall ThreadPoolWaitWorkCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
    _Inout_opt_ void* context,
    _Inout_ PTP_WORK /*work*/)
{
    ThreadPoolWaitWorkContext& myContext = *reinterpret_cast<ThreadPoolWaitWorkContext*>(context);
    ::InterlockedIncrement(&myContext.Counter);
}

template <typename WaitResourceT>
void ThreadPoolWaitWorkHelper(bool requireExactCallbackCount)
{
    ThreadPoolWaitWorkContext myContext;

    WaitResourceT work;
    work.reset(CreateThreadpoolWork(ThreadPoolWaitWorkCallback, &myContext, NULL));
    REQUIRE(work);

    const int loopCount = 5;
    for (int itr = 0; itr != loopCount; ++itr)
    {
        SubmitThreadpoolWork(work.get());
    }

    work.reset();

    if (requireExactCallbackCount)
    {
        REQUIRE(loopCount == myContext.Counter);
    }
    else
    {
        REQUIRE(loopCount >= myContext.Counter);
    }
}

TEST_CASE("WindowsInternalTests::ThreadPoolWorkTest", "[resource][unique_threadpool_work]")
{
    ThreadPoolWaitWorkHelper<wil::unique_threadpool_work>(false);
    ThreadPoolWaitWorkHelper<wil::unique_threadpool_work_nocancel>(true);
}

struct ThreadPoolTimerWorkContext
{
    volatile LONG Counter = 0;
    wil::unique_event_nothrow Event;
};

static void __stdcall ThreadPoolTimerWorkCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
    _Inout_opt_ void* context,
    _Inout_ PTP_TIMER /*timer*/)
{
    ThreadPoolTimerWorkContext& myContext = *reinterpret_cast<ThreadPoolTimerWorkContext*>(context);
    myContext.Event.SetEvent();
    ::InterlockedIncrement(&myContext.Counter);
}

template <typename TimerResourceT, typename DueTimeT, typename SetThreadpoolTimerT>
void ThreadPoolTimerWorkHelper(SetThreadpoolTimerT const &setThreadpoolTimerFn, bool requireExactCallbackCount)
{
    ThreadPoolTimerWorkContext myContext;
    REQUIRE_SUCCEEDED(myContext.Event.create());

    TimerResourceT timer;
    timer.reset(CreateThreadpoolTimer(ThreadPoolTimerWorkCallback, &myContext, nullptr));
    REQUIRE(timer);

    const int loopCount = 5;
    for (int currCallbackCount = 0; currCallbackCount != loopCount; ++currCallbackCount)
    {
        // Schedule timer
        myContext.Event.ResetEvent();
        const auto allowedWindow = 0;
        LONGLONG dueTime = -5 * 10000I64; // 5ms
        setThreadpoolTimerFn(timer.get(), reinterpret_cast<DueTimeT *>(&dueTime), 0, allowedWindow);

        // Wait until 'myContext.Counter' increments by 1.
        REQUIRE(myContext.Event.wait(500));
        for (int itr = 0; itr != 50 && currCallbackCount == myContext.Counter; ++itr)
        {
            Sleep(10);
        }

        // Ensure we didn't timeout
        REQUIRE(currCallbackCount + 1 == myContext.Counter);
    }

    // Schedule one last timer.
    myContext.Event.ResetEvent();
    const auto allowedWindow = 0;
    LONGLONG dueTime = -5 * 10000I64; // 5ms
    setThreadpoolTimerFn(timer.get(), reinterpret_cast<DueTimeT *>(&dueTime), 0, allowedWindow);

    if (requireExactCallbackCount)
    {
        // Wait for the event to be set
        REQUIRE(myContext.Event.wait(500));
    }

    // Close timer.
    timer.reset();
    myContext.Event.reset();

    // Verify counter.
    if (requireExactCallbackCount)
    {
        REQUIRE(loopCount + 1 == myContext.Counter);
    }
    else
    {
        REQUIRE((loopCount + 1 == myContext.Counter || loopCount == myContext.Counter));
    }
}

TEST_CASE("WindowsInternalTests::ThreadPoolTimerTest", "[resource][unique_threadpool_timer]")
{
    static_assert(sizeof(FILETIME) == sizeof(LONGLONG), "FILETIME and LONGLONG must be same size");
    ThreadPoolTimerWorkHelper<wil::unique_threadpool_timer, FILETIME>(SetThreadpoolTimer, false);
    ThreadPoolTimerWorkHelper<wil::unique_threadpool_timer_nocancel, FILETIME>(SetThreadpoolTimer, true);
}

#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
static void __stdcall SlimEventTrollCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
    _Inout_opt_ void* context,
    _Inout_ PTP_TIMER /*timer*/)
{
    auto event = reinterpret_cast<wil::slim_event*>(context);

    // Wake up the thread without setting the event.
    // Note: This relies on the fact that the 'wil::slim_event' class only has a single member variable.
    WakeByAddressAll(event);
}

static void __stdcall SlimEventFriendlyCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*instance*/,
    _Inout_opt_ void* context,
    _Inout_ PTP_TIMER /*timer*/)
{
    auto event = reinterpret_cast<wil::slim_event*>(context);
    event->SetEvent();
}

TEST_CASE("WindowsInternalTests::SlimEventTests", "[resource][slim_event]")
{
    {
        wil::slim_event event;

        // Verify simple timeouts work on an auto-reset event.
        REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 0));
        REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 10));

        wil::unique_threadpool_timer trollTimer(CreateThreadpoolTimer(SlimEventTrollCallback, &event, nullptr));
        REQUIRE(trollTimer);

        FILETIME trollDueTime = wil::filetime::from_int64(0);
        SetThreadpoolTimer(trollTimer.get(), &trollDueTime, /*period(ms)*/ 5, /*window(ms)*/ 0);

        // Ensure we timeout in spite of being constantly woken up unnecessarily.
        REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 100));

        wil::unique_threadpool_timer friendlyTimer(CreateThreadpoolTimer(SlimEventFriendlyCallback, &event, nullptr));
        REQUIRE(friendlyTimer);

        FILETIME friendlyDueTime = wil::filetime::from_int64(UINT64(-100 * wil::filetime_duration::one_millisecond)); // 100ms (relative to now)
        SetThreadpoolTimer(friendlyTimer.get(), &friendlyDueTime, /*period(ms)*/ 0, /*window(ms)*/ 0);

        // Now that the 'friendlyTimer' is queued, we should succeed.
        REQUIRE(event.wait(INFINITE));

        // Ensure event is auto-reset.
        REQUIRE_FALSE(event.wait(/*timeout(ms)*/ 100));
    }

    {
        wil::slim_event_manual_reset manualResetEvent;

        // Verify simple timeouts work on a manual-reset event.
        REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 0));
        REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 10));

        // Ensure multiple waits can occur on a manual-reset event.
        manualResetEvent.SetEvent();
        REQUIRE(manualResetEvent.wait());
        REQUIRE(manualResetEvent.wait(/*timeout(ms)*/ 100));
        REQUIRE(manualResetEvent.wait(INFINITE));

        // Verify 'ResetEvent' works.
        manualResetEvent.ResetEvent();
        REQUIRE_FALSE(manualResetEvent.wait(/*timeout(ms)*/ 10));
    }

}
#endif // WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)

struct ConditionVariableCSCallbackContext
{
    wil::condition_variable event;
    wil::critical_section lock;
    auto acquire() { return lock.lock(); }
};

struct ConditionVariableSRWCallbackContext
{
    wil::condition_variable event;
    wil::srwlock lock;
    auto acquire() { return lock.lock_exclusive(); }
};

template <typename T>
static void __stdcall ConditionVariableCallback(
    _Inout_ PTP_CALLBACK_INSTANCE /*Instance*/,
    _In_ void* Context)
{
    auto callbackContext = reinterpret_cast<T*>(Context);

    // Acquire the lock to ensure we don't notify the condition variable before the other thread has
    // gone to sleep.
    auto gate = callbackContext->acquire();

    // Signal the condition variable.
    callbackContext->event.notify_all();
}

// A quick sanity check of the 'wil::condition_variable' type.
TEST_CASE("WindowsInternalTests::ConditionVariableTests", "[resource][condition_variable]")
{
    SECTION("Test 'wil::condition_variable' with 'wil::critical_section'")
    {
        ConditionVariableCSCallbackContext callbackContext;
        auto gate = callbackContext.lock.lock();

        // Schedule the thread that will wake up this thread.
        REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback<ConditionVariableCSCallbackContext>, &callbackContext, nullptr));

        // Wait on the condition variable.
        REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500));
    }

    SECTION("Test 'wil::condition_variable' with 'wil::srwlock'")
    {
        ConditionVariableSRWCallbackContext callbackContext;

        // Test exclusive lock.
        {
            auto gate = callbackContext.lock.lock_exclusive();

            // Schedule the thread that will wake up this thread.
            REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback<ConditionVariableSRWCallbackContext>, &callbackContext, nullptr));

            // Wait on the condition variable.
            REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500));
        }

        // Test shared lock.
        {
            auto gate = callbackContext.lock.lock_shared();

            // Schedule the thread that will wake up this thread.
            REQUIRE(TrySubmitThreadpoolCallback(ConditionVariableCallback<ConditionVariableSRWCallbackContext>, &callbackContext, nullptr));

            // Wait on the condition variable.
            REQUIRE(callbackContext.event.wait_for(gate, /*timeout(ms)*/ 500));
        }
    }
}

TEST_CASE("WindowsInternalTests::ReturnWithExpectedTests", "[result_macros]")
{
    wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;

    // Succeeded
    REQUIRE_RETURNS_EXPECTED(S_OK, [] { RETURN_IF_FAILED_WITH_EXPECTED(MDEC(hrOKRef()), E_UNEXPECTED); return S_OK; });

    // Expected
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_FAIL); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_UNEXPECTED, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG); return S_OK; });

    // Unexpected
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED); return S_OK; });
    REQUIRE_RETURNS_EXPECTED(E_FAIL, [] { RETURN_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG); return S_OK; });
}

TEST_CASE("WindowsInternalTests::LogWithExpectedTests", "[result_macros]")
{
    wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;

    // Succeeded
    REQUIRE_LOG(S_OK, [] { REQUIRE(S_OK == LOG_IF_FAILED_WITH_EXPECTED(MDEC(hrOKRef()), E_FAIL, E_INVALIDARG)); });

    // Expected
    REQUIRE_LOG(S_OK, [] { REQUIRE(E_UNEXPECTED == LOG_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_UNEXPECTED, E_INVALIDARG)); });
    REQUIRE_LOG(S_OK, [] { REQUIRE(E_UNEXPECTED == LOG_IF_FAILED_WITH_EXPECTED(E_UNEXPECTED, E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG)); });

    // Unexpected
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED)); });
    REQUIRE_LOG(E_FAIL, [] { REQUIRE(E_FAIL == LOG_IF_FAILED_WITH_EXPECTED(E_FAIL, E_UNEXPECTED, E_POINTER, E_INVALIDARG)); });
}

// Verifies that the shutdown-aware objects respect the alignment
// of the wrapped object.
template<template<typename> class Wrapper>
void VerifyAlignment()
{
    // Some of the wrappers require a method called ProcessShutdown(), so we'll give it one.
    struct alignment_sensitive_struct
    {
        // Use SLIST_HEADER as our poster child alignment-sensitive data type.
        SLIST_HEADER value;
        void ProcessShutdown() { }
    };
    static_assert(alignof(alignment_sensitive_struct) != alignof(char), "Need to choose a better alignment-sensitive type");

    // Create a custom structure that tries to force misalignment.
    struct attempted_misalignment
    {
        char c;
        Wrapper<alignment_sensitive_struct> wrapper;
    } possibly_misaligned{};

    static_assert(alignof(attempted_misalignment) == alignof(alignment_sensitive_struct), "Wrapper type does not respect alignment");

    // Verify that the wrapper type placed the inner object at proper alignment.
    // Note: use std::addressof in case the alignment_sensitive_struct overrides the & operator.
    REQUIRE(reinterpret_cast<uintptr_t>(std::addressof(possibly_misaligned.wrapper.get())) % alignof(alignment_sensitive_struct) == 0);
}

TEST_CASE("WindowsInternalTests::ShutdownAwareObjectAlignmentTests", "[result_macros]")
{
    VerifyAlignment<wil::manually_managed_shutdown_aware_object>();
    VerifyAlignment<wil::shutdown_aware_object>();
    VerifyAlignment<wil::object_without_destructor_on_shutdown>();
}

#pragma warning(pop)