PrefabDetachPrefabAndRemoveContainerTests.cpp 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577
  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. // this file is essentially a duplicate of PrefabDetachPrefabTests.cpp
  9. // but it calls the API which deletes the container.
  10. // This means that SOME, but not necessarily all, of the setup for each test is the same
  11. // But the actual outcome is very different for each test.
  12. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  13. #include <Prefab/PrefabTestFixture.h>
  14. namespace UnitTest
  15. {
  16. using PrefabDetachPrefabTests = PrefabTestFixture;
  17. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityUnderLevelSucceeds)
  18. {
  19. // Level
  20. // | Car (prefab) <-- detach prefab
  21. // | Tire
  22. // | Belt
  23. // detaching removes the wrapper prefab object, so result is just
  24. // Level
  25. // | Tire
  26. // | Belt
  27. const AZStd::string carPrefabName = "CarPrefab";
  28. const AZStd::string tireEntityName = "Tire";
  29. const AZStd::string beltEntityName = "Belt";
  30. AZ::IO::Path engineRootPath;
  31. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  32. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  33. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  34. CreateEditorEntity(beltEntityName, tireEntityId);
  35. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  36. InstanceAlias carInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  37. // Detach the car prefab.
  38. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  39. ASSERT_TRUE(result.IsSuccess());
  40. PropagateAllTemplateChanges();
  41. // Validate there is no nested instance in the level prefab instance.
  42. ValidateNestedInstanceNotUnderInstance(GetRootContainerEntityId(), carInstanceAlias);
  43. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  44. ASSERT_TRUE(levelInstance.has_value());
  45. // Validate there are two entities in the level prefab instance (Tire, Belt)
  46. EXPECT_EQ(levelInstance->get().GetEntityAliasCount(), 2);
  47. // Validate that the car entity (the prefab wrapper) does not exist.
  48. AZStd::string carEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  49. EXPECT_STREQ(carEntityAliasAfterDetach.c_str(), "");
  50. // Validate that the tire's parent entity is the level container entity.
  51. AZStd::string tireEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), tireEntityName);
  52. AZ::EntityId tireEntityIdAfterDetach = levelInstance->get().GetEntityId(tireEntityAliasAfterDetach);
  53. EXPECT_TRUE(tireEntityIdAfterDetach.IsValid());
  54. AZ::EntityId parentEntityIdForTire;
  55. AZ::TransformBus::EventResult(parentEntityIdForTire, tireEntityIdAfterDetach, &AZ::TransformInterface::GetParentId);
  56. EXPECT_EQ(levelInstance->get().GetContainerEntityId(), parentEntityIdForTire);
  57. // Validate that the belt's parent entity is the tire.
  58. AZStd::string beltEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), beltEntityName);
  59. AZ::EntityId beltEntityIdAfterDetach = levelInstance->get().GetEntityId(beltEntityAliasAfterDetach);
  60. EXPECT_TRUE(beltEntityIdAfterDetach.IsValid());
  61. AZ::EntityId parentEntityIdForBelt;
  62. AZ::TransformBus::EventResult(parentEntityIdForBelt, beltEntityIdAfterDetach, &AZ::TransformInterface::GetParentId);
  63. EXPECT_EQ(tireEntityIdAfterDetach, parentEntityIdForBelt);
  64. }
  65. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityUnderParentSucceeds)
  66. {
  67. // Level
  68. // | Garage
  69. // | Car (prefab) <-- detach prefab
  70. // | Tire
  71. // expected result
  72. // Level
  73. // | Garage
  74. // | Tire (car is gone)
  75. const AZStd::string carPrefabName = "CarPrefab";
  76. const AZStd::string garageEntityName = "Garage";
  77. const AZStd::string tireEntityName = "Tire";
  78. AZ::IO::Path engineRootPath;
  79. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  80. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  81. AZ::EntityId garageEntityId = CreateEditorEntityUnderRoot(garageEntityName);
  82. AZ::EntityId tireEntityId = CreateEditorEntity(tireEntityName, garageEntityId);
  83. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  84. InstanceAlias carInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  85. // Detach the car prefab.
  86. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  87. ASSERT_TRUE(result.IsSuccess());
  88. PropagateAllTemplateChanges();
  89. // Validate there is no nested instance in the level prefab instance.
  90. ValidateNestedInstanceNotUnderInstance(GetRootContainerEntityId(), carInstanceAlias);
  91. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  92. ASSERT_TRUE(levelInstance.has_value());
  93. // Validate there are two entities in the level prefab instance (the car should be gone)
  94. EXPECT_EQ(levelInstance->get().GetEntityAliasCount(), 2);
  95. // Validate that the garage's parent entity is the level container entity.
  96. AZStd::string garageEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), garageEntityName);
  97. AZ::EntityId garageEntityIdAfterDetach = levelInstance->get().GetEntityId(garageEntityAliasAfterDetach);
  98. EXPECT_TRUE(garageEntityIdAfterDetach.IsValid());
  99. AZ::EntityId parentEntityIdForGarage;
  100. AZ::TransformBus::EventResult(parentEntityIdForGarage, garageEntityIdAfterDetach, &AZ::TransformInterface::GetParentId);
  101. EXPECT_EQ(levelInstance->get().GetContainerEntityId(), parentEntityIdForGarage);
  102. // Validate that the car is gone
  103. AZStd::string carEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  104. AZ::EntityId carEntityIdAfterDetach = levelInstance->get().GetEntityId(carEntityAliasAfterDetach);
  105. EXPECT_FALSE(carEntityIdAfterDetach.IsValid());
  106. // Validate that the tire's parent entity is the garage.
  107. AZStd::string tireEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), tireEntityName);
  108. AZ::EntityId tireEntityIdAfterDetach = levelInstance->get().GetEntityId(tireEntityAliasAfterDetach);
  109. EXPECT_TRUE(tireEntityIdAfterDetach.IsValid());
  110. AZ::EntityId parentEntityIdForTire;
  111. AZ::TransformBus::EventResult(parentEntityIdForTire, tireEntityIdAfterDetach, &AZ::TransformInterface::GetParentId);
  112. EXPECT_EQ(garageEntityIdAfterDetach, parentEntityIdForTire);
  113. }
  114. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityWithNestedPrefabSucceeds)
  115. {
  116. // Level
  117. // | Car (prefab) <-- detach prefab
  118. // | Wheel (prefab)
  119. // | Tire
  120. // expected result
  121. // Level
  122. // | Wheel (prefab), car is gone
  123. // | Tire
  124. const AZStd::string carPrefabName = "CarPrefab";
  125. const AZStd::string wheelPrefabName = "WheelPrefab";
  126. const AZStd::string tireEntityName = "Tire";
  127. AZ::IO::Path engineRootPath;
  128. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  129. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  130. AZ::IO::Path wheelPrefabFilepath = engineRootPath / wheelPrefabName;
  131. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  132. AZ::EntityId wheelContainerId = CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  133. EntityAlias tireEntityAlias = FindEntityAliasInInstance(wheelContainerId, tireEntityName);
  134. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { wheelContainerId });
  135. InstanceAlias carInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  136. // Detach the car prefab.
  137. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  138. ASSERT_TRUE(result.IsSuccess());
  139. PropagateAllTemplateChanges();
  140. // Validate there is no car instance in the level prefab instance.
  141. ValidateNestedInstanceNotUnderInstance(GetRootContainerEntityId(), carInstanceAlias);
  142. // Validate there is wheel instance in the level prefab instance.
  143. InstanceAlias wheelInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), wheelPrefabName);
  144. ValidateNestedInstanceUnderInstance(GetRootContainerEntityId(), wheelInstanceAlias);
  145. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  146. ASSERT_TRUE(levelInstance.has_value());
  147. AZStd::vector<InstanceOptionalReference> nestedInstances;
  148. levelInstance->get().GetNestedInstances(
  149. [&nestedInstances](AZStd::unique_ptr<Instance>& nestedInstance)
  150. {
  151. nestedInstances.push_back(*(nestedInstance.get()));
  152. });
  153. EXPECT_EQ(nestedInstances.size(), 1) << "There should be only one nested instance in level after detaching.";
  154. EXPECT_TRUE(nestedInstances[0].has_value());
  155. // Validate that the car prefab is gone
  156. AZStd::string carEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  157. AZ::EntityId carEntityIdAfterDetach = levelInstance->get().GetEntityId(carEntityAliasAfterDetach);
  158. EXPECT_FALSE(carEntityIdAfterDetach.IsValid());
  159. // Validate that the wheel's parent entity is the level.
  160. Instance& wheelInstanceAfterDetach = nestedInstances[0]->get();
  161. AZ::EntityId wheelContainerIdAfterDetach = wheelInstanceAfterDetach.GetContainerEntityId();
  162. EXPECT_TRUE(wheelContainerIdAfterDetach.IsValid());
  163. AZ::EntityId parentEntityIdForWheel;
  164. AZ::TransformBus::EventResult(parentEntityIdForWheel, wheelContainerIdAfterDetach, &AZ::TransformInterface::GetParentId);
  165. EXPECT_EQ(levelInstance->get().GetContainerEntityId(), parentEntityIdForWheel);
  166. // Validate that the tire's parent entity is the wheel.
  167. tireEntityId = wheelInstanceAfterDetach.GetEntityId(tireEntityAlias);
  168. EXPECT_TRUE(tireEntityId.IsValid());
  169. AZ::EntityId parentEntityIdForTire;
  170. AZ::TransformBus::EventResult(parentEntityIdForTire, tireEntityId, &AZ::TransformInterface::GetParentId);
  171. EXPECT_EQ(wheelContainerIdAfterDetach, parentEntityIdForTire);
  172. }
  173. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityWithNestedPrefabUnderTopLevelEntitySucceeds)
  174. {
  175. // Level
  176. // | Car (prefab) <-- detach prefab
  177. // | Wheels <-- top level entity
  178. // | Wheel (prefab)
  179. // | Tire
  180. // result (car is gone)
  181. // Level
  182. // | Wheels <-- top level entity
  183. // | Wheel (prefab)
  184. // | Tire
  185. const AZStd::string carPrefabName = "CarPrefab";
  186. const AZStd::string wheelPrefabName = "WheelPrefab";
  187. const AZStd::string wheelsEntityName = "Wheels";
  188. const AZStd::string tireEntityName = "Tire";
  189. AZ::IO::Path engineRootPath;
  190. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  191. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  192. AZ::IO::Path wheelPrefabFilepath = engineRootPath / wheelPrefabName;
  193. // Create the wheels and tire entities.
  194. AZ::EntityId wheelsEntityId = CreateEditorEntityUnderRoot(wheelsEntityName);
  195. AZ::EntityId tireEntityId = CreateEditorEntity(tireEntityName, wheelsEntityId);
  196. // Create the wheel prefab.
  197. AZ::EntityId wheelContainerId = CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  198. EntityAlias tireEntityAlias = FindEntityAliasInInstance(wheelContainerId, tireEntityName);
  199. // Create the car prefab.
  200. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { wheelsEntityId });
  201. InstanceAlias carInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  202. // Detach the car prefab.
  203. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  204. ASSERT_TRUE(result.IsSuccess());
  205. PropagateAllTemplateChanges();
  206. // Validate there is no car instance in the level prefab instance.
  207. ValidateNestedInstanceNotUnderInstance(GetRootContainerEntityId(), carInstanceAlias);
  208. // Validate there is wheels entity in the level prefab instance.
  209. EntityAlias wheelsEntityAlias = FindEntityAliasInInstance(GetRootContainerEntityId(), wheelsEntityName);
  210. ValidateEntityUnderInstance(GetRootContainerEntityId(), wheelsEntityAlias, wheelsEntityName);
  211. // Validate there is wheel instance in the level prefab instance.
  212. InstanceAlias wheelInstanceAlias = FindNestedInstanceAliasInInstance(GetRootContainerEntityId(), wheelPrefabName);
  213. ValidateNestedInstanceUnderInstance(GetRootContainerEntityId(), wheelInstanceAlias);
  214. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  215. ASSERT_TRUE(levelInstance.has_value());
  216. AZStd::vector<InstanceOptionalReference> nestedInstances;
  217. levelInstance->get().GetNestedInstances(
  218. [&nestedInstances](AZStd::unique_ptr<Instance>& nestedInstance)
  219. {
  220. nestedInstances.push_back(*(nestedInstance.get()));
  221. });
  222. EXPECT_EQ(nestedInstances.size(), 1) << "There should be only one nested instance in level after detaching.";
  223. EXPECT_TRUE(nestedInstances[0].has_value());
  224. // Validate that the car is gone
  225. AZStd::string carEntityAliasAfterDetach = FindEntityAliasInInstance(GetRootContainerEntityId(), carPrefabName);
  226. AZ::EntityId carEntityIdAfterDetach = levelInstance->get().GetEntityId(carEntityAliasAfterDetach);
  227. EXPECT_FALSE(carEntityIdAfterDetach.IsValid());
  228. // Validate that the wheels' parent entity is the level instance.
  229. AZ::EntityId wheelsEntityIdAfterDetach = levelInstance->get().GetEntityId(wheelsEntityAlias);
  230. EXPECT_TRUE(wheelsEntityIdAfterDetach.IsValid());
  231. AZ::EntityId parentEntityIdForWheels;
  232. AZ::TransformBus::EventResult(parentEntityIdForWheels, wheelsEntityIdAfterDetach, &AZ::TransformInterface::GetParentId);
  233. EXPECT_EQ(levelInstance->get().GetContainerEntityId(), parentEntityIdForWheels);
  234. // Validate that the wheel prefab's container entity is the "wheels" entity.
  235. Instance& wheelInstanceAfterDetach = nestedInstances[0]->get();
  236. AZ::EntityId wheelContainerIdAfterDetach = wheelInstanceAfterDetach.GetContainerEntityId();
  237. EXPECT_TRUE(wheelContainerIdAfterDetach.IsValid());
  238. AZ::EntityId parentEntityIdForWheel;
  239. AZ::TransformBus::EventResult(parentEntityIdForWheel, wheelContainerIdAfterDetach, &AZ::TransformInterface::GetParentId);
  240. EXPECT_EQ(wheelsEntityIdAfterDetach, parentEntityIdForWheel);
  241. }
  242. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityValidatesDetachedContainerEntityOrder)
  243. {
  244. // Validate the detached container entity's sort order in its parent.
  245. // The detached container entity should not be moved to the beginning or end of the child entity list.
  246. //
  247. // Level
  248. // | Station
  249. // | Car (prefab) <-- detach prefab
  250. // | Tire
  251. // | House
  252. // result (car is gone)
  253. // The detached container entity should not be moved to the beginning or end of the child entity list.
  254. //
  255. // Level
  256. // | Station
  257. // | Tire
  258. // | House
  259. const AZStd::string carPrefabName = "CarPrefab";
  260. const AZStd::string tireEntityName = "Tire";
  261. const AZStd::string stationEntityName = "Station";
  262. const AZStd::string houseEntityName = "House";
  263. AZ::IO::Path engineRootPath;
  264. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  265. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  266. CreateEditorEntityUnderRoot(stationEntityName);
  267. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  268. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { tireEntityId });
  269. CreateEditorEntityUnderRoot(houseEntityName);
  270. // Validate child entity order before detaching the car prefab.
  271. AzToolsFramework::EntityOrderArray entityOrderArrayBeforeDetach =
  272. AzToolsFramework::GetEntityChildOrder(GetRootContainerEntityId());
  273. EXPECT_EQ(entityOrderArrayBeforeDetach.size(), 3);
  274. AZStd::string childEntityName;
  275. AZ::ComponentApplicationBus::BroadcastResult(
  276. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[0]);
  277. EXPECT_EQ(childEntityName, stationEntityName);
  278. AZ::ComponentApplicationBus::BroadcastResult(
  279. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[1]);
  280. EXPECT_EQ(childEntityName, carPrefabName);
  281. AZ::ComponentApplicationBus::BroadcastResult(
  282. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[2]);
  283. EXPECT_EQ(childEntityName, houseEntityName);
  284. // Detach the car prefab.
  285. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  286. ASSERT_TRUE(result.IsSuccess());
  287. PropagateAllTemplateChanges();
  288. // Validate child entity order after detaching the car prefab.
  289. AzToolsFramework::EntityOrderArray entityOrderArrayAfterDetach =
  290. AzToolsFramework::GetEntityChildOrder(GetRootContainerEntityId());
  291. EXPECT_EQ(entityOrderArrayAfterDetach.size(), 3);
  292. childEntityName = "";
  293. AZ::ComponentApplicationBus::BroadcastResult(
  294. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[0]);
  295. EXPECT_EQ(childEntityName, stationEntityName);
  296. AZ::ComponentApplicationBus::BroadcastResult(
  297. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[1]);
  298. EXPECT_EQ(childEntityName, tireEntityName);
  299. AZ::ComponentApplicationBus::BroadcastResult(
  300. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[2]);
  301. EXPECT_EQ(childEntityName, houseEntityName);
  302. }
  303. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityValidatesDetachedChildEntityOrder)
  304. {
  305. // Validate the sort order of top-level child entities.
  306. // Level
  307. // Car (prefab) child 0
  308. // Engine
  309. // Wheel (prefab)
  310. // Tire
  311. // Battery
  312. // expected result (car is gone)
  313. // Level
  314. // Engine child 0
  315. // Wheel (prefab) child 1
  316. // Tire
  317. // Battery child 2
  318. const AZStd::string carPrefabName = "CarPrefab";
  319. const AZStd::string wheelPrefabName = "WheelPrefab";
  320. const AZStd::string tireEntityName = "Tire";
  321. const AZStd::string engineEntityName = "Engine";
  322. const AZStd::string batteryEntityName = "Battery";
  323. AZ::IO::Path engineRootPath;
  324. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  325. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  326. AZ::IO::Path wheelPrefabFilepath = engineRootPath / wheelPrefabName;
  327. AZ::EntityId engineEntityId = CreateEditorEntityUnderRoot(engineEntityName);
  328. AZ::EntityId tireEntityId = CreateEditorEntityUnderRoot(tireEntityName);
  329. AZ::EntityId wheelContainerId = CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  330. AZ::EntityId batteryEntityId = CreateEditorEntityUnderRoot(batteryEntityName);
  331. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { engineEntityId, wheelContainerId, batteryEntityId });
  332. // Validate child entity order under car before detaching the car prefab.
  333. AzToolsFramework::EntityOrderArray entityOrderArrayBeforeDetach = AzToolsFramework::GetEntityChildOrder(carContainerId);
  334. EXPECT_EQ(entityOrderArrayBeforeDetach.size(), 3);
  335. AZStd::string childEntityName;
  336. AZ::ComponentApplicationBus::BroadcastResult(
  337. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[0]);
  338. EXPECT_EQ(childEntityName, engineEntityName);
  339. AZ::ComponentApplicationBus::BroadcastResult(
  340. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[1]);
  341. EXPECT_EQ(childEntityName, wheelPrefabName);
  342. AZ::ComponentApplicationBus::BroadcastResult(
  343. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[2]);
  344. EXPECT_EQ(childEntityName, batteryEntityName);
  345. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  346. ASSERT_TRUE(levelInstance.has_value());
  347. // before we detach, the level should contain 1 entity (the container entity) and 1 instance (of the car)
  348. int count = 0;
  349. levelInstance->get().GetEntityIds([&count](AZ::EntityId) { count++; return true; });
  350. EXPECT_EQ(count, 1); // expect 1 real entity, that is, the container for the car.
  351. count = 0;
  352. levelInstance->get().GetNestedInstances([&count](AZStd::unique_ptr<Instance>&) { count++; });
  353. EXPECT_EQ(count, 1); // expect 1 instance of another prefab (The the car)
  354. // Detach the car prefab.
  355. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  356. ASSERT_TRUE(result.IsSuccess());
  357. PropagateAllTemplateChanges();
  358. // after we detach, the level should contain 3 entities (2 of them real entities, one of them an instance container)
  359. count = 0;
  360. levelInstance->get().GetEntityIds([&count](const AZ::EntityId&) { count++; return true; });
  361. EXPECT_EQ(count, 3); // expect 2 REAL entities.
  362. count = 0;
  363. levelInstance->get().GetNestedInstances([&count](AZStd::unique_ptr<Instance>&) { count++; });
  364. EXPECT_EQ(count, 1); // expect 1 instance of another prefab (the wheel)
  365. // Validate child entity order under the level after detaching the car prefab. Should be engine, wheel, battery.
  366. AzToolsFramework::EntityOrderArray entityOrderArrayAfterDetach =
  367. AzToolsFramework::GetEntityChildOrder(levelInstance->get().GetContainerEntityId());
  368. EXPECT_EQ(entityOrderArrayAfterDetach.size(), 3);
  369. childEntityName = "";
  370. AZ::ComponentApplicationBus::BroadcastResult(childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[0]);
  371. EXPECT_EQ(childEntityName, engineEntityName);
  372. AZ::ComponentApplicationBus::BroadcastResult(childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[1]);
  373. EXPECT_EQ(childEntityName, wheelPrefabName);
  374. AZ::ComponentApplicationBus::BroadcastResult(childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[2]);
  375. EXPECT_EQ(childEntityName, batteryEntityName);
  376. }
  377. TEST_F(PrefabDetachPrefabTests, DetachPrefabAndRemoveContainerEntityValidatesTopLevelChildEntityOrder)
  378. {
  379. // Validate the sort order of child entities and prefabs that are under the top level entity.
  380. //
  381. // Level
  382. // | Car (prefab) <-- detach prefab
  383. // | Wheels <-- top level entity
  384. // | Red_Wheel
  385. // | Wheel (prefab)
  386. // | Tire
  387. // | Black_Wheel
  388. //
  389. // expected result (car is gone)
  390. // Level
  391. // | Wheels <-- top level entity
  392. // | Red_Wheel
  393. // | Wheel (prefab)
  394. // | Tire
  395. // | Black_Wheel
  396. const AZStd::string carPrefabName = "CarPrefab";
  397. const AZStd::string wheelPrefabName = "WheelPrefab";
  398. const AZStd::string wheelsEntityName = "Wheels";
  399. const AZStd::string redWheelEntityName = "Red_Wheel";
  400. const AZStd::string blackWheelEntityName = "Black_Wheel";
  401. const AZStd::string tireEntityName = "Tire";
  402. AZ::IO::Path engineRootPath;
  403. m_settingsRegistryInterface->Get(engineRootPath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  404. AZ::IO::Path carPrefabFilepath = engineRootPath / carPrefabName;
  405. AZ::IO::Path wheelPrefabFilepath = engineRootPath / wheelPrefabName;
  406. // Create the wheels, red wheel and tire entities.
  407. AZ::EntityId wheelsEntityId = CreateEditorEntityUnderRoot(wheelsEntityName);
  408. CreateEditorEntity(redWheelEntityName, wheelsEntityId);
  409. AZ::EntityId tireEntityId = CreateEditorEntity(tireEntityName, wheelsEntityId);
  410. // Create the wheel prefab.
  411. CreateEditorPrefab(wheelPrefabFilepath, { tireEntityId });
  412. // Create the black wheel entity.
  413. CreateEditorEntity(blackWheelEntityName, wheelsEntityId);
  414. // Create the car prefab.
  415. AZ::EntityId carContainerId = CreateEditorPrefab(carPrefabFilepath, { wheelsEntityId });
  416. InstanceOptionalReference levelInstance = m_instanceEntityMapperInterface->FindOwningInstance(GetRootContainerEntityId());
  417. ASSERT_TRUE(levelInstance.has_value());
  418. // Validate child entity order under wheels before detaching the car prefab.
  419. EntityAlias wheelsEntityAlias = FindEntityAliasInInstance(carContainerId, wheelsEntityName);
  420. InstanceOptionalReference carInstance = m_instanceEntityMapperInterface->FindOwningInstance(carContainerId);
  421. EXPECT_TRUE(carInstance.has_value());
  422. wheelsEntityId = carInstance->get().GetEntityId(wheelsEntityAlias);
  423. AzToolsFramework::EntityOrderArray entityOrderArrayBeforeDetach = AzToolsFramework::GetEntityChildOrder(wheelsEntityId);
  424. EXPECT_EQ(entityOrderArrayBeforeDetach.size(), 3);
  425. AZStd::string childEntityName;
  426. AZ::ComponentApplicationBus::BroadcastResult(
  427. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[0]);
  428. EXPECT_EQ(childEntityName, redWheelEntityName);
  429. AZ::ComponentApplicationBus::BroadcastResult(
  430. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[1]);
  431. EXPECT_EQ(childEntityName, wheelPrefabName);
  432. AZ::ComponentApplicationBus::BroadcastResult(
  433. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayBeforeDetach[2]);
  434. EXPECT_EQ(childEntityName, blackWheelEntityName);
  435. // Detach the car prefab.
  436. PrefabOperationResult result = m_prefabPublicInterface->DetachPrefabAndRemoveContainerEntity(carContainerId);
  437. ASSERT_TRUE(result.IsSuccess());
  438. PropagateAllTemplateChanges();
  439. // Validate child entity order under wheels after detaching the car prefab.
  440. wheelsEntityAlias = FindEntityAliasInInstance(levelInstance->get().GetContainerEntityId(), wheelsEntityName);
  441. wheelsEntityId = levelInstance->get().GetEntityId(wheelsEntityAlias);
  442. AzToolsFramework::EntityOrderArray entityOrderArrayAfterDetach = AzToolsFramework::GetEntityChildOrder(wheelsEntityId);
  443. EXPECT_EQ(entityOrderArrayAfterDetach.size(), 3);
  444. AZ::ComponentApplicationBus::BroadcastResult(
  445. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[0]);
  446. EXPECT_EQ(childEntityName, redWheelEntityName);
  447. AZ::ComponentApplicationBus::BroadcastResult(
  448. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[1]);
  449. EXPECT_EQ(childEntityName, wheelPrefabName);
  450. AZ::ComponentApplicationBus::BroadcastResult(
  451. childEntityName, &AZ::ComponentApplicationRequests::GetEntityName, entityOrderArrayAfterDetach[2]);
  452. EXPECT_EQ(childEntityName, blackWheelEntityName);
  453. }
  454. } // namespace UnitTest