SystemComponentFixture.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  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. #pragma once
  9. #include <AzTest/AzTest.h>
  10. #include <AzCore/Asset/AssetManager.h>
  11. #include <AzCore/Asset/AssetManagerComponent.h>
  12. #include <AzCore/Component/ComponentApplication.h>
  13. #include <AzCore/IO/FileIO.h>
  14. #include <AzCore/IO/Streamer/StreamerComponent.h>
  15. #include <AzCore/Jobs/JobManagerComponent.h>
  16. #include <AzCore/UnitTest/TestTypes.h>
  17. #include <AzCore/Module/Module.h>
  18. #include <AzCore/Module/ModuleManagerBus.h>
  19. #include <AzCore/Settings/SettingsRegistryMergeUtils.h>
  20. #include <AzCore/Utils/Utils.h>
  21. #include <AzCore/UserSettings/UserSettingsComponent.h>
  22. #include <AzFramework/Application/Application.h>
  23. #include <AzFramework/Asset/AssetCatalogComponent.h>
  24. #include <AzFramework/IO/LocalFileIO.h>
  25. #include <AzFramework/Physics/Material/PhysicsMaterialSystemComponent.h>
  26. #include <Integration/System/SystemComponent.h>
  27. #include <Integration/AnimationBus.h>
  28. #include <Tests/Printers.h>
  29. #include <Tests/Matchers.h>
  30. namespace EMotionFX
  31. {
  32. template<class... Components>
  33. class EMotionFXTestModule
  34. : public AZ::Module
  35. {
  36. public:
  37. AZ_RTTI(EMotionFXTestModule, "{32567457-5341-4D8D-91A9-E48D8395DE65}", AZ::Module);
  38. AZ_CLASS_ALLOCATOR(EMotionFXTestModule, AZ::OSAllocator);
  39. EMotionFXTestModule()
  40. {
  41. m_descriptors.insert(m_descriptors.end(), {Components::CreateDescriptor()...});
  42. }
  43. };
  44. template<class... Components>
  45. class ComponentFixtureApp
  46. : public AzFramework::Application
  47. {
  48. public:
  49. AZ_CLASS_ALLOCATOR(ComponentFixtureApp, AZ::SystemAllocator)
  50. ComponentFixtureApp()
  51. {
  52. using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
  53. constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
  54. if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  55. {
  56. AZ::IO::FixedMaxPath enginePath;
  57. settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
  58. settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
  59. AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
  60. }
  61. }
  62. AZ::ComponentTypeList GetRequiredSystemComponents() const override
  63. {
  64. return {azrtti_typeid<Components>()...};
  65. }
  66. void CreateStaticModules(AZStd::vector<AZ::Module*>& outModules) override
  67. {
  68. // This is intentionally bypassing the static modules that
  69. // AzFramework::Application would create. That creates more
  70. // components than these tests need.
  71. outModules.emplace_back(aznew EMotionFXTestModule<Components...>());
  72. }
  73. void StartCommon(AZ::Entity* systemEntity) override
  74. {
  75. m_systemEntity = systemEntity;
  76. AzFramework::Application::StartCommon(systemEntity);
  77. }
  78. AZ::Entity* GetSystemEntity() const
  79. {
  80. return m_systemEntity;
  81. }
  82. private:
  83. AZ::Entity* m_systemEntity = nullptr;
  84. };
  85. //! A fixture that constructs an EMotionFX::Integration::SystemComponent
  86. /*!
  87. * This fixture can be used by any test that needs the EMotionFX runtime to
  88. * be working. It will construct all necessary allocators for EMotionFX
  89. * objects to be successfully instantiated.
  90. */
  91. template<class... Components>
  92. class ComponentFixture
  93. : public UnitTest::LeakDetectionFixture
  94. {
  95. public:
  96. void SetUp() override
  97. {
  98. UnitTest::LeakDetectionFixture::SetUp();
  99. PreStart();
  100. AZ::ComponentApplication::StartupParameters startupParameters;
  101. startupParameters.m_createEditContext = true;
  102. startupParameters.m_loadAssetCatalog = false;
  103. startupParameters.m_loadSettingsRegistry = false;
  104. // Add EMotionFX as an active gem within the Settings Registry for unit test
  105. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  106. {
  107. AZ::Test::AddActiveGem("EMotionFX", *settingsRegistry);
  108. }
  109. m_app.Start(AZ::ComponentApplication::Descriptor{}, startupParameters);
  110. // Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
  111. // shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
  112. // in the unit tests.
  113. AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
  114. GetSerializeContext()->CreateEditContext();
  115. }
  116. void TearDown() override
  117. {
  118. // If we loaded the asset catalog, call this function to release all the assets that has been loaded internally.
  119. if (HasComponentType<AzFramework::AssetCatalogComponent>())
  120. {
  121. AZ::Data::AssetManager::Instance().DispatchEvents();
  122. }
  123. GetSerializeContext()->DestroyEditContext();
  124. // Clear the queue of messages from unit tests on our buses
  125. EMotionFX::Integration::ActorNotificationBus::ClearQueuedEvents();
  126. m_app.Stop();
  127. UnitTest::LeakDetectionFixture::TearDown();
  128. }
  129. ~ComponentFixture() override
  130. {
  131. if (GetSystemEntity()->GetState() == AZ::Entity::State::Active)
  132. {
  133. GetSystemEntity()->Deactivate();
  134. }
  135. }
  136. AZ::SerializeContext* GetSerializeContext()
  137. {
  138. return m_app.GetSerializeContext();
  139. }
  140. AZ::Entity* GetSystemEntity() const
  141. {
  142. return m_app.GetSystemEntity();
  143. }
  144. AZStd::string ResolvePath(const char* path) const
  145. {
  146. AZStd::string result;
  147. result.resize(AZ::IO::MaxPathLength);
  148. AZ::IO::FileIOBase::GetInstance()->ResolvePath(path, result.data(), result.size());
  149. result.resize_no_construct(AZStd::char_traits<char>::length(result.data()));
  150. return result;
  151. }
  152. protected:
  153. virtual AZStd::string GetAssetFolder() const
  154. {
  155. AZStd::string assetCachePath;
  156. if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
  157. {
  158. settingsRegistry->Get(assetCachePath, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
  159. }
  160. return assetCachePath;
  161. }
  162. // Runs after allocators are set up but before application startup
  163. // Used by the InitSceneAPI fixture to load the SceneAPI dlls
  164. virtual void PreStart() {}
  165. template<typename T>
  166. static constexpr bool HasComponentType()
  167. {
  168. // Return true if T is somewhere in the Components parameter pack
  169. // This expands to
  170. // return (((AZStd::is_same_v<Components[0], T> || AZStd::is_same_v<Components[1], T>) || AZStd::is_same_v<Components[2], T>) || ...)
  171. return ((... || AZStd::is_same_v<Components, T>));
  172. }
  173. protected:
  174. // The ComponentApplication must not be a pointer, because it cannot be
  175. // dynamically allocated. Calls to new will try to use the SystemAllocator
  176. // that has not been created yet. If one is created before
  177. // ComponentApplication::Create() is called, ComponentApplication will
  178. // complain that there's already a SystemAllocator, as it tries to make one
  179. // itself.
  180. ComponentFixtureApp<Components...> m_app;
  181. };
  182. using SystemComponentFixture = ComponentFixture<
  183. AZ::AssetManagerComponent,
  184. AZ::JobManagerComponent,
  185. AZ::StreamerComponent,
  186. Physics::MaterialSystemComponent,
  187. EMotionFX::Integration::SystemComponent
  188. >;
  189. // Use this fixture if you want to load asset catalog. Some assets (reference anim graph for example)
  190. // can only be loaded when asset catalog is loaded.
  191. using SystemComponentFixtureWithCatalog = ComponentFixture<
  192. AZ::AssetManagerComponent,
  193. AZ::JobManagerComponent,
  194. AZ::StreamerComponent,
  195. AzFramework::AssetCatalogComponent,
  196. Physics::MaterialSystemComponent,
  197. EMotionFX::Integration::SystemComponent
  198. >;
  199. } // end namespace EMotionFX