/* * 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 #include #include #include #include namespace UnitTest { using namespace AZ; using namespace AZ::Render; class IndexedDataVectorTests : public UnitTest::AllocatorsTestFixture { public: void SetUp() override { UnitTest::AllocatorsTestFixture::SetUp(); } void TearDown() override { UnitTest::AllocatorsTestFixture::TearDown(); } template IndexedDataVector SetupIndexedDataVector(size_t size, T initialValue = T(0), T incrementAmount = T(1), AZStd::vector* indices = nullptr) { IndexedDataVector data; T value = initialValue; for (size_t i = 0; i < size; ++i) { uint16_t index = data.GetFreeSlotIndex(); EXPECT_NE(index, IndexedDataVector::NoFreeSlot); if (indices) { indices->push_back(index); } if (index != IndexedDataVector::NoFreeSlot) { data.GetData(index) = value; value += incrementAmount; } } return data; } template void ShuffleIndexedDataVector(IndexedDataVector& dataVector, AZStd::vector& indices) { AZStd::vector values; // remove every other element and store it for (size_t i = 0; i < indices.size(); ++i) { values.push_back(dataVector.GetData(indices.at(i))); dataVector.RemoveIndex(indices.at(i)); indices.erase(&indices.at(i)); } for (T value : values) { uint16_t index = dataVector.GetFreeSlotIndex(); indices.push_back(index); dataVector.GetData(index) = value; } } }; TEST_F(IndexedDataVectorTests, Construction) { IndexedDataVector testVector; uint16_t index = testVector.GetFreeSlotIndex(); EXPECT_NE(index, IndexedDataVector::NoFreeSlot); } TEST_F(IndexedDataVectorTests, TestInsertGetBasic) { constexpr size_t count = 16; constexpr int initialValue = 0; constexpr int increment = 1; AZStd::vector indices; IndexedDataVector testVector = SetupIndexedDataVector(count, initialValue, increment, &indices); int value = initialValue; for (size_t i = 0; i < count; ++i) { EXPECT_EQ(testVector.GetData(indices.at(i)), value); value += increment; } } TEST_F(IndexedDataVectorTests, TestInsertGetComplex) { constexpr size_t count = 16; constexpr int initialValue = 0; constexpr int increment = 1; AZStd::vector indices; IndexedDataVector testVector = SetupIndexedDataVector(count, initialValue, increment, &indices); // Create a set of the data that should be in the IndexedDataVector AZStd::set values; for (int i = 0; i < count; ++i) { values.emplace(initialValue + i * increment); } // Add and remove items to shuffle the underlying data ShuffleIndexedDataVector(testVector, indices); // Check to make sure all the data is still there AZStd::vector& underlyingVector = testVector.GetDataVector(); for (size_t i = 0; i < underlyingVector.size(); ++i) { EXPECT_TRUE(values.contains(underlyingVector.at(i))); } } TEST_F(IndexedDataVectorTests, TestSize) { constexpr size_t count = 32; IndexedDataVector testVector = SetupIndexedDataVector(count); EXPECT_EQ(testVector.GetDataCount(), count); } TEST_F(IndexedDataVectorTests, TestClear) { constexpr size_t count = 32; IndexedDataVector testVector = SetupIndexedDataVector(count); testVector.Clear(); EXPECT_EQ(testVector.GetDataCount(), 0); } TEST_F(IndexedDataVectorTests, TestRemove) { constexpr size_t count = 8; constexpr int initialValue = 0; constexpr int increment = 8; AZStd::vector indices; IndexedDataVector testVector = SetupIndexedDataVector(count, initialValue, increment, &indices); // Remove every other element by index for (uint16_t i = 0; i < count; i += 2) { testVector.RemoveIndex(i); } EXPECT_EQ(testVector.GetDataCount(), count / 2); // Make sure the rest of the data is still there AZStd::vector remainingIndices; for (size_t i = 1; i < count; i += 2) { int value = testVector.GetData(indices.at(i)); EXPECT_EQ(value, initialValue + increment * i); remainingIndices.push_back(indices.at(i)); } // remove the rest of the valus by value for (uint16_t index : remainingIndices) { int* valuePtr = &testVector.GetData(index); testVector.RemoveData(valuePtr); } EXPECT_EQ(testVector.GetDataCount(), 0); } TEST_F(IndexedDataVectorTests, TestIndexForData) { constexpr size_t count = 8; constexpr int initialValue = 0; constexpr int increment = 8; AZStd::vector indices; IndexedDataVector testVector = SetupIndexedDataVector(count, initialValue, increment, &indices); // Add and remove items to shuffle the underlying data ShuffleIndexedDataVector(testVector, indices); AZStd::vector& underlyingVector = testVector.GetDataVector(); for (size_t i = 0; i < underlyingVector.size(); ++i) { int value = underlyingVector.at(i); uint16_t index = testVector.GetIndexForData(&underlyingVector.at(i)); // The data from GetData(index) should match for the index retrieved using GetIndexForData() for the same data. EXPECT_EQ(testVector.GetData(index), value); } } TEST_F(IndexedDataVectorTests, TestRawIndex) { constexpr size_t count = 8; constexpr int initialValue = 0; constexpr int increment = 8; AZStd::vector indices; IndexedDataVector testVector = SetupIndexedDataVector(count, initialValue, increment, &indices); // Add and remove items to shuffle the underlying data ShuffleIndexedDataVector(testVector, indices); AZStd::vector& underlyingVector = testVector.GetDataVector(); for (size_t i = 0; i < indices.size(); ++i) { // Check that the data retrieved from GetData for a given index matches the data in the underlying vector for the raw index. EXPECT_EQ(testVector.GetData(indices.at(i)), underlyingVector.at(testVector.GetRawIndex(indices.at(i)))); } } }