Integrating latest from github/TIF/Runtime

monroegm-disable-blank-issue-2
alexpete 5 years ago
parent eee1597929
commit c2a6addf1f

@ -1,14 +1,14 @@
/*
* 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.
*
*/
* 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.
*
*/
int main([[maybe_unused]] int argc, [[maybe_unused]] char** argv)
{

@ -9,8 +9,14 @@
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/${PAL_PLATFORM_NAME})
ly_get_list_relative_pal_filename(common_dir ${CMAKE_CURRENT_LIST_DIR}/Source/Platform/Common)
add_subdirectory(Tests/TestProcess)
add_subdirectory(Tests/TestTargetA)
add_subdirectory(Tests/TestTargetB)
add_subdirectory(Tests/TestTargetC)
add_subdirectory(Tests/TestTargetD)
ly_add_target(
NAME TestImpact.Runtime.Static STATIC
@ -18,12 +24,64 @@ ly_add_target(
FILES_CMAKE
testimpactframework_runtime_files.cmake
${pal_dir}/platform_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake
PLATFORM_INCLUDE_FILES
${common_dir}/${PAL_TRAIT_COMPILER_ID}/testimpactframework_${PAL_TRAIT_COMPILER_ID_LOWERCASE}.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
Public
PUBLIC
AZ::AzCore
)
)
################################################################################
# Tests
################################################################################
ly_add_target(
NAME TestImpact.Runtime.Tests ${PAL_TRAIT_TEST_TARGET_TYPE}
NAMESPACE AZ
FILES_CMAKE
testimpactframework_runtime_tests_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Include
Source
Tests
BUILD_DEPENDENCIES
PRIVATE
AZ::AzTestShared
AZ::AzTest
AZ::TestImpact.Runtime.Static
RUNTIME_DEPENDENCIES
AZ::AzTestRunner
AZ::TestImpact.TestProcess.Console
AZ::TestImpact.TestTargetA.Tests
AZ::TestImpact.TestTargetB.Tests
AZ::TestImpact.TestTargetC.Tests
AZ::TestImpact.TestTargetD.Tests
COMPILE_DEFINITIONS
PRIVATE
LY_TEST_IMPACT_AZ_TESTRUNNER_BIN="$<TARGET_FILE:AzTestRunner>"
LY_TEST_IMPACT_TEST_PROCESS_BIN="$<TARGET_FILE:TestImpact.TestProcess.Console>"
LY_TEST_IMPACT_TEST_TARGET_A_BIN="$<TARGET_FILE:TestImpact.TestTargetA.Tests>"
LY_TEST_IMPACT_TEST_TARGET_B_BIN="$<TARGET_FILE:TestImpact.TestTargetB.Tests>"
LY_TEST_IMPACT_TEST_TARGET_C_BIN="$<TARGET_FILE:TestImpact.TestTargetC.Tests>"
LY_TEST_IMPACT_TEST_TARGET_D_BIN="$<TARGET_FILE:TestImpact.TestTargetD.Tests>"
LY_TEST_IMPACT_TEST_TARGET_A_BASE_NAME="$<TARGET_FILE_BASE_NAME:TestImpact.TestTargetA.Tests>"
LY_TEST_IMPACT_TEST_TARGET_B_BASE_NAME="$<TARGET_FILE_BASE_NAME:TestImpact.TestTargetB.Tests>"
LY_TEST_IMPACT_TEST_TARGET_C_BASE_NAME="$<TARGET_FILE_BASE_NAME:TestImpact.TestTargetC.Tests>"
LY_TEST_IMPACT_TEST_TARGET_D_BASE_NAME="$<TARGET_FILE_BASE_NAME:TestImpact.TestTargetD.Tests>"
LY_TEST_IMPACT_TEST_TARGET_ENUMERATION_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Enum"
LY_TEST_IMPACT_TEST_TARGET_RESULTS_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Result"
LY_TEST_IMPACT_TEST_TARGET_COVERAGE_DIR="${GTEST_XML_OUTPUT_DIR}/TestImpact/Temp/Exclusive/Coverage"
LY_TEST_IMPACT_INSTRUMENTATION_BIN="${LY_TEST_IMPACT_INSTRUMENTATION_BIN}"
LY_TEST_IMPACT_MODULES_DIR="${CMAKE_BINARY_DIR}"
LY_TEST_IMPACT_COVERAGE_SOURCES_DIR="${CMAKE_CURRENT_SOURCE_DIR}"
)
ly_add_googletest(
NAME AZ::TestImpact.Runtime.Tests
)

@ -0,0 +1,35 @@
/*
* 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
namespace TestImpact
{
//! Convinience namespace for allowing bitwise operations on enum classes.
//! @note Any types declared in this namespace will have the bitwise operator overloads declared within.
namespace Bitwise
{
template<typename Flags>
Flags operator|(Flags lhs, Flags rhs)
{
return static_cast<Flags>(
static_cast<std::underlying_type<Flags>::type>(lhs) | static_cast<std::underlying_type<Flags>::type>(rhs));
}
template<typename Flags>
bool IsFlagSet(Flags flags, Flags flag)
{
return static_cast<bool>(
static_cast<std::underlying_type<Flags>::type>(flags) & static_cast<std::underlying_type<Flags>::type>(flag));
}
} // namespace Bitwise
} // namespace TestImpact

@ -0,0 +1,23 @@
/*
* 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
namespace TestImpact
{
//! Generic callback result used by test impact systems.
enum class CallbackResult : bool
{
Continue,
Abort
};
} // namespace TestImpact

@ -0,0 +1,51 @@
/*
* 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 <AzCore/std/string/string.h>
#include <stdexcept>
//! Evaluates the specified condition and throws the specified exception with the specified
// !message upon failure.
#define AZ_TestImpact_Eval(CONDITION, EXCEPTION_TYPE, MSG) \
do \
{ \
static_assert( \
AZStd::is_base_of_v<TestImpact::Exception, EXCEPTION_TYPE>, \
"TestImpact Eval macro must only be used with TestImpact exceptions"); \
if(!(CONDITION)) \
{ \
throw(EXCEPTION_TYPE(MSG)); \
} \
} \
while (0)
namespace TestImpact
{
//! Base class for test impact framework exceptions.
//! @note The message passed in to the constructor is copied and thus safe with dynamic strings.
class Exception
: public std::exception
{
public:
explicit Exception() = default;
explicit Exception(const AZStd::string& msg);
explicit Exception(const char* msg);
const char* what() const noexcept override;
private:
//! Error message detailing the reason for the exception.
AZStd::string m_msg;
};
} // namespace TestImpact

@ -0,0 +1,41 @@
/*
* 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 <AzCore/IO/Path/Path.h>
namespace TestImpact
{
//! Wrapper for OS paths relative to a specified parent path.
//! @note Mimics path semantics only, makes no guarantees about validity.
class FrameworkPath
{
public:
FrameworkPath() = default;
//! Creates a path with no parent.
explicit FrameworkPath(const AZ::IO::Path& absolutePath);
//! Creates a path with an absolute path and path relative to the specified parent path.
explicit FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo);
//! Retrieves the absolute path.
const AZ::IO::Path& Absolute() const;
//! Retrieves the path relative to the specified parent path.
const AZ::IO::Path& Relative() const;
private:
//! The absolute path value.
AZ::IO::Path m_absolutePath;
//! The path value relative to the specified parent path.
AZ::IO::Path m_relativePath;
};
} // namespace TestImpact

@ -0,0 +1,27 @@
/*
* 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 <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Artifact produced by the unified diff parsing process representing the file CRUD operations of a given diff.
struct ChangeList
{
AZStd::vector<AZStd::string> m_createdFiles;
AZStd::vector<AZStd::string> m_updatedFiles;
AZStd::vector<AZStd::string> m_deletedFiles;
};
} // namespace TestImpact

@ -0,0 +1,41 @@
/*
* 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 <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
//! Coverage information about a particular line.
struct LineCoverage
{
size_t m_lineNumber = 0; //!< The source line number this covers.
size_t m_hitCount = 0; //!< Number of times this line was covered (zero if not covered).
};
//! Coverage information about a particular source file.
struct SourceCoverage
{
AZ::IO::Path m_path; //!< Source file path.
AZStd::optional<AZStd::vector<LineCoverage>> m_coverage; //!< Source file line coverage (empty if source level coverage only).
};
//! Coverage information about a particular module (executable, shared library).
struct ModuleCoverage
{
AZ::IO::Path m_path; //!< Module path.
AZStd::vector<SourceCoverage> m_sources; //!< Sources of this module that are covered.
};
} // namespace TestImpact

@ -0,0 +1,21 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestSuite.h>
namespace TestImpact
{
using TestEnumerationCase = TestCase; //!< Test case for test enumeration artifacts.
using TestEnumerationSuite = TestSuite<TestEnumerationCase>; //!< Test suite for test enumeration artifacts.
} // namespace TestImpact

@ -0,0 +1,51 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestSuite.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
//! Result of a test that was ran.
enum class TestRunResult : bool
{
Failed, //! The given test failed.
Passed //! The given test passed.
};
//! Status of test as to whether or not it was ran.
enum class TestRunStatus : bool
{
NotRun, //!< The test was not run (typically because the test run was aborted by the client or runner before the test could run).
Run //!< The test was run (see TestRunResult for the result of this test).
};
//! Test case for test run artifacts.
struct TestRunCase
: public TestCase
{
AZStd::optional<TestRunResult> m_result;
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; //! Duration this test took to run.
TestRunStatus m_status = TestRunStatus::NotRun;
};
//! Test suite for test run artifacts.
struct TestRunSuite
: public TestSuite<TestRunCase>
{
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{0}; //!< Duration this test suite took to run all of its tests.
};
} // namespace TestImpact

@ -0,0 +1,35 @@
/*
* 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 <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Artifact describing basic information about a test case.
struct TestCase
{
AZStd::string m_name;
bool m_enabled = false;
};
//! Artifact describing basic information about a test suite.
template<typename Test>
struct TestSuite
{
AZStd::string m_name;
bool m_enabled = false;
AZStd::vector<Test> m_tests;
};
} // namespace TestImpact

@ -0,0 +1,172 @@
/*
* 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 <Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/document.h>
#include <AzCore/std/string/regex.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent JSON node and attribute names
constexpr const char* Keys[] =
{
"target",
"name",
"output_name",
"path",
"sources",
"static",
"input",
"output"
};
enum
{
TargetKey,
NameKey,
OutputNameKey,
PathKey,
SourcesKey,
StaticKey,
InputKey,
OutputKey
};
} // namespace
AutogenSources PairAutogenSources(
const AZStd::vector<AZ::IO::Path>& inputSources,
const AZStd::vector<AZ::IO::Path>& outputSources,
const AZStd::string& autogenMatcher)
{
AutogenSources autogenSources;
const auto matcherPattern = AZStd::regex(autogenMatcher);
AZStd::smatch inputMatches, outputMatches;
// This has the potential to be optimized to O(n(n-1)/2) time complexity but to be perfectly honest it's not a serious
// bottleneck right now and easier gains would be achieved by constructing build target artifacts in parallel rather than
// trying to squeeze any more juice here as each build target is independent of one and other with no shared memory
for (const auto& input : inputSources)
{
AutogenPairs autogenPairs;
autogenPairs.m_input = input.String();
const AZStd::string inputString = input.Stem().Native();
if (AZStd::regex_search(inputString, inputMatches, matcherPattern))
{
for (const auto& output : outputSources)
{
const AZStd::string outputString = output.Stem().Native();
if (AZStd::regex_search(outputString, outputMatches, matcherPattern))
{
// Note: [0] contains the whole match, [1] contains the first capture group
const auto& inputMatch = inputMatches[1];
const auto& outputMatch = outputMatches[1];
if (inputMatch == outputMatch)
{
autogenPairs.m_outputs.emplace_back(output);
}
}
}
}
if (!autogenPairs.m_outputs.empty())
{
autogenSources.emplace_back(AZStd::move(autogenPairs));
}
}
return autogenSources;
}
BuildTargetDescriptor BuildTargetDescriptorFactory(
const AZStd::string& buildTargetData,
const AZStd::vector<AZStd::string>& staticSourceExtensionExcludes,
const AZStd::vector<AZStd::string>& autogenInputExtensionExcludes,
const AZStd::string& autogenMatcher)
{
AZ_TestImpact_Eval(!autogenMatcher.empty(), ArtifactException, "Autogen matcher cannot be empty");
BuildTargetDescriptor buildTargetDescriptor;
rapidjson::Document buildTarget;
if (buildTarget.Parse(buildTargetData.c_str()).HasParseError())
{
throw TestImpact::ArtifactException("Could not parse build target data");
}
const auto& target = buildTarget[Keys[TargetKey]];
buildTargetDescriptor.m_buildMetaData.m_name = target[Keys[NameKey]].GetString();
buildTargetDescriptor.m_buildMetaData.m_outputName = target[Keys[OutputNameKey]].GetString();
buildTargetDescriptor.m_buildMetaData.m_path = target["path"].GetString();
AZ_TestImpact_Eval(!buildTargetDescriptor.m_buildMetaData.m_name.empty(), ArtifactException, "Target name cannot be empty");
AZ_TestImpact_Eval(
!buildTargetDescriptor.m_buildMetaData.m_outputName.empty(), ArtifactException, "Target output name cannot be empty");
AZ_TestImpact_Eval(!buildTargetDescriptor.m_buildMetaData.m_path.empty(), ArtifactException, "Target path cannot be empty");
const auto& sources = buildTarget[Keys[SourcesKey]];
const auto& staticSources = sources[Keys[StaticKey]].GetArray();
if (!staticSources.Empty())
{
buildTargetDescriptor.m_sources.m_staticSources = AZStd::vector<AZStd::string>();
for (const auto& source : staticSources)
{
const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString());
if (AZStd::find(
staticSourceExtensionExcludes.begin(), staticSourceExtensionExcludes.end(), sourcePath.Extension().Native()) ==
staticSourceExtensionExcludes.end())
{
buildTargetDescriptor.m_sources.m_staticSources.emplace_back(AZStd::move(sourcePath));
}
}
}
const auto& inputSources = buildTarget[Keys[SourcesKey]][Keys[InputKey]].GetArray();
const auto& outputSources = buildTarget[Keys[SourcesKey]][Keys[OutputKey]].GetArray();
if (!inputSources.Empty() || !outputSources.Empty())
{
AZ_TestImpact_Eval(
!inputSources.Empty() && !outputSources.Empty(), ArtifactException, "Autogen malformed, input or output sources are empty");
AZStd::vector<AZ::IO::Path> inputPaths;
AZStd::vector<AZ::IO::Path> outputPaths;
inputPaths.reserve(inputSources.Size());
outputPaths.reserve(outputSources.Size());
for (const auto& source : inputSources)
{
const AZ::IO::Path sourcePath = AZ::IO::Path(source.GetString());
if (AZStd::find(
autogenInputExtensionExcludes.begin(), autogenInputExtensionExcludes.end(), sourcePath.Extension().Native()) ==
autogenInputExtensionExcludes.end())
{
inputPaths.emplace_back(AZStd::move(sourcePath));
}
}
for (const auto& source : outputSources)
{
outputPaths.emplace_back(AZStd::move(AZ::IO::Path(source.GetString())));
}
buildTargetDescriptor.m_sources.m_autogenSources = PairAutogenSources(inputPaths, outputPaths, autogenMatcher);
}
return buildTargetDescriptor;
}
} // namespace TestImpact

@ -0,0 +1,33 @@
/*
* 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 <Artifact/Static/TestImpactBuildTargetDescriptor.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
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 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<AZStd::string>& staticSourceExtentsionExcludes,
const AZStd::vector<AZStd::string>& autogenInputExtentsionExcludes,
const AZStd::string& autogenMatcher);
} // namespace TestImpact

@ -0,0 +1,175 @@
/*
* 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 <Artifact/Factory/TestImpactChangeListFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
namespace Utils
{
template<typename C>
auto split(C&& str, const AZStd::string& delimiter)
{
AZStd::vector<C> strings;
for (auto p = str.data(), end = p + str.length(); p != end; p += ((p == end) ? 0 : delimiter.length()))
{
const auto pre = p;
p = AZStd::search(pre, end, delimiter.cbegin(), delimiter.cend());
if (p != pre)
{
strings.emplace_back(pre, p - pre);
}
}
return strings;
}
} // namespace Utils
namespace UnifiedDiff
{
class UnifiedDiffParser
{
public:
ChangeList Parse(const AZStd::string& unifiedDiff);
private:
AZStd::optional<AZStd::string_view> GetTargetFile(const AZStd::string_view& targetFile);
ChangeList GenerateChangelist(
const AZStd::vector<AZStd::optional<AZStd::string_view>>& src,
const AZStd::vector<AZStd::optional<AZStd::string_view>>& dst);
const AZStd::string m_srcFilePrefix = "--- ";
const AZStd::string m_dstFilePrefix = "+++ ";
const AZStd::string m_gitTargetPrefix = "b/";
const AZStd::string m_perforceTargetPrefix = "/b/";
const AZStd::string m_renameFromPrefix = "rename from ";
const AZStd::string m_renameToPrefix = "rename to ";
const AZStd::string m_nullFile = "/dev/null";
bool m_hasGitHeader = false;
};
AZStd::optional<AZStd::string_view> UnifiedDiffParser::GetTargetFile(const AZStd::string_view& targetFile)
{
size_t startIndex = 0;
if (targetFile.starts_with(m_renameFromPrefix))
{
startIndex = m_renameFromPrefix.length();
}
else if (targetFile.starts_with(m_renameToPrefix))
{
startIndex = m_renameToPrefix.length();
}
else if (targetFile.find(m_nullFile) != AZStd::string::npos)
{
return AZStd::nullopt;
}
else
{
startIndex = m_hasGitHeader ? m_gitTargetPrefix.size() + m_dstFilePrefix.size()
: m_perforceTargetPrefix.size() + m_dstFilePrefix.size();
}
const auto endIndex = targetFile.find('\t');
if (endIndex != AZStd::string::npos)
{
return targetFile.substr(startIndex, endIndex - startIndex);
}
return targetFile.substr(startIndex);
}
ChangeList UnifiedDiffParser::GenerateChangelist(
const AZStd::vector<AZStd::optional<AZStd::string_view>>& src, const AZStd::vector<AZStd::optional<AZStd::string_view>>& dst)
{
AZ_TestImpact_Eval(src.size() == dst.size(), ArtifactException, "Change list source and destination file count mismatch");
ChangeList changelist;
for (size_t i = 0; i < src.size(); i++)
{
if (!src[i].has_value())
{
changelist.m_createdFiles.emplace_back(dst[i].value());
}
else if (!dst[i].has_value())
{
changelist.m_deletedFiles.emplace_back(src[i].value());
}
else if (src[i] != dst[i])
{
changelist.m_deletedFiles.emplace_back(src[i].value());
changelist.m_createdFiles.emplace_back(dst[i].value());
}
else
{
changelist.m_updatedFiles.emplace_back(src[i].value());
}
}
return changelist;
}
ChangeList UnifiedDiffParser::Parse(const AZStd::string& unifiedDiff)
{
const AZStd::string GitHeader = "diff --git";
const auto lines = Utils::split<AZStd::string_view>(unifiedDiff, "\n");
AZStd::vector<AZStd::optional<AZStd::string_view>> src;
AZStd::vector<AZStd::optional<AZStd::string_view>> dst;
for (const auto& line : lines)
{
if (line.starts_with(GitHeader))
{
m_hasGitHeader = true;
}
else if (line.starts_with(m_srcFilePrefix))
{
src.emplace_back(GetTargetFile(line));
}
else if (line.starts_with(m_dstFilePrefix))
{
dst.emplace_back(GetTargetFile(line));
}
else if (line.starts_with(m_renameFromPrefix))
{
src.emplace_back(GetTargetFile(line));
}
else if (line.starts_with(m_renameToPrefix))
{
dst.emplace_back(GetTargetFile(line));
}
}
return GenerateChangelist(src, dst);
}
ChangeList ChangeListFactory(const AZStd::string& unifiedDiff)
{
AZ_TestImpact_Eval(!unifiedDiff.empty(), ArtifactException, "Unified diff is empty");
UnifiedDiffParser diff;
ChangeList changeList = diff.Parse(unifiedDiff);
AZ_TestImpact_Eval(
!changeList.m_createdFiles.empty() ||
!changeList.m_updatedFiles.empty() ||
!changeList.m_deletedFiles.empty(),
ArtifactException, "The unified diff contained no changes");
return changeList;
}
} // namespace UnifiedDiff
} // namespace TestImpact

@ -0,0 +1,29 @@
/*
* 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 <Artifact/Dynamic/TestImpactChangeList.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
namespace UnifiedDiff
{
//! Constructs a change list artifact from the specified unified diff data.
//! @param unifiedDiffData The raw change list data in unified diff format.
//! @return The constructed change list artifact.
ChangeList ChangeListFactory(const AZStd::string& unifiedDiffData);
} // namespace UnifiedDiff
} // namespace TestImpact

@ -0,0 +1,154 @@
/*
* 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 <Artifact/Factory/TestImpactModuleCoverageFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/XML/rapidxml.h>
#include <AzCore/std/string/conversions.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent XML node and attribute names
constexpr const char* Keys[] =
{
"packages",
"name",
"filename",
"coverage",
"classes",
"lines",
"line",
"number",
"hits",
"sources",
"source"
};
enum
{
PackagesKey,
NameKey,
FileNameKey,
CoverageKey,
ClassesKey,
LinesKey,
LineKey,
NumberKey,
HitsKey,
SourcesKey,
SourceKey
};
} // namespace
namespace Cobertura
{
// Note: OpenCppCoverage appears to have a very liberal interpretation of the Cobertura coverage file format so consider
// this implementation to be provisional and coupled to the Windows platform and OpenCppCoverage tool
AZStd::vector<ModuleCoverage> ModuleCoveragesFactory(const AZStd::string& coverageData)
{
AZ_TestImpact_Eval(!coverageData.empty(), ArtifactException, "Cannot parse coverage, string is empty");
AZStd::vector<ModuleCoverage> modules;
AZStd::vector<char> rawData(coverageData.begin(), coverageData.end());
try
{
AZ::rapidxml::xml_document<> doc;
// Parse the XML doc with default flags
doc.parse<0>(rawData.data());
// Coverage
const auto coverage_node = doc.first_node(Keys[CoverageKey]);
AZ_TestImpact_Eval(coverage_node, ArtifactException, "Could not parse coverage node");
// Sources
const auto sources_node = coverage_node->first_node(Keys[SourcesKey]);
if (!sources_node)
{
return {};
}
// Source
const auto source_node = sources_node->first_node(Keys[SourceKey]);
if (!source_node)
{
return {};
}
// Root drive (this seems to be an unconventional use of the sources section by OpenCppCoverage)
const AZStd::string pathRoot = AZStd::string(source_node->value(), source_node->value() + source_node->value_size()) + "\\";
const auto packages_node = coverage_node->first_node(Keys[PackagesKey]);
if (packages_node)
{
// Modules
for (auto package_node = packages_node->first_node(); package_node; package_node = package_node->next_sibling())
{
// Module
ModuleCoverage moduleCoverage;
moduleCoverage.m_path = AZ::IO::Path(package_node->first_attribute(Keys[NameKey])->value());
const auto classes_node = package_node->first_node(Keys[ClassesKey]);
if (classes_node)
{
// Sources
for (auto class_node = classes_node->first_node(); class_node; class_node = class_node->next_sibling())
{
// Source
SourceCoverage sourceCoverage;
sourceCoverage.m_path = AZ::IO::Path(pathRoot + class_node->first_attribute(Keys[FileNameKey])->value());
const auto lines_node = class_node->first_node(Keys[LinesKey]);
if (lines_node)
{
AZStd::vector<LineCoverage> lineCoverage;
// Lines
for (auto line_node = lines_node->first_node(); line_node; line_node = line_node->next_sibling())
{
// Line
const size_t number =
AZStd::stol(AZStd::string(line_node->first_attribute(Keys[NumberKey])->value()));
const size_t hits = AZStd::stol(AZStd::string(line_node->first_attribute(Keys[HitsKey])->value()));
lineCoverage.emplace_back(LineCoverage{number, hits});
}
if (!lineCoverage.empty())
{
sourceCoverage.m_coverage.emplace(AZStd::move(lineCoverage));
}
}
moduleCoverage.m_sources.emplace_back(AZStd::move(sourceCoverage));
}
}
modules.emplace_back(AZStd::move(moduleCoverage));
}
}
}
catch (const std::exception& e)
{
AZ_Error("ModuleCoveragesFactory", false, e.what());
throw ArtifactException(e.what());
}
catch (...)
{
throw ArtifactException("An unknown error occurred parsing the XML data");
}
return modules;
}
} // namespace Cobertura
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Artifact/Dynamic/TestImpactCoverage.h>
namespace TestImpact
{
namespace Cobertura
{
//! Constructs a list of module coverage artifacts from the specified coverage data.
//! @param coverageData The raw coverage data in XML format.
//! @return The constructed list of module coverage artifacts.
AZStd::vector<ModuleCoverage> ModuleCoveragesFactory(const AZStd::string& coverageData);
} // namespace Cobertura
} // namespace TestImpact

@ -0,0 +1,94 @@
/*
* 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 <Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/XML/rapidxml.h>
#include <AzCore/std/containers/vector.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent XML node and attribute names
constexpr const char* Keys[] =
{
"testsuites",
"testsuite",
"name",
"testcase"
};
enum
{
TestSuitesKey,
TestSuiteKey,
NameKey,
TestCaseKey
};
} // namespace
namespace GTest
{
AZStd::vector<TestEnumerationSuite> TestEnumerationSuitesFactory(const AZStd::string& testEnumerationData)
{
AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse enumeration, string is empty");
AZStd::vector<TestEnumerationSuite> testSuites;
AZStd::vector<char> rawData(testEnumerationData.begin(), testEnumerationData.end());
try
{
AZ::rapidxml::xml_document<> doc;
// Parse the XML doc with default flags
doc.parse<0>(rawData.data());
const auto testsuites_node = doc.first_node(Keys[TestSuitesKey]);
AZ_TestImpact_Eval(testsuites_node, ArtifactException, "Could not parse enumeration, XML is invalid");
for (auto testsuite_node = testsuites_node->first_node(Keys[TestSuiteKey]); testsuite_node;
testsuite_node = testsuite_node->next_sibling())
{
const auto isEnabled = [](const AZStd::string& name)
{
return !name.starts_with("DISABLED_") && name.find("/DISABLED_") == AZStd::string::npos;
};
TestEnumerationSuite testSuite;
testSuite.m_name = testsuite_node->first_attribute(Keys[NameKey])->value();
testSuite.m_enabled = isEnabled(testSuite.m_name);
for (auto testcase_node = testsuite_node->first_node(Keys[TestCaseKey]); testcase_node;
testcase_node = testcase_node->next_sibling())
{
TestEnumerationCase testCase;
testCase.m_name = testcase_node->first_attribute(Keys[NameKey])->value();
testCase.m_enabled = isEnabled(testCase.m_name);
testSuite.m_tests.emplace_back(AZStd::move(testCase));
}
testSuites.emplace_back(AZStd::move(testSuite));
}
}
catch (const std::exception& e)
{
AZ_Error("TestEnumerationSuitesFactory", false, e.what());
throw ArtifactException(e.what());
}
catch (...)
{
throw ArtifactException("An unknown error occured parsing the XML data");
}
return testSuites;
}
} // namespace GTest
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestEnumerationSuite.h>
namespace TestImpact
{
namespace GTest
{
//! Constructs a list of test enumeration suite artifacts from the specified test enumeraion data.
//! @param testEnumerationData The raw test enumeration data in XML format.
//! @return The constructed list of test enumeration suite artifacts.
AZStd::vector<TestEnumerationSuite> TestEnumerationSuitesFactory(const AZStd::string& testEnumerationData);
} // namespace GTest
} // namespace TestImpact

@ -0,0 +1,143 @@
/*
* 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 <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/XML/rapidxml.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/conversions.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent XML node and attribute names
constexpr const char* Keys[] =
{
"testsuites",
"testsuite",
"name",
"testcase",
"status",
"run",
"notrun",
"time"
};
enum
{
TestSuitesKey,
TestSuiteKey,
NameKey,
TestCaseKey,
StatusKey,
RunKey,
NotRunKey,
DurationKey
};
} // namespace
namespace GTest
{
AZStd::vector<TestRunSuite> TestRunSuitesFactory(const AZStd::string& testEnumerationData)
{
AZ_TestImpact_Eval(!testEnumerationData.empty(), ArtifactException, "Cannot parse test run, string is empty");
AZStd::vector<TestRunSuite> testSuites;
AZStd::vector<char> rawData(testEnumerationData.begin(), testEnumerationData.end());
try
{
AZ::rapidxml::xml_document<> doc;
// Parse the XML doc with default flags
doc.parse<0>(rawData.data());
const auto testsuites_node = doc.first_node(Keys[TestSuitesKey]);
AZ_TestImpact_Eval(testsuites_node, ArtifactException, "Could not parse enumeration, XML is invalid");
for (auto testsuite_node = testsuites_node->first_node(Keys[TestSuiteKey]); testsuite_node;
testsuite_node = testsuite_node->next_sibling())
{
const auto isEnabled = [](const AZStd::string& name)
{
return !name.starts_with("DISABLED_") && name.find("/DISABLED_") == AZStd::string::npos;
};
const auto getDuration = [](const AZ::rapidxml::xml_node<>* node)
{
const AZStd::string duration = node->first_attribute(Keys[DurationKey])->value();
return AZStd::chrono::milliseconds(AZStd::stof(duration) * 1000.f);
};
TestRunSuite testSuite;
testSuite.m_name = testsuite_node->first_attribute(Keys[NameKey])->value();
testSuite.m_enabled = isEnabled(testSuite.m_name);
testSuite.m_duration = getDuration(testsuite_node);
for (auto testcase_node = testsuite_node->first_node(Keys[TestCaseKey]); testcase_node;
testcase_node = testcase_node->next_sibling())
{
const auto getStatus = [](const AZ::rapidxml::xml_node<>* node)
{
const AZStd::string status = node->first_attribute(Keys[StatusKey])->value();
if (status == Keys[RunKey])
{
return TestRunStatus::Run;
}
else if (status == Keys[NotRunKey])
{
return TestRunStatus::NotRun;
}
throw ArtifactException(AZStd::string::format("Unexpected run status: %s", status.c_str()));
};
const auto getResult = [](const AZ::rapidxml::xml_node<>* node)
{
for (auto child_node = node->first_node("failure"); child_node; child_node = child_node->next_sibling())
{
return TestRunResult::Failed;
}
return TestRunResult::Passed;
};
TestRunCase testCase;
testCase.m_name = testcase_node->first_attribute(Keys[NameKey])->value();
testCase.m_enabled = isEnabled(testCase.m_name);
testCase.m_duration = getDuration(testcase_node);
testCase.m_status = getStatus(testcase_node);
if (testCase.m_status == TestRunStatus::Run)
{
testCase.m_result = getResult(testcase_node);
}
testSuite.m_tests.emplace_back(AZStd::move(testCase));
}
testSuites.emplace_back(AZStd::move(testSuite));
}
}
catch (const std::exception& e)
{
AZ_Error("TestRunSuitesFactory", false, e.what());
throw ArtifactException(e.what());
}
catch (...)
{
throw ArtifactException("An unknown error occurred parsing the XML data");
}
return testSuites;
}
} // namespace GTest
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestRunSuite.h>
namespace TestImpact
{
namespace GTest
{
//! Constructs a list of test run suite artifacts from the specified test run data.
//! @param testRunData The raw test run data in XML format.
//! @return The constructed list of test run suite artifacts.
AZStd::vector<TestRunSuite> TestRunSuitesFactory(const AZStd::string& testRunData);
} // namespace GTest
} // namespace TestImpact

@ -0,0 +1,25 @@
/*
* 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 <Artifact/Static/TestImpactTestTargetMetaArtifact.h>
#include <AzCore/std/containers/vector.h>
namespace TestImpact
{
//! Constructs a list of test target meta-data artifacts from the specified master test list data.
//! @param masterTestListData The raw master test list data in JSON format.
//! @return The constructed list of test target meta-data artifacts.
TestTargetMetas TestTargetMetaMapFactory(const AZStd::string& masterTestListData);
} // namespace TestImpact

@ -0,0 +1,89 @@
/*
* 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 <Artifact/Factory/TestImpactTestTargetMetaMapFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/JSON/document.h>
#include <cstring>
namespace TestImpact
{
namespace
{
// Keys for pertinent JSON node and attribute names
constexpr const char* Keys[] =
{
"google",
"test",
"tests",
"suite",
"launch_method",
"test_runner",
"stand_alone",
"name"
};
enum
{
GoogleKey,
TestKey,
TestsKey,
SuiteKey,
LaunchMethodKey,
TestRunnerKey,
StandAloneKey,
NameKey
};
} // namespace
TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData)
{
TestTargetMetaMap testMetas;
rapidjson::Document masterTestList;
if (masterTestList.Parse(masterTestListData.c_str()).HasParseError())
{
throw TestImpact::ArtifactException("Could not parse test meta-data file");
}
const auto tests = masterTestList[Keys[GoogleKey]][Keys[TestKey]][Keys[TestsKey]].GetArray();
for (const auto& test : tests)
{
TestTargetMeta testMeta;
testMeta.m_suite = test[Keys[SuiteKey]].GetString();
if (const auto buildTypeString = test[Keys[LaunchMethodKey]].GetString(); strcmp(buildTypeString, Keys[TestRunnerKey]) == 0)
{
testMeta.m_launchMethod = LaunchMethod::TestRunner;
}
else if (strcmp(buildTypeString, Keys[StandAloneKey]) == 0)
{
testMeta.m_launchMethod = LaunchMethod::StandAlone;
}
else
{
throw(ArtifactException("Unexpected test build type"));
}
AZStd::string name = test[Keys[NameKey]].GetString();
AZ_TestImpact_Eval(!name.empty(), ArtifactException, "Test name field cannot be empty");
testMetas.emplace(AZStd::move(name), AZStd::move(testMeta));
}
// If there's no tests in the repo then something is seriously wrong
AZ_TestImpact_Eval(!testMetas.empty(), ArtifactException, "No tests were found in the repository");
return testMetas;
}
} // namespace TestImpact

@ -0,0 +1,25 @@
/*
* 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 <Artifact/Static/TestImpactTestTargetMeta.h>
#include <AzCore/std/containers/vector.h>
namespace TestImpact
{
//! Constructs a list of test target meta-data artifacts from the specified master test list data.
//! @param masterTestListData The raw master test list data in JSON format.
//! @return The constructed list of test target meta-data artifacts.
TestTargetMetaMap TestTargetMetaMapFactory(const AZStd::string& masterTestListData);
} // namespace TestImpact

@ -0,0 +1,22 @@
/*
* 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 <Artifact/Static/TestImpactBuildTargetDescriptor.h>
namespace TestImpact
{
BuildTargetDescriptor::BuildTargetDescriptor(BuildMetaData&& buildMetaData, TargetSources&& sources)
: m_buildMetaData(AZStd::move(buildMetaData))
, m_sources(AZStd::move(sources))
{
}
} // namespace TestImpact

@ -0,0 +1,55 @@
/*
* 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 <AzCore/IO/Path/Path.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Pairing between a given autogen input source and the generated output source(s).
struct AutogenPairs
{
AZStd::string m_input;
AZStd::vector<AZStd::string> m_outputs;
};
using AutogenSources = AZStd::vector<AutogenPairs>;
//! Representation of a given built target's source list.
struct TargetSources
{
AZStd::vector<AZStd::string> m_staticSources; //!< Source files used to build this target (if any).
AutogenSources m_autogenSources; //!< Autogen source files (if any).
};
//! Representation of a given build target's basic build infotmation.
struct BuildMetaData
{
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).
};
//! Artifact produced by the build system for each build target. Contains source and output information about said targets.
struct BuildTargetDescriptor
{
BuildTargetDescriptor() = default;
BuildTargetDescriptor(BuildMetaData&& buildMetaData, TargetSources&& sources);
BuildMetaData m_buildMetaData;
TargetSources m_sources;
};
} // namespace TestImpact

@ -0,0 +1,21 @@
/*
* 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 <Artifact/Static/TestImpactProductionTargetDescriptor.h>
namespace TestImpact
{
ProductionTargetDescriptor::ProductionTargetDescriptor(BuildTargetDescriptor&& buildTargetDescriptor)
: BuildTargetDescriptor(AZStd::move(buildTargetDescriptor))
{
}
} // namespace TestImpact

@ -0,0 +1,25 @@
/*
* 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 <Artifact/Static/TestImpactBuildTargetDescriptor.h>
namespace TestImpact
{
//! Artifact produced by the target artifact compiler that represents a production build target in the repository.
struct ProductionTargetDescriptor
: public BuildTargetDescriptor
{
ProductionTargetDescriptor(BuildTargetDescriptor&& buildTarget);
};
} // namespace TestImpact

@ -0,0 +1,45 @@
/*
* 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 <Artifact/Static/TestImpactTargetDescriptorCompiler.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/std/containers/unordered_map.h>
namespace TestImpact
{
AZStd::tuple<AZStd::vector<ProductionTargetDescriptor>, AZStd::vector<TestTargetDescriptor>> CompileTargetDescriptors(
AZStd::vector<BuildTargetDescriptor>&& buildTargets, TestTargetMetaMap&& testTargetMetaMap)
{
AZ_TestImpact_Eval(!buildTargets.empty(), ArtifactException, "Build target descriptor list cannot be null");
AZ_TestImpact_Eval(!testTargetMetaMap.empty(), ArtifactException, "Test target meta map cannot be null");
AZStd::tuple<AZStd::vector<ProductionTargetDescriptor>, AZStd::vector<TestTargetDescriptor>> outputTargets;
auto& [productionTargets, testTargets] = outputTargets;
for (auto&& buildTarget : buildTargets)
{
// If this build target has an associated test artifact then it is a test target, otherwise it is a production target
if (auto&& testTargetMeta = testTargetMetaMap.find(buildTarget.m_buildMetaData.m_name);
testTargetMeta != testTargetMetaMap.end())
{
testTargets.emplace_back(TestTargetDescriptor(AZStd::move(buildTarget), AZStd::move(testTargetMeta->second)));
}
else
{
productionTargets.emplace_back(ProductionTargetDescriptor(AZStd::move(buildTarget)));
}
}
return outputTargets;
}
} // namespace TestImpact

@ -0,0 +1,31 @@
/*
* 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 <Artifact/Static/TestImpactProductionTargetDescriptor.h>
#include <Artifact/Static/TestImpactTestTargetDescriptor.h>
#include <Artifact/Static/TestImpactTestTargetMeta.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/tuple.h>
namespace TestImpact
{
//! Compiles the production target artifacts and test target artifactss from the supplied build target artifacts and test target meta
//! map artifact.
//! @param buildTargets The list of build target artifacts to be sorted into production and test artifact types.
//! @param testTargetMetaMap The map of test target meta artifacts containing the additional meta-data about each test target.
//! @return A tuple containing the production artifacts and test artifacts.
AZStd::tuple<AZStd::vector<ProductionTargetDescriptor>, AZStd::vector<TestTargetDescriptor>> CompileTargetDescriptors(
AZStd::vector<BuildTargetDescriptor>&& buildTargets, TestTargetMetaMap&& testTargetMetaMap);
} // namespace TestImpact

@ -0,0 +1,22 @@
/*
* 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 <Artifact/Static/TestImpactTestTargetDescriptor.h>
namespace TestImpact
{
TestTargetDescriptor::TestTargetDescriptor(BuildTargetDescriptor&& buildTarget, TestTargetMeta&& testTargetMeta)
: BuildTargetDescriptor(AZStd::move(buildTarget))
, m_testMetaData(AZStd::move(testTargetMeta))
{
}
} // namespace TestImpact

@ -0,0 +1,28 @@
/*
* 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 <Artifact/Static/TestImpactBuildTargetDescriptor.h>
#include <Artifact/Static/TestImpactTestTargetMeta.h>
namespace TestImpact
{
//! Artifact produced by the target artifact compiler that represents a test build target in the repository.
struct TestTargetDescriptor
: public BuildTargetDescriptor
{
TestTargetDescriptor(BuildTargetDescriptor&& buildTarget, TestTargetMeta&& testTargetMeta);
TestTargetMeta m_testMetaData;
};
} // namespace TestImpact

@ -0,0 +1,36 @@
/*
* 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 <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Method used to launch the test target.
enum class LaunchMethod : bool
{
TestRunner, //!< Target is launched through a separate test runner binary.
StandAlone //!< Target is launched directly by itself.
};
//! Artifact produced by the build system for each test target containing the additional meta-data about the test.
struct TestTargetMeta
{
AZStd::string m_suite;
LaunchMethod m_launchMethod = LaunchMethod::TestRunner;
};
//! Map between test target name and test target meta-data.
using TestTargetMetaMap = AZStd::unordered_map<AZStd::string, TestTargetMeta>;
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 artifacts and artifact parsing operations.
class ArtifactException
: public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact

@ -1,11 +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.
*
*/

