RCControllerTest.cpp 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  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 "RCControllerTest.h"
  9. #include "native/resourcecompiler/rccontroller.h"
  10. TEST_F(RCcontrollerTest, CompileGroupCreatedWithUnknownStatusForFailedJobs)
  11. {
  12. //Strategy Add a failed job to the job queue list and than ask the rc controller to request compile, it should emit unknown status
  13. using namespace AssetProcessor;
  14. // we have to initialize this to something other than Assetstatus_Unknown here because later on we will be testing the value of assetstatus
  15. AzFramework::AssetSystem::AssetStatus assetStatus = AzFramework::AssetSystem::AssetStatus_Failed;
  16. RCController rcController;
  17. QObject::connect(&rcController, &RCController::CompileGroupCreated,
  18. [&assetStatus]([[maybe_unused]] AssetProcessor::NetworkRequestID groupID, AzFramework::AssetSystem::AssetStatus status)
  19. {
  20. assetStatus = status;
  21. }
  22. );
  23. RCJobListModel* rcJobListModel = rcController.GetQueueModel();
  24. RCJob* job = new RCJob(rcJobListModel);
  25. AssetProcessor::JobDetails jobDetails;
  26. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/failed.dds");
  27. jobDetails.m_jobEntry.m_platformInfo = { "pc", {"desktop", "renderer"} };
  28. jobDetails.m_jobEntry.m_jobKey = "Compile Stuff";
  29. job->SetState(RCJob::failed);
  30. job->Init(jobDetails);
  31. rcJobListModel->addNewJob(job);
  32. // Exact Match
  33. NetworkRequestID requestID(1, 1234);
  34. rcController.OnRequestCompileGroup(requestID, "pc", "somepath/failed.dds", AZ::Data::AssetId());
  35. ASSERT_TRUE(assetStatus == AzFramework::AssetSystem::AssetStatus_Unknown);
  36. assetStatus = AzFramework::AssetSystem::AssetStatus_Failed;
  37. // Broader Match
  38. rcController.OnRequestCompileGroup(requestID, "pc", "somepath", AZ::Data::AssetId() );
  39. ASSERT_TRUE(assetStatus == AzFramework::AssetSystem::AssetStatus_Unknown);
  40. }
  41. class RCcontrollerTest_Cancellation
  42. : public RCcontrollerTest
  43. {
  44. public:
  45. RCcontrollerTest_Cancellation()
  46. {
  47. }
  48. virtual ~RCcontrollerTest_Cancellation()
  49. {
  50. }
  51. void SetUp() override
  52. {
  53. RCcontrollerTest::SetUp();
  54. using namespace AssetProcessor;
  55. m_rcController.reset(new RCController());
  56. m_rcController->SetDispatchPaused(true);
  57. m_rcJobListModel = m_rcController->GetQueueModel();
  58. {
  59. RCJob* job = new RCJob(m_rcJobListModel);
  60. AssetProcessor::JobDetails jobDetails;
  61. jobDetails.m_jobEntry.m_computedFingerprint = 1;
  62. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/failed.dds");
  63. jobDetails.m_jobEntry.m_platformInfo = { "ios",{ "mobile", "renderer" } };
  64. jobDetails.m_jobEntry.m_jobRunKey = 1;
  65. jobDetails.m_jobEntry.m_jobKey = "tiff";
  66. job->SetState(RCJob::JobState::pending);
  67. job->Init(jobDetails);
  68. m_rcJobListModel->addNewJob(job);
  69. }
  70. {
  71. RCJob* job = new RCJob(m_rcJobListModel);
  72. // note that Init() is a move operation. we cannot reuse jobDetails.
  73. AssetProcessor::JobDetails jobDetails;
  74. jobDetails.m_jobEntry.m_computedFingerprint = 1;
  75. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/failed.dds");
  76. jobDetails.m_jobEntry.m_platformInfo = { "pc",{ "desktop", "renderer" } };
  77. jobDetails.m_jobEntry.m_jobRunKey = 2;
  78. jobDetails.m_jobEntry.m_jobKey = "tiff";
  79. job->SetState(RCJob::JobState::pending);
  80. job->Init(jobDetails);
  81. m_rcJobListModel->addNewJob(job);
  82. m_rcJobListModel->markAsStarted(job);
  83. m_rcJobListModel->markAsProcessing(job); // job is now "in flight"
  84. }
  85. }
  86. void TearDown() override
  87. {
  88. m_rcJobListModel = nullptr;
  89. m_rcController.reset();
  90. RCcontrollerTest::TearDown();
  91. }
  92. AZStd::unique_ptr<AssetProcessor::RCController> m_rcController;
  93. AssetProcessor::RCJobListModel* m_rcJobListModel = nullptr; // convenience pointer into m_rcController->GetQueueModel()
  94. };
  95. TEST_F(RCcontrollerTest_Cancellation, JobSubmitted_SameFingerprint_DoesNotCancelTheJob)
  96. {
  97. // submit a new job for the same details as the already running one.
  98. {
  99. AssetProcessor::JobDetails jobDetails;
  100. jobDetails.m_jobEntry.m_computedFingerprint = 1; // same as above in SetUp
  101. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/failed.dds");
  102. jobDetails.m_jobEntry.m_platformInfo = { "pc",{ "desktop", "renderer" } };
  103. jobDetails.m_jobEntry.m_jobKey = "tiff";
  104. jobDetails.m_jobEntry.m_jobRunKey = 3;
  105. m_rcController->JobSubmitted(jobDetails);
  106. }
  107. for (int idx = 0; idx < m_rcJobListModel->itemCount(); idx++)
  108. {
  109. // neither job should be cancelled.
  110. AssetProcessor::RCJob* rcJob = m_rcJobListModel->getItem(idx);
  111. ASSERT_TRUE(rcJob->GetState() != AssetProcessor::RCJob::JobState::cancelled);
  112. }
  113. }
  114. TEST_F(RCcontrollerTest_Cancellation, JobSubmitted_DifferentFingerprint_CancelsTheJob_OnlyIfInProgress)
  115. {
  116. // submit a new job for the same details as the already running one.
  117. {
  118. AssetProcessor::JobDetails jobDetails;
  119. jobDetails.m_jobEntry.m_computedFingerprint = 2; // different from setup.
  120. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/failed.dds");
  121. jobDetails.m_jobEntry.m_platformInfo = { "pc",{ "desktop", "renderer" } };
  122. jobDetails.m_jobEntry.m_jobKey = "tiff";
  123. jobDetails.m_jobEntry.m_jobRunKey = 3;
  124. m_rcController->JobSubmitted(jobDetails);
  125. }
  126. for (int idx = 0; idx < m_rcJobListModel->itemCount(); idx++)
  127. {
  128. // neither job should be cancelled.
  129. AssetProcessor::RCJob* rcJob = m_rcJobListModel->getItem(idx);
  130. if (rcJob->GetJobEntry().m_jobRunKey == 2)
  131. {
  132. // the one with run key 2 should have been cancelled and replaced with run key 3
  133. ASSERT_TRUE(rcJob->GetState() == AssetProcessor::RCJob::JobState::cancelled);
  134. }
  135. else
  136. {
  137. // the other one should have been left alone since it had not yet begun.
  138. ASSERT_TRUE(rcJob->GetState() != AssetProcessor::RCJob::JobState::cancelled);
  139. }
  140. }
  141. }
  142. class RCcontrollerTest_Simple
  143. : public RCcontrollerTest
  144. {
  145. public:
  146. void SetUp() override
  147. {
  148. RCcontrollerTest::SetUp();
  149. using namespace AssetProcessor;
  150. m_rcController.reset(new RCController(/*minJobs*/1, /*maxJobs*/1));
  151. m_rcController->SetDispatchPaused(false);
  152. m_rcJobListModel = m_rcController->GetQueueModel();
  153. qRegisterMetaType<AssetBuilderSDK::ProcessJobResponse>("ProcessJobResponse");
  154. QObject::connect(m_rcController.get(), &RCController::BecameIdle, [this]()
  155. {
  156. m_wait.release();
  157. });
  158. }
  159. void TearDown() override
  160. {
  161. m_rcJobListModel = nullptr;
  162. m_rcController.reset();
  163. RCcontrollerTest::TearDown();
  164. }
  165. void SubmitJob();
  166. AZStd::binary_semaphore m_wait;
  167. AZStd::unique_ptr<AssetProcessor::RCController> m_rcController;
  168. AssetProcessor::RCJobListModel* m_rcJobListModel = nullptr; // convenience pointer into m_rcController->GetQueueModel()
  169. };
  170. void RCcontrollerTest_Simple::SubmitJob()
  171. {
  172. using namespace AssetBuilderSDK;
  173. {
  174. AssetProcessor::JobDetails jobDetails;
  175. jobDetails.m_jobEntry.m_computedFingerprint = 123;
  176. jobDetails.m_jobEntry.m_sourceAssetReference = AssetProcessor::SourceAssetReference("c:/somepath/a.dds");
  177. jobDetails.m_jobEntry.m_platformInfo = { "pc",{ "desktop", "renderer" } };
  178. jobDetails.m_jobEntry.m_jobKey = "tiff";
  179. jobDetails.m_jobEntry.m_jobRunKey = 3;
  180. jobDetails.m_assetBuilderDesc.m_processJobFunction = []([[maybe_unused]] const ProcessJobRequest& request, ProcessJobResponse& response)
  181. {
  182. response.m_resultCode = AssetBuilderSDK::ProcessJobResult_Success;
  183. };
  184. m_rcController->JobSubmitted(jobDetails);
  185. }
  186. // Numbers are a bit arbitrary but this should result in a max wait time of 5s
  187. int retryCount = 100;
  188. do
  189. {
  190. QCoreApplication::processEvents(QEventLoop::AllEvents, 10);
  191. } while (m_wait.try_acquire_for(AZStd::chrono::milliseconds(5)) == false && --retryCount > 0);
  192. ASSERT_GT(retryCount, 0);
  193. }
  194. // This is a regresssion test to ensure the rccontroller can handle multiple jobs for the same file being completed before
  195. // the APM has a chance to send OnFinishedProcesssingJob events
  196. TEST_F(RCcontrollerTest_Simple, DISABLED_SameJobIsCompletedMultipleTimes_CompletesWithoutError)
  197. {
  198. using namespace AssetProcessor;
  199. AZStd::vector<JobEntry> jobEntries;
  200. QObject::connect(m_rcController.get(), &RCController::FileCompiled, [&jobEntries](JobEntry entry, AssetBuilderSDK::ProcessJobResponse response)
  201. {
  202. jobEntries.push_back(entry);
  203. });
  204. SubmitJob();
  205. SubmitJob();
  206. ASSERT_EQ(jobEntries.size(), 2);
  207. for (const JobEntry& entry : jobEntries)
  208. {
  209. m_rcController->OnAddedToCatalog(entry);
  210. }
  211. ASSERT_EQ(m_errorAbsorber->m_numAssertsAbsorbed, 4); // Expected that there are 4 errors related to the files not existing on disk. Error message: GenerateFingerprint was called but no input files were requested for fingerprinting.
  212. ASSERT_EQ(m_errorAbsorber->m_numErrorsAbsorbed, 0);
  213. }