Merge branch 'upstream/development' into genewalt/gitflow_210628

Signed-off-by: Gene Walters <genewalt@amazon.com>
monroegm-disable-blank-issue-2
Gene Walters 5 years ago
commit 6fb2558e44

@ -0,0 +1,73 @@
---
name: RFC Feature request
about: Create Feature RFC for this project
title: Proposed RFC Feature =description=
labels: 'rfc-feature'
assignees: ''
---
# O3DE RFC Feature Template
### When using this template, you do not have to fill out every question below. The questions are there for guidance.
This RFC feature template should be used for any feature that is not a bug or a substantial reorganization of the O3DE product.
If you submit a pull request to implement a new feature without going through the RFC process, it may be closed with a polite request to submit an RFC first.
A hastily proposed RFC can hurt its chances of acceptance. Low-quality proposals, proposals for previously-rejected features, or those that don't fit into the near-term roadmap may be quickly rejected, demotivating the unprepared contributor. Laying some groundwork ahead of the RFC can make the process smoother.
Although there is no single way to prepare for submitting an RFC, it is generally a good idea to pursue feedback from other project developers beforehand to ascertain that the RFC may be desirable; having a consistent impact on the project requires concerted effort toward consensus-building.
The most common preparations for writing and submitting an RFC include:
- Talking the idea over on our Discord server.
- Discussing the topic on our GitHub RFCs discussions page.
- Occasionally posting "pre-RFCs" on the GitHub RFCs discussion page.
You may file issues in the RFCs repo for discussion, but these are not actively looked at by the teams.
As a rule of thumb, receiving encouraging feedback from long-standing project developers, and particularly members of the relevant sub-team, is a good indication that the RFC is worth pursuing.
# ----- DELETE EVERYTHING FROM THE TOP TO THE SUMMARY LINE BELOW WHEN USING TEMPLATE ----- #
### Summary:
Single paragraph explanation of the feature
### What is the relevance of this feature?
Why is this important? What are the use cases? What will it do once completed?
### Feature design description:
- Explain the design of the feature with enough detail that someone familiar with the environment and framework can understand the concept and explain it to others.
- It should include at least one end-to-end example of how a developer will use it along with specific details, including outlying use cases.
- If there is any new terminology, it should be defined here.
### Technical design description:
- Explain the technical portion of the work in enough detail that members can implement the feature.
- Explain any API or process changes required to implement this feature
- This section should relate to the feature design description by reference and explain in greater detail how it makes the feature design examples work.
- This should also provide detailed information on compatibility with different hardware platforms.
### What are the advantages of the feature?
- Explain the advantages for someone to use this feature
### What are the disadvantages of the feature?
- Explain any disadvantages for someone to use this feature
### How will this be implemented or integrated into the O3DE environment?
- Explain how a developer will integrate this into the codebase of O3DE and provide any specific library or technical stack requirements.
### Are there any alternatives to this feature?
- Provide any other designs that have been considered. Explain what the impact might be of not doing this.
- If there is any prior art or approaches with other frameworks in the same domain, explain how they may have solved this problem or implemented this feature.
### How will users learn this feature?
- Detail how it can be best presented and how it is used as an extension or a standalone tool used with O3DE.
- Explain if and how it may change how individuals would use the platform and if any documentation must be changed or reorganized.
- Explain how it would be taught to new and existing O3DE users.
### Are there any open questions?
- What are some of the open questions and potential scenarios that should be considered?

@ -0,0 +1,61 @@
---
name: RFC Suggestion request
about: Create Suggestion RFC for this project
title: Proposed RFC Suggestion =description=
labels: 'rfc-suggestion'
assignees: ''
---
# O3DE Suggestion RFC Template
### When using this template, you do not have to fill out every question below. The questions are there for guidance.
This RFC template should be used for any suggestion that is not based upon code or content related to the O3DE product itself. This template is for proposing new process models, approaches, or ideas to improve the O3DE community.
A hastily proposed RFC can hurt its chances of acceptance. Low-quality proposals, proposals for previously rejected features, or those that do not have any substantive value to the project may be quickly rejected, demotivating the unprepared contributor. Laying some groundwork with others in the community ahead of the RFC can make the process much smoother.
Although there is no single way to prepare for submitting an RFC, it is generally a good idea to pursue feedback from other project members beforehand. Keep in mind that you want other members to contribute and back your suggestion, which can drastically improve the chances of implementation.
The most common preparations for writing and submitting an RFC include:
- Talking the idea over on our Discord server.
- Creating a discussion on our GitHub RFCs discussions page.
- Occasionally posting "pre-RFCs" on the GitHub RFCs discussion page.
You may file issues in the RFCs repo for discussion, but these are not actively looked at by the teams.
As a rule of thumb, receiving encouraging feedback from long-standing community members is a good indication that the RFC is worth pursuing.
# ----- DELETE EVERYTHING FROM THE TOP TO THE SUMMARY LINE BELOW WHEN USING TEMPLATE ----- #
### Summary:
Single paragraph explanation of the suggestion
### What is the motivation for this suggestion?
Why is this important?
What are the use cases for this suggestion?
What should the outcome be if this suggestion is implemented?
### Suggestion design description:
- Explain the suggestion with enough detail that someone familiar with the process and environment of the project can understand the suggestion and explain it to others.
- It should include at least one end-to-end example of how the community will use it along with the specific details with outlying use cases.
- If there is any new terminology, it should be defined here.
### What are the advantages of the suggestion?
- Explain the advantages of using this suggestion
### What are the disadvantages of the suggestion?
- Explain any disadvantages or trade-offs to using this suggestion
### How will this be work within the O3DE project?
- Explain how this suggestion will be work within the O3DE project.
### Are there any alternatives to this suggestion?
- Provide any other alternative ways that have been considered.
- Explain what the impact might be of not implementing this suggestion.
- If there are other similar suggestions previously used, list them and explain which parts may have solved some or all of this problem.
### What is the strategy for adoption?
- Explain how new and existing users will adopt this suggestion.
- Point out any efforts needed if it requires coordination with multiple SIGs or other projects.
- Explain how it would be taught to new and existing O3DE users.

@ -7,3 +7,4 @@
add_subdirectory(Code)
add_subdirectory(PythonTests)
add_subdirectory(PythonCoverage)

@ -48,6 +48,7 @@ set(ENABLED_GEMS
LyShine
HttpRequestor
Atom_AtomBridge
PythonCoverage
)
# TODO remove conditional add once AWSNativeSDK libs are fixed for Android and Linux Monolithic release.

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
add_subdirectory(Code)

@ -0,0 +1,62 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name})
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED)
if(PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME PythonCoverage.Editor.Static STATIC
NAMESPACE Gem
FILES_CMAKE
pythoncoverage_editor_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
COMPILE_DEFINITIONS
PUBLIC
PYTHON_COVERAGE_EDITOR
PRIVATE
${LY_TEST_IMPACT_CONFIG_FILE_PATH_DEFINITION}
BUILD_DEPENDENCIES
PUBLIC
AZ::AzToolsFramework
RUNTIME_DEPENDENCIES
Gem::EditorPythonBindings.Editor
)
ly_add_target(
NAME PythonCoverage.Editor GEM_MODULE
NAMESPACE Gem
AUTOMOC
OUTPUT_NAME Gem.PythonCoverage.Editor
FILES_CMAKE
pythoncoverage_editor_shared_files.cmake
COMPILE_DEFINITIONS
PRIVATE
PYTHON_COVERAGE_EDITOR
INCLUDE_DIRECTORIES
PRIVATE
Source
PUBLIC
Include
BUILD_DEPENDENCIES
PUBLIC
Gem::PythonCoverage.Editor.Static
)
ly_create_alias(NAME PythonCoverage.Tools NAMESPACE Gem TARGETS Gem::PythonCoverage.Editor)
ly_create_alias(NAME PythonCoverage.Builders NAMESPACE Gem TARGETS Gem::PythonCoverage.Editor)
endif()
endif()

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED FALSE)

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED FALSE)

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED FALSE)

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE)

