Initial commit

This commit is contained in:
alexpete
2021-03-05 11:26:34 -08:00
commit a10351f38d
27091 changed files with 5521199 additions and 0 deletions
@@ -0,0 +1,136 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#include <Prefab/Benchmark/PrefabBenchmarkFixture.h>
#include <AzCore/Component/TransformBus.h> //for create entity
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestDataUtils.h>
namespace Benchmark
{
void BM_Prefab::SetupPrefabSystem()
{
m_app = AZStd::make_unique<AzToolsFramework::ToolsApplication>();
ASSERT_TRUE(m_app != nullptr);
m_app->Start(AzFramework::Application::Descriptor());
AZ::Entity* systemEntity = m_app->FindEntity(AZ::SystemEntityId);
ASSERT_TRUE(systemEntity != nullptr);
m_prefabSystemComponent = systemEntity->FindComponent<AzToolsFramework::Prefab::PrefabSystemComponent>();
ASSERT_TRUE(m_prefabSystemComponent != nullptr);
m_mockIOActionValidator = AZStd::make_unique<UnitTest::MockPrefabFileIOActionValidator>();
ASSERT_TRUE(m_mockIOActionValidator != nullptr);
m_instanceUpdateExecutorInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get();
ASSERT_TRUE(m_instanceUpdateExecutorInterface != nullptr);
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
void BM_Prefab::TearDownPrefabSystem()
{
m_mockIOActionValidator.reset();
m_app.reset();
}
void BM_Prefab::ResetPrefabSystem()
{
TearDownPrefabSystem();
SetupPrefabSystem();
}
void BM_Prefab::SetUp(::benchmark::State & state)
{
AZ::Debug::TraceMessageBus::Handler::BusConnect();
UnitTest::AllocatorsBenchmarkFixture::SetUp(state);
SetupPrefabSystem();
}
void BM_Prefab::TearDown(::benchmark::State & state)
{
m_paths = {};
TearDownPrefabSystem();
UnitTest::AllocatorsBenchmarkFixture::TearDown(state);
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
}
AZ::Entity* BM_Prefab::CreateEntity(const char* entityName, const AZ::EntityId& parentId)
{
// Circumvent the EntityContext system and generate a new entity with a transformcomponent
AZ::Entity* newEntity = aznew AZ::Entity(entityName);
newEntity->CreateComponent(AZ::TransformComponentTypeId);
newEntity->Init();
newEntity->Activate();
SetEntityParent(newEntity->GetId(), parentId);
return newEntity;
}
void BM_Prefab::CreateEntities(const unsigned int entityCount, AZStd::vector<AZ::Entity*>& entities)
{
for (int entityIndex = 0; entityIndex < entityCount; ++entityIndex)
{
AZStd::string entityName = "TestEntity";
entityName = entityName + AZStd::to_string(entityIndex);
entities.emplace_back(CreateEntity(entityName.c_str()));
}
}
void BM_Prefab::SetEntityParent(const AZ::EntityId& entityId, const AZ::EntityId& parentId)
{
AZ::TransformBus::Event(entityId, &AZ::TransformBus::Events::SetParent, parentId);
}
void BM_Prefab::CreateFakePaths(const unsigned int pathCount)
{
//setup fake paths
for (int number = 0; number < pathCount; ++number)
{
AZStd::string path = m_pathString;
m_paths.push_back(path + AZStd::to_string(number) + "_" + AZStd::to_string(pathCount));
}
}
void BM_Prefab::SetUpMockValidatorForReadPrefab()
{
int pathCount = m_paths.size();
for (int number = 0; number < pathCount; ++number)
{
m_mockIOActionValidator->ReadPrefabDom(
m_paths[number], UnitTest::PrefabTestDomUtils::CreatePrefabDom());
}
}
void BM_Prefab::DeleteInstances(const AzToolsFramework::Prefab::InstanceList& instancesToDelete)
{
for (AzToolsFramework::Prefab::Instance* instanceToDelete : instancesToDelete)
{
ASSERT_TRUE(instanceToDelete);
delete instanceToDelete;
instanceToDelete = nullptr;
}
}
}
#endif
@@ -0,0 +1,67 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#pragma once
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <Prefab/MockPrefabFileIOActionValidator.h>
#include <Prefab/PrefabSystemComponent.h>
#include <Prefab/PrefabTestData.h>
#include <Prefab/PrefabTestUtils.h>
namespace Benchmark
{
using namespace UnitTest::PrefabTestUtils;
class BM_Prefab
: public UnitTest::AllocatorsBenchmarkFixture
, public UnitTest::TraceBusRedirector
{
protected:
using ::benchmark::Fixture::SetUp;
using ::benchmark::Fixture::TearDown;
void SetUp(::benchmark::State& state) override;
void TearDown(::benchmark::State& state) override;
AZ::Entity* CreateEntity(
const char* entityName,
const AZ::EntityId& parentId = AZ::EntityId());
void CreateEntities(const unsigned int entityCount, AZStd::vector<AZ::Entity*>& entities);
void SetEntityParent(const AZ::EntityId& entityId, const AZ::EntityId& parentId);
void CreateFakePaths(const unsigned int pathCount);
void SetUpMockValidatorForReadPrefab();
void DeleteInstances(const AzToolsFramework::Prefab::InstanceList& instances);
void SetupPrefabSystem();
void TearDownPrefabSystem();
void ResetPrefabSystem();
//prefab specific
AZStd::unique_ptr<AzToolsFramework::ToolsApplication> m_app;
AzToolsFramework::Prefab::PrefabSystemComponent* m_prefabSystemComponent = nullptr;
AzToolsFramework::Prefab::PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
AzToolsFramework::Prefab::InstanceUpdateExecutorInterface* m_instanceUpdateExecutorInterface = nullptr;
const char* m_pathString = "path/to/template";
AZStd::vector<AZStd::string> m_paths;
AZStd::unique_ptr <UnitTest::MockPrefabFileIOActionValidator> m_mockIOActionValidator;
};
}
#endif
@@ -0,0 +1,188 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#include <Prefab/Benchmark/PrefabBenchmarkFixture.h>
namespace Benchmark
{
using BM_PrefabCreate = BM_Prefab;
using namespace AzToolsFramework::Prefab;
BENCHMARK_DEFINE_F(BM_PrefabCreate, CreatePrefabs_SingleEntityEach)(::benchmark::State& state)
{
const unsigned int numEntities = state.range();
const unsigned int numInstances = numEntities;
CreateFakePaths(numInstances);
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<AZ::Entity*> entities;
CreateEntities(numEntities, entities);
AZStd::vector<AZStd::unique_ptr<Instance>> newInstances;
state.ResumeTiming();
for (int instanceCounter = 0; instanceCounter < numInstances; ++instanceCounter)
{
newInstances.push_back(m_prefabSystemComponent->CreatePrefab(
{ entities[instanceCounter] },
{},
m_paths[instanceCounter]));
}
state.PauseTiming();
newInstances.clear();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
BENCHMARK_REGISTER_F(BM_PrefabCreate, CreatePrefabs_SingleEntityEach)
->RangeMultiplier(10)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
BENCHMARK_DEFINE_F(BM_PrefabCreate, CreatePrefab_FromEntities)(::benchmark::State& state)
{
const unsigned int numEntities = state.range();
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<AZ::Entity*> entities;
CreateEntities(numEntities, entities);
state.ResumeTiming();
AZStd::unique_ptr<Instance> instance = m_prefabSystemComponent->CreatePrefab(
entities
, {}
, m_pathString);
state.PauseTiming();
instance.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numEntities);
}
BENCHMARK_REGISTER_F(BM_PrefabCreate, CreatePrefab_FromEntities)
->RangeMultiplier(10)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
BENCHMARK_DEFINE_F(BM_PrefabCreate, CreatePrefab_FromSingleDepthInstances)(::benchmark::State& state)
{
const unsigned int numInstancesToAdd = state.range();
const unsigned int numEntities = numInstancesToAdd;
// Create fake paths for all the nested instances
// plus the instance receiving them
CreateFakePaths(numInstancesToAdd + 1);
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<AZ::Entity*> entities;
CreateEntities(numEntities, entities);
AZStd::vector<AZStd::unique_ptr<Instance>> testInstances;
testInstances.resize(numInstancesToAdd);
for (int instanceCounter = 0; instanceCounter < numInstancesToAdd; ++instanceCounter)
{
testInstances[instanceCounter] = (m_prefabSystemComponent->CreatePrefab(
{ entities[instanceCounter] }
, {}
, m_paths[instanceCounter]));
}
state.ResumeTiming();
AZStd::unique_ptr<Instance> nestedInstance = m_prefabSystemComponent->CreatePrefab(
{}
, AZStd::move(testInstances)
, m_paths.back());
state.PauseTiming();
nestedInstance.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstancesToAdd);
}
BENCHMARK_REGISTER_F(BM_PrefabCreate, CreatePrefab_FromSingleDepthInstances)
->RangeMultiplier(10)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
BENCHMARK_DEFINE_F(BM_PrefabCreate, CreatePrefab_FromLinearNestingOfInstances)(::benchmark::State& state)
{
const unsigned int numInstances = state.range();
// Create fake paths for all the nested instances
// plus the root instance
CreateFakePaths(numInstances + 1);
for (auto _ : state)
{
state.PauseTiming();
AZStd::unique_ptr<Instance> nestedInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{ CreateEntity("Entity1") },
{},
m_paths.back());
state.ResumeTiming();
for (int instanceCounter = 0; instanceCounter < numInstances; ++instanceCounter)
{
nestedInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(nestedInstanceRoot) ),
m_paths[instanceCounter]);
}
state.PauseTiming();
nestedInstanceRoot.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
}
#endif
@@ -0,0 +1,55 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#include <Prefab/Benchmark/PrefabBenchmarkFixture.h>
namespace Benchmark
{
using BM_PrefabInstantiate = BM_Prefab;
using namespace AzToolsFramework::Prefab;
BENCHMARK_DEFINE_F(BM_PrefabInstantiate, InstantiatePrefab_SingleEntityInstance)(::benchmark::State& state)
{
const unsigned int numInstances = state.range();
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab(
{ CreateEntity("Entity1") },
{},
m_pathString);
TemplateId templateToInstantiateId = firstInstance->GetTemplateId();
for (auto _ : state)
{
state.PauseTiming();
AZStd::vector<AZStd::unique_ptr<Instance>> newInstances;
newInstances.resize(numInstances);
state.ResumeTiming();
for (int instanceCounter = 0; instanceCounter < numInstances; ++instanceCounter)
{
newInstances[instanceCounter] = m_prefabSystemComponent->InstantiatePrefab(templateToInstantiateId);
}
}
state.SetComplexityN(numInstances);
}
BENCHMARK_REGISTER_F(BM_PrefabInstantiate, InstantiatePrefab_SingleEntityInstance)
->RangeMultiplier(10)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
}
#endif
@@ -0,0 +1,57 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#include <Prefab/Benchmark/PrefabBenchmarkFixture.h>
namespace Benchmark
{
using BM_PrefabLoad = BM_Prefab;
using namespace AzToolsFramework::Prefab;
BENCHMARK_DEFINE_F(BM_PrefabLoad, LoadPrefab_Basic)(::benchmark::State& state)
{
const unsigned int numTemplates = state.range();
CreateFakePaths(numTemplates);
for (auto _ : state)
{
state.PauseTiming();
SetUpMockValidatorForReadPrefab();
m_prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
state.ResumeTiming();
for (int templateCounter = 0; templateCounter < numTemplates; ++templateCounter)
{
m_prefabLoaderInterface->LoadTemplate(m_paths[templateCounter]);
}
state.PauseTiming();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numTemplates);
}
BENCHMARK_REGISTER_F(BM_PrefabLoad, LoadPrefab_Basic)
->RangeMultiplier(10)
->Range(100, 1000)
->Unit(benchmark::kMillisecond)
->Complexity();
}
#endif
@@ -0,0 +1,256 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
*remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#if defined(HAVE_BENCHMARK)
#include <Prefab/Benchmark/PrefabBenchmarkFixture.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
namespace Benchmark
{
using BM_PrefabUpdateInstances = BM_Prefab;
using namespace AzToolsFramework::Prefab;
BENCHMARK_DEFINE_F(BM_PrefabUpdateInstances, UpdateInstances_SingeEntityInstances)(::benchmark::State& state)
{
const unsigned int numInstances = state.range();
CreateFakePaths(2);
const auto& nestedTemplatePath = m_paths.front();
const auto& enclosingTemplatePath = m_paths.back();
for (auto _ : state)
{
state.PauseTiming();
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> nestedInstance = m_prefabSystemComponent->CreatePrefab(
{ entity },
{},
nestedTemplatePath);
AZStd::unique_ptr<Instance> enclosingInstance = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(nestedInstance) ),
enclosingTemplatePath);
TemplateId templateToInstantiateId = enclosingInstance->GetTemplateId();
{
AZStd::vector<AZStd::unique_ptr<Instance>> newInstances;
newInstances.resize(numInstances);
for (unsigned int instanceCounter = 0; instanceCounter < numInstances; ++instanceCounter)
{
newInstances[instanceCounter] = m_prefabSystemComponent->InstantiatePrefab(templateToInstantiateId);
}
entity->SetName("Updated Entity");
PrefabDom updatedPrefabDom;
PrefabDomUtils::StoreInstanceInPrefabDom(*enclosingInstance, updatedPrefabDom);
PrefabDom& enclosingTemplatePrefabDom = m_prefabSystemComponent->FindTemplateDom(templateToInstantiateId);
enclosingTemplatePrefabDom.CopyFrom(updatedPrefabDom, enclosingTemplatePrefabDom.GetAllocator());
state.ResumeTiming();
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(templateToInstantiateId);
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
state.PauseTiming();
}
enclosingInstance.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
BENCHMARK_REGISTER_F(BM_PrefabUpdateInstances, UpdateInstances_SingeEntityInstances)
->RangeMultiplier(10)
->Range(100, 10000)
->Unit(benchmark::kMillisecond)
->Complexity();
BENCHMARK_DEFINE_F(BM_PrefabUpdateInstances, UpdateInstances_SingleLinearNestingOfInstances)(::benchmark::State& state)
{
const unsigned int maxDepth = state.range();
CreateFakePaths(maxDepth);
const unsigned int numInstances = maxDepth;
for (auto _ : state)
{
state.PauseTiming();
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{ entity },
{},
m_paths.back());
for (unsigned int currentDepth = 1; currentDepth < maxDepth; ++currentDepth)
{
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(currentInstanceRoot) ),
m_paths[currentDepth - 1]);
}
entity->SetName("Updated Entity");
PrefabDom updatedPrefabDom;
PrefabDomUtils::StoreInstanceInPrefabDom(*currentInstanceRoot, updatedPrefabDom);
const TemplateId rootTemplateId = currentInstanceRoot->GetTemplateId();
PrefabDom& rootTemplatePrefabDom = m_prefabSystemComponent->FindTemplateDom(rootTemplateId);
rootTemplatePrefabDom.CopyFrom(updatedPrefabDom, rootTemplatePrefabDom.GetAllocator());
state.ResumeTiming();
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(rootTemplateId);
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
state.PauseTiming();
currentInstanceRoot.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
BENCHMARK_DEFINE_F(BM_PrefabUpdateInstances, UpdateInstances_MultipleLinearNestingOfInstances)(::benchmark::State& state)
{
const unsigned int numRootInstances = state.range();
const unsigned int maxDepth = state.range();
CreateFakePaths(maxDepth);
const unsigned int numInstances = numRootInstances * maxDepth;
for (auto _ : state)
{
state.PauseTiming();
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{ entity },
{},
m_paths.back());
for (unsigned int currentDepth = 0; currentDepth < maxDepth - 1; ++currentDepth)
{
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(currentInstanceRoot) ),
m_paths[currentDepth]);
}
const TemplateId rootTemplateId = currentInstanceRoot->GetTemplateId();
{
AZStd::vector<AZStd::unique_ptr<Instance>> newInstances;
newInstances.resize(numRootInstances - 1);
for (unsigned int instanceCounter = 0; instanceCounter < numRootInstances - 1; ++instanceCounter)
{
newInstances[instanceCounter] = m_prefabSystemComponent->InstantiatePrefab(rootTemplateId);
}
entity->SetName("Updated Entity");
PrefabDom updatedPrefabDom;
PrefabDomUtils::StoreInstanceInPrefabDom(*currentInstanceRoot, updatedPrefabDom);
PrefabDom& rootTemplatePrefabDom = m_prefabSystemComponent->FindTemplateDom(rootTemplateId);
rootTemplatePrefabDom.CopyFrom(updatedPrefabDom, rootTemplatePrefabDom.GetAllocator());
state.ResumeTiming();
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(rootTemplateId);
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
state.PauseTiming();
}
currentInstanceRoot.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
BENCHMARK_DEFINE_F(BM_PrefabUpdateInstances, UpdateInstances_BinaryTreeNestedInstanceHierarchy)(::benchmark::State& state)
{
const unsigned int maxDepth = state.range();
CreateFakePaths(maxDepth);
const unsigned int numInstances = (1 << maxDepth) - 1;
for (auto _ : state)
{
state.PauseTiming();
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{ entity },
{},
m_paths.back());
for (unsigned int currentDepth = 0; currentDepth < maxDepth - 1; ++currentDepth)
{
AZStd::unique_ptr<Instance> extraNestedInstance =
m_prefabSystemComponent->InstantiatePrefab(currentInstanceRoot->GetTemplateId());
currentInstanceRoot = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(currentInstanceRoot), AZStd::move(extraNestedInstance) ),
m_paths[currentDepth]);
}
entity->SetName("Updated Entity");
PrefabDom updatedPrefabDom;
PrefabDomUtils::StoreInstanceInPrefabDom(*currentInstanceRoot, updatedPrefabDom);
const TemplateId rootTemplateId = currentInstanceRoot->GetTemplateId();
PrefabDom& rootTemplatePrefabDom = m_prefabSystemComponent->FindTemplateDom(rootTemplateId);
rootTemplatePrefabDom.CopyFrom(updatedPrefabDom, rootTemplatePrefabDom.GetAllocator());
state.ResumeTiming();
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(rootTemplateId);
m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
state.PauseTiming();
currentInstanceRoot.reset();
ResetPrefabSystem();
state.ResumeTiming();
}
state.SetComplexityN(numInstances);
}
BENCHMARK_REGISTER_F(BM_PrefabUpdateInstances, UpdateInstances_BinaryTreeNestedInstanceHierarchy)
->DenseRange(8, 12, 2)
->Unit(benchmark::kMillisecond)
->Complexity();
}
#endif
@@ -0,0 +1,93 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/MockPrefabFileIOActionValidator.h>
#include <AzCore/JSON/prettywriter.h>
namespace UnitTest
{
MockPrefabFileIOActionValidator::MockPrefabFileIOActionValidator()
{
// Cache the existing file io instance and build our mock file io
m_priorFileIO = AZ::IO::FileIOBase::GetInstance();
m_fileIOMock = AZStd::make_unique<testing::NiceMock<AZ::IO::MockFileIOBase>>();
// Swap out current file io instance for our mock
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_fileIOMock.get());
// Setup the default returns for our mock file io calls
AZ::IO::MockFileIOBase::InstallDefaultReturns(*m_fileIOMock.get());
}
MockPrefabFileIOActionValidator::~MockPrefabFileIOActionValidator()
{
// Restore our original file io instance
AZ::IO::FileIOBase::SetInstance(nullptr);
AZ::IO::FileIOBase::SetInstance(m_priorFileIO);
}
void MockPrefabFileIOActionValidator::ReadPrefabDom(
const AZStd::string& prefabFilePath,
const AzToolsFramework::Prefab::PrefabDom& prefabFileContentDom,
AZ::IO::ResultCode expectedReadResultCode,
AZ::IO::ResultCode expectedOpenResultCode,
AZ::IO::ResultCode expectedSizeResultCode,
AZ::IO::ResultCode expectedCloseResultCode)
{
rapidjson::StringBuffer prefabFileContentBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(prefabFileContentBuffer);
prefabFileContentDom.Accept(writer);
AZStd::string prefabFileContent(prefabFileContentBuffer.GetString());
ReadPrefabDom(prefabFilePath, prefabFileContent,
expectedReadResultCode, expectedOpenResultCode, expectedSizeResultCode, expectedCloseResultCode);
}
void MockPrefabFileIOActionValidator::ReadPrefabDom(
const AZStd::string& prefabFilePath,
const AZStd::string& prefabFileContent,
AZ::IO::ResultCode expectedReadResultCode,
AZ::IO::ResultCode expectedOpenResultCode,
AZ::IO::ResultCode expectedSizeResultCode,
AZ::IO::ResultCode expectedCloseResultCode)
{
AZ::IO::HandleType fileHandle = m_fileHandleCounter++;
EXPECT_CALL(*m_fileIOMock.get(), Open(
testing::StrEq(prefabFilePath.c_str()), testing::_, testing::_))
.WillRepeatedly(
testing::DoAll(
testing::SetArgReferee<2>(fileHandle),
testing::Return(AZ::IO::Result(expectedOpenResultCode))));
EXPECT_CALL(*m_fileIOMock.get(), Size(fileHandle, testing::_))
.WillRepeatedly(
testing::DoAll(
testing::SetArgReferee<1>(prefabFileContent.size()),
testing::Return(AZ::IO::Result(expectedSizeResultCode))));
EXPECT_CALL(*m_fileIOMock.get(), Read(fileHandle, testing::_, prefabFileContent.size(), testing::_, testing::_))
.WillRepeatedly(testing::Invoke([prefabFileContent, expectedReadResultCode](AZ::IO::HandleType, void* buffer, AZ::u64, bool, AZ::u64* bytesRead)
{
memcpy(buffer, prefabFileContent.data(), prefabFileContent.size());
*bytesRead = prefabFileContent.size();
return AZ::IO::Result(expectedReadResultCode);
}));
EXPECT_CALL(*m_fileIOMock.get(), Close(fileHandle))
.WillRepeatedly(testing::Return(AZ::IO::Result(expectedCloseResultCode)));
}
}
@@ -0,0 +1,54 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/functional.h>
#include <AzCore/UnitTest/Mocks/MockFileIOBase.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
namespace UnitTest
{
class MockPrefabFileIOActionValidator
{
public:
MockPrefabFileIOActionValidator();
~MockPrefabFileIOActionValidator();
void ReadPrefabDom(
const AZStd::string& prefabFilePath,
const AzToolsFramework::Prefab::PrefabDom& prefabFileContentDom,
AZ::IO::ResultCode expectedReadResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedOpenResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedSizeResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedCloseResultCode = AZ::IO::ResultCode::Success);
void ReadPrefabDom(
const AZStd::string& prefabFilePath,
const AZStd::string& prefabFileContent,
AZ::IO::ResultCode expectedReadResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedOpenResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedSizeResultCode = AZ::IO::ResultCode::Success,
AZ::IO::ResultCode expectedCloseResultCode = AZ::IO::ResultCode::Success);
private:
// A counter for creating new file handles.
AZStd::atomic<AZ::IO::HandleType> m_fileHandleCounter = 1u;
// A mock file io for testing.
AZStd::unique_ptr<testing::NiceMock<AZ::IO::MockFileIOBase>> m_fileIOMock;
// A cache for the existing file io.
AZ::IO::FileIOBase* m_priorFileIO = nullptr;
};
}
@@ -0,0 +1,266 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzFramework/Components/TransformComponent.h>
namespace UnitTest
{
using PrefabInstanceToTemplateTests = PrefabTestFixture;
TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_UpdateEntityOnInstance)
{
//create template with single entity
const char* newEntityName = "New Entity";
AZ::Entity* newEntity = CreateEntity(newEntityName, false);
ASSERT_TRUE(newEntity);
AZ::EntityId entityId = newEntity->GetId();
//add a transform component for testing purposes
newEntity->CreateComponent(AZ::EditorTransformComponentTypeId);
newEntity->Init();
newEntity->Activate();
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
ASSERT_TRUE(firstInstance);
//get template id
TemplateId templateId = firstInstance->GetTemplateId();
//instantiate second instance
AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
ASSERT_TRUE(secondInstance);
//create document with before change snapshot
PrefabDom entityDomBeforeUpdate;
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBeforeUpdate, *newEntity);
//update values on entity
const float updatedXValue = 5.0f;
AZ::TransformBus::Event(entityId, &AZ::TransformInterface::SetWorldX, updatedXValue);
//create document with after change snapshot
PrefabDom entityDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomAfterUpdate, *newEntity);
//generate patch
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, entityDomBeforeUpdate, entityDomAfterUpdate);
//update template
m_instanceToTemplateInterface->PatchEntityInTemplate(patch, entityId);
//activate the entity so we can access via transform bus
secondInstance->InitializeNestedEntities();
secondInstance->ActivateNestedEntities();
//get the entity id
AZStd::vector<AZ::EntityId> entityIdVector;
secondInstance->GetEntityIds([&entityIdVector](const AZ::EntityId& entityId)
{
entityIdVector.push_back(entityId);
return true;
});
EXPECT_EQ(entityIdVector.size(), 1);
AZStd::optional<AZ::EntityId> secondEntityId = entityIdVector[0];
//verify template updated correctly
//get the values from the transform on the entity
float confirmXValue = 0.0f;
AZ::TransformBus::EventResult(confirmXValue, entityId, &AZ::TransformInterface::GetWorldX);
AZ::TransformBus::EventResult(confirmXValue, secondEntityId.value(), &AZ::TransformInterface::GetWorldX);
ASSERT_TRUE(confirmXValue == updatedXValue);
}
TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_AddEntityToInstance)
{
//create template with single entity
const char* newEntityName = "New Entity";
AZ::Entity* newEntity = CreateEntity(newEntityName, false);
ASSERT_TRUE(newEntity);
//create a first instance where the entity will be added
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/path");
ASSERT_TRUE(firstInstance);
//get template id
TemplateId templateId = firstInstance->GetTemplateId();
//instantiate second instance for checking if propogation works
AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
ASSERT_TRUE(secondInstance);
//create document with before change snapshot
PrefabDom instanceDomBeforeUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, *firstInstance);
//add entity to instance
firstInstance->AddEntity(*newEntity);
//create document with after change snapshot
PrefabDom instanceDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfterUpdate, *firstInstance);
//generate patch
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
//update template
m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
//get the entity id
AZStd::vector<AZ::EntityId> entityIdVector;
secondInstance->GetEntityIds([&entityIdVector](const AZ::EntityId& entityId)
{
entityIdVector.push_back(entityId);
return true;
});
EXPECT_EQ(entityIdVector.size(), 1);
}
TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_RemoveEntityFromInstance)
{
//create template with single entity
const char* newEntityName = "New Entity";
AZ::Entity* newEntity = CreateEntity(newEntityName, false);
ASSERT_TRUE(newEntity);
AZ::EntityId entityId = newEntity->GetId();
//create a first instance where the entity will be removed
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
ASSERT_TRUE(firstInstance);
//get template id
TemplateId templateId = firstInstance->GetTemplateId();
//instantiate second instance for checking if propogation works
AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
ASSERT_TRUE(secondInstance);
//create document with before change snapshot
PrefabDom instanceDomBeforeUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, *firstInstance);
//remove entity from instance
firstInstance->DetachEntity(entityId);
//create document with after change snapshot
PrefabDom instanceDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfterUpdate, *firstInstance);
//generate patch
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
//update template
m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
//get the entity id
AZStd::vector<AZ::EntityId> entityIdVector;
secondInstance->GetEntityIds([&entityIdVector](const AZ::EntityId& entityId)
{
entityIdVector.push_back(entityId);
return true;
});
EXPECT_EQ(entityIdVector.size(), 0);
}
TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_AddInstanceToInstance)
{
//create a first instance where the instance will be added
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/path");
ASSERT_TRUE(firstInstance);
//get template id
TemplateId templateId = firstInstance->GetTemplateId();
//instantiate second instance for checking if propogation works
AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
ASSERT_TRUE(secondInstance);
//create document with before change snapshot
PrefabDom instanceDomBeforeUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, *firstInstance);
//create new instance and get alias
AZStd::unique_ptr<Instance> addedInstance = m_prefabSystemComponent->CreatePrefab({}, {}, "test/pathtest");
//add instance to instance
InstanceOptionalConstReference addedInstanceRef { firstInstance->AddInstance(AZStd::move(addedInstance)) };
const AzToolsFramework::Prefab::InstanceAlias addedAlias = addedInstanceRef->get().GetInstanceAlias();
//create document with after change snapshot
PrefabDom instanceDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfterUpdate, *firstInstance);
//generate patch
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
//update template
m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
EXPECT_NE(secondInstance->FindNestedInstance(addedAlias), AZStd::nullopt);
}
TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_RemoveInstanceFromInstance)
{
AZStd::unique_ptr<Instance> addedInstancePtr = m_prefabSystemComponent->CreatePrefab({}, {}, "test/pathtest");
Instance& addedInstance = *addedInstancePtr;
//create a first instance where the instance will be removed
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(addedInstancePtr) ), "test/path");
ASSERT_TRUE(firstInstance);
//get added instance alias
const AzToolsFramework::Prefab::InstanceAlias addedAlias = addedInstance.GetInstanceAlias();
//get template id
TemplateId templateId = firstInstance->GetTemplateId();
//instantiate second instance for checking if propogation works
AZStd::unique_ptr<Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(templateId);
ASSERT_TRUE(secondInstance);
//create document with before change snapshot
PrefabDom instanceDomBeforeUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomBeforeUpdate, *firstInstance);
//remove instance from instance
firstInstance->DetachNestedInstance(addedAlias);
//create document with after change snapshot
PrefabDom instanceDomAfterUpdate;
m_instanceToTemplateInterface->GenerateDomForInstance(instanceDomAfterUpdate, *firstInstance);
//generate patch
PrefabDom patch;
m_instanceToTemplateInterface->GeneratePatch(patch, instanceDomBeforeUpdate, instanceDomAfterUpdate);
//update template
m_instanceToTemplateInterface->PatchTemplate(patch, templateId);
EXPECT_EQ(secondInstance->FindNestedInstance(addedAlias), AZStd::nullopt);
}
}
@@ -0,0 +1,84 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
using PrefabInstantiateTest = PrefabTestFixture;
TEST_F(PrefabInstantiateTest, PrefabInstantiate_InstantiateInvalidTemplate_InstantiateFails)
{
EXPECT_FALSE(m_prefabSystemComponent->InstantiatePrefab(AzToolsFramework::Prefab::InvalidTemplateId));
}
TEST_F(PrefabInstantiateTest, PrefabInstantiate_NoNestingTemplate_InstantiateSucceeds)
{
AZ::Entity* newEntity = CreateEntity("New Entity");
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
ASSERT_TRUE(firstInstance);
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> secondInstance = m_prefabSystemComponent->InstantiatePrefab(firstInstance->GetTemplateId());
ASSERT_TRUE(secondInstance);
CompareInstances(*firstInstance, *secondInstance);
}
TEST_F(PrefabInstantiateTest, PrefabInstantiate_TripleNestingTemplate_InstantiateSucceeds)
{
AZ::Entity* newEntity = CreateEntity("New Entity");
// Build a 3 level deep nested Template
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path1");
ASSERT_TRUE(firstInstance);
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> secondInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(firstInstance) ), "test/path2");
ASSERT_TRUE(secondInstance);
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> thirdInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(secondInstance) ), "test/path3");
ASSERT_TRUE(thirdInstance);
//Instantiate it
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> fourthInstance =
m_prefabSystemComponent->InstantiatePrefab(thirdInstance->GetTemplateId());
ASSERT_TRUE(fourthInstance);
CompareInstances(*thirdInstance, *fourthInstance, false);
}
TEST_F(PrefabInstantiateTest, PrefabInstantiate_Instantiate10Times_InstantiatesSucceed)
{
AZ::Entity* newEntity = CreateEntity("New Entity");
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path");
// Store the generated instances so that the unique_ptrs are destroyed at the end of the test
// This allows us to have all the instances around at the same time
AZStd::vector<AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>> newInstances;
for (int instanceCount = 0; instanceCount < 10; ++instanceCount)
{
AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>
newInstance(m_prefabSystemComponent->InstantiatePrefab(firstInstance->GetTemplateId()));
ASSERT_TRUE(newInstance);
CompareInstances(*firstInstance, *newInstance);
newInstances.push_back(AZStd::move(newInstance));
}
}
}
@@ -0,0 +1,291 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/MockPrefabFileIOActionValidator.h>
#include <Prefab/PrefabTestData.h>
#include <Prefab/PrefabTestDataUtils.h>
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
using PrefabLoadTemplateTest = PrefabTestFixture;
TEST_F(PrefabLoadTemplateTest, LoadTemplate_TemplateWithNoNestedInstance)
{
TemplateData templateData;
templateData.m_filePath = "path/to/template/with/no/nested/instance";
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(
templateData.m_filePath, PrefabTestDomUtils::CreatePrefabDom());
templateData.m_id = m_prefabLoaderInterface->LoadTemplate(templateData.m_filePath);
PrefabTestDataUtils::ValidateTemplateLoad(templateData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_TemplateWithOneNestedInstance_WithNoPatches)
{
TemplateData sourceTemplateData;
sourceTemplateData.m_filePath = "path/to/template/with/no/nested/instance";
TemplateData targetTemplateData;
targetTemplateData.m_filePath = "path/to/template/with/one/nested/instance";
InstanceData targetTemplateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
"sourceTemplateInstance", sourceTemplateData.m_filePath);
targetTemplateData.m_instancesData[targetTemplateInstanceData.m_name] = targetTemplateInstanceData;
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(
sourceTemplateData.m_filePath, PrefabTestDomUtils::CreatePrefabDom());
mockIOActionValidator.ReadPrefabDom(
targetTemplateData.m_filePath, PrefabTestDomUtils::CreatePrefabDom({ targetTemplateInstanceData }));
targetTemplateData.m_id = m_prefabLoaderInterface->LoadTemplate(targetTemplateData.m_filePath);
sourceTemplateData.m_id = m_prefabSystemComponent->GetTemplateIdFromFilePath(sourceTemplateData.m_filePath);
LinkData linkData = PrefabTestDataUtils::CreateLinkData(
targetTemplateInstanceData, sourceTemplateData.m_id, targetTemplateData.m_id);
PrefabTestDataUtils::CheckIfTemplatesConnected(sourceTemplateData, targetTemplateData, linkData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_TemplateDependingOnItself_TemplateLoadedWithErrorsAdded)
{
TemplateData templateData;
templateData.m_filePath = "path/to/template/depending/on/itself";
auto templatePrefabDom = PrefabTestDomUtils::CreatePrefabDom({
PrefabTestDataUtils::CreateInstanceDataWithNoPatches("instance", templateData.m_filePath) });
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(templateData.m_filePath, templatePrefabDom);
templateData.m_id = m_prefabLoaderInterface->LoadTemplate(templateData.m_filePath);
templateData.m_isLoadedWithErrors = true;
PrefabTestDataUtils::ValidateTemplateLoad(templateData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_SourceTemplateDependingOnTargetTemplate_TemplatesLoadedWithErrorsAdded)
{
// Prepare two Template Data which has cyclical dependency between them.
// Set data of expected source Template.
TemplateData sourceTemplateData;
sourceTemplateData.m_filePath = "path/to/source/template";
// Set data of expected target Template.
TemplateData targetTemplateData;
targetTemplateData.m_filePath = "path/to/target/template";
// Set data of expected nested Instance in source Template.
// The Template of this Instance is target Template so that
// source Template depends on target Template.
InstanceData sourceTemplateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
"targetTemplateInstance", targetTemplateData.m_filePath);
// Data of expected nested Instance in target Template.
// The Template of this Instance is source Template so that
// target Template depends on source Template.
InstanceData targetTemplateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
"sourceTemplateInstance", sourceTemplateData.m_filePath);
// Set expected target Template's Instance data.
// There should be NO Instance data in expected source Template
// since cyclical dependency will be detected and LoadTemplate will stop.
targetTemplateData.m_instancesData[targetTemplateInstanceData.m_name] = targetTemplateInstanceData;
// Create PrefabDoms for both source/target Template.
auto sourceTemplatePrefabDom = PrefabTestDomUtils::CreatePrefabDom({ sourceTemplateInstanceData });
auto targetTemplatePrefabDom = PrefabTestDomUtils::CreatePrefabDom({ targetTemplateInstanceData });
// The mock file IO will let the PrefabSystemComponent read expected PrefabDoms while calling LoadTemplate.
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(
sourceTemplateData.m_filePath, sourceTemplatePrefabDom);
mockIOActionValidator.ReadPrefabDom(
targetTemplateData.m_filePath, targetTemplatePrefabDom);
// Load target and source Templates and get their Ids.
targetTemplateData.m_id = m_prefabLoaderInterface->LoadTemplate(targetTemplateData.m_filePath);
sourceTemplateData.m_id = m_prefabSystemComponent->GetTemplateIdFromFilePath(sourceTemplateData.m_filePath);
// Because of cyclical dependency, the two Templates should be loaded with errors.
sourceTemplateData.m_isLoadedWithErrors = true;
targetTemplateData.m_isLoadedWithErrors = true;
// Set expected data of Link from source Template to target Template.
// There should be no Link from target Template to source Template.
LinkData linkData = PrefabTestDataUtils::CreateLinkData(
targetTemplateInstanceData, sourceTemplateData.m_id, targetTemplateData.m_id);
// Verify if actual source/target Templates have the expected Template data.
// Also check if actual Link from source to target has the expected Link data.
PrefabTestDataUtils::CheckIfTemplatesConnected(sourceTemplateData, targetTemplateData, linkData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_InstanceWithEmptySource_TemplateLoadedWithErrorsAdded)
{
TemplateData templateData;
templateData.m_filePath = "path/to/template/with/no/instance/source";
templateData.m_isLoadedWithErrors = true;
auto templatePrefabDom = PrefabTestDomUtils::CreatePrefabDom({
PrefabTestDataUtils::CreateInstanceDataWithNoPatches("templateInstance", "") });
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(templateData.m_filePath, templatePrefabDom);
templateData.m_id = m_prefabLoaderInterface->LoadTemplate(templateData.m_filePath);
PrefabTestDataUtils::ValidateTemplateLoad(templateData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_InstanceWithEmptyName_TemplateLoadedWithErrorsAdded)
{
TemplateData templateData;
templateData.m_filePath = "path/to/template/with/no/instance/name";
templateData.m_isLoadedWithErrors = true;
auto templatePrefabDom = PrefabTestDomUtils::CreatePrefabDom({
PrefabTestDataUtils::CreateInstanceDataWithNoPatches("", "template/instance/source") });
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(templateData.m_filePath, templatePrefabDom);
templateData.m_id = m_prefabLoaderInterface->LoadTemplate(templateData.m_filePath);
PrefabTestDataUtils::ValidateTemplateLoad(templateData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_OpenSourceTemplateFileFailed_TemplateLoadedWithErrorsAdded)
{
TemplateData templateData;
templateData.m_filePath = "path/to/template";
templateData.m_isLoadedWithErrors = true;
InstanceData templateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
"templateInstance", "wrong/path");
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(
templateData.m_filePath,
PrefabTestDomUtils::CreatePrefabDom({ templateInstanceData }));
mockIOActionValidator.ReadPrefabDom(
templateInstanceData.m_source, PrefabTestDomUtils::CreatePrefabDom(),
AZ::IO::ResultCode::Success, AZ::IO::ResultCode::Error);
templateData.m_id = m_prefabLoaderInterface->LoadTemplate(templateData.m_filePath);
PrefabTestDataUtils::ValidateTemplateLoad(templateData);
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_MultiLevelTemplates_WithNoPatches)
{
MockPrefabFileIOActionValidator mockIOActionValidator;
AZStd::vector<TemplateData> templatesData;
const int nestedHierarchyLevel = 3;
for (int i = 0; i < nestedHierarchyLevel; i++)
{
TemplateData templateData;
templateData.m_filePath = AZStd::string::format("path/to/level/%d/template", i);
templatesData.emplace_back(templateData);
if (i != 0)
{
InstanceData templateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
AZStd::string::format("level%dTemplateInstance", i), templatesData[i - 1].m_filePath);
templatesData[i].m_instancesData[templateInstanceData.m_name] = templateInstanceData;
mockIOActionValidator.ReadPrefabDom(
templatesData[i].m_filePath, PrefabTestDomUtils::CreatePrefabDom({ templateInstanceData }));
}
else
{
mockIOActionValidator.ReadPrefabDom(
templatesData[i].m_filePath, PrefabTestDomUtils::CreatePrefabDom());
}
}
templatesData.back().m_id = m_prefabLoaderInterface->LoadTemplate(templatesData.back().m_filePath);
for (int i = nestedHierarchyLevel - 2; i >= 0; i--)
{
templatesData[i].m_id = m_prefabSystemComponent->GetTemplateIdFromFilePath(templatesData[i].m_filePath);
LinkData linkData = PrefabTestDataUtils::CreateLinkData(
templatesData[i + 1].m_instancesData[AZStd::string::format("level%dTemplateInstance", i + 1)],
templatesData[i].m_id, templatesData[i + 1].m_id);
PrefabTestDataUtils::CheckIfTemplatesConnected(templatesData[i], templatesData[i + 1], linkData);
}
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_TemplateWithMultiInstances_WithNoPatches)
{
MockPrefabFileIOActionValidator mockIOActionValidator;
AZStd::vector<TemplateData> sourceTemplatesData;
AZStd::vector<InstanceData> targetTemplateInstancesData;
TemplateData targetTemplateData;
targetTemplateData.m_filePath = "path/to/target/template";
const int numInstances = 3;
for (int i = 0; i < numInstances; i++)
{
TemplateData sourceTemplateData;
sourceTemplateData.m_filePath = AZStd::string::format("path/to/source/%d/template", i);
InstanceData targetTemplateInstanceData = PrefabTestDataUtils::CreateInstanceDataWithNoPatches(
AZStd::string::format("source%dTemplateInstance", i), sourceTemplateData.m_filePath);
targetTemplateData.m_instancesData[targetTemplateInstanceData.m_name] = targetTemplateInstanceData;
mockIOActionValidator.ReadPrefabDom(
sourceTemplateData.m_filePath, PrefabTestDomUtils::CreatePrefabDom());
sourceTemplatesData.emplace_back(sourceTemplateData);
targetTemplateInstancesData.emplace_back(targetTemplateInstanceData);
}
mockIOActionValidator.ReadPrefabDom(
targetTemplateData.m_filePath, PrefabTestDomUtils::CreatePrefabDom(targetTemplateInstancesData));
targetTemplateData.m_id = m_prefabLoaderInterface->LoadTemplate(targetTemplateData.m_filePath);
for (int i = 0; i < numInstances; i++)
{
sourceTemplatesData[i].m_id = m_prefabSystemComponent->GetTemplateIdFromFilePath(sourceTemplatesData[i].m_filePath);
LinkData linkFromSourceData = PrefabTestDataUtils::CreateLinkData(targetTemplateInstancesData[i],
sourceTemplatesData[i].m_id, targetTemplateData.m_id);
PrefabTestDataUtils::CheckIfTemplatesConnected(sourceTemplatesData[i], targetTemplateData, linkFromSourceData);
}
}
TEST_F(PrefabLoadTemplateTest, LoadTemplate_LoadCorruptedPrefabFileData_InvalidTemplateIdReturned)
{
const AZStd::string corruptedPrefabContent = "{ Corrupted PrefabDom";
const AZStd::string pathToCorruptedPrefab = "path/to/corrupted/prefab/file";
MockPrefabFileIOActionValidator mockIOActionValidator;
mockIOActionValidator.ReadPrefabDom(pathToCorruptedPrefab, corruptedPrefabContent);
auto tmeplateId = m_prefabLoaderInterface->LoadTemplate(pathToCorruptedPrefab);
EXPECT_EQ(tmeplateId, AzToolsFramework::Prefab::InvalidTemplateId);
}
}
@@ -0,0 +1,32 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestComponent.h>
namespace UnitTest
{
void PrefabTestComponent::Reflect(AZ::ReflectContext* reflection)
{
AZ::SerializeContext* serializeContext = AZ::RttiCast<AZ::SerializeContext*>(reflection);
if (serializeContext)
{
serializeContext->Class<PrefabTestComponent, AzToolsFramework::Components::EditorComponentBase>()->
Field("BoolProperty", &PrefabTestComponent::m_boolProperty);
}
}
PrefabTestComponent::PrefabTestComponent(bool boolProperty)
: m_boolProperty(boolProperty)
{
}
}
@@ -0,0 +1,32 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
namespace UnitTest
{
class PrefabTestComponent
: public AzToolsFramework::Components::EditorComponentBase
{
public:
AZ_EDITOR_COMPONENT(PrefabTestComponent, "{C5FCF40A-FAEC-473C-BFAF-68A66DC45B33}");
PrefabTestComponent() = default;
explicit PrefabTestComponent(bool boolProperty);
static void Reflect(AZ::ReflectContext* reflection);
bool m_boolProperty = false;
};
}
@@ -0,0 +1,32 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestData.h>
namespace UnitTest
{
InstanceData::InstanceData(const InstanceData& other)
: m_name(other.m_name)
, m_source(other.m_source)
{
m_patches.CopyFrom(other.m_patches, m_patches.GetAllocator());
}
InstanceData& InstanceData::InstanceData::operator=(
const InstanceData& other)
{
m_name = other.m_name;
m_source = other.m_source;
m_patches.CopyFrom(other.m_patches, m_patches.GetAllocator());
return *this;
}
}
@@ -0,0 +1,51 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
namespace UnitTest
{
struct InstanceData
{
InstanceData() = default;
InstanceData(const InstanceData& other);
InstanceData& operator=(const InstanceData& other);
AZStd::string m_name;
AZStd::string m_source;
AzToolsFramework::Prefab::PrefabDom m_patches;
};
struct TemplateData
{
AzToolsFramework::Prefab::TemplateId m_id = AzToolsFramework::Prefab::InvalidTemplateId;
bool m_isValid = true;
bool m_isLoadedWithErrors = false;
AZStd::string m_filePath;
AZStd::unordered_map<AZStd::string, InstanceData> m_instancesData;
};
struct LinkData
{
bool m_isValid = true;
InstanceData m_instanceData;
AzToolsFramework::Prefab::TemplateId m_sourceTemplateId = AzToolsFramework::Prefab::InvalidTemplateId;
AzToolsFramework::Prefab::TemplateId m_targetTemplateId = AzToolsFramework::Prefab::InvalidTemplateId;
};
}
@@ -0,0 +1,147 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestDataUtils.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <Prefab/PrefabTestDomUtils.h>
namespace UnitTest
{
namespace PrefabTestDataUtils
{
using namespace AzToolsFramework::Prefab;
LinkData CreateLinkData(
const InstanceData& instanceData,
const TemplateId& sourceTemplateId,
const TemplateId& targetTemplateId)
{
LinkData newLinkData;
newLinkData.m_instanceData = instanceData;
newLinkData.m_sourceTemplateId = sourceTemplateId;
newLinkData.m_targetTemplateId = targetTemplateId;
return newLinkData;
}
InstanceData CreateInstanceDataWithNoPatches(
const AZStd::string& name,
const AZStd::string& source)
{
InstanceData newInstanceData;
newInstanceData.m_name = name;
newInstanceData.m_source = source;
return newInstanceData;
}
void ValidateTemplateLoad(
const TemplateData& expectedTemplateData)
{
PrefabSystemComponentInterface* prefabSystemComponent = AZ::Interface<PrefabSystemComponentInterface>::Get();
ASSERT_TRUE(prefabSystemComponent != nullptr);
ASSERT_TRUE(expectedTemplateData.m_id != InvalidTemplateId);
auto templateReference = prefabSystemComponent->FindTemplate(expectedTemplateData.m_id);
ASSERT_TRUE(templateReference.has_value());
auto& actualTemplate = templateReference->get();
EXPECT_EQ(expectedTemplateData.m_filePath, actualTemplate.GetFilePath());
EXPECT_EQ(expectedTemplateData.m_isValid, actualTemplate.IsValid());
EXPECT_EQ(expectedTemplateData.m_isLoadedWithErrors, actualTemplate.IsLoadedWithErrors());
auto& actualInstancesLinkIds = actualTemplate.GetLinks();
EXPECT_EQ(expectedTemplateData.m_instancesData.size(), actualInstancesLinkIds.size());
for (auto& actualLinkId : actualInstancesLinkIds)
{
auto linkReference = prefabSystemComponent->FindLink(actualLinkId);
ASSERT_TRUE(linkReference.has_value());
auto& actualLink = linkReference->get();
AZStd::string actualLinkName(actualLink.GetInstanceName());
EXPECT_EQ(expectedTemplateData.m_instancesData.count(actualLinkName), 1);
auto& expectedInstanceData = expectedTemplateData.m_instancesData.find(actualLinkName)->second;
EXPECT_EQ(expectedTemplateData.m_id, actualLink.GetTargetTemplateId());
EXPECT_EQ(expectedInstanceData.m_name, actualLinkName);
EXPECT_EQ(
PrefabTestDomUtils::GetPrefabDomInstancePath(expectedInstanceData.m_name.c_str()),
actualLink.GetInstancePath());
ValidateTemplatePatches(actualLink, expectedInstanceData.m_patches);
}
}
void ValidateTemplatePatches(const Link& actualLink, const PrefabDom& expectedTemplatePatches)
{
PrefabDomValueConstReference patchesReference =
PrefabDomUtils::FindPrefabDomValue(actualLink.GetLinkDom(), PrefabDomUtils::PatchesName);
if (!expectedTemplatePatches.IsNull())
{
EXPECT_EQ(AZ::JsonSerialization::Compare(expectedTemplatePatches, patchesReference->get()),
AZ::JsonSerializerCompareResult::Equal);
}
else
{
EXPECT_FALSE(patchesReference.has_value());
}
}
void CheckIfTemplatesConnected(
const TemplateData& expectedSourceTemplateData,
const TemplateData& expectedTargetTemplateData,
const LinkData& expectedLinkData)
{
ValidateTemplateLoad(expectedSourceTemplateData);
ValidateTemplateLoad(expectedTargetTemplateData);
PrefabSystemComponentInterface* prefabSystemComponent = AZ::Interface<PrefabSystemComponentInterface>::Get();
ASSERT_TRUE(prefabSystemComponent != nullptr);
auto& actualSourceTemplate =
prefabSystemComponent->FindTemplate(expectedSourceTemplateData.m_id)->get();
auto& actualTargetTemplate =
prefabSystemComponent->FindTemplate(expectedTargetTemplateData.m_id)->get();
EXPECT_EQ(expectedLinkData.m_instanceData.m_source, actualSourceTemplate.GetFilePath());
auto& actualTargetTemplateLinkIds = actualTargetTemplate.GetLinks();
EXPECT_EQ(expectedTargetTemplateData.m_instancesData.size(), actualTargetTemplateLinkIds.size());
bool expectedLinkFound = false;
for (auto actualTargetTemplateLinkId : actualTargetTemplateLinkIds)
{
auto linkReference = prefabSystemComponent->FindLink(actualTargetTemplateLinkId);
ASSERT_TRUE(linkReference.has_value());
auto& actualLink = linkReference->get();
if (expectedLinkData.m_instanceData.m_name == actualLink.GetInstanceName())
{
EXPECT_EQ(expectedLinkData.m_isValid, actualLink.IsValid());
EXPECT_EQ(expectedLinkData.m_sourceTemplateId, actualLink.GetSourceTemplateId());
EXPECT_EQ(expectedLinkData.m_targetTemplateId, actualLink.GetTargetTemplateId());
ValidateTemplatePatches(actualLink, expectedLinkData.m_instanceData.m_patches);
EXPECT_EQ(
PrefabTestDomUtils::GetPrefabDomInstancePath(expectedLinkData.m_instanceData.m_name.c_str()),
actualLink.GetInstancePath());
expectedLinkFound = true;
break;
}
}
EXPECT_TRUE(expectedLinkFound);
}
}
}
@@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <Prefab/PrefabTestData.h>
#include <AzToolsFramework/Prefab/Link/Link.h>
namespace UnitTest
{
namespace PrefabTestDataUtils
{
LinkData CreateLinkData(
const InstanceData& instanceData,
const AzToolsFramework::Prefab::TemplateId& sourceTemplateId,
const AzToolsFramework::Prefab::TemplateId& targetTemplateId);
InstanceData CreateInstanceDataWithNoPatches(
const AZStd::string& name,
const AZStd::string& source);
void ValidateTemplateLoad(
const TemplateData& expectedTemplateData);
void ValidateTemplatePatches(
const AzToolsFramework::Prefab::Link& actualLink,
const AzToolsFramework::Prefab::PrefabDom& expectedTemplatePatches);
void CheckIfTemplatesConnected(
const TemplateData& expectedSourceTemplateData,
const TemplateData& expectedTargetTemplateData,
const LinkData& expectedLinkData);
}
}
@@ -0,0 +1,222 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestDomUtils.h>
#include <AzCore/Interface/Interface.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/std/optional.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/Prefab/Instance/TemplateInstanceMapperInterface.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
namespace UnitTest
{
namespace PrefabTestDomUtils
{
void SetPrefabDomInstance(
PrefabDom& prefabDom,
const char* instanceName,
const char* source,
const PrefabDomValue& patches)
{
rapidjson::SetValueByPointer(prefabDom, GetPrefabDomSourcePath(instanceName), source);
if (!patches.IsNull())
{
rapidjson::SetValueByPointer(prefabDom, GetPrefabDomPatchesPath(instanceName), patches, prefabDom.GetAllocator());
}
}
PrefabDom CreatePrefabDom()
{
PrefabDom newPrefabDom;
rapidjson::SetValueByPointer(newPrefabDom, "/Entities", rapidjson::Value());
return newPrefabDom;
}
PrefabDom CreatePrefabDom(
const AZStd::vector<InstanceData>& instancesData)
{
PrefabDom newPrefabDom = CreatePrefabDom();
for (auto& instanceData : instancesData)
{
PrefabTestDomUtils::SetPrefabDomInstance(
newPrefabDom, instanceData.m_name.c_str(),
instanceData.m_source.c_str(), instanceData.m_patches);
}
return newPrefabDom;
}
void ValidateInstances(
const TemplateId& templateId,
const PrefabDomValue& expectedContent,
const PrefabDomPath& contentPath,
bool isContentAnInstance)
{
TemplateInstanceMapperInterface* templateInstanceMapper =
AZ::Interface<TemplateInstanceMapperInterface>::Get();
ASSERT_TRUE(templateInstanceMapper != nullptr);
ASSERT_TRUE(templateId != AzToolsFramework::Prefab::InvalidTemplateId);
auto instancesReference = templateInstanceMapper->FindInstancesOwnedByTemplate(templateId);
ASSERT_TRUE(instancesReference.has_value());
auto& actualInstances = instancesReference->get();
for (auto instance : actualInstances)
{
PrefabDom instancePrefabDom;
const bool result = PrefabDomUtils::StoreInstanceInPrefabDom(*instance, instancePrefabDom);
ASSERT_TRUE(result);
auto* actualContent = contentPath.Get(instancePrefabDom);
ASSERT_TRUE(actualContent != nullptr);
if (isContentAnInstance)
{
ComparePrefabDoms(*actualContent, expectedContent, false);
}
else
{
ComparePrefabDomValues(*actualContent, expectedContent);
}
}
}
void ValidatePrefabDomEntities(const AZStd::vector<EntityAlias>& entityAliases, PrefabDom& prefabDom)
{
PrefabDomValueReference templateEntities = PrefabDomUtils::FindPrefabDomValue(prefabDom, "Entities");
ASSERT_TRUE(templateEntities.has_value());
for (EntityAlias entityAlias : entityAliases)
{
EXPECT_TRUE(PrefabDomUtils::FindPrefabDomValue(templateEntities->get(), entityAlias.c_str()).has_value());
}
}
void ValidatePrefabDomInstances(
const AZStd::vector<InstanceAlias>& instanceAliases,
const AzToolsFramework::Prefab::PrefabDom& prefabDom,
const AzToolsFramework::Prefab::PrefabDom& expectedNestedInstanceDom)
{
PrefabDomValueConstReference templateInstances = PrefabDomUtils::FindPrefabDomValue(prefabDom, "Instances");
ASSERT_TRUE(templateInstances.has_value());
for (InstanceAlias instanceAlias : instanceAliases)
{
PrefabDomValueConstReference actualNestedInstanceDom = PrefabDomUtils::FindPrefabDomValue(templateInstances->get(), instanceAlias.c_str());
ASSERT_TRUE(actualNestedInstanceDom.has_value());
ComparePrefabDoms(actualNestedInstanceDom, expectedNestedInstanceDom, false);
}
}
void ComparePrefabDoms(PrefabDomValueConstReference valueA, PrefabDomValueConstReference valueB, bool shouldCompareLinkIds)
{
ASSERT_TRUE(valueA.has_value());
ASSERT_TRUE(valueB.has_value());
const PrefabDomValue& valueADom = valueA->get();
const PrefabDomValue& valueBDom = valueB->get();
if (shouldCompareLinkIds)
{
EXPECT_EQ(AZ::JsonSerialization::Compare(valueADom, valueBDom), AZ::JsonSerializerCompareResult::Equal);
}
else
{
// Compare the source values of the two DOMs.
PrefabDomValueConstReference actualNestedInstanceDomSource =
PrefabDomUtils::FindPrefabDomValue(valueADom, PrefabDomUtils::SourceName);
PrefabDomValueConstReference expectedNestedInstanceDomSource =
PrefabDomUtils::FindPrefabDomValue(valueBDom, PrefabDomUtils::SourceName);
ComparePrefabDomValues(actualNestedInstanceDomSource, expectedNestedInstanceDomSource);
// Compare the entities values of the two DOMs.
PrefabDomValueConstReference actualNestedInstanceDomEntities =
PrefabDomUtils::FindPrefabDomValue(valueADom, PrefabTestDomUtils::EntitiesValueName);
PrefabDomValueConstReference expectedNestedInstanceDomEntities =
PrefabDomUtils::FindPrefabDomValue(valueBDom, PrefabTestDomUtils::EntitiesValueName);
ComparePrefabDomValues(actualNestedInstanceDomEntities, expectedNestedInstanceDomEntities);
// Compare the instances values of the two DOMs, which involves iterating over each expected instance and comparing it
// with its counterpart in the actual instance.
PrefabDomValueConstReference actualNestedInstanceDomInstances =
PrefabDomUtils::FindPrefabDomValue(valueADom, PrefabDomUtils::InstancesName);
PrefabDomValueConstReference expectedNestedInstanceDomInstances =
PrefabDomUtils::FindPrefabDomValue(valueBDom, PrefabDomUtils::InstancesName);
if (expectedNestedInstanceDomInstances.has_value())
{
ASSERT_TRUE(actualNestedInstanceDomInstances.has_value());
for (auto instanceIterator = expectedNestedInstanceDomInstances->get().MemberBegin();
instanceIterator != expectedNestedInstanceDomInstances->get().MemberEnd(); ++instanceIterator)
{
ComparePrefabDoms(instanceIterator->value,
PrefabDomUtils::FindPrefabDomValue(actualNestedInstanceDomInstances->get(), instanceIterator->name.GetString()),
shouldCompareLinkIds);
}
}
}
}
void ComparePrefabDomValues(PrefabDomValueConstReference valueA, PrefabDomValueConstReference valueB)
{
if (!valueA.has_value())
{
EXPECT_FALSE(valueB.has_value());
}
else
{
EXPECT_TRUE(valueB.has_value());
EXPECT_EQ(AZ::JsonSerialization::Compare(valueA->get(), valueB->get()), AZ::JsonSerializerCompareResult::Equal);
}
}
void PrintPrefabDom(const AzToolsFramework::Prefab::PrefabDomValue& prefabDom)
{
rapidjson::StringBuffer prefabBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(prefabBuffer);
prefabDom.Accept(writer);
std::cout << prefabBuffer.GetString() << std::endl;
}
void ValidateEntitiesOfInstances(
const AzToolsFramework::Prefab::TemplateId& templateId,
const AzToolsFramework::Prefab::PrefabDom& expectedPrefabDom,
const AZStd::vector<EntityAlias>& entityAliases)
{
for (auto& entityAlias : entityAliases)
{
PrefabDomPath entityPath = PrefabTestDomUtils::GetPrefabDomEntityPath(entityAlias);
const PrefabDomValue* expectedEntityValue = PrefabTestDomUtils::GetPrefabDomEntity(expectedPrefabDom, entityAlias);
ASSERT_TRUE(expectedEntityValue != nullptr);
PrefabTestDomUtils::ValidateInstances(templateId, *expectedEntityValue, entityPath);
}
}
void ValidateNestedInstancesOfInstances(
const AzToolsFramework::Prefab::TemplateId& templateId,
const AzToolsFramework::Prefab::PrefabDom& expectedPrefabDom,
const AZStd::vector<InstanceAlias>& nestedInstanceAliases)
{
for (auto& nestedInstanceAlias : nestedInstanceAliases)
{
PrefabDomPath nestedInstancePath = PrefabTestDomUtils::GetPrefabDomInstancePath(nestedInstanceAlias);
const PrefabDomValue* nestedInstanceValue =
PrefabTestDomUtils::GetPrefabDomInstance(expectedPrefabDom, nestedInstanceAlias);
ASSERT_TRUE(nestedInstanceValue != nullptr);
PrefabTestDomUtils::ValidateInstances(templateId, *nestedInstanceValue, nestedInstancePath, true);
}
}
}
}
@@ -0,0 +1,167 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/EntityId.h>
#include <AzCore/Math/Uuid.h>
#include <AzCore/std/containers/unordered_set.h>
#include <AzToolsFramework/Prefab/PrefabDomTypes.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/PrefabTestData.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
namespace UnitTest
{
namespace PrefabTestDomUtils
{
using namespace AzToolsFramework::Prefab;
inline static const char* ComponentsValueName = "Components";
inline static const char* ComponentIdName = "Id";
inline static const char* EntitiesValueName = "Entities";
inline static const char* EntityNameValueName = "Name";
inline static const char* BoolPropertyName = "BoolProperty";
inline PrefabDomPath GetPrefabDomEntitiesPath()
{
return PrefabDomPath()
.Append(EntitiesValueName);
};
inline PrefabDomPath GetPrefabDomEntityPath(
const EntityAlias& entityAlias)
{
return GetPrefabDomEntitiesPath()
.Append(entityAlias.c_str(), entityAlias.length());
};
inline PrefabDomPath GetPrefabDomEntityNamePath(
const EntityAlias& entityAlias)
{
return GetPrefabDomEntityPath(entityAlias)
.Append(EntityNameValueName);
};
inline PrefabDomPath GetPrefabDomComponentsPath(const EntityAlias& entityAlias)
{
return GetPrefabDomEntityPath(entityAlias).Append(ComponentsValueName);
};
inline PrefabDomPath GetPrefabDomInstancesPath()
{
return PrefabDomPath()
.Append(PrefabDomUtils::InstancesName);
};
inline PrefabDomPath GetPrefabDomInstancePath(
const InstanceAlias& instanceAlias)
{
return GetPrefabDomInstancesPath().Append(instanceAlias.c_str(), instanceAlias.length());
};
inline PrefabDomPath GetPrefabDomInstancePath(
const char* instanceName)
{
return GetPrefabDomInstancesPath().Append(instanceName);
};
inline PrefabDomPath GetPrefabDomSourcePath(
const char* instanceName)
{
return GetPrefabDomInstancePath(instanceName).Append(PrefabDomUtils::SourceName);
};
inline PrefabDomPath GetPrefabDomPatchesPath(
const char* instanceName)
{
return GetPrefabDomInstancePath(instanceName).Append(PrefabDomUtils::PatchesName);
};
inline const PrefabDomValue* GetPrefabDomComponents(
const PrefabDom& prefabDom,
const EntityAlias& entityAlias)
{
return PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(prefabDom);
}
inline const PrefabDomValue* GetPrefabDomInstance(
const PrefabDom& prefabDom,
const InstanceAlias& instanceAlias)
{
return PrefabTestDomUtils::GetPrefabDomInstancePath(instanceAlias).Get(prefabDom);
}
inline const PrefabDomValue* GetPrefabDomEntity(
const PrefabDom& prefabDom,
const EntityAlias& entityAlias)
{
return PrefabTestDomUtils::GetPrefabDomEntityPath(entityAlias).Get(prefabDom);
}
inline const PrefabDomValue* GetPrefabDomEntityName(
const PrefabDom& prefabDom,
const EntityAlias& entityAlias)
{
return PrefabTestDomUtils::GetPrefabDomEntityNamePath(entityAlias).Get(prefabDom);
}
void SetPrefabDomInstance(
PrefabDom& prefabDom,
const char* instanceName,
const char* source,
const PrefabDomValue& patches);
void ValidateInstances(
const TemplateId& templateId,
const PrefabDomValue& expectedContent,
const PrefabDomPath& contentPath,
bool isContentAnInstance = false);
PrefabDom CreatePrefabDom();
PrefabDom CreatePrefabDom(const AZStd::vector<InstanceData>& instancesData);
/**
* Validates that the entities with the given entity aliases are present in the given prefab DOM.
*/
void ValidatePrefabDomEntities(const AZStd::vector<EntityAlias>& entityAliases,
PrefabDom& prefabDom);
/**
* Extracts the DOM of the instances using the given instance aliases from the prefab DOM and
* validates that they match with the expectedNestedInstanceDom.
*/
void ValidatePrefabDomInstances(const AZStd::vector<InstanceAlias>& instanceAliases,
const PrefabDom& prefabDom,
const PrefabDom& expectedNestedInstanceDom);
void ComparePrefabDoms(PrefabDomValueConstReference valueA, PrefabDomValueConstReference valueB, bool shouldCompareLinkIds = true);
void ComparePrefabDomValues(PrefabDomValueConstReference valueA, PrefabDomValueConstReference valueB);
/**
* Prints the contents of the given prefab DOM to the console in a readable format.
*/
void PrintPrefabDom(const PrefabDomValue& prefabDom);
void ValidateEntitiesOfInstances(
const AzToolsFramework::Prefab::TemplateId& templateId,
const AzToolsFramework::Prefab::PrefabDom& expectedPrefabDom,
const AZStd::vector<EntityAlias>& entityAliases);
void ValidateNestedInstancesOfInstances(
const AzToolsFramework::Prefab::TemplateId& templateId,
const AzToolsFramework::Prefab::PrefabDom& expectedPrefabDom,
const AZStd::vector<InstanceAlias>& nestedInstanceAliases);
}
}
@@ -0,0 +1,99 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabTestFixture.h>
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <AzToolsFramework/Prefab/PrefabLoaderInterface.h>
#include <Prefab/PrefabTestComponent.h>
#include <Prefab/PrefabTestDomUtils.h>
namespace UnitTest
{
void PrefabTestFixture::SetUpEditorFixtureImpl()
{
// Acquire the system entity
AZ::Entity* systemEntity = GetApplication()->FindEntity(AZ::SystemEntityId);
EXPECT_TRUE(systemEntity);
// Acquire the prefab system component to gain access to its APIs for testing
m_prefabSystemComponent = systemEntity->FindComponent<AzToolsFramework::Prefab::PrefabSystemComponent>();
EXPECT_TRUE(m_prefabSystemComponent);
// Acquire the interface of PrefabLoader to gain access to its APIs for testing
m_prefabLoaderInterface = AZ::Interface<AzToolsFramework::Prefab::PrefabLoaderInterface>::Get();
EXPECT_TRUE(m_prefabLoaderInterface);
// Acquire the interface of InstanceUpdateQueueInterface to gain access to its APIs for testing
m_instanceUpdateExecutorInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceUpdateExecutorInterface>::Get();
EXPECT_TRUE(m_instanceUpdateExecutorInterface);
// Acquire the interface of InstanceToTemplate to gain access to its APIs for testing
m_instanceToTemplateInterface = AZ::Interface<AzToolsFramework::Prefab::InstanceToTemplateInterface>::Get();
EXPECT_TRUE(m_instanceToTemplateInterface);
GetApplication()->RegisterComponentDescriptor(PrefabTestComponent::CreateDescriptor());
}
AZ::Entity* PrefabTestFixture::CreateEntity(const char* entityName, const bool shouldActivate)
{
// Circumvent the EntityContext system and generate a new entity with a transformcomponent
AZ::Entity* newEntity = aznew AZ::Entity(entityName);
if(shouldActivate)
{
newEntity->Init();
newEntity->Activate();
}
return newEntity;
}
void PrefabTestFixture::CompareInstances(const AzToolsFramework::Prefab::Instance& instanceA,
const AzToolsFramework::Prefab::Instance& instanceB, bool shouldCompareLinkIds)
{
AzToolsFramework::Prefab::TemplateId templateAId = instanceA.GetTemplateId();
AzToolsFramework::Prefab::TemplateId templateBId = instanceB.GetTemplateId();
ASSERT_TRUE(templateAId != AzToolsFramework::Prefab::InvalidTemplateId);
ASSERT_TRUE(templateBId != AzToolsFramework::Prefab::InvalidTemplateId);
EXPECT_EQ(templateAId, templateBId);
AzToolsFramework::Prefab::TemplateReference templateA =
m_prefabSystemComponent->FindTemplate(templateAId);
ASSERT_TRUE(templateA.has_value());
AzToolsFramework::Prefab::PrefabDom prefabDomA;
ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceA, prefabDomA));
AzToolsFramework::Prefab::PrefabDom prefabDomB;
ASSERT_TRUE(AzToolsFramework::Prefab::PrefabDomUtils::StoreInstanceInPrefabDom(instanceB, prefabDomB));
// Validate that both instances match when serialized
PrefabTestDomUtils::ComparePrefabDoms(prefabDomA, prefabDomB);
// Validate that the serialized instances match the shared template when serialized
PrefabTestDomUtils::ComparePrefabDoms(templateA->get().GetPrefabDom(), prefabDomB, shouldCompareLinkIds);
}
void PrefabTestFixture::DeleteInstances(const InstanceList& instancesToDelete)
{
for (Instance* instanceToDelete : instancesToDelete)
{
ASSERT_TRUE(instanceToDelete);
delete instanceToDelete;
instanceToDelete = nullptr;
}
}
}
@@ -0,0 +1,59 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/Prefab/PrefabSystemComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <Prefab/PrefabTestData.h>
#include <Prefab/PrefabTestUtils.h>
namespace AzToolsFramework
{
namespace Prefab
{
class PrefabLoaderInterface;
}
}
namespace UnitTest
{
using namespace AzToolsFramework::Prefab;
using namespace PrefabTestUtils;
class PrefabTestFixture
: public ToolsApplicationFixture
{
protected:
inline static const char* PrefabMockFilePath = "SomePath";
inline static const char* NestedPrefabMockFilePath = "SomePathToNested";
inline static const char* WheelPrefabMockFilePath = "SomePathToWheel";
inline static const char* AxlePrefabMockFilePath = "SomePathToAxle";
inline static const char* CarPrefabMockFilePath = "SomePathToCar";
void SetUpEditorFixtureImpl() override;
AZ::Entity* CreateEntity(const char* entityName, const bool shouldActivate = true);
void CompareInstances(const Instance& instanceA,
const Instance& instanceB, bool shouldCompareLinkIds = true);
void DeleteInstances(const InstanceList& instancesToDelete);
PrefabSystemComponent* m_prefabSystemComponent = nullptr;
PrefabLoaderInterface* m_prefabLoaderInterface = nullptr;
InstanceUpdateExecutorInterface* m_instanceUpdateExecutorInterface = nullptr;
InstanceToTemplateInterface* m_instanceToTemplateInterface = nullptr;
};
}
@@ -0,0 +1,40 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution(the "License").All use of this software is governed by the License,
* or , if provided, by the license below or the license accompanying this file.Do not
* remove or modify any license notices.This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <vector>
#include <memory>
#include <Prefab/Instance/Instance.h>
namespace UnitTest
{
namespace PrefabTestUtils
{
using namespace AzToolsFramework::Prefab;
template<typename... InstanceArgs>
inline AZStd::vector<AZStd::unique_ptr<Instance>> MakeInstanceList(InstanceArgs&&... instances)
{
static_assert((AZStd::is_same_v<InstanceArgs, AZStd::unique_ptr<Instance>>&& ...), "All arguments must be a AZStd::unique_ptr<Instance>&&");
AZStd::vector<AZStd::unique_ptr<Instance>> instanceList;
instanceList.reserve(sizeof...(InstanceArgs));
(instanceList.emplace_back(AZStd::forward<InstanceArgs>(instances)), ...);
return instanceList;
}
inline AZStd::vector<AZStd::unique_ptr<Instance>> MakeInstanceList()
{
return {};
}
}
}
@@ -0,0 +1,444 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/PrefabTestComponent.h>
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
using PrefabUpdateInstancesTest = PrefabTestFixture;
TEST_F(PrefabUpdateInstancesTest, PrefabUpdateInstances_UpdateEntityName_UpdateSucceeds)
{
// Create a Template from an Instance owning a single entity.
using namespace AzToolsFramework::Prefab;
const char* newEntityName = "New Entity";
AZ::Entity* newEntity = CreateEntity(newEntityName);
AZStd::unique_ptr<Instance> firstInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, PrefabMockFilePath);
ASSERT_TRUE(firstInstance);
TemplateId newTemplateId = firstInstance->GetTemplateId();
EXPECT_TRUE(newTemplateId != InvalidTemplateId);
PrefabDom& templatePrefabDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> entityAliases = firstInstance->GetEntityAliases();
EXPECT_EQ(entityAliases.size(), 1);
// Instantiate Instances and validate if all entities of each Template's Instance have the given entity names.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabDomPath entityNamePath = PrefabTestDomUtils::GetPrefabDomEntityNamePath(entityAliases.front());
const PrefabDomValue* entityNameValue =
PrefabTestDomUtils::GetPrefabDomEntityName(templatePrefabDom, entityAliases.front());
ASSERT_TRUE(entityNameValue != nullptr);
PrefabTestDomUtils::ValidateInstances(newTemplateId, *entityNameValue, entityNamePath);
// Update Template's PrefabDom with a new entity name.
entityNamePath.Set(templatePrefabDom, "Updated Entity");
// Update Template's Instances.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
// Validate if all entities of each Template's Instance have the updated entity names.
PrefabTestDomUtils::ValidateInstances(newTemplateId, *entityNameValue, entityNamePath);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_AddEntity_UpdateSucceeds)
{
// Create a Template from an Instance owning a single entity.
using namespace AzToolsFramework::Prefab;
AZ::Entity* entity1 = CreateEntity("Entity 1");
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab({ entity1 }, {}, PrefabMockFilePath);
TemplateId newTemplateId = newInstance->GetTemplateId();
EXPECT_TRUE(newTemplateId != InvalidTemplateId);
PrefabDom& newTemplateDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> newTemplateEntityAliases = newInstance->GetEntityAliases();
EXPECT_EQ(newTemplateEntityAliases.size(), 1);
// Instantiate Instances and validate if all Instances have the entity.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
// Add another entity to the Instance and use it to update the PrefabDom of Template.
AZ::Entity* entity2 = CreateEntity("Entity 2");
newInstance->AddEntity(*entity2);
newTemplateEntityAliases = newInstance->GetEntityAliases();
EXPECT_EQ(newTemplateEntityAliases.size(), 2);
PrefabDom updatedTemplateDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newInstance, updatedTemplateDom));
newTemplateDom.CopyFrom(updatedTemplateDom, newTemplateDom.GetAllocator());
// Update Template's Instances and validate if all Instances have the new entity.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_AddInstance_UpdateSucceeds)
{
// Create a Template with single entity.
using namespace AzToolsFramework::Prefab;
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> newNestedInstance = m_prefabSystemComponent->CreatePrefab({ entity }, {}, NestedPrefabMockFilePath);
TemplateId newNestedTemplateId = newNestedInstance->GetTemplateId();
EXPECT_TRUE(newNestedTemplateId != InvalidTemplateId);
EXPECT_EQ(newNestedInstance->GetEntityAliases().size(), 1);
// Create an enclosing Template with 0 entities and 1 nested Instance.
AZStd::unique_ptr<Instance> nestedInstance1 = m_prefabSystemComponent->InstantiatePrefab(newNestedTemplateId);
AZStd::unique_ptr<Instance> newEnclosingInstance = m_prefabSystemComponent->CreatePrefab({}, MakeInstanceList( AZStd::move(nestedInstance1) ), PrefabMockFilePath);
TemplateId newEnclosingTemplateId = newEnclosingInstance->GetTemplateId();
EXPECT_TRUE(newEnclosingTemplateId != InvalidTemplateId);
PrefabDom& newEnclosingTemplateDom = m_prefabSystemComponent->FindTemplateDom(newEnclosingTemplateId);
AZStd::vector<InstanceAlias> nestedInstanceAliases = newEnclosingInstance->GetNestedInstanceAliases(newNestedTemplateId);
EXPECT_EQ(nestedInstanceAliases.size(), 1);
// Instantiate enclosing Instances and validate if all enclosing Instances have the nested Instance.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newEnclosingTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newEnclosingTemplateId);
}
PrefabTestDomUtils::ValidateNestedInstancesOfInstances(
newEnclosingTemplateId, newEnclosingTemplateDom, nestedInstanceAliases);
// Add another nested Instance to the enclosing Instance and use it to update the PrefabDom of Template.
AZStd::unique_ptr<Instance> nestedInstance2 = m_prefabSystemComponent->InstantiatePrefab(newNestedTemplateId);
newEnclosingInstance->AddInstance(AZStd::move(nestedInstance2));
PrefabDom updatedTemplateDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newEnclosingInstance, updatedTemplateDom));
newEnclosingTemplateDom.CopyFrom(updatedTemplateDom, newEnclosingTemplateDom.GetAllocator());
// Validate that there are 2 wheel Instances under the axle Instance
nestedInstanceAliases = newEnclosingInstance->GetNestedInstanceAliases(newNestedTemplateId);
EXPECT_EQ(nestedInstanceAliases.size(), 2);
// Update axle Template's Instances and validate if all axle Instances have the new wheel Instance.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newEnclosingTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateNestedInstancesOfInstances(
newEnclosingTemplateId, newEnclosingTemplateDom, nestedInstanceAliases);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_AddComponent_UpdateSucceeds)
{
// Create a Template from an Instance owning a single entity.
AZ::Entity* entity = CreateEntity("Entity", false);
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab({ entity }, {}, PrefabMockFilePath);
TemplateId newTemplateId = newInstance->GetTemplateId();
PrefabDom& newTemplateDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> newTemplateEntityAliases = newInstance->GetEntityAliases();
ASSERT_EQ(newTemplateEntityAliases.size(), 1);
// Validate that the entity doesn't have any components under it.
const PrefabDomValue* entityComponents =
PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents == nullptr);
// Instantiate Instances and validate if all Instances have the entity.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
// Add a component to the Instance and use it to update the PrefabDom of Template.
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
entity->AddComponent(prefabTestComponent);
auto expectedComponentId = prefabTestComponent->GetId();
PrefabDom updatedDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newInstance, updatedDom));
newTemplateDom.CopyFrom(updatedDom, newTemplateDom.GetAllocator());
// Validate that the entity does have a component under it.
entityComponents = PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents != nullptr && entityComponents->IsArray());
EXPECT_EQ(entityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in Template and verify that it matches with the component id of the Instance.
PrefabDomValueConstReference findEntityComponentIdValueResult =
PrefabDomUtils::FindPrefabDomValue(*entityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(findEntityComponentIdValueResult.has_value());
EXPECT_EQ(expectedComponentId, findEntityComponentIdValueResult->get().GetUint64());
// Update Template's Instances and validate if all Instances have the new component under their entities.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateInstances(newTemplateId, *entityComponents,
PrefabTestDomUtils::GetPrefabDomComponentsPath(newTemplateEntityAliases.front()));
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_DetachEntity_UpdateSucceeds)
{
// Create a Template from an Instance owning 2 entities.
using namespace AzToolsFramework::Prefab;
AZ::Entity* entity1 = CreateEntity("Entity 1");
AZ::Entity* entity2 = CreateEntity("Entity 2");
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab(
{ entity1, entity2 },
{},
PrefabMockFilePath);
TemplateId newTemplateId = newInstance->GetTemplateId();
EXPECT_TRUE(newTemplateId != InvalidTemplateId);
PrefabDom& newTemplateDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> newTemplateEntityAliases = newInstance->GetEntityAliases();
EXPECT_EQ(newTemplateEntityAliases.size(), 2);
// Instantiate Instances and validate if all Instances have both entities.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
// Remove an entity from the Instance and use the updated Instance to update the PrefabDom of Template.
AZStd::unique_ptr<AZ::Entity> detachedEntity = newInstance->DetachEntity(entity1->GetId());
ASSERT_TRUE(detachedEntity);
EXPECT_EQ(detachedEntity->GetId(), entity1->GetId());
newTemplateEntityAliases = newInstance->GetEntityAliases();
EXPECT_EQ(newTemplateEntityAliases.size(), 1);
PrefabDom updatedTemplateDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newInstance, updatedTemplateDom));
newTemplateDom.CopyFrom(updatedTemplateDom, newTemplateDom.GetAllocator());
// Update Template's Instances and validate if all Instances have the remaining entity.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_DetachNestedInstance_UpdateSucceeds)
{
// Create a Template with single entity.
using namespace AzToolsFramework::Prefab;
AZ::Entity* entity = CreateEntity("Entity");
AZStd::unique_ptr<Instance> newNestedInstance = m_prefabSystemComponent->CreatePrefab({ entity }, {}, NestedPrefabMockFilePath);
TemplateId newNestedTemplateId = newNestedInstance->GetTemplateId();
EXPECT_TRUE(newNestedTemplateId != InvalidTemplateId);
EXPECT_EQ(newNestedInstance->GetEntityAliases().size(), 1);
// Create an enclosing Template with 0 entities and 2 nested Instances.
AZStd::unique_ptr<Instance> nestedInstance1 = m_prefabSystemComponent->InstantiatePrefab(newNestedTemplateId);
AZStd::unique_ptr<Instance> nestedInstance2 = m_prefabSystemComponent->InstantiatePrefab(newNestedTemplateId);
AZStd::unique_ptr<Instance> newEnclosingInstance = m_prefabSystemComponent->CreatePrefab(
{},
MakeInstanceList( AZStd::move(nestedInstance1), AZStd::move(nestedInstance2) ),
PrefabMockFilePath);
TemplateId newEnclosingTemplateId = newEnclosingInstance->GetTemplateId();
EXPECT_TRUE(newEnclosingTemplateId != InvalidTemplateId);
PrefabDom& newEnclosingTemplateDom = m_prefabSystemComponent->FindTemplateDom(newEnclosingTemplateId);
AZStd::vector<InstanceAlias> nestedInstanceAliases = newEnclosingInstance->GetNestedInstanceAliases(newNestedTemplateId);
EXPECT_EQ(nestedInstanceAliases.size(), 2);
// Instantiate enclosing Instances and validate if all enclosing Instances have both nested Instances.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newEnclosingTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newEnclosingTemplateId);
}
PrefabTestDomUtils::ValidateNestedInstancesOfInstances(
newEnclosingTemplateId, newEnclosingTemplateDom, nestedInstanceAliases);
// Remove one nested Instance from the enclosing Instance
// and use the updated enclosing Instance to update the PrefabDom of Template.
AZStd::unique_ptr<Instance> detachedInstance = newEnclosingInstance->DetachNestedInstance(nestedInstanceAliases.front());
ASSERT_TRUE(detachedInstance);
PrefabDom updatedTemplateDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newEnclosingInstance, updatedTemplateDom));
newEnclosingTemplateDom.CopyFrom(updatedTemplateDom, newEnclosingTemplateDom.GetAllocator());
// Validate that there is only one nested Instances under the enclosing Instance.
nestedInstanceAliases = newEnclosingInstance->GetNestedInstanceAliases(newNestedTemplateId);
EXPECT_EQ(nestedInstanceAliases.size(), 1);
// Update enclosing Template's Instances and validate if all enclosing Instances have the remaining nested Instances.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newEnclosingTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateNestedInstancesOfInstances(
newEnclosingTemplateId, newEnclosingTemplateDom, nestedInstanceAliases);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_RemoveComponent_UpdateSucceeds)
{
// Create a Template from an Instance owning a single entity with a prefabTestComponent.
AZ::Entity* entity = CreateEntity("Entity", false);
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
entity->AddComponent(prefabTestComponent);
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab({ entity }, {}, PrefabMockFilePath);
TemplateId newTemplateId = newInstance->GetTemplateId();
PrefabDom& newTemplateDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> newTemplateEntityAliases = newInstance->GetEntityAliases();
ASSERT_EQ(newTemplateEntityAliases.size(), 1);
// Validate that the entity has exactly 1 component under it.
const PrefabDomValue* entityComponents =
PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents != nullptr && entityComponents->IsArray());
EXPECT_EQ(entityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in the Template and verify that it matches with the component id of the entity's component.
PrefabDomValueConstReference entityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*entityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(entityComponentIdValue.has_value());
EXPECT_EQ(prefabTestComponent->GetId(), entityComponentIdValue->get().GetUint64());
// Instantiate Instances and validate if all Instances have the entity.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabTestDomUtils::ValidateInstances(
newTemplateId, *entityComponents, PrefabTestDomUtils::GetPrefabDomComponentsPath(newTemplateEntityAliases.front()));
// Remove a component from the Instance's entity and use the Instance to update the PrefabDom of Template.
entity->RemoveComponent(prefabTestComponent);
delete prefabTestComponent;
PrefabDom updatedDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newInstance, updatedDom));
newTemplateDom.CopyFrom(updatedDom, newTemplateDom.GetAllocator());
// Validate that the entity does not have any component under it.
entityComponents = PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents == nullptr);
// Update Template's Instances and validate if all Instances have no component under their entities.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateEntitiesOfInstances(newTemplateId, newTemplateDom, newTemplateEntityAliases);
}
TEST_F(PrefabUpdateInstancesTest, UpdatePrefabInstances_ChangeComponentProperty_UpdateSucceeds)
{
// Create a Template from an Instance owning a single entity with a PrefabTestComponent.
AZ::Entity* entity = CreateEntity("Entity", false);
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
entity->AddComponent(prefabTestComponent);
AZStd::unique_ptr<Instance> newInstance = m_prefabSystemComponent->CreatePrefab({ entity }, {}, PrefabMockFilePath);
TemplateId newTemplateId = newInstance->GetTemplateId();
PrefabDom& newTemplateDom = m_prefabSystemComponent->FindTemplateDom(newTemplateId);
AZStd::vector<EntityAlias> newTemplateEntityAliases = newInstance->GetEntityAliases();
ASSERT_EQ(newTemplateEntityAliases.size(), 1);
// Validate that the entity has exactly 1 component under it.
const PrefabDomValue* entityComponents =
PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents != nullptr && entityComponents->IsArray());
EXPECT_EQ(entityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in the Template and verify that it matches with the component id of the entity's component.
PrefabDomValueConstReference entityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*entityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(entityComponentIdValue.has_value());
EXPECT_EQ(prefabTestComponent->GetId(), entityComponentIdValue->get().GetUint64());
// Instantiate Instances and validate if all Instances have the entity.
const int numberOfInstances = 3;
AZStd::vector<AZStd::unique_ptr<Instance>> instantiatedInstances;
for (int i = 0; i < numberOfInstances; ++i)
{
instantiatedInstances.emplace_back(m_prefabSystemComponent->InstantiatePrefab(newTemplateId));
ASSERT_TRUE(instantiatedInstances.back());
EXPECT_EQ(instantiatedInstances.back()->GetTemplateId(), newTemplateId);
}
PrefabDomPath entityComponentsPath = PrefabTestDomUtils::GetPrefabDomComponentsPath(newTemplateEntityAliases.front());
PrefabTestDomUtils::ValidateInstances(
newTemplateId, *entityComponents, entityComponentsPath);
// Change the bool property of the component from the Instance and use the Instance to update the PrefabDom of Template.
prefabTestComponent->m_boolProperty = false;
PrefabDom updatedDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*newInstance, updatedDom));
newTemplateDom.CopyFrom(updatedDom, newTemplateDom.GetAllocator());
// Validate that the prefabTestComponent in the Template's DOM doesn't have a BoolProperty.
// Even though we changed the property to false, it won't be serialized out because it's a default value.
entityComponents = PrefabTestDomUtils::GetPrefabDomComponents(newTemplateDom, newTemplateEntityAliases.front());
ASSERT_TRUE(entityComponents != nullptr && entityComponents->IsArray());
EXPECT_EQ(entityComponents->GetArray().Size(), 1);
PrefabDomValueConstReference entityComponentBoolPropertyValue =
PrefabDomUtils::FindPrefabDomValue(*entityComponents->Begin(), PrefabTestDomUtils::BoolPropertyName);
EXPECT_FALSE(entityComponentBoolPropertyValue.has_value());
// Update Template's Instances and validate if all Instances have no BoolProperty under their prefabTestComponents in entities.
m_instanceUpdateExecutorInterface->AddTemplateInstancesToQueue(newTemplateId);
const bool updateResult = m_instanceUpdateExecutorInterface->UpdateTemplateInstancesInQueue();
EXPECT_TRUE(updateResult);
PrefabTestDomUtils::ValidateInstances(newTemplateId, *entityComponents, entityComponentsPath);
}
}
@@ -0,0 +1,420 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/Component/TransformBus.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/PrefabTestComponent.h>
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
namespace UnitTest
{
using PrefabUpdateTemplateTest = PrefabTestFixture;
/*
The below tests use an example of car->axle->wheel templates to test that change propagation works correctly within templates.
The car template will have axle templates nested under it and the axle template will have wheel templates nested under it.
Because of the complexity that arises from multiple levels of prefab nesting, it's easier to write tests using an example scenario
than use generic nesting terminology.
*/
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_AddEntity_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1");
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity }, {}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
// Validate that the wheel template has the same entities(1) as the instance it was created from.
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
PrefabTestDomUtils::ValidatePrefabDomEntities(wheelTemplateEntityAliases, wheelTemplateDom);
// Create an axle with 0 entities and 2 wheel instances.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> wheel2UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle), AZStd::move(wheel2UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
// Create a car with 0 entities, 2 axle instances and 1 wheel instance.
AZStd::unique_ptr<Instance> axle1UnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> axle2UnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> spareWheelUnderCar = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axle1UnderCar), AZStd::move(axle2UnderCar), AZStd::move(spareWheelUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(wheelTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Add another entity to a wheel instance and use it to update the wheel template.
wheelIsolatedInstance->AddEntity(*CreateEntity("WheelEntity2"));
PrefabDom updatedWheelInstance;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*wheelIsolatedInstance, updatedWheelInstance));
m_prefabSystemComponent->UpdatePrefabTemplate(wheelTemplateId, updatedWheelInstance);
// Validate that the wheel template has the same entities(2) as the updated instance.
wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
ASSERT_EQ(wheelTemplateEntityAliases.size(), 2);
PrefabTestDomUtils::ValidatePrefabDomEntities(wheelTemplateEntityAliases, wheelTemplateDom);
// Validate that the wheels under axle are updated with 2 entities
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the wheels of axles under the car have 2 entities
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
// Validate that the wheel under the car has 2 entities
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderCar, carTemplateDom, wheelTemplateDom);
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_AddInstance_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1");
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Validate that there is only 1 wheel instance under axle.
ASSERT_EQ(wheelInstanceAliasesUnderAxle.size(), 1);
// Create a car with 0 entities and 2 axle instances.
AZStd::unique_ptr<Instance> axle1UnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> axle2UnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axle1UnderCar), AZStd::move(axle2UnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Add another Wheel instance to Axle instance and use it to update the Axle template.
AZStd::unique_ptr<Instance> wheel2UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
axleInstance->AddInstance(AZStd::move(wheel2UnderAxle));
PrefabDom updatedAxleInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*axleInstance, updatedAxleInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(axleTemplateId, updatedAxleInstanceDom);
// Validate that there are 2 wheel instances under axle
wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
ASSERT_EQ(wheelInstanceAliasesUnderAxle.size(), 2);
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_AddComponent_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1", false);
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
// Validate that the wheel template has the same entities(1) as the instance it was created from.
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
// Validate that the wheel entity doesn't have any components under it.
EntityAlias entityAlias = wheelTemplateEntityAliases.front();
PrefabDomValue* wheelEntityComponents =
PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents == nullptr);
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axleUnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axleUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Add a component to Wheel instance and use it to update the wheel template.
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
wheelEntity->AddComponent(prefabTestComponent);
auto expectedComponentId = prefabTestComponent->GetId();
PrefabDom updatedWheelInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*wheelIsolatedInstance, updatedWheelInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(wheelTemplateId, updatedWheelInstanceDom);
// Validate that the wheel entity does have a component under it.
wheelEntityComponents = PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents != nullptr && wheelEntityComponents->IsArray());
EXPECT_EQ(wheelEntityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in wheel template and verify that it matches with the component id of the wheel instance.
PrefabDomValueReference wheelEntityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(wheelEntityComponentIdValue.has_value());
EXPECT_EQ(expectedComponentId, wheelEntityComponentIdValue->get().GetUint64());
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_DetachEntity_AllDependentTemplatesUpdated)
{
// Create wheel instance with 2 entities and create a template out of it.
AZ::Entity* wheelEntity1 = CreateEntity("WheelEntity1");
AZ::Entity* wheelEntity2 = CreateEntity("WheelEntity2");
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity1, wheelEntity2 },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
// Validate that the wheel template has the same entities(2) as the instance it was created from.
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
ASSERT_EQ(wheelTemplateEntityAliases.size(), 2);
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axleUnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axleUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Detach the first entity from the Wheel instance and use it to update the wheel template.
AZStd::unique_ptr<AZ::Entity> detachedEntity = wheelIsolatedInstance->DetachEntity(wheelEntity1->GetId());
ASSERT_TRUE(detachedEntity);
PrefabDom updatedWheelInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*wheelIsolatedInstance, updatedWheelInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(wheelTemplateId, updatedWheelInstanceDom);
// Validate that the wheel template only has 1 entity now.
wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
PrefabTestDomUtils::ValidatePrefabDomEntities(wheelTemplateEntityAliases, wheelTemplateDom);
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_DetachNestedInstance_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1");
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
// Create an axle with 0 entities and 2 wheel instances.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> wheel2UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle), AZStd::move(wheel2UnderAxle) ),
AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Validate that there are 2 wheel instances under axle.
ASSERT_EQ(wheelInstanceAliasesUnderAxle.size(), 2);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axle1UnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axle1UnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Detach second wheel instance from Axle instance and use it to update the Axle template.
InstanceAlias aliasOfWheelInstanceToRetain = wheelInstanceAliasesUnderAxle.front();
AZStd::unique_ptr<Instance> detachedInstance = axleInstance->DetachNestedInstance(wheelInstanceAliasesUnderAxle.back());
ASSERT_TRUE(detachedInstance);
PrefabDom updatedAxleInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*axleInstance, updatedAxleInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(axleTemplateId, updatedAxleInstanceDom);
// Validate that there is only 1 wheel instances under axle
wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
ASSERT_EQ(wheelInstanceAliasesUnderAxle.size(), 1);
EXPECT_EQ(wheelInstanceAliasesUnderAxle.front(), aliasOfWheelInstanceToRetain);
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_RemoveComponent_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance with a PrefabTestComponent and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1", false);
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
wheelEntity->AddComponent(prefabTestComponent);
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
// Validate that the wheel template has the same entities(1) as the instance it was created from.
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
// Validate that the wheel entity has 1 component under it.
AZStd::string entityAlias = wheelTemplateEntityAliases.front();
PrefabDomValue* wheelEntityComponents =
PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents != nullptr && wheelEntityComponents->IsArray());
EXPECT_EQ(wheelEntityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in wheel template and verify that it matches with the component id of the wheel instance.
PrefabDomValueReference wheelEntityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(wheelEntityComponentIdValue.has_value());
EXPECT_EQ(prefabTestComponent->GetId(), wheelEntityComponentIdValue->get().GetUint64());
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axleUnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axleUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Remove the component from Wheel instance and use it to update the wheel template.
wheelEntity->RemoveComponent(prefabTestComponent);
PrefabDom updatedWheelInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*wheelIsolatedInstance, updatedWheelInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(wheelTemplateId, updatedWheelInstanceDom);
// Validate that the wheel entity does not have a component under it.
wheelEntityComponents = PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents == nullptr);
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
delete prefabTestComponent;
}
TEST_F(PrefabUpdateTemplateTest, UpdatePrefabTemplate_ChangeComponentProperty_AllDependentTemplatesUpdated)
{
// Create a single entity wheel instance with a PrefabTestComponent and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1", false);
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
wheelEntity->AddComponent(prefabTestComponent);
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
// Validate that the wheel template has the same entities(1) as the instance it was created from.
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
// Validate that the wheel entity has 1 component under it.
AZStd::string entityAlias = wheelTemplateEntityAliases.front();
PrefabDomValue* wheelEntityComponents =
PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents != nullptr && wheelEntityComponents->IsArray());
EXPECT_EQ(wheelEntityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in wheel template and verify that it matches with the component id of the wheel instance.
PrefabDomValueReference wheelEntityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(wheelEntityComponentIdValue.has_value());
EXPECT_EQ(prefabTestComponent->GetId(), wheelEntityComponentIdValue->get().GetUint64());
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(wheel1UnderAxle) ), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axleUnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axleUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
// Change the bool property of the component from Wheel instance and use it to update the wheel template.
prefabTestComponent->m_boolProperty = false;
PrefabDom updatedWheelInstanceDom;
ASSERT_TRUE(PrefabDomUtils::StoreInstanceInPrefabDom(*wheelIsolatedInstance, updatedWheelInstanceDom));
m_prefabSystemComponent->UpdatePrefabTemplate(wheelTemplateId, updatedWheelInstanceDom);
// Validate that the prefabTestComponent in the wheel template DOM doesn't have a BoolProperty.
// Even though we changed the property to false, it won't be serialized out because it's a default value.
wheelEntityComponents = PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents != nullptr && wheelEntityComponents->IsArray());
EXPECT_EQ(wheelEntityComponents->GetArray().Size(), 1);
PrefabDomValueReference wheelEntityComponentBoolPropertyValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::BoolPropertyName);
ASSERT_FALSE(wheelEntityComponentBoolPropertyValue.has_value());
// Validate that the wheels under the axle have the same DOM as the wheel template.
PrefabTestDomUtils::ValidatePrefabDomInstances(wheelInstanceAliasesUnderAxle, axleTemplateDom, wheelTemplateDom);
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
}
@@ -0,0 +1,131 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Prefab/PrefabDomUtils.h>
#include <Prefab/PrefabTestComponent.h>
#include <Prefab/PrefabTestDomUtils.h>
#include <Prefab/PrefabTestFixture.h>
#include <AzCore/Component/ComponentApplicationBus.h>
namespace UnitTest
{
using PrefabUpdateWithPatchesTest = PrefabTestFixture;
/*
The below tests use an example of car->axle->wheel templates to test that change propagation works correctly within templates.
The car template will have axle templates nested under it and the axle template will have wheel templates nested under it.
Because of the complexity that arises from multiple levels of prefab nesting, it's easier to write tests using an example scenario
than use generic nesting terminology.
*/
TEST_F(PrefabUpdateWithPatchesTest, ApplyPatchesToInstance_ComponentUpdated_PatchAppliedCorrectly)
{
// Create a single entity wheel instance with a PrefabTestComponent and create a template out of it.
AZ::Entity* wheelEntity = CreateEntity("WheelEntity1", false);
PrefabTestComponent* prefabTestComponent = aznew PrefabTestComponent(true);
wheelEntity->AddComponent(prefabTestComponent);
wheelEntity->Init();
wheelEntity->Activate();
AZStd::unique_ptr<Instance> wheelIsolatedInstance = m_prefabSystemComponent->CreatePrefab({ wheelEntity },
{}, WheelPrefabMockFilePath);
const TemplateId wheelTemplateId = wheelIsolatedInstance->GetTemplateId();
PrefabDom& wheelTemplateDom = m_prefabSystemComponent->FindTemplateDom(wheelTemplateId);
AZStd::vector<EntityAlias> wheelTemplateEntityAliases = wheelIsolatedInstance->GetEntityAliases();
// Validate that the wheel template has the same entities(1) as the instance it was created from.
ASSERT_EQ(wheelTemplateEntityAliases.size(), 1);
// Validate that the wheel entity has 1 component under it.
AZStd::string entityAlias = wheelTemplateEntityAliases.front();
PrefabDomValue* wheelEntityComponents =
PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(wheelTemplateDom);
ASSERT_TRUE(wheelEntityComponents != nullptr && wheelEntityComponents->IsArray());
EXPECT_EQ(wheelEntityComponents->GetArray().Size(), 1);
// Extract the component id of the entity in wheel template and verify that it matches with the component id of the wheel instance.
PrefabDomValueReference wheelEntityComponentIdValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::ComponentIdName);
ASSERT_TRUE(wheelEntityComponentIdValue.has_value());
EXPECT_EQ(prefabTestComponent->GetId(), wheelEntityComponentIdValue->get().GetUint64());
// Create an axle with 0 entities and 1 wheel instance.
AZStd::unique_ptr<Instance> wheel1UnderAxle = m_prefabSystemComponent->InstantiatePrefab(wheelTemplateId);
const AZStd::vector<EntityAlias> wheelEntityAliasesUnderAxle = wheel1UnderAxle->GetEntityAliases();
AZStd::unique_ptr<Instance> axleInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList(AZStd::move(wheel1UnderAxle)), AxlePrefabMockFilePath);
const TemplateId axleTemplateId = axleInstance->GetTemplateId();
PrefabDom& axleTemplateDom = m_prefabSystemComponent->FindTemplateDom(axleTemplateId);
const AZStd::vector<InstanceAlias> wheelInstanceAliasesUnderAxle = axleInstance->GetNestedInstanceAliases(wheelTemplateId);
// Create a car with 0 entities and 1 axle instance.
AZStd::unique_ptr<Instance> axleUnderCar = m_prefabSystemComponent->InstantiatePrefab(axleTemplateId);
AZStd::unique_ptr<Instance> carInstance = m_prefabSystemComponent->CreatePrefab({},
MakeInstanceList( AZStd::move(axleUnderCar) ), CarPrefabMockFilePath);
const TemplateId carTemplateId = carInstance->GetTemplateId();
const AZStd::vector<InstanceAlias> axleInstanceAliasesUnderCar = carInstance->GetNestedInstanceAliases(axleTemplateId);
PrefabDom& carTemplateDom = m_prefabSystemComponent->FindTemplateDom(carTemplateId);
//activate the entity so we can access via transform bus
axleInstance->InitializeNestedEntities();
axleInstance->ActivateNestedEntities();
//get the entity id
AZStd::vector<AZ::EntityId> entityIdVector;
axleInstance->GetNestedEntityIds([&entityIdVector](const AZ::EntityId& entityId)
{
entityIdVector.push_back(entityId);
return true;
});
EXPECT_EQ(entityIdVector.size(), 1);
AZ::EntityId wheelEntityIdUnderAxle = entityIdVector.front();
// Retrieve the entity pointer from the component application bus.
AZ::Entity* wheelEntityUnderAxle = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(wheelEntityUnderAxle, &AZ::ComponentApplicationBus::Events::FindEntity, wheelEntityIdUnderAxle);
//create document with before change snapshot
PrefabDom entityDomBefore;
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomBefore, *wheelEntityUnderAxle);
PrefabTestComponent* axlewheelComponent = wheelEntityUnderAxle->FindComponent<PrefabTestComponent>();
// Change the bool property of the component from Wheel instance and use it to update the wheel template.
axlewheelComponent->m_boolProperty = false;
//create document with after change snapshot
PrefabDom entityDomAfter;
m_instanceToTemplateInterface->GenerateDomForEntity(entityDomAfter, *wheelEntityUnderAxle);
InstanceOptionalReference topMostInstanceInHierarchy = m_instanceToTemplateInterface->GetTopMostInstanceInHierarchy(wheelEntityIdUnderAxle);
ASSERT_TRUE(topMostInstanceInHierarchy);
PrefabDom patches;
InstanceOptionalReference wheelInstanceUnderAxle = axleInstance->FindNestedInstance(wheelInstanceAliasesUnderAxle.front());
m_instanceToTemplateInterface->GeneratePatchForLink(patches, entityDomBefore, entityDomAfter, wheelInstanceUnderAxle->get().GetLinkId());
m_instanceToTemplateInterface->ApplyPatchesToInstance(wheelEntityIdUnderAxle, patches, topMostInstanceInHierarchy->get());
// Validate that the prefabTestComponent in the wheel instance under axle doesn't have a BoolProperty.
// Even though we changed the property to false, it won't be serialized out because it's a default value.
PrefabDomValue* wheelInstanceDomUnderAxle =
PrefabTestDomUtils::GetPrefabDomInstancePath(wheelInstanceAliasesUnderAxle.front()).Get(axleTemplateDom);
wheelEntityComponents = PrefabTestDomUtils::GetPrefabDomComponentsPath(entityAlias).Get(*wheelInstanceDomUnderAxle);
ASSERT_TRUE(wheelEntityComponents != nullptr);
PrefabDomValueReference wheelEntityComponentBoolPropertyValue =
PrefabDomUtils::FindPrefabDomValue(*wheelEntityComponents->Begin(), PrefabTestDomUtils::BoolPropertyName);
ASSERT_FALSE(wheelEntityComponentBoolPropertyValue.has_value());
// Validate that the axles under the car have the same DOM as the axle template.
PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom);
}
}