From 0e0f266fdd248268a5bfdf3d721bc830c9063712 Mon Sep 17 00:00:00 2001 From: John Date: Mon, 21 Jun 2021 16:21:47 +0100 Subject: [PATCH] Provisional implementation of PythonCoverage gem Signed-off-by: John --- AutomatedTesting/Gem/CMakeLists.txt | 1 + AutomatedTesting/Gem/Code/enabled_gems.cmake | 1 + .../Gem/PythonCoverage/CMakeLists.txt | 21 ++ .../Gem/PythonCoverage/Code/CMakeLists.txt | 168 +++++++++++++ .../Code/Platform/Android/PAL_android.cmake | 4 + .../pythoncoverage_android_files.cmake | 8 + ...e_editor_shared_android_files - Copy.cmake | 8 + .../pythoncoverage_shared_android_files.cmake | 8 + .../Code/Platform/Linux/PAL_linux.cmake | 4 + ...oncoverage_editor_shared_linux_files.cmake | 8 + .../Linux/pythoncoverage_linux_files.cmake | 8 + .../pythoncoverage_shared_linux_files.cmake | 8 + .../Code/Platform/Mac/PAL_mac.cmake | 4 + ...thoncoverage_editor_shared_mac_files.cmake | 8 + .../Mac/pythoncoverage_mac_files.cmake | 8 + .../Mac/pythoncoverage_shared_mac_files.cmake | 8 + .../Code/Platform/Windows/PAL_windows.cmake | 4 + .../pythoncoverage_editor_windows_files.cmake | 8 + .../pythoncoverage_shared_windows_files.cmake | 8 + .../pythoncoverage_windows_files.cmake | 8 + .../Code/Platform/iOS/PAL_ios.cmake | 4 + ...thoncoverage_editor_shared_ios_files.cmake | 8 + .../iOS/pythoncoverage_ios_files.cmake | 8 + .../iOS/pythoncoverage_shared_ios_files.cmake | 8 + .../Source/PythonCoverageEditorModule.cpp | 40 +++ .../Code/Source/PythonCoverageEditorModule.h | 31 +++ .../PythonCoverageEditorSystemComponent.cpp | 228 ++++++++++++++++++ .../PythonCoverageEditorSystemComponent.h | 87 +++++++ .../Code/Source/PythonCoverageModule.cpp | 26 ++ .../Code/Source/PythonCoverageModule.h | 26 ++ .../Source/PythonCoverageSystemComponent.cpp | 70 ++++++ .../Source/PythonCoverageSystemComponent.h | 44 ++++ .../Code/Tests/PythonCoverageEditorTest.cpp | 24 ++ .../Code/Tests/PythonCoverageTest.cpp | 24 ++ .../Code/pythoncoverage_editor_files.cmake | 5 + .../pythoncoverage_editor_shared_files.cmake | 7 + .../pythoncoverage_editor_tests_files.cmake | 4 + .../Code/pythoncoverage_files.cmake | 5 + .../Code/pythoncoverage_shared_files.cmake | 5 + .../Code/pythoncoverage_tests_files.cmake | 4 + .../Platform/Android/android_gem.cmake | 1 + .../Platform/Android/android_gem.json | 3 + .../Platform/Linux/linux_gem.cmake | 1 + .../Platform/Linux/linux_gem.json | 3 + .../PythonCoverage/Platform/Mac/mac_gem.cmake | 1 + .../PythonCoverage/Platform/Mac/mac_gem.json | 3 + .../Platform/Windows/windows_gem.cmake | 1 + .../Platform/Windows/windows_gem.json | 3 + .../PythonCoverage/Platform/iOS/ios_gem.cmake | 1 + .../PythonCoverage/Platform/iOS/ios_gem.json | 3 + AutomatedTesting/Gem/PythonCoverage/gem.json | 15 ++ .../Gem/PythonCoverage/preview.png | 3 + .../automatedtesting_shared/base.py | 2 +- .../API/EditorPythonRunnerRequestsBus.h | 14 +- .../API/EditorPythonScriptNotificationsBus.h | 45 ++++ .../aztoolsframework_files.cmake | 1 + Code/Sandbox/Editor/CryEdit.cpp | 10 +- .../Code/Source/PythonSystemComponent.cpp | 12 +- .../Code/Source/PythonSystemComponent.h | 2 +- 59 files changed, 1077 insertions(+), 8 deletions(-) create mode 100644 AutomatedTesting/Gem/PythonCoverage/CMakeLists.txt create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/CMakeLists.txt create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/PAL_android.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_android_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_editor_shared_android_files - Copy.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_shared_android_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/PAL_linux.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_editor_shared_linux_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_linux_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_shared_linux_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/PAL_mac.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_editor_shared_mac_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_mac_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_shared_mac_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/PAL_windows.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_editor_windows_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_shared_windows_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_windows_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/PAL_ios.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_editor_shared_ios_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_ios_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_shared_ios_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.h create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.h create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.h create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.h create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageEditorTest.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageTest.cpp create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_shared_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_tests_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_shared_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_tests_files.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.cmake create mode 100644 AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/gem.json create mode 100644 AutomatedTesting/Gem/PythonCoverage/preview.png create mode 100644 Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonScriptNotificationsBus.h diff --git a/AutomatedTesting/Gem/CMakeLists.txt b/AutomatedTesting/Gem/CMakeLists.txt index 5d881f3f95..990fa72222 100644 --- a/AutomatedTesting/Gem/CMakeLists.txt +++ b/AutomatedTesting/Gem/CMakeLists.txt @@ -11,3 +11,4 @@ add_subdirectory(Code) add_subdirectory(PythonTests) +add_subdirectory(PythonCoverage) diff --git a/AutomatedTesting/Gem/Code/enabled_gems.cmake b/AutomatedTesting/Gem/Code/enabled_gems.cmake index d99d17b55e..4f04e61f2a 100644 --- a/AutomatedTesting/Gem/Code/enabled_gems.cmake +++ b/AutomatedTesting/Gem/Code/enabled_gems.cmake @@ -55,4 +55,5 @@ set(ENABLED_GEMS AWSCore AWSClientAuth AWSMetrics + PythonCoverage ) diff --git a/AutomatedTesting/Gem/PythonCoverage/CMakeLists.txt b/AutomatedTesting/Gem/PythonCoverage/CMakeLists.txt new file mode 100644 index 0000000000..a753914c4b --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/CMakeLists.txt @@ -0,0 +1,21 @@ + +set(o3de_gem_path ${CMAKE_CURRENT_LIST_DIR}) +set(o3de_gem_json ${o3de_gem_path}/gem.json) +o3de_read_json_key(o3de_gem_name ${o3de_gem_json} "gem_name") +o3de_restricted_path(${o3de_gem_json} o3de_gem_restricted_path) + +# Currently we are in the DefaultProjectSource folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here: DefaultProjectSource/Platform/ or +# //DefaultProjectSource +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}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# project cmake for this platform. +include(${pal_dir}/${PAL_PLATFORM_NAME_LOWERCASE}_gem.cmake) + +ly_add_external_target_path(${CMAKE_CURRENT_LIST_DIR}/3rdParty) + +add_subdirectory(Code) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/CMakeLists.txt b/AutomatedTesting/Gem/PythonCoverage/Code/CMakeLists.txt new file mode 100644 index 0000000000..c288377483 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/CMakeLists.txt @@ -0,0 +1,168 @@ + +# Currently we are in the Code folder: ${CMAKE_CURRENT_LIST_DIR} +# Get the platform specific folder ${pal_dir} for the current folder: ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} +# Note: ly_get_list_relative_pal_filename will take care of the details for us, as this may be a restricted platform +# in which case it will see if that platform is present here or in the restricted folder. +# i.e. It could here in our gem : Gems/PythonCoverage/Code/Platform/ or +# //Gems/PythonCoverage/Code +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}) + +# Now that we have the platform abstraction layer (PAL) folder for this folder, thats where we will find the +# traits for this platform. Traits for a platform are defines for things like whether or not something in this gem +# is supported by this platform. +include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) + +# Add the PythonCoverage.Static target +# Note: We include the common files and the platform specific files which are set in pythoncoverage_common_files.cmake +# and in ${pal_dir}/pythoncoverage_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake +ly_add_target( + NAME PythonCoverage.Static STATIC + NAMESPACE Gem + FILES_CMAKE + pythoncoverage_files.cmake + ${pal_dir}/pythoncoverage_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PUBLIC + AZ::AzCore + AZ::AzFramework +) + +# Here add PythonCoverage target, it depends on the PythonCoverage.Static +ly_add_target( + NAME PythonCoverage ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE} + NAMESPACE Gem + FILES_CMAKE + pythoncoverage_shared_files.cmake + ${pal_dir}/pythoncoverage_shared_${PAL_PLATFORM_NAME_LOWERCASE}_files.cmake + INCLUDE_DIRECTORIES + PUBLIC + Include + PRIVATE + Source + BUILD_DEPENDENCIES + PRIVATE + Gem::PythonCoverage.Static +) + +# By default, we will specify that the above target PythonCoverage would be used by +# Client and Server type targets when this gem is enabled. If you don't want it +# active in Clients or Servers by default, delete one of both of the following lines: +ly_create_alias(NAME PythonCoverage.Clients NAMESPACE Gem TARGETS Gem::PythonCoverage) +ly_create_alias(NAME PythonCoverage.Servers NAMESPACE Gem TARGETS Gem::PythonCoverage) + +# If we are on a host platform, we want to add the host tools targets like the PythonCoverage.Editor target which +# will also depend on PythonCoverage.Static +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_DEFAULT_CONFIG_FILE=\"\" + BUILD_DEPENDENCIES + PUBLIC + AZ::AzToolsFramework + Gem::PythonCoverage.Static + ) + + ly_add_target( + NAME PythonCoverage.Editor 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 + ) + + # By default, we will specify that the above target PythonCoverage would be used by + # Tool and Builder type targets when this gem is enabled. If you don't want it + # active in Tools or Builders by default, delete one of both of the following lines: + 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() + +################################################################################ +# Tests +################################################################################ +# See if globally, tests are supported +if(PAL_TRAIT_BUILD_TESTS_SUPPORTED) + # We globally support tests, see if we support tests on this platform for PythonCoverage.Static + if(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED) + # We support PythonCoverage.Tests on this platform, add PythonCoverage.Tests target which depends on PythonCoverage.Static + ly_add_target( + NAME PythonCoverage.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + pythoncoverage_files.cmake + pythoncoverage_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + AZ::AzFramework + Gem::PythonCoverage.Static + ) + + # Add PythonCoverage.Tests to googletest + ly_add_googletest( + NAME Gem::PythonCoverage.Tests + ) + endif() + + # If we are a host platform we want to add tools test like editor tests here + if(PAL_TRAIT_BUILD_HOST_TOOLS) + # We are a host platform, see if Editor tests are supported on this platform + if(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED) + # We support PythonCoverage.Editor.Tests on this platform, add PythonCoverage.Editor.Tests target which depends on PythonCoverage.Editor + ly_add_target( + NAME PythonCoverage.Editor.Tests ${PAL_TRAIT_TEST_TARGET_TYPE} + NAMESPACE Gem + FILES_CMAKE + pythoncoverage_editor_tests_files.cmake + INCLUDE_DIRECTORIES + PRIVATE + Tests + Source + BUILD_DEPENDENCIES + PRIVATE + AZ::AzTest + Gem::PythonCoverage.Editor + ) + + # Add PythonCoverage.Editor.Tests to googletest + ly_add_googletest( + NAME Gem::PythonCoverage.Editor.Tests + ) + endif() + endif() +endif() diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/PAL_android.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/PAL_android.cmake new file mode 100644 index 0000000000..1be29fc854 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/PAL_android.cmake @@ -0,0 +1,4 @@ + +set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED TRUE) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_android_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_android_files.cmake new file mode 100644 index 0000000000..d337110ed9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_android_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Android +# i.e. ../Source/Android/PythonCoverageAndroid.cpp +# ../Source/Android/PythonCoverageAndroid.h +# ../Include/Android/PythonCoverageAndroid.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_editor_shared_android_files - Copy.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_editor_shared_android_files - Copy.cmake new file mode 100644 index 0000000000..d337110ed9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_editor_shared_android_files - Copy.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Android +# i.e. ../Source/Android/PythonCoverageAndroid.cpp +# ../Source/Android/PythonCoverageAndroid.h +# ../Include/Android/PythonCoverageAndroid.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_shared_android_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_shared_android_files.cmake new file mode 100644 index 0000000000..d337110ed9 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Android/pythoncoverage_shared_android_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Android +# i.e. ../Source/Android/PythonCoverageAndroid.cpp +# ../Source/Android/PythonCoverageAndroid.h +# ../Include/Android/PythonCoverageAndroid.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/PAL_linux.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/PAL_linux.cmake new file mode 100644 index 0000000000..bf0d788480 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/PAL_linux.cmake @@ -0,0 +1,4 @@ + +set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_editor_shared_linux_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_editor_shared_linux_files.cmake new file mode 100644 index 0000000000..7c08fd3b2f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_editor_shared_linux_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Linux +# i.e. ../Source/Linux/PythonCoverageLinux.cpp +# ../Source/Linux/PythonCoverageLinux.h +# ../Include/Linux/PythonCoverageLinux.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_linux_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_linux_files.cmake new file mode 100644 index 0000000000..7c08fd3b2f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_linux_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Linux +# i.e. ../Source/Linux/PythonCoverageLinux.cpp +# ../Source/Linux/PythonCoverageLinux.h +# ../Include/Linux/PythonCoverageLinux.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_shared_linux_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_shared_linux_files.cmake new file mode 100644 index 0000000000..7c08fd3b2f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Linux/pythoncoverage_shared_linux_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Linux +# i.e. ../Source/Linux/PythonCoverageLinux.cpp +# ../Source/Linux/PythonCoverageLinux.h +# ../Include/Linux/PythonCoverageLinux.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/PAL_mac.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/PAL_mac.cmake new file mode 100644 index 0000000000..bf0d788480 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/PAL_mac.cmake @@ -0,0 +1,4 @@ + +set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_editor_shared_mac_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_editor_shared_mac_files.cmake new file mode 100644 index 0000000000..1a11934818 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_editor_shared_mac_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Mac +# i.e. ../Source/Mac/PythonCoverageMac.cpp +# ../Source/Mac/PythonCoverageMac.h +# ../Include/Mac/PythonCoverageMac.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_mac_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_mac_files.cmake new file mode 100644 index 0000000000..1a11934818 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_mac_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Mac +# i.e. ../Source/Mac/PythonCoverageMac.cpp +# ../Source/Mac/PythonCoverageMac.h +# ../Include/Mac/PythonCoverageMac.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_shared_mac_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_shared_mac_files.cmake new file mode 100644 index 0000000000..1a11934818 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Mac/pythoncoverage_shared_mac_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Mac +# i.e. ../Source/Mac/PythonCoverageMac.cpp +# ../Source/Mac/PythonCoverageMac.h +# ../Include/Mac/PythonCoverageMac.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/PAL_windows.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/PAL_windows.cmake new file mode 100644 index 0000000000..bf0d788480 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/PAL_windows.cmake @@ -0,0 +1,4 @@ + +set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_editor_windows_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_editor_windows_files.cmake new file mode 100644 index 0000000000..b4c8689f5f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_editor_windows_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Windows +# i.e. ../Source/Windows/PythonCoverageWindows.cpp +# ../Source/Windows/PythonCoverageWindows.h +# ../Include/Windows/PythonCoverageWindows.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_shared_windows_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_shared_windows_files.cmake new file mode 100644 index 0000000000..b4c8689f5f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_shared_windows_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Windows +# i.e. ../Source/Windows/PythonCoverageWindows.cpp +# ../Source/Windows/PythonCoverageWindows.h +# ../Include/Windows/PythonCoverageWindows.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_windows_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_windows_files.cmake new file mode 100644 index 0000000000..b4c8689f5f --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/Windows/pythoncoverage_windows_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for Windows +# i.e. ../Source/Windows/PythonCoverageWindows.cpp +# ../Source/Windows/PythonCoverageWindows.h +# ../Include/Windows/PythonCoverageWindows.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/PAL_ios.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/PAL_ios.cmake new file mode 100644 index 0000000000..bf0d788480 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/PAL_ios.cmake @@ -0,0 +1,4 @@ + +set(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_TEST_SUPPORTED TRUE) +set(PAL_TRAIT_PYTHONCOVERAGE_EDITOR_TEST_SUPPORTED TRUE) \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_editor_shared_ios_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_editor_shared_ios_files.cmake new file mode 100644 index 0000000000..d21ab15181 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_editor_shared_ios_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for iOS +# i.e. ../Source/iOS/PythonCoverageiOS.cpp +# ../Source/iOS/PythonCoverageiOS.h +# ../Include/iOS/PythonCoverageiOS.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_ios_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_ios_files.cmake new file mode 100644 index 0000000000..d21ab15181 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_ios_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for iOS +# i.e. ../Source/iOS/PythonCoverageiOS.cpp +# ../Source/iOS/PythonCoverageiOS.h +# ../Include/iOS/PythonCoverageiOS.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_shared_ios_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_shared_ios_files.cmake new file mode 100644 index 0000000000..d21ab15181 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Platform/iOS/pythoncoverage_shared_ios_files.cmake @@ -0,0 +1,8 @@ + +# Platform specific files for iOS +# i.e. ../Source/iOS/PythonCoverageiOS.cpp +# ../Source/iOS/PythonCoverageiOS.h +# ../Include/iOS/PythonCoverageiOS.h + +set(FILES +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.cpp new file mode 100644 index 0000000000..60c7d8463e --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.cpp @@ -0,0 +1,40 @@ +/* + * 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() + : PythonCoverageModule() + { + // push results of [MyComponent]::CreateDescriptor() into m_descriptors here + 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() }; + } +} // namespace PythonCoverage + +AZ_DECLARE_MODULE_CLASS(Gem_PythonCoverageEditor, PythonCoverage::PythonCoverageEditorModule) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.h b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.h new file mode 100644 index 0000000000..669673ec49 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorModule.h @@ -0,0 +1,31 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include "PythonCoverageModule.h" + +namespace PythonCoverage +{ + class PythonCoverageEditorModule : public PythonCoverageModule + { + public: + AZ_CLASS_ALLOCATOR_DECL + AZ_RTTI(PythonCoverageEditorModule, "{32C0FFEA-09A7-460F-9257-5BDEF74FCD5B}", PythonCoverageModule); + + PythonCoverageEditorModule(); + ~PythonCoverageEditorModule(); + + // PythonCoverageModule ... + AZ::ComponentTypeList GetRequiredSystemComponents() const override; + }; +} // namespace WhiteBox diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.cpp new file mode 100644 index 0000000000..26522ebf08 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.cpp @@ -0,0 +1,228 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include + +#pragma optimize("", off) + +namespace PythonCoverage +{ + constexpr char* const Caller = "PythonCoverageEditorSystemComponent"; + + void PythonCoverageEditorSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (auto serializeContext = azrtti_cast(context)) + { + serializeContext->Class()->Version(1); + } + } + + void PythonCoverageEditorSystemComponent::Activate() + { + AzToolsFramework::EditorPythonScriptNotificationsBus::Handler::BusConnect(); + AZ::EntitySystemBus::Handler::BusConnect(); + ParseCoverageOutputDirectory(); + EnumerateAllModuleComponents(); + } + + void PythonCoverageEditorSystemComponent::Deactivate() + { + AZ::EntitySystemBus::Handler::BusDisconnect(); + AzToolsFramework::EditorPythonScriptNotificationsBus::Handler::BusDisconnect(); + } + + void PythonCoverageEditorSystemComponent::OnEntityActivated(const AZ::EntityId& entityId) + { + EnumerateComponentsForEntity(entityId); + WriteCoverageFile(); + } + + void PythonCoverageEditorSystemComponent::ParseCoverageOutputDirectory() + { + m_coverageState = CoverageState::Disabled; + const AZStd::string configFilePath = LY_TEST_IMPACT_DEFAULT_CONFIG_FILE; + if (configFilePath.empty()) + { + AZ_Warning(Caller, false, "No test impact analysis framework config found."); + return; + } + + const auto fileSize = AZ::IO::SystemFile::Length(configFilePath.c_str()); + if(!fileSize) + { + AZ_Error(Caller, false, "File %s does not exist", configFilePath.c_str()); + return; + } + + AZStd::vector buffer(fileSize + 1); + buffer[fileSize] = '\0'; + if (!AZ::IO::SystemFile::Read(configFilePath.c_str(), buffer.data())) + { + AZ_Error(Caller, false, "Could not read contents of file %s", configFilePath.c_str()); + return; + } + + const AZStd::string configurationData = AZStd::string(buffer.begin(), buffer.end()); + rapidjson::Document configurationFile; + if (configurationFile.Parse(configurationData.c_str()).HasParseError()) + { + AZ_Error(Caller, false, "Could not parse runtimeConfig data, JSON has errors"); + return; + } + + const AZ::IO::Path tempWorkspaceRootDir = configurationFile["workspace"]["temp"]["root"].GetString(); + const AZ::IO::Path artifactRelativeDir = configurationFile["workspace"]["temp"]["relative_paths"]["artifact_dir"].GetString(); + m_coverageDir = tempWorkspaceRootDir / artifactRelativeDir; + m_coverageState = CoverageState::Idle; + } + + void PythonCoverageEditorSystemComponent::WriteCoverageFile() + { + // Yes, we're doing blocking file operations on the main thread... If this becomes an issue this can be offloaded + // to a worker thread + if (m_coverageState == CoverageState::Gathering) + { + AZStd::string contents; + 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 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( + Caller, false, + "Couldn't open file %s for writing", m_coverageFile.c_str()); + return; + } + + if (!file.Write(bytes.data(), bytes.size())) + { + AZ_Error( + Caller, 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) + { + const AZStd::string moduleName = moduleData.GetDebugName(); + if (moduleData.GetDynamicModuleHandle()) + { + const auto fileName = moduleData.GetDynamicModuleHandle()->GetFilename(); + 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 PythonCoverageEditorSystemComponent::GetParentComponentModulesForAllActivatedEntities( + const AZStd::unordered_map& entityComponents) const + { + AZStd::unordered_set 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::OnExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector& 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(Caller, 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 diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.h b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.h new file mode 100644 index 0000000000..0494cf1778 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageEditorSystemComponent.h @@ -0,0 +1,87 @@ +/* + * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or + * its licensors. + * + * For complete copyright and license terms please see the LICENSE at the root of this + * distribution (the "License"). All use of this software is governed by the License, + * or, if provided, by the license below or the license accompanying this file. Do not + * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +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: + // AZ::Component + void Activate() override; + void Deactivate() override; + + // AZ::EntitySystemBus ... + void OnEntityActivated(const AZ::EntityId& entityId) override; + + // AZ::EditorPythonScriptNotificationsBus ... + void OnExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector& args) override; + + //! 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. + void ParseCoverageOutputDirectory(); + + //! 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); + + //! 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 GetParentComponentModulesForAllActivatedEntities( + const AZStd::unordered_map& entityComponents) const; + + //! + void WriteCoverageFile(); + + 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. + }; + + CoverageState m_coverageState = CoverageState::Disabled; //!< Current coverage state. + AZStd::unordered_map> m_entityComponentMap; //!< Map of + //!< component IDs to component descriptors for all activated entities, organized by test cases. + AZStd::unordered_map 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 diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.cpp new file mode 100644 index 0000000000..2c56987482 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.cpp @@ -0,0 +1,26 @@ +#include + +#pragma optimize("", off) + +namespace PythonCoverage +{ + PythonCoverageModule::PythonCoverageModule() + : AZ::Module() + { + // Push results of [MyComponent]::CreateDescriptor() into m_descriptors here. + m_descriptors.insert( + m_descriptors.end(), + { PythonCoverageSystemComponent::CreateDescriptor() }); + } + + AZ::ComponentTypeList PythonCoverageModule::GetRequiredSystemComponents() const + { + return AZ::ComponentTypeList{ + azrtti_typeid(), + }; + } +}// namespace PythonCoverage + +#if !defined(PYTHON_COVERAGE_EDITOR) +AZ_DECLARE_MODULE_CLASS(Gem_PythonCoverage, PythonCoverage::PythonCoverageModule) +#endif // !defined(PYTHON_COVERAGE_EDITOR) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.h b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.h new file mode 100644 index 0000000000..4ec6184906 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageModule.h @@ -0,0 +1,26 @@ + +#include +#include + +#include + +#pragma once + +#pragma optimize("", off) + +namespace PythonCoverage +{ + class PythonCoverageModule : public AZ::Module + { + public: + AZ_RTTI(PythonCoverageModule, "{dc706de0-22c4-4b05-9b99-438692afc082}", AZ::Module); + AZ_CLASS_ALLOCATOR(PythonCoverageModule, AZ::SystemAllocator, 0); + + PythonCoverageModule(); + + /** + * Add required SystemComponents to the SystemEntity. + */ + AZ::ComponentTypeList GetRequiredSystemComponents() const override; + }; +} // namespace PythonCoverage diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.cpp new file mode 100644 index 0000000000..46ffdfdca8 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.cpp @@ -0,0 +1,70 @@ + +#include + +#include +#include +#include + +namespace PythonCoverage +{ + void PythonCoverageSystemComponent::Reflect(AZ::ReflectContext* context) + { + if (AZ::SerializeContext* serialize = azrtti_cast(context)) + { + serialize->Class() + ->Version(0) + ; + + if (AZ::EditContext* ec = serialize->GetEditContext()) + { + ec->Class("PythonCoverage", "[Description of functionality provided by this System Component]") + ->ClassElement(AZ::Edit::ClassElements::EditorData, "") + ->Attribute(AZ::Edit::Attributes::AppearsInAddComponentMenu, AZ_CRC("System")) + ->Attribute(AZ::Edit::Attributes::AutoExpand, true) + ; + } + } + } + + void PythonCoverageSystemComponent::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided) + { + provided.push_back(AZ_CRC("PythonCoverageService")); + } + + void PythonCoverageSystemComponent::GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible) + { + incompatible.push_back(AZ_CRC("PythonCoverageService")); + } + + void PythonCoverageSystemComponent::GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required) + { + AZ_UNUSED(required); + } + + void PythonCoverageSystemComponent::GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent) + { + AZ_UNUSED(dependent); + } + + void PythonCoverageSystemComponent::Init() + { + } + + void PythonCoverageSystemComponent::Activate() + { + PythonCoverageRequestBus::Handler::BusConnect(); + AZ::TickBus::Handler::BusConnect(); + } + + void PythonCoverageSystemComponent::Deactivate() + { + AZ::TickBus::Handler::BusDisconnect(); + PythonCoverageRequestBus::Handler::BusDisconnect(); + } + + void PythonCoverageSystemComponent::OnTick(float /*deltaTime*/, AZ::ScriptTimePoint /*time*/) + { + + } + +} // namespace PythonCoverage diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.h b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.h new file mode 100644 index 0000000000..9ca1f60c74 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Source/PythonCoverageSystemComponent.h @@ -0,0 +1,44 @@ + +#pragma once + +#include +#include +#include + +namespace PythonCoverage +{ + class PythonCoverageSystemComponent + : public AZ::Component + , protected PythonCoverageRequestBus::Handler + , public AZ::TickBus::Handler + { + public: + AZ_COMPONENT(PythonCoverageSystemComponent, "{b2f692ae-1047-4a6d-a4ed-27b1aac40ba5}"); + + static void Reflect(AZ::ReflectContext* context); + + static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided); + static void GetIncompatibleServices(AZ::ComponentDescriptor::DependencyArrayType& incompatible); + static void GetRequiredServices(AZ::ComponentDescriptor::DependencyArrayType& required); + static void GetDependentServices(AZ::ComponentDescriptor::DependencyArrayType& dependent); + + protected: + //////////////////////////////////////////////////////////////////////// + // PythonCoverageRequestBus interface implementation + + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZ::Component interface implementation + void Init() override; + void Activate() override; + void Deactivate() override; + //////////////////////////////////////////////////////////////////////// + + //////////////////////////////////////////////////////////////////////// + // AZTickBus interface implementation + void OnTick(float deltaTime, AZ::ScriptTimePoint time) override; + //////////////////////////////////////////////////////////////////////// + }; + +} // namespace PythonCoverage diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageEditorTest.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageEditorTest.cpp new file mode 100644 index 0000000000..2c97ecaf73 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageEditorTest.cpp @@ -0,0 +1,24 @@ + +#include + +class PythonCoverageEditorTest + : public ::testing::Test +{ +protected: + void SetUp() override + { + + } + + void TearDown() override + { + + } +}; + +TEST_F(PythonCoverageEditorTest, SanityTest) +{ + ASSERT_TRUE(true); +} + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageTest.cpp b/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageTest.cpp new file mode 100644 index 0000000000..b1060df364 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/Tests/PythonCoverageTest.cpp @@ -0,0 +1,24 @@ + +#include + +class PythonCoverageTest + : public ::testing::Test +{ +protected: + void SetUp() override + { + + } + + void TearDown() override + { + + } +}; + +TEST_F(PythonCoverageTest, SanityTest) +{ + ASSERT_TRUE(true); +} + +AZ_UNIT_TEST_HOOK(DEFAULT_UNIT_TEST_ENV); diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_files.cmake new file mode 100644 index 0000000000..83fc442299 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_files.cmake @@ -0,0 +1,5 @@ + +set(FILES + Source/PythonCoverageEditorSystemComponent.cpp + Source/PythonCoverageEditorSystemComponent.h +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_shared_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_shared_files.cmake new file mode 100644 index 0000000000..33f8f244c6 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_shared_files.cmake @@ -0,0 +1,7 @@ + +set(FILES +Source/PythonCoverageModule.cpp +Source/PythonCoverageModule.h +Source/PythonCoverageEditorModule.cpp +Source/PythonCoverageEditorModule.h +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_tests_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_tests_files.cmake new file mode 100644 index 0000000000..551b0bf062 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_editor_tests_files.cmake @@ -0,0 +1,4 @@ + +set(FILES + Tests/PythonCoverageEditorTest.cpp +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_files.cmake new file mode 100644 index 0000000000..bf6453e4f5 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_files.cmake @@ -0,0 +1,5 @@ + +set(FILES + Source/PythonCoverageSystemComponent.cpp + Source/PythonCoverageSystemComponent.h +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_shared_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_shared_files.cmake new file mode 100644 index 0000000000..3e614ee802 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_shared_files.cmake @@ -0,0 +1,5 @@ + +set(FILES + Source/PythonCoverageModule.cpp + Source/PythonCoverageModule.h +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_tests_files.cmake b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_tests_files.cmake new file mode 100644 index 0000000000..55101af371 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Code/pythoncoverage_tests_files.cmake @@ -0,0 +1,4 @@ + +set(FILES + Tests/PythonCoverageTest.cpp +) diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.cmake b/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.cmake new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.cmake @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.json b/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.json new file mode 100644 index 0000000000..23bbb28e66 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Android/android_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Android"], +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.cmake b/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.cmake new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.cmake @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.json b/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.json new file mode 100644 index 0000000000..d08fbf53ba --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Linux/linux_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Linux"] +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.cmake b/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.cmake new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.cmake @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.json b/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.json new file mode 100644 index 0000000000..d42b6f8186 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Mac/mac_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Mac"] +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.cmake b/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.cmake new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.cmake @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.json b/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.json new file mode 100644 index 0000000000..a052f1e05a --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/Windows/windows_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["Windows"] +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.cmake b/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.cmake new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.cmake @@ -0,0 +1 @@ + diff --git a/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.json b/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.json new file mode 100644 index 0000000000..b2dab56d05 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/Platform/iOS/ios_gem.json @@ -0,0 +1,3 @@ +{ + "Tags": ["iOS"] +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/gem.json b/AutomatedTesting/Gem/PythonCoverage/gem.json new file mode 100644 index 0000000000..3ec79ac633 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/gem.json @@ -0,0 +1,15 @@ +{ + "gem_name": "PythonCoverage", + "origin": "The primary repo for PythonCoverage goes here: i.e. http://www.mydomain.com", + "license": "What license PythonCoverage uses goes here: i.e. https://opensource.org/licenses/MIT", + "display_name": "PythonCoverage", + "summary": "A short description of PythonCoverage.", + "canonical_tags": [ + "Gem" + ], + "user_tags": [ + "PythonCoverage" + ], + "icon_path": "preview.png", + "restricted_name": "gems" +} \ No newline at end of file diff --git a/AutomatedTesting/Gem/PythonCoverage/preview.png b/AutomatedTesting/Gem/PythonCoverage/preview.png new file mode 100644 index 0000000000..2f1ed47754 --- /dev/null +++ b/AutomatedTesting/Gem/PythonCoverage/preview.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6d6204c6730e5675791765ca194e9b1cbec282208e280507de830afc2805e5fa +size 41127 diff --git a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py index f00227c47f..9d183ea326 100755 --- a/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py +++ b/AutomatedTesting/Gem/PythonTests/automatedtesting_shared/base.py @@ -94,7 +94,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: diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonRunnerRequestsBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonRunnerRequestsBus.h index 6a2553f90b..1b423621e1 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonRunnerRequestsBus.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonRunnerRequestsBus.h @@ -29,16 +29,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& args) { AZ_UNUSED(filename); AZ_UNUSED(args); } + virtual void ExecuteByFilenameWithArgs( + [[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] const AZStd::vector& args) {} //! executes a Python script as a test - virtual void ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector& 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& args) + { + } }; using EditorPythonRunnerRequestBus = AZ::EBus; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonScriptNotificationsBus.h b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonScriptNotificationsBus.h new file mode 100644 index 0000000000..586a348e76 --- /dev/null +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/API/EditorPythonScriptNotificationsBus.h @@ -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 +#include + +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 execution of a Python script using a string. + virtual void OnExecuteByString([[maybe_unused]] AZStd::string_view script) {} + + //! Notifies the execution of a Python script using a filename. + virtual void OnExecuteByFilename([[maybe_unused]] AZStd::string_view filename) {} + + //! Notifies the execution of a Python script using a filename and args. + virtual void OnExecuteByFilenameWithArgs( + [[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] const AZStd::vector& args) {} + + //! Notifies the execution of a Python script as a test. + virtual void OnExecuteByFilenameAsTest( + [[maybe_unused]] AZStd::string_view filename, [[maybe_unused]] AZStd::string_view testCase, [[maybe_unused]] const AZStd::vector& args) {} + }; + using EditorPythonScriptNotificationsBus = AZ::EBus; +} diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake index 9ec0deaed6..bdb1ab6f0a 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/aztoolsframework_files.cmake @@ -41,6 +41,7 @@ set(FILES API/EditorVegetationRequestsBus.h API/EditorPythonConsoleBus.h API/EditorPythonRunnerRequestsBus.h + API/EditorPythonScriptNotificationsBus.h API/EntityPropertyEditorRequestsBus.h API/EditorWindowRequestBus.h API/EntityCompositionRequestBus.h diff --git a/Code/Sandbox/Editor/CryEdit.cpp b/Code/Sandbox/Editor/CryEdit.cpp index 792f817362..ae93ef04a4 100644 --- a/Code/Sandbox/Editor/CryEdit.cpp +++ b/Code/Sandbox/Editor/CryEdit.cpp @@ -542,6 +542,7 @@ public: QString m_appRoot; QString m_logFile; QString m_pythonArgs; + QString m_pythontTestCase; QString m_execFile; QString m_execLineCmd; @@ -589,6 +590,7 @@ public: const std::vector > 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 }, @@ -1527,7 +1529,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); diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp index 00dbca12c9..e9e75b966f 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.cpp @@ -39,6 +39,7 @@ #include #include +#include namespace Platform { @@ -579,6 +580,9 @@ namespace EditorPythonBindings if (!script.empty()) { + AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast( + &AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnExecuteByString, script); + // Acquire GIL before calling Python code AZStd::lock_guard lock(m_lock); pybind11::gil_scoped_acquire acquire; @@ -639,11 +643,15 @@ namespace EditorPythonBindings void PythonSystemComponent::ExecuteByFilename(AZStd::string_view filename) { AZStd::vector args; + AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast( + &AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnExecuteByFilename, filename); ExecuteByFilenameWithArgs(filename, args); } - void PythonSystemComponent::ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector& args) + void PythonSystemComponent::ExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector& args) { + AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast( + &AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnExecuteByFilenameAsTest, filename, testCase, args); const Result evalResult = EvaluateFile(filename, args); if (evalResult == Result::Okay) { @@ -659,6 +667,8 @@ namespace EditorPythonBindings void PythonSystemComponent::ExecuteByFilenameWithArgs(AZStd::string_view filename, const AZStd::vector& args) { + AzToolsFramework::EditorPythonScriptNotificationsBus::Broadcast( + &AzToolsFramework::EditorPythonScriptNotificationsBus::Events::OnExecuteByFilenameWithArgs, filename, args); EvaluateFile(filename, args); } diff --git a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.h b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.h index d5d1634c9d..69882e7d57 100644 --- a/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.h +++ b/Gems/EditorPythonBindings/Code/Source/PythonSystemComponent.h @@ -58,7 +58,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& args) override; - void ExecuteByFilenameAsTest(AZStd::string_view filename, const AZStd::vector& args) override; + void ExecuteByFilenameAsTest(AZStd::string_view filename, AZStd::string_view testCase, const AZStd::vector& args) override; //////////////////////////////////////////////////////////////////////// private: