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/LmbrCentral/Code/Tests/SpawnerComponentTest.cpp

694 lines
26 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
*
*/
#include "LmbrCentralReflectionTest.h"
#include <AzCore/Asset/AssetManager.h>
#include <AzCore/Asset/AssetManagerComponent.h>
#include <AzCore/Jobs/JobManagerComponent.h>
#include <AzCore/IO/Streamer/StreamerComponent.h>
#include <AzCore/Memory/MemoryComponent.h>
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/Slice/SliceSystemComponent.h>
#include <AzFramework/Application/Application.h>
#include <AzFramework/Asset/AssetSystemComponent.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/GameEntityContextComponent.h>
#include <AzCore/UnitTest/TestTypes.h>
// LmbrCentral/Source
#include "Scripting/SpawnerComponent.h"
#include "LmbrCentral.h"
#ifdef LMBR_CENTRAL_EDITOR
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/ToolsComponents/GenericComponentWrapper.h>
#include "LmbrCentralEditor.h"
#include "Scripting/EditorSpawnerComponent.h"
#endif
// Tracks SpawnerComponentNotificationBus events
class SpawnWatcher
: public LmbrCentral::SpawnerComponentNotificationBus::Handler
{
public:
AZ_CLASS_ALLOCATOR(SpawnWatcher, AZ::SystemAllocator, 0);
SpawnWatcher(AZ::EntityId spawnerEntityId)
{
LmbrCentral::SpawnerComponentNotificationBus::Handler::BusConnect(spawnerEntityId);
}
void OnSpawnBegin(const AzFramework::SliceInstantiationTicket& ticket) override
{
m_tickets[ticket].m_onSpawnBegin = true;
}
void OnSpawnEnd(const AzFramework::SliceInstantiationTicket& ticket) override
{
m_tickets[ticket].m_onSpawnEnd = true;
}
void OnEntitySpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZ::EntityId& spawnedEntity) override
{
m_tickets[ticket].m_onEntitySpawned.emplace_back(spawnedEntity);
}
void OnEntitiesSpawned(const AzFramework::SliceInstantiationTicket& ticket, const AZStd::vector<AZ::EntityId>& spawnedEntities) override
{
m_tickets[ticket].m_onEntitiesSpawned = spawnedEntities;
}
void OnSpawnedSliceDestroyed(const AzFramework::SliceInstantiationTicket& ticket) override
{
m_tickets[ticket].m_onSpawnedSliceDestroyed = true;
}
// Which events have fired for a particular ticket
struct TicketInfo
{
bool m_onSpawnBegin = false;
bool m_onSpawnEnd = false;
AZStd::vector<AZ::EntityId> m_onEntitySpawned;
AZStd::vector<AZ::EntityId> m_onEntitiesSpawned;
bool m_onSpawnedSliceDestroyed = false;
};
AZStd::unordered_map<AzFramework::SliceInstantiationTicket, TicketInfo> m_tickets;
};
// Simplified version of AzFramework::Application
class SpawnerApplication
: public AzFramework::Application
{
// override and only include system components required for spawner tests.
AZ::ComponentTypeList GetRequiredSystemComponents() const override
{
return AZ::ComponentTypeList{
azrtti_typeid<AZ::MemoryComponent>(),
azrtti_typeid<AZ::JobManagerComponent>(),
azrtti_typeid<AZ::StreamerComponent>(),
azrtti_typeid<AZ::AssetManagerComponent>(),
azrtti_typeid<AZ::SliceSystemComponent>(),
azrtti_typeid<AzFramework::GameEntityContextComponent>(),
azrtti_typeid<AzFramework::AssetSystem::AssetSystemComponent>(),
};
}
void RegisterCoreComponents() override
{
AzFramework::Application::RegisterCoreComponents();
RegisterComponentDescriptor(LmbrCentral::SpawnerComponent::CreateDescriptor());
}
};
class SpawnerComponentTest
: public testing::Test
{
public:
void SetUp() override
{
// start application
AZ::AllocatorInstance<AZ::SystemAllocator>::Create(AZ::SystemAllocator::Descriptor());
AZ::ComponentApplication::Descriptor appDescriptor;
appDescriptor.m_useExistingAllocator = true;
m_application = aznew SpawnerApplication();
m_application->Start(appDescriptor, AZ::ComponentApplication::StartupParameters());
// create a dynamic slice in the asset system
AZ::Entity* sliceAssetEntity = aznew AZ::Entity();
AZ::SliceComponent* sliceAssetComponent = sliceAssetEntity->CreateComponent<AZ::SliceComponent>();
sliceAssetComponent->SetSerializeContext(m_application->GetSerializeContext());
sliceAssetEntity->Init();
sliceAssetEntity->Activate();
AZ::Entity* entityInSlice1 = aznew AZ::Entity("spawned entity 1");
entityInSlice1->CreateComponent<AzFramework::TransformComponent>();
sliceAssetComponent->AddEntity(entityInSlice1);
AZ::Entity* entityInSlice2 = aznew AZ::Entity("spawned entity 2");
entityInSlice2->CreateComponent<AzFramework::TransformComponent>();
sliceAssetComponent->AddEntity(entityInSlice2);
m_sliceAssetRef = AZ::Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(AZ::Data::AssetId("{E47E78B1-FF5E-4191-BE72-A06428D324F3}"), AZ::Data::AssetLoadBehavior::Default);
m_sliceAssetRef.Get()->SetData(sliceAssetEntity, sliceAssetComponent);
// create an entity with a spawner component
AZ::Entity* spawnerEntity = aznew AZ::Entity("spawner");
m_spawnerComponent = spawnerEntity->CreateComponent<LmbrCentral::SpawnerComponent>();
spawnerEntity->Init();
spawnerEntity->Activate();
// create class that will watch for spawner component notifications
m_spawnWatcher = aznew SpawnWatcher(m_spawnerComponent->GetEntityId());
}
void TearDown() override
{
delete m_spawnWatcher;
m_spawnWatcher = nullptr;
delete m_application->FindEntity(m_spawnerComponent->GetEntityId());
m_spawnerComponent = nullptr;
// reset game context (delete any spawned slices and their entities)
AzFramework::GameEntityContextRequestBus::Broadcast(&AzFramework::GameEntityContextRequestBus::Events::ResetGameContext);
m_sliceAssetRef = AZ::Data::Asset<AZ::SliceAsset>();
m_application->Stop();
delete m_application;
m_application = nullptr;
AZ::AllocatorInstance<AZ::SystemAllocator>::Destroy();
}
// Tick application until 'condition' function returns true.
// If 'maxTicks' elapse without condition passing, return false.
bool TickUntil(AZStd::function<bool()> condition, size_t maxTicks=100)
{
for (size_t tickI = 0; tickI < maxTicks; ++tickI)
{
if (condition())
{
return true;
}
m_application->Tick();
}
return false;
}
// Common test operation: Spawn m_sliceAssetRef and tick application until OnSpawnEnd fires.
AzFramework::SliceInstantiationTicket SpawnDefaultSlice()
{
AzFramework::SliceInstantiationTicket ticket = m_spawnerComponent->SpawnSlice(m_sliceAssetRef);
bool onSpawnEndFired = TickUntil([this, ticket]() { return m_spawnWatcher->m_tickets[ticket].m_onSpawnEnd; });
EXPECT_TRUE(onSpawnEndFired); // sanity check
return ticket;
}
// Common test operation: Spawn m_sliceAssetRef many times and tick application until OnSpawnEnd fires for each spawn.
AZStd::vector<AzFramework::SliceInstantiationTicket> SpawnManyDefaultSlices()
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets;
for (int i = 0; i < 10; ++i)
{
tickets.emplace_back(m_spawnerComponent->SpawnSlice(m_sliceAssetRef));
}
bool onSpawnEndFiredForAll = TickUntil(
[this, &tickets]()
{
for (AzFramework::SliceInstantiationTicket& ticket : tickets)
{
if (!m_spawnWatcher->m_tickets[ticket].m_onSpawnEnd)
{
return false;
}
}
return true;
});
EXPECT_TRUE(onSpawnEndFiredForAll); // sanity check
return tickets;
}
SpawnerApplication* m_application = nullptr;
AZ::Data::Asset<AZ::SliceAsset> m_sliceAssetRef; // a slice asset to spawn
LmbrCentral::SpawnerComponent* m_spawnerComponent = nullptr; // a spawner component to test
SpawnWatcher* m_spawnWatcher = nullptr; // tracks events from the spawner component
};
const size_t kEntitiesInSlice = 2; // number of entities in asset we're testing with
TEST_F(SpawnerComponentTest, SanityCheck)
{
// Tests that Setup/TearDown work as expected
}
TEST_F(SpawnerComponentTest, SpawnSlice_OnSpawnEnd_Fires)
{
// First test the helper function, which checks for OnSpawnEnd
SpawnDefaultSlice();
}
TEST_F(SpawnerComponentTest, SpawnSlice_OnSpawnBegin_Fires)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
EXPECT_TRUE(m_spawnWatcher->m_tickets[ticket].m_onSpawnBegin);
}
TEST_F(SpawnerComponentTest, SpawnSlice_OnEntitySpawned_FiresOncePerEntity)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
EXPECT_EQ(kEntitiesInSlice, m_spawnWatcher->m_tickets[ticket].m_onEntitySpawned.size());
}
TEST_F(SpawnerComponentTest, SpawnSlice_OnEntitiesSpawned_FiresWithAllEntities)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
EXPECT_EQ(kEntitiesInSlice, m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned.size());
}
TEST_F(SpawnerComponentTest, OnSpawnedSliceDestroyed_FiresAfterEntitiesDeleted)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
for (AZ::EntityId spawnedEntityId : m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned)
{
AzFramework::GameEntityContextRequestBus::Broadcast(&AzFramework::GameEntityContextRequestBus::Events::DestroyGameEntity, spawnedEntityId);
}
bool spawnDestructionFired = TickUntil([this, ticket]() { return m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed; });
EXPECT_TRUE(spawnDestructionFired);
}
TEST_F(SpawnerComponentTest, DISABLED_OnSpawnedSliceDestroyed_FiresWhenSpawningBadAssets) // disabled because AZ_TEST_START_TRACE_SUPPRESSION isn't currently suppressing the asserts
{
// ID is made up and not registered with asset manager
auto nonexistentAsset = AZ::Data::Asset<AZ::SliceAsset>(AZ::Data::AssetId("{9E3862CC-B6DF-485F-A9D8-5F4A966DE88B}"), AZ::AzTypeInfo<AZ::SliceAsset>::Uuid());
AzFramework::SliceInstantiationTicket ticket = m_spawnerComponent->SpawnSlice(nonexistentAsset);
AZ_TEST_START_TRACE_SUPPRESSION;
bool spawnDestructionFired = TickUntil([this, ticket]() { return m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed; });
AZ_TEST_STOP_TRACE_SUPPRESSION(1);
EXPECT_TRUE(spawnDestructionFired);
}
TEST_F(SpawnerComponentTest, DestroySpawnedSlice_EntitiesFromSpawn_AreDeleted)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
m_spawnerComponent->DestroySpawnedSlice(ticket);
bool entitiesRemoved = TickUntil(
[this, ticket]()
{
for (AZ::EntityId entityId : m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned)
{
if (m_application->FindEntity(entityId))
{
return false;
}
}
return true;
});
EXPECT_TRUE(entitiesRemoved);
}
TEST_F(SpawnerComponentTest, DestroySpawnedSlice_OnSpawnedSliceDestroyed_Fires)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
m_spawnerComponent->DestroySpawnedSlice(ticket);
bool onSpawnedSliceDestroyed = TickUntil([this, ticket](){ return m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed; });
EXPECT_TRUE(onSpawnedSliceDestroyed);
}
TEST_F(SpawnerComponentTest, DestroySpawnedSlice_BeforeOnSpawnBegin_PreventsInstantiation)
{
AzFramework::SliceInstantiationTicket ticket = m_spawnerComponent->SpawnSlice(m_sliceAssetRef);
m_spawnerComponent->DestroySpawnedSlice(ticket);
// wait a long time, just to be sure no queued entity instantiation takes place
for (int i = 0; i < 100; ++i)
{
m_application->Tick();
}
EXPECT_FALSE(m_spawnWatcher->m_tickets[ticket].m_onSpawnBegin);
EXPECT_TRUE(m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed);
}
class GameEntityContextWatcher
: public AzFramework::SliceGameEntityOwnershipServiceNotificationBus::Handler
{
public:
GameEntityContextWatcher()
{
AzFramework::SliceGameEntityOwnershipServiceNotificationBus::Handler::BusConnect();
}
void OnSliceInstantiated(const AZ::Data::AssetId& /*sliceAssetId*/, const AZ::SliceComponent::SliceInstanceAddress& /*instance*/, const AzFramework::SliceInstantiationTicket& ticket) override
{
m_onSliceInstantiatedTickets.emplace(ticket);
}
void OnSliceInstantiationFailed(const AZ::Data::AssetId& /*sliceAssetId*/, const AzFramework::SliceInstantiationTicket& ticket) override
{
m_onSliceInstantiationFailedTickets.emplace(ticket);
}
AZStd::unordered_set<AzFramework::SliceInstantiationTicket> m_onSliceInstantiatedTickets;
AZStd::unordered_set<AzFramework::SliceInstantiationTicket> m_onSliceInstantiationFailedTickets;
};
TEST_F(SpawnerComponentTest, DestroySpawnedSlice_BeforeOnSpawnBegin_ContextFiresOnSliceInstantiationFailed)
{
// context should send out instantiation failure message, even if ticket is explicitly cancelled.
// others might be listening to the context and not know about the cancellation.
GameEntityContextWatcher contextWatcher;
AzFramework::SliceInstantiationTicket ticket = m_spawnerComponent->SpawnSlice(m_sliceAssetRef);
m_spawnerComponent->DestroySpawnedSlice(ticket);
TickUntil([this, ticket]() { return m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed; });
bool onSliceInstantiationFailed = contextWatcher.m_onSliceInstantiationFailedTickets.count(ticket) > 0;
EXPECT_TRUE(onSliceInstantiationFailed);
bool onSliceInstantiated = contextWatcher.m_onSliceInstantiatedTickets.count(ticket) > 0;
EXPECT_FALSE(onSliceInstantiated);
}
TEST_F(SpawnerComponentTest, DestroySpawnedSlice_WhenManySpawnsInProgress_DoesntAffectOtherSpawns)
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets;
for (int i = 0; i < 10; ++i)
{
tickets.emplace_back(m_spawnerComponent->SpawnSlice(m_sliceAssetRef));
}
m_spawnerComponent->DestroySpawnedSlice(tickets[0]);
// check that other slices finish spawning
bool entitiesSpawnedInOtherSlices = TickUntil(
[this, &tickets]()
{
for (size_t i = 1; i < tickets.size(); ++i)
{
if (m_spawnWatcher->m_tickets[tickets[i]].m_onEntitiesSpawned.size() > 0)
{
return false;
}
}
return true;
});
EXPECT_TRUE(entitiesSpawnedInOtherSlices);
// check that one slice destroyed
bool sliceDestroyed = TickUntil([this, &tickets]()
{
return m_spawnWatcher->m_tickets[tickets[0]].m_onSpawnedSliceDestroyed;
});
EXPECT_TRUE(sliceDestroyed);
// make sure no other slice get destroyed
bool anyOtherSliceDestroyed = false;
for (size_t i = 1; i < tickets.size(); ++i)
{
if (m_spawnWatcher->m_tickets[tickets[i]].m_onSpawnedSliceDestroyed)
{
anyOtherSliceDestroyed = true;
}
}
EXPECT_FALSE(anyOtherSliceDestroyed);
}
TEST_F(SpawnerComponentTest, DestroyAllSpawnedSlices_AllSpawnedEntities_AreDestroyed)
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets = SpawnManyDefaultSlices();
m_spawnerComponent->DestroyAllSpawnedSlices();
bool allEntitiesDestroyed = TickUntil(
[this, &tickets]()
{
for (AzFramework::SliceInstantiationTicket& ticket : tickets)
{
for (AZ::EntityId spawnedEntityId : m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned)
{
if (m_application->FindEntity(spawnedEntityId))
{
return false;
}
}
}
return true;
});
EXPECT_TRUE(allEntitiesDestroyed);
}
TEST_F(SpawnerComponentTest, DestroyAllSpawnedSlices_OnSpawnedSliceDestroyed_FiresForAll)
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets = SpawnManyDefaultSlices();
m_spawnerComponent->DestroyAllSpawnedSlices();
bool onSpawnedSliceDestroyedFiresForAll = TickUntil(
[this, &tickets]()
{
for (AzFramework::SliceInstantiationTicket& ticket : tickets)
{
if (!m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed)
{
return false;
}
}
return true;
});
EXPECT_TRUE(onSpawnedSliceDestroyedFiresForAll);
}
TEST_F(SpawnerComponentTest, DestroyAllSpawnedSlices_BeforeOnSpawnBegin_PreventsInstantiation)
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets;
for (int i = 0; i < 10; ++i)
{
tickets.emplace_back(m_spawnerComponent->SpawnSlice(m_sliceAssetRef));
}
m_spawnerComponent->DestroyAllSpawnedSlices();
// wait a long time, to ensure no queued activity results in an instantiation
for (int i = 0; i < 100; ++i)
{
m_application->Tick();
}
bool anyOnSpawnBegan = false;
bool allOnSpawnedSliceDestroyed = true;
for (AzFramework::SliceInstantiationTicket& ticket : tickets)
{
anyOnSpawnBegan |= m_spawnWatcher->m_tickets[ticket].m_onSpawnBegin;
allOnSpawnedSliceDestroyed &= m_spawnWatcher->m_tickets[ticket].m_onSpawnedSliceDestroyed;
}
EXPECT_FALSE(anyOnSpawnBegan);
EXPECT_TRUE(allOnSpawnedSliceDestroyed);
}
TEST_F(SpawnerComponentTest, GetCurrentEntitiesFromSpawnedSlice_ReturnsEntities)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
AZStd::vector<AZ::EntityId> entities = m_spawnerComponent->GetCurrentEntitiesFromSpawnedSlice(ticket);
EXPECT_EQ(m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned.size(), entities.size());
}
TEST_F(SpawnerComponentTest, GetCurrentEntitiesFromSpawnedSlice_WithEntityDeleted_DoesNotReturnDeletedEntity)
{
AzFramework::SliceInstantiationTicket ticket = SpawnDefaultSlice();
AZStd::vector<AZ::EntityId>& entitiesBeforeDelete = m_spawnWatcher->m_tickets[ticket].m_onEntitiesSpawned;
AZ::EntityId entityToDelete = entitiesBeforeDelete[0];
delete m_application->FindEntity(entityToDelete);
AZStd::vector<AZ::EntityId> entitiesAfterDelete = m_spawnerComponent->GetCurrentEntitiesFromSpawnedSlice(ticket);
EXPECT_EQ(entitiesBeforeDelete.size() - 1, entitiesAfterDelete.size());
bool deletedEntityPresent = AZStd::find(entitiesAfterDelete.begin(), entitiesAfterDelete.end(), entityToDelete) != entitiesAfterDelete.end();
EXPECT_FALSE(deletedEntityPresent);
}
TEST_F(SpawnerComponentTest, GetAllCurrentlySpawnedEntities_ReturnsEntities)
{
AZStd::vector<AzFramework::SliceInstantiationTicket> tickets = SpawnManyDefaultSlices();
AZStd::vector<AZ::EntityId> entities = m_spawnerComponent->GetAllCurrentlySpawnedEntities();
bool allEntitiesFound = true;
size_t numEntities = 0;
// compare against entities from OnEntitiesSpawned event
for (auto& ticketInfoPair : m_spawnWatcher->m_tickets)
{
for (AZ::EntityId spawnedEntity : ticketInfoPair.second.m_onEntitiesSpawned)
{
++numEntities;
allEntitiesFound &= AZStd::find(entities.begin(), entities.end(), spawnedEntity) != entities.end();
}
}
EXPECT_EQ(numEntities, entities.size());
EXPECT_TRUE(allEntitiesFound);
}
// Legacy SpawnerComponent from game data
// Should get converted into modern SpawnerComponent
const char kWrappedGameSpawnerComponent[] =
R"DELIMITER(<ObjectStream version="3">
<Class name="SpawnerComponent" field="element" version="1" type="{8022A627-FA7D-4516-A155-657A0927A3CA}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="8317941343245109563" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="Asset" field="Slice" value="id={6F11134F-84C9-559F-AABA-3D1778656707}:2,type={78802ABF-9595-463A-8D2B-D022F906F9B1},hint={slices/particle_electrical_damage.dynamicslice}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
<Class name="bool" field="SpawnOnActivate" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="DestroyOnDeactivate" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
</Class>
</ObjectStream>)DELIMITER";
class LoadSpawnerComponentFromLegacyGameData
: public LoadReflectedObjectTest<AzFramework::Application, LmbrCentral::LmbrCentralModule, LmbrCentral::SpawnerComponent>
{
public:
const char* GetSourceDataBuffer() const { return kWrappedGameSpawnerComponent; }
void SetUp()
{
LoadReflectedObjectTest::SetUp();
if (m_object)
{
m_readConfigSuccess = m_object->GetConfiguration(m_spawnerConfig);
}
}
LmbrCentral::SpawnerConfig m_spawnerConfig;
bool m_readConfigSuccess = false;
};
TEST_F(LoadSpawnerComponentFromLegacyGameData, Fixture_SanityCheck)
{
EXPECT_NE(nullptr, GetApplication());
}
TEST_F(LoadSpawnerComponentFromLegacyGameData, SpawnerComponent_LoadsFromData)
{
EXPECT_NE(nullptr, m_object.get());
}
TEST_F(LoadSpawnerComponentFromLegacyGameData, ComponentId_ValuePreserved)
{
EXPECT_EQ(AZ::ComponentId(8317941343245109563ULL), m_object->GetId());
}
TEST_F(LoadSpawnerComponentFromLegacyGameData, SliceAsset_ValuePreserved)
{
EXPECT_EQ(AZ::Uuid("{6F11134F-84C9-559F-AABA-3D1778656707}"), m_spawnerConfig.m_sliceAsset.GetId().m_guid);
}
TEST_F(LoadSpawnerComponentFromLegacyGameData, SpawnOnActivate_ValuePreserved)
{
EXPECT_TRUE(m_spawnerConfig.m_spawnOnActivate);
}
TEST_F(LoadSpawnerComponentFromLegacyGameData, DestroyOnDeactivate_ValuePreserved)
{
EXPECT_TRUE(m_spawnerConfig.m_destroyOnDeactivate);
}
#ifdef LMBR_CENTRAL_EDITOR
// Legacy SpawnerComponent from editor data (wrapped inside a GenericComponentWrapper)
// Should get converted into EditorSpawnerComponent
const char kWrappedLegacySpawnerComponent[] =
R"DELIMITER(<ObjectStream version="3">
<Class name="GenericComponentWrapper" field="element" type="{68D358CA-89B9-4730-8BA6-E181DEA28FDE}">
<Class name="EditorComponentBase" field="BaseClass1" version="1" type="{D5346BD4-7F20-444E-B370-327ACD03D4A0}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="6866719809809621109" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
</Class>
<Class name="SpawnerComponent" field="m_template" version="1" type="{8022A627-FA7D-4516-A155-657A0927A3CA}">
<Class name="AZ::Component" field="BaseClass1" type="{EDFCB2CF-F75D-43BE-B26B-F35821B29247}">
<Class name="AZ::u64" field="Id" value="0" type="{D6597933-47CD-4FC8-B911-63F3E2B0993A}"/>
</Class>
<Class name="Asset" field="Slice" value="id={3987FC80-0CF5-5A22-BE55-1EEDF382909E}:2,type={78802ABF-9595-463A-8D2B-D022F906F9B1},hint={slices/ai_walker.dynamicslice}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
<Class name="bool" field="SpawnOnActivate" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
<Class name="bool" field="DestroyOnDeactivate" value="true" type="{A0CA880C-AFE4-43CB-926C-59AC48496112}"/>
</Class>
</Class>
</ObjectStream>)DELIMITER";
class LoadSpawnerComponentFromLegacyEditorData
: public LoadReflectedObjectTest<AzToolsFramework::ToolsApplication, LmbrCentral::LmbrCentralEditorModule, AzToolsFramework::Components::GenericComponentWrapper>
{
public:
const char* GetSourceDataBuffer() const override { return kWrappedLegacySpawnerComponent; }
void SetUp() override
{
LoadReflectedObjectTest::SetUp();
// reset values from previous run
m_editorSpawnerComponent = nullptr;
m_readConfigSuccess = false;
if (m_object)
{
m_editorSpawnerComponent = azrtti_cast<LmbrCentral::EditorSpawnerComponent*>(m_object->GetTemplate());
if (m_editorSpawnerComponent)
{
m_readConfigSuccess = m_editorSpawnerComponent->GetConfiguration(m_spawnerConfig);
}
}
}
LmbrCentral::EditorSpawnerComponent* m_editorSpawnerComponent;
LmbrCentral::SpawnerConfig m_spawnerConfig;
bool m_readConfigSuccess;
};
TEST_F(LoadSpawnerComponentFromLegacyEditorData, Fixture_SanityCheck)
{
EXPECT_NE(nullptr, GetApplication());
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, ObjectStream_LoadsComponents)
{
EXPECT_NE(nullptr, m_object.get());
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, LegacySpawnerComponent_TurnedIntoEditorSpawnerComponent)
{
EXPECT_NE(nullptr, m_editorSpawnerComponent);
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, SpawnerConfig_SuccessfullyRead)
{
EXPECT_TRUE(m_readConfigSuccess);
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, SliceAsset_ValuePreserved)
{
EXPECT_EQ(AZ::Uuid("{3987FC80-0CF5-5A22-BE55-1EEDF382909E}"), m_spawnerConfig.m_sliceAsset.GetId().m_guid);
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, SpawnOnActivate_ValuePreserved)
{
EXPECT_TRUE(m_spawnerConfig.m_spawnOnActivate);
}
TEST_F(LoadSpawnerComponentFromLegacyEditorData, DestroyOnDeactivate_ValuePreserved)
{
EXPECT_TRUE(m_spawnerConfig.m_destroyOnDeactivate);
}
#endif // LMBR_CENTRAL_EDITOR