RemappableId.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  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/Component/Entity.h>
  10. #include <AzCore/Serialization/IdUtils.h>
  11. namespace UnitTest
  12. {
  13. using namespace AZ;
  14. //! Unit Test for IdUtils GenerateNewIdsAndFixRefs functions for generating new unique ids
  15. class RemappableIdTest
  16. : public LeakDetectionFixture
  17. {
  18. public:
  19. RemappableIdTest()
  20. : LeakDetectionFixture()
  21. {
  22. }
  23. ~RemappableIdTest() override
  24. {
  25. }
  26. void SetUp() override
  27. {
  28. LeakDetectionFixture::SetUp();
  29. m_serializeContext.reset(aznew AZ::SerializeContext());
  30. Entity::Reflect(m_serializeContext.get());
  31. RemapIdData::Reflect(*m_serializeContext);
  32. RemapUuidAndEntityIdData::Reflect(*m_serializeContext);
  33. ParentEntityContainer::Reflect(*m_serializeContext);
  34. RootParentElementWrapper::Reflect(*m_serializeContext);
  35. }
  36. void TearDown() override
  37. {
  38. m_serializeContext.reset();
  39. LeakDetectionFixture::TearDown();
  40. }
  41. AZStd::unique_ptr<SerializeContext> m_serializeContext;
  42. struct RemapIdData
  43. {
  44. AZ_TYPE_INFO(RemapIdData, "{0431A85A-C2CC-453A-92A7-807BCEAB46D7}");
  45. AZ_CLASS_ALLOCATOR(RemapIdData, AZ::SystemAllocator);
  46. static void Reflect(AZ::SerializeContext& serializeContext)
  47. {
  48. serializeContext.Class<RemapIdData>()
  49. ->Field("m_remappableUuid", &RemapIdData::m_remappableUuid)
  50. ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Uuid::CreateRandom)
  51. ->Field("m_uuid1", &RemapIdData::m_uuid1)
  52. ->Field("m_uuid2", &RemapIdData::m_uuid2)
  53. ;
  54. }
  55. Uuid m_remappableUuid;
  56. Uuid m_uuid1;
  57. Uuid m_uuid2;
  58. };
  59. struct RemapUuidAndEntityIdData
  60. {
  61. AZ_TYPE_INFO(RemapUuidAndEntityIdData, "{6951A116-505C-47E0-9310-2E60D040F390}");
  62. AZ_CLASS_ALLOCATOR(RemapUuidAndEntityIdData, AZ::SystemAllocator);
  63. // copying entities is forbidden.
  64. RemapUuidAndEntityIdData()
  65. {
  66. }
  67. RemapUuidAndEntityIdData(const RemapUuidAndEntityIdData& other)
  68. {
  69. m_remappableUuid = other.m_remappableUuid;
  70. m_uuid1 = other.m_uuid1;
  71. m_uuid2 = other.m_uuid2;
  72. m_remappableEntityId = other.m_remappableEntityId;
  73. m_entityId1 = other.m_entityId1;
  74. m_entityId2 = other.m_entityId2;
  75. m_entity.SetId(other.m_entity.GetId()); // for this test, we only copy the ID.
  76. m_entityRef = other.m_entityRef;
  77. }
  78. static void Reflect(AZ::SerializeContext& serializeContext)
  79. {
  80. serializeContext.Class<RemapUuidAndEntityIdData>()
  81. ->Field("m_remappableUuid", &RemapUuidAndEntityIdData::m_remappableUuid)
  82. ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Uuid::CreateRandom)
  83. ->Field("m_uuid1", &RemapUuidAndEntityIdData::m_uuid1)
  84. ->Field("m_uuid2", &RemapUuidAndEntityIdData::m_uuid2)
  85. ->Field("m_remappbleEntityId", &RemapUuidAndEntityIdData::m_remappableEntityId)
  86. ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
  87. ->Field("m_entityId1", &RemapUuidAndEntityIdData::m_entityId1)
  88. ->Field("m_entityId2", &RemapUuidAndEntityIdData::m_entityId2)
  89. ->Field("m_entity", &RemapUuidAndEntityIdData::m_entity)
  90. ->Field("m_entityRef", &RemapUuidAndEntityIdData::m_entityRef)
  91. ;
  92. }
  93. Uuid m_remappableUuid;
  94. Uuid m_uuid1;
  95. Uuid m_uuid2;
  96. AZ::EntityId m_remappableEntityId;
  97. AZ::EntityId m_entityId1;
  98. AZ::EntityId m_entityId2;
  99. AZ::Entity m_entity;
  100. AZ::EntityId m_entityRef;
  101. };
  102. struct ParentEntityContainer
  103. {
  104. AZ_TYPE_INFO(ParentEntityContainer, "{60EFD610-4B9A-444C-9004-A651B772927F}");
  105. AZ_CLASS_ALLOCATOR(ParentEntityContainer, AZ::SystemAllocator);
  106. static void Reflect(AZ::SerializeContext& serializeContext);
  107. AZ::EntityId m_remappableEntityId1;
  108. AZ::EntityId m_remappableEntityId2;
  109. int m_beginWriteCount = 0;
  110. int m_endWriteCount = 0;
  111. };
  112. struct RootParentElementWrapper
  113. {
  114. AZ_TYPE_INFO(RootParentElementWrapper, "{BFC46F3C-D696-4A34-B824-F18428CAA910}");
  115. AZ_CLASS_ALLOCATOR(RootParentElementWrapper, AZ::SystemAllocator);
  116. static void Reflect(AZ::SerializeContext& serializeContext)
  117. {
  118. serializeContext.Class<RootParentElementWrapper>()
  119. ->Field("m_parentEntityContainer", &RootParentElementWrapper::m_parentEntityContainer)
  120. ;
  121. }
  122. ~RootParentElementWrapper()
  123. {
  124. delete m_parentEntityContainer;
  125. }
  126. ParentEntityContainer* m_parentEntityContainer = nullptr;
  127. };
  128. struct ParentEntityContainerEventHandler
  129. : public AZ::SerializeContext::IEventHandler
  130. {
  131. static bool s_isCheckingCallbackOrder;
  132. void OnWriteBegin(void* classPtr) override
  133. {
  134. auto parentEntityContainer = reinterpret_cast<ParentEntityContainer*>(classPtr);
  135. if (s_isCheckingCallbackOrder)
  136. {
  137. EXPECT_EQ(parentEntityContainer->m_beginWriteCount, parentEntityContainer->m_endWriteCount);
  138. }
  139. parentEntityContainer->m_beginWriteCount++;
  140. }
  141. void OnWriteEnd(void* classPtr) override
  142. {
  143. auto parentEntityContainer = reinterpret_cast<ParentEntityContainer*>(classPtr);
  144. if (s_isCheckingCallbackOrder)
  145. {
  146. EXPECT_EQ(parentEntityContainer->m_beginWriteCount, parentEntityContainer->m_endWriteCount + 1);
  147. }
  148. parentEntityContainer->m_endWriteCount++;
  149. }
  150. };
  151. };
  152. bool RemappableIdTest::ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = false;
  153. void RemappableIdTest::ParentEntityContainer::Reflect(AZ::SerializeContext& serializeContext)
  154. {
  155. serializeContext.Class<ParentEntityContainer>()
  156. ->EventHandler<ParentEntityContainerEventHandler>()
  157. ->Field("m_remappableEntityId1", &ParentEntityContainer::m_remappableEntityId1)
  158. ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
  159. ->Field("m_remappableEntityId2", &ParentEntityContainer::m_remappableEntityId2)
  160. ->Attribute(AZ::Edit::Attributes::IdGeneratorFunction, &AZ::Entity::MakeId)
  161. ;
  162. }
  163. TEST_F(RemappableIdTest, UuidRemapTest)
  164. {
  165. RemapIdData testData;
  166. testData.m_remappableUuid = AZ::Uuid::CreateRandom();
  167. testData.m_uuid1 = testData.m_remappableUuid;
  168. testData.m_uuid2 = AZ::Uuid::CreateRandom();
  169. EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
  170. EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
  171. RemapIdData originalData = testData;
  172. EXPECT_EQ(originalData.m_remappableUuid, testData.m_remappableUuid);
  173. EXPECT_EQ(originalData.m_uuid1, testData.m_uuid1);
  174. EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
  175. AZStd::unordered_map<Uuid, Uuid> remapIds;
  176. IdUtils::Remapper<Uuid>::GenerateNewIdsAndFixRefs(&testData, remapIds, m_serializeContext.get());
  177. EXPECT_NE(originalData.m_remappableUuid, testData.m_remappableUuid);
  178. EXPECT_NE(originalData.m_uuid1, testData.m_uuid1);
  179. // Uuid2 never gets remapped so it should remain the same throughout the test
  180. EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
  181. // Uuid1 has the same value as the RemappableUuid so it gets updated as well
  182. EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
  183. auto remapIdsIt = remapIds.find(originalData.m_remappableUuid);
  184. EXPECT_NE(remapIds.end(), remapIdsIt);
  185. EXPECT_EQ(testData.m_remappableUuid, remapIdsIt->second);
  186. }
  187. TEST_F(RemappableIdTest, UuidAndEntityIdRemapTest)
  188. {
  189. RemapUuidAndEntityIdData testData;
  190. testData.m_remappableUuid = AZ::Uuid::CreateRandom();
  191. testData.m_uuid1 = testData.m_remappableUuid;
  192. testData.m_uuid2 = AZ::Uuid::CreateRandom();
  193. testData.m_remappableEntityId = AZ::Entity::MakeId();
  194. testData.m_entityId1 = testData.m_remappableEntityId;
  195. testData.m_entityId2 = AZ::Entity::MakeId();
  196. testData.m_entityRef = testData.m_entity.GetId();
  197. //Uuid Compare
  198. EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
  199. EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
  200. // EntityId Compare
  201. EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
  202. EXPECT_NE(testData.m_remappableEntityId, testData.m_entityId2);
  203. RemapUuidAndEntityIdData originalData = testData;
  204. EXPECT_EQ(originalData.m_remappableUuid, testData.m_remappableUuid);
  205. EXPECT_EQ(originalData.m_uuid1, testData.m_uuid1);
  206. EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
  207. EXPECT_EQ(originalData.m_remappableEntityId, testData.m_remappableEntityId);
  208. EXPECT_EQ(originalData.m_entityId1, testData.m_entityId1);
  209. EXPECT_EQ(originalData.m_entityId2, testData.m_entityId2);
  210. EXPECT_EQ(originalData.m_entity.GetId(), testData.m_entity.GetId());
  211. EXPECT_EQ(originalData.m_entityRef, testData.m_entityRef);
  212. // Remap Uuids
  213. AZStd::unordered_map<Uuid, Uuid> remapUuids;
  214. IdUtils::Remapper<Uuid>::GenerateNewIdsAndFixRefs(&testData, remapUuids, m_serializeContext.get());
  215. // Remap EntityIds
  216. AZStd::unordered_map<EntityId, EntityId> remapEntityIds;
  217. IdUtils::Remapper<EntityId>::GenerateNewIdsAndFixRefs(&testData, remapEntityIds, m_serializeContext.get());
  218. // Remapped Uuid Compare
  219. // Uuid1 has the same value as the RemappableUuid so it gets updated as well
  220. EXPECT_NE(originalData.m_remappableUuid, testData.m_remappableUuid);
  221. EXPECT_NE(originalData.m_uuid1, testData.m_uuid1);
  222. EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
  223. // Uuid2 never gets remapped so it should remain the same throughout the test
  224. EXPECT_EQ(originalData.m_uuid2, testData.m_uuid2);
  225. // Remapped EntityId Compare
  226. // EntityId1 has the same value as the RemappableEntityId so it gets updated as well
  227. EXPECT_NE(originalData.m_remappableEntityId, testData.m_remappableEntityId);
  228. EXPECT_NE(originalData.m_entityId1, testData.m_entityId1);
  229. EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
  230. EXPECT_NE(originalData.m_entity.GetId(), testData.m_entity.GetId());
  231. EXPECT_NE(originalData.m_entityRef, testData.m_entityRef);
  232. EXPECT_EQ(testData.m_entity.GetId(), testData.m_entityRef);
  233. // EntityId does not match the RemappableEntityId so it does not get remapped
  234. EXPECT_EQ(originalData.m_entityId2, testData.m_entityId2);
  235. auto remapUuidIt = remapUuids.find(originalData.m_remappableUuid);
  236. EXPECT_NE(remapUuids.end(), remapUuidIt);
  237. EXPECT_EQ(testData.m_remappableUuid, remapUuidIt->second);
  238. auto remapEntityIdIt = remapEntityIds.find(originalData.m_remappableEntityId);
  239. EXPECT_NE(remapEntityIds.end(), remapEntityIdIt);
  240. EXPECT_EQ(testData.m_remappableEntityId, remapEntityIdIt->second);
  241. }
  242. TEST_F(RemappableIdTest, CloneObjectAndRemapUuidAndEntityIdTest)
  243. {
  244. RemapUuidAndEntityIdData testData;
  245. testData.m_remappableUuid = AZ::Uuid::CreateRandom();
  246. testData.m_uuid1 = testData.m_remappableUuid;
  247. testData.m_uuid2 = AZ::Uuid::CreateRandom();
  248. testData.m_remappableEntityId = AZ::Entity::MakeId();
  249. testData.m_entityId1 = testData.m_remappableEntityId;
  250. testData.m_entityId2 = AZ::Entity::MakeId();
  251. testData.m_entityRef = testData.m_entity.GetId();
  252. //Uuid Compare
  253. EXPECT_EQ(testData.m_remappableUuid, testData.m_uuid1);
  254. EXPECT_NE(testData.m_remappableUuid, testData.m_uuid2);
  255. // EntityId Compare
  256. EXPECT_EQ(testData.m_remappableEntityId, testData.m_entityId1);
  257. EXPECT_NE(testData.m_remappableEntityId, testData.m_entityId2);
  258. // Clone Object and Remap Uuids
  259. AZStd::unordered_map<Uuid, Uuid> remapUuids;
  260. RemapUuidAndEntityIdData* cloneData = IdUtils::Remapper<Uuid>::CloneObjectAndGenerateNewIdsAndFixRefs(&testData, remapUuids, m_serializeContext.get());
  261. // Remap EntityIds
  262. AZStd::unordered_map<EntityId, EntityId> remapEntityIds;
  263. IdUtils::Remapper<EntityId>::GenerateNewIdsAndFixRefs(cloneData, remapEntityIds, m_serializeContext.get());
  264. // Remapped Uuid Compare
  265. // Uuid1 has the same value as the RemappableUuid so it gets updated as well
  266. EXPECT_NE(testData.m_remappableUuid, cloneData->m_remappableUuid);
  267. EXPECT_NE(testData.m_uuid1, cloneData->m_uuid1);
  268. EXPECT_EQ(cloneData->m_remappableUuid, cloneData->m_uuid1);
  269. // Uuid2 never gets remapped so it should remain the same throughout the test
  270. EXPECT_EQ(testData.m_uuid2, cloneData->m_uuid2);
  271. // Remapped EntityId Compare
  272. // EntityId1 has the same value as the RemappableEntityId so it gets updated as well
  273. EXPECT_NE(testData.m_remappableEntityId, cloneData->m_remappableEntityId);
  274. EXPECT_NE(testData.m_entityId1, cloneData->m_entityId1);
  275. EXPECT_EQ(cloneData->m_remappableEntityId, cloneData->m_entityId1);
  276. EXPECT_NE(testData.m_entity.GetId(), cloneData->m_entity.GetId());
  277. EXPECT_NE(testData.m_entityRef, cloneData->m_entityRef);
  278. EXPECT_EQ(cloneData->m_entity.GetId(), cloneData->m_entityRef);
  279. // EntityId does not match the RemappableEntityId so it does not get remapped
  280. EXPECT_EQ(testData.m_entityId2, cloneData->m_entityId2);
  281. auto remapUuidIt = remapUuids.find(testData.m_remappableUuid);
  282. EXPECT_NE(remapUuids.end(), remapUuidIt);
  283. EXPECT_EQ(cloneData->m_remappableUuid, remapUuidIt->second);
  284. auto remapEntityIdIt = remapEntityIds.find(testData.m_remappableEntityId);
  285. EXPECT_NE(remapEntityIds.end(), remapEntityIdIt);
  286. EXPECT_EQ(cloneData->m_remappableEntityId, remapEntityIdIt->second);
  287. delete cloneData;
  288. }
  289. // Test that the IdUtils::RemapIds function does not crash when invoking the IEventHandler function
  290. // on a pointer to a class element(i.e ClassType**)
  291. TEST_F(RemappableIdTest, OnWriteCallbacks_WhenParentHasPointer_DoesNotCrash)
  292. {
  293. RootParentElementWrapper rootWrapper;
  294. rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
  295. auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
  296. {
  297. return idGenerator();
  298. };
  299. AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
  300. EXPECT_NE(0, rootWrapper.m_parentEntityContainer->m_beginWriteCount); // check that callbacks were invoked at all
  301. }
  302. // Test that each OnWriteBegin is paired with an OnWriteEnd
  303. // and that we don't get two OnWriteBegins in a row
  304. TEST_F(RemappableIdTest, OnWriteCallbacks_CalledInCorrectOrder)
  305. {
  306. ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = true;
  307. RootParentElementWrapper rootWrapper;
  308. rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
  309. auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
  310. {
  311. return idGenerator();
  312. };
  313. AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
  314. EXPECT_EQ(rootWrapper.m_parentEntityContainer->m_beginWriteCount, rootWrapper.m_parentEntityContainer->m_endWriteCount);
  315. ParentEntityContainerEventHandler::s_isCheckingCallbackOrder = false;
  316. }
  317. // Test that we only call OnWriteBegin once per node, even if multiple IDs on child nodes are remapped
  318. TEST_F(RemappableIdTest, OnWriteCallbacks_WhenMultipleChildrenChanged_CalledOnceOnParent)
  319. {
  320. RootParentElementWrapper rootWrapper;
  321. rootWrapper.m_parentEntityContainer = aznew ParentEntityContainer;
  322. auto entityIdMapper = [](const AZ::EntityId&, bool, const AZ::IdUtils::Remapper<AZ::EntityId>::IdGenerator& idGenerator)
  323. {
  324. return idGenerator();
  325. };
  326. unsigned int remappedIdCount = AZ::IdUtils::Remapper<AZ::EntityId>::RemapIds(&rootWrapper, entityIdMapper, m_serializeContext.get(), true);
  327. EXPECT_EQ(2, remappedIdCount);
  328. EXPECT_EQ(1, rootWrapper.m_parentEntityContainer->m_beginWriteCount);
  329. }
  330. } // namespace UnitTest