@ -0,0 +1,12 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED FALSE)

@ -0,0 +1,38 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include "PythonCoverageEditorModule.h"
#include "PythonCoverageEditorSystemComponent.h"
namespace PythonCoverage
{
AZ_CLASS_ALLOCATOR_IMPL(PythonCoverageEditorModule, AZ::SystemAllocator, 0)
PythonCoverageEditorModule::PythonCoverageEditorModule()
{
m_descriptors.insert(
m_descriptors.end(),
{
PythonCoverageEditorSystemComponent::CreateDescriptor()
});
}
PythonCoverageEditorModule::~PythonCoverageEditorModule() = default;
AZ::ComponentTypeList PythonCoverageEditorModule::GetRequiredSystemComponents() const
{
// add required SystemComponents to the SystemEntity
return AZ::ComponentTypeList{ azrtti_typeid<PythonCoverageEditorSystemComponent>() };
}
} // namespace PythonCoverage
AZ_DECLARE_MODULE_CLASS(Gem_PythonCoverageEditor, PythonCoverage::PythonCoverageEditorModule)

@ -0,0 +1,33 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Memory/SystemAllocator.h>
#include <AzCore/Module/Module.h>
namespace PythonCoverage
{
class PythonCoverageEditorModule
: public AZ::Module
{
public:
AZ_CLASS_ALLOCATOR_DECL
AZ_RTTI(PythonCoverageEditorModule, "{32C0FFEA-09A7-460F-9257-5BDEF74FCD5B}", AZ::Module);
PythonCoverageEditorModule();
~PythonCoverageEditorModule();
// PythonCoverageModule ...
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
};
} // namespace WhiteBox

@ -0,0 +1,246 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <AzCore/IO/Path/Path.h>
#include <AzCore/JSON/document.h>
#include <AzCore/Module/ModuleManagerBus.h>
#include <AzCore/Module/Module.h>
#include <AzCore/Module/DynamicModuleHandle.h>
#include <AzCore/Serialization/SerializeContext.h>
#include <PythonCoverageEditorSystemComponent.h>
namespace PythonCoverage
{
static constexpr char* const LogCallSite = "PythonCoverageEditorSystemComponent";
void PythonCoverageEditorSystemComponent::Reflect(AZ::ReflectContext* context)
{
if (auto serializeContext = azrtti_cast<AZ::SerializeContext*>(context))
{
serializeContext->Class<PythonCoverageEditorSystemComponent, AZ::Component>()->Version(1);
}
}
void PythonCoverageEditorSystemComponent::Activate()
{
AzToolsFramework::EditorPythonScriptNotificationsBus::Handler::BusConnect();
AZ::EntitySystemBus::Handler::BusConnect();
// If no output directory discovered, coverage gathering will be disabled
if (ParseCoverageOutputDirectory() == CoverageState::Disabled)
{
return;
}
EnumerateAllModuleComponents();
}
void PythonCoverageEditorSystemComponent::Deactivate()
{
AZ::EntitySystemBus::Handler::BusDisconnect();
AzToolsFramework::EditorPythonScriptNotificationsBus::Handler::BusDisconnect();
}
void PythonCoverageEditorSystemComponent::OnEntityActivated(const AZ::EntityId& entityId)
{
if (m_coverageState == CoverageState::Disabled)
{
return;
}
EnumerateComponentsForEntity(entityId);
// There is currently no way to receive a graceful exit signal in order to properly handle the coverage end of life so
// instead we have to serialize the data on-the-fly with blocking disk writes on the main thread... if this adversely
// affects performance in a measurable way then this could potentially be put on a worker thread, although it remains to
// be seen whether the asynchronous nature of such a thread results in queued up coverage being lost due to the hard exit
if (m_coverageState == CoverageState::Gathering)
{
WriteCoverageFile();
}
}
PythonCoverageEditorSystemComponent::CoverageState PythonCoverageEditorSystemComponent::ParseCoverageOutputDirectory()
{
m_coverageState = CoverageState::Disabled;
const AZStd::string configFilePath = LY_TEST_IMPACT_DEFAULT_CONFIG_FILE;
if (configFilePath.empty())
{
AZ_Warning(LogCallSite, false, "No test impact analysis framework config file specified.");
return m_coverageState;
}
const auto fileSize = AZ::IO::SystemFile::Length(configFilePath.c_str());
if(!fileSize)
{
AZ_Error(LogCallSite, false, "Test impact analysis framework config file '%s' does not exist", configFilePath.c_str());
return m_coverageState;
}
AZStd::vector<char> buffer(fileSize + 1);
buffer[fileSize] = '\0';
if (!AZ::IO::SystemFile::Read(configFilePath.c_str(), buffer.data()))
{
AZ_Error(LogCallSite, false, "Could not read contents of test impact analysis framework config file '%s'", configFilePath.c_str());
return m_coverageState;
}
const AZStd::string configurationData = AZStd::string(buffer.begin(), buffer.end());
rapidjson::Document configurationFile;
if (configurationFile.Parse(configurationData.c_str()).HasParseError())
{
AZ_Error(LogCallSite, false, "Could not parse test impact analysis framework config file data, JSON has errors");
return m_coverageState;
}
const auto& tempConfig = configurationFile["workspace"]["temp"];
// Temp directory root path is absolute
const AZ::IO::Path tempWorkspaceRootDir = tempConfig["root"].GetString();
// Artifact directory is relative to temp directory root
const AZ::IO::Path artifactRelativeDir = tempConfig["relative_paths"]["artifact_dir"].GetString();
m_coverageDir = tempWorkspaceRootDir / artifactRelativeDir;
// Everything is good to go, await the first python test case
m_coverageState = CoverageState::Idle;
return m_coverageState;
}
void PythonCoverageEditorSystemComponent::WriteCoverageFile()
{
AZStd::string contents;
// Compile the coverage for all test cases in this script
for (const auto& [testCase, entityComponents] : m_entityComponentMap)
{
const auto coveringModules = GetParentComponentModulesForAllActivatedEntities(entityComponents);
if (coveringModules.empty())
{
return;
}
contents = testCase + "\n";
for (const auto& coveringModule : coveringModules)
{
contents += AZStd::string::format(" %s\n", coveringModule.c_str());
}
}
AZ::IO::SystemFile file;
const AZStd::vector<char> bytes(contents.begin(), contents.end());
if (!file.Open(
m_coverageFile.c_str(),
AZ::IO::SystemFile::SF_OPEN_CREATE | AZ::IO::SystemFile::SF_OPEN_CREATE_PATH | AZ::IO::SystemFile::SF_OPEN_WRITE_ONLY))
{
AZ_Error(LogCallSite, false, "Couldn't open file '%s' for writing", m_coverageFile.c_str());
return;
}
if (!file.Write(bytes.data(), bytes.size()))
{
AZ_Error(LogCallSite, false, "Couldn't write contents for file '%s'", m_coverageFile.c_str());
return;
}
}
void PythonCoverageEditorSystemComponent::EnumerateAllModuleComponents()
{
AZ::ModuleManagerRequestBus::Broadcast(
&AZ::ModuleManagerRequestBus::Events::EnumerateModules,
[this](const AZ::ModuleData& moduleData)
{
// We can only enumerate shared libs, static libs are invisible to us
if (moduleData.GetDynamicModuleHandle())
{
for (const auto* moduleComponentDescriptor : moduleData.GetModule()->GetComponentDescriptors())
{
m_moduleComponents[moduleComponentDescriptor->GetUuid()] = moduleData.GetDebugName();
}
}
return true;
});
}
void PythonCoverageEditorSystemComponent::EnumerateComponentsForEntity(const AZ::EntityId& entityId)
{
AZ::Entity* entity = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(entity, &AZ::ComponentApplicationBus::Events::FindEntity, AZ::EntityId(entityId));
if (entity)
{
auto& entityComponents = m_entityComponentMap[m_testCase];
for (const auto& entityComponent : entity->GetComponents())
{
const auto componentTypeId = entityComponent->GetUnderlyingComponentType();
AZ::ComponentDescriptor* componentDescriptor = nullptr;
AZ::ComponentDescriptorBus::EventResult(
componentDescriptor, componentTypeId, &AZ::ComponentDescriptorBus::Events::GetDescriptor);
entityComponents[componentTypeId] = componentDescriptor;
}
}
}
AZStd::unordered_set<AZStd::string> PythonCoverageEditorSystemComponent::GetParentComponentModulesForAllActivatedEntities(
const AZStd::unordered_map<AZ::Uuid, AZ::ComponentDescriptor*>& entityComponents) const
{
AZStd::unordered_set<AZStd::string> coveringModuleOutputNames;
for (const auto& [uuid, componentDescriptor] : entityComponents)
{
if (const auto moduleComponent = m_moduleComponents.find(uuid); moduleComponent != m_moduleComponents.end())
{
coveringModuleOutputNames.insert(moduleComponent->second);
}
}
return coveringModuleOutputNames;
}
void PythonCoverageEditorSystemComponent::OnStartExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args)
{
if (m_coverageState == CoverageState::Disabled)
{
return;
}
if (m_coverageState == CoverageState::Gathering)
{
// Dump any existing coverage data to disk
WriteCoverageFile();
m_coverageState = CoverageState::Idle;
}
if (testCase.empty())
{
// We need to be able to pinpoint the coverage data to the specific test case names otherwise we will not be able
// to specify which specific tests should be run in the future (filename does not necessarily equate to test case name)
AZ_Error(LogCallSite, false, "No test case specified, coverage data gathering will be disabled for this test");
return;
}
const AZStd::string scriptName = AZ::IO::Path(filename).Stem().Native();
const auto coverageFile = m_coverageDir / AZStd::string::format("%s.pycoverage", scriptName.c_str());
// If this is a different python script we clear the existing entity components and start afresh
if (m_coverageFile != coverageFile)
{
m_entityComponentMap.clear();
m_coverageFile = coverageFile;
}
m_testCase = testCase;
m_coverageState = CoverageState::Gathering;
}
} // namespace PythonCoverage

