OutcomeTests.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "AzCore/Outcome/Outcome.h"
  9. #include <AzCore/Serialization/SerializeContext.h>
  10. #include <AzCore/Memory/PoolAllocator.h>
  11. #include <AzCore/UnitTest/TestTypes.h>
  12. namespace UnitTest
  13. {
  14. //! Class that tracks how it was build and modified
  15. struct KnowThyself
  16. {
  17. enum class State
  18. {
  19. NormalConstructor,
  20. CopyConstructor,
  21. MoveConstructor,
  22. InitializerListConstructor,
  23. CopyAssignment,
  24. MoveAssignment,
  25. StolenFrom,
  26. Destroyed,
  27. };
  28. KnowThyself(int id)
  29. : m_state(State::NormalConstructor)
  30. , m_id(id)
  31. {}
  32. KnowThyself(const KnowThyself& rhs)
  33. : m_state(State::CopyConstructor)
  34. , m_id(rhs.m_id)
  35. , m_originalState(rhs.m_state)
  36. , m_lineage(rhs.m_lineage + 1)
  37. {
  38. }
  39. KnowThyself(KnowThyself&& rhs)
  40. : m_state(State::MoveConstructor)
  41. , m_id(rhs.m_id)
  42. , m_originalState(rhs.m_state)
  43. , m_lineage(rhs.m_lineage + 1)
  44. {
  45. rhs.m_state = State::StolenFrom;
  46. }
  47. KnowThyself(std::initializer_list<int> lastBecomesId)
  48. : m_state(State::InitializerListConstructor)
  49. , m_id(*(lastBecomesId.end() - 1))
  50. {}
  51. KnowThyself& operator=(const KnowThyself& rhs)
  52. {
  53. m_state = State::CopyAssignment;
  54. m_id = rhs.m_id;
  55. m_originalState = rhs.m_state;
  56. m_lineage = rhs.m_lineage + 1;
  57. return *this;
  58. }
  59. KnowThyself& operator=(KnowThyself&& rhs)
  60. {
  61. m_state = State::MoveAssignment;
  62. m_id = rhs.m_id;
  63. m_originalState = rhs.m_state;
  64. m_lineage = rhs.m_lineage + 1;
  65. rhs.m_state = State::StolenFrom;
  66. return *this;
  67. }
  68. ~KnowThyself()
  69. {
  70. m_state = State::Destroyed;
  71. }
  72. State m_state = State::Destroyed;
  73. int m_id = -1;
  74. State m_originalState = State::Destroyed;
  75. int m_lineage = 0;
  76. };
  77. struct Aligned16
  78. {
  79. alignas(16) char m_data;
  80. };
  81. //! Class that changes a value when it's created and destroyed.
  82. //! +1 on creation. -1 on destruction.
  83. struct RefCounter
  84. {
  85. RefCounter(int* ref)
  86. {
  87. intPtr = ref;
  88. (*intPtr)++;
  89. }
  90. RefCounter(const RefCounter& other)
  91. {
  92. intPtr = other.intPtr;
  93. (*intPtr)++;
  94. }
  95. RefCounter(RefCounter&& other)
  96. {
  97. intPtr = other.intPtr;
  98. other.intPtr = nullptr;
  99. }
  100. ~RefCounter()
  101. {
  102. if (intPtr)
  103. {
  104. (*intPtr)--;
  105. }
  106. }
  107. RefCounter& operator=(const RefCounter& other)
  108. {
  109. intPtr = other.intPtr;
  110. (*intPtr)++;
  111. return *this;
  112. }
  113. int* intPtr;
  114. };
  115. //! A class for testing that the appropriate constructor and operator=
  116. //! functions are used when copying Outcome's contained value.
  117. //! A raw copy of this class or its members will not suffice.
  118. struct ClassWithComplexMembers
  119. {
  120. //! unordered_set contains a list,
  121. //! The list contains a head node with next and prev pointers.
  122. //! In an empty list, the head node points at itself.
  123. //! A raw copy of this list results in a head node that
  124. //! points to the head node from the other list.
  125. AZStd::unordered_set<uint32_t> s1;
  126. AZStd::unordered_set<uint32_t> s2;
  127. };
  128. class OutcomeTest
  129. : public ::testing::Test
  130. {
  131. public:
  132. enum class Error
  133. {
  134. InvalidInput,
  135. KnuckleHeads,
  136. };
  137. AZ::Outcome<KnowThyself, Error> TestReturnsError()
  138. {
  139. return AZ::Failure(Error::InvalidInput);
  140. }
  141. AZ::Outcome<KnowThyself, Error> TestReturnsValue(int id)
  142. {
  143. return AZ::Success(KnowThyself(id));
  144. }
  145. void run()
  146. {
  147. { // test function returning error
  148. auto outcome = TestReturnsError();
  149. AZ_TEST_ASSERT(!outcome.IsSuccess());
  150. AZ_TEST_ASSERT(outcome.GetError() == Error::InvalidInput);
  151. if (outcome)
  152. {
  153. AZ_TEST_ASSERT(false && "bool operator not working for errors");
  154. }
  155. // test const and non-const GetError()
  156. const auto& constOutcome = outcome;
  157. AZ_TEST_ASSERT(constOutcome.GetError() == Error::InvalidInput);
  158. outcome.GetError() = Error::KnuckleHeads;
  159. AZ_TEST_ASSERT(outcome.GetError() == Error::KnuckleHeads);
  160. }
  161. { // test function returning success
  162. auto outcome = TestReturnsValue(5);
  163. AZ_TEST_ASSERT(outcome.IsSuccess());
  164. AZ_TEST_ASSERT(outcome.GetValue().m_id == 5);
  165. if (!outcome)
  166. {
  167. AZ_TEST_ASSERT(false && "bool operator not working for successes");
  168. }
  169. // test const and non-const GetValue()
  170. const auto& constOutcome = outcome;
  171. AZ_TEST_ASSERT(constOutcome.GetValue().m_id == 5);
  172. outcome.GetValue().m_id = 7;
  173. AZ_TEST_ASSERT(outcome.GetValue().m_id == 7);
  174. }
  175. { // test value move constructor
  176. AZ::Outcome<KnowThyself, Error> outcome = AZ::Success(KnowThyself(5));
  177. AZ_TEST_ASSERT(outcome.IsSuccess());
  178. AZ_TEST_ASSERT(outcome.GetValue().m_id == 5);
  179. AZ_TEST_ASSERT(outcome.GetValue().m_state == KnowThyself::State::MoveConstructor);
  180. }
  181. { // test value copy constructor
  182. KnowThyself lvalue(5);
  183. AZ::Outcome<KnowThyself, Error> outcome = AZ::Success(lvalue);
  184. AZ_TEST_ASSERT(outcome.IsSuccess());
  185. AZ_TEST_ASSERT(outcome.GetValue().m_id == 5);
  186. AZ_TEST_ASSERT(outcome.GetValue().m_state == KnowThyself::State::CopyConstructor);
  187. AZ_TEST_ASSERT(lvalue.m_state != KnowThyself::State::StolenFrom);
  188. }
  189. { // test error copy constructor
  190. KnowThyself lvalue(5);
  191. AZ::Outcome<int, KnowThyself> outcome = AZ::Failure(lvalue);
  192. AZ_TEST_ASSERT(!outcome.IsSuccess());
  193. AZ_TEST_ASSERT(outcome.GetError().m_id == 5);
  194. AZ_TEST_ASSERT(outcome.GetError().m_state == KnowThyself::State::CopyConstructor);
  195. }
  196. { // test error move constructor
  197. AZ::Outcome<int, KnowThyself> outcome = AZ::Failure(KnowThyself(5));
  198. AZ_TEST_ASSERT(!outcome.IsSuccess());
  199. AZ_TEST_ASSERT(outcome.GetError().m_id == 5);
  200. AZ_TEST_ASSERT(outcome.GetError().m_state == KnowThyself::State::MoveConstructor);
  201. }
  202. { // test copy constructor on successful outcome
  203. AZ::Outcome<KnowThyself, Error> outcomeSrc = AZ::Success(KnowThyself(5));
  204. AZ::Outcome<KnowThyself, Error> outcomeDst = outcomeSrc;
  205. AZ_TEST_ASSERT(outcomeDst.IsSuccess());
  206. AZ_TEST_ASSERT(outcomeDst.GetValue().m_id == 5);
  207. AZ_TEST_ASSERT(outcomeDst.GetValue().m_state == KnowThyself::State::CopyConstructor);
  208. AZ_TEST_ASSERT(outcomeSrc.GetValue().m_state != KnowThyself::State::StolenFrom);
  209. }
  210. { // test copy constructor on failed outcome
  211. AZ::Outcome<int, KnowThyself> outcomeSrc = AZ::Failure(KnowThyself(5));
  212. AZ::Outcome<int, KnowThyself> outcomeDst = outcomeSrc;
  213. AZ_TEST_ASSERT(!outcomeDst.IsSuccess());
  214. AZ_TEST_ASSERT(outcomeDst.GetError().m_id == 5);
  215. AZ_TEST_ASSERT(outcomeDst.GetError().m_state == KnowThyself::State::CopyConstructor);
  216. AZ_TEST_ASSERT(outcomeSrc.GetError().m_state != KnowThyself::State::StolenFrom);
  217. }
  218. { // test move constructor on successful outcome
  219. AZ::Outcome<KnowThyself, Error> outcome(AZ::Outcome<KnowThyself, Error>(AZ::Success(KnowThyself(5))));
  220. AZ_TEST_ASSERT(outcome.IsSuccess());
  221. AZ_TEST_ASSERT(outcome.GetValue().m_id == 5);
  222. AZ_TEST_ASSERT(outcome.GetValue().m_state == KnowThyself::State::MoveConstructor);
  223. }
  224. { // test move constructor on failed outcome
  225. AZ::Outcome<int, KnowThyself> outcome(AZ::Outcome<int, KnowThyself>(AZ::Failure(KnowThyself(5))));
  226. AZ_TEST_ASSERT(!outcome.IsSuccess());
  227. AZ_TEST_ASSERT(outcome.GetError().m_id == 5);
  228. AZ_TEST_ASSERT(outcome.GetError().m_state == KnowThyself::State::MoveConstructor);
  229. }
  230. { // test assignment between successful outcomes
  231. AZ::Outcome<KnowThyself, int> outcomeSrc(AZ::Success(KnowThyself(5)));
  232. AZ::Outcome<KnowThyself, int> outcomeDst(AZ::Success(KnowThyself(99)));
  233. outcomeDst = outcomeSrc; // copy from src
  234. AZ_TEST_ASSERT(outcomeDst.GetValue().m_id == 5);
  235. AZ_TEST_ASSERT(outcomeDst.GetValue().m_state == KnowThyself::State::CopyAssignment);
  236. AZ_TEST_ASSERT(outcomeSrc.GetValue().m_state != KnowThyself::State::StolenFrom);
  237. outcomeDst = AZStd::move(outcomeSrc); // move from src
  238. AZ_TEST_ASSERT(outcomeDst.GetValue().m_id == 5);
  239. AZ_TEST_ASSERT(outcomeDst.GetValue().m_state == KnowThyself::State::MoveAssignment);
  240. AZ_TEST_ASSERT(outcomeSrc.GetValue().m_state == KnowThyself::State::StolenFrom);
  241. }
  242. { // test assignment between failed outcomes
  243. AZ::Outcome<int, KnowThyself> outcomeSrc(AZ::Failure(KnowThyself(5)));
  244. AZ::Outcome<int, KnowThyself> outcomeDst(AZ::Failure(KnowThyself(99)));
  245. outcomeDst = outcomeSrc; // copy from src
  246. AZ_TEST_ASSERT(outcomeDst.GetError().m_id == 5);
  247. AZ_TEST_ASSERT(outcomeDst.GetError().m_state == KnowThyself::State::CopyAssignment);
  248. AZ_TEST_ASSERT(outcomeSrc.GetError().m_state != KnowThyself::State::StolenFrom);
  249. outcomeDst = AZStd::move(outcomeSrc); // move from src
  250. AZ_TEST_ASSERT(outcomeDst.GetError().m_id == 5);
  251. AZ_TEST_ASSERT(outcomeDst.GetError().m_state == KnowThyself::State::MoveAssignment);
  252. AZ_TEST_ASSERT(outcomeSrc.GetError().m_state == KnowThyself::State::StolenFrom);
  253. }
  254. { // test assignment operator going from successful to failed outcome
  255. int counter = 0;
  256. AZ::Outcome<RefCounter, KnowThyself> successToFailure = AZ::Success(RefCounter(&counter));
  257. AZ::Outcome<RefCounter, KnowThyself> failedOutcome = AZ::Failure(KnowThyself(5));
  258. AZ_TEST_ASSERT(counter == 1);
  259. successToFailure = failedOutcome;
  260. AZ_TEST_ASSERT(counter == 0); // ensure destructor ran for successToFailure.m_success
  261. AZ_TEST_ASSERT(!successToFailure.IsSuccess());
  262. AZ_TEST_ASSERT(successToFailure.GetError().m_id == 5);
  263. AZ_TEST_ASSERT(failedOutcome.GetError().m_state != KnowThyself::State::StolenFrom);
  264. successToFailure = AZ::Success(RefCounter(&counter)); // reset back to success
  265. AZ_TEST_ASSERT(counter == 1);
  266. successToFailure = AZStd::move(failedOutcome);
  267. AZ_TEST_ASSERT(counter == 0); // ensure destructor ran for successToFailure.m_success
  268. AZ_TEST_ASSERT(!successToFailure.IsSuccess());
  269. AZ_TEST_ASSERT(successToFailure.GetError().m_id == 5);
  270. AZ_TEST_ASSERT(failedOutcome.GetError().m_state == KnowThyself::State::StolenFrom);
  271. }
  272. { // test assignment operator going from failed to successful outcome
  273. int counter = 0;
  274. AZ::Outcome<KnowThyself, RefCounter> failureToSuccess = AZ::Failure(RefCounter(&counter));
  275. AZ::Outcome<KnowThyself, RefCounter> successfulOutcome = AZ::Success(KnowThyself(5));
  276. AZ_TEST_ASSERT(counter == 1);
  277. failureToSuccess = successfulOutcome;
  278. AZ_TEST_ASSERT(counter == 0); // ensure destructor ran for failureToSuccess.m_failure
  279. AZ_TEST_ASSERT(failureToSuccess.IsSuccess());
  280. AZ_TEST_ASSERT(failureToSuccess.GetValue().m_id == 5);
  281. AZ_TEST_ASSERT(successfulOutcome.GetValue().m_state != KnowThyself::State::StolenFrom);
  282. failureToSuccess = AZ::Failure(RefCounter(&counter)); // reset back to failure
  283. AZ_TEST_ASSERT(counter == 1);
  284. failureToSuccess = AZStd::move(successfulOutcome);
  285. AZ_TEST_ASSERT(counter == 0); // ensure destructor ran for failureToSuccess.m_failure
  286. AZ_TEST_ASSERT(failureToSuccess.IsSuccess());
  287. AZ_TEST_ASSERT(failureToSuccess.GetValue().m_id == 5);
  288. AZ_TEST_ASSERT(successfulOutcome.GetValue().m_state == KnowThyself::State::StolenFrom);
  289. }
  290. { // test value type with trivial destructor
  291. AZ::Outcome<int, KnowThyself> outcome = AZ::Success(5);
  292. AZ_TEST_ASSERT(outcome.IsSuccess());
  293. }
  294. { // test error type with trivial destructor
  295. AZ::Outcome<KnowThyself, int> outcome = AZ::Failure(5);
  296. AZ_TEST_ASSERT(!outcome.IsSuccess());
  297. }
  298. { // test TakeValue and TakeError
  299. AZ::Outcome<KnowThyself, Error> goodOutcome = AZ::Success(KnowThyself(5));
  300. KnowThyself fromGoodOutcome = goodOutcome.TakeValue();
  301. AZ_TEST_ASSERT(fromGoodOutcome.m_id == 5);
  302. AZ_TEST_ASSERT(fromGoodOutcome.m_state == KnowThyself::State::MoveConstructor);
  303. AZ_TEST_ASSERT(goodOutcome.GetValue().m_state == KnowThyself::State::StolenFrom);
  304. AZ::Outcome<int, KnowThyself> failedOutcome = AZ::Failure(KnowThyself(5));
  305. KnowThyself fromFailedOutcome = failedOutcome.TakeError();
  306. AZ_TEST_ASSERT(fromFailedOutcome.m_id == 5);
  307. AZ_TEST_ASSERT(fromFailedOutcome.m_state == KnowThyself::State::MoveConstructor);
  308. AZ_TEST_ASSERT(failedOutcome.GetError().m_state == KnowThyself::State::StolenFrom);
  309. }
  310. { // test GetValueOr()
  311. AZ::Outcome<KnowThyself, Error> goodOutcome = AZ::Success(KnowThyself(5));
  312. AZ::Outcome<KnowThyself, Error> failedOutcome = AZ::Failure(Error::InvalidInput);
  313. KnowThyself shouldBeFive = goodOutcome.GetValueOr(KnowThyself(7));
  314. AZ_TEST_ASSERT(shouldBeFive.m_id == 5);
  315. KnowThyself shouldBeSeven = failedOutcome.GetValueOr(KnowThyself(7));
  316. AZ_TEST_ASSERT(shouldBeSeven.m_id == 7);
  317. // test that GetValueOr properly handles rvalue parameters
  318. KnowThyself shouldBeStolenFrom(9);
  319. KnowThyself shouldBeNine = failedOutcome.GetValueOr(AZStd::move(shouldBeStolenFrom));
  320. AZ_TEST_ASSERT(shouldBeNine.m_id == 9);
  321. AZ_TEST_ASSERT(shouldBeStolenFrom.m_state == KnowThyself::State::StolenFrom);
  322. // test that GetValueOr properly handles lvalue parameters
  323. KnowThyself shouldEqualGood = failedOutcome.GetValueOr(goodOutcome.GetValue());
  324. AZ_TEST_ASSERT(shouldEqualGood.m_id == 5);
  325. AZ_TEST_ASSERT(goodOutcome.GetValue().m_state != KnowThyself::State::StolenFrom);
  326. }
  327. { // test alignment
  328. AZ_TEST_STATIC_ASSERT((AZStd::alignment_of<AZ::Outcome<Aligned16, char> >::value == 16));
  329. AZ_TEST_STATIC_ASSERT((AZStd::alignment_of<AZ::Outcome<char, Aligned16> >::value == 16));
  330. AZ::Outcome<Aligned16, char> outcomeA = AZ::Success(Aligned16());
  331. AZ::Outcome<char, Aligned16> outcomeB = AZ::Failure(Aligned16());
  332. AZ_TEST_ASSERT(reinterpret_cast<size_t>(&outcomeA.GetValue().m_data) % 16 == 0);
  333. AZ_TEST_ASSERT(reinterpret_cast<size_t>(&outcomeB.GetError().m_data) % 16 == 0);
  334. }
  335. { // test with void types
  336. AZ::Outcome<void, void> outcome1 = AZ::Success();
  337. AZ_TEST_ASSERT(outcome1.IsSuccess());
  338. AZ::Outcome<void, void> outcome2 = AZ::Failure();
  339. AZ_TEST_ASSERT(!outcome2.IsSuccess());
  340. AZ::Outcome<void, Error> outcome3 = AZ::Success();
  341. AZ_TEST_ASSERT(outcome3.IsSuccess());
  342. AZ::Outcome<void, Error> outcome4 = AZ::Failure(Error::KnuckleHeads);
  343. AZ_TEST_ASSERT(!outcome4.IsSuccess());
  344. AZ_TEST_ASSERT(outcome4.GetError() == Error::KnuckleHeads);
  345. AZ::Outcome<Error, void> outcome5 = AZ::Success(Error::KnuckleHeads);
  346. AZ_TEST_ASSERT(outcome5.IsSuccess());
  347. AZ_TEST_ASSERT(outcome5.GetValue() == Error::KnuckleHeads);
  348. AZ::Outcome<Error, void> outcome6 = AZ::Failure();
  349. AZ_TEST_ASSERT(!outcome6.IsSuccess());
  350. }
  351. { // test that not too many copies/moves are performed
  352. // Please edit these test cases when number of moves/copies changes
  353. AZ::Outcome<KnowThyself> outcome1 = AZ::Success(KnowThyself(2));
  354. AZ_TEST_ASSERT(outcome1.GetValue().m_state == KnowThyself::State::MoveConstructor);
  355. AZ_TEST_ASSERT(outcome1.GetValue().m_lineage == 2);
  356. KnowThyself val1 = outcome1.GetValue();
  357. AZ_TEST_ASSERT(val1.m_lineage == 3);
  358. AZ::Outcome<KnowThyself> outcome2 = AZStd::move(outcome1);
  359. AZ_TEST_ASSERT(outcome2.GetValue().m_state == KnowThyself::State::MoveConstructor);
  360. AZ::Outcome<KnowThyself> outcome3 = outcome2;
  361. AZ_TEST_ASSERT(outcome3.GetValue().m_state == KnowThyself::State::CopyConstructor);
  362. }
  363. {
  364. // A previous iteration of Outcome would fail due to
  365. // the SuccessValue<ClassWithComplexMembers> being copied improperly.
  366. ClassWithComplexMembers complex;
  367. AZ::Outcome<ClassWithComplexMembers> outcome = AZ::Success(AZStd::move(complex));
  368. AZ_TEST_ASSERT(outcome.GetValue().s1.empty());
  369. }
  370. }
  371. };
  372. TEST_F(OutcomeTest, Test)
  373. {
  374. run();
  375. }
  376. class OutcomeSerializationTest
  377. : public LeakDetectionFixture
  378. {
  379. public:
  380. // We must expose the class for serialization first.
  381. void SetUp() override
  382. {
  383. LeakDetectionFixture::SetUp();
  384. m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  385. }
  386. void TearDown() override
  387. {
  388. m_serializeContext.reset();
  389. LeakDetectionFixture::TearDown();
  390. }
  391. protected:
  392. AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
  393. };
  394. TEST_F(OutcomeSerializationTest, ReflectingOutcomeTwiceDoesNotLogError)
  395. {
  396. using TestOutcome = AZ::Outcome<int, int>;
  397. AZ::GenericClassInfo* outcomeGenericInfo = AZ::SerializeGenericTypeInfo<TestOutcome>::GetGenericInfo();
  398. ASSERT_NE(nullptr, outcomeGenericInfo);
  399. outcomeGenericInfo->Reflect(m_serializeContext.get());
  400. AZ_TEST_START_TRACE_SUPPRESSION;
  401. outcomeGenericInfo->Reflect(m_serializeContext.get());
  402. AZ_TEST_STOP_TRACE_SUPPRESSION(0);
  403. }
  404. } // namespace UnitTest