PrefabDeleteAsOverrideTests.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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 <AzToolsFramework/Entity/EditorEntityHelpers.h>
  10. namespace UnitTest
  11. {
  12. using PrefabDeleteAsOverrideTests = PrefabTestFixture;
  13. TEST_F(PrefabDeleteAsOverrideTests, DeleteEntitiesAndAllDescendantsInInstance_DeleteSingleEntitySucceeds)
  14. {
  15. // Level <-- focused
  16. // | Car_1
  17. // | Tire <-- delete
  18. // | Car_2
  19. // | Tire
  20. const AZStd::string carPrefabName = "CarPrefab";
  21. const AZStd::string tireEntityName = "Tire";
  22. const AZStd::string firstCarName = "Car_1";
  23. const AZStd::string secondCarName = "Car_2";
  24. AZ::IO::Path engineRootPath;
  25. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  26. AZ::IO::Path carPrefabFilepath(engineRootPath);
  27. carPrefabFilepath.Append(carPrefabName);
  28. // Create and rename the first car.
  29. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  30. AZ::EntityId firstCarContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  31. EntityAlias tireEntityAlias = FindEntityAliasInInstance(firstCarContainerId, tireEntityName);
  32. RenameEntity(firstCarContainerId, firstCarName);
  33. // Create and rename the second car.
  34. AZ::EntityId secondCarContainerId = InstantiateEditorPrefab(carPrefabFilepath, GetRootContainerEntityId());
  35. RenameEntity(secondCarContainerId, secondCarName);
  36. // Delete the tire entity in the first car.
  37. // Note: Level root instance is focused by default.
  38. InstanceOptionalReference firstCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(firstCarContainerId);
  39. AZ::EntityId firstTireEntityId = firstCarInstance->get().GetEntityId(tireEntityAlias);
  40. InstanceOptionalReference secondCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(secondCarContainerId);
  41. AZ::EntityId secondTireEntityId = secondCarInstance->get().GetEntityId(tireEntityAlias);
  42. PrefabOperationResult result = m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance({ firstTireEntityId });
  43. ASSERT_TRUE(result.IsSuccess());
  44. // Validate that only the tire in the first car is deleted.
  45. AZ::Entity* firstTireEntity = AzToolsFramework::GetEntityById(firstTireEntityId);
  46. EXPECT_TRUE(firstTireEntity == nullptr);
  47. AZ::Entity* secondTireEntity = AzToolsFramework::GetEntityById(secondTireEntityId);
  48. EXPECT_TRUE(secondTireEntity != nullptr);
  49. ValidateEntityNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), tireEntityAlias);
  50. ValidateEntityUnderInstance(secondCarInstance->get().GetContainerEntityId(), tireEntityAlias, tireEntityName);
  51. }
  52. TEST_F(PrefabDeleteAsOverrideTests, DeleteEntitiesAndAllDescendantsInInstance_DeleteSinglePrefabSucceeds)
  53. {
  54. // Level <-- focused
  55. // | Car_1
  56. // | Wheel <-- delete
  57. // | Tire
  58. // | Car_2
  59. // | Wheel
  60. // | Tire
  61. const AZStd::string carPrefabName = "CarPrefab";
  62. const AZStd::string wheelPrefabName = "WheelPrefab";
  63. const AZStd::string tireEntityName = "Tire";
  64. const AZStd::string firstCarName = "Car_1";
  65. const AZStd::string secondCarName = "Car_2";
  66. AZ::IO::Path engineRootPath;
  67. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  68. AZ::IO::Path carPrefabFilepath(engineRootPath);
  69. carPrefabFilepath.Append(carPrefabName);
  70. AZ::IO::Path wheelPrefabFilepath(engineRootPath);
  71. wheelPrefabFilepath.Append(wheelPrefabName);
  72. // Create and rename the first car.
  73. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  74. AZ::EntityId wheelContainerId = CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  75. AZ::EntityId firstCarContainerId = CreateEditorPrefab(carPrefabFilepath, { wheelContainerId });
  76. RenameEntity(firstCarContainerId, firstCarName);
  77. // Create and rename the second car.
  78. AZ::EntityId secondCarContainerId = InstantiateEditorPrefab(carPrefabFilepath, GetRootContainerEntityId());
  79. RenameEntity(secondCarContainerId, secondCarName);
  80. InstanceAlias wheelInstanceAlias = FindNestedInstanceAliasInInstance(firstCarContainerId, wheelPrefabName);
  81. ASSERT_FALSE(wheelInstanceAlias.empty());
  82. // Delete the wheel instance in the first car.
  83. // Note: Level root instance is focused by default.
  84. InstanceOptionalReference firstCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(firstCarContainerId);
  85. AZ::EntityId firstWheelContainerId;
  86. firstCarInstance->get().GetNestedInstances(
  87. [&firstWheelContainerId, wheelInstanceAlias](AZStd::unique_ptr<Instance>& nestedInstance)
  88. {
  89. if (nestedInstance->GetInstanceAlias() == wheelInstanceAlias)
  90. {
  91. firstWheelContainerId = nestedInstance->GetContainerEntityId();
  92. return;
  93. }
  94. });
  95. ASSERT_TRUE(firstWheelContainerId.IsValid()) << "Cannot get wheel container entity id in the first car.";
  96. InstanceOptionalReference secondCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(secondCarContainerId);
  97. PrefabOperationResult result = m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance({ firstWheelContainerId });
  98. ASSERT_TRUE(result.IsSuccess());
  99. // Validate that only the wheel instance in the first car is deleted.
  100. AZ::Entity* firstWheelContainerEntity = AzToolsFramework::GetEntityById(firstWheelContainerId);
  101. EXPECT_TRUE(firstWheelContainerEntity == nullptr);
  102. ValidateNestedInstanceNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), wheelInstanceAlias);
  103. ValidateNestedInstanceUnderInstance(secondCarInstance->get().GetContainerEntityId(), wheelInstanceAlias);
  104. }
  105. TEST_F(PrefabDeleteAsOverrideTests, DeleteEntitiesAndAllDescendantsInInstance_DeletingEntityDeletesChildEntityToo)
  106. {
  107. // Level <-- focused
  108. // | Car_1
  109. // | Tire <-- delete
  110. // | ChildEntity
  111. // | Car_2
  112. // | Tire
  113. // | ChildEntity
  114. const AZStd::string carPrefabName = "CarPrefab";
  115. const AZStd::string tireEntityName = "Tire";
  116. const AZStd::string childEntityName = "ChildEntity";
  117. const AZStd::string firstCarName = "Car_1";
  118. const AZStd::string secondCarName = "Car_2";
  119. AZ::IO::Path engineRootPath;
  120. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  121. AZ::IO::Path carPrefabFilepath(engineRootPath);
  122. carPrefabFilepath.Append(carPrefabName);
  123. // Create and rename the first car.
  124. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  125. CreateEditorEntity(childEntityName, tireEntityId);
  126. AZ::EntityId firstCarContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  127. EntityAlias tireEntityAlias = FindEntityAliasInInstance(firstCarContainerId, tireEntityName);
  128. EntityAlias childEntityAlias = FindEntityAliasInInstance(firstCarContainerId, childEntityName);
  129. RenameEntity(firstCarContainerId, firstCarName);
  130. // Create and rename the second car.
  131. AZ::EntityId secondCarContainerId = InstantiateEditorPrefab(carPrefabFilepath, GetRootContainerEntityId());
  132. RenameEntity(secondCarContainerId, secondCarName);
  133. // Delete the tire entity in the first car.
  134. // Note: Level root instance is focused by default.
  135. InstanceOptionalReference firstCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(firstCarContainerId);
  136. AZ::EntityId firstTireEntityId = firstCarInstance->get().GetEntityId(tireEntityAlias);
  137. AZ::EntityId firstChildEntityId = firstCarInstance->get().GetEntityId(childEntityAlias);
  138. InstanceOptionalReference secondCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(secondCarContainerId);
  139. AZ::EntityId secondTireEntityId = secondCarInstance->get().GetEntityId(tireEntityAlias);
  140. AZ::EntityId secondChildEntityId = secondCarInstance->get().GetEntityId(childEntityAlias);
  141. PrefabOperationResult result = m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance({ firstTireEntityId });
  142. ASSERT_TRUE(result.IsSuccess());
  143. // Validate that only the tire and its child entity in the first car are deleted.
  144. AZ::Entity* firstTireEntity = AzToolsFramework::GetEntityById(firstTireEntityId);
  145. EXPECT_TRUE(firstTireEntity == nullptr);
  146. AZ::Entity* firstChildEntity = AzToolsFramework::GetEntityById(firstChildEntityId);
  147. EXPECT_TRUE(firstChildEntity == nullptr);
  148. ValidateEntityNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), tireEntityAlias);
  149. ValidateEntityNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), childEntityAlias);
  150. AZ::Entity* secondTireEntity = AzToolsFramework::GetEntityById(secondTireEntityId);
  151. EXPECT_TRUE(secondTireEntity != nullptr);
  152. AZ::Entity* secondChildEntity = AzToolsFramework::GetEntityById(secondChildEntityId);
  153. EXPECT_TRUE(secondChildEntity != nullptr);
  154. ValidateEntityUnderInstance(secondCarInstance->get().GetContainerEntityId(), tireEntityAlias, tireEntityName);
  155. ValidateEntityUnderInstance(secondCarInstance->get().GetContainerEntityId(), childEntityAlias, childEntityName);
  156. }
  157. TEST_F(PrefabDeleteAsOverrideTests, DeleteEntitiesAndAllDescendantsInInstance_DeletingEntityDeletesChildPrefabToo)
  158. {
  159. // Level <-- focused
  160. // | Car_1
  161. // | Tire <-- delete
  162. // | ChildPrefab
  163. // | ChildEntity
  164. // | Car_2
  165. // | Tire
  166. // | ChildPrefab
  167. // | ChildEntity
  168. const AZStd::string carPrefabName = "CarPrefab";
  169. const AZStd::string tireEntityName = "Tire";
  170. const AZStd::string childPrefabName = "ChildPrefab";
  171. const AZStd::string childEntityName = "ChildEntity";
  172. const AZStd::string firstCarName = "Car_1";
  173. const AZStd::string secondCarName = "Car_2";
  174. AZ::IO::Path engineRootPath;
  175. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  176. AZ::IO::Path carPrefabFilepath(engineRootPath);
  177. carPrefabFilepath.Append(carPrefabName);
  178. AZ::IO::Path childPrefabFilepath(engineRootPath);
  179. childPrefabFilepath.Append(childPrefabName);
  180. // Create and rename the first car.
  181. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  182. AZ::EntityId childEntityId = CreateEditorEntity(childEntityName, tireEntityId);
  183. CreateEditorPrefab(childPrefabFilepath, { childEntityId });
  184. AZ::EntityId firstCarContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  185. EntityAlias tireEntityAlias = FindEntityAliasInInstance(firstCarContainerId, tireEntityName);
  186. InstanceAlias childInstanceAlias = FindNestedInstanceAliasInInstance(firstCarContainerId, childPrefabName);
  187. RenameEntity(firstCarContainerId, firstCarName);
  188. // Create and rename the second car.
  189. AZ::EntityId secondCarContainerId = InstantiateEditorPrefab(carPrefabFilepath, GetRootContainerEntityId());
  190. RenameEntity(secondCarContainerId, secondCarName);
  191. // Delete the tire entity in the first car.
  192. // Note: Level root instance is focused by default.
  193. InstanceOptionalReference firstCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(firstCarContainerId);
  194. AZ::EntityId firstTireEntityId = firstCarInstance->get().GetEntityId(tireEntityAlias);
  195. AZ::EntityId firstChildContainerId;
  196. firstCarInstance->get().GetNestedInstances(
  197. [&firstChildContainerId, childInstanceAlias](AZStd::unique_ptr<Instance>& nestedInstance)
  198. {
  199. if (nestedInstance->GetInstanceAlias() == childInstanceAlias)
  200. {
  201. firstChildContainerId = nestedInstance->GetContainerEntityId();
  202. return;
  203. }
  204. });
  205. ASSERT_TRUE(firstChildContainerId.IsValid()) << "Cannot get child container entity id in the first car.";
  206. InstanceOptionalReference secondCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(secondCarContainerId);
  207. AZ::EntityId secondTireEntityId = secondCarInstance->get().GetEntityId(tireEntityAlias);
  208. AZ::EntityId secondChildContainerId;
  209. secondCarInstance->get().GetNestedInstances(
  210. [&secondChildContainerId, childInstanceAlias](AZStd::unique_ptr<Instance>& nestedInstance)
  211. {
  212. if (nestedInstance->GetInstanceAlias() == childInstanceAlias)
  213. {
  214. secondChildContainerId = nestedInstance->GetContainerEntityId();
  215. return;
  216. }
  217. });
  218. ASSERT_TRUE(secondChildContainerId.IsValid()) << "Cannot get child container entity id in the second car.";
  219. PrefabOperationResult result = m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance({ firstTireEntityId });
  220. ASSERT_TRUE(result.IsSuccess());
  221. // Validate that only the tire and its child prefab instance in the first car are deleted.
  222. AZ::Entity* firstTireEntity = AzToolsFramework::GetEntityById(firstTireEntityId);
  223. EXPECT_TRUE(firstTireEntity == nullptr);
  224. AZ::Entity* firstChildContainerEntity = AzToolsFramework::GetEntityById(firstChildContainerId);
  225. EXPECT_TRUE(firstChildContainerEntity == nullptr);
  226. ValidateEntityNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), tireEntityAlias);
  227. ValidateNestedInstanceNotUnderInstance(firstCarInstance->get().GetContainerEntityId(), childInstanceAlias);
  228. AZ::Entity* secondTireEntity = AzToolsFramework::GetEntityById(secondTireEntityId);
  229. EXPECT_TRUE(secondTireEntity != nullptr);
  230. AZ::Entity* secondChildContainerEntity = AzToolsFramework::GetEntityById(secondChildContainerId);
  231. EXPECT_TRUE(secondChildContainerEntity != nullptr);
  232. ValidateEntityUnderInstance(secondCarInstance->get().GetContainerEntityId(), tireEntityAlias, tireEntityName);
  233. ValidateNestedInstanceUnderInstance(secondCarInstance->get().GetContainerEntityId(), childInstanceAlias);
  234. }
  235. TEST_F(PrefabDeleteAsOverrideTests, DeleteEntitiesAndAllDescendantsInInstance_FocusOnDeletedPrefabFromRootSucceeds)
  236. {
  237. // Level <-- deletes Wheel instance as an override
  238. // | Car <-- focuses on Car to make Wheel available
  239. // | Wheel <-- focuses on Wheel
  240. // | Tire
  241. const AZStd::string carPrefabName = "CarPrefab";
  242. const AZStd::string wheelPrefabName = "WheelPrefab";
  243. const AZStd::string tireEntityName = "Tire";
  244. AZ::IO::Path engineRootPath;
  245. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  246. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  247. AZ::IO::Path wheelPrefabFilepath = engineRootPath / wheelPrefabName;
  248. // Create Car prefab hierarchy
  249. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  250. AZ::EntityId wheelContainerId = CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  251. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { wheelContainerId });
  252. // Find the Wheel instance under the Car instance
  253. InstanceAlias wheelInstanceAlias = FindNestedInstanceAliasInInstance(carContainerId, wheelPrefabName);
  254. ASSERT_FALSE(wheelInstanceAlias.empty());
  255. InstanceOptionalReference carInstance = m_instanceEntityMapperInterface->FindOwningInstance(carContainerId);
  256. AZ::EntityId wheelInstanceContainerId;
  257. carInstance->get().GetNestedInstances(
  258. [&wheelInstanceContainerId, wheelInstanceAlias](AZStd::unique_ptr<Instance>& nestedInstance)
  259. {
  260. if (nestedInstance->GetInstanceAlias() == wheelInstanceAlias)
  261. {
  262. wheelInstanceContainerId = nestedInstance->GetContainerEntityId();
  263. return;
  264. }
  265. });
  266. ASSERT_TRUE(wheelInstanceContainerId.IsValid()) << "Cannot get wheel container entity id in the car.";
  267. // Delete the Wheel instance. This adds an override on the level/root prefab
  268. PrefabOperationResult result = m_prefabPublicInterface->DeleteEntitiesAndAllDescendantsInInstance({ wheelInstanceContainerId });
  269. ASSERT_TRUE(result.IsSuccess());
  270. // Propagate changes after deleting the Wheel instance
  271. ProcessDeferredUpdates();
  272. // Validate that the Wheel instance is not under the Car instance
  273. ValidateNestedInstanceNotUnderInstance(carContainerId, wheelInstanceAlias);
  274. // Focus on the Car instance
  275. auto* prefabFocusPublicInterface = AZ::Interface<PrefabFocusPublicInterface>::Get();
  276. EXPECT_TRUE(prefabFocusPublicInterface != nullptr);
  277. PrefabFocusOperationResult focusResult = prefabFocusPublicInterface->FocusOnOwningPrefab(carContainerId);
  278. EXPECT_TRUE(focusResult.IsSuccess());
  279. // Propagate changes after the focus change
  280. ProcessDeferredUpdates();
  281. // Find the focused Car instance
  282. AzFramework::EntityContextId editorEntityContextId = AzToolsFramework::GetEntityContextId();
  283. AZ::EntityId focusedCarContainerId = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
  284. ASSERT_TRUE(focusedCarContainerId.IsValid()) << "Cannot get the focused instance.";
  285. // Validate that the Wheel instance exists again
  286. InstanceAlias wheelInstanceAliasInFocusedCar = FindNestedInstanceAliasInInstance(focusedCarContainerId, wheelPrefabName);
  287. EXPECT_TRUE(!wheelInstanceAliasInFocusedCar.empty());
  288. // Find the Wheel instance under the focused Car instance
  289. AZ::EntityId wheelInstanceContainerIdInFocusedCar;
  290. InstanceOptionalReference focusedCarInstance = m_instanceEntityMapperInterface->FindOwningInstance(focusedCarContainerId);
  291. focusedCarInstance->get().GetNestedInstances(
  292. [&wheelInstanceContainerIdInFocusedCar, wheelInstanceAliasInFocusedCar](AZStd::unique_ptr<Instance>& nestedInstance)
  293. {
  294. if (nestedInstance->GetInstanceAlias() == wheelInstanceAliasInFocusedCar)
  295. {
  296. wheelInstanceContainerIdInFocusedCar = nestedInstance->GetContainerEntityId();
  297. return;
  298. }
  299. });
  300. ASSERT_TRUE(wheelInstanceContainerIdInFocusedCar.IsValid()) << "Cannot get wheel container entity id after focusing on Car.";
  301. // Focus on the Wheel instance
  302. focusResult = prefabFocusPublicInterface->FocusOnOwningPrefab(wheelInstanceContainerIdInFocusedCar);
  303. EXPECT_TRUE(focusResult.IsSuccess());
  304. // Propagate changes after the focus change
  305. ProcessDeferredUpdates();
  306. // Verify that the parent of the wheel container entity is valid
  307. AZ::EntityId focusedWheelContainerId = prefabFocusPublicInterface->GetFocusedPrefabContainerEntityId(editorEntityContextId);
  308. AZ::EntityId parentEntityId;
  309. AZ::TransformBus::EventResult(parentEntityId, focusedWheelContainerId, &AZ::TransformInterface::GetParentId);
  310. EXPECT_TRUE(parentEntityId.IsValid());
  311. }
  312. } // namespace UnitTest