@ -0,0 +1,88 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Component/EntityBus.h>
#include <AzCore/IO/Path/Path.h>
#include <AzCore/std/optional.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/API/EditorPythonScriptNotificationsBus.h>
namespace AZ
{
class ComponentDescriptor;
}
namespace PythonCoverage
{
//! System component for PythonCoverage editor.
class PythonCoverageEditorSystemComponent
: public AZ::Component
, private AZ::EntitySystemBus::Handler
, private AzToolsFramework::EditorPythonScriptNotificationsBus::Handler
{
public:
AZ_COMPONENT(PythonCoverageEditorSystemComponent, "{33370075-3aea-49c4-823d-476f8ac95b6f}");
static void Reflect(AZ::ReflectContext* context);
PythonCoverageEditorSystemComponent() = default;
private:
//! The coverage state for Python tests.
enum class CoverageState : AZ::u8
{
Disabled, //!< Python coverage is disabled.
Idle, //!< Python coverage is enabled but not actively gathering coverage data.
Gathering //!< Python coverage is enabled and actively gathering coverage data.
};
// AZ::Component overrides...
void Activate() override;
void Deactivate() override;
// AZ::EntitySystemBus overrides...
void OnEntityActivated(const AZ::EntityId& entityId) override;
// AZ::EditorPythonScriptNotificationsBus ...
void OnStartExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector<AZStd::string_view>& args) override;
//! Enumerates all of the loaded shared library modules and the component descriptors that belong to them.
void EnumerateAllModuleComponents();
//! Enumerates all of the component descriptors for the specified entity.
void EnumerateComponentsForEntity(const AZ::EntityId& entityId);
//! Attempts to parse the test impact analysis framework configuration file.
//! If either the test impact analysis framework is disabled or the configuration file cannot be parsed, python coverage is disabled.
//! @returns The coverage state after the parsing attempt.
CoverageState ParseCoverageOutputDirectory();
//! Returns all of the shared library modules that parent the component descriptors of the specified set of activated entities.
//! @note Entity component descriptors are still retrieved even if the entity in question has since been deactivated.
//! @param entityComponents The set of activated entities and their component descriptors to get the parent modules for.
AZStd::unordered_set<AZStd::string> GetParentComponentModulesForAllActivatedEntities(
const AZStd::unordered_map<AZ::Uuid, AZ::ComponentDescriptor*>& entityComponents) const;
//! Writes the current coverage data snapshot to disk.
void WriteCoverageFile();
CoverageState m_coverageState = CoverageState::Disabled; //!< Current coverage state.
AZStd::unordered_map<AZStd::string, AZStd::unordered_map<AZ::Uuid, AZ::ComponentDescriptor*>> m_entityComponentMap; //!< Map of
//!< component IDs to component descriptors for all activated entities, organized by test cases.
AZStd::unordered_map<AZ::Uuid, AZStd::string> m_moduleComponents; //!< Map of component IDs to module names for all modules.
AZ::IO::Path m_coverageDir; //!< Directory to write coverage data to.
AZ::IO::Path m_coverageFile; //!< Full file path to write coverage data to.
AZStd::string m_testCase; //!< Name of current test case that coverage data is being gathered for.
};
} // namespace PythonCoverage

@ -0,0 +1,15 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/PythonCoverageEditorSystemComponent.cpp
Source/PythonCoverageEditorSystemComponent.h
)

@ -0,0 +1,15 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/PythonCoverageEditorModule.cpp
Source/PythonCoverageEditorModule.h
)

@ -0,0 +1,13 @@
{
"gem_name": "PythonCoverage",
"display_name": "PythonCoverage",
"summary": "A tool for generating gem coverage for Python tests.",
"canonical_tags": [
"Gem"
],
"user_tags": [
"PythonCoverage"
],
"icon_path": "preview.png",
"restricted_name": "gems"
}

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d6204c6730e5675791765ca194e9b1cbec282208e280507de830afc2805e5fa
size 41127

@ -49,7 +49,7 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
logger.debug("Running automated test: {}".format(editor_script))
editor.args.extend(["--skipWelcomeScreenDialog", "--regset=/Amazon/Settings/EnableSourceControl=false",
"--regset=/Amazon/Preferences/EnablePrefabSystem=false", run_python, test_case,
"--runpythonargs", " ".join(cfg_args)])
f"--pythontestcase={request.node.originalname}", "--runpythonargs", " ".join(cfg_args)])
if auto_test_mode:
editor.args.extend(["--autotest_mode"])
if null_renderer:

@ -89,7 +89,7 @@ class TestAutomationBase:
editor_starttime = time.time()
self.logger.debug("Running automated test")
testcase_module_filepath = self._get_testcase_module_filepath(testcase_module)
pycmd = ["--runpythontest", testcase_module_filepath, "-BatchMode", "-autotest_mode", "-rhi=null"] + extra_cmdline_args
pycmd = ["--runpythontest", testcase_module_filepath, "-BatchMode", "-autotest_mode", "-rhi=null", f"-pythontestcase={request.node.originalname}"] + extra_cmdline_args
editor.args.extend(pycmd) # args are added to the WinLauncher start command
editor.start(backupFiles = False, launch_ap = False)
try:

