RCJobTest.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407
  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 <native/tests/AssetProcessorTest.h>
  9. #include <native/tests/UnitTestUtilities.h>
  10. #include <native/resourcecompiler/rcjob.h>
  11. #include <AzCore/Serialization/SerializeContext.h>
  12. #include <tests/assetmanager/AssetManagerTestingBase.h>
  13. #include <native/utilities/AssetServerHandler.h>
  14. namespace UnitTests
  15. {
  16. using namespace testing;
  17. using ::testing::NiceMock;
  18. using namespace AssetProcessor;
  19. using namespace AssetBuilderSDK;
  20. class IgnoreNotifyTracker : public ProcessingJobInfoBus::Handler
  21. {
  22. public:
  23. // Will notify other systems which old product is just about to get removed from the cache
  24. // before we copy the new product instead along.
  25. void BeginCacheFileUpdate(const char* productPath) override
  26. {
  27. m_capturedStartPaths.push_back(productPath);
  28. }
  29. // Will notify other systems which product we are trying to copy in the cache
  30. // along with status of whether that copy succeeded or failed.
  31. void EndCacheFileUpdate(const char* productPath, bool /*queueAgainForProcessing*/) override
  32. {
  33. m_capturedStopPaths.push_back(productPath);
  34. }
  35. AZStd::vector<AZStd::string> m_capturedStartPaths;
  36. AZStd::vector<AZStd::string> m_capturedStopPaths;
  37. };
  38. class RCJobTest : public AssetProcessorTest
  39. {
  40. public:
  41. void SetUp() override
  42. {
  43. AssetProcessorTest::SetUp();
  44. m_data.reset(new StaticData());
  45. m_data->tempDirPath = QDir(m_data->m_tempDir.path());
  46. m_data->m_absolutePathToTempInputFolder = m_data->tempDirPath.absoluteFilePath("InputFolder").toUtf8().constData();
  47. // note that the case of OutputFolder is intentionally upper/lower case because
  48. // while files inside the output folder should be lowercased, the path to there should not be lowercased by RCJob.
  49. m_data->m_absolutePathToTempOutputFolder = m_data->tempDirPath.absoluteFilePath("OutputFolder").toUtf8().constData();
  50. m_data->tempDirPath.mkpath(QString::fromUtf8(m_data->m_absolutePathToTempInputFolder.c_str()));
  51. m_data->m_notifyTracker.BusConnect();
  52. // this can be overridden in each test but if you don't override it, then this fixture will do it.
  53. ON_CALL(m_data->m_diskSpaceResponder, CheckSufficientDiskSpace(_, _))
  54. .WillByDefault(Return(true));
  55. }
  56. void TearDown() override
  57. {
  58. m_data->m_notifyTracker.BusDisconnect();
  59. m_data.reset();
  60. AssetProcessorTest::TearDown();
  61. }
  62. protected:
  63. struct StaticData
  64. {
  65. QTemporaryDir m_tempDir;
  66. QDir tempDirPath;
  67. AZStd::string m_absolutePathToTempInputFolder;
  68. AZStd::string m_absolutePathToTempOutputFolder;
  69. AzToolsFramework::UuidUtilComponent m_uuidUtil;
  70. NiceMock<MockDiskSpaceResponder> m_diskSpaceResponder;
  71. IgnoreNotifyTracker m_notifyTracker;
  72. };
  73. AZStd::unique_ptr<StaticData> m_data;
  74. };
  75. TEST_F(RCJobTest, CopyCompiledAssets_NoWorkToDo_Succeeds)
  76. {
  77. BuilderParams builderParams;
  78. ProcessJobResponse response;
  79. EXPECT_TRUE(RCJob::CopyCompiledAssets(builderParams, response));
  80. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  81. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
  82. EXPECT_EQ(m_errorAbsorber->m_numWarningsAbsorbed, 0);
  83. }
  84. TEST_F(RCJobTest, CopyCompiledAssets_InvalidOutputPath_FailsAndAsserts)
  85. {
  86. BuilderParams builderParams;
  87. ProcessJobResponse response;
  88. response.m_resultCode = ProcessJobResult_Success;
  89. response.m_outputProducts.push_back({ "file1.txt" }); // make sure that there is at least one product so that it doesn't early out.
  90. // set only the input path, not the output path:
  91. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  92. EXPECT_FALSE(RCJob::CopyCompiledAssets(builderParams, response));
  93. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 1);
  94. }
  95. TEST_F(RCJobTest, CopyCompiledAssets_InvalidInputPath_FailsAndAsserts)
  96. {
  97. BuilderParams builderParams;
  98. ProcessJobResponse response;
  99. response.m_resultCode = ProcessJobResult_Success;
  100. response.m_outputProducts.push_back({ "file1.txt" }); // make sure that there is at least one product so that it doesn't early out.
  101. // set the input dir to be a broken invalid dir:
  102. builderParams.m_processJobRequest.m_tempDirPath = AZ::Uuid::CreateRandom().ToString<AZStd::string>();
  103. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  104. builderParams.m_intermediateOutputDir = AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  105. EXPECT_FALSE(RCJob::CopyCompiledAssets(builderParams, response));
  106. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 1);
  107. }
  108. TEST_F(RCJobTest, CopyCompiledAssets_TooLongPath_FailsButDoesNotAssert)
  109. {
  110. BuilderParams builderParams;
  111. ProcessJobResponse response;
  112. response.m_resultCode = ProcessJobResult_Success;
  113. // set only the output path, but not the input path:
  114. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  115. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  116. builderParams.m_intermediateOutputDir = AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  117. // give it an overly long file name:
  118. AZStd::string reallyLongFileName;
  119. reallyLongFileName.resize(4096, 'x');
  120. response.m_outputProducts.push_back({ reallyLongFileName.c_str() });
  121. EXPECT_FALSE(RCJob::CopyCompiledAssets(builderParams, response));
  122. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  123. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 1);
  124. }
  125. TEST_F(RCJobTest, CopyCompiledAssets_OutOfDiskSpace_FailsButDoesNotAssert)
  126. {
  127. BuilderParams builderParams;
  128. ProcessJobResponse response;
  129. response.m_resultCode = ProcessJobResult_Success;
  130. // set only the output path, but not the input path:
  131. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  132. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  133. builderParams.m_intermediateOutputDir =
  134. AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  135. response.m_resultCode = ProcessJobResult_Success;
  136. response.m_outputProducts.push_back({ "file1.txt" }); // make sure that there is at least one product so that it doesn't early out.
  137. UnitTestUtils::CreateDummyFile(QDir(m_data->m_absolutePathToTempInputFolder.c_str()).absoluteFilePath("file1.txt"), "output of file 1");
  138. response.m_outputProducts.push_back({ "file2.txt" }); // make sure that there is at least one product so that it doesn't early out.
  139. UnitTestUtils::CreateDummyFile(QDir(m_data->m_absolutePathToTempInputFolder.c_str()).absoluteFilePath("file2.txt"), "output of file 2");
  140. // we expect exactly one call to check for disk space, (not once for each file), and in this case, we'll return false.
  141. EXPECT_CALL(m_data->m_diskSpaceResponder, CheckSufficientDiskSpace(_,_))
  142. .Times(1)
  143. .WillRepeatedly(Return(false));
  144. EXPECT_FALSE(RCJob::CopyCompiledAssets(builderParams, response));
  145. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  146. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 1);
  147. // no notifies should be hit since the operation should not have been attempted at all (disk space should be checked up front)
  148. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStartPaths.size(), 0);
  149. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStopPaths.size(), 0);
  150. // no cached files should have been copied at all.
  151. QString expectedFinalOutputPath = QDir(QString::fromUtf8(m_data->m_absolutePathToTempOutputFolder.c_str())).absoluteFilePath("file1.txt");
  152. EXPECT_FALSE(QFile::exists(expectedFinalOutputPath));
  153. expectedFinalOutputPath = QDir(QString::fromUtf8(m_data->m_absolutePathToTempOutputFolder.c_str())).absoluteFilePath("file2.txt");
  154. EXPECT_FALSE(QFile::exists(expectedFinalOutputPath));
  155. }
  156. // The RC Copy Compiled Assets routine is supposed to check up front for problem situations such as out of disk space
  157. // or missing source files, before it tries to perform any operation. This test gives it one file which does work
  158. // but one missing file also, and expects it to fail (without asserting) but without even trying to copy the files at all.
  159. TEST_F(RCJobTest, CopyCompiledAssets_MissingInputFile_Fails_DoesNotAssert_DoesNotAlterCache)
  160. {
  161. BuilderParams builderParams;
  162. ProcessJobResponse response;
  163. response.m_resultCode = ProcessJobResult_Success;
  164. // set only the output path, but not the input path:
  165. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  166. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  167. builderParams.m_intermediateOutputDir = AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  168. response.m_resultCode = ProcessJobResult_Success;
  169. response.m_outputProducts.push_back({ "FiLe1.TxT" }); // make sure that there is at least one product so that it doesn't early out.
  170. UnitTestUtils::CreateDummyFile(QDir(m_data->m_absolutePathToTempInputFolder.c_str()).absoluteFilePath("FiLe1.TxT"), "output of file 1");
  171. response.m_outputProducts.push_back({ "FiLe2.txt" });
  172. // note well that we create the first file but we don't acutally create the second one, so it is missing.
  173. EXPECT_FALSE(RCJob::CopyCompiledAssets(builderParams, response));
  174. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  175. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 1);
  176. // no notifies should be hit since the operation should not have been attempted at all.
  177. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStartPaths.size(), 0);
  178. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStopPaths.size(), 0);
  179. QString expectedFinalOutputPath = QDir(QString::fromUtf8(m_data->m_absolutePathToTempOutputFolder.c_str())).absoluteFilePath("file1.txt");
  180. EXPECT_FALSE(QFile::exists(expectedFinalOutputPath));
  181. }
  182. TEST_F(RCJobTest, CopyCompiledAssets_AbsolutePath_SucceedsAndNotifiesAboutCacheDelete)
  183. {
  184. BuilderParams builderParams;
  185. ProcessJobResponse response;
  186. response.m_resultCode = ProcessJobResult_Success;
  187. // set only the output path, but not the input path:
  188. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  189. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  190. builderParams.m_intermediateOutputDir = AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  191. builderParams.m_relativePath = "";
  192. response.m_resultCode = ProcessJobResult_Success;
  193. // make up a completely different random path to put an absolute file in:
  194. QTemporaryDir extraDir;
  195. QDir randomDir(extraDir.path());
  196. randomDir.mkpath(extraDir.path());
  197. QString absolutePathToCreate = randomDir.absoluteFilePath("someabsolutefile.txt");
  198. UnitTestUtils::CreateDummyFile(absolutePathToCreate, "output of the file");
  199. response.m_outputProducts.push_back({ absolutePathToCreate.toUtf8().constData() }); // absolute path to file not actually in the product scratch space folder.
  200. // this should copy that file into the target path.
  201. EXPECT_TRUE(RCJob::CopyCompiledAssets(builderParams, response));
  202. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  203. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
  204. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStartPaths.size(), 1);
  205. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStopPaths.size(), 1);
  206. // note that output files are automatically lowercased within the cache but the path to the cache folder itself is not lowered, just the output file.
  207. // this is to make sure that game code never has to worry about the casing of output file paths, CRYPAK can just always lower the relpath and always know
  208. // that even on case-sensitive platforms it won't cause trouble or a difference of behavior from non-case-sensitive ones.
  209. QString expectedFinalOutputPath = QDir(QString::fromUtf8(m_data->m_absolutePathToTempOutputFolder.c_str())).absoluteFilePath("someabsolutefile.txt");
  210. ASSERT_STREQ(m_data->m_notifyTracker.m_capturedStartPaths[0].c_str(), m_data->m_notifyTracker.m_capturedStopPaths[0].c_str());
  211. ASSERT_STREQ(m_data->m_notifyTracker.m_capturedStartPaths[0].c_str(), expectedFinalOutputPath.toUtf8().constData());
  212. EXPECT_TRUE(QFile::exists(expectedFinalOutputPath));
  213. }
  214. TEST_F(RCJobTest, CopyCompiledAssets_RelativePath_SucceedsAndNotifiesAboutCacheDelete)
  215. {
  216. BuilderParams builderParams;
  217. ProcessJobResponse response;
  218. response.m_resultCode = ProcessJobResult_Success;
  219. // set only the output path, but not the input path:
  220. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  221. builderParams.m_cacheOutputDir = m_data->m_absolutePathToTempOutputFolder; // output folder in the 'cache'
  222. builderParams.m_intermediateOutputDir = AssetUtilities::GetIntermediateAssetsFolder(m_data->m_absolutePathToTempOutputFolder.c_str());
  223. response.m_resultCode = ProcessJobResult_Success;
  224. response.m_outputProducts.push_back({ "FiLe1.TxT" }); // make sure that there is at least one product so that it doesn't early out.
  225. UnitTestUtils::CreateDummyFile(QDir(m_data->m_absolutePathToTempInputFolder.c_str()).absoluteFilePath("FiLe1.TxT"), "output of file 1");
  226. EXPECT_TRUE(RCJob::CopyCompiledAssets(builderParams, response));
  227. EXPECT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 0);
  228. EXPECT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
  229. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStartPaths.size(), 1);
  230. ASSERT_EQ(m_data->m_notifyTracker.m_capturedStopPaths.size(), 1);
  231. // note that output files are automatically lowercased within the cache but the path to the cache folder itself is not lowered, just the output file.
  232. // this is to make sure that game code never has to worry about the casing of output file paths, CRYPAK can just always lower the relpath and always know
  233. // that even on case-sensitive platforms it won't cause trouble or a difference of behavior from non-case-sensitive ones.
  234. QString expectedFinalOutputPath = QDir(QString::fromUtf8(m_data->m_absolutePathToTempOutputFolder.c_str())).absoluteFilePath("file1.txt");
  235. ASSERT_STREQ(m_data->m_notifyTracker.m_capturedStartPaths[0].c_str(), m_data->m_notifyTracker.m_capturedStopPaths[0].c_str());
  236. ASSERT_STREQ(m_data->m_notifyTracker.m_capturedStartPaths[0].c_str(), expectedFinalOutputPath.toUtf8().constData());
  237. EXPECT_TRUE(QFile::exists(expectedFinalOutputPath));
  238. // Start and end paths should, however, be normalized even if the input is not.
  239. QString normalizedStartPath = QString::fromUtf8(m_data->m_notifyTracker.m_capturedStartPaths[0].c_str());
  240. normalizedStartPath = AssetUtilities::NormalizeFilePath(normalizedStartPath);
  241. EXPECT_STREQ(normalizedStartPath.toUtf8().constData(), m_data->m_notifyTracker.m_capturedStartPaths[0].c_str());
  242. QString normalizedStopPath = QString::fromUtf8(m_data->m_notifyTracker.m_capturedStopPaths[0].c_str());
  243. normalizedStopPath = AssetUtilities::NormalizeFilePath(normalizedStopPath);
  244. EXPECT_STREQ(normalizedStopPath.toUtf8().constData(), m_data->m_notifyTracker.m_capturedStopPaths[0].c_str());
  245. }
  246. TEST_F(RCJobTest, BeforeStoringJobResult_NonNormalPath_Succeeds)
  247. {
  248. auto serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  249. AssetBuilderSDK::ProcessJobResponse::Reflect(&*serializeContext);
  250. AssetBuilderSDK::JobProduct::Reflect(&*serializeContext);
  251. AzToolsFramework::AssetSystem::AssetJobLogResponse::Reflect(&*serializeContext);
  252. AzFramework::AssetSystem::BaseAssetProcessorMessage::Reflect(&*serializeContext);
  253. auto mockComponentApplication = NiceMock<::UnitTests::MockComponentApplication>();
  254. ON_CALL(mockComponentApplication, GetSerializeContext()).WillByDefault(Return(serializeContext.get()));
  255. AssetProcessor::RCJob rcJob;
  256. BuilderParams builderParams(&rcJob);
  257. builderParams.m_processJobRequest.m_tempDirPath = m_data->m_absolutePathToTempInputFolder.c_str(); // input working scratch space folder
  258. AZStd::string backSlashedPath = builderParams.m_processJobRequest.m_tempDirPath;
  259. AZStd::replace(backSlashedPath.begin(), backSlashedPath.end(), '/', '\\');
  260. AZStd::string frontSlashedPath = builderParams.m_processJobRequest.m_tempDirPath;
  261. AZStd::replace(frontSlashedPath.begin(), frontSlashedPath.end(), '\\', '/');
  262. AZStd::string mixedSlashedPath = builderParams.m_processJobRequest.m_tempDirPath;
  263. auto slashPos = mixedSlashedPath.find_first_of('\\');
  264. if (slashPos == AZStd::string::npos)
  265. {
  266. slashPos = mixedSlashedPath.find_first_of('/');
  267. ASSERT_TRUE(slashPos != AZStd::string::npos);
  268. mixedSlashedPath.replace(slashPos, 1, 1, '\\');
  269. }
  270. else
  271. {
  272. mixedSlashedPath.replace(slashPos, 1, 1, '/');
  273. }
  274. AssetBuilderSDK::ProcessJobResponse jobResponse;
  275. jobResponse.m_outputProducts.push_back({ (AZ::IO::Path(backSlashedPath) / "file1.txt").c_str() });
  276. jobResponse.m_outputProducts.push_back({ (AZ::IO::Path(frontSlashedPath) / "file2.txt").c_str() });
  277. jobResponse.m_outputProducts.push_back({ (AZ::IO::Path(mixedSlashedPath) / "file3.txt").c_str() });
  278. auto outcome = RCJob::BeforeStoringJobResult(builderParams, jobResponse);
  279. EXPECT_TRUE(outcome.IsSuccess());
  280. AZStd::string responseFilePath;
  281. AzFramework::StringFunc::Path::ConstructFull(builderParams.m_processJobRequest.m_tempDirPath.c_str(), AssetBuilderSDK::s_processJobResponseFileName, responseFilePath, true);
  282. const auto* responseOnDisk = AZ::Utils::LoadObjectFromFile<AssetBuilderSDK::ProcessJobResponse>(responseFilePath);
  283. ASSERT_TRUE(responseOnDisk);
  284. EXPECT_STREQ(responseOnDisk->m_outputProducts[0].m_productFileName.c_str(), (AZ::IO::Path("%TEMP%") / "file1.txt").c_str());
  285. EXPECT_STREQ(responseOnDisk->m_outputProducts[1].m_productFileName.c_str(), (AZ::IO::Path("%TEMP%") / "file2.txt").c_str());
  286. EXPECT_STREQ(responseOnDisk->m_outputProducts[2].m_productFileName.c_str(), (AZ::IO::Path("%TEMP%") / "file3.txt").c_str());
  287. delete responseOnDisk;
  288. }
  289. TEST_F(RCJobTest, RCJob_DoWork_SetsProductOutputFlagsCachedAssetAsServer)
  290. {
  291. struct TestRCJob
  292. : public RCJob
  293. {
  294. void DoWork(AssetBuilderSDK::ProcessJobResponse& result, BuilderParams& builderParams, AssetUtilities::QuitListener& listener) override
  295. {
  296. RCJob::DoWork(result, builderParams, listener);
  297. }
  298. };
  299. struct ProxyAssetServerBus
  300. : public AssetServerHandler
  301. {
  302. AssetServerMode GetRemoteCachingMode() const override
  303. {
  304. return AssetServerMode::Server;
  305. }
  306. bool StoreJobResult(const AssetProcessor::BuilderParams&, AZStd::vector<AZStd::string>&) override
  307. {
  308. return true;
  309. }
  310. };
  311. auto serializeContext = AZStd::make_unique<AZ::SerializeContext>();
  312. AssetBuilderSDK::ProcessJobResponse::Reflect(&*serializeContext);
  313. AssetBuilderSDK::JobProduct::Reflect(&*serializeContext);
  314. AzToolsFramework::AssetSystem::AssetJobLogResponse::Reflect(&*serializeContext);
  315. AzFramework::AssetSystem::BaseAssetProcessorMessage::Reflect(&*serializeContext);
  316. auto mockComponentApplication = NiceMock<::UnitTests::MockComponentApplication>();
  317. ON_CALL(mockComponentApplication, GetSerializeContext()).WillByDefault(Return(serializeContext.get()));
  318. ProxyAssetServerBus proxyAssetServerBus;
  319. JobDetails jobDetails;
  320. jobDetails.m_jobEntry.m_jobRunKey = 1;
  321. jobDetails.m_checkServer = true;
  322. AssetProcessor::RCJob rcJob;
  323. rcJob.Init(jobDetails);
  324. BuilderParams builderParams;
  325. builderParams.m_rcJob = &rcJob;
  326. builderParams.m_assetBuilderDesc.m_processJobFunction = [](const ProcessJobRequest&, ProcessJobResponse& response)
  327. {
  328. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  329. response.m_outputProducts.emplace_back(AssetBuilderSDK::JobProduct("outputfile_1.txt"));
  330. response.m_outputProducts.emplace_back(AssetBuilderSDK::JobProduct("outputfile_2.txt"));
  331. };
  332. AssetBuilderSDK::ProcessJobResponse result;
  333. AssetUtilities::QuitListener listener;
  334. TestRCJob testRcJob;
  335. testRcJob.Init(jobDetails);
  336. testRcJob.DoWork(result, builderParams, listener);
  337. EXPECT_EQ(result.m_outputProducts.size(), 2);
  338. EXPECT_EQ(result.m_outputProducts.at(0).m_outputFlags & ProductOutputFlags::CachedAsset, ProductOutputFlags::CachedAsset);
  339. EXPECT_EQ(result.m_outputProducts.at(1).m_outputFlags & ProductOutputFlags::CachedAsset, ProductOutputFlags::CachedAsset);
  340. }
  341. } // end namespace UnitTests