@ -0,0 +1,12 @@
#
# 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.
#
set(LY_COMPILE_OPTIONS PUBLIC -fexceptions)

@ -0,0 +1,12 @@
#
# 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.
#
set(LY_COMPILE_OPTIONS PUBLIC /EHsc)

@ -1,11 +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.
*
*/

@ -0,0 +1,92 @@
/*
* 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 <AzCore/PlatformIncl.h>
namespace TestImpact
{
//! OS function to cleanup handle
using CleanupFunc = BOOL (*)(HANDLE);
//! RAII wrapper around OS handles.
template<CleanupFunc CleanupFuncT>
class Handle
{
public:
Handle() = default;
explicit Handle(HANDLE handle);
~Handle();
operator HANDLE&();
PHANDLE operator&();
HANDLE& operator=(HANDLE handle);
void Close();
private:
HANDLE m_handle = INVALID_HANDLE_VALUE;
};
template<CleanupFunc CleanupFuncT>
Handle<CleanupFuncT>::Handle(HANDLE handle)
: m_handle(handle)
{
}
template<CleanupFunc CleanupFuncT>
Handle<CleanupFuncT>::~Handle()
{
Close();
}
template<CleanupFunc CleanupFuncT>
Handle<CleanupFuncT>::operator HANDLE&()
{
return m_handle;
}
template<CleanupFunc CleanupFuncT>
PHANDLE Handle<CleanupFuncT>::operator&()
{
return &m_handle;
}
template<CleanupFunc CleanupFuncT>
HANDLE& Handle<CleanupFuncT>::operator=(HANDLE handle)
{
// Setting the handle to INVALID_HANDLE_VALUE will close the handle
if (handle == INVALID_HANDLE_VALUE && m_handle != INVALID_HANDLE_VALUE)
{
Close();
}
else
{
m_handle = handle;
}
return m_handle;
}
template<CleanupFunc CleanupFuncT>
void Handle<CleanupFuncT>::Close()
{
if (m_handle != INVALID_HANDLE_VALUE)
{
CleanupFuncT(m_handle);
m_handle = INVALID_HANDLE_VALUE;
}
}
using ObjectHandle = Handle<CloseHandle>;
using WaitHandle = Handle<UnregisterWait>;
} // namespace TestImpact

@ -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.
*
*/
#include "TestImpactWin32_Pipe.h"
#include <Process/TestImpactProcessException.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
Pipe::Pipe(SECURITY_ATTRIBUTES& sa, HANDLE& stdChannel)
{
if (!CreatePipe(&m_parent, &m_child, &sa, 0))
{
throw ProcessException("Couldn't create pipe");
}
SetHandleInformation(m_parent, HANDLE_FLAG_INHERIT, 0);
stdChannel = m_child;
}
void Pipe::ReleaseChild()
{
m_child.Close();
}
void Pipe::EmptyPipe()
{
DWORD bytesAvailable = 0;
while (PeekNamedPipe(m_parent, NULL, 0, NULL, &bytesAvailable, NULL) && bytesAvailable > 0)
{
// Grow the buffer by the number of bytes available in the pipe and append the new data
DWORD bytesRead;
const size_t currentSize = m_buffer.size();
m_buffer.resize(m_buffer.size() + bytesAvailable);
if (!ReadFile(m_parent, m_buffer.data() + currentSize, bytesAvailable, &bytesRead, NULL) || bytesRead == 0)
{
throw ProcessException("Couldn't read child output from pipe");
}
}
}
AZStd::string Pipe::GetContentsAndClearInternalBuffer()
{
EmptyPipe();
AZStd::string contents;
if (m_buffer.size() > 0)
{
contents = AZStd::string(m_buffer.begin(), m_buffer.end());
m_buffer.clear();
}
return contents;
}
} // namespace TestImpact

