GenericComponentWrapperTest.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 <AzTest/AzTest.h>
  9. #include <AzCore/Slice/SliceComponent.h>
  10. #include <AzCore/Serialization/Utils.h>
  11. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  12. #include <AzCore/UnitTest/TestTypes.h>
  13. #include <AzCore/UserSettings/UserSettingsComponent.h>
  14. #include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
  15. #include <AzToolsFramework/Application/ToolsApplication.h>
  16. #include <AzToolsFramework/API/EntityCompositionRequestBus.h>
  17. // Test that editor-components wrapped within a GenericComponentWrapper
  18. // are moved out of the wrapper when a slice is loaded.
  19. const char kWrappedEditorComponent[] =
  20. R"DELIMITER(<ObjectStream version="1">
  21. <Class name="SliceComponent" field="element" version="1" type="{AFD304E4-1773-47C8-855A-8B622398934F}">
  22. <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  23. <Class name="AZ::u64" field="Id" value="7737200995084371546" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  24. </Class>
  25. <Class name="AZStd::vector" field="Entities" type="{2BADE35A-6F1B-4698-B2BC-3373D010020C}">
  26. <Class name="AZ::Entity" field="element" version="2" type="{75651658-8663-478D-9090-2432DFCAFA44}">
  27. <Class name="EntityId" field="Id" version="1" type="{6383F1D3-BB27-4E6B-A49A-6409B2059EAA}">
  28. <Class name="AZ::u64" field="id" value="16119032733109672753" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  29. </Class>
  30. <Class name="AZStd::string" field="Name" value="RigidPhysicsMesh" type="{EF8FF807-DDEE-4EB0-B678-4CA3A2C490A4}"/>
  31. <Class name="bool" field="IsDependencyReady" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
  32. <Class name="AZStd::vector" field="Components" type="{2BADE35A-6F1B-4698-B2BC-3373D010020C}">
  33. <Class name="GenericComponentWrapper" field="element" type="{68D358CA-89B9-4730-8BA6-E181DEA28FDE}">
  34. <Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
  35. <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  36. <Class name="AZ::u64" field="Id" value="11874523501682509824" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  37. </Class>
  38. </Class>
  39. <Class name="SelectionComponent" field="m_template" type="{A7CBE7BC-9B4A-47DC-962F-1BFAE85DBF3A}">
  40. <Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
  41. <Class name="AZ::u64" field="Id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
  42. </Class>
  43. </Class>
  44. </Class>
  45. </Class>
  46. </Class>
  47. </Class>
  48. <Class name="AZStd::list" field="Prefabs" type="{B845AD64-B5A0-4CCD-A86B-3477A36779BE}"/>
  49. <Class name="bool" field="IsDynamic" value="false" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
  50. </Class>
  51. </ObjectStream>)DELIMITER";
  52. class WrappedEditorComponentTest
  53. : public UnitTest::LeakDetectionFixture
  54. {
  55. protected:
  56. void SetUp() override
  57. {
  58. AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
  59. auto projectPathKey =
  60. AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
  61. AZ::IO::FixedMaxPath enginePath;
  62. registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  63. registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
  64. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
  65. AZ::ComponentApplication::StartupParameters startupParameters;
  66. startupParameters.m_loadSettingsRegistry = false;
  67. m_app.Start(AZ::ComponentApplication::Descriptor(), startupParameters);
  68. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  69. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  70. // in the unit tests.
  71. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  72. m_slice.reset(AZ::Utils::LoadObjectFromBuffer<AZ::SliceComponent>(kWrappedEditorComponent, strlen(kWrappedEditorComponent) + 1));
  73. if (m_slice)
  74. {
  75. if (m_slice->GetNewEntities().size() > 0)
  76. {
  77. m_entityFromSlice = m_slice->GetNewEntities()[0];
  78. if (m_entityFromSlice)
  79. {
  80. if (m_entityFromSlice->GetComponents().size() > 0)
  81. {
  82. m_componentFromSlice = m_entityFromSlice->GetComponents()[0];
  83. }
  84. }
  85. }
  86. }
  87. }
  88. void TearDown() override
  89. {
  90. m_slice.reset();
  91. m_app.Stop();
  92. }
  93. AzToolsFramework::ToolsApplication m_app;
  94. AZStd::unique_ptr<AZ::SliceComponent> m_slice;
  95. AZ::Entity* m_entityFromSlice = nullptr;
  96. AZ::Component* m_componentFromSlice = nullptr;
  97. };
  98. TEST_F(WrappedEditorComponentTest, Slice_Loaded)
  99. {
  100. EXPECT_NE(m_slice.get(), nullptr);
  101. }
  102. TEST_F(WrappedEditorComponentTest, EntityFromSlice_Exists)
  103. {
  104. EXPECT_NE(m_entityFromSlice, nullptr);
  105. }
  106. TEST_F(WrappedEditorComponentTest, ComponentFromSlice_Exists)
  107. {
  108. EXPECT_NE(m_componentFromSlice, nullptr);
  109. }
  110. TEST_F(WrappedEditorComponentTest, Component_IsNotGenericComponentWrapper)
  111. {
  112. EXPECT_EQ(azrtti_cast<AzToolsFramework::Components::GenericComponentWrapper*>(m_componentFromSlice), nullptr);
  113. }
  114. // The swapped component should have adopted the GenericComponentWrapper's ComponentId.
  115. TEST_F(WrappedEditorComponentTest, ComponentId_MatchesWrapperId)
  116. {
  117. EXPECT_EQ(m_componentFromSlice->GetId(), 11874523501682509824u);
  118. }
  119. static constexpr AZ::TypeId InGameOnlyComponentTypeId{ "{1D538623-2052-464F-B0DA-D000E1520333}" };
  120. class InGameOnlyComponent
  121. : public AZ::Component
  122. {
  123. public:
  124. AZ_COMPONENT(InGameOnlyComponent, InGameOnlyComponentTypeId);
  125. void Activate() override {}
  126. void Deactivate() override {}
  127. static void Reflect(AZ::ReflectContext* reflection)
  128. {
  129. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  130. {
  131. serializeContext->Class<InGameOnlyComponent, AZ::Component>();
  132. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  133. {
  134. editContext->Class<InGameOnlyComponent>("InGame Only", "")
  135. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  136. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
  137. }
  138. }
  139. }
  140. };
  141. static constexpr AZ::Uuid NoneEditorComponentTypeId{ "{AE3454BA-D785-4EE2-A55B-A089F2B2916A}" };
  142. class NoneEditorComponent
  143. : public AZ::Component
  144. {
  145. public:
  146. AZ_COMPONENT(NoneEditorComponent, NoneEditorComponentTypeId);
  147. void Activate() override {}
  148. void Deactivate() override {}
  149. static void Reflect(AZ::ReflectContext* reflection)
  150. {
  151. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(reflection))
  152. {
  153. serializeContext->Class<NoneEditorComponent, AZ::Component>();
  154. if (AZ::EditContext* editContext = serializeContext->GetEditContext())
  155. {
  156. editContext->Class<NoneEditorComponent>("None Editor", "")
  157. ->ClassElement(AZ::Edit::ClassElements::EditorData, "")
  158. ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC_CE("Game"));
  159. }
  160. }
  161. }
  162. };
  163. class FindWrappedComponentsTest
  164. : public UnitTest::LeakDetectionFixture
  165. {
  166. public:
  167. void SetUp() override
  168. {
  169. AZ::SettingsRegistryInterface* registry = AZ::SettingsRegistry::Get();
  170. auto projectPathKey =
  171. AZ::SettingsRegistryInterface::FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
  172. AZ::IO::FixedMaxPath enginePath;
  173. registry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  174. registry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
  175. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*registry);
  176. AZ::ComponentApplication::StartupParameters startupParameters;
  177. startupParameters.m_loadSettingsRegistry = false;
  178. m_app.Start(AzFramework::Application::Descriptor(), startupParameters);
  179. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  180. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  181. // in the unit tests.
  182. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  183. m_app.RegisterComponentDescriptor(InGameOnlyComponent::CreateDescriptor());
  184. m_app.RegisterComponentDescriptor(NoneEditorComponent::CreateDescriptor());
  185. m_entity = new AZ::Entity("Entity1");
  186. AZ::Component* inGameOnlyComponent = nullptr;
  187. AZ::ComponentDescriptorBus::EventResult(inGameOnlyComponent, InGameOnlyComponentTypeId, &AZ::ComponentDescriptorBus::Events::CreateComponent);
  188. AZ::Component* genericComponent0 = aznew AzToolsFramework::Components::GenericComponentWrapper(inGameOnlyComponent);
  189. m_entity->AddComponent(genericComponent0);
  190. AZ::Component* noneEditorComponent = nullptr;
  191. AZ::ComponentDescriptorBus::EventResult(noneEditorComponent, NoneEditorComponentTypeId, &AZ::ComponentDescriptorBus::Events::CreateComponent);
  192. AZ::Component* genericComponent1 = aznew AzToolsFramework::Components::GenericComponentWrapper(noneEditorComponent);
  193. m_entity->AddComponent(genericComponent1);
  194. m_entity->Init();
  195. }
  196. void TearDown() override
  197. {
  198. m_app.Stop();
  199. }
  200. AzToolsFramework::ToolsApplication m_app;
  201. AZ::Entity* m_entity = nullptr;
  202. };
  203. TEST_F(FindWrappedComponentsTest, found)
  204. {
  205. InGameOnlyComponent* ingameOnlyComponent = AzToolsFramework::FindWrappedComponentForEntity<InGameOnlyComponent>(m_entity);
  206. EXPECT_NE(ingameOnlyComponent, nullptr);
  207. NoneEditorComponent* noneEditorComponent = AzToolsFramework::FindWrappedComponentForEntity<NoneEditorComponent>(m_entity);
  208. EXPECT_NE(noneEditorComponent, nullptr);
  209. }