EntityInspectorTests.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586
  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. /*
  9. * Copyright (c) Contributors to the Open 3D Engine Project.
  10. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  11. *
  12. * SPDX-License-Identifier: Apache-2.0 OR MIT
  13. *
  14. */
  15. // Test Environment
  16. #include <AzCore/UnitTest/TestTypes.h>
  17. #include <AzCore/Component/Component.h>
  18. #include <AzCore/Serialization/SerializeContext.h>
  19. #include <AzCore/UserSettings/UserSettingsComponent.h>
  20. #include <AzToolsFramework/Application/ToolsApplication.h>
  21. #include <AzToolsFramework/API/ToolsApplicationAPI.h>
  22. #include <AzToolsFramework/Entity/EditorEntityContextBus.h>
  23. #include <AzToolsFramework/Entity/EditorEntityHelpers.h>
  24. #include <AzToolsFramework/ToolsComponents/EditorInspectorComponent.h>
  25. #include <AzToolsFramework/UnitTest/ToolsTestApplication.h>
  26. #include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
  27. // Inspector Test Includes
  28. #include <AzToolsFramework/UI/ComponentPalette/ComponentPaletteUtil.hxx>
  29. #include <gmock/gmock.h>
  30. namespace UnitTest
  31. {
  32. // Test component that is NOT available for a user to interact with
  33. // It does not appear in the Add Component menu in the Editor
  34. // It is not a system or game component
  35. class Inspector_TestComponent1
  36. : public AZ::Component
  37. {
  38. public:
  39. AZ_COMPONENT(Inspector_TestComponent1, "{BD25A077-DF38-4B67-BEA5-F4587A747A36}", AZ::Component);
  40. static void Reflect(AZ::ReflectContext* context)
  41. {
  42. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  43. {
  44. serializeContext->Class<Inspector_TestComponent1, AZ::Component>()
  45. ->Field("Data", &Inspector_TestComponent1::m_data)
  46. ;
  47. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  48. {
  49. editContext->Class<Inspector_TestComponent1>("InspectorTestComponent1", "Component 1 for AZ Tools Framework Unit Tests")
  50. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  51. ->Attribute(AZ::Edit::Attributes::AddableByUser, false)
  52. ->Attribute(AZ::Edit::Attributes::Visibility, AZ::Edit::PropertyVisibility::Hide)
  53. ->Attribute(AZ::Edit::Attributes::SliceFlags, AZ::Edit::SliceFlags::NotPushable)
  54. ->Attribute(AZ::Edit::Attributes::HideIcon, true);
  55. }
  56. }
  57. }
  58. static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  59. {
  60. services.push_back(AZ_CRC_CE("InspectorTestService1"));
  61. }
  62. static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  63. {
  64. services.push_back(AZ_CRC_CE("InspectorTestService1"));
  65. }
  66. ~Inspector_TestComponent1() override
  67. {
  68. }
  69. void SetData(int data)
  70. {
  71. m_data = data;
  72. };
  73. int GetData()
  74. {
  75. return m_data;
  76. }
  77. private:
  78. void Init() override
  79. {}
  80. void Activate() override
  81. {}
  82. void Deactivate() override
  83. {}
  84. /// Whether this entity is locked
  85. int m_data = 0;
  86. };
  87. // Test component that IS available for a user to interact with
  88. // It does appear in the Add Component menu in the editor and is a game component
  89. class Inspector_TestComponent2
  90. : public AZ::Component
  91. {
  92. public:
  93. AZ_COMPONENT(Inspector_TestComponent2, "{57D1C818-FD31-4FCD-A4DB-705EABF4E98B}", AZ::Component);
  94. static void Reflect(AZ::ReflectContext* context)
  95. {
  96. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  97. {
  98. serializeContext->Class<Inspector_TestComponent2, AZ::Component>()
  99. ->Field("Data", &Inspector_TestComponent2::m_data)
  100. ;
  101. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  102. {
  103. editContext->Class<Inspector_TestComponent2>("InspectorTestComponent2", "Component 2 for AZ Tools Framework Unit Tests")
  104. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  105. ->Attribute(AZ::Edit::Attributes::AddableByUser, true)
  106. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  107. ->Attribute(AZ::Edit::Attributes::Category, "Inspector Test Components")
  108. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Tag.png")
  109. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Tag.png")
  110. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  111. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components")
  112. ->DataElement(AZ::Edit::UIHandlers::Default, &Inspector_TestComponent2::m_data, "Data", "The component's Data");
  113. }
  114. }
  115. }
  116. static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  117. {
  118. services.push_back(AZ_CRC_CE("InspectorTestService2"));
  119. }
  120. static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  121. {
  122. services.push_back(AZ_CRC_CE("InspectorTestService2"));
  123. }
  124. ~Inspector_TestComponent2() override
  125. {
  126. }
  127. void SetData(int data)
  128. {
  129. m_data = data;
  130. };
  131. int GetData()
  132. {
  133. return m_data;
  134. }
  135. private:
  136. void Init() override
  137. {}
  138. void Activate() override
  139. {}
  140. void Deactivate() override
  141. {}
  142. /// Whether this entity is locked
  143. int m_data = 0;
  144. };
  145. // Test component that IS available for a user to interact with
  146. // It does appear in an Add Component menu and is a system component
  147. class Inspector_TestComponent3
  148. : public AZ::Component
  149. {
  150. public:
  151. AZ_COMPONENT(Inspector_TestComponent3, "{552CCFB1-135E-4B02-A492-25A3BBDFA381}", AZ::Component);
  152. static void Reflect(AZ::ReflectContext* context)
  153. {
  154. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  155. {
  156. serializeContext->Class<Inspector_TestComponent3, AZ::Component>()
  157. ->Field("Data", &Inspector_TestComponent3::m_data)
  158. ;
  159. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  160. {
  161. editContext->Class<Inspector_TestComponent3>("InspectorTestComponent3", "Component 3 for AZ Tools Framework Unit Tests")
  162. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  163. ->Attribute(AZ::Edit::Attributes::AddableByUser, true)
  164. ->Attribute(AZ::Edit::Attributes::Category, "Inspector Test Components")
  165. ->Attribute(AZ::Edit::Attributes::Icon, "Icons/Components/Tag.png")
  166. ->Attribute(AZ::Edit::Attributes::ViewportIcon, "Icons/Components/Viewport/Tag.png")
  167. ->Attribute(AZ::Edit::Attributes::AutoExpand, true)
  168. ->Attribute(AZ::Edit::Attributes::HelpPageURL, "https://o3de.org/docs/user-guide/components")
  169. ->DataElement(AZ::Edit::UIHandlers::Default, &Inspector_TestComponent3::m_data, "Data", "The component's Data");
  170. }
  171. }
  172. }
  173. static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  174. {
  175. services.push_back(AZ_CRC_CE("InspectorTestService3"));
  176. }
  177. static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& services)
  178. {
  179. services.push_back(AZ_CRC_CE("InspectorTestService3"));
  180. }
  181. ~Inspector_TestComponent3() override
  182. {
  183. }
  184. void SetData(int data)
  185. {
  186. m_data = data;
  187. };
  188. int GetData()
  189. {
  190. return m_data;
  191. }
  192. private:
  193. void Init() override
  194. {}
  195. void Activate() override
  196. {}
  197. void Deactivate() override
  198. {}
  199. /// Whether this entity is locked
  200. int m_data = 0;
  201. };
  202. // Component Filters for Testing
  203. bool Filter_IsTestComponent1(const AZ::SerializeContext::ClassData& classData)
  204. {
  205. AZ::Uuid testComponent1_typeId = azrtti_typeid<Inspector_TestComponent1>();
  206. return classData.m_typeId == testComponent1_typeId;
  207. }
  208. // Component Filters for Testing
  209. bool Filter_IsTestComponent2(const AZ::SerializeContext::ClassData& classData)
  210. {
  211. AZ::Uuid testComponent2_typeId = azrtti_typeid<Inspector_TestComponent2>();
  212. return classData.m_typeId == testComponent2_typeId;
  213. }
  214. // Component Filters for Testing
  215. bool Filter_IsTestComponent3(const AZ::SerializeContext::ClassData& classData)
  216. {
  217. AZ::Uuid testComponent3_typeId = azrtti_typeid<Inspector_TestComponent2>();
  218. return classData.m_typeId == testComponent3_typeId;
  219. }
  220. class ComponentPaletteTests
  221. : public LeakDetectionFixture
  222. {
  223. public:
  224. ComponentPaletteTests()
  225. : LeakDetectionFixture()
  226. { }
  227. void SetUp() override
  228. {
  229. AZ::ComponentApplication::Descriptor componentApplicationDesc;
  230. componentApplicationDesc.m_useExistingAllocator = true;
  231. m_application = aznew ToolsTestApplication("ComponentPaletteTests");
  232. AZ::ComponentApplication::StartupParameters startupParameters;
  233. startupParameters.m_loadSettingsRegistry = false;
  234. startupParameters.m_loadAssetCatalog = false;
  235. m_application->Start(componentApplicationDesc, startupParameters);
  236. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  237. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  238. // in the unit tests.
  239. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  240. }
  241. void TearDown() override
  242. {
  243. // Release all slice asset references, so AssetManager doens't complain.
  244. delete m_application;
  245. }
  246. public:
  247. ToolsTestApplication* m_application = nullptr;
  248. };
  249. // Test pushing slices to create news slices that could result in cyclic
  250. // dependency, e.g. push slice1 => slice2 and slice2 => slice1 at the same
  251. // time.
  252. TEST_F(ComponentPaletteTests, TestComponentPalleteUtilities)
  253. {
  254. AZ::SerializeContext* context = m_application->GetSerializeContext();
  255. // Register our test components (This process also reflects them to the appropriate contexts)
  256. auto* Inspector_TestComponent1Descriptor = Inspector_TestComponent1::CreateDescriptor();
  257. auto* Inspector_TestComponent2Descriptor = Inspector_TestComponent2::CreateDescriptor();
  258. auto* Inspector_TestComponent3Descriptor = Inspector_TestComponent3::CreateDescriptor();
  259. m_application->RegisterComponentDescriptor(Inspector_TestComponent1Descriptor);
  260. m_application->RegisterComponentDescriptor(Inspector_TestComponent2Descriptor);
  261. m_application->RegisterComponentDescriptor(Inspector_TestComponent3Descriptor);
  262. AZ::Uuid testComponent1_typeId = azrtti_typeid<Inspector_TestComponent1>();
  263. AZ::Uuid testComponent2_typeId = azrtti_typeid<Inspector_TestComponent2>();
  264. //////////////////////////////////////////////////////////////////////////
  265. // TEST OffersRequiredServices()
  266. //////////////////////////////////////////////////////////////////////////
  267. // Verify that OffersRequiredServices returns true with the services provided by the component.
  268. AZ::ComponentDescriptor::DependencyArrayType testComponent1_ProvidedServices;
  269. Inspector_TestComponent1::GetProvidedServices(testComponent1_ProvidedServices);
  270. AZ_TEST_ASSERT(testComponent1_ProvidedServices.size() == 1);
  271. const AZ::SerializeContext::ClassData* testComponent1_ClassData = context->FindClassData(testComponent1_typeId);
  272. EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent1_ProvidedServices));
  273. // Verify that OffersRequiredServices returns when given services provided by a different component
  274. AZ::ComponentDescriptor::DependencyArrayType testComponent2_ProvidedServices;
  275. Inspector_TestComponent2::GetProvidedServices(testComponent2_ProvidedServices);
  276. AZ_TEST_ASSERT(testComponent2_ProvidedServices.size() == 1);
  277. AZ_TEST_ASSERT(testComponent1_ProvidedServices != testComponent2_ProvidedServices);
  278. EXPECT_FALSE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, testComponent2_ProvidedServices));
  279. // verify that OffersRequiredServices returns true when provided with an empty list of services
  280. EXPECT_TRUE(AzToolsFramework::OffersRequiredServices(testComponent1_ClassData, AZ::ComponentDescriptor::DependencyArrayType()));
  281. //////////////////////////////////////////////////////////////////////////
  282. // TEST IsAddableByUser()
  283. //////////////////////////////////////////////////////////////////////////
  284. // Verify that IsAddableByUser returns false when given a component that is not editable or viewable by the user
  285. EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::IsAddableByUser(testComponent1_ClassData));
  286. // Verify that IsAddableByUser returns true when given a component that has the appropriate edit context reflection
  287. const AZ::SerializeContext::ClassData* testComponent2_ClassData = context->FindClassData(testComponent2_typeId);
  288. EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::IsAddableByUser(testComponent2_ClassData));
  289. //////////////////////////////////////////////////////////////////////////
  290. // TEST ContainsEditableComponents()
  291. //////////////////////////////////////////////////////////////////////////
  292. // Remove reflection of Test Component 2 for the first test
  293. m_application->UnregisterComponentDescriptor(Inspector_TestComponent2Descriptor);
  294. context->EnableRemoveReflection();
  295. Inspector_TestComponent2::Reflect(context);
  296. context->DisableRemoveReflection();
  297. // Verify that there are no components that satisfy the AppearsInGameComponentMenu filter without service dependency conditions
  298. EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent2, AZ::ComponentDescriptor::DependencyArrayType()));
  299. // Reflect Test Component 2 for subsequent tests
  300. m_application->RegisterComponentDescriptor(Inspector_TestComponent2Descriptor);
  301. // Verify that there is now a component that satisfies the AppearsInGameComponentMenu filter without service dependency conditions
  302. EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent2, AZ::ComponentDescriptor::DependencyArrayType()));
  303. // Verify that true is returned here because test component 2 is editable and provides test component 2 services
  304. EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent2, testComponent2_ProvidedServices));
  305. // Verify that false is returned here because test component 2 does not provide any of the required services
  306. EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent2, testComponent1_ProvidedServices));
  307. // Verify that even though Test Component 1 exists and is returned by the filter and there are no services to match, false is returned
  308. // because Test Component 1 is not editable.
  309. EXPECT_FALSE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent1, AZ::ComponentDescriptor::DependencyArrayType()));
  310. // Verify that true is returned here when a system component is editable
  311. EXPECT_TRUE(AzToolsFramework::ComponentPaletteUtil::ContainsEditableComponents(context, &Filter_IsTestComponent3, AZ::ComponentDescriptor::DependencyArrayType()));
  312. }
  313. // helperfunction to reflect serialize for all these components to keep code short
  314. template<typename ComponentType>
  315. void RegisterSerialize(AZ::ReflectContext* context, bool visible, const char* iconPath, int fixedIndex = -1)
  316. {
  317. if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  318. {
  319. serializeContext->Class<ComponentType, AZ::Component>();
  320. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  321. {
  322. auto reflectionBuilder = editContext->Class<ComponentType>(AZ_STRINGIZE(ComponentType), AZ_STRINGIZE(ComponentType));
  323. auto classElement = reflectionBuilder->ClassElement(AZ::Edit::ClassElements::EditorData, "");
  324. classElement->Attribute(AZ::Edit::Attributes::AddableByUser, true)
  325. ->Attribute(AZ::Edit::Attributes::Visibility, visible ? AZ::Edit::PropertyVisibility::Show : AZ::Edit::PropertyVisibility::Hide)
  326. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"))
  327. ->Attribute(AZ::Edit::Attributes::Category, "Inspector Test Components")
  328. ->Attribute(AZ::Edit::Attributes::Icon, iconPath)
  329. ->Attribute(AZ::Edit::Attributes::ViewportIcon, iconPath);
  330. if (fixedIndex != -1)
  331. {
  332. classElement->Attribute(AZ::Edit::Attributes::FixedComponentListIndex, fixedIndex);
  333. }
  334. }
  335. }
  336. }
  337. class EditorInspectorTestComponentBase : public AZ::Component
  338. {
  339. public:
  340. // These functions are mandatory to provide but are of no use in this case.
  341. void Activate() override {}
  342. void Deactivate() override {}
  343. };
  344. class EditorInspectorTestComponent1 : public EditorInspectorTestComponentBase
  345. {
  346. public:
  347. AZ_COMPONENT(EditorInspectorTestComponent1, "{EF3D8047-4FAA-4615-93E1-C2B5B6EB3C08}", AZ::Component);
  348. static void Reflect(AZ::ReflectContext* context)
  349. {
  350. // A component that is user movable and is visible
  351. RegisterSerialize<EditorInspectorTestComponent1>(context, true, "Component1.png");
  352. }
  353. };
  354. class EditorInspectorTestComponent2 : public EditorInspectorTestComponentBase
  355. {
  356. public:
  357. AZ_COMPONENT(EditorInspectorTestComponent2, "{42BE5BEE-A7B9-4D8D-8F61-C0E0FDAA1450}", AZ::Component);
  358. static void Reflect(AZ::ReflectContext* context)
  359. {
  360. // A component that is not movable, but is visible
  361. RegisterSerialize<EditorInspectorTestComponent2>(context, true, "Component2.png", 0);
  362. }
  363. };
  364. class EditorInspectorTestComponent3 : public EditorInspectorTestComponentBase
  365. {
  366. public:
  367. AZ_COMPONENT(EditorInspectorTestComponent3, "{71329B94-76B3-4C8B-AF4B-159D51BDE820}", AZ::Component);
  368. static void Reflect(AZ::ReflectContext* context)
  369. {
  370. // A component that is not visible
  371. RegisterSerialize<EditorInspectorTestComponent3>(context, false, "Component3.png");
  372. }
  373. };
  374. class EditorInspectorTestComponent4 : public EditorInspectorTestComponentBase
  375. {
  376. public:
  377. AZ_COMPONENT(EditorInspectorTestComponent4, "{10385AEF-88AA-4682-AF1E-3EBE21E4632B}", AZ::Component);
  378. static void Reflect(AZ::ReflectContext* context)
  379. {
  380. // Another component that is visible and movable
  381. RegisterSerialize<EditorInspectorTestComponent4>(context, true, "Component4.png");
  382. }
  383. };
  384. class MockEditorInspectorNotificationBusHandler : public AzToolsFramework::EditorInspectorComponentNotificationBus::Handler
  385. {
  386. public:
  387. MOCK_METHOD0(OnComponentOrderChanged, void());
  388. };
  389. class InspectorComponentOrderingTest
  390. : public ComponentPaletteTests
  391. {
  392. void SetUp() override
  393. {
  394. ComponentPaletteTests::SetUp();
  395. m_application->RegisterComponentDescriptor(EditorInspectorTestComponent1::CreateDescriptor());
  396. m_application->RegisterComponentDescriptor(EditorInspectorTestComponent2::CreateDescriptor());
  397. m_application->RegisterComponentDescriptor(EditorInspectorTestComponent3::CreateDescriptor());
  398. m_application->RegisterComponentDescriptor(EditorInspectorTestComponent4::CreateDescriptor());
  399. m_mockedInspectorBusHandler = AZStd::make_unique<::testing::NiceMock<MockEditorInspectorNotificationBusHandler>>();
  400. }
  401. void TearDown() override
  402. {
  403. m_mockedInspectorBusHandler->BusDisconnect();
  404. m_mockedInspectorBusHandler.reset();
  405. ComponentPaletteTests::TearDown();
  406. }
  407. protected:
  408. AZStd::unique_ptr<::testing::NiceMock<MockEditorInspectorNotificationBusHandler>> m_mockedInspectorBusHandler;
  409. };
  410. // Makes sure that the inspector component (responsible for keeping track of any order overrides of components on it)
  411. // only stores data and only emits events when the components are in a non default order.
  412. // Also makes sure (since it invokes them) that the actual ordering utility functions, such as RemoveHiddenComponents,
  413. // SortComponentsByPriority, and the functions they call, all work as expected.
  414. TEST_F(InspectorComponentOrderingTest, AddingComponents_InspectorComponent_PersistsDataOnlyIfDifferentFromDefault)
  415. {
  416. using namespace AzToolsFramework;
  417. AZ::EntityId entityId(123);
  418. AZ::Entity testEntity(entityId);
  419. testEntity.AddComponent(aznew EditorInspectorTestComponent1);
  420. testEntity.AddComponent(aznew EditorInspectorTestComponent2);
  421. testEntity.AddComponent(aznew EditorInspectorTestComponent3);
  422. testEntity.AddComponent(aznew EditorInspectorTestComponent4);
  423. testEntity.AddComponent(aznew Components::EditorInspectorComponent);
  424. m_mockedInspectorBusHandler->BusConnect(entityId);
  425. // activating the entity should not invoke the component order change bus at all, anything that cares about activation should listen for activation, not reorder.
  426. EXPECT_CALL(*m_mockedInspectorBusHandler, OnComponentOrderChanged()).Times(0);
  427. // Activating an entity does reorder the actual components on the entity itself.
  428. // They will not be in the order added.
  429. // The actual order on the entity is not relevant to this test, but the stable sort function itself will place
  430. // the components that provide services (EditorInspectorComponent) in this case, ahead of ones which don't, and
  431. // if there is a tie, it will sort them by their typeid (their GUID). In this case, it means the order will be:
  432. // * EditorInspectorComponent (because it has services provided)
  433. // * EditorInspectorTestComponent4 // TypeID starts with 10385AEF
  434. // * EditorInspectorTestComponent2 // TypeID starts with 42BE5BEE
  435. // * EditorInspectorTestComponent3 // TypeID starts with 71329B94
  436. // * EditorInspectorTestComponent1 // TypeID starts with EF3D8047
  437. testEntity.Init();
  438. testEntity.Activate();
  439. AZ::Entity::ComponentArrayType componentsOnEntity = testEntity.GetComponents();
  440. EXPECT_EQ(componentsOnEntity.size(), 5);
  441. // An empty component order array sent to an already empty entity should result in no callbacks.
  442. ComponentOrderArray componentOrderArray;
  443. EditorInspectorComponentRequestBus::Event(entityId, &EditorInspectorComponentRequests::SetComponentOrderArray, componentOrderArray);
  444. EditorInspectorComponentRequestBus::EventResult(componentOrderArray, entityId, &EditorInspectorComponentRequests::GetComponentOrderArray);
  445. EXPECT_TRUE(componentOrderArray.empty());
  446. // Setting an empty component order when its already empty should result in no calls to the "Component order changed!" event.
  447. EXPECT_CALL(*m_mockedInspectorBusHandler, OnComponentOrderChanged()).Times(0);
  448. // Setting the component order array to what is already the default order should result in no callbacks:
  449. AZ::Entity::ComponentArrayType components;
  450. components = testEntity.GetComponents();
  451. EXPECT_EQ(components.size(), 5);
  452. RemoveHiddenComponents(components);
  453. EXPECT_EQ(components.size(), 3); // the inspector component and the test Component 3 are hidden.
  454. SortComponentsByPriority(components);
  455. ASSERT_EQ(components.size(), 3); // sorting components should not change the number of components.
  456. // After the sort, the first one in the array should be the fixed order one, that says it "must be in position 0"
  457. EXPECT_EQ(components[0]->RTTI_GetType(), azrtti_typeid<EditorInspectorTestComponent2>());
  458. // the others should remain in their original order, but be after it:
  459. EXPECT_EQ(components[1]->RTTI_GetType(), azrtti_typeid<EditorInspectorTestComponent4>()); // Note the above, 4 comes before 1 due to the stable sort
  460. EXPECT_EQ(components[2]->RTTI_GetType(), azrtti_typeid<EditorInspectorTestComponent1>());
  461. // convert the vector of Component* to a vector of ComponentId
  462. ComponentOrderArray defaultComponentOrder;
  463. for (const AZ::Component* component : components)
  464. {
  465. defaultComponentOrder.push_back(component->GetId());
  466. }
  467. // set the order. Since its the default order, this should again not emit an event, nor update any data that would be persisted:
  468. EditorInspectorComponentRequestBus::Event(entityId, &EditorInspectorComponentRequests::SetComponentOrderArray, defaultComponentOrder);
  469. EditorInspectorComponentRequestBus::EventResult(componentOrderArray, entityId, &EditorInspectorComponentRequests::GetComponentOrderArray);
  470. EXPECT_TRUE(componentOrderArray.empty());
  471. // Setting the component order array to a different order than default should result in a callback and result in data to save.
  472. // in this case, we swap the order of element [1] and [2], so that the final order should be [Component2, Component1, Component4]
  473. ComponentOrderArray nonDefaultOrder = defaultComponentOrder;
  474. AZStd::iter_swap(nonDefaultOrder.begin() + 1, nonDefaultOrder.begin() + 2);
  475. EXPECT_CALL(*m_mockedInspectorBusHandler, OnComponentOrderChanged()).Times(1);
  476. EditorInspectorComponentRequestBus::Event(entityId, &EditorInspectorComponentRequests::SetComponentOrderArray, nonDefaultOrder);
  477. EditorInspectorComponentRequestBus::EventResult(componentOrderArray, entityId, &EditorInspectorComponentRequests::GetComponentOrderArray);
  478. EXPECT_EQ(componentOrderArray.size(), 3);
  479. EXPECT_EQ(componentOrderArray, nonDefaultOrder);
  480. // Setting the component order array back to default, should result in it emptying it out and notifying since its changing (from non-default to default)
  481. EXPECT_CALL(*m_mockedInspectorBusHandler, OnComponentOrderChanged()).Times(1);
  482. EditorInspectorComponentRequestBus::Event(entityId, &EditorInspectorComponentRequests::SetComponentOrderArray, defaultComponentOrder);
  483. EditorInspectorComponentRequestBus::EventResult(componentOrderArray, entityId, &EditorInspectorComponentRequests::GetComponentOrderArray);
  484. EXPECT_TRUE(componentOrderArray.empty());
  485. m_mockedInspectorBusHandler->BusDisconnect();
  486. testEntity.Deactivate();
  487. }
  488. }