@ -24,16 +24,22 @@ namespace AzToolsFramework
//////////////////////////////////////////////////////////////////////////
//! executes a Python script using a string, prints the result if printResult is true and script is an expression
virtual void ExecuteByString(AZStd::string_view script, bool printResult) { AZ_UNUSED(script); AZ_UNUSED(printResult); }
virtual void ExecuteByString([[maybe_unused]] AZStd::string_view script, [[maybe_unused]] bool printResult) {}
//! executes a Python script using a filename
virtual void ExecuteByFilename(AZStd::string_view filename) { AZ_UNUSED(filename); }
virtual void ExecuteByFilename([[maybe_unused]] AZStd::string_view filename) {}
//! executes a Python script using a filename and args
virtual void ExecuteByFilenameWithArgs(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) { AZ_UNUSED(filename); AZ_UNUSED(args); }
virtual void ExecuteByFilenameWithArgs(
[[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args) {}
//! executes a Python script as a test
virtual void ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) { AZ_UNUSED(filename); AZ_UNUSED(args); }
virtual void ExecuteByFilenameAsTest(
[[maybe_unused]] AZStd::string_view filename,
[[maybe_unused]] AZStd::string_view testCase,
[[maybe_unused]] const AZStd::vector<AZStd::string_view>& args)
{
}
};
using EditorPythonRunnerRequestBus = AZ::EBus<EditorPythonRunnerRequests>;

