Move test runners

This commit is contained in:
jonawals
2021-05-21 13:11:24 +01:00
parent f6397b6feb
commit e68ac30bcb
25 changed files with 562 additions and 144 deletions
@@ -1,38 +0,0 @@
/*
* 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.
*
*/
#include <TestImpactFramework/TestImpactException.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#pragma once
namespace TestImpact
{
template<typename ExceptionType>
AZStd::string ReadFileContents(const AZ::IO::Path& file)
{
static_assert(AZStd::is_base_of<Exception, ExceptionType>::value, "Exception must be a TestImpact exception or derived type");
const auto fileSize = AZ::IO::SystemFile::Length(file.c_str());
AZ_TestImpact_Eval(fileSize > 0, ExceptionType, AZStd::string::format("File %s does not exist", file.c_str()));
AZStd::vector<char> buffer(fileSize + 1);
buffer[fileSize] = '\0';
AZ_TestImpact_Eval(AZ::IO::SystemFile::Read(file.c_str(), buffer.data()), ExceptionType, "Could not read file contents");
return AZStd::string(buffer.begin(), buffer.end());
}
} // namespace TestImpact
@@ -1,26 +0,0 @@
/*
* 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 <TestImpactFramework/TestImpactException.h>
namespace TestImpact
{
//! Exception for test job related operations.
class TestJobException
: public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact
@@ -13,7 +13,7 @@
#pragma once
#include <Artifact/Dynamic/TestImpactTestEnumerationSuite.h>
#include <Test/TestImpactTestSuiteContainer.h>
#include <TestEngine/TestImpactTestSuiteContainer.h>
namespace TestImpact
{
@@ -10,8 +10,8 @@
*
*/
#include <Test/Enumeration/TestImpactTestEnumerationException.h>
#include <Test/Enumeration/TestImpactTestEnumerationSerializer.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerationException.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/prettywriter.h>
@@ -12,7 +12,7 @@
#pragma once
#include <Test/Enumeration/TestImpactTestEnumeration.h>
#include <TestEngine/Enumeration/TestImpactTestEnumeration.h>
#include <AzCore/std/string/string.h>
@@ -10,11 +10,13 @@
*
*/
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h>
#include <Test/Enumeration/TestImpactTestEnumerationException.h>
#include <Test/Enumeration/TestImpactTestEnumerationSerializer.h>
#include <Test/Enumeration/TestImpactTestEnumerator.h>
#include <Test/Job/TestImpactTestJobCommon.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerationException.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerationSerializer.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerator.h>
#include <TestEngine/Enumeration/TestImpactTestEnumeration.h>
#include <AzCore/IO/SystemFile.h>
@@ -23,7 +25,7 @@ namespace TestImpact
namespace
{
void WriteCacheFile(
const TestEnumeration& enumeration, const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
const TestEnumeration& enumeration, const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
{
const AZStd::string cacheJSON = SerializeTestEnumeration(enumeration);
const AZStd::vector<char> cacheBytes(cacheJSON.begin(), cacheJSON.end());
@@ -48,7 +50,7 @@ namespace TestImpact
}
}
AZStd::optional<TestEnumeration> ReadCacheFile(const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
AZStd::optional<TestEnumeration> ReadCacheFile(const RepoPath& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
{
AZ::IO::SystemFile cacheFile;
AZStd::string cacheJSON;
@@ -83,18 +85,18 @@ namespace TestImpact
}
} // namespace
TestEnumeration ParseTestEnumerationFile(const AZ::IO::Path& enumerationFile)
TestEnumeration ParseTestEnumerationFile(const RepoPath& enumerationFile)
{
return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents<TestEnumerationException>(enumerationFile)));
}
TestEnumerationJobData::TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional<Cache>&& cache)
TestEnumerationJobData::TestEnumerationJobData(const RepoPath& enumerationArtifact, AZStd::optional<Cache>&& cache)
: m_enumerationArtifact(enumerationArtifact)
, m_cache(AZStd::move(cache))
{
}
const AZ::IO::Path& TestEnumerationJobData::GetEnumerationArtifactPath() const
const RepoPath& TestEnumerationJobData::GetEnumerationArtifactPath() const
{
return m_enumerationArtifact;
}
@@ -163,7 +165,7 @@ namespace TestImpact
const auto& [meta, jobInfo] = jobData;
if (meta.m_result == JobResult::ExecutedWithSuccess)
{
const auto& enumeration = enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath());
const auto& enumeration = (enumerations[jobId] = ParseTestEnumerationFile(jobInfo->GetEnumerationArtifactPath()));
// Write out the enumeration to a cache file if we have a cache write policy for this job
if (jobInfo->GetCache().has_value() && jobInfo->GetCache()->m_policy == JobData::CachePolicy::Write)
@@ -12,11 +12,11 @@
#pragma once
#include <Artifact/TestImpactArtifactException.h>
#include <Test/Enumeration/TestImpactTestEnumeration.h>
#include <Test/Job/TestImpactTestJobRunner.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <AzCore/IO/Path/Path.h>
#include <Artifact/TestImpactArtifactException.h>
#include <TestEngine/Enumeration/TestImpactTestEnumeration.h>
#include <TestEngine/JobRunner/TestImpactTestJobRunner.h>
namespace TestImpact
{
@@ -35,19 +35,19 @@ namespace TestImpact
struct Cache
{
CachePolicy m_policy;
AZ::IO::Path m_file;
RepoPath m_file;
};
TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional<Cache>&& cache);
TestEnumerationJobData(const RepoPath& enumerationArtifact, AZStd::optional<Cache>&& cache);
//! Returns the path to the enumeration artifact produced by the test target.
const AZ::IO::Path& GetEnumerationArtifactPath() const;
const RepoPath& GetEnumerationArtifactPath() const;
//! Returns the cache details for this job.
const AZStd::optional<Cache>& GetCache() const;
private:
AZ::IO::Path m_enumerationArtifact; //!< Path to enumeration artifact to be processed.
RepoPath m_enumerationArtifact; //!< Path to enumeration artifact to be processed.
AZStd::optional<Cache> m_cache = AZStd::nullopt; //!< No caching takes place if cache is empty.
};
@@ -0,0 +1,167 @@
/*
* 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.
*
*/
#include <Target/TestImpactTestTarget.h>
#include <TestEngine/JobRunner/TestImpactTestJobInfoGenerator.h>
namespace TestImpact
{
static constexpr char* const standAloneExtension = ".exe"; // these are os specific so move these to the platform dir
static constexpr char* const testRunnerExtension = ".dll"; // these are os specific so move these to the platform dir
// SPLIT INTO COMMAND GENERATOR (INSTRUMENT, RUNNER, ENUM, RESULTS)
TestJobInfoGenerator::TestJobInfoGenerator(
const RepoPath& sourceDir,
const RepoPath& targetBinaryDir,
const RepoPath& cacheDir,
const RepoPath& artifactDir,
const RepoPath& testRunnerBinary,
const RepoPath& instrumentBinary)
: m_sourceDir(sourceDir)
, m_targetBinaryDir(targetBinaryDir)
, m_cacheDir(cacheDir)
, m_artifactDir(artifactDir)
, m_testRunnerBinary(testRunnerBinary)
, m_instrumentBinary(instrumentBinary)
{
}
AZStd::string TestJobInfoGenerator::GenerateLaunchArgument(const TestTarget* testTarget) const
{
if (testTarget->GetLaunchMethod() == LaunchMethod::StandAlone)
{
return AZStd::string::format(
"%s%s %s",
(m_targetBinaryDir / testTarget->GetOutputName()).c_str(),
standAloneExtension,
testTarget->GetCustomArgs().c_str()).c_str();
}
else
{
return AZStd::string::format(
"\"%s\" \"%s%s\" %s",
m_testRunnerBinary.c_str(),
(m_targetBinaryDir / testTarget->GetOutputName()).c_str(),
testRunnerExtension,
testTarget->GetCustomArgs().c_str()).c_str();
}
}
RepoPath TestJobInfoGenerator::GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const
{
return AZStd::string::format("%s.cache", (m_artifactDir / testTarget->GetName()).c_str());
}
RepoPath TestJobInfoGenerator::GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const
{
return AZStd::string::format("%s.Enumeration.xml", (m_artifactDir / testTarget->GetName()).c_str());
}
RepoPath TestJobInfoGenerator::GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const
{
return AZStd::string::format("%s.Run.xml", (m_artifactDir / testTarget->GetName()).c_str());
}
RepoPath TestJobInfoGenerator::GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const
{
return AZStd::string::format("%s.Coverage.xml", (m_artifactDir / testTarget->GetName()).c_str());
}
const RepoPath& TestJobInfoGenerator::GetCacheDir() const
{
return m_cacheDir;
}
const RepoPath& TestJobInfoGenerator::GetArtifactDir() const
{
return m_artifactDir;
}
TestEnumerator::JobInfo TestJobInfoGenerator::GenerateTestEnumerationJobInfo(
const TestTarget* testTarget,
TestEnumerator::JobInfo::Id jobId,
TestEnumerator::JobInfo::CachePolicy cachePolicy) const
{
using Command = TestEnumerator::Command;
using JobInfo = TestEnumerator::JobInfo;
using JobData = TestEnumerator::JobData;
using Cache = TestEnumerator::JobData::Cache;
const auto enumerationArtifact = GenerateTargetEnumerationArtifactFilePath(testTarget);
const Command args =
{
AZStd::string::format(
"%s --gtest_list_tests --gtest_output=xml:\"%s\"",
GenerateLaunchArgument(testTarget).c_str(),
enumerationArtifact.c_str())
};
return JobInfo(jobId, args, JobData(enumerationArtifact, Cache{ cachePolicy, GenerateTargetEnumerationCacheFilePath(testTarget) }));
}
TestRunner::JobInfo TestJobInfoGenerator::GenerateRegularTestRunJobInfo(
const TestTarget* testTarget,
TestRunner::JobInfo::Id jobId) const
{
using Command = TestRunner::Command;
using JobInfo = TestRunner::JobInfo;
using JobData = TestRunner::JobData;
const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget);
const Command args =
{
AZStd::string::format(
"%s --gtest_output=xml:\"%s\"",
GenerateLaunchArgument(testTarget).c_str(),
runArtifact.c_str())
};
return JobInfo(jobId, args, JobData(runArtifact));
}
InstrumentedTestRunner::JobInfo TestJobInfoGenerator::GenerateInstrumentedTestRunJobInfo(
const TestTarget* testTarget,
InstrumentedTestRunner::JobInfo::Id jobId,
CoverageLevel coverageLevel) const
{
using Command = InstrumentedTestRunner::Command;
using JobInfo = InstrumentedTestRunner::JobInfo;
using JobData = InstrumentedTestRunner::JobData;
const auto coverageArtifact = GenerateTargetCoverageArtifactFilePath(testTarget);
const auto runArtifact = GenerateTargetRunArtifactFilePath(testTarget);
const Command args =
{
AZStd::string::format(
"\"%s\" " // 1. Instrumented test runner
"--coverage_level %s " // 2. Coverage level
"--export_type cobertura:\"%s\" " // 3. Test coverage artifact path
"--modules \"%s\" " // 4. Modules path
"--excluded_modules \"%s\" " // 5. Exclude modules
"--sources \"%s\" -- " // 6. Sources path
"%s " // 7. Launch command
"--gtest_output=xml:\"%s\"", // 8. Result artifact
m_instrumentBinary.c_str(), // 1. Instrumented test runner
(coverageLevel == CoverageLevel::Line ? "line" : "source"), // 2. Coverage level
coverageArtifact.c_str(), // 3. Test coverage artifact path
m_targetBinaryDir.c_str(), // 4. Modules path
m_testRunnerBinary.c_str(), // 5. Exclude modules
m_sourceDir.c_str(), // 6. Sources path
GenerateLaunchArgument(testTarget).c_str(), // 7. Launch command
runArtifact.c_str()) // 8. Result artifact
};
return JobInfo(jobId, args, JobData(runArtifact, coverageArtifact));
}
}
@@ -0,0 +1,67 @@
/*
* 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 <TestImpactFramework/TestImpactRuntime.h>
#include <Artifact/Dynamic/TestImpactCoverage.h>
#include <TestEngine/Enumeration/TestImpactTestEnumerator.h>
#include <TestEngine/Run/TestImpactInstrumentedTestRunner.h>
#include <TestEngine/Run/TestImpactTestRunner.h>
namespace TestImpact
{
class TestTarget;
class TestJobInfoGenerator
{
public:
TestJobInfoGenerator(
const RepoPath& sourceDir,
const RepoPath& targetBinaryDir,
const RepoPath& cacheDir,
const RepoPath& artifactDir,
const RepoPath& testRunnerBinary,
const RepoPath& instrumentBinary);
const RepoPath& GetCacheDir() const;
const RepoPath& GetArtifactDir() const;
TestEnumerator::JobInfo GenerateTestEnumerationJobInfo(
const TestTarget* testTarget,
TestEnumerator::JobInfo::Id jobId,
TestEnumerator::JobInfo::CachePolicy cachePolicy) const;
TestRunner::JobInfo GenerateRegularTestRunJobInfo(
const TestTarget* testTarget,
TestRunner::JobInfo::Id jobId) const;
InstrumentedTestRunner::JobInfo GenerateInstrumentedTestRunJobInfo(
const TestTarget* testTarget,
InstrumentedTestRunner::JobInfo::Id jobId,
CoverageLevel coverageLevel) const;
private:
AZStd::string GenerateLaunchArgument(const TestTarget* testTarget) const;
RepoPath GenerateTargetEnumerationCacheFilePath(const TestTarget* testTarget) const;
RepoPath GenerateTargetEnumerationArtifactFilePath(const TestTarget* testTarget) const;
RepoPath GenerateTargetRunArtifactFilePath(const TestTarget* testTarget) const;
RepoPath GenerateTargetCoverageArtifactFilePath(const TestTarget* testTarget) const;
RepoPath m_sourceDir;
RepoPath m_targetBinaryDir;
RepoPath m_cacheDir;
RepoPath m_artifactDir;
RepoPath m_testRunnerBinary;
RepoPath m_instrumentBinary;
};
}
@@ -12,11 +12,10 @@
#pragma once
#include <TestImpactFramework/TestImpactBitwise.h>
#include <TestImpactBitwise.h>
#include <Process/JobRunner/TestImpactProcessJob.h>
#include <Process/JobRunner/TestImpactProcessJobRunner.h>
#include <Test/Job/TestImpactTestJobException.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
@@ -45,6 +44,7 @@ namespace TestImpact
public:
using JobData = AdditionalInfo;
using JobInfo = JobInfo<AdditionalInfo>;
using Command = typename JobInfo::Command;
using JobPayload = Payload;
using Job = Job<JobInfo, Payload>;
using ClientJobCallback = AZStd::function<void(const JobInfo& jobInfo, const JobMeta& meta)>;
@@ -111,20 +111,22 @@ namespace TestImpact
// Callback to handle job exception policies and client/derived callbacks
const auto jobCallback = [this, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std)
{
auto callbackResult = ProcessCallbackResult::Continue;
if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute))
{
throw TestJobException("Job failed to execute");
callbackResult = ProcessCallbackResult::Abort;
}
else if (meta.m_result == JobResult::ExecutedWithFailure && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure))
{
throw TestJobException("Job executed with failure");
callbackResult = ProcessCallbackResult::Abort;
}
if (m_derivedJobCallback.has_value())
{
if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); result != CallbackResult::Continue)
if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std));
result == ProcessCallbackResult::Abort)
{
return result;
callbackResult = ProcessCallbackResult::Abort;
}
}
@@ -133,7 +135,7 @@ namespace TestImpact
(*m_clientJobCallback)(jobInfo, meta);
}
return CallbackResult::Continue;
return callbackResult;
};
return m_jobRunner.Execute(jobInfos, jobCallback, payloadMapProducer);
@@ -10,31 +10,32 @@
*
*/
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactModuleCoverageFactory.h>
#include <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
#include <Test/Job/TestImpactTestJobCommon.h>
#include <Test/Run/TestImpactInstrumentedTestRunner.h>
#include <Test/Run/TestImpactTestRunException.h>
#include <Test/Run/TestImpactTestRunSerializer.h>
#include <TestEngine/Run/TestImpactInstrumentedTestRunner.h>
#include <TestEngine/Run/TestImpactTestRunException.h>
#include <TestEngine/Run/TestImpactTestRunSerializer.h>
#include <AzCore/IO/SystemFile.h>
namespace TestImpact
{
InstrumentedTestRunJobData::InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact)
InstrumentedTestRunJobData::InstrumentedTestRunJobData(const RepoPath& resultsArtifact, const RepoPath& coverageArtifact)
: TestRunJobData(resultsArtifact)
, m_coverageArtifact(coverageArtifact)
{
}
const AZ::IO::Path& InstrumentedTestRunJobData::GetCoverageArtifactPath() const
const RepoPath& InstrumentedTestRunJobData::GetCoverageArtifactPath() const
{
return m_coverageArtifact;
}
InstrumentedTestRunner::JobPayload ParseTestRunAndCoverageFiles(
const AZ::IO::Path& runFile,
const AZ::IO::Path& coverageFile,
const RepoPath& runFile,
const RepoPath& coverageFile,
AZStd::chrono::milliseconds duration,
InstrumentedTestRunner::CoverageExceptionPolicy coverageExceptionPolicy)
{
@@ -44,7 +45,7 @@ namespace TestImpact
{
AZ_TestImpact_Eval(
!IsFlagSet(coverageExceptionPolicy, Bitwise::CoverageExceptionPolicy::OnEmptyCoverage), TestRunException,
"No coverage data generated");
AZStd::string::format("No coverage data generated for '%s'", coverageFile.c_str()));
}
TestCoverage coverage(AZStd::move(moduleCoverages));
@@ -73,11 +74,24 @@ namespace TestImpact
const auto& [meta, jobInfo] = jobData;
if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure)
{
runs[jobId] = ParseTestRunAndCoverageFiles(
jobInfo->GetRunArtifactPath(),
jobInfo->GetCoverageArtifactPath(),
meta.m_duration.value(),
coverageExceptionPolicy);
try
{
runs[jobId] = ParseTestRunAndCoverageFiles(
jobInfo->GetRunArtifactPath(),
jobInfo->GetCoverageArtifactPath(),
meta.m_duration.value(),
coverageExceptionPolicy);
}
catch (const Exception& e)
{
AZ_Warning("RunInstrumentedTests", false, e.what());
runs[jobId] = AZStd::nullopt;
if (coverageExceptionPolicy == CoverageExceptionPolicy::OnEmptyCoverage)
{
break;
}
}
}
}
@@ -12,24 +12,25 @@
#pragma once
#include <Test/Job/TestImpactTestJobRunner.h>
#include <Test/Run/TestImpactTestCoverage.h>
#include <Test/Run/TestImpactTestRun.h>
#include <Test/Run/TestImpactTestRunJobData.h>
#include <TestEngine/JobRunner/TestImpactTestJobRunner.h>
#include <TestEngine/Run/TestImpactTestCoverage.h>
#include <TestEngine/Run/TestImpactTestRun.h>
#include <TestEngine/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
//! Per-job data for instrumented test runs.
class InstrumentedTestRunJobData : public TestRunJobData
class InstrumentedTestRunJobData
: public TestRunJobData
{
public:
InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact);
InstrumentedTestRunJobData(const RepoPath& resultsArtifact, const RepoPath& coverageArtifact);
//! Returns the path to the coverage artifact produced by the test target.
const AZ::IO::Path& GetCoverageArtifactPath() const;
const RepoPath& GetCoverageArtifactPath() const;
private:
AZ::IO::Path m_coverageArtifact; //!< Path to coverage data.
RepoPath m_coverageArtifact; //!< Path to coverage data.
};
namespace Bitwise
@@ -43,7 +44,8 @@ namespace TestImpact
} // namespace Bitwise
//! Runs a batch of test targets to determine the test coverage and passes/failures.
class InstrumentedTestRunner : public TestJobRunner<InstrumentedTestRunJobData, AZStd::pair<TestRun, TestCoverage>>
class InstrumentedTestRunner
: public TestJobRunner<InstrumentedTestRunJobData, AZStd::pair<TestRun, TestCoverage>>
{
using JobRunner = TestJobRunner<InstrumentedTestRunJobData, AZStd::pair<TestRun, TestCoverage>>;
@@ -10,16 +10,75 @@
*
*/
#include <Test/Run/TestImpactTestCoverage.h>
#include <TestEngine/Run/TestImpactTestCoverage.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/sort.h>
namespace TestImpact
{
TestCoverage::TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages)
TestCoverage::TestCoverage(const TestCoverage& other)
: m_modules(other.m_modules)
, m_sourcesCovered(other.m_sourcesCovered)
, m_coverageLevel(other.m_coverageLevel)
{
}
TestCoverage::TestCoverage(TestCoverage&& other) noexcept
: m_modules(AZStd::move(other.m_modules))
, m_sourcesCovered(AZStd::move(other.m_sourcesCovered))
{
AZStd::swap(m_coverageLevel, other.m_coverageLevel);
other.~TestCoverage();
}
TestCoverage::TestCoverage(const AZStd::vector<ModuleCoverage>& moduleCoverages)
: m_modules(moduleCoverages)
{
CalculateTestMetrics();
}
TestCoverage::TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages) noexcept
: m_modules(AZStd::move(moduleCoverages))
{
CalculateTestMetrics();
}
TestCoverage::~TestCoverage()
{
m_modules.clear();
m_coverageLevel.reset();
m_sourcesCovered.clear();
}
TestCoverage& TestCoverage::operator=(const TestCoverage& other)
{
if (this != &other)
{
this->~TestCoverage();
new(this)TestCoverage(other);
}
return *this;
}
TestCoverage& TestCoverage::operator=(TestCoverage&& other) noexcept
{
if (this != &other)
{
this->~TestCoverage();
new(this)TestCoverage(AZStd::move(other));
other.~TestCoverage();
}
return *this;
}
void TestCoverage::CalculateTestMetrics()
{
m_coverageLevel.reset();
m_sourcesCovered.clear();
for (const auto& moduleCovered : m_modules)
{
for (const auto& sourceCovered : moduleCovered.m_sources)
@@ -29,7 +29,14 @@ namespace TestImpact
class TestCoverage
{
public:
TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages);
TestCoverage(const TestCoverage&);
TestCoverage(TestCoverage&&) noexcept;
TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages) noexcept;
TestCoverage(const AZStd::vector<ModuleCoverage>& moduleCoverages);
~TestCoverage();
TestCoverage& operator=(const TestCoverage&);
TestCoverage& operator=(TestCoverage&&) noexcept;
//! Returns the number of unique sources covered.
size_t GetNumSourcesCovered() const;
@@ -47,6 +54,8 @@ namespace TestImpact
AZStd::optional<CoverageLevel> GetCoverageLevel() const;
private:
void CalculateTestMetrics();
AZStd::vector<ModuleCoverage> m_modules;
AZStd::vector<AZStd::string> m_sourcesCovered;
AZStd::optional<CoverageLevel> m_coverageLevel;
@@ -14,10 +14,76 @@
namespace TestImpact
{
TestRun::TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration)
TestRun::TestRun(const TestRun& other)
: TestSuiteContainer(other)
{
CalculateTestMetrics();
}
TestRun::TestRun(TestRun&& other) noexcept
: TestSuiteContainer(AZStd::move(other))
, m_numRuns(other.m_numRuns)
, m_numNotRuns(other.m_numNotRuns)
, m_numPasses(other.m_numPasses)
, m_numFailures(other.m_numFailures)
, m_duration(other.m_duration)
{
other.~TestRun();
}
TestRun::TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration) noexcept
: TestSuiteContainer(AZStd::move(testSuites))
, m_duration(duration)
{
CalculateTestMetrics();
}
TestRun::TestRun(const AZStd::vector<TestRunSuite>& testSuites, AZStd::chrono::milliseconds duration)
: TestSuiteContainer(testSuites)
, m_duration(duration)
{
CalculateTestMetrics();
}
TestRun::~TestRun()
{
m_numRuns = 0;
m_numNotRuns = 0;
m_numPasses = 0;
m_numFailures = 0;
m_duration = AZStd::chrono::milliseconds{ 0 };
}
TestRun& TestRun::operator=(TestRun&& other) noexcept
{
if (this != &other)
{
this->~TestRun();
new(this)TestRun(AZStd::move(other));
other.~TestRun();
}
return *this;
}
TestRun& TestRun::operator=(const TestRun& other)
{
if (this != &other)
{
this->~TestRun();
new(this)TestRun(other);
}
return *this;
}
void TestRun::CalculateTestMetrics()
{
m_numRuns = 0;
m_numNotRuns = 0;
m_numPasses = 0;
m_numFailures = 0;
for (const auto& suite : m_testSuites)
{
for (const auto& test : suite.m_tests)
@@ -13,7 +13,7 @@
#pragma once
#include <Artifact/Dynamic/TestImpactTestRunSuite.h>
#include <Test/TestImpactTestSuiteContainer.h>
#include <TestEngine/TestImpactTestSuiteContainer.h>
namespace TestImpact
{
@@ -24,7 +24,14 @@ namespace TestImpact
using TestSuiteContainer = TestSuiteContainer<TestRunSuite>;
public:
TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration);
TestRun(const TestRun&);
TestRun(TestRun&&) noexcept;
TestRun(const AZStd::vector<TestRunSuite>& testSuites, AZStd::chrono::milliseconds duration);
TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration) noexcept;
~TestRun();
TestRun& operator=(const TestRun&);
TestRun& operator=(TestRun&&) noexcept;
//! Returns the total number of tests that were run.
size_t GetNumRuns() const;
@@ -42,10 +49,12 @@ namespace TestImpact
AZStd::chrono::milliseconds GetDuration() const;
private:
void CalculateTestMetrics();
size_t m_numRuns = 0;
size_t m_numNotRuns = 0;
size_t m_numPasses = 0;
size_t m_numFailures = 0;
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0};
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; // this might be removed...
};
} // namespace TestImpact
@@ -10,16 +10,16 @@
*
*/
#include <Test/Run/TestImpactTestRunJobData.h>
#include <TestEngine/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
TestRunJobData::TestRunJobData(const AZ::IO::Path& resultsArtifact)
TestRunJobData::TestRunJobData(const RepoPath& resultsArtifact)
: m_runArtifact(resultsArtifact)
{
}
const AZ::IO::Path& TestRunJobData::GetRunArtifactPath() const
const RepoPath& TestRunJobData::GetRunArtifactPath() const
{
return m_runArtifact;
}
@@ -12,7 +12,7 @@
#pragma once
#include <Test/Job/TestImpactTestJobRunner.h>
#include <TestEngine/JobRunner/TestImpactTestJobRunner.h>
namespace TestImpact
{
@@ -20,12 +20,12 @@ namespace TestImpact
class TestRunJobData
{
public:
TestRunJobData(const AZ::IO::Path& resultsArtifact);
TestRunJobData(const RepoPath& resultsArtifact);
//! Returns the path to the test run artifact produced by the test target.
const AZ::IO::Path& GetRunArtifactPath() const;
const RepoPath& GetRunArtifactPath() const;
private:
AZ::IO::Path m_runArtifact; //!< Path to results data.
RepoPath m_runArtifact; //!< Path to results data.
};
} // namespace TestImpact
@@ -10,8 +10,8 @@
*
*/
#include <Test/Run/TestImpactTestRunException.h>
#include <Test/Run/TestImpactTestRunSerializer.h>
#include <TestEngine/Run/TestImpactTestRunException.h>
#include <TestEngine/Run/TestImpactTestRunSerializer.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/prettywriter.h>
@@ -12,7 +12,7 @@
#pragma once
#include <Test/Run/TestImpactTestRun.h>
#include <TestEngine/Run/TestImpactTestRun.h>
#include <AzCore/std/string/string.h>
@@ -10,17 +10,18 @@
*
*/
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
#include <Test/Job/TestImpactTestJobCommon.h>
#include <Test/Run/TestImpactTestRunException.h>
#include <Test/Run/TestImpactTestRunSerializer.h>
#include <Test/Run/TestImpactTestRunner.h>
#include <TestEngine/Run/TestImpactTestRunException.h>
#include <TestEngine/Run/TestImpactTestRunSerializer.h>
#include <TestEngine/Run/TestImpactTestRunner.h>
#include <AzCore/IO/SystemFile.h>
namespace TestImpact
{
TestRun ParseTestRunFile(const AZ::IO::Path& runFile, AZStd::chrono::milliseconds duration)
TestRun ParseTestRunFile(const RepoPath& runFile, AZStd::chrono::milliseconds duration)
{
return TestRun(GTest::TestRunSuitesFactory(ReadFileContents<TestRunException>(runFile)), duration);
}
@@ -46,7 +47,15 @@ namespace TestImpact
const auto& [meta, jobInfo] = jobData;
if (meta.m_result == JobResult::ExecutedWithSuccess || meta.m_result == JobResult::ExecutedWithFailure)
{
runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value());
try
{
runs[jobId] = ParseTestRunFile(jobInfo->GetRunArtifactPath(), meta.m_duration.value());
}
catch (const Exception& e)
{
AZ_Warning("RunTests", false, e.what());
runs[jobId] = AZStd::nullopt;
}
}
}
@@ -12,9 +12,9 @@
#pragma once
#include <Test/Job/TestImpactTestJobRunner.h>
#include <Test/Run/TestImpactTestRun.h>
#include <Test/Run/TestImpactTestRunJobData.h>
#include <TestEngine/JobRunner/TestImpactTestJobRunner.h>
#include <TestEngine/Run/TestImpactTestRun.h>
#include <TestEngine/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
@@ -22,7 +22,14 @@ namespace TestImpact
class TestSuiteContainer
{
public:
TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites);
TestSuiteContainer(const TestSuiteContainer&);
TestSuiteContainer(TestSuiteContainer&&) noexcept;
TestSuiteContainer(const AZStd::vector<TestSuite>& testSuites);
TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites) noexcept;
virtual ~TestSuiteContainer();
TestSuiteContainer& operator=(const TestSuiteContainer&);
TestSuiteContainer& operator=(TestSuiteContainer&&) noexcept;
//! Returns the test suites in this container.
const AZStd::vector<TestSuite>& GetTestSuites() const;
@@ -39,6 +46,9 @@ namespace TestImpact
//! Returns the total number of disabled tests across all test suites.
size_t GetNumDisabledTests() const;
private:
void CalculateTestMetrics();
protected:
AZStd::vector<TestSuite> m_testSuites;
size_t m_numDisabledTests = 0;
@@ -46,9 +56,75 @@ namespace TestImpact
};
template<typename TestSuite>
TestSuiteContainer<TestSuite>::TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites)
TestSuiteContainer<TestSuite>::TestSuiteContainer(TestSuiteContainer&& other) noexcept
: m_testSuites(AZStd::move(other.m_testSuites))
, m_numDisabledTests(other.m_numDisabledTests)
, m_numEnabledTests(other.m_numEnabledTests)
{
other.~TestSuiteContainer();
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>::TestSuiteContainer(const TestSuiteContainer& other)
: m_testSuites(other.m_testSuites.begin(), other.m_testSuites.end())
, m_numDisabledTests(other.m_numDisabledTests)
, m_numEnabledTests(other.m_numEnabledTests)
{
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>::~TestSuiteContainer()
{
m_testSuites.clear();
m_numDisabledTests = 0;
m_numEnabledTests = 0;
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>::TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites) noexcept
: m_testSuites(std::move(testSuites))
{
CalculateTestMetrics();
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>::TestSuiteContainer(const AZStd::vector<TestSuite>& testSuites)
: m_testSuites(testSuites)
{
CalculateTestMetrics();
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>& TestSuiteContainer<TestSuite>::operator=(TestSuiteContainer&& other) noexcept
{
if (this != &other)
{
this->~TestSuiteContainer();
new(this)TestSuiteContainer(AZStd::move(other));
other.~TestSuiteContainer();
}
return *this;
}
template<typename TestSuite>
TestSuiteContainer<TestSuite>& TestSuiteContainer<TestSuite>::operator=(const TestSuiteContainer& other)
{
if (this != &other)
{
this->~TestSuiteContainer();
new(this)TestSuiteContainer(other);
}
return *this;
}
template<typename TestSuite>
void TestSuiteContainer<TestSuite>::CalculateTestMetrics()
{
m_numDisabledTests = 0;
m_numEnabledTests = 0;
for (const auto& suite : m_testSuites)
{
if (suite.m_enabled)