PrefabDetachPrefabTests.cpp 28 KB

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