PrefabInstanceToTemplatePropagatorTests.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  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 <Prefab/PrefabTestDomUtils.h>
  9. #include <Prefab/PrefabTestFixture.h>
  10. #include <Prefab/PrefabTestComponent.h>
  11. #include <AzCore/Component/TransformBus.h>
  12. #include <AzCore/Component/ComponentApplicationBus.h>
  13. #include <AzFramework/Components/TransformComponent.h>
  14. namespace UnitTest
  15. {
  16. using PrefabInstanceToTemplateTests = PrefabTestFixture;
  17. TEST_F(PrefabInstanceToTemplateTests, GenerateEntityDom_InvalidType_InvalidTypeSkipped)
  18. {
  19. const char* newEntityName = "New Entity";
  20. AZ::Entity* newEntity = CreateEntity(newEntityName, false);
  21. ASSERT_TRUE(newEntity);
  22. // Add a component with a member that is missing reflection info
  23. // and a member that is properly reflected
  24. PrefabTestComponentWithUnReflectedTypeMember* newComponent =
  25. newEntity->CreateComponent<PrefabTestComponentWithUnReflectedTypeMember>();
  26. ASSERT_TRUE(newComponent);
  27. AZStd::unique_ptr<Instance> prefabInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
  28. ASSERT_TRUE(prefabInstance);
  29. PrefabDom entityDom;
  30. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(entityDom, *newEntity);
  31. auto componentListDom = entityDom.FindMember("Components");
  32. // Confirm that there is only one component in the entityDom
  33. ASSERT_NE(componentListDom, entityDom.MemberEnd());
  34. ASSERT_TRUE(componentListDom->value.IsObject());
  35. ASSERT_EQ(componentListDom->value.MemberCount(), 1);
  36. auto testComponentDom = componentListDom->value.MemberBegin();
  37. ASSERT_TRUE(testComponentDom->value.IsObject());
  38. // Confirm that the componentDom does not contained the invalid UnReflectedType
  39. // We want to skip over it and produce a best effort entityDom
  40. auto unReflectedTypeDom = testComponentDom->value.FindMember("UnReflectedType");
  41. ASSERT_EQ(unReflectedTypeDom, testComponentDom->value.MemberEnd());
  42. // Confirm the presence of the valid ReflectedType
  43. auto reflectedTypeDom = testComponentDom->value.FindMember("ReflectedType");
  44. ASSERT_NE(reflectedTypeDom, testComponentDom->value.MemberEnd());
  45. // Confirm the reflected type has the correct type and value
  46. ASSERT_TRUE(reflectedTypeDom->value.IsInt());
  47. EXPECT_EQ(reflectedTypeDom->value.GetInt(), newComponent->m_reflectedType);
  48. }
  49. TEST_F(PrefabInstanceToTemplateTests, GenerateInstanceDom_InvalidType_InvalidTypeSkipped)
  50. {
  51. const char* newEntityName = "New Entity";
  52. AZ::Entity* newEntity = CreateEntity(newEntityName, false);
  53. ASSERT_TRUE(newEntity);
  54. // Add a component with a member that is missing reflection info
  55. // and a member that is properly reflected
  56. PrefabTestComponentWithUnReflectedTypeMember* newComponent =
  57. newEntity->CreateComponent<PrefabTestComponentWithUnReflectedTypeMember>();
  58. ASSERT_TRUE(newComponent);
  59. AZStd::unique_ptr<Instance> prefabInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
  60. ASSERT_TRUE(prefabInstance);
  61. PrefabDom instanceDom;
  62. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDom, *prefabInstance);
  63. // Acquire the entity out of the instanceDom
  64. auto entitiesDom = instanceDom.FindMember(PrefabDomUtils::EntitiesName);
  65. ASSERT_NE(entitiesDom, instanceDom.MemberEnd());
  66. ASSERT_EQ(entitiesDom->value.MemberCount(), 1);
  67. auto entityDom = entitiesDom->value.MemberBegin();
  68. auto componentListDom = entityDom->value.FindMember("Components");
  69. // Confirm that there is only one component in the entityDom
  70. ASSERT_NE(componentListDom, entityDom->value.MemberEnd());
  71. ASSERT_TRUE(componentListDom->value.IsObject());
  72. ASSERT_EQ(componentListDom->value.MemberCount(), 1);
  73. auto testComponentDom = componentListDom->value.MemberBegin();
  74. ASSERT_TRUE(testComponentDom->value.IsObject());
  75. // Confirm that the componentDom does not contained the invalid UnReflectedType
  76. // We want to skip over it and produce a best effort entityDom
  77. auto unReflectedTypeDom = testComponentDom->value.FindMember("UnReflectedType");
  78. ASSERT_EQ(unReflectedTypeDom, testComponentDom->value.MemberEnd());
  79. // Confirm the presence of the valid ReflectedType
  80. auto reflectedTypeDom = testComponentDom->value.FindMember("ReflectedType");
  81. ASSERT_NE(reflectedTypeDom, testComponentDom->value.MemberEnd());
  82. // Confirm the reflected type has the correct type and value
  83. ASSERT_TRUE(reflectedTypeDom->value.IsInt());
  84. EXPECT_EQ(reflectedTypeDom->value.GetInt(), newComponent->m_reflectedType);
  85. }
  86. TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_UpdateEntityOnInstance)
  87. {
  88. //create template with single entity
  89. const char* newEntityName = "New Entity";
  90. AZ::Entity* newEntity = CreateEntity(newEntityName, false);
  91. ASSERT_TRUE(newEntity);
  92. AZ::EntityId entityId = newEntity->GetId();
  93. //add a transform component for testing purposes
  94. newEntity->CreateComponent(AZ::EditorTransformComponentTypeId);
  95. newEntity->Init();
  96. newEntity->Activate();
  97. AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
  98. ASSERT_TRUE(firstInstance);
  99. EntityAliasOptionalReference newEntityAliasReference = firstInstance->GetEntityAlias(entityId);
  100. ASSERT_TRUE(newEntityAliasReference);
  101. EntityAlias newEntityAlias = newEntityAliasReference.value();
  102. //get template id
  103. TemplateId templateId = firstInstance->GetTemplateId();
  104. //instantiate second instance
  105. AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
  106. ASSERT_TRUE(secondInstance);
  107. //create document with before change snapshot
  108. PrefabDom entityDomBeforeUpdate;
  109. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(entityDomBeforeUpdate, *newEntity);
  110. //update values on entity
  111. const float updatedXValue = 5.0f;
  112. AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetWorldX, updatedXValue);
  113. //create document with after change snapshot
  114. PrefabDom entityDomAfterUpdate;
  115. m_instanceToTemplateInterface->GenerateEntityDomBySerializing(entityDomAfterUpdate, *newEntity);
  116. //generate patch
  117. PrefabDom patch;
  118. m_instanceToTemplateInterface->GeneratePatch(patch, entityDomBeforeUpdate, entityDomAfterUpdate);
  119. //update template
  120. ASSERT_TRUE(m_instanceToTemplateInterface->PatchEntityInTemplate(patch, entityId));
  121. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  122. //get the entity id
  123. AZ::EntityId secondEntityId = secondInstance->GetEntityId(newEntityAlias);
  124. ASSERT_TRUE(secondEntityId.IsValid());
  125. //verify template updated correctly
  126. //get the values from the transform on the entity
  127. float confirmXValue = 0.0f;
  128. AZ::TransformBus::EventResult(confirmXValue, entityId, &AZ::TransformInterface::GetWorldX);
  129. AZ::TransformBus::EventResult(confirmXValue, secondEntityId, &AZ::TransformInterface::GetWorldX);
  130. ASSERT_TRUE(confirmXValue == updatedXValue);
  131. }
  132. TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_AddEntityToInstance)
  133. {
  134. //create template with single entity
  135. const char* newEntityName = "New Entity";
  136. AZ::Entity* newEntity = CreateEntity(newEntityName, false);
  137. ASSERT_TRUE(newEntity);
  138. //create a first instance where the entity will be added
  139. AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/path");
  140. ASSERT_TRUE(firstInstance);
  141. //get template id
  142. TemplateId templateId = firstInstance->GetTemplateId();
  143. //instantiate second instance for checking if propogation works
  144. AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
  145. ASSERT_TRUE(secondInstance);
  146. //create document with before change snapshot
  147. PrefabDom instanceDomBeforeUpdate;
  148. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomBeforeUpdate, *firstInstance);
  149. //add entity to instance
  150. firstInstance->AddEntity(*newEntity);
  151. //create document with after change snapshot
  152. PrefabDom instanceDomAfterUpdate;
  153. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomAfterUpdate, *firstInstance);
  154. //generate patch
  155. PrefabDom patch;
  156. m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
  157. //update template
  158. m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
  159. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  160. //get the entity id
  161. AZStd::vector<AZ::EntityId> entityIdVector;
  162. secondInstance->GetEntityIds([&entityIdVector](AZ::EntityId entityId)
  163. {
  164. entityIdVector.push_back(entityId);
  165. return true;
  166. });
  167. // There should be 2 entities in the instance, the container entity and the one we added
  168. EXPECT_EQ(entityIdVector.size(), 2);
  169. }
  170. TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_RemoveEntityFromInstance)
  171. {
  172. //create template with single entity
  173. AZ::Entity* newEntity = CreateEntity("New Entity", false);
  174. ASSERT_TRUE(newEntity);
  175. AZ::EntityId entityId = newEntity->GetId();
  176. //create a first instance where the entity will be removed
  177. AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
  178. ASSERT_TRUE(firstInstance);
  179. //get template id
  180. TemplateId templateId = firstInstance->GetTemplateId();
  181. //instantiate second instance for checking if propogation works
  182. AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
  183. ASSERT_TRUE(secondInstance);
  184. //create document with before change snapshot
  185. PrefabDom instanceDomBeforeUpdate;
  186. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomBeforeUpdate, *firstInstance);
  187. //remove entity from instance
  188. firstInstance->DetachEntity(entityId);
  189. //create document with after change snapshot
  190. PrefabDom instanceDomAfterUpdate;
  191. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomAfterUpdate, *firstInstance);
  192. //generate patch
  193. PrefabDom patch;
  194. m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
  195. //update template
  196. m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
  197. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  198. //get the entity id
  199. AZStd::vector<AZ::EntityId> entityIdVector;
  200. secondInstance->GetEntityIds([&entityIdVector](AZ::EntityId entityId)
  201. {
  202. entityIdVector.push_back(entityId);
  203. return true;
  204. });
  205. // There should be 1 entity, the container and no others since we removed the other
  206. EXPECT_EQ(entityIdVector.size(), 1);
  207. }
  208. TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_AddInstanceToInstance)
  209. {
  210. //create a first instance where the instance will be added
  211. AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/path");
  212. ASSERT_TRUE(firstInstance);
  213. //get template id
  214. TemplateId templateId = firstInstance->GetTemplateId();
  215. //instantiate second instance for checking if propogation works
  216. AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
  217. ASSERT_TRUE(secondInstance);
  218. //create document with before change snapshot
  219. PrefabDom instanceDomBeforeUpdate;
  220. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomBeforeUpdate, *firstInstance);
  221. //create new instance and get alias
  222. AZStd::unique_ptr<Instance> addedInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/pathtest");
  223. //add instance to instance
  224. Instance& addedInstanceRef = firstInstance->AddInstance(AZStd::move(addedInstance));
  225. const AzToolsFramework::Prefab::InstanceAlias addedAlias = addedInstanceRef.GetInstanceAlias();
  226. //create document with after change snapshot
  227. PrefabDom instanceDomAfterUpdate;
  228. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomAfterUpdate, *firstInstance);
  229. //generate patch
  230. PrefabDom patch;
  231. m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
  232. //update template
  233. m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
  234. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  235. EXPECT_NE(secondInstance->FindNestedInstance(addedAlias), AZStd::nullopt);
  236. }
  237. TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_RemoveInstanceFromInstance)
  238. {
  239. AZStd::unique_ptr<Instance> addedInstancePtr = m_prefabSystemComponent->CreatePrefab({}, {}, "test/pathtest");
  240. Instance& addedInstance = *addedInstancePtr;
  241. //create a first instance where the instance will be removed
  242. AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList(AZStd::move(addedInstancePtr)), "test/path");
  243. ASSERT_TRUE(firstInstance);
  244. //get added instance alias
  245. const AzToolsFramework::Prefab::InstanceAlias addedAlias = addedInstance.GetInstanceAlias();
  246. //get template id
  247. TemplateId templateId = firstInstance->GetTemplateId();
  248. //instantiate second instance for checking if propagation works
  249. AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
  250. ASSERT_TRUE(secondInstance);
  251. //create document with before change snapshot
  252. PrefabDom instanceDomBeforeUpdate;
  253. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomBeforeUpdate, *firstInstance);
  254. //remove instance from instance
  255. AZStd::unique_ptr<Instance> detachedInstance = firstInstance->DetachNestedInstance(addedAlias);
  256. ASSERT_TRUE(detachedInstance != nullptr);
  257. m_prefabSystemComponent->RemoveLink(detachedInstance->GetLinkId());
  258. //create document with after change snapshot
  259. PrefabDom instanceDomAfterUpdate;
  260. m_instanceToTemplateInterface->GenerateInstanceDomBySerializing(instanceDomAfterUpdate, *firstInstance);
  261. //generate patch
  262. PrefabDom patch;
  263. m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
  264. //update template
  265. m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
  266. m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
  267. EXPECT_EQ(secondInstance->FindNestedInstance(addedAlias), AZStd::nullopt);
  268. }
  269. } // namespace UnitTest