@ -0,0 +1,45 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/EBus/EBus.h>
#include <AzCore/std/string/string.h>
namespace AzToolsFramework
{
//! Provides a bus to notify when Python scripts are about to run.
class EditorPythonScriptNotifications
: public AZ::EBusTraits
{
public:
//////////////////////////////////////////////////////////////////////////
// EBusTraits overrides
static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Multiple;
static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single;
//////////////////////////////////////////////////////////////////////////
//! Notifies the start of execution of a Python script using a string.
virtual void OnStartExecuteByString([[maybe_unused]] AZStd::string_view script) {}
//! Notifies the start of execution of a Python script using a filename.
virtual void OnStartExecuteByFilename([[maybe_unused]] AZStd::string_view filename) {}
//! Notifies the start of execution of a Python script using a filename and args.
virtual void OnStartExecuteByFilenameWithArgs(
[[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args) {}
//! Notifies the start of execution of a Python script as a test.
virtual void OnStartExecuteByFilenameAsTest(
[[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector<AZStd::string_view>& args) {}
};
using EditorPythonScriptNotificationsBus = AZ::EBus<EditorPythonScriptNotifications>;
}

@ -180,10 +180,11 @@ namespace AzToolsFramework
void AssetBrowserTreeView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
{
// if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially marked for deletion
if (selectionModel() && selectionModel()->selectedIndexes().size() == 1)
// if selected entry is being removed, clear selection so not to select (and attempt to preview) other entries potentially
// marked for deletion
if (selectionModel() && selectedIndexes().size() == 1)
{
QModelIndex selectedIndex = selectionModel()->selectedIndexes().first();
QModelIndex selectedIndex = selectedIndexes().first();
QModelIndex parentSelectedIndex = selectedIndex.parent();
if (parentSelectedIndex == parent && selectedIndex.row() >= start && selectedIndex.row() <= end)
{
@ -193,6 +194,14 @@ namespace AzToolsFramework
QTreeView::rowsAboutToBeRemoved(parent, start, end);
}
// Item data for hidden columns normally isn't copied by Qt during drag-and-drop (see QTBUG-30242).
// However, for the AssetBrowser, the hidden columns should get copied. By overriding selectedIndexes() to
// include all selected indices, not just the visible ones, we can get the behavior we're looking for.
QModelIndexList AssetBrowserTreeView::selectedIndexes() const
{
return selectionModel()->selectedIndexes();
}
void AssetBrowserTreeView::SetThumbnailContext(const char* thumbnailContext) const
{
m_delegate->SetThumbnailContext(thumbnailContext);

@ -90,6 +90,9 @@ namespace AzToolsFramework
void selectionChanged(const QItemSelection& selected, const QItemSelection& deselected) override;
void rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end) override;
protected:
QModelIndexList selectedIndexes() const override;
private:
QPointer<AssetBrowserModel> m_assetBrowserModel = nullptr;
QPointer<AssetBrowserFilterModel> m_assetBrowserSortFilterProxyModel = nullptr;

@ -37,6 +37,7 @@ set(FILES
API/EditorVegetationRequestsBus.h
API/EditorPythonConsoleBus.h
API/EditorPythonRunnerRequestsBus.h
API/EditorPythonScriptNotificationsBus.h
API/EntityPropertyEditorRequestsBus.h
API/EditorWindowRequestBus.h
API/EntityCompositionRequestBus.h

@ -532,6 +532,7 @@ public:
QString m_appRoot;
QString m_logFile;
QString m_pythonArgs;
QString m_pythontTestCase;
QString m_execFile;
QString m_execLineCmd;
@ -578,6 +579,7 @@ public:
const std::vector<std::pair<CommandLineStringOption, QString&> > stringOptions = {
{{"logfile", "File name of the log file to write out to.", "logfile"}, m_logFile},
{{"runpythonargs", "Command-line argument string to pass to the python script if --runpython or --runpythontest was used.", "runpythonargs"}, m_pythonArgs},
{{"pythontestcase", "Test case name of python test script if --runpythontest was used.", "pythontestcase"}, m_pythontTestCase},
{{"exec", "cfg file to run on startup, used for systems like automation", "exec"}, m_execFile},
{{"rhi", "Command-line argument to force which rhi to use", "dummyString"}, dummyString },
{{"rhi-device-validation", "Command-line argument to configure rhi validation", "dummyString"}, dummyString },
@ -1516,7 +1518,13 @@ void CCryEditApp::RunInitPythonScript(CEditCommandLineInfo& cmdInfo)
std::transform(tokens.begin(), tokens.end(), std::back_inserter(pythonArgs), [](auto& tokenData) { return tokenData.c_str(); });
if (cmdInfo.m_bRunPythonTestScript)
{
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilenameAsTest, cmdInfo.m_strFileName.toUtf8().constData(), pythonArgs);
AZStd::string pythonTestCase;
if (!cmdInfo.m_pythontTestCase.isEmpty())
{
pythonTestCase = cmdInfo.m_pythontTestCase.toUtf8().constData();
}
EditorPythonRunnerRequestBus::Broadcast(&EditorPythonRunnerRequestBus::Events::ExecuteByFilenameAsTest, cmdInfo.m_strFileName.toUtf8().constData(), pythonTestCase, pythonArgs);
// Close the editor gracefully as the test has completed
GetIEditor()->GetDocument()->SetModifiedFlag(false);

@ -15,6 +15,9 @@ ly_add_target(
Include
PRIVATE
Source
COMPILE_DEFINITIONS
PRIVATE
${LY_TEST_IMPACT_CONFIG_FILE_PATH_DEFINITION}
BUILD_DEPENDENCIES
PUBLIC
AZ::TestImpact.Runtime.Static

@ -61,30 +61,7 @@ namespace AZ
RawStringList requiredLayers = GetRequiredLayers();
RawStringList requiredExtensions = GetRequiredExtensions();
StringList deviceExtensions = physicalDevice.GetDeviceExtensionNames();
RawStringList optionalDeviceExtensions = { {
VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME,
VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,
VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME,
VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
// ray tracing extensions
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
VK_KHR_SPIRV_1_4_EXTENSION_NAME,
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
} };
RawStringList optionalExtensions = FilterList(optionalDeviceExtensions, deviceExtensions);
RawStringList optionalExtensions = physicalDevice.FilterSupportedOptionalExtensions();
requiredExtensions.insert(requiredExtensions.end(), optionalExtensions.begin(), optionalExtensions.end());
//We now need to find the queues that the physical device has available and make sure
@ -159,7 +136,7 @@ namespace AZ
// unbounded array functionality
VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptorIndexingFeatures = {};
descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES;
descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
const VkPhysicalDeviceDescriptorIndexingFeaturesEXT& physicalDeviceDescriptorIndexingFeatures =
physicalDevice.GetPhysicalDeviceDescriptorIndexingFeatures();
descriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing = physicalDeviceDescriptorIndexingFeatures.shaderInputAttachmentArrayDynamicIndexing;
@ -176,6 +153,15 @@ namespace AZ
descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount = physicalDeviceDescriptorIndexingFeatures.descriptorBindingVariableDescriptorCount;
descriptorIndexingFeatures.runtimeDescriptorArray = physicalDeviceDescriptorIndexingFeatures.runtimeDescriptorArray;
VkPhysicalDeviceBufferDeviceAddressFeaturesEXT bufferDeviceAddressFeatures = {};
bufferDeviceAddressFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT;
const VkPhysicalDeviceBufferDeviceAddressFeaturesEXT& physicalDeviceBufferDeviceAddressFeatures =
physicalDevice.GetPhysicalDeviceBufferDeviceAddressFeatures();
bufferDeviceAddressFeatures.bufferDeviceAddress = physicalDeviceBufferDeviceAddressFeatures.bufferDeviceAddress;
bufferDeviceAddressFeatures.bufferDeviceAddressCaptureReplay = physicalDeviceBufferDeviceAddressFeatures.bufferDeviceAddressCaptureReplay;
bufferDeviceAddressFeatures.bufferDeviceAddressMultiDevice = physicalDeviceBufferDeviceAddressFeatures.bufferDeviceAddressMultiDevice;
descriptorIndexingFeatures.pNext = &bufferDeviceAddressFeatures;
VkPhysicalDeviceDepthClipEnableFeaturesEXT depthClipEnabled = {};
depthClipEnabled.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
depthClipEnabled.depthClipEnable = physicalDevice.GetPhysicalDeviceDepthClipEnableFeatures().depthClipEnable;
@ -184,7 +170,7 @@ namespace AZ
VkPhysicalDeviceRobustness2FeaturesEXT robustness2 = {};
robustness2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT;
robustness2.nullDescriptor = physicalDevice.GetPhysicalDeviceRobutness2Features().nullDescriptor;
depthClipEnabled.pNext = &robustness2;
bufferDeviceAddressFeatures.pNext = &robustness2;
VkPhysicalDeviceVulkan12Features vulkan12Features = {};
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR float16Int8 = {};
@ -195,6 +181,7 @@ namespace AZ
if (majorVersion >= 1 && minorVersion >= 2)
{
vulkan12Features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;
VkPhysicalDeviceVulkan12Features physicalDeviceVulkan12Features = physicalDevice.GetPhysicalDeviceVulkan12Features();
vulkan12Features.drawIndirectCount = physicalDevice.GetPhysicalDeviceVulkan12Features().drawIndirectCount;
vulkan12Features.shaderFloat16 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderFloat16;
vulkan12Features.shaderInt8 = physicalDevice.GetPhysicalDeviceVulkan12Features().shaderInt8;
@ -795,6 +782,27 @@ namespace AZ
return *m_nullDescriptorManager;
}
VkBufferUsageFlags Device::GetBufferUsageFlagBitsUnderRestrictions(RHI::BufferBindFlags bindFlags) const
{
VkBufferUsageFlags bufferUsageFlags = GetBufferUsageFlagBits(bindFlags);
const auto& physicalDevice = static_cast<const PhysicalDevice&>(GetPhysicalDevice());
// VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT require bufferDeviceAddress enabled.
if (!physicalDevice.GetPhysicalDeviceBufferDeviceAddressFeatures().bufferDeviceAddress)
{
bufferUsageFlags &= ~VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT;
}
// VK_KHR_acceleration_structure provides VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR
// Otherwise unrecognized flag.
if (!physicalDevice.IsOptionalDeviceExtensionSupported(OptionalDeviceExtension::AccelerationStructure))
{
bufferUsageFlags &= ~VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR;
}
return bufferUsageFlags;
}
VkBuffer Device::CreateBufferResouce(const RHI::BufferDescriptor& descriptor) const
{
AZ_Assert(descriptor.m_sharedQueueMask != RHI::HardwareQueueClassMask::None, "Invalid shared queue mask");
@ -805,9 +813,14 @@ namespace AZ
createInfo.pNext = nullptr;
createInfo.flags = 0;
createInfo.size = descriptor.m_byteCount;
createInfo.usage = GetBufferUsageFlagBits(descriptor.m_bindFlags);
createInfo.usage = GetBufferUsageFlagBitsUnderRestrictions(descriptor.m_bindFlags);
// Trying to guess here if the buffers are going to be used as attachments. Maybe it would be better to add an explicit flag in the descriptor.
createInfo.sharingMode = RHI::CheckBitsAny(descriptor.m_bindFlags, RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::Indirect) ? VK_SHARING_MODE_EXCLUSIVE : VK_SHARING_MODE_CONCURRENT;
createInfo.sharingMode =
RHI::CheckBitsAny(
descriptor.m_bindFlags,
RHI::BufferBindFlags::ShaderWrite | RHI::BufferBindFlags::Predication | RHI::BufferBindFlags::Indirect)
? VK_SHARING_MODE_EXCLUSIVE
: VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = static_cast<uint32_t>(queueFamilies.size());
createInfo.pQueueFamilyIndices = queueFamilies.empty() ? nullptr : queueFamilies.data();

@ -144,6 +144,10 @@ namespace AZ
template <typename ObjectType, typename... Args>
RHI::Ptr<ObjectType> AcquireObjectFromCache(ObjectCache<ObjectType>& cache, const size_t hash, Args... args);
//! Get the vulkan buffer usage flags from buffer bind flags.
//! Flags will be corrected if required features or extensions are not enabled.
VkBufferUsageFlags GetBufferUsageFlagBitsUnderRestrictions(RHI::BufferBindFlags bindFlags) const;
VkDevice m_nativeDevice = VK_NULL_HANDLE;
VkPhysicalDeviceFeatures m_enabledDeviceFeatures{};
VkPipelineStageFlags m_supportedPipelineStageFlagsMask = ~0;

@ -128,6 +128,11 @@ namespace AZ
return m_descriptorIndexingFeatures;
}
const VkPhysicalDeviceBufferDeviceAddressFeaturesEXT& PhysicalDevice::GetPhysicalDeviceBufferDeviceAddressFeatures() const
{
return m_bufferDeviceAddressFeatures;
}
const VkPhysicalDeviceVulkan12Features& PhysicalDevice::GetPhysicalDeviceVulkan12Features() const
{
return m_vulkan12Features;
@ -229,6 +234,56 @@ namespace AZ
(m_separateDepthStencilFeatures.separateDepthStencilLayouts && VK_DEVICE_EXTENSION_SUPPORTED(KHR_separate_depth_stencil_layouts)) ||
(m_vulkan12Features.separateDepthStencilLayouts));
m_features.set(static_cast<size_t>(DeviceFeature::DescriptorIndexing), VK_DEVICE_EXTENSION_SUPPORTED(EXT_descriptor_indexing));
m_features.set(static_cast<size_t>(DeviceFeature::BufferDeviceAddress), VK_DEVICE_EXTENSION_SUPPORTED(EXT_buffer_device_address));
}
RawStringList PhysicalDevice::FilterSupportedOptionalExtensions()
{
// The order must match the enum OptionalDeviceExtensions
RawStringList optionalExtensions = { {
VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME,
VK_EXT_CONDITIONAL_RENDERING_EXTENSION_NAME,
VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,
VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME,
VK_KHR_DRAW_INDIRECT_COUNT_EXTENSION_NAME,
VK_KHR_RELAXED_BLOCK_LAYOUT_EXTENSION_NAME,
VK_EXT_ROBUSTNESS_2_EXTENSION_NAME,
VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
// ray tracing extensions
VK_KHR_ACCELERATION_STRUCTURE_EXTENSION_NAME,
VK_KHR_RAY_TRACING_PIPELINE_EXTENSION_NAME,
VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,
VK_KHR_DEFERRED_HOST_OPERATIONS_EXTENSION_NAME,
VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,
VK_KHR_SPIRV_1_4_EXTENSION_NAME,
VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME
} };
uint32_t optionalExtensionCount = sizeof(optionalExtensions) / sizeof(VK_EXT_SAMPLE_LOCATIONS_EXTENSION_NAME);
AZ_Assert(optionalExtensionCount == static_cast<uint32_t>(OptionalDeviceExtension::Count), "The order and size must match the enum OptionalDeviceExtensions.");
// Optional device extensions are filtered based on what the device support.
// It returns in the same order as in the original list.
StringList deviceExtensions = GetDeviceExtensionNames();
RawStringList filteredOptionalExtensions = FilterList(optionalExtensions, deviceExtensions);
// Mark the supported optional extensions in the bitset for faster look up compared to string search.
uint32_t originalIndex = 0;
for (const auto& extension : filteredOptionalExtensions)
{
AZ_Assert(originalIndex < optionalExtensionCount, "Out of range index. Check FilterList algorithm if list is returned in the original order.");
while (strcmp(extension, optionalExtensions[originalIndex]) != 0)
{
++originalIndex;
}
m_optionalExtensions.set(originalIndex);
++originalIndex;
}
return filteredOptionalExtensions;
}
void PhysicalDevice::CompileMemoryStatistics(RHI::MemoryStatisticsBuilder& builder) const
@ -266,10 +321,15 @@ namespace AZ
descriptorIndexingFeatures = {};
descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
VkPhysicalDeviceBufferDeviceAddressFeaturesEXT& bufferDeviceAddressFeatures = m_bufferDeviceAddressFeatures;
bufferDeviceAddressFeatures = {};
bufferDeviceAddressFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_BUFFER_DEVICE_ADDRESS_FEATURES_EXT;
descriptorIndexingFeatures.pNext = &bufferDeviceAddressFeatures;
VkPhysicalDeviceDepthClipEnableFeaturesEXT& dephClipEnableFeatures = m_dephClipEnableFeatures;
dephClipEnableFeatures = {};
dephClipEnableFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DEPTH_CLIP_ENABLE_FEATURES_EXT;
descriptorIndexingFeatures.pNext = &dephClipEnableFeatures;
bufferDeviceAddressFeatures.pNext = &dephClipEnableFeatures;
VkPhysicalDeviceRobustness2FeaturesEXT& robustness2Feature = m_robutness2Features;
robustness2Feature = {};
@ -397,5 +457,11 @@ namespace AZ
return m_features.test(index);
}
bool PhysicalDevice::IsOptionalDeviceExtensionSupported(OptionalDeviceExtension optionalDeviceExtension) const
{
uint32_t index = static_cast<uint32_t>(optionalDeviceExtension);
AZ_Assert(index < m_optionalExtensions.size(), "Invalid feature %d", index);
return m_optionalExtensions.test(index);
}
}
}

@ -30,9 +30,31 @@ namespace AZ
NullDescriptor,
SeparateDepthStencil,
DescriptorIndexing,
BufferDeviceAddress,
Count // Must be last
};
enum class OptionalDeviceExtension : uint32_t
{
SampleLocation = 0,
ConditionalRendering,
MemoryBudget,
DepthClipEnable,
ConservativeRasterization,
DrawIndirectCount,
RelaxedBlockLayout,
Robustness2,
ShaderFloat16Int8,
AccelerationStructure,
RayTracingPipeline,
BufferDeviceAddress,
DeferredHostOperations,
DescriptorIndexing,
Spirv14,
ShaderFloatControls,
Count
};
class PhysicalDevice final
: public RHI::PhysicalDevice
{
@ -46,6 +68,7 @@ namespace AZ
const VkPhysicalDevice& GetNativePhysicalDevice() const;
const VkPhysicalDeviceMemoryProperties& GetMemoryProperties() const;
bool IsFeatureSupported(DeviceFeature feature) const;
bool IsOptionalDeviceExtensionSupported(OptionalDeviceExtension optionalDeviceExtension) const;
const VkPhysicalDeviceLimits& GetDeviceLimits() const;
const VkPhysicalDeviceFeatures& GetPhysicalDeviceFeatures() const;
const VkPhysicalDeviceProperties& GetPhysicalDeviceProperties() const;
@ -54,6 +77,7 @@ namespace AZ
const VkPhysicalDeviceRobustness2FeaturesEXT& GetPhysicalDeviceRobutness2Features() const;
const VkPhysicalDeviceShaderFloat16Int8FeaturesKHR& GetPhysicalDeviceFloat16Int8Features() const;
const VkPhysicalDeviceDescriptorIndexingFeaturesEXT& GetPhysicalDeviceDescriptorIndexingFeatures() const;
const VkPhysicalDeviceBufferDeviceAddressFeaturesEXT& GetPhysicalDeviceBufferDeviceAddressFeatures() const;
const VkPhysicalDeviceVulkan12Features& GetPhysicalDeviceVulkan12Features() const;
const VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR& GetPhysicalDeviceSeparateDepthStencilFeatures() const;
const VkPhysicalDeviceAccelerationStructurePropertiesKHR& GetPhysicalDeviceAccelerationStructureProperties() const;
@ -63,6 +87,8 @@ namespace AZ
StringList GetDeviceExtensionNames(const char* layerName = nullptr) const;
bool IsFormatSupported(RHI::Format format, VkImageTiling tiling, VkFormatFeatureFlags features) const;
void LoadSupportedFeatures();
//! Filter optional extensions based on what the physics device support.
RawStringList FilterSupportedOptionalExtensions();
void CompileMemoryStatistics(RHI::MemoryStatisticsBuilder& builder) const;
private:
@ -77,6 +103,7 @@ namespace AZ
VkPhysicalDevice m_vkPhysicalDevice = VK_NULL_HANDLE;
VkPhysicalDeviceMemoryProperties m_memoryProperty{};
AZStd::bitset<static_cast<uint32_t>(OptionalDeviceExtension::Count)> m_optionalExtensions;
AZStd::bitset<static_cast<uint32_t>(DeviceFeature::Count)> m_features;
VkPhysicalDeviceFeatures m_deviceFeatures{};
VkPhysicalDeviceProperties m_deviceProperties{};
@ -85,6 +112,7 @@ namespace AZ
VkPhysicalDeviceRobustness2FeaturesEXT m_robutness2Features{};
VkPhysicalDeviceShaderFloat16Int8FeaturesKHR m_float16Int8Features{};
VkPhysicalDeviceDescriptorIndexingFeaturesEXT m_descriptorIndexingFeatures{};
VkPhysicalDeviceBufferDeviceAddressFeaturesEXT m_bufferDeviceAddressFeatures{};
VkPhysicalDeviceSeparateDepthStencilLayoutsFeaturesKHR m_separateDepthStencilFeatures{};
VkPhysicalDeviceAccelerationStructurePropertiesKHR m_accelerationStructureProperties{};
VkPhysicalDeviceRayTracingPipelinePropertiesKHR m_rayTracingPipelineProperties{};

@ -34,6 +34,7 @@
#include <AzFramework/StringFunc/StringFunc.h>
#include <AzToolsFramework/API/EditorPythonConsoleBus.h>
#include <AzToolsFramework/API/EditorPythonScriptNotificationsBus.h>
namespace Platform
{
@ -575,6 +576,9 @@ namespace EditorPythonBindings
if (!script.empty())
{
AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast(
&AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnStartExecuteByString, script);
// Acquire GIL before calling Python code
AZStd::lock_guard<decltype(m_lock)> lock(m_lock);
pybind11::gil_scoped_acquire acquire;
@ -635,11 +639,15 @@ namespace EditorPythonBindings
void PythonSystemComponent::ExecuteByFilename(AZStd::string_view filename)
{
AZStd::vector<AZStd::string_view> args;
AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast(
&AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnStartExecuteByFilename, filename);
ExecuteByFilenameWithArgs(filename, args);
}
void PythonSystemComponent::ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args)
void PythonSystemComponent::ExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector<AZStd::string_view>& args)
{
AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast(
&AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnStartExecuteByFilenameAsTest, filename, testCase, args);
const Result evalResult = EvaluateFile(filename, args);
if (evalResult == Result::Okay)
{
@ -655,6 +663,8 @@ namespace EditorPythonBindings
void PythonSystemComponent::ExecuteByFilenameWithArgs(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args)
{
AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast(
&AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnStartExecuteByFilenameWithArgs, filename, args);
EvaluateFile(filename, args);
}

@ -53,7 +53,7 @@ namespace EditorPythonBindings
void ExecuteByString(AZStd::string_view script, bool printResult) override;
void ExecuteByFilename(AZStd::string_view filename) override;
void ExecuteByFilenameWithArgs(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) override;
void ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector<AZStd::string_view>& args) override;
void ExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector<AZStd::string_view>& args) override;
////////////////////////////////////////////////////////////////////////
private:

@ -70,6 +70,9 @@
},
"test_target_meta": {
"file": "${test_target_type_file}"
},
"gem_target": {
"file": "${gem_target_file}"
}
}
},
@ -87,122 +90,6 @@
],
"shard": [
{
"policy": "fixture_contiguous",
"target": "AzCore.Tests"
},
{
"policy": "fixture_contiguous",
"target": "AzToolsFramework.Tests"
},
{
"policy": "test_interleaved",
"target": "Framework.Tests"
},
{
"policy": "test_interleaved",
"target": "LmbrCentral.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "EditorLib.Tests"
},
{
"policy": "test_interleaved",
"target": "PhysX.Tests"
},
{
"policy": "test_interleaved",
"target": "ImageProcessing.Tests"
},
{
"policy": "test_interleaved",
"target": "Atom_RPI.Tests"
},
{
"policy": "test_interleaved",
"target": "Atom_RHI.Tests"
},
{
"policy": "test_interleaved",
"target": "AzManipulatorFramework.Tests"
},
{
"policy": "test_interleaved",
"target": "WhiteBox.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "ImageProcessing.Tests"
},
{
"policy": "test_interleaved",
"target": "AzManipulatorTestFramework.Tests"
},
{
"policy": "test_interleaved",
"target": "AtomCore.Tests"
},
{
"policy": "test_interleaved",
"target": "ImageProcessingAtom.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "EditorPythonBindings.Tests"
},
{
"policy": "test_interleaved",
"target": "Atom_Utils.Tests"
},
{
"policy": "test_interleaved",
"target": "AudioEngineWwise.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "Multiplayer.Tests"
},
{
"policy": "test_interleaved",
"target": "LmbrCentral.Tests"
},
{
"policy": "fixture_contiguous",
"target": "LyMetricsShared.Tests"
},
{
"policy": "test_interleaved",
"target": "PhysX.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "ComponentEntityEditorPlugin.Tests"
},
{
"policy": "test_interleaved",
"target": "DeltaCataloger.Tests"
},
{
"policy": "test_interleaved",
"target": "GradientSignal.Tests"
},
{
"policy": "test_interleaved",
"target": "LyShine.Tests"
},
{
"policy": "test_interleaved",
"target": "EMotionFX.Editor.Tests"
},
{
"policy": "test_interleaved",
"target": "EMotionFX.Tests"
},
{
"policy": "test_interleaved",
"target": "CrySystem.Tests"
}
]
}
}

