5fc4551ac0
* Enable relocation of the Project Game Release Layout Relocating the Project Game Release Layout to another directory on the file system failed due to the querying of the engine root failing due to the ComponentApplication::m_engineRoot not using the project path stored in the SettingsRegisry if the engine root cannot be detected Removed the ApplicationRequestBus GetEngineRoot function. The ComponentApplicationRequestBus has a function of the same name that returns the same path. Removed the deprecated GetAppRoot function. The path it returns has no defined value. It was not the engine root or the project root. Removed unused CFileUtil and CFileUtil_impl functions that were invoking the ApplicationREquestBus GetEngineRoot function. On the way to update the functions it was discovered that they aren't called Added a CalculateBranchToken overload that can populate a fixed_string to avoid heap allocations Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com> * Protect against an empty list of artifacts to remove when generating the engine.pak Signed-off-by: lumberyard-employee-dm <56135373+lumberyard-employee-dm@users.noreply.github.com>
699 lines
26 KiB
C++
699 lines
26 KiB
C++
/*
|
|
* Copyright (c) Contributors to the Open 3D Engine Project.
|
|
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
*
|
|
*/
|
|
|
|
#include <AssetBuilderSDK/SerializationDependencies.h>
|
|
#include <AzCore/Serialization/SerializeContext.h>
|
|
#include <AzTest/AzTest.h>
|
|
#include <AzToolsFramework/Slice/SliceUtilities.h>
|
|
#include <AzCore/Asset/AssetSerializer.h>
|
|
#include <AzCore/IO/ByteContainerStream.h>
|
|
#include <AzCore/Slice/SliceAssetHandler.h>
|
|
#include <AzCore/std/smart_ptr/make_shared.h>
|
|
#include <AzCore/UnitTest/TestTypes.h>
|
|
#include <AzCore/Component/ComponentApplication.h>
|
|
#include <AzFramework/Asset/GenericAssetHandler.h>
|
|
#include <Builders/SliceBuilder/SliceBuilderWorker.h>
|
|
#include <AzCore/Slice/SliceComponent.h>
|
|
#include <AzCore/Slice/SliceMetadataInfoComponent.h>
|
|
#include <AzToolsFramework/ToolsComponents/EditorComponentBase.h>
|
|
#include <Builders/SliceBuilder/SliceBuilderComponent.h>
|
|
|
|
#include "AzFramework/Asset/SimpleAsset.h"
|
|
#include "Tests/AZTestShared/Utils/Utils.h"
|
|
|
|
namespace UnitTest
|
|
{
|
|
using namespace SliceBuilder;
|
|
using namespace AZ;
|
|
|
|
struct MockAsset
|
|
: public AZ::Data::AssetData
|
|
{
|
|
AZ_RTTI(MockAsset, "{6A98A05A-5B8B-455B-BA92-508A7CF76024}", AZ::Data::AssetData);
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockAsset>()
|
|
->Field("value", &MockAsset::m_value);
|
|
}
|
|
}
|
|
|
|
int m_value = 0;
|
|
};
|
|
|
|
struct MockAssetRefComponent
|
|
: public AZ::Component
|
|
{
|
|
AZ_COMPONENT(MockAssetRefComponent, "{92A6CEC4-BB83-4BED-B062-8A69302E0C9D}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockAssetRefComponent, AZ::Component>()
|
|
->Field("asset", &MockAssetRefComponent::m_asset);
|
|
}
|
|
}
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
Data::Asset<MockAsset> m_asset;
|
|
};
|
|
|
|
class MockSimpleSliceAsset
|
|
{
|
|
public:
|
|
AZ_TYPE_INFO(MockSimpleSliceAsset, "{923AE476-3491-49F7-A77C-70C896C1B1FD}");
|
|
|
|
static const char* GetFileFilter()
|
|
{
|
|
return "*.txt;";
|
|
}
|
|
};
|
|
|
|
struct MockSubType
|
|
{
|
|
AZ_TYPE_INFO(MockSubType, "{25824223-EE7E-4F44-8181-6D3AC5119BB9}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockSubType>()
|
|
->Version(m_version);
|
|
}
|
|
}
|
|
|
|
static int m_version;
|
|
};
|
|
|
|
int MockSubType::m_version = 1;
|
|
|
|
struct MockComponent : AZ::Component
|
|
{
|
|
AZ_COMPONENT(MockComponent, "{0A556691-1658-48B7-9745-5FDBA8E13D11}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockComponent, AZ::Component>()
|
|
->Field("subdata", &MockComponent::m_subData);
|
|
}
|
|
}
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
MockSubType m_subData{};
|
|
};
|
|
|
|
namespace SliceBuilder
|
|
{
|
|
struct MockSimpleSliceAssetRefComponent
|
|
: public AZ::Component
|
|
{
|
|
AZ_COMPONENT(MockSimpleSliceAssetRefComponent, "{C3B2F100-D08C-4912-AC16-57506B190C2F}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
AzFramework::SimpleAssetReference<MockSimpleSliceAsset>::Register(*serializeContext);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockSimpleSliceAssetRefComponent, AZ::Component>()
|
|
->Field("asset", &MockSimpleSliceAssetRefComponent::m_asset);
|
|
}
|
|
}
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
AzFramework::SimpleAssetReference<MockSimpleSliceAsset> m_asset;
|
|
};
|
|
}
|
|
|
|
struct MockEditorComponent
|
|
: public AzToolsFramework::Components::EditorComponentBase
|
|
{
|
|
AZ_EDITOR_COMPONENT(MockEditorComponent, "{550BA62B-9A98-4A6E-BF7D-7BC939796CF5}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<MockEditorComponent, AzToolsFramework::Components::EditorComponentBase>()
|
|
->Field("uuid", &MockEditorComponent::m_uuid);
|
|
}
|
|
}
|
|
|
|
void BuildGameEntity(AZ::Entity* gameEntity) override
|
|
{
|
|
auto* assetComponent = aznew MockAssetRefComponent;
|
|
assetComponent->m_asset = Data::AssetManager::Instance().CreateAsset<MockAsset>(AZ::Data::AssetId(m_uuid, 0), AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
gameEntity->AddComponent(assetComponent);
|
|
}
|
|
|
|
AZ::Uuid m_uuid;
|
|
};
|
|
|
|
using namespace AZ::Data;
|
|
|
|
class SliceBuilderTest_MockCatalog
|
|
: public AssetCatalog
|
|
, public AZ::Data::AssetCatalogRequestBus::Handler
|
|
{
|
|
private:
|
|
AZ::Uuid randomUuid = AZ::Uuid::CreateRandom();
|
|
AZStd::vector<AssetId> m_mockAssetIds;
|
|
|
|
public:
|
|
AZ_CLASS_ALLOCATOR(SliceBuilderTest_MockCatalog, AZ::SystemAllocator, 0);
|
|
|
|
SliceBuilderTest_MockCatalog()
|
|
{
|
|
AssetCatalogRequestBus::Handler::BusConnect();
|
|
}
|
|
|
|
~SliceBuilderTest_MockCatalog()
|
|
{
|
|
AssetCatalogRequestBus::Handler::BusDisconnect();
|
|
}
|
|
|
|
AssetId GenerateMockAssetId()
|
|
{
|
|
AssetId assetId = AssetId(AZ::Uuid::CreateRandom(), 0);
|
|
m_mockAssetIds.push_back(assetId);
|
|
return assetId;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// AssetCatalogRequestBus
|
|
AssetInfo GetAssetInfoById(const AssetId& id) override
|
|
{
|
|
AssetInfo result;
|
|
result.m_assetType = AZ::AzTypeInfo<AZ::SliceAsset>::Uuid();
|
|
for (const AssetId& assetId : m_mockAssetIds)
|
|
{
|
|
if (assetId == id)
|
|
{
|
|
result.m_assetId = id;
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
AssetStreamInfo GetStreamInfoForLoad(const AssetId& id, const AssetType& type) override
|
|
{
|
|
EXPECT_TRUE(type == AzTypeInfo<SliceAsset>::Uuid());
|
|
AssetStreamInfo info;
|
|
info.m_dataOffset = 0;
|
|
info.m_streamFlags = IO::OpenMode::ModeRead;
|
|
|
|
for (int i = 0; i < m_mockAssetIds.size(); ++i)
|
|
{
|
|
if (m_mockAssetIds[i] == id)
|
|
{
|
|
info.m_streamName = AZStd::string::format("MockSliceAssetName%d", i);
|
|
}
|
|
}
|
|
|
|
if (!info.m_streamName.empty())
|
|
{
|
|
// this ensures tha parallel running unit tests do not overlap their files that they use.
|
|
AZStd::string fullName = AZStd::string::format("%s%s-%s", GetTestFolderPath().c_str(), randomUuid.ToString<AZStd::string>().c_str(), info.m_streamName.c_str());
|
|
info.m_streamName = fullName;
|
|
info.m_dataLen = static_cast<size_t>(IO::SystemFile::Length(info.m_streamName.c_str()));
|
|
}
|
|
else
|
|
{
|
|
info.m_dataLen = 0;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
AssetStreamInfo GetStreamInfoForSave(const AssetId& id, const AssetType& type) override
|
|
{
|
|
AssetStreamInfo info;
|
|
info = GetStreamInfoForLoad(id, type);
|
|
info.m_streamFlags = IO::OpenMode::ModeWrite;
|
|
return info;
|
|
}
|
|
|
|
bool SaveAsset(Asset<SliceAsset>& asset)
|
|
{
|
|
volatile bool isDone = false;
|
|
volatile bool succeeded = false;
|
|
AssetBusCallbacks callbacks;
|
|
callbacks.SetCallbacks(nullptr, nullptr, nullptr,
|
|
[&isDone, &succeeded](const Asset<AssetData>& /*asset*/, bool isSuccessful, AssetBusCallbacks& /*callbacks*/)
|
|
{
|
|
isDone = true;
|
|
succeeded = isSuccessful;
|
|
}, nullptr, nullptr, nullptr);
|
|
|
|
callbacks.BusConnect(asset.GetId());
|
|
asset.Save();
|
|
|
|
while (!isDone)
|
|
{
|
|
AssetManager::Instance().DispatchEvents();
|
|
}
|
|
return succeeded;
|
|
}
|
|
};
|
|
|
|
class DependencyTest
|
|
: public AllocatorsFixture
|
|
, public ComponentApplicationBus::Handler
|
|
{
|
|
public:
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// ComponentApplicationMessages
|
|
ComponentApplication* GetApplication() override { return nullptr; }
|
|
void RegisterComponentDescriptor(const ComponentDescriptor*) override { }
|
|
void UnregisterComponentDescriptor(const ComponentDescriptor*) override { }
|
|
void RegisterEntityAddedEventHandler(EntityAddedEvent::Handler&) override { }
|
|
void RegisterEntityRemovedEventHandler(EntityRemovedEvent::Handler&) override { }
|
|
void RegisterEntityActivatedEventHandler(EntityActivatedEvent::Handler&) override { }
|
|
void RegisterEntityDeactivatedEventHandler(EntityDeactivatedEvent::Handler&) override { }
|
|
void SignalEntityActivated(Entity*) override { }
|
|
void SignalEntityDeactivated(Entity*) override { }
|
|
bool AddEntity(Entity*) override { return true; }
|
|
bool RemoveEntity(Entity*) override { return true; }
|
|
bool DeleteEntity(const AZ::EntityId&) override { return true; }
|
|
Entity* FindEntity(const AZ::EntityId&) override { return nullptr; }
|
|
SerializeContext* GetSerializeContext() override { return m_serializeContext; }
|
|
BehaviorContext* GetBehaviorContext() override { return nullptr; }
|
|
JsonRegistrationContext* GetJsonRegistrationContext() override { return nullptr; }
|
|
const char* GetEngineRoot() const override { return nullptr; }
|
|
const char* GetExecutableFolder() const override { return nullptr; }
|
|
void EnumerateEntities(const EntityCallback& /*callback*/) override {}
|
|
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override {}
|
|
//////////////////////////////////////////////////////////////////////////
|
|
|
|
void SetUp() override
|
|
{
|
|
AllocatorsFixture::SetUp();
|
|
|
|
AllocatorInstance<PoolAllocator>::Create();
|
|
AllocatorInstance<ThreadPoolAllocator>::Create();
|
|
|
|
m_serializeContext = aznew SerializeContext(true, true);
|
|
|
|
ComponentApplicationBus::Handler::BusConnect();
|
|
AZ::Interface<AZ::ComponentApplicationRequests>::Register(this);
|
|
|
|
m_sliceDescriptor = SliceComponent::CreateDescriptor();
|
|
m_mockAssetDescriptor = MockAssetRefComponent::CreateDescriptor();
|
|
m_mockSimpleAssetDescriptor = SliceBuilder::MockSimpleSliceAssetRefComponent::CreateDescriptor();
|
|
|
|
m_sliceDescriptor->Reflect(m_serializeContext);
|
|
m_mockAssetDescriptor->Reflect(m_serializeContext);
|
|
m_mockSimpleAssetDescriptor->Reflect(m_serializeContext);
|
|
|
|
AzFramework::SimpleAssetReferenceBase::Reflect(m_serializeContext);
|
|
MockAsset::Reflect(m_serializeContext);
|
|
MockEditorComponent::Reflect(m_serializeContext);
|
|
Entity::Reflect(m_serializeContext);
|
|
DataPatch::Reflect(m_serializeContext);
|
|
SliceMetadataInfoComponent::Reflect(m_serializeContext);
|
|
AzToolsFramework::Components::EditorComponentBase::Reflect(m_serializeContext);
|
|
|
|
// Create database
|
|
Data::AssetManager::Descriptor desc;
|
|
Data::AssetManager::Create(desc);
|
|
Data::AssetManager::Instance().RegisterHandler(aznew SliceAssetHandler(m_serializeContext), AzTypeInfo<AZ::SliceAsset>::Uuid());
|
|
Data::AssetManager::Instance().RegisterHandler(aznew AzFramework::GenericAssetHandler<MockAsset>("Mock Asset", "Other", "mockasset"), AZ::AzTypeInfo<MockAsset>::Uuid());
|
|
m_catalog.reset(aznew SliceBuilderTest_MockCatalog());
|
|
AssetManager::Instance().RegisterCatalog(m_catalog.get(), AzTypeInfo<AZ::SliceAsset>::Uuid());
|
|
}
|
|
|
|
void TearDown() override
|
|
{
|
|
m_catalog->DisableCatalog();
|
|
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(this);
|
|
ComponentApplicationBus::Handler::BusDisconnect();
|
|
|
|
Data::AssetManager::Destroy();
|
|
m_catalog.reset();
|
|
delete m_mockSimpleAssetDescriptor;
|
|
delete m_mockAssetDescriptor;
|
|
delete m_sliceDescriptor;
|
|
delete m_serializeContext;
|
|
|
|
AllocatorInstance<PoolAllocator>::Destroy();
|
|
AllocatorInstance<ThreadPoolAllocator>::Destroy();
|
|
|
|
AllocatorsFixture::TearDown();
|
|
}
|
|
|
|
void VerifyDependency(AZ::Data::Asset<SliceAsset>& sliceAsset, AZ::Data::AssetId mockAssetId)
|
|
{
|
|
AZ::PlatformTagSet platformTags;
|
|
AZ::Data::Asset<SliceAsset> exportSliceAsset;
|
|
|
|
AZStd::shared_ptr<AZ::Data::AssetDataStream> assetDataStream = AZStd::make_shared<AZ::Data::AssetDataStream>();
|
|
|
|
// Save the slice asset into a memory buffer, then hand ownership of the buffer to assetDataStream
|
|
{
|
|
AZ::SliceAssetHandler assetHandler;
|
|
assetHandler.SetSerializeContext(nullptr);
|
|
|
|
AZStd::vector<AZ::u8> charBuffer;
|
|
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8>> charStream(&charBuffer);
|
|
assetHandler.SaveAssetData(sliceAsset, &charStream);
|
|
|
|
assetDataStream->Open(AZStd::move(charBuffer));
|
|
}
|
|
|
|
bool result = SliceBuilderWorker::GetCompiledSliceAsset(assetDataStream, "MockAsset.slice", platformTags, exportSliceAsset);
|
|
ASSERT_TRUE(result);
|
|
|
|
AssetBuilderSDK::JobProduct jobProduct;
|
|
ASSERT_TRUE(SliceBuilderWorker::OutputSliceJob(exportSliceAsset, "test.slice", jobProduct));
|
|
|
|
ASSERT_EQ(jobProduct.m_dependencies.size(), 1);
|
|
ASSERT_EQ(jobProduct.m_dependencies[0].m_dependencyId, mockAssetId);
|
|
}
|
|
|
|
void BuildSliceWithSimpleAssetReference(
|
|
const char* simpleAssetPath,
|
|
AZStd::vector<AssetBuilderSDK::ProductDependency>& productDependencies,
|
|
AssetBuilderSDK::ProductPathDependencySet& productPathDependencies)
|
|
{
|
|
auto* assetComponent = aznew SliceBuilder::MockSimpleSliceAssetRefComponent;
|
|
|
|
assetComponent->m_asset.SetAssetPath(simpleAssetPath);
|
|
|
|
auto sliceAsset = AZ::Test::CreateSliceFromComponent(assetComponent, m_catalog->GenerateMockAssetId());
|
|
|
|
AZ::SliceAssetHandler assetHandler;
|
|
assetHandler.SetSerializeContext(nullptr);
|
|
|
|
AZStd::shared_ptr<AZ::Data::AssetDataStream> assetDataStream = AZStd::make_shared<AZ::Data::AssetDataStream>();
|
|
|
|
// Save the slice asset into a memory buffer, then hand ownership of the buffer to assetDataStream
|
|
{
|
|
AZStd::vector<AZ::u8> charBuffer;
|
|
AZ::IO::ByteContainerStream<AZStd::vector<AZ::u8>> charStream(&charBuffer);
|
|
assetHandler.SaveAssetData(sliceAsset, &charStream);
|
|
|
|
assetDataStream->Open(AZStd::move(charBuffer));
|
|
}
|
|
|
|
AZ::PlatformTagSet platformTags;
|
|
AZ::Data::Asset<SliceAsset> exportSliceAsset;
|
|
|
|
bool result = SliceBuilderWorker::GetCompiledSliceAsset(assetDataStream, "MockAsset.slice", platformTags, exportSliceAsset);
|
|
ASSERT_TRUE(result);
|
|
|
|
AssetBuilderSDK::JobProduct jobProduct;
|
|
ASSERT_TRUE(SliceBuilderWorker::OutputSliceJob(exportSliceAsset, "test.slice", jobProduct));
|
|
|
|
productDependencies = AZStd::move(jobProduct.m_dependencies);
|
|
productPathDependencies = AZStd::move(jobProduct.m_pathDependencies);
|
|
}
|
|
|
|
SerializeContext* m_serializeContext;
|
|
ComponentDescriptor* m_sliceDescriptor;
|
|
ComponentDescriptor* m_mockAssetDescriptor;
|
|
ComponentDescriptor* m_mockSimpleAssetDescriptor;
|
|
AZStd::unique_ptr<SliceBuilderTest_MockCatalog> m_catalog;
|
|
};
|
|
|
|
TEST_F(DependencyTest, SimpleSliceTest)
|
|
{
|
|
// Test a slice containing a component that references an asset
|
|
// Should return a dependency on the asset
|
|
|
|
auto* assetComponent = aznew MockAssetRefComponent;
|
|
|
|
AZ::Data::AssetId mockAssetId(AZ::Uuid::CreateRandom(), 0);
|
|
assetComponent->m_asset = Data::AssetManager::Instance().CreateAsset<MockAsset>(mockAssetId, AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
auto sliceAsset = AZ::Test::CreateSliceFromComponent(assetComponent, m_catalog->GenerateMockAssetId());
|
|
|
|
VerifyDependency(sliceAsset, mockAssetId);
|
|
}
|
|
|
|
TEST_F(DependencyTest, NestedSliceTest)
|
|
{
|
|
// Test a slice that references another slice, which contains a reference to an asset.
|
|
// Should return only a dependency on the asset, and not the inner slice
|
|
|
|
auto* outerSliceEntity = aznew AZ::Entity;
|
|
auto* assetComponent = aznew MockAssetRefComponent;
|
|
|
|
AZ::Data::AssetId mockAssetId(m_catalog->GenerateMockAssetId());
|
|
assetComponent->m_asset = Data::AssetManager::Instance().CreateAsset<MockAsset>(mockAssetId, AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
auto innerSliceAsset = AZ::Test::CreateSliceFromComponent(assetComponent, m_catalog->GenerateMockAssetId());
|
|
|
|
AZ::Data::AssetId outerSliceAssetId(m_catalog->GenerateMockAssetId());
|
|
auto outerSliceAsset = Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(outerSliceAssetId, AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
AZ::SliceComponent* outerSlice = outerSliceEntity->CreateComponent<AZ::SliceComponent>();
|
|
outerSlice->SetIsDynamic(true);
|
|
outerSliceAsset.Get()->SetData(outerSliceEntity, outerSlice);
|
|
outerSlice->AddSlice(innerSliceAsset);
|
|
|
|
VerifyDependency(outerSliceAsset, mockAssetId);
|
|
}
|
|
|
|
TEST_F(DependencyTest, DataPatchTest)
|
|
{
|
|
// Test a slice that references another slice, with the outer slice being data-patched to have a reference to an asset
|
|
// Should return a dependency on the asset, but not the inner slice
|
|
|
|
auto* outerSliceEntity = aznew AZ::Entity;
|
|
auto* assetComponent = aznew MockAssetRefComponent;
|
|
|
|
AZ::Data::AssetId outerSliceAssetId(m_catalog->GenerateMockAssetId());
|
|
auto outerSliceAsset = Data::AssetManager::Instance().CreateAsset<AZ::SliceAsset>(outerSliceAssetId, AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
auto innerSliceAsset = AZ::Test::CreateSliceFromComponent(nullptr, m_catalog->GenerateMockAssetId());
|
|
|
|
AZ::SliceComponent* outerSlice = outerSliceEntity->CreateComponent<AZ::SliceComponent>();
|
|
outerSlice->SetIsDynamic(true);
|
|
outerSliceAsset.Get()->SetData(outerSliceEntity, outerSlice);
|
|
outerSlice->AddSlice(innerSliceAsset);
|
|
|
|
outerSlice->Instantiate();
|
|
|
|
auto* sliceRef = outerSlice->GetSlice(innerSliceAsset);
|
|
auto& instances = sliceRef->GetInstances();
|
|
|
|
AZ::Data::AssetId mockAssetId(m_catalog->GenerateMockAssetId());
|
|
assetComponent->m_asset = Data::AssetManager::Instance().CreateAsset<MockAsset>(mockAssetId, AZ::Data::AssetLoadBehavior::Default);
|
|
|
|
for (const AZ::SliceComponent::SliceInstance& i : instances)
|
|
{
|
|
const AZ::SliceComponent::InstantiatedContainer* container = i.GetInstantiated();
|
|
container->m_entities[0]->AddComponent(assetComponent);
|
|
|
|
sliceRef->ComputeDataPatch();
|
|
}
|
|
|
|
VerifyDependency(outerSliceAsset, mockAssetId);
|
|
|
|
delete assetComponent;
|
|
}
|
|
|
|
TEST_F(DependencyTest, DynamicAssetReferenceTest)
|
|
{
|
|
// Test a slice that has a component which synthesizes an asset reference at runtime
|
|
// Should return a dependency on the asset
|
|
|
|
auto* assetComponent = aznew MockEditorComponent;
|
|
|
|
AZ::Data::AssetId mockAssetId(AZ::Uuid::CreateRandom(), 0);
|
|
assetComponent->m_uuid = mockAssetId.m_guid;
|
|
|
|
auto sliceAsset = AZ::Test::CreateSliceFromComponent(assetComponent, m_catalog->GenerateMockAssetId());
|
|
|
|
VerifyDependency(sliceAsset, mockAssetId);
|
|
}
|
|
|
|
TEST_F(DependencyTest, Slice_HasPopulatedSimpleAssetReference_HasCorrectProductDependency)
|
|
{
|
|
// Test a slice containing a component with a simple asset reference
|
|
// Should return a path dependency
|
|
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
|
|
AssetBuilderSDK::ProductPathDependencySet productPathDependencySet;
|
|
constexpr char testPath[] = "some/test/path.txt";
|
|
BuildSliceWithSimpleAssetReference(testPath, productDependencies, productPathDependencySet);
|
|
|
|
ASSERT_EQ(productDependencies.size(), 0);
|
|
ASSERT_EQ(productPathDependencySet.size(), 1);
|
|
|
|
auto& dependency = *productPathDependencySet.begin();
|
|
ASSERT_STREQ(dependency.m_dependencyPath.c_str(), testPath);
|
|
}
|
|
|
|
TEST_F(DependencyTest, Slice_HasEmptySimpleAssetReference_HasNoProductDependency)
|
|
{
|
|
// Test a slice containing a component with an empty simple asset reference
|
|
// Should not return a path dependency
|
|
AZStd::vector<AssetBuilderSDK::ProductDependency> productDependencies;
|
|
AssetBuilderSDK::ProductPathDependencySet productPathDependencySet;
|
|
BuildSliceWithSimpleAssetReference("", productDependencies, productPathDependencySet);
|
|
|
|
ASSERT_EQ(productDependencies.size(), 0);
|
|
ASSERT_EQ(productPathDependencySet.size(), 0);
|
|
}
|
|
|
|
struct ServiceTestComponent
|
|
: public AZ::Component
|
|
{
|
|
AZ_COMPONENT(ServiceTestComponent, "{CBC4FCB6-FFD2-4097-844D-A01B09042DF4}");
|
|
|
|
static void Reflect(ReflectContext* reflection)
|
|
{
|
|
SerializeContext* serializeContext = azrtti_cast<SerializeContext*>(reflection);
|
|
|
|
if (serializeContext)
|
|
{
|
|
serializeContext->Class<ServiceTestComponent, AZ::Component>()
|
|
->Field("field", &ServiceTestComponent::m_field);
|
|
}
|
|
}
|
|
|
|
static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required)
|
|
{
|
|
if (m_enableServiceDependency)
|
|
{
|
|
required.push_back(AZ_CRC("SomeService", 0x657d5763));
|
|
}
|
|
}
|
|
|
|
void Activate() override {}
|
|
void Deactivate() override {}
|
|
|
|
int m_field{};
|
|
|
|
static bool m_enableServiceDependency;
|
|
};
|
|
|
|
bool ServiceTestComponent::m_enableServiceDependency = false;
|
|
|
|
TEST_F(DependencyTest, SliceFingerprint_ChangesWhenComponentServicesChange)
|
|
{
|
|
using namespace AzToolsFramework::Fingerprinting;
|
|
|
|
AZStd::unique_ptr<ComponentDescriptor> descriptor(ServiceTestComponent::CreateDescriptor());
|
|
descriptor->Reflect(m_serializeContext);
|
|
|
|
auto* assetComponent = aznew ServiceTestComponent;
|
|
auto sliceAsset = AZ::Test::CreateSliceFromComponent(assetComponent, m_catalog->GenerateMockAssetId());
|
|
SliceComponent* sourcePrefab = sliceAsset.Get() ? sliceAsset.Get()->GetComponent() : nullptr;
|
|
|
|
TypeFingerprint fingerprintNoService, fingerprintWithService;
|
|
|
|
{
|
|
TypeFingerprinter fingerprinter(*m_serializeContext);
|
|
fingerprintNoService = fingerprinter.GenerateFingerprintForAllTypesInObject(sourcePrefab);
|
|
}
|
|
|
|
ServiceTestComponent::m_enableServiceDependency = true;
|
|
|
|
{
|
|
TypeFingerprinter fingerprinter(*m_serializeContext);
|
|
fingerprintWithService = fingerprinter.GenerateFingerprintForAllTypesInObject(sourcePrefab);
|
|
}
|
|
|
|
ASSERT_NE(fingerprintNoService, fingerprintWithService);
|
|
|
|
ServiceTestComponent::m_enableServiceDependency = false;
|
|
|
|
{
|
|
// Check again to make sure the fingerprint is stable
|
|
TypeFingerprinter fingerprinter(*m_serializeContext);
|
|
TypeFingerprint fingerprintNoServiceDoubleCheck = fingerprinter.GenerateFingerprintForAllTypesInObject(sourcePrefab);
|
|
|
|
ASSERT_EQ(fingerprintNoService, fingerprintNoServiceDoubleCheck);
|
|
}
|
|
}
|
|
|
|
struct BuilderRegisterListener : AssetBuilderSDK::AssetBuilderBus::Handler
|
|
{
|
|
BuilderRegisterListener()
|
|
{
|
|
BusConnect();
|
|
}
|
|
|
|
~BuilderRegisterListener()
|
|
{
|
|
BusDisconnect();
|
|
}
|
|
|
|
|
|
void RegisterBuilderInformation(const AssetBuilderSDK::AssetBuilderDesc& desc) override
|
|
{
|
|
m_desc = desc;
|
|
}
|
|
|
|
AssetBuilderSDK::AssetBuilderDesc m_desc;
|
|
};
|
|
|
|
TEST_F(DependencyTest, SliceBuilderFingerprint_ChangesWhenNestedTypeChanges)
|
|
{
|
|
BuilderRegisterListener listener;
|
|
AZStd::string fingerprintA, fingerprintB;
|
|
|
|
auto* descriptor = MockComponent::CreateDescriptor();
|
|
|
|
descriptor->Reflect(m_serializeContext);
|
|
MockSubType::Reflect(m_serializeContext);
|
|
|
|
{
|
|
BuilderPluginComponent builder;
|
|
builder.Activate();
|
|
fingerprintA = listener.m_desc.m_analysisFingerprint;
|
|
}
|
|
|
|
// Unreflect the sub type, change the version, and reflect again
|
|
m_serializeContext->EnableRemoveReflection();
|
|
MockSubType::Reflect(m_serializeContext);
|
|
m_serializeContext->DisableRemoveReflection();
|
|
|
|
MockSubType::m_version = 2;
|
|
MockSubType::Reflect(m_serializeContext);
|
|
|
|
{
|
|
BuilderPluginComponent builder;
|
|
builder.Activate();
|
|
fingerprintB = listener.m_desc.m_analysisFingerprint;
|
|
}
|
|
|
|
delete descriptor;
|
|
|
|
EXPECT_STRNE(fingerprintA.c_str(), fingerprintB.c_str());
|
|
}
|
|
}
|