diff --git a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py index 9e857ed8bc..59e454479c 100644 --- a/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py +++ b/AutomatedTesting/Gem/PythonTests/EditorPythonTestTools/editor_python_test_tools/editor_entity_utils.py @@ -132,6 +132,7 @@ class EditorEntity: def __init__(self, id: azlmbr.entity.EntityId): self.id: azlmbr.entity.EntityId = id + self.components: List[EditorComponent] = [] # Creation functions @classmethod @@ -279,7 +280,7 @@ class EditorEntity: ), f"Failure: Could not add component: '{new_comp.get_component_name()}' to entity: '{self.get_name()}'" new_comp.id = add_component_outcome.GetValue()[0] components.append(new_comp) - + self.components.append(new_comp) return components def get_components_of_type(self, component_names: list) -> List[EditorComponent]: diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt index c2123d683c..98801b49d6 100644 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/CMakeLists.txt @@ -24,7 +24,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ LargeWorlds ) - ly_add_pytest( NAME AutomatedTesting::DynamicVegetationTests_Periodic TEST_SERIAL @@ -39,6 +38,20 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_ LargeWorlds ) + ly_add_pytest( + NAME AutomatedTesting::DynamicVegetationTests_Periodic_Optimized + TEST_SERIAL + TEST_SUITE periodic + PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg/TestSuite_Periodic_Optimized.py + RUNTIME_DEPENDENCIES + AZ::AssetProcessor + Legacy::Editor + AutomatedTesting.Assets + AutomatedTesting.GameLauncher + COMPONENT + LargeWorlds + ) + ly_add_pytest( NAME AutomatedTesting::DynamicVegetationTests_Main_Optimized TEST_SERIAL diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py new file mode 100644 index 0000000000..d016473d5e --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/EditorScripts/DynVegUtils_TempPrefabCreationWorks.py @@ -0,0 +1,85 @@ +""" +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 +""" + + +def DynVegUtils_TempPrefabCreationWorks(): + """ + Summary: + An existing level is opened. Each Prefab setup to be spawned by Dynamic Vegetation tests is created in memory and + validated against existing test slice components/mesh assignments. + + Expected Behavior: + Temporary prefabs contain the expected components/assets. + + Test Steps: + 1) Open an existing level + 2) Create each of the necessary temporary Mesh prefabs, and validate the component/mesh setups + 3) Create the necessary temporary PhysX Collider, and validate the component setup + 4) Report errors/asserts + + :return: None + """ + + import os + + import azlmbr.asset as asset + import azlmbr.bus as bus + import azlmbr.math as math + + from Prefab.tests import PrefabTestUtils as prefab_test_utils + from largeworlds.large_worlds_utils import editor_dynveg_test_helper as dynveg + from editor_python_test_tools.utils import Report, Tracer + from editor_python_test_tools.utils import TestHelper as helper + from editor_python_test_tools.prefab_utils import PrefabInstance + + with Tracer() as error_tracer: + # Create dictionary for prefab filenames and paths to create using helper function + mesh_prefabs = { + "PinkFlower": os.path.join("assets", "objects", "foliage", "grass_flower_pink.azmodel"), + "PurpleFlower": os.path.join("assets", "objects", "foliage", "grass_flower_purple.azmodel"), + "1m_Cube": os.path.join("objects", "_primitives", "_box_1x1.azmodel"), + "CedarTree": os.path.join("assets", "objects", "foliage", "cedar.azmodel"), + "Bush": os.path.join("assets", "objects", "foliage", "bush_privet_01.azmodel"), + } + + # 1) Open an existing simple level + prefab_test_utils.open_base_tests_level() + + # 2) Create each of the Mesh asset prefabs and validate that the prefab created successfully + for prefab_filename, asset_path in mesh_prefabs.items(): + mesh_prefab_created = ( + f"Temporary mesh prefab: {prefab_filename} created successfully", + f"Failed to create temporary mesh prefab: {prefab_filename}" + ) + prefab = dynveg.create_temp_mesh_prefab(asset_path, prefab_filename) + Report.result(mesh_prefab_created, helper.wait_for_condition(lambda: + PrefabInstance.is_valid(prefab[1]), 3.0)) + + # 3) Create temp PhysX Collider prefab and validate that the prefab created successfully + physx_prefab_filename = "CedarTree_Collision" + physx_collider_prefab_created = ( + f"Temporary mesh prefab: {physx_prefab_filename} created successfully", + f"Failed to create temporary mesh prefab: {physx_prefab_filename}" + ) + test_physx_mesh_asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", os.path.join( + "assets", "objects", "foliage", "cedar.pxmesh"), math.Uuid(), False) + dynveg.create_temp_physx_mesh_collider(test_physx_mesh_asset_id, physx_prefab_filename) + Report.result(physx_collider_prefab_created, helper.wait_for_condition(lambda: + PrefabInstance.is_valid(prefab[1]), 3.0)) + + # 4) Report errors/asserts + helper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0) + for error_info in error_tracer.errors: + Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}") + for assert_info in error_tracer.asserts: + Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}") + + +if __name__ == "__main__": + + from editor_python_test_tools.utils import Report + Report.start_test(DynVegUtils_TempPrefabCreationWorks) diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py new file mode 100644 index 0000000000..8d298e970b --- /dev/null +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/dyn_veg/TestSuite_Periodic_Optimized.py @@ -0,0 +1,23 @@ +""" +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 pytest + +import ly_test_tools.environment.file_system as file_system +from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite + + +@pytest.mark.SUITE_main +@pytest.mark.parametrize("launcher_platform", ['windows_editor']) +@pytest.mark.parametrize("project", ["AutomatedTesting"]) +class TestAutomation(EditorTestSuite): + + global_extra_cmdline_args = ["-BatchMode", "-autotest_mode", "--regset=/Amazon/Preferences/EnablePrefabSystem=true"] + + class test_DynVegUtils_TempPrefabCreationWorks(EditorSharedTest): + from .EditorScripts import DynVegUtils_TempPrefabCreationWorks as test_module diff --git a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py index 957536fffb..d7d5842518 100755 --- a/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py +++ b/AutomatedTesting/Gem/PythonTests/largeworlds/large_worlds_utils/editor_dynveg_test_helper.py @@ -19,6 +19,45 @@ import azlmbr.paths sys.path.append(os.path.join(azlmbr.paths.projectroot, 'Gem', 'PythonTests')) import editor_python_test_tools.hydra_editor_utils as hydra +from editor_python_test_tools.editor_entity_utils import EditorEntity +from editor_python_test_tools.prefab_utils import Prefab + + +def create_temp_mesh_prefab(mesh_asset_path, prefab_filename): + # Create initial entity + root = EditorEntity.create_editor_entity(name=prefab_filename) + assert root.exists(), "Failed to create entity" + # Add mesh component + mesh_component = root.add_component("Mesh") + assert root.has_component("Mesh") and mesh_component.is_enabled(), "Failed to add/activate Mesh component" + # Assign the specified mesh asset + mesh_asset = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", mesh_asset_path, math.Uuid(), False) + mesh_component.set_component_property_value("Controller|Configuration|Mesh Asset", mesh_asset) + assert mesh_component.get_component_property_value("Controller|Configuration|Mesh Asset") == mesh_asset, \ + "Failed to set Mesh asset" + # Create and return the temporary/in-memory prefab + temp_prefab = Prefab.create_prefab([root], prefab_filename) + return temp_prefab + + +def create_temp_physx_mesh_collider(physx_mesh_id, prefab_filename): + # Create initial entity + root = EditorEntity.create_editor_entity(name=prefab_filename) + assert root.exists(), "Failed to create entity" + # Add PhysX Collider component + collider_component = root.add_component("PhysX Collider") + assert root.has_component("PhysX Collider") and collider_component.is_enabled(), \ + "Failed to add/activate PhysX Collider component" + # Set the Collider's Shape Configuration field to PhysicsAsset, and assign the specified PhysX Mesh asset + collider_component.set_component_property_value("Shape Configuration|Shape", 7) + assert collider_component.get_component_property_value("Shape Configuration|Shape") == 7, \ + "Failed to set Collider Shape to PhysicsAsset" + collider_component.set_component_property_value("Shape Configuration|Asset|PhysX Mesh", physx_mesh_id) + assert collider_component.get_component_property_value("Shape Configuration|Asset|PhysX Mesh") == physx_mesh_id, \ + "Failed to assign PhysX Mesh asset" + # Create and return the temporary/in-memory prefab + temp_prefab = Prefab.create_prefab([root], prefab_filename) + return temp_prefab def create_surface_entity(name, center_point, box_size_x, box_size_y, box_size_z):