123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- /*
- * 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/Component/Entity.h>
- #include <AzCore/Serialization/IdUtils.h>
- namespace UnitTest
- {
- using namespace AZ;
- //! Unit Test for IdUtils GenerateNewIdsAndFixRefs functions for generating new unique ids
- class RemappableIdTest
- : public LeakDetectionFixture
- {
- public:
- RemappableIdTest()
- : LeakDetectionFixture()
- {
- }
- ~RemappableIdTest() override
- {
- }
- void SetUp() override
- {
- LeakDetectionFixture::SetUp();
- m_serializeContext.reset(aznew AZ::SerializeContext());
- Entity::Reflect(m_serializeContext.get());
- RemapIdData::Reflect(*m_serializeContext);
- RemapUuidAndEntityIdData::Reflect(*m_serializeContext);
- ParentEntityContainer::Reflect(*m_serializeContext);
- RootParentElementWrapper::Reflect(*m_serializeContext);
- }
- void TearDown() override
- {
- m_serializeContext.reset();
- LeakDetectionFixture::TearDown();
- }
- AZStd::unique_ptr<SerializeContext> m_serializeContext;
- struct RemapIdData
- {
- AZ_TYPE_INFO(RemapIdData, "{0431A85A-C2CC-453A-92A7-807BCEAB46D7}");
- AZ_CLASS_ALLOCATOR(RemapIdData, AZ::SystemAllocator);
- static void Reflect(AZ::SerializeContext& serializeContext)
- {
- serializeContext.Class<RemapIdData>()
- ->Field("m_remappableUuid", &RemapIdData::m_remappableUuid)
- ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Uuid::CreateRandom)
- ->Field("m_uuid1", &RemapIdData::m_uuid1)
- ->Field("m_uuid2", &RemapIdData::m_uuid2)
- ;
- }
- Uuid m_remappableUuid;
- Uuid m_uuid1;
- Uuid m_uuid2;
- };
- struct RemapUuidAndEntityIdData
- {
- AZ_TYPE_INFO(RemapUuidAndEntityIdData, "{6951A116-505C-47E0-9310-2E60D040F390}");
- AZ_CLASS_ALLOCATOR(RemapUuidAndEntityIdData, AZ::SystemAllocator);
- // copying entities is forbidden.
- RemapUuidAndEntityIdData()
- {
- }
- RemapUuidAndEntityIdData(const RemapUuidAndEntityIdData& other)
- {
- m_remappableUuid = other.m_remappableUuid;
- m_uuid1 = other.m_uuid1;
- m_uuid2 = other.m_uuid2;
- m_remappableEntityId = other.m_remappableEntityId;
- m_entityId1 = other.m_entityId1;
- m_entityId2 = other.m_entityId2;
- m_entity.SetId(other.m_entity.GetId()); // for this test, we only copy the ID.
- m_entityRef = other.m_entityRef;
- }
- static void Reflect(AZ::SerializeContext& serializeContext)
- {
- serializeContext.Class<RemapUuidAndEntityIdData>()
- ->Field("m_remappableUuid", &RemapUuidAndEntityIdData::m_remappableUuid)
- ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Uuid::CreateRandom)
- ->Field("m_uuid1", &RemapUuidAndEntityIdData::m_uuid1)
- ->Field("m_uuid2", &RemapUuidAndEntityIdData::m_uuid2)
- ->Field("m_remappbleEntityId", &RemapUuidAndEntityIdData::m_remappableEntityId)
- ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
- ->Field("m_entityId1", &RemapUuidAndEntityIdData::m_entityId1)
- ->Field("m_entityId2", &RemapUuidAndEntityIdData::m_entityId2)
- ->Field("m_entity", &RemapUuidAndEntityIdData::m_entity)
- ->Field("m_entityRef", &RemapUuidAndEntityIdData::m_entityRef)
- ;
- }
- Uuid m_remappableUuid;
- Uuid m_uuid1;
- Uuid m_uuid2;
- AZ::EntityId m_remappableEntityId;
- AZ::EntityId m_entityId1;
- AZ::EntityId m_entityId2;
- AZ::Entity m_entity;
- AZ::EntityId m_entityRef;
- };
- struct ParentEntityContainer
- {
- AZ_TYPE_INFO(ParentEntityContainer, "{60EFD610-4B9A-444C-9004-A651B772927F}");
- AZ_CLASS_ALLOCATOR(ParentEntityContainer, AZ::SystemAllocator);
- static void Reflect(AZ::SerializeContext& serializeContext);
- AZ::EntityId m_remappableEntityId1;
- AZ::EntityId m_remappableEntityId2;
- int m_beginWriteCount = 0;
- int m_endWriteCount = 0;
- };
- struct RootParentElementWrapper
- {
- AZ_TYPE_INFO(RootParentElementWrapper, "{BFC46F3C-D696-4A34-B824-F18428CAA910}");
- AZ_CLASS_ALLOCATOR(RootParentElementWrapper, AZ::SystemAllocator);
- static void Reflect(AZ::SerializeContext& serializeContext)
- {
- serializeContext.Class<RootParentElementWrapper>()
- ->Field("m_parentEntityContainer", &RootParentElementWrapper::m_parentEntityContainer)
- ;
- }
- ~RootParentElementWrapper()
- {
- delete m_parentEntityContainer;
- }
- ParentEntityContainer* m_parentEntityContainer = nullptr;
- };
- struct ParentEntityContainerEventHandler
- : public AZ::SerializeContext::IEventHandler
- {
- static bool s_isCheckingCallbackOrder;
- void OnWriteBegin(void* classPtr) override
- {
- auto parentEntityContainer = reinterpret_cast<ParentEntityContainer*>(classPtr);
- if (s_isCheckingCallbackOrder)
- {
- EXPECT_EQ(parentEntityContainer->m_beginWriteCount, parentEntityContainer->m_endWriteCount);
- }
- parentEntityContainer->m_beginWriteCount++;
- }
- void OnWriteEnd(void* classPtr) override
- {
- auto parentEntityContainer = reinterpret_cast<ParentEntityContainer*>(classPtr);
- if (s_isCheckingCallbackOrder)
- {
- EXPECT_EQ(parentEntityContainer->m_beginWriteCount, parentEntityContainer->m_endWriteCount + 1);
- }
- parentEntityContainer->m_endWriteCount++;
- }
- };
- };
- bool RemappableIdTest::ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = false;
- void RemappableIdTest::ParentEntityContainer::Reflect(AZ::SerializeContext& serializeContext)
- {
- serializeContext.Class<ParentEntityContainer>()
- ->EventHandler<ParentEntityContainerEventHandler>()
- ->Field("m_remappableEntityId1", &ParentEntityContainer::m_remappableEntityId1)
- ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
- ->Field("m_remappableEntityId2", &ParentEntityContainer::m_remappableEntityId2)
- ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
- ;
- }
- TEST_F(RemappableIdTest, UuidRemapTest)
- {
- RemapIdData testData;
- testData.m_remappableUuid = AZ::Uuid::CreateRandom();
- testData.m_uuid1 = testData.m_remappableUuid;
- testData.m_uuid2 = AZ::Uuid::CreateRandom();
- EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
- EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
- RemapIdData originalData = testData;
- EXPECT_EQ(originalData.m_remappableUuid, testData.m_remappableUuid);
- EXPECT_EQ(originalData.m_uuid1, testData.m_uuid1);
- EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
- AZStd::unordered_map<Uuid, Uuid> remapIds;
- IdUtils::Remapper<Uuid>::GenerateNewIdsAndFixRefs(&testData, remapIds, m_serializeContext.get());
- EXPECT_NE(originalData.m_remappableUuid, testData.m_remappableUuid);
- EXPECT_NE(originalData.m_uuid1, testData.m_uuid1);
- // Uuid2 never gets remapped so it should remain the same throughout the test
- EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
- // Uuid1 has the same value as the RemappableUuid so it gets updated as well
- EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
- auto remapIdsIt = remapIds.find(originalData.m_remappableUuid);
- EXPECT_NE(remapIds.end(), remapIdsIt);
- EXPECT_EQ(testData.m_remappableUuid, remapIdsIt->second);
- }
- TEST_F(RemappableIdTest, UuidAndEntityIdRemapTest)
- {
- RemapUuidAndEntityIdData testData;
- testData.m_remappableUuid = AZ::Uuid::CreateRandom();
- testData.m_uuid1 = testData.m_remappableUuid;
- testData.m_uuid2 = AZ::Uuid::CreateRandom();
- testData.m_remappableEntityId = AZ::Entity::MakeId();
- testData.m_entityId1 = testData.m_remappableEntityId;
- testData.m_entityId2 = AZ::Entity::MakeId();
- testData.m_entityRef = testData.m_entity.GetId();
- //Uuid Compare
- EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
- EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
- // EntityId Compare
- EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
- EXPECT_NE(testData.m_remappableEntityId, testData.m_entityId2);
- RemapUuidAndEntityIdData originalData = testData;
- EXPECT_EQ(originalData.m_remappableUuid, testData.m_remappableUuid);
- EXPECT_EQ(originalData.m_uuid1, testData.m_uuid1);
- EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
- EXPECT_EQ(originalData.m_remappableEntityId, testData.m_remappableEntityId);
- EXPECT_EQ(originalData.m_entityId1, testData.m_entityId1);
- EXPECT_EQ(originalData.m_entityId2, testData.m_entityId2);
- EXPECT_EQ(originalData.m_entity.GetId(), testData.m_entity.GetId());
- EXPECT_EQ(originalData.m_entityRef, testData.m_entityRef);
- // Remap Uuids
- AZStd::unordered_map<Uuid, Uuid> remapUuids;
- IdUtils::Remapper<Uuid>::GenerateNewIdsAndFixRefs(&testData, remapUuids, m_serializeContext.get());
-
- // Remap EntityIds
- AZStd::unordered_map<EntityId, EntityId> remapEntityIds;
- IdUtils::Remapper<EntityId>::GenerateNewIdsAndFixRefs(&testData, remapEntityIds, m_serializeContext.get());
- // Remapped Uuid Compare
- // Uuid1 has the same value as the RemappableUuid so it gets updated as well
- EXPECT_NE(originalData.m_remappableUuid, testData.m_remappableUuid);
- EXPECT_NE(originalData.m_uuid1, testData.m_uuid1);
- EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
- // Uuid2 never gets remapped so it should remain the same throughout the test
- EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
- // Remapped EntityId Compare
- // EntityId1 has the same value as the RemappableEntityId so it gets updated as well
- EXPECT_NE(originalData.m_remappableEntityId, testData.m_remappableEntityId);
- EXPECT_NE(originalData.m_entityId1, testData.m_entityId1);
- EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
-
- EXPECT_NE(originalData.m_entity.GetId(), testData.m_entity.GetId());
- EXPECT_NE(originalData.m_entityRef, testData.m_entityRef);
- EXPECT_EQ(testData.m_entity.GetId(), testData.m_entityRef);
- // EntityId does not match the RemappableEntityId so it does not get remapped
- EXPECT_EQ(originalData.m_entityId2, testData.m_entityId2);
- auto remapUuidIt = remapUuids.find(originalData.m_remappableUuid);
- EXPECT_NE(remapUuids.end(), remapUuidIt);
- EXPECT_EQ(testData.m_remappableUuid, remapUuidIt->second);
- auto remapEntityIdIt = remapEntityIds.find(originalData.m_remappableEntityId);
- EXPECT_NE(remapEntityIds.end(), remapEntityIdIt);
- EXPECT_EQ(testData.m_remappableEntityId, remapEntityIdIt->second);
- }
- TEST_F(RemappableIdTest, CloneObjectAndRemapUuidAndEntityIdTest)
- {
- RemapUuidAndEntityIdData testData;
- testData.m_remappableUuid = AZ::Uuid::CreateRandom();
- testData.m_uuid1 = testData.m_remappableUuid;
- testData.m_uuid2 = AZ::Uuid::CreateRandom();
- testData.m_remappableEntityId = AZ::Entity::MakeId();
- testData.m_entityId1 = testData.m_remappableEntityId;
- testData.m_entityId2 = AZ::Entity::MakeId();
- testData.m_entityRef = testData.m_entity.GetId();
- //Uuid Compare
- EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
- EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
- // EntityId Compare
- EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
- EXPECT_NE(testData.m_remappableEntityId, testData.m_entityId2);
- // Clone Object and Remap Uuids
- AZStd::unordered_map<Uuid, Uuid> remapUuids;
- RemapUuidAndEntityIdData* cloneData = IdUtils::Remapper<Uuid>::CloneObjectAndGenerateNewIdsAndFixRefs(&testData, remapUuids, m_serializeContext.get());
- // Remap EntityIds
- AZStd::unordered_map<EntityId, EntityId> remapEntityIds;
- IdUtils::Remapper<EntityId>::GenerateNewIdsAndFixRefs(cloneData, remapEntityIds, m_serializeContext.get());
- // Remapped Uuid Compare
- // Uuid1 has the same value as the RemappableUuid so it gets updated as well
- EXPECT_NE(testData.m_remappableUuid, cloneData->m_remappableUuid);
- EXPECT_NE(testData.m_uuid1, cloneData->m_uuid1);
- EXPECT_EQ(cloneData->m_remappableUuid, cloneData->m_uuid1);
- // Uuid2 never gets remapped so it should remain the same throughout the test
- EXPECT_EQ(testData.m_uuid2, cloneData->m_uuid2);
- // Remapped EntityId Compare
- // EntityId1 has the same value as the RemappableEntityId so it gets updated as well
- EXPECT_NE(testData.m_remappableEntityId, cloneData->m_remappableEntityId);
- EXPECT_NE(testData.m_entityId1, cloneData->m_entityId1);
- EXPECT_EQ(cloneData->m_remappableEntityId, cloneData->m_entityId1);
- EXPECT_NE(testData.m_entity.GetId(), cloneData->m_entity.GetId());
- EXPECT_NE(testData.m_entityRef, cloneData->m_entityRef);
- EXPECT_EQ(cloneData->m_entity.GetId(), cloneData->m_entityRef);
- // EntityId does not match the RemappableEntityId so it does not get remapped
- EXPECT_EQ(testData.m_entityId2, cloneData->m_entityId2);
- auto remapUuidIt = remapUuids.find(testData.m_remappableUuid);
- EXPECT_NE(remapUuids.end(), remapUuidIt);
- EXPECT_EQ(cloneData->m_remappableUuid, remapUuidIt->second);
- auto remapEntityIdIt = remapEntityIds.find(testData.m_remappableEntityId);
- EXPECT_NE(remapEntityIds.end(), remapEntityIdIt);
- EXPECT_EQ(cloneData->m_remappableEntityId, remapEntityIdIt->second);
- delete cloneData;
- }
- // Test that the IdUtils::RemapIds function does not crash when invoking the IEventHandler function
- // on a pointer to a class element(i.e ClassType**)
- TEST_F(RemappableIdTest, OnWriteCallbacks_WhenParentHasPointer_DoesNotCrash)
- {
- RootParentElementWrapper rootWrapper;
- rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
- auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
- {
- return idGenerator();
- };
- AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
- EXPECT_NE(0, rootWrapper.m_parentEntityContainer->m_beginWriteCount); // check that callbacks were invoked at all
- }
- // Test that each OnWriteBegin is paired with an OnWriteEnd
- // and that we don't get two OnWriteBegins in a row
- TEST_F(RemappableIdTest, OnWriteCallbacks_CalledInCorrectOrder)
- {
- ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = true;
- RootParentElementWrapper rootWrapper;
- rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
- auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
- {
- return idGenerator();
- };
- AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
- EXPECT_EQ(rootWrapper.m_parentEntityContainer->m_beginWriteCount, rootWrapper.m_parentEntityContainer->m_endWriteCount);
- ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = false;
- }
- // Test that we only call OnWriteBegin once per node, even if multiple IDs on child nodes are remapped
- TEST_F(RemappableIdTest, OnWriteCallbacks_WhenMultipleChildrenChanged_CalledOnceOnParent)
- {
- RootParentElementWrapper rootWrapper;
- rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
- auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
- {
- return idGenerator();
- };
- unsigned int remappedIdCount = AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
- EXPECT_EQ(2, remappedIdCount);
- EXPECT_EQ(1, rootWrapper.m_parentEntityContainer->m_beginWriteCount);
- }
- } // namespace UnitTest
|