ResultTests.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. #include <wil/com.h>
  2. #include <wil/result.h>
  3. #if (NTDDI_VERSION >= NTDDI_WIN8)
  4. #include <wil/result_originate.h>
  5. #endif
  6. #include <roerrorapi.h>
  7. #include "common.h"
  8. static volatile long objectCount = 0;
  9. struct SharedObject
  10. {
  11. SharedObject()
  12. {
  13. ::InterlockedIncrement(&objectCount);
  14. }
  15. ~SharedObject()
  16. {
  17. ::InterlockedDecrement(&objectCount);
  18. }
  19. void ProcessShutdown()
  20. {
  21. }
  22. int value{};
  23. };
  24. TEST_CASE("ResultTests::SemaphoreValue", "[result]")
  25. {
  26. auto TestValue = [&](auto start, auto end)
  27. {
  28. wil::details_abi::SemaphoreValue semaphore;
  29. for (auto index = start; index <= end; index++)
  30. {
  31. semaphore.Destroy();
  32. REQUIRE(SUCCEEDED(semaphore.CreateFromValue(L"test", index)));
  33. auto num1 = index;
  34. auto num2 = index;
  35. REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num1)));
  36. REQUIRE(SUCCEEDED(semaphore.TryGetValue(L"test", &num2)));
  37. REQUIRE(num1 == index);
  38. REQUIRE(num2 == index);
  39. }
  40. };
  41. // Test 32-bit values (edge cases)
  42. TestValue(0u, 10u);
  43. TestValue(250u, 260u);
  44. TestValue(0x7FFFFFF0u, 0x7FFFFFFFu);
  45. // Test 64-bit values (edge cases)
  46. TestValue(0ull, 10ull);
  47. TestValue(250ull, 260ull);
  48. TestValue(0x000000007FFFFFF0ull, 0x000000008000000Full);
  49. TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
  50. TestValue(0x00000000FFFFFFF0ull, 0x000000010000000Full);
  51. TestValue(0x3FFFFFFFFFFFFFF0ull, 0x3FFFFFFFFFFFFFFFull);
  52. // Test pointer values
  53. wil::details_abi::SemaphoreValue semaphore;
  54. void* address = &semaphore;
  55. REQUIRE(SUCCEEDED(semaphore.CreateFromPointer(L"test", address)));
  56. void* ptr;
  57. REQUIRE(SUCCEEDED(semaphore.TryGetPointer(L"test", &ptr)));
  58. REQUIRE(ptr == address);
  59. }
  60. TEST_CASE("ResultTests::ProcessLocalStorage", "[result]")
  61. {
  62. // Test process local storage memory and ref-counting
  63. {
  64. wil::details_abi::ProcessLocalStorage<SharedObject> obj1("ver1");
  65. wil::details_abi::ProcessLocalStorage<SharedObject> obj2("ver1");
  66. auto& o1 = *obj1.GetShared();
  67. auto& o2 = *obj2.GetShared();
  68. REQUIRE(o1.value == 0);
  69. REQUIRE(o2.value == 0);
  70. o1.value = 42;
  71. REQUIRE(o2.value == 42);
  72. REQUIRE(objectCount == 1);
  73. wil::details_abi::ProcessLocalStorage<SharedObject> obj3("ver3");
  74. auto& o3 = *obj3.GetShared();
  75. REQUIRE(o3.value == 0);
  76. REQUIRE(objectCount == 2);
  77. }
  78. REQUIRE(objectCount == 0);
  79. }
  80. #ifdef WIL_ENABLE_EXCEPTIONS
  81. #pragma warning(push)
  82. #pragma warning(disable: 4702) // Unreachable code
  83. TEST_CASE("ResultTests::ExceptionHandling", "[result]")
  84. {
  85. witest::TestFailureCache failures;
  86. SECTION("Test 'what()' implementation on ResultException")
  87. {
  88. auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
  89. try
  90. {
  91. THROW_HR(E_INVALIDARG);
  92. FAIL("Expected an exception");
  93. }
  94. catch (const std::exception& exception)
  95. {
  96. REQUIRE(failures.size() == 1);
  97. REQUIRE(failures[0].hr == E_INVALIDARG);
  98. auto what = exception.what();
  99. REQUIRE((what && *what));
  100. REQUIRE(strstr(what, "Exception") != nullptr);
  101. }
  102. }
  103. failures.clear();
  104. SECTION("Test messaging from an unhandled std exception")
  105. {
  106. // #pragma warning(suppress: 28931) // unused assignment -- it IS being used... seems like a tool issue.
  107. auto hr = []()
  108. {
  109. try
  110. {
  111. throw std::runtime_error("runtime");
  112. }
  113. catch (...)
  114. {
  115. RETURN_CAUGHT_EXCEPTION();
  116. }
  117. }();
  118. REQUIRE(failures.size() == 1);
  119. REQUIRE(failures[0].hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
  120. REQUIRE(wcsstr(failures[0].pszMessage, L"runtime") != nullptr); // should get the exception what() string...
  121. REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
  122. }
  123. failures.clear();
  124. SECTION("Test messaging from bad_alloc")
  125. {
  126. auto hr = []() -> HRESULT
  127. {
  128. try
  129. {
  130. throw std::bad_alloc();
  131. }
  132. catch (...)
  133. {
  134. RETURN_CAUGHT_EXCEPTION();
  135. }
  136. }();
  137. REQUIRE(failures.size() == 1);
  138. REQUIRE(failures[0].hr == E_OUTOFMEMORY);
  139. REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
  140. REQUIRE(hr == E_OUTOFMEMORY);
  141. }
  142. failures.clear();
  143. SECTION("Test messaging from a WIL exception")
  144. {
  145. auto hr = []() -> HRESULT
  146. {
  147. try
  148. {
  149. THROW_HR(E_INVALIDARG);
  150. }
  151. catch (...)
  152. {
  153. RETURN_CAUGHT_EXCEPTION();
  154. }
  155. return S_OK;
  156. }();
  157. REQUIRE(failures.size() == 2);
  158. REQUIRE(failures[0].hr == E_INVALIDARG);
  159. REQUIRE(failures[0].pszMessage == nullptr);
  160. REQUIRE(failures[1].hr == E_INVALIDARG);
  161. REQUIRE(wcsstr(failures[1].pszMessage, L"Exception") != nullptr); // should get the exception debug string...
  162. REQUIRE(hr == E_INVALIDARG);
  163. }
  164. failures.clear();
  165. SECTION("Fail fast an unknown exception")
  166. {
  167. REQUIRE(witest::DoesCodeCrash([]()
  168. {
  169. try
  170. {
  171. throw E_INVALIDARG; // bad throw... (long)
  172. }
  173. catch (...)
  174. {
  175. RETURN_CAUGHT_EXCEPTION();
  176. }
  177. }));
  178. }
  179. failures.clear();
  180. SECTION("Log test (returns hr)")
  181. {
  182. HRESULT hr = S_OK;
  183. try
  184. {
  185. throw std::bad_alloc();
  186. }
  187. catch (...)
  188. {
  189. hr = LOG_CAUGHT_EXCEPTION();
  190. auto hrDirect = wil::ResultFromCaughtException();
  191. REQUIRE(hr == hrDirect);
  192. }
  193. REQUIRE(failures.size() == 1);
  194. REQUIRE(failures[0].hr == E_OUTOFMEMORY);
  195. REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
  196. REQUIRE(hr == E_OUTOFMEMORY);
  197. }
  198. failures.clear();
  199. SECTION("Fail-fast test")
  200. {
  201. REQUIRE_CRASH([]()
  202. {
  203. try
  204. {
  205. throw std::bad_alloc();
  206. }
  207. catch (...)
  208. {
  209. FAIL_FAST_CAUGHT_EXCEPTION();
  210. }
  211. }());
  212. }
  213. failures.clear();
  214. SECTION("Exception test (different exception type thrown...)")
  215. {
  216. auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
  217. size_t line = 0;
  218. try
  219. {
  220. try
  221. {
  222. throw std::bad_alloc();
  223. }
  224. catch (...)
  225. {
  226. line = __LINE__; THROW_NORMALIZED_CAUGHT_EXCEPTION();
  227. }
  228. }
  229. catch (const wil::ResultException& exception)
  230. {
  231. REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have thrown new, so we should have the rethrow line number
  232. REQUIRE(exception.GetErrorCode() == E_OUTOFMEMORY);
  233. }
  234. catch (...)
  235. {
  236. FAIL();
  237. }
  238. REQUIRE(failures.size() == 1);
  239. REQUIRE(failures[0].hr == E_OUTOFMEMORY);
  240. REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
  241. }
  242. failures.clear();
  243. SECTION("Exception test (rethrow same exception type...)")
  244. {
  245. auto swap = witest::AssignTemporaryValue(&wil::g_fResultThrowPlatformException, false);
  246. size_t line = 0;
  247. try
  248. {
  249. try
  250. {
  251. line = __LINE__; THROW_HR(E_OUTOFMEMORY);
  252. }
  253. catch (...)
  254. {
  255. THROW_NORMALIZED_CAUGHT_EXCEPTION();
  256. }
  257. }
  258. catch (const wil::ResultException& exception)
  259. {
  260. REQUIRE(exception.GetFailureInfo().uLineNumber == line); // should have re-thrown the original exception (with the original line number)
  261. }
  262. catch (...)
  263. {
  264. FAIL();
  265. }
  266. }
  267. failures.clear();
  268. SECTION("Test catch message")
  269. {
  270. try
  271. {
  272. throw std::bad_alloc();
  273. }
  274. catch (...)
  275. {
  276. LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42);
  277. }
  278. REQUIRE(failures.size() == 1);
  279. REQUIRE(failures[0].hr == E_OUTOFMEMORY);
  280. REQUIRE(wcsstr(failures[0].pszMessage, L"alloc") != nullptr); // should get the exception what() string...
  281. REQUIRE(wcsstr(failures[0].pszMessage, L"train") != nullptr); // should *also* get the message...
  282. REQUIRE(wcsstr(failures[0].pszMessage, L"42") != nullptr);
  283. }
  284. failures.clear();
  285. SECTION("Test messaging from a WIL exception")
  286. {
  287. auto hr = []() -> HRESULT
  288. {
  289. try
  290. {
  291. throw std::bad_alloc();
  292. }
  293. catch (...)
  294. {
  295. RETURN_CAUGHT_EXCEPTION_EXPECTED();
  296. }
  297. }();
  298. REQUIRE(failures.empty());
  299. REQUIRE(hr == E_OUTOFMEMORY);
  300. }
  301. failures.clear();
  302. SECTION("Test ResultFromException...")
  303. {
  304. auto hrOk = wil::ResultFromException([&]
  305. {
  306. });
  307. REQUIRE(hrOk == S_OK);
  308. auto hr = wil::ResultFromException([&]
  309. {
  310. throw std::bad_alloc();
  311. });
  312. REQUIRE(failures.empty());
  313. REQUIRE(hr == E_OUTOFMEMORY);
  314. }
  315. failures.clear();
  316. SECTION("Explicit failfast for unrecognized")
  317. {
  318. REQUIRE_CRASH(wil::ResultFromException([&]
  319. {
  320. throw E_FAIL;
  321. }));
  322. }
  323. failures.clear();
  324. SECTION("Manual debug-only validation of the SEH failfast")
  325. {
  326. auto hr1 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]()
  327. {
  328. // Uncomment to test SEH fail-fast
  329. // throw E_FAIL;
  330. });
  331. REQUIRE(hr1 == S_OK);
  332. auto hr2 = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::Thrown, [&]
  333. {
  334. // Uncomment to test SEH fail-fast
  335. // throw std::range_error("range");
  336. });
  337. REQUIRE(hr2 == S_OK);
  338. wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
  339. {
  340. // Uncomment to test SEH fail-fast
  341. // THROW_HR(E_FAIL);
  342. });
  343. }
  344. failures.clear();
  345. SECTION("Standard")
  346. {
  347. auto line = __LINE__; auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
  348. {
  349. THROW_HR(E_INVALIDARG);
  350. });
  351. REQUIRE(failures.size() == 2);
  352. REQUIRE(static_cast<decltype(line)>(failures[1].uLineNumber) == line);
  353. REQUIRE(hr == E_INVALIDARG);
  354. }
  355. failures.clear();
  356. SECTION("bad_alloc")
  357. {
  358. auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
  359. {
  360. throw std::bad_alloc();
  361. });
  362. REQUIRE(failures.size() == 1);
  363. REQUIRE(hr == E_OUTOFMEMORY);
  364. }
  365. failures.clear();
  366. SECTION("std::exception")
  367. {
  368. auto hr = wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, [&]
  369. {
  370. throw std::range_error("range");
  371. });
  372. REQUIRE(failures.size() == 1);
  373. REQUIRE(wcsstr(failures[0].pszMessage, L"range") != nullptr);
  374. REQUIRE(hr == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION));
  375. }
  376. }
  377. void ExceptionHandlingCompilationTest()
  378. {
  379. []{ try { throw std::bad_alloc(); } CATCH_RETURN(); }();
  380. []{ try { throw std::bad_alloc(); } CATCH_RETURN_MSG("train: %d", 42); }();
  381. []{ try { throw std::bad_alloc(); } CATCH_RETURN_EXPECTED(); }();
  382. []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION(); } }();
  383. []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_MSG("train: %d", 42); } }();
  384. []{ try { throw std::bad_alloc(); } catch (...) { RETURN_CAUGHT_EXCEPTION_EXPECTED(); } }();
  385. try { throw std::bad_alloc(); } CATCH_LOG();
  386. try { throw std::bad_alloc(); } CATCH_LOG_MSG("train: %d", 42);
  387. try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION(); }
  388. try { throw std::bad_alloc(); } catch (...) { LOG_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
  389. try { throw std::bad_alloc(); } CATCH_FAIL_FAST();
  390. try { throw std::bad_alloc(); } CATCH_FAIL_FAST_MSG("train: %d", 42);
  391. try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION(); }
  392. try { throw std::bad_alloc(); } catch (...) { FAIL_FAST_CAUGHT_EXCEPTION_MSG("train: %d", 42); }
  393. try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED(); } catch (...) {}
  394. try { try { throw std::bad_alloc(); } CATCH_THROW_NORMALIZED_MSG("train: %d", 42); } catch (...) {}
  395. try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION(); } } catch (...) {}
  396. try { try { throw std::bad_alloc(); } catch (...) { THROW_NORMALIZED_CAUGHT_EXCEPTION_MSG("train: %d", 42); } } catch (...) {}
  397. wil::ResultFromExceptionDebug(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::All, [&]
  398. {
  399. THROW_HR(E_FAIL);
  400. });
  401. wil::ResultFromException(WI_DIAGNOSTICS_INFO, wil::SupportedExceptions::None, [&]
  402. {
  403. });
  404. wil::ResultFromException([&]
  405. {
  406. });
  407. wil::FailFastException(WI_DIAGNOSTICS_INFO, [&]
  408. {
  409. });
  410. }
  411. #pragma warning(pop)
  412. #endif
  413. TEST_CASE("ResultTests::ErrorMacros", "[result]")
  414. {
  415. REQUIRE_ERROR(FAIL_FAST());
  416. REQUIRE_ERROR(FAIL_FAST_IF(true));
  417. REQUIRE_ERROR(FAIL_FAST_IF_NULL(nullptr));
  418. REQUIRE_NOERROR(FAIL_FAST_IF(false));
  419. REQUIRE_NOERROR(FAIL_FAST_IF_NULL(_ReturnAddress()));
  420. REQUIRE_ERROR(FAIL_FAST_MSG("%d", 42));
  421. REQUIRE_ERROR(FAIL_FAST_IF_MSG(true, "%d", 42));
  422. REQUIRE_ERROR(FAIL_FAST_IF_NULL_MSG(nullptr, "%d", 42));
  423. REQUIRE_NOERROR(FAIL_FAST_IF_MSG(false, "%d", 42));
  424. REQUIRE_NOERROR(FAIL_FAST_IF_NULL_MSG(_ReturnAddress(), "%d", 42));
  425. //wil::g_pfnResultLoggingCallback = ResultMacrosLoggingCallback;
  426. SetLastError(ERROR_PRINTER_ALREADY_EXISTS);
  427. REQUIRE_ERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(FALSE));
  428. REQUIRE_NOERROR(__FAIL_FAST_ASSERT_WIN32_BOOL_FALSE__(TRUE));
  429. }
  430. // The originate helper isn't compatible with CX so don't test it in that mode.
  431. #if !defined(__cplusplus_winrt) && (NTDDI_VERSION >= NTDDI_WIN8)
  432. TEST_CASE("ResultTests::NoOriginationByDefault", "[result]")
  433. {
  434. ::wil::SetOriginateErrorCallback(nullptr);
  435. wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
  436. // We can't guarantee test order, so clear the error payload prior to starting
  437. SetRestrictedErrorInfo(nullptr);
  438. []() -> HRESULT
  439. {
  440. RETURN_HR(S_OK);
  441. }();
  442. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  443. #ifdef WIL_ENABLE_EXCEPTIONS
  444. try
  445. {
  446. THROW_HR(E_FAIL);
  447. }
  448. catch (...) {}
  449. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  450. #endif // WIL_ENABLE_EXCEPTIONS
  451. []() -> HRESULT
  452. {
  453. RETURN_HR(E_FAIL);
  454. }();
  455. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  456. []() -> HRESULT
  457. {
  458. RETURN_IF_FAILED_EXPECTED(E_ACCESSDENIED);
  459. return S_OK;
  460. }();
  461. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  462. }
  463. TEST_CASE("ResultTests::AutomaticOriginationOnFailure", "[result]")
  464. {
  465. ::wil::SetOriginateErrorCallback(::wil::details::RaiseRoOriginateOnWilExceptions);
  466. wil::com_ptr_nothrow<IRestrictedErrorInfo> restrictedErrorInformation;
  467. // Make sure we don't start with an error payload
  468. SetRestrictedErrorInfo(nullptr);
  469. // Success codes shouldn't originate.
  470. []()
  471. {
  472. RETURN_HR(S_OK);
  473. }();
  474. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  475. auto validateOriginatedError = [&](HRESULT hrExpected)
  476. {
  477. wil::unique_bstr descriptionUnused;
  478. HRESULT existingHr = S_OK;
  479. wil::unique_bstr restrictedDescriptionUnused;
  480. wil::unique_bstr capabilitySidUnused;
  481. REQUIRE_SUCCEEDED(restrictedErrorInformation->GetErrorDetails(&descriptionUnused, &existingHr, &restrictedDescriptionUnused, &capabilitySidUnused));
  482. REQUIRE(hrExpected == existingHr);
  483. };
  484. #ifdef WIL_ENABLE_EXCEPTIONS
  485. // Throwing an error should originate.
  486. constexpr HRESULT thrownErrorCode = TYPE_E_ELEMENTNOTFOUND;
  487. try
  488. {
  489. THROW_HR(thrownErrorCode);
  490. }
  491. catch (...) {}
  492. REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
  493. validateOriginatedError(thrownErrorCode);
  494. #endif // WIL_ENABLE_EXCEPTIONS
  495. // Returning an error code should originate.
  496. static constexpr HRESULT returnedErrorCode = REGDB_E_CLASSNOTREG;
  497. []()
  498. {
  499. RETURN_HR(returnedErrorCode);
  500. }();
  501. REQUIRE(S_OK == GetRestrictedErrorInfo(&restrictedErrorInformation));
  502. validateOriginatedError(returnedErrorCode);
  503. // _EXPECTED errors should NOT originate.
  504. static constexpr HRESULT expectedErrorCode = E_ACCESSDENIED;
  505. []()
  506. {
  507. RETURN_IF_FAILED_EXPECTED(expectedErrorCode);
  508. return S_OK;
  509. }();
  510. REQUIRE(S_FALSE == GetRestrictedErrorInfo(&restrictedErrorInformation));
  511. }
  512. #endif
  513. TEST_CASE("ResultTests::ReportDoesNotChangeLastError", "[result]")
  514. {
  515. decltype(wil::details::g_pfnLoggingCallback) oopsie = [](wil::FailureInfo const&) noexcept
  516. {
  517. ::SetLastError(ERROR_ABANDON_HIBERFILE);
  518. };
  519. auto swap = witest::AssignTemporaryValue(&wil::details::g_pfnLoggingCallback, oopsie);
  520. ::SetLastError(ERROR_ABIOS_ERROR);
  521. LOG_IF_WIN32_BOOL_FALSE(FALSE);
  522. REQUIRE(::GetLastError() == ERROR_ABIOS_ERROR);
  523. }