You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Gems/EMotionFX/Code/Tests/SystemComponentFixture.h

226 lines
8.0 KiB
C++

/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzTest/AzTest.h>
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/IO/FileIO.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Module/Module.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Asset/AssetCatalogComponent.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <Integration/System/SystemComponent.h>
#include <Integration/AnimationBus.h>
#include <Tests/Printers.h>
#include <Tests/Matchers.h>
namespace EMotionFX
{
template<class... Components>
class EMotionFXTestModule
: public AZ::Module
{
public:
AZ_RTTI(EMotionFXTestModule, "{32567457-5341-4D8D-91A9-E48D8395DE65}", AZ::Module);
AZ_CLASS_ALLOCATOR(EMotionFXTestModule, AZ::OSAllocator, 0);
EMotionFXTestModule()
{
m_descriptors.insert(m_descriptors.end(), {Components::CreateDescriptor()...});
}
};
template<class... Components>
class ComponentFixtureApp
: public AzFramework::Application
{
public:
ComponentFixtureApp()
{
using FixedValueString = AZ::SettingsRegistryInterface::FixedValueString;
constexpr auto projectPathKey = FixedValueString(AZ::SettingsRegistryMergeUtils::BootstrapSettingsRootKey) + "/project_path";
if(auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
AZ::IO::FixedMaxPath enginePath;
settingsRegistry->Get(enginePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder);
settingsRegistry->Set(projectPathKey, (enginePath / "AutomatedTesting").Native());
AZ::SettingsRegistryMergeUtils::MergeSettingsToRegistry_AddRuntimeFilePaths(*settingsRegistry);
}
}
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return {azrtti_typeid<Components>()...};
}
void CreateStaticModules(AZStd::vector<AZ::Module*>& outModules) override
{
// This is intentionally bypassing the static modules that
// AzFramework::Application would create. That creates more
// components than these tests need.
outModules.emplace_back(aznew EMotionFXTestModule<Components...>());
}
void StartCommon(AZ::Entity* systemEntity) override
{
m_systemEntity = systemEntity;
AzFramework::Application::StartCommon(systemEntity);
}
AZ::Entity* GetSystemEntity() const
{
return m_systemEntity;
}
private:
AZ::Entity* m_systemEntity = nullptr;
};
//! A fixture that constructs an EMotionFX::Integration::SystemComponent
/*!
* This fixture can be used by any test that needs the EMotionFX runtime to
* be working. It will construct all necessary allocators for EMotionFX
* objects to be successfully instantiated.
*/
template<class... Components>
class ComponentFixture
: public UnitTest::ScopedAllocatorSetupFixture
{
public:
void SetUp() override
{
UnitTest::ScopedAllocatorSetupFixture::SetUp();
PreStart();
AZ::ComponentApplication::StartupParameters startupParameters;
startupParameters.m_createEditContext = true;
m_app.Start(AZ::ComponentApplication::Descriptor{}, startupParameters);
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
GetSerializeContext()->CreateEditContext();
}
void TearDown() override
{
// If we loaded the asset catalog, call this function to release all the assets that has been loaded internally.
if (HasComponentType<AzFramework::AssetCatalogComponent>())
{
AZ::Data::AssetManager::Instance().DispatchEvents();
}
GetSerializeContext()->DestroyEditContext();
// Clear the queue of messages from unit tests on our buses
EMotionFX::Integration::ActorNotificationBus::ClearQueuedEvents();
UnitTest::ScopedAllocatorSetupFixture::TearDown();
}
~ComponentFixture() override
{
if (GetSystemEntity()->GetState() == AZ::Entity::State::Active)
{
GetSystemEntity()->Deactivate();
}
}
AZ::SerializeContext* GetSerializeContext()
{
return m_app.GetSerializeContext();
}
AZ::Entity* GetSystemEntity() const
{
return m_app.GetSystemEntity();
}
AZStd::string ResolvePath(const char* path) const
{
AZStd::string result;
result.resize(AZ::IO::MaxPathLength);
AZ::IO::FileIOBase::GetInstance()->ResolvePath(path, result.data(), result.size());
result.resize_no_construct(AZStd::char_traits<char>::length(result.data()));
return result;
}
protected:
virtual AZStd::string GetAssetFolder() const
{
AZStd::string assetCachePath;
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
settingsRegistry->Get(assetCachePath, AZ::SettingsRegistryMergeUtils::FilePathKey_CacheRootFolder);
}
return assetCachePath;
}
// Runs after allocators are set up but before application startup
// Used by the InitSceneAPI fixture to load the SceneAPI dlls
virtual void PreStart() {}
template<typename T>
static constexpr bool HasComponentType()
{
// Return true if T is somewhere in the Components parameter pack
// This expands to
// return (((AZStd::is_same_v<Components[0], T> || AZStd::is_same_v<Components[1], T>) || AZStd::is_same_v<Components[2], T>) || ...)
return ((... || AZStd::is_same_v<Components, T>));
}
protected:
// The ComponentApplication must not be a pointer, because it cannot be
// dynamically allocated. Calls to new will try to use the SystemAllocator
// that has not been created yet. If one is created before
// ComponentApplication::Create() is called, ComponentApplication will
// complain that there's already a SystemAllocator, as it tries to make one
// itself.
ComponentFixtureApp<Components...> m_app;
};
using SystemComponentFixture = ComponentFixture<
AZ::MemoryComponent,
AZ::AssetManagerComponent,
AZ::JobManagerComponent,
AZ::StreamerComponent,
EMotionFX::Integration::SystemComponent
>;
// Use this fixture if you want to load asset catalog. Some assets (reference anim graph for example)
// can only be loaded when asset catalog is loaded.
using SystemComponentFixtureWithCatalog = ComponentFixture<
AZ::MemoryComponent,
AZ::AssetManagerComponent,
AZ::JobManagerComponent,
AZ::StreamerComponent,
AzFramework::AssetCatalogComponent,
EMotionFX::Integration::SystemComponent
>;
} // end namespace EMotionFX