You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/Atom/Utils/Code/Tests/StableDynamicArrayTests.cpp

943 lines
36 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Memory/PoolAllocator.h>
#include <AzCore/Component/ComponentApplication.h>
#include <Atom/Utils/StableDynamicArray.h>
namespace UnitTest
{
// Fixture that creates a bare-bones app
class StableDynamicArrayTests
: public AllocatorsFixture
{
public:
void SetUp() override
{
AllocatorsFixture::SetUp();
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
handles.reserve(s_testCount);
}
void TearDown() override
{
handles = AZStd::vector<AZ::StableDynamicArray<TestItem>::Handle>(); // force memory deallocation.
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
AllocatorsFixture::TearDown();
}
struct TestItem
{
TestItem() = default;
TestItem(uint32_t value) : index(value) {}
uint32_t index = 0;
};
static constexpr uint32_t s_testCount = 1000000;
AZStd::vector<AZ::StableDynamicArray<TestItem>::Handle> handles;
};
TEST_F(StableDynamicArrayTests, insert_erase)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.insert({});
handle->index = i;
handles.push_back(AZStd::move(handle));
}
EXPECT_EQ(testArray.size(), s_testCount);
StableDynamicArrayMetrics metrics = testArray.GetMetrics();
EXPECT_EQ(metrics.m_totalElements, s_testCount);
// remove half of the elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
testArray.erase(handles.at(i));
}
EXPECT_EQ(testArray.size(), s_testCount / 2);
metrics = testArray.GetMetrics();
EXPECT_EQ(metrics.m_totalElements, s_testCount / 2);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, emplace_Free)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handles.push_back(AZStd::move(handle));
}
StableDynamicArrayMetrics metrics = testArray.GetMetrics();
EXPECT_EQ(metrics.m_totalElements, s_testCount);
// remove half of the elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
handles.at(i).Free();
}
metrics = testArray.GetMetrics();
EXPECT_EQ(metrics.m_totalElements, s_testCount / 2);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, ReleaseEmptyPages)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// Test removing items at the end
// fill with items
TestItem item; // test lvalue insert
for (uint32_t i = 0; i < s_testCount; ++i)
{
item.index = i;
StableDynamicArray<TestItem>::Handle handle = testArray.insert(item);
handles.push_back(AZStd::move(handle));
}
StableDynamicArrayMetrics metrics1 = testArray.GetMetrics();
size_t fullPageCount = metrics1.m_elementsPerPage.size();
// remove the last half of the elements
for (uint32_t i = 0; i < s_testCount / 2; ++i)
{
handles.pop_back();
}
// release the pages at the end that are now empty
testArray.ReleaseEmptyPages();
StableDynamicArrayMetrics metrics2 = testArray.GetMetrics();
size_t endReducedPageCount = metrics2.m_elementsPerPage.size();
// There should be fewer pages now than before
EXPECT_LT(endReducedPageCount, fullPageCount);
// Test removing All the items
handles.clear(); // cleanup remaining handles.
// release all the pages.
testArray.ReleaseEmptyPages();
StableDynamicArrayMetrics metrics3 = testArray.GetMetrics();
size_t emptyPageCount = metrics3.m_elementsPerPage.size();
// there should be 0 pages now.
EXPECT_EQ(emptyPageCount, 0);
// Test removing items from the beginning
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handles.push_back(AZStd::move(handle));
}
// remove the first half of the elements
for (uint32_t i = 0; i < s_testCount / 2; ++i)
{
handles.at(i).Free();
}
// release the pages at the beginning that are now empty
testArray.ReleaseEmptyPages();
StableDynamicArrayMetrics metrics4 = testArray.GetMetrics();
size_t beginReducedPageCount = metrics4.m_elementsPerPage.size();
// There should be fewer pages now than before
EXPECT_LT(beginReducedPageCount, fullPageCount);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, DefragmentHandle)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
StableDynamicArrayMetrics metrics;
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handle->index = i;
handles.push_back(AZStd::move(handle));
}
metrics = testArray.GetMetrics();
size_t pageCount1 = metrics.m_elementsPerPage.size();
// remove every other elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
handles.at(i).Free();
}
// release shouldn't be able to do anything since ever other element was removed
testArray.ReleaseEmptyPages();
metrics = testArray.GetMetrics();
size_t pageCount2 = metrics.m_elementsPerPage.size();
EXPECT_EQ(pageCount1, pageCount2);
// compact the elements
for (uint32_t i = 0; i < s_testCount; ++i)
{
testArray.DefragmentHandle(handles.at(i));
}
// now that the elements are compacted we should be able to remove some pages
testArray.ReleaseEmptyPages();
metrics = testArray.GetMetrics();
size_t pageCount3 = metrics.m_elementsPerPage.size();
EXPECT_LT(pageCount3, pageCount2);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, Iterator)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handle->index = i;
handles.push_back(AZStd::move(handle));
}
// make sure the iterator hits each item
size_t index = 0;
bool success = true;
for (TestItem& item : testArray)
{
success = success && (item.index == index);
++index;
}
EXPECT_TRUE(success);
success = true;
// remove every other elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
handles.at(i).Free();
}
// now the iterator should hit every other item (starting at 1 since 0 was freed).
index = 1;
for (TestItem& item : testArray)
{
success = success && (item.index == index);
index += 2;
}
EXPECT_TRUE(success);
// remove the first half completely so there are a bunch of empty pages to skip
for (uint32_t i = 0; i < s_testCount / 2; ++i)
{
handles.at(i).Free();
}
// now the iterator should hit every other item after s_testCount / 2.
success = true;
index = s_testCount / 2 + 1;
for (TestItem& item : testArray)
{
success = success && (item.index == index);
index += 2;
}
EXPECT_TRUE(success);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, ConstIterator)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handles.push_back(AZStd::move(handle));
}
// make sure the const iterator hits each item
size_t index = 0;
bool success = true;
for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
{
success = success && (it->index == index);
++index;
}
EXPECT_TRUE(success);
success = true;
// remove every other elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
handles.at(i).Free();
}
// now the iterator should hit every other item (starting at 1 since 0 was freed).
index = 1;
for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
{
success = success && (it->index == index);
index += 2;
}
EXPECT_TRUE(success);
// remove the first half completely so there are a bunch of empty pages to skip
for (uint32_t i = 0; i < s_testCount / 2; ++i)
{
handles.at(i).Free();
}
// now the iterator should hit every other item after s_testCount / 2.
success = true;
index = s_testCount / 2 + 1;
for (auto it = testArray.cbegin(); it != testArray.cend(); ++it)
{
success = success && (it->index == index);
index += 2;
}
EXPECT_TRUE(success);
handles.clear(); // cleanup remaining handles.
}
TEST_F(StableDynamicArrayTests, PageIterator)
{
using namespace AZ;
AZ::StableDynamicArray<TestItem> testArray;
// Fill with items
for (uint32_t i = 0; i < s_testCount; ++i)
{
StableDynamicArray<TestItem>::Handle handle = testArray.emplace(i);
handle->index = i;
handles.push_back(AZStd::move(handle));
}
// Make sure the page iterators hits each item
size_t index = 0;
bool success = true;
auto pageIterators = testArray.GetParallelRanges();
for (auto iteratorPair : pageIterators)
{
for (auto iterator = iteratorPair.first; iterator != iteratorPair.second; ++iterator)
{
TestItem& item = *iterator;
success = success && (item.index == index);
++index;
}
}
EXPECT_TRUE(success);
success = true;
// Remove every other elements
for (uint32_t i = 0; i < s_testCount; i += 2)
{
handles.at(i).Free();
}
// Now the page iterators should hit every other item (starting at 1 since 0 was freed).
index = 1;
pageIterators = testArray.GetParallelRanges();
for (auto iteratorPair : pageIterators)
{
for (auto iterator = iteratorPair.first; iterator != iteratorPair.second; ++iterator)
{
TestItem& item = *iterator;
success = success && (item.index == index);
index += 2;
}
}
EXPECT_TRUE(success);
// Remove the first half completely so there are a bunch of empty pages to skip
for (uint32_t i = 0; i < s_testCount / 2; ++i)
{
handles.at(i).Free();
}
// Now the page iterators should hit every other item after s_testCount / 2.
// By this passing, it provesthe first few page iterrators begin and end are equal (as they should be for empty pages).
success = true;
index = s_testCount / 2 + 1;
pageIterators = testArray.GetParallelRanges();
for (auto iteratorPair : pageIterators)
{
for (auto iterator = iteratorPair.first; iterator != iteratorPair.second; ++iterator)
{
TestItem& item = *iterator;
success = success && (item.index == index);
index += 2;
}
}
EXPECT_TRUE(success);
handles.clear(); // cleanup remaining handles.
}
// Fixture for testing handles and ensuring the correct number of objects are created, modified, and/or destroyed
class StableDynamicArrayHandleTests
: public UnitTest::ScopedAllocatorSetupFixture
{
friend class StableDynamicArrayOwner;
friend class MoveTest;
public:
void SetUp() override
{
AZ::AllocatorInstance<AZ::PoolAllocator>::Create();
s_testItemsConstructed = 0;
s_testItemsDestructed = 0;
s_testItemsModified = 0;
}
void TearDown() override
{
AZ::AllocatorInstance<AZ::PoolAllocator>::Destroy();
}
// Used to keep track of the number of times a constructor/destructor/function is called
// to validate that TestItems are being properly created, destroyed, and modified even when accessed via an interface
static int s_testItemsConstructed;
static int s_testItemsDestructed;
static int s_testItemsModified;
};
int StableDynamicArrayHandleTests::s_testItemsConstructed = 0;
int StableDynamicArrayHandleTests::s_testItemsDestructed = 0;
int StableDynamicArrayHandleTests::s_testItemsModified = 0;
// Class used to test that the right number of items are created, modified, and destroyed
// Follows similar pattern to what a FeatureProcessor might do
class StableDynamicArrayOwner
{
public:
class TestItemInterface
{
public:
AZ_RTTI(TestItemInterface, "{96502D93-8FBC-4492-B3F8-9962D9E6A93B}");
virtual void SetValue(int value) = 0;
virtual int GetValue() const = 0;
};
class TestItemImplementation
: public TestItemInterface
{
public:
AZ_RTTI(TestItemImplementation, "{AFE3A7B6-2133-4206-BF91-0E1BB38FC2D1}", TestItemInterface);
TestItemImplementation(int value)
: m_value(value)
{
StableDynamicArrayHandleTests::s_testItemsConstructed++;
}
virtual ~TestItemImplementation()
{
StableDynamicArrayHandleTests::s_testItemsDestructed++;
}
void SetValue(int value) override
{
m_value = value;
StableDynamicArrayHandleTests::s_testItemsModified++;
}
int GetValue() const override
{
return m_value;
}
private:
int m_value = 0;
};
class TestItemImplementation2
: public TestItemInterface
{
public:
AZ_RTTI(TestItemImplementation2, "{F9B94C63-88C2-459C-B752-5963D263C97D}", TestItemInterface);
TestItemImplementation2(int value)
: m_value(value)
{
}
virtual ~TestItemImplementation2()
{
}
void SetValue(int value) override
{
m_value = value;
}
int GetValue() const override
{
return m_value;
}
private:
int m_value = 0;
};
class TestItemImplementationUnrelated
{
public:
AZ_RTTI(TestItemImplementationUnrelated, "{C583B659-E187-4355-82F9-310A97D4E35B}");
TestItemImplementationUnrelated(int value)
: m_value(value)
{
}
virtual ~TestItemImplementationUnrelated()
{
}
void SetValue(int value)
{
m_value = value;
}
int GetValue() const
{
return m_value;
}
private:
int m_value = 0;
};
AZ::StableDynamicArrayHandle<TestItemImplementation> AcquireItem(uint32_t value)
{
return m_testArray.emplace(value);
}
void ReleaseItem(AZ::StableDynamicArrayHandle<TestItemInterface>& interfaceHandle)
{
AZ::StableDynamicArrayHandle<TestItemImplementation> temp(AZStd::move(interfaceHandle));
ReleaseItem(temp);
}
void ReleaseItem(AZ::StableDynamicArrayHandle<TestItemImplementation>& handle)
{
m_testArray.erase(handle);
}
private:
AZ::StableDynamicArray<TestItemImplementation> m_testArray;
};
using TestItemInterfaceHandle = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemInterface>;
using TestItemHandle = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementation>;
using TestItemHandleSibling = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementation2>;
using TestItemHandleUnrelated = AZ::StableDynamicArrayHandle<StableDynamicArrayOwner::TestItemImplementationUnrelated>;
// This class runs several scenarios around transferring ownership from one handle to another
template<typename SourceTestItemType, typename DestinationTestItemType>
class MoveTests
{
using SourceHandle = AZ::StableDynamicArrayHandle<SourceTestItemType>;
using DestinationHandle = AZ::StableDynamicArrayHandle<DestinationTestItemType>;
public:
MoveTests()
{
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.");
}
void MoveValidSourceToNullDestination_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
SourceHandle source = owner.AcquireItem(123);
DestinationHandle destination = AZStd::move(source);
// Source handle should be invalid after move, destination handle should be valid
EXPECT_EQ(source.IsValid(), false);
EXPECT_EQ(source.IsNull(), true);
EXPECT_EQ(destination.IsValid(), true);
EXPECT_EQ(destination.IsNull(), false);
// The destination handle should have the value that came from the source handle
EXPECT_EQ(destination->GetValue(), 123);
// The destination handle should be pointing to real data that can be modified
destination->SetValue(789);
EXPECT_EQ(destination->GetValue(), 789);
// One item was constructed, none destructed, one modified
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 1);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 0);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 1);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
void MoveValidSourceToValidDestination_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
SourceHandle source = owner.AcquireItem(123);
DestinationHandle destination = owner.AcquireItem(456);
destination = AZStd::move(source);
// Source handle should be invalid after move, destination handle should be valid
EXPECT_EQ(source.IsValid(), false);
EXPECT_EQ(source.IsNull(), true);
EXPECT_EQ(destination.IsValid(), true);
EXPECT_EQ(destination.IsNull(), false);
// The destination handle should have the value that came from the source handle
EXPECT_EQ(destination->GetValue(), 123);
// The destination handle should be pointing to real data that can be modified
destination->SetValue(789);
EXPECT_EQ(destination->GetValue(), 789);
// Two items were constructed, one destructed, one modified
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 1);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
void MoveNullSourceToValidDestination_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
SourceHandle source;
DestinationHandle destination = owner.AcquireItem(456);
destination = AZStd::move(source);
// Both handles should be invalid after move
EXPECT_EQ(source.IsValid(), false);
EXPECT_EQ(source.IsNull(), true);
EXPECT_EQ(destination.IsValid(), false);
EXPECT_EQ(destination.IsNull(), true);
// One item was constructed and destructed
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 1);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
void MoveHandleAndReleaseByOwner_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
SourceHandle source = owner.AcquireItem(123);
DestinationHandle destination = owner.AcquireItem(456);
destination = AZStd::move(source);
// Attempting to release the invalid source handle should be a no-op
owner.ReleaseItem(source);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
// Releasing the valid destination handle should succeed
owner.ReleaseItem(destination);
EXPECT_FALSE(destination.IsValid());
EXPECT_TRUE(destination.IsNull());
// One item was constructed and destructed
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
void MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
SourceHandle source = owner.AcquireItem(123);
DestinationHandle destination = owner.AcquireItem(456);
destination = AZStd::move(source);
// Attempting to release the invalid source handle should be a no-op
source.Free();
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
// Releasing the valid destination handle should succeed
destination.Free();
EXPECT_FALSE(destination.IsValid());
EXPECT_TRUE(destination.IsNull());
// One item was constructed and destructed
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
void MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed()
{
{
StableDynamicArrayOwner owner;
{
DestinationHandle destination = owner.AcquireItem(456);
{
SourceHandle source = owner.AcquireItem(123);
destination = AZStd::move(source);
}
// Letting the invalid source item go out of scope should be a no-op
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 1);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 0);
}
// Releasing the valid destination handle by letting it go out of scope should succeed
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsDestructed, 2);
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsModified, 0);
}
EXPECT_EQ(StableDynamicArrayHandleTests::s_testItemsConstructed, StableDynamicArrayHandleTests::s_testItemsDestructed);
}
};
// Move TestItem->TestItem
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToNullTestItemHandle_SourceTestItemMovedToDestination)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidTestItemHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
}
// Move TestItem->Interface
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToNullInterfaceHandle_SourceTestItemMovedToDestination)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidTestItemHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemImplementation, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
}
// Move Interface->TestItem
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToNullTestItemHandle_SourceTestItemMovedToDestination)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidInterfaceHandleToValidTestItemHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemImplementation> moveTest;
moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
}
// Move Interface->Interface
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToNullInterfaceHandle_SourceTestItemMovedToDestination)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveValidSourceToNullDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleasedThenSourceTestItemMoved)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveValidSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_FromNullInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveNullSourceToValidDestination_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByOwner_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByOwner_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByCallingFreeDirectlyOnHandle_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandleAndReleaseByLettingHandleGoOutOfScope_FromValidInterfaceHandleToValidInterfaceHandle_DestinationTestItemReleased)
{
MoveTests<StableDynamicArrayOwner::TestItemInterface, StableDynamicArrayOwner::TestItemInterface> moveTest;
moveTest.MoveHandleAndReleaseByLettingHandleGoOutOfScope_ExpectMoveToSucceed();
}
TEST_F(StableDynamicArrayHandleTests, MoveHandle_SelfAssignment_DoesNotModifyHandle)
{
StableDynamicArrayOwner owner;
TestItemHandle handle = owner.AcquireItem(1);
int testValue = 12;
handle->SetValue(testValue);
// Self assignment should not invalidate the handle
handle = AZStd::move(handle);
EXPECT_TRUE(handle.IsValid());
EXPECT_FALSE(handle.IsNull());
EXPECT_EQ(handle->GetValue(), testValue);
}
//
// Invalid cases
//
TEST_F(StableDynamicArrayHandleTests, MoveHandleBetweenDifferentTypes_FromInterfaceToASiblingHandle_AssertsAndLeavesBothHandlesInvalid)
{
{
StableDynamicArrayOwner owner;
// The the underlying type that the interface handle refers to is a TestItemImplementation
TestItemInterfaceHandle interfaceHandle = owner.AcquireItem(1);
AZ_TEST_START_ASSERTTEST;
// The interface handle is referring to a TestItemImplementation, so you should not be able to move it to a handle to a TestItemImplementation2
TestItemHandleSibling testItemHandle2FromInterface = AZStd::move(interfaceHandle);
AZ_TEST_STOP_ASSERTTEST(1);
EXPECT_FALSE(interfaceHandle.IsValid());
EXPECT_TRUE(interfaceHandle.IsNull());
EXPECT_FALSE(testItemHandle2FromInterface.IsValid());
EXPECT_TRUE(testItemHandle2FromInterface.IsNull());
}
EXPECT_EQ(s_testItemsConstructed, s_testItemsDestructed);
}
}
AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV);