StableDynamicArrayTests.cpp 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024
  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/UnitTest/TestTypes.h>
  9. #include <AzCore/Memory/PoolAllocator.h>
  10. #include <AzCore/Component/ComponentApplication.h>
  11. #include <Atom/Utils/StableDynamicArray.h>
  12. namespace UnitTest
  13. {
  14. // Fixture that creates a bare-bones app
  15. class StableDynamicArrayTests
  16. : public LeakDetectionFixture
  17. {
  18. public:
  19. void SetUp() override
  20. {
  21. LeakDetectionFixture::SetUp();
  22. handles.reserve(s_testCount);
  23. }
  24. void TearDown() override
  25. {
  26. handles = AZStd::vector<AZ::StableDynamicArray<TestItem>::Handle>(); // force memory deallocation.
  27. LeakDetectionFixture::TearDown();
  28. }
  29. struct TestItem
  30. {
  31. TestItem() = default;
  32. TestItem(uint32_t value) : index(value) {}
  33. uint32_t index = 0;
  34. };
  35. static constexpr uint32_t s_testCount = 1000000;
  36. AZStd::vector<AZ::StableDynamicArray<TestItem>::Handle> handles;
  37. };
  38. TEST_F(StableDynamicArrayTests, insert_erase)
  39. {
  40. using namespace AZ;
  41. AZ::StableDynamicArray<TestItem> testArray;
  42. // fill with items
  43. for (uint32_t i = 0; i < s_testCount; ++i)
  44. {
  45. StableDynamicArray<TestItem>::Handle handle = testArray.insert({});
  46. handle->index = i;
  47. handles.push_back(AZStd::move(handle));
  48. }
  49. EXPECT_EQ(testArray.size(), s_testCount);
  50. StableDynamicArrayMetrics metrics = testArray.GetMetrics();
  51. EXPECT_EQ(metrics.m_totalElements, s_testCount);
  52. // remove half of the elements
  53. for (uint32_t i = 0; i < s_testCount; i += 2)
  54. {
  55. testArray.erase(handles.at(i));
  56. }
  57. EXPECT_EQ(testArray.size(), s_testCount / 2);
  58. metrics = testArray.GetMetrics();
  59. EXPECT_EQ(metrics.m_totalElements, s_testCount / 2);
  60. handles.clear(); // cleanup remaining handles.
  61. }
  62. TEST_F(StableDynamicArrayTests, emplace_Free)
  63. {
  64. using namespace AZ;
  65. AZ::StableDynamicArray<TestItem> testArray;
  66. // fill with items
  67. for (uint32_t i = 0; i < s_testCount; ++i)
  68. {
  69. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  70. handles.push_back(AZStd::move(handle));
  71. }
  72. StableDynamicArrayMetrics metrics = testArray.GetMetrics();
  73. EXPECT_EQ(metrics.m_totalElements, s_testCount);
  74. // remove half of the elements
  75. for (uint32_t i = 0; i < s_testCount; i += 2)
  76. {
  77. handles.at(i).Free();
  78. }
  79. metrics = testArray.GetMetrics();
  80. EXPECT_EQ(metrics.m_totalElements, s_testCount / 2);
  81. handles.clear(); // cleanup remaining handles.
  82. }
  83. TEST_F(StableDynamicArrayTests, ReleaseEmptyPages)
  84. {
  85. using namespace AZ;
  86. AZ::StableDynamicArray<TestItem> testArray;
  87. // Test removing items at the end
  88. // fill with items
  89. TestItem item; // test lvalue insert
  90. for (uint32_t i = 0; i < s_testCount; ++i)
  91. {
  92. item.index = i;
  93. StableDynamicArray<TestItem>::Handle handle = testArray.insert(item);
  94. handles.push_back(AZStd::move(handle));
  95. }
  96. StableDynamicArrayMetrics metrics1 = testArray.GetMetrics();
  97. size_t fullPageCount = metrics1.m_elementsPerPage.size();
  98. // remove the last half of the elements
  99. for (uint32_t i = 0; i < s_testCount / 2; ++i)
  100. {
  101. handles.pop_back();
  102. }
  103. // release the pages at the end that are now empty
  104. testArray.ReleaseEmptyPages();
  105. // Defragmenting a handle should still work after releasing empty pages
  106. testArray.DefragmentHandle(handles.back());
  107. StableDynamicArrayMetrics metrics2 = testArray.GetMetrics();
  108. size_t endReducedPageCount = metrics2.m_elementsPerPage.size();
  109. // There should be fewer pages now than before
  110. EXPECT_LT(endReducedPageCount, fullPageCount);
  111. // Test removing All the items
  112. handles.clear(); // cleanup remaining handles.
  113. // release all the pages.
  114. testArray.ReleaseEmptyPages();
  115. StableDynamicArrayMetrics metrics3 = testArray.GetMetrics();
  116. size_t emptyPageCount = metrics3.m_elementsPerPage.size();
  117. // there should be 0 pages now.
  118. EXPECT_EQ(emptyPageCount, 0);
  119. // Test removing items from the beginning
  120. // fill with items
  121. for (uint32_t i = 0; i < s_testCount; ++i)
  122. {
  123. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  124. handles.push_back(AZStd::move(handle));
  125. }
  126. // remove the first half of the elements
  127. for (uint32_t i = 0; i < s_testCount / 2; ++i)
  128. {
  129. handles.at(i).Free();
  130. }
  131. // release the pages at the beginning that are now empty
  132. testArray.ReleaseEmptyPages();
  133. StableDynamicArrayMetrics metrics4 = testArray.GetMetrics();
  134. size_t beginReducedPageCount = metrics4.m_elementsPerPage.size();
  135. // There should be fewer pages now than before
  136. EXPECT_LT(beginReducedPageCount, fullPageCount);
  137. handles.clear(); // cleanup remaining handles.
  138. }
  139. TEST_F(StableDynamicArrayTests, CheckForHolesBetweenPages)
  140. {
  141. constexpr size_t pageSize = 64;
  142. using namespace AZ;
  143. AZ::StableDynamicArray<TestItem, pageSize> testArray;
  144. // fill with 10 pages of items
  145. TestItem item; // test lvalue insert
  146. for (uint32_t i = 0; i < pageSize * 10; ++i)
  147. {
  148. item.index = i;
  149. StableDynamicArray<TestItem>::Handle handle = testArray.insert(item);
  150. handles.push_back(AZStd::move(handle));
  151. }
  152. // Create a hole between the pages by releaseing items in a page
  153. for (uint32_t i = pageSize * 5; i < pageSize * 6; ++i)
  154. {
  155. handles.at(i).Free();
  156. }
  157. testArray.ReleaseEmptyPages();
  158. // Use this lambda to force the test array to think the first page may be empty
  159. auto markFirstPageAsEmpty = [&]()
  160. {
  161. // Also free an element in the first page, so that the first empty page is at the beginning
  162. testArray.erase(handles.at(0));
  163. // Fill the first page back up, so that any further operations will be forced to
  164. // iterate past the hole in search of the next available page
  165. {
  166. TestItem replacementItem;
  167. replacementItem.index = 0;
  168. StableDynamicArray<TestItem>::Handle handle = testArray.insert(replacementItem);
  169. handles.at(0) = AZStd::move(handle);
  170. }
  171. };
  172. markFirstPageAsEmpty();
  173. // Each of these operations will attempt to iterate over all the pages
  174. // This test is validating that they do not crash because they are properly checking for holes
  175. testArray.ReleaseEmptyPages();
  176. markFirstPageAsEmpty();
  177. testArray.GetParallelRanges();
  178. testArray.GetMetrics();
  179. // Test insert
  180. handles.push_back(testArray.emplace(item));
  181. markFirstPageAsEmpty();
  182. // Test defragment
  183. testArray.DefragmentHandle(handles.back());
  184. markFirstPageAsEmpty();
  185. // Test erase
  186. testArray.erase(handles.back());
  187. handles.clear();
  188. }
  189. TEST_F(StableDynamicArrayTests, DefragmentHandle)
  190. {
  191. using namespace AZ;
  192. AZ::StableDynamicArray<TestItem> testArray;
  193. StableDynamicArrayMetrics metrics;
  194. // fill with items
  195. for (uint32_t i = 0; i < s_testCount; ++i)
  196. {
  197. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  198. handle->index = i;
  199. handles.push_back(AZStd::move(handle));
  200. }
  201. metrics = testArray.GetMetrics();
  202. size_t pageCount1 = metrics.m_elementsPerPage.size();
  203. // remove every other elements
  204. for (uint32_t i = 0; i < s_testCount; i += 2)
  205. {
  206. handles.at(i).Free();
  207. }
  208. // release shouldn't be able to do anything since every other element was removed
  209. testArray.ReleaseEmptyPages();
  210. metrics = testArray.GetMetrics();
  211. size_t pageCount2 = metrics.m_elementsPerPage.size();
  212. EXPECT_EQ(pageCount1, pageCount2);
  213. // compact the elements
  214. for (uint32_t i = 0; i < s_testCount; ++i)
  215. {
  216. testArray.DefragmentHandle(handles.at(i));
  217. }
  218. // now that the elements are compacted we should be able to remove some pages
  219. testArray.ReleaseEmptyPages();
  220. metrics = testArray.GetMetrics();
  221. size_t pageCount3 = metrics.m_elementsPerPage.size();
  222. EXPECT_LT(pageCount3, pageCount2);
  223. // The the defragmented handles should still have valid weak handles
  224. for (StableDynamicArray<TestItem>::Handle& handle : handles)
  225. {
  226. if (handle.IsValid())
  227. {
  228. StableDynamicArrayWeakHandle<TestItem> weakHandle = handle.GetWeakHandle();
  229. // The weak handle should be referring to the same data as the owning handle
  230. EXPECT_EQ(handle->index, weakHandle->index);
  231. }
  232. }
  233. handles.clear(); // cleanup remaining handles.
  234. }
  235. TEST_F(StableDynamicArrayTests, Iterator)
  236. {
  237. using namespace AZ;
  238. AZ::StableDynamicArray<TestItem> testArray;
  239. // fill with items
  240. for (uint32_t i = 0; i < s_testCount; ++i)
  241. {
  242. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  243. handle->index = i;
  244. handles.push_back(AZStd::move(handle));
  245. }
  246. // make sure the iterator hits each item
  247. size_t index = 0;
  248. bool success = true;
  249. for (TestItem& item : testArray)
  250. {
  251. success = success && (item.index == index);
  252. ++index;
  253. }
  254. EXPECT_TRUE(success);
  255. success = true;
  256. // remove every other elements
  257. for (uint32_t i = 0; i < s_testCount; i += 2)
  258. {
  259. handles.at(i).Free();
  260. }
  261. // now the iterator should hit every other item (starting at 1 since 0 was freed).
  262. index = 1;
  263. for (TestItem& item : testArray)
  264. {
  265. success = success && (item.index == index);
  266. index += 2;
  267. }
  268. EXPECT_TRUE(success);
  269. // remove the first half completely so there are a bunch of empty pages to skip
  270. for (uint32_t i = 0; i < s_testCount / 2; ++i)
  271. {
  272. handles.at(i).Free();
  273. }
  274. // now the iterator should hit every other item after s_testCount / 2.
  275. success = true;
  276. index = s_testCount / 2 + 1;
  277. for (TestItem& item : testArray)
  278. {
  279. success = success && (item.index == index);
  280. index += 2;
  281. }
  282. EXPECT_TRUE(success);
  283. handles.clear(); // cleanup remaining handles.
  284. }
  285. TEST_F(StableDynamicArrayTests, ConstIterator)
  286. {
  287. using namespace AZ;
  288. AZ::StableDynamicArray<TestItem> testArray;
  289. // fill with items
  290. for (uint32_t i = 0; i < s_testCount; ++i)
  291. {
  292. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  293. handles.push_back(AZStd::move(handle));
  294. }
  295. // make sure the const iterator hits each item
  296. size_t index = 0;
  297. bool success = true;
  298. for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
  299. {
  300. success = success && (it->index == index);
  301. ++index;
  302. }
  303. EXPECT_TRUE(success);
  304. success = true;
  305. // remove every other elements
  306. for (uint32_t i = 0; i < s_testCount; i += 2)
  307. {
  308. handles.at(i).Free();
  309. }
  310. // now the iterator should hit every other item (starting at 1 since 0 was freed).
  311. index = 1;
  312. for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
  313. {
  314. success = success && (it->index == index);
  315. index += 2;
  316. }
  317. EXPECT_TRUE(success);
  318. // remove the first half completely so there are a bunch of empty pages to skip
  319. for (uint32_t i = 0; i < s_testCount / 2; ++i)
  320. {
  321. handles.at(i).Free();
  322. }
  323. // now the iterator should hit every other item after s_testCount / 2.
  324. success = true;
  325. index = s_testCount / 2 + 1;
  326. for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
  327. {
  328. success = success && (it->index == index);
  329. index += 2;
  330. }
  331. EXPECT_TRUE(success);
  332. handles.clear(); // cleanup remaining handles.
  333. }
  334. TEST_F(StableDynamicArrayTests, PageIterator)
  335. {
  336. using namespace AZ;
  337. AZ::StableDynamicArray<TestItem> testArray;
  338. // Fill with items
  339. for (uint32_t i = 0; i < s_testCount; ++i)
  340. {
  341. StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
  342. handle->index = i;
  343. handles.push_back(AZStd::move(handle));
  344. }
  345. // Make sure the page iterators hits each item
  346. size_t index = 0;
  347. bool success = true;
  348. auto pageIterators = testArray.GetParallelRanges();
  349. for (auto iteratorPair : pageIterators)
  350. {
  351. for (auto iterator = iteratorPair.m_begin; iterator != iteratorPair.m_end; ++iterator)
  352. {
  353. TestItem& item = *iterator;
  354. success = success && (item.index == index);
  355. ++index;
  356. }
  357. }
  358. EXPECT_TRUE(success);
  359. success = true;
  360. // Remove every other elements
  361. for (uint32_t i = 0; i < s_testCount; i += 2)
  362. {
  363. handles.at(i).Free();
  364. }
  365. // Now the page iterators should hit every other item (starting at 1 since 0 was freed).
  366. index = 1;
  367. pageIterators = testArray.GetParallelRanges();
  368. for (auto iteratorPair : pageIterators)
  369. {
  370. for (auto iterator = iteratorPair.m_begin; iterator != iteratorPair.m_end; ++iterator)
  371. {
  372. TestItem& item = *iterator;
  373. success = success && (item.index == index);
  374. index += 2;
  375. }
  376. }
  377. EXPECT_TRUE(success);
  378. // Remove the first half completely so there are a bunch of empty pages to skip
  379. for (uint32_t i = 0; i < s_testCount / 2; ++i)
  380. {
  381. handles.at(i).Free();
  382. }
  383. // Now the page iterators should hit every other item after s_testCount / 2.
  384. // By this passing, it provesthe first few page iterrators begin and end are equal (as they should be for empty pages).
  385. success = true;
  386. index = s_testCount / 2 + 1;
  387. pageIterators = testArray.GetParallelRanges();
  388. for (auto iteratorPair : pageIterators)
  389. {
  390. for (auto iterator = iteratorPair.m_begin; iterator != iteratorPair.m_end; ++iterator)
  391. {
  392. TestItem& item = *iterator;
  393. success = success && (item.index == index);
  394. index += 2;
  395. }
  396. }
  397. EXPECT_TRUE(success);
  398. handles.clear(); // cleanup remaining handles.
  399. }
  400. // Fixture for testing handles and ensuring the correct number of objects are created, modified, and/or destroyed
  401. class StableDynamicArrayHandleTests
  402. : public UnitTest::LeakDetectionFixture
  403. {
  404. friend class StableDynamicArrayOwner;
  405. friend class MoveTest;
  406. public:
  407. void SetUp() override
  408. {
  409. s_testItemsConstructed = 0;
  410. s_testItemsDestructed = 0;
  411. s_testItemsModified = 0;
  412. }
  413. // Used to keep track of the number of times a constructor/destructor/function is called
  414. // to validate that TestItems are being properly created, destroyed, and modified even when accessed via an interface
  415. static int s_testItemsConstructed;
  416. static int s_testItemsDestructed;
  417. static int s_testItemsModified;
  418. };
  419. int StableDynamicArrayHandleTests::s_testItemsConstructed = 0;
  420. int StableDynamicArrayHandleTests::s_testItemsDestructed = 0;
  421. int StableDynamicArrayHandleTests::s_testItemsModified = 0;
  422. // Class used to test that the right number of items are created, modified, and destroyed
  423. // Follows similar pattern to what a FeatureProcessor might do
  424. class StableDynamicArrayOwner
  425. {
  426. public:
  427. class TestItemInterface
  428. {
  429. public:
  430. AZ_RTTI(TestItemInterface, "{96502D93-8FBC-4492-B3F8-9962D9E6A93B}");
  431. virtual void SetValue(int value) = 0;
  432. virtual int GetValue() const = 0;
  433. };
  434. class TestItemImplementation
  435. : public TestItemInterface
  436. {
  437. public:
  438. AZ_RTTI(TestItemImplementation, "{AFE3A7B6-2133-4206-BF91-0E1BB38FC2D1}", TestItemInterface);
  439. TestItemImplementation(int value)
  440. : m_value(value)
  441. {
  442. StableDynamicArrayHandleTests::s_testItemsConstructed++;
  443. }
  444. virtual ~TestItemImplementation()
  445. {
  446. StableDynamicArrayHandleTests::s_testItemsDestructed++;
  447. }
  448. void SetValue(int value) override
  449. {
  450. m_value = value;
  451. StableDynamicArrayHandleTests::s_testItemsModified++;
  452. }
  453. int GetValue() const override
  454. {
  455. return m_value;
  456. }
  457. private:
  458. int m_value = 0;
  459. };
  460. class TestItemImplementation2
  461. : public TestItemInterface
  462. {
  463. public:
  464. AZ_RTTI(TestItemImplementation2, "{F9B94C63-88C2-459C-B752-5963D263C97D}", TestItemInterface);
  465. TestItemImplementation2(int value)
  466. : m_value(value)
  467. {
  468. }
  469. virtual ~TestItemImplementation2()
  470. {
  471. }
  472. void SetValue(int value) override
  473. {
  474. m_value = value;
  475. }
  476. int GetValue() const override
  477. {
  478. return m_value;
  479. }
  480. private:
  481. int m_value = 0;
  482. };
  483. class TestItemImplementationUnrelated
  484. {
  485. public:
  486. AZ_RTTI(TestItemImplementationUnrelated, "{C583B659-E187-4355-82F9-310A97D4E35B}");
  487. TestItemImplementationUnrelated(int value)
  488. : m_value(value)
  489. {
  490. }
  491. virtual ~TestItemImplementationUnrelated()
  492. {
  493. }
  494. void SetValue(int value)
  495. {
  496. m_value = value;
  497. }
  498. int GetValue() const
  499. {
  500. return m_value;
  501. }
  502. private:
  503. int m_value = 0;
  504. };
  505. AZ::StableDynamicArrayHandle<TestItemImplementation> AcquireItem(uint32_t value)
  506. {
  507. return m_testArray.emplace(value);
  508. }
  509. void ReleaseItem(AZ::StableDynamicArrayHandle<TestItemInterface>& interfaceHandle)
  510. {
  511. AZ::StableDynamicArrayHandle<TestItemImplementation> temp(AZStd::move(interfaceHandle));
  512. ReleaseItem(temp);
  513. }
  514. void ReleaseItem(AZ::StableDynamicArrayHandle<TestItemImplementation>& handle)
  515. {
  516. m_testArray.erase(handle);
  517. }
  518. private:
  519. AZ::StableDynamicArray<TestItemImplementation> m_testArray;
  520. };
  521. using TestItemInterfaceHandle = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemInterface>;
  522. using TestItemHandle = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementation>;
  523. using TestItemWeakHandle = AZ::StableDynamicArrayWeakHandle<StableDynamicArrayOwner::TestItemImplementation>;
  524. using TestItemHandleSibling = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementation2>;
  525. using TestItemHandleUnrelated = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementationUnrelated>;
  526. // This class runs several scenarios around transferring ownership from one handle to another
  527. template<typename SourceTestItemType, typename DestinationTestItemType>
  528. class MoveTests
  529. {
  530. using SourceHandle = AZ::StableDynamicArrayHandle<SourceTestItemType>;
  531. using DestinationHandle = AZ::StableDynamicArrayHandle<DestinationTestItemType>;
  532. public:
  533. MoveTests()
  534. {
  535. AZ_Assert(SourceTestItemType::RTTI_IsContainType(DestinationTestItemType::RTTI_Type()) || DestinationTestItemType::RTTI_IsContainType(SourceTestItemType::RTTI_Type()), "These tests expect the transfer of ownership from one handle to the other will succeed, and should only be called with compatible types.");
  536. }
  537. void MoveValidSourceToNullDestination_ExpectMoveToSucceed()
  538. {
  539. {
  540. StableDynamicArrayOwner owner;
  541. SourceHandle source = owner.AcquireItem(123);
  542. DestinationHandle destination = AZStd::move(source);
  543. // Source handle should be invalid after move, destination handle should be valid
  544. EXPECT_EQ(source.IsValid(), false);
  545. EXPECT_EQ(source.IsNull(), true);
  546. EXPECT_EQ(destination.IsValid(), true);
  547. EXPECT_EQ(destination.IsNull(), false);
  548. // The destination handle should have the value that came from the source handle
  549. EXPECT_EQ(destination->GetValue(), 123);
  550. // The destination handle should be pointing to real data that can be modified
  551. destination->SetValue(789);
  552. EXPECT_EQ(destination->GetValue(), 789);
  553. // One item was constructed, none destructed, one modified
  554. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 1);
  555. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 0);
  556. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 1);
  557. }
  558. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  559. }
  560. void MoveValidSourceToValidDestination_ExpectMoveToSucceed()
  561. {
  562. {
  563. StableDynamicArrayOwner owner;
  564. SourceHandle source = owner.AcquireItem(123);
  565. DestinationHandle destination = owner.AcquireItem(456);
  566. destination = AZStd::move(source);
  567. // Source handle should be invalid after move, destination handle should be valid
  568. EXPECT_EQ(source.IsValid(), false);
  569. EXPECT_EQ(source.IsNull(), true);
  570. EXPECT_EQ(destination.IsValid(), true);
  571. EXPECT_EQ(destination.IsNull(), false);
  572. // The destination handle should have the value that came from the source handle
  573. EXPECT_EQ(destination->GetValue(), 123);
  574. // The destination handle should be pointing to real data that can be modified
  575. destination->SetValue(789);
  576. EXPECT_EQ(destination->GetValue(), 789);
  577. // Two items were constructed, one destructed, one modified
  578. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  579. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
  580. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 1);
  581. }
  582. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  583. }
  584. void MoveNullSourceToValidDestination_ExpectMoveToSucceed()
  585. {
  586. {
  587. StableDynamicArrayOwner owner;
  588. SourceHandle source;
  589. DestinationHandle destination = owner.AcquireItem(456);
  590. destination = AZStd::move(source);
  591. // Both handles should be invalid after move
  592. EXPECT_EQ(source.IsValid(), false);
  593. EXPECT_EQ(source.IsNull(), true);
  594. EXPECT_EQ(destination.IsValid(), false);
  595. EXPECT_EQ(destination.IsNull(), true);
  596. // One item was constructed and destructed
  597. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 1);
  598. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
  599. }
  600. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  601. }
  602. void MoveHandleAndReleaseByOwner_ExpectMoveToSucceed()
  603. {
  604. {
  605. StableDynamicArrayOwner owner;
  606. SourceHandle source = owner.AcquireItem(123);
  607. DestinationHandle destination = owner.AcquireItem(456);
  608. destination = AZStd::move(source);
  609. // Attempting to release the invalid source handle should be a no-op
  610. owner.ReleaseItem(source);
  611. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  612. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
  613. // Releasing the valid destination handle should succeed
  614. owner.ReleaseItem(destination);
  615. EXPECT_FALSE(destination.IsValid());
  616. EXPECT_TRUE(destination.IsNull());
  617. // One item was constructed and destructed
  618. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  619. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
  620. }
  621. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  622. }
  623. void MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed()
  624. {
  625. {
  626. StableDynamicArrayOwner owner;
  627. SourceHandle source = owner.AcquireItem(123);
  628. DestinationHandle destination = owner.AcquireItem(456);
  629. destination = AZStd::move(source);
  630. // Attempting to release the invalid source handle should be a no-op
  631. source.Free();
  632. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  633. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
  634. // Releasing the valid destination handle should succeed
  635. destination.Free();
  636. EXPECT_FALSE(destination.IsValid());
  637. EXPECT_TRUE(destination.IsNull());
  638. // One item was constructed and destructed
  639. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  640. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
  641. }
  642. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  643. }
  644. void MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed()
  645. {
  646. {
  647. StableDynamicArrayOwner owner;
  648. {
  649. DestinationHandle destination = owner.AcquireItem(456);
  650. {
  651. SourceHandle source = owner.AcquireItem(123);
  652. destination = AZStd::move(source);
  653. }
  654. // Letting the invalid source item go out of scope should be a no-op
  655. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  656. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
  657. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 0);
  658. }
  659. // Releasing the valid destination handle by letting it go out of scope should succeed
  660. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
  661. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
  662. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 0);
  663. }
  664. EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
  665. }
  666. };
  667. // Move TestItem->TestItem
  668. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToNullTestItemHandle_SourceTestItemMovedToDestination)
  669. {
  670. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  671. moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
  672. }
  673. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
  674. {
  675. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  676. moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
  677. }
  678. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
  679. {
  680. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  681. moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
  682. }
  683. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
  684. {
  685. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  686. moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
  687. }
  688. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
  689. {
  690. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  691. moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
  692. }
  693. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
  694. {
  695. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  696. moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
  697. }
  698. // Move TestItem->Interface
  699. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToNullInterfaceHandle_SourceTestItemMovedToDestination)
  700. {
  701. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  702. moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
  703. }
  704. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
  705. {
  706. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  707. moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
  708. }
  709. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
  710. {
  711. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  712. moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
  713. }
  714. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
  715. {
  716. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  717. moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
  718. }
  719. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
  720. {
  721. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  722. moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
  723. }
  724. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
  725. {
  726. MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
  727. moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
  728. }
  729. // Move Interface->TestItem
  730. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToNullTestItemHandle_SourceTestItemMovedToDestination)
  731. {
  732. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  733. moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
  734. }
  735. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
  736. {
  737. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  738. moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
  739. }
  740. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
  741. {
  742. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  743. moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
  744. }
  745. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
  746. {
  747. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  748. moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
  749. }
  750. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
  751. {
  752. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  753. moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
  754. }
  755. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
  756. {
  757. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
  758. moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
  759. }
  760. // Move Interface->Interface
  761. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToNullInterfaceHandle_SourceTestItemMovedToDestination)
  762. {
  763. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  764. moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
  765. }
  766. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
  767. {
  768. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  769. moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
  770. }
  771. TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
  772. {
  773. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  774. moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
  775. }
  776. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
  777. {
  778. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  779. moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
  780. }
  781. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
  782. {
  783. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  784. moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
  785. }
  786. TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
  787. {
  788. MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
  789. moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
  790. }
  791. TEST_F(StableDynamicArrayHandleTests, MoveHandle_SelfAssignment_DoesNotModifyHandle)
  792. {
  793. StableDynamicArrayOwner owner;
  794. TestItemHandle handle = owner.AcquireItem(1);
  795. int testValue = 12;
  796. handle->SetValue(testValue);
  797. // Self assignment should not invalidate the handle
  798. handle = AZStd::move(handle);
  799. EXPECT_TRUE(handle.IsValid());
  800. EXPECT_FALSE(handle.IsNull());
  801. EXPECT_EQ(handle->GetValue(), testValue);
  802. }
  803. TEST_F(StableDynamicArrayHandleTests, WeakHandle_GetDataFromOwner_CanAccessData)
  804. {
  805. StableDynamicArrayOwner owner;
  806. TestItemHandle handle = owner.AcquireItem(1);
  807. TestItemWeakHandle weakHandle = handle.GetWeakHandle();
  808. int testValue = 12;
  809. weakHandle->SetValue(testValue);
  810. EXPECT_EQ(handle->GetValue(), testValue);
  811. EXPECT_EQ(weakHandle->GetValue(), testValue);
  812. EXPECT_EQ((*weakHandle).GetValue(), testValue);
  813. }
  814. //
  815. // Invalid cases
  816. //
  817. TEST_F(StableDynamicArrayHandleTests, MoveHandleBetweenDifferentTypes_FromInterfaceToASiblingHandle_AssertsAndLeavesBothHandlesInvalid)
  818. {
  819. {
  820. StableDynamicArrayOwner owner;
  821. // The the underlying type that the interface handle refers to is a TestItemImplementation
  822. TestItemInterfaceHandle interfaceHandle = owner.AcquireItem(1);
  823. AZ_TEST_START_ASSERTTEST;
  824. // The interface handle is referring to a TestItemImplementation, so you should not be able to move it to a handle to a TestItemImplementation2
  825. TestItemHandleSibling testItemHandle2FromInterface = AZStd::move(interfaceHandle);
  826. AZ_TEST_STOP_ASSERTTEST(1);
  827. EXPECT_FALSE(interfaceHandle.IsValid());
  828. EXPECT_TRUE(interfaceHandle.IsNull());
  829. EXPECT_FALSE(testItemHandle2FromInterface.IsValid());
  830. EXPECT_TRUE(testItemHandle2FromInterface.IsNull());
  831. }
  832. EXPECT_EQ(s_testItemsConstructed, s_testItemsDestructed);
  833. }
  834. }
  835. AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);