diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt index f91423324d..117c710824 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/CMakeLists.txt @@ -65,15 +65,4 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT COMPONENT Atom ) - ly_add_pytest( - NAME AutomatedTesting::AtomRenderer_HydraTests_ShaderBuildPipeline - TEST_SUITE main - PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_ShaderBuildPipelineSuite.py - TEST_SERIAL - TIMEOUT 600 - RUNTIME_DEPENDENCIES - AssetProcessor - AutomatedTesting.Assets - Editor - ) endif() diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py index a05420d960..f13227aa9d 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/atom_hydra_scripts/hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges.py @@ -3,78 +3,19 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution. SPDX-License-Identifier: Apache-2.0 OR MIT - """ -import os -import shutil - -def _copy_file(src_file, src_path, target_file, target_path): - # type: (str, str, str, str) -> None - """ - Copies the [src_file] located in [src_path] to the [target_file] located at [target_path]. - Leaves the [target_file] unlocked for reading and writing privileges - :param src_file: The source file to copy (file name) - :param src_path: The source file's path - :param target_file: The target file to copy into (file name) - :param target_path: The target file's path - :return: None - """ - target_file_path = os.path.join(target_path, target_file) - src_file_path = os.path.join(src_path, src_file) - if os.path.exists(target_file_path): - fs.unlock_file(target_file_path) - shutil.copyfile(src_file_path, target_file_path) - -def _copy_tmp_files_in_order(src_directory, file_list, dst_directory, wait_time_in_between = 0.0): - # type: (str, list, str, float) -> None - """ - This function assumes that for each file name listed in @file_list - there's file named "@filename.txt" which the original source file - but they will be copied with just the @filename (.txt removed). - """ - for filename in file_list: - src_name = f"{filename}.txt" - _copy_file(src_name, src_directory, filename, dst_directory) - if wait_time_in_between > 0.0: - print(f"Created {filename} in {dst_directory}") - general.idle_wait(wait_time_in_between) - - -def _remove_file(src_file, src_path): - # type: (str, str) -> None - """ - Removes the [src_file] located in [src_path]. - :param src_file: The source file to copy (file name) - :param src_path: The source file's path - :return: None - """ - src_file_path = os.path.join(src_path, src_file) - if os.path.exists(src_file_path): - fs.unlock_file(src_file_path) - os.remove(src_file_path) - - -def _remove_files(directory, file_list): - for filename in file_list: - _remove_file(filename, directory) - - -def _asset_exists(cache_relative_path): - asset_id = azasset.AssetCatalogRequestBus(azbus.Broadcast, "GetAssetIdByPath", cache_relative_path, azmath.Uuid(), False) - return asset_id.is_valid() - -# List of results that we want to check, this is not 100% necessary but it's a good -# practice to make it easier to debug tests. -# Here we define a tuple of tests -class Results(): - azshader_was_removed = ("azshader was removed", "Failed to remove azshader") - azshader_was_compiled = ("azshader was compiled", "Failed to compile azshader") +# fmt: off +class Tests(): + azshader_was_removed = ("azshader was removed", "Failed to remove azshader") + azshader_was_compiled = ("azshader was compiled", "Failed to compile azshader") + no_error_occurred = ("No errors detected", "Errors were detected") +# fmt: on def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): """ - This test validates [ATOM-5441] Shader Builders May Fail When Multiple New Files Are Added + This test validates: "Shader Builders May Fail When Multiple New Files Are Added" It creates source assets to compile a particular shader. 1- The first phase generates the source assets out of order and slowly. The AP should wakeup each time one of the source dependencies appears but will fail each time. Only when the @@ -82,6 +23,71 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): 2- The second phase is similar as above, except that all source assets will be created at once and We also expect that in the end the shader is built successfully. """ + import os + import shutil + + import azlmbr.asset as azasset + import azlmbr.bus as azbus + import azlmbr.legacy.general as general + import azlmbr.math as azmath + + from editor_python_test_tools.utils import TestHelper as helper + from editor_python_test_tools.utils import Tracer + import ly_test_tools.environment.file_system as fs + + def _copy_file(src_file, src_path, target_file, target_path): + # type: (str, str, str, str) -> None + """ + Copies the [src_file] located in [src_path] to the [target_file] located at [target_path]. + Leaves the [target_file] unlocked for reading and writing privileges + :param src_file: The source file to copy (file name) + :param src_path: The source file's path + :param target_file: The target file to copy into (file name) + :param target_path: The target file's path + :return: None + """ + target_file_path = os.path.join(target_path, target_file) + src_file_path = os.path.join(src_path, src_file) + if os.path.exists(target_file_path): + fs.unlock_file(target_file_path) + shutil.copyfile(src_file_path, target_file_path) + + def _copy_tmp_files_in_order(src_directory, file_list, dst_directory, wait_time_in_between=0.0): + # type: (str, list, str, float) -> None + """ + This function assumes that for each file name listed in @file_list + there's file named "@filename.txt" which the original source file + but they will be copied with just the @filename (.txt removed). + """ + for filename in file_list: + src_name = f"{filename}.txt" + _copy_file(src_name, src_directory, filename, dst_directory) + if wait_time_in_between > 0.0: + print(f"Created {filename} in {dst_directory}") + general.idle_wait(wait_time_in_between) + + def _remove_file(src_file, src_path): + # type: (str, str) -> None + """ + Removes the [src_file] located in [src_path]. + :param src_file: The source file to copy (file name) + :param src_path: The source file's path + :return: None + """ + src_file_path = os.path.join(src_path, src_file) + if os.path.exists(src_file_path): + fs.unlock_file(src_file_path) + os.remove(src_file_path) + + def _remove_files(directory, file_list): + for filename in file_list: + _remove_file(filename, directory) + + def _asset_exists(cache_relative_path): + asset_id = azasset.AssetCatalogRequestBus(azbus.Broadcast, "GetAssetIdByPath", cache_relative_path, + azmath.Uuid(), False) + return asset_id.is_valid() + # Required for automated tests helper.init_idle() @@ -115,14 +121,14 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): azshader_name = "assets/dependencyvalidation.azshader" helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) _copy_tmp_files_in_order(src_assets_subdir, file_list, game_asset_path, 1.0) # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # The first part was about compiling the shader under normal conditions. # Let's remove the files from the previous phase and will proceed @@ -130,7 +136,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # ShaderAssetBuilder will only succeed when the last file becomes visible. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) # Remark, if you are running this test manually from the Editor with "pyRunFile", # You'll notice how the AP issues notifications that it fails to compile the shader @@ -148,7 +154,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # The last phase of the test puts stress on potential race conditions # when all required files appear as soon as possible. @@ -157,7 +163,7 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Remove left over files. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) - Report.critical_result(Results.azshader_was_removed, not _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_removed, not _asset_exists(azshader_name)) # Now let's copy all the source files to the "Assets" folder as fast as possible. _copy_tmp_files_in_order(src_assets_subdir, reverse_file_list, game_asset_path) @@ -165,24 +171,17 @@ def ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(): # Give enough time to AP to compile the shader helper.wait_for_condition(lambda: _asset_exists(azshader_name), 60.0) - Report.critical_result(Results.azshader_was_compiled, _asset_exists(azshader_name)) + Report.critical_result(Tests.azshader_was_compiled, _asset_exists(azshader_name)) # All good, let's cleanup leftover files before closing the test. _remove_files(game_asset_path, reverse_file_list) helper.wait_for_condition(lambda: not _asset_exists(azshader_name), 5.0) + # Look for errors to raise. + helper.wait_for_condition(lambda: error_tracer.has_errors, 1.0) + Report.result(Tests.no_error_occurred, not error_tracer.has_errors) + if __name__ == "__main__": - # All exposed python bindings are in azlmbr - import azlmbr.legacy.general as general - import azlmbr.bus as azbus - import azlmbr.asset as azasset - import azlmbr.math as azmath - - # Import report and test helper utilities from editor_python_test_tools.utils import Report - from editor_python_test_tools.utils import TestHelper as helper - from editor_python_test_tools.utils import Tracer - import ly_test_tools.environment.file_system as fs - - Report.start_test(ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges) \ No newline at end of file + Report.start_test(ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges) diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py index 329d3ecb91..e206e77d60 100644 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py +++ b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_MainSuite_Optimized.py @@ -41,3 +41,6 @@ class TestAutomation(EditorTestSuite): class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest): from atom_renderer.atom_hydra_scripts import hydra_AtomEditorComponents_DisplayMapperAdded as test_module + + class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest): + from .atom_hydra_scripts import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module diff --git a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py b/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py deleted file mode 100644 index 9ef93ea238..0000000000 --- a/AutomatedTesting/Gem/PythonTests/atom_renderer/test_Atom_ShaderBuildPipelineSuite.py +++ /dev/null @@ -1,19 +0,0 @@ -""" -Copyright (c) Contributors to the Open 3D Engine Project. -For complete copyright and license terms please see the LICENSE at the root of this distribution. - -SPDX-License-Identifier: Apache-2.0 OR MIT - -Main suite tests for the Shader Build Pipeline. -""" -import pytest -from ly_test_tools import LAUNCHERS -from ly_test_tools.o3de.editor_test import EditorTestSuite, EditorSingleTest - -@pytest.mark.parametrize("project", ["AutomatedTesting"]) -@pytest.mark.parametrize("launcher_platform", ['windows_editor']) -class TestShaderBuildPipelineMain(EditorTestSuite): - """Holds tests for Shader Build Pipeline validation""" - - class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSingleTest): - from .atom_hydra_scripts import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module \ No newline at end of file