@ -0,0 +1,52 @@
/*
* 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 "TestImpactWin32_Handle.h"
#include <AzCore/PlatformIncl.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! RAII wrapper around OS pipes.
//! Used to connect the standard output and standard error of the child process to a sink accessible to the
//! parent process to allow the parent process to read the output(s) of the child process.
class Pipe
{
public:
Pipe(SECURITY_ATTRIBUTES& sa, HANDLE& stdChannel);
Pipe(Pipe&& other) = delete;
Pipe(Pipe& other) = delete;
Pipe& operator=(Pipe& other) = delete;
Pipe& operator=(Pipe&& other) = delete;
//! Releases the child end of the pipe (not needed once parent has their end).
void ReleaseChild();
//! Empties the contents of the pipe into the internal buffer.
void EmptyPipe();
//! Empties the contents of the pipe into a string, clearing the internal buffer.
AZStd::string GetContentsAndClearInternalBuffer();
private:
// Parent and child process ends of pipe
ObjectHandle m_parent;
ObjectHandle m_child;
// Buffer for emptying pipe upon child processes exit
AZStd::vector<char> m_buffer;
};
} // namespace TestImpact

@ -0,0 +1,259 @@
/*
* 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 "TestImpactWin32_Process.h"
#include <Process/TestImpactProcessException.h>
#include <AzCore/std/parallel/lock.h>
namespace TestImpact
{
// Note: this is called from an OS thread
VOID ProcessWin32::ProcessExitCallback(PVOID processPtr, [[maybe_unused]] BOOLEAN EventSignalled)
{
// Lock the process destructor from being entered from the client thread
AZStd::lock_guard lifeLock(m_lifeCycleMutex);
ProcessId id = reinterpret_cast<ProcessId>(processPtr);
auto process = m_masterProcessList[id];
// Check that the process hasn't already been destructed from the client thread
if (process && process->m_isRunning)
{
// Lock state access and/or mutation from the client thread
AZStd::lock_guard stateLock(process->m_stateMutex);
process->RetrieveOSReturnCodeAndCleanUpProcess();
}
}
ProcessWin32::ProcessWin32(const ProcessInfo& processInfo)
: Process(processInfo)
{
AZStd::string args(m_processInfo.GetProcessPath().String());
if (m_processInfo.GetStartupArgs().length())
{
args = AZStd::string::format("%s %s", args.c_str(), m_processInfo.GetStartupArgs().c_str());
}
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = nullptr;
sa.bInheritHandle = IsPiping();
STARTUPINFO si;
ZeroMemory(&si, sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
PROCESS_INFORMATION pi;
ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
CreatePipes(sa, si);
if (!CreateProcess(
NULL,
&args[0],
NULL,
NULL,
IsPiping(),
CREATE_NEW_PROCESS_GROUP | CREATE_NO_WINDOW,
NULL, NULL,
&si, &pi))
{
throw ProcessException("Couldn't create process");
}
ReleaseChildPipes();
m_process = pi.hProcess;
m_thread = pi.hThread;
m_isRunning = true;
{
// Lock reading of the master process list from the OS thread
AZStd::lock_guard lock(m_lifeCycleMutex);
// Register this process with a unique id in the master process list
m_uniqueId = m_uniqueIdCounter++;
m_masterProcessList[m_uniqueId] = this;
}
// Register the process exit signal callback
if (!RegisterWaitForSingleObject(
&m_waitCallback,
pi.hProcess,
ProcessExitCallback,
reinterpret_cast<PVOID>(m_uniqueId),
INFINITE,
WT_EXECUTEONLYONCE))
{
throw ProcessException("Couldn't register wait object for process exit event");
}
}
bool ProcessWin32::IsPiping() const
{
return m_processInfo.ParentHasStdOutput() || m_processInfo.ParentHasStdError();
}
void ProcessWin32::CreatePipes(SECURITY_ATTRIBUTES& sa, STARTUPINFO& si)
{
if (IsPiping())
{
si.dwFlags = STARTF_USESTDHANDLES;
if (m_processInfo.ParentHasStdOutput())
{
m_stdOutPipe.emplace(sa, si.hStdOutput);
}
if (m_processInfo.ParentHasStdError())
{
m_stdErrPipe.emplace(sa, si.hStdError);
}
}
}
void ProcessWin32::ReleaseChildPipes()
{
if (m_stdOutPipe)
{
m_stdOutPipe->ReleaseChild();
}
if (m_stdErrPipe)
{
m_stdErrPipe->ReleaseChild();
}
}
void ProcessWin32::EmptyPipes()
{
if (m_stdOutPipe)
{
m_stdOutPipe->EmptyPipe();
}
if (m_stdErrPipe)
{
m_stdErrPipe->EmptyPipe();
}
}
AZStd::optional<AZStd::string> ProcessWin32::ConsumeStdOut()
{
if (m_stdOutPipe)
{
AZStd::string contents = m_stdOutPipe->GetContentsAndClearInternalBuffer();
if (!contents.empty())
{
return contents;
}
}
return AZStd::nullopt;
}
AZStd::optional<AZStd::string> ProcessWin32::ConsumeStdErr()
{
if (m_stdErrPipe)
{
AZStd::string contents = m_stdErrPipe->GetContentsAndClearInternalBuffer();
if (!contents.empty())
{
return contents;
}
}
return AZStd::nullopt;
}
void ProcessWin32::Terminate(ReturnCode returnCode)
{
// Lock process cleanup from the OS thread
AZStd::lock_guard lock(m_stateMutex);
if (m_isRunning)
{
// Cancel the callback so we can wait for the signal ourselves
// Note: we keep the state mutex locked as closing the callback is not guaranteed to be instantaneous
m_waitCallback.Close();
// Terminate the process and set the error code to the terminate code
TerminateProcess(m_process, returnCode);
SetReturnCodeAndCleanUpProcesses(returnCode);
}
}
bool ProcessWin32::IsRunning() const
{
return m_isRunning;
}
void ProcessWin32::BlockUntilExit()
{
// Lock process cleanup from the OS thread
AZStd::lock_guard lock(m_stateMutex);
if (m_isRunning)
{
// Cancel the callback so we can wait for the signal ourselves
// Note: we keep the state mutex locked as closing the callback is not guaranteed to be instantaneous
m_waitCallback.Close();
if (IsPiping())
{
// This process will be blocked from exiting if pipe not emptied so will deadlock if we wait
// indefintely whilst there is still output in the pipes so instead keep waiting and checking
// if the pipes need emptying until the process exits
while (WAIT_OBJECT_0 != WaitForSingleObject(m_process, 1))
{
EmptyPipes();
}
}
else
{
// No possibility of pipe deadlocking, safe to wait indefinitely for process exit
WaitForSingleObject(m_process, INFINITE);
}
// Now that the this process has definately exited we are safe to clean up
RetrieveOSReturnCodeAndCleanUpProcess();
}
}
void ProcessWin32::RetrieveOSReturnCodeAndCleanUpProcess()
{
DWORD returnCode;
GetExitCodeProcess(m_process, &returnCode);
SetReturnCodeAndCleanUpProcesses(returnCode);
}
void ProcessWin32::SetReturnCodeAndCleanUpProcesses(ReturnCode returnCode)
{
m_returnCode = returnCode;
m_process.Close();
m_thread.Close();
m_waitCallback.Close();
m_isRunning = false;
}
ProcessWin32::~ProcessWin32()
{
// Lock the process exit signal callback from being entered OS thread
AZStd::lock_guard lock(m_lifeCycleMutex);
// Remove this process from the master list so the process exit signal doesn't attempt to cleanup
// this process if it is deleted client side
m_masterProcessList[m_uniqueId] = nullptr;
}
} // namespace TestImpact

@ -0,0 +1,101 @@
/*
* 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 "TestImpactWin32_Handle.h"
#include "TestImpactWin32_Pipe.h"
#include <Process/TestImpactProcess.h>
#include <AzCore/PlatformIncl.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/parallel/atomic.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Platform-specific implementation of Process.
class ProcessWin32
: public Process
{
public:
explicit ProcessWin32(const ProcessInfo& processInfo);
ProcessWin32(ProcessWin32&& other) = delete;
ProcessWin32(ProcessWin32& other) = delete;
ProcessWin32& operator=(ProcessWin32& other) = delete;
ProcessWin32& operator=(ProcessWin32&& other) = delete;
~ProcessWin32();
// Process overrides...
void Terminate(ReturnCode returnCode) override;
void BlockUntilExit() override;
bool IsRunning() const override;
AZStd::optional<AZStd::string> ConsumeStdOut() override;
AZStd::optional<AZStd::string> ConsumeStdErr() override;
private:
//! Callback for process exit signal.
static VOID ProcessExitCallback(PVOID processPtr, BOOLEAN EventSignalled);
//! Retrieves the return code and cleans up the OS handles.
void RetrieveOSReturnCodeAndCleanUpProcess();
//! Sets the return code and cleans up the OS handles
void SetReturnCodeAndCleanUpProcesses(ReturnCode returnCode);
//! Returns true if either stdout or stderr is beign redirected.
bool IsPiping() const;
//! Creates the parent and child pipes for stdout and/or stderr.
void CreatePipes(SECURITY_ATTRIBUTES& sa, STARTUPINFO& si);
//! Empties all pipes so the process can exit without deadlocking.
void EmptyPipes();
//! Releases the child end of the stdout and/or stderr pipes/
void ReleaseChildPipes();
// Flag to determine whether or not the process is in flight
AZStd::atomic_bool m_isRunning = false;
// Unique id assigned to this process (not the same as the id assigned by the client in the ProcessInfo class)
// as used in the master process list
size_t m_uniqueId = 0;
// Handles to OS process
ObjectHandle m_process;
ObjectHandle m_thread;
// Handle to process exit signal callback
WaitHandle m_waitCallback;
// Process to parent standard output piping
AZStd::optional<Pipe> m_stdOutPipe;
AZStd::optional<Pipe> m_stdErrPipe;
// Mutex protecting process state access/mutation from the OS thread and client thread
mutable AZStd::mutex m_stateMutex;
// Mutex keeping the process life cycles in sync between the OS thread and client thread
inline static AZStd::mutex m_lifeCycleMutex;
// Unique counter to give each launched process a unique id
inline static size_t m_uniqueIdCounter = 1;
// Master process list used to ensure consistency of process lifecycles between OS thread and client thread
inline static std::unordered_map<ProcessId, ProcessWin32*> m_masterProcessList;
};
} // namespace TestImpact

@ -0,0 +1,24 @@
/*
* 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 "TestImpactWin32_Process.h"
#include <Process/TestImpactProcess.h>
#include <Process/TestImpactProcessLauncher.h>
namespace TestImpact
{
AZStd::unique_ptr<Process> LaunchProcess(const ProcessInfo& processInfo)
{
return AZStd::make_unique<ProcessWin32>(processInfo);
}
} // namespace TestImpact

@ -10,5 +10,10 @@
#
set(FILES
Dummy_Windows.cpp
Process/TestImpactWin32_ProcessLauncher.cpp
Process/TestImpactWin32_Process.cpp
Process/TestImpactWin32_Process.h
Process/TestImpactWin32_Handle.h
Process/TestImpactWin32_Pipe.cpp
Process/TestImpactWin32_Pipe.h
)

@ -0,0 +1,139 @@
/*
* 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 <Process/JobRunner/TestImpactProcessJobInfo.h>
#include <Process/TestImpactProcessInfo.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/optional.h>
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<AZStd::chrono::high_resolution_clock::time_point>
m_startTime; //!< The time, relative to the job runner start, that this job started.
AZStd::optional<AZStd::chrono::milliseconds> m_duration; //!< The duration that this job took to complete.
AZStd::optional<ReturnCode> 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<typename JobInfoT, typename JobPayloadT>
class Job
{
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>&& 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<ReturnCode> GetReturnCode() const;
//! Returns the payload produced by this job.
const AZStd::optional<Payload>& GetPayload() const;
private:
Info m_jobInfo;
JobMeta m_meta;
AZStd::optional<Payload> m_payload;
};
template<typename JobInfoT, typename JobPayloadT>
Job<JobInfoT, JobPayloadT>::Job(Info jobInfo, JobMeta&& jobMeta, AZStd::optional<Payload>&& payload)
: m_jobInfo(jobInfo)
, m_meta(AZStd::move(jobMeta))
, m_payload(AZStd::move(payload))
{
}
template<typename JobInfoT, typename JobPayloadT>
const JobInfoT& Job<JobInfoT, JobPayloadT>::GetJobInfo() const
{
return m_jobInfo;
}
template<typename JobInfoT, typename JobPayloadT>
JobResult Job<JobInfoT, JobPayloadT>::GetResult() const
{
return m_meta.m_result;
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::optional<ReturnCode> Job<JobInfoT, JobPayloadT>::GetReturnCode() const
{
return m_meta.m_returnCode;
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::high_resolution_clock::time_point Job<JobInfoT, JobPayloadT>::GetStartTime() const
{
return m_meta.m_startTime.value_or(AZStd::chrono::high_resolution_clock::time_point());
}
template<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::high_resolution_clock::time_point Job<JobInfoT, JobPayloadT>::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<typename JobInfoT, typename JobPayloadT>
AZStd::chrono::milliseconds Job<JobInfoT, JobPayloadT>::GetDuration() const
{
return m_meta.m_duration.value_or(AZStd::chrono::milliseconds{0});
}
template<typename JobInfoT, typename JobPayloadT>
const AZStd::optional<JobPayloadT>& Job<JobInfoT, JobPayloadT>::GetPayload() const
{
return m_payload;
}
} // namespace TestImpact

@ -0,0 +1,73 @@
/*
* 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 <AzCore/std/string/string.h>
namespace TestImpact
{
//! Per-job information to configure and run jobs and process the resulting artifacts.
//! @tparam AdditionalInfo Additional information to be provided to each job to be consumed by client.
template<typename AdditionalInfo>
class JobInfo
: public AdditionalInfo
{
public:
using IdType = size_t;
//! Client-provided identifier to distinguish between different jobs.
//! @note Ids of different job types are not interchangeable.
struct Id
{
IdType m_value;
};
//! 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 additionalInfo The arguments to be provided to the additional information data structure.
template<typename... AdditionalInfoArgs>
JobInfo(Id jobId, const AZStd::string& args, 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;
private:
Id m_id;
AZStd::string m_args;
};
template<typename AdditionalInfo>
template<typename... AdditionalInfoArgs>
JobInfo<AdditionalInfo>::JobInfo(Id jobId, const AZStd::string& args, AdditionalInfoArgs&&... additionalInfo)
: AdditionalInfo{std::forward<AdditionalInfoArgs>(additionalInfo)...}
, m_id(jobId)
, m_args(args)
{
}
template<typename AdditionalInfo>
typename JobInfo<AdditionalInfo>::Id JobInfo<AdditionalInfo>::GetId() const
{
return m_id;
}
template<typename AdditionalInfo>
const AZStd::string& JobInfo<AdditionalInfo>::GetArgs() const
{
return m_args;
}
} // namespace TestImpact

@ -0,0 +1,190 @@
/*
* 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 <Process/Scheduler/TestImpactProcessScheduler.h>
#include <TestImpactFramework/TestImpactCallback.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Callback for job completion/failure.
//! @param jobInfo The job information associated with this job.
//! @param meta The meta-data about the job run.
//! @param std The standard output and standard error of the process running the job.
template<typename Job>
using JobCallback = AZStd::function<CallbackResult(const typename Job::Info& jobInfo, const JobMeta& meta, StdContent&& std)>;
//! 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<typename Job>
using PayloadMap = AZStd::unordered_map<typename Job::Info::IdType, AZStd::optional<typename Job::Payload>>;
//! The map used by the client to associate the job information and meta-data with the job ids.
template<typename Job>
using JobDataMap = AZStd::unordered_map<typename Job::Info::IdType, AZStd::pair<JobMeta, const typename Job::Info*>>;
//! The callback for producing the payloads for the jobs after all jobs have finished executing.
//! @param jobInfos The information for each job run.
//! @param jobDataMap The job data (in the form of job info and meta-data) for each job run.
template<typename Job>
using PayloadMapProducer = AZStd::function<PayloadMap<Job>(const JobDataMap<Job>& jobDataMap)>;
//! Generic job runner that launches a process for each job, records metrics about each job run and hands the payload artifacts
//! produced by each job to the client before compositing the metrics and payload artifacts for each job into a single interface
//! to be consumed by the client.
template<typename JobT>
class JobRunner
{
public:
//! Constructs the job runner with the specified parameters to constrain job runs.
//! @param stdOutRouting The standard output routing to be specified for all jobs.
//! @param stdErrRouting The standard error routing to be specified for all jobs.
//! @param maxConcurrentProcesses he maximum number of concurrent jobs in-flight.
//! @param processTimeout The maximum duration a job may be in-flight before being forcefully terminated (nullopt if no timeout).
//! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight jobs (nullopt if
//! no timeout).
JobRunner(
StdOutputRouting stdOutRouting,
StdErrorRouting stdErrRouting,
size_t maxConcurrentProcesses,
AZStd::optional<AZStd::chrono::milliseconds> processTimeout,
AZStd::optional<AZStd::chrono::milliseconds> scheduleTimeout);
//! Executes the specified jobs and returns the products of their labor.
//! @note: the job and payload callbacks are specified here rather than in the constructor to allow clients to use capturing lambdas
//! should they desire to.
//! @param jobs The arguments (and other pertinent information) required for each job to be run.
//! @param jobCallback The client callback to be called when each job changes state.
//! @param payloadMapProducer The client callback to be called when all jobs have finished to transform the work produced by each
//! job into the desired output.
AZStd::vector<typename JobT> Execute(
const AZStd::vector<typename JobT::Info>& jobs, JobCallback<typename JobT> jobCallback,
PayloadMapProducer<JobT> payloadMapProducer);
private:
size_t m_maxConcurrentProcesses = 0; //!< Maximum number of concurrent jobs being executed at a given time.
StdOutputRouting m_stdOutRouting; //!< Standard output routing from each job process to job runner.
StdErrorRouting m_stdErrRouting; //!< Standard error routing from each job process to job runner
AZStd::optional<AZStd::chrono::milliseconds> m_jobTimeout; //!< Maximum time a job can run for before being forcefully terminated.
AZStd::optional<AZStd::chrono::milliseconds> m_runnerTimeout; //!< Maximum time the job runner can run before forcefully terminating all in-flight jobs and shutting down.
};
template<typename JobT>
JobRunner<JobT>::JobRunner(
StdOutputRouting stdOutRouting,
StdErrorRouting stdErrRouting,
size_t maxConcurrentProcesses,
AZStd::optional<AZStd::chrono::milliseconds> jobTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout)
: m_maxConcurrentProcesses(maxConcurrentProcesses)
, m_stdOutRouting(stdOutRouting)
, m_stdErrRouting(stdErrRouting)
, m_jobTimeout(jobTimeout)
, m_runnerTimeout(runnerTimeout)
{
}
template<typename JobT>
AZStd::vector<JobT> JobRunner<JobT>::Execute(
const AZStd::vector<typename JobT::Info>& jobInfos,
JobCallback<JobT> jobCallback,
PayloadMapProducer<JobT> payloadMapProducer)
{
AZStd::vector<ProcessInfo> processes;
AZStd::unordered_map<JobT::Info::IdType, AZStd::pair<JobMeta, const typename JobT::Info*>> metas;
AZStd::vector<JobT> jobs;
jobs.reserve(jobInfos.size());
processes.reserve(jobInfos.size());
// Transform the job infos into the underlying process infos required for each job
for (size_t jobIndex = 0; jobIndex < jobInfos.size(); jobIndex++)
{
const auto* jobInfo = &jobInfos[jobIndex];
const auto jobId = jobInfo->GetId().m_value;
metas.emplace(jobId, AZStd::pair<JobMeta, const typename JobT::Info*>{JobMeta{}, jobInfo});
processes.emplace_back(jobId, m_stdOutRouting, m_stdErrRouting, jobInfo->GetArgs());
}
// Wrapper around low-level process launch callback to gather job meta-data and present a simplified callback interface to the client
const auto processLaunchCallback = [&jobCallback, &jobInfos, &metas](
TestImpact::ProcessId pid,
TestImpact::LaunchResult launchResult,
AZStd::chrono::high_resolution_clock::time_point createTime)
{
auto& [meta, jobInfo] = metas.at(pid);
if (launchResult == LaunchResult::Failure)
{
meta.m_result = JobResult::FailedToExecute;
return jobCallback(*jobInfo, meta, {});
}
else
{
meta.m_startTime = createTime;
return CallbackResult::Continue;
}
};
// Wrapper around low-level process exit callback to gather job meta-data and present a simplified callback interface to the client
const auto processExitCallback = [&jobCallback, &jobInfos, &metas](
TestImpact::ProcessId pid,
TestImpact::ExitCondition exitCondition,
TestImpact::ReturnCode returnCode,
TestImpact::StdContent&& std,
AZStd::chrono::high_resolution_clock::time_point exitTime)
{
auto& [meta, jobInfo] = metas.at(pid);
meta.m_returnCode = returnCode;
meta.m_duration = AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(exitTime - *meta.m_startTime);
if (exitCondition == ExitCondition::Gracefull && returnCode == 0)
{
meta.m_result = JobResult::ExecutedWithSuccess;
}
else if (exitCondition == ExitCondition::Terminated || exitCondition == ExitCondition::Timeout)
{
meta.m_result = JobResult::Terminated;
}
else
{
meta.m_result = JobResult::ExecutedWithFailure;
}
return jobCallback(*jobInfo, meta, AZStd::move(std));
};
// Schedule all jobs for execution
ProcessScheduler scheduler(
processes,
processLaunchCallback,
processExitCallback,
m_maxConcurrentProcesses,
m_jobTimeout,
m_runnerTimeout
);
// Hand off the jobs to the client for payload generation
auto payloadMap = payloadMapProducer(metas);
// Unpack the payload map produced by the client into a vector of jobs containing the job data and payload for each job
for (const auto& jobInfo : jobInfos)
{
const auto jobId = jobInfo.GetId().m_value;
jobs.emplace_back(JobT(jobInfo, AZStd::move(metas.at(jobId).first), AZStd::move(payloadMap[jobId])));
}
return jobs;
}
} // namespace TestImpact

@ -0,0 +1,270 @@
/*
* 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 <Process/Scheduler/TestImpactProcessScheduler.h>
#include <Process/TestImpactProcess.h>
#include <Process/TestImpactProcessException.h>
#include <Process/TestImpactProcessInfo.h>
#include <Process/TestImpactProcessLauncher.h>
namespace TestImpact
{
struct ProcessScheduler::ProcessInFlight
{
AZStd::unique_ptr<Process> m_process;
AZStd::optional<AZStd::chrono::high_resolution_clock::time_point> m_startTime;
AZStd::string m_stdOutput;
AZStd::string m_stdError;
};
ProcessScheduler::ProcessScheduler(
const AZStd::vector<ProcessInfo>& processes,
const ProcessLaunchCallback& processLaunchCallback,
const ProcessExitCallback& processExitCallback,
size_t maxConcurrentProcesses,
AZStd::optional<AZStd::chrono::milliseconds> processTimeout,
AZStd::optional<AZStd::chrono::milliseconds> scheduleTimeout)
: m_processCreateCallback(processLaunchCallback)
, m_processExitCallback(processExitCallback)
, m_processTimeout(processTimeout)
, m_scheduleTimeout(scheduleTimeout)
, m_startTime(AZStd::chrono::high_resolution_clock::now())
{
AZ_TestImpact_Eval(maxConcurrentProcesses != 0, ProcessException, "Max Number of concurrent processes in flight cannot be 0");
AZ_TestImpact_Eval(!processes.empty(), ProcessException, "Number of processes to launch cannot be 0");
AZ_TestImpact_Eval(
!m_processTimeout.has_value() || m_processTimeout->count() > 0, ProcessException,
"Process timeout must be empty or non-zero value");
AZ_TestImpact_Eval(
!m_scheduleTimeout.has_value() || m_scheduleTimeout->count() > 0, ProcessException,
"Scheduler timeout must be empty or non-zero value");
const size_t numConcurrentProcesses = AZStd::min(processes.size(), maxConcurrentProcesses);
m_processPool.resize(numConcurrentProcesses);
for (const auto& process : processes)
{
m_processQueue.emplace(process);
}
for (auto& process : m_processPool)
{
if (PopAndLaunch(process) == CallbackResult::Abort)
{
TerminateAllProcesses(ExitCondition::Terminated);
return;
}
}
MonitorProcesses();
}
ProcessScheduler::~ProcessScheduler()
{
TerminateAllProcesses(ExitCondition::Terminated);
}
void ProcessScheduler::MonitorProcesses()
{
while (true)
{
// Check to see whether or not the scheduling has exceeded its specified runtime
if (m_scheduleTimeout.has_value())
{
const auto shedulerRunTime = AZStd::chrono::milliseconds(AZStd::chrono::high_resolution_clock::now() - m_startTime);
if (shedulerRunTime > m_scheduleTimeout)
{
// Runtime exceeded, terminate all proccesses and schedule no further
TerminateAllProcesses(ExitCondition::Timeout);
return;
}
}
// Flag to determine whether or not there are currently any processes in-flight
bool processesInFlight = false;
// Loop round the process pool and visit round robin queued up processes for launch
for (auto& processInFlight : m_processPool)
{
if (processInFlight.m_process)
{
// Process is alive (note: not necessarilly currently running)
AccumulateProcessStdContent(processInFlight);
const ProcessId processId = processInFlight.m_process->GetProcessInfo().GetId();
if (!processInFlight.m_process->IsRunning())
{
// Process has exited of its own accord
const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value();
processInFlight.m_process.reset();
const auto exitTime = AZStd::chrono::high_resolution_clock::now();
// Inform the client that the processes has exited
if (CallbackResult::Abort == m_processExitCallback(
processId,
ExitCondition::Gracefull,
returnCode,
ConsumeProcessStdContent(processInFlight),
exitTime))
{
// Client chose to abort the scheduler
TerminateAllProcesses(ExitCondition::Terminated);
return;
}
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)
{
// Client chose to abort the scheduler
TerminateAllProcesses(ExitCondition::Terminated);
return;
}
else
{
// We know from the above PopAndLaunch there is at least one process in-flight this iteration
processesInFlight = true;
}
}
}
else
{
// Process is still in-flight
const auto exitTime = AZStd::chrono::high_resolution_clock::now();
const auto runTime = AZStd::chrono::milliseconds(exitTime - processInFlight.m_startTime.value());
// Check to see whether or not the processes has exceeded its specified flight time
if (m_processTimeout.has_value() && runTime > m_processTimeout)
{
processInFlight.m_process->Terminate(ProcessTimeoutErrorCode);
const ReturnCode returnCode = processInFlight.m_process->GetReturnCode().value();
processInFlight.m_process.reset();
if (CallbackResult::Abort == m_processExitCallback(
processId,
ExitCondition::Timeout,
returnCode,
ConsumeProcessStdContent(processInFlight),
exitTime))
{
// Flight time exceeded, terminate this process
TerminateAllProcesses(ExitCondition::Terminated);
return;
}
}
// We know that at least this process is in-flight this iteration
processesInFlight = true;
}
}
else
{
// Queue is empty, no more processes to launch
if (!m_processQueue.empty())
{
if (PopAndLaunch(processInFlight) == CallbackResult::Abort)
{
// Client chose to abort the scheduler
TerminateAllProcesses(ExitCondition::Terminated);
return;
}
else
{
// We know from the above PopAndLaunch there is at least one process in-flight this iteration
processesInFlight = true;
}
}
}
}
if (!processesInFlight)
{
break;
}
}
}
CallbackResult ProcessScheduler::PopAndLaunch(ProcessInFlight& processInFlight)
{
auto processInfo = m_processQueue.front();
m_processQueue.pop();
const auto createTime = AZStd::chrono::high_resolution_clock::now();
LaunchResult createResult = LaunchResult::Success;
try
{
processInFlight.m_process = LaunchProcess(AZStd::move(processInfo));
processInFlight.m_startTime = createTime;
}
catch (ProcessException& e)
{
AZ_Warning("ProcessScheduler", false, e.what());
createResult = LaunchResult::Failure;
}
return m_processCreateCallback(processInfo.GetId(), createResult, createTime);
}
void ProcessScheduler::AccumulateProcessStdContent(ProcessInFlight& processInFlight)
{
// Accumulate the stdout/stderr so we don't deadlock with the process waiting for the pipe to empty before finishing
processInFlight.m_stdOutput += processInFlight.m_process->ConsumeStdOut().value_or("");
processInFlight.m_stdError += processInFlight.m_process->ConsumeStdErr().value_or("");
}
StdContent ProcessScheduler::ConsumeProcessStdContent(ProcessInFlight& processInFlight)
{
return
{
!processInFlight.m_stdOutput.empty()
? AZStd::optional<AZStd::string>{AZStd::move(processInFlight.m_stdOutput)}
: AZStd::nullopt,
!processInFlight.m_stdError.empty()
? AZStd::optional<AZStd::string>{AZStd::move(processInFlight.m_stdError)}
: AZStd::nullopt
};
}
void ProcessScheduler::TerminateAllProcesses(ExitCondition exitStatus)
{
bool isCallingBackToClient = true;
const ReturnCode returnCode = static_cast<ReturnCode>(exitStatus);
for (auto& processInFlight : m_processPool)
{
if (processInFlight.m_process)
{
processInFlight.m_process->Terminate(ProcessTerminateErrorCode);
AccumulateProcessStdContent(processInFlight);
const ProcessId processId = processInFlight.m_process->GetProcessInfo().GetId();
if (isCallingBackToClient)
{
const auto exitTime = AZStd::chrono::high_resolution_clock::now();
if (CallbackResult::Abort == m_processExitCallback(
processInFlight.m_process->GetProcessInfo().GetId(),
exitStatus,
returnCode,
ConsumeProcessStdContent(processInFlight),
exitTime))
{
// Client chose to abort the scheduler, do not make any further callbacks
isCallingBackToClient = false;
}
}
processInFlight.m_process.reset();
}
}
}
} // namespace TestImpact

@ -0,0 +1,110 @@
/*
* 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 <Process/TestImpactProcessInfo.h>
#include <TestImpactFramework/TestImpactCallback.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/containers/queue.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/functional.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Result of the attempt to launch a process.
enum class LaunchResult : bool
{
Failure,
Success
};
//! The condition under which the processes exited.
//! @note For convinience, the terminate and timeout condition values are set to the corresponding return value sent to the
//! process.
enum class ExitCondition : ReturnCode
{
Gracefull, //!< Process has exited of its own accord.
Terminated = ProcessTerminateErrorCode, //!< The process was terminated by the client/scheduler.
Timeout = ProcessTimeoutErrorCode //!< The process was terminated by the scheduler due to exceeding runtime limit.
};
//! 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<CallbackResult(
ProcessId processId,
LaunchResult launchResult,
AZStd::chrono::high_resolution_clock::time_point createTime)>;
//! Callback for process exit of successfully launched process.
//! @param processId The id of the process that attempted to launch.
//! @param exitStatus The circumstances upon which the processes exited.
//! @param returnCode The return code of the exited process.
//! @param std The standard output and standard error of the process.
//! @param createTime The timestamp of the process exit.
using ProcessExitCallback =
AZStd::function<CallbackResult(
ProcessId processId,
ExitCondition exitStatus,
ReturnCode returnCode,
StdContent&& std,
AZStd::chrono::high_resolution_clock::time_point exitTime)>;
//! Schedules a batch of processes for launch using a round robin approach to distribute the in-flight processes over
//! the specified number of concurrent process slots.
class ProcessScheduler
{
public:
//! Constructs the scheduler with the specified batch of processes.
//! @param processes The batch of processes to schedule.
//! @param processLaunchCallback The process launch callback function.
//! @param processExitCallback The process exit callback function.
//! @param maxConcurrentProcesses The maximum number of concurrent processes in-flight.
//! @param processTimeout The maximum duration a process may be in-flight for before being forcefully terminated.
//! @param scheduleTimeout The maximum duration the scheduler may run before forcefully terminating all in-flight processes.
//! processes and abandoning any queued processes.
ProcessScheduler(
const AZStd::vector<ProcessInfo>& processes,
const ProcessLaunchCallback& processLaunchCallback,
const ProcessExitCallback& processExitCallback,
size_t maxConcurrentProcesses,
AZStd::optional<AZStd::chrono::milliseconds> processTimeout,
AZStd::optional<AZStd::chrono::milliseconds> scheduleTimeout);
~ProcessScheduler();
private:
struct ProcessInFlight;
void MonitorProcesses();
CallbackResult PopAndLaunch(ProcessInFlight& processInFlight);
void TerminateAllProcesses(ExitCondition exitStatus);
StdContent ConsumeProcessStdContent(ProcessInFlight& processInFlight);
void AccumulateProcessStdContent(ProcessInFlight& processInFlight);
const ProcessLaunchCallback m_processCreateCallback;
const ProcessExitCallback m_processExitCallback;
const AZStd::optional<AZStd::chrono::milliseconds> m_processTimeout;
const AZStd::optional<AZStd::chrono::milliseconds> m_scheduleTimeout;
const AZStd::chrono::high_resolution_clock::time_point m_startTime;
AZStd::vector<ProcessInFlight> m_processPool;
AZStd::queue<ProcessInfo> m_processQueue;
};
} // namespace TestImpact

@ -0,0 +1,32 @@
/*
* 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 <Process/TestImpactProcess.h>
#include <Process/TestImpactProcessInfo.h>
namespace TestImpact
{
Process::Process(const ProcessInfo& processInfo)
: m_processInfo(processInfo)
{
}
const ProcessInfo& Process::GetProcessInfo() const
{
return m_processInfo;
}
AZStd::optional<ReturnCode> Process::GetReturnCode() const
{
return m_returnCode;
}
} // namespace TestImpact

@ -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 <Process/TestImpactProcessInfo.h>
#include <AzCore/std/optional.h>
namespace TestImpact
{
//! Abstraction of platform-specific process.
class Process
{
public:
explicit Process(const ProcessInfo& processInfo);
virtual ~Process() = default;
//! Terminates the process with the specified return code.
virtual void Terminate(ReturnCode returnCode) = 0;
//! Block the calling thread until the process exits.
virtual void BlockUntilExit() = 0;
//! Returns whether or not the process is still running.
virtual bool IsRunning() const = 0;
//! Returns the process info associated with this process.
const ProcessInfo& GetProcessInfo() const;
//! Returns the return code of the exited process.
//! Will be empty if the process is still running or was not successfully launched.
AZStd::optional<ReturnCode> GetReturnCode() const;
//! Flushes the internal buffer and returns the process's buffered standard output.
//! Subsequent calls will keep returning data so long as the process is producing output.
//! Will return nullopt if no output routing or no output produced.
virtual AZStd::optional<AZStd::string> ConsumeStdOut() = 0;
//! Flushes the internal buffer and returns the process's buffered standard error.
//! Subsequent calls will keep returning data so long as the process is producing errors.
//! Will return nullopt if no error routing or no errors produced.
virtual AZStd::optional<AZStd::string> ConsumeStdErr() = 0;
protected:
//! The information used to launch the process.
ProcessInfo m_processInfo;
//! The return code of a successfully launched process (otherwise is empty)
AZStd::optional<ReturnCode> m_returnCode;
};
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 processes and process-related operations.
class ProcessException
: public Exception
{
public:
using Exception::Exception;
};
}

@ -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.
*
*/
#include <Process/TestImpactProcessException.h>
#include <Process/TestImpactProcessInfo.h>
namespace TestImpact
{
ProcessInfo::ProcessInfo(ProcessId id, const AZ::IO::Path& processPath, const AZStd::string& startupArgs)
: m_id(id)
, m_parentHasStdOutput(false)
, m_parentHasStdErr(false)
, m_processPath(processPath)
, m_startupArgs(startupArgs)
{
AZ_TestImpact_Eval(processPath.String().length() > 0, ProcessException, "Process path cannot be empty");
}
ProcessInfo::ProcessInfo(
ProcessId id,
StdOutputRouting stdOut,
StdErrorRouting stdErr,
const AZ::IO::Path& processPath,
const AZStd::string& startupArgs)
: m_id(id)
, m_processPath(processPath)
, m_startupArgs(startupArgs)
, m_parentHasStdOutput(stdOut == StdOutputRouting::ToParent ? true : false)
, m_parentHasStdErr(stdErr == StdErrorRouting::ToParent ? true : false)
{
AZ_TestImpact_Eval(processPath.String().length() > 0, ProcessException, "Process path cannot be empty");
}
ProcessId ProcessInfo::GetId() const
{
return m_id;
}
const AZ::IO::Path& ProcessInfo::GetProcessPath() const
{
return m_processPath;
}
const AZStd::string& ProcessInfo::GetStartupArgs() const
{
return m_startupArgs;
}
bool ProcessInfo::ParentHasStdOutput() const
{
return m_parentHasStdOutput;
}
bool ProcessInfo::ParentHasStdError() const
{
return m_parentHasStdErr;
}
} // namespace TestImpact

