diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp index 5e1f2bc890..1bb88912d1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp @@ -10,18 +10,19 @@ * */ +#include + #include #include -#include #include #include namespace TestImpact { AutogenSources PairAutogenSources( - const AZStd::vector& inputSources, - const AZStd::vector& outputSources, + const AZStd::vector& inputSources, + const AZStd::vector& outputSources, const AZStd::string& autogenMatcher) { AutogenSources autogenSources; @@ -65,8 +66,8 @@ namespace TestImpact BuildTargetDescriptor BuildTargetDescriptorFactory( const AZStd::string& buildTargetData, - const AZStd::vector& staticSourceExtensionExcludes, - const AZStd::vector& autogenInputExtensionExcludes, + const AZStd::vector& staticSourceExtensionIncludes, + const AZStd::vector& autogenInputExtensionIncludes, const AZStd::string& autogenMatcher) { // Keys for pertinent JSON node and attribute names @@ -118,14 +119,14 @@ namespace TestImpact const auto& staticSources = sources[Keys[StaticKey]].GetArray(); if (!staticSources.Empty()) { - buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector(); + buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector(); for (const auto& source : staticSources) { - const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + const RepoPath sourcePath = RepoPath(source.GetString()); if (AZStd::find( - staticSourceExtensionExcludes.begin(), staticSourceExtensionExcludes.end(), sourcePath.Extension().Native()) == - staticSourceExtensionExcludes.end()) + staticSourceExtensionIncludes.begin(), staticSourceExtensionIncludes.end(), sourcePath.Extension().Native()) != + staticSourceExtensionIncludes.end()) { buildTargetDescriptor.m_sources.m_staticSources.emplace_back(AZStd::move(sourcePath)); } @@ -139,17 +140,17 @@ namespace TestImpact AZ_TestImpact_Eval( !inputSources.Empty() && !outputSources.Empty(), ArtifactException, "Autogen malformed, input or output sources are empty"); - AZStd::vector inputPaths; - AZStd::vector outputPaths; + AZStd::vector inputPaths; + AZStd::vector outputPaths; inputPaths.reserve(inputSources.Size()); outputPaths.reserve(outputSources.Size()); for (const auto& source : inputSources) { - const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString()); + const RepoPath sourcePath = RepoPath(source.GetString()); if (AZStd::find( - autogenInputExtensionExcludes.begin(), autogenInputExtensionExcludes.end(), sourcePath.Extension().Native()) == - autogenInputExtensionExcludes.end()) + autogenInputExtensionIncludes.begin(), autogenInputExtensionIncludes.end(), sourcePath.Extension().Native()) != + autogenInputExtensionIncludes.end()) { inputPaths.emplace_back(AZStd::move(sourcePath)); } @@ -157,7 +158,7 @@ namespace TestImpact for (const auto& source : outputSources) { - outputPaths.emplace_back(AZStd::move(AZ::IO::Path(source.GetString()))); + outputPaths.emplace_back(AZStd::move(RepoPath(source.GetString()))); } buildTargetDescriptor.m_sources.m_autogenSources = PairAutogenSources(inputPaths, outputPaths, autogenMatcher); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h index b23e00478a..cf4e431a14 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h @@ -21,13 +21,13 @@ namespace TestImpact { //! Constructs a build target artifact from the specified build target data. //! @param buildTargetData The raw build target data in JSON format. - //! @param staticSourceExcludes The list of file extensions to exclude for static sources. - //! @param autogenInputExtentsionExcludes The list of file extensions to exclude for autogen input sources. + //! @param staticSourceIncludes The list of file extensions to include for static sources. + //! @param autogenInputExtentsionIncludes The list of file extensions to include for autogen input sources. //! @param autogenMatcher The regex pattern used to match autogen input filenames with output filenames. //! @return The constructed build target artifact. BuildTargetDescriptor BuildTargetDescriptorFactory( const AZStd::string& buildTargetData, - const AZStd::vector& staticSourceExtentsionExcludes, - const AZStd::vector& autogenInputExtentsionExcludes, + const AZStd::vector& staticSourceExtentsionIncludes, + const AZStd::vector& autogenInputExtentsionIncludes, const AZStd::string& autogenMatcher); } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp index db4d5f8563..8bbb5200bb 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Factory/TestImpactTestTargetMetaMapFactory.cpp @@ -31,7 +31,9 @@ namespace TestImpact "launch_method", "test_runner", "stand_alone", - "name" + "name", + "command", + "timeout" }; enum @@ -43,7 +45,9 @@ namespace TestImpact LaunchMethodKey, TestRunnerKey, StandAloneKey, - NameKey + NameKey, + CommandKey, + TimeoutKey }; AZ_TestImpact_Eval(!masterTestListData.empty(), ArtifactException, "test meta-data cannot be empty"); @@ -61,6 +65,8 @@ namespace TestImpact { TestTargetMeta testMeta; testMeta.m_suite = test[Keys[SuiteKey]].GetString(); + testMeta.m_customArgs = test[Keys[CommandKey]].GetString(); + testMeta.m_timeout = AZStd::chrono::seconds{ test[Keys[TimeoutKey]].GetUint() }; if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0) { diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h index e247b663f2..49820a4bdb 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactBuildTargetDescriptor.h @@ -12,7 +12,8 @@ #pragma once -#include +#include + #include #include #include @@ -22,8 +23,8 @@ namespace TestImpact //! Pairing between a given autogen input source and the generated output source(s). struct AutogenPairs { - AZStd::string m_input; - AZStd::vector m_outputs; + RepoPath m_input; + AZStd::vector m_outputs; }; using AutogenSources = AZStd::vector; @@ -31,7 +32,7 @@ namespace TestImpact //! Representation of a given built target's source list. struct TargetSources { - AZStd::vector m_staticSources; //!< Source files used to build this target (if any). + AZStd::vector m_staticSources; //!< Source files used to build this target (if any). AutogenSources m_autogenSources; //!< Autogen source files (if any). }; @@ -40,7 +41,7 @@ namespace TestImpact { AZStd::string m_name; //!< Build target name. AZStd::string m_outputName; //!< Output name (sans extension) of build target binary. - AZ::IO::Path m_path; //!< Path to build target location in source tree (relative to repository root). + RepoPath m_path; //!< Path to build target location in source tree (relative to repository root). }; //! Artifact produced by the build system for each build target. Contains source and output information about said targets. diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h index 57ae2fe894..bef9d9a44f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Artifact/Static/TestImpactTestTargetMeta.h @@ -14,6 +14,7 @@ #include #include +#include namespace TestImpact { @@ -28,6 +29,8 @@ namespace TestImpact struct TestTargetMeta { AZStd::string m_suite; + AZStd::string m_customArgs; + AZStd::chrono::milliseconds m_timeout = AZStd::chrono::milliseconds{ 0 }; LaunchMethod m_launchMethod = LaunchMethod::TestRunner; }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp index f99300ad4e..93fb5ee366 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.cpp @@ -25,7 +25,7 @@ namespace TestImpact { for (const auto& source : target->GetSources().m_staticSources) { - if (auto mapping = m_sourceDependencyMap.find(source); + if (auto mapping = m_sourceDependencyMap.find(source.String()); mapping != m_sourceDependencyMap.end()) { // This is an existing entry in the dependency map so update the parent build targets with this target @@ -43,7 +43,7 @@ namespace TestImpact { for (const auto& output : autogen.m_outputs) { - m_autogenInputToOutputMap[autogen.m_input].push_back(output); + m_autogenInputToOutputMap[autogen.m_input.String()].push_back(output.String()); } } }; @@ -138,11 +138,11 @@ namespace TestImpact { // Autogen input files are not compiled sources and thus supplying coverage data for them makes no sense AZ_TestImpact_Eval( - m_autogenInputToOutputMap.find(sourceCoverage.GetPath()) == m_autogenInputToOutputMap.end(), + m_autogenInputToOutputMap.find(sourceCoverage.GetPath().c_str()) == m_autogenInputToOutputMap.end(), DependencyException, AZStd::string::format("Couldn't replace source coverage for %s, source file is an autogen input file", sourceCoverage.GetPath().c_str()).c_str()); - auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath()); + auto [it, inserted] = m_sourceDependencyMap.insert(sourceCoverage.GetPath().String()); auto& [key, sourceDependency] = *it; // Clear any existing coverage for the delta @@ -178,22 +178,26 @@ namespace TestImpact } } - void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) + void DynamicDependencyMap::ClearSourceCoverage(const AZStd::vector& paths) { for (const auto& path : paths) { - if (const auto outputSources = m_autogenInputToOutputMap.find(path); + if (const auto outputSources = m_autogenInputToOutputMap.find(path.String()); outputSources != m_autogenInputToOutputMap.end()) { // Clearing the coverage data of an autogen input source instead clears the coverage data of its output sources for (const auto& outputSource : outputSources->second) { - ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(outputSource) })); + AZStd::vector coverage; + coverage.emplace_back(RepoPath(outputSource)); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); } } else { - ReplaceSourceCoverage(SourceCoveringTestsList({ SourceCoveringTests(path, { }) })); + AZStd::vector coverage; + coverage.emplace_back(RepoPath(path)); + ReplaceSourceCoverage(SourceCoveringTestsList(AZStd::move(coverage))); } } } @@ -221,7 +225,7 @@ namespace TestImpact return coveringTestTargets; } - AZStd::optional DynamicDependencyMap::GetSourceDependency(const AZStd::string& path) const + AZStd::optional DynamicDependencyMap::GetSourceDependency(const RepoPath& path) const { AZStd::unordered_set parentTargets; AZStd::unordered_set coveringTestTargets; @@ -243,7 +247,7 @@ namespace TestImpact } }; - if (const auto outputSources = m_autogenInputToOutputMap.find(path); outputSources != m_autogenInputToOutputMap.end()) + if (const auto outputSources = m_autogenInputToOutputMap.find(path.String()); outputSources != m_autogenInputToOutputMap.end()) { // Consolidate the parentage and coverage of each of the autogen input file's generated output files for (const auto& outputSource : outputSources->second) @@ -253,7 +257,7 @@ namespace TestImpact } else { - getSourceDependency(path); + getSourceDependency(path.String()); } if (!parentTargets.empty() || !coveringTestTargets.empty()) @@ -264,7 +268,7 @@ namespace TestImpact return AZStd::nullopt; } - SourceDependency DynamicDependencyMap::GetSourceDependencyOrThrow(const AZStd::string& path) const + SourceDependency DynamicDependencyMap::GetSourceDependencyOrThrow(const RepoPath& path) const { auto sourceDependency = GetSourceDependency(path); AZ_TestImpact_Eval(sourceDependency.has_value(), DependencyException, AZStd::string::format("Couldn't find source %s", path.c_str()).c_str()); @@ -282,7 +286,7 @@ namespace TestImpact souceCoveringTests.push_back(testTarget->GetName()); } - coverage.push_back(SourceCoveringTests(path, AZStd::move(souceCoveringTests))); + coverage.push_back(SourceCoveringTests(RepoPath(path), AZStd::move(souceCoveringTests))); } return SourceCoveringTestsList(AZStd::move(coverage)); @@ -310,7 +314,7 @@ namespace TestImpact // Keep track of the coverage to delete as a post step rather than deleting it in situ so that erroneous change lists // do not corrupt the dynamic dependency map - AZStd::vector coverageToDelete; + AZStd::vector coverageToDelete; // Create operations for (const auto& createdFile : changeList.m_createdFiles) diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h index 527a6555c6..6faef76452 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactDynamicDependencyMap.h @@ -12,7 +12,8 @@ #pragma once -#include +#include + #include #include #include @@ -74,10 +75,10 @@ namespace TestImpact //! Gets the source dependency for the specified source file. //! @note Autogen input source dependencies are the consolidated source dependencies of all of their generated output sources. //! @returns If found, the source dependency information for the specified source file, otherwise empty. - AZStd::optional GetSourceDependency(const AZStd::string& path) const; + AZStd::optional GetSourceDependency(const RepoPath& path) const; //! Gets the source dependency for the specified source file or throw DependencyException. - SourceDependency GetSourceDependencyOrThrow(const AZStd::string& path) const; + SourceDependency GetSourceDependencyOrThrow(const RepoPath& path) const; //! Replaces the source coverage of the specified sources with the specified source coverage. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. @@ -99,7 +100,7 @@ namespace TestImpact private: //! Clears the source coverage of the specified sources. //! @note The covering targets for the parent test target(s) will not be pruned if those covering targets are removed. - void ClearSourceCoverage(const AZStd::vector& paths); + void ClearSourceCoverage(const AZStd::vector& paths); //! The sorted list of unique production targets in the repository. ProductionTargetList m_productionTargets; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp index 792eb1f0b2..101bdf862c 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.cpp @@ -16,18 +16,36 @@ namespace TestImpact { - SourceCoveringTests::SourceCoveringTests(const AZStd::string& path) + AZStd::vector ExtractTargetsFromSet(AZStd::unordered_set&& coveringTestTargets) + { + AZStd::vector testTargets; + testTargets.reserve(coveringTestTargets.size()); + for (auto it = coveringTestTargets.begin(); it != coveringTestTargets.end(); ) + { + testTargets.push_back(std::move(coveringTestTargets.extract(it++).value())); + } + + return testTargets; + } + + SourceCoveringTests::SourceCoveringTests(const RepoPath& path) : m_path(path) { } - SourceCoveringTests::SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets) + SourceCoveringTests::SourceCoveringTests(const RepoPath& path, AZStd::vector&& coveringTestTargets) : m_path(path) , m_coveringTestTargets(AZStd::move(coveringTestTargets)) { } - const AZStd::string& SourceCoveringTests::GetPath() const + SourceCoveringTests::SourceCoveringTests(const RepoPath& path, AZStd::unordered_set&& coveringTestTargets) + : m_path(path) + , m_coveringTestTargets(ExtractTargetsFromSet(AZStd::move(coveringTestTargets))) + { + } + + const RepoPath& SourceCoveringTests::GetPath() const { return m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h index 52f3a7c3f6..f501ccefd8 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceCoveringTestsList.h @@ -12,7 +12,10 @@ #pragma once +#include + #include +#include #include namespace TestImpact @@ -21,11 +24,13 @@ namespace TestImpact class SourceCoveringTests { public: - explicit SourceCoveringTests(const AZStd::string& path); - SourceCoveringTests(const AZStd::string& path, AZStd::vector&& coveringTestTargets); + //SourceCoveringTests(const SourceCoveringTests&); + explicit SourceCoveringTests(const RepoPath& path); + SourceCoveringTests(const RepoPath& path, AZStd::vector&& coveringTestTargets); + SourceCoveringTests(const RepoPath& path, AZStd::unordered_set&& coveringTestTargets); //! Returns the path of this source file. - const AZStd::string& GetPath() const; + const RepoPath& GetPath() const; //! Returns the number of unresolved test targets covering this source file. size_t GetNumCoveringTestTargets() const; @@ -33,7 +38,7 @@ namespace TestImpact //! Returns the unresolved test targets covering this source file. const AZStd::vector& GetCoveringTestTargets() const; private: - AZStd::string m_path; //!< The path of this source file. + RepoPath m_path; //!< The path of this source file. AZStd::vector m_coveringTestTargets; //!< The unresolved test targets that cover this source file. }; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp index 39193d4dd9..7d4e76acd1 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.cpp @@ -52,14 +52,14 @@ namespace TestImpact } SourceDependency::SourceDependency( - const AZStd::string& path, + const RepoPath& path, DependencyData&& dependencyData) : m_path(path) , m_dependencyData(AZStd::move(dependencyData)) { } - const AZStd::string& SourceDependency::GetPath() const + const RepoPath& SourceDependency::GetPath() const { return m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h index a3b6783d13..d80c4a0540 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Dependency/TestImpactSourceDependency.h @@ -71,11 +71,11 @@ namespace TestImpact { public: SourceDependency( - const AZStd::string& path, + const RepoPath& path, DependencyData&& dependencyData); //! Returns the path of this source file. - const AZStd::string& GetPath() const; + const RepoPath& GetPath() const; //! Returns the number of parent build targets this source belongs to. size_t GetNumParentTargets() const; @@ -89,7 +89,7 @@ namespace TestImpact //! Returns the test targets covering this source file. const AZStd::unordered_set& GetCoveringTestTargets() const; private: - AZStd::string m_path; //!< The path of this source file. + RepoPath m_path; //!< The path of this source file. DependencyData m_dependencyData; //!< The dependency data for this source file. }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp index 0a8a8643f3..06dd4e846f 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Platform/Windows/Process/TestImpactWin32_Process.cpp @@ -69,7 +69,7 @@ namespace TestImpact NULL, NULL, &si, &pi)) { - throw ProcessException("Couldn't create process"); + throw ProcessException(AZStd::string::format("Couldn't create process with args: %s", args.c_str())); } ReleaseChildPipes(); diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h index d9dd4870dd..4710cf2141 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJob.h @@ -13,77 +13,42 @@ #pragma once #include -#include - -#include -#include +#include namespace TestImpact { - //! Result of a job that was run. - enum class JobResult - { - NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed). - FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid). - Terminated, //!< The job was terminated by the job runner (e.g. job or runner timeout exceeded while job was in-flight). - ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero). - ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero). - }; - - //! The meta-data for a given job. - struct JobMeta - { - JobResult m_result = JobResult::NotExecuted; - AZStd::optional - m_startTime; //!< The time, relative to the job runner start, that this job started. - AZStd::optional m_duration; //!< The duration that this job took to complete. - AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. - }; - //! Representation of a unit of work to be performed by a process. //! @tparam JobInfoT The JobInfo structure containing the information required to run this job. //! @tparam JobPayloadT The resulting output of the processed artifact produced by this job. template class Job + : public JobMetaContainer { public: using Info = JobInfoT; using Payload = JobPayloadT; //! Constructor with r-values for the specific use case of the job runner. - Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload); + Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload); //! Returns the job info associated with this job. const Info& GetJobInfo() const; - //! Returns the result of this job. - JobResult GetResult() const; - - //! Returns the start time, relative to the job runner start, that this job started. - AZStd::chrono::high_resolution_clock::time_point GetStartTime() const; - - //! Returns the end time, relative to the job runner start, that this job ended. - AZStd::chrono::high_resolution_clock::time_point GetEndTime() const; - - //! Returns the duration that this job took to complete. - AZStd::chrono::milliseconds GetDuration() const; - - //! Returns the return code of the underlying processes of this job. - AZStd::optional GetReturnCode() const; - //! Returns the payload produced by this job. const AZStd::optional& GetPayload() const; + //! Facilitates the client consuming the payload. + AZStd::optional&& ReleasePayload(); + private: Info m_jobInfo; - JobMeta m_meta; AZStd::optional m_payload; }; template - Job::Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) - : m_jobInfo(jobInfo) - , m_meta(AZStd::move(jobMeta)) + Job::Job(const Info& jobInfo, JobMeta&& jobMeta, AZStd::optional&& payload) + : JobMetaContainer(AZStd::move(jobMeta)) + , m_jobInfo(jobInfo) , m_payload(AZStd::move(payload)) { } @@ -95,45 +60,14 @@ namespace TestImpact } template - JobResult Job::GetResult() const - { - return m_meta.m_result; - } - - template - AZStd::optional Job::GetReturnCode() const - { - return m_meta.m_returnCode; - } - - template - AZStd::chrono::high_resolution_clock::time_point Job::GetStartTime() const - { - return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); - } - - template - AZStd::chrono::high_resolution_clock::time_point Job::GetEndTime() const - { - if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) - { - return m_meta.m_startTime.value() + m_meta.m_duration.value(); - } - else - { - return AZStd::chrono::high_resolution_clock::time_point(); - } - } - - template - AZStd::chrono::milliseconds Job::GetDuration() const + const AZStd::optional& Job::GetPayload() const { - return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{0}); + return m_payload; } template - const AZStd::optional& Job::GetPayload() const + AZStd::optional&& Job::ReleasePayload() { - return m_payload; + return AZStd::move(m_payload); } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h index fd9d76652a..fbdad82c12 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobInfo.h @@ -24,6 +24,7 @@ namespace TestImpact { public: using IdType = size_t; + using CommandType = AZStd::string; //! Client-provided identifier to distinguish between different jobs. //! @note Ids of different job types are not interchangeable. @@ -32,30 +33,37 @@ namespace TestImpact IdType m_value; }; + //! Command used my ProcessScheduler to execute this job. + //! @note Commands of different job types are not interchangeable. + struct Command + { + CommandType m_args; + }; + //! Constructs the job information with any additional information required by the job. //! @param jobId The client-provided unique identifier for the job. - //! @param args The arguments used to launch the process running the job. + //! @param command The command used to launch the process running the job. //! @param additionalInfo The arguments to be provided to the additional information data structure. template - JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo); + JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo); //! Returns the id of this job. Id GetId() const; //! Returns the command arguments used to execute this job. - const AZStd::string& GetArgs() const; + const Command& GetCommand() const; private: Id m_id; - AZStd::string m_args; + Command m_command; }; template template - JobInfo::JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo) + JobInfo::JobInfo(Id jobId, const Command& command, AdditionalInfoArgs&&... additionalInfo) : AdditionalInfo{std::forward(additionalInfo)...} , m_id(jobId) - , m_args(args) + , m_command(command) { } @@ -66,8 +74,8 @@ namespace TestImpact } template - const AZStd::string& JobInfo::GetArgs() const + const typename JobInfo::Command& JobInfo::GetCommand() const { - return m_args; + return m_command; } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp new file mode 100644 index 0000000000..ba6ad4c585 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.cpp @@ -0,0 +1,61 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +namespace TestImpact +{ + JobMetaContainer::JobMetaContainer(const JobMeta& jobMeta) + : m_meta(jobMeta) + { + } + + JobMetaContainer::JobMetaContainer(JobMeta&& jobMeta) + : m_meta(AZStd::move(jobMeta)) + { + } + + JobResult JobMetaContainer::GetJobResult() const + { + return m_meta.m_result; + } + + AZStd::optional JobMetaContainer::GetReturnCode() const + { + return m_meta.m_returnCode; + } + + AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetStartTime() const + { + return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point()); + } + + AZStd::chrono::high_resolution_clock::time_point JobMetaContainer::GetEndTime() const + { + if (m_meta.m_startTime.has_value() && m_meta.m_duration.has_value()) + { + return m_meta.m_startTime.value() + m_meta.m_duration.value(); + } + else + { + return AZStd::chrono::high_resolution_clock::time_point(); + } + } + + AZStd::chrono::milliseconds JobMetaContainer::GetDuration() const + { + return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{ 0 }); + } + +} // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h new file mode 100644 index 0000000000..6e7ce97468 --- /dev/null +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobMeta.h @@ -0,0 +1,68 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include + +#include +#include + +namespace TestImpact +{ + //! Result of a job that was run. + enum class JobResult + { + NotExecuted, //!< The job was not executed (e.g. the job runner terminated before the job could be executed). + FailedToExecute, //!< The job failed to execute (e.g. due to the arguments used to execute the job being invalid). + Timeout, //!< The job was terminated by the job runner (e.g. job timeout exceeded while job was in-flight). + Terminated, //!< The job was terminated by the job runner (e.g. global timeout exceeded while job was in-flight). + ExecutedWithFailure, //!< The job was executed but exited in an erroneous state (the underlying process returned non-zero). + ExecutedWithSuccess //!< The job was executed and exited in a successful state (the underlying processes returned zero). + }; + + //! The meta-data for a given job. + struct JobMeta + { + JobResult m_result = JobResult::NotExecuted; + AZStd::optional + m_startTime; //!< The time, relative to the job runner start, that this job started. + AZStd::optional m_duration; //!< The duration that this job took to complete. + AZStd::optional m_returnCode; //!< The return code of the underlying processes of this job. + }; + + class JobMetaContainer + { + public: + JobMetaContainer(const JobMeta& jobMeta); + JobMetaContainer(JobMeta&& jobMeta); + + //! Returns the result of this job. + JobResult GetJobResult() const; + + //! Returns the start time, relative to the job runner start, that this job started. + AZStd::chrono::high_resolution_clock::time_point GetStartTime() const; + + //! Returns the end time, relative to the job runner start, that this job ended. + AZStd::chrono::high_resolution_clock::time_point GetEndTime() const; + + //! Returns the duration that this job took to complete. + AZStd::chrono::milliseconds GetDuration() const; + + //! Returns the return code of the underlying processes of this job. + AZStd::optional GetReturnCode() const; + + private: + JobMeta m_meta; + }; +} // namespace TestImpact + diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h index 8b9e4fa69e..0c30accc84 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/JobRunner/TestImpactProcessJobRunner.h @@ -13,7 +13,6 @@ #pragma once #include -#include #include #include @@ -27,7 +26,7 @@ namespace TestImpact //! @param meta The meta-data about the job run. //! @param std The standard output and standard error of the process running the job. template - using JobCallback = AZStd::function; + using JobCallback = AZStd::function; //! The payloads produced by the job-specific payload producer in the form of a map associating each job id with the job's payload. template @@ -116,7 +115,7 @@ namespace TestImpact const auto* jobInfo = &jobInfos[jobIndex]; const auto jobId = jobInfo->GetId().m_value; metas.emplace(jobId, AZStd::pair{JobMeta{}, jobInfo}); - processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetArgs()); + processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetCommand().m_args); } // Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client @@ -134,7 +133,7 @@ namespace TestImpact else { meta.m_startTime = createTime; - return CallbackResult::Continue; + return ProcessCallbackResult::Continue; } }; @@ -153,10 +152,14 @@ namespace TestImpact { meta.m_result = JobResult::ExecutedWithSuccess; } - else if (exitCondition == ExitCondition::Terminated || exitCondition == ExitCondition::Timeout) + else if (exitCondition == ExitCondition::Terminated) { meta.m_result = JobResult::Terminated; } + else if (exitCondition == ExitCondition::Timeout) + { + meta.m_result = JobResult::Timeout; + } else { meta.m_result = JobResult::ExecutedWithFailure; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp index 15230b1a46..04a68bf612 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.cpp @@ -58,7 +58,7 @@ namespace TestImpact for (auto& process : m_processPool) { - if (PopAndLaunch(process) == CallbackResult::Abort) + if (PopAndLaunch(process) == ProcessCallbackResult::Abort) { TerminateAllProcesses(ExitCondition::Terminated); return; @@ -110,7 +110,7 @@ namespace TestImpact const auto exitTime = AZStd::chrono::high_resolution_clock::now(); // Inform the client that the processes has exited - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processId, ExitCondition::Gracefull, returnCode, @@ -124,7 +124,7 @@ namespace TestImpact else if (!m_processQueue.empty()) { // This slot in the pool is free so launch one of the processes waiting in the queue - if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort) { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); @@ -150,7 +150,7 @@ namespace TestImpact const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value(); processInFlight.m_process.reset(); - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processId, ExitCondition::Timeout, returnCode, @@ -172,7 +172,7 @@ namespace TestImpact // Queue is empty, no more processes to launch if (!m_processQueue.empty()) { - if (PopAndLaunch(processInFlight) == CallbackResult::Abort) + if (PopAndLaunch(processInFlight) == ProcessCallbackResult::Abort) { // Client chose to abort the scheduler TerminateAllProcesses(ExitCondition::Terminated); @@ -194,7 +194,7 @@ namespace TestImpact } } - CallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) + ProcessCallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight) { auto processInfo = m_processQueue.front(); m_processQueue.pop(); @@ -251,7 +251,7 @@ namespace TestImpact if (isCallingBackToClient) { const auto exitTime = AZStd::chrono::high_resolution_clock::now(); - if (CallbackResult::Abort == m_processExitCallback( + if (ProcessCallbackResult::Abort == m_processExitCallback( processInFlight.m_process->GetProcessInfo().GetId(), exitStatus, returnCode, diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h index 5531c78397..27171d25ee 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Process/Scheduler/TestImpactProcessScheduler.h @@ -12,11 +12,10 @@ #pragma once -#include +#include -#include +#include -#include #include #include #include @@ -43,12 +42,19 @@ namespace TestImpact Timeout = ProcessTimeoutErrorCode //!< The process was terminated by the scheduler due to exceeding runtime limit. }; + //! Client result for process scheduler callbacks. + enum class ProcessCallbackResult : bool + { + Continue, //!< Continune scheduling. + Abort //!< Abort scheduling immediately. + }; + //! Callback for process launch attempt. //! @param processId The id of the process that attempted to launch. //! @param launchResult The result of the process launch attempt. //! @param createTime The timestamp of the process launch attempt. using ProcessLaunchCallback = - AZStd::function; @@ -60,7 +66,7 @@ namespace TestImpact //! @param std The standard output and standard error of the process. //! @param createTime The timestamp of the process exit. using ProcessExitCallback = - AZStd::function +#include + #include #include @@ -64,9 +65,9 @@ namespace TestImpact ProcessId processId, StdOutputRouting stdOut, StdErrorRouting stdErr, - const AZ::IO::Path& processPath, + const RepoPath& processPath, const AZStd::string& startupArgs = ""); - ProcessInfo(ProcessId processId, const AZ::IO::Path& processPath, const AZStd::string& startupArgs = ""); + ProcessInfo(ProcessId processId, const RepoPath& processPath, const AZStd::string& startupArgs = ""); //! Returns the identifier of this process. ProcessId GetId() const; @@ -78,7 +79,7 @@ namespace TestImpact bool ParentHasStdError() const; // Returns the path to the process binary. - const AZ::IO::Path& GetProcessPath() const; + const RepoPath& GetProcessPath() const; //! Returns the command line arguments used to launch the process. const AZStd::string& GetStartupArgs() const; @@ -87,7 +88,7 @@ namespace TestImpact const ProcessId m_id; const bool m_parentHasStdOutput; const bool m_parentHasStdErr; - const AZ::IO::Path m_processPath; + const RepoPath m_processPath; const AZStd::string m_startupArgs; }; } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp index bce6fa25d6..54161899ae 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.cpp @@ -31,7 +31,7 @@ namespace TestImpact return m_buildMetaData.m_outputName; } - const AZ::IO::Path& BuildTarget::GetPath() const + const RepoPath& BuildTarget::GetPath() const { return m_buildMetaData.m_path; } diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h index 0cd6369472..d5d32b8ff5 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTarget.h @@ -49,7 +49,7 @@ namespace TestImpact const AZStd::string& GetOutputName() const; //! Returns the path in the source tree to the build target location. - const AZ::IO::Path& GetPath() const; + const RepoPath& GetPath() const; //! Returns the build target's sources. const TargetSources& GetSources() const; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h index dfd102b55f..9731b68d61 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactBuildTargetList.h @@ -42,6 +42,9 @@ namespace TestImpact //! Returns the target with the specified name or throws if target not found. const Target* GetTargetOrThrow(const AZStd::string& name) const; + //! Returns true if the specified target is in the list, otherwise false. + bool HasTarget(const AZStd::string& name) const; + // Returns the number of targets in the list. size_t GetNumTargets() const; @@ -71,7 +74,7 @@ namespace TestImpact m_targets.reserve(descriptors.size()); for (auto&& descriptor : descriptors) { - m_targets.emplace_back(Target(AZStd::move(descriptor))); + m_targets.emplace_back(AZStd::move(Target(AZStd::move(descriptor)))); } } @@ -122,4 +125,10 @@ namespace TestImpact AZ_TestImpact_Eval(target, TargetException, AZStd::string::format("Couldn't find target %s", name.c_str()).c_str()); return target; } + + template + bool BuildTargetList::HasTarget(const AZStd::string& name) const + { + return GetTarget(name) != nullptr; + } } // namespace TestImpact diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp index 0189bc9e78..1890863981 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.cpp @@ -25,6 +25,16 @@ namespace TestImpact return m_testMetaData.m_suite; } + const AZStd::string& TestTarget::GetCustomArgs() const + { + return m_testMetaData.m_customArgs; + } + + AZStd::chrono::milliseconds TestTarget::GetTimeout() const + { + return m_testMetaData.m_timeout; + } + LaunchMethod TestTarget::GetLaunchMethod() const { return m_testMetaData.m_launchMethod; diff --git a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h index b6f44e7cc1..e8449dde3d 100644 --- a/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h +++ b/Code/Tools/TestImpactFramework/Runtime/Code/Source/Target/TestImpactTestTarget.h @@ -29,6 +29,12 @@ namespace TestImpact //! Returns the test target suite. const AZStd::string& GetSuite() const; + //! Returns the launcher custom arguments. + const AZStd::string& GetCustomArgs() const; + + //! Returns the test run timeout. + AZStd::chrono::milliseconds GetTimeout() const; + //! Returns the test target launch method. LaunchMethod GetLaunchMethod() const;