123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- #include <wil/cppwinrt.h>
- #include <winrt/Windows.Foundation.h>
- #include <winrt/Windows.Foundation.Collections.h>
- #include <wil/cppwinrt_helpers.h>
- #include <winrt/Windows.System.h>
- #include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
- #include "catch.hpp"
- // HRESULT values that C++/WinRT throws as something other than winrt::hresult_error - e.g. a type derived from
- // winrt::hresult_error, std::*, etc.
- static const HRESULT cppwinrt_mapped_hresults[] =
- {
- E_ACCESSDENIED,
- RPC_E_WRONG_THREAD,
- E_NOTIMPL,
- E_INVALIDARG,
- E_BOUNDS,
- E_NOINTERFACE,
- CLASS_E_CLASSNOTAVAILABLE,
- E_CHANGED_STATE,
- E_ILLEGAL_METHOD_CALL,
- E_ILLEGAL_STATE_CHANGE,
- E_ILLEGAL_DELEGATE_ASSIGNMENT,
- HRESULT_FROM_WIN32(ERROR_CANCELLED),
- E_OUTOFMEMORY,
- };
- template<typename T> auto copy_thing(T const& src)
- {
- return std::decay_t<T>(src);
- }
- template<typename T, typename K>
- void CheckMapVector(std::vector<winrt::Windows::Foundation::Collections::IKeyValuePair<T, K>> const& test, std::map<T, K> const& src)
- {
- REQUIRE(test.size() == src.size());
- for (auto&& i : test)
- {
- REQUIRE(i.Value() == src.at(i.Key()));
- }
- }
- struct vector_like
- {
- uint32_t Size() const { return 100; }
- int GetAt(uint32_t) const { return 15; }
- uint32_t GetMany(uint32_t start, winrt::array_view<int> items) const
- {
- if (start > 0)
- {
- throw winrt::hresult_out_of_bounds();
- }
- uint32_t const to_fill = (std::min)(items.size(), Size());
- std::fill_n(items.begin(), to_fill, GetAt(0));
- return to_fill;
- }
- };
- struct iterator_like
- {
- static const uint32_t total = 20;
- mutable uint32_t remaining = total;
- int Current() const { return 3; }
- uint32_t GetMany(winrt::array_view<int> items) const
- {
- auto to_copy = (std::min)(items.size(), remaining);
- std::fill_n(items.begin(), to_copy, Current());
- remaining -= to_copy;
- return to_copy;
- }
- };
- struct iterable_like
- {
- auto First() const { return iterator_like{}; }
- };
- struct unstable_vector : winrt::implements<unstable_vector, winrt::Windows::Foundation::Collections::IVectorView<int>>
- {
- auto Size() { return 4; }
- int GetAt(uint32_t) { return 7; }
- uint32_t GetMany(uint32_t, winrt::array_view<int> items)
- {
- std::fill(items.begin(), items.end(), GetAt(0));
- return items.size();
- }
- bool IndexOf(int, uint32_t) { throw winrt::hresult_not_implemented(); }
- };
- TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
- {
- winrt::init_apartment();
- {
- std::vector<winrt::hstring> src_vector = { L"foo", L"bar", L"bas" };
- auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
- REQUIRE(wil::to_vector(sv) == src_vector);
- REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
- REQUIRE(wil::to_vector(sv.First()) == src_vector);
- REQUIRE(wil::to_vector(sv.First()) == src_vector);
- REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) == src_vector);
- }
- {
- std::vector<uint32_t> src_vector = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19 };
- auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
- REQUIRE(wil::to_vector(sv) == src_vector);
- REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
- REQUIRE(wil::to_vector(sv.First()) == src_vector);
- REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<uint32_t>>()) == src_vector);
- }
- {
- std::vector<float> src_vector;
- auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
- REQUIRE(wil::to_vector(sv) == src_vector);
- REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
- REQUIRE(wil::to_vector(sv.First()) == src_vector);
- REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<float>>()) == src_vector);
- }
- {
- std::map<winrt::hstring, winrt::hstring> src_map{{L"kittens", L"fluffy"}, {L"puppies", L"cute"}};
- auto sm = winrt::single_threaded_map(copy_thing(src_map));
- CheckMapVector(wil::to_vector(sm), src_map);
- CheckMapVector(wil::to_vector(sm.GetView()), src_map);
- CheckMapVector(wil::to_vector(sm.First()), src_map);
- }
- {
- winrt::Windows::Foundation::Collections::PropertySet props;
- props.Insert(L"kitten", winrt::box_value(L"fluffy"));
- props.Insert(L"puppy", winrt::box_value<uint32_t>(25));
- auto converted = wil::to_vector(props);
- REQUIRE(converted.size() == props.Size());
- for (auto&& kv : converted)
- {
- if (kv.Key() == L"kitten")
- {
- REQUIRE(kv.Value().as<winrt::hstring>() == L"fluffy");
- }
- else if (kv.Key() == L"puppy")
- {
- REQUIRE(kv.Value().as<uint32_t>() == 25);
- }
- else
- {
- REQUIRE(false);
- }
- }
- }
-
- REQUIRE_THROWS(wil::to_vector(winrt::make<unstable_vector>()));
- auto ilike = wil::to_vector(iterable_like{});
- REQUIRE(ilike.size() == iterator_like::total);
- for (auto&& i : ilike) REQUIRE(i == iterator_like{}.Current());
- auto vlike = wil::to_vector(vector_like{});
- REQUIRE(vlike.size() == vector_like{}.Size());
- for (auto&& i : vlike) REQUIRE(i == vector_like{}.GetAt(0));
- winrt::clear_factory_cache();
- winrt::uninit_apartment();
- }
- TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
- {
- auto test = [](HRESULT hr)
- {
- try
- {
- THROW_HR(hr);
- }
- catch (...)
- {
- REQUIRE(hr == winrt::to_hresult());
- }
- };
- for (auto hr : cppwinrt_mapped_hresults)
- {
- test(hr);
- }
- // A non-mapped HRESULT
- test(E_UNEXPECTED);
- }
- TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
- {
- auto test = [](HRESULT hr)
- {
- try
- {
- winrt::check_hresult(hr);
- }
- catch (...)
- {
- REQUIRE(hr == wil::ResultFromCaughtException());
- }
- };
- for (auto hr : cppwinrt_mapped_hresults)
- {
- test(hr);
- }
- // A non-mapped HRESULT
- test(E_UNEXPECTED);
- }
- TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]")
- {
- auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions)
- {
- auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]()
- {
- winrt::check_hresult(hr);
- });
- REQUIRE(hr == result);
- };
- for (auto hr : cppwinrt_mapped_hresults)
- {
- test(hr, wil::SupportedExceptions::Known);
- test(hr, wil::SupportedExceptions::All);
- }
- // A non-mapped HRESULT
- test(E_UNEXPECTED, wil::SupportedExceptions::Known);
- test(E_UNEXPECTED, wil::SupportedExceptions::All);
- // Uncomment any of the following to validate SEH failfast
- //test(E_UNEXPECTED, wil::SupportedExceptions::None);
- //test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown);
- //test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc);
- }
- TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
- {
- // Since setting 'winrt_to_hresult_handler' opts us into _all_ C++/WinRT exception translation handling, we need to
- // make sure that we preserve behavior, at least with 'check_hresult', especially when C++/WinRT maps a particular
- // HRESULT value to a different exception type
- auto test = [](HRESULT hr)
- {
- try
- {
- winrt::check_hresult(hr);
- }
- catch (...)
- {
- REQUIRE(hr == winrt::to_hresult());
- }
- };
- for (auto hr : cppwinrt_mapped_hresults)
- {
- test(hr);
- }
- // A non-mapped HRESULT
- test(E_UNEXPECTED);
- // C++/WinRT also maps a few std::* exceptions to various HRESULTs. We should preserve this behavior
- try
- {
- throw std::out_of_range("oopsie");
- }
- catch (...)
- {
- REQUIRE(winrt::to_hresult() == E_BOUNDS);
- }
- try
- {
- throw std::invalid_argument("daisy");
- }
- catch (...)
- {
- REQUIRE(winrt::to_hresult() == E_INVALIDARG);
- }
- // NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
- // that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
- }
- TEST_CASE("CppWinRTTests::ModuleReference", "[cppwinrt]")
- {
- auto peek_module_ref_count = []()
- {
- ++winrt::get_module_lock();
- return --winrt::get_module_lock();
- };
- auto initial = peek_module_ref_count();
- // Basic test: Construct and destruct.
- {
- auto module_ref = wil::winrt_module_reference();
- REQUIRE(peek_module_ref_count() == initial + 1);
- }
- REQUIRE(peek_module_ref_count() == initial);
- // Fancy test: Copy object with embedded reference.
- {
- struct object_with_ref
- {
- wil::winrt_module_reference ref;
- };
- object_with_ref o1;
- REQUIRE(peek_module_ref_count() == initial + 1);
- auto o2 = o1;
- REQUIRE(peek_module_ref_count() == initial + 2);
- o1 = o2;
- REQUIRE(peek_module_ref_count() == initial + 2);
- o2 = std::move(o1);
- REQUIRE(peek_module_ref_count() == initial + 2);
- }
- REQUIRE(peek_module_ref_count() == initial);
- }
- #if (!defined(__clang__) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
- // Define our own custom dispatcher that we can force it to behave in certain ways.
- // wil::resume_foreground supports any dispatcher that has a dispatcher_traits.
- namespace test
- {
- enum class TestDispatcherPriority
- {
- Normal = 0,
- Weird = 1,
- };
- using TestDispatcherHandler = winrt::delegate<>;
- enum class TestDispatcherMode
- {
- Dispatch,
- RaceDispatch,
- Orphan,
- Fail,
- };
- struct TestDispatcher
- {
- TestDispatcher() = default;
- TestDispatcher(TestDispatcher const&) = delete;
- TestDispatcherMode mode = TestDispatcherMode::Dispatch;
- TestDispatcherPriority expected_priority = TestDispatcherPriority::Normal;
- void TryEnqueue(TestDispatcherPriority priority, TestDispatcherHandler const& handler) const
- {
- REQUIRE(priority == expected_priority);
- if (mode == TestDispatcherMode::Fail)
- {
- throw winrt::hresult_not_implemented();
- }
- if (mode == TestDispatcherMode::RaceDispatch)
- {
- handler();
- return;
- }
- std::ignore = [](auto mode, auto handler) ->winrt::fire_and_forget
- {
- co_await winrt::resume_background();
- if (mode == TestDispatcherMode::Dispatch)
- {
- handler();
- }
- }(mode, handler);
- }
- };
- }
- namespace wil::details
- {
- template<>
- struct dispatcher_traits<test::TestDispatcher>
- {
- using Priority = test::TestDispatcherPriority;
- using Handler = test::TestDispatcherHandler;
- using Scheduler = dispatcher_TryEnqueue;
- };
- }
- TEST_CASE("CppWinRTTests::ResumeForegroundTests", "[cppwinrt]")
- {
- // Verify that the DispatcherQueue version has been unlocked.
- using Verify = decltype(wil::resume_foreground(winrt::Windows::System::DispatcherQueue{ nullptr }));
- static_assert(wistd::is_trivial_v<Verify> || !wistd::is_trivial_v<Verify>);
- []() -> winrt::Windows::Foundation::IAsyncAction
- {
- test::TestDispatcher dispatcher;
- // Normal case: Resumes on new thread.
- dispatcher.mode = test::TestDispatcherMode::Dispatch;
- co_await wil::resume_foreground(dispatcher);
- // Race case: Resumes before TryEnqueue returns.
- dispatcher.mode = test::TestDispatcherMode::RaceDispatch;
- co_await wil::resume_foreground(dispatcher);
- // Orphan case: Never resumes, detected when handler is destructed without ever being invoked.
- dispatcher.mode = test::TestDispatcherMode::Orphan;
- bool seen = false;
- try
- {
- co_await wil::resume_foreground(dispatcher);
- }
- catch (winrt::hresult_error const& e)
- {
- seen = e.code() == HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE));
- }
- REQUIRE(seen);
- // Fail case: Can't even schedule the resumption.
- dispatcher.mode = test::TestDispatcherMode::Fail;
- seen = false;
- try
- {
- co_await wil::resume_foreground(dispatcher);
- }
- catch (winrt::hresult_not_implemented const&)
- {
- seen = true;
- }
- REQUIRE(seen);
- // Custom priority.
- dispatcher.mode = test::TestDispatcherMode::Dispatch;
- dispatcher.expected_priority = test::TestDispatcherPriority::Weird;
- co_await wil::resume_foreground(dispatcher, test::TestDispatcherPriority::Weird);
- }().get();
- }
- #endif // coroutines
|