CppWinRTTests.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. #include <wil/cppwinrt.h>
  2. #include <winrt/Windows.Foundation.h>
  3. #include <winrt/Windows.Foundation.Collections.h>
  4. #include <wil/cppwinrt_helpers.h>
  5. #include <winrt/Windows.System.h>
  6. #include <wil/cppwinrt_helpers.h> // Verify can include a second time to unlock more features
  7. #include "catch.hpp"
  8. // HRESULT values that C++/WinRT throws as something other than winrt::hresult_error - e.g. a type derived from
  9. // winrt::hresult_error, std::*, etc.
  10. static const HRESULT cppwinrt_mapped_hresults[] =
  11. {
  12. E_ACCESSDENIED,
  13. RPC_E_WRONG_THREAD,
  14. E_NOTIMPL,
  15. E_INVALIDARG,
  16. E_BOUNDS,
  17. E_NOINTERFACE,
  18. CLASS_E_CLASSNOTAVAILABLE,
  19. E_CHANGED_STATE,
  20. E_ILLEGAL_METHOD_CALL,
  21. E_ILLEGAL_STATE_CHANGE,
  22. E_ILLEGAL_DELEGATE_ASSIGNMENT,
  23. HRESULT_FROM_WIN32(ERROR_CANCELLED),
  24. E_OUTOFMEMORY,
  25. };
  26. template<typename T> auto copy_thing(T const& src)
  27. {
  28. return std::decay_t<T>(src);
  29. }
  30. template<typename T, typename K>
  31. void CheckMapVector(std::vector<winrt::Windows::Foundation::Collections::IKeyValuePair<T, K>> const& test, std::map<T, K> const& src)
  32. {
  33. REQUIRE(test.size() == src.size());
  34. for (auto&& i : test)
  35. {
  36. REQUIRE(i.Value() == src.at(i.Key()));
  37. }
  38. }
  39. struct vector_like
  40. {
  41. uint32_t Size() const { return 100; }
  42. int GetAt(uint32_t) const { return 15; }
  43. uint32_t GetMany(uint32_t start, winrt::array_view<int> items) const
  44. {
  45. if (start > 0)
  46. {
  47. throw winrt::hresult_out_of_bounds();
  48. }
  49. uint32_t const to_fill = (std::min)(items.size(), Size());
  50. std::fill_n(items.begin(), to_fill, GetAt(0));
  51. return to_fill;
  52. }
  53. };
  54. struct iterator_like
  55. {
  56. static const uint32_t total = 20;
  57. mutable uint32_t remaining = total;
  58. int Current() const { return 3; }
  59. uint32_t GetMany(winrt::array_view<int> items) const
  60. {
  61. auto to_copy = (std::min)(items.size(), remaining);
  62. std::fill_n(items.begin(), to_copy, Current());
  63. remaining -= to_copy;
  64. return to_copy;
  65. }
  66. };
  67. struct iterable_like
  68. {
  69. auto First() const { return iterator_like{}; }
  70. };
  71. struct unstable_vector : winrt::implements<unstable_vector, winrt::Windows::Foundation::Collections::IVectorView<int>>
  72. {
  73. auto Size() { return 4; }
  74. int GetAt(uint32_t) { return 7; }
  75. uint32_t GetMany(uint32_t, winrt::array_view<int> items)
  76. {
  77. std::fill(items.begin(), items.end(), GetAt(0));
  78. return items.size();
  79. }
  80. bool IndexOf(int, uint32_t) { throw winrt::hresult_not_implemented(); }
  81. };
  82. TEST_CASE("CppWinRTTests::VectorToVector", "[cppwinrt]")
  83. {
  84. winrt::init_apartment();
  85. {
  86. std::vector<winrt::hstring> src_vector = { L"foo", L"bar", L"bas" };
  87. auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
  88. REQUIRE(wil::to_vector(sv) == src_vector);
  89. REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
  90. REQUIRE(wil::to_vector(sv.First()) == src_vector);
  91. REQUIRE(wil::to_vector(sv.First()) == src_vector);
  92. REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) == src_vector);
  93. }
  94. {
  95. 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 };
  96. auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
  97. REQUIRE(wil::to_vector(sv) == src_vector);
  98. REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
  99. REQUIRE(wil::to_vector(sv.First()) == src_vector);
  100. REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<uint32_t>>()) == src_vector);
  101. }
  102. {
  103. std::vector<float> src_vector;
  104. auto sv = winrt::single_threaded_vector(copy_thing(src_vector));
  105. REQUIRE(wil::to_vector(sv) == src_vector);
  106. REQUIRE(wil::to_vector(sv.GetView()) == src_vector);
  107. REQUIRE(wil::to_vector(sv.First()) == src_vector);
  108. REQUIRE(wil::to_vector(sv.as<winrt::Windows::Foundation::Collections::IIterable<float>>()) == src_vector);
  109. }
  110. {
  111. std::map<winrt::hstring, winrt::hstring> src_map{{L"kittens", L"fluffy"}, {L"puppies", L"cute"}};
  112. auto sm = winrt::single_threaded_map(copy_thing(src_map));
  113. CheckMapVector(wil::to_vector(sm), src_map);
  114. CheckMapVector(wil::to_vector(sm.GetView()), src_map);
  115. CheckMapVector(wil::to_vector(sm.First()), src_map);
  116. }
  117. {
  118. winrt::Windows::Foundation::Collections::PropertySet props;
  119. props.Insert(L"kitten", winrt::box_value(L"fluffy"));
  120. props.Insert(L"puppy", winrt::box_value<uint32_t>(25));
  121. auto converted = wil::to_vector(props);
  122. REQUIRE(converted.size() == props.Size());
  123. for (auto&& kv : converted)
  124. {
  125. if (kv.Key() == L"kitten")
  126. {
  127. REQUIRE(kv.Value().as<winrt::hstring>() == L"fluffy");
  128. }
  129. else if (kv.Key() == L"puppy")
  130. {
  131. REQUIRE(kv.Value().as<uint32_t>() == 25);
  132. }
  133. else
  134. {
  135. REQUIRE(false);
  136. }
  137. }
  138. }
  139. REQUIRE_THROWS(wil::to_vector(winrt::make<unstable_vector>()));
  140. auto ilike = wil::to_vector(iterable_like{});
  141. REQUIRE(ilike.size() == iterator_like::total);
  142. for (auto&& i : ilike) REQUIRE(i == iterator_like{}.Current());
  143. auto vlike = wil::to_vector(vector_like{});
  144. REQUIRE(vlike.size() == vector_like{}.Size());
  145. for (auto&& i : vlike) REQUIRE(i == vector_like{}.GetAt(0));
  146. winrt::clear_factory_cache();
  147. winrt::uninit_apartment();
  148. }
  149. TEST_CASE("CppWinRTTests::WilToCppWinRTExceptionTranslationTest", "[cppwinrt]")
  150. {
  151. auto test = [](HRESULT hr)
  152. {
  153. try
  154. {
  155. THROW_HR(hr);
  156. }
  157. catch (...)
  158. {
  159. REQUIRE(hr == winrt::to_hresult());
  160. }
  161. };
  162. for (auto hr : cppwinrt_mapped_hresults)
  163. {
  164. test(hr);
  165. }
  166. // A non-mapped HRESULT
  167. test(E_UNEXPECTED);
  168. }
  169. TEST_CASE("CppWinRTTests::CppWinRTToWilExceptionTranslationTest", "[cppwinrt]")
  170. {
  171. auto test = [](HRESULT hr)
  172. {
  173. try
  174. {
  175. winrt::check_hresult(hr);
  176. }
  177. catch (...)
  178. {
  179. REQUIRE(hr == wil::ResultFromCaughtException());
  180. }
  181. };
  182. for (auto hr : cppwinrt_mapped_hresults)
  183. {
  184. test(hr);
  185. }
  186. // A non-mapped HRESULT
  187. test(E_UNEXPECTED);
  188. }
  189. TEST_CASE("CppWinRTTests::ResultFromExceptionDebugTest", "[cppwinrt]")
  190. {
  191. auto test = [](HRESULT hr, wil::SupportedExceptions supportedExceptions)
  192. {
  193. auto result = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, supportedExceptions, [&]()
  194. {
  195. winrt::check_hresult(hr);
  196. });
  197. REQUIRE(hr == result);
  198. };
  199. for (auto hr : cppwinrt_mapped_hresults)
  200. {
  201. test(hr, wil::SupportedExceptions::Known);
  202. test(hr, wil::SupportedExceptions::All);
  203. }
  204. // A non-mapped HRESULT
  205. test(E_UNEXPECTED, wil::SupportedExceptions::Known);
  206. test(E_UNEXPECTED, wil::SupportedExceptions::All);
  207. // Uncomment any of the following to validate SEH failfast
  208. //test(E_UNEXPECTED, wil::SupportedExceptions::None);
  209. //test(E_ACCESSDENIED, wil::SupportedExceptions::Thrown);
  210. //test(E_INVALIDARG, wil::SupportedExceptions::ThrownOrAlloc);
  211. }
  212. TEST_CASE("CppWinRTTests::CppWinRTConsistencyTest", "[cppwinrt]")
  213. {
  214. // Since setting 'winrt_to_hresult_handler' opts us into _all_ C++/WinRT exception translation handling, we need to
  215. // make sure that we preserve behavior, at least with 'check_hresult', especially when C++/WinRT maps a particular
  216. // HRESULT value to a different exception type
  217. auto test = [](HRESULT hr)
  218. {
  219. try
  220. {
  221. winrt::check_hresult(hr);
  222. }
  223. catch (...)
  224. {
  225. REQUIRE(hr == winrt::to_hresult());
  226. }
  227. };
  228. for (auto hr : cppwinrt_mapped_hresults)
  229. {
  230. test(hr);
  231. }
  232. // A non-mapped HRESULT
  233. test(E_UNEXPECTED);
  234. // C++/WinRT also maps a few std::* exceptions to various HRESULTs. We should preserve this behavior
  235. try
  236. {
  237. throw std::out_of_range("oopsie");
  238. }
  239. catch (...)
  240. {
  241. REQUIRE(winrt::to_hresult() == E_BOUNDS);
  242. }
  243. try
  244. {
  245. throw std::invalid_argument("daisy");
  246. }
  247. catch (...)
  248. {
  249. REQUIRE(winrt::to_hresult() == E_INVALIDARG);
  250. }
  251. // NOTE: C++/WinRT maps other 'std::exception' derived exceptions to E_FAIL, however we preserve the WIL behavior
  252. // that such exceptions become HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION)
  253. }
  254. TEST_CASE("CppWinRTTests::ModuleReference", "[cppwinrt]")
  255. {
  256. auto peek_module_ref_count = []()
  257. {
  258. ++winrt::get_module_lock();
  259. return --winrt::get_module_lock();
  260. };
  261. auto initial = peek_module_ref_count();
  262. // Basic test: Construct and destruct.
  263. {
  264. auto module_ref = wil::winrt_module_reference();
  265. REQUIRE(peek_module_ref_count() == initial + 1);
  266. }
  267. REQUIRE(peek_module_ref_count() == initial);
  268. // Fancy test: Copy object with embedded reference.
  269. {
  270. struct object_with_ref
  271. {
  272. wil::winrt_module_reference ref;
  273. };
  274. object_with_ref o1;
  275. REQUIRE(peek_module_ref_count() == initial + 1);
  276. auto o2 = o1;
  277. REQUIRE(peek_module_ref_count() == initial + 2);
  278. o1 = o2;
  279. REQUIRE(peek_module_ref_count() == initial + 2);
  280. o2 = std::move(o1);
  281. REQUIRE(peek_module_ref_count() == initial + 2);
  282. }
  283. REQUIRE(peek_module_ref_count() == initial);
  284. }
  285. #if (!defined(__clang__) && defined(__cpp_lib_coroutine) && (__cpp_lib_coroutine >= 201902L)) || defined(_RESUMABLE_FUNCTIONS_SUPPORTED)
  286. // Define our own custom dispatcher that we can force it to behave in certain ways.
  287. // wil::resume_foreground supports any dispatcher that has a dispatcher_traits.
  288. namespace test
  289. {
  290. enum class TestDispatcherPriority
  291. {
  292. Normal = 0,
  293. Weird = 1,
  294. };
  295. using TestDispatcherHandler = winrt::delegate<>;
  296. enum class TestDispatcherMode
  297. {
  298. Dispatch,
  299. RaceDispatch,
  300. Orphan,
  301. Fail,
  302. };
  303. struct TestDispatcher
  304. {
  305. TestDispatcher() = default;
  306. TestDispatcher(TestDispatcher const&) = delete;
  307. TestDispatcherMode mode = TestDispatcherMode::Dispatch;
  308. TestDispatcherPriority expected_priority = TestDispatcherPriority::Normal;
  309. void TryEnqueue(TestDispatcherPriority priority, TestDispatcherHandler const& handler) const
  310. {
  311. REQUIRE(priority == expected_priority);
  312. if (mode == TestDispatcherMode::Fail)
  313. {
  314. throw winrt::hresult_not_implemented();
  315. }
  316. if (mode == TestDispatcherMode::RaceDispatch)
  317. {
  318. handler();
  319. return;
  320. }
  321. std::ignore = [](auto mode, auto handler) ->winrt::fire_and_forget
  322. {
  323. co_await winrt::resume_background();
  324. if (mode == TestDispatcherMode::Dispatch)
  325. {
  326. handler();
  327. }
  328. }(mode, handler);
  329. }
  330. };
  331. }
  332. namespace wil::details
  333. {
  334. template<>
  335. struct dispatcher_traits<test::TestDispatcher>
  336. {
  337. using Priority = test::TestDispatcherPriority;
  338. using Handler = test::TestDispatcherHandler;
  339. using Scheduler = dispatcher_TryEnqueue;
  340. };
  341. }
  342. TEST_CASE("CppWinRTTests::ResumeForegroundTests", "[cppwinrt]")
  343. {
  344. // Verify that the DispatcherQueue version has been unlocked.
  345. using Verify = decltype(wil::resume_foreground(winrt::Windows::System::DispatcherQueue{ nullptr }));
  346. static_assert(wistd::is_trivial_v<Verify> || !wistd::is_trivial_v<Verify>);
  347. []() -> winrt::Windows::Foundation::IAsyncAction
  348. {
  349. test::TestDispatcher dispatcher;
  350. // Normal case: Resumes on new thread.
  351. dispatcher.mode = test::TestDispatcherMode::Dispatch;
  352. co_await wil::resume_foreground(dispatcher);
  353. // Race case: Resumes before TryEnqueue returns.
  354. dispatcher.mode = test::TestDispatcherMode::RaceDispatch;
  355. co_await wil::resume_foreground(dispatcher);
  356. // Orphan case: Never resumes, detected when handler is destructed without ever being invoked.
  357. dispatcher.mode = test::TestDispatcherMode::Orphan;
  358. bool seen = false;
  359. try
  360. {
  361. co_await wil::resume_foreground(dispatcher);
  362. }
  363. catch (winrt::hresult_error const& e)
  364. {
  365. seen = e.code() == HRESULT_FROM_WIN32(HRESULT_FROM_WIN32(ERROR_NO_TASK_QUEUE));
  366. }
  367. REQUIRE(seen);
  368. // Fail case: Can't even schedule the resumption.
  369. dispatcher.mode = test::TestDispatcherMode::Fail;
  370. seen = false;
  371. try
  372. {
  373. co_await wil::resume_foreground(dispatcher);
  374. }
  375. catch (winrt::hresult_not_implemented const&)
  376. {
  377. seen = true;
  378. }
  379. REQUIRE(seen);
  380. // Custom priority.
  381. dispatcher.mode = test::TestDispatcherMode::Dispatch;
  382. dispatcher.expected_priority = test::TestDispatcherPriority::Weird;
  383. co_await wil::resume_foreground(dispatcher, test::TestDispatcherPriority::Weird);
  384. }().get();
  385. }
  386. #endif // coroutines