@ -0,0 +1,93 @@
/*
* 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 <AzCore/IO/Path/Path.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Identifier to distinguish between processes.
using ProcessId = size_t;
//! Return code of successfully launched process.
using ReturnCode = int;
//! Error code for processes that are forcefully terminated whilst in-flight by the client.
inline constexpr const ReturnCode ProcessTerminateErrorCode = 0xF10BAD;
//! Error code for processes that are forcefully terminated whilst in-flight by the scheduler due to timing out.
inline constexpr const ReturnCode ProcessTimeoutErrorCode = 0xBADF10;
//! Specifier for how the process's standard out willt be routed
enum class StdOutputRouting
{
ToParent,
None
};
enum class StdErrorRouting
{
ToParent,
None
};
//! Container for process standard output and standard error.
struct StdContent
{
AZStd::optional<AZStd::string> m_out;
AZStd::optional<AZStd::string> m_err;
};
//! Information about a process the arguments used to launch it.
class ProcessInfo
{
public:
//! Provides the information required to launch a process.
//! @param processId Client-supplied id to diffrentiate between processes.
//! @param stdOut Routing of process standard output.
//! @param stdErr Routing of process standard error.
//! @param processPath Path to executable binary to launch.
//! @param startupArgs Arguments to launch the process with.
ProcessInfo(
ProcessId processId,
StdOutputRouting stdOut,
StdErrorRouting stdErr,
const AZ::IO::Path& processPath,
const AZStd::string& startupArgs = "");
ProcessInfo(ProcessId processId, const AZ::IO::Path& processPath, const AZStd::string& startupArgs = "");
//! Returns the identifier of this process.
ProcessId GetId() const;
//! Returns whether or not stdoutput is routed to the parent process.
bool ParentHasStdOutput() const;
//! Returns whether or not stderror is routed to the parent process.
bool ParentHasStdError() const;
// Returns the path to the process binary.
const AZ::IO::Path& GetProcessPath() const;
//! Returns the command line arguments used to launch the process.
const AZStd::string& GetStartupArgs() const;
private:
const ProcessId m_id;
const bool m_parentHasStdOutput;
const bool m_parentHasStdErr;
const AZ::IO::Path m_processPath;
const AZStd::string m_startupArgs;
};
} // namespace TestImpact

@ -0,0 +1,27 @@
/*
* 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 <Process/TestImpactProcessException.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
namespace TestImpact
{
class Process;
class ProcessInfo;
//! Attempts to launch a process with the provided command line arguments.
//! @param processInfo The path and command line arguments to launch the process with.
AZStd::unique_ptr<Process> LaunchProcess(const ProcessInfo& processInfo);
} // namespace TestImpact

@ -0,0 +1,48 @@
/*
* 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 "TestImpactBuildTarget.h"
namespace TestImpact
{
BuildTarget::BuildTarget(BuildTargetDescriptor&& descriptor, TargetType type)
: m_buildMetaData(AZStd::move(descriptor.m_buildMetaData))
, m_sources(AZStd::move(descriptor.m_sources))
, m_type(type)
{
}
const AZStd::string& BuildTarget::GetName() const
{
return m_buildMetaData.m_name;
}
const AZStd::string& BuildTarget::GetOutputName() const
{
return m_buildMetaData.m_outputName;
}
const AZ::IO::Path& BuildTarget::GetPath() const
{
return m_buildMetaData.m_path;
}
const TargetSources& BuildTarget::GetSources() const
{
return m_sources;
}
TargetType BuildTarget::GetType() const
{
return m_type;
}
} // namespace TestImpact

@ -0,0 +1,55 @@
/*
* 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 <Artifact/Static/TestImpactBuildTargetDescriptor.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Type id for querying specialized derived target types from base pointer/reference.
enum class TargetType : bool
{
Production, //!< Production build target.
Test //!< Test build target.
};
//! Representation of a generic build target in the repository.
class BuildTarget
{
public:
BuildTarget(BuildTargetDescriptor&& descriptor, TargetType type);
virtual ~BuildTarget() = default;
//! Returns the build target name.
const AZStd::string& GetName() const;
//! Returns the build target's compiled binary name.
const AZStd::string& GetOutputName() const;
//! Returns the path in the source tree to the build target location.
const AZ::IO::Path& GetPath() const;
//! Returns the build target's sources.
const TargetSources& GetSources() const;
//! Returns the build target type.
TargetType GetType() const;
private:
BuildMetaData m_buildMetaData;
TargetSources m_sources;
TargetType m_type;
};
} // namespace TestImpact

@ -0,0 +1,125 @@
/*
* 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 <Target/TestImpactTargetException.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/sort.h>
#include <AzCore/std/string/string.h>
#include <algorithm>
namespace TestImpact
{
//! Container for unique set of sorted build target types.
//! @tparam Target The specialized build target type.
template<typename Target>
class BuildTargetList
{
public:
using TargetType = Target;
BuildTargetList(AZStd::vector<typename Target::Descriptor>&& descriptors);
//! Returns the targets in the collection.
const AZStd::vector<Target>& GetTargets() const;
//! Returns the target with the specified name.
const Target* GetTarget(const AZStd::string& name) const;
//! Returns the target with the specified name or throws if target not found.
const Target* GetTargetOrThrow(const AZStd::string& name) const;
// Returns the number of targets in the list.
size_t GetNumTargets() const;
private:
AZStd::vector<Target> m_targets;
};
template<typename Target>
BuildTargetList<Target>::BuildTargetList(AZStd::vector<typename Target::Descriptor>&& descriptors)
{
AZ_TestImpact_Eval(!descriptors.empty(), TargetException, "Target list is empty");
AZStd::sort(
descriptors.begin(), descriptors.end(), [](const typename Target::Descriptor& lhs, const typename Target::Descriptor& rhs)
{
return lhs.m_buildMetaData.m_name < rhs.m_buildMetaData.m_name;
});
const auto duplicateElement = AZStd::adjacent_find(
descriptors.begin(), descriptors.end(), [](const typename Target::Descriptor& lhs, const typename Target::Descriptor& rhs)
{
return lhs.m_buildMetaData.m_name == rhs.m_buildMetaData.m_name;
});
AZ_TestImpact_Eval(duplicateElement == descriptors.end(), TargetException, "Target list contains duplicate targets");
m_targets.reserve(descriptors.size());
for (auto&& descriptor : descriptors)
{
m_targets.emplace_back(Target(AZStd::move(descriptor)));
}
}
template<typename Target>
const AZStd::vector<Target>& BuildTargetList<Target>::GetTargets() const
{
return m_targets;
}
template<typename Target>
size_t BuildTargetList<Target>::GetNumTargets() const
{
return m_targets.size();
}
template<typename Target>
const Target* BuildTargetList<Target>::GetTarget(const AZStd::string& name) const
{
struct TargetComparator
{
bool operator()(const Target& target, const AZStd::string& name) const
{
return target.GetName() < name;
}
bool operator()(const AZStd::string& name, const Target& target) const
{
return name < target.GetName();
}
};
const auto targetRange = std::equal_range(m_targets.begin(), m_targets.end(), name, TargetComparator{});
if (targetRange.first != targetRange.second)
{
return targetRange.first;
}
else
{
return nullptr;
}
}
template<typename Target>
const Target* BuildTargetList<Target>::GetTargetOrThrow(const AZStd::string& name) const
{
const Target* target = GetTarget(name);
AZ_TestImpact_Eval(target, TargetException, AZStd::string::format("Couldn't find target %s", name.c_str()).c_str());
return target;
}
} // namespace TestImpact

@ -0,0 +1,21 @@
/*
* 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 "TestImpactProductionTarget.h"
namespace TestImpact
{
ProductionTarget::ProductionTarget(Descriptor&& descriptor)
: BuildTarget(AZStd::move(descriptor), TargetType::Production)
{
}
} // namespace TestImpact

@ -0,0 +1,31 @@
/*
* 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 <Artifact/Static/TestImpactProductionTargetDescriptor.h>
#include <Target/TestImpactBuildTarget.h>
namespace TestImpact
{
//! Build target specialization for production targets (build targets containing production code and no test code).
class ProductionTarget
: public BuildTarget
{
public:
using Descriptor = ProductionTargetDescriptor;
ProductionTarget(Descriptor&& descriptor);
};
template<typename Target>
inline constexpr bool IsProductionTarget = AZStd::is_same_v<ProductionTarget, AZStd::remove_const_t<AZStd::remove_pointer_t<AZStd::decay_t<Target>>>>;
} // namespace TestImpact

@ -0,0 +1,22 @@
/*
* 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 <Target/TestImpactBuildTargetList.h>
#include <Target/TestImpactProductionTarget.h>
namespace TestImpact
{
//! Container for set of sorted production targets containing no duplicates.
using ProductionTargetList = BuildTargetList<ProductionTarget>;
} // namespace TestImpact

@ -0,0 +1,25 @@
/*
* 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 target and target-related operations.
class TargetException : public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact

@ -0,0 +1,32 @@
/*
* 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 "TestImpactTestTarget.h"
namespace TestImpact
{
TestTarget::TestTarget(Descriptor&& descriptor)
: BuildTarget(AZStd::move(descriptor), TargetType::Test)
, m_testMetaData(AZStd::move(descriptor.m_testMetaData))
{
}
const AZStd::string& TestTarget::GetSuite() const
{
return m_testMetaData.m_suite;
}
LaunchMethod TestTarget::GetLaunchMethod() const
{
return m_testMetaData.m_launchMethod;
}
} // namespace TestImpact

@ -0,0 +1,41 @@
/*
* 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 <Artifact/Static/TestImpactTestTargetDescriptor.h>
#include <Target/TestImpactBuildTarget.h>
namespace TestImpact
{
//! Build target specialization for test targets (build targets containing test code and no production code).
class TestTarget
: public BuildTarget
{
public:
using Descriptor = TestTargetDescriptor;
TestTarget(Descriptor&& descriptor);
//! Returns the test target suite.
const AZStd::string& GetSuite() const;
//! Returns the test target launch method.
LaunchMethod GetLaunchMethod() const;
private:
const TestTargetMeta m_testMetaData;
};
template<typename Target>
inline constexpr bool IsTestTarget = AZStd::is_same_v<TestTarget, AZStd::remove_const_t<AZStd::remove_pointer_t<AZStd::decay_t<Target>>>>;
} // namespace TestImpact

@ -0,0 +1,22 @@
/*
* 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 <Target/TestImpactBuildTargetList.h>
#include <Target/TestImpactTestTarget.h>
namespace TestImpact
{
//! Container for set of sorted test targets containing no duplicates.
using TestTargetList = BuildTargetList<TestTarget>;
} // namespace TestImpact

@ -0,0 +1,22 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestEnumerationSuite.h>
#include <Test/TestImpactTestSuiteContainer.h>
namespace TestImpact
{
//! Representation of a given test target's enumerated tests.
using TestEnumeration = TestSuiteContainer<TestEnumerationSuite>;
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 enumerations and test enumeration related operations.
class TestEnumerationException
: public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact

@ -0,0 +1,100 @@
/*
* 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 <Test/Enumeration/TestImpactTestEnumerationException.h>
#include <Test/Enumeration/TestImpactTestEnumerationSerializer.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/stringbuffer.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent JSON node and attribute names
constexpr const char* Keys[] =
{
"suites",
"name",
"enabled",
"tests"
};
enum
{
SuitesKey,
NameKey,
EnabledKey,
TestsKey
};
} // namespace
AZStd::string SerializeTestEnumeration(const TestEnumeration& testEnum)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
writer.StartObject();
writer.Key(Keys[SuitesKey]);
writer.StartArray();
for (const auto& suite : testEnum.GetTestSuites())
{
writer.StartObject();
writer.Key(Keys[NameKey]);
writer.String(suite.m_name.c_str());
writer.Key(Keys[EnabledKey]);
writer.Bool(suite.m_enabled);
writer.Key(Keys[TestsKey]);
writer.StartArray();
for (const auto& test : suite.m_tests)
{
writer.StartObject();
writer.Key(Keys[NameKey]);
writer.String(test.m_name.c_str());
writer.Key(Keys[EnabledKey]);
writer.Bool(test.m_enabled);
writer.EndObject();
}
writer.EndArray();
writer.EndObject();
}
writer.EndArray();
writer.EndObject();
return stringBuffer.GetString();
}
TestEnumeration DeserializeTestEnumeration(const AZStd::string& testEnumString)
{
AZStd::vector<TestEnumerationSuite> testSuites;
rapidjson::Document doc;
if (doc.Parse<0>(testEnumString.c_str()).HasParseError())
{
throw TestEnumerationException("Could not parse enumeration data");
}
for (const auto& suite : doc[Keys[SuitesKey]].GetArray())
{
testSuites.emplace_back(TestEnumerationSuite{suite[Keys[NameKey]].GetString(), suite[Keys[EnabledKey]].GetBool(), {}});
for (const auto& test : suite[Keys[TestsKey]].GetArray())
{
testSuites.back().m_tests.emplace_back(
TestEnumerationCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool()});
}
}
return TestEnumeration(std::move(testSuites));
}
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Test/Enumeration/TestImpactTestEnumeration.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Serializes the specified test enumeration to JSON format.
AZStd::string SerializeTestEnumeration(const TestEnumeration& testEnumeration);
//! Deserializes a test enumeration from the specified test enumeration data in JSON format.
TestEnumeration DeserializeTestEnumeration(const AZStd::string& testEnumerationString);
} // namespace TestImpact

@ -0,0 +1,190 @@
/*
* 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 <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 <AzCore/IO/SystemFile.h>
namespace TestImpact
{
namespace
{
void WriteCacheFile(
const TestEnumeration& enumeration, const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
{
const AZStd::string cacheJSON = SerializeTestEnumeration(enumeration);
const AZStd::vector<char> cacheBytes(cacheJSON.begin(), cacheJSON.end());
AZ::IO::SystemFile cacheFile;
if (!cacheFile.Open(
path.c_str(),
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_TestImpact_Eval(
!IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException,
"Couldn't open cache file for writing");
return;
}
if (cacheFile.Write(cacheBytes.data(), cacheBytes.size()) == 0)
{
AZ_TestImpact_Eval(
!IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheWriteFailure), TestEnumerationException,
"Couldn't write cache file data");
return;
}
}
AZStd::optional<TestEnumeration> ReadCacheFile(const AZ::IO::Path& path, Bitwise::CacheExceptionPolicy cacheExceptionPolicy)
{
AZ::IO::SystemFile cacheFile;
AZStd::string cacheJSON;
if (!cacheFile.Open(path.c_str(), AZ::IO::SystemFile::SF_OPEN_READ_ONLY))
{
AZ_TestImpact_Eval(
!IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheNotExist), TestEnumerationException,
"Couldn't locate cache file");
return AZStd::nullopt;
}
const AZ::IO::SystemFile::SizeType length = cacheFile.Length();
if (length == 0)
{
AZ_TestImpact_Eval(
!IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException,
"Cache file is empty");
return AZStd::nullopt;
}
cacheFile.Seek(0, AZ::IO::SystemFile::SF_SEEK_BEGIN);
cacheJSON.resize(length);
if (cacheFile.Read(length, cacheJSON.data()) != length)
{
AZ_TestImpact_Eval(
!IsFlagSet(cacheExceptionPolicy, Bitwise::CacheExceptionPolicy::OnCacheReadFailure), TestEnumerationException,
"Couldn't read cache file");
return AZStd::nullopt;
}
return DeserializeTestEnumeration(cacheJSON);
}
} // namespace
TestEnumeration ParseTestEnumerationFile(const AZ::IO::Path& enumerationFile)
{
return TestEnumeration(GTest::TestEnumerationSuitesFactory(ReadFileContents<TestEnumerationException>(enumerationFile)));
}
TestEnumerationJobData::TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional<Cache>&& cache)
: m_enumerationArtifact(enumerationArtifact)
, m_cache(AZStd::move(cache))
{
}
const AZ::IO::Path& TestEnumerationJobData::GetEnumerationArtifactPath() const
{
return m_enumerationArtifact;
}
const AZStd::optional<TestEnumerationJobData::Cache>& TestEnumerationJobData::GetCache() const
{
return m_cache;
}
TestEnumerator::TestEnumerator(
AZStd::optional<ClientJobCallback> clientCallback,
size_t maxConcurrentEnumerations,
AZStd::optional<AZStd::chrono::milliseconds> enumerationTimeout,
AZStd::optional<AZStd::chrono::milliseconds> enumeratorTimeout)
: JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentEnumerations, enumerationTimeout, enumeratorTimeout)
{
}
AZStd::vector<TestEnumerator::Job> TestEnumerator::Enumerate(
const AZStd::vector<JobInfo>& jobInfos,
CacheExceptionPolicy cacheExceptionPolicy,
JobExceptionPolicy jobExceptionPolicy)
{
AZStd::vector<Job> cachedJobs;
AZStd::vector<JobInfo> jobQueue;
for (const auto& jobInfo : jobInfos)
{
// If this job has a cache read policy attempt to read the cache
if (jobInfo.GetCache().has_value() && jobInfo.GetCache()->m_policy == JobData::CachePolicy::Read)
{
JobMeta meta;
auto enumeration = ReadCacheFile(jobInfo.GetCache()->m_file, cacheExceptionPolicy);
// Even though cached jobs don't get executed we still give the client the opportunity to handle the job state
// change in order to make the caching process transparent to the client
if (enumeration.has_value())
{
if (m_clientJobCallback.has_value())
{
(*m_clientJobCallback)(jobInfo, meta);
}
// Cache read successfully, this job will not be placed in the job queue
cachedJobs.emplace_back(Job(jobInfo, AZStd::move(meta), AZStd::move(enumeration)));
}
else
{
// The cache read failed and exception policy for cache read failures is not to throw so instead place this job in the
// job queue
jobQueue.emplace_back(jobInfo);
}
}
else
{
// This job has no cache read policy so place in job queue
jobQueue.emplace_back(jobInfo);
}
}
const auto payloadGenerator = [this, cacheExceptionPolicy](const JobDataMap& jobDataMap)
{
PayloadMap<Job> enumerations;
for (const auto& [jobId, jobData] : jobDataMap)
{
const auto& [meta, jobInfo] = jobData;
if (meta.m_result == JobResult::ExecutedWithSuccess)
{
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)
{
WriteCacheFile(enumeration.value(), jobInfo->GetCache()->m_file, cacheExceptionPolicy);
}
}
}
return enumerations;
};
// Generate the enumeration results for the jobs that weren't cached
auto jobs = ExecuteJobs(jobQueue, payloadGenerator, jobExceptionPolicy);
// We need to add the cached jobs to the completed job list even though they technically weren't executed
for (auto&& job : cachedJobs)
{
jobs.emplace_back(AZStd::move(job));
}
return jobs;
}
} // namespace TestImpact

@ -0,0 +1,94 @@
/*
* 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 <Artifact/TestImpactArtifactException.h>
#include <Test/Enumeration/TestImpactTestEnumeration.h>
#include <Test/Job/TestImpactTestJobRunner.h>
#include <AzCore/IO/Path/Path.h>
namespace TestImpact
{
//! Per-job data for test enumerations.
class TestEnumerationJobData
{
public:
//! Policy for how a test enumeration will be written/read from the a previous cache instead of enumerated from the test target.
enum class CachePolicy
{
Read, //!< Do read from a cache file but do not overwrite any existing cache file.
Write //!< Do not read from a cache file but instead overwrite any existing cache file.
};
//! Cache configuration for a given test enumeration command.
struct Cache
{
CachePolicy m_policy;
AZ::IO::Path m_file;
};
TestEnumerationJobData(const AZ::IO::Path& enumerationArtifact, AZStd::optional<Cache>&& cache);
//! Returns the path to the enumeration artifact produced by the test target.
const AZ::IO::Path& 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.
AZStd::optional<Cache> m_cache = AZStd::nullopt; //!< No caching takes place if cache is empty.
};
namespace Bitwise
{
//! Exception policy for test enumeration cache reads/writes.
enum class CacheExceptionPolicy
{
Never = 0, //! Never throw.
OnCacheNotExist = 1, //! Throw when a cache read policy was in place but a cache file for this job doesn't exist.
OnCacheReadFailure = 1 << 1, //! Throw when a cache read policy is in place but the cache file could not be read.
OnCacheWriteFailure = 1 << 2 //! Throw when a cache write policy is in place but the cache file could not be written.
};
} // namespace Bitwise
//! Enumerate a batch of test targets to determine the test suites and fixtures they contain, caching the results where applicable.
class TestEnumerator
: public TestJobRunner<TestEnumerationJobData, TestEnumeration>
{
using JobRunner = TestJobRunner<TestEnumerationJobData, TestEnumeration>;
public:
using CacheExceptionPolicy = Bitwise::CacheExceptionPolicy;
//! Constructs a test enumerator with the specified parameters common to all enumeration job runs of this enumerator.
//! @param clientCallback The optional client callback to be called whenever an enumeration job changes state.
//! @param maxConcurrentEnumerations The maximum number of enumerations to be in flight at any given time.
//! @param enumerationTimeout The maximum duration an enumeration may be in-flight for before being forcefully terminated.
//! @param enumeratorTimeout The maximum duration the enumerator may run before forcefully terminating all in-flight enumerations.
TestEnumerator(
AZStd::optional<ClientJobCallback> clientCallback,
size_t maxConcurrentEnumerations,
AZStd::optional<AZStd::chrono::milliseconds> enumerationTimeout,
AZStd::optional<AZStd::chrono::milliseconds> enumeratorTimeout);
//! Executes the specified test enumeration jobs according to the specified cache and job exception policies.
//! @param jobInfos The enumeration jobs to execute.
//! @param cacheExceptionPolicy The cache exception policy to be used for this run.
//! @param jobExceptionPolicy The enumeration job exception policy to be used for this run.
//! @return the test enumeration jobs with their associated test enumeration payloads.
AZStd::vector<Job> Enumerate(
const AZStd::vector<JobInfo>& jobInfos, CacheExceptionPolicy cacheExceptionPolicy, JobExceptionPolicy jobExceptionPolicy);
};
} // namespace TestImpact

@ -0,0 +1,36 @@
/*
* 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>
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

@ -0,0 +1,26 @@
/*
* 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

@ -0,0 +1,141 @@
/*
* 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/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>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
namespace Bitwise
{
//! Exception policy for test jobs (and derived jobs).
enum class TestJobExceptionPolicy
{
Never = 0, //!< Never throw.
OnFailedToExecute = 1, //!< Throw when a job fails to execute.
OnExecutedWithFailure = 1 << 1 //!< Throw when a job returns with an error code.
};
} // namespace Bitwise
//! Base class for test related job runners.
//! @tparam AdditionalInfo The data structure containing the information additional to the command arguments necessary to execute and
//! complete a job.
//! @tparam Payload The output produced by a job.
template<typename AdditionalInfo, typename Payload>
class TestJobRunner
{
public:
using JobData = AdditionalInfo;
using JobInfo = JobInfo<AdditionalInfo>;
using JobPayload = Payload;
using Job = Job<JobInfo, Payload>;
using ClientJobCallback = AZStd::function<void(const JobInfo& jobInfo, const JobMeta& meta)>;
using DerivedJobCallback = JobCallback<Job>;
using JobExceptionPolicy = Bitwise::TestJobExceptionPolicy;
using JobDataMap = JobDataMap<Job>;
//! Constructs the job runner with the specified parameters common to all job runs of this runner.
//! @param clientCallback The optional callback function provided by the client to be called upon job state change.
//! @param clientCallback The optional callback function provided by the derived job runner to be called upon job state change.
//! @param stdOutRouting The standard output routing from the underlying job processes to the derived runner.
//! @param stdErrorRouting The standard error routing from the underlying job processes to the derived runner.
//! @param maxConcurrentJobs The maximum number of jobs to be in flight at any given time.
//! @param jobTimeout The maximum duration a job may be in-flight for before being forcefully terminated (nullopt if no timeout).
//! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight jobs (nullopt if no
//! timeout).
TestJobRunner(
AZStd::optional<ClientJobCallback> clientCallback,
AZStd::optional<DerivedJobCallback> derivedJobCallback,
StdOutputRouting stdOutRouting,
StdErrorRouting stdErrRouting,
size_t maxConcurrentJobs,
AZStd::optional<AZStd::chrono::milliseconds> jobTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout);
protected:
//! Runs the specified jobs and returns the completed payloads produced by each job.
//! @param jobInfos The batch of jobs to execute.
//! @param payloadMapProducer The client callback for producing the payload map based on the completed job data.
//! @param jobExceptionPolicy The job execution policy for this job run.
AZStd::vector<Job> ExecuteJobs(
const AZStd::vector<JobInfo>& jobInfos,
PayloadMapProducer<Job> payloadMapProducer,
JobExceptionPolicy jobExceptionPolicy);
const AZStd::optional<ClientJobCallback> m_clientJobCallback;
private:
JobRunner<Job> m_jobRunner;
const AZStd::optional<DerivedJobCallback> m_derivedJobCallback;
};
template<typename Data, typename Payload>
TestJobRunner<Data, Payload>::TestJobRunner(
AZStd::optional<ClientJobCallback> clientCallback,
AZStd::optional<DerivedJobCallback> derivedJobCallback,
StdOutputRouting stdOutRouting,
StdErrorRouting stdErrRouting,
size_t maxConcurrentJobs,
AZStd::optional<AZStd::chrono::milliseconds> jobTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout)
: m_jobRunner(stdOutRouting, stdErrRouting, maxConcurrentJobs, jobTimeout, runnerTimeout)
, m_clientJobCallback(clientCallback)
, m_derivedJobCallback(derivedJobCallback)
{
}
template<typename Data, typename Payload>
AZStd::vector<typename TestJobRunner<Data, Payload>::Job> TestJobRunner<Data, Payload>::ExecuteJobs(
const AZStd::vector<JobInfo>& jobInfos,
PayloadMapProducer<Job> payloadMapProducer,
JobExceptionPolicy jobExceptionPolicy)
{
// Callback to handle job exception policies and client/derived callbacks
const auto jobCallback = [this, &jobExceptionPolicy](const JobInfo& jobInfo, const JobMeta& meta, StdContent&& std)
{
if (meta.m_result == JobResult::FailedToExecute && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnFailedToExecute))
{
throw TestJobException("Job failed to execute");
}
else if (meta.m_result == JobResult::ExecutedWithFailure && IsFlagSet(jobExceptionPolicy, JobExceptionPolicy::OnExecutedWithFailure))
{
throw TestJobException("Job executed with failure");
}
if (m_derivedJobCallback.has_value())
{
if (const auto result = (*m_derivedJobCallback)(jobInfo, meta, AZStd::move(std)); result != CallbackResult::Continue)
{
return result;
}
}
if (m_clientJobCallback.has_value())
{
(*m_clientJobCallback)(jobInfo, meta);
}
return CallbackResult::Continue;
};
return m_jobRunner.Execute(jobInfos, jobCallback, payloadMapProducer);
}
} // namespace TestImpact

@ -0,0 +1,89 @@
/*
* 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 <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 <AzCore/IO/SystemFile.h>
namespace TestImpact
{
InstrumentedTestRunJobData::InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact)
: TestRunJobData(resultsArtifact)
, m_coverageArtifact(coverageArtifact)
{
}
const AZ::IO::Path& InstrumentedTestRunJobData::GetCoverageArtifactPath() const
{
return m_coverageArtifact;
}
InstrumentedTestRunner::JobPayload ParseTestRunAndCoverageFiles(
const AZ::IO::Path& runFile,
const AZ::IO::Path& coverageFile,
AZStd::chrono::milliseconds duration,
InstrumentedTestRunner::CoverageExceptionPolicy coverageExceptionPolicy)
{
TestRun run(GTest::TestRunSuitesFactory(ReadFileContents<TestRunException>(runFile)), duration);
AZStd::vector<ModuleCoverage> moduleCoverages = Cobertura::ModuleCoveragesFactory(ReadFileContents<TestRunException>(coverageFile));
if (moduleCoverages.empty())
{
AZ_TestImpact_Eval(
!IsFlagSet(coverageExceptionPolicy, Bitwise::CoverageExceptionPolicy::OnEmptyCoverage), TestRunException,
"No coverage data generated");
}
TestCoverage coverage(AZStd::move(moduleCoverages));
return {AZStd::move(run), AZStd::move(coverage)};
}
InstrumentedTestRunner::InstrumentedTestRunner(
AZStd::optional<ClientJobCallback> clientCallback,
size_t maxConcurrentRuns,
AZStd::optional<AZStd::chrono::milliseconds> runTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout)
: JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout)
{
}
AZStd::vector<InstrumentedTestRunner::Job> InstrumentedTestRunner::RunInstrumentedTests(
const AZStd::vector<JobInfo>& jobInfos,
CoverageExceptionPolicy coverageExceptionPolicy,
JobExceptionPolicy jobExceptionPolicy)
{
const auto payloadGenerator = [this, coverageExceptionPolicy](const JobDataMap& jobDataMap)
{
PayloadMap<Job> runs;
for (const auto& [jobId, jobData] : jobDataMap)
{
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);
}
}
return runs;
};
return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy);
}
} // namespace TestImpact

@ -0,0 +1,71 @@
/*
* 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 <Test/Job/TestImpactTestJobRunner.h>
#include <Test/Run/TestImpactTestCoverage.h>
#include <Test/Run/TestImpactTestRun.h>
#include <Test/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
//! Per-job data for instrumented test runs.
class InstrumentedTestRunJobData : public TestRunJobData
{
public:
InstrumentedTestRunJobData(const AZ::IO::Path& resultsArtifact, const AZ::IO::Path& coverageArtifact);
//! Returns the path to the coverage artifact produced by the test target.
const AZ::IO::Path& GetCoverageArtifactPath() const;
private:
AZ::IO::Path m_coverageArtifact; //!< Path to coverage data.
};
namespace Bitwise
{
//! Exception policy for test coverage artifacts.
enum class CoverageExceptionPolicy
{
Never = 0, //! Never throw.
OnEmptyCoverage = 1 //! Throw when no coverage data was produced.
};
} // 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>>
{
using JobRunner = TestJobRunner<InstrumentedTestRunJobData, AZStd::pair<TestRun, TestCoverage>>;
public:
using CoverageExceptionPolicy = Bitwise::CoverageExceptionPolicy;
//! Constructs an instrumented test runner with the specified parameters common to all job runs of this runner.
//! @param clientCallback The optional client callback to be called whenever a run job changes state.
//! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time.
//! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated.
//! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs.
InstrumentedTestRunner(
AZStd::optional<ClientJobCallback> clientCallback, size_t maxConcurrentRuns,
AZStd::optional<AZStd::chrono::milliseconds> runTimeout, AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout);
//! Executes the specified instrumented test run jobs according to the specified job exception policies.
//! @param jobInfos The test run jobs to execute.
//! @param CoverageExceptionPolicy The coverage exception policy to be used for this run.
//! @param jobExceptionPolicy The test run job exception policy to be used for this run (use
//! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures).
//! @return the instrumented test run jobs with their associated test run and test coverage payloads.
AZStd::vector<Job> RunInstrumentedTests(
const AZStd::vector<JobInfo>& jobInfos, CoverageExceptionPolicy coverageExceptionPolicy, JobExceptionPolicy jobExceptionPolicy);
};
} // namespace TestImpact

@ -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.
*
*/
#include <Test/Run/TestImpactTestCoverage.h>
#include <AzCore/std/algorithm.h>
#include <AzCore/std/sort.h>
namespace TestImpact
{
TestCoverage::TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages)
: m_modules(AZStd::move(moduleCoverages))
{
for (const auto& moduleCovered : m_modules)
{
for (const auto& sourceCovered : moduleCovered.m_sources)
{
m_sourcesCovered.emplace_back(sourceCovered.m_path);
if (sourceCovered.m_coverage.has_value())
{
m_coverageLevel = CoverageLevel::Line;
}
}
}
AZStd::sort(m_sourcesCovered.begin(), m_sourcesCovered.end());
m_sourcesCovered.erase(AZStd::unique(m_sourcesCovered.begin(), m_sourcesCovered.end()), m_sourcesCovered.end());
if (!m_coverageLevel.has_value() && !m_sourcesCovered.empty())
{
m_coverageLevel = CoverageLevel::Source;
}
}
size_t TestCoverage::GetNumSourcesCovered() const
{
return m_sourcesCovered.size();
}
size_t TestCoverage::GetNumModulesCovered() const
{
return m_modules.size();
}
const AZStd::vector<AZ::IO::Path>& TestCoverage::GetSourcesCovered() const
{
return m_sourcesCovered;
}
const AZStd::vector<ModuleCoverage>& TestCoverage::GetModuleCoverages() const
{
return m_modules;
}
AZStd::optional<CoverageLevel> TestCoverage::GetCoverageLevel() const
{
return m_coverageLevel;
}
} // namespace TestImpact

