TestImpactRuntime.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. /*
  2. * 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.
  3. *
  4. * SPDX-License-Identifier: Apache-2.0 OR MIT
  5. *
  6. */
  7. #include <TestImpactFramework/TestImpactFileUtils.h>
  8. #include <TestImpactFramework/TestImpactRuntime.h>
  9. #include <TestImpactFramework/TestImpactRuntimeException.h>
  10. #include <TestImpactRuntimeUtils.h>
  11. #include <Dependency/TestImpactDependencyException.h>
  12. #include <Dependency/TestImpactDynamicDependencyMap.h>
  13. #include <Dependency/TestImpactSourceCoveringTestsSerializer.h>
  14. #include <Dependency/TestImpactTestSelectorAndPrioritizer.h>
  15. #include <TestEngine/TestImpactTestEngine.h>
  16. #include <AzCore/IO/SystemFile.h>
  17. namespace TestImpact
  18. {
  19. namespace
  20. {
  21. static const char* const LogCallSite = "TestImpact";
  22. //! Simple helper class for tracking basic timing information.
  23. class Timer
  24. {
  25. public:
  26. Timer()
  27. : m_startTime(AZStd::chrono::high_resolution_clock::now())
  28. {
  29. }
  30. //! Returns the time elapsed (in milliseconds) since the timer was instantiated
  31. AZStd::chrono::milliseconds Elapsed()
  32. {
  33. const auto endTime = AZStd::chrono::high_resolution_clock::now();
  34. return AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(endTime - m_startTime);
  35. }
  36. private:
  37. AZStd::chrono::high_resolution_clock::time_point m_startTime;
  38. };
  39. //! Handler for test run complete events.
  40. class TestRunCompleteCallbackHandler
  41. {
  42. public:
  43. TestRunCompleteCallbackHandler(AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
  44. : m_testCompleteCallback(testCompleteCallback)
  45. {
  46. }
  47. void operator()(const TestEngineJob& testJob)
  48. {
  49. if (m_testCompleteCallback.has_value())
  50. {
  51. (*m_testCompleteCallback)
  52. (Client::TestRun(testJob.GetTestTarget()->GetName(), testJob.GetTestResult(), testJob.GetDuration()));
  53. }
  54. }
  55. private:
  56. AZStd::optional<TestRunCompleteCallback> m_testCompleteCallback;
  57. };
  58. }
  59. //! Utility for concatenating two vectors.
  60. template<typename T>
  61. AZStd::vector<T> ConcatenateVectors(const AZStd::vector<T>& v1, const AZStd::vector<T>& v2)
  62. {
  63. AZStd::vector<T> result;
  64. result.reserve(v1.size() + v2.size());
  65. result.insert(result.end(), v1.begin(), v1.end());
  66. result.insert(result.end(), v2.begin(), v2.end());
  67. return result;
  68. }
  69. Runtime::Runtime(
  70. RuntimeConfig&& config,
  71. SuiteType suiteFilter,
  72. Policy::ExecutionFailure executionFailurePolicy,
  73. Policy::FailedTestCoverage failedTestCoveragePolicy,
  74. Policy::TestFailure testFailurePolicy,
  75. Policy::IntegrityFailure integrationFailurePolicy,
  76. Policy::TestSharding testShardingPolicy,
  77. Policy::TargetOutputCapture targetOutputCapture,
  78. AZStd::optional<size_t> maxConcurrency)
  79. : m_config(AZStd::move(config))
  80. , m_suiteFilter(suiteFilter)
  81. , m_executionFailurePolicy(executionFailurePolicy)
  82. , m_failedTestCoveragePolicy(failedTestCoveragePolicy)
  83. , m_testFailurePolicy(testFailurePolicy)
  84. , m_integrationFailurePolicy(integrationFailurePolicy)
  85. , m_testShardingPolicy(testShardingPolicy)
  86. , m_targetOutputCapture(targetOutputCapture)
  87. , m_maxConcurrency(maxConcurrency.value_or(AZStd::thread::hardware_concurrency()))
  88. {
  89. // Construct the dynamic dependency map from the build target descriptors
  90. m_dynamicDependencyMap = ConstructDynamicDependencyMap(suiteFilter, m_config.m_buildTargetDescriptor, m_config.m_testTargetMeta);
  91. // Construct the test selector and prioritizer from the dependency graph data (NOTE: currently not implemented)
  92. m_testSelectorAndPrioritizer = AZStd::make_unique<TestSelectorAndPrioritizer>(m_dynamicDependencyMap.get(), DependencyGraphDataMap{});
  93. // Construct the target exclude list from the target configuration data
  94. m_testTargetExcludeList = ConstructTestTargetExcludeList(m_dynamicDependencyMap->GetTestTargetList(), m_config.m_target.m_excludedTestTargets);
  95. // Construct the test engine with the workspace path and launcher binaries
  96. m_testEngine = AZStd::make_unique<TestEngine>(
  97. m_config.m_repo.m_root,
  98. m_config.m_target.m_outputDirectory,
  99. m_config.m_workspace.m_active.m_enumerationCacheDirectory,
  100. m_config.m_workspace.m_temp.m_artifactDirectory,
  101. m_config.m_testEngine.m_testRunner.m_binary,
  102. m_config.m_testEngine.m_instrumentation.m_binary,
  103. m_maxConcurrency);
  104. try
  105. {
  106. // Populate the dynamic dependency map with the existing source coverage data (if any)
  107. m_sparTIAFile = m_config.m_workspace.m_active.m_sparTIAFiles[static_cast<size_t>(m_suiteFilter)].String();
  108. const auto tiaDataRaw = ReadFileContents<Exception>(m_sparTIAFile);
  109. const auto tiaData = DeserializeSourceCoveringTestsList(tiaDataRaw);
  110. if (tiaData.GetNumSources())
  111. {
  112. m_dynamicDependencyMap->ReplaceSourceCoverage(tiaData);
  113. m_hasImpactAnalysisData = true;
  114. // Enumerate new test targets
  115. const auto testTargetsWithNoEnumeration = m_dynamicDependencyMap->GetNotCoveringTests();
  116. if (!testTargetsWithNoEnumeration.empty())
  117. {
  118. m_testEngine->UpdateEnumerationCache(
  119. testTargetsWithNoEnumeration,
  120. Policy::ExecutionFailure::Ignore,
  121. Policy::TestFailure::Continue,
  122. AZStd::nullopt,
  123. AZStd::nullopt,
  124. AZStd::nullopt);
  125. }
  126. }
  127. }
  128. catch (const DependencyException& e)
  129. {
  130. if (integrationFailurePolicy == Policy::IntegrityFailure::Abort)
  131. {
  132. throw RuntimeException(e.what());
  133. }
  134. }
  135. catch ([[maybe_unused]]const Exception& e)
  136. {
  137. AZ_Printf(
  138. LogCallSite,
  139. AZStd::string::format(
  140. "No test impact analysis data found for suite '%s' at %s\n", GetSuiteTypeName(m_suiteFilter).c_str(), m_sparTIAFile.c_str()).c_str());
  141. }
  142. }
  143. Runtime::~Runtime() = default;
  144. void Runtime::EnumerateMutatedTestTargets(const ChangeDependencyList& changeDependencyList)
  145. {
  146. AZStd::vector<const TestTarget*> testTargets;
  147. const auto addMutatedTestTargetsToEnumerationList = [this, &testTargets](const AZStd::vector<SourceDependency>& sourceDependencies)
  148. {
  149. for (const auto& sourceDependency : sourceDependencies)
  150. {
  151. for (const auto& parentTarget : sourceDependency.GetParentTargets())
  152. {
  153. AZStd::visit([&testTargets]([[maybe_unused]] auto&& target)
  154. {
  155. if constexpr (IsTestTarget<decltype(target)>)
  156. {
  157. testTargets.push_back(target);
  158. }
  159. }, parentTarget.GetTarget());
  160. }
  161. }
  162. };
  163. // Gather all of the test targets that have had any of their sources modified
  164. addMutatedTestTargetsToEnumerationList(changeDependencyList.GetCreateSourceDependencies());
  165. addMutatedTestTargetsToEnumerationList(changeDependencyList.GetUpdateSourceDependencies());
  166. addMutatedTestTargetsToEnumerationList(changeDependencyList.GetDeleteSourceDependencies());
  167. // Enumerate the mutated test targets to ensure their enumeration caches are up to date
  168. if (!testTargets.empty())
  169. {
  170. m_testEngine->UpdateEnumerationCache(
  171. testTargets,
  172. Policy::ExecutionFailure::Ignore,
  173. Policy::TestFailure::Continue,
  174. AZStd::nullopt,
  175. AZStd::nullopt,
  176. AZStd::nullopt);
  177. }
  178. }
  179. AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> Runtime::SelectCoveringTestTargetsAndUpdateEnumerationCache(
  180. const ChangeList& changeList,
  181. Policy::TestPrioritization testPrioritizationPolicy)
  182. {
  183. AZStd::vector<const TestTarget*> discardedTestTargets;
  184. // Select and prioritize the test targets pertinent to this change list
  185. const auto changeDependencyList = m_dynamicDependencyMap->ApplyAndResoveChangeList(changeList);
  186. const auto selectedTestTargets = m_testSelectorAndPrioritizer->SelectTestTargets(changeDependencyList, testPrioritizationPolicy);
  187. // Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list
  188. const AZStd::unordered_set<const TestTarget*> selectedTestTargetSet(selectedTestTargets.begin(), selectedTestTargets.end());
  189. // Update the enumeration caches of mutated targets regardless of the current sharding policy
  190. EnumerateMutatedTestTargets(changeDependencyList);
  191. // The test targets in the main list not in the selected test target set are the test targets not selected for this change list
  192. for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
  193. {
  194. if (!selectedTestTargetSet.contains(&testTarget))
  195. {
  196. discardedTestTargets.push_back(&testTarget);
  197. }
  198. }
  199. return { selectedTestTargets, discardedTestTargets };
  200. }
  201. AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> Runtime::SelectTestTargetsByExcludeList(
  202. AZStd::vector<const TestTarget*> testTargets) const
  203. {
  204. AZStd::vector<const TestTarget*> includedTestTargets;
  205. AZStd::vector<const TestTarget*> excludedTestTargets;
  206. if (m_testTargetExcludeList.empty())
  207. {
  208. return { testTargets, {} };
  209. }
  210. for (const auto& testTarget : testTargets)
  211. {
  212. if (!m_testTargetExcludeList.contains(testTarget))
  213. {
  214. includedTestTargets.push_back(testTarget);
  215. }
  216. else
  217. {
  218. excludedTestTargets.push_back(testTarget);
  219. }
  220. }
  221. return { includedTestTargets, excludedTestTargets };
  222. }
  223. void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile()
  224. {
  225. m_dynamicDependencyMap->ClearAllSourceCoverage();
  226. DeleteFile(m_sparTIAFile);
  227. }
  228. SourceCoveringTestsList Runtime::CreateSourceCoveringTestFromTestCoverages(const AZStd::vector<TestEngineInstrumentedRun>& jobs)
  229. {
  230. AZStd::unordered_map<AZStd::string, AZStd::unordered_set<AZStd::string>> coverage;
  231. for (const auto& job : jobs)
  232. {
  233. // First we must remove any existing coverage for the test target so as to not end up with source remnants from previous
  234. // coverage that is no longer covered by this revision of the test target
  235. m_dynamicDependencyMap->RemoveTestTargetFromSourceCoverage(job.GetTestTarget());
  236. // Next we will update the coverage of test targets that completed (with or without failures), unless the failed test coverage
  237. // policy dictates we should instead discard the coverage of test targets with failing tests
  238. const auto testResult = job.GetTestResult();
  239. if (m_failedTestCoveragePolicy == Policy::FailedTestCoverage::Discard && testResult == Client::TestRunResult::TestFailures)
  240. {
  241. // Discard the coverage for this job
  242. continue;
  243. }
  244. if (testResult == Client::TestRunResult::AllTestsPass || testResult == Client::TestRunResult::TestFailures)
  245. {
  246. if (testResult == Client::TestRunResult::AllTestsPass)
  247. {
  248. // Passing tests should have coverage data, otherwise something is very wrong
  249. AZ_TestImpact_Eval(
  250. job.GetTestCoverge().has_value(),
  251. RuntimeException,
  252. AZStd::string::format(
  253. "Test target '%s' completed its test run successfully but produced no coverage data. Command string: '%s'",
  254. job.GetTestTarget()->GetName().c_str(), job.GetCommandString().c_str()));
  255. }
  256. if (!job.GetTestCoverge().has_value())
  257. {
  258. // When a test run completes with failing tests but produces no coverage artifact that's typically a sign of the
  259. // test aborting due to an unhandled exception, in which case ignore it and let it be picked up in the failure report
  260. continue;
  261. }
  262. for (const auto& source : job.GetTestCoverge().value().GetSourcesCovered())
  263. {
  264. coverage[source.String()].insert(job.GetTestTarget()->GetName());
  265. }
  266. }
  267. }
  268. AZStd::vector<SourceCoveringTests> sourceCoveringTests;
  269. sourceCoveringTests.reserve(coverage.size());
  270. for (auto&& [source, testTargets] : coverage)
  271. {
  272. if (const auto sourcePath = RepoPath(source);
  273. sourcePath.IsRelativeTo(m_config.m_repo.m_root))
  274. {
  275. sourceCoveringTests.push_back(
  276. SourceCoveringTests(RepoPath(sourcePath.LexicallyRelative(m_config.m_repo.m_root)), AZStd::move(testTargets)));
  277. }
  278. else
  279. {
  280. AZ_Warning(LogCallSite, false, "Ignoring source, source it outside of repo: '%s'", sourcePath.c_str());
  281. }
  282. }
  283. return SourceCoveringTestsList(AZStd::move(sourceCoveringTests));
  284. }
  285. void Runtime::UpdateAndSerializeDynamicDependencyMap(const AZStd::vector<TestEngineInstrumentedRun>& jobs)
  286. {
  287. try
  288. {
  289. const auto sourceCoverageTestsList = CreateSourceCoveringTestFromTestCoverages(jobs);
  290. if (sourceCoverageTestsList.GetNumSources() == 0)
  291. {
  292. return;
  293. }
  294. m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList);
  295. const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage();
  296. const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA);
  297. WriteFileContents<RuntimeException>(sparTIAData, m_sparTIAFile);
  298. m_hasImpactAnalysisData = true;
  299. }
  300. catch(const RuntimeException& e)
  301. {
  302. if (m_integrationFailurePolicy == Policy::IntegrityFailure::Abort)
  303. {
  304. throw e;
  305. }
  306. else
  307. {
  308. AZ_Error(LogCallSite, false, e.what());
  309. }
  310. }
  311. }
  312. TestSequenceResult Runtime::RegularTestSequence(
  313. AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
  314. AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
  315. AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
  316. AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
  317. AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
  318. {
  319. Timer timer;
  320. AZStd::vector<const TestTarget*> includedTestTargets;
  321. AZStd::vector<const TestTarget*> excludedTestTargets;
  322. // Separate the test targets into those that are excluded by either the test filter or exclusion list and those that are not
  323. for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
  324. {
  325. if (!m_testTargetExcludeList.contains(&testTarget))
  326. {
  327. includedTestTargets.push_back(&testTarget);
  328. }
  329. else
  330. {
  331. // Test targets on the exclude list are excluded
  332. excludedTestTargets.push_back(&testTarget);
  333. }
  334. }
  335. // Sequence start callback
  336. if (testSequenceStartCallback.has_value())
  337. {
  338. (*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets)));
  339. }
  340. const auto [result, testJobs] = m_testEngine->RegularRun(
  341. includedTestTargets,
  342. m_testShardingPolicy,
  343. m_executionFailurePolicy,
  344. m_testFailurePolicy,
  345. m_targetOutputCapture,
  346. testTargetTimeout,
  347. globalTimeout,
  348. TestRunCompleteCallbackHandler(testCompleteCallback));
  349. if (testSequenceEndCallback.has_value())
  350. {
  351. (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
  352. }
  353. return result;
  354. }
  355. TestSequenceResult Runtime::ImpactAnalysisTestSequence(
  356. const ChangeList& changeList,
  357. Policy::TestPrioritization testPrioritizationPolicy,
  358. Policy::DynamicDependencyMap dynamicDependencyMapPolicy,
  359. AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
  360. AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
  361. AZStd::optional<ImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
  362. AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
  363. AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
  364. {
  365. Timer timer;
  366. // Draft in the test targets that have no coverage entries in the dynamic dependency map
  367. AZStd::vector<const TestTarget*> draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests();
  368. // The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not
  369. auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy);
  370. // The subset of selected test targets that are not on the configuration's exclude list and those that are
  371. auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets);
  372. // We present to the client the included selected test targets and the drafted test targets as distinct sets but internally
  373. // we consider the concatenated set of the two the actual set of tests to run
  374. AZStd::vector<const TestTarget*> testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets);
  375. if (testSequenceStartCallback.has_value())
  376. {
  377. (*testSequenceStartCallback)(
  378. Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)),
  379. ExtractTestTargetNames(discardedTestTargets),
  380. ExtractTestTargetNames(draftedTestTargets));
  381. }
  382. if (dynamicDependencyMapPolicy == Policy::DynamicDependencyMap::Update)
  383. {
  384. const auto [result, testJobs] = m_testEngine->InstrumentedRun(
  385. testTargetsToRun,
  386. m_testShardingPolicy,
  387. m_executionFailurePolicy,
  388. Policy::IntegrityFailure::Continue,
  389. m_testFailurePolicy,
  390. m_targetOutputCapture,
  391. testTargetTimeout,
  392. globalTimeout,
  393. TestRunCompleteCallbackHandler(testCompleteCallback));
  394. UpdateAndSerializeDynamicDependencyMap(testJobs);
  395. if (testSequenceEndCallback.has_value())
  396. {
  397. (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
  398. }
  399. return result;
  400. }
  401. else
  402. {
  403. const auto [result, testJobs] = m_testEngine->RegularRun(
  404. testTargetsToRun,
  405. m_testShardingPolicy,
  406. m_executionFailurePolicy,
  407. m_testFailurePolicy,
  408. m_targetOutputCapture,
  409. testTargetTimeout,
  410. globalTimeout,
  411. TestRunCompleteCallbackHandler(testCompleteCallback));
  412. if (testSequenceEndCallback.has_value())
  413. {
  414. (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
  415. }
  416. return result;
  417. }
  418. }
  419. AZStd::pair<TestSequenceResult, TestSequenceResult> Runtime::SafeImpactAnalysisTestSequence(
  420. const ChangeList& changeList,
  421. Policy::TestPrioritization testPrioritizationPolicy,
  422. AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
  423. AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
  424. AZStd::optional<SafeImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
  425. AZStd::optional<SafeTestSequenceCompleteCallback> testSequenceEndCallback,
  426. AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
  427. {
  428. Timer timer;
  429. // Draft in the test targets that have no coverage entries in the dynamic dependency map
  430. AZStd::vector<const TestTarget*> draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests();
  431. // The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not
  432. auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy);
  433. // The subset of selected test targets that are not on the configuration's exclude list and those that are
  434. auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets);
  435. // The subset of discarded test targets that are not on the configuration's exclude list and those that are
  436. auto [includedDiscardedTestTargets, excludedDiscardedTestTargets] = SelectTestTargetsByExcludeList(discardedTestTargets);
  437. // We present to the client the included selected test targets and the drafted test targets as distinct sets but internally
  438. // we consider the concatenated set of the two the actual set of tests to run
  439. AZStd::vector<const TestTarget*> testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets);
  440. if (testSequenceStartCallback.has_value())
  441. {
  442. (*testSequenceStartCallback)(
  443. Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)),
  444. Client::TestRunSelection(ExtractTestTargetNames(includedDiscardedTestTargets), ExtractTestTargetNames(excludedDiscardedTestTargets)),
  445. ExtractTestTargetNames(draftedTestTargets));
  446. }
  447. // Impact analysis run of the selected test targets
  448. const auto [selectedResult, selectedTestJobs] = m_testEngine->InstrumentedRun(
  449. testTargetsToRun,
  450. m_testShardingPolicy,
  451. m_executionFailurePolicy,
  452. Policy::IntegrityFailure::Continue,
  453. m_testFailurePolicy,
  454. m_targetOutputCapture,
  455. testTargetTimeout,
  456. globalTimeout,
  457. TestRunCompleteCallbackHandler(testCompleteCallback));
  458. const auto selectedDuraton = timer.Elapsed();
  459. // Carry the remaining global sequence time over to the discarded test run
  460. if (globalTimeout.has_value())
  461. {
  462. const auto elapsed = timer.Elapsed();
  463. globalTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0);
  464. }
  465. // Regular run of the discarded test targets
  466. const auto [discardedResult, discardedTestJobs] = m_testEngine->RegularRun(
  467. includedDiscardedTestTargets,
  468. m_testShardingPolicy,
  469. m_executionFailurePolicy,
  470. m_testFailurePolicy,
  471. m_targetOutputCapture,
  472. testTargetTimeout,
  473. globalTimeout,
  474. TestRunCompleteCallbackHandler(testCompleteCallback));
  475. const auto discardedDuraton = timer.Elapsed();
  476. if (testSequenceEndCallback.has_value())
  477. {
  478. (*testSequenceEndCallback)(
  479. GenerateSequenceFailureReport(selectedTestJobs),
  480. GenerateSequenceFailureReport(discardedTestJobs),
  481. selectedDuraton,
  482. discardedDuraton);
  483. }
  484. UpdateAndSerializeDynamicDependencyMap(selectedTestJobs);
  485. return { selectedResult, discardedResult };
  486. }
  487. TestSequenceResult Runtime::SeededTestSequence(
  488. AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
  489. AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
  490. AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
  491. AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
  492. AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
  493. {
  494. Timer timer;
  495. AZStd::vector<const TestTarget*> includedTestTargets;
  496. AZStd::vector<const TestTarget*> excludedTestTargets;
  497. for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
  498. {
  499. if (!m_testTargetExcludeList.contains(&testTarget))
  500. {
  501. includedTestTargets.push_back(&testTarget);
  502. }
  503. else
  504. {
  505. excludedTestTargets.push_back(&testTarget);
  506. }
  507. }
  508. if (testSequenceStartCallback.has_value())
  509. {
  510. (*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets)));
  511. }
  512. const auto [result, testJobs] = m_testEngine->InstrumentedRun(
  513. includedTestTargets,
  514. m_testShardingPolicy,
  515. m_executionFailurePolicy,
  516. Policy::IntegrityFailure::Continue,
  517. m_testFailurePolicy,
  518. m_targetOutputCapture,
  519. testTargetTimeout,
  520. globalTimeout,
  521. TestRunCompleteCallbackHandler(testCompleteCallback));
  522. if (testSequenceEndCallback.has_value())
  523. {
  524. (*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
  525. }
  526. ClearDynamicDependencyMapAndRemoveExistingFile();
  527. UpdateAndSerializeDynamicDependencyMap(testJobs);
  528. return result;
  529. }
  530. bool Runtime::HasImpactAnalysisData() const
  531. {
  532. return m_hasImpactAnalysisData;
  533. }
  534. }