Merge branch 'development' into redcode/crythread-2nd-pass

Signed-off-by: AMZN-ScottR <24445312+AMZN-ScottR@users.noreply.github.com>
This commit is contained in:
AMZN-ScottR
2021-08-11 20:31:58 -07:00
1650 changed files with 39333 additions and 62370 deletions
@@ -13,3 +13,27 @@ set_target_properties(AssetProcessor PROPERTIES
RESOURCE ${CMAKE_CURRENT_SOURCE_DIR}/Platform/Mac/Images.xcassets
XCODE_ATTRIBUTE_ASSETCATALOG_COMPILER_APPICON_NAME AssetProcessorAppIcon
)
# We cannot use ly_add_target here because we're already including this file from inside ly_add_target
# So we need to setup target, dependencies and install logic manually.
add_executable(AssetProcessorDummy Platform/Mac/main_dummy.cpp)
add_executable(AZ::AssetProcessorDummy ALIAS AssetProcessorDummy)
ly_target_link_libraries(AssetProcessorDummy
PRIVATE
AZ::AzCore
AZ::AzFramework)
ly_add_dependencies(AssetProcessor AssetProcessorDummy)
# Store the aliased target into a DIRECTORY property
set_property(DIRECTORY APPEND PROPERTY LY_DIRECTORY_TARGETS AZ::AssetProcessorDummy)
# Store the directory path in a GLOBAL property so that it can be accessed
# in the layout install logic. Skip if the directory has already been added
get_property(ly_all_target_directories GLOBAL PROPERTY LY_ALL_TARGET_DIRECTORIES)
if(NOT CMAKE_CURRENT_SOURCE_DIR IN_LIST ly_all_target_directories)
set_property(GLOBAL APPEND PROPERTY LY_ALL_TARGET_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR})
endif()
ly_install_add_install_path_setreg(AssetProcessor)
@@ -11,7 +11,7 @@
<key>CFBundleSignature</key>
<string>ASPR</string>
<key>CFBundleExecutable</key>
<string>AssetProcessor</string>
<string>AssetProcessorDummy</string>
<key>CFBundleIdentifier</key>
<string>com.Amazon.AssetProcessor</string>
</dict>
@@ -0,0 +1,75 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <AzCore/Component/ComponentApplication.h>
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
#include <AzCore/Utils/Utils.h>
#include <AzFramework/Process/ProcessWatcher.h>
#include <cstdlib>
int main(int argc, char* argv[])
{
// Create a ComponentApplication to initialize the AZ::SystemAllocator and initialize the SettingsRegistry
AZ::ComponentApplication::Descriptor desc;
AZ::ComponentApplication application;
application.Create(desc);
AZStd::vector<AZStd::string> envVars;
const char* homePath = std::getenv("HOME");
envVars.push_back(AZStd::string::format("HOME=%s", homePath));
if (auto settingsRegistry = AZ::SettingsRegistry::Get(); settingsRegistry != nullptr)
{
const char* dyldLibPathOrig = std::getenv("DYLD_LIBRARY_PATH");
AZStd::string dyldSearchPath = AZStd::string::format("DYLD_LIBRARY_PATH=%s", dyldLibPathOrig);
if (AZ::IO::FixedMaxPath projectModulePath;
settingsRegistry->Get(projectModulePath.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectConfigurationBinPath))
{
dyldSearchPath.append(":");
dyldSearchPath.append(projectModulePath.c_str());
}
if (AZ::IO::FixedMaxPath installedBinariesFolder;
settingsRegistry->Get(installedBinariesFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_InstalledBinaryFolder))
{
if (AZ::IO::FixedMaxPath engineRootFolder;
settingsRegistry->Get(engineRootFolder.Native(), AZ::SettingsRegistryMergeUtils::FilePathKey_EngineRootFolder))
{
installedBinariesFolder = engineRootFolder / installedBinariesFolder;
dyldSearchPath.append(":");
dyldSearchPath.append(installedBinariesFolder.c_str());
}
}
envVars.push_back(dyldSearchPath);
}
AZStd::string commandArgs;
for (int i = 1; i < argc; i++)
{
commandArgs.append(argv[i]);
commandArgs.append(" ");
}
AzFramework::ProcessLauncher::ProcessLaunchInfo processLaunchInfo;
AZ::IO::Path processPath{ AZ::IO::PathView(AZ::Utils::GetExecutableDirectory()) };
processPath /= "AssetProcessor";
processLaunchInfo.m_processExecutableString = AZStd::move(processPath.Native());
processLaunchInfo.m_commandlineParameters = commandArgs;
processLaunchInfo.m_environmentVariables = &envVars;
processLaunchInfo.m_showWindow = true;
AzFramework::ProcessWatcher* processWatcher = AzFramework::ProcessWatcher::LaunchProcess(processLaunchInfo, AzFramework::ProcessCommunicationType::COMMUNICATOR_TYPE_NONE);
application.Destroy();
return 0;
}
@@ -10,6 +10,7 @@
#if !defined(Q_MOC_RUN)
#include <AzCore/std/containers/list.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <QSortFilterProxyModel>
#endif
@@ -195,7 +195,7 @@ namespace AssetProcessor
AZ::IO::Path currentFullFolderPath;
const AZ::IO::PathView filename = productNamePath.Filename();
const AZ::IO::PathView fullPathWithoutFilename = productNamePath.RemoveFilename();
AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath;
AZ::IO::FixedMaxPathString currentPath;
for (auto pathIt = fullPathWithoutFilename.begin(); pathIt != fullPathWithoutFilename.end(); ++pathIt)
{
currentPath = pathIt->FixedMaxPathString();
@@ -236,7 +236,7 @@ namespace AssetProcessor
}
AZStd::shared_ptr<ProductAssetTreeItemData> productItemData =
ProductAssetTreeItemData::MakeShared(&product, product.m_productName, AZStd::fixed_string<AZ::IO::MaxPathLength>(filename.Native()).c_str(), false, sourceId);
ProductAssetTreeItemData::MakeShared(&product, product.m_productName, AZ::IO::FixedMaxPathString(filename.Native()).c_str(), false, sourceId);
m_productToTreeItem[product.m_productName] =
parentItem->CreateChild(productItemData);
m_productIdToTreeItem[product.m_productID] = m_productToTreeItem[product.m_productName];
@@ -10,6 +10,7 @@
#if !defined(Q_MOC_RUN)
#include "AssetDetailsPanel.h"
#include <AzCore/std/smart_ptr/shared_ptr.h>
#include <QScopedPointer>
#endif
@@ -94,7 +94,7 @@ namespace AssetProcessor
AZ::IO::Path currentFullFolderPath(AZ::IO::PosixPathSeparator);
const AZ::IO::FixedMaxPath filename = fullPath.Filename();
fullPath.RemoveFilename();
AZStd::fixed_string<AZ::IO::MaxPathLength> currentPath;
AZ::IO::FixedMaxPathString currentPath;
for (auto pathIt = fullPath.begin(); pathIt != fullPath.end(); ++pathIt)
{
currentPath = pathIt->FixedMaxPathString();
@@ -125,7 +125,7 @@ namespace AssetProcessor
}
m_sourceToTreeItem[source.m_sourceName] =
parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, AZStd::fixed_string<AZ::IO::MaxPathLength>(filename.Native()).c_str(), false));
parentItem->CreateChild(SourceAssetTreeItemData::MakeShared(&source, &scanFolder, source.m_sourceName, AZ::IO::FixedMaxPathString(filename.Native()).c_str(), false));
m_sourceIdToTreeItem[source.m_sourceID] = m_sourceToTreeItem[source.m_sourceName];
if (!modelIsResetting)
{
@@ -28,7 +28,7 @@ namespace AssetProcessor
//! Amount of time in seconds to wait for a builder to start up and connect
// sometimes, builders take a long time to start because of things like virus scanners scanning each
// builder DLL, so we give them a large margin.
static const int s_StartupConnectionWaitTimeS = 120;
static const int s_StartupConnectionWaitTimeS = 300;
static const int s_MillisecondsInASecond = 1000;
@@ -11,6 +11,8 @@
#pragma once
#include <AzCore/PlatformDef.h>
#include <AzCore/std/string/conversions.h>
#include <AzCore/Utils/Utils.h>
#include <algorithm>
#include <string>
@@ -23,30 +25,24 @@
namespace CrashHandler
{
std::string GetTimeString();
void GetExecutablePathA(char* pathBuffer, int& bufferSize);
void GetExecutablePathW(wchar_t* pathBuffer, int& bufferSize);
void GetTimeInfo(tm& curTime);
template <typename T>
inline void GetExecutablePath(T& returnPath)
inline void GetExecutablePath(std::string& returnPath)
{
char currentFileName[CRASH_HANDLER_MAX_PATH_LEN] = { 0 };
int bufferLen{ CRASH_HANDLER_MAX_PATH_LEN };
GetExecutablePathA(currentFileName, bufferLen);
AZ::Utils::GetExecutablePath(currentFileName, CRASH_HANDLER_MAX_PATH_LEN);
returnPath = currentFileName;
std::replace(returnPath.begin(), returnPath.end(), '\\', '/');
}
template <>
inline void GetExecutablePath<std::wstring>(std::wstring& returnPath)
inline void GetExecutablePath(std::wstring& returnPathW)
{
wchar_t currentFileName[CRASH_HANDLER_MAX_PATH_LEN] = { 0 };
int bufferLen{ CRASH_HANDLER_MAX_PATH_LEN };
GetExecutablePathW(currentFileName, bufferLen);
returnPath = currentFileName;
std::replace(returnPath.begin(), returnPath.end(), '\\', '/');
std::string returnPath;
GetExecutablePath(returnPath);
wchar_t currentFileNameW[CRASH_HANDLER_MAX_PATH_LEN] = { 0 };
AZStd::to_wstring(currentFileNameW, CRASH_HANDLER_MAX_PATH_LEN, { returnPath.c_str(), returnPath.size() });
returnPathW = currentFileNameW;
}
template<typename T>
@@ -10,13 +10,14 @@
#include <algorithm>
#include <AzCore/PlatformIncl.h>
#include <AzCore/Utils/Utils.h>
#include <time.h>
namespace CrashHandler
{
void GetExecutablePathA(char* pathBuffer, int& bufferSize)
void GetExecutablePath(char* pathBuffer, int& bufferSize)
{
GetModuleFileNameA(nullptr, pathBuffer, bufferSize);
AZ::Utils::GetExecutablePath(pathBuffer, bufferSize);
}
void GetExecutablePathW(wchar_t* pathBuffer, int& bufferSize)
+5 -11
View File
@@ -358,17 +358,11 @@ GridHubComponent::GridHubComponent()
m_isLogToFile = false;
#ifdef AZ_PLATFORM_WINDOWS
TCHAR name[MAX_COMPUTERNAME_LENGTH + 1];
wchar_t name[MAX_COMPUTERNAME_LENGTH + 1];
DWORD dwCompNameLen = AZ_ARRAY_SIZE(name);
if ( GetComputerName(name, &dwCompNameLen) != 0 )
if (GetComputerName(name, &dwCompNameLen) != 0)
{
#ifdef _UNICODE
char c[MAX_COMPUTERNAME_LENGTH + 1];
wcstombs(c, name, AZ_ARRAY_SIZE(c));
m_hubName = c;
#else
m_hubName = name;
#endif
AZStd::to_string(m_hubName, name);
}
else
#endif
@@ -549,7 +543,7 @@ GridHubComponent::OnMemberJoined([[maybe_unused]] GridMate::GridSession* session
case AZ::PlatformID::PLATFORM_WINDOWS_64:
case AZ::PlatformID::PLATFORM_APPLE_MAC:
{
GridMate::string localMachineName = GridMate::Utils::GetMachineAddress();
AZStd::string localMachineName = GridMate::Utils::GetMachineAddress();
if( member->GetMachineName() == localMachineName )
{
ExternalProcessMonitor mi;
@@ -635,7 +629,7 @@ bool GridHubComponent::StartSession(bool isRestarting)
AZ_Assert(GridMate::HasGridMateService<GridMate::LANSessionService>(m_gridMate), "Failed to start multiplayer service for LAN!");
// if we get an address 169.X.X.X (AZCP is NOT ready) or 127.0.0.1 when network is not ready
GridMate::string machineIP = GridMate::Utils::GetMachineAddress();
AZStd::string machineIP = GridMate::Utils::GetMachineAddress();
if( machineIP == "127.0.0.1" || machineIP.compare(0,4,"169.") == 0 )
{
AZ_Warning("GridHub", false, "\nCurrent IP %s might be invalid.\n",machineIP.c_str());
+1 -1
View File
@@ -141,7 +141,7 @@ public:
/// Callback that notifies the title when a session will be left. session pointer is NOT valid after the callback returns.
void OnSessionDelete(GridMate::GridSession* session) override;
/// Called when a session error occurs.
void OnSessionError(GridMate::GridSession* session, const GridMate::string& errorMsg ) { (void)session; (void)errorMsg; }
void OnSessionError(GridMate::GridSession* session, const AZStd::string& errorMsg ) { (void)session; (void)errorMsg; }
/// Called when the actual game(match) starts
void OnSessionStart(GridMate::GridSession* session) { (void)session; }
/// Called when the actual game(match) ends
+53 -53
View File
@@ -16,7 +16,6 @@
#include <shlguid.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <tchar.h>
#endif
#include "gridhub.hxx"
@@ -39,6 +38,7 @@ AZ_POP_DISABLE_WARNING
#include <AzCore/Math/Crc.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/parallel/mutex.h>
#include <AzCore/Utils/Utils.h>
#ifdef AZ_PLATFORM_WINDOWS
#include <Shlwapi.h>
@@ -53,9 +53,9 @@ AZ_POP_DISABLE_WARNING
#endif
#ifdef AZ_PLATFORM_WINDOWS
#define GRIDHUB_TSR_SUFFIX _T("_copyapp_")
#define GRIDHUB_TSR_NAME _T("GridHub_copyapp_.exe")
#define GRIDHUB_IMAGE_NAME _T("GridHub.exe")
#define GRIDHUB_TSR_SUFFIX L"_copyapp_"
#define GRIDHUB_TSR_NAME L"GridHub_copyapp_.exe"
#define GRIDHUB_IMAGE_NAME L"GridHub.exe"
#else
#define GRIDHUB_TSR_SUFFIX "_copyapp_"
#define GRIDHUB_TSR_NAME "GridHub_copyapp_"
@@ -191,7 +191,7 @@ protected:
specializations.Append("gridhub");
}
QString m_originalExeFileName;
QString m_originalExeFileName;
QDateTime m_originalExeLastModified;
bool m_monitorForExeChanges;
bool m_needToRelaunch;
@@ -305,22 +305,22 @@ public:
{
#ifdef AZ_PLATFORM_WINDOWS
HRESULT hres;
TCHAR startupFolder[MAX_PATH] = {0};
TCHAR fullLinkName[MAX_PATH] = {0};
wchar_t startupFolder[MAX_PATH] = {0};
wchar_t fullLinkName[MAX_PATH] = {0};
LPITEMIDLIST pidlFolder = NULL;
hres = SHGetFolderLocation(0,/*CSIDL_COMMON_STARTUP all users required admin access*/CSIDL_STARTUP,NULL,0,&pidlFolder);
if (SUCCEEDED(hres))
{
if( SHGetPathFromIDList(pidlFolder,startupFolder) )
if (SHGetPathFromIDList(pidlFolder, startupFolder))
{
_tcscat_s(fullLinkName,startupFolder);
_tcscat_s(fullLinkName,"\\Amazon Grid Hub.lnk");
wcscat_s(fullLinkName, startupFolder);
wcscat_s(fullLinkName, L"\\Amazon Grid Hub.lnk");
}
CoTaskMemFree(pidlFolder);
}
if( moduleFilename.isEmpty() || _tcslen(fullLinkName) == 0 )
if( moduleFilename.isEmpty() || wcslen(fullLinkName) == 0 )
return;
// for development, never autoadd to startup
@@ -342,8 +342,8 @@ public:
IPersistFile* ppf;
// Set the path to the shortcut target and add the description.
psl->SetPath(moduleFilename.toUtf8().data());
psl->SetDescription("Amazon Grid Hub");
psl->SetPath(moduleFilename.toStdWString().c_str());
psl->SetDescription(L"Amazon Grid Hub");
// Query IShellLink for the IPersistFile interface, used for saving the
// shortcut in persistent storage.
@@ -351,16 +351,8 @@ public:
if (SUCCEEDED(hres))
{
WCHAR wsz[MAX_PATH];
// Ensure that the string is Unicode.
MultiByteToWideChar(CP_ACP, 0, fullLinkName, -1, wsz, MAX_PATH);
// Add code here to check return value from MultiByteWideChar
// for success.
// Save the link by calling IPersistFile::Save.
hres = ppf->Save(wsz, TRUE);
hres = ppf->Save(fullLinkName, TRUE);
ppf->Release();
}
psl->Release();
@@ -412,13 +404,17 @@ GridHubApplication::Create(const Descriptor& descriptor, const StartupParameters
{
bool isError = false;
#ifdef AZ_PLATFORM_WINDOWS
TCHAR originalExeFileName[MAX_PATH];
if (GetModuleFileName(NULL, originalExeFileName, AZ_ARRAY_SIZE(originalExeFileName)))
char originalExeFileName[MAX_PATH];
if (AZ::Utils::GetExecutablePath(originalExeFileName, AZ_ARRAY_SIZE(originalExeFileName)).m_pathStored == AZ::Utils::ExecutablePathResult::Success)
{
PathRemoveFileSpec(originalExeFileName);
PathAppend(originalExeFileName, GRIDHUB_IMAGE_NAME);
wchar_t originalExeFileNameW[MAX_PATH];
AZStd::to_wstring(originalExeFileNameW, MAX_PATH, originalExeFileName);
PathRemoveFileSpec(originalExeFileNameW);
PathAppend(originalExeFileNameW, GRIDHUB_IMAGE_NAME);
m_originalExeFileName = originalExeFileName;
AZStd::string finalExeFileName;
AZStd::to_string(finalExeFileName, originalExeFileNameW);
m_originalExeFileName = finalExeFileName.c_str();
m_originalExeLastModified = QFileInfo(m_originalExeFileName).lastModified();
}
@@ -489,18 +485,20 @@ void GridHubApplication::RegisterCoreComponents()
void CopyAndRun(bool failSilently)
{
#ifdef AZ_PLATFORM_WINDOWS
TCHAR myFileName[MAX_PATH] = { _T(0) };
if (GetModuleFileName(NULL, myFileName, MAX_PATH ))
char myFileName[MAX_PATH] = { 0 };
if (AZ::Utils::GetExecutablePath(myFileName, MAX_PATH).m_pathStored == AZ::Utils::ExecutablePathResult::Success)
{
TCHAR sourceProcPath[MAX_PATH] = { _T(0) };
TCHAR targetProcPath[MAX_PATH] = { _T(0) };
TCHAR procDrive[MAX_PATH] = { _T(0) };
TCHAR procDir[MAX_PATH] = { _T(0) };
TCHAR procFname[MAX_PATH] = { _T(0) };
TCHAR procExt[MAX_PATH] = { _T(0) };
_tsplitpath_s(myFileName, procDrive, procDir, procFname, procExt);
_tmakepath_s(sourceProcPath, procDrive, procDir, GRIDHUB_IMAGE_NAME, NULL);
_tmakepath_s(targetProcPath, procDrive, procDir, GRIDHUB_TSR_NAME, NULL);
wchar_t myFileNameW[MAX_PATH] = { 0 };
AZStd::to_wstring(myFileNameW, MAX_PATH, myFileName);
wchar_t sourceProcPath[MAX_PATH] = { 0 };
wchar_t targetProcPath[MAX_PATH] = { 0 };
wchar_t procDrive[MAX_PATH] = { 0 };
wchar_t procDir[MAX_PATH] = { 0 };
wchar_t procFname[MAX_PATH] = { 0 };
wchar_t procExt[MAX_PATH] = { 0 };
_wsplitpath_s(myFileNameW, procDrive, procDir, procFname, procExt);
_wmakepath_s(sourceProcPath, procDrive, procDir, GRIDHUB_IMAGE_NAME, NULL);
_wmakepath_s(targetProcPath, procDrive, procDir, GRIDHUB_TSR_NAME, NULL);
if (CopyFileEx(sourceProcPath, targetProcPath, NULL, NULL, NULL, 0))
{
STARTUPINFO si;
@@ -527,9 +525,9 @@ void CopyAndRun(bool failSilently)
{
if (!failSilently)
{
TCHAR errorMsg[1024] = { _T(0) };
_stprintf_s(errorMsg, _T("Failed to copy GridHub. Make sure that %s%s is writable!"), procFname, procExt);
MessageBox(NULL, errorMsg, NULL, MB_ICONSTOP|MB_OK);
wchar_t errorMsg[1024] = { 0 };
swprintf_s(errorMsg, L"Failed to copy GridHub. Make sure that %s%s is writable!", procFname, procExt);
MessageBoxW(NULL, errorMsg, NULL, MB_ICONSTOP|MB_OK);
}
}
}
@@ -565,16 +563,18 @@ void CopyAndRun(bool failSilently)
void RelaunchImage()
{
#ifdef AZ_PLATFORM_WINDOWS
TCHAR myFileName[MAX_PATH] = { _T(0) };
if (GetModuleFileName(NULL, myFileName, MAX_PATH))
char myFileName[MAX_PATH] = { 0 };
if (AZ::Utils::GetExecutablePath(myFileName, MAX_PATH).m_pathStored == AZ::Utils::ExecutablePathResult::Success)
{
TCHAR targetProcPath[MAX_PATH] = { _T(0) };
TCHAR procDrive[MAX_PATH] = { _T(0) };
TCHAR procDir[MAX_PATH] = { _T(0) };
TCHAR procFname[MAX_PATH] = { _T(0) };
TCHAR procExt[MAX_PATH] = { _T(0) };
_tsplitpath_s(myFileName, procDrive, procDir, procFname, procExt);
_tmakepath_s(targetProcPath, procDrive, procDir, GRIDHUB_IMAGE_NAME, NULL);
wchar_t myFileNameW[MAX_PATH] = { 0 };
AZStd::to_wstring(myFileNameW, MAX_PATH, myFileName);
wchar_t targetProcPath[MAX_PATH] = { 0 };
wchar_t procDrive[MAX_PATH] = { 0 };
wchar_t procDir[MAX_PATH] = { 0 };
wchar_t procFname[MAX_PATH] = { 0 };
wchar_t procExt[MAX_PATH] = { 0 };
_wsplitpath_s(myFileNameW, procDrive, procDir, procFname, procExt);
_wmakepath_s(targetProcPath, procDrive, procDir, GRIDHUB_IMAGE_NAME, NULL);
STARTUPINFO si;
PROCESS_INFORMATION pi;
@@ -635,8 +635,8 @@ int main(int argc, char *argv[])
{
#ifdef AZ_PLATFORM_WINDOWS
TCHAR exeFileName[MAX_PATH];
if( GetModuleFileName(NULL,exeFileName,AZ_ARRAY_SIZE(exeFileName)) )
char exeFileName[MAX_PATH];
if (AZ::Utils::GetExecutablePath(exeFileName, AZ_ARRAY_SIZE(exeFileName)).m_pathStored == AZ::Utils::ExecutablePathResult::Success)
#elif defined AZ_PLATFORM_LINUX
//KDAB_TODO
char exeFileName[MAXPATHLEN];
@@ -661,7 +661,7 @@ int main(int argc, char *argv[])
{
#ifdef AZ_PLATFORM_WINDOWS
// Create a OS named mutex while the OS is running
HANDLE hInstanceMutex = CreateMutex(NULL,TRUE,"Global\\GridHub-Instance");
HANDLE hInstanceMutex = CreateMutex(NULL, TRUE, L"Global\\GridHub-Instance");
AZ_Assert(hInstanceMutex!=NULL,"Failed to create OS mutex [GridHub-Instance]\n");
if( hInstanceMutex != NULL && GetLastError() == ERROR_ALREADY_EXISTS)
{
@@ -56,8 +56,6 @@ namespace O3DE::ProjectManager
QCoreApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings);
QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates));
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
AzQtComponents::Utilities::HandleDpiAwareness(AzQtComponents::Utilities::SystemDpiAware);
@@ -462,10 +462,10 @@ bool SRemoteClient::SendPackage(const char* buffer, int size)
/////////////////////////////////////////////////////////////////////////////////////////////
void SRemoteClient::FillAutoCompleteList(AZStd::vector<AZStd::string>& list)
{
AZStd::vector<const char*> cmds;
size_t count = gEnv->pConsole->GetSortedVars(nullptr, 0);
AZStd::vector<AZStd::string_view> cmds;
size_t count = gEnv->pConsole->GetSortedVars(cmds);
cmds.resize(count);
count = gEnv->pConsole->GetSortedVars(&cmds[0], count);
count = gEnv->pConsole->GetSortedVars(cmds);
for (size_t i = 0; i < count; ++i)
{
list.push_back(cmds[i]);
@@ -194,7 +194,7 @@ namespace AZ
AZStd::string AssImpMaterialWrapper::GetTextureFileName(MaterialMapType textureType) const
{
/// Engine currently doesn't support multiple textures. Right now we only use first texture.
int textureIndex = 0;
unsigned int textureIndex = 0;
aiString absTexturePath;
switch (textureType)
{
@@ -7,6 +7,7 @@
*/
#pragma once
#include <AzCore/RTTI/RTTI.h>
#include <AzCore/std/smart_ptr/shared_ptr.h>
struct aiNode;
@@ -445,11 +445,11 @@ namespace AZ
AZStd::unordered_set<AZStd::string> boneList;
for (int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
for (unsigned int meshIndex = 0; meshIndex < scene->mNumMeshes; ++meshIndex)
{
aiMesh* mesh = scene->mMeshes[meshIndex];
for (int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
for (unsigned int boneIndex = 0; boneIndex < mesh->mNumBones; ++boneIndex)
{
aiBone* bone = mesh->mBones[boneIndex];
@@ -612,10 +612,10 @@ namespace AZ
ValueToKeyDataMap valueToKeyDataMap;
// Key time can be less than zero, normalize to have zero be the lowest time.
double keyOffset = 0;
for (int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
for (unsigned int keyIdx = 0; keyIdx < meshMorphAnim->mNumKeys; keyIdx++)
{
aiMeshMorphKey& key = meshMorphAnim->mKeys[keyIdx];
for (int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
for (unsigned int valIdx = 0; valIdx < key.mNumValuesAndWeights; ++valIdx)
{
int currentValue = key.mValues[valIdx];
KeyData thisKey(key.mWeights[valIdx], key.mTime);
@@ -89,11 +89,11 @@ namespace AZ
bitangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
bitangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (!mesh->HasTangentsAndBitangents())
{
@@ -85,7 +85,7 @@ namespace AZ
{
int sceneMeshIdx = context.m_sourceNode.GetAssImpNode()->mMeshes[nodeMeshIdx];
const aiMesh* aiMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[sceneMeshIdx];
for (int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
for (unsigned int animIdx = 0; animIdx < aiMesh->mNumAnimMeshes; animIdx++)
{
aiAnimMesh* aiAnimMesh = aiMesh->mAnimMeshes[animIdx];
animToMeshToAnimMeshIndices[aiAnimMesh->mName.C_Str()].emplace_back(nodeMeshIdx, animIdx);
@@ -130,7 +130,7 @@ namespace AZ
blendShapeData->ReserveData(
aiAnimMesh->mNumVertices, aiAnimMesh->HasTangentsAndBitangents(), uvSetUsedFlags, colorSetUsedFlags);
for (int vertIdx = 0; vertIdx < aiAnimMesh->mNumVertices; ++vertIdx)
for (unsigned int vertIdx = 0; vertIdx < aiAnimMesh->mNumVertices; ++vertIdx)
{
AZ::Vector3 vertex(AssImpSDKWrapper::AssImpTypeConverter::ToVector3(aiAnimMesh->mVertices[vertIdx]));
@@ -184,7 +184,7 @@ namespace AZ
}
// aiAnimMesh just has a list of positions for vertices. The face indices are on the original mesh.
for (int faceIdx = 0; faceIdx < aiMesh->mNumFaces; ++faceIdx)
for (unsigned int faceIdx = 0; faceIdx < aiMesh->mNumFaces; ++faceIdx)
{
aiFace face = aiMesh->mFaces[faceIdx];
DataTypes::IBlendShapeData::Face blendFace;
@@ -199,7 +199,7 @@ namespace AZ
face.mNumIndices);
continue;
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
for (unsigned int idx = 0; idx < face.mNumIndices; ++idx)
{
blendFace.vertexIndex[idx] = face.mIndices[idx] + vertexOffset;
}
@@ -103,10 +103,6 @@ namespace AZ
}
}
if(!isBone)
{
return Events::ProcessingResult::Ignored;
}
// If the current scene node (our eventual parent) contains bone data, we are not a root bone
AZStd::shared_ptr<SceneData::GraphData::BoneData> createdBoneData;
@@ -56,7 +56,7 @@ namespace AZ
const aiScene* scene = context.m_sourceScene.GetAssImpScene();
// This node has at least one mesh, verify that the color channel counts are the same for all meshes.
const int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
const unsigned int expectedColorChannels = scene->mMeshes[currentNode->mMeshes[0]]->GetNumColorChannels();
const bool allMeshesHaveSameNumberOfColorChannels =
AZStd::all_of(currentNode->mMeshes + 1, currentNode->mMeshes + currentNode->mNumMeshes, [scene, expectedColorChannels](const unsigned int meshIndex)
{
@@ -80,17 +80,16 @@ namespace AZ
const uint64_t vertexCount = GetVertexCountForAllMeshesOnNode(*currentNode, *scene);
Events::ProcessingResultCombiner combinedVertexColorResults;
for (int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
for (unsigned int colorSetIndex = 0; colorSetIndex < expectedColorChannels; ++colorSetIndex)
{
AZStd::shared_ptr<SceneData::GraphData::MeshVertexColorData> vertexColors =
AZStd::make_shared<AZ::SceneData::GraphData::MeshVertexColorData>();
vertexColors->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (colorSetIndex < mesh->GetNumColorChannels())
{
@@ -105,7 +105,7 @@ namespace AZ
nodesWithNoMesh.emplace(currentNode->mName.C_Str());
}
for (int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
for (unsigned int childIndex = 0; childIndex < currentNode->mNumChildren; ++childIndex)
{
queue.push(currentNode->mChildren[childIndex]);
}
@@ -135,18 +135,13 @@ namespace AZ
const aiBone* bone = FindFirstBoneByNodeName(node, boneByNameMap);
if (bone)
{
const DataTypes::MatrixType inverseOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(bone->mOffsetMatrix).GetInverseFull();
const aiBone* parentBone = FindFirstBoneByNodeName(node->mParent, boneByNameMap);
if (parentBone)
{
DataTypes::MatrixType inverseOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(bone->mOffsetMatrix).GetInverseFull();
const DataTypes::MatrixType parentBoneOffsetMatrix = AssImpSDKWrapper::AssImpTypeConverter::ToTransform(parentBone->mOffsetMatrix);
return parentBoneOffsetMatrix * inverseOffsetMatrix;
}
else
{
return inverseOffsetMatrix;
}
}
return AssImpSDKWrapper::AssImpTypeConverter::ToTransform(GetConcatenatedLocalTransform(node));
@@ -176,7 +171,7 @@ namespace AZ
return true;
}
for (int childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
for (unsigned int childIndex = 0; childIndex < node->mNumChildren; ++childIndex)
{
const aiNode* childNode = node->mChildren[childIndex];
if (RecursiveHasChildBone(childNode, boneByNameMap))
@@ -56,7 +56,7 @@ namespace AZ
Events::ProcessingResultCombiner combinedMaterialImportResults;
AZStd::unordered_map<int, AZStd::shared_ptr<SceneData::GraphData::MaterialData>> materialMap;
for (int idx = 0; idx < context.m_sourceNode.m_assImpNode->mNumMeshes; ++idx)
for (unsigned int idx = 0; idx < context.m_sourceNode.m_assImpNode->mNumMeshes; ++idx)
{
int meshIndex = context.m_sourceNode.m_assImpNode->mMeshes[idx];
const aiMesh* assImpMesh = context.m_sourceScene.GetAssImpScene()->mMeshes[meshIndex];
@@ -91,11 +91,11 @@ namespace AZ
tangentStream->SetGenerationMethod(AZ::SceneAPI::DataTypes::TangentGenerationMethod::FromSourceScene);
tangentStream->ReserveContainerSpace(vertexCount);
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (!mesh->HasTangentsAndBitangents())
{
@@ -62,7 +62,7 @@ namespace AZ
// so they can be separated by engine code instead.
bool foundTextureCoordinates = false;
AZStd::array<int, AI_MAX_NUMBER_OF_TEXTURECOORDS> meshesPerTextureCoordinateIndex = {};
for (int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
for (unsigned int localMeshIndex = 0; localMeshIndex < currentNode->mNumMeshes; ++localMeshIndex)
{
aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[localMeshIndex]];
for (int texCoordIndex = 0; texCoordIndex < meshesPerTextureCoordinateIndex.size(); ++texCoordIndex)
@@ -110,7 +110,7 @@ namespace AZ
uvMap->ReserveContainerSpace(vertexCount);
bool customNameFound = false;
AZStd::string name(AZStd::string::format("%s%d", m_defaultNodeName, texCoordIndex));
for (int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
for (unsigned int sdkMeshIndex = 0; sdkMeshIndex < currentNode->mNumMeshes; ++sdkMeshIndex)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[sdkMeshIndex]];
if(mesh->mTextureCoords[texCoordIndex])
@@ -136,7 +136,7 @@ namespace AZ
}
}
for (int v = 0; v < mesh->mNumVertices; ++v)
for (unsigned int v = 0; v < mesh->mNumVertices; ++v)
{
if (mesh->mTextureCoords[texCoordIndex])
{
@@ -40,7 +40,7 @@ namespace AZ::SceneAPI::SceneBuilder
// This code re-combines them to match previous FBX SDK behavior,
// so they can be separated by engine code instead.
int vertOffset = 0;
for (int m = 0; m < currentNode->mNumMeshes; ++m)
for (unsigned int m = 0; m < currentNode->mNumMeshes; ++m)
{
const aiMesh* mesh = scene->mMeshes[currentNode->mMeshes[m]];
@@ -50,7 +50,7 @@ namespace AZ::SceneAPI::SceneBuilder
assImpMatIndexToLYIndex.insert(AZStd::pair<int, int>(mesh->mMaterialIndex, lyMeshIndex++));
}
for (int vertIdx = 0; vertIdx < mesh->mNumVertices; ++vertIdx)
for (unsigned int vertIdx = 0; vertIdx < mesh->mNumVertices; ++vertIdx)
{
AZ::Vector3 vertex(mesh->mVertices[vertIdx].x, mesh->mVertices[vertIdx].y, mesh->mVertices[vertIdx].z);
@@ -68,7 +68,7 @@ namespace AZ::SceneAPI::SceneBuilder
}
}
for (int faceIdx = 0; faceIdx < mesh->mNumFaces; ++faceIdx)
for (unsigned int faceIdx = 0; faceIdx < mesh->mNumFaces; ++faceIdx)
{
aiFace face = mesh->mFaces[faceIdx];
AZ::SceneAPI::DataTypes::IMeshData::Face meshFace;
@@ -82,7 +82,7 @@ namespace AZ::SceneAPI::SceneBuilder
face.mNumIndices);
continue;
}
for (int idx = 0; idx < face.mNumIndices; ++idx)
for (unsigned int idx = 0; idx < face.mNumIndices; ++idx)
{
meshFace.vertexIndex[idx] = face.mIndices[idx] + vertOffset;
}
@@ -11,6 +11,7 @@
#include <AzCore/std/string/string.h>
#include <AzCore/RTTI/RTTI.h>
#include <SceneAPI/SceneCore/DataTypes/Rules/IRule.h>
#include <AzCore/std/string/fixed_string.h>
namespace AZ
{
@@ -18,7 +19,8 @@ namespace AZ
{
namespace DataTypes
{
const static AZStd::string s_advancedDisabledString = "Disabled";
static const char* s_advancedDisabledString = "Disabled";
class IMeshAdvancedRule
: public IRule
{
@@ -13,6 +13,7 @@
#include <SceneAPI/SceneCore/Utilities/HashHelper.h>
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/optional.h>
#include <cinttypes>
namespace AZ::SceneAPI::Utilities
@@ -18,11 +18,6 @@ namespace AZ
{
namespace DataTypes = AZ::SceneAPI::DataTypes;
const AZStd::string MaterialData::s_DiffuseMapName = "Diffuse";
const AZStd::string MaterialData::s_SpecularMapName = "Specular";
const AZStd::string MaterialData::s_BumpMapName = "Bump";
const AZStd::string MaterialData::s_emptyString = "";
MaterialData::MaterialData()
: m_isNoDraw(false)
, m_diffuseColor(AZ::Vector3::CreateOne())
@@ -72,7 +67,7 @@ namespace AZ
return result->second;
}
return s_emptyString;
return m_emptyString;
}
void MaterialData::SetNoDraw(bool isNoDraw)
@@ -11,6 +11,7 @@
#include <SceneAPI/SceneData/SceneDataConfiguration.h>
#include <SceneAPI/SceneCore/DataTypes/GraphData/IMaterialData.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/string/fixed_string.h>
namespace AZ
{
@@ -96,10 +97,7 @@ namespace AZ
bool m_isNoDraw;
const static AZStd::string s_DiffuseMapName;
const static AZStd::string s_SpecularMapName;
const static AZStd::string s_BumpMapName;
const static AZStd::string s_emptyString;
const AZStd::string m_emptyString;
// A unique id which is used to identify a material in a fbx.
// This is the same as the ID in the fbx file's FbxNode
-2
View File
@@ -36,7 +36,6 @@ ly_add_target(
${additional_dependencies}
COMPILE_DEFINITIONS
PRIVATE
UNICODE
STANDALONETOOLS_ENABLE_LUA_IDE
)
@@ -68,6 +67,5 @@ ly_add_target(
${additional_dependencies}
COMPILE_DEFINITIONS
PRIVATE
UNICODE
STANDALONETOOLS_ENABLE_PROFILER
)
@@ -11,8 +11,6 @@ ly_get_list_relative_pal_filename(pal_source_dir ${CMAKE_CURRENT_LIST_DIR}/Platf
include(${pal_source_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_TEST_IMPACT_FRAMEWORK_SUPPORTED)
if(LY_TEST_IMPACT_INSTRUMENTATION_BIN)
add_subdirectory(Runtime)
add_subdirectory(Frontend)
endif()
add_subdirectory(Runtime)
add_subdirectory(Frontend)
endif()
@@ -6,6 +6,8 @@
*
*/
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactCommandLineOptions.h>
#include <TestImpactCommandLineOptionsUtils.h>
@@ -19,8 +21,9 @@ namespace TestImpact
{
// Options
ConfigKey,
DataFileKey,
ChangeListKey,
OutputChangeListKey,
SequenceReportKey,
SequenceKey,
TestPrioritizationPolicyKey,
ExecutionFailurePolicyKey,
@@ -55,8 +58,9 @@ namespace TestImpact
{
// Options
"config",
"datafile",
"changelist",
"ochangelist",
"report",
"sequence",
"ppolicy",
"epolicy",
@@ -92,14 +96,19 @@ namespace TestImpact
return ParsePathOption(OptionKeys[ConfigKey], cmd).value_or(LY_TEST_IMPACT_DEFAULT_CONFIG_FILE);
}
AZStd::optional<RepoPath> ParseDataFile(const AZ::CommandLine& cmd)
{
return ParsePathOption(OptionKeys[DataFileKey], cmd);
}
AZStd::optional<RepoPath> ParseChangeListFile(const AZ::CommandLine& cmd)
{
return ParsePathOption(OptionKeys[ChangeListKey], cmd);
}
bool ParseOutputChangeList(const AZ::CommandLine& cmd)
AZStd::optional<RepoPath> ParseSequenceReportFile(const AZ::CommandLine& cmd)
{
return ParseOnOffOption(OptionKeys[OutputChangeListKey], BinaryStateValue<bool>{ false, true }, cmd).value_or(false);
return ParsePathOption(OptionKeys[SequenceReportKey], cmd);
}
TestSequenceType ParseTestSequenceType(const AZ::CommandLine& cmd)
@@ -255,9 +264,9 @@ namespace TestImpact
{
const AZStd::vector<AZStd::pair<AZStd::string, SuiteType>> states =
{
{GetSuiteTypeName(SuiteType::Main), SuiteType::Main},
{GetSuiteTypeName(SuiteType::Periodic), SuiteType::Periodic},
{GetSuiteTypeName(SuiteType::Sandbox), SuiteType::Sandbox}
{ SuiteTypeAsString(SuiteType::Main), SuiteType::Main },
{ SuiteTypeAsString(SuiteType::Periodic), SuiteType::Periodic },
{ SuiteTypeAsString(SuiteType::Sandbox), SuiteType::Sandbox }
};
return ParseMultiStateOption(OptionKeys[SuiteFilterKey], states, cmd).value_or(SuiteType::Main);
@@ -270,8 +279,9 @@ namespace TestImpact
cmd.Parse(argc, argv);
m_configurationFile = ParseConfigurationFile(cmd);
m_dataFile = ParseDataFile(cmd);
m_changeListFile = ParseChangeListFile(cmd);
m_outputChangeList = ParseOutputChangeList(cmd);
m_sequenceReportFile = ParseSequenceReportFile(cmd);
m_testSequenceType = ParseTestSequenceType(cmd);
m_testPrioritizationPolicy = ParseTestPrioritizationPolicy(cmd);
m_executionFailurePolicy = ParseExecutionFailurePolicy(cmd);
@@ -286,28 +296,43 @@ namespace TestImpact
m_safeMode = ParseSafeMode(cmd);
m_suiteFilter = ParseSuiteFilter(cmd);
}
bool CommandLineOptions::HasDataFilePath() const
{
return m_dataFile.has_value();
}
bool CommandLineOptions::HasChangeListFile() const
bool CommandLineOptions::HasChangeListFilePath() const
{
return m_changeListFile.has_value();
}
bool CommandLineOptions::HasSequenceReportFilePath() const
{
return m_sequenceReportFile.has_value();
}
bool CommandLineOptions::HasSafeMode() const
{
return m_safeMode;
}
const AZStd::optional<RepoPath>& CommandLineOptions::GetChangeListFile() const
const AZStd::optional<RepoPath>& CommandLineOptions::GetDataFilePath() const
{
return m_dataFile;
}
const AZStd::optional<RepoPath>& CommandLineOptions::GetChangeListFilePath() const
{
return m_changeListFile;
}
bool CommandLineOptions::HasOutputChangeList() const
const AZStd::optional<RepoPath>& CommandLineOptions::GetSequenceReportFilePath() const
{
return m_outputChangeList;
return m_sequenceReportFile;
}
const RepoPath& CommandLineOptions::GetConfigurationFile() const
const RepoPath& CommandLineOptions::GetConfigurationFilePath() const
{
return m_configurationFile;
}
@@ -379,8 +404,12 @@ namespace TestImpact
" options:\n"
" -config=<filename> Path to the configuration file for the TIAF runtime (default: \n"
" <tiaf binay build dir>.<tiaf binary build type>.json).\n"
" -datafile=<filename> Optional path to a test impact data file that will used instead of that\n"
" specified in the config file.\n"
" -changelist=<filename> Path to the JSON of source file changes to perform test impact \n"
" analysis on.\n"
" -report=<filename> Path to where the sequence report file will be written (if this option \n"
" is not specified, no report will be written).\n"
" -gtimeout=<seconds> Global timeout value to terminate the entire test sequence should it \n"
" be exceeded.\n"
" -ttimeout=<seconds> Timeout value to terminate individual test targets should it be \n"
@@ -443,7 +472,6 @@ namespace TestImpact
" available, no prioritization will occur).\n"
" -maxconcurrency=<number> The maximum number of concurrent test targets/shards to be in flight at \n"
" any given moment.\n"
" -ochangelist=<on,off> Outputs the change list used for test selection.\n"
" -suite=<main, periodic, sandbox> The test suite to select from for this test sequence.";
return help;
@@ -36,20 +36,29 @@ namespace TestImpact
CommandLineOptions(int argc, char** argv);
static AZStd::string GetCommandLineUsageString();
//! Returns true if a test impact data file path has been supplied, otherwise false.
bool HasDataFilePath() const;
//! Returns true if a change list file path has been supplied, otherwise false.
bool HasChangeListFile() const;
bool HasChangeListFilePath() const;
//! Returns true if a sequence report file path has been supplied, otherwise false.
bool HasSequenceReportFilePath() const;
//! Returns true if the safe mode option has been enabled, otherwise false.
bool HasSafeMode() const;
//! Returns true if the output change list option has been enabled, otherwise false.
bool HasOutputChangeList() const;
//! Returns the path to the runtime configuration file.
const RepoPath& GetConfigurationFile() const;
const RepoPath& GetConfigurationFilePath() const;
//! Returns the path to the data file (if any).
const AZStd::optional<RepoPath>& GetDataFilePath() const;
//! Returns the path to the change list file (if any).
const AZStd::optional<RepoPath>& GetChangeListFile() const;
const AZStd::optional<RepoPath>& GetChangeListFilePath() const;
//! Returns the path to the sequence report file (if any).
const AZStd::optional<RepoPath>& GetSequenceReportFilePath() const;
//! Returns the test sequence type to run.
TestSequenceType GetTestSequenceType() const;
@@ -89,8 +98,9 @@ namespace TestImpact
private:
RepoPath m_configurationFile;
AZStd::optional<RepoPath> m_dataFile;
AZStd::optional<RepoPath> m_changeListFile;
bool m_outputChangeList = false;
AZStd::optional<RepoPath> m_sequenceReportFile;
TestSequenceType m_testSequenceType;
Policy::TestPrioritization m_testPrioritizationPolicy = Policy::TestPrioritization::None;
Policy::ExecutionFailure m_executionFailurePolicy = Policy::ExecutionFailure::Continue;
@@ -9,14 +9,16 @@
#include <TestImpactFramework/TestImpactException.h>
#include <TestImpactFramework/TestImpactChangeListException.h>
#include <TestImpactFramework/TestImpactConfigurationException.h>
#include <TestImpactFramework/TestImpactSequenceReportException.h>
#include <TestImpactFramework/TestImpactRuntimeException.h>
#include <TestImpactFramework/TestImpactConsoleMain.h>
#include <TestImpactFramework/TestImpactChangeListSerializer.h>
#include <TestImpactFramework/TestImpactChangeList.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactFramework/TestImpactClientTestSelection.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <TestImpactFramework/TestImpactClientSequenceReportSerializer.h>
#include <TestImpactConsoleTestSequenceEventHandler.h>
#include <TestImpactCommandLineOptions.h>
@@ -33,31 +35,6 @@ namespace TestImpact
{
namespace Console
{
//! Generates a string to be used for printing to the console for the specified change list.
AZStd::string GenerateChangeListString(const ChangeList& changeList)
{
AZStd::string output;
const auto& outputFiles = [&output](const AZStd::vector<RepoPath>& files)
{
for (const auto& file : files)
{
output += AZStd::string::format("\t%s\n", file.c_str());
}
};
output += AZStd::string::format("Created files (%u):\n", changeList.m_createdFiles.size());
outputFiles(changeList.m_createdFiles);
output += AZStd::string::format("Updated files (%u):\n", changeList.m_updatedFiles.size());
outputFiles(changeList.m_updatedFiles);
output += AZStd::string::format("Deleted files (%u):\n", changeList.m_deletedFiles.size());
outputFiles(changeList.m_deletedFiles);
return output;
}
//! Gets the appropriate console return code for the specified test sequence result.
ReturnCode GetReturnCodeForTestSequenceResult(TestSequenceResult result)
{
@@ -75,9 +52,22 @@ namespace TestImpact
}
}
//! Wrapper around sequence reports to optionally serialize them and transform the result into a return code.
template<typename SequenceReportType>
ReturnCode ConsumeSequenceReportAndGetReturnCode(const SequenceReportType& sequenceReport, const CommandLineOptions& options)
{
if (options.HasSequenceReportFilePath())
{
std::cout << "Exporting sequence report '" << options.GetSequenceReportFilePath().value().c_str() << "'" << std::endl;
const auto sequenceReportJson = SerializeSequenceReport(sequenceReport);
WriteFileContents<SequenceReportException>(sequenceReportJson, options.GetSequenceReportFilePath().value());
}
return GetReturnCodeForTestSequenceResult(sequenceReport.GetResult());
}
//! Wrapper around impact analysis sequences to handle the case where the safe mode option is active.
ReturnCode WrappedImpactAnalysisTestSequence(
TestSequenceEventHandler& sequenceEventHandler,
const CommandLineOptions& options,
Runtime& runtime,
const AZStd::optional<ChangeList>& changeList)
@@ -89,49 +79,34 @@ namespace TestImpact
CommandLineOptionsException,
"Expected a change list for impact analysis but none was provided");
TestSequenceResult result = TestSequenceResult::Failure;
if (options.HasSafeMode())
{
if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysis)
{
auto [selectedResult, discardedResult] = runtime.SafeImpactAnalysisTestSequence(
changeList.value(),
options.GetTestPrioritizationPolicy(),
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
// Handling the possible timeout and failure permutations of the selected and discarded test results is splitting hairs
// so apply the following, admittedly arbitrary, rules to determine what the composite test sequence result should be
if (selectedResult == TestSequenceResult::Success && discardedResult == TestSequenceResult::Success)
{
// Trivial case: both sequences succeeded
result = TestSequenceResult::Success;
}
else if (selectedResult == TestSequenceResult::Failure || discardedResult == TestSequenceResult::Failure)
{
// One sequence failed whilst the other sequence either succeeded or timed out
result = TestSequenceResult::Failure;
}
else
{
// One or both sequences timed out or failed
result = TestSequenceResult::Timeout;
}
return ConsumeSequenceReportAndGetReturnCode(
runtime.SafeImpactAnalysisTestSequence(
changeList.value(),
options.GetTestPrioritizationPolicy(),
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
SafeImpactAnalysisTestSequenceStartCallback,
SafeImpactAnalysisTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
else if (options.GetTestSequenceType() == TestSequenceType::ImpactAnalysisNoWrite)
{
// A no-write impact analysis sequence with safe mode enabled is functionally identical to a regular sequence type
// due to a) the selected tests being run without instrumentation and b) the discarded tests also being run without
// instrumentation
result = runtime.RegularTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
return ConsumeSequenceReportAndGetReturnCode(
runtime.RegularTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
TestSequenceStartCallback,
RegularTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
else
{
@@ -153,19 +128,19 @@ namespace TestImpact
{
throw(Exception("Unexpected sequence type"));
}
result = runtime.ImpactAnalysisTestSequence(
changeList.value(),
options.GetTestPrioritizationPolicy(),
dynamicDependencyMapPolicy,
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
return ConsumeSequenceReportAndGetReturnCode(
runtime.ImpactAnalysisTestSequence(
changeList.value(),
options.GetTestPrioritizationPolicy(),
dynamicDependencyMapPolicy,
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
ImpactAnalysisTestSequenceStartCallback,
ImpactAnalysisTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
return GetReturnCodeForTestSequenceResult(result);
};
//! Entry point for the test impact analysis framework console front end application.
@@ -177,28 +152,22 @@ namespace TestImpact
AZStd::optional<ChangeList> changeList;
// If we have a change list, check to see whether or not the client has requested the printing of said change list
if (options.HasChangeListFile())
if (options.HasChangeListFilePath())
{
changeList = DeserializeChangeList(ReadFileContents<CommandLineOptionsException>(*options.GetChangeListFile()));
if (options.HasOutputChangeList())
{
std::cout << "Change List:\n";
std::cout << GenerateChangeListString(*changeList).c_str();
if (options.GetTestSequenceType() == TestSequenceType::None)
{
return ReturnCode::Success;
}
}
changeList = DeserializeChangeList(ReadFileContents<CommandLineOptionsException>(*options.GetChangeListFilePath()));
}
// As of now, there are no other non-test operations other than printing a change list so getting this far is considered an error
AZ_TestImpact_Eval(options.GetTestSequenceType() != TestSequenceType::None, CommandLineOptionsException, "No action specified");
// As of now, there are no non-test operations but leave this door open for the future
if (options.GetTestSequenceType() == TestSequenceType::None)
{
return ReturnCode::Success;
}
std::cout << "Constructing in-memory model of source tree and test coverage for test suite ";
std::cout << GetSuiteTypeName(options.GetSuiteFilter()).c_str() << ", this may take a moment...\n";
std::cout << SuiteTypeAsString(options.GetSuiteFilter()).c_str() << ", this may take a moment...\n";
Runtime runtime(
RuntimeConfigurationFactory(ReadFileContents<CommandLineOptionsException>(options.GetConfigurationFile())),
RuntimeConfigurationFactory(ReadFileContents<CommandLineOptionsException>(options.GetConfigurationFilePath())),
options.GetDataFilePath(),
options.GetSuiteFilter(),
options.GetExecutionFailurePolicy(),
options.GetFailedTestCoveragePolicy(),
@@ -217,53 +186,51 @@ namespace TestImpact
std::cout << "Test impact analysis data for this repository was not found, seed or regular sequence fallbacks will be used.\n";
}
TestSequenceEventHandler sequenceEventHandler(options.GetSuiteFilter());
switch (const auto type = options.GetTestSequenceType())
{
case TestSequenceType::Regular:
{
const auto result = runtime.RegularTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
return GetReturnCodeForTestSequenceResult(result);
return ConsumeSequenceReportAndGetReturnCode(
runtime.RegularTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
TestSequenceStartCallback,
RegularTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
case TestSequenceType::Seed:
{
const auto result = runtime.SeededTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
return GetReturnCodeForTestSequenceResult(result);
return ConsumeSequenceReportAndGetReturnCode(
runtime.SeededTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
TestSequenceStartCallback,
SeedTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
case TestSequenceType::ImpactAnalysisNoWrite:
case TestSequenceType::ImpactAnalysis:
{
return WrappedImpactAnalysisTestSequence(sequenceEventHandler, options, runtime, changeList);
return WrappedImpactAnalysisTestSequence(options, runtime, changeList);
}
case TestSequenceType::ImpactAnalysisOrSeed:
{
if (runtime.HasImpactAnalysisData())
{
return WrappedImpactAnalysisTestSequence(sequenceEventHandler, options, runtime, changeList);
return WrappedImpactAnalysisTestSequence(options, runtime, changeList);
}
else
{
const auto result = runtime.SeededTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler),
AZStd::ref(sequenceEventHandler));
return GetReturnCodeForTestSequenceResult(result);
return ConsumeSequenceReportAndGetReturnCode(
runtime.SeededTestSequence(
options.GetTestTargetTimeout(),
options.GetGlobalTimeout(),
TestSequenceStartCallback,
SeedTestSequenceCompleteCallback,
TestRunCompleteCallback),
options);
}
}
default:
@@ -6,8 +6,9 @@
*
*/
#include <TestImpactConsoleTestSequenceEventHandler.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactConsoleTestSequenceEventHandler.h>
#include <TestImpactConsoleUtils.h>
#include <iostream>
@@ -20,7 +21,7 @@ namespace TestImpact
{
void TestSuiteFilter(SuiteType filter)
{
std::cout << "Test suite filter: " << GetSuiteTypeName(filter).c_str() << "\n";
std::cout << "Test suite filter: " << SuiteTypeAsString(filter).c_str() << "\n";
}
void ImpactAnalysisTestSelection(size_t numSelectedTests, size_t numDiscardedTests, size_t numExcludedTests, size_t numDraftedTests)
@@ -32,72 +33,70 @@ namespace TestImpact
std::cout << "Of which " << numExcludedTests << " tests have been excluded and " << numDraftedTests << " tests have been drafted.\n";
}
void FailureReport(const Client::SequenceFailure& failureReport, AZStd::chrono::milliseconds duration)
void FailureReport(const Client::TestRunReport& testRunReport)
{
std::cout << "Sequence completed in " << (duration.count() / 1000.f) << "s with";
std::cout << "Sequence completed in " << (testRunReport.GetDuration().count() / 1000.f) << "s with";
if (!failureReport.GetExecutionFailures().empty() ||
!failureReport.GetTestRunFailures().empty() ||
!failureReport.GetTimedOutTests().empty() ||
!failureReport.GetUnexecutedTests().empty())
if (!testRunReport.GetExecutionFailureTestRuns().empty() ||
!testRunReport.GetFailingTestRuns().empty() ||
!testRunReport.GetTimedOutTestRuns().empty() ||
!testRunReport.GetUnexecutedTestRuns().empty())
{
std::cout << ":\n";
std::cout << SetColor(Foreground::White, Background::Red).c_str()
<< failureReport.GetTestRunFailures().size()
<< testRunReport.GetFailingTestRuns().size()
<< ResetColor().c_str() << " test failures\n";
std::cout << SetColor(Foreground::White, Background::Red).c_str()
<< failureReport.GetExecutionFailures().size()
<< testRunReport.GetExecutionFailureTestRuns().size()
<< ResetColor().c_str() << " execution failures\n";
std::cout << SetColor(Foreground::White, Background::Red).c_str()
<< failureReport.GetTimedOutTests().size()
<< testRunReport.GetTimedOutTestRuns().size()
<< ResetColor().c_str() << " test timeouts\n";
std::cout << SetColor(Foreground::White, Background::Red).c_str()
<< failureReport.GetUnexecutedTests().size()
<< testRunReport.GetUnexecutedTestRuns().size()
<< ResetColor().c_str() << " unexecuted tests\n";
if (!failureReport.GetTestRunFailures().empty())
if (!testRunReport.GetFailingTestRuns().empty())
{
std::cout << "\nTest failures:\n";
for (const auto& testRunFailure : failureReport.GetTestRunFailures())
for (const auto& testRunFailure : testRunReport.GetFailingTestRuns())
{
std::cout << " " << testRunFailure.GetTargetName().c_str();
for (const auto& testCaseFailure : testRunFailure.GetTestCaseFailures())
for (const auto& test : testRunFailure.GetTests())
{
std::cout << "." << testCaseFailure.GetName().c_str();
for (const auto& testFailure : testCaseFailure.GetTestFailures())
if (test.GetResult() == Client::TestResult::Failed)
{
std::cout << "." << testFailure.GetName().c_str() << "\n";
std::cout << " " << test.GetName().c_str() << "\n";
}
}
}
}
if (!failureReport.GetExecutionFailures().empty())
if (!testRunReport.GetExecutionFailureTestRuns().empty())
{
std::cout << "\nExecution failures:\n";
for (const auto& executionFailure : failureReport.GetExecutionFailures())
for (const auto& executionFailure : testRunReport.GetExecutionFailureTestRuns())
{
std::cout << " " << executionFailure.GetTargetName().c_str() << "\n";
std::cout << executionFailure.GetCommandString().c_str() << "\n";
}
}
if (!failureReport.GetTimedOutTests().empty())
if (!testRunReport.GetTimedOutTestRuns().empty())
{
std::cout << "\nTimed out tests:\n";
for (const auto& testTimeout : failureReport.GetTimedOutTests())
for (const auto& testTimeout : testRunReport.GetTimedOutTestRuns())
{
std::cout << " " << testTimeout.GetTargetName().c_str() << "\n";
}
}
if (!failureReport.GetUnexecutedTests().empty())
if (!testRunReport.GetUnexecutedTestRuns().empty())
{
std::cout << "\nUnexecuted tests:\n";
for (const auto& unexecutedTest : failureReport.GetUnexecutedTests())
for (const auto& unexecutedTest : testRunReport.GetUnexecutedTestRuns())
{
std::cout << " " << unexecutedTest.GetTargetName().c_str() << "\n";
}
@@ -105,50 +104,42 @@ namespace TestImpact
}
else
{
std::cout << SetColor(Foreground::White, Background::Green).c_str() << " \100% passes!\n" << ResetColor().c_str();
std::cout << " " << SetColor(Foreground::White, Background::Green).c_str() << "100% passes!\n" << ResetColor().c_str() << "\n";
}
}
}
TestSequenceEventHandler::TestSequenceEventHandler(SuiteType suiteFilter)
: m_suiteFilter(suiteFilter)
void TestSequenceStartCallback(SuiteType suiteType, const Client::TestRunSelection& selectedTests)
{
Output::TestSuiteFilter(suiteType);
std::cout << selectedTests.GetNumIncludedTestRuns() << " tests selected, " << selectedTests.GetNumExcludedTestRuns()
<< " excluded.\n";
}
// TestSequenceStartCallback
void TestSequenceEventHandler::operator()(Client::TestRunSelection&& selectedTests)
void TestSequenceCompleteCallback(SuiteType suiteType, const Client::TestRunSelection& selectedTests)
{
ClearState();
m_numTests = selectedTests.GetNumIncludedTestRuns();
Output::TestSuiteFilter(m_suiteFilter);
Output::TestSuiteFilter(suiteType);
std::cout << selectedTests.GetNumIncludedTestRuns() << " tests selected, " << selectedTests.GetNumExcludedTestRuns() << " excluded.\n";
}
// ImpactAnalysisTestSequenceStartCallback
void TestSequenceEventHandler::operator()(
Client::TestRunSelection&& selectedTests,
AZStd::vector<AZStd::string>&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests)
void ImpactAnalysisTestSequenceStartCallback(
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const AZStd::vector<AZStd::string>& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests)
{
ClearState();
m_numTests = selectedTests.GetNumIncludedTestRuns() + draftedTests.size();
Output::TestSuiteFilter(m_suiteFilter);
Output::TestSuiteFilter(suiteType);
Output::ImpactAnalysisTestSelection(
selectedTests.GetTotalNumTests(), discardedTests.size(), selectedTests.GetNumExcludedTestRuns(), draftedTests.size());
}
// SafeImpactAnalysisTestSequenceStartCallback
void TestSequenceEventHandler::operator()(
Client::TestRunSelection&& selectedTests,
Client::TestRunSelection&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests)
void SafeImpactAnalysisTestSequenceStartCallback(
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const Client::TestRunSelection& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests)
{
ClearState();
m_numTests = selectedTests.GetNumIncludedTestRuns() + draftedTests.size();
Output::TestSuiteFilter(m_suiteFilter);
Output::TestSuiteFilter(suiteType);
Output::ImpactAnalysisTestSelection(
selectedTests.GetTotalNumTests(),
discardedTests.GetTotalNumTests(),
@@ -156,40 +147,49 @@ namespace TestImpact
draftedTests.size());
}
// TestSequenceCompleteCallback
void TestSequenceEventHandler::operator()(
Client::SequenceFailure&& failureReport,
AZStd::chrono::milliseconds duration)
void RegularTestSequenceCompleteCallback(const Client::RegularSequenceReport& sequenceReport)
{
Output::FailureReport(failureReport, duration);
Output::FailureReport(sequenceReport.GetSelectedTestRunReport());
std::cout << "Updating and serializing the test impact analysis data, this may take a moment...\n";
}
// SafeTestSequenceCompleteCallback
void TestSequenceEventHandler::operator()(
Client::SequenceFailure&& selectedFailureReport,
Client::SequenceFailure&& discardedFailureReport,
AZStd::chrono::milliseconds selectedDuration,
AZStd::chrono::milliseconds discaredDuration)
void SeedTestSequenceCompleteCallback(const Client::SeedSequenceReport& sequenceReport)
{
Output::FailureReport(sequenceReport.GetSelectedTestRunReport());
}
void ImpactAnalysisTestSequenceCompleteCallback(const Client::ImpactAnalysisSequenceReport& sequenceReport)
{
std::cout << "Selected test run:\n";
Output::FailureReport(selectedFailureReport, selectedDuration);
Output::FailureReport(sequenceReport.GetSelectedTestRunReport());
std::cout << "Discarded test run:\n";
Output::FailureReport(discardedFailureReport, discaredDuration);
std::cout << "Drafted test run:\n";
Output::FailureReport(sequenceReport.GetDraftedTestRunReport());
std::cout << "Updating and serializing the test impact analysis data, this may take a moment...\n";
}
// TestRunCompleteCallback
void TestSequenceEventHandler::operator()([[maybe_unused]] Client::TestRun&& test)
void SafeImpactAnalysisTestSequenceCompleteCallback(const Client::SafeImpactAnalysisSequenceReport& sequenceReport)
{
m_numTestsComplete++;
const auto progress = AZStd::string::format("(%03u/%03u)", m_numTestsComplete, m_numTests, test.GetTargetName().c_str());
std::cout << "Selected test run:\n";
Output::FailureReport(sequenceReport.GetSelectedTestRunReport());
std::cout << "Discarded test run:\n";
Output::FailureReport(sequenceReport.GetDiscardedTestRunReport());
std::cout << "Drafted test run:\n";
Output::FailureReport(sequenceReport.GetDraftedTestRunReport());
std::cout << "Updating and serializing the test impact analysis data, this may take a moment...\n";
}
void TestRunCompleteCallback(const Client::TestRunBase& testRun, size_t numTestRunsCompleted, size_t totalNumTestRuns)
{
const auto progress =
AZStd::string::format("(%03u/%03u)", numTestRunsCompleted, totalNumTestRuns, testRun.GetTargetName().c_str());
AZStd::string result;
switch (test.GetResult())
switch (testRun.GetResult())
{
case Client::TestRunResult::AllTestsPass:
{
@@ -216,15 +216,14 @@ namespace TestImpact
result = SetColorForString(Foreground::White, Background::Magenta, "TIME");
break;
}
default:
{
AZ_Error("TestRunCompleteCallback", false, "Unexpected test result to handle: %u", aznumeric_cast<AZ::u32>(testRun.GetResult()));
}
}
std::cout << progress.c_str() << " " << result.c_str() << " " << test.GetTargetName().c_str() << " (" << (test.GetDuration().count() / 1000.f) << "s)\n";
}
void TestSequenceEventHandler::ClearState()
{
m_numTests = 0;
m_numTestsComplete = 0;
std::cout << progress.c_str() << " " << result.c_str() << " " << testRun.GetTargetName().c_str() << " ("
<< (testRun.GetDuration().count() / 1000.f) << "s)\n";
}
} // namespace Console
} // namespace TestImpact
@@ -8,7 +8,7 @@
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <TestImpactFramework/TestImpactClientTestSelection.h>
#include <TestImpactFramework/TestImpactClientFailureReport.h>
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
#include <TestImpactFramework/TestImpactClientTestRun.h>
#include <AzCore/std/chrono/chrono.h>
@@ -21,48 +21,36 @@ namespace TestImpact
{
namespace Console
{
//! Event handler for all test sequence types.
class TestSequenceEventHandler
{
public:
explicit TestSequenceEventHandler(SuiteType suiteFilter);
//! Handler for TestSequenceStartCallback event.
void TestSequenceStartCallback(SuiteType suiteType, const Client::TestRunSelection& selectedTests);
//! TestSequenceStartCallback.
void operator()(Client::TestRunSelection&& selectedTests);
//! Handler for TestSequenceStartCallback event.
void ImpactAnalysisTestSequenceStartCallback(
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const AZStd::vector<AZStd::string>& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests);
//! ImpactAnalysisTestSequenceStartCallback.
void operator()(
Client::TestRunSelection&& selectedTests,
AZStd::vector<AZStd::string>&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests);
//! Handler for SafeImpactAnalysisTestSequenceStartCallback event.
void SafeImpactAnalysisTestSequenceStartCallback(
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const Client::TestRunSelection& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests);
//! SafeImpactAnalysisTestSequenceStartCallback.
void operator()(
Client::TestRunSelection&& selectedTests,
Client::TestRunSelection&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests);
//! Handler for RegularTestSequenceCompleteCallback event.
void RegularTestSequenceCompleteCallback(const Client::RegularSequenceReport& sequenceReport);
//! TestSequenceCompleteCallback.
void operator()(
Client::SequenceFailure&& failureReport,
AZStd::chrono::milliseconds duration);
//! Handler for SeedTestSequenceCompleteCallback event.
void SeedTestSequenceCompleteCallback(const Client::SeedSequenceReport& sequenceReport);
//! SafeTestSequenceCompleteCallback.
void operator()(
Client::SequenceFailure&& selectedFailureReport,
Client::SequenceFailure&& discardedFailureReport,
AZStd::chrono::milliseconds selectedDuration,
AZStd::chrono::milliseconds discaredDuration);
//! Handler for ImpactAnalysisTestSequenceCompleteCallback event.
void ImpactAnalysisTestSequenceCompleteCallback(const Client::ImpactAnalysisSequenceReport& sequenceReport);
//! TestRunCompleteCallback.
void operator()(Client::TestRun&& test);
//! Handler for SafeImpactAnalysisTestSequenceCompleteCallback event.
void SafeImpactAnalysisTestSequenceCompleteCallback(const Client::SafeImpactAnalysisSequenceReport& sequenceReport);
private:
void ClearState();
SuiteType m_suiteFilter;
size_t m_numTests = 0;
size_t m_numTestsComplete = 0;
};
//! Handler for TestRunCompleteCallback event.
void TestRunCompleteCallback(const Client::TestRunBase& testRun, size_t numTestRunsCompleted, size_t totalNumTestRuns);
} // namespace Console
} // namespace TestImpact
@@ -7,6 +7,7 @@
*/
#include <TestImpactFramework/TestImpactConfigurationException.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactRuntimeConfigurationFactory.h>
@@ -140,17 +141,17 @@ namespace TestImpact
return tempWorkspaceConfig;
}
AZStd::array<RepoPath, 3> ParseTestImpactAnalysisDataFiles(const RepoPath& root, const rapidjson::Value& sparTIAFile)
AZStd::array<RepoPath, 3> ParseTestImpactAnalysisDataFiles(const RepoPath& root, const rapidjson::Value& sparTiaFile)
{
AZStd::array<RepoPath, 3> sparTIAFiles;
sparTIAFiles[static_cast<size_t>(SuiteType::Main)] =
GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Main).c_str()].GetString());
sparTIAFiles[static_cast<size_t>(SuiteType::Periodic)] =
GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Periodic).c_str()].GetString());
sparTIAFiles[static_cast<size_t>(SuiteType::Sandbox)] =
GetAbsPathFromRelPath(root, sparTIAFile[GetSuiteTypeName(SuiteType::Sandbox).c_str()].GetString());
AZStd::array<RepoPath, 3> sparTiaFiles;
sparTiaFiles[static_cast<size_t>(SuiteType::Main)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Main).c_str()].GetString());
sparTiaFiles[static_cast<size_t>(SuiteType::Periodic)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Periodic).c_str()].GetString());
sparTiaFiles[static_cast<size_t>(SuiteType::Sandbox)] =
GetAbsPathFromRelPath(root, sparTiaFile[SuiteTypeAsString(SuiteType::Sandbox).c_str()].GetString());
return sparTIAFiles;
return sparTiaFiles;
}
WorkspaceConfig::Active ParseActiveWorkspaceConfig(const rapidjson::Value& activeWorkspace)
@@ -160,7 +161,7 @@ namespace TestImpact
activeWorkspaceConfig.m_root = activeWorkspace[Config::Keys[Config::Root]].GetString();
activeWorkspaceConfig.m_enumerationCacheDirectory
= GetAbsPathFromRelPath(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::EnumerationCacheDir]].GetString());
activeWorkspaceConfig.m_sparTIAFiles =
activeWorkspaceConfig.m_sparTiaFiles =
ParseTestImpactAnalysisDataFiles(activeWorkspaceConfig.m_root, relativePaths[Config::Keys[Config::TestImpactDataFiles]]);
return activeWorkspaceConfig;
}
@@ -1,125 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/string/string.h>
#include <AzCore/std/containers/vector.h>
namespace TestImpact
{
namespace Client
{
//! Represents a test target that failed, either due to failing to execute, completing in an abnormal state or completing with failing tests.
class TargetFailure
{
public:
TargetFailure(const AZStd::string& targetName);
//! Returns the name of the test target this failure pertains to.
const AZStd::string& GetTargetName() const;
private:
AZStd::string m_targetName;
};
//! Represents a test target that failed to execute.
class ExecutionFailure
: public TargetFailure
{
public:
ExecutionFailure(const AZStd::string& targetName, const AZStd::string& command);
//! Returns the command string used to execute this test target.
const AZStd::string& GetCommandString() const;
private:
AZStd::string m_commandString;
};
//! Represents an individual test of a test target that failed.
class TestFailure
{
public:
TestFailure(const AZStd::string& testName, const AZStd::string& errorMessage);
//! Returns the name of the test that failed.
const AZStd::string& GetName() const;
//! Returns the error message of the test that failed.
const AZStd::string& GetErrorMessage() const;
private:
AZStd::string m_name;
AZStd::string m_errorMessage;
};
//! Represents a collection of tests that failed.
//! @note Only the failing tests are included in the collection.
class TestCaseFailure
{
public:
TestCaseFailure(const AZStd::string& testCaseName, AZStd::vector<TestFailure>&& testFailures);
//! Returns the name of the test case containing the failing tests.
const AZStd::string& GetName() const;
//! Returns the collection of tests in this test case that failed.
const AZStd::vector<TestFailure>& GetTestFailures() const;
private:
AZStd::string m_name;
AZStd::vector<TestFailure> m_testFailures;
};
//! Represents a test target that launched successfully but contains failing tests.
class TestRunFailure
: public TargetFailure
{
public:
TestRunFailure(const AZStd::string& targetName, AZStd::vector<TestCaseFailure>&& testFailures);
//! Returns the total number of failing tests in this run.
size_t GetNumTestFailures() const;
//! Returns the test cases in this run containing failing tests.
const AZStd::vector<TestCaseFailure>& GetTestCaseFailures() const;
private:
AZStd::vector<TestCaseFailure> m_testCaseFailures;
size_t m_numTestFailures = 0;
};
//! Base class for reporting failing test sequences.
class SequenceFailure
{
public:
SequenceFailure(
AZStd::vector<ExecutionFailure>&& executionFailures,
AZStd::vector<TestRunFailure>&& testRunFailures,
AZStd::vector<TargetFailure>&& timedOutTests,
AZStd::vector<TargetFailure>&& unexecutedTests);
//! Returns the test targets in this sequence that failed to execute.
const AZStd::vector<ExecutionFailure>& GetExecutionFailures() const;
//! Returns the test targets that contain failing tests.
const AZStd::vector<TestRunFailure>& GetTestRunFailures() const;
//! Returns the test targets in this sequence that were terminated for exceeding their allotted runtime.
const AZStd::vector<TargetFailure>& GetTimedOutTests() const;
//! Returns the test targets in this sequence that were not executed due to the sequence terminating prematurely.
const AZStd::vector<TargetFailure>& GetUnexecutedTests() const;
private:
AZStd::vector<ExecutionFailure> m_executionFailures;
AZStd::vector<TestRunFailure> m_testRunFailures;
AZStd::vector<TargetFailure> m_timedOutTests;
AZStd::vector<TargetFailure> m_unexecutedTests;
};
} // namespace Client
} // namespace TestImpact
@@ -0,0 +1,544 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <TestImpactFramework/TestImpactClientTestRun.h>
#include <TestImpactFramework/TestImpactClientTestSelection.h>
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <AzCore/std/optional.h>
#include <AzCore/std/chrono/chrono.h>
namespace TestImpact
{
namespace Client
{
//! The report types generated by each sequence.
enum class SequenceReportType : AZ::u8
{
RegularSequence,
SeedSequence,
ImpactAnalysisSequence,
SafeImpactAnalysisSequence
};
//! Calculates the final sequence result for a composite of multiple sequences.
TestSequenceResult CalculateMultiTestSequenceResult(const AZStd::vector<TestSequenceResult>& results);
//! Report detailing the result and duration of a given set of test runs along with the details of each individual test run.
class TestRunReport
{
public:
//! Constructs the report for the given set of test runs that were run together in the same set.
//! @param result The result of this set of test runs.
//! @param startTime The time point his set of test runs started.
//! @param duration The duration this set of test runs took to complete.
//! @param passingTestRuns The set of test runs that executed successfully with no failing test runs.
//! @param failingTestRuns The set of test runs that executed successfully but had one or more failing tests.
//! @param executionFailureTestRuns The set of test runs that failed to execute.
//! @param timedOutTestRuns The set of test runs that executed successfully but were terminated prematurely due to timing out.
//! @param unexecutedTestRuns The set of test runs that were queued up for execution but did not get the opportunity to execute.
TestRunReport(
TestSequenceResult result,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
AZStd::vector<PassingTestRun>&& passingTestRuns,
AZStd::vector<FailingTestRun>&& failingTestRuns,
AZStd::vector<TestRunWithExecutionFailure>&& executionFailureTestRuns,
AZStd::vector<TimedOutTestRun>&& timedOutTestRuns,
AZStd::vector<UnexecutedTestRun>&& unexecutedTestRuns);
//! Returns the result of this sequence of test runs.
TestSequenceResult GetResult() const;
//! Returns the time this sequence of test runs started relative to T0.
AZStd::chrono::high_resolution_clock::time_point GetStartTime() const;
//! Returns the time this sequence of test runs ended relative to T0.
AZStd::chrono::high_resolution_clock::time_point GetEndTime() const;
//! Returns the duration this sequence of test runs took to complete.
AZStd::chrono::milliseconds GetDuration() const;
//! Returns the total number of test runs.
size_t GetTotalNumTestRuns() const;
//! Returns the number of passing test runs.
size_t GetNumPassingTestRuns() const;
//! Returns the number of failing test runs.
size_t GetNumFailingTestRuns() const;
//! Returns the number of test runs that failed to execute.
size_t GetNumExecutionFailureTestRuns() const;
//! Returns the number of timed out test runs.
size_t GetNumTimedOutTestRuns() const;
//! Returns the number of unexecuted test runs.
size_t GetNumUnexecutedTestRuns() const;
//! Returns the total number of passing tests across all test runs in the report.
size_t GetTotalNumPassingTests() const;
//! Returns the total number of failing tests across all test runs in the report.
size_t GetTotalNumFailingTests() const;
//! Returns the total number of disabled tests across all test runs in the report.
size_t GetTotalNumDisabledTests() const;
//! Returns the set of test runs that executed successfully with no failing tests.
const AZStd::vector<PassingTestRun>& GetPassingTestRuns() const;
//! Returns the set of test runs that executed successfully but had one or more failing tests.
const AZStd::vector<FailingTestRun>& GetFailingTestRuns() const;
//! Returns the set of test runs that failed to execute.
const AZStd::vector<TestRunWithExecutionFailure>& GetExecutionFailureTestRuns() const;
//! Returns the set of test runs that executed successfully but were terminated prematurely due to timing out.
const AZStd::vector<TimedOutTestRun>& GetTimedOutTestRuns() const;
//! Returns the set of test runs that were queued up for execution but did not get the opportunity to execute.
const AZStd::vector<UnexecutedTestRun>& GetUnexecutedTestRuns() const;
private:
TestSequenceResult m_result = TestSequenceResult::Success;
AZStd::chrono::high_resolution_clock::time_point m_startTime;
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{ 0 };
AZStd::vector<PassingTestRun> m_passingTestRuns;
AZStd::vector<FailingTestRun> m_failingTestRuns;
AZStd::vector<TestRunWithExecutionFailure> m_executionFailureTestRuns;
AZStd::vector<TimedOutTestRun> m_timedOutTestRuns;
AZStd::vector<UnexecutedTestRun> m_unexecutedTestRuns;
size_t m_totalNumPassingTests = 0;
size_t m_totalNumFailingTests = 0;
size_t m_totalNumDisabledTests = 0;
};
//! Base class for all sequence report types.
template<typename PolicyStateType>
class SequenceReportBase
{
public:
//! Constructs the report for a sequence of selected tests.
//! @param type The type of sequence this report is generated for.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
SequenceReportBase(
SequenceReportType type,
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const PolicyStateType& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
TestRunReport&& selectedTestRunReport)
: m_type(type)
, m_maxConcurrency(maxConcurrency)
, m_testTargetTimeout(testTargetTimeout)
, m_globalTimeout(globalTimeout)
, m_policyState(policyState)
, m_suite(suiteType)
, m_selectedTestRuns(selectedTestRuns)
, m_selectedTestRunReport(AZStd::move(selectedTestRunReport))
{
}
virtual ~SequenceReportBase() = default;
//! Returns the identifying type for this sequence report.
SequenceReportType GetType() const
{
return m_type;
}
//! Returns the maximum concurrency for this sequence.
size_t GetMaxConcurrency() const
{
return m_maxConcurrency;
}
//! Returns the global timeout for this sequence.
const AZStd::optional<AZStd::chrono::milliseconds>& GetGlobalTimeout() const
{
return m_globalTimeout;
}
//! Returns the test target timeout for this sequence.
const AZStd::optional<AZStd::chrono::milliseconds>& GetTestTargetTimeout() const
{
return m_testTargetTimeout;
}
//! Returns the policy state for this sequence.
const PolicyStateType& GetPolicyState() const
{
return m_policyState;
}
//! Returns the suite for this sequence.
SuiteType GetSuite() const
{
return m_suite;
}
//! Returns the result of the sequence.
virtual TestSequenceResult GetResult() const
{
return m_selectedTestRunReport.GetResult();
}
//! Returns the tests selected for running in the sequence.
TestRunSelection GetSelectedTestRuns() const
{
return m_selectedTestRuns;
}
//! Returns the report for the selected test runs.
TestRunReport GetSelectedTestRunReport() const
{
return m_selectedTestRunReport;
}
//! Returns the start time of the sequence.
AZStd::chrono::high_resolution_clock::time_point GetStartTime() const
{
return m_selectedTestRunReport.GetStartTime();
}
//! Returns the end time of the sequence.
AZStd::chrono::high_resolution_clock::time_point GetEndTime() const
{
return GetStartTime() + GetDuration();
}
//! Returns the entire duration the sequence took from start to finish.
virtual AZStd::chrono::milliseconds GetDuration() const
{
return m_selectedTestRunReport.GetDuration();
}
//! Returns the total number of test runs across all test run reports.
virtual size_t GetTotalNumTestRuns() const
{
return m_selectedTestRunReport.GetTotalNumTestRuns();
}
//! Returns the total number of passing tests across all test targets in all test run reports.
virtual size_t GetTotalNumPassingTests() const
{
return m_selectedTestRunReport.GetTotalNumPassingTests();
}
//! Returns the total number of failing tests across all test targets in all test run reports.
virtual size_t GetTotalNumFailingTests() const
{
return m_selectedTestRunReport.GetTotalNumFailingTests();
}
//! Returns the total number of unexecuted tests across all test targets in all test run reports.
virtual size_t GetTotalNumDisabledTests() const
{
return m_selectedTestRunReport.GetTotalNumDisabledTests();
}
//! Get the total number of test runs in the sequence that passed.
virtual size_t GetTotalNumPassingTestRuns() const
{
return m_selectedTestRunReport.GetNumPassingTestRuns();
}
//! Get the total number of test runs in the sequence that contain one or more test failures.
virtual size_t GetTotalNumFailingTestRuns() const
{
return m_selectedTestRunReport.GetNumFailingTestRuns();
}
//! Returns the total number of test runs that failed to execute.
virtual size_t GetTotalNumExecutionFailureTestRuns() const
{
return m_selectedTestRunReport.GetNumExecutionFailureTestRuns();
}
//! Get the total number of test runs in the sequence that timed out whilst in flight.
virtual size_t GetTotalNumTimedOutTestRuns() const
{
return m_selectedTestRunReport.GetNumTimedOutTestRuns();
}
//! Get the total number of test runs in the sequence that were queued for execution but did not get the opportunity to execute.
virtual size_t GetTotalNumUnexecutedTestRuns() const
{
return m_selectedTestRunReport.GetNumUnexecutedTestRuns();
}
private:
SequenceReportType m_type;
size_t m_maxConcurrency = 0;
AZStd::optional<AZStd::chrono::milliseconds> m_testTargetTimeout;
AZStd::optional<AZStd::chrono::milliseconds> m_globalTimeout;
PolicyStateType m_policyState;
SuiteType m_suite = SuiteType::Main;
TestRunSelection m_selectedTestRuns;
TestRunReport m_selectedTestRunReport;
};
//! Report type for regular test sequences.
class RegularSequenceReport
: public SequenceReportBase<SequencePolicyState>
{
public:
//! Constructs the report for a regular sequence.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
RegularSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
TestRunReport&& selectedTestRunReport);
};
//! Report type for seed test sequences.
class SeedSequenceReport
: public SequenceReportBase<SequencePolicyState>
{
public:
//! Constructs the report for a seed sequence.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
SeedSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
TestRunReport&& selectedTestRunReport);
};
//! Report detailing a test run sequence of selected and drafted tests.
template<typename PolicyStateType>
class DraftingSequenceReportBase
: public SequenceReportBase<PolicyStateType>
{
public:
//! Constructs the report for sequences that draft in previously failed/newly added test targets.
//! @param type The type of sequence this report is generated for.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param draftedTestRuns The target names of the drafted test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
//! @param draftedTestRunReport The report for the set of drafted test runs.
DraftingSequenceReportBase(
SequenceReportType type,
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const PolicyStateType& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
const AZStd::vector<AZStd::string>& draftedTestRuns,
TestRunReport&& selectedTestRunReport,
TestRunReport&& draftedTestRunReport)
: SequenceReportBase(
type,
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTestRuns,
AZStd::move(selectedTestRunReport))
, m_draftedTestRuns(draftedTestRuns)
, m_draftedTestRunReport(AZStd::move(draftedTestRunReport))
{
}
//! Returns the tests drafted for running in the sequence.
const AZStd::vector<AZStd::string>& GetDraftedTestRuns() const
{
return m_draftedTestRuns;
}
//! Returns the report for the drafted test runs.
TestRunReport GetDraftedTestRunReport() const
{
return m_draftedTestRunReport;
}
// SequenceReport overrides ...
AZStd::chrono::milliseconds GetDuration() const override
{
return SequenceReportBase::GetDuration() + m_draftedTestRunReport.GetDuration();
}
TestSequenceResult GetResult() const override
{
return CalculateMultiTestSequenceResult({ SequenceReportBase::GetResult(), m_draftedTestRunReport.GetResult() });
}
size_t GetTotalNumTestRuns() const override
{
return SequenceReportBase::GetTotalNumTestRuns() + m_draftedTestRunReport.GetTotalNumTestRuns();
}
size_t GetTotalNumPassingTests() const override
{
return SequenceReportBase::GetTotalNumPassingTests() + m_draftedTestRunReport.GetTotalNumPassingTests();
}
size_t GetTotalNumFailingTests() const override
{
return SequenceReportBase::GetTotalNumFailingTests() + m_draftedTestRunReport.GetTotalNumFailingTests();
}
size_t GetTotalNumDisabledTests() const override
{
return SequenceReportBase::GetTotalNumDisabledTests() + m_draftedTestRunReport.GetTotalNumDisabledTests();
}
size_t GetTotalNumPassingTestRuns() const override
{
return SequenceReportBase::GetTotalNumPassingTestRuns() + m_draftedTestRunReport.GetNumPassingTestRuns();
}
size_t GetTotalNumFailingTestRuns() const override
{
return SequenceReportBase::GetTotalNumFailingTestRuns() + m_draftedTestRunReport.GetNumFailingTestRuns();
}
size_t GetTotalNumExecutionFailureTestRuns() const override
{
return SequenceReportBase::GetTotalNumExecutionFailureTestRuns() + m_draftedTestRunReport.GetNumExecutionFailureTestRuns();
}
size_t GetTotalNumTimedOutTestRuns() const override
{
return SequenceReportBase::GetTotalNumTimedOutTestRuns() + m_draftedTestRunReport.GetNumTimedOutTestRuns();
}
size_t GetTotalNumUnexecutedTestRuns() const override
{
return SequenceReportBase::GetTotalNumUnexecutedTestRuns() + m_draftedTestRunReport.GetNumUnexecutedTestRuns();
}
private:
AZStd::vector<AZStd::string> m_draftedTestRuns;
TestRunReport m_draftedTestRunReport;
};
//! Report detailing an impact analysis sequence of selected, discarded and drafted tests.
class ImpactAnalysisSequenceReport
: public DraftingSequenceReportBase<ImpactAnalysisSequencePolicyState>
{
public:
//! Constructs the report for an impact analysis sequence.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param draftedTestRuns The target names of the drafted test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
//! @param draftedTestRunReport The report for the set of drafted test runs.
ImpactAnalysisSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const ImpactAnalysisSequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
const AZStd::vector<AZStd::string>& discardedTestRuns,
const AZStd::vector<AZStd::string>& draftedTestRuns,
TestRunReport&& selectedTestRunReport,
TestRunReport&& draftedTestRunReport);
//! Returns the test runs discarded from running in the sequence.
const AZStd::vector<AZStd::string>& GetDiscardedTestRuns() const;
private:
AZStd::vector<AZStd::string> m_discardedTestRuns;
};
//! Report detailing an impact analysis sequence of selected, discarded and drafted test runs.
class SafeImpactAnalysisSequenceReport
: public DraftingSequenceReportBase<SafeImpactAnalysisSequencePolicyState>
{
public:
//! Constructs the report for a sequence of selected, discarded and drafted test runs.
//! @param maxConcurrency The maximum number of concurrent test targets in flight at any given time.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param policyState The policy state this sequence was executed under.
//! @param suiteType The suite from which the tests have been selected from.
//! @param selectedTestRuns The target names of the selected test runs.
//! @param discardedTestRuns The target names of the discarded test runs.
//! @param draftedTestRuns The target names of the drafted test runs.
//! @param selectedTestRunReport The report for the set of selected test runs.
//! @param discardedTestRunReport The report for the set of discarded test runs.
//! @param draftedTestRunReport The report for the set of drafted test runs.
SafeImpactAnalysisSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SafeImpactAnalysisSequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
const TestRunSelection& discardedTestRuns,
const AZStd::vector<AZStd::string>& draftedTestRuns,
TestRunReport&& selectedTestRunReport,
TestRunReport&& discardedTestRunReport,
TestRunReport&& draftedTestRunReport);
// SequenceReport overrides ...
AZStd::chrono::milliseconds GetDuration() const override;
TestSequenceResult GetResult() const override;
size_t GetTotalNumTestRuns() const override;
size_t GetTotalNumPassingTests() const override;
size_t GetTotalNumFailingTests() const override;
size_t GetTotalNumDisabledTests() const override;
size_t GetTotalNumPassingTestRuns() const override;
size_t GetTotalNumFailingTestRuns() const override;
size_t GetTotalNumExecutionFailureTestRuns() const override;
size_t GetTotalNumTimedOutTestRuns() const override;
size_t GetTotalNumUnexecutedTestRuns() const override;
//! Returns the report for the discarded test runs.
const TestRunSelection GetDiscardedTestRuns() const;
//! Returns the report for the discarded test runs.
TestRunReport GetDiscardedTestRunReport() const;
private:
TestRunSelection m_discardedTestRuns;
TestRunReport m_discardedTestRunReport;
};
} // namespace Client
} // namespace TestImpact
@@ -0,0 +1,28 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
#include <AzCore/std/string/string.h>
namespace TestImpact
{
//! Serializes a regular sequence report to JSON format.
AZStd::string SerializeSequenceReport(const Client::RegularSequenceReport& sequenceReport);
//! Serializes a seed sequence report to JSON format.
AZStd::string SerializeSequenceReport(const Client::SeedSequenceReport& sequenceReport);
//! Serializes an impact analysis sequence report to JSON format.
AZStd::string SerializeSequenceReport(const Client::ImpactAnalysisSequenceReport& sequenceReport);
//! Serializes a safe impact analysis sequence report to JSON format.
AZStd::string SerializeSequenceReport(const Client::SafeImpactAnalysisSequenceReport& sequenceReport);
} // namespace TestImpact
@@ -6,8 +6,9 @@
*
*/
#include <AzCore/std/string/string.h>
#include <AzCore/std/chrono/chrono.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#pragma once
@@ -25,18 +26,165 @@ namespace TestImpact
AllTestsPass //!< The test run completed its run and all tests passed.
};
class TestRun
//! Representation of a test run.
class TestRunBase
{
public:
TestRun(const AZStd::string& name, TestRunResult result, AZStd::chrono::milliseconds duration);
//! Constructs the client facing representation of a given test target's run.
//! @param name The name of the test target.
//! @param commandString The command string used to execute this test target.
//! @param startTime The start time, relative to the sequence start, that this run started.
//! @param duration The duration that this test run took to complete.
//! @param result The result of the run.
TestRunBase(
const AZStd::string& name,
const AZStd::string& commandString,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
TestRunResult result);
virtual ~TestRunBase() = default;
//! Returns the test target name.
const AZStd::string& GetTargetName() const;
//! Returns the test run result.
TestRunResult GetResult() const;
//! Returns the test run start time.
AZStd::chrono::high_resolution_clock::time_point GetStartTime() const;
//! Returns the end time, relative to the sequence start, that this run ended.
AZStd::chrono::high_resolution_clock::time_point GetEndTime() const;
//! Returns the duration that this test run took to complete.
AZStd::chrono::milliseconds GetDuration() const;
//! Returns the command string used to execute this test target.
const AZStd::string& GetCommandString() const;
private:
AZStd::string m_targetName;
AZStd::string m_commandString;
TestRunResult m_result;
AZStd::chrono::high_resolution_clock::time_point m_startTime;
AZStd::chrono::milliseconds m_duration;
};
//! Representation of a test run that failed to execute.
class TestRunWithExecutionFailure
: public TestRunBase
{
public:
using TestRunBase::TestRunBase;
TestRunWithExecutionFailure(TestRunBase&& testRun);
};
//! Representation of a test run that was terminated in-flight due to timing out.
class TimedOutTestRun
: public TestRunBase
{
public:
using TestRunBase::TestRunBase;
TimedOutTestRun(TestRunBase&& testRun);
};
//! Representation of a test run that was not executed.
class UnexecutedTestRun
: public TestRunBase
{
public:
using TestRunBase::TestRunBase;
UnexecutedTestRun(TestRunBase&& testRun);
};
// Result of a test executed during a test run.
enum class TestResult : AZ::u8
{
Passed,
Failed,
NotRun
};
//! Representation of a single test in a test target.
class Test
{
public:
//! Constructs the test with the specified name and result.
Test(const AZStd::string& testName, TestResult result);
//! Returns the name of this test.
const AZStd::string& GetName() const;
//! Returns the result of executing this test.
TestResult GetResult() const;
private:
AZStd::string m_name;
TestResult m_result;
};
//! Representation of a test run that completed with or without test failures.
class CompletedTestRun
: public TestRunBase
{
public:
//! Constructs the test run from the specified test target executaion data.
//! @param name The name of the test target for this run.
//! @param commandString The command string used to execute the test target for this run.
//! @param startTime The start time, offset from the sequence start time, that this test run started.
//! @param duration The duration that this test run took to complete.
//! @param result The result of this test run.
//! @param tests The tests contained in the test target for this test run.
CompletedTestRun(
const AZStd::string& name,
const AZStd::string& commandString,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
TestRunResult result,
AZStd::vector<Test>&& tests);
//! Constructs the test run from the specified test target executaion data.
CompletedTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests);
//! Returns the total number of tests in the run.
size_t GetTotalNumTests() const;
//! Returns the total number of passing tests in the run.
size_t GetTotalNumPassingTests() const;
//! Returns the total number of failing tests in the run.
size_t GetTotalNumFailingTests() const;
//! Returns the total number of disabled tests in the run.
size_t GetTotalNumDisabledTests() const;
//! Returns the tests in the run.
const AZStd::vector<Test>& GetTests() const;
private:
AZStd::vector<Test> m_tests;
size_t m_totalNumPassingTests = 0;
size_t m_totalNumFailingTests = 0;
size_t m_totalNumDisabledTests = 0;
};
//! Representation of a test run that completed with no test failures.
class PassingTestRun
: public CompletedTestRun
{
public:
using CompletedTestRun::CompletedTestRun;
PassingTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests);
};
//! Representation of a test run that completed with one or more test failures.
class FailingTestRun
: public CompletedTestRun
{
public:
using CompletedTestRun::CompletedTestRun;
FailingTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests);
};
} // namespace Client
} // namespace TestImpact
@@ -21,6 +21,7 @@ namespace TestImpact
class TestRunSelection
{
public:
TestRunSelection() = default;
TestRunSelection(const AZStd::vector<AZStd::string>& includedTests, const AZStd::vector<AZStd::string>& excludedTests);
TestRunSelection(AZStd::vector<AZStd::string>&& includedTests, AZStd::vector<AZStd::string>&& excludedTests);
@@ -44,7 +44,7 @@ namespace TestImpact
{
RepoPath m_root; //!< Path to the persistent workspace tracked by the repository.
RepoPath m_enumerationCacheDirectory; //!< Path to the test enumerations cache.
AZStd::array<RepoPath, 3> m_sparTIAFiles; //!< Paths to the test impact analysis data files for each test suite.
AZStd::array<RepoPath, 3> m_sparTiaFiles; //!< Paths to the test impact analysis data files for each test suite.
};
Temp m_temp;
@@ -0,0 +1,81 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/base.h>
namespace TestImpact
{
namespace Policy
{
//! Policy for handling of test targets that fail to execute (e.g. due to the binary not being found).
//! @note Test targets that fail to execute will be tagged such that their execution can be attempted at a later date. This is
//! important as otherwise it would be erroneously assumed that they cover no sources due to having no entries in the dynamic
//! dependency map.
enum class ExecutionFailure : AZ::u8
{
Abort, //!< Abort the test sequence and report a failure.
Continue, //!< Continue the test sequence but treat the execution failures as test failures after the run.
Ignore //!< Continue the test sequence and ignore the execution failures.
};
//! Policy for handling the coverage data of failed tests targets (both tests that failed to execute and tests that ran but failed).
enum class FailedTestCoverage : AZ::u8
{
Discard, //!< Discard the coverage data produced by the failing tests, causing them to be drafted into future test runs.
Keep //!< Keep any existing coverage data and update the coverage data for failed test targets that produce coverage.
};
//! Policy for prioritizing selected tests.
enum class TestPrioritization : AZ::u8
{
None, //!< Do not attempt any test prioritization.
DependencyLocality //!< Prioritize test targets according to the locality of the production targets they cover in the build
//!< dependency graph.
};
//! Policy for handling test targets that report failing tests.
enum class TestFailure : AZ::u8
{
Abort, //!< Abort the test sequence and report the test failure.
Continue //!< Continue the test sequence and report the test failures after the run.
};
//! Policy for handling integrity failures of the dynamic dependency map and the source to target mappings.
enum class IntegrityFailure : AZ::u8
{
Abort, //!< Abort the test sequence and report the test failure.
Continue //!< Continue the test sequence and report the test failures after the run.
};
//! Policy for updating the dynamic dependency map with the coverage data of produced by test sequences.
enum class DynamicDependencyMap : AZ::u8
{
Discard, //!< Discard the coverage data produced by test sequences.
Update //!< Update the dynamic dependency map with the coverage data produced by test sequences.
};
//! Policy for sharding test targets that have been marked for test sharding.
enum class TestSharding : AZ::u8
{
Never, //!< Do not shard any test targets.
Always //!< Shard all test targets that have been marked for test sharding.
};
//! Standard output capture of test target runs.
enum class TargetOutputCapture : AZ::u8
{
None, //!< Do not capture any output.
StdOut, //!< Send captured output to standard output
File, //!< Write captured output to file.
StdOutAndFile //!< Send captured output to standard output and write to file.
};
} // namespace Policy
} // namespace TestImpact
@@ -12,7 +12,7 @@
#include <TestImpactFramework/TestImpactChangeList.h>
#include <TestImpactFramework/TestImpactClientTestSelection.h>
#include <TestImpactFramework/TestImpactClientTestRun.h>
#include <TestImpactFramework/TestImpactClientFailureReport.h>
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <AzCore/std/string/string.h>
@@ -34,10 +34,12 @@ namespace TestImpact
class TestEngineInstrumentedRun;
//! Callback for a test sequence that isn't using test impact analysis to determine selected tests.
//! @parm suiteType The test suite to select tests from.
//! @param tests The tests that will be run for this sequence.
using TestSequenceStartCallback = AZStd::function<void(Client::TestRunSelection&& tests)>;
using TestSequenceStartCallback = AZStd::function<void(SuiteType suiteType, const Client::TestRunSelection& tests)>;
//! Callback for a test sequence using test impact analysis.
//! @parm suiteType The test suite to select tests from.
//! @param selectedTests The tests that have been selected for this run by test impact analysis.
//! @param discardedTests The tests that have been rejected for this run by test impact analysis.
//! @param draftedTests The tests that have been drafted in for this run due to requirements outside of test impact analysis
@@ -46,11 +48,13 @@ namespace TestImpact
//! These tests will be run with coverage instrumentation.
//! @note discardedTests and draftedTests may contain overlapping tests.
using ImpactAnalysisTestSequenceStartCallback = AZStd::function<void(
Client::TestRunSelection&& selectedTests,
AZStd::vector<AZStd::string>&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests)>;
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const AZStd::vector<AZStd::string>& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests)>;
//! Callback for a test sequence using test impact analysis.
//! @parm suiteType The test suite to select tests from.
//! @param selectedTests The tests that have been selected for this run by test impact analysis.
//! @param discardedTests The tests that have been rejected for this run by test impact analysis.
//! These tests will not be run without coverage instrumentation unless there is an entry in the draftedTests list.
@@ -59,30 +63,22 @@ namespace TestImpact
//! to execute previously).
//! @note discardedTests and draftedTests may contain overlapping tests.
using SafeImpactAnalysisTestSequenceStartCallback = AZStd::function<void(
Client::TestRunSelection&& selectedTests,
Client::TestRunSelection&& discardedTests,
AZStd::vector<AZStd::string>&& draftedTests)>;
SuiteType suiteType,
const Client::TestRunSelection& selectedTests,
const Client::TestRunSelection& discardedTests,
const AZStd::vector<AZStd::string>& draftedTests)>;
//! Callback for end of a test sequence.
//! @param failureReport The test runs that failed for any reason during this sequence.
//! @param duration The total duration of this test sequence.
using TestSequenceCompleteCallback = AZStd::function<void(
Client::SequenceFailure&& failureReport,
AZStd::chrono::milliseconds duration)>;
//! Callback for end of a test impact analysis test sequence.
//! @param selectedFailureReport The selected test runs that failed for any reason during this sequence.
//! @param discardedFailureReport The discarded test runs that failed for any reason during this sequence.
//! @param duration The total duration of this test sequence.
using SafeTestSequenceCompleteCallback = AZStd::function<void(
Client::SequenceFailure&& selectedFailureReport,
Client::SequenceFailure&& discardedFailureReport,
AZStd::chrono::milliseconds selectedDuration,
AZStd::chrono::milliseconds discardedDuration)>;
//! @tparam SequenceReportType The report type to be used for the sequence.
//! @param sequenceReport The completed sequence report.
template<typename SequenceReportType>
using TestSequenceCompleteCallback = AZStd::function<void(const SequenceReportType& sequenceReport)>;
//! Callback for test runs that have completed for any reason.
//! @param selectedTests The test that has completed.
using TestRunCompleteCallback = AZStd::function<void(Client::TestRun&& selectedTests)>;
//! @param testRunMeta The test that has completed.
//! @param numTestRunsCompleted The number of test runs that have completed.
//! @param totalNumTestRuns The total number of test runs in the sequence.
using TestRunCompleteCallback = AZStd::function<void(Client::TestRunBase& testRun, size_t numTestRunsCompleted, size_t totalNumTestRuns)>;
//! The API exposed to the client responsible for all test runs and persistent data management.
class Runtime
@@ -90,6 +86,7 @@ namespace TestImpact
public:
//! Constructs a runtime with the specified configuration and policies.
//! @param config The configuration used for this runtime instance.
//! @param dataFile The optional data file to be used instead of that specified in the config file.
//! @param suiteFilter The test suite for which the coverage data and test selection will draw from.
//! @param executionFailurePolicy Determines how to handle test targets that fail to execute.
//! @param executionFailureDraftingPolicy Determines how test targets that previously failed to execute are drafted into subsequent test sequences.
@@ -98,6 +95,7 @@ namespace TestImpact
//! @param testShardingPolicy Determines how to handle test targets that have opted in to test sharding.
Runtime(
RuntimeConfig&& config,
AZStd::optional<RepoPath> dataFile,
SuiteType suiteFilter,
Policy::ExecutionFailure executionFailurePolicy,
Policy::FailedTestCoverage failedTestCoveragePolicy,
@@ -108,19 +106,19 @@ namespace TestImpact
AZStd::optional<size_t> maxConcurrency = AZStd::nullopt);
~Runtime();
//! Runs a test sequence where all tests with a matching suite in the suite filter and also not on the excluded list are selected.
//! @param testTargetTimeout The maximum duration individual test targets may be in flight for (infinite if empty).
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests.
//! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed.
//! @param testRunCompleteCallback The client function to be called after an individual test run has completed.
//! @returns
TestSequenceResult RegularTestSequence(
//! @returns The test run and sequence report for the selected test sequence.
Client::RegularSequenceReport RegularTestSequence(
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceCompleteCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::RegularSequenceReport>> testSequenceCompleteCallback,
AZStd::optional<TestRunCompleteCallback> testRunCompleteCallback);
//! Runs a test sequence where tests are selected according to test impact analysis so long as they are not on the excluded list.
@@ -132,15 +130,15 @@ namespace TestImpact
//! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests.
//! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed.
//! @param testRunCompleteCallback The client function to be called after an individual test run has completed.
//! @returns
TestSequenceResult ImpactAnalysisTestSequence(
//! @returns The test run and sequence report for the selected and drafted test sequences.
Client::ImpactAnalysisSequenceReport ImpactAnalysisTestSequence(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy,
Policy::DynamicDependencyMap dynamicDependencyMapPolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<ImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceCompleteCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::ImpactAnalysisSequenceReport>> testSequenceCompleteCallback,
AZStd::optional<TestRunCompleteCallback> testRunCompleteCallback);
//! Runs a test sequence as per the ImpactAnalysisTestSequence where the tests not selected are also run (albeit without instrumentation).
@@ -151,14 +149,14 @@ namespace TestImpact
//! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests.
//! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed.
//! @param testRunCompleteCallback The client function to be called after an individual test run has completed.
//! @returns
AZStd::pair<TestSequenceResult, TestSequenceResult> SafeImpactAnalysisTestSequence(
//! @returns The test run and sequence report for the selected, discarded and drafted test sequences.
Client::SafeImpactAnalysisSequenceReport SafeImpactAnalysisTestSequence(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<SafeImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<SafeTestSequenceCompleteCallback> testSequenceCompleteCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::SafeImpactAnalysisSequenceReport>> testSequenceCompleteCallback,
AZStd::optional<TestRunCompleteCallback> testRunCompleteCallback);
//! Runs all tests not on the excluded list and uses their coverage data to seed the test impact analysis data (ant existing data will be overwritten).
@@ -167,12 +165,12 @@ namespace TestImpact
//! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the tests.
//! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed.
//! @param testRunCompleteCallback The client function to be called after an individual test run has completed.
//!
TestSequenceResult SeededTestSequence(
//! @returns The test run and sequence report for the selected test sequence.
Client::SeedSequenceReport SeededTestSequence(
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceCompleteCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::SeedSequenceReport>> testSequenceCompleteCallback,
AZStd::optional<TestRunCompleteCallback> testRunCompleteCallback);
//! Returns true if the runtime has test impact analysis data (either preexisting or generated).
@@ -188,7 +186,7 @@ namespace TestImpact
//! @param changeList The change list for which the covering tests and enumeration cache updates will be generated for.
//! @param testPrioritizationPolicy The test prioritization strategy to use for the selected test targets.
//! @returns The pair of selected test targets and discarded test targets.
AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> SelectCoveringTestTargetsAndUpdateEnumerationCache(
AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> SelectCoveringTestTargets(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy);
@@ -208,9 +206,23 @@ namespace TestImpact
//! Updates the dynamic dependency map and serializes the entire map to disk.
void UpdateAndSerializeDynamicDependencyMap(const AZStd::vector<TestEngineInstrumentedRun>& jobs);
//! Generates a base policy state for the current runtime policy runtime configuration.
PolicyStateBase GeneratePolicyStateBase() const;
//! Generates a regular/seed sequence policy state for the current runtime policy runtime configuration.
SequencePolicyState GenerateSequencePolicyState() const;
//! Generates a safe impact analysis sequence policy state for the current runtime policy runtime configuration.
SafeImpactAnalysisSequencePolicyState GenerateSafeImpactAnalysisSequencePolicyState(
Policy::TestPrioritization testPrioritizationPolicy) const;
//! Generates an impact analysis sequence policy state for the current runtime policy runtime configuration.
ImpactAnalysisSequencePolicyState GenerateImpactAnalysisSequencePolicyState(
Policy::TestPrioritization testPrioritizationPolicy, Policy::DynamicDependencyMap dynamicDependencyMapPolicy) const;
RuntimeConfig m_config;
RepoPath m_sparTiaFile;
SuiteType m_suiteFilter;
RepoPath m_sparTIAFile;
Policy::ExecutionFailure m_executionFailurePolicy;
Policy::FailedTestCoverage m_failedTestCoveragePolicy;
Policy::TestFailure m_testFailurePolicy;
@@ -0,0 +1,22 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <TestImpactFramework/TestImpactException.h>
namespace TestImpact
{
//! Exception for sequence report operations.
class SequenceReportException
: public Exception
{
public:
using Exception::Exception;
};
} // namespace TestImpact
@@ -9,76 +9,12 @@
#pragma once
#include <TestImpactFramework/TestImpactRuntimeException.h>
#include <TestImpactFramework/TestImpactPolicy.h>
#include <AzCore/std/containers/array.h>
namespace TestImpact
{
namespace Policy
{
//! Policy for handling of test targets that fail to execute (e.g. due to the binary not being found).
//! @note Test targets that fail to execute will be tagged such that their execution can be attempted at a later date. This is
//! important as otherwise it would be erroneously assumed that they cover no sources due to having no entries in the dynamic
//! dependency map.
enum class ExecutionFailure
{
Abort, //!< Abort the test sequence and report a failure.
Continue, //!< Continue the test sequence but treat the execution failures as test failures after the run.
Ignore //!< Continue the test sequence and ignore the execution failures.
};
//! Policy for handling the coverage data of failed tests targets (both test that failed to execute and tests that ran but failed).
enum class FailedTestCoverage
{
Discard, //!< Discard the coverage data produced by the failing tests, causing them to be drafted into future test runs.
Keep //!< Keep any existing coverage data and update the coverage data for failed test targetss that produce coverage.
};
//! Policy for prioritizing selected tests.
enum class TestPrioritization
{
None, //!< Do not attempt any test prioritization.
DependencyLocality //!< Prioritize test targets according to the locality of the production targets they cover in the build dependency graph.
};
//! Policy for handling test targets that report failing tests.
enum class TestFailure
{
Abort, //!< Abort the test sequence and report the test failure.
Continue //!< Continue the test sequence and report the test failures after the run.
};
//! Policy for handling integrity failures of the dynamic dependency map and the source to target mappings.
enum class IntegrityFailure
{
Abort, //!< Abort the test sequence and report the test failure.
Continue //!< Continue the test sequence and report the test failures after the run.
};
//! Policy for updating the dynamic dependency map with the coverage data of produced by test sequences.
enum class DynamicDependencyMap
{
Discard, //!< Discard the coverage data produced by test sequences.
Update //!< Update the dynamic dependency map with the coverage data produced by test sequences.
};
//! Policy for sharding test targets that have been marked for test sharding.
enum class TestSharding
{
Never, //!< Do not shard any test targets.
Always //!< Shard all test targets that have been marked for test sharding.
};
//! Standard output capture of test target runs.
enum class TargetOutputCapture
{
None, //!< Do not capture any output.
StdOut, //!< Send captured output to standard output
File, //!< Write captured output to file.
StdOutAndFile //!< Send captured output to standard output and write to file.
};
}
//! Configuration for test targets that opt in to test sharding.
enum class ShardConfiguration
{
@@ -97,22 +33,6 @@ namespace TestImpact
Sandbox
};
//! User-friendly names for the test suite types.
inline AZStd::string GetSuiteTypeName(SuiteType suiteType)
{
switch (suiteType)
{
case SuiteType::Main:
return "main";
case SuiteType::Periodic:
return "periodic";
case SuiteType::Sandbox:
return "sandbox";
default:
throw(RuntimeException("Unexpected suite type"));
}
}
//! Result of a test sequence that was run.
enum class TestSequenceResult
{
@@ -120,4 +40,36 @@ namespace TestImpact
Failure, //!< One or more tests failed and/or timed out and/or failed to launch and/or an integrity failure was encountered.
Timeout //!< The global timeout for the sequence was exceeded.
};
//! Base representation of runtime policies.
struct PolicyStateBase
{
Policy::ExecutionFailure m_executionFailurePolicy = Policy::ExecutionFailure::Continue;
Policy::FailedTestCoverage m_failedTestCoveragePolicy = Policy::FailedTestCoverage::Keep;
Policy::TestFailure m_testFailurePolicy = Policy::TestFailure::Abort;
Policy::IntegrityFailure m_integrityFailurePolicy = Policy::IntegrityFailure::Abort;
Policy::TestSharding m_testShardingPolicy = Policy::TestSharding::Never;
Policy::TargetOutputCapture m_targetOutputCapture = Policy::TargetOutputCapture::None;
};
//! Representation of regular and seed sequence policies.
struct SequencePolicyState
{
PolicyStateBase m_basePolicies;
};
//! Representation of impact analysis sequence policies.
struct ImpactAnalysisSequencePolicyState
{
PolicyStateBase m_basePolicies;
Policy::TestPrioritization m_testPrioritizationPolicy = Policy::TestPrioritization::None;
Policy::DynamicDependencyMap m_dynamicDependencyMap = Policy::DynamicDependencyMap::Update;
};
//! Representation of safe impact analysis sequence policies.
struct SafeImpactAnalysisSequencePolicyState
{
PolicyStateBase m_basePolicies;
Policy::TestPrioritization m_testPrioritizationPolicy = Policy::TestPrioritization::None;
};
} // namespace TestImpact
@@ -6,12 +6,12 @@
*
*/
#include <TestImpactFramework/TestImpactException.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <TestImpactFramework/TestImpactRepoPath.h>
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
#include <AzCore/IO/SystemFile.h>
#include <AzCore/std/containers/vector.h>
#include <AzCore/std/string/string.h>
#pragma once
@@ -59,23 +59,48 @@ namespace TestImpact
//! Delete the files that match the pattern from the specified directory.
//! @param path The path to the directory to pattern match the files for deletion.
//! @param pattern The pattern to match files for deletion.
inline void DeleteFiles(const RepoPath& path, const AZStd::string& pattern)
{
AZ::IO::SystemFile::FindFiles(AZStd::string::format("%s/%s", path.c_str(), pattern.c_str()).c_str(),
[&path](const char* file, bool isFile)
{
if (isFile)
{
AZ::IO::SystemFile::Delete(AZStd::string::format("%s/%s", path.c_str(), file).c_str());
}
return true;
});
}
//! @return The number of files that were deleted.
size_t DeleteFiles(const RepoPath& path, const AZStd::string& pattern);
//! Deletes the specified file.
inline void DeleteFile(const RepoPath& file)
{
DeleteFiles(file.ParentPath(), file.Filename().Native());
}
void DeleteFile(const RepoPath& file);
//! User-friendly names for the test suite types.
AZStd::string SuiteTypeAsString(SuiteType suiteType);
//! User-friendly names for the sequence report types.
AZStd::string SequenceReportTypeAsString(Client::SequenceReportType type);
//! User-friendly names for the sequence result types.
AZStd::string TestSequenceResultAsString(TestSequenceResult result);
//! User-friendly names for the test run result types.
AZStd::string TestRunResultAsString(Client::TestRunResult result);
//! User-friendly names for the execution failure policy types.
AZStd::string ExecutionFailurePolicyAsString(Policy::ExecutionFailure executionFailurePolicy);
//! User-friendly names for the failed test coverage policy types.
AZStd::string FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage failedTestCoveragePolicy);
//! User-friendly names for the test prioritization policy types.
AZStd::string TestPrioritizationPolicyAsString(Policy::TestPrioritization testPrioritizationPolicy);
//! User-friendly names for the test failure policy types.
AZStd::string TestFailurePolicyAsString(Policy::TestFailure testFailurePolicy);
//! User-friendly names for the integrity failure policy types.
AZStd::string IntegrityFailurePolicyAsString(Policy::IntegrityFailure integrityFailurePolicy);
//! User-friendly names for the dynamic dependency map policy types.
AZStd::string DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap dynamicDependencyMapPolicy);
//! User-friendly names for the test sharding policy types.
AZStd::string TestShardingPolicyAsString(Policy::TestSharding testShardingPolicy);
//! User-friendly names for the target output capture policy types.
AZStd::string TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture targetOutputCapturePolicy);
//! User-friendly names for the client test result types.
AZStd::string ClientTestResultAsString(Client::TestResult result);
} // namespace TestImpact
@@ -6,6 +6,8 @@
*
*/
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactTestTargetMetaMapFactory.h>
#include <Artifact/TestImpactArtifactException.h>
@@ -67,7 +69,7 @@ namespace TestImpact
{
// Check to see if this test target has the suite we're looking for
if (const auto suiteName = suite[Keys[SuiteKey]].GetString();
strcmp(GetSuiteTypeName(suiteType).c_str(), suiteName) == 0)
strcmp(SuiteTypeAsString(suiteType).c_str(), suiteName) == 0)
{
testMeta.m_suite = suiteName;
testMeta.m_customArgs = suite[Keys[CommandKey]].GetString();
@@ -156,8 +156,6 @@ namespace TestImpact
{
coveringTestTargetIt->second.erase(source);
}
}
// 2.
@@ -9,7 +9,7 @@
#pragma once
#include <TestImpactFramework/TestImpactChangeList.h>
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <TestImpactFramework/TestImpactPolicy.h>
#include <Artifact/Static/TestImpactProductionTargetDescriptor.h>
#include <Artifact/Static/TestImpactTestTargetDescriptor.h>
@@ -8,7 +8,7 @@
#pragma once
#include <TestImpactFramework/TestImpactTestSequence.h>
#include <TestImpactFramework/TestImpactPolicy.h>
#include <Artifact/Static/TestImpactDependencyGraphData.h>
#include <Dependency/TestImpactChangeDependencyList.h>
@@ -11,6 +11,7 @@
#include <Process/TestImpactProcessException.h>
#include <AzCore/std/parallel/lock.h>
#include <AzCore/std/string/conversions.h>
namespace TestImpact
{
@@ -55,9 +56,11 @@ namespace TestImpact
CreatePipes(sa, si);
if (!CreateProcess(
AZStd::wstring argsW;
AZStd::to_wstring(argsW, args.c_str());
if (!CreateProcessW(
NULL,
&args[0],
argsW.data(),
NULL,
NULL,
IsPiping(),
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactTestEnumerationSuiteFactory.h>
#include <TestEngine/TestImpactTestEngineException.h>
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactModuleCoverageFactory.h>
#include <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <Artifact/Factory/TestImpactTestRunSuiteFactory.h>
#include <TestEngine/TestImpactTestEngineException.h>
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <Target/TestImpactTestTarget.h>
#include <TestEngine/TestImpactTestEngineException.h>
@@ -262,7 +262,7 @@ namespace TestImpact
Policy::TestFailure testFailurePolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback)
AZStd::optional<TestEngineJobCompleteCallback> callback) const
{
TestEngineJobMap<TestEnumerator::JobInfo::IdType> engineJobs;
const auto jobInfos = m_testJobInfoGenerator->GenerateTestEnumerationJobInfos(testTargets, TestEnumerator::JobInfo::CachePolicy::Write);
@@ -285,7 +285,7 @@ namespace TestImpact
[[maybe_unused]]Policy::TargetOutputCapture targetOutputCapture,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback)
AZStd::optional<TestEngineJobCompleteCallback> callback) const
{
DeleteArtifactXmls();
@@ -312,7 +312,7 @@ namespace TestImpact
[[maybe_unused]]Policy::TargetOutputCapture targetOutputCapture,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback)
AZStd::optional<TestEngineJobCompleteCallback> callback) const
{
DeleteArtifactXmls();
@@ -69,7 +69,7 @@ namespace TestImpact
Policy::TestFailure testFailurePolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback);
AZStd::optional<TestEngineJobCompleteCallback> callback) const;
//! Performs a test run without any instrumentation and, for each test target, returns the test run results and metrics about the run.
//! @param testTargets The test targets to run.
@@ -89,7 +89,7 @@ namespace TestImpact
Policy::TargetOutputCapture targetOutputCapture,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback);
AZStd::optional<TestEngineJobCompleteCallback> callback) const;
//! Performs a test run with instrumentation and, for each test target, returns the test run results, coverage data and metrics about the run.
//! @param testTargets The test targets to run.
@@ -111,7 +111,7 @@ namespace TestImpact
Policy::TargetOutputCapture targetOutputCapture,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestEngineJobCompleteCallback> callback);
AZStd::optional<TestEngineJobCompleteCallback> callback) const;
private:
//! Cleans up the artifacts directory of any artifacts from previous runs.
@@ -1,120 +0,0 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <TestImpactFramework/TestImpactClientFailureReport.h>
namespace TestImpact
{
namespace Client
{
TargetFailure::TargetFailure(const AZStd::string& targetName)
: m_targetName(targetName)
{
}
const AZStd::string& TargetFailure::GetTargetName() const
{
return m_targetName;
}
ExecutionFailure::ExecutionFailure(const AZStd::string& targetName, const AZStd::string& command)
: TargetFailure(targetName)
, m_commandString(command)
{
}
const AZStd::string& ExecutionFailure::GetCommandString() const
{
return m_commandString;
}
TestFailure::TestFailure(const AZStd::string& testName, const AZStd::string& errorMessage)
: m_name(testName)
, m_errorMessage(errorMessage)
{
}
const AZStd::string& TestFailure::GetName() const
{
return m_name;
}
const AZStd::string& TestFailure::GetErrorMessage() const
{
return m_errorMessage;
}
TestCaseFailure::TestCaseFailure(const AZStd::string& testCaseName, AZStd::vector<TestFailure>&& testFailures)
: m_name(testCaseName)
, m_testFailures(AZStd::move(testFailures))
{
}
const AZStd::string& TestCaseFailure::GetName() const
{
return m_name;
}
const AZStd::vector<TestFailure>& TestCaseFailure::GetTestFailures() const
{
return m_testFailures;
}
TestRunFailure::TestRunFailure(const AZStd::string& targetName, AZStd::vector<TestCaseFailure>&& testFailures)
: TargetFailure(targetName)
, m_testCaseFailures(AZStd::move(testFailures))
{
for (const auto& testCase : m_testCaseFailures)
{
m_numTestFailures += testCase.GetTestFailures().size();
}
}
size_t TestRunFailure::GetNumTestFailures() const
{
return m_numTestFailures;
}
const AZStd::vector<TestCaseFailure>& TestRunFailure::GetTestCaseFailures() const
{
return m_testCaseFailures;
}
SequenceFailure::SequenceFailure(
AZStd::vector<ExecutionFailure>&& executionFailures,
AZStd::vector<TestRunFailure>&& testRunFailures,
AZStd::vector<TargetFailure>&& timedOutTests,
AZStd::vector<TargetFailure>&& unexecutionTests)
: m_executionFailures(AZStd::move(executionFailures))
, m_testRunFailures(testRunFailures)
, m_timedOutTests(AZStd::move(timedOutTests))
, m_unexecutedTests(AZStd::move(unexecutionTests))
{
}
const AZStd::vector<ExecutionFailure>& SequenceFailure::GetExecutionFailures() const
{
return m_executionFailures;
}
const AZStd::vector<TestRunFailure>& SequenceFailure::GetTestRunFailures() const
{
return m_testRunFailures;
}
const AZStd::vector<TargetFailure>& SequenceFailure::GetTimedOutTests() const
{
return m_timedOutTests;
}
const AZStd::vector<TargetFailure>& SequenceFailure::GetUnexecutedTests() const
{
return m_unexecutedTests;
}
} // namespace Client
} // namespace TestImpact
@@ -0,0 +1,326 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
namespace TestImpact
{
namespace Client
{
TestSequenceResult CalculateMultiTestSequenceResult(const AZStd::vector<TestSequenceResult>& results)
{
// Order of precedence:
// 1. TestSequenceResult::Failure
// 2. TestSequenceResult::Timeout
// 3. TestSequenceResult::Success
if (const auto it = AZStd::find(results.begin(), results.end(), TestSequenceResult::Failure); it != results.end())
{
return TestSequenceResult::Failure;
}
if (const auto it = AZStd::find(results.begin(), results.end(), TestSequenceResult::Timeout); it != results.end())
{
return TestSequenceResult::Timeout;
}
return TestSequenceResult::Success;
}
TestRunReport::TestRunReport(
TestSequenceResult result,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
AZStd::vector<PassingTestRun>&& passingTestRuns,
AZStd::vector<FailingTestRun>&& failingTestRuns,
AZStd::vector<TestRunWithExecutionFailure>&& executionFailureTestRuns,
AZStd::vector<TimedOutTestRun>&& timedOutTestRuns,
AZStd::vector<UnexecutedTestRun>&& unexecutedTestRuns)
: m_startTime(startTime)
, m_result(result)
, m_duration(duration)
, m_passingTestRuns(AZStd::move(passingTestRuns))
, m_failingTestRuns(AZStd::move(failingTestRuns))
, m_executionFailureTestRuns(AZStd::move(executionFailureTestRuns))
, m_timedOutTestRuns(AZStd::move(timedOutTestRuns))
, m_unexecutedTestRuns(AZStd::move(unexecutedTestRuns))
{
for (const auto& failingTestRun : m_failingTestRuns)
{
m_totalNumPassingTests += failingTestRun.GetTotalNumPassingTests();
m_totalNumFailingTests += failingTestRun.GetTotalNumFailingTests();
m_totalNumDisabledTests += failingTestRun.GetTotalNumDisabledTests();
}
for (const auto& passingTestRun : m_passingTestRuns)
{
m_totalNumPassingTests += passingTestRun.GetTotalNumPassingTests();
m_totalNumDisabledTests += passingTestRun.GetTotalNumDisabledTests();
}
}
TestSequenceResult TestRunReport::GetResult() const
{
return m_result;
}
AZStd::chrono::high_resolution_clock::time_point TestRunReport::GetStartTime() const
{
return m_startTime;
}
AZStd::chrono::high_resolution_clock::time_point TestRunReport::GetEndTime() const
{
return m_startTime + m_duration;
}
AZStd::chrono::milliseconds TestRunReport::GetDuration() const
{
return m_duration;
}
size_t TestRunReport::GetTotalNumTestRuns() const
{
return
GetNumPassingTestRuns() +
GetNumFailingTestRuns() +
GetNumExecutionFailureTestRuns() +
GetNumTimedOutTestRuns() +
GetNumUnexecutedTestRuns();
}
size_t TestRunReport::GetNumPassingTestRuns() const
{
return m_passingTestRuns.size();
}
size_t TestRunReport::GetNumFailingTestRuns() const
{
return m_failingTestRuns.size();
}
size_t TestRunReport::GetNumExecutionFailureTestRuns() const
{
return m_executionFailureTestRuns.size();
}
size_t TestRunReport::TestRunReport::GetNumTimedOutTestRuns() const
{
return m_timedOutTestRuns.size();
}
size_t TestRunReport::GetNumUnexecutedTestRuns() const
{
return m_unexecutedTestRuns.size();
}
const AZStd::vector<PassingTestRun>& TestRunReport::GetPassingTestRuns() const
{
return m_passingTestRuns;
}
const AZStd::vector<FailingTestRun>& TestRunReport::GetFailingTestRuns() const
{
return m_failingTestRuns;
}
const AZStd::vector<TestRunWithExecutionFailure>& TestRunReport::GetExecutionFailureTestRuns() const
{
return m_executionFailureTestRuns;
}
const AZStd::vector<TimedOutTestRun>& TestRunReport::GetTimedOutTestRuns() const
{
return m_timedOutTestRuns;
}
const AZStd::vector<UnexecutedTestRun>& TestRunReport::GetUnexecutedTestRuns() const
{
return m_unexecutedTestRuns;
}
size_t TestRunReport::GetTotalNumPassingTests() const
{
return m_totalNumPassingTests;
}
size_t TestRunReport::GetTotalNumFailingTests() const
{
return m_totalNumFailingTests;
}
size_t TestRunReport::GetTotalNumDisabledTests() const
{
return m_totalNumDisabledTests;
}
RegularSequenceReport::RegularSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
TestRunReport&& selectedTestRunReport)
: SequenceReportBase(
SequenceReportType::RegularSequence,
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTestRuns,
AZStd::move(selectedTestRunReport))
{
}
SeedSequenceReport::SeedSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
TestRunReport&& selectedTestRunReport)
: SequenceReportBase(
SequenceReportType::SeedSequence,
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTestRuns,
AZStd::move(selectedTestRunReport))
{
}
ImpactAnalysisSequenceReport::ImpactAnalysisSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const ImpactAnalysisSequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
const AZStd::vector<AZStd::string>& discardedTestRuns,
const AZStd::vector<AZStd::string>& draftedTestRuns,
TestRunReport&& selectedTestRunReport,
TestRunReport&& draftedTestRunReport)
: DraftingSequenceReportBase(
SequenceReportType::ImpactAnalysisSequence,
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTestRuns,
draftedTestRuns,
AZStd::move(selectedTestRunReport),
AZStd::move(draftedTestRunReport))
, m_discardedTestRuns(discardedTestRuns)
{
}
const AZStd::vector<AZStd::string>& ImpactAnalysisSequenceReport::GetDiscardedTestRuns() const
{
return m_discardedTestRuns;
}
SafeImpactAnalysisSequenceReport::SafeImpactAnalysisSequenceReport(
size_t maxConcurrency,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
const SafeImpactAnalysisSequencePolicyState& policyState,
SuiteType suiteType,
const TestRunSelection& selectedTestRuns,
const TestRunSelection& discardedTestRuns,
const AZStd::vector<AZStd::string>& draftedTestRuns,
TestRunReport&& selectedTestRunReport,
TestRunReport&& discardedTestRunReport,
TestRunReport&& draftedTestRunReport)
: DraftingSequenceReportBase(
SequenceReportType::SafeImpactAnalysisSequence,
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTestRuns,
draftedTestRuns,
AZStd::move(selectedTestRunReport),
AZStd::move(draftedTestRunReport))
, m_discardedTestRuns(discardedTestRuns)
, m_discardedTestRunReport(AZStd::move(discardedTestRunReport))
{
}
TestSequenceResult SafeImpactAnalysisSequenceReport::GetResult() const
{
return CalculateMultiTestSequenceResult({ DraftingSequenceReportBase::GetResult(), m_discardedTestRunReport.GetResult() });
}
AZStd::chrono::milliseconds SafeImpactAnalysisSequenceReport::GetDuration() const
{
return DraftingSequenceReportBase::GetDuration() + m_discardedTestRunReport.GetDuration();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumTestRuns() + m_discardedTestRunReport.GetTotalNumTestRuns();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumPassingTests() const
{
return DraftingSequenceReportBase::GetTotalNumPassingTests() + m_discardedTestRunReport.GetTotalNumPassingTests();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumFailingTests() const
{
return DraftingSequenceReportBase::GetTotalNumFailingTests() + m_discardedTestRunReport.GetTotalNumFailingTests();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumDisabledTests() const
{
return DraftingSequenceReportBase::GetTotalNumDisabledTests() + m_discardedTestRunReport.GetTotalNumDisabledTests();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumPassingTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumPassingTestRuns() + m_discardedTestRunReport.GetNumPassingTestRuns();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumFailingTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumFailingTestRuns() + m_discardedTestRunReport.GetNumFailingTestRuns();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumExecutionFailureTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumExecutionFailureTestRuns() + m_discardedTestRunReport.GetNumExecutionFailureTestRuns();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumTimedOutTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumTimedOutTestRuns() + m_discardedTestRunReport.GetNumTimedOutTestRuns();
}
size_t SafeImpactAnalysisSequenceReport::GetTotalNumUnexecutedTestRuns() const
{
return DraftingSequenceReportBase::GetTotalNumUnexecutedTestRuns() + m_discardedTestRunReport.GetNumUnexecutedTestRuns();
}
const TestRunSelection SafeImpactAnalysisSequenceReport::GetDiscardedTestRuns() const
{
return m_discardedTestRuns;
}
TestRunReport SafeImpactAnalysisSequenceReport::GetDiscardedTestRunReport() const
{
return m_discardedTestRunReport;
}
} // namespace Client
} // namespace TestImpact
@@ -0,0 +1,606 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <TestImpactFramework/TestImpactClientSequenceReportSerializer.h>
#include <TestImpactFramework/TestImpactSequenceReportException.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <AzCore/JSON/document.h>
#include <AzCore/JSON/prettywriter.h>
#include <AzCore/JSON/rapidjson.h>
#include <AzCore/JSON/stringbuffer.h>
namespace TestImpact
{
namespace
{
namespace SequenceReportFields
{
// Keys for pertinent JSON node and attribute names
constexpr const char* Keys[] =
{
"name",
"command_args",
"start_time",
"end_time",
"duration",
"result",
"num_passing_tests",
"num_failing_tests",
"num_disabled_tests",
"tests",
"num_passing_test_runs",
"num_failing_test_runs",
"num_execution_failure_test_runs",
"num_timed_out_test_runs",
"num_unexecuted_test_runs",
"passing_test_runs",
"failing_test_runs",
"execution_failure_test_runs",
"timed_out_test_runs",
"unexecuted_test_runs",
"total_num_passing_tests",
"total_num_failing_tests",
"total_num_disabled_tests",
"total_num_test_runs",
"num_included_test_runs",
"num_excluded_test_runs",
"included_test_runs",
"excluded_test_runs",
"execution_failure",
"coverage_failure",
"test_failure",
"integrity_failure",
"test_sharding",
"target_output_capture",
"test_prioritization",
"dynamic_dependency_map",
"type",
"test_target_timeout",
"global_timeout",
"max_concurrency",
"policy",
"suite",
"selected_test_runs",
"selected_test_run_report",
"total_num_passing_test_runs",
"total_num_failing_test_runs",
"total_num_execution_failure_test_runs",
"total_num_timed_out_test_runs",
"total_num_unexecuted_test_runs",
"drafted_test_runs",
"drafted_test_run_report",
"discarded_test_runs",
"discarded_test_run_report"
};
enum
{
Name,
CommandArgs,
StartTime,
EndTime,
Duration,
Result,
NumPassingTests,
NumFailingTests,
NumDisabledTests,
Tests,
NumPassingTestRuns,
NumFailingTestRuns,
NumExecutionFailureTestRuns,
NumTimedOutTestRuns,
NumUnexecutedTestRuns,
PassingTestRuns,
FailingTestRuns,
ExecutionFailureTestRuns,
TimedOutTestRuns,
UnexecutedTestRuns,
TotalNumPassingTests,
TotalNumFailingTests,
TotalNumDisabledTests,
TotalNumTestRuns,
NumIncludedTestRuns,
NumExcludedTestRuns,
IncludedTestRuns,
ExcludedTestRuns,
ExecutionFailure,
CoverageFailure,
TestFailure,
IntegrityFailure,
TestSharding,
TargetOutputCapture,
TestPrioritization,
DynamicDependencyMap,
Type,
TestTargetTimeout,
GlobalTimeout,
MaxConcurrency,
Policy,
Suite,
SelectedTestRuns,
SelectedTestRunReport,
TotalNumPassingTestRuns,
TotalNumFailingTestRuns,
TotalNumExecutionFailureTestRuns,
TotalNumTimedOutTestRuns,
TotalNumUnexecutedTestRuns,
DraftedTestRuns,
DraftedTestRunReport,
DiscardedTestRuns,
DiscardedTestRunReport
};
} // namespace SequenceReportFields
AZ::u64 TimePointInMsAsInt64(AZStd::chrono::high_resolution_clock::time_point timePoint)
{
return AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(timePoint.time_since_epoch()).count();
}
void SerializeTestRunMembers(const Client::TestRunBase& testRun, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
// Name
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Name]);
writer.String(testRun.GetTargetName().c_str());
// Command string
writer.Key(SequenceReportFields::Keys[SequenceReportFields::CommandArgs]);
writer.String(testRun.GetCommandString().c_str());
// Start time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::StartTime]);
writer.Int64(TimePointInMsAsInt64(testRun.GetStartTime()));
// End time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::EndTime]);
writer.Int64(TimePointInMsAsInt64(testRun.GetEndTime()));
// Duration
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Duration]);
writer.Uint64(testRun.GetDuration().count());
// Result
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Result]);
writer.String(TestRunResultAsString(testRun.GetResult()).c_str());
}
void SerializeTestRun(const Client::TestRunBase& testRun, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
writer.StartObject();
{
SerializeTestRunMembers(testRun, writer);
}
writer.EndObject();
}
void SerializeCompletedTestRun(const Client::CompletedTestRun& testRun, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
writer.StartObject();
{
SerializeTestRunMembers(testRun, writer);
// Number of passing test cases
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumPassingTests]);
writer.Uint64(testRun.GetTotalNumPassingTests());
// Number of failing test cases
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumFailingTests]);
writer.Uint64(testRun.GetTotalNumFailingTests());
// Number of disabled test cases
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumDisabledTests]);
writer.Uint64(testRun.GetTotalNumDisabledTests());
// Tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Tests]);
writer.StartArray();
for (const auto& test : testRun.GetTests())
{
// Test
writer.StartObject();
// Name
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Name]);
writer.String(test.GetName().c_str());
// Result
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Result]);
writer.String(ClientTestResultAsString(test.GetResult()).c_str());
writer.EndObject(); // Test
}
writer.EndArray(); // Tests
}
writer.EndObject();
}
void SerializeTestRunReport(
const Client::TestRunReport& testRunReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
writer.StartObject();
{
// Result
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Result]);
writer.String(TestSequenceResultAsString(testRunReport.GetResult()).c_str());
// Start time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::StartTime]);
writer.Int64(TimePointInMsAsInt64(testRunReport.GetStartTime()));
// End time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::EndTime]);
writer.Int64(TimePointInMsAsInt64(testRunReport.GetEndTime()));
// Duration
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Duration]);
writer.Uint64(testRunReport.GetDuration().count());
// Number of passing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumPassingTestRuns]);
writer.Uint64(testRunReport.GetNumPassingTestRuns());
// Number of failing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumFailingTestRuns]);
writer.Uint64(testRunReport.GetNumFailingTestRuns());
// Number of test runs that failed to execute
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumExecutionFailureTestRuns]);
writer.Uint64(testRunReport.GetNumExecutionFailureTestRuns());
// Number of timed out test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumTimedOutTestRuns]);
writer.Uint64(testRunReport.GetNumTimedOutTestRuns());
// Number of unexecuted test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumUnexecutedTestRuns]);
writer.Uint64(testRunReport.GetNumUnexecutedTestRuns());
// Passing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::PassingTestRuns]);
writer.StartArray();
for (const auto& testRun : testRunReport.GetPassingTestRuns())
{
SerializeCompletedTestRun(testRun, writer);
}
writer.EndArray(); // Passing test runs
// Failing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::FailingTestRuns]);
writer.StartArray();
for (const auto& testRun : testRunReport.GetFailingTestRuns())
{
SerializeCompletedTestRun(testRun, writer);
}
writer.EndArray(); // Failing test runs
// Execution failures
writer.Key(SequenceReportFields::Keys[SequenceReportFields::ExecutionFailureTestRuns]);
writer.StartArray();
for (const auto& testRun : testRunReport.GetExecutionFailureTestRuns())
{
SerializeTestRun(testRun, writer);
}
writer.EndArray(); // Execution failures
// Timed out test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TimedOutTestRuns]);
writer.StartArray();
for (const auto& testRun : testRunReport.GetTimedOutTestRuns())
{
SerializeTestRun(testRun, writer);
}
writer.EndArray(); // Timed out test runs
// Unexecuted test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::UnexecutedTestRuns]);
writer.StartArray();
for (const auto& testRun : testRunReport.GetUnexecutedTestRuns())
{
SerializeTestRun(testRun, writer);
}
writer.EndArray(); // Unexecuted test runs
// Number of passing tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumPassingTests]);
writer.Uint64(testRunReport.GetTotalNumPassingTests());
// Number of failing tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumFailingTests]);
writer.Uint64(testRunReport.GetTotalNumFailingTests());
// Number of disabled tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumDisabledTests]);
writer.Uint64(testRunReport.GetTotalNumDisabledTests());
}
writer.EndObject();
}
void SerializeTestSelection(
const Client::TestRunSelection& testSelection, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
writer.StartObject();
{
// Total number of test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumTestRuns]);
writer.Uint64(testSelection.GetTotalNumTests());
// Number of included test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumIncludedTestRuns]);
writer.Uint64(testSelection.GetNumIncludedTestRuns());
// Number of excluded test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::NumExcludedTestRuns]);
writer.Uint64(testSelection.GetNumExcludedTestRuns());
// Included test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::IncludedTestRuns]);
writer.StartArray();
for (const auto& testRun : testSelection.GetIncludededTestRuns())
{
writer.String(testRun.c_str());
}
writer.EndArray(); // Included test runs
// Excluded test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::ExcludedTestRuns]);
writer.StartArray();
for (const auto& testRun : testSelection.GetExcludedTestRuns())
{
writer.String(testRun.c_str());
}
writer.EndArray(); // Excluded test runs
}
writer.EndObject();
}
void SerializePolicyStateBaseMembers(const PolicyStateBase& policyState, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
// Execution failure
writer.Key(SequenceReportFields::Keys[SequenceReportFields::ExecutionFailure]);
writer.String(ExecutionFailurePolicyAsString(policyState.m_executionFailurePolicy).c_str());
// Failed test coverage
writer.Key(SequenceReportFields::Keys[SequenceReportFields::CoverageFailure]);
writer.String(FailedTestCoveragePolicyAsString(policyState.m_failedTestCoveragePolicy).c_str());
// Test failure
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestFailure]);
writer.String(TestFailurePolicyAsString(policyState.m_testFailurePolicy).c_str());
// Integrity failure
writer.Key(SequenceReportFields::Keys[SequenceReportFields::IntegrityFailure]);
writer.String(IntegrityFailurePolicyAsString(policyState.m_integrityFailurePolicy).c_str());
// Test sharding
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestSharding]);
writer.String(TestShardingPolicyAsString(policyState.m_testShardingPolicy).c_str());
// Target output capture
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TargetOutputCapture]);
writer.String(TargetOutputCapturePolicyAsString(policyState.m_targetOutputCapture).c_str());
}
void SerializePolicyStateMembers(
const SequencePolicyState& policyState, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
SerializePolicyStateBaseMembers(policyState.m_basePolicies, writer);
}
void SerializePolicyStateMembers(
const SafeImpactAnalysisSequencePolicyState& policyState, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
SerializePolicyStateBaseMembers(policyState.m_basePolicies, writer);
// Test prioritization
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]);
writer.String(TestPrioritizationPolicyAsString(policyState.m_testPrioritizationPolicy).c_str());
}
void SerializePolicyStateMembers(
const ImpactAnalysisSequencePolicyState& policyState, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
SerializePolicyStateBaseMembers(policyState.m_basePolicies, writer);
// Test prioritization
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestPrioritization]);
writer.String(TestPrioritizationPolicyAsString(policyState.m_testPrioritizationPolicy).c_str());
// Dynamic dependency map
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DynamicDependencyMap]);
writer.String(DynamicDependencyMapPolicyAsString(policyState.m_dynamicDependencyMap).c_str());
}
template<typename PolicyStateType>
void SerializeSequenceReportBaseMembers(
const Client::SequenceReportBase<PolicyStateType>& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
// Type
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Type]);
writer.String(SequenceReportTypeAsString(sequenceReport.GetType()).c_str());
// Test target timeout
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TestTargetTimeout]);
writer.Uint64(sequenceReport.GetTestTargetTimeout().value_or(AZStd::chrono::milliseconds{0}).count());
// Global timeout
writer.Key(SequenceReportFields::Keys[SequenceReportFields::GlobalTimeout]);
writer.Uint64(sequenceReport.GetGlobalTimeout().value_or(AZStd::chrono::milliseconds{ 0 }).count());
// Maximum concurrency
writer.Key(SequenceReportFields::Keys[SequenceReportFields::MaxConcurrency]);
writer.Uint64(sequenceReport.GetMaxConcurrency());
// Policies
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Policy]);
writer.StartObject();
{
SerializePolicyStateMembers(sequenceReport.GetPolicyState(), writer);
}
writer.EndObject(); // Policies
// Suite
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Suite]);
writer.String(SuiteTypeAsString(sequenceReport.GetSuite()).c_str());
// Selected test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::SelectedTestRuns]);
SerializeTestSelection(sequenceReport.GetSelectedTestRuns(), writer);
// Selected test run report
writer.Key(SequenceReportFields::Keys[SequenceReportFields::SelectedTestRunReport]);
SerializeTestRunReport(sequenceReport.GetSelectedTestRunReport(), writer);
// Start time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::StartTime]);
writer.Int64(TimePointInMsAsInt64(sequenceReport.GetStartTime()));
// End time
writer.Key(SequenceReportFields::Keys[SequenceReportFields::EndTime]);
writer.Int64(TimePointInMsAsInt64(sequenceReport.GetEndTime()));
// Duration
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Duration]);
writer.Uint64(sequenceReport.GetDuration().count());
// Result
writer.Key(SequenceReportFields::Keys[SequenceReportFields::Result]);
writer.String(TestSequenceResultAsString(sequenceReport.GetResult()).c_str());
// Total number of test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumTestRuns());
// Total number of passing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumPassingTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumPassingTestRuns());
// Total number of failing test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumFailingTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumFailingTestRuns());
// Total number of test runs that failed to execute
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumExecutionFailureTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumExecutionFailureTestRuns());
// Total number of timed out test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumTimedOutTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumTimedOutTestRuns());
// Total number of unexecuted test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumUnexecutedTestRuns]);
writer.Uint64(sequenceReport.GetTotalNumUnexecutedTestRuns());
// Total number of passing tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumPassingTests]);
writer.Uint64(sequenceReport.GetTotalNumPassingTests());
// Total number of failing tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumFailingTests]);
writer.Uint64(sequenceReport.GetTotalNumFailingTests());
// Total number of disabled tests
writer.Key(SequenceReportFields::Keys[SequenceReportFields::TotalNumDisabledTests]);
writer.Uint64(sequenceReport.GetTotalNumDisabledTests());
}
template<typename PolicyStateType>
void SerializeDraftingSequenceReportMembers(
const Client::DraftingSequenceReportBase<PolicyStateType>& sequenceReport, rapidjson::PrettyWriter<rapidjson::StringBuffer>& writer)
{
SerializeSequenceReportBaseMembers(sequenceReport, writer);
// Drafted test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DraftedTestRuns]);
writer.StartArray();
for (const auto& testRun : sequenceReport.GetDraftedTestRuns())
{
writer.String(testRun.c_str());
}
writer.EndArray(); // Drafted test runs
// Drafted test run report
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DraftedTestRunReport]);
SerializeTestRunReport(sequenceReport.GetDraftedTestRunReport(), writer);
}
} // namespace
AZStd::string SerializeSequenceReport(const Client::RegularSequenceReport& sequenceReport)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
writer.StartObject();
{
SerializeSequenceReportBaseMembers(sequenceReport, writer);
}
writer.EndObject();
return stringBuffer.GetString();
}
AZStd::string SerializeSequenceReport(const Client::SeedSequenceReport& sequenceReport)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
writer.StartObject();
{
SerializeSequenceReportBaseMembers(sequenceReport, writer);
}
writer.EndObject();
return stringBuffer.GetString();
}
AZStd::string SerializeSequenceReport(const Client::ImpactAnalysisSequenceReport& sequenceReport)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
writer.StartObject();
{
SerializeDraftingSequenceReportMembers(sequenceReport, writer);
// Discarded test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]);
writer.StartArray();
for (const auto& testRun : sequenceReport.GetDiscardedTestRuns())
{
writer.String(testRun.c_str());
}
writer.EndArray(); // Discarded test runs
}
writer.EndObject();
return stringBuffer.GetString();
}
AZStd::string SerializeSequenceReport(const Client::SafeImpactAnalysisSequenceReport& sequenceReport)
{
rapidjson::StringBuffer stringBuffer;
rapidjson::PrettyWriter<rapidjson::StringBuffer> writer(stringBuffer);
writer.StartObject();
{
SerializeDraftingSequenceReportMembers(sequenceReport, writer);
// Discarded test runs
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRuns]);
SerializeTestSelection(sequenceReport.GetDiscardedTestRuns(), writer);
// Discarded test run report
writer.Key(SequenceReportFields::Keys[SequenceReportFields::DiscardedTestRunReport]);
SerializeTestRunReport(sequenceReport.GetDiscardedTestRunReport(), writer);
}
writer.EndObject();
return stringBuffer.GetString();
}
} // namespace TestImpact
@@ -7,30 +7,166 @@
*/
#include <TestImpactFramework/TestImpactClientTestRun.h>
#include <AzCore/std/tuple.h>
namespace TestImpact
{
namespace Client
{
TestRun::TestRun(const AZStd::string& name, TestRunResult result, AZStd::chrono::milliseconds duration)
TestRunBase::TestRunBase(
const AZStd::string& name,
const AZStd::string& commandString,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
TestRunResult result)
: m_targetName(name)
, m_result(result)
, m_commandString(commandString)
, m_startTime(startTime)
, m_duration(duration)
, m_result(result)
{
}
const AZStd::string& TestRun::GetTargetName() const
const AZStd::string& TestRunBase::GetTargetName() const
{
return m_targetName;
}
AZStd::chrono::milliseconds TestRun::GetDuration() const
const AZStd::string& TestRunBase::GetCommandString() const
{
return m_commandString;
}
AZStd::chrono::high_resolution_clock::time_point TestRunBase::GetStartTime() const
{
return m_startTime;
}
AZStd::chrono::high_resolution_clock::time_point TestRunBase::GetEndTime() const
{
return m_startTime + m_duration;
}
AZStd::chrono::milliseconds TestRunBase::GetDuration() const
{
return m_duration;
}
TestRunResult TestRun::GetResult() const
TestRunResult TestRunBase::GetResult() const
{
return m_result;
}
TestRunWithExecutionFailure::TestRunWithExecutionFailure(TestRunBase&& testRun)
: TestRunBase(AZStd::move(testRun))
{
}
TimedOutTestRun::TimedOutTestRun(TestRunBase&& testRun)
: TestRunBase(AZStd::move(testRun))
{
}
UnexecutedTestRun::UnexecutedTestRun(TestRunBase&& testRun)
: TestRunBase(AZStd::move(testRun))
{
}
Test::Test(const AZStd::string& testName, TestResult result)
: m_name(testName)
, m_result(result)
{
}
const AZStd::string& Test::GetName() const
{
return m_name;
}
TestResult Test::GetResult() const
{
return m_result;
}
AZStd::tuple<size_t, size_t, size_t> CalculateTestCaseMetrics(const AZStd::vector<Test>& tests)
{
size_t totalNumPassingTests = 0;
size_t totalNumFailingTests = 0;
size_t totalNumDisabledTests = 0;
for (const auto& test : tests)
{
if (test.GetResult() == Client::TestResult::Passed)
{
totalNumPassingTests++;
}
else if (test.GetResult() == Client::TestResult::Failed)
{
totalNumFailingTests++;
}
else
{
totalNumDisabledTests++;
}
}
return { totalNumPassingTests, totalNumFailingTests, totalNumDisabledTests };
}
CompletedTestRun::CompletedTestRun(
const AZStd::string& name,
const AZStd::string& commandString,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
TestRunResult result,
AZStd::vector<Test>&& tests)
: TestRunBase(name, commandString, startTime, duration, result)
, m_tests(AZStd::move(tests))
{
AZStd::tie(m_totalNumPassingTests, m_totalNumFailingTests, m_totalNumDisabledTests) = CalculateTestCaseMetrics(m_tests);
}
CompletedTestRun::CompletedTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests)
: TestRunBase(AZStd::move(testRun))
, m_tests(AZStd::move(tests))
{
AZStd::tie(m_totalNumPassingTests, m_totalNumFailingTests, m_totalNumDisabledTests) = CalculateTestCaseMetrics(m_tests);
}
size_t CompletedTestRun::GetTotalNumTests() const
{
return m_tests.size();
}
size_t CompletedTestRun::GetTotalNumPassingTests() const
{
return m_totalNumPassingTests;
}
size_t CompletedTestRun::GetTotalNumFailingTests() const
{
return m_totalNumFailingTests;
}
size_t CompletedTestRun::GetTotalNumDisabledTests() const
{
return m_totalNumDisabledTests;
}
const AZStd::vector<Test>& CompletedTestRun::GetTests() const
{
return m_tests;
}
PassingTestRun::PassingTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests)
: CompletedTestRun(AZStd::move(testRun), AZStd::move(tests))
{
}
FailingTestRun::FailingTestRun(TestRunBase&& testRun, AZStd::vector<Test>&& tests)
: CompletedTestRun(AZStd::move(testRun), AZStd::move(tests))
{
}
} // namespace Client
} // namespace TestImpact
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactFramework/TestImpactRuntime.h>
#include <TestImpactFramework/TestImpactRuntimeException.h>
@@ -34,13 +34,33 @@ namespace TestImpact
{
}
//! Returns the time elapsed (in milliseconds) since the timer was instantiated
AZStd::chrono::milliseconds Elapsed()
//! Returns the time point that the timer was instantiated.
AZStd::chrono::high_resolution_clock::time_point GetStartTimePoint() const
{
return m_startTime;
}
//! Returns the time point that the timer was instantiated relative to the specified starting time point.
AZStd::chrono::high_resolution_clock::time_point GetStartTimePointRelative(const Timer& start) const
{
return AZStd::chrono::high_resolution_clock::time_point() +
AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(m_startTime - start.GetStartTimePoint());
}
//! Returns the time elapsed (in milliseconds) since the timer was instantiated.
AZStd::chrono::milliseconds GetElapsedMs() const
{
const auto endTime = AZStd::chrono::high_resolution_clock::now();
return AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(endTime - m_startTime);
}
//! Returns the current time point relative to the time point the timer was instantiated.
AZStd::chrono::high_resolution_clock::time_point GetElapsedTimepoint() const
{
const auto endTime = AZStd::chrono::high_resolution_clock::now();
return m_startTime + AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(endTime - m_startTime);
}
private:
AZStd::chrono::high_resolution_clock::time_point m_startTime;
};
@@ -49,8 +69,11 @@ namespace TestImpact
class TestRunCompleteCallbackHandler
{
public:
TestRunCompleteCallbackHandler(AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
: m_testCompleteCallback(testCompleteCallback)
TestRunCompleteCallbackHandler(
size_t totalTests,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
: m_totalTests(totalTests)
, m_testCompleteCallback(testCompleteCallback)
{
}
@@ -58,19 +81,27 @@ namespace TestImpact
{
if (m_testCompleteCallback.has_value())
{
(*m_testCompleteCallback)
(Client::TestRun(testJob.GetTestTarget()->GetName(), testJob.GetTestResult(), testJob.GetDuration()));
Client::TestRunBase testRun(
testJob.GetTestTarget()->GetName(),
testJob.GetCommandString(),
testJob.GetStartTime(),
testJob.GetDuration(),
testJob.GetTestResult());
(*m_testCompleteCallback)(testRun, ++m_numTestsCompleted, m_totalTests);
}
}
private:
const size_t m_totalTests; //!< The total number of tests to run for the entire sequence.
size_t m_numTestsCompleted = 0; //!< The running total of tests that have completed.
AZStd::optional<TestRunCompleteCallback> m_testCompleteCallback;
};
}
//! Utility for concatenating two vectors.
template<typename T>
AZStd::vector<T> ConcatenateVectors(const AZStd::vector<T>& v1, const AZStd::vector<T>& v2)
static AZStd::vector<T> ConcatenateVectors(const AZStd::vector<T>& v1, const AZStd::vector<T>& v2)
{
AZStd::vector<T> result;
result.reserve(v1.size() + v2.size());
@@ -79,8 +110,140 @@ namespace TestImpact
return result;
}
//! Utility structure for holding the pertinent data for test run reports.
template<typename TestJob>
struct TestRunData
{
TestSequenceResult m_result = TestSequenceResult::Success;
AZStd::vector<TestJob> m_jobs;
AZStd::chrono::high_resolution_clock::time_point m_relativeStartTime;
AZStd::chrono::milliseconds m_duration = AZStd::chrono::milliseconds{ 0 };
};
//! Wrapper for the impact analysis test sequence to handle both the updating and non-updating policies through a common pathway.
//! @tparam TestRunnerFunctor The functor for running the specified tests.
//! @tparam TestJob The test engine job type returned by the functor.
//! @param maxConcurrency The maximum concurrency being used for this sequence.
//! @param policyState The policy state being used for the sequence.
//! @param suiteType The suite type used for this sequence.
//! @param timer The timer to use for the test run timings.
//! @param testRunner The test runner functor to use for each of the test runs.
//! @param includedSelectedTestTargets The subset of test targets that were selected to run and not also fully excluded from running.
//! @param excludedSelectedTestTargets The subset of test targets that were selected to run but were fully excluded running.
//! @param discardedTestTargets The subset of test targets that were discarded from the test selection and will not be run.
//! @param globalTimeout The maximum duration the entire test sequence may run for (infinite if empty).
//! @param testSequenceStartCallback The client function to be called after the test targets have been selected but prior to running the
//! tests.
//! @param testSequenceCompleteCallback The client function to be called after the test sequence has completed.
//! @param testRunCompleteCallback The client function to be called after an individual test run has completed.
//! @param updateCoverage The function to call to update the dynamic dependency map with test coverage (if any).
template<typename TestRunnerFunctor, typename TestJob>
Client::ImpactAnalysisSequenceReport ImpactAnalysisTestSequenceWrapper(
size_t maxConcurrency,
const ImpactAnalysisSequencePolicyState& policyState,
SuiteType suiteType,
const Timer& sequenceTimer,
const TestRunnerFunctor& testRunner,
const AZStd::vector<const TestTarget*>& includedSelectedTestTargets,
const AZStd::vector<const TestTarget*>& excludedSelectedTestTargets,
const AZStd::vector<const TestTarget*>& discardedTestTargets,
const AZStd::vector<const TestTarget*>& draftedTestTargets,
const AZStd::optional<AZStd::chrono::milliseconds>& testTargetTimeout,
const AZStd::optional<AZStd::chrono::milliseconds>& globalTimeout,
AZStd::optional<ImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::ImpactAnalysisSequenceReport>> testSequenceEndCallback,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback,
AZStd::optional<AZStd::function<void(const AZStd::vector<TestJob>& jobs)>> updateCoverage)
{
TestRunData<TestJob> selectedTestRunData, draftedTestRunData;
AZStd::optional<AZStd::chrono::milliseconds> sequenceTimeout = globalTimeout;
// Extract the client facing representation of selected, discarded and drafted test targets
const Client::TestRunSelection selectedTests(
ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets));
const auto discardedTests = ExtractTestTargetNames(discardedTestTargets);
const auto draftedTests = ExtractTestTargetNames(draftedTestTargets);
// Inform the client that the sequence is about to start
if (testSequenceStartCallback.has_value())
{
(*testSequenceStartCallback)(suiteType, selectedTests, discardedTests, draftedTests);
}
// We share the test run complete handler between the selected and drafted test runs as to present them together as one
// continuous test sequence to the client rather than two discrete test runs
const size_t totalNumTestRuns = includedSelectedTestTargets.size() + draftedTestTargets.size();
TestRunCompleteCallbackHandler testRunCompleteHandler(totalNumTestRuns, testCompleteCallback);
const auto gatherTestRunData = [&sequenceTimer, &testRunner, &testRunCompleteHandler, &globalTimeout]
(const AZStd::vector<const TestTarget*>& testsTargets, TestRunData<TestJob>& testRunData)
{
const Timer testRunTimer;
testRunData.m_relativeStartTime = testRunTimer.GetStartTimePointRelative(sequenceTimer);
auto [result, jobs] = testRunner(testsTargets, testRunCompleteHandler, globalTimeout);
testRunData.m_result = result;
testRunData.m_jobs = AZStd::move(jobs);
testRunData.m_duration = testRunTimer.GetElapsedMs();
};
if (!includedSelectedTestTargets.empty())
{
// Run the selected test targets and collect the test run results
gatherTestRunData(includedSelectedTestTargets, selectedTestRunData);
// Carry the remaining global sequence time over to the drafted test run
if (globalTimeout.has_value())
{
const auto elapsed = selectedTestRunData.m_duration;
sequenceTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0);
}
}
if (!draftedTestTargets.empty())
{
// Run the drafted test targets and collect the test run results
gatherTestRunData(draftedTestTargets, draftedTestRunData);
}
// Generate the sequence report for the client
const auto sequenceReport = Client::ImpactAnalysisSequenceReport(
maxConcurrency,
testTargetTimeout,
globalTimeout,
policyState,
suiteType,
selectedTests,
discardedTests,
draftedTests,
GenerateTestRunReport(
selectedTestRunData.m_result,
selectedTestRunData.m_relativeStartTime,
selectedTestRunData.m_duration,
selectedTestRunData.m_jobs),
GenerateTestRunReport(
draftedTestRunData.m_result,
draftedTestRunData.m_relativeStartTime,
draftedTestRunData.m_duration,
draftedTestRunData.m_jobs));
// Inform the client that the sequence has ended
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(sequenceReport);
}
// Update the dynamic dependency map with the latest coverage data (if any)
if (updateCoverage.has_value())
{
(*updateCoverage)(ConcatenateVectors(selectedTestRunData.m_jobs, draftedTestRunData.m_jobs));
}
return sequenceReport;
}
Runtime::Runtime(
RuntimeConfig&& config,
AZStd::optional<RepoPath> dataFile,
SuiteType suiteFilter,
Policy::ExecutionFailure executionFailurePolicy,
Policy::FailedTestCoverage failedTestCoveragePolicy,
@@ -120,27 +283,22 @@ namespace TestImpact
try
{
if (dataFile.has_value())
{
m_sparTiaFile = dataFile.value().String();
}
else
{
m_sparTiaFile = m_config.m_workspace.m_active.m_sparTiaFiles[static_cast<size_t>(m_suiteFilter)].String();
}
// Populate the dynamic dependency map with the existing source coverage data (if any)
m_sparTIAFile = m_config.m_workspace.m_active.m_sparTIAFiles[static_cast<size_t>(m_suiteFilter)].String();
const auto tiaDataRaw = ReadFileContents<Exception>(m_sparTIAFile);
const auto tiaDataRaw = ReadFileContents<Exception>(m_sparTiaFile);
const auto tiaData = DeserializeSourceCoveringTestsList(tiaDataRaw);
if (tiaData.GetNumSources())
{
m_dynamicDependencyMap->ReplaceSourceCoverage(tiaData);
m_hasImpactAnalysisData = true;
// Enumerate new test targets
const auto testTargetsWithNoEnumeration = m_dynamicDependencyMap->GetNotCoveringTests();
if (!testTargetsWithNoEnumeration.empty())
{
m_testEngine->UpdateEnumerationCache(
testTargetsWithNoEnumeration,
Policy::ExecutionFailure::Ignore,
Policy::TestFailure::Continue,
AZStd::nullopt,
AZStd::nullopt,
AZStd::nullopt);
}
}
}
catch (const DependencyException& e)
@@ -155,7 +313,7 @@ namespace TestImpact
AZ_Printf(
LogCallSite,
AZStd::string::format(
"No test impact analysis data found for suite '%s' at %s\n", GetSuiteTypeName(m_suiteFilter).c_str(), m_sparTIAFile.c_str()).c_str());
"No test impact analysis data found for suite '%s' at %s\n", SuiteTypeAsString(m_suiteFilter).c_str(), m_sparTiaFile.c_str()).c_str());
}
}
@@ -199,7 +357,7 @@ namespace TestImpact
}
}
AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> Runtime::SelectCoveringTestTargetsAndUpdateEnumerationCache(
AZStd::pair<AZStd::vector<const TestTarget*>, AZStd::vector<const TestTarget*>> Runtime::SelectCoveringTestTargets(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy)
{
@@ -212,9 +370,6 @@ namespace TestImpact
// Populate a set with the selected test targets so that we can infer the discarded test target not selected for this change list
const AZStd::unordered_set<const TestTarget*> selectedTestTargetSet(selectedTestTargets.begin(), selectedTestTargets.end());
// Update the enumeration caches of mutated targets regardless of the current sharding policy
EnumerateMutatedTestTargets(changeDependencyList);
// The test targets in the main list not in the selected test target set are the test targets not selected for this change list
for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
{
@@ -256,7 +411,7 @@ namespace TestImpact
void Runtime::ClearDynamicDependencyMapAndRemoveExistingFile()
{
m_dynamicDependencyMap->ClearAllSourceCoverage();
DeleteFile(m_sparTIAFile);
DeleteFile(m_sparTiaFile);
}
SourceCoveringTestsList Runtime::CreateSourceCoveringTestFromTestCoverages(const AZStd::vector<TestEngineInstrumentedRun>& jobs)
@@ -298,6 +453,7 @@ namespace TestImpact
continue;
}
// Add the sources covered by this test target to the coverage map
for (const auto& source : job.GetTestCoverge().value().GetSourcesCovered())
{
coverage[source.String()].insert(job.GetTestTarget()->GetName());
@@ -309,6 +465,7 @@ namespace TestImpact
sourceCoveringTests.reserve(coverage.size());
for (auto&& [source, testTargets] : coverage)
{
// Check to see whether this source is inside the repo or not (not a perfect check but weeds out the obvious non-repo sources)
if (const auto sourcePath = RepoPath(source);
sourcePath.IsRelativeTo(m_config.m_repo.m_root))
{
@@ -335,9 +492,9 @@ namespace TestImpact
}
m_dynamicDependencyMap->ReplaceSourceCoverage(sourceCoverageTestsList);
const auto sparTIA = m_dynamicDependencyMap->ExportSourceCoverage();
const auto sparTIAData = SerializeSourceCoveringTestsList(sparTIA);
WriteFileContents<RuntimeException>(sparTIAData, m_sparTIAFile);
const auto sparTia = m_dynamicDependencyMap->ExportSourceCoverage();
const auto sparTiaData = SerializeSourceCoveringTestsList(sparTia);
WriteFileContents<RuntimeException>(sparTiaData, m_sparTiaFile);
m_hasImpactAnalysisData = true;
}
catch(const RuntimeException& e)
@@ -353,17 +510,48 @@ namespace TestImpact
}
}
TestSequenceResult Runtime::RegularTestSequence(
PolicyStateBase Runtime::GeneratePolicyStateBase() const
{
PolicyStateBase policyState;
policyState.m_executionFailurePolicy = m_executionFailurePolicy;
policyState.m_failedTestCoveragePolicy = m_failedTestCoveragePolicy;
policyState.m_integrityFailurePolicy = m_integrationFailurePolicy;
policyState.m_targetOutputCapture = m_targetOutputCapture;
policyState.m_testFailurePolicy = m_testFailurePolicy;
policyState.m_testShardingPolicy = m_testShardingPolicy;
return policyState;
}
SequencePolicyState Runtime::GenerateSequencePolicyState() const
{
return { GeneratePolicyStateBase() };
}
SafeImpactAnalysisSequencePolicyState Runtime::GenerateSafeImpactAnalysisSequencePolicyState(
Policy::TestPrioritization testPrioritizationPolicy) const
{
return { GeneratePolicyStateBase(), testPrioritizationPolicy };
}
ImpactAnalysisSequencePolicyState Runtime::GenerateImpactAnalysisSequencePolicyState(
Policy::TestPrioritization testPrioritizationPolicy, Policy::DynamicDependencyMap dynamicDependencyMapPolicy) const
{
return { GeneratePolicyStateBase(), testPrioritizationPolicy, dynamicDependencyMapPolicy };
}
Client::RegularSequenceReport Runtime::RegularTestSequence(
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::RegularSequenceReport>> testSequenceEndCallback,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
{
Timer timer;
const Timer sequenceTimer;
AZStd::vector<const TestTarget*> includedTestTargets;
AZStd::vector<const TestTarget*> excludedTestTargets;
// Separate the test targets into those that are excluded by either the test filter or exclusion list and those that are not
for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
{
@@ -378,12 +566,17 @@ namespace TestImpact
}
}
// Sequence start callback
// Extract the client facing representation of selected test targets
const Client::TestRunSelection selectedTests(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets));
// Inform the client that the sequence is about to start
if (testSequenceStartCallback.has_value())
{
(*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets)));
(*testSequenceStartCallback)(m_suiteFilter, selectedTests);
}
// Run the test targets and collect the test run results
const Timer testRunTimer;
const auto [result, testJobs] = m_testEngine->RegularRun(
includedTestTargets,
m_testShardingPolicy,
@@ -392,108 +585,171 @@ namespace TestImpact
m_targetOutputCapture,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
TestRunCompleteCallbackHandler(includedTestTargets.size(), testCompleteCallback));
const auto testRunDuration = testRunTimer.GetElapsedMs();
// Generate the sequence report for the client
const auto sequenceReport = Client::RegularSequenceReport(
m_maxConcurrency,
testTargetTimeout,
globalTimeout,
GenerateSequencePolicyState(),
m_suiteFilter,
selectedTests,
GenerateTestRunReport(result, testRunTimer.GetStartTimePointRelative(sequenceTimer), testRunDuration, testJobs));
// Inform the client that the sequence has ended
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
(*testSequenceEndCallback)(sequenceReport);
}
return result;
return sequenceReport;
}
TestSequenceResult Runtime::ImpactAnalysisTestSequence(
Client::ImpactAnalysisSequenceReport Runtime::ImpactAnalysisTestSequence(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy,
Policy::DynamicDependencyMap dynamicDependencyMapPolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<ImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::ImpactAnalysisSequenceReport>> testSequenceEndCallback,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
{
Timer timer;
const Timer sequenceTimer;
// Draft in the test targets that have no coverage entries in the dynamic dependency map
AZStd::vector<const TestTarget*> draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests();
const AZStd::vector<const TestTarget*> draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests();
// The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not
auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy);
const auto selectCoveringTestTargetsAndPruneDraftedFromDiscarded =
[this, &draftedTestTargets, &changeList, testPrioritizationPolicy]()
{
// The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not
const auto [selectedTestTargets, discardedTestTargets] =
SelectCoveringTestTargets(changeList, testPrioritizationPolicy);
const AZStd::unordered_set<const TestTarget*> draftedTestTargetsSet(draftedTestTargets.begin(), draftedTestTargets.end());
AZStd::vector<const TestTarget*> discardedNotDraftedTestTargets;
for (const auto* testTarget : discardedTestTargets)
{
if (!draftedTestTargetsSet.count(testTarget))
{
discardedNotDraftedTestTargets.push_back(testTarget);
}
}
return AZStd::pair{ selectedTestTargets, discardedNotDraftedTestTargets };
};
const auto [selectedTestTargets, discardedTestTargets] = selectCoveringTestTargetsAndPruneDraftedFromDiscarded();
// The subset of selected test targets that are not on the configuration's exclude list and those that are
auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets);
// We present to the client the included selected test targets and the drafted test targets as distinct sets but internally
// we consider the concatenated set of the two the actual set of tests to run
AZStd::vector<const TestTarget*> testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets);
if (testSequenceStartCallback.has_value())
// Functor for running instrumented test targets
const auto instrumentedTestRun =
[this, &testTargetTimeout](
const AZStd::vector<const TestTarget*>& testsTargets,
TestRunCompleteCallbackHandler& testRunCompleteHandler,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout)
{
(*testSequenceStartCallback)(
Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)),
ExtractTestTargetNames(discardedTestTargets),
ExtractTestTargetNames(draftedTestTargets));
}
return m_testEngine->InstrumentedRun(
testsTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
m_integrationFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
globalTimeout,
AZStd::ref(testRunCompleteHandler));
};
// Functor for running uninstrumented test targets
const auto regularTestRun =
[this, &testTargetTimeout](
const AZStd::vector<const TestTarget*>& testsTargets,
TestRunCompleteCallbackHandler& testRunCompleteHandler,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout)
{
return m_testEngine->RegularRun(
testsTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
globalTimeout,
AZStd::ref(testRunCompleteHandler));
};
if (dynamicDependencyMapPolicy == Policy::DynamicDependencyMap::Update)
{
const auto [result, testJobs] = m_testEngine->InstrumentedRun(
testTargetsToRun,
m_testShardingPolicy,
m_executionFailurePolicy,
Policy::IntegrityFailure::Continue,
m_testFailurePolicy,
m_targetOutputCapture,
AZStd::optional<AZStd::function<void(const AZStd::vector<TestEngineInstrumentedRun>& jobs)>> updateCoverage =
[this](const AZStd::vector<TestEngineInstrumentedRun>& jobs)
{
UpdateAndSerializeDynamicDependencyMap(jobs);
};
return ImpactAnalysisTestSequenceWrapper(
m_maxConcurrency,
GenerateImpactAnalysisSequencePolicyState(testPrioritizationPolicy, dynamicDependencyMapPolicy),
m_suiteFilter,
sequenceTimer,
instrumentedTestRun,
includedSelectedTestTargets,
excludedSelectedTestTargets,
discardedTestTargets,
draftedTestTargets,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
UpdateAndSerializeDynamicDependencyMap(testJobs);
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
}
return result;
testSequenceStartCallback,
testSequenceEndCallback,
testCompleteCallback,
updateCoverage);
}
else
{
const auto [result, testJobs] = m_testEngine->RegularRun(
testTargetsToRun,
m_testShardingPolicy,
m_executionFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
return ImpactAnalysisTestSequenceWrapper(
m_maxConcurrency,
GenerateImpactAnalysisSequencePolicyState(testPrioritizationPolicy, dynamicDependencyMapPolicy),
m_suiteFilter,
sequenceTimer,
regularTestRun,
includedSelectedTestTargets,
excludedSelectedTestTargets,
discardedTestTargets,
draftedTestTargets,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
}
return result;
testSequenceStartCallback,
testSequenceEndCallback,
testCompleteCallback,
AZStd::optional<AZStd::function<void(const AZStd::vector<TestEngineRegularRun>& jobs)>>{ AZStd::nullopt });
}
}
AZStd::pair<TestSequenceResult, TestSequenceResult> Runtime::SafeImpactAnalysisTestSequence(
Client::SafeImpactAnalysisSequenceReport Runtime::SafeImpactAnalysisTestSequence(
const ChangeList& changeList,
Policy::TestPrioritization testPrioritizationPolicy,
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<SafeImpactAnalysisTestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<SafeTestSequenceCompleteCallback> testSequenceEndCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::SafeImpactAnalysisSequenceReport>> testSequenceEndCallback,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
{
Timer timer;
const Timer sequenceTimer;
TestRunData<TestEngineInstrumentedRun> selectedTestRunData, draftedTestRunData;
TestRunData<TestEngineRegularRun> discardedTestRunData;
AZStd::optional<AZStd::chrono::milliseconds> sequenceTimeout = globalTimeout;
// Draft in the test targets that have no coverage entries in the dynamic dependency map
AZStd::vector<const TestTarget*> draftedTestTargets = m_dynamicDependencyMap->GetNotCoveringTests();
// The test targets that were selected for the change list by the dynamic dependency map and the test targets that were not
auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargetsAndUpdateEnumerationCache(changeList, testPrioritizationPolicy);
const auto [selectedTestTargets, discardedTestTargets] = SelectCoveringTestTargets(changeList, testPrioritizationPolicy);
// The subset of selected test targets that are not on the configuration's exclude list and those that are
auto [includedSelectedTestTargets, excludedSelectedTestTargets] = SelectTestTargetsByExcludeList(selectedTestTargets);
@@ -501,76 +757,146 @@ namespace TestImpact
// The subset of discarded test targets that are not on the configuration's exclude list and those that are
auto [includedDiscardedTestTargets, excludedDiscardedTestTargets] = SelectTestTargetsByExcludeList(discardedTestTargets);
// We present to the client the included selected test targets and the drafted test targets as distinct sets but internally
// we consider the concatenated set of the two the actual set of tests to run
AZStd::vector<const TestTarget*> testTargetsToRun = ConcatenateVectors(includedSelectedTestTargets, draftedTestTargets);
// Extract the client facing representation of selected, discarded and drafted test targets
const Client::TestRunSelection selectedTests(
ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets));
const Client::TestRunSelection discardedTests(ExtractTestTargetNames(includedDiscardedTestTargets), ExtractTestTargetNames(excludedDiscardedTestTargets));
const auto draftedTests = ExtractTestTargetNames(draftedTestTargets);
// Inform the client that the sequence is about to start
if (testSequenceStartCallback.has_value())
{
(*testSequenceStartCallback)(
Client::TestRunSelection(ExtractTestTargetNames(includedSelectedTestTargets), ExtractTestTargetNames(excludedSelectedTestTargets)),
Client::TestRunSelection(ExtractTestTargetNames(includedDiscardedTestTargets), ExtractTestTargetNames(excludedDiscardedTestTargets)),
ExtractTestTargetNames(draftedTestTargets));
(*testSequenceStartCallback)(m_suiteFilter, selectedTests, discardedTests, draftedTests);
}
// Impact analysis run of the selected test targets
const auto [selectedResult, selectedTestJobs] = m_testEngine->InstrumentedRun(
testTargetsToRun,
m_testShardingPolicy,
m_executionFailurePolicy,
Policy::IntegrityFailure::Continue,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
// We share the test run complete handler between the selected, discarded and drafted test runs as to present them together as one
// continuous test sequence to the client rather than three discrete test runs
const size_t totalNumTestRuns = includedSelectedTestTargets.size() + draftedTestTargets.size() + includedDiscardedTestTargets.size();
TestRunCompleteCallbackHandler testRunCompleteHandler(totalNumTestRuns, testCompleteCallback);
const auto selectedDuraton = timer.Elapsed();
// Carry the remaining global sequence time over to the discarded test run
if (globalTimeout.has_value())
// Functor for running instrumented test targets
const auto instrumentedTestRun =
[this, &testTargetTimeout, &sequenceTimeout, &testRunCompleteHandler](const AZStd::vector<const TestTarget*>& testsTargets)
{
const auto elapsed = timer.Elapsed();
globalTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0);
return m_testEngine->InstrumentedRun(
testsTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
m_integrationFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
sequenceTimeout,
AZStd::ref(testRunCompleteHandler));
};
// Functor for running uninstrumented test targets
const auto regularTestRun =
[this, &testTargetTimeout, &sequenceTimeout, &testRunCompleteHandler](const AZStd::vector<const TestTarget*>& testsTargets)
{
return m_testEngine->RegularRun(
testsTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
sequenceTimeout,
AZStd::ref(testRunCompleteHandler));
};
// Functor for running instrumented test targets
const auto gatherTestRunData = [&sequenceTimer]
(const AZStd::vector<const TestTarget*>& testsTargets, const auto& testRunner, auto& testRunData)
{
const Timer testRunTimer;
testRunData.m_relativeStartTime = testRunTimer.GetStartTimePointRelative(sequenceTimer);
auto [result, jobs] = testRunner(testsTargets);
testRunData.m_result = result;
testRunData.m_jobs = AZStd::move(jobs);
testRunData.m_duration = testRunTimer.GetElapsedMs();
};
if (!includedSelectedTestTargets.empty())
{
// Run the selected test targets and collect the test run results
gatherTestRunData(includedSelectedTestTargets, instrumentedTestRun, selectedTestRunData);
// Carry the remaining global sequence time over to the discarded test run
if (globalTimeout.has_value())
{
const auto elapsed = selectedTestRunData.m_duration;
sequenceTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0);
}
}
// Regular run of the discarded test targets
const auto [discardedResult, discardedTestJobs] = m_testEngine->RegularRun(
includedDiscardedTestTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
if (!includedDiscardedTestTargets.empty())
{
// Run the discarded test targets and collect the test run results
gatherTestRunData(includedDiscardedTestTargets, regularTestRun, discardedTestRunData);
// Carry the remaining global sequence time over to the drafted test run
if (globalTimeout.has_value())
{
const auto elapsed = selectedTestRunData.m_duration + discardedTestRunData.m_duration;
sequenceTimeout = elapsed < globalTimeout.value() ? globalTimeout.value() - elapsed : AZStd::chrono::milliseconds(0);
}
}
if (!draftedTestTargets.empty())
{
// Run the drafted test targets and collect the test run results
gatherTestRunData(draftedTestTargets, instrumentedTestRun, draftedTestRunData);
}
// Generate the sequence report for the client
const auto sequenceReport = Client::SafeImpactAnalysisSequenceReport(
m_maxConcurrency,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
const auto discardedDuraton = timer.Elapsed();
GenerateSafeImpactAnalysisSequencePolicyState(testPrioritizationPolicy),
m_suiteFilter,
selectedTests,
discardedTests,
draftedTests,
GenerateTestRunReport(
selectedTestRunData.m_result,
selectedTestRunData.m_relativeStartTime,
selectedTestRunData.m_duration,
selectedTestRunData.m_jobs),
GenerateTestRunReport(
discardedTestRunData.m_result,
discardedTestRunData.m_relativeStartTime,
discardedTestRunData.m_duration,
discardedTestRunData.m_jobs),
GenerateTestRunReport(
draftedTestRunData.m_result,
draftedTestRunData.m_relativeStartTime,
draftedTestRunData.m_duration,
draftedTestRunData.m_jobs));
// Inform the client that the sequence has ended
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(
GenerateSequenceFailureReport(selectedTestJobs),
GenerateSequenceFailureReport(discardedTestJobs),
selectedDuraton,
discardedDuraton);
(*testSequenceEndCallback)(sequenceReport);
}
UpdateAndSerializeDynamicDependencyMap(selectedTestJobs);
return { selectedResult, discardedResult };
UpdateAndSerializeDynamicDependencyMap(ConcatenateVectors(selectedTestRunData.m_jobs, draftedTestRunData.m_jobs));
return sequenceReport;
}
TestSequenceResult Runtime::SeededTestSequence(
Client::SeedSequenceReport Runtime::SeededTestSequence(
AZStd::optional<AZStd::chrono::milliseconds> testTargetTimeout,
AZStd::optional<AZStd::chrono::milliseconds> globalTimeout,
AZStd::optional<TestSequenceStartCallback> testSequenceStartCallback,
AZStd::optional<TestSequenceCompleteCallback> testSequenceEndCallback,
AZStd::optional<TestSequenceCompleteCallback<Client::SeedSequenceReport>> testSequenceEndCallback,
AZStd::optional<TestRunCompleteCallback> testCompleteCallback)
{
Timer timer;
const Timer sequenceTimer;
AZStd::vector<const TestTarget*> includedTestTargets;
AZStd::vector<const TestTarget*> excludedTestTargets;
// Separate the test targets into those that are excluded by either the test filter or exclusion list and those that are not
for (const auto& testTarget : m_dynamicDependencyMap->GetTestTargetList().GetTargets())
{
if (!m_testTargetExcludeList.contains(&testTarget))
@@ -583,31 +909,48 @@ namespace TestImpact
}
}
// Extract the client facing representation of selected test targets
Client::TestRunSelection selectedTests(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets));
// Inform the client that the sequence is about to start
if (testSequenceStartCallback.has_value())
{
(*testSequenceStartCallback)(Client::TestRunSelection(ExtractTestTargetNames(includedTestTargets), ExtractTestTargetNames(excludedTestTargets)));
(*testSequenceStartCallback)(m_suiteFilter, selectedTests);
}
// Run the test targets and collect the test run results
const Timer testRunTimer;
const auto [result, testJobs] = m_testEngine->InstrumentedRun(
includedTestTargets,
m_testShardingPolicy,
m_executionFailurePolicy,
Policy::IntegrityFailure::Continue,
m_integrationFailurePolicy,
m_testFailurePolicy,
m_targetOutputCapture,
testTargetTimeout,
globalTimeout,
TestRunCompleteCallbackHandler(testCompleteCallback));
TestRunCompleteCallbackHandler(includedTestTargets.size(), testCompleteCallback));
const auto testRunDuration = testRunTimer.GetElapsedMs();
// Generate the sequence report for the client
const auto sequenceReport = Client::SeedSequenceReport(
m_maxConcurrency,
testTargetTimeout,
globalTimeout,
GenerateSequencePolicyState(),
m_suiteFilter,
selectedTests,
GenerateTestRunReport(result, testRunTimer.GetStartTimePointRelative(sequenceTimer), testRunDuration, testJobs));
// Inform the client that the sequence has ended
if (testSequenceEndCallback.has_value())
{
(*testSequenceEndCallback)(GenerateSequenceFailureReport(testJobs), timer.Elapsed());
(*testSequenceEndCallback)(sequenceReport);
}
ClearDynamicDependencyMapAndRemoveExistingFile();
UpdateAndSerializeDynamicDependencyMap(testJobs);
return result;
return sequenceReport;
}
bool Runtime::HasImpactAnalysisData() const
@@ -6,7 +6,7 @@
*
*/
#include <TestImpactFramework/TestImpactFileUtils.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <TestImpactFramework/TestImpactRuntimeException.h>
#include <TestImpactRuntimeUtils.h>
@@ -24,13 +24,13 @@ namespace TestImpact
return TestTargetMetaMapFactory(masterTestListData, suiteFilter);
}
AZStd::vector<TestImpact::BuildTargetDescriptor> ReadBuildTargetDescriptorFiles(const BuildTargetDescriptorConfig& buildTargetDescriptorConfig)
AZStd::vector<BuildTargetDescriptor> ReadBuildTargetDescriptorFiles(const BuildTargetDescriptorConfig& buildTargetDescriptorConfig)
{
AZStd::vector<TestImpact::BuildTargetDescriptor> buildTargetDescriptors;
AZStd::vector<BuildTargetDescriptor> buildTargetDescriptors;
for (const auto& buildTargetDescriptorFile : std::filesystem::directory_iterator(buildTargetDescriptorConfig.m_mappingDirectory.c_str()))
{
const auto buildTargetDescriptorContents = ReadFileContents<RuntimeException>(buildTargetDescriptorFile.path().string().c_str());
auto buildTargetDescriptor = TestImpact::BuildTargetDescriptorFactory(
auto buildTargetDescriptor = BuildTargetDescriptorFactory(
buildTargetDescriptorContents,
buildTargetDescriptorConfig.m_staticInclusionFilters,
buildTargetDescriptorConfig.m_inputInclusionFilters,
@@ -41,7 +41,7 @@ namespace TestImpact
return buildTargetDescriptors;
}
AZStd::unique_ptr<TestImpact::DynamicDependencyMap> ConstructDynamicDependencyMap(
AZStd::unique_ptr<DynamicDependencyMap> ConstructDynamicDependencyMap(
SuiteType suiteFilter,
const BuildTargetDescriptorConfig& buildTargetDescriptorConfig,
const TestTargetMetaConfig& testTargetMetaConfig)
@@ -50,7 +50,7 @@ namespace TestImpact
auto buildTargetDescriptors = ReadBuildTargetDescriptorFiles(buildTargetDescriptorConfig);
auto buildTargets = CompileTargetDescriptors(AZStd::move(buildTargetDescriptors), AZStd::move(testTargetmetaMap));
auto&& [productionTargets, testTargets] = buildTargets;
return AZStd::make_unique<TestImpact::DynamicDependencyMap>(AZStd::move(productionTargets), AZStd::move(testTargets));
return AZStd::make_unique<DynamicDependencyMap>(AZStd::move(productionTargets), AZStd::move(testTargets));
}
AZStd::unordered_set<const TestTarget*> ConstructTestTargetExcludeList(
@@ -68,7 +68,7 @@ namespace TestImpact
return testTargetExcludeList;
}
AZStd::vector<AZStd::string> ExtractTestTargetNames(const AZStd::vector<const TestTarget*> testTargets)
AZStd::vector<AZStd::string> ExtractTestTargetNames(const AZStd::vector<const TestTarget*>& testTargets)
{
AZStd::vector<AZStd::string> testNames;
AZStd::transform(testTargets.begin(), testTargets.end(), AZStd::back_inserter(testNames), [](const TestTarget* testTarget)
@@ -10,7 +10,7 @@
#include <TestImpactFramework/TestImpactConfiguration.h>
#include <TestImpactFramework/TestImpactClientTestSelection.h>
#include <TestImpactFramework/TestImpactClientFailureReport.h>
#include <TestImpactFramework/TestImpactClientSequenceReport.h>
#include <Artifact/Static/TestImpactTestTargetMeta.h>
#include <Artifact/Static/TestImpactBuildTargetDescriptor.h>
@@ -25,7 +25,7 @@
namespace TestImpact
{
//! Construct a dynamic dependency map from the build target descriptors and test target metas.
AZStd::unique_ptr<TestImpact::DynamicDependencyMap> ConstructDynamicDependencyMap(
AZStd::unique_ptr<DynamicDependencyMap> ConstructDynamicDependencyMap(
SuiteType suiteFilter,
const BuildTargetDescriptorConfig& buildTargetDescriptorConfig,
const TestTargetMetaConfig& testTargetMetaConfig);
@@ -36,77 +36,97 @@ namespace TestImpact
const AZStd::vector<AZStd::string>& excludedTestTargets);
//! Extracts the name information from the specified test targets.
AZStd::vector<AZStd::string> ExtractTestTargetNames(const AZStd::vector<const TestTarget*> testTargets);
AZStd::vector<AZStd::string> ExtractTestTargetNames(const AZStd::vector<const TestTarget*>& testTargets);
//! Generates a test run failure report from the specified test engine job information.
//! Generates the test suites from the specified test engine job information.
//! @tparam TestJob The test engine job type.
template<typename TestJob>
Client::TestRunFailure GenerateTestRunFailure(const TestJob& testJob)
AZStd::vector<Client::Test> GenerateClientTests(const TestJob& testJob)
{
AZStd::vector<Client::Test> tests;
if (testJob.GetTestRun().has_value())
{
AZStd::vector<Client::TestCaseFailure> testCaseFailures;
for (const auto& testSuite : testJob.GetTestRun()->GetTestSuites())
{
AZStd::vector<Client::TestFailure> testFailures;
for (const auto& testCase : testSuite.m_tests)
{
if (testCase.m_result.value_or(TestRunResult::Passed) == TestRunResult::Failed)
auto result = Client::TestResult::NotRun;
if (testCase.m_result.has_value())
{
testFailures.push_back(Client::TestFailure(testCase.m_name, "No error message retrieved"));
if (testCase.m_result.value() == TestRunResult::Passed)
{
result = Client::TestResult::Passed;
}
else if (testCase.m_result.value() == TestRunResult::Failed)
{
result = Client::TestResult::Failed;
}
else
{
throw RuntimeException(AZStd::string::format(
"Unexpected test run result: %u", aznumeric_cast<AZ::u32>(testCase.m_result.value())));
}
}
}
if (!testFailures.empty())
{
testCaseFailures.push_back(Client::TestCaseFailure(testSuite.m_name, AZStd::move(testFailures)));
const auto name = AZStd::string::format("%s.%s", testSuite.m_name.c_str(), testCase.m_name.c_str());
tests.push_back(Client::Test(name, result));
}
}
}
return Client::TestRunFailure(Client::TestRunFailure(testJob.GetTestTarget()->GetName(), AZStd::move(testCaseFailures)));
}
else
{
return Client::TestRunFailure(testJob.GetTestTarget()->GetName(), { });
}
return tests;
}
//! Generates a sequence failure report from the specified list of test engine jobs.
//! @tparam TestJob The test engine job type.
template<typename TestJob>
Client::SequenceFailure GenerateSequenceFailureReport(const AZStd::vector<TestJob>& testJobs)
Client::TestRunReport GenerateTestRunReport(
TestSequenceResult result,
AZStd::chrono::high_resolution_clock::time_point startTime,
AZStd::chrono::milliseconds duration,
const AZStd::vector<TestJob>& testJobs)
{
AZStd::vector<Client::ExecutionFailure> executionFailures;
AZStd::vector<Client::TestRunFailure> testRunFailures;
AZStd::vector<Client::TargetFailure> timedOutTestRuns;
AZStd::vector<Client::TargetFailure> unexecutedTestRuns;
AZStd::vector<Client::PassingTestRun> passingTests;
AZStd::vector<Client::FailingTestRun> failingTests;
AZStd::vector<Client::TestRunWithExecutionFailure> executionFailureTests;
AZStd::vector<Client::TimedOutTestRun> timedOutTests;
AZStd::vector<Client::UnexecutedTestRun> unexecutedTests;
for (const auto& testJob : testJobs)
{
// Test job start time relative to start time
const auto relativeStartTime =
AZStd::chrono::high_resolution_clock::time_point() +
AZStd::chrono::duration_cast<AZStd::chrono::milliseconds>(testJob.GetStartTime() - startTime);
Client::TestRunBase clientTestRun(
testJob.GetTestTarget()->GetName(), testJob.GetCommandString(), relativeStartTime, testJob.GetDuration(),
testJob.GetTestResult());
switch (testJob.GetTestResult())
{
case Client::TestRunResult::FailedToExecute:
{
executionFailures.push_back(Client::ExecutionFailure(testJob.GetTestTarget()->GetName(), testJob.GetCommandString()));
executionFailureTests.emplace_back(AZStd::move(clientTestRun));
break;
}
case Client::TestRunResult::NotRun:
{
unexecutedTestRuns.push_back(testJob.GetTestTarget()->GetName());
unexecutedTests.emplace_back(AZStd::move(clientTestRun));
break;
}
case Client::TestRunResult::Timeout:
{
timedOutTestRuns.push_back(testJob.GetTestTarget()->GetName());
timedOutTests.emplace_back(AZStd::move(clientTestRun));
break;
}
case Client::TestRunResult::AllTestsPass:
{
passingTests.emplace_back(AZStd::move(clientTestRun), GenerateClientTests(testJob));
break;
}
case Client::TestRunResult::TestFailures:
{
testRunFailures.push_back(GenerateTestRunFailure(testJob));
failingTests.emplace_back(AZStd::move(clientTestRun), GenerateClientTests(testJob));
break;
}
default:
@@ -116,11 +136,15 @@ namespace TestImpact
}
}
}
return Client::SequenceFailure(
AZStd::move(executionFailures),
AZStd::move(testRunFailures),
AZStd::move(timedOutTestRuns),
AZStd::move(unexecutedTestRuns));
return Client::TestRunReport(
result,
startTime,
duration,
AZStd::move(passingTests),
AZStd::move(failingTests),
AZStd::move(executionFailureTests),
AZStd::move(timedOutTests),
AZStd::move(unexecutedTests));
}
}
} // namespace TestImpact
@@ -0,0 +1,244 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#include <TestImpactFramework/TestImpactException.h>
#include <TestImpactFramework/TestImpactUtils.h>
#include <AzCore/std/functional.h>
namespace TestImpact
{
//! Delete the files that match the pattern from the specified directory.
//! @param path The path to the directory to pattern match the files for deletion.
//! @param pattern The pattern to match files for deletion.
size_t DeleteFiles(const RepoPath& path, const AZStd::string& pattern)
{
size_t numFilesDeleted = 0;
AZ::IO::SystemFile::FindFiles(
AZStd::string::format("%s/%s", path.c_str(), pattern.c_str()).c_str(),
[&path, &numFilesDeleted](const char* file, bool isFile)
{
if (isFile)
{
AZ::IO::SystemFile::Delete(AZStd::string::format("%s/%s", path.c_str(), file).c_str());
numFilesDeleted++;
}
return true;
});
return numFilesDeleted;
}
//! Deletes the specified file.
void DeleteFile(const RepoPath& file)
{
DeleteFiles(file.ParentPath(), file.Filename().Native());
}
//! User-friendly names for the test suite types.
AZStd::string SuiteTypeAsString(SuiteType suiteType)
{
switch (suiteType)
{
case SuiteType::Main:
return "main";
case SuiteType::Periodic:
return "periodic";
case SuiteType::Sandbox:
return "sandbox";
default:
throw(Exception("Unexpected suite type"));
}
}
AZStd::string SequenceReportTypeAsString(Client::SequenceReportType type)
{
switch (type)
{
case Client::SequenceReportType::RegularSequence:
return "regular";
case Client::SequenceReportType::SeedSequence:
return "seed";
case Client::SequenceReportType::ImpactAnalysisSequence:
return "impact_analysis";
case Client::SequenceReportType::SafeImpactAnalysisSequence:
return "safe_impact_analysis";
default:
throw(Exception(AZStd::string::format("Unexpected sequence report type: %u", aznumeric_cast<AZ::u32>(type))));
}
}
AZStd::string TestSequenceResultAsString(TestSequenceResult result)
{
switch (result)
{
case TestSequenceResult::Failure:
return "failure";
case TestSequenceResult::Success:
return "success";
case TestSequenceResult::Timeout:
return "timeout";
default:
throw(Exception(AZStd::string::format("Unexpected test sequence result: %u", aznumeric_cast<AZ::u32>(result))));
}
}
AZStd::string TestRunResultAsString(Client::TestRunResult result)
{
switch (result)
{
case Client::TestRunResult::AllTestsPass:
return "all_tests_pass";
case Client::TestRunResult::FailedToExecute:
return "failed_to_execute";
case Client::TestRunResult::NotRun:
return "not_run";
case Client::TestRunResult::TestFailures:
return "test_failures";
case Client::TestRunResult::Timeout:
return "timeout";
default:
throw(Exception(AZStd::string::format("Unexpected test run result: %u", aznumeric_cast<AZ::u32>(result))));
}
}
AZStd::string ExecutionFailurePolicyAsString(Policy::ExecutionFailure executionFailurePolicy)
{
switch (executionFailurePolicy)
{
case Policy::ExecutionFailure::Abort:
return "abort";
case Policy::ExecutionFailure::Continue:
return "continue";
case Policy::ExecutionFailure::Ignore:
return "ignore";
default:
throw(Exception(
AZStd::string::format("Unexpected execution failure policy: %u", aznumeric_cast<AZ::u32>(executionFailurePolicy))));
}
}
AZStd::string FailedTestCoveragePolicyAsString(Policy::FailedTestCoverage failedTestCoveragePolicy)
{
switch (failedTestCoveragePolicy)
{
case Policy::FailedTestCoverage::Discard:
return "discard";
case Policy::FailedTestCoverage::Keep:
return "keep";
default:
throw(Exception(
AZStd::string::format("Unexpected failed test coverage policy: %u", aznumeric_cast<AZ::u32>(failedTestCoveragePolicy))));
}
}
AZStd::string TestPrioritizationPolicyAsString(Policy::TestPrioritization testPrioritizationPolicy)
{
switch (testPrioritizationPolicy)
{
case Policy::TestPrioritization::DependencyLocality:
return "dependency_locality";
case Policy::TestPrioritization::None:
return "none";
default:
throw(Exception(
AZStd::string::format("Unexpected test prioritization policy: %u", aznumeric_cast<AZ::u32>(testPrioritizationPolicy))));
}
}
AZStd::string TestFailurePolicyAsString(Policy::TestFailure testFailurePolicy)
{
switch (testFailurePolicy)
{
case Policy::TestFailure::Abort:
return "abort";
case Policy::TestFailure::Continue:
return "continue";
default:
throw(
Exception(AZStd::string::format("Unexpected test failure policy: %u", aznumeric_cast<AZ::u32>(testFailurePolicy))));
}
}
AZStd::string IntegrityFailurePolicyAsString(Policy::IntegrityFailure integrityFailurePolicy)
{
switch (integrityFailurePolicy)
{
case Policy::IntegrityFailure::Abort:
return "abort";
case Policy::IntegrityFailure::Continue:
return "continue";
default:
throw(Exception(
AZStd::string::format("Unexpected integration failure policy: %u", aznumeric_cast<AZ::u32>(integrityFailurePolicy))));
}
}
AZStd::string DynamicDependencyMapPolicyAsString(Policy::DynamicDependencyMap dynamicDependencyMapPolicy)
{
switch (dynamicDependencyMapPolicy)
{
case Policy::DynamicDependencyMap::Discard:
return "discard";
case Policy::DynamicDependencyMap::Update:
return "update";
default:
throw(Exception(AZStd::string::format(
"Unexpected dynamic dependency map policy: %u", aznumeric_cast<AZ::u32>(dynamicDependencyMapPolicy))));
}
}
AZStd::string TestShardingPolicyAsString(Policy::TestSharding testShardingPolicy)
{
switch (testShardingPolicy)
{
case Policy::TestSharding::Always:
return "always";
case Policy::TestSharding::Never:
return "never";
default:
throw(Exception(
AZStd::string::format("Unexpected test sharding policy: %u", aznumeric_cast<AZ::u32>(testShardingPolicy))));
}
}
AZStd::string TargetOutputCapturePolicyAsString(Policy::TargetOutputCapture targetOutputCapturePolicy)
{
switch (targetOutputCapturePolicy)
{
case Policy::TargetOutputCapture::File:
return "file";
case Policy::TargetOutputCapture::None:
return "none";
case Policy::TargetOutputCapture::StdOut:
return "stdout";
case Policy::TargetOutputCapture::StdOutAndFile:
return "stdout_file";
default:
throw(Exception(
AZStd::string::format("Unexpected target output capture policy: %u", aznumeric_cast<AZ::u32>(targetOutputCapturePolicy))));
}
}
AZStd::string ClientTestResultAsString(Client::TestResult result)
{
switch (result)
{
case Client::TestResult::Failed:
return "failed";
case Client::TestResult::NotRun:
return "not_run";
case Client::TestResult::Passed:
return "passed";
default:
throw(Exception(AZStd::string::format("Unexpected client test case result: %u", aznumeric_cast<AZ::u32>(result))));
}
}
} // namespace TestImpact
@@ -16,11 +16,14 @@ set(FILES
Include/TestImpactFramework/TestImpactChangelist.h
Include/TestImpactFramework/TestImpactChangelistSerializer.h
Include/TestImpactFramework/TestImpactChangelistException.h
Include/TestImpactFramework/TestImpactPolicy.h
Include/TestImpactFramework/TestImpactTestSequence.h
Include/TestImpactFramework/TestImpactClientTestSelection.h
Include/TestImpactFramework/TestImpactClientTestRun.h
Include/TestImpactFramework/TestImpactClientFailureReport.h
Include/TestImpactFramework/TestImpactFileUtils.h
Include/TestImpactFramework/TestImpactClientSequenceReport.h
Include/TestImpactFramework/TestImpactUtils.h
Include/TestImpactFramework/TestImpactClientSequenceReportSerializer.h
Include/TestImpactFramework/TestImpactSequenceReportException.h
Source/Artifact/TestImpactArtifactException.h
Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.cpp
Source/Artifact/Factory/TestImpactBuildTargetDescriptorFactory.h
@@ -123,7 +126,9 @@ set(FILES
Source/TestImpactRuntimeUtils.h
Source/TestImpactClientTestSelection.cpp
Source/TestImpactClientTestRun.cpp
Source/TestImpactClientFailureReport.cpp
Source/TestImpactClientSequenceReport.cpp
Source/TestImpactChangeListSerializer.cpp
Source/TestImpactClientSequenceReportSerializer.cpp
Source/TestImpactRepoPath.cpp
Source/TestImpactUtils.cpp
)