@ -0,0 +1,54 @@
/*
* 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 <Artifact/Dynamic/TestImpactCoverage.h>
#include <AzCore/std/containers/set.h>
namespace TestImpact
{
//! Scope of coverage data.
enum class CoverageLevel : bool
{
Source, //!< Line-level coverage data.
Line //!< Source-level coverage data.
};
//! Representation of a given test target's test coverage results.
class TestCoverage
{
public:
TestCoverage(AZStd::vector<ModuleCoverage>&& moduleCoverages);
//! Returns the number of unique sources covered.
size_t GetNumSourcesCovered() const;
//! Returns the number of modules (dynamic libraries, child processes, etc.) covered.
size_t GetNumModulesCovered() const;
//! Returns the sorted set of unique sources covered (empty if no coverage).
const AZStd::vector<AZ::IO::Path>& GetSourcesCovered() const;
//! Returns the modules covered (empty if no coverage).
const AZStd::vector<ModuleCoverage>& GetModuleCoverages() const;
//! Returns the coverage level (empty if no coverage).
AZStd::optional<CoverageLevel> GetCoverageLevel() const;
private:
AZStd::vector<ModuleCoverage> m_modules;
AZStd::vector<AZ::IO::Path> m_sourcesCovered;
AZStd::optional<CoverageLevel> m_coverageLevel;
};
} // namespace TestImpact

@ -0,0 +1,70 @@
/*
* 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 "TestImpactTestRun.h"
namespace TestImpact
{
TestRun::TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration)
: TestSuiteContainer(AZStd::move(testSuites))
, m_duration(duration)
{
for (const auto& suite : m_testSuites)
{
for (const auto& test : suite.m_tests)
{
if (test.m_status == TestRunStatus::Run)
{
m_numRuns++;
if (test.m_result.value() == TestRunResult::Passed)
{
m_numPasses++;
}
else
{
m_numFailures++;
}
}
else
{
m_numNotRuns++;
}
}
}
}
size_t TestRun::GetNumRuns() const
{
return m_numRuns;
}
size_t TestRun::GetNumNotRuns() const
{
return m_numNotRuns;
}
size_t TestRun::GetNumPasses() const
{
return m_numPasses;
}
size_t TestRun::GetNumFailures() const
{
return m_numFailures;
}
AZStd::chrono::milliseconds TestRun::GetDuration() const
{
return m_duration;
}
} // namespace TestImpact

@ -0,0 +1,51 @@
/*
* 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 <Artifact/Dynamic/TestImpactTestRunSuite.h>
#include <Test/TestImpactTestSuiteContainer.h>
namespace TestImpact
{
//! Representation of a given test target's test run results.
class TestRun
: public TestSuiteContainer<TestRunSuite>
{
using TestSuiteContainer = TestSuiteContainer<TestRunSuite>;
public:
TestRun(AZStd::vector<TestRunSuite>&& testSuites, AZStd::chrono::milliseconds duration);
//! Returns the total number of tests that were run.
size_t GetNumRuns() const;
//! Returns the total number of tests that were not run.
size_t GetNumNotRuns() const;
//! Returns the total number of tests that were run and passed.
size_t GetNumPasses() const;
//! Returns the total number of tests that were run and failed.
size_t GetNumFailures() const;
//! Returns the duration of the job that was executed to yield this run data.
AZStd::chrono::milliseconds GetDuration() const;
private:
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};
};
} // namespace TestImpact

@ -0,0 +1,25 @@
/*
* 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 runs and test run related operations.
class TestRunException : public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Test/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
TestRunJobData::TestRunJobData(const AZ::IO::Path& resultsArtifact)
: m_runArtifact(resultsArtifact)
{
}
const AZ::IO::Path& TestRunJobData::GetRunArtifactPath() const
{
return m_runArtifact;
}
} // namespace TestImpact

@ -0,0 +1,31 @@
/*
* 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 <Test/Job/TestImpactTestJobRunner.h>
namespace TestImpact
{
//! Per-job data for test runs.
class TestRunJobData
{
public:
TestRunJobData(const AZ::IO::Path& resultsArtifact);
//! Returns the path to the test run artifact produced by the test target.
const AZ::IO::Path& GetRunArtifactPath() const;
private:
AZ::IO::Path m_runArtifact; //!< Path to results data.
};
} // namespace TestImpact

@ -0,0 +1,186 @@
/*
* 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 <Test/Run/TestImpactTestRunException.h>
#include <Test/Run/TestImpactTestRunSerializer.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/stringbuffer.h>
namespace TestImpact
{
namespace
{
// Keys for pertinent JSON node and attribute names
constexpr const char* Keys[] =
{
"suites",
"name",
"enabled",
"tests",
"duration",
"status",
"result"
};
enum
{
SuitesKey,
NameKey,
EnabledKey,
TestsKey,
DurationKey,
StatusKey,
ResultKey
};
} // namespace
AZStd::string SerializeTestRun(const TestRun& testRun)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
// Run
writer.StartObject();
// Run duration
writer.Key(Keys[DurationKey]);
writer.Uint(testRun.GetDuration().count());
// Suites
writer.Key(Keys[SuitesKey]);
writer.StartArray();
for (const auto& suite : testRun.GetTestSuites())
{
// Suite
writer.StartObject();
// Suite name
writer.Key(Keys[NameKey]);
writer.String(suite.m_name.c_str());
// Suite duration
writer.Key(Keys[DurationKey]);
writer.Uint(suite.m_duration.count());
// Suite enabled
writer.Key(Keys[EnabledKey]);
writer.Bool(suite.m_enabled);
// Suite tests
writer.Key(Keys[TestsKey]);
writer.StartArray();
for (const auto& test : suite.m_tests)
{
// Test
writer.StartObject();
// Test name
writer.Key(Keys[NameKey]);
writer.String(test.m_name.c_str());
// Test enabled
writer.Key(Keys[EnabledKey]);
writer.Bool(test.m_enabled);
// Test duration
writer.Key(Keys[DurationKey]);
writer.Uint(test.m_duration.count());
// Test status
writer.Key(Keys[StatusKey]);
writer.Bool(static_cast<bool>(test.m_status));
// Test result
if (test.m_status == TestRunStatus::Run)
{
writer.Key(Keys[ResultKey]);
writer.Bool(static_cast<size_t>(test.m_result.value()));
}
else
{
writer.Key(Keys[ResultKey]);
writer.Null();
}
// End test
writer.EndObject();
}
// End tests
writer.EndArray();
// End suite
writer.EndObject();
}
// End suites
writer.EndArray();
// End run
writer.EndObject();
return stringBuffer.GetString();
}
TestRun DeserializeTestRun(const AZStd::string& testEnumString)
{
AZStd::vector<TestRunSuite> testSuites;
rapidjson::Document doc;
if (doc.Parse<0>(testEnumString.c_str()).HasParseError())
{
throw TestRunException("Could not parse enumeration data");
}
// Run duration
const AZStd::chrono::milliseconds runDuration = AZStd::chrono::milliseconds{doc[Keys[DurationKey]].GetUint()};
// Suites
for (const auto& suite : doc[Keys[SuitesKey]].GetArray())
{
// Suite name
const AZStd::string name = suite[Keys[NameKey]].GetString();
// Suite duration
const AZStd::chrono::milliseconds suiteDuration = AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()};
// Suite enabled
const bool enabled = suite[Keys[EnabledKey]].GetBool();
testSuites.emplace_back(TestRunSuite{
suite[Keys[NameKey]].GetString(),
suite[Keys[EnabledKey]].GetBool(),
{},
AZStd::chrono::milliseconds{suite[Keys[DurationKey]].GetUint()}});
// Suite tests
for (const auto& test : suite[Keys[TestsKey]].GetArray())
{
AZStd::optional<TestRunResult> result;
TestRunStatus status = static_cast<TestRunStatus>(test[Keys[StatusKey]].GetBool());
if (status == TestRunStatus::Run)
{
result = static_cast<TestRunResult>(test[Keys[ResultKey]].GetBool());
}
const AZStd::chrono::milliseconds testDuration = AZStd::chrono::milliseconds{test[Keys[DurationKey]].GetUint()};
testSuites.back().m_tests.emplace_back(
TestRunCase{test[Keys[NameKey]].GetString(), test[Keys[EnabledKey]].GetBool(), result, testDuration, status});
}
}
return TestRun(std::move(testSuites), runDuration);
}
} // namespace TestImpact

@ -0,0 +1,26 @@
/*
* 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 <Test/Run/TestImpactTestRun.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Serializes the specified test run to JSON format.
AZStd::string SerializeTestRun(const TestRun& testRun);
//! Deserializes a test run from the specified test run data in JSON format.
TestRun DeserializeTestRun(const AZStd::string& testRunString);
} // namespace TestImpact

@ -0,0 +1,58 @@
/*
* 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 <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 <AzCore/IO/SystemFile.h>
namespace TestImpact
{
TestRun ParseTestRunFile(const AZ::IO::Path& runFile, AZStd::chrono::milliseconds duration)
{
return TestRun(GTest::TestRunSuitesFactory(ReadFileContents<TestRunException>(runFile)), duration);
}
TestRunner::TestRunner(
AZStd::optional<ClientJobCallback> clientCallback,
size_t maxConcurrentRuns,
AZStd::optional<AZStd::chrono::milliseconds> runTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout)
: JobRunner(clientCallback, AZStd::nullopt, StdOutputRouting::None, StdErrorRouting::None, maxConcurrentRuns, runTimeout, runnerTimeout)
{
}
AZStd::vector<TestRunner::Job> TestRunner::RunTests(
const AZStd::vector<JobInfo>& jobInfos,
JobExceptionPolicy jobExceptionPolicy)
{
const auto payloadGenerator = [this](const JobDataMap& jobDataMap)
{
PayloadMap<Job> runs;
for (const auto& [jobId, jobData] : jobDataMap)
{
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());
}
}
return runs;
};
return ExecuteJobs(jobInfos, payloadGenerator, jobExceptionPolicy);
}
} // namespace TestImpact

@ -0,0 +1,46 @@
/*
* 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 <Test/Job/TestImpactTestJobRunner.h>
#include <Test/Run/TestImpactTestRun.h>
#include <Test/Run/TestImpactTestRunJobData.h>
namespace TestImpact
{
//! Runs a batch of test targets to determine the test passes/failures.
class TestRunner
: public TestJobRunner<TestRunJobData, TestRun>
{
using JobRunner = TestJobRunner<TestRunJobData, TestRun>;
public:
//! Constructs a test runner with the specified parameters common to all job runs of this runner.
//! @param clientCallback The optional client callback to be called whenever a run job changes state.
//! @param maxConcurrentRuns The maximum number of runs to be in flight at any given time.
//! @param runTimeout The maximum duration a run may be in-flight for before being forcefully terminated.
//! @param runnerTimeout The maximum duration the runner may run before forcefully terminating all in-flight runs.
TestRunner(
AZStd::optional<ClientJobCallback> clientCallback,
size_t maxConcurrentRuns,
AZStd::optional<AZStd::chrono::milliseconds> runTimeout,
AZStd::optional<AZStd::chrono::milliseconds> runnerTimeout);
//! Executes the specified test run jobs according to the specified job exception policies.
//! @param jobInfos The test run jobs to execute.
//! @param jobExceptionPolicy The test run job exception policy to be used for this run (use
//! TestJobExceptionPolicy::OnFailedToExecute to throw on test failures).
//! @return the test run jobs with their associated test run payloads.
AZStd::vector<Job> RunTests(const AZStd::vector<JobInfo>& jobInfos, JobExceptionPolicy jobExceptionPolicy);
};
} // namespace TestImpact

@ -0,0 +1,101 @@
/*
* 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 <AzCore/std/containers/vector.h>
namespace TestImpact
{
//! Encapsulation of test suites into a class with meta-data about each the suites.
//! @tparam TestSuite The test suite data structure to encapsulate.
template<typename TestSuite>
class TestSuiteContainer
{
public:
TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites);
//! Returns the test suites in this container.
const AZStd::vector<TestSuite>& GetTestSuites() const;
//! Returns the number of test suites in this container.
size_t GetNumTestSuites() const;
//! Returns the total number of tests across all test suites.
size_t GetNumTests() const;
//! Returns the total number of enabled tests across all test suites.
size_t GetNumEnabledTests() const;
//! Returns the total number of disabled tests across all test suites.
size_t GetNumDisabledTests() const;
protected:
AZStd::vector<TestSuite> m_testSuites;
size_t m_numDisabledTests = 0;
size_t m_numEnabledTests = 0;
};
template<typename TestSuite>
TestSuiteContainer<TestSuite>::TestSuiteContainer(AZStd::vector<TestSuite>&& testSuites)
: m_testSuites(std::move(testSuites))
{
for (const auto& suite : m_testSuites)
{
if (suite.m_enabled)
{
const auto enabled = std::count_if(suite.m_tests.begin(), suite.m_tests.end(), [](const auto& test)
{
return test.m_enabled;
});
m_numEnabledTests += enabled;
m_numDisabledTests += suite.m_tests.size() - enabled;
}
else
{
// Disabled status of suites propagates down to all tests regardless of whether or not each individual test is disabled
m_numDisabledTests += suite.m_tests.size();
}
}
}
template<typename TestSuite>
const AZStd::vector<TestSuite>& TestSuiteContainer<TestSuite>::GetTestSuites() const
{
return m_testSuites;
}
template<typename TestSuite>
size_t TestSuiteContainer<TestSuite>::GetNumTests() const
{
return m_numEnabledTests + m_numDisabledTests;
}
template<typename TestSuite>
size_t TestSuiteContainer<TestSuite>::GetNumEnabledTests() const
{
return m_numEnabledTests;
}
template<typename TestSuite>
size_t TestSuiteContainer<TestSuite>::GetNumDisabledTests() const
{
return m_numDisabledTests;
}
template<typename TestSuite>
size_t TestSuiteContainer<TestSuite>::GetNumTestSuites() const
{
return m_testSuites.size();
}
} // namespace TestImpact

@ -0,0 +1,31 @@
/*
* 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>
namespace TestImpact
{
Exception::Exception(const AZStd::string& msg)
: m_msg(msg)
{
}
Exception::Exception(const char* msg)
: m_msg(msg)
{
}
const char* Exception::what() const noexcept
{
return m_msg.c_str();
}
} // namespace TestImpact

@ -0,0 +1,38 @@
/*
* 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/TestImpactFrameworkPath.h>
namespace TestImpact
{
FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath)
{
m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred();
m_relativePath = m_absolutePath.LexicallyRelative(m_absolutePath);
}
FrameworkPath::FrameworkPath(const AZ::IO::Path& absolutePath, const FrameworkPath& relativeTo)
{
m_absolutePath = AZ::IO::Path(absolutePath).MakePreferred();
m_relativePath = m_absolutePath.LexicallyRelative(relativeTo.Absolute());
}
const AZ::IO::Path& FrameworkPath::Absolute() const
{
return m_absolutePath;
}
const AZ::IO::Path& FrameworkPath::Relative() const
{
return m_relativePath;
}
} // namespace TestImpact

@ -0,0 +1,370 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
class BuildTargetDescriptorFactoryTestFixture
: public AllocatorsTestFixture
{
protected:
const AZStd::vector<AZStd::string> m_staticExclude = {".cmake"};
const AZStd::vector<AZStd::string> m_inputExclude = {".jinja"};
const AZStd::string m_autogenMatcher = {"(.*)\\..*"};
const AZStd::vector<AZStd::string> m_autogenInputs =
{
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml",
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml",
"Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Header.jinja",
"Gems/ScriptCanvas/Code/Include/ScriptCanvas/AutoGen/ScriptCanvasNode_Source.jinja"
};
const AZStd::vector<AZStd::string> m_autogenOutputs =
{
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp"
};
const AZStd::vector<AZStd::string> m_staticSources =
{
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp",
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h",
"Gems/ScriptCanvasDiagnosticLibrary/Code/scriptcanvasdiagnosticlibrary_autogen_files.cmake",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp"
};
const AZStd::vector<AZStd::string> m_expectedStaticSources =
{
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.cpp",
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/precompiled.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp"
};
const AZStd::string m_name = "ScriptCanvasDiagnosticLibrary.Static";
const AZStd::string m_outputName = "ScriptCanvasDiagnosticLibrary";
const AZStd::string m_path = "Gems/ScriptCanvasDiagnosticLibrary/Code";
const TestImpact::AutogenSources m_expectedAutogenSources =
{
{
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/Log.ScriptCanvasNode.xml",
{
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/Log.generated.cpp"
}
},
{
"Gems/ScriptCanvasDiagnosticLibrary/Code/Source/DrawText.ScriptCanvasNode.xml",
{
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.h",
"windows_vs2019/Gems/ScriptCanvasDiagnosticLibrary/Code/Azcg/Generated/Source/DrawText.generated.cpp"
}
}
};
};
TEST_F(BuildTargetDescriptorFactoryTestFixture, NoRawData_ExpectArtifactException)
{
// Given an empty raw descriptor string
const AZStd::string rawTargetDescriptor;
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, InvalidRawData_ExpectArtifactException)
{
// Given a raw descriptor string of invalid data
const AZStd::string rawTargetDescriptor = "abcde";
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenMatcher_ExpectArtifactException)
{
// Given a valid raw descriptor string
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs);
try
{
// When attempting to construct the build target descriptor with an empty autogen matcher
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, "");
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyName_ExpectArtifactException)
{
// Given a invalid raw descriptor string lacking build meta-data name
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString("", m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs);
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyOutputName_ExpectArtifactException)
{
// Given a invalid raw descriptor string lacking build meta-data output name
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, "", m_path, m_staticSources, m_autogenInputs, m_autogenOutputs);
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, EmptyPath_ExpectArtifactException)
{
// Given a invalid raw descriptor string lacking build meta-data path
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, "", m_staticSources, m_autogenInputs, m_autogenOutputs);
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticSources_ExpectValidDescriptor)
{
const TestImpact::BuildTargetDescriptor expectedBuiltTarget =
GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, m_expectedAutogenSources);
// Given a valid raw descriptor string with no static sources
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, m_autogenInputs, m_autogenOutputs);
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, {}, m_inputExclude, m_autogenMatcher);
// Expect the constructed build target descriptor to match the specified descriptor
EXPECT_TRUE(buildTarget == expectedBuiltTarget);
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, NoAutogenSources_ExpectValidDescriptor)
{
const TestImpact::BuildTargetDescriptor expectedBuiltTarget =
GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, {});
// Given a valid raw descriptor string with no autogen sources
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, {});
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Expect the constructed build target descriptor to match the specified descriptor
EXPECT_TRUE(buildTarget == expectedBuiltTarget);
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, NoStaticOrAutogenSources_ExpectValidDescriptor)
{
const TestImpact::BuildTargetDescriptor expectedBuiltTarget = GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, {}, {});
// Given a valid raw descriptor string with no static or autogen sources
const AZStd::string rawTargetDescriptor = GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, {}, {}, {});
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Expect the constructed build target descriptor to match the specified descriptor
EXPECT_TRUE(buildTarget == expectedBuiltTarget);
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenOutputSourcesButNoAutogenInputSources_ExpectArtifactException)
{
// Given a valid raw descriptor string with autogen output sources but no autogen input sources
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, {}, m_autogenOutputs);
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, AutogenInputSourcesButNoAutogenOutputSources_ExpectArtifactException)
{
// Given a valid raw descriptor string with autogen inout sources but no autogen output sources
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, {});
try
{
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(BuildTargetDescriptorFactoryTestFixture, StaticAndAutogenSources_ExpectValidDescriptor)
{
const TestImpact::BuildTargetDescriptor expectedBuiltTarget =
GenerateBuildTargetDescriptor(m_name, m_outputName, m_path, m_expectedStaticSources, m_expectedAutogenSources);
// Given a valid raw descriptor string with static and autogen sources
const AZStd::string rawTargetDescriptor =
GenerateBuildTargetDescriptorString(m_name, m_outputName, m_path, m_staticSources, m_autogenInputs, m_autogenOutputs);
// When attempting to construct the build target descriptor
const TestImpact::BuildTargetDescriptor buildTarget =
TestImpact::BuildTargetDescriptorFactory(rawTargetDescriptor, m_staticExclude, m_inputExclude, m_autogenMatcher);
// Expect the constructed build target descriptor to match the specified descriptor
EXPECT_TRUE(buildTarget == expectedBuiltTarget);
}
} // namespace UnitTest

@ -0,0 +1,288 @@
/*
* 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 <Artifact/Factory/TestImpactChangeListFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
TEST(ChangeListFactoryTest, NoRawData_ExpectArtifactException)
{
// Given an empty unified diff string
const AZStd::string unifiedDiff;
try
{
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(ChangeListFactoryTest, NoChanges_ExpectArtifactException)
{
// Given a unified diff string with no changes
const AZStd::string unifiedDiff = "On this day in 1738 absolutely nothing happened";
try
{
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(ChangeListFactoryTest, CreateOnly_ExpectValidChangeListWithFileCreateOperations)
{
// Given a unified diff with only one file creation and no file updates or deletions
const AZStd::string unifiedDiff =
"From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n"
"From: user <user@website.com>\n"
"Date: Sat, 13 Mar 2021 22:58:07 +0000\n"
"Subject: Test\n"
"\n"
"---\n"
" New.txt | 1 +\n"
" create mode 100644 New.txt\n"
"diff --git a/New.txt b/New.txt\n"
"new file mode 100644\n"
"index 0000000..30d74d2\n"
"--- /dev/null\n"
"+++ b/New.txt\n"
"@@ -0,0 +1 @@\n"
"+test\n"
"\\ No newline at end of file\n"
"-- \n"
"2.30.0.windows.2\n"
"\n"
"\n";
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Expect the change list to contain the 1 created file
EXPECT_EQ(changeList.m_createdFiles.size(), 1);
EXPECT_TRUE(
AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end());
// Expect the change list to contain no updated files
EXPECT_TRUE(changeList.m_updatedFiles.empty());
// Expect the change list to contain no deleted files
EXPECT_TRUE(changeList.m_deletedFiles.empty());
}
TEST(ChangeListFactoryTest, UpdateOnly_ExpectValidChangeListWithFileUpdateOperations)
{
// Given a unified diff with only one file update and no file creations or deletions
const AZStd::string unifiedDiff =
"From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n"
"From: user <user@website.com>\n"
"Date: Sat, 13 Mar 2021 22:58:07 +0000\n"
"Subject: Test\n"
"\n"
"---\n"
" A.txt | 2 +-\n"
"diff --git a/A.txt b/A.txt\n"
"index 7c4a013..e132db2 100644\n"
"--- a/A.txt\n"
"+++ b/A.txt\n"
"@@ -1 +1 @@\n"
"-aaa\n"
"\\ No newline at end of file\n"
"+zzz\n"
"\\ No newline at end of file\n"
"-- \n"
"2.30.0.windows.2\n"
"\n"
"\n";
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Expect the change list to contain no created files
EXPECT_TRUE(changeList.m_createdFiles.empty());
// Expect the change list to contain one updated file
EXPECT_EQ(changeList.m_updatedFiles.size(), 1);
EXPECT_TRUE(
AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end());
// Expect the change list to contain no deleted files
EXPECT_TRUE(changeList.m_deletedFiles.empty());
}
TEST(ChangeListFactoryTest, DeleteOnly_ExpectValidChangeListWithFileDeleteOperations)
{
// Given a unified diff with only one file deletion and no file creations or updates
const AZStd::string unifiedDiff =
"From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n"
"From: user <user@website.com>\n"
"Date: Sat, 13 Mar 2021 22:58:07 +0000\n"
"Subject: Test\n"
"\n"
"---\n"
" B.txt | 1 -\n"
" delete mode 100644 B.txt\n"
"diff --git a/B.txt b/B.txt\n"
"deleted file mode 100644\n"
"index 01f02e3..0000000\n"
"--- a/B.txt\n"
"+++ /dev/null\n"
"@@ -1 +0,0 @@\n"
"-bbb\n"
"\\ No newline at end of file\n"
"-- \n"
"2.30.0.windows.2\n"
"\n"
"\n";
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Expect the change list to contain no created files
EXPECT_TRUE(changeList.m_createdFiles.empty());
// Expect the change list to contain no updated files
EXPECT_TRUE(changeList.m_updatedFiles.empty());
// Expect the change list to contain one deleted file
EXPECT_EQ(changeList.m_deletedFiles.size(), 1);
EXPECT_TRUE(
AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end());
}
TEST(ChangeListFactoryTest, ParseUnifiedDiffWithAllPossibleOperations_ExpectChangeListMatchingOperations)
{
// Given a unified diff with created files, updated files, deleted files, renamed files and moved files
const AZStd::string unifiedDiff =
"From f642a2f698452fc18484758b0046132415f09467 Mon Sep 17 00:00:00 2001\n"
"From: user <user@website.com>\n"
"Date: Sat, 13 Mar 2021 22:58:07 +0000\n"
"Subject: Test\n"
"\n"
"---\n"
" A.txt | 2 +-\n"
" B.txt | 1 -\n"
" D.txt => Foo/D.txt | 0\n"
" E.txt => Foo/Y.txt | 0\n"
" New.txt | 1 +\n"
" C.txt => X.txt | 0\n"
" 6 files changed, 2 insertions(+), 2 deletions(-)\n"
" delete mode 100644 B.txt\n"
" rename D.txt => Foo/D.txt (100%)\n"
" rename E.txt => Foo/Y.txt (100%)\n"
" create mode 100644 New.txt\n"
" rename C.txt => X.txt (100%)\n"
"\n"
"diff --git a/A.txt b/A.txt\n"
"index 7c4a013..e132db2 100644\n"
"--- a/A.txt\n"
"+++ b/A.txt\n"
"@@ -1 +1 @@\n"
"-aaa\n"
"\\ No newline at end of file\n"
"+zzz\n"
"\\ No newline at end of file\n"
"diff --git a/B.txt b/B.txt\n"
"deleted file mode 100644\n"
"index 01f02e3..0000000\n"
"--- a/B.txt\n"
"+++ /dev/null\n"
"@@ -1 +0,0 @@\n"
"-bbb\n"
"\\ No newline at end of file\n"
"diff --git a/D.txt b/Foo/D.txt\n"
"similarity index 100%\n"
"rename from D.txt\n"
"rename to Foo/D.txt\n"
"diff --git a/E.txt b/Foo/Y.txt\n"
"similarity index 100%\n"
"rename from E.txt\n"
"rename to Foo/Y.txt\n"
"diff --git a/New.txt b/New.txt\n"
"new file mode 100644\n"
"index 0000000..30d74d2\n"
"--- /dev/null\n"
"+++ b/New.txt\n"
"@@ -0,0 +1 @@\n"
"+test\n"
"\\ No newline at end of file\n"
"diff --git a/C.txt b/X.txt\n"
"similarity index 100%\n"
"rename from C.txt\n"
"rename to X.txt\n"
"-- \n"
"2.30.0.windows.2\n"
"\n"
"\n";
// When attempting to construct the change list
const TestImpact::ChangeList changeList = TestImpact::UnifiedDiff::ChangeListFactory(unifiedDiff);
// Expect the change list to contain the 4 created files
EXPECT_EQ(changeList.m_createdFiles.size(), 4);
EXPECT_TRUE(
AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/D.txt") !=
changeList.m_createdFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "Foo/Y.txt") !=
changeList.m_createdFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "X.txt") != changeList.m_createdFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_createdFiles.begin(), changeList.m_createdFiles.end(), "New.txt") != changeList.m_createdFiles.end());
// Expect the change list to contain the 1 updated file
EXPECT_EQ(changeList.m_updatedFiles.size(), 1);
EXPECT_TRUE(
AZStd::find(changeList.m_updatedFiles.begin(), changeList.m_updatedFiles.end(), "A.txt") != changeList.m_updatedFiles.end());
// Expect the change list to contain the 4 deleted files
EXPECT_EQ(changeList.m_deletedFiles.size(), 4);
EXPECT_TRUE(
AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "B.txt") != changeList.m_deletedFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "D.txt") != changeList.m_deletedFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "E.txt") != changeList.m_deletedFiles.end());
EXPECT_TRUE(
AZStd::find(changeList.m_deletedFiles.begin(), changeList.m_deletedFiles.end(), "C.txt") != changeList.m_deletedFiles.end());
}
} // namespace UnitTest

@ -0,0 +1,522 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Factory/TestImpactModuleCoverageFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
TEST(CoberturaModuleCoveragesFactory, ParseEmptyString_ThrowsArtifactException)
{
// Given an empty string
const AZStd::string rawCoverage;
try
{
// When attempting to parse the empty coverage
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(CoberturaModuleCoveragesFactory, ParseInvalidString_ThrowsArtifactException)
{
// Given an invalid string
const AZStd::string rawCoverage = "!@?";
try
{
// When attempting to parse the invalid coverage
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(CoberturaModuleCoveragesFactory, ParseEmptyCoverage_ExpectEmptyModuleCoverages)
{
// Given an empty coverage string
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617713965\" lines-covered=\"0\" lines-valid=\"0\" version=\"0\">"
" <sources/>"
" <packages/>"
"</coverage>"
"";
// When attempting to parse the empty coverage
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect an empty module coverages
EXPECT_TRUE(coverage.empty());
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageA_ReturnsValidLineCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetALineModuleCoverages();
// Given the raw line coverage output of TestTargetA
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617124634\" lines-covered=\"41\" lines-valid=\"41\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetA.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines>"
" <line number=\"22\" hits=\"1\"/>"
" <line number=\"23\" hits=\"1\"/>"
" <line number=\"24\" hits=\"1\"/>"
" <line number=\"25\" hits=\"1\"/>"
" <line number=\"27\" hits=\"1\"/>"
" <line number=\"28\" hits=\"1\"/>"
" <line number=\"29\" hits=\"1\"/>"
" <line number=\"30\" hits=\"1\"/>"
" <line number=\"32\" hits=\"1\"/>"
" <line number=\"33\" hits=\"1\"/>"
" <line number=\"34\" hits=\"1\"/>"
" <line number=\"35\" hits=\"1\"/>"
" <line number=\"37\" hits=\"1\"/>"
" <line number=\"38\" hits=\"1\"/>"
" <line number=\"39\" hits=\"1\"/>"
" <line number=\"40\" hits=\"1\"/>"
" <line number=\"42\" hits=\"1\"/>"
" <line number=\"43\" hits=\"1\"/>"
" <line number=\"44\" hits=\"1\"/>"
" <line number=\"45\" hits=\"1\"/>"
" <line number=\"47\" hits=\"1\"/>"
" <line number=\"48\" hits=\"1\"/>"
" <line number=\"49\" hits=\"1\"/>"
" <line number=\"50\" hits=\"1\"/>"
" <line number=\"52\" hits=\"1\"/>"
" <line number=\"53\" hits=\"1\"/>"
" <line number=\"54\" hits=\"1\"/>"
" <line number=\"55\" hits=\"1\"/>"
" <line number=\"57\" hits=\"1\"/>"
" <line number=\"58\" hits=\"1\"/>"
" <line number=\"59\" hits=\"1\"/>"
" <line number=\"60\" hits=\"1\"/>"
" <line number=\"62\" hits=\"1\"/>"
" <line number=\"63\" hits=\"1\"/>"
" <line number=\"64\" hits=\"1\"/>"
" <line number=\"65\" hits=\"1\"/>"
" <line number=\"67\" hits=\"1\"/>"
" <line number=\"68\" hits=\"1\"/>"
" <line number=\"69\" hits=\"1\"/>"
" <line number=\"70\" hits=\"1\"/>"
" <line number=\"73\" hits=\"1\"/>"
" </lines>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw line coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageA_ReturnsValidSourceCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetASourceModuleCoverages();
// Given the raw source coverage output of TestTargetA
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617117760\" lines-covered=\"0\" lines-valid=\"0\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetA.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetA.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines/>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw source coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageB_ReturnsValidLineCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetBLineModuleCoverages();
// Given the raw line coverage output of TestTargetB
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617124605\" lines-covered=\"29\" lines-valid=\"29\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetB.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines>"
" <line number=\"29\" hits=\"1\"/>"
" <line number=\"30\" hits=\"1\"/>"
" <line number=\"31\" hits=\"1\"/>"
" <line number=\"32\" hits=\"1\"/>"
" <line number=\"34\" hits=\"1\"/>"
" <line number=\"35\" hits=\"1\"/>"
" <line number=\"36\" hits=\"1\"/>"
" <line number=\"37\" hits=\"1\"/>"
" <line number=\"39\" hits=\"1\"/>"
" <line number=\"40\" hits=\"1\"/>"
" <line number=\"41\" hits=\"1\"/>"
" <line number=\"42\" hits=\"1\"/>"
" <line number=\"44\" hits=\"1\"/>"
" <line number=\"45\" hits=\"1\"/>"
" <line number=\"46\" hits=\"1\"/>"
" <line number=\"47\" hits=\"1\"/>"
" <line number=\"49\" hits=\"1\"/>"
" <line number=\"50\" hits=\"1\"/>"
" <line number=\"51\" hits=\"1\"/>"
" <line number=\"52\" hits=\"1\"/>"
" <line number=\"54\" hits=\"1\"/>"
" <line number=\"55\" hits=\"1\"/>"
" <line number=\"56\" hits=\"1\"/>"
" <line number=\"57\" hits=\"1\"/>"
" <line number=\"59\" hits=\"1\"/>"
" <line number=\"66\" hits=\"1\"/>"
" <line number=\"68\" hits=\"1\"/>"
" <line number=\"75\" hits=\"1\"/>"
" <line number=\"78\" hits=\"1\"/>"
" </lines>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw line coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageB_ReturnsValidSourceCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetBSourceModuleCoverages();
// Given the raw source coverage output of TestTargetB
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617117785\" lines-covered=\"0\" lines-valid=\"0\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetB.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetB.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetB\\Code\\Tests\\TestImpactTestTargetB.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines/>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw source coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageC_ReturnsValidLineCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetCLineModuleCoverages();
// Given the raw line coverage output of TestTargetC
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617124593\" lines-covered=\"25\" lines-valid=\"25\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetC.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines>"
" <line number=\"32\" hits=\"1\"/>"
" <line number=\"33\" hits=\"1\"/>"
" <line number=\"34\" hits=\"1\"/>"
" <line number=\"35\" hits=\"1\"/>"
" <line number=\"37\" hits=\"1\"/>"
" <line number=\"38\" hits=\"1\"/>"
" <line number=\"39\" hits=\"1\"/>"
" <line number=\"40\" hits=\"1\"/>"
" <line number=\"42\" hits=\"1\"/>"
" <line number=\"43\" hits=\"1\"/>"
" <line number=\"44\" hits=\"1\"/>"
" <line number=\"45\" hits=\"1\"/>"
" <line number=\"47\" hits=\"1\"/>"
" <line number=\"48\" hits=\"1\"/>"
" <line number=\"49\" hits=\"1\"/>"
" <line number=\"50\" hits=\"1\"/>"
" <line number=\"52\" hits=\"1\"/>"
" <line number=\"53\" hits=\"1\"/>"
" <line number=\"54\" hits=\"1\"/>"
" <line number=\"55\" hits=\"1\"/>"
" <line number=\"57\" hits=\"1\"/>"
" <line number=\"58\" hits=\"1\"/>"
" <line number=\"59\" hits=\"1\"/>"
" <line number=\"60\" hits=\"1\"/>"
" <line number=\"63\" hits=\"1\"/>"
" </lines>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw line coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageC_ReturnsValidSourceCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetCSourceModuleCoverages();
// Given the raw source coverage output of TestTargetC
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617117796\" lines-covered=\"0\" lines-valid=\"0\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetC.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetC.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines/>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw source coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetLineCoverageD_ReturnsValidLineCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetDLineModuleCoverages();
// Given the raw line coverage output of TestTargetD
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"0.6470588235294118\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617124579\" lines-covered=\"55\" lines-valid=\"85\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll\" line-rate=\"0.6470588235294118\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetD.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line-rate=\"0.6470588235294118\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines>"
" <line number=\"56\" hits=\"1\"/>"
" <line number=\"57\" hits=\"1\"/>"
" <line number=\"58\" hits=\"1\"/>"
" <line number=\"59\" hits=\"1\"/>"
" <line number=\"61\" hits=\"1\"/>"
" <line number=\"62\" hits=\"0\"/>"
" <line number=\"63\" hits=\"0\"/>"
" <line number=\"64\" hits=\"0\"/>"
" <line number=\"66\" hits=\"1\"/>"
" <line number=\"67\" hits=\"1\"/>"
" <line number=\"68\" hits=\"1\"/>"
" <line number=\"69\" hits=\"1\"/>"
" <line number=\"71\" hits=\"1\"/>"
" <line number=\"72\" hits=\"1\"/>"
" <line number=\"73\" hits=\"1\"/>"
" <line number=\"74\" hits=\"1\"/>"
" <line number=\"76\" hits=\"1\"/>"
" <line number=\"77\" hits=\"1\"/>"
" <line number=\"78\" hits=\"1\"/>"
" <line number=\"79\" hits=\"1\"/>"
" <line number=\"81\" hits=\"1\"/>"
" <line number=\"82\" hits=\"1\"/>"
" <line number=\"83\" hits=\"1\"/>"
" <line number=\"84\" hits=\"1\"/>"
" <line number=\"86\" hits=\"1\"/>"
" <line number=\"87\" hits=\"1\"/>"
" <line number=\"88\" hits=\"1\"/>"
" <line number=\"89\" hits=\"1\"/>"
" <line number=\"91\" hits=\"1\"/>"
" <line number=\"92\" hits=\"0\"/>"
" <line number=\"93\" hits=\"0\"/>"
" <line number=\"94\" hits=\"0\"/>"
" <line number=\"96\" hits=\"1\"/>"
" <line number=\"97\" hits=\"0\"/>"
" <line number=\"98\" hits=\"0\"/>"
" <line number=\"99\" hits=\"0\"/>"
" <line number=\"101\" hits=\"1\"/>"
" <line number=\"102\" hits=\"1\"/>"
" <line number=\"103\" hits=\"1\"/>"
" <line number=\"104\" hits=\"1\"/>"
" <line number=\"106\" hits=\"1\"/>"
" <line number=\"107\" hits=\"0\"/>"
" <line number=\"108\" hits=\"0\"/>"
" <line number=\"109\" hits=\"0\"/>"
" <line number=\"111\" hits=\"1\"/>"
" <line number=\"112\" hits=\"0\"/>"
" <line number=\"113\" hits=\"0\"/>"
" <line number=\"114\" hits=\"0\"/>"
" <line number=\"116\" hits=\"1\"/>"
" <line number=\"117\" hits=\"0\"/>"
" <line number=\"118\" hits=\"0\"/>"
" <line number=\"119\" hits=\"0\"/>"
" <line number=\"121\" hits=\"1\"/>"
" <line number=\"128\" hits=\"1\"/>"
" <line number=\"130\" hits=\"1\"/>"
" <line number=\"137\" hits=\"1\"/>"
" <line number=\"139\" hits=\"1\"/>"
" <line number=\"146\" hits=\"1\"/>"
" <line number=\"148\" hits=\"1\"/>"
" <line number=\"155\" hits=\"1\"/>"
" <line number=\"157\" hits=\"1\"/>"
" <line number=\"158\" hits=\"1\"/>"
" <line number=\"159\" hits=\"1\"/>"
" <line number=\"160\" hits=\"1\"/>"
" <line number=\"162\" hits=\"1\"/>"
" <line number=\"163\" hits=\"0\"/>"
" <line number=\"164\" hits=\"0\"/>"
" <line number=\"165\" hits=\"0\"/>"
" <line number=\"167\" hits=\"1\"/>"
" <line number=\"168\" hits=\"1\"/>"
" <line number=\"169\" hits=\"1\"/>"
" <line number=\"170\" hits=\"1\"/>"
" <line number=\"172\" hits=\"1\"/>"
" <line number=\"173\" hits=\"0\"/>"
" <line number=\"174\" hits=\"0\"/>"
" <line number=\"175\" hits=\"0\"/>"
" <line number=\"177\" hits=\"1\"/>"
" <line number=\"178\" hits=\"0\"/>"
" <line number=\"179\" hits=\"0\"/>"
" <line number=\"180\" hits=\"0\"/>"
" <line number=\"182\" hits=\"1\"/>"
" <line number=\"183\" hits=\"0\"/>"
" <line number=\"184\" hits=\"0\"/>"
" <line number=\"185\" hits=\"0\"/>"
" <line number=\"188\" hits=\"1\"/>"
" </lines>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw line coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
TEST(CoberturaModuleCoveragesFactory, ParseTestTargetSourceCoverageD_ReturnsValidSourceCoverage)
{
const AZStd::vector<TestImpact::ModuleCoverage> expectedCoverage = GetTestTargetDSourceModuleCoverages();
// Given the raw source coverage output of TestTargetD
const AZStd::string rawCoverage =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>"
"<coverage line-rate=\"1\" branch-rate=\"0\" complexity=\"0\" branches-covered=\"0\" branches-valid=\"0\" timestamp=\"1617117804\" lines-covered=\"0\" lines-valid=\"0\" version=\"0\">"
" <sources>"
" <source>C:</source>"
" </sources>"
" <packages>"
" <package name=\"C:\\Lumberyard\\windows_vs2019\\bin\\debug\\TestImpact.TestTargetD.Tests.dll\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <classes>"
" <class name=\"TestImpactTestTargetD.cpp\" filename=\"Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line-rate=\"1\" branch-rate=\"0\" complexity=\"0\">"
" <methods/>"
" <lines/>"
" </class>"
" </classes>"
" </package>"
" </packages>"
"</coverage>"
"";
// When the raw source coverage text is parsed
const AZStd::vector<TestImpact::ModuleCoverage> coverage = TestImpact::Cobertura::ModuleCoveragesFactory(rawCoverage);
// Expect the generated suite data to match that of the raw coverage text
EXPECT_TRUE(coverage == expectedCoverage);
}
} // namespace UnitTest

@ -0,0 +1,150 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Static/TestImpactTargetDescriptorCompiler.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
class TargetDescriptorCompilerTestFixture
: public AllocatorsTestFixture
{
public:
void SetUp() override
{
AllocatorsTestFixture::SetUp();
m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetA", "", ""}, {}});
m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"TestTargetB", "", ""}, {}});
m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetA", "", ""}, {}});
m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetB", "", ""}, {}});
m_buildTargetDescriptors.emplace_back(TestImpact::BuildTargetDescriptor{{"ProductionTargetC", "", ""}, {}});
m_testTargetMetaMap.emplace("TestTargetA", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner});
m_testTargetMetaMap.emplace("TestTargetB", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::StandAlone});
}
protected:
AZStd::vector<TestImpact::BuildTargetDescriptor> m_buildTargetDescriptors;
TestImpact::TestTargetMetaMap m_testTargetMetaMap;
};
TestImpact::ProductionTargetDescriptor ConstructProductionTargetDescriptor(const AZStd::string& name)
{
return TestImpact::ProductionTargetDescriptor{TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}};
}
TestImpact::TestTargetDescriptor ConstructTestTargetDescriptor(const AZStd::string& name, TestImpact::LaunchMethod launchMethod)
{
return TestImpact::TestTargetDescriptor{
TestImpact::BuildTargetDescriptor{{name, "", ""}, {{}, {}}}, TestImpact::TestTargetMeta{"", launchMethod}};
}
TEST_F(TargetDescriptorCompilerTestFixture, EmptyBuildTargetDescriptorList_ExpectArtifactException)
{
try
{
// Given an empty build target descriptor list but valid test target meta map
// When attempting to construct the test target
const auto& [productionTargetDescriptors, testTargetDescriptors] =
TestImpact::CompileTargetDescriptors({}, AZStd::move(m_testTargetMetaMap));
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(TargetDescriptorCompilerTestFixture, EmptyTestTargetMetaMap_ExpectArtifactException)
{
try
{
// Given a valid build target descriptor list but empty test target meta map
// When attempting to construct the test target
const auto& [productionTargetDescriptors, testTargetDescriptors] =
TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {});
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(TargetDescriptorCompilerTestFixture, TestTargetWithNoMatchingMeta_ExpectArtifactException)
{
try
{
// Given a valid build target descriptor list but a test target meta map with an orphan entry
m_testTargetMetaMap.emplace("Orphan", TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner});
// When attempting to construct the test target
const auto& [productionTargetDescriptors, testTargetDescriptors] =
TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), {});
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(TargetDescriptorCompilerTestFixture, ValidProductionTargetsAndTestTargetMetas_ExpectValidProductionAndTestTargets)
{
// Given a valid build target descriptor list and a valid test target meta map
// When attempting to construct the test target
const auto& [productionTargetDescriptors, testTargetDescriptors] =
TestImpact::CompileTargetDescriptors(AZStd::move(m_buildTargetDescriptors), AZStd::move(m_testTargetMetaMap));
// Expect the production targets to match the expected targets
EXPECT_TRUE(productionTargetDescriptors.size() == 3);
EXPECT_TRUE(productionTargetDescriptors[0] == ConstructProductionTargetDescriptor("ProductionTargetA"));
EXPECT_TRUE(productionTargetDescriptors[1] == ConstructProductionTargetDescriptor("ProductionTargetB"));
EXPECT_TRUE(productionTargetDescriptors[2] == ConstructProductionTargetDescriptor("ProductionTargetC"));
// Expect the test targets to match the expected targets
EXPECT_TRUE(testTargetDescriptors.size() == 2);
EXPECT_TRUE(testTargetDescriptors[0] == ConstructTestTargetDescriptor("TestTargetA", TestImpact::LaunchMethod::TestRunner));
EXPECT_TRUE(testTargetDescriptors[1] == ConstructTestTargetDescriptor("TestTargetB", TestImpact::LaunchMethod::StandAlone));
}
} // namespace UnitTest

@ -0,0 +1,589 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/tuple.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
TEST(GTestEnumerationSuiteFactory, ParseEmptyString_ThrowsArtifactException)
{
// Given an empty string
const AZStd::string rawEnum;
try
{
// When attempting to parse the empty suite
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(GTestEnumerationSuiteFactory, ParseInvalidString_ThrowsArtifactException)
{
// Given an invalid string
const AZStd::string rawEnum = "!@?";
try
{
// When attempting to parse the empty suite
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(GTestEnumerationSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestEnumerationSuite> expectedSuites = GetTestTargetATestEnumerationSuites();
// Given the raw enum output of TestTargetA
const AZStd::string rawEnum =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<testsuites tests=\"10\" name=\"AllTests\">\n"
" <testsuite name=\"TestCase\" tests=\"7\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"22\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"27\" />\n"
" <testcase name=\"Test3_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"32\" />\n"
" <testcase name=\"Test4_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"37\" />\n"
" <testcase name=\"Test5_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"42\" />\n"
" <testcase name=\"Test6_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"47\" />\n"
" <testcase name=\"Test7_WillFail\" file=\"C:\\Tests\\TestImpactTestTargetA.cpp\" line=\"52\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixture\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp\" line=\"57\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp\" line=\"62\" />\n"
" <testcase name=\"Test3_WillPass\" file=\"C:\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp\" line=\"67\" />\n"
" </testsuite>\n"
"</testsuites>\n"
"\n";
// When the raw enumeration text is parsed
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Expect the generated suite data to match that of the raw enum text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestEnumerationSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestEnumerationSuite> expectedSuites = GetTestTargetBTestEnumerationSuites();
// Given the raw enum output of TestTargetB
const AZStd::string rawEnum =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<testsuites tests=\"112\" name=\"AllTests\">\n"
" <testsuite name=\"TestCase\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"29\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"34\" />\n"
" <testcase name=\"Test3_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"39\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixture\" tests=\"1\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"44\" />\n"
" </testsuite>\n"
" <testsuite name=\"PermutationA/TestFixtureWithParams\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithParams\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" <testcase name=\"Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestImpactTestTargetB.cpp\" line=\"49\" />\n"
" </testsuite>\n"
"</testsuites>\n"
"\n";
// When the raw enumeration text is parsed
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Expect the generated suite data to match that of the raw enum text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestEnumerationSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestEnumerationSuite> expectedSuites = GetTestTargetCTestEnumerationSuites();
// Given the raw enum output of TestTargetC
const AZStd::string rawEnum =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<testsuites tests=\"18\" name=\"AllTests\">\n"
" <testsuite name=\"TestFixture\" tests=\"2\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"32\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"37\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes/0\" tests=\"4\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"42\" />\n"
" <testcase name=\"Test2_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"47\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"52\" />\n"
" <testcase name=\"Test4_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"57\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes/1\" tests=\"4\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"42\" />\n"
" <testcase name=\"Test2_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"47\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"52\" />\n"
" <testcase name=\"Test4_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"57\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes/2\" tests=\"4\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"42\" />\n"
" <testcase name=\"Test2_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"47\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"52\" />\n"
" <testcase name=\"Test4_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"57\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes/3\" tests=\"4\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"42\" />\n"
" <testcase name=\"Test2_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"47\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"52\" />\n"
" <testcase name=\"Test4_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetC\\Code\\Tests\\TestImpactTestTargetC.cpp\" line=\"57\" />\n"
" </testsuite>\n"
"</testsuites>\n"
"\n";
// When the raw enumeration text is parsed
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Expect the generated suite data to match that of the raw enum text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestEnumerationSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestEnumerationSuite> expectedSuites = GetTestTargetDTestEnumerationSuites();
// Given the raw enum output of TestTargetD
const AZStd::string rawEnum =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<testsuites tests=\"249\" name=\"AllTests\">\n"
" <testsuite name=\"TestCase\" tests=\"5\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"56\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"61\" />\n"
" <testcase name=\"Test3_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"66\" />\n"
" <testcase name=\"Test4_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"71\" />\n"
" <testcase name=\"Test5_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"76\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixture1\" tests=\"2\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"81\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"86\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixture2\" tests=\"2\">\n"
" <testcase name=\"Test1_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"91\" />\n"
" <testcase name=\"Test2_WillPass\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"96\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes1/0\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"157\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"162\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"167\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes1/1\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"157\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"162\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"167\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes1/2\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"157\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"162\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"167\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithTypes1/3\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"157\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"162\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"167\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/0\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"172\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"177\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"182\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/1\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"172\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"177\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"182\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/2\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"172\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"177\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"182\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/3\" tests=\"3\">\n"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"172\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"177\" />\n"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"182\" />\n"
" </testsuite>\n"
" <testsuite name=\"PermutationA/TestFixtureWithParams1\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" </testsuite>\n"
" <testsuite name=\"TestFixtureWithParams1\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"101\" />\n"
" </testsuite>\n"
" <testsuite name=\"PermutationA/DISABLED_TestFixtureWithParams2\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" </testsuite>\n"
" <testsuite name=\"DISABLED_TestFixtureWithParams2\" tests=\"54\">\n"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" file=\"C:\\Tests\\TestTargetD\\Code\\Tests\\TestImpactTestTargetD.cpp\" line=\"111\" />\n"
" </testsuite>\n"
"</testsuites>\n"
"\n";
// When the raw enumeration text is parsed
const AZStd::vector<TestImpact::TestEnumerationSuite> suites = TestImpact::GTest::TestEnumerationSuitesFactory(rawEnum);
// Expect the generated suite data to match that of the raw enum text
EXPECT_TRUE(suites == expectedSuites);
}
} // namespace UnitTest

@ -0,0 +1,592 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/regex.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/tuple.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
TEST(GTestRunSuiteFactory, ParseEmptyString_ThrowsArtifactException)
{
// Given an empty string
const AZStd::string rawRun;
try
{
// When attempting to parse the empty suite
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect a artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(GTestRunSuiteFactory, ParseInvalidString_ThrowsArtifactException)
{
// Given an invalid string
const AZStd::string rawRun = "!@?";
try
{
// When attempting to parse the invalid suite
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Do not expect the parsing to succeed
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect a artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(GTestRunSuiteFactory, ParseTestTargetA_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestRunSuite> expectedSuites = GetTestTargetATestRunSuites();
// Given the raw run output of TestTargetA
const AZStd::string rawRun =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<testsuites tests=\"10\" failures=\"1\" disabled=\"0\" errors=\"0\" timestamp=\"2021-03-26T19:02:37\" time=\"0.051\" name=\"AllTests\">"
" <testsuite name=\"TestCase\" tests=\"7\" failures=\"1\" disabled=\"0\" errors=\"0\" time=\"0.003\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test2_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test3_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test4_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test5_WillPass\" status=\"run\" time=\"0.001\" classname=\"TestCase\" />"
" <testcase name=\"Test6_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test7_WillFail\" status=\"run\" time=\"0.001\" classname=\"TestCase\">"
" <failure message=\"C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp:54&#x0A;Failed\" type=\"\"><![CDATA[C:\\Lumberyard\\Code\\Tools\\TestImpactFramework\\Runtime\\Code\\Tests\\TestTargetA\\Code\\Tests\\TestImpactTestTargetA.cpp:54"
"Failed]]></failure>"
" </testcase>"
" </testsuite>"
" <testsuite name=\"TestFixture\" tests=\"3\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.038\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.004\" classname=\"TestFixture\" />"
" <testcase name=\"Test2_WillPass\" status=\"run\" time=\"0\" classname=\"TestFixture\" />"
" <testcase name=\"Test3_WillPass\" status=\"run\" time=\"0.001\" classname=\"TestFixture\" />"
" </testsuite>"
"</testsuites>"
"";
// When the raw run text is parsed
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Expect the generated suite data to match that of the raw run text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestRunSuiteFactory, ParseTestTargetB_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestRunSuite> expectedSuites = GetTestTargetBTestRunSuites();
// Given the raw run output of TestTargetB
const AZStd::string rawRun =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<testsuites tests=\"112\" failures=\"0\" disabled=\"0\" errors=\"0\" timestamp=\"2021-03-27T11:56:14\" time=\"7.155\" name=\"AllTests\">"
" <testsuite name=\"TestCase\" tests=\"3\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.202\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.003\" classname=\"TestCase\" />"
" <testcase name=\"Test2_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test3_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" </testsuite>"
" <testsuite name=\"TestFixture\" tests=\"1\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.062\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.005\" classname=\"TestFixture\" />"
" </testsuite>"
" <testsuite name=\"PermutationA/TestFixtureWithParams\" tests=\"54\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"3.203\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithParams\" tests=\"54\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"3.36\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0.002\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" <testcase name=\"Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams\" />"
" </testsuite>"
"</testsuites>"
"";
// When the raw run text is parsed
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Expect the generated suite data to match that of the raw run text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestRunSuiteFactory, ParseTestTargetC_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestRunSuite> expectedSuites = GetTestTargetCTestRunSuites();
// Given the raw run output of TestTargetC
const AZStd::string rawRun =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<testsuites tests=\"18\" failures=\"0\" disabled=\"0\" errors=\"0\" timestamp=\"2021-03-27T12:35:40\" time=\"1.022\" name=\"AllTests\">"
" <testsuite name=\"TestFixture\" tests=\"2\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.125\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.004\" classname=\"TestFixture\" />"
" <testcase name=\"Test2_WillPass\" status=\"run\" time=\"0\" classname=\"TestFixture\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes/0\" tests=\"4\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.21\">"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/0\" />"
" <testcase name=\"Test2_WillPass\" type_param=\"int\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes/0\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes/0\" />"
" <testcase name=\"Test4_WillPass\" type_param=\"int\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes/0\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes/1\" tests=\"4\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.208\">"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes/1\" />"
" <testcase name=\"Test2_WillPass\" type_param=\"float\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes/1\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/1\" />"
" <testcase name=\"Test4_WillPass\" type_param=\"float\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/1\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes/2\" tests=\"4\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.199\">"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/2\" />"
" <testcase name=\"Test2_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/2\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/2\" />"
" <testcase name=\"Test4_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/2\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes/3\" tests=\"4\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.049\">"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/3\" />"
" <testcase name=\"Test2_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/3\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/3\" />"
" <testcase name=\"Test4_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes/3\" />"
" </testsuite>"
"</testsuites>"
"";
// When the raw run text is parsed
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Expect the generated suite data to match that of the raw run text
EXPECT_TRUE(suites == expectedSuites);
}
TEST(GTestRunSuiteFactory, ParseTestTargetD_ReturnsValidSuitesAndTests)
{
const AZStd::vector<TestImpact::TestRunSuite> expectedSuites = GetTestTargetDTestRunSuites();
// Given the raw run output of TestTargetD
const AZStd::string rawRun =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<testsuites tests=\"249\" failures=\"0\" disabled=\"181\" errors=\"0\" timestamp=\"2021-03-25T15:18:40\" time=\"0.314\" name=\"AllTests\">"
" <testsuite name=\"TestCase\" tests=\"5\" failures=\"0\" disabled=\"1\" errors=\"0\" time=\"0.003\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.001\" classname=\"TestCase\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" status=\"notrun\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test3_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test4_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" <testcase name=\"Test5_WillPass\" status=\"run\" time=\"0\" classname=\"TestCase\" />"
" </testsuite>"
" <testsuite name=\"TestFixture1\" tests=\"2\" failures=\"0\" disabled=\"0\" errors=\"0\" time=\"0.004\">"
" <testcase name=\"Test1_WillPass\" status=\"run\" time=\"0.002\" classname=\"TestFixture1\" />"
" <testcase name=\"Test2_WillPass\" status=\"run\" time=\"0\" classname=\"TestFixture1\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixture2\" tests=\"2\" failures=\"0\" disabled=\"2\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixture2\" />"
" <testcase name=\"Test2_WillPass\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixture2\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes1/0\" tests=\"3\" failures=\"0\" disabled=\"1\" errors=\"0\" time=\"0.001\">"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/0\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"int\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithTypes1/0\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/0\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes1/1\" tests=\"3\" failures=\"0\" disabled=\"1\" errors=\"0\" time=\"0.003\">"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithTypes1/1\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"float\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithTypes1/1\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/1\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes1/2\" tests=\"3\" failures=\"0\" disabled=\"1\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/2\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"double\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithTypes1/2\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/2\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithTypes1/3\" tests=\"3\" failures=\"0\" disabled=\"1\" errors=\"0\" time=\"0.001\">"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/3\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"char\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithTypes1/3\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" status=\"run\" time=\"0\" classname=\"TestFixtureWithTypes1/3\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/0\" tests=\"3\" failures=\"0\" disabled=\"3\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" type_param=\"int\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/0\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"int\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/0\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"int\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/0\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/1\" tests=\"3\" failures=\"0\" disabled=\"3\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" type_param=\"float\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/1\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"float\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/1\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"float\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/1\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/2\" tests=\"3\" failures=\"0\" disabled=\"3\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" type_param=\"double\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/2\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"double\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/2\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"double\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/2\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixtureWithTypes2/3\" tests=\"3\" failures=\"0\" disabled=\"3\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass\" type_param=\"char\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/3\" />"
" <testcase name=\"DISABLED_Test2_WillPass\" type_param=\"char\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/3\" />"
" <testcase name=\"Test3_WillPass\" type_param=\"char\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithTypes2/3\" />"
" </testsuite>"
" <testsuite name=\"PermutationA/TestFixtureWithParams1\" tests=\"54\" failures=\"0\" disabled=\"27\" errors=\"0\" time=\"0.173\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"run\" time=\"0.001\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"run\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/TestFixtureWithParams1\" />"
" </testsuite>"
" <testsuite name=\"TestFixtureWithParams1\" tests=\"54\" failures=\"0\" disabled=\"27\" errors=\"0\" time=\"0.102\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"run\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"run\" time=\"0.001\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"TestFixtureWithParams1\" />"
" </testsuite>"
" <testsuite name=\"PermutationA/DISABLED_TestFixtureWithParams2\" tests=\"54\" failures=\"0\" disabled=\"54\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(1, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(1, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(1, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(1, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(1, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(1, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(1, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(1, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(1, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(2, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(2, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(2, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(2, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(2, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(2, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(2, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(2, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(2, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(4, &apos;\\x3&apos; (3), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(4, &apos;\\x3&apos; (3), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(4, &apos;\\x3&apos; (3), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(4, &apos;\\x5&apos; (5), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(4, &apos;\\x5&apos; (5), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(4, &apos;\\x5&apos; (5), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(4, &apos;\\a&apos; (7), -0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(4, &apos;\\a&apos; (7), 0)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(4, &apos;\\a&apos; (7), 1)\" status=\"notrun\" time=\"0\" classname=\"PermutationA/DISABLED_TestFixtureWithParams2\" />"
" </testsuite>"
" <testsuite name=\"DISABLED_TestFixtureWithParams2\" tests=\"54\" failures=\"0\" disabled=\"54\" errors=\"0\" time=\"0\">"
" <testcase name=\"Test1_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"Test1_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/0\" value_param=\"(8, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/1\" value_param=\"(8, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/2\" value_param=\"(8, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/3\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/4\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/5\" value_param=\"(8, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/6\" value_param=\"(8, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/7\" value_param=\"(8, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/8\" value_param=\"(8, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/9\" value_param=\"(16, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/10\" value_param=\"(16, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/11\" value_param=\"(16, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/12\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/13\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/14\" value_param=\"(16, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/15\" value_param=\"(16, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/16\" value_param=\"(16, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/17\" value_param=\"(16, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/18\" value_param=\"(32, &apos;\\t&apos; (9), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/19\" value_param=\"(32, &apos;\\t&apos; (9), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/20\" value_param=\"(32, &apos;\\t&apos; (9), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/21\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/22\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/23\" value_param=\"(32, &apos;\\r&apos; (13, 0xD), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/24\" value_param=\"(32, &apos;\\x11&apos; (17), -10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/25\" value_param=\"(32, &apos;\\x11&apos; (17), 0.05)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" <testcase name=\"DISABLED_Test2_WillPass/26\" value_param=\"(32, &apos;\\x11&apos; (17), 10)\" status=\"notrun\" time=\"0\" classname=\"DISABLED_TestFixtureWithParams2\" />"
" </testsuite>"
"</testsuites>"
"";
// When the raw run text is parsed
const AZStd::vector<TestImpact::TestRunSuite> suites = TestImpact::GTest::TestRunSuitesFactory(rawRun);
// Expect the generated suite data to match that of the raw run text
EXPECT_TRUE(suites == expectedSuites);
}
} // namespace UnitTest

@ -0,0 +1,239 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Artifact/Factory/TestImpactTestTargetMetaMapFactory.h>
#include <Artifact/TestImpactArtifactException.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
TEST(TestTargetMetaMapFactoryTest, NoRawData_ExpectArtifactException)
{
// Given an empty meta data string
const AZStd::string rawTestTargetMetaData;
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, InvalidRawData_ExpectArtifactException)
{
// Given a raw meta data string of invalid data
const AZStd::string rawTestTargetMetaData = "abcde";
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, EmptyTestMetaArray_ExpectArtifactException)
{
// Given a raw meta data string of valid JSON that contains no tests
const AZStd::string rawTestTargetMetaData =
"{"
" \"google\": {"
" \"test\": {"
" \"tests\": ["
" ]"
" }"
" }"
"}";
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, EmptyName_ExpectArtifactException)
{
// Given a raw meta data string with a test that has no name value
const AZStd::string rawTestTargetMetaData =
"{"
" \"google\": {"
" \"test\": {"
" \"tests\": ["
" { \"name\": \"\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" }"
" ]"
" }"
" }"
"}";
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, EmptyBuildType_ExpectArtifactException)
{
// Given a raw meta data string with a test that has no build type
const AZStd::string rawTestTargetMetaData =
"{"
" \"google\": {"
" \"test\": {"
" \"tests\": ["
" { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"\" }"
" ]"
" }"
" }"
"}";
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, InvalidBuildType_ExpectArtifactException)
{
// Given a raw meta data string with a test that has an invalid build type
const AZStd::string rawTestTargetMetaData =
"{"
" \"google\": {"
" \"test\": {"
" \"tests\": ["
" { \"name\": \"TestName\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"Unknown\" }"
" ]"
" }"
" }"
"}";
try
{
// When attempting to construct the test target
const TestImpact::TestTargetMetaMap testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Do not expect this statement to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ArtifactException& e)
{
// Expect an artifact exception
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST(TestTargetMetaMapFactoryTest, ValidMetaData_ExpectValidTestMetaData)
{
// Given a raw meta data string valid test meta-data
const AZStd::string rawTestTargetMetaData =
"{"
" \"google\": {"
" \"test\": {"
" \"tests\": ["
" { \"name\": \"TestA\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"test_runner\" },"
" { \"name\": \"TestB\", \"namespace\": \"\", \"suite\": \"main\", \"launch_method\": \"test_runner\" },"
" { \"name\": \"TestC\", \"namespace\": \"\", \"suite\": \"\", \"launch_method\": \"test_runner\" },"
" { \"name\": \"TestD\", \"namespace\": \"Legacy\", \"suite\": \"main\", \"launch_method\": \"stand_alone\" }"
" ]"
" }"
" }"
"}";
// When attempting to construct the test target
const auto testTargetMetaData = TestImpact::TestTargetMetaMapFactory(rawTestTargetMetaData);
// Expect the constructed test meta-data to match that of the supplied raw data
EXPECT_EQ(testTargetMetaData.size(), 4);
EXPECT_TRUE(testTargetMetaData.find("TestA") != testTargetMetaData.end());
EXPECT_TRUE((testTargetMetaData.at("TestA") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner}));
EXPECT_TRUE(testTargetMetaData.find("TestB") != testTargetMetaData.end());
EXPECT_TRUE((testTargetMetaData.at("TestB") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::TestRunner}));
EXPECT_TRUE(testTargetMetaData.find("TestC") != testTargetMetaData.end());
EXPECT_TRUE((testTargetMetaData.at("TestC") == TestImpact::TestTargetMeta{"", TestImpact::LaunchMethod::TestRunner}));
EXPECT_TRUE(testTargetMetaData.find("TestD") != testTargetMetaData.end());
EXPECT_TRUE((testTargetMetaData.at("TestD") == TestImpact::TestTargetMeta{"main", TestImpact::LaunchMethod::StandAlone}));
}
} // namespace UnitTest

@ -0,0 +1,718 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Process/Scheduler/TestImpactProcessScheduler.h>
#include <AzCore/Debug/TraceMessageBus.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/std/tuple.h>
#include <AzTest/AzTest.h>
#include <vector>
namespace UnitTest
{
using SchedulerPermutation = AZStd::tuple
<
size_t, // Number of concurrent processes
size_t // Number of processes to launch
>;
class ProcessSchedulerTestFixtureWithParams
: public AllocatorsTestFixture
, private AZ::Debug::TraceMessageBus::Handler
, public ::testing::WithParamInterface<SchedulerPermutation>
{
private:
bool OnOutput(const char*, const char*) override;
void SetUp() override;
void TearDown() override;
protected:
void ConfigurePermutation();
void ExpectSuccessfulLaunch(TestImpact::ProcessId pid);
void ExpectGracefulExit(TestImpact::ProcessId pid);
void ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid);
void ExpectExitCondition(
TestImpact::ProcessId pid, AZStd::optional<TestImpact::ExitCondition> expectedExitCondition = AZStd::nullopt);
void DoNotExpectExitCondition(TestImpact::ProcessId pid);
void ExpectTerminatedProcess(TestImpact::ProcessId pid);
void ExpectTimeoutProcess(TestImpact::ProcessId pid);
void ExpectUnlaunchedProcess(TestImpact::ProcessId pid);
void ExpectLargeStdOutput(TestImpact::ProcessId pid);
void ExpectLargeStdError(TestImpact::ProcessId pid);
void ExpectSmallStdOutput(TestImpact::ProcessId pid);
void ExpectSmallStdError(TestImpact::ProcessId pid);
void DoNotExpectStdOutput(TestImpact::ProcessId pid);
void DoNotExpectStdError(TestImpact::ProcessId pid);
struct ProcessResult
{
AZStd::optional<TestImpact::LaunchResult> m_launchResult;
AZStd::optional<TestImpact::ExitCondition> m_exitStatus;
AZStd::optional<AZStd::chrono::high_resolution_clock::time_point> m_createTime;
AZStd::optional<AZStd::chrono::high_resolution_clock::time_point> m_exitTime;
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds(0);
AZStd::optional<TestImpact::ReturnCode> m_returnCode;
TestImpact::StdContent m_std;
};
size_t m_numMaxConcurrentProcesses = 0;
size_t m_numProcessesToLaunch = 0;
AZStd::unique_ptr<TestImpact::ProcessScheduler> m_processScheduler;
TestImpact::ProcessLaunchCallback m_processLaunchCallback;
TestImpact::ProcessExitCallback m_processExitCallback;
AZStd::vector<TestImpact::ProcessInfo> m_processInfos;
AZStd::vector<ProcessResult> m_processResults;
};
// Permutation values for small process batches
const std::vector<size_t> SmallNumMaxConcurrentProcesses = {1, 4, 8};
const std::vector<size_t> SmallNumProcessesToLaunch = {1, 8, 32};
// Permutation values for large process batches
const std::vector<size_t> LargeNumMaxConcurrentProcesses = {16, 32, 64};
const std::vector<size_t> LargeNumProcessesToLaunch = {128, 256, 512};
void ProcessSchedulerTestFixtureWithParams::ConfigurePermutation()
{
const auto& [numMaxConcurrentProcesses, numProcessesToLaunch] = GetParam();
m_numMaxConcurrentProcesses = numMaxConcurrentProcesses;
m_numProcessesToLaunch = numProcessesToLaunch;
}
// Consume AZ log spam
bool ProcessSchedulerTestFixtureWithParams::OnOutput([[maybe_unused]] const char*, [[maybe_unused]] const char*)
{
return true;
}
void ProcessSchedulerTestFixtureWithParams::SetUp()
{
UnitTest::AllocatorsTestFixture::SetUp();
AZ::Debug::TraceMessageBus::Handler::BusConnect();
ConfigurePermutation();
m_processLaunchCallback = [this](
TestImpact::ProcessId pid,
TestImpact::LaunchResult launchResult,
AZStd::chrono::high_resolution_clock::time_point createTime)
{
m_processResults[pid].m_launchResult = launchResult;
m_processResults[pid].m_createTime.emplace(createTime);
return TestImpact::CallbackResult::Continue;
};
m_processExitCallback = [this](
TestImpact::ProcessId pid,
TestImpact::ExitCondition exitStatus,
TestImpact::ReturnCode returnCode,
TestImpact::StdContent&& std,
AZStd::chrono::high_resolution_clock::time_point exitTime)
{
m_processResults[pid].m_std = std::move(std);
m_processResults[pid].m_returnCode.emplace(returnCode);
m_processResults[pid].m_exitStatus = exitStatus;
m_processResults[pid].m_exitTime = exitTime;
m_processResults[pid].m_duration =
AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(exitTime - *m_processResults[pid].m_createTime);
return TestImpact::CallbackResult::Continue;
};
m_processResults.resize(m_numProcessesToLaunch);
}
void ProcessSchedulerTestFixtureWithParams::TearDown()
{
AZ::Debug::TraceMessageBus::Handler::BusDisconnect();
UnitTest::AllocatorsTestFixture::TearDown();
}
// Expects the process to have exited under the specified circumstances
void ProcessSchedulerTestFixtureWithParams::ExpectExitCondition(
TestImpact::ProcessId pid, AZStd::optional<TestImpact::ExitCondition> expectedExitCondition)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
// Expect the processes to have exited
EXPECT_TRUE(exitStatus.has_value());
// Expect the return code to be valid
EXPECT_TRUE(returnCode.has_value());
if (expectedExitCondition.has_value())
{
// Expect the process to have exited under the specified conditions
EXPECT_EQ(exitStatus, expectedExitCondition);
// Expect the return code to be that of the process's is (for gracefull exists) or that of the error code
// associated with the abnormal exit condition
EXPECT_EQ(returnCode,
expectedExitCondition == TestImpact::ExitCondition::Gracefull
? pid
: static_cast<TestImpact::ReturnCode>(*exitStatus));
}
// Expect the duration to be non zero and the create time and exit time to have values as the processes has been in-flight
EXPECT_GT(duration.count(), 0);
EXPECT_TRUE(createTime.has_value());
EXPECT_TRUE(exitTime.has_value());
// Expect the exit time to be later than the start time
EXPECT_GT(exitTime, createTime);
}
// Expects the process to not have exited for to lack of being in-flight
void ProcessSchedulerTestFixtureWithParams::DoNotExpectExitCondition(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
// Expect the processes to not have exited as the process has not been launched successfully
EXPECT_FALSE(exitStatus.has_value());
// Expect the return code to be invalid
EXPECT_FALSE(returnCode.has_value());
// Expect the duration to be zero as the process has not been in-flight
EXPECT_EQ(duration.count(), 0);
// Do not expect the exit time to have a value
EXPECT_FALSE(exitTime.has_value());
}
// Expects the process to have been launched successfully, makes no assumptions about how it exited
void ProcessSchedulerTestFixtureWithParams::ExpectSuccessfulLaunch(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
// Expect a launch to have been attempted by this process (not still in queue)
EXPECT_TRUE(launchResult.has_value());
// Expect the process to have launched successfully
EXPECT_EQ(launchResult, TestImpact::LaunchResult::Success);
}
// Expects the process to have failed to launch
void ProcessSchedulerTestFixtureWithParams::ExpectUnsuccessfulLaunch(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
// Expect a launch to have been attempted by this process (not still in queue)
EXPECT_TRUE(launchResult.has_value());
// Expect the process to have launched unsuccessfully
EXPECT_EQ(launchResult, TestImpact::LaunchResult::Failure);
// Expect the create time to have a value as the process was technically created
EXPECT_TRUE(createTime.has_value());
DoNotExpectExitCondition(pid);
}
// Expects the process to have exited gracefully of its own accord (i.e. not terminated for any reason by the scheduler)
void ProcessSchedulerTestFixtureWithParams::ExpectGracefulExit(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
ExpectSuccessfulLaunch(pid);
ExpectExitCondition(pid, TestImpact::ExitCondition::Gracefull);
}
// Expects the process to have been terminated by the client or scheduler
void ProcessSchedulerTestFixtureWithParams::ExpectTerminatedProcess(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
ExpectSuccessfulLaunch(pid);
ExpectExitCondition(pid, TestImpact::ExitCondition::Terminated);
}
// Expects the process to have been terminated by the scheduler due to the process or scheduler timing out
void ProcessSchedulerTestFixtureWithParams::ExpectTimeoutProcess(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
ExpectSuccessfulLaunch(pid);
ExpectExitCondition(pid, TestImpact::ExitCondition::Timeout);
}
// Expects the process to have not been attempted to launch (was still in the queue)
void ProcessSchedulerTestFixtureWithParams::ExpectUnlaunchedProcess(TestImpact::ProcessId pid)
{
const auto& [launchResult, exitStatus, createTime, exitTime, duration, returnCode, std] = m_processResults[pid];
// Expect a launch to not have been attempted by this process (still in queue)
EXPECT_FALSE(launchResult.has_value());
// Expect the create time to have a value as the process was technically created
EXPECT_FALSE(createTime.has_value());
DoNotExpectExitCondition(pid);
}
// Expects the std output to have been captured from the process with a large volume of text
void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdOutput(TestImpact::ProcessId pid)
{
const auto& stdOut = m_processResults[pid].m_std.m_out;
// Expect standard output to have a value
EXPECT_TRUE(stdOut.has_value());
// Expect the output length to be that of the large text output from the child process
EXPECT_EQ(stdOut.value().length(), LargeTextSize);
}
// Expects the std error to have been captured from the process with a large volume of text
void ProcessSchedulerTestFixtureWithParams::ExpectLargeStdError(TestImpact::ProcessId pid)
{
const auto& stdErr = m_processResults[pid].m_std.m_err;
// Expect standard error to have a value
EXPECT_TRUE(stdErr.has_value());
// Expect the error length to be that of the large text output from the child process
EXPECT_EQ(stdErr.value().length(), LargeTextSize);
}
// Expects the std output to have been captured from the process with a small known text string
void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdOutput(TestImpact::ProcessId pid)
{
const auto& stdOut = m_processResults[pid].m_std.m_out;
// Expect standard output to have a value
EXPECT_TRUE(stdOut.has_value());
// Expect the output to match the known stdout of the child
EXPECT_EQ(stdOut.value(), KnownTestProcessOutputString(pid));
}
// Expects the std error to have been captured from the process with a small known text string
void ProcessSchedulerTestFixtureWithParams::ExpectSmallStdError(TestImpact::ProcessId pid)
{
const auto& stdErr = m_processResults[pid].m_std.m_err;
// Expect standard error to have a value
EXPECT_TRUE(stdErr.has_value());
// Expect the output to match the known stderr of the child
EXPECT_EQ(stdErr.value(), KnownTestProcessErrorString(pid));
}
// Expects no standard output from the child process
void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdOutput(TestImpact::ProcessId pid)
{
const auto& stdOut = m_processResults[pid].m_std.m_out;
// Do not expect standard output to have a value
EXPECT_FALSE(stdOut.has_value());
}
// Expects no standard error from the child process
void ProcessSchedulerTestFixtureWithParams::DoNotExpectStdError(TestImpact::ProcessId pid)
{
const auto& stdErr = m_processResults[pid].m_std.m_err;
// Do not expect standard error to have a value
EXPECT_FALSE(stdErr.has_value());
}
TEST_P(ProcessSchedulerTestFixtureWithParams, ValidProcesses_SuccessfulLaunches)
{
// Given a set of processes to launch with valid arguments
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
ExpectGracefulExit(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, ValidAndInvalidProcesses_LaunchSuccessesAndFailures)
{
const size_t invalidProcessGroup = 4;
// Given a mixture of processes to launch with valid and invalid arguments
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid % invalidProcessGroup == 0)
{
m_processInfos.emplace_back(
pid,
InvalidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
else
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
}
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid % invalidProcessGroup == 0)
{
ExpectUnsuccessfulLaunch(pid);
}
else
{
ExpectGracefulExit(pid);
}
DoNotExpectStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessTimeouts_InFlightProcessesTimeout)
{
const size_t timeoutProcessGroup = 4;
// Given a mixture of processes to launch with some processes sleeping indefinately
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid % timeoutProcessGroup == 0)
{
m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, LongSleep));
}
else
{
m_processInfos.emplace_back(pid, ValidProcessPath, ConstructTestProcessArgs(pid, NoSleep));
}
}
// When the process scheduler launches the processes with a process timeout value
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::chrono::milliseconds(100),
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid % timeoutProcessGroup == 0)
{
ExpectTimeoutProcess(pid);
}
else
{
ExpectExitCondition(pid);
}
DoNotExpectStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(
ProcessSchedulerTestFixtureWithParams, ProcessLaunchCallbackAbort_InFlightProcessesTerminatedAndQueuedProcessesUnlaunched)
{
const size_t processToAbort = 8;
// Given a set of processes to launch
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
// Given a launch callback that will return the abort value for the process to abort
const auto abortingLaunchCallback = [this, processToAbort](
TestImpact::ProcessId pid,
TestImpact::LaunchResult launchResult,
AZStd::chrono::high_resolution_clock::time_point createTime)
{
m_processLaunchCallback(pid, launchResult, createTime);
if (pid == processToAbort)
{
return TestImpact::CallbackResult::Abort;
}
else
{
return TestImpact::CallbackResult::Continue;
}
};
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
abortingLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid < processToAbort)
{
ExpectSuccessfulLaunch(pid);
}
else if (pid == processToAbort)
{
ExpectTerminatedProcess(pid);
}
else
{
ExpectUnlaunchedProcess(pid);
}
DoNotExpectStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, ProcessExitCallbackAbort_InFlightProcessesTerminated)
{
const size_t processToAbort = 4;
// Given a set of processes to launch
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
// Given an exit callback that will return the abort value for the process to abort
const auto abortingExitCallback = [this, processToAbort](
TestImpact::ProcessId pid,
TestImpact::ExitCondition exitStatus,
TestImpact::ReturnCode returnCode,
TestImpact::StdContent&& std,
AZStd::chrono::high_resolution_clock::time_point exitTime)
{
m_processExitCallback(pid, exitStatus, returnCode, std::move(std), exitTime);
if (pid == processToAbort)
{
return TestImpact::CallbackResult::Abort;
}
else
{
return TestImpact::CallbackResult::Continue;
}
};
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
abortingExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid < processToAbort)
{
ExpectSuccessfulLaunch(pid);
}
else if (pid == processToAbort)
{
ExpectGracefulExit(pid);
}
else
{
if (m_processResults[pid].m_launchResult.has_value())
{
ExpectSuccessfulLaunch(pid);
}
else
{
ExpectUnlaunchedProcess(pid);
}
}
DoNotExpectStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, SchedulerTimeout_QueuedAndInFlightProcessesTerminated)
{
const size_t processToTimeout = 0;
// Given a set of processes to launch where one process will sleep indefinately
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid == processToTimeout)
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, LongSleep));
}
else
{
m_processInfos.emplace_back(
pid,
ValidProcessPath,
ConstructTestProcessArgs(pid, NoSleep));
}
}
// When the process scheduler launches the processes with a scheduler timeout value
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::chrono::milliseconds(100)
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
if (pid == processToTimeout)
{
ExpectTimeoutProcess(pid);
}
else
{
if (m_processResults[pid].m_launchResult.has_value())
{
ExpectSuccessfulLaunch(pid);
}
else
{
ExpectUnlaunchedProcess(pid);
}
}
DoNotExpectStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdOut_StdOutputIsLargeTextString)
{
// Given a set of processes to launch
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
m_processInfos.emplace_back(
pid,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
ConstructTestProcessArgsLargeText(pid, NoSleep));
}
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
ExpectGracefulExit(pid);
ExpectLargeStdOutput(pid);
DoNotExpectStdError(pid);
}
}
TEST_P(ProcessSchedulerTestFixtureWithParams, RedirectStdError_StdErrorIsLargeTextString)
{
// Given a set of processes to launch
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
m_processInfos.emplace_back(
pid,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
ConstructTestProcessArgsLargeText(pid, NoSleep));
}
// When the process scheduler launches the processes
m_processScheduler = AZStd::make_unique<TestImpact::ProcessScheduler>(
m_processInfos,
m_processLaunchCallback,
m_processExitCallback,
m_numMaxConcurrentProcesses,
AZStd::nullopt,
AZStd::nullopt
);
for (size_t pid = 0; pid < m_numProcessesToLaunch; pid++)
{
ExpectGracefulExit(pid);
DoNotExpectStdOutput(pid);
ExpectLargeStdError(pid);
}
}
INSTANTIATE_TEST_CASE_P(
SmallConcurrentProcesses,
ProcessSchedulerTestFixtureWithParams,
::testing::Combine(
::testing::ValuesIn(SmallNumMaxConcurrentProcesses),
::testing::ValuesIn(SmallNumProcessesToLaunch)
));
INSTANTIATE_TEST_CASE_P(
LargeConcurrentProcesses,
ProcessSchedulerTestFixtureWithParams,
::testing::Combine(
::testing::ValuesIn(LargeNumMaxConcurrentProcesses),
::testing::ValuesIn(LargeNumProcessesToLaunch)
));
} // namespace UnitTest

@ -0,0 +1,491 @@
/*
* 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 <TestImpactTestUtils.h>
#include <Process/TestImpactProcess.h>
#include <Process/TestImpactProcessLauncher.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/std/string/conversions.h>
#include <AzTest/AzTest.h>
namespace UnitTest
{
class ProcessTestFixture
: public AllocatorsTestFixture
{
protected:
void SetUp() override
{
UnitTest::AllocatorsTestFixture::SetUp();
}
void TearDown() override
{
if (m_process && m_process->IsRunning())
{
m_process->Terminate(0);
}
UnitTest::AllocatorsTestFixture::TearDown();
}
AZStd::unique_ptr<TestImpact::Process> m_process;
static constexpr const TestImpact::ProcessId m_id = 1;
static constexpr const TestImpact::ReturnCode m_terminateErrorCode = 666;
};
TEST_F(ProcessTestFixture, LaunchValidProcess_ProcessReturnsSuccessfully)
{
// Given a process launched with a valid binary
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process has exited
m_process->BlockUntilExit();
// Expect the process to no longer be running
EXPECT_FALSE(m_process->IsRunning());
}
TEST_F(ProcessTestFixture, LaunchInvalidProcessInfo_ThrowsProcessException)
{
try
{
// Given a process launched with an invalid binary
TestImpact::ProcessInfo processInfo(m_id, "");
// Do not expect this code to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ProcessException& e)
{
// Except a process exception to be thrown
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(ProcessTestFixture, LaunchInvalidBinary_ThrowsProcessException)
{
try
{
// Given a process launched with an ivalid binary
TestImpact::ProcessInfo processInfo(m_id, "#!#zz:/z/z/z.exe.z@");
// When the process is attempted launch
m_process = TestImpact::LaunchProcess(processInfo);
// Do not expect this code to be reachable
FAIL();
}
catch ([[maybe_unused]] const TestImpact::ProcessException& e)
{
// Except a process exception to be thrown
SUCCEED();
}
catch (...)
{
// Do not expect any other exceptions
FAIL();
}
}
TEST_F(ProcessTestFixture, GetProcessInfo_ReturnsProcessInfo)
{
// Given a process launched with a valid binary and args
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process has exited
m_process->BlockUntilExit();
// Expect the retrieved process info to match to process info used to launch the process
EXPECT_EQ(m_process->GetProcessInfo().GetId(), m_id);
EXPECT_EQ(m_process->GetProcessInfo().GetProcessPath(), ValidProcessPath);
EXPECT_EQ(m_process->GetProcessInfo().GetStartupArgs(), args);
}
TEST_F(ProcessTestFixture, GetReturnCodeAfterExit_ReturnsArg)
{
// Given a process launched with a valid binary and args
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process has exited
m_process->BlockUntilExit();
// Expect the process to no longer be running
EXPECT_FALSE(m_process->IsRunning());
// Expect the return value to be the process id
EXPECT_EQ(m_process->GetReturnCode(), m_id);
}
TEST_F(ProcessTestFixture, GetReturnCodeInFlight_ReturnsNullOpt)
{
// Given a process launched with a valid binary and args to sleep for 100 seconds
const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep);
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args);
// When the process is running
m_process = TestImpact::LaunchProcess(processInfo);
// Expect the return value to be empty
EXPECT_EQ(m_process->GetReturnCode(), AZStd::nullopt);
}
TEST_F(ProcessTestFixture, TerminateWithErrorCode_ReturnsErrorCode)
{
// Given a process launched with a valid binary and args to sleep for 100 seconds
const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep);
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process is terminated by the client with the specified error code
m_process->Terminate(m_terminateErrorCode);
// Expect the return value to be the error code specified in the Terminate call
EXPECT_EQ(m_process->GetReturnCode(), m_terminateErrorCode);
// Expect the process to no longer be running
EXPECT_FALSE(m_process->IsRunning());
}
TEST_F(ProcessTestFixture, CheckIsRunningWhilstRunning_ProcessIsRunning)
{
// Given a process launched with a valid binary and args to sleep for 100 seconds
const AZStd::string args = ConstructTestProcessArgs(m_id, LongSleep);
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath, args);
// When the process is running
m_process = TestImpact::LaunchProcess(processInfo);
// Expect the process to be running
EXPECT_TRUE(m_process->IsRunning());
}
TEST_F(ProcessTestFixture, CheckIsRunningWhilstNotRunning_ReturnsFalse)
{
// Given a process launched with a valid binary
TestImpact::ProcessInfo processInfo(m_id, ValidProcessPath);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process is terminated by the client
m_process->Terminate(0);
// Expect the process to be running
EXPECT_FALSE(m_process->IsRunning());
}
TEST_F(ProcessTestFixture, RedirectStdOut_OutputIsKnownTestProcessOutputString)
{
// Given a process launched with a valid binary and standard output redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Expect stdoutput to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput());
// Do not expect stdouterr to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to match the known stdout of the child
EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id));
}
TEST_F(ProcessTestFixture, RedirectStdErr_OutputIsKnownTestProcessOutputString)
{
// Given a process launched with a valid binary and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Do not expect stdoutput to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput());
// Expect stderror to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to match the known stdout of the child
EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id));
}
TEST_F(ProcessTestFixture, RedirectStdOutAndTerminate_OutputIsEmpty)
{
// Given a process launched with a valid binary and standard output redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process is terminated
m_process->Terminate(0);
// Expect stdoutput to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput());
// Do not expect stdouterr to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
}
TEST_F(ProcessTestFixture, RedirectStdErrAndTerminate_OutputIsEmpty)
{
// Given a process launched with a valid binary and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process is terminated
m_process->Terminate(0);
// Do not expect stdoutput to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput());
// Expect stderror to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to be empty
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
TEST_F(ProcessTestFixture, RedirectStdOutAndStdErrorRouting_OutputsAreKnownTestProcessOutputStrings)
{
// Given a process launched with a valid binary and both standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Expect stdoutput to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdOutput());
// Expect stderror to be rerouted to the parent
EXPECT_TRUE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to match the known stdout and stderr of the child
EXPECT_EQ(m_process->ConsumeStdOut(), KnownTestProcessOutputString(m_id));
EXPECT_EQ(m_process->ConsumeStdErr(), KnownTestProcessErrorString(m_id));
}
TEST_F(ProcessTestFixture, NoStdOutOrStdErrRedirect_OutputIsEmpty)
{
// Given a process launched with a valid binary and both standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgs(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Do not expect stdoutput to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdOutput());
// Do not expect stdouterr to be rerouted to the parent
EXPECT_FALSE(m_process->GetProcessInfo().ParentHasStdError());
// Expect the output to to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
TEST_F(ProcessTestFixture, LargePipeNoRedirects_OutputsAreEmpty)
{
// Given a process outputting large text with no standard output and no standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Expect the output to to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
TEST_F(ProcessTestFixture, LargePipeNoRedirectsAndTerminated_OutputsAreEmpty)
{
// Given a process outputting large text with no standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
// When the process is running
m_process = TestImpact::LaunchProcess(processInfo);
// Expect the output to to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
// When the process is terminated
m_process->Terminate(0);
// Expect the output to to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_OutputsAreEmpty)
{
// Given a process outputting large text with no standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, ShortSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::None,
TestImpact::StdErrorRouting::None,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the outputs are read as the process is running
while (m_process->IsRunning())
{
// Expect the outputs to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
}
TEST_F(ProcessTestFixture, LargePipeRedirectsAndTerminated_OutputsAreEmpty)
{
// Given a process outputting large text with both standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, LongSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process is terminated
m_process->Terminate(0);
// Expect the outputs to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
TEST_F(ProcessTestFixture, LargePipeRedirectsAndBlockedUntilExit_OutputsAreLargeTextFileSize)
{
// Given a process outputting large text with both standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath,
args);
m_process = TestImpact::LaunchProcess(processInfo);
// When the process exits
m_process->BlockUntilExit();
// Expect the output lengths to be that of the large text output from the child process
EXPECT_EQ(m_process->ConsumeStdOut().value().length(), LargeTextSize);
EXPECT_EQ(m_process->ConsumeStdErr().value().length(), LargeTextSize);
}
TEST_F(ProcessTestFixture, LargePipeRedirectsAndReadWhilstRunning_TotalOutputsAreLargeTextFileSize)
{
// Given a process outputting large text with both standard output and standard error redirected to parent
const AZStd::string args = ConstructTestProcessArgsLargeText(m_id, NoSleep);
TestImpact::ProcessInfo processInfo(
m_id,
TestImpact::StdOutputRouting::ToParent,
TestImpact::StdErrorRouting::ToParent,
ValidProcessPath, args);
m_process = TestImpact::LaunchProcess(processInfo);
size_t stdOutBytes = 0;
size_t stdErrBytes = 0;
while (m_process->IsRunning())
{
// When the outputs are read as the process is running
if (auto output = m_process->ConsumeStdOut(); output.has_value())
{
stdOutBytes += output.value().length();
}
if (auto output = m_process->ConsumeStdErr(); output.has_value())
{
stdErrBytes += output.value().length();
}
}
// Expect the output lengths to be that of the large text output from the child process
EXPECT_EQ(stdOutBytes, LargeTextSize);
EXPECT_EQ(stdErrBytes, LargeTextSize);
// Expect the outputs to be empty
EXPECT_FALSE(m_process->ConsumeStdOut().has_value());
EXPECT_FALSE(m_process->ConsumeStdErr().has_value());
}
} // namespace UnitTest

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save