3
0

PrefabTestFixture.cpp 17 KB

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