3
0

AnyAssetBuilderTest.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <AzTest/AzTest.h>
  9. #include <AzCore/IO/Streamer/FileRequest.h>
  10. #include <AzCore/Serialization/ObjectStream.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <AzCore/Serialization/Utils.h>
  13. #include <AzCore/std/smart_ptr/make_shared.h>
  14. #include <AzCore/Serialization/Json/JsonUtils.h>
  15. #include <Atom/RPI.Edit/Common/ConvertibleSource.h>
  16. #include <Atom/RPI.Reflect/System/AnyAsset.h>
  17. #include <Common/AnyAssetBuilder.h>
  18. #include <Tests.Builders/BuilderTestFixture.h>
  19. namespace UnitTest
  20. {
  21. using namespace AZ;
  22. // Basic test class which is also used for output class of convertible class test
  23. class Test1
  24. {
  25. public:
  26. AZ_TYPE_INFO(Test1, "{A3369968-6E98-4319-A4CA-A0E2CF9F2E7C}");
  27. AZ_CLASS_ALLOCATOR(Test1, AZ::SystemAllocator);
  28. static void Reflect(ReflectContext* context)
  29. {
  30. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  31. {
  32. serializeContext->Class<Test1>()
  33. ->Version(1)
  34. ->Field("m_data", &Test1::m_data)
  35. ->Field("m_isConverted", &Test1::m_isConverted)
  36. ;
  37. }
  38. }
  39. AZStd::string m_data = "Test1";
  40. bool m_isConverted = false;
  41. };
  42. // Test class with convertible source
  43. class Test2Source :
  44. public RPI::ConvertibleSource
  45. {
  46. public:
  47. AZ_TYPE_INFO(Test2Source, "{D472B405-F688-4EAF-A361-D8D1C63E303D}");
  48. AZ_CLASS_ALLOCATOR(Test2Source, AZ::SystemAllocator);
  49. static void Reflect(ReflectContext* context)
  50. {
  51. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  52. {
  53. serializeContext->Class<Test2Source, ConvertibleSource>()
  54. ->Version(1)
  55. ;
  56. }
  57. }
  58. // ConvertibleSource overrides...
  59. bool Convert(TypeId& outTypeId, AZStd::shared_ptr<void>& outData) const override
  60. {
  61. // convert this to Test1
  62. outTypeId = AzTypeInfo<Test1>::Uuid();
  63. auto data = aznew Test1();
  64. data->m_isConverted = true;
  65. outData = AZStd::shared_ptr<void>(data);
  66. return true;
  67. }
  68. };
  69. class TestAssetData
  70. : public Data::AssetData
  71. {
  72. public:
  73. AZ_CLASS_ALLOCATOR(TestAssetData, AZ::SystemAllocator);
  74. AZ_RTTI(TestAssetData, "{A7D2C40A-2559-4DF7-A308-D52286EE16D8}", Data::AssetData);
  75. };
  76. // Test class type with AssetId and Asset reference
  77. class TestAssetIdReference
  78. {
  79. public:
  80. AZ_TYPE_INFO(TestAssetIdReference, "{87DC6B1E-4660-4AEA-AEE1-6F50EF7FA0D7}");
  81. AZ_CLASS_ALLOCATOR(TestAssetIdReference, AZ::SystemAllocator);
  82. virtual ~TestAssetIdReference() = default;
  83. static void Reflect(ReflectContext* context)
  84. {
  85. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  86. {
  87. serializeContext->Class<TestAssetIdReference>()
  88. ->Version(1)
  89. // Not supported by Json Serializer yet. LY-105721
  90. //->Field("m_asset", &TestAssetIdReference::m_asset)
  91. ->Field("EmptyAssetId", &TestAssetIdReference::m_emptyAssetId)
  92. ->Field("ValidAssetId", &TestAssetIdReference::m_validAssetId)
  93. ->Field("DuplicateAssetId", &TestAssetIdReference::m_duplicateAssetId)
  94. ->Field("AssetIdInContainer", &TestAssetIdReference::m_assetIdInContainer)
  95. ;
  96. }
  97. }
  98. TestAssetIdReference()
  99. : m_asset(Data::AssetLoadBehavior::NoLoad)
  100. {
  101. }
  102. virtual void Init()
  103. {
  104. m_validAssetId = Data::AssetId(Uuid::CreateRandom(), 1);
  105. m_duplicateAssetId = m_validAssetId;
  106. m_asset = Data::Asset<Data::AssetData>(Data::AssetId(Uuid::CreateRandom(), 0), TestAssetData::TYPEINFO_Uuid());
  107. m_assetIdInContainer.push_back(Data::AssetId(Uuid::CreateRandom(), 0));
  108. }
  109. Data::Asset<TestAssetData> m_asset;
  110. Data::AssetId m_emptyAssetId;
  111. Data::AssetId m_validAssetId;
  112. Data::AssetId m_duplicateAssetId;
  113. AZStd::vector<Data::AssetId> m_assetIdInContainer;
  114. // The total amount of unique asset ids referenced by this class object
  115. // one from m_asset, one from m_validAssetId
  116. static const int s_uniqueAssetIdCount = 2;
  117. };
  118. class DerivedTestAssetIdReference
  119. : public TestAssetIdReference
  120. {
  121. public:
  122. AZ_TYPE_INFO(DerivedTestAssetIdReference, "{B5778901-A553-41B2-B411-CF8FBE2B1E10}");
  123. AZ_CLASS_ALLOCATOR(DerivedTestAssetIdReference, AZ::SystemAllocator);
  124. static void Reflect(ReflectContext* context)
  125. {
  126. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  127. {
  128. serializeContext->Class<DerivedTestAssetIdReference, TestAssetIdReference>()
  129. ->Version(1)
  130. ;
  131. }
  132. }
  133. };
  134. // Test class includes member which is a class that has asset id and asset reference as members
  135. class TestIndirectAssetIdReference
  136. {
  137. public:
  138. AZ_TYPE_INFO(TestIndirectAssetIdReference, "{402D2672-55CD-46B9-9387-E34D6B10F88A}");
  139. AZ_CLASS_ALLOCATOR(TestIndirectAssetIdReference, AZ::SystemAllocator);
  140. static void Reflect(ReflectContext* context)
  141. {
  142. if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
  143. {
  144. serializeContext->Class<TestIndirectAssetIdReference>()
  145. ->Version(1)
  146. ->Field("m_object", &TestIndirectAssetIdReference::m_object)
  147. ->Field("m_objectPtr", &TestIndirectAssetIdReference::m_objectPtr)
  148. ->Field("m_objects", &TestIndirectAssetIdReference::m_objects)
  149. ->Field("m_objectWithBase", &TestIndirectAssetIdReference::m_objectWithBase)
  150. ;
  151. }
  152. }
  153. void Init()
  154. {
  155. m_object.Init();
  156. m_objectPtr = AZStd::make_unique<TestAssetIdReference>();
  157. m_objectPtr->Init();
  158. m_objects["Test4"].Init();
  159. m_objectWithBase.Init();
  160. }
  161. TestAssetIdReference m_object;
  162. AZStd::unique_ptr<TestAssetIdReference> m_objectPtr = nullptr;
  163. AZStd::unordered_map<AZStd::string, TestAssetIdReference> m_objects;
  164. DerivedTestAssetIdReference m_objectWithBase;
  165. static const int s_uniqueAssetIdCount = 4 * TestAssetIdReference::s_uniqueAssetIdCount;
  166. };
  167. class AnyAssetBuilderTests
  168. : public BuilderTestFixture
  169. {
  170. protected:
  171. RPI::AnyAssetHandler* m_assetHandler;
  172. void SetUp() override
  173. {
  174. BuilderTestFixture::SetUp();
  175. Test1::Reflect(m_context.get());
  176. Test2Source::Reflect(m_context.get());
  177. TestAssetIdReference::Reflect(m_context.get());
  178. DerivedTestAssetIdReference::Reflect(m_context.get());
  179. TestIndirectAssetIdReference::Reflect(m_context.get());
  180. m_assetHandler = new RPI::AnyAssetHandler();
  181. m_assetHandler->m_serializeContext = m_context.get();
  182. m_assetHandler->Register();
  183. }
  184. void TearDown() override
  185. {
  186. m_assetHandler->Unregister();
  187. delete m_assetHandler;
  188. BuilderTestFixture::TearDown();
  189. }
  190. //////////////////////////////////////////////////////////////////////////
  191. // Helper function to generate source AnyAsset and save it to specified folder
  192. template<typename T>
  193. void SaveClassToAnyAssetSourceFile(T& data, const AZStd::string& saveFileName)
  194. {
  195. JsonSerializationUtils::SaveObjectToFile<T>(&data, saveFileName);
  196. }
  197. Data::Asset<Data::AssetData> LoadAssetFromFile(const char* assetFile)
  198. {
  199. Data::Asset<Data::AssetData> outAsset(m_assetHandler->CreateAsset(Data::AssetId(Uuid::CreateRandom(), 1), Data::AssetType()),
  200. Data::AssetLoadBehavior::PreLoad);
  201. AZ::u64 fileLength = 0;
  202. AZ::IO::FileIOBase::GetInstance()->Size(assetFile, fileLength);
  203. AZStd::shared_ptr<AZ::Data::AssetDataStream> stream = AZStd::make_shared<AZ::Data::AssetDataStream>();
  204. stream->Open(assetFile, 0, fileLength);
  205. stream->BlockUntilLoadComplete();
  206. m_assetHandler->LoadAssetData(outAsset, stream, {});
  207. stream->Close();
  208. // Force a file streamer flush to ensure that file handles don't remain used
  209. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  210. AZStd::binary_semaphore wait;
  211. AZ::IO::FileRequestPtr flushRequest = streamer->FlushCaches();
  212. streamer->SetRequestCompleteCallback(flushRequest, [&wait]([[maybe_unused]] AZ::IO::FileRequestHandle request)
  213. {
  214. wait.release();
  215. });
  216. streamer->QueueRequest(flushRequest);
  217. wait.acquire();
  218. return outAsset;
  219. }
  220. };
  221. TEST_F(AnyAssetBuilderTests, ProcessJobBasic)
  222. {
  223. const char* testAssetName = "AnyAssetTest.source";
  224. AZ::Test::ScopedAutoTempDirectory productDir;
  225. AZ::Test::ScopedAutoTempDirectory sourceDir;
  226. AZ::IO::Path sourceFilePath = sourceDir.Resolve(testAssetName);
  227. // Basic test: test data before and after are same. Test data class doesn't have converter or asset reference.
  228. RPI::AnyAssetBuilder builder;
  229. AssetBuilderSDK::ProcessJobRequest request;
  230. AssetBuilderSDK::ProcessJobResponse response;
  231. // Initial job request
  232. request.m_fullPath = sourceFilePath.Native();
  233. request.m_tempDirPath = productDir.GetDirectory();
  234. Test1 test1;
  235. test1.m_data = "first";
  236. SaveClassToAnyAssetSourceFile<Test1>(test1, sourceFilePath.Native());
  237. // Process
  238. builder.ProcessJob(request, response);
  239. // verify job output
  240. EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
  241. EXPECT_TRUE(response.m_outputProducts.size() == 1);
  242. EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == 0);
  243. // verify input and output data are same
  244. auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
  245. auto outTest1 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<Test1>();
  246. EXPECT_TRUE(test1.m_data == outTest1->m_data);
  247. }
  248. TEST_F(AnyAssetBuilderTests, ProcessJobConvert)
  249. {
  250. const char* testAssetName = "AnyAssetTest.source";
  251. AZ::Test::ScopedAutoTempDirectory productDir;
  252. AZ::Test::ScopedAutoTempDirectory sourceDir;
  253. AZ::IO::Path sourceFilePath = sourceDir.Resolve(testAssetName);
  254. RPI::AnyAssetBuilder builder;
  255. AssetBuilderSDK::ProcessJobRequest request;
  256. AssetBuilderSDK::ProcessJobResponse response;
  257. // Initial job request
  258. request.m_fullPath = sourceFilePath.Native();
  259. request.m_tempDirPath = productDir.GetDirectory();
  260. // Test data class which has a converter
  261. Test2Source test2;
  262. SaveClassToAnyAssetSourceFile<Test2Source>(test2, sourceFilePath.Native());
  263. builder.ProcessJob(request, response);
  264. EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
  265. auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
  266. auto outTest2 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<Test1>();
  267. EXPECT_TRUE(outTest2 != nullptr);
  268. EXPECT_TRUE(outTest2->m_isConverted);
  269. }
  270. TEST_F(AnyAssetBuilderTests, ProcessJobDependencyDirect)
  271. {
  272. const char* testAssetName = "AnyAssetTest.source";
  273. AZ::Test::ScopedAutoTempDirectory productDir;
  274. AZ::Test::ScopedAutoTempDirectory sourceDir;
  275. AZ::IO::Path sourceFilePath = sourceDir.Resolve(testAssetName);
  276. RPI::AnyAssetBuilder builder;
  277. AssetBuilderSDK::ProcessJobRequest request;
  278. AssetBuilderSDK::ProcessJobResponse response;
  279. // Initial job request once
  280. request.m_fullPath = sourceFilePath.Native();
  281. request.m_tempDirPath = productDir.GetDirectory();
  282. // Test class which has asset id and asset reference as member variables
  283. TestAssetIdReference objectHasAssetIds;
  284. objectHasAssetIds.Init();
  285. SaveClassToAnyAssetSourceFile<TestAssetIdReference>(objectHasAssetIds, sourceFilePath.Native());
  286. builder.ProcessJob(request, response);
  287. EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
  288. auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
  289. auto outTest3 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<TestAssetIdReference>();
  290. EXPECT_TRUE(outTest3 != nullptr);
  291. EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == objectHasAssetIds.s_uniqueAssetIdCount);
  292. }
  293. TEST_F(AnyAssetBuilderTests, ProcessJobDependencyIndirect)
  294. {
  295. const char* testAssetName = "AnyAssetTest.source";
  296. AZ::Test::ScopedAutoTempDirectory productDir;
  297. AZ::Test::ScopedAutoTempDirectory sourceDir;
  298. AZ::IO::Path sourceFilePath = sourceDir.Resolve(testAssetName);
  299. RPI::AnyAssetBuilder builder;
  300. AssetBuilderSDK::ProcessJobRequest request;
  301. AssetBuilderSDK::ProcessJobResponse response;
  302. // Initial job request once
  303. request.m_fullPath = sourceFilePath.Native();
  304. request.m_tempDirPath = productDir.GetDirectory();
  305. // Test class includes member which its class has asset id and asset reference as children
  306. TestIndirectAssetIdReference test4;
  307. test4.Init();
  308. SaveClassToAnyAssetSourceFile<TestIndirectAssetIdReference>(test4, sourceFilePath.Native());
  309. builder.ProcessJob(request, response);
  310. EXPECT_TRUE(response.m_resultCode == AssetBuilderSDK::ProcessJobResult_Success);
  311. auto outAsset = LoadAssetFromFile(response.m_outputProducts[0].m_productFileName.c_str());
  312. auto outTest4 = static_cast<RPI::AnyAsset*>(outAsset.GetData())->GetDataAs<TestIndirectAssetIdReference>();
  313. EXPECT_TRUE(outTest4 != nullptr);
  314. EXPECT_TRUE(response.m_outputProducts[0].m_dependencies.size() == test4.s_uniqueAssetIdCount);
  315. }
  316. } // namespace UnitTests