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

351 lines
13 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 <AzTest/AzTest.h>
#include <AzCore/Serialization/ObjectStream.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/Serialization/Utils.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/Serialization/Json/JsonUtils.h>
#include <Atom/RPI.Edit/Common/ConvertibleSource.h>
#include <Atom/RPI.Reflect/System/AnyAsset.h>
#include <Common/AnyAssetBuilder.h>
#include <Tests.Builders/BuilderTestFixture.h>
namespace UnitTest
{
using namespace AZ;
// Basic test class which is also used for output class of convertible class test
class Test1
{
public:
AZ_TYPE_INFO(Test1, "{A3369968-6E98-4319-A4CA-A0E2CF9F2E7C}");
AZ_CLASS_ALLOCATOR(Test1, AZ::SystemAllocator, 0);
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Test1>()
->Version(1)
->Field("m_data", &Test1::m_data)
->Field("m_isConverted", &Test1::m_isConverted)
;
}
}
AZStd::string m_data = "Test1";
bool m_isConverted = false;
};
// Test class with convertible source
class Test2Source :
public RPI::ConvertibleSource
{
public:
AZ_TYPE_INFO(Test2Source, "{D472B405-F688-4EAF-A361-D8D1C63E303D}");
AZ_CLASS_ALLOCATOR(Test2Source, AZ::SystemAllocator, 0);
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<Test2Source, ConvertibleSource>()
->Version(1)
;
}
}
// ConvertibleSource overrides...
bool Convert(TypeId& outTypeId, AZStd::shared_ptr<void>& outData) const override
{
// convert this to Test1
outTypeId = AzTypeInfo<Test1>::Uuid();
auto data = aznew Test1();
data->m_isConverted = true;
outData = AZStd::shared_ptr<void>(data);
return true;
}
};
class TestAssetData
: public Data::AssetData
{
public:
AZ_CLASS_ALLOCATOR(TestAssetData, AZ::SystemAllocator, 0);
AZ_RTTI(TestAssetData, "{A7D2C40A-2559-4DF7-A308-D52286EE16D8}", Data::AssetData);
};
// Test class type with AssetId and Asset reference
class TestAssetIdReference
{
public:
AZ_TYPE_INFO(TestAssetIdReference, "{87DC6B1E-4660-4AEA-AEE1-6F50EF7FA0D7}");
AZ_CLASS_ALLOCATOR(TestAssetIdReference, AZ::SystemAllocator, 0);
virtual ~TestAssetIdReference() = default;
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<TestAssetIdReference>()
->Version(1)
// Not supported by Json Serializer yet. LY-105721
//->Field("m_asset", &TestAssetIdReference::m_asset)
->Field("EmptyAssetId", &TestAssetIdReference::m_emptyAssetId)
->Field("ValidAssetId", &TestAssetIdReference::m_validAssetId)
->Field("DuplicateAssetId", &TestAssetIdReference::m_duplicateAssetId)
->Field("AssetIdInContainer", &TestAssetIdReference::m_assetIdInContainer)
;
}
}
TestAssetIdReference()
: m_asset(Data::AssetLoadBehavior::NoLoad)
{
}
virtual void Init()
{
m_validAssetId = Data::AssetId(Uuid::CreateRandom(), 1);
m_duplicateAssetId = m_validAssetId;
m_asset = Data::Asset<Data::AssetData>(Data::AssetId(Uuid::CreateRandom(), 0), TestAssetData::TYPEINFO_Uuid());
m_assetIdInContainer.push_back(Data::AssetId(Uuid::CreateRandom(), 0));
}
Data::Asset<TestAssetData> m_asset;
Data::AssetId m_emptyAssetId;
Data::AssetId m_validAssetId;
Data::AssetId m_duplicateAssetId;
AZStd::vector<Data::AssetId> m_assetIdInContainer;
// The total amount of unique asset ids referenced by this class object
// one from m_asset, one from m_validAssetId
static const int s_uniqueAssetIdCount = 2;
};
class DerivedTestAssetIdReference
: public TestAssetIdReference
{
public:
AZ_TYPE_INFO(DerivedTestAssetIdReference, "{B5778901-A553-41B2-B411-CF8FBE2B1E10}");
AZ_CLASS_ALLOCATOR(DerivedTestAssetIdReference, AZ::SystemAllocator, 0);
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<DerivedTestAssetIdReference, TestAssetIdReference>()
->Version(1)
;
}
}
};
// Test class includes member which is a class that has asset id and asset reference as members
class TestIndirectAssetIdReference
{
public:
AZ_TYPE_INFO(TestIndirectAssetIdReference, "{402D2672-55CD-46B9-9387-E34D6B10F88A}");
AZ_CLASS_ALLOCATOR(TestIndirectAssetIdReference, AZ::SystemAllocator, 0);
static void Reflect(ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<TestIndirectAssetIdReference>()
->Version(1)
->Field("m_object", &TestIndirectAssetIdReference::m_object)
->Field("m_objectPtr", &TestIndirectAssetIdReference::m_objectPtr)
->Field("m_objects", &TestIndirectAssetIdReference::m_objects)
->Field("m_objectWithBase", &TestIndirectAssetIdReference::m_objectWithBase)
;
}
}
void Init()
{
m_object.Init();
m_objectPtr = AZStd::make_unique<TestAssetIdReference>();
m_objectPtr->Init();
m_objects["Test4"].Init();
m_objectWithBase.Init();
}
TestAssetIdReference m_object;
AZStd::unique_ptr<TestAssetIdReference> m_objectPtr = nullptr;
AZStd::unordered_map<AZStd::string, TestAssetIdReference> m_objects;
DerivedTestAssetIdReference m_objectWithBase;
static const int s_uniqueAssetIdCount = 4 * TestAssetIdReference::s_uniqueAssetIdCount;
};
class AnyAssetBuilderTests
: public BuilderTestFixture
{
protected:
RPI::AnyAssetHandler* m_assetHandler;
void SetUp() override
{
BuilderTestFixture::SetUp();
Test1::Reflect(m_context.get());
Test2Source::Reflect(m_context.get());
TestAssetIdReference::Reflect(m_context.get());
DerivedTestAssetIdReference::Reflect(m_context.get());
TestIndirectAssetIdReference::Reflect(m_context.get());
m_assetHandler = new RPI::AnyAssetHandler();
m_assetHandler->m_serializeContext = m_context.get();
m_assetHandler->Register();
}
void TearDown() override
{
m_assetHandler->Unregister();
delete m_assetHandler;
BuilderTestFixture::TearDown();
}
//////////////////////////////////////////////////////////////////////////
// Helper function to generate source AnyAsset and save it to specified folder
template<typename T>
void SaveClassToAnyAssetSourceFile(T& data, const char* saveFileName)
{
JsonSerializationUtils::SaveObjectToFile<T>(&data, saveFileName);
}
Data::Asset<Data::AssetData> LoadAssetFromFile(const char* assetFile)
{
Data::Asset<Data::AssetData> outAsset(m_assetHandler->CreateAsset(Data::AssetId(Uuid::CreateRandom(), 1), Data::AssetType()),
Data::AssetLoadBehavior::PreLoad);
AZ::u64 fileLength = 0;
AZ::IO::FileIOBase::GetInstance()->Size(assetFile, fileLength);
AZStd::shared_ptr<AZ::Data::AssetDataStream> stream = AZStd::make_shared<AZ::Data::AssetDataStream>();
stream->Open(assetFile, 0, fileLength);
stream->BlockUntilLoadComplete();
m_assetHandler->LoadAssetData(outAsset, stream, {});
return outAsset;
}
};
TEST_F(AnyAssetBuilderTests, ProcessJobBasic)
{
// Basic test: test data before and after are same. Test data class doesn't have converter or asset reference.
RPI::AnyAssetBuilder builder;
AssetBuilderSDK::ProcessJobRequest request;
AssetBuilderSDK::ProcessJobResponse response;
// Initial job request
const char* testAssetName = "AnyAssetTest.source";
request.m_fullPath = testAssetName;
request.m_tempDirPath = m_currentDir;
Test1 test1;
test1.m_data = "first";
SaveClassToAnyAssetSourceFile<Test1>(test1, testAssetName);
// Process
builder.ProcessJob(request, response);
// verify job output
EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
EXPECT_TRUE(response.m_outputProducts.size() == 1);
EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == 0);
// verify input and output data are same
auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
auto outTest1 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<Test1>();
EXPECT_TRUE(test1.m_data == outTest1->m_data);
}
TEST_F(AnyAssetBuilderTests, ProcessJobConvert)
{
RPI::AnyAssetBuilder builder;
AssetBuilderSDK::ProcessJobRequest request;
AssetBuilderSDK::ProcessJobResponse response;
// Initial job request
const char* testAssetName = "AnyAssetTest.source";
request.m_fullPath = testAssetName;
request.m_tempDirPath = m_currentDir;
// Test data class which has a converter
Test2Source test2;
SaveClassToAnyAssetSourceFile<Test2Source>(test2, testAssetName);
builder.ProcessJob(request, response);
EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
auto outTest2 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<Test1>();
EXPECT_TRUE(outTest2 != nullptr);
EXPECT_TRUE(outTest2->m_isConverted);
}
TEST_F(AnyAssetBuilderTests, ProcessJobDependencyDirect)
{
RPI::AnyAssetBuilder builder;
AssetBuilderSDK::ProcessJobRequest request;
AssetBuilderSDK::ProcessJobResponse response;
// Initial job request once
const char* testAssetName = "AnyAssetTest.source";
request.m_fullPath = testAssetName;
request.m_tempDirPath = m_currentDir;
// Test class which has asset id and asset reference as member variables
TestAssetIdReference objectHasAssetIds;
objectHasAssetIds.Init();
SaveClassToAnyAssetSourceFile<TestAssetIdReference>(objectHasAssetIds, testAssetName);
builder.ProcessJob(request, response);
EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
auto outTest3 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<TestAssetIdReference>();
EXPECT_TRUE(outTest3 != nullptr);
EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == objectHasAssetIds.s_uniqueAssetIdCount);
}
TEST_F(AnyAssetBuilderTests, ProcessJobDependencyIndirect)
{
RPI::AnyAssetBuilder builder;
AssetBuilderSDK::ProcessJobRequest request;
AssetBuilderSDK::ProcessJobResponse response;
// Initial job request once
const char* testAssetName = "AnyAssetTest.source";
request.m_fullPath = testAssetName;
request.m_tempDirPath = m_currentDir;
// Test class includes member which its class has asset id and asset reference as children
TestIndirectAssetIdReference test4;
test4.Init();
SaveClassToAnyAssetSourceFile<TestIndirectAssetIdReference>(test4, testAssetName);
builder.ProcessJob(request, response);
EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
auto outTest4 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<TestIndirectAssetIdReference>();
EXPECT_TRUE(outTest4 != nullptr);
EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == test4.s_uniqueAssetIdCount);
}
} // namespace UnitTests