/* * 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 #include #include #include #include #include #include #include #include #include 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(context)) { serializeContext->Class() ->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(context)) { serializeContext->Class() ->Version(1) ; } } // ConvertibleSource overrides... bool Convert(TypeId& outTypeId, AZStd::shared_ptr& outData) const override { // convert this to Test1 outTypeId = AzTypeInfo::Uuid(); auto data = aznew Test1(); data->m_isConverted = true; outData = AZStd::shared_ptr(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(context)) { serializeContext->Class() ->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::AssetId(Uuid::CreateRandom(), 0), TestAssetData::TYPEINFO_Uuid()); m_assetIdInContainer.push_back(Data::AssetId(Uuid::CreateRandom(), 0)); } Data::Asset m_asset; Data::AssetId m_emptyAssetId; Data::AssetId m_validAssetId; Data::AssetId m_duplicateAssetId; AZStd::vector 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(context)) { serializeContext->Class() ->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(context)) { serializeContext->Class() ->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(); m_objectPtr->Init(); m_objects["Test4"].Init(); m_objectWithBase.Init(); } TestAssetIdReference m_object; AZStd::unique_ptr m_objectPtr = nullptr; AZStd::unordered_map 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 void SaveClassToAnyAssetSourceFile(T& data, const char* saveFileName) { JsonSerializationUtils::SaveObjectToFile(&data, saveFileName); } Data::Asset LoadAssetFromFile(const char* assetFile) { Data::Asset 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 stream = AZStd::make_shared(); 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, 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(outAsset.GetData())->GetDataAs(); 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(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(outAsset.GetData())->GetDataAs(); 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(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(outAsset.GetData())->GetDataAs(); 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(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(outAsset.GetData())->GetDataAs(); EXPECT_TRUE(outTest4 != nullptr); EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == test4.s_uniqueAssetIdCount); } } // namespace UnitTests