PrefabTestFixture.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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/PrefabTestFixture.h>
  9. #include <AzCore/Component/TransformBus.h>
  10. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  11. #include <AzToolsFramework/Entity/PrefabEditorEntityOwnershipInterface.h>
  12. #include <AzToolsFramework/Prefab/PrefabDomUtils.h>
  13. #include <AzToolsFramework/Prefab/Instance/InstanceEntityMapperInterface.h>
  14. #include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
  15. #include <AzToolsFramework/ToolsComponents/TransformComponent.h>
  16. #include <Prefab/PrefabTestComponent.h>
  17. #include <Prefab/PrefabTestDomUtils.h>
  18. namespace UnitTest
  19. {
  20. PrefabTestToolsApplication::PrefabTestToolsApplication(AZStd::string appName)
  21. : ToolsTestApplication(AZStd::move(appName))
  22. {
  23. }
  24. void PrefabTestFixture::SetUpEditorFixtureImpl()
  25. {
  26. // Acquire the system entity
  27. AZ::Entity* systemEntity = GetApplication()->FindEntity(AZ::SystemEntityId);
  28. EXPECT_TRUE(systemEntity);
  29. m_prefabSystemComponent = systemEntity->FindComponent<AzToolsFramework::Prefab::PrefabSystemComponent>();
  30. EXPECT_TRUE(m_prefabSystemComponent);
  31. m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
  32. EXPECT_TRUE(m_prefabLoaderInterface);
  33. m_prefabPublicInterface = AZ::Interface<PrefabPublicInterface>::Get();
  34. EXPECT_TRUE(m_prefabPublicInterface);
  35. m_instanceEntityMapperInterface = AZ::Interface<InstanceEntityMapperInterface>::Get();
  36. EXPECT_TRUE(m_instanceEntityMapperInterface);
  37. m_instanceUpdateExecutorInterface = AZ::Interface<InstanceUpdateExecutorInterface>::Get();
  38. EXPECT_TRUE(m_instanceUpdateExecutorInterface);
  39. m_instanceToTemplateInterface = AZ::Interface<InstanceToTemplateInterface>::Get();
  40. EXPECT_TRUE(m_instanceToTemplateInterface);
  41. m_prefabEditorEntityOwnershipInterface = AZ::Interface<AzToolsFramework::PrefabEditorEntityOwnershipInterface>::Get();
  42. EXPECT_TRUE(m_prefabEditorEntityOwnershipInterface);
  43. m_settingsRegistryInterface = AZ::SettingsRegistry::Get();
  44. EXPECT_TRUE(m_settingsRegistryInterface);
  45. // Have all instances cache their DOM for testing purposes.
  46. Instance::s_DomCachingEnabledDefault = true;
  47. // This is for calling CreateEditorRepresentation that adds required editor components.
  48. AzToolsFramework::EditorRequestBus::Handler::BusConnect();
  49. GetApplication()->RegisterComponentDescriptor(PrefabTestComponent::CreateDescriptor());
  50. GetApplication()->RegisterComponentDescriptor(PrefabNonEditorComponent::CreateDescriptor());
  51. GetApplication()->RegisterComponentDescriptor(PrefabTestComponentWithUnReflectedTypeMember::CreateDescriptor());
  52. // Gets undo stack.
  53. AzToolsFramework::ToolsApplicationRequestBus::BroadcastResult(
  54. m_undoStack, &AzToolsFramework::ToolsApplicationRequestBus::Events::GetUndoStack);
  55. AZ_Assert(m_undoStack, "Failed to look up undo stack from tools application");
  56. // This ensures that the flag (if root prefab is assigned) in prefab editor entity ownership service is set to true.
  57. // Public prefab operations like "create prefab" will fail if the flag is off.
  58. CreateRootPrefab();
  59. }
  60. void PrefabTestFixture::TearDownEditorFixtureImpl()
  61. {
  62. m_undoStack = nullptr;
  63. AzToolsFramework::EditorRequestBus::Handler::BusDisconnect();
  64. }
  65. AZStd::unique_ptr<ToolsTestApplication> PrefabTestFixture::CreateTestApplication()
  66. {
  67. return AZStd::make_unique<PrefabTestToolsApplication>("PrefabTestApplication");
  68. }
  69. void PrefabTestFixture::CreateRootPrefab()
  70. {
  71. m_prefabEditorEntityOwnershipInterface->CreateNewLevelPrefab("UnitTestRoot.prefab", "");
  72. InstanceOptionalReference rootInstance = m_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
  73. ASSERT_TRUE(rootInstance.has_value());
  74. EntityOptionalReference rootContainerEntity = rootInstance->get().GetContainerEntity();
  75. ASSERT_TRUE(rootContainerEntity.has_value());
  76. if (rootContainerEntity->get().GetState() == AZ::Entity::State::Constructed)
  77. {
  78. rootContainerEntity->get().Init();
  79. }
  80. // Focus on root prefab instance.
  81. auto* prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
  82. EXPECT_TRUE(prefabFocusPublicInterface != nullptr);
  83. PrefabFocusOperationResult focusResult = prefabFocusPublicInterface->FocusOnOwningPrefab(
  84. rootContainerEntity->get().GetId());
  85. EXPECT_TRUE(focusResult.IsSuccess());
  86. }
  87. void PrefabTestFixture::PropagateAllTemplateChanges()
  88. {
  89. m_prefabSystemComponent->OnSystemTick();
  90. }
  91. AZ::EntityId PrefabTestFixture::CreateEditorEntityUnderRoot(const AZStd::string& entityName)
  92. {
  93. return CreateEditorEntity(entityName, GetRootContainerEntityId());
  94. }
  95. AZ::EntityId PrefabTestFixture::CreateEditorEntity(const AZStd::string& entityName, AZ::EntityId parentId)
  96. {
  97. PrefabEntityResult createResult = m_prefabPublicInterface->CreateEntity(parentId, AZ::Vector3());
  98. AZ_Assert(createResult.IsSuccess(), "CreateEditorEntity - Failed to create entity %s. Error: %s",
  99. entityName.c_str(), createResult.GetError().c_str());
  100. // Verify new entity.
  101. AZ::EntityId newEntityId = createResult.GetValue();
  102. EXPECT_TRUE(newEntityId.IsValid());
  103. AZ::Entity* newEntity = AzToolsFramework::GetEntityById(newEntityId);
  104. EXPECT_TRUE(newEntity != nullptr);
  105. newEntity->SetName(entityName);
  106. m_prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(newEntityId, m_undoStack->GetTop());
  107. PropagateAllTemplateChanges();
  108. return newEntityId;
  109. }
  110. AZ::EntityId PrefabTestFixture::CreateEditorPrefab(AZ::IO::PathView filePath, const AzToolsFramework::EntityIdList& entityIds)
  111. {
  112. // New prefab instance is reparent under the common root entity of the input entities.
  113. CreatePrefabResult createResult = m_prefabPublicInterface->CreatePrefabInMemory(entityIds, filePath);
  114. AZ_Assert(createResult.IsSuccess(), "CreateEditorPrefab - Failed to create prefab %s. Error: %s",
  115. AZStd::string(filePath.Native()).c_str(),
  116. createResult.GetError().c_str());
  117. // Verify new container entity.
  118. AZ::EntityId prefabContainerId = createResult.GetValue();
  119. EXPECT_TRUE(prefabContainerId.IsValid());
  120. AZ::Entity* prefabContainerEntity = AzToolsFramework::GetEntityById(prefabContainerId);
  121. EXPECT_TRUE(prefabContainerEntity != nullptr);
  122. PropagateAllTemplateChanges();
  123. return prefabContainerId;
  124. }
  125. AZ::EntityId PrefabTestFixture::InstantiateEditorPrefab(AZ::IO::PathView filePath, AZ::EntityId parentId)
  126. {
  127. InstantiatePrefabResult instantiateResult = m_prefabPublicInterface->InstantiatePrefab(
  128. filePath.Native(), parentId, AZ::Vector3());
  129. AZ_Assert(instantiateResult.IsSuccess(), "InstantiateEditorPrefab - Failed to instantiate prefab %s. Error: %s",
  130. AZStd::string(filePath.Native()).c_str(),
  131. instantiateResult.GetError().c_str());
  132. // Verify new container entity.
  133. AZ::EntityId prefabContainerId = instantiateResult.GetValue();
  134. EXPECT_TRUE(prefabContainerId.IsValid());
  135. AZ::Entity* prefabContainerEntity = AzToolsFramework::GetEntityById(prefabContainerId);
  136. EXPECT_TRUE(prefabContainerEntity != nullptr);
  137. PropagateAllTemplateChanges();
  138. return prefabContainerId;
  139. }
  140. AZ::Entity* PrefabTestFixture::CreateEntity(const AZStd::string& entityName, bool shouldActivate)
  141. {
  142. AZ::Entity* newEntity = aznew AZ::Entity(entityName);
  143. if (shouldActivate)
  144. {
  145. newEntity->Init();
  146. newEntity->Activate();
  147. }
  148. return newEntity;
  149. }
  150. AZ::EntityId PrefabTestFixture::GetRootContainerEntityId()
  151. {
  152. auto rootInstance = m_prefabEditorEntityOwnershipInterface->GetRootPrefabInstance();
  153. EXPECT_TRUE(rootInstance.has_value());
  154. AZ::EntityId rootContainerId = rootInstance->get().GetContainerEntityId();
  155. EXPECT_TRUE(rootContainerId.IsValid());
  156. return rootContainerId;
  157. }
  158. void PrefabTestFixture::CompareInstances(const AzToolsFramework::Prefab::Instance& instanceA,
  159. const AzToolsFramework::Prefab::Instance& instanceB, bool shouldCompareLinkIds, bool shouldCompareContainerEntities)
  160. {
  161. AzToolsFramework::Prefab::TemplateId templateAId = instanceA.GetTemplateId();
  162. AzToolsFramework::Prefab::TemplateId templateBId = instanceB.GetTemplateId();
  163. ASSERT_TRUE(templateAId != AzToolsFramework::Prefab::InvalidTemplateId);
  164. ASSERT_TRUE(templateBId != AzToolsFramework::Prefab::InvalidTemplateId);
  165. EXPECT_EQ(templateAId, templateBId);
  166. AzToolsFramework::Prefab::TemplateReference templateA =
  167. m_prefabSystemComponent->FindTemplate(templateAId);
  168. ASSERT_TRUE(templateA.has_value());
  169. AzToolsFramework::Prefab::PrefabDom prefabDomA;
  170. ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceA, prefabDomA));
  171. AzToolsFramework::Prefab::PrefabDom prefabDomB;
  172. ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceB, prefabDomB));
  173. // Validate that both instances match when serialized.
  174. PrefabTestDomUtils::ComparePrefabDoms(prefabDomA, prefabDomB, shouldCompareLinkIds, shouldCompareContainerEntities);
  175. // Validate that the serialized instances match the shared template when serialized
  176. // Note: We do not compare the link ids. The template DOM is not supposed to have this member.
  177. PrefabTestDomUtils::ComparePrefabDoms(templateA->get().GetPrefabDom(), prefabDomB, false, shouldCompareContainerEntities);
  178. }
  179. void PrefabTestFixture::DeleteInstances(const InstanceList& instancesToDelete)
  180. {
  181. for (Instance* instanceToDelete : instancesToDelete)
  182. {
  183. ASSERT_TRUE(instanceToDelete);
  184. delete instanceToDelete;
  185. instanceToDelete = nullptr;
  186. }
  187. }
  188. EntityAlias PrefabTestFixture::FindEntityAliasInInstance(
  189. AZ::EntityId containerEntityId, const AZStd::string& entityName)
  190. {
  191. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  192. EXPECT_TRUE(owningInstance.has_value());
  193. EntityAlias foundEntityAlias = "";
  194. owningInstance->get().GetEntities(
  195. [&foundEntityAlias, &owningInstance, &entityName](AZStd::unique_ptr<AZ::Entity>& entity)
  196. {
  197. if (entity->GetName() == entityName)
  198. {
  199. auto entityAlias = owningInstance->get().GetEntityAlias(entity->GetId());
  200. EXPECT_TRUE(entityAlias.has_value()) << "FindEntityAliasInInstance - Retrieved entity alias is null.";
  201. foundEntityAlias = entityAlias->get();
  202. return false;
  203. }
  204. return true;
  205. });
  206. return foundEntityAlias;
  207. }
  208. InstanceAlias PrefabTestFixture::FindNestedInstanceAliasInInstance(
  209. AZ::EntityId containerEntityId, const AZStd::string& nestedContainerEntityName)
  210. {
  211. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  212. EXPECT_TRUE(owningInstance.has_value());
  213. InstanceAlias foundInstanceAlias = "";
  214. owningInstance->get().GetNestedInstances(
  215. [&foundInstanceAlias, &nestedContainerEntityName](AZStd::unique_ptr<Instance>& nestedInstance)
  216. {
  217. auto nestedContainerEntity = nestedInstance->GetContainerEntity();
  218. EXPECT_TRUE(nestedContainerEntity.has_value());
  219. if (nestedContainerEntity->get().GetName() == nestedContainerEntityName)
  220. {
  221. foundInstanceAlias = nestedInstance->GetInstanceAlias();
  222. return;
  223. }
  224. });
  225. return foundInstanceAlias;
  226. }
  227. void PrefabTestFixture::RenameEntity(AZ::EntityId entityId, const AZStd::string& newName)
  228. {
  229. ASSERT_FALSE(newName.empty()) << "Cannot rename an entity to empty string.";
  230. AZ::Entity* entityToRename = AzToolsFramework::GetEntityById(entityId);
  231. ASSERT_TRUE(entityToRename != nullptr) << "Cannot rename a null entity.";
  232. entityToRename->SetName(newName);
  233. m_prefabPublicInterface->GenerateUndoNodesForEntityChangeAndUpdateCache(entityId, m_undoStack->GetTop());
  234. PropagateAllTemplateChanges();
  235. }
  236. void PrefabTestFixture::ValidateEntityUnderInstance(
  237. AZ::EntityId containerEntityId, const EntityAlias& entityAlias, const AZStd::string& entityName)
  238. {
  239. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  240. EXPECT_TRUE(owningInstance.has_value());
  241. auto entity = owningInstance->get().GetEntity(entityAlias);
  242. ASSERT_TRUE(entity.has_value());
  243. ASSERT_EQ(entity->get().GetName(), entityName);
  244. }
  245. void PrefabTestFixture::ValidateEntityNotUnderInstance(
  246. AZ::EntityId containerEntityId, const EntityAlias& entityAlias)
  247. {
  248. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  249. EXPECT_TRUE(owningInstance.has_value());
  250. auto entity = owningInstance->get().GetEntity(entityAlias);
  251. ASSERT_FALSE(entity.has_value());
  252. }
  253. void PrefabTestFixture::ValidateNestedInstanceUnderInstance(
  254. AZ::EntityId containerEntityId, const InstanceAlias& nestedInstanceAlias)
  255. {
  256. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  257. EXPECT_TRUE(owningInstance.has_value());
  258. auto nestedInstance = owningInstance->get().FindNestedInstance(nestedInstanceAlias);
  259. ASSERT_TRUE(nestedInstance.has_value());
  260. }
  261. void PrefabTestFixture::ValidateNestedInstanceNotUnderInstance(
  262. AZ::EntityId containerEntityId, const InstanceAlias& nestedInstanceAlias)
  263. {
  264. auto owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(containerEntityId);
  265. EXPECT_TRUE(owningInstance.has_value());
  266. auto nestedInstance = owningInstance->get().FindNestedInstance(nestedInstanceAlias);
  267. ASSERT_FALSE(nestedInstance.has_value());
  268. }
  269. void PrefabTestFixture::ValidateInstanceEntitiesActive(Instance& instance)
  270. {
  271. AZStd::vector<AZ::EntityId> entityIdVector;
  272. instance.GetAllEntityIdsInHierarchy([&entityIdVector](AZ::EntityId entityId) {
  273. entityIdVector.push_back(entityId);
  274. return true;
  275. });
  276. for (AZ::EntityId entityId : entityIdVector)
  277. {
  278. AZ::Entity* entityInInstance = nullptr;
  279. AZ::ComponentApplicationBus::BroadcastResult(entityInInstance, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
  280. ASSERT_TRUE(entityInInstance);
  281. EXPECT_EQ(entityInInstance->GetState(), AZ::Entity::State::Active);
  282. }
  283. }
  284. void PrefabTestFixture::ProcessDeferredUpdates()
  285. {
  286. // Force a prefab propagation for updates that are deferred to the next tick.
  287. m_prefabSystemComponent->OnSystemTick();
  288. }
  289. void PrefabTestFixture::Undo()
  290. {
  291. m_undoStack->Undo();
  292. ProcessDeferredUpdates();
  293. }
  294. void PrefabTestFixture::Redo()
  295. {
  296. m_undoStack->Redo();
  297. ProcessDeferredUpdates();
  298. }
  299. void PrefabTestFixture::AddRequiredEditorComponents(const AzToolsFramework::EntityIdList& entityIds)
  300. {
  301. for (AZ::EntityId entityId : entityIds)
  302. {
  303. AZ::Entity* entity = nullptr;
  304. AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, entityId);
  305. EXPECT_TRUE(entity != nullptr) << "The entity to be added required editor components is nullptr.";
  306. entity->Deactivate();
  307. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  308. &AzToolsFramework::EditorEntityContextRequests::AddRequiredComponents, *entity);
  309. entity->Activate();
  310. }
  311. }
  312. // EditorRequestBus
  313. void PrefabTestFixture::CreateEditorRepresentation(AZ::Entity* entity)
  314. {
  315. if (!entity)
  316. {
  317. EXPECT_TRUE(false) << "Cannot call CreateEditorRepresentation for a null entity.";
  318. return;
  319. }
  320. AzToolsFramework::EditorEntityContextRequestBus::Broadcast(
  321. &AzToolsFramework::EditorEntityContextRequestBus::Events::AddRequiredComponents, *entity);
  322. }
  323. } // namespace UnitTest