@ -0,0 +1,7 @@
{
"gems": {
[
${enumerated_gem_targets}
]
}
}

@ -14,15 +14,21 @@ option(LY_TEST_IMPACT_INSTRUMENTATION_BIN "Path to test impact framework instrum
# Name of test impact framework console static library target
set(LY_TEST_IMPACT_CONSOLE_STATIC_TARGET "TestImpact.Frontend.Console.Static")
# Name of test impact framework python coverage gem target
set(LY_TEST_IMPACT_PYTHON_COVERAGE_STATIC_TARGET "PythonCoverage.Editor.Static")
# Name of test impact framework console target
set(LY_TEST_IMPACT_CONSOLE_TARGET "TestImpact.Frontend.Console")
# Directory for non-persistent test impact data trashed with each generation of build system
# Directory for test impact artifacts and data
set(LY_TEST_IMPACT_WORKING_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/TestImpactFramework")
# Directory for temporary files generated at runtime
# Directory for artifacts generated at runtime
set(LY_TEST_IMPACT_TEMP_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Temp")
# Directory for files that persist between runtime runs
set(LY_TEST_IMPACT_PERSISTENT_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Persistent")
# Directory for static artifacts produced as part of the build system generation process
set(LY_TEST_IMPACT_ARTIFACT_DIR "${LY_TEST_IMPACT_WORKING_DIR}/Artifact")
@ -32,9 +38,18 @@ set(LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/Map
# Directory for build target dependency/depender graphs
set(LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR "${LY_TEST_IMPACT_ARTIFACT_DIR}/Dependency")
# Master test enumeration file for all test types
# Main test enumeration file for all test types
set(LY_TEST_IMPACT_TEST_TYPE_FILE "${LY_TEST_IMPACT_ARTIFACT_DIR}/TestType/All.tests")
# Main gem target file for all shared library gems
set(LY_TEST_IMPACT_GEM_TARGET_FILE "${LY_TEST_IMPACT_ARTIFACT_DIR}/BuildType/All.gems")
# Path to the config file for each build configuration
set(LY_TEST_IMPACT_CONFIG_FILE_PATH "${LY_TEST_IMPACT_PERSISTENT_DIR}/tiaf.$<CONFIG>.json")
# Preprocessor directive for the config file path
set(LY_TEST_IMPACT_CONFIG_FILE_PATH_DEFINITION "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${LY_TEST_IMPACT_CONFIG_FILE_PATH}\"")
#! ly_test_impact_rebase_file_to_repo_root: rebases the relative and/or absolute path to be relative to repo root directory and places the resulting path in quotes.
#
# \arg:INPUT_FILE the file to rebase
@ -208,7 +223,7 @@ function(ly_test_impact_extract_python_test_params COMPOSITE_TEST COMPOSITE_SUIT
set(${TEST_SUITES} ${test_suites} PARENT_SCOPE)
endfunction()
#! ly_test_impact_write_test_enumeration_file: exports the master test lists to file.
#! ly_test_impact_write_test_enumeration_file: exports the main test list to file.
#
# \arg:TEST_ENUMERATION_TEMPLATE_FILE path to test enumeration template file
function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FILE)
@ -258,6 +273,33 @@ function(ly_test_impact_write_test_enumeration_file TEST_ENUMERATION_TEMPLATE_FI
configure_file(${TEST_ENUMERATION_TEMPLATE_FILE} ${LY_TEST_IMPACT_TEST_TYPE_FILE})
endfunction()
#! ly_test_impact_write_gem_target_enumeration_file: exports the main gem target list to file.
#
# \arg:GEM_TARGET_TEMPLATE_FILE path to source to gem target template file
function(ly_test_impact_write_gem_target_enumeration_file GEM_TARGET_TEMPLATE_FILE)
get_property(LY_ALL_TARGETS GLOBAL PROPERTY LY_ALL_TARGETS)
set(enumerated_gem_targets "")
# Walk the build targets
foreach(aliased_target ${LY_ALL_TARGETS})
unset(target)
ly_de_alias_target(${aliased_target} target)
get_target_property(gem_module ${target} GEM_MODULE)
get_target_property(target_type ${target} TYPE)
if("${gem_module}" STREQUAL "TRUE")
if("${target_type}" STREQUAL "SHARED_LIBRARY" OR "${target_type}" STREQUAL "MODULE_LIBRARY")
list(APPEND enumerated_gem_targets " \"${target}\"")
endif()
endif()
endforeach()
string (REPLACE ";" ",\n" enumerated_gem_targets "${enumerated_gem_targets}")
# Write out source to target mapping file
set(mapping_path "${LY_TEST_IMPACT_GEM_TARGET_FILE}")
configure_file(${GEM_TARGET_TEMPLATE_FILE} ${mapping_path})
endfunction()
#! ly_test_impact_export_source_target_mappings: exports the static source to target mappings to file.
#
# \arg:MAPPING_TEMPLATE_FILE path to source to target template file
@ -269,6 +311,7 @@ function(ly_test_impact_export_source_target_mappings MAPPING_TEMPLATE_FILE)
unset(target)
ly_de_alias_target(${aliased_target} target)
message(TRACE "Exporting static source file mappings for ${target}")
# Target name and path relative to root
@ -329,9 +372,8 @@ endfunction()
#! ly_test_impact_write_config_file: writes out the test impact framework config file using the data derived from the build generation process.
#
# \arg:CONFIG_TEMPLATE_FILE path to the runtime configuration template file
# \arg:PERSISTENT_DATA_DIR path to the test impact framework persistent data directory
# \arg:BIN_DIR path to repo binary output directory
function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_DIR BIN_DIR)
function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE BIN_DIR)
# Platform this config file is being generated for
set(platform ${PAL_PLATFORM_NAME})
@ -362,10 +404,10 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D
set(temp_dir "${LY_TEST_IMPACT_TEMP_DIR}")
# Active persistent data dir
set(active_dir "${PERSISTENT_DATA_DIR}/active")
set(active_dir "${LY_TEST_IMPACT_PERSISTENT_DIR}/active")
# Historic persistent data dir
set(historic_dir "${PERSISTENT_DATA_DIR}/historic")
set(historic_dir "${LY_TEST_IMPACT_PERSISTENT_DIR}/historic")
# Source to target mappings dir
set(source_target_mapping_dir "${LY_TEST_IMPACT_SOURCE_TARGET_MAPPING_DIR}")
@ -373,6 +415,9 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D
# Test type artifact file
set(test_target_type_file "${LY_TEST_IMPACT_TEST_TYPE_FILE}")
# Gem target file
set(gem_target_file "${LY_TEST_IMPACT_GEM_TARGET_FILE}")
# Build dependency artifact dir
set(target_dependency_dir "${LY_TEST_IMPACT_TARGET_DEPENDENCY_DIR}")
@ -385,12 +430,10 @@ function(ly_test_impact_write_config_file CONFIG_TEMPLATE_FILE PERSISTENT_DATA_D
# Write out entire config contents to a file in the build directory of the test impact framework console target
file(GENERATE
OUTPUT "${PERSISTENT_DATA_DIR}/$<TARGET_FILE_BASE_NAME:${LY_TEST_IMPACT_CONSOLE_TARGET}>.$<CONFIG>.json"
OUTPUT "${LY_TEST_IMPACT_CONFIG_FILE_PATH}"
CONTENT ${config_file}
)
# Set the above config file as the default config file to use for the test impact framework console target
target_compile_definitions(${LY_TEST_IMPACT_CONSOLE_STATIC_TARGET} PUBLIC "LY_TEST_IMPACT_DEFAULT_CONFIG_FILE=\"${PERSISTENT_DATA_DIR}/$<TARGET_FILE_BASE_NAME:${LY_TEST_IMPACT_CONSOLE_TARGET}>.$<CONFIG>.json\"")
message(DEBUG "Test impact framework post steps complete")
endfunction()
@ -400,13 +443,11 @@ function(ly_test_impact_post_step)
return()
endif()
# Directory per build config for persistent test impact data (to be checked in)
set(persistent_data_dir "${LY_TEST_IMPACT_WORKING_DIR}/persistent")
# Directory for binaries built for this profile
set(bin_dir "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/$<CONFIG>")
# Erase any existing non-persistent data to avoid getting test impact framework out of sync with current repo state
file(REMOVE_RECURSE "${LY_TEST_IMPACT_WORKING_DIR}")
file(REMOVE_RECURSE "${LY_TEST_IMPACT_TEMP_DIR}")
# Export the soruce to target mapping files
ly_test_impact_export_source_target_mappings(
@ -418,10 +459,14 @@ function(ly_test_impact_post_step)
"cmake/TestImpactFramework/EnumeratedTests.in"
)
# Export the enumerated gems
ly_test_impact_write_gem_target_enumeration_file(
"cmake/TestImpactFramework/EnumeratedGemTargets.in"
)
# Write out the configuration file
ly_test_impact_write_config_file(
"cmake/TestImpactFramework/ConsoleFrontendConfig.in"
${persistent_data_dir}
${bin_dir}
)

@ -476,7 +476,7 @@ try {
}
} else {
// Non-PR builds
choice(defaultValue: DEFAULT_BUILD_SNAPSHOT, name: 'SNAPSHOT', choices: BUILD_SNAPSHOTS, description: 'Selects the build snapshot to use. A more diverted snapshot will cause longer build times, but will not cause build failures.')
pipelineParameters.add(choice(defaultValue: DEFAULT_BUILD_SNAPSHOT, name: 'SNAPSHOT', choices: BUILD_SNAPSHOTS, description: 'Selects the build snapshot to use. A more diverted snapshot will cause longer build times, but will not cause build failures.'))
snapshot = env.SNAPSHOT
echo "Snapshot \"${snapshot}\" selected."
}

@ -33,7 +33,7 @@
],
"steps": [
"profile_vs2019",
"test_impact_analysis",
"test_impact_analysis_profile_vs2019",
"asset_profile_vs2019",
"test_cpu_profile_vs2019"
]
@ -82,13 +82,15 @@
"SCRIPT_PARAMETERS": "--platform 3rdParty --type 3rdParty_all"
}
},
"test_impact_analysis": {
"test_impact_analysis_profile_vs2019": {
"TAGS": [
],
"COMMAND": "python_windows.cmd",
"PARAMETERS": {
"OUTPUT_DIRECTORY": "build/windows_vs2019",
"CONFIGURATION": "profile",
"SCRIPT_PATH": "scripts/build/TestImpactAnalysis/tiaf_driver.py",
"SCRIPT_PARAMETERS": "--testFailurePolicy=continue --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"build\\windows_vs2019\\bin\\TestImpactFramework\\persistent\\tiaf.profile.json\""
"SCRIPT_PARAMETERS": "--testFailurePolicy=continue --suite main --pipeline !PIPELINE_NAME! --destCommit !CHANGE_ID! --config \"!OUTPUT_DIRECTORY!/bin/TestImpactFramework/persistent/tiaf.profile.json\""
}
},
"debug_vs2019": {

Loading…
Cancel
Save