Merge remote-tracking branch 'upstream/development' into Atom/santora/UseLibPng

Signed-off-by: santorac <55155825+santorac@users.noreply.github.com>
monroegm-disable-blank-issue-2
santorac 4 years ago
commit 3d3324cc2b

@ -0,0 +1,36 @@
---
name: Nightly Build Error bug report
about: Create a report when the nightly build process fails
title: 'Nightly Build Failure'
labels: 'needs-triage,needs-sig,kind/bug,kind/nightlybuildfailure'
---
**Describe the bug**
A clear and concise description of what the bug is.
**Failure type**
Build | Asset Processing | Test Tools | Infrastructure | Test
**To Reproduce Test Failures**
- Paste the command line that reproduces the test failure
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Logs**
Attach the Jenkins logs that are relevant to the failure
**Desktop/Device (please complete the following information):**
- Device: [e.g. PC, Mac, iPhone, Samsung]
- OS: [e.g. Windows, macOS, iOS, Android]
- Version [e.g. 10, Bug Sur, Oreo]
- CPU [e.g. Intel I9-9900k , Ryzen 5900x, ]
- GPU [AMD 6800 XT, NVidia RTX 3090]
- Memory [e.g. 16GB]
**Additional context**
Add any other context about the problem here.

3
.gitignore vendored

@ -5,8 +5,9 @@ __pycache__
AssetProcessorTemp/**
[Bb]uild/**
[Oo]ut/**
CMakeUserPresets.json
[Cc]ache/
/install/
/[Ii]nstall/
Editor/EditorEventLog.xml
Editor/EditorLayout.xml
**/*egg-info/**

@ -0,0 +1,9 @@
#
# 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
#
#
ly_install_directory(DIRECTORIES .)

@ -1,6 +0,0 @@
e_StatoscopeScreenshotCapturePeriod = 1
e_StatoscopeDataGroups = Oulm
sys_pakLogInvalidFileAccess = 1
e_StatoscopeWriteTimeout = 10
e_StatoscopeConnectTimeout = 10

@ -35,37 +35,11 @@ ly_create_alias(NAME AutomatedTesting.Servers NAMESPACE Gem TARGETS Gem::Automa
# Gem dependencies
################################################################################
# The GameLauncher uses "Clients" gem variants:
ly_enable_gems(PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake
TARGETS AutomatedTesting.GameLauncher
VARIANTS Clients)
# Enable the enabled_gems for the Project:
ly_enable_gems(PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake)
# If we build a server, then apply the gems to the server
# Add project to the list server projects to create the AutomatedTesting.ServerLauncher
if(PAL_TRAIT_BUILD_SERVER_SUPPORTED)
# if we're making a server, then add the "Server" gem variants to it:
ly_enable_gems(PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake
TARGETS AutomatedTesting.ServerLauncher
VARIANTS Servers)
set_property(GLOBAL APPEND PROPERTY LY_LAUNCHER_SERVER_PROJECTS AutomatedTesting)
endif()
if (PAL_TRAIT_BUILD_HOST_TOOLS)
# The Editor uses "Tools" gem variants:
ly_enable_gems(
PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake
TARGETS Editor
VARIANTS Tools)
# The Material Editor needs the Lyshine "Tools" gem variant for the custom LyShine pass
ly_enable_gems(
PROJECT_NAME AutomatedTesting GEMS LyShine
TARGETS MaterialEditor
VARIANTS Tools)
# The pipeline tools use "Builders" gem variants:
ly_enable_gems(
PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake
TARGETS AssetBuilder AssetProcessor AssetProcessorBatch
VARIANTS Builders)
endif()

@ -17,7 +17,7 @@
namespace PythonCoverage
{
static constexpr char* const LogCallSite = "PythonCoverageEditorSystemComponent";
static constexpr const char* const LogCallSite = "PythonCoverageEditorSystemComponent";
void PythonCoverageEditorSystemComponent::Reflect(AZ::ReflectContext* context)
{

@ -17,15 +17,15 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
return()
endif()
# Enable after installing NodeJS and CDK on jenkins Windows AMI.
ly_add_pytest(
NAME AutomatedTesting::AWSTests
TEST_SUITE periodic
TEST_SUITE awsi
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/${PAL_PLATFORM_NAME}/
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.GameLauncher
AutomatedTesting.Assets
COMPONENT
AWS

@ -133,7 +133,7 @@ def update_kinesis_analytics_application_status(aws_metrics_utils: pytest.fixtur
aws_metrics_utils.stop_kinesis_data_analytics_application(
resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsApplicationName'))
@pytest.mark.SUITE_periodic
@pytest.mark.SUITE_awsi
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('aws_credentials')
@pytest.mark.usefixtures('resource_mappings')

@ -21,7 +21,7 @@ AWS_CLIENT_AUTH_FEATURE_NAME = 'AWSClientAuth'
logger = logging.getLogger(__name__)
@pytest.mark.SUITE_periodic
@pytest.mark.SUITE_awsi
@pytest.mark.usefixtures('asset_processor')
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('aws_utils')

@ -74,7 +74,7 @@ def write_test_data_to_dynamodb_table(resource_mappings: pytest.fixture, aws_uti
raise
@pytest.mark.SUITE_periodic
@pytest.mark.SUITE_awsi
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('asset_processor')
@pytest.mark.parametrize('feature_name', [AWS_CORE_FEATURE_NAME])

@ -5,12 +5,14 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
# ARN of the IAM role to assume for retrieving temporary AWS credentials
ASSUME_ROLE_ARN = 'arn:aws:iam::645075835648:role/o3de-automation-tests'
ASSUME_ROLE_ARN = os.environ.get('ASSUME_ROLE_ARN', 'arn:aws:iam::645075835648:role/o3de-automation-tests')
# Name of the AWS project deployed by the CDK applications
AWS_PROJECT_NAME = 'AWSAUTO'
AWS_PROJECT_NAME = os.environ.get('O3DE_AWS_PROJECT_NAME', 'AWSAUTO')
# Region for the existing CloudFormation stacks used by the automation tests
AWS_REGION = 'us-east-1'
AWS_REGION = os.environ.get('O3DE_AWS_DEPLOY_REGION', 'us-east-1')
# Name of the default resource mapping config file used by the automation tests
AWS_RESOURCE_MAPPING_FILE_NAME = 'default_aws_resource_mappings.json'
# Name of the game launcher log

@ -1,33 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterCapsuleDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def CapsuleDamage(target_id, position0):
position1 = azlmbr.object.construct('Vector3', position0.x + 1.0, position0.y, position0.z)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Capsule Damage", target_id,
position0, position1,
Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(CapsuleDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterCapsuleDamage)

@ -1,110 +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
"""
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_moving = ("Moving entity found", "Moving entity not found")
find_destructable = ("Destructable entity found", "Destructable entity not found")
find_root_actor = ("Root actor of destructable entity found", "Root actor of destructable entity not found")
collisions = ("Collision occurred between entities", "Collision did not occur between entities")
root_actor_destroyed = ("Root actor destroyed", "Root actor was not destroyed")
child_actors_created = ("Child actors created", "Child actors were not created")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def ActorSplitsAfterCollision():
"""
Summary:
Open a Project that already has 1 rigid sphere entity and 1 destructable entity and verify collision and damage
to destructable entity.
Level Description:
Moving Sphere and Destructable entities are created in level with same collision layer and same collision group.
Moving Sphere entity is placed above the Destructable entity.
Expected Behavior:
Moving Sphere has to collide with Destructable entity under the effect of gravity. Destructable entity root actor
should be destroyed and 2 child actors should be created to replace it.
Test Steps:
1) Open level and Enter game mode
2) Retrieve and validate Entities
3) Get the entity representing root actor of destructable entity
4) Check moving sphere collides with root actor of destructable entity due to gravity
5) Check root actor is destroyed
6) Check child actors of root actor are created
7) Exit game mode
8) Close the editor
:return: None
"""
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import azlmbr.legacy.general as general
import azlmbr.bus
from BlastUtils import CollisionHandler
from BlastUtils import BlastNotificationHandler
# Constants
TIMEOUT = 2.0
helper.init_idle()
# 1) Open level and Enter game mode
helper.open_level("Blast", "Blast_ActorSplitsAfterCollision")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve and validate Entities
moving_id = general.find_game_entity("Sphere_Moving")
Report.critical_result(Tests.find_moving, moving_id.IsValid())
destructable_id = general.find_game_entity("Destructable_Box")
Report.critical_result(Tests.find_destructable, destructable_id.IsValid())
# 3) Get the entity representing root actor of destructable entity
destructable_initial_actors = azlmbr.destruction.BlastFamilyComponentRequestBus(azlmbr.bus.Event, "Get Actors Data", destructable_id)
Report.critical_result(Tests.find_root_actor, len(destructable_initial_actors) == 1)
destructable_initial_actor_id = destructable_initial_actors[0].EntityId
collision = CollisionHandler(destructable_initial_actor_id, moving_id)
family_handler = BlastNotificationHandler(destructable_id)
# 4) Check moving sphere collides with root actor of destructable entity due to gravity
helper.wait_for_condition(lambda: collision.collision_happened, TIMEOUT)
Report.result(Tests.collisions, collision.collision_happened)
# 5) Check root actor is destroyed
helper.wait_for_condition(lambda: family_handler.actors_destroyed == 1, TIMEOUT)
Report.result(Tests.root_actor_destroyed, family_handler.actors_destroyed == 1)
# 6) Check child actors of root actor are created
helper.wait_for_condition(lambda: family_handler.actors_created == 2, TIMEOUT)
Report.result(Tests.child_actors_created, family_handler.actors_created == 2)
# 7) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 8) Close the editor
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterCollision)

@ -1,93 +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
"""
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_destructable = ("Destructable entity found", "Destructable entity not found")
find_root_actor = ("Root actor of destructable entity found", "Root actor of destructable entity not found")
root_actor_destroyed = ("Root actor destroyed", "Root actor was not destroyed")
child_actors_created = ("Child actors created", "Child actors were not created")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def base_run(damage_func):
"""
Summary:
Open a Project that already has 1 destructable entity and verify damage destructs it.
Level Description:
Destructable entity is in a level.
Expected Behavior:
Damage is applied to destructable entity. Destructable entity root actor
should be destroyed and 2 child actors should be created to replace it.
Test Steps:
1) Open level and Enter game mode
2) Retrieve and validate Entities
3) Get the entity representing root actor of destructable entity
4) Apply damage to the destructable entity
5) Check root actor is destroyed
6) Check child actors of root actor are created
7) Exit game mode
8) Close the editor
:return: None
"""
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import azlmbr.legacy.general as general
import azlmbr.bus
from BlastUtils import BlastNotificationHandler
# Constants
TIMEOUT = 2.0
helper.init_idle()
# 1) Open level and Enter game mode
helper.open_level("Blast", "Blast_ActorSplitsAfterDamage")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve and validate Entities
destructable_id = general.find_game_entity("Destructable_Box")
Report.critical_result(Tests.find_destructable, destructable_id.IsValid())
# 3) Get the entity representing root actor of destructable entity
destructable_initial_actors = azlmbr.destruction.BlastFamilyComponentRequestBus(azlmbr.bus.Event, "Get Actors Data", destructable_id)
Report.critical_result(Tests.find_root_actor, len(destructable_initial_actors) == 1)
destructable_initial_actor_id = destructable_initial_actors[0].EntityId
initial_actor_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", destructable_initial_actor_id)
# 4) Apply damage to the destructable entity
damage_func(destructable_id, initial_actor_position)
family_handler = BlastNotificationHandler(destructable_id)
# 5) Check root actor is destroyed
helper.wait_for_condition(lambda: family_handler.actors_destroyed == 1, TIMEOUT)
Report.result(Tests.root_actor_destroyed, family_handler.actors_destroyed == 1)
# 6) Check child actors of root actor are created
helper.wait_for_condition(lambda: family_handler.actors_created == 2, TIMEOUT)
Report.result(Tests.child_actors_created, family_handler.actors_created == 2)
# 7) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 8) Close the editor
helper.close_editor()

@ -1,31 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterImpactSpreadDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def ImpactSpreadDamage(target_id, position):
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Impact Spread Damage", target_id,
position, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(ImpactSpreadDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterImpactSpreadDamage)

@ -1,31 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterRadialDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def RadialDamage(target_id, position):
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Radial Damage", target_id,
position, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(RadialDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterRadialDamage)

@ -1,32 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterShearDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def ShearDamage(target_id, position):
normal = azlmbr.object.construct('Vector3', 1.0, 0.0, 0.0)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Shear Damage", target_id,
position, normal, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(ShearDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterShearDamage)

@ -1,31 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterStressDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def StressDamage(target_id, position):
force = azlmbr.object.construct('Vector3', 0.0, 0.0, -100.0) # Should be enough to break `brittle` objects
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Stress Damage", target_id,
position, force)
internal_run(StressDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterStressDamage)

@ -1,35 +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
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
from ActorSplitsAfterDamage import Tests
def ActorSplitsAfterTriangleDamage():
from ActorSplitsAfterDamage import base_run as internal_run
from BlastUtils import Constants
def TriangleDamage(target_id, position):
# Some points that form a triangle that contains the given position
position0 = azlmbr.object.construct('Vector3', position.x, position.y + 1.0, position.z)
position1 = azlmbr.object.construct('Vector3', position.x + 1.0, position.y - 1.0, position.z)
position2 = azlmbr.object.construct('Vector3', position.x - 1.0, position.y - 1.0, position.z)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Triangle Damage", target_id,
position0, position1, position2,
Constants.DAMAGE_AMOUNT)
internal_run(TriangleDamage)
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(ActorSplitsAfterTriangleDamage)

@ -8,10 +8,10 @@
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_pytest(
NAME AutomatedTesting::BlastTests
NAME AutomatedTesting::BlastTests_Main
TEST_SUITE main
TEST_SERIAL TRUE
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor

@ -1,11 +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
"""
def init():
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')

@ -1,50 +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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_ActorSplitsAfterCollision(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterCollision as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterRadialDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterRadialDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterCapsuleDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterCapsuleDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterImpactSpreadDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterImpactSpreadDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterShearDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterShearDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterTriangleDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterTriangleDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterStressDamage(self, request, workspace, editor, launcher_platform):
from . import ActorSplitsAfterStressDamage as test_module
self._run_test(request, workspace, editor, test_module)

@ -0,0 +1,50 @@
"""
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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_ActorSplitsAfterCollision(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterCollision as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterRadialDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterRadialDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterCapsuleDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterCapsuleDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterImpactSpreadDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterImpactSpreadDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterShearDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterShearDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterTriangleDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterTriangleDamage as test_module
self._run_test(request, workspace, editor, test_module)
def test_ActorSplitsAfterStressDamage(self, request, workspace, editor, launcher_platform):
from .tests import Blast_ActorSplitsAfterStressDamage as test_module
self._run_test(request, workspace, editor, test_module)

@ -0,0 +1,89 @@
"""
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
"""
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_destructable = ("Destructable entity found", "Destructable entity not found")
find_root_actor = ("Root actor of destructable entity found", "Root actor of destructable entity not found")
root_actor_destroyed = ("Root actor destroyed", "Root actor was not destroyed")
child_actors_created = ("Child actors created", "Child actors were not created")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def base_run(damage_func):
"""
Summary:
Open a Project that already has 1 destructable entity and verify damage destructs it.
Level Description:
Destructable entity is in a level.
Expected Behavior:
Damage is applied to destructable entity. Destructable entity root actor
should be destroyed and 2 child actors should be created to replace it.
Test Steps:
1) Open level and Enter game mode
2) Retrieve and validate Entities
3) Get the entity representing root actor of destructable entity
4) Apply damage to the destructable entity
5) Check root actor is destroyed
6) Check child actors of root actor are created
7) Exit game mode
8) Close the editor
:return: None
"""
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import azlmbr.legacy.general as general
import azlmbr.bus
from BlastUtils import BlastNotificationHandler
# Constants
TIMEOUT = 2.0
helper.init_idle()
# 1) Open level and Enter game mode
helper.open_level("Blast", "Blast_ActorSplitsAfterDamage")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve and validate Entities
destructable_id = general.find_game_entity("Destructable_Box")
Report.critical_result(Tests.find_destructable, destructable_id.IsValid())
# 3) Get the entity representing root actor of destructable entity
destructable_initial_actors = azlmbr.destruction.BlastFamilyComponentRequestBus(azlmbr.bus.Event, "Get Actors Data", destructable_id)
Report.critical_result(Tests.find_root_actor, len(destructable_initial_actors) == 1)
destructable_initial_actor_id = destructable_initial_actors[0].EntityId
initial_actor_position = azlmbr.components.TransformBus(azlmbr.bus.Event, "GetWorldTranslation", destructable_initial_actor_id)
# 4) Apply damage to the destructable entity
damage_func(destructable_id, initial_actor_position)
family_handler = BlastNotificationHandler(destructable_id)
# 5) Check root actor is destroyed
helper.wait_for_condition(lambda: family_handler.actors_destroyed == 1, TIMEOUT)
Report.result(Tests.root_actor_destroyed, family_handler.actors_destroyed == 1)
# 6) Check child actors of root actor are created
helper.wait_for_condition(lambda: family_handler.actors_created == 2, TIMEOUT)
Report.result(Tests.child_actors_created, family_handler.actors_created == 2)
# 7) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 8) Close the editor
helper.close_editor()

@ -0,0 +1,28 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterCapsuleDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def CapsuleDamage(target_id, position0):
position1 = azlmbr.object.construct('Vector3', position0.x + 1.0, position0.y, position0.z)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Capsule Damage", target_id,
position0, position1,
Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(CapsuleDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterCapsuleDamage)

@ -0,0 +1,103 @@
"""
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
"""
# fmt: off
class Tests():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
find_moving = ("Moving entity found", "Moving entity not found")
find_destructable = ("Destructable entity found", "Destructable entity not found")
find_root_actor = ("Root actor of destructable entity found", "Root actor of destructable entity not found")
collisions = ("Collision occurred between entities", "Collision did not occur between entities")
root_actor_destroyed = ("Root actor destroyed", "Root actor was not destroyed")
child_actors_created = ("Child actors created", "Child actors were not created")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
# fmt: on
def Blast_ActorSplitsAfterCollision():
"""
Summary:
Open a Project that already has 1 rigid sphere entity and 1 destructable entity and verify collision and damage
to destructable entity.
Level Description:
Moving Sphere and Destructable entities are created in level with same collision layer and same collision group.
Moving Sphere entity is placed above the Destructable entity.
Expected Behavior:
Moving Sphere has to collide with Destructable entity under the effect of gravity. Destructable entity root actor
should be destroyed and 2 child actors should be created to replace it.
Test Steps:
1) Open level and Enter game mode
2) Retrieve and validate Entities
3) Get the entity representing root actor of destructable entity
4) Check moving sphere collides with root actor of destructable entity due to gravity
5) Check root actor is destroyed
6) Check child actors of root actor are created
7) Exit game mode
8) Close the editor
:return: None
"""
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
import azlmbr.legacy.general as general
import azlmbr.bus
from BlastUtils import CollisionHandler
from BlastUtils import BlastNotificationHandler
# Constants
TIMEOUT = 2.0
helper.init_idle()
# 1) Open level and Enter game mode
helper.open_level("Blast", "Blast_ActorSplitsAfterCollision")
helper.enter_game_mode(Tests.enter_game_mode)
# 2) Retrieve and validate Entities
moving_id = general.find_game_entity("Sphere_Moving")
Report.critical_result(Tests.find_moving, moving_id.IsValid())
destructable_id = general.find_game_entity("Destructable_Box")
Report.critical_result(Tests.find_destructable, destructable_id.IsValid())
# 3) Get the entity representing root actor of destructable entity
destructable_initial_actors = azlmbr.destruction.BlastFamilyComponentRequestBus(azlmbr.bus.Event, "Get Actors Data", destructable_id)
Report.critical_result(Tests.find_root_actor, len(destructable_initial_actors) == 1)
destructable_initial_actor_id = destructable_initial_actors[0].EntityId
collision = CollisionHandler(destructable_initial_actor_id, moving_id)
family_handler = BlastNotificationHandler(destructable_id)
# 4) Check moving sphere collides with root actor of destructable entity due to gravity
helper.wait_for_condition(lambda: collision.collision_happened, TIMEOUT)
Report.result(Tests.collisions, collision.collision_happened)
# 5) Check root actor is destroyed
helper.wait_for_condition(lambda: family_handler.actors_destroyed == 1, TIMEOUT)
Report.result(Tests.root_actor_destroyed, family_handler.actors_destroyed == 1)
# 6) Check child actors of root actor are created
helper.wait_for_condition(lambda: family_handler.actors_created == 2, TIMEOUT)
Report.result(Tests.child_actors_created, family_handler.actors_created == 2)
# 7) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 8) Close the editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterCollision)

@ -0,0 +1,26 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterImpactSpreadDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def ImpactSpreadDamage(target_id, position):
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Impact Spread Damage", target_id,
position, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(ImpactSpreadDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterImpactSpreadDamage)

@ -0,0 +1,26 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterRadialDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def RadialDamage(target_id, position):
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Radial Damage", target_id,
position, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(RadialDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterRadialDamage)

@ -0,0 +1,27 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterShearDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def ShearDamage(target_id, position):
normal = azlmbr.object.construct('Vector3', 1.0, 0.0, 0.0)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Shear Damage", target_id,
position, normal, Constants.DAMAGE_MIN_RADIUS,
Constants.DAMAGE_MAX_RADIUS, Constants.DAMAGE_AMOUNT)
internal_run(ShearDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterShearDamage)

@ -0,0 +1,26 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterStressDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def StressDamage(target_id, position):
force = azlmbr.object.construct('Vector3', 0.0, 0.0, -100.0) # Should be enough to break `brittle` objects
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Stress Damage", target_id,
position, force)
internal_run(StressDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterStressDamage)

@ -0,0 +1,30 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
def Blast_ActorSplitsAfterTriangleDamage():
from BaseDamageTest import base_run as internal_run
from BlastUtils import Constants
def TriangleDamage(target_id, position):
# Some points that form a triangle that contains the given position
position0 = azlmbr.object.construct('Vector3', position.x, position.y + 1.0, position.z)
position1 = azlmbr.object.construct('Vector3', position.x + 1.0, position.y - 1.0, position.z)
position2 = azlmbr.object.construct('Vector3', position.x - 1.0, position.y - 1.0, position.z)
azlmbr.destruction.BlastFamilyDamageRequestBus(azlmbr.bus.Event, "Triangle Damage", target_id,
position0, position1, position2,
Constants.DAMAGE_AMOUNT)
internal_run(TriangleDamage)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Blast_ActorSplitsAfterTriangleDamage)

@ -0,0 +1,47 @@
"""
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
"""
# Built-in Imports
from __future__ import annotations
# Open 3D Engine Imports
import azlmbr.bus as bus
import azlmbr.asset as azasset
import azlmbr.math as math
class Asset:
"""
Used to find Asset Id by its path and path of asset by its Id
If a component has any asset property, then this class object can be called as:
asset_id = editor_python_test_tools.editor_entity_utils.EditorComponent.get_component_property_value(<arguments>)
asset = asset_utils.Asset(asset_id)
"""
def __init__(self, id: azasset.AssetId):
self.id: azasset.AssetId = id
# Creation functions
@classmethod
def find_asset_by_path(cls, path: str, RegisterType: bool = False) -> Asset:
"""
:param path: Absolute file path of the asset
:param RegisterType: Whether to register the asset if it's not in the database,
default to false for the general case
:return: Asset object associated with file path
"""
asset_id = azasset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", path, math.Uuid(), RegisterType)
assert asset_id.is_valid(), f"Couldn't find Asset with path: {path}"
asset = cls(asset_id)
return asset
# Methods
def get_path(self) -> str:
"""
:return: Absolute file path of Asset
"""
assert self.id.is_valid(), "Invalid Asset Id"
return azasset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetPathById", self.id)

@ -214,6 +214,12 @@ class EditorEntity:
"""
return editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", self.id)
def get_children_ids(self) -> List[azlmbr.entity.EntityId]:
"""
:return: Entity ids of children. Type: [entity.EntityId()]
"""
return editor.EditorEntityInfoRequestBus(bus.Event, "GetChildren", self.id)
def add_component(self, component_name: str) -> EditorComponent:
"""
Used to add new component to Entity.

@ -1,90 +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
"""
# Test case ID : C18977329
# Test Case Title : Add cloth simulation to a Mesh
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
# fmt: on
def C18977329_NvCloth_AddClothSimulationToMesh():
"""
Summary:
Load level with Entity having Mesh and Cloth components already setup. Verify that editor remains stable in Game mode.
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the level
2) Start the Tracer to catch any errors and warnings
3) Enter game mode
4) Wait in game mode some frames to let cloth simulation run
5) Verify there are no errors and warnings in the logs
6) Exit game mode
7) Close the editor
Note:
At the time of writing this test it was not possible to set a component property
with a dropdown, which is necessary to setup a Cloth component. Because of this limitation
the components of this test are not setup by the script and a level was provided instead.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report
# Helper file Imports
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Tracer
# Constants
FRAMES_IN_GAME_MODE = 200
helper.init_idle()
# 1) Load the level
helper.open_level("NvCloth", "C18977329_NvCloth_AddClothSimulationToMesh")
# 2) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 3) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 4) Wait in game mode some frames to let cloth simulation run
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
# 5) Verify there are no errors and warnings in the logs
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
Report.result(Tests.no_errors_and_warnings_found, success_condition)
if not success_condition:
if section_tracer.has_warnings:
Report.info(f"Warnings found: {section_tracer.warnings}")
if section_tracer.has_errors:
Report.info(f"Errors found: {section_tracer.errors}")
Report.failure(Tests.no_errors_and_warnings_found)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 7) Close the editor
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C18977329_NvCloth_AddClothSimulationToMesh)

@ -1,90 +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
"""
# Test case ID : C18977330
# Test Case Title : Add cloth simulation to an Actor
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
# fmt: on
def C18977330_NvCloth_AddClothSimulationToActor():
"""
Summary:
Load level with Entity having Actor and Cloth components already setup. Verify that editor remains stable in Game mode.
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the level
2) Start the Tracer to catch any errors and warnings
3) Enter game mode
4) Wait in game mode some frames to let cloth simulation run
5) Verify there are no errors and warnings in the logs
6) Exit game mode
7) Close the editor
Note:
At the time of writing this test it was not possible to set a component property
with a dropdown, which is necessary to setup a Cloth component. Because of this limitation
the components of this test are not setup by the script and a level was provided instead.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report
# Helper file Imports
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import TestHelper as helper
from editor_python_test_tools.utils import Tracer
# Constants
FRAMES_IN_GAME_MODE = 200
helper.init_idle()
# 1) Load the level
helper.open_level("NvCloth", "C18977330_NvCloth_AddClothSimulationToActor")
# 2) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 3) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 4) Wait in game mode some frames to let cloth simulation run
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
# 5) Verify there are no errors and warnings in the logs
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
Report.result(Tests.no_errors_and_warnings_found, success_condition)
if not success_condition:
if section_tracer.has_warnings:
Report.info(f"Warnings found: {section_tracer.warnings}")
if section_tracer.has_errors:
Report.info(f"Errors found: {section_tracer.errors}")
Report.failure(Tests.no_errors_and_warnings_found)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 7) Close the editor
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C18977330_NvCloth_AddClothSimulationToActor)

@ -12,7 +12,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE main
TEST_REQUIRES gpu
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor

@ -1,11 +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
"""
def init():
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')

@ -1,33 +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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
use_null_renderer = False # Use default renderer (needs gpu)
extra_cmdline_args = []
def test_C18977329_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform):
from . import C18977329_NvCloth_AddClothSimulationToMesh as test_module
self._run_test(request, workspace, editor, test_module, self.extra_cmdline_args, self.use_null_renderer)
def test_C18977330_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform):
from . import C18977330_NvCloth_AddClothSimulationToActor as test_module
self._run_test(request, workspace, editor, test_module, self.extra_cmdline_args, self.use_null_renderer)

@ -0,0 +1,32 @@
"""
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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import os
import sys
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
use_null_renderer = False # Use default renderer (needs gpu)
def test_NvCloth_AddClothSimulationToMesh(self, request, workspace, editor, launcher_platform):
from .tests import NvCloth_AddClothSimulationToMesh as test_module
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)
def test_NvCloth_AddClothSimulationToActor(self, request, workspace, editor, launcher_platform):
from .tests import NvCloth_AddClothSimulationToActor as test_module
self._run_test(request, workspace, editor, test_module, use_null_renderer = self.use_null_renderer)

@ -0,0 +1,84 @@
"""
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
"""
# Test case ID : C18977330
# Test Case Title : Add cloth simulation to an Actor
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
# fmt: on
def NvCloth_AddClothSimulationToActor():
"""
Summary:
Load level with Entity having Actor and Cloth components already setup. Verify that editor remains stable in Game mode.
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the level
2) Start the Tracer to catch any errors and warnings
3) Enter game mode
4) Wait in game mode some frames to let cloth simulation run
5) Verify there are no errors and warnings in the logs
6) Exit game mode
7) Close the editor
Note:
At the time of writing this test it was not possible to set a component property
with a dropdown, which is necessary to setup a Cloth component. Because of this limitation
the components of this test are not setup by the script and a level was provided instead.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
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
# Constants
FRAMES_IN_GAME_MODE = 200
helper.init_idle()
# 1) Load the level
helper.open_level("NvCloth", "NvCloth_AddClothSimulationToActor")
# 2) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 3) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 4) Wait in game mode some frames to let cloth simulation run
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
# 5) Verify there are no errors and warnings in the logs
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
Report.result(Tests.no_errors_and_warnings_found, success_condition)
if not success_condition:
if section_tracer.has_warnings:
Report.info(f"Warnings found: {section_tracer.warnings}")
if section_tracer.has_errors:
Report.info(f"Errors found: {section_tracer.errors}")
Report.failure(Tests.no_errors_and_warnings_found)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 7) Close the editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(NvCloth_AddClothSimulationToActor)

@ -0,0 +1,84 @@
"""
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
"""
# Test case ID : C18977329
# Test Case Title : Add cloth simulation to a Mesh
# fmt: off
class Tests:
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
no_errors_and_warnings_found = ("No errors and warnings found", "Found errors and warnings")
exit_game_mode = ("Exited game mode", "Failed to exit game mode")
# fmt: on
def NvCloth_AddClothSimulationToMesh():
"""
Summary:
Load level with Entity having Mesh and Cloth components already setup. Verify that editor remains stable in Game mode.
Expected Behavior:
The Editor is stable there are no warnings or errors.
Test Steps:
1) Load the level
2) Start the Tracer to catch any errors and warnings
3) Enter game mode
4) Wait in game mode some frames to let cloth simulation run
5) Verify there are no errors and warnings in the logs
6) Exit game mode
7) Close the editor
Note:
At the time of writing this test it was not possible to set a component property
with a dropdown, which is necessary to setup a Cloth component. Because of this limitation
the components of this test are not setup by the script and a level was provided instead.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorEntity
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
# Constants
FRAMES_IN_GAME_MODE = 200
helper.init_idle()
# 1) Load the level
helper.open_level("NvCloth", "NvCloth_AddClothSimulationToMesh")
# 2) Start the Tracer to catch any errors and warnings
with Tracer() as section_tracer:
# 3) Enter game mode
helper.enter_game_mode(Tests.enter_game_mode)
# 4) Wait in game mode some frames to let cloth simulation run
general.idle_wait_frames(FRAMES_IN_GAME_MODE)
# 5) Verify there are no errors and warnings in the logs
success_condition = not (section_tracer.has_errors or section_tracer.has_warnings)
Report.result(Tests.no_errors_and_warnings_found, success_condition)
if not success_condition:
if section_tracer.has_warnings:
Report.info(f"Warnings found: {section_tracer.warnings}")
if section_tracer.has_errors:
Report.info(f"Errors found: {section_tracer.errors}")
Report.failure(Tests.no_errors_and_warnings_found)
# 6) Exit game mode
helper.exit_game_mode(Tests.exit_game_mode)
# 7) Close the editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(NvCloth_AddClothSimulationToMesh)

@ -1,60 +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
"""
# Test case ID : C28798177
# Test Case Title : White Box Tool Component can be added to an Entity
# fmt:off
class Tests():
white_box_entity_created = ("White box entity created", "Failed to create white box entity")
white_box_component_enabled = ("White box component enabled", "Failed to enable white box component")
# fmt:on
def C28798177_WhiteBox_AddComponentToEntity():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import ImportPathHelper as imports
imports.init()
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# open level
helper.init_idle()
general.open_level("EmptyLevel")
# create white box entity and attach component
white_box_entity = init.create_white_box_entity()
white_box_mesh_component = init.create_white_box_component(white_box_entity)
init.create_white_box_handle(white_box_mesh_component)
# verify results
entity_id = general.find_editor_entity('WhiteBox')
Report.result(Tests.white_box_entity_created, entity_id.IsValid())
component_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', white_box_mesh_component)
Report.result(Tests.white_box_component_enabled, component_enabled)
# close editor
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C28798177_WhiteBox_AddComponentToEntity)

@ -1,70 +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
"""
# Test case ID : C28798205
# Test Case Title : From the White Box Component Card the White Box Mesh can be set to be invisible in Game View
# fmt:off
class Tests():
white_box_initially_visible = ("White box initially invisible", "White box not initially invisible")
white_box_set_to_invisible = ("White box set to invisible", "Failed to set white box to invisible")
# fmt:on
def C28798205_WhiteBox_SetInvisible():
# note: This automated test does not fully replicate the test case in Test Rail as it's
# not currently possible using the Hydra API to get an EntityComponentIdPair at runtime,
# in future game_mode will be activated and a runtime White Box Component queried
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import ImportPathHelper as imports
import editor_python_test_tools.hydra_editor_utils as hydra
imports.init()
import azlmbr.whitebox.api as api
import azlmbr.whitebox
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# open level
helper.init_idle()
general.open_level("EmptyLevel")
white_box_entity_name = 'WhiteBox-Visibility'
white_box_visibility_path = 'White Box Material|Visible'
# create white box entity and attach component
white_box_entity = init.create_white_box_entity(white_box_entity_name)
white_box_mesh_component = init.create_white_box_component(white_box_entity)
# verify preconditions
visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path)
Report.result(Tests.white_box_initially_visible, visible_property)
# verify setting visibility
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", white_box_mesh_component, white_box_visibility_path, False)
visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path)
Report.result(Tests.white_box_set_to_invisible, not visible_property)
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C28798205_WhiteBox_SetInvisible)

@ -1,109 +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
"""
# Test case ID : C29279329
# Test Case Title : White Box mesh shape can be changed with the Default Shape dropdown on the Component
# fmt:off
class Tests():
default_shape_cube = ("Default shape set to cube", "Default shape is not set to cube")
default_shape_tetrahedron = ("Default shape changed to tetrahedron", "Failed to change default shape to tetrahedron")
default_shape_icosahedron = ("Default shape changed to icosahedron", "Failed to change default shape to icosahedron")
default_shape_cylinder = ("Default shape changed to cylinder", "Failed to change default shape to cylinder")
default_shape_sphere = ("Default shape changed to sphere", "Failed to change default shape to sphere")
# fmt:on
critical_shape_check = ("Default shape has more than 0 sides", "default shape has 0 sides")
def C29279329_WhiteBox_SetDefaultShape():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import ImportPathHelper as imports
imports.init()
import azlmbr.whitebox.api as api
import azlmbr.bus as bus
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def check_shape_result(success_fail_tuple, condition):
result = Report.result(success_fail_tuple, condition)
if not result:
face_count = white_box_handle.MeshFaceCount()
Report.critical_result(critical_shape_check, face_count > 0)
Report.info(f"Face count = {face_count}")
shape_types = azlmbr.globals.property
# expected results
expected_results = {
shape_types.CUBE: 12,
shape_types.TETRAHEDRON: 4,
shape_types.ICOSAHEDRON: 20,
shape_types.CYLINDER: 64,
shape_types.SPHERE: 320
}
# open level
helper.init_idle()
general.open_level("EmptyLevel")
# create white box entity and attach component
white_box_entity = init.create_white_box_entity()
white_box_mesh_component = init.create_white_box_component(white_box_entity)
white_box_handle = init.create_white_box_handle(white_box_mesh_component)
# verify results
# cube
check_shape_result(
Tests.default_shape_cube,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.CUBE))
# tetrahedron
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.TETRAHEDRON)
check_shape_result(
Tests.default_shape_tetrahedron,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.TETRAHEDRON))
# icosahedron
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.ICOSAHEDRON)
check_shape_result(
Tests.default_shape_icosahedron,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.ICOSAHEDRON))
# cylinder
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.CYLINDER)
check_shape_result(
Tests.default_shape_cylinder,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.CYLINDER))
# sphere
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.SPHERE)
check_shape_result(
Tests.default_shape_sphere,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.SPHERE))
# close editor
helper.close_editor()
if __name__ == "__main__":
import ImportPathHelper as imports
imports.init()
from editor_python_test_tools.utils import Report
Report.start_test(C29279329_WhiteBox_SetDefaultShape)

@ -11,7 +11,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
NAME AutomatedTesting::WhiteBoxTests
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor

@ -1,350 +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
"""
# Imports
import os
import shutil
import json
import logging
import ly_test_tools.environment.file_system as fs
class FileManagement:
"""
File Management static class: Handles file backup and restoration.
Organizes backing up files into a single directory and
mapping the back up files to their original files.
Features accessible via the exposed decorator functions.
"""
# Static variables
MAX_BACKUPS = 10000 # Arbitrary number to cap same-file-name name generating
backup_folder_name = ".backup"
backup_folder_path = os.path.join(os.path.split(__file__)[0], backup_folder_name) # CWD plus backup folder name
file_map_name = "_filebackup_map.json" # JSON file to store original to back up file mappings
@staticmethod
def _load_file_map():
# type: () -> {str: str}
"""
Loads the FileManagement's json file map.
The returned dictionary has key: value sets such that:
keys: full path to an original file currently being backed up
values: name of the original file's backup file. Uses a numbering system for
storing multiple file names that are identical but located in different directories.
If there is no current json file or the file cannot be parsed, an empty dictionary is returned.
:return: Dictionary to map original file's to their back up file names.
"""
json_file_path = os.path.join(FileManagement.backup_folder_path, FileManagement.file_map_name)
if os.path.exists(json_file_path):
try:
with open(json_file_path, "r") as f:
return json.load(f)
except ValueError:
logging.info("Decoding JSON file {} failed. Empty dictionary used".format(json_file_path))
return {}
@staticmethod
def _save_file_map(file_map):
# type: ({str: str}) -> None
"""
Saves the [file_map] dictionary as a json file.
If [file_map] is empty, deletes the json file.
:param file_map: A dictionary mapping original file paths to their back up file names.
:return: None
"""
file_path = os.path.join(FileManagement.backup_folder_path, FileManagement.file_map_name)
if os.path.exists(file_path):
fs.unlock_file(file_path)
if len(file_map) > 0:
with open(file_path, "w") as f:
json.dump(file_map, f)
fs.lock_file(file_path)
else:
fs.delete([file_path], True, False)
@staticmethod
def _next_available_name(file_name, file_map):
# type: (str, {str: str}) -> str or None
"""
Generates the next available backup file name using a FILE_NAME_x.ext naming convention
where x is a number. Returns None if we've maxed out the numbering system.
:param file_name: The base file to generate a back up file name for.
:param file_map: The file_map for the FileManagement system
:return: str
"""
suffix, extension = file_name.split(".", 1)
for i in range(FileManagement.MAX_BACKUPS):
potential_name = suffix + "_" + str(i) + "." + extension
if potential_name not in file_map.values():
return potential_name
return None
@staticmethod
def _backup_file(file_name, file_path):
# type: (str, str) -> None
"""
Backs up the [file_name] located at [file_path] into the FileManagement's backup folder.
Leaves the backup file locked from writing privileges.
:param file_name: The file's name that will be backed up
:param file_path: The path to the file to back up
:return: None
"""
file_map = FileManagement._load_file_map()
backup_path = FileManagement.backup_folder_path
backup_file_name = "{}.bak".format(file_name)
backup_file = os.path.join(backup_path, backup_file_name)
# If backup directory DNE, make one
if not os.path.exists(backup_path):
os.mkdir(backup_path)
# If "traditional" backup file exists, delete it (myFile.txt.bak)
if os.path.exists(backup_file):
fs.delete([backup_file], True, False)
# Find my next storage name (myFile_1.txt.bak)
backup_storage_file_name = FileManagement._next_available_name(backup_file_name, file_map)
if backup_storage_file_name is None:
# If _next_available_name returns None, we have backed up MAX_BACKUPS of files name [file_name]
raise Exception(
"FileManagement class ran out of backups per name. Max: {}".format(FileManagement.MAX_BACKUPS)
)
backup_storage_file = os.path.join(backup_path, backup_storage_file_name)
if os.path.exists(backup_storage_file):
# This file should not exists, but if it does it's about to get clobbered!
fs.unlock_file(backup_storage_file)
# Create "traditional" backup file (myFile.txt.bak)
fs.create_backup(os.path.join(file_path, file_name), backup_path)
# Copy "traditional" backup file into storage backup (myFile_1.txt.bak)
FileManagement._copy_file(backup_file_name, backup_path, backup_storage_file_name, backup_path)
fs.lock_file(backup_storage_file)
# Delete "traditional" back up file
fs.unlock_file(backup_file)
fs.delete([backup_file], True, False)
# Update file map with new file
file_map[os.path.join(file_path, file_name)] = backup_storage_file_name
FileManagement._save_file_map(file_map)
# Unlock original file to get it ready to be edited by the test
fs.unlock_file(os.path.join(file_path, file_name))
@staticmethod
def _restore_file(file_name, file_path):
# type: (str, str) -> None
"""
Restores the [file_name] located at [file_path] which is backed up in FileManagement's backup folder.
Locks [file_name] from writing privileges when done restoring.
:param file_name: The Original file that was backed up, and now restored
:param file_path: The location of the original file that was backed up
:return: None
"""
file_map = FileManagement._load_file_map()
backup_path = FileManagement.backup_folder_path
src_file = os.path.join(file_path, file_name)
if src_file in file_map:
backup_file = os.path.join(backup_path, file_map[src_file])
if os.path.exists(backup_file):
fs.unlock_file(backup_file)
fs.unlock_file(src_file)
# Make temporary copy of backed up file to restore from
temp_file = "{}.bak".format(file_name)
FileManagement._copy_file(file_map[src_file], backup_path, temp_file, backup_path)
fs.restore_backup(src_file, backup_path)
fs.lock_file(src_file)
# Delete backup file
fs.delete([os.path.join(backup_path, temp_file)], True, False)
fs.delete([backup_file], True, False)
# Remove from file map
del file_map[src_file]
FileManagement._save_file_map(file_map)
if not os.listdir(backup_path):
# Delete backup folder if it's empty now
os.rmdir(backup_path)
@staticmethod
def _find_files(file_names, root_dir, search_subdirs=False):
# type: ([str], str, bool) -> {str:str}
"""
Searches the [root_dir] directory tree for each of the file names in [file_names]. Returns a dictionary
where keys = the entries in file_names, and their values are the paths they were found at.
Raises a runtime warning if not exactly one copy of each file is found.
:param file_names: A list of file_names to look for
:param root_dir: The root directory to begin the search
collect all file paths matching that file_name
:param search_subdirs: Optional boolean flag for searching sub-directories of [parent_path]
:return: A dictionary where keys are file names, and values are the file's path.
"""
file_list = {}
found_count = 0
for file_name in file_names:
file_list[file_name] = None
if search_subdirs:
# Search parent path for all file name arguments
for dir_path, _, dir_files in os.walk(root_dir):
for dir_file in dir_files:
if dir_file in file_list.keys():
if file_list[dir_file] is None:
file_list[dir_file] = dir_path
found_count += 1
else:
# Found multiple files with same name. Raise warning.
raise RuntimeWarning("Found multiple files with the name {}.".format(dir_file))
else:
for dir_file in os.listdir(root_dir):
if os.path.isfile(os.path.join(root_dir, dir_file)) and dir_file in file_names:
file_list[dir_file] = root_dir
not_found = [file_name for file_name in file_names if file_list[file_name] is None]
if not_found:
raise RuntimeWarning("Could not find the following files: {}".format(not_found))
return file_list
@staticmethod
def _copy_file(src_file, src_path, target_file, target_path):
# type: (str, str, str, str) -> None
"""
Copies the [src_file] at 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)
@staticmethod
def file_revert(file_name, parent_path=None, search_subdirs=False):
# type: (str, str, bool) -> function
"""
Function decorator:
Convenience version of file_revert_list for passing a single file
Calls file_revert_list on a list one [file_name]
:param file_name: The name of a file to backup (not path)
:param parent_path: The root directory to search for the [file_names] (relative to the dev folder).
Defaults to the project folder described by the inner function's workspace fixture
:param search_subdirs: A boolean option for searching sub-directories for the files in [file_names]
:return: function as per the function decorator pattern
"""
return FileManagement.file_revert_list([file_name], parent_path, search_subdirs)
@staticmethod
def file_revert_list(file_names, parent_path=None, search_subdirs=False):
# type: ([str], str, bool) -> function
"""
Function decorator:
Collects file names specified in [file_names] in the [parent_path] directory.
Backs up these files before executing the parameter to the "wrap" function.
If the search_subdirs flag is True, searches all subdirectories of [parent_path] for files.
:param file_names: A list of file names (not paths)
:param parent_path: The root directory to search for the [file_names] (relative to the dev folder).
Defaults to the project folder described by the inner function's workspace fixture
:param search_subdirs: A boolean option for searching sub-directories for the files in [file_names]
:return: function as per the function decorator pattern
"""
def wrap(func):
# type: (function) -> function
def inner(self, request, workspace, editor, launcher_platform):
# type: (...) -> None
"""
Inner function to wrap around decorated function. Function being decorated must match this
functions parameter order.
"""
root_path = parent_path
if root_path is not None:
root_path = os.path.join(workspace.paths.engine_root(), root_path)
else:
# Default to project folder (AutomatedTesting)
root_path = workspace.paths.project()
# Try to locate files
try:
file_list = FileManagement._find_files(file_names, root_path, search_subdirs)
except RuntimeWarning as w:
assert False, (
w.message
+ " Please check use of search_subdirs; make sure you are using the correct parent directory."
)
# Restore from backups
for file_name, file_path in file_list.items():
FileManagement._restore_file(file_name, file_path)
# Create backups for each discovered path for each specified filename
for file_name, file_path in file_list.items():
FileManagement._backup_file(file_name, file_path)
try:
# Run the test
func(self, request, workspace, editor, launcher_platform)
finally:
# Restore backups for each discovered path for each specified filename if they exist
for file_name, file_path in file_list.items():
FileManagement._restore_file(file_name, file_path)
return inner
return wrap
@staticmethod
def file_override(target_file, src_file, parent_path=None, search_subdirs=False):
# type: (str, str, str, bool) -> function
"""
Function decorator:
Searches for [target_file] and [src_file] in [parent_path](relative to the project dev folder)
and performs backup and file swapping. [target_file] is backed up and replaced with [src_file] before the
decorated function executes.
After the decorated function is executed [target_file] is restored to the state before being swapped.
If [parent_path] is not specified, the project folder described by the current workspace will be used.
If the search_subdirs flag is True, searches all subdirectories of [parent_path] for files.
:param target_file: The name of the file being backed up and swapped into
:param src_file: The name of the file to swap into [target_file]
:param parent_path: The root directory to search for the [file_names] (relative to the dev folder).
Defaults to the project folder described by the inner function's workspace fixture
:param search_subdirs: A boolean option for searching sub-directories for the files in [file_names]
:return: The decorated function
"""
def wrap(func):
def inner(self, request, workspace, editor, launcher_platform):
"""
Inner function to wrap around decorated function. Function being decorated must match this
functions parameter order.
"""
root_path = parent_path
if root_path is not None:
root_path = os.path.join(workspace.paths.engine_root(), root_path)
else:
# Default to project folder (AutomatedTesting)
root_path = workspace.paths.project()
# Try to locate both target and source files
try:
file_list = FileManagement._find_files([target_file, src_file], root_path, search_subdirs)
except RuntimeWarning as w:
assert False, (
w.message
+ " Please check use of search_subdirs; make sure you are using the correct parent directory."
)
FileManagement._restore_file(target_file, file_list[target_file])
FileManagement._backup_file(target_file, file_list[target_file])
FileManagement._copy_file(src_file, file_list[src_file], target_file, file_list[target_file])
try:
# Run Test
func(self, request, workspace, editor, launcher_platform)
finally:
FileManagement._restore_file(target_file, file_list[target_file])
return inner
return wrap

@ -1,11 +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
"""
def init():
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')

@ -1,36 +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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import sys
import os
from .FileManagement import FileManagement as fm
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_C28798177_WhiteBox_AddComponentToEntity(self, request, workspace, editor, launcher_platform):
from . import C28798177_WhiteBox_AddComponentToEntity as test_module
self._run_test(request, workspace, editor, test_module)
def test_C29279329_WhiteBox_SetDefaultShape(self, request, workspace, editor, launcher_platform):
from . import C29279329_WhiteBox_SetDefaultShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_C28798205_WhiteBox_SetInvisible(self, request, workspace, editor, launcher_platform):
from . import C28798205_WhiteBox_SetInvisible as test_module
self._run_test(request, workspace, editor, test_module)

@ -0,0 +1,35 @@
"""
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
"""
# This suite consists of all test cases that are passing and have been verified.
import pytest
import sys
import os
from ly_test_tools import LAUNCHERS
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_WhiteBox_AddComponentToEntity(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_AddComponentToEntity as test_module
self._run_test(request, workspace, editor, test_module)
def test_WhiteBox_SetDefaultShape(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_SetDefaultShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_WhiteBox_SetInvisible(self, request, workspace, editor, launcher_platform):
from .tests import WhiteBox_SetInvisible as test_module
self._run_test(request, workspace, editor, test_module)

@ -0,0 +1,55 @@
"""
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
"""
# Test case ID : C28798177
# Test Case Title : White Box Tool Component can be added to an Entity
# fmt:off
class Tests():
white_box_entity_created = ("White box entity created", "Failed to create white box entity")
white_box_component_enabled = ("White box component enabled", "Failed to enable white box component")
# fmt:on
def C28798177_WhiteBox_AddComponentToEntity():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# open level
helper.init_idle()
general.open_level("EmptyLevel")
# create white box entity and attach component
white_box_entity = init.create_white_box_entity()
white_box_mesh_component = init.create_white_box_component(white_box_entity)
init.create_white_box_handle(white_box_mesh_component)
# verify results
entity_id = general.find_editor_entity('WhiteBox')
Report.result(Tests.white_box_entity_created, entity_id.IsValid())
component_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', white_box_mesh_component)
Report.result(Tests.white_box_component_enabled, component_enabled)
# close editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(C28798177_WhiteBox_AddComponentToEntity)

@ -0,0 +1,104 @@
"""
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
"""
# Test case ID : C29279329
# Test Case Title : White Box mesh shape can be changed with the Default Shape dropdown on the Component
# fmt:off
class Tests():
default_shape_cube = ("Default shape set to cube", "Default shape is not set to cube")
default_shape_tetrahedron = ("Default shape changed to tetrahedron", "Failed to change default shape to tetrahedron")
default_shape_icosahedron = ("Default shape changed to icosahedron", "Failed to change default shape to icosahedron")
default_shape_cylinder = ("Default shape changed to cylinder", "Failed to change default shape to cylinder")
default_shape_sphere = ("Default shape changed to sphere", "Failed to change default shape to sphere")
# fmt:on
critical_shape_check = ("Default shape has more than 0 sides", "default shape has 0 sides")
def C29279329_WhiteBox_SetDefaultShape():
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import azlmbr.whitebox.api as api
import azlmbr.bus as bus
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def check_shape_result(success_fail_tuple, condition):
result = Report.result(success_fail_tuple, condition)
if not result:
face_count = white_box_handle.MeshFaceCount()
Report.critical_result(critical_shape_check, face_count > 0)
Report.info(f"Face count = {face_count}")
shape_types = azlmbr.globals.property
# expected results
expected_results = {
shape_types.CUBE: 12,
shape_types.TETRAHEDRON: 4,
shape_types.ICOSAHEDRON: 20,
shape_types.CYLINDER: 64,
shape_types.SPHERE: 320
}
# open level
helper.init_idle()
general.open_level("EmptyLevel")
# create white box entity and attach component
white_box_entity = init.create_white_box_entity()
white_box_mesh_component = init.create_white_box_component(white_box_entity)
white_box_handle = init.create_white_box_handle(white_box_mesh_component)
# verify results
# cube
check_shape_result(
Tests.default_shape_cube,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.CUBE))
# tetrahedron
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.TETRAHEDRON)
check_shape_result(
Tests.default_shape_tetrahedron,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.TETRAHEDRON))
# icosahedron
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.ICOSAHEDRON)
check_shape_result(
Tests.default_shape_icosahedron,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.ICOSAHEDRON))
# cylinder
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.CYLINDER)
check_shape_result(
Tests.default_shape_cylinder,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.CYLINDER))
# sphere
azlmbr.whitebox.request.bus.EditorWhiteBoxComponentRequestBus(
bus.Event, 'SetDefaultShape', white_box_mesh_component, shape_types.SPHERE)
check_shape_result(
Tests.default_shape_sphere,
white_box_handle.MeshFaceCount() == expected_results.get(shape_types.SPHERE))
# close editor
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(C29279329_WhiteBox_SetDefaultShape)

@ -0,0 +1,65 @@
"""
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
"""
# Test case ID : C28798205
# Test Case Title : From the White Box Component Card the White Box Mesh can be set to be invisible in Game View
# fmt:off
class Tests():
white_box_initially_visible = ("White box initially invisible", "White box not initially invisible")
white_box_set_to_invisible = ("White box set to invisible", "Failed to set white box to invisible")
# fmt:on
def C28798205_WhiteBox_SetInvisible():
# note: This automated test does not fully replicate the test case in Test Rail as it's
# not currently possible using the Hydra API to get an EntityComponentIdPair at runtime,
# in future game_mode will be activated and a runtime White Box Component queried
import os
import sys
from Gems.WhiteBox.Editor.Scripts import WhiteBoxInit as init
import editor_python_test_tools.hydra_editor_utils as hydra
import azlmbr.whitebox.api as api
import azlmbr.whitebox
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# open level
helper.init_idle()
general.open_level("EmptyLevel")
white_box_entity_name = 'WhiteBox-Visibility'
white_box_visibility_path = 'White Box Material|Visible'
# create white box entity and attach component
white_box_entity = init.create_white_box_entity(white_box_entity_name)
white_box_mesh_component = init.create_white_box_component(white_box_entity)
# verify preconditions
visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path)
Report.result(Tests.white_box_initially_visible, visible_property)
# verify setting visibility
editor.EditorComponentAPIBus(bus.Broadcast, "SetComponentProperty", white_box_mesh_component, white_box_visibility_path, False)
visible_property = hydra.get_component_property_value(white_box_mesh_component, white_box_visibility_path)
Report.result(Tests.white_box_set_to_invisible, not visible_property)
helper.close_editor()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(C28798205_WhiteBox_SetInvisible)

@ -33,6 +33,7 @@ MATERIAL_TYPE_PATH = os.path.join(
azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets",
"Materials", "Types", "StandardPBR.materialtype",
)
CACHE_FILE_EXTENSION = ".azmaterial"
def run():
@ -67,6 +68,7 @@ def run():
material_editor.save_document_as_child(document_id, target_path)
material_editor.wait_for_condition(lambda: os.path.exists(target_path), 2.0)
print(f"New asset created: {os.path.exists(target_path)}")
time.sleep(2.0)
# Verify if the newly created document is open
new_document_id = material_editor.open_material(target_path)
@ -101,22 +103,29 @@ def run():
expected_color = math.Color(0.25, 0.25, 0.25, 1.0)
material_editor.set_property(document_id, property_name, expected_color)
material_editor.save_document(document_id)
time.sleep(2.0)
# 7) Test Case: Saving as a New Material
# Assign new color to the material file and save the document as copy
expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0)
material_editor.set_property(document_id, property_name, expected_color_1)
target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1)
cache_file_name_1 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_1', '.material')
cache_file_1 = f"{cache_file_name_1[0]}{CACHE_FILE_EXTENSION}"
target_path_1_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_1)
material_editor.save_document_as_copy(document_id, target_path_1)
time.sleep(2.0)
material_editor.wait_for_condition(lambda: os.path.exists(target_path_1_cache), 4.0)
# 8) Test Case: Saving as a Child Material
# Assign new color to the material file save the document as child
expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0)
material_editor.set_property(document_id, property_name, expected_color_2)
target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2)
cache_file_name_2 = os.path.splitext(NEW_MATERIAL_1) # Example output: ('test_material_2', '.material')
cache_file_2 = f"{cache_file_name_2[0]}{CACHE_FILE_EXTENSION}"
target_path_2_cache = os.path.join(azlmbr.paths.devassets, "Cache", "pc", "materials", cache_file_2)
material_editor.save_document_as_child(document_id, target_path_2)
time.sleep(2.0)
material_editor.wait_for_condition(lambda: os.path.exists(target_path_2_cache), 4.0)
# Close/Reopen documents
material_editor.close_all_documents()

@ -7,8 +7,10 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
Tests that require a GPU in order to run.
"""
import datetime
import logging
import os
import zipfile
import pytest
@ -40,6 +42,33 @@ def golden_images_directory():
return golden_images_dir
def create_screenshots_archive(screenshot_path):
"""
Creates a new zip file archive at archive_path containing all files listed within archive_path.
:param screenshot_path: location containing the files to archive, the zip archive file will also be saved here.
:return: None, but creates a new zip file archive inside path containing all of the files inside archive_path.
"""
files_to_archive = []
# Search for .png and .ppm files to add to the zip archive file.
for (folder_name, sub_folders, file_names) in os.walk(screenshot_path):
for file_name in file_names:
if file_name.endswith(".png") or file_name.endswith(".ppm"):
file_path = os.path.join(folder_name, file_name)
files_to_archive.append(file_path)
# Setup variables for naming the zip archive file.
timestamp = datetime.datetime.now().timestamp()
formatted_timestamp = datetime.datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d_%H-%M-%S")
screenshots_file = os.path.join(screenshot_path, f'zip_archive_{formatted_timestamp}.zip')
# Write all of the valid .png and .ppm files to the archive file.
with zipfile.ZipFile(screenshots_file, 'w', compression=zipfile.ZIP_DEFLATED, allowZip64=True) as zip_archive:
for file_path in files_to_archive:
file_name = os.path.basename(file_path)
zip_archive.write(file_path, file_name)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
@pytest.mark.parametrize("level", ["auto_test"])
@ -53,8 +82,8 @@ class TestAllComponentsIndepthTests(object):
Tests that a basic rendering level setup can be created (lighting, meshes, materials, etc.).
"""
# Clear existing test screenshots before starting test.
test_screenshots = [os.path.join(
workspace.paths.project(), DEFAULT_SUBFOLDER_PATH, screenshot_name)]
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
test_screenshots = [os.path.join(screenshot_directory, screenshot_name)]
file_system.delete(test_screenshots, True, True)
golden_images = [os.path.join(golden_images_directory(), screenshot_name)]
@ -86,6 +115,8 @@ class TestAllComponentsIndepthTests(object):
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
compare_screenshots(test_screenshot, golden_screenshot)
create_screenshots_archive(screenshot_directory)
def test_LightComponent_ScreenshotMatchesGoldenImage(
self, request, editor, workspace, project, launcher_platform, level):
"""
@ -105,9 +136,10 @@ class TestAllComponentsIndepthTests(object):
"SpotLight_5.ppm",
"SpotLight_6.ppm",
]
screenshot_directory = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH)
test_screenshots = []
for screenshot in screenshot_names:
screenshot_path = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH, screenshot)
screenshot_path = os.path.join(screenshot_directory, screenshot)
test_screenshots.append(screenshot_path)
file_system.delete(test_screenshots, True, True)
@ -139,6 +171,8 @@ class TestAllComponentsIndepthTests(object):
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
compare_screenshots(test_screenshot, golden_screenshot)
create_screenshots_archive(screenshot_directory)
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@ -181,3 +215,39 @@ class TestPerformanceBenchmarkSuite(object):
aggregator = BenchmarkDataAggregator(workspace, logger, 'periodic')
aggregator.upload_metrics(rhi)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
@pytest.mark.system
class TestMaterialEditor(object):
@pytest.mark.parametrize("cfg_args", ["-rhi=dx12", "-rhi=Vulkan"])
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
def test_MaterialEditorLaunch_AllRHIOptionsSucceed(
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name, cfg_args):
"""
Tests each valid RHI option (Null RHI excluded) can be launched with the MaterialEditor.
Checks for the "Finished loading viewport configurtions." success message post lounch.
"""
expected_lines = ["Finished loading viewport configurtions."]
unexpected_lines = [
# "Trace::Assert",
# "Trace::Error",
"Traceback (most recent call last):",
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
generic_launcher,
editor_script="",
run_python="--runpython",
timeout=30,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=False,
null_renderer=False,
cfg_args=[cfg_args],
log_file_name="MaterialEditor.log"
)

@ -16,7 +16,6 @@ import editor_python_test_tools.hydra_test_utils as hydra
from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES
logger = logging.getLogger(__name__)
EDITOR_TIMEOUT = 120
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")
@ -175,7 +174,7 @@ class TestAtomEditorComponentsMain(object):
TEST_DIRECTORY,
editor,
"hydra_AtomEditorComponents_AddedToEntity.py",
timeout=EDITOR_TIMEOUT,
timeout=120,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
@ -236,7 +235,7 @@ class TestAtomEditorComponentsMain(object):
TEST_DIRECTORY,
editor,
"hydra_AtomEditorComponents_LightComponent.py",
timeout=EDITOR_TIMEOUT,
timeout=120,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
@ -299,9 +298,10 @@ class TestMaterialEditorBasicTests(object):
generic_launcher,
"hydra_AtomMaterialEditor_BasicTests.py",
run_python="--runpython",
timeout=80,
timeout=120,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
null_renderer=True,
log_file_name="MaterialEditor.log",
)

@ -51,8 +51,8 @@ class TestAutomationBase:
cls.asset_processor.teardown()
cls._kill_ly_processes()
def _run_test(self, request, workspace, editor, testcase_module, extra_cmdline_args=[], use_null_renderer=True):
def _run_test(self, request, workspace, editor, testcase_module, extra_cmdline_args=[], batch_mode=True,
autotest_mode=True, use_null_renderer=True):
test_starttime = time.time()
self.logger = logging.getLogger(__name__)
errors = []
@ -90,9 +90,13 @@ 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", f"-pythontestcase={request.node.originalname}"]
pycmd = ["--runpythontest", testcase_module_filepath, f"-pythontestcase={request.node.originalname}"]
if use_null_renderer:
pycmd += ["-rhi=null"]
if batch_mode:
pycmd += ["-BatchMode"]
if autotest_mode:
pycmd += ["-autotest_mode"]
pycmd += extra_cmdline_args
editor.args.extend(pycmd) # args are added to the WinLauncher start command
editor.start(backupFiles = False, launch_ap = False)

@ -11,8 +11,23 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::EditorTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_main and not REQUIRES_gpu"
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
PYTEST_MARKS "not REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Main_GPU
TEST_SUITE main
TEST_SERIAL
TEST_REQUIRES gpu
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
PYTEST_MARKS "REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@ -25,8 +40,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::EditorTests_Periodic
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_periodic and not REQUIRES_gpu"
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Periodic.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@ -36,12 +50,39 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Main_GPU
NAME AutomatedTesting::EditorTests_Sandbox
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Main_Optimized
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
PYTEST_MARKS "not REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Main_GPU_Optimized
TEST_SUITE main
TEST_SERIAL
TEST_REQUIRES gpu
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_main and REQUIRES_gpu"
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
PYTEST_MARKS "REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@ -51,11 +92,10 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Sandbox
NAME AutomatedTesting::EditorTests_Sandbox_Optimized
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_sandbox"
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox_Optimized.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@ -63,4 +103,5 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
COMPONENT
Editor
)
endif()

@ -5,30 +5,24 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C13660194 : Asset Browser - Filtering
"""
import os
import sys
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
import azlmbr.paths
class Tests:
asset_filtered = (
"Asset was filtered to in the Asset Browser",
"Failed to filter to the expected asset"
)
asset_type_filtered = (
"Expected asset type was filtered to in the Asset Browser",
"Failed to filter to the expected asset type"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.editor_test_helper import EditorTestHelper
def AssetBrowser_SearchFiltering():
class AssetBrowserSearchFilteringTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AssetBrowser_SearchFiltering", args=["level"])
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Asset Browser - Filtering
@ -60,7 +54,13 @@ class AssetBrowserSearchFilteringTest(EditorTestHelper):
:return: None
"""
self.incorrect_file_found = False
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def verify_files_appeared(model, allowed_asset_extentions, parent_index=QtCore.QModelIndex()):
indexes = [parent_index]
@ -74,25 +74,24 @@ class AssetBrowserSearchFilteringTest(EditorTestHelper):
and (cur_data.lower().split(".")[-1] not in allowed_asset_extentions)
and not cur_data[-1] == ")"
):
print(f"Incorrect file found: {cur_data}")
self.incorrect_file_found = True
indexes = list()
break
Report.info(f"Incorrect file found: {cur_data}")
return False
indexes.append(cur_index)
return True
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 2) Open Asset Browser
general.close_pane("Asset Browser")
general.open_pane("Asset Browser")
# 2) Open Asset Browser (if not opened already)
editor_window = pyside_utils.get_editor_main_window()
asset_browser_open = general.is_pane_visible("Asset Browser")
if not asset_browser_open:
Report.info("Opening Asset Browser")
action = pyside_utils.get_action_for_menu_path(editor_window, "Tools", "Asset Browser")
action.trigger()
else:
Report.info("Asset Browser is already open")
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
@ -103,10 +102,9 @@ class AssetBrowserSearchFilteringTest(EditorTestHelper):
asset_browser_tree = asset_browser.findChild(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")
model_index = pyside_utils.find_child_by_pattern(asset_browser_tree, "cedar.fbx")
pyside_utils.item_view_index_mouse_click(asset_browser_tree, model_index)
is_filtered = pyside_utils.wait_for_condition(
is_filtered = await pyside_utils.wait_for_condition(
lambda: asset_browser_tree.indexBelow(asset_browser_tree.currentIndex()) == QtCore.QModelIndex(), 5.0)
if is_filtered:
print("cedar.fbx asset is filtered in Asset Browser")
Report.result(Tests.asset_filtered, is_filtered)
# 4) Click the "X" in the search bar.
clear_search = asset_browser.findChild(QtWidgets.QToolButton, "ClearToolButton")
@ -122,40 +120,47 @@ class AssetBrowserSearchFilteringTest(EditorTestHelper):
tree.model().setData(animation_model_index, 2, Qt.CheckStateRole)
general.idle_wait(1.0)
# check asset types after clicking on Animation filter
verify_files_appeared(asset_browser_tree.model(), ["i_caf", "fbx", "xml", "animgraph", "motionset"])
print(f"Animation file type(s) is present in the file tree: {not self.incorrect_file_found}")
asset_type_filter = verify_files_appeared(asset_browser_tree.model(), ["i_caf", "fbx", "xml", "animgraph", "motionset"])
Report.result(Tests.asset_type_filtered, asset_type_filter)
# 6) Add additional filter(FileTag) from the filter menu
self.incorrect_file_found = False
line_edit.setText("FileTag")
filetag_model_index = await pyside_utils.wait_for_child_by_pattern(tree, "FileTag")
tree.model().setData(filetag_model_index, 2, Qt.CheckStateRole)
general.idle_wait(1.0)
# check asset types after clicking on FileTag filter
verify_files_appeared(
more_types_filtered = verify_files_appeared(
asset_browser_tree.model(), ["i_caf", "fbx", "xml", "animgraph", "motionset", "filetag"]
)
print(f"FileTag file type(s) and Animation file type(s) is present in the file tree: {not self.incorrect_file_found}")
Report.result(Tests.asset_type_filtered, more_types_filtered)
# 7) Remove one of the filtered asset types from the list of applied filters
self.incorrect_file_found = False
filter_layout = asset_browser.findChild(QtWidgets.QFrame, "filteredLayout")
animation_close_button = filter_layout.children()[1]
first_close_button = animation_close_button.findChild(QtWidgets.QPushButton, "closeTag")
first_close_button.click()
general.idle_wait(1.0)
# check asset types after removing Animation filter
verify_files_appeared(asset_browser_tree.model(), ["filetag"])
print(f"FileTag file type(s) is present in the file tree after removing Animation filter: {not self.incorrect_file_found}")
remove_filtered = verify_files_appeared(asset_browser_tree.model(), ["filetag"])
Report.result(Tests.asset_type_filtered, remove_filtered)
# 8) Remove all of the filter asset types from the list of filters
filetag_close_button = filter_layout.children()[1]
second_close_button = filetag_close_button.findChild(QtWidgets.QPushButton, "closeTag")
second_close_button.click()
# 9) Close the asset browser
asset_browser.close()
# Click off of the Asset Browser filter window to close it
QtTest.QTest.mouseClick(tree, Qt.LeftButton, Qt.NoModifier)
# 9) Restore Asset Browser tool state and
if not asset_browser_open:
Report.info("Closing Asset Browser")
general.close_pane("Asset Browser")
run_test()
if __name__ == "__main__":
test = AssetBrowserSearchFilteringTest()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(AssetBrowser_SearchFiltering)

@ -5,124 +5,119 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C13660195: Asset Browser - File Tree Navigation
"""
import os
import sys
from PySide2 import QtWidgets, QtTest, QtCore
import azlmbr.legacy.general as general
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
class AssetBrowserTreeNavigationTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AssetBrowser_TreeNavigation", args=["level"])
def run_test(self):
"""
Summary:
Verify if we are able to expand a file hierarchy in the Asset Browser and ScrollBar appears
appropriately.
Expected Behavior:
The folder list is expanded to display the children of the selected folder.
A scroll bar appears to allow scrolling up and down through the asset browser.
Assets are present in the Asset Browser.
Test Steps:
1) Open a new level
2) Open Asset Browser
3) Collapse all files initially
4) Get all Model Indexes
5) Expand each of the folder and verify if it is opened
6) Verify if the ScrollBar appears after expanding the tree
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
def collapse_expand_and_verify(model_index, hierarchy_level):
tree.collapse(model_index)
collapse_success = not tree.isExpanded(model_index)
self.log(f"Level {hierarchy_level} collapsed: {collapse_success}")
tree.expand(model_index)
expand_success = tree.isExpanded(model_index)
self.log(f"Level {hierarchy_level} expanded: {expand_success}")
return collapse_success and expand_success
# This is the hierarchy we are expanding (4 steps inside)
self.file_path = ("AutomatedTesting", "Assets", "ImageGradients", "image_grad_test_gsi.png")
# 1) Open a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 2) Open Asset Browser (if not opened already)
editor_window = pyside_utils.get_editor_main_window()
asset_browser_open = general.is_pane_visible("Asset Browser")
if not asset_browser_open:
self.log("Opening Asset Browser")
action = pyside_utils.get_action_for_menu_path(editor_window, "Tools", "Asset Browser")
action.trigger()
else:
self.log("Asset Browser is already open")
# 3) Collapse all files initially
main_window = editor_window.findChild(QtWidgets.QMainWindow)
asset_browser = pyside_utils.find_child_by_hierarchy(main_window, ..., "Asset Browser")
tree = pyside_utils.find_child_by_hierarchy(asset_browser, ..., "m_assetBrowserTreeViewWidget")
scroll_area = tree.findChild(QtWidgets.QWidget, "qt_scrollarea_vcontainer")
scroll_bar = scroll_area.findChild(QtWidgets.QScrollBar)
tree.collapseAll()
# 4) Get all Model Indexes
model_index_1 = pyside_utils.find_child_by_hierarchy(tree, self.file_path[0])
model_index_2 = pyside_utils.find_child_by_hierarchy(model_index_1, self.file_path[1])
model_index_3 = pyside_utils.find_child_by_hierarchy(model_index_2, self.file_path[2])
model_index_4 = pyside_utils.find_child_by_hierarchy(model_index_3, self.file_path[3])
# 5) Verify each level of the hierarchy to the file can be collapsed/expanded
self.test_success = collapse_expand_and_verify(model_index_1, 1) and self.test_success
self.test_success = collapse_expand_and_verify(model_index_2, 2) and self.test_success
self.test_success = collapse_expand_and_verify(model_index_3, 3) and self.test_success
self.log(f"Collapse/Expand tests: {self.test_success}")
# Select the asset
tree.scrollTo(model_index_4)
pyside_utils.item_view_index_mouse_click(tree, model_index_4)
# Verify if the currently selected item model index is same as the Asset Model index
# to prove that it is visible
asset_visible = tree.currentIndex() == model_index_4
self.test_success = asset_visible and self.test_success
self.log(f"Asset visibility test: {asset_visible}")
# 6) Verify if the ScrollBar appears after expanding the tree
scrollbar_visible = scroll_bar.isVisible()
self.test_success = scrollbar_visible and self.test_success
self.log(f"Scrollbar visibility test: {scrollbar_visible}")
# 7) Restore Asset Browser tool state
if not asset_browser_open:
self.log("Closing Asset Browser")
general.close_pane("Asset Browser")
test = AssetBrowserTreeNavigationTest()
test.run()
class Tests:
collapse_expand = (
"Asset Browser hierarchy successfully collapsed/expanded",
"Failed to collapse/expand Asset Browser hierarchy"
)
asset_visible = (
"Expected asset is visible in the Asset Browser hierarchy",
"Failed to find expected asset in the Asset Browser hierarchy"
)
scrollbar_visible = (
"Scrollbar is visible",
"Scrollbar was not found"
)
def AssetBrowser_TreeNavigation():
"""
Summary:
Verify if we are able to expand a file hierarchy in the Asset Browser and ScrollBar appears
appropriately.
Expected Behavior:
The folder list is expanded to display the children of the selected folder.
A scroll bar appears to allow scrolling up and down through the asset browser.
Assets are present in the Asset Browser.
Test Steps:
1) Open a simple level
2) Open Asset Browser
3) Collapse all files initially
4) Get all Model Indexes
5) Expand each of the folder and verify if it is opened
6) Verify if the ScrollBar appears after expanding the tree
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
from PySide2 import QtWidgets, QtTest, QtCore
import azlmbr.legacy.general as general
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def collapse_expand_and_verify(model_index, hierarchy_level):
tree.collapse(model_index)
collapse_success = not tree.isExpanded(model_index)
Report.info(f"Level {hierarchy_level} collapsed: {collapse_success}")
tree.expand(model_index)
expand_success = tree.isExpanded(model_index)
Report.info(f"Level {hierarchy_level} expanded: {expand_success}")
return collapse_success and expand_success
# This is the hierarchy we are expanding (4 steps inside)
file_path = ("AutomatedTesting", "Assets", "ImageGradients", "image_grad_test_gsi.png")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Open Asset Browser (if not opened already)
editor_window = pyside_utils.get_editor_main_window()
asset_browser_open = general.is_pane_visible("Asset Browser")
if not asset_browser_open:
Report.info("Opening Asset Browser")
action = pyside_utils.get_action_for_menu_path(editor_window, "Tools", "Asset Browser")
action.trigger()
else:
Report.info("Asset Browser is already open")
# 3) Collapse all files initially
main_window = editor_window.findChild(QtWidgets.QMainWindow)
asset_browser = pyside_utils.find_child_by_hierarchy(main_window, ..., "Asset Browser")
tree = pyside_utils.find_child_by_hierarchy(asset_browser, ..., "m_assetBrowserTreeViewWidget")
scroll_area = tree.findChild(QtWidgets.QWidget, "qt_scrollarea_vcontainer")
scroll_bar = scroll_area.findChild(QtWidgets.QScrollBar)
tree.collapseAll()
# 4) Get all Model Indexes
model_index_1 = pyside_utils.find_child_by_hierarchy(tree, file_path[0])
model_index_2 = pyside_utils.find_child_by_hierarchy(model_index_1, file_path[1])
model_index_3 = pyside_utils.find_child_by_hierarchy(model_index_2, file_path[2])
model_index_4 = pyside_utils.find_child_by_hierarchy(model_index_3, file_path[3])
# 5) Verify each level of the hierarchy to the file can be collapsed/expanded
Report.result(Tests.collapse_expand, collapse_expand_and_verify(model_index_1, 1) and
collapse_expand_and_verify(model_index_2, 2) and collapse_expand_and_verify(model_index_3, 3))
# Select the asset
tree.scrollTo(model_index_4)
pyside_utils.item_view_index_mouse_click(tree, model_index_4)
# Verify if the currently selected item model index is same as the Asset Model index
# to prove that it is visible
Report.result(Tests.asset_visible, tree.currentIndex() == model_index_4)
# 6) Verify if the ScrollBar appears after expanding the tree
Report.result(Tests.scrollbar_visible, scroll_bar.isVisible())
# 7) Restore Asset Browser tool state
if not asset_browser_open:
Report.info("Closing Asset Browser")
general.close_pane("Asset Browser")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AssetBrowser_TreeNavigation)

@ -5,33 +5,13 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C13751579: Asset Picker UI/UX
"""
import os
import sys
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.paths
import azlmbr.math as math
def AssetPicker_UI_UX():
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class AssetPickerUIUXTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AssetPicker_UI_UX", args=["level"])
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Verify the functionality of Asset Picker and UI/UX properties
@ -45,7 +25,7 @@ class AssetPickerUIUXTest(EditorTestHelper):
The asset picker is closed and the selected asset is assigned to the mesh component.
Test Steps:
1) Open a new level
1) Open a simple level
2) Create entity and add Mesh component
3) Access Entity Inspector
4) Click Asset Picker (Mesh Asset)
@ -68,10 +48,20 @@ class AssetPickerUIUXTest(EditorTestHelper):
:return: None
"""
self.file_path = ["AutomatedTesting", "Assets", "Objects", "Foliage"]
self.incorrect_file_found = False
self.mesh_asset = "cedar.azmodel"
self.prefix = ""
import os
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.legacy.general as general
import azlmbr.math as math
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
file_path = ["AutomatedTesting", "Assets", "Objects", "Foliage"]
def is_asset_assigned(component, interaction_option):
path = os.path.join("assets", "objects", "foliage", "cedar.azmodel")
@ -80,7 +70,7 @@ class AssetPickerUIUXTest(EditorTestHelper):
result = hydra.get_component_property_value(component, "Controller|Configuration|Mesh Asset")
expected_asset_str = expected_asset_id.invoke("ToString")
result_str = result.invoke("ToString")
print(f"Asset assigned for {interaction_option} option: {expected_asset_str == result_str}")
Report.info(f"Asset assigned for {interaction_option} option: {expected_asset_str == result_str}")
return expected_asset_str == result_str
def move_and_resize_widget(widget):
@ -89,9 +79,11 @@ class AssetPickerUIUXTest(EditorTestHelper):
x, y = initial_position.x() + 5, initial_position.y() + 5
widget.move(x, y)
curr_position = widget.pos()
move_success = curr_position.x() == x and curr_position.y() == y
self.test_success = move_success and self.test_success
self.log(f"Widget Move Test: {move_success}")
asset_picker_moved = (
"Asset Picker widget moved successfully",
"Failed to move Asset Picker widget"
)
Report.result(asset_picker_moved, curr_position.x() == x and curr_position.y() == y)
# Resize the widget and verify size
width, height = (
@ -99,9 +91,36 @@ class AssetPickerUIUXTest(EditorTestHelper):
widget.geometry().height() + 10,
)
widget.resize(width, height)
resize_success = widget.geometry().width() == width and widget.geometry().height() == height
self.test_success = resize_success and self.test_success
self.log(f"Widget Resize Test: {resize_success}")
asset_picker_resized = (
"Resized Asset Picker widget successfully",
"Failed to resize Asset Picker widget"
)
Report.result(asset_picker_resized, widget.geometry().width() == width and widget.geometry().height() ==
height)
def verify_expand(model_index, tree):
initially_collapsed = (
"Folder initially collapsed",
"Folder unexpectedly expanded"
)
expanded = (
"Folder expanded successfully",
"Failed to expand folder"
)
# Check initial collapse
Report.result(initially_collapsed, not tree.isExpanded(model_index))
# Expand at the specified index
tree.expand(model_index)
# Verify expansion
Report.result(expanded, tree.isExpanded(model_index))
def verify_collapse(model_index, tree):
collapsed = (
"Folder hierarchy collapsed successfully",
"Failed to collapse folder hierarchy"
)
tree.collapse(model_index)
Report.result(collapsed, not tree.isExpanded(model_index))
def verify_files_appeared(model, allowed_asset_extensions, parent_index=QtCore.QModelIndex()):
indices = [parent_index]
@ -115,22 +134,20 @@ class AssetPickerUIUXTest(EditorTestHelper):
and (cur_data.lower().split(".")[-1] not in allowed_asset_extensions)
and not cur_data[-1] == ")"
):
print(f"Incorrect file found: {cur_data}")
self.incorrect_file_found = True
indices = list()
break
Report.info(f"Incorrect file found: {cur_data}")
return False
indices.append(cur_index)
self.test_success = not self.incorrect_file_found and self.test_success
def print_message_prefix(message):
print(f"{self.prefix}: {message}")
return True
async def asset_picker(prefix, allowed_asset_extensions, asset, interaction_option):
async def asset_picker(allowed_asset_extensions, asset, interaction_option):
active_modal_widget = await pyside_utils.wait_for_modal_widget()
if active_modal_widget and self.prefix == "":
self.prefix = prefix
if active_modal_widget:
dialog = active_modal_widget.findChildren(QtWidgets.QDialog, "AssetPickerDialogClass")[0]
print_message_prefix(f"Asset Picker title for Mesh: {dialog.windowTitle()}")
asset_picker_title = (
"Asset Picker window is titled as expected",
"Asset Picker window has an unexpected title"
)
Report.result(asset_picker_title, dialog.windowTitle() == "Pick ModelAsset")
tree = dialog.findChildren(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")[0]
scroll_area = tree.findChild(QtWidgets.QWidget, "qt_scrollarea_vcontainer")
scroll_bar = scroll_area.findChild(QtWidgets.QScrollBar)
@ -138,39 +155,42 @@ class AssetPickerUIUXTest(EditorTestHelper):
# a) Collapse all the files initially and verify if scroll bar is not visible
tree.collapseAll()
await pyside_utils.wait_for_condition(lambda: not scroll_bar.isVisible(), 0.5)
print_message_prefix(
f"Scroll Bar is not visible before expanding the tree: {not scroll_bar.isVisible()}"
scroll_bar_hidden = (
"Scroll Bar is not visible before tree expansion",
"Scroll Bar is visible before tree expansion"
)
Report.result(scroll_bar_hidden, not scroll_bar.isVisible())
# Get Model Index of the file paths
model_index_1 = pyside_utils.find_child_by_pattern(tree, self.file_path[0])
print(model_index_1.model())
model_index_2 = pyside_utils.find_child_by_pattern(model_index_1, self.file_path[1])
model_index_1 = pyside_utils.find_child_by_pattern(tree, file_path[0])
model_index_2 = pyside_utils.find_child_by_pattern(model_index_1, file_path[1])
# b) Expand/Verify Top folder of file path
print_message_prefix(f"Top level folder initially collapsed: {not tree.isExpanded(model_index_1)}")
tree.expand(model_index_1)
print_message_prefix(f"Top level folder expanded: {tree.isExpanded(model_index_1)}")
verify_expand(model_index_1, tree)
# c) Expand/Verify Nested folder of file path
print_message_prefix(f"Nested folder initially collapsed: {not tree.isExpanded(model_index_2)}")
tree.expand(model_index_2)
print_message_prefix(f"Nested folder expanded: {tree.isExpanded(model_index_2)}")
verify_expand(model_index_2, tree)
# d) Verify if the ScrollBar appears after expanding folders
tree.expandAll()
await pyside_utils.wait_for_condition(lambda: scroll_bar.isVisible(), 0.5)
print_message_prefix(f"Scroll Bar appeared after expanding tree: {scroll_bar.isVisible()}")
scroll_bar_visible = (
"Scroll Bar is visible after tree expansion",
"Scroll Bar is not visible after tree expansion"
)
Report.result(scroll_bar_visible, scroll_bar.isVisible())
# e) Collapse Nested and Top Level folders and verify if collapsed
tree.collapse(model_index_2)
print_message_prefix(f"Nested folder collapsed: {not tree.isExpanded(model_index_2)}")
tree.collapse(model_index_1)
print_message_prefix(f"Top level folder collapsed: {not tree.isExpanded(model_index_1)}")
verify_collapse(model_index_2, tree)
verify_collapse(model_index_1, tree)
# f) Verify if the correct files are appearing in the Asset Picker
verify_files_appeared(tree.model(), allowed_asset_extensions)
print_message_prefix(f"Expected Assets populated in the file picker: {not self.incorrect_file_found}")
asset_picker_correct_files_appear = (
"Expected assets populated in the file picker",
"Found unexpected assets in the file picker"
)
Report.result(asset_picker_correct_files_appear, verify_files_appeared(tree.model(),
allowed_asset_extensions))
# While we are here we can also check if we can resize and move the widget
move_and_resize_widget(active_modal_widget)
@ -193,16 +213,10 @@ class AssetPickerUIUXTest(EditorTestHelper):
await pyside_utils.click_button_async(ok_button)
elif interaction_option == "enter":
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
self.prefix = ""
# 1) Open a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Create entity and add Mesh component
entity_position = math.Vector3(125.0, 136.0, 32.0)
@ -222,7 +236,7 @@ class AssetPickerUIUXTest(EditorTestHelper):
# Assign Mesh Asset via OK button
pyside_utils.click_button_async(attached_button)
await asset_picker("Mesh Asset", ["azmodel", "fbx"], "cedar (ModelAsset)", "ok")
await asset_picker(["azmodel", "fbx"], "cedar (ModelAsset)", "ok")
# 5) Verify if Mesh Asset is assigned
try:
@ -231,7 +245,11 @@ class AssetPickerUIUXTest(EditorTestHelper):
except pyside_utils.EventLoopTimeoutException as err:
print(err)
mesh_success = False
self.test_success = mesh_success and self.test_success
mesh_asset_assigned_ok = (
"Successfully assigned Mesh asset via OK button",
"Failed to assign Mesh asset via OK button"
)
Report.result(mesh_asset_assigned_ok, mesh_success)
# Clear Mesh Asset
hydra.get_set_test(entity, 0, "Controller|Configuration|Mesh Asset", None)
@ -242,7 +260,7 @@ class AssetPickerUIUXTest(EditorTestHelper):
# Assign Mesh Asset via Enter
pyside_utils.click_button_async(attached_button)
await asset_picker("Mesh Asset", ["azmodel", "fbx"], "cedar (ModelAsset)", "enter")
await asset_picker(["azmodel", "fbx"], "cedar (ModelAsset)", "enter")
# 5) Verify if Mesh Asset is assigned
try:
@ -251,8 +269,16 @@ class AssetPickerUIUXTest(EditorTestHelper):
except pyside_utils.EventLoopTimeoutException as err:
print(err)
mesh_success = False
self.test_success = mesh_success and self.test_success
mesh_asset_assigned_enter = (
"Successfully assigned Mesh asset via Enter button",
"Failed to assign Mesh asset via Enter button"
)
Report.result(mesh_asset_assigned_enter, mesh_success)
run_test()
if __name__ == "__main__":
test = AssetPickerUIUXTest()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(AssetPicker_UI_UX)

@ -5,36 +5,44 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C6351273: Create a new level
C6384955: Basic Workflow: Entity Manipulation in the Outliner
C16929880: Add Delete Components
C15167490: Save a level
C15167491: Export a level
"""
import os
import sys
from PySide2 import QtWidgets
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
import editor_python_test_tools.hydra_editor_utils as hydra
class TestBasicEditorWorkflows(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="BasicEditorWorkflows_LevelEntityComponent", args=["level"])
class Tests:
level_created = (
"New level created successfully",
"Failed to create new level"
)
new_entity_created = (
"New entity created successfully",
"Failed to create a new entity"
)
child_entity_created = (
"New child entity created successfully",
"Failed to create new child entity"
)
component_added = (
"Component added to entity successfully",
"Failed to add component to entity"
)
component_updated = (
"Component property updated successfully",
"Failed to update component property"
)
component_removed = (
"Component removed from entity successfully",
"Failed to remove component from entity"
)
level_saved_and_exported = (
"Level saved and exported successfully",
"Failed to save/export level"
)
def BasicEditorWorkflows_LevelEntityComponentCRUD():
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Open O3DE editor and check if basic Editor workflows are completable.
@ -55,6 +63,18 @@ class TestBasicEditorWorkflows(EditorTestHelper):
:return: None
"""
import os
from PySide2 import QtWidgets
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
def find_entity_by_name(entity_name):
search_filter = entity.SearchFilter()
search_filter.names = [entity_name]
@ -64,6 +84,7 @@ class TestBasicEditorWorkflows(EditorTestHelper):
return None
# 1) Create a new level
level = "tmp_level"
editor_window = pyside_utils.get_editor_main_window()
new_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "New Level")
pyside_utils.trigger_action_async(new_level_action)
@ -71,21 +92,17 @@ class TestBasicEditorWorkflows(EditorTestHelper):
new_level_dlg = active_modal_widget.findChild(QtWidgets.QWidget, "CNewLevelDialog")
if new_level_dlg:
if new_level_dlg.windowTitle() == "New Level":
self.log("New Level dialog opened")
Report.info("New Level dialog opened")
grp_box = new_level_dlg.findChild(QtWidgets.QGroupBox, "STATIC_GROUP1")
level_name = grp_box.findChild(QtWidgets.QLineEdit, "LEVEL")
level_name.setText(self.args["level"])
level_name.setText(level)
button_box = new_level_dlg.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
# Verify new level was created successfully
level_create_success = await pyside_utils.wait_for_condition(lambda: editor.EditorToolsApplicationRequestBus(
bus.Broadcast, "GetCurrentLevelName") == self.args["level"], 5.0)
self.test_success = level_create_success
self.log(f"Create and load new level: {level_create_success}")
# Execute EditorTestHelper setup since level was created outside of EditorTestHelper's methods
self.test_success = self.test_success and self.after_level_load()
bus.Broadcast, "GetCurrentLevelName") == level, 5.0)
Report.critical_result(Tests.level_created, level_create_success)
# 2) Delete existing entities, and create and manipulate new entities via Entity Inspector
search_filter = azlmbr.entity.SearchFilter()
@ -99,8 +116,7 @@ class TestBasicEditorWorkflows(EditorTestHelper):
# Find the new entity
parent_entity_id = find_entity_by_name("Entity1")
parent_entity_success = await pyside_utils.wait_for_condition(lambda: parent_entity_id is not None, 5.0)
self.test_success = self.test_success and parent_entity_success
self.log(f"New entity creation: {parent_entity_success}")
Report.critical_result(Tests.new_entity_created, parent_entity_success)
# TODO: Replace Hydra call to creates child entity and add components with context menu triggering - LYN-3951
# Create a new child entity
@ -111,29 +127,27 @@ class TestBasicEditorWorkflows(EditorTestHelper):
# Verify entity hierarchy
child_entity.get_parent_info()
self.test_success = self.test_success and child_entity.parent_id == parent_entity_id
self.log(f"Create entity hierarchy: {child_entity.parent_id == parent_entity_id}")
Report.result(Tests.child_entity_created, child_entity.parent_id == parent_entity_id)
# 3) Add/configure a component on an entity
# Add component and verify success
child_entity.add_component("Box Shape")
component_add_success = self.wait_for_condition(lambda: hydra.has_components(child_entity.id, ["Box Shape"]), 5.0)
self.test_success = self.test_success and component_add_success
self.log(f"Add component: {component_add_success}")
component_add_success = await pyside_utils.wait_for_condition(lambda: hydra.has_components(child_entity.id,
["Box Shape"]), 5.0)
Report.result(Tests.component_added, component_add_success)
# Update the component
dimensions_to_set = math.Vector3(16.0, 16.0, 16.0)
child_entity.get_set_test(0, "Box Shape|Box Configuration|Dimensions", dimensions_to_set)
box_shape_dimensions = hydra.get_component_property_value(child_entity.components[0], "Box Shape|Box Configuration|Dimensions")
self.test_success = self.test_success and box_shape_dimensions == dimensions_to_set
self.log(f"Component update: {box_shape_dimensions == dimensions_to_set}")
box_shape_dimensions = hydra.get_component_property_value(child_entity.components[0],
"Box Shape|Box Configuration|Dimensions")
Report.result(Tests.component_updated, box_shape_dimensions == dimensions_to_set)
# Remove the component
child_entity.remove_component("Box Shape")
component_rem_success = self.wait_for_condition(lambda: not hydra.has_components(child_entity.id, ["Box Shape"]),
5.0)
self.test_success = self.test_success and component_rem_success
self.log(f"Remove component: {component_rem_success}")
component_rem_success = await pyside_utils.wait_for_condition(lambda: not hydra.has_components(child_entity.id,
["Box Shape"]), 5.0)
Report.result(Tests.component_removed, component_rem_success)
# 4) Save the level
save_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save")
@ -143,12 +157,15 @@ class TestBasicEditorWorkflows(EditorTestHelper):
export_action = pyside_utils.get_action_for_menu_path(editor_window, "Game", "Export to Engine")
pyside_utils.trigger_action_async(export_action)
level_pak_file = os.path.join(
"AutomatedTesting", "Levels", self.args["level"], "level.pak"
"AutomatedTesting", "Levels", level, "level.pak"
)
export_success = self.wait_for_condition(lambda: os.path.exists(level_pak_file), 5.0)
self.test_success = self.test_success and export_success
self.log(f"Save and Export: {export_success}")
export_success = await pyside_utils.wait_for_condition(lambda: os.path.exists(level_pak_file), 5.0)
Report.result(Tests.level_saved_and_exported, export_success)
run_test()
if __name__ == "__main__":
test = TestBasicEditorWorkflows()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(BasicEditorWorkflows_LevelEntityComponentCRUD)

@ -5,37 +5,39 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C16929880: Add Delete Components
"""
import os
import sys
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class AddDeleteComponentsTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ComponentCRUD_Add_Delete_Components", args=["level"])
class Tests:
entity_created = (
"Entity created successfully",
"Failed to create entity"
)
box_component_added = (
"Box Shape component added to entity",
"Failed to add Box Shape component to entity"
)
mesh_component_added = (
"Mesh component added to entity",
"Failed to add Mesh component to entity"
)
mesh_component_deleted = (
"Mesh component removed from entity",
"Failed to remove Mesh component from entity"
)
mesh_component_delete_undo = (
"Mesh component removal was successfully undone",
"Failed to undo Mesh component removal"
)
def ComponentCRUD_Add_Delete_Components():
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Add/Delete Components to an entity.
Add/Delete Components to/from an entity.
Expected Behavior:
1) Components can be added to an entity.
@ -61,36 +63,43 @@ class AddDeleteComponentsTest(EditorTestHelper):
:return: None
"""
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
async def add_component(component_name):
pyside_utils.click_button_async(add_comp_btn)
popup = await pyside_utils.wait_for_popup_widget()
tree = popup.findChild(QtWidgets.QTreeView, "Tree")
component_index = pyside_utils.find_child_by_pattern(tree, component_name)
if component_index.isValid():
print(f"{component_name} found")
Report.info(f"{component_name} found")
tree.expand(component_index)
tree.setCurrentIndex(component_index)
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Create entity
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, entity.EntityId()
)
if entity_id.IsValid():
print("Entity Created")
Report.critical_result(Tests.entity_created, entity_id.IsValid())
# 3) Select the newly created entity
general.select_object("Entity2")
general.select_object("Entity1")
# Give the Entity Inspector time to fully create its contents
general.idle_wait(0.5)
@ -100,11 +109,11 @@ class AddDeleteComponentsTest(EditorTestHelper):
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
add_comp_btn = entity_inspector.findChild(QtWidgets.QPushButton, "m_addComponentButton")
await add_component("Box Shape")
print(f"Box Shape Component added: {hydra.has_components(entity_id, ['Box Shape'])}")
Report.result(Tests.box_component_added, hydra.has_components(entity_id, ['Box Shape']))
# 5) Add/verify Mesh component
await add_component("Mesh")
print(f"Mesh Component added: {hydra.has_components(entity_id, ['Mesh'])}")
Report.result(Tests.mesh_component_added, hydra.has_components(entity_id, ['Mesh']))
# 6) Delete Mesh Component
general.idle_wait(0.5)
@ -116,15 +125,17 @@ class AddDeleteComponentsTest(EditorTestHelper):
QtTest.QTest.mouseClick(mesh_frame, Qt.LeftButton, Qt.NoModifier)
QtTest.QTest.keyClick(mesh_frame, Qt.Key_Delete, Qt.NoModifier)
success = await pyside_utils.wait_for_condition(lambda: not hydra.has_components(entity_id, ['Mesh']), 5.0)
if success:
print(f"Mesh Component deleted: {not hydra.has_components(entity_id, ['Mesh'])}")
Report.result(Tests.mesh_component_deleted, success)
# 7) Undo deletion of component
QtTest.QTest.keyPress(entity_inspector, Qt.Key_Z, Qt.ControlModifier)
success = await pyside_utils.wait_for_condition(lambda: hydra.has_components(entity_id, ['Mesh']), 5.0)
if success:
print(f"Mesh Component deletion undone: {hydra.has_components(entity_id, ['Mesh'])}")
Report.result(Tests.mesh_component_delete_undo, success)
run_test()
if __name__ == "__main__":
test = AddDeleteComponentsTest()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(ComponentCRUD_Add_Delete_Components)

@ -7,27 +7,32 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
C6376081: Basic Function: Docked/Undocked Tools
"""
import os
import sys
from PySide2 import QtWidgets, QtTest, QtCore
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
class TestDockingBasicDockedTools(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="Docking_BasicDockedTools", args=["level"])
class Tests:
all_tools_docked = (
"The tools are all docked together in a tabbed widget",
"Failed to dock all tools together"
)
docked_outliner_works = (
"Entity Outliner works when docked, can select an Entity",
"Failed to select an Entity in the Outliner while docked"
)
docked_inspector_works = (
"Entity Inspector works when docked, Entity name changed",
"Failed to change Entity name in the Inspector while docked"
)
docked_console_works = (
"Console works when docked, sent a Console Command",
"Failed to send Console Command in the Console while docked"
)
def Docking_BasicDockedTools():
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Test that tools still work as expected when docked together.
@ -50,14 +55,19 @@ class TestDockingBasicDockedTools(EditorTestHelper):
:return: None
"""
# Create a level since we are going to be dealing with an Entity.
self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
from PySide2 import QtWidgets, QtTest, QtCore
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the Entity Outliner, Entity Inspector and Console tools are open
general.open_pane("Entity Outliner (PREVIEW)")
@ -101,12 +111,14 @@ class TestDockingBasicDockedTools(EditorTestHelper):
entity_inspector_parent = entity_inspector.parentWidget()
entity_outliner_parent = entity_outliner.parentWidget()
console_parent = console.parentWidget()
print(f"Entity Inspector parent = {entity_inspector_parent}, Entity Outliner parent = {entity_outliner_parent}, Console parent = {console_parent}")
return isinstance(entity_inspector_parent, QtWidgets.QStackedWidget) and (entity_inspector_parent == entity_outliner_parent) and (entity_outliner_parent == console_parent)
Report.info(f"Entity Inspector parent = {entity_inspector_parent}, Entity Outliner parent = "
f"{entity_outliner_parent}, Console parent = {console_parent}")
return isinstance(entity_inspector_parent, QtWidgets.QStackedWidget) and \
(entity_inspector_parent == entity_outliner_parent) and \
(entity_outliner_parent == console_parent)
success = await pyside_utils.wait_for(check_all_panes_tabbed, timeout=3.0)
if success:
print("The tools are all docked together in a tabbed widget")
Report.result(Tests.all_tools_docked, success)
# 2.1,2) Select an Entity in the Entity Outliner.
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
@ -116,8 +128,7 @@ class TestDockingBasicDockedTools(EditorTestHelper):
test_entity_index = pyside_utils.find_child_by_pattern(object_tree, entity_original_name)
object_tree.clearSelection()
object_tree.setCurrentIndex(test_entity_index)
if object_tree.currentIndex():
print("Entity Outliner works when docked, can select an Entity")
Report.result(Tests.docked_outliner_works, object_tree.currentIndex() == test_entity_index)
# 2.3,4) Change the name of the selected Entity via the Entity Inspector.
entity_inspector_name_field = entity_inspector.findChild(QtWidgets.QLineEdit, "m_entityNameEditor")
@ -125,14 +136,23 @@ class TestDockingBasicDockedTools(EditorTestHelper):
entity_inspector_name_field.setText(expected_new_name)
QtTest.QTest.keyClick(entity_inspector_name_field, QtCore.Qt.Key_Enter)
entity_new_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
if entity_new_name == expected_new_name:
print(f"Entity Inspector works when docked, Entity name changed to {entity_new_name}")
Report.result(Tests.docked_inspector_works, entity_new_name == expected_new_name)
# 2.5,6) Send a console command.
console_line_edit = console.findChild(QtWidgets.QLineEdit, "lineEdit")
console_line_edit.setText("Hello, world!")
console_line_edit.setText("t_Scale 2")
QtTest.QTest.keyClick(console_line_edit, QtCore.Qt.Key_Enter)
general.get_cvar("t_Scale")
Report.result(Tests.docked_console_works, general.get_cvar("t_Scale") == "2")
# Reset the altered cvar
console_line_edit.setText("t_Scale 1")
QtTest.QTest.keyClick(console_line_edit, QtCore.Qt.Key_Enter)
run_test()
if __name__ == "__main__":
test = TestDockingBasicDockedTools()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(Docking_BasicDockedTools)

@ -5,32 +5,36 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C1506881: Adding/Removing Event Groups
"""
import os
import sys
from PySide2 import QtWidgets
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class AddRemoveInputEventsTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="InputBindings_Add_Remove_Input_Events", args=["level"])
class Tests:
asset_editor_opened = (
"Successfully opened the Asset Editor",
"Failed to open the Asset Editor"
)
event_groups_added = (
"Successfully added event groups via +",
"Failed to add event groups"
)
single_event_group_deleted = (
"Successfully deleted an event group",
"Failed to delete event group"
)
all_event_groups_deleted = (
"Successfully deleted all event groups",
"Failed to delete all event groups"
)
asset_editor_closed = (
"Successfully closed the Asset Editor",
"Failed to close the Asset Editor"
)
def InputBindings_Add_Remove_Input_Events():
import editor_python_test_tools.pyside_utils as pyside_utils
@pyside_utils.wrap_async
async def run_test(self):
async def run_test():
"""
Summary:
Verify if we are able add/remove input events in inputbindings file.
@ -42,7 +46,7 @@ class AddRemoveInputEventsTest(EditorTestHelper):
Test Steps:
1) Open a new level
1) Open an existing level
2) Open Asset Editor
3) Access Asset Editor
4) Create a new .inputbindings file and add event groups
@ -61,6 +65,13 @@ class AddRemoveInputEventsTest(EditorTestHelper):
:return: None
"""
from PySide2 import QtWidgets
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def open_asset_editor():
general.open_pane("Asset Editor")
return general.is_pane_visible("Asset Editor")
@ -69,17 +80,12 @@ class AddRemoveInputEventsTest(EditorTestHelper):
general.close_pane("Asset Editor")
return not general.is_pane_visible("Asset Editor")
# 1) Open a new level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Open Asset Editor
print(f"Asset Editor opened: {open_asset_editor()}")
Report.result(Tests.asset_editor_opened, open_asset_editor())
# 3) Access Asset Editor
editor_window = pyside_utils.get_editor_main_window()
@ -103,8 +109,7 @@ class AddRemoveInputEventsTest(EditorTestHelper):
# 5) Verify if there are 3 elements in the Input Event Groups label
no_of_elements_label = input_event_groups.findChild(QtWidgets.QLabel, "DefaultLabel")
success = await pyside_utils.wait_for_condition(lambda: "3 elements" in no_of_elements_label.text(), 2.0)
if success:
print("New Event Groups added when + is clicked")
Report.result(Tests.event_groups_added, success)
# 6) Delete one event group
event = asset_editor_widget.findChildren(QtWidgets.QFrame, "<Unspecified Event>")[0]
@ -121,11 +126,11 @@ class AddRemoveInputEventsTest(EditorTestHelper):
input_event_group = input_event_groups[1]
no_of_elements_label = input_event_group.findChild(QtWidgets.QLabel, "DefaultLabel")
return no_of_elements_label.text()
return ""
return "";
success = await pyside_utils.wait_for_condition(lambda: "2 elements" in get_elements_label_text(asset_editor_widget), 2.0)
if success:
print("Event Group deleted when the Delete button is clicked on an Event Group")
success = await pyside_utils.wait_for_condition(lambda: "2 elements" in
get_elements_label_text(asset_editor_widget), 2.0)
Report.result(Tests.single_event_group_deleted, success)
# 8) Click on Delete button to delete all the Event Groups
# First QToolButton child of active input_event_groups is +, Second QToolButton is Delete
@ -141,13 +146,17 @@ class AddRemoveInputEventsTest(EditorTestHelper):
yes_button.click()
# 9) Verify if all the elements are deleted
success = await pyside_utils.wait_for_condition(lambda: "0 elements" in get_elements_label_text(asset_editor_widget), 2.0)
if success:
print("All event groups deleted on clicking the Delete button")
success = await pyside_utils.wait_for_condition(lambda: "0 elements" in
get_elements_label_text(asset_editor_widget), 2.0)
Report.result(Tests.all_event_groups_deleted, success)
# 10) Close Asset Editor
print(f"Asset Editor closed: {close_asset_editor()}")
Report.result(Tests.asset_editor_closed, close_asset_editor())
run_test()
if __name__ == "__main__":
test = AddRemoveInputEventsTest()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(InputBindings_Add_Remove_Input_Events)

@ -5,93 +5,78 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C24064529: Base Edit Menu Options
"""
import os
import sys
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
class TestEditMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="Menus_EditMenuOptions", args=["level"])
def run_test(self):
"""
Summary:
Interact with Edit Menu options and verify if all the options are working.
Expected Behavior:
The Edit menu functions normally.
Test Steps:
1) Create a temp level
2) Interact with Edit Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
edit_menu_options = [
("Undo",),
("Redo",),
("Duplicate",),
("Delete",),
("Select All",),
("Invert Selection",),
("Toggle Pivot Location",),
("Reset Entity Transform",),
("Reset Manipulator",),
("Reset Transform (Local)",),
("Reset Transform (World)",),
("Hide Selection",),
("Show All",),
("Modify", "Snap", "Snap angle"),
("Modify", "Transform Mode", "Move"),
("Modify", "Transform Mode", "Rotate"),
("Modify", "Transform Mode", "Scale"),
("Editor Settings", "Global Preferences"),
("Editor Settings", "Editor Settings Manager"),
("Editor Settings", "Keyboard Customization", "Customize Keyboard"),
("Editor Settings", "Keyboard Customization", "Export Keyboard Settings"),
("Editor Settings", "Keyboard Customization", "Import Keyboard Settings"),
]
# 1) Create and open the temp level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with Edit Menu options
def Menus_EditMenuOptions_Work():
"""
Summary:
Interact with Edit Menu options and verify if all the options are working.
Expected Behavior:
The Edit menu functions normally.
Test Steps:
1) Open an existing level
2) Interact with Edit Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
edit_menu_options = [
("Undo",),
("Redo",),
("Duplicate",),
("Delete",),
("Select All",),
("Invert Selection",),
("Toggle Pivot Location",),
("Reset Entity Transform",),
("Reset Manipulator",),
("Reset Transform (Local)",),
("Reset Transform (World)",),
("Hide Selection",),
("Show All",),
("Modify", "Snap", "Snap angle"),
("Modify", "Transform Mode", "Move"),
("Modify", "Transform Mode", "Rotate"),
("Modify", "Transform Mode", "Scale"),
("Editor Settings", "Global Preferences"),
("Editor Settings", "Editor Settings Manager"),
("Editor Settings", "Keyboard Customization", "Customize Keyboard"),
("Editor Settings", "Keyboard Customization", "Export Keyboard Settings"),
("Editor Settings", "Keyboard Customization", "Import Keyboard Settings"),
]
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Interact with Edit Menu options
editor_window = pyside_utils.get_editor_main_window()
for option in edit_menu_options:
try:
editor_window = pyside_utils.get_editor_main_window()
for option in edit_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", *option)
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", *option)
action.trigger()
action_triggered = True
except Exception as e:
self.test_success = False
action_triggered = False
print(e)
menu_action_triggered = (
f"{action.iconText()} action triggered successfully",
f"Failed to trigger {action.iconText()} action"
)
Report.result(menu_action_triggered, action_triggered)
if __name__ == "__main__":
test = TestEditMenuOptions()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(Menus_EditMenuOptions_Work)

@ -5,80 +5,69 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
class TestFileMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="file_menu_options: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with File Menu options and verify if all the options are working.
Expected Behavior:
The File menu functions normally.
Test Steps:
1) Open level
2) Interact with File Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
file_menu_options = [
("New Level",),
("Open Level",),
("Import",),
("Save",),
("Save As",),
("Save Level Statistics",),
("Edit Project Settings",),
("Edit Platform Settings",),
("New Project",),
("Open Project",),
("Show Log File",),
("Resave All Slices",),
("Exit",),
]
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with File Menu options
def Menus_FileMenuOptions_Work():
"""
Summary:
Interact with File Menu options and verify if all the options are working.
Expected Behavior:
The File menu functions normally.
Test Steps:
1) Open level
2) Interact with File Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
file_menu_options = [
("New Level",),
("Open Level",),
("Import",),
("Save",),
("Save As",),
("Save Level Statistics",),
("Edit Project Settings",),
("Edit Platform Settings",),
("New Project",),
("Open Project",),
("Show Log File",),
("Resave All Slices",),
("Exit",),
]
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Interact with File Menu options
editor_window = pyside_utils.get_editor_main_window()
for option in file_menu_options:
try:
editor_window = pyside_utils.get_editor_main_window()
for option in file_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "File", *option)
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
action = pyside_utils.get_action_for_menu_path(editor_window, "File", *option)
action.trigger()
action_triggered = True
except Exception as e:
self.test_success = False
action_triggered = False
print(e)
menu_action_triggered = (
f"{action.iconText()} action triggered successfully",
f"Failed to trigger {action.iconText()} action"
)
Report.result(menu_action_triggered, action_triggered)
if __name__ == "__main__":
test = TestFileMenuOptions()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(Menus_FileMenuOptions_Work)

@ -5,81 +5,66 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C24064534: The View menu options function normally
"""
import os
import sys
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
import editor_python_test_tools.pyside_utils as pyside_utils
class TestViewMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="Menus_EditMenuOptions", args=["level"])
def run_test(self):
"""
Summary:
Interact with View Menu options and verify if all the options are working.
Expected Behavior:
The View menu functions normally.
Test Steps:
1) Create a temp level
2) Interact with View Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
view_menu_options = [
("Center on Selection",),
("Show Quick Access Bar",),
("Viewport", "Configure Layout"),
("Viewport", "Go to Position"),
("Viewport", "Center on Selection"),
("Viewport", "Go to Location"),
("Viewport", "Remember Location"),
("Viewport", "Switch Camera"),
("Viewport", "Show/Hide Helpers"),
("Refresh Style",),
]
# 1) Create and open the temp level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with View Menu options
def Menus_ViewMenuOptions_Work():
"""
Summary:
Interact with View Menu options and verify if all the options are working.
Expected Behavior:
The View menu functions normally.
Test Steps:
1) Open an existing level
2) Interact with View Menu options
Note:
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import editor_python_test_tools.pyside_utils as pyside_utils
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
view_menu_options = [
("Center on Selection",),
("Show Quick Access Bar",),
("Viewport", "Configure Layout"),
("Viewport", "Go to Position"),
("Viewport", "Center on Selection"),
("Viewport", "Go to Location"),
("Viewport", "Remember Location"),
("Viewport", "Switch Camera"),
("Viewport", "Show/Hide Helpers"),
("Refresh Style",),
]
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Interact with View Menu options
editor_window = pyside_utils.get_editor_main_window()
for option in view_menu_options:
try:
editor_window = pyside_utils.get_editor_main_window()
for option in view_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "View", *option)
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
action = pyside_utils.get_action_for_menu_path(editor_window, "View", *option)
action.trigger()
action_triggered = True
except Exception as e:
self.test_success = False
action_triggered = False
print(e)
menu_action_triggered = (
f"{action.iconText()} action triggered successfully",
f"Failed to trigger {action.iconText()} action"
)
Report.result(menu_action_triggered, action_triggered)
if __name__ == "__main__":
test = TestViewMenuOptions()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(Menus_ViewMenuOptions_Work)

@ -0,0 +1,43 @@
"""
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 sys
import ly_test_tools.environment.file_system as file_system
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.fixture
def remove_test_level(request, workspace, project):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True)
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True)
request.addfinalizer(teardown)
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform,
remove_test_level):
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False)
@pytest.mark.REQUIRES_gpu
def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, workspace, editor, launcher_platform,
remove_test_level):
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False,
use_null_renderer=False)

@ -0,0 +1,75 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
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.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomationNoAutoTestMode(EditorTestSuite):
# Disable -autotest_mode and -BatchMode. Tests cannot run in -BatchMode due to UI interactions, and these tests
# interact with modal dialogs
global_extra_cmdline_args = []
class test_BasicEditorWorkflows_LevelEntityComponentCRUD(EditorSingleTest):
# Custom teardown to remove slice asset created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True)
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
@pytest.mark.REQUIRES_gpu
class test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(EditorSingleTest):
# Disable null renderer
use_null_renderer = False
# Custom teardown to remove slice asset created during test
def teardown(self, request, workspace, editor, editor_test_results, launcher_platform):
file_system.delete([os.path.join(workspace.paths.engine_root(), "AutomatedTesting", "Levels", "tmp_level")],
True, True)
from .EditorScripts import BasicEditorWorkflows_LevelEntityComponentCRUD as test_module
class test_InputBindings_Add_Remove_Input_Events(EditorSharedTest):
from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
class test_AssetPicker_UI_UX(EditorSharedTest):
from .EditorScripts import AssetPicker_UI_UX as test_module
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_main
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomationAutoTestMode(EditorTestSuite):
# Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions
global_extra_cmdline_args = ["-autotest_mode"]
class test_AssetBrowser_TreeNavigation(EditorSharedTest):
from .EditorScripts import AssetBrowser_TreeNavigation as test_module
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
class test_AssetBrowser_SearchFiltering(EditorSharedTest):
from .EditorScripts import AssetBrowser_SearchFiltering as test_module
class test_ComponentCRUD_Add_Delete_Components(EditorSharedTest):
from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module
class test_Menus_ViewMenuOptions_Work(EditorSharedTest):
from .EditorScripts import Menus_ViewMenuOptions as test_module
@pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208")
class test_Menus_FileMenuOptions_Work(EditorSharedTest):
from .EditorScripts import Menus_FileMenuOptions as test_module

@ -0,0 +1,62 @@
"""
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 sys
import ly_test_tools.environment.file_system as file_system
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.fixture
def remove_test_level(request, workspace, project):
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True)
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", "tmp_level")], True, True)
request.addfinalizer(teardown)
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_AssetBrowser_TreeNavigation(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetBrowser_TreeNavigation as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
def test_AssetBrowser_SearchFiltering(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetBrowser_SearchFiltering as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
@pytest.mark.skip(reason="Crashes Editor: ATOM-15493")
def test_AssetPicker_UI_UX(self, request, workspace, editor, launcher_platform):
from .EditorScripts import AssetPicker_UI_UX as test_module
self._run_test(request, workspace, editor, test_module, autotest_mode=False, batch_mode=False)
def test_ComponentCRUD_Add_Delete_Components(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ComponentCRUD_Add_Delete_Components as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
def test_InputBindings_Add_Remove_Input_Events(self, request, workspace, editor, launcher_platform):
from .EditorScripts import InputBindings_Add_Remove_Input_Events as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False, autotest_mode=False)
def test_Menus_ViewMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_ViewMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
@pytest.mark.skip(reason="Times out due to dialogs failing to dismiss: LYN-4208")
def test_Menus_FileMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_FileMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)

@ -0,0 +1,27 @@
"""
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 sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_Menus_EditMenuOptions_Work(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Menus_EditMenuOptions as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)
def test_Docking_BasicDockedTools(self, request, workspace, editor, launcher_platform):
from .EditorScripts import Docking_BasicDockedTools as test_module
self._run_test(request, workspace, editor, test_module, batch_mode=False)

@ -0,0 +1,26 @@
"""
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
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
@pytest.mark.SUITE_sandbox
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomationAutoTestMode(EditorTestSuite):
# Enable only -autotest_mode for these tests. Tests cannot run in -BatchMode due to UI interactions
global_extra_cmdline_args = ["-autotest_mode"]
class test_Docking_BasicDockedTools(EditorSharedTest):
from .EditorScripts import Docking_BasicDockedTools as test_module
class test_Menus_EditMenuOptions_Work(EditorSharedTest):
from .EditorScripts import Menus_EditMenuOptions as test_module

@ -1,89 +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
"""
"""
C13660195: Asset Browser - File Tree Navigation
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAssetBrowser(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C13660195")
@pytest.mark.SUITE_periodic
def test_AssetBrowser_TreeNavigation(self, request, editor, level, launcher_platform):
expected_lines = [
"Collapse/Expand tests: True",
"Asset visibility test: True",
"Scrollbar visibility test: True",
"AssetBrowser_TreeNavigation: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AssetBrowser_TreeNavigation.py",
expected_lines,
run_python="--runpython",
cfg_args=[level],
timeout=log_monitor_timeout
)
@pytest.mark.test_case_id("C13660194")
@pytest.mark.SUITE_periodic
def test_AssetBrowser_SearchFiltering(self, request, editor, level, launcher_platform):
expected_lines = [
"cedar.fbx asset is filtered in Asset Browser",
"Animation file type(s) is present in the file tree: True",
"FileTag file type(s) and Animation file type(s) is present in the file tree: True",
"FileTag file type(s) is present in the file tree after removing Animation filter: True",
]
unexpected_lines = [
"Asset Browser opened: False",
"Animation file type(s) is present in the file tree: False",
"FileTag file type(s) and Animation file type(s) is present in the file tree: False",
"FileTag file type(s) is present in the file tree after removing Animation filter: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AssetBrowser_SearchFiltering.py",
expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=[level],
auto_test_mode=False,
run_python="--runpython",
timeout=log_monitor_timeout,
)

@ -1,74 +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
"""
"""
C13751579: Asset Picker UI/UX
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 90
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAssetPicker(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C13751579", "C1508814")
@pytest.mark.SUITE_periodic
@pytest.mark.xfail # ATOM-15493
def test_AssetPicker_UI_UX(self, request, editor, level, launcher_platform):
expected_lines = [
"TestEntity Entity successfully created",
"Mesh component was added to entity",
"Entity has a Mesh component",
"Mesh Asset: Asset Picker title for Mesh: Pick ModelAsset",
"Mesh Asset: Scroll Bar is not visible before expanding the tree: True",
"Mesh Asset: Top level folder initially collapsed: True",
"Mesh Asset: Top level folder expanded: True",
"Mesh Asset: Nested folder initially collapsed: True",
"Mesh Asset: Nested folder expanded: True",
"Mesh Asset: Scroll Bar appeared after expanding tree: True",
"Mesh Asset: Nested folder collapsed: True",
"Mesh Asset: Top level folder collapsed: True",
"Mesh Asset: Expected Assets populated in the file picker: True",
"Widget Move Test: True",
"Widget Resize Test: True",
"Asset assigned for ok option: True",
"Asset assigned for enter option: True",
"AssetPicker_UI_UX: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"AssetPicker_UI_UX.py",
expected_lines,
cfg_args=[level],
run_python="--runpython",
auto_test_mode=False,
timeout=log_monitor_timeout,
)

@ -1,96 +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
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools._internal.pytest_plugin as internal_plugin
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestBasicEditorWorkflows(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C6351273", "C6384955", "C16929880", "C15167490", "C15167491")
@pytest.mark.SUITE_main
def test_BasicEditorWorkflows_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform):
# Skip test if running against Debug build
if "debug" in internal_plugin.build_directory:
pytest.skip("Does not execute against debug builds.")
expected_lines = [
"Create and load new level: True",
"New entity creation: True",
"Create entity hierarchy: True",
"Add component: True",
"Component update: True",
"Remove component: True",
"Save and Export: True",
"BasicEditorWorkflows_LevelEntityComponent: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"BasicEditorWorkflows_LevelEntityComponentCRUD.py",
expected_lines,
cfg_args=[level],
timeout=log_monitor_timeout,
auto_test_mode=False
)
@pytest.mark.test_case_id("C6351273", "C6384955", "C16929880", "C15167490", "C15167491")
@pytest.mark.SUITE_main
@pytest.mark.REQUIRES_gpu
def test_BasicEditorWorkflows_GPU_LevelEntityComponentCRUD(self, request, editor, level, launcher_platform):
# Skip test if running against Debug build
if "debug" in internal_plugin.build_directory:
pytest.skip("Does not execute against debug builds.")
expected_lines = [
"Create and load new level: True",
"New entity creation: True",
"Create entity hierarchy: True",
"Add component: True",
"Component update: True",
"Remove component: True",
"Save and Export: True",
"BasicEditorWorkflows_LevelEntityComponent: result=SUCCESS",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"BasicEditorWorkflows_LevelEntityComponentCRUD.py",
expected_lines,
cfg_args=[level],
timeout=log_monitor_timeout,
auto_test_mode=False,
null_renderer=False
)

@ -1,62 +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
"""
"""
C16929880: Add Delete Components
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestComponentCRUD(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C16929880", "C16877220")
@pytest.mark.SUITE_periodic
@pytest.mark.BAT
def test_ComponentCRUD_Add_Delete_Components(self, request, editor, level, launcher_platform):
expected_lines = [
"Entity Created",
"Box Shape found",
"Box Shape Component added: True",
"Mesh found",
"Mesh Component added: True",
"Mesh Component deleted: True",
"Mesh Component deletion undone: True",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"ComponentCRUD_Add_Delete_Components.py",
expected_lines,
cfg_args=[level],
auto_test_mode=False,
timeout=log_monitor_timeout
)

@ -1,55 +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
C6376081: Basic Function: Docked/Undocked Tools
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestDocking(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C6376081")
@pytest.mark.SUITE_sandbox
def test_Docking_BasicDockedTools(self, request, editor, level, launcher_platform):
expected_lines = [
"The tools are all docked together in a tabbed widget",
"Entity Outliner works when docked, can select an Entity",
"Entity Inspector works when docked, Entity name changed to DifferentName",
"Hello, world!" # This line verifies the Console is working while docked
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"Docking_BasicDockedTools.py",
expected_lines,
cfg_args=[level],
timeout=log_monitor_timeout,
)

@ -1,66 +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
"""
"""
C1506881: Adding/Removing Event Groups
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestInputBindings(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C1506881")
@pytest.mark.SUITE_periodic
def test_InputBindings_Add_Remove_Input_Events(self, request, editor, level, launcher_platform):
expected_lines = [
"Asset Editor opened: True",
"New Event Groups added when + is clicked",
"Event Group deleted when the Delete button is clicked on an Event Group",
"All event groups deleted on clicking the Delete button",
"Asset Editor closed: True",
]
unexpected_lines = [
"Asset Editor opened: False",
"Asset Editor closed: False",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"InputBindings_Add_Remove_Input_Events.py",
expected_lines,
unexpected_lines=unexpected_lines,
cfg_args=[level],
run_python="--runpython",
auto_test_mode=False,
timeout=log_monitor_timeout,
)

@ -1,132 +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
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import ly_test_tools.environment.process_utils as process_utils
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
log_monitor_timeout = 180
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestMenus(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
process_utils.kill_processes_named("o3de", ignore_extensions=True) # Kill ProjectManager windows
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C16780783", "C2174438")
@pytest.mark.SUITE_sandbox
def test_Menus_EditMenuOptions_Work(self, request, editor, level, launcher_platform):
expected_lines = [
"Undo Action triggered",
"Redo Action triggered",
"Duplicate Action triggered",
"Delete Action triggered",
"Select All Action triggered",
"Invert Selection Action triggered",
"Toggle Pivot Location Action triggered",
"Reset Entity Transform",
"Reset Manipulator",
"Reset Transform (Local) Action triggered",
"Reset Transform (World) Action triggered",
"Hide Selection Action triggered",
"Show All Action triggered",
"Snap angle Action triggered",
"Move Action triggered",
"Rotate Action triggered",
"Scale Action triggered",
"Global Preferences Action triggered",
"Editor Settings Manager Action triggered",
"Customize Keyboard Action triggered",
"Export Keyboard Settings Action triggered",
"Import Keyboard Settings Action triggered",
"Menus_EditMenuOptions: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"Menus_EditMenuOptions.py",
expected_lines,
cfg_args=[level],
run_python="--runpython",
timeout=log_monitor_timeout
)
@pytest.mark.test_case_id("C16780807")
@pytest.mark.SUITE_periodic
def test_Menus_ViewMenuOptions_Work(self, request, editor, level, launcher_platform):
expected_lines = [
"Center on Selection Action triggered",
"Show Quick Access Bar Action triggered",
"Configure Layout Action triggered",
"Go to Position Action triggered",
"Center on Selection Action triggered",
"Go to Location Action triggered",
"Remember Location Action triggered",
"Switch Camera Action triggered",
"Show/Hide Helpers Action triggered",
"Refresh Style Action triggered",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"Menus_ViewMenuOptions.py",
expected_lines,
cfg_args=[level],
run_python="--runpython",
timeout=log_monitor_timeout
)
@pytest.mark.test_case_id("C16780778")
@pytest.mark.SUITE_sandbox
@pytest.mark.xfail # LYN-4208
def test_Menus_FileMenuOptions_Work(self, request, editor, level, launcher_platform):
expected_lines = [
"New Level Action triggered",
"Open Level Action triggered",
"Import Action triggered",
"Save Action triggered",
"Save As Action triggered",
"Save Level Statistics Action triggered",
"Edit Project Settings Action triggered",
"Edit Platform Settings Action triggered",
"New Project Action triggered",
"Open Project Action triggered",
"Show Log File Action triggered",
"Resave All Slices Action triggered",
"Exit Action triggered",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"Menus_FileMenuOptions.py",
expected_lines,
cfg_args=[level],
run_python="--runpython",
timeout=log_monitor_timeout
)

@ -124,13 +124,14 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
COMPONENT
LargeWorlds
)
## LandscapeCanvas ##
ly_add_pytest(
NAME AutomatedTesting::LandscapeCanvasTests_Main
TEST_SERIAL
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/test_LandscapeCanvas_Main.py
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/TestSuite_Main.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@ -143,7 +144,20 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::LandscapeCanvasTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/test_LandscapeCanvas_Periodic.py
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/TestSuite_Periodic.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::LandscapeCanvasTests_Main_Optimized
TEST_SERIAL
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/TestSuite_Main_Optimized.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@ -153,11 +167,25 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
)
## GradientSignal ##
ly_add_pytest(
NAME AutomatedTesting::GradientSignalTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/test_GradientSignal_Periodic.py
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
AutomatedTesting.Assets
COMPONENT
LargeWorlds
)
ly_add_pytest(
NAME AutomatedTesting::GradientSignalTests_Periodic_Optimized
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/TestSuite_Periodic_Optimized.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor

@ -156,7 +156,7 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
# Setup collider entity with a PhysX Mesh
test_physx_mesh_asset_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics",
"c4044697_material_perfacematerialvalidation",
"Material_PerFaceMaterialGetsCorrectMaterial",
"test.pxmesh"), math.Uuid(), False)
# Remove/re-add component due to LYN-5496

@ -0,0 +1,56 @@
"""
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 pytest
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
@pytest.mark.xfail(reason="Optimized tests are experimental, we will enable xfail and monitor them temporarily.")
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
class test_GradientGenerators_Incompatibilities(EditorSharedTest):
from .EditorScripts import GradientGenerators_Incompatibilities as test_module
class test_GradientModifiers_Incompatibilities(EditorSharedTest):
from .EditorScripts import GradientModifiers_Incompatibilities as test_module
class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest):
from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module
class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest):
from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module
class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest):
from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module
class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest):
from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module
class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest):
from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
class test_GradientTransform_RequiresShape(EditorSharedTest):
from .EditorScripts import GradientTransform_RequiresShape as test_module
class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest):
from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module
class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module
class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module
class test_ImageGradient_RequiresShape(EditorSharedTest):
from .EditorScripts import ImageGradient_RequiresShape as test_module
class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest):
from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module

@ -1,103 +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
"""
"""
Tests that the Gradient Generator components are incompatible with Vegetation Area components
"""
import os
import pytest
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
gradient_generators = [
'Altitude Gradient',
'Constant Gradient',
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient',
'Shape Falloff Gradient',
'Slope Gradient',
'Surface Mask Gradient'
]
gradient_modifiers = [
'Dither Gradient Modifier',
'Gradient Mixer',
'Invert Gradient Modifier',
'Levels Gradient Modifier',
'Posterize Gradient Modifier',
'Smooth-Step Gradient Modifier',
'Threshold Gradient Modifier'
]
vegetation_areas = [
'Vegetation Layer Spawner',
'Vegetation Layer Blender',
'Vegetation Layer Blocker',
'Vegetation Layer Blocker (Mesh)'
]
all_gradients = gradient_modifiers + gradient_generators
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestGradientIncompatibilities(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
@pytest.mark.test_case_id('C2691648', 'C2691649', 'C2691650', 'C2691651',
'C2691653', 'C2691656', 'C2691657', 'C2691658',
'C2691647', 'C2691655')
@pytest.mark.SUITE_periodic
def test_GradientGenerators_Incompatibilities(self, request, editor, level, launcher_platform):
cfg_args = [level]
expected_lines = []
for gradient_generator in gradient_generators:
for vegetation_area in vegetation_areas:
expected_lines.append(f"{gradient_generator} is disabled before removing {vegetation_area} component")
expected_lines.append(f"{gradient_generator} is enabled after removing {vegetation_area} component")
expected_lines.append("GradientGeneratorIncompatibilities: result=SUCCESS")
hydra.launch_and_validate_results(request, test_directory, editor,
'GradientGenerators_Incompatibilities.py',
expected_lines=expected_lines, cfg_args=cfg_args)
@pytest.mark.test_case_id('C3416464', 'C3416546', 'C3961318', 'C3961319',
'C3961323', 'C3961324', 'C3980656', 'C3980657',
'C3980661', 'C3980662', 'C3980666', 'C3980667',
'C2691652')
@pytest.mark.SUITE_periodic
def test_GradientModifiers_Incompatibilities(self, request, editor, level, launcher_platform):
cfg_args = [level]
expected_lines = []
for gradient_modifier in gradient_modifiers:
for vegetation_area in vegetation_areas:
expected_lines.append(f"{gradient_modifier} is disabled before removing {vegetation_area} component")
expected_lines.append(f"{gradient_modifier} is enabled after removing {vegetation_area} component")
for conflicting_gradient in all_gradients:
expected_lines.append(f"{gradient_modifier} is disabled before removing {conflicting_gradient} component")
expected_lines.append(f"{gradient_modifier} is enabled after removing {conflicting_gradient} component")
expected_lines.append("GradientModifiersIncompatibilities: result=SUCCESS")
hydra.launch_and_validate_results(request, test_directory, editor,
'GradientModifiers_Incompatibilities.py',
expected_lines=expected_lines, cfg_args=cfg_args)

@ -1,118 +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
"""
import os
import pytest
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip('ly_test_tools')
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
test_directory = os.path.join(os.path.dirname(__file__), 'EditorScripts')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestGradientPreviewSettings(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id('C3980668', 'C2676825', 'C2676828', 'C2676822', 'C3416547', 'C3961320', 'C3961325',
'C3980658', 'C3980663')
@pytest.mark.SUITE_periodic
def test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(self, request, editor, level, launcher_platform):
expected_lines = [
"Perlin Noise Gradient has Preview pinned to own Entity result: SUCCESS",
"Random Noise Gradient has Preview pinned to own Entity result: SUCCESS",
"FastNoise Gradient has Preview pinned to own Entity result: SUCCESS",
"Dither Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"Invert Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"Levels Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"Posterize Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"Smooth-Step Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"Threshold Gradient Modifier has Preview pinned to own Entity result: SUCCESS",
"GradientPreviewSettings_DefaultPinnedEntity: result=SUCCESS"
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"GradientPreviewSettings_DefaultPinnedEntityIsSelf.py",
expected_lines,
cfg_args=[level]
)
@pytest.mark.test_case_id("C2676829", "C3961326", "C3980659", "C3980664", "C3980669", "C3416548", "C2676823",
"C3961321", "C2676826")
@pytest.mark.SUITE_periodic
def test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(self, request, editor, level,
launcher_platform):
expected_lines = [
"Random Noise Gradient entity Created",
"Entity has a Random Noise Gradient component",
"Entity has a Gradient Transform Modifier component",
"Entity has a Box Shape component",
"Random Noise Gradient Preview Settings|Pin Preview to Shape: SUCCESS",
"Random Noise Gradient --- Preview Position set to world origin",
"Random Noise Gradient --- Preview Size set to (1, 1, 1)",
"Levels Gradient Modifier entity Created",
"Entity has a Levels Gradient Modifier component",
"Levels Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Levels Gradient Modifier --- Preview Position set to world origin",
"Posterize Gradient Modifier entity Created",
"Entity has a Posterize Gradient Modifier component",
"Posterize Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Posterize Gradient Modifier --- Preview Position set to world origin",
"Smooth-Step Gradient Modifier entity Created",
"Entity has a Smooth-Step Gradient Modifier component",
"Smooth-Step Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Smooth-Step Gradient Modifier --- Preview Position set to world origin",
"Threshold Gradient Modifier entity Created",
"Entity has a Threshold Gradient Modifier component",
"Threshold Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Threshold Gradient Modifier --- Preview Position set to world origin",
"FastNoise Gradient entity Created",
"Entity has a FastNoise Gradient component",
"FastNoise Gradient Preview Settings|Pin Preview to Shape: SUCCESS",
"FastNoise Gradient --- Preview Position set to world origin",
"FastNoise Gradient --- Preview Size set to (1, 1, 1)",
"Dither Gradient Modifier entity Created",
"Entity has a Dither Gradient Modifier component",
"Dither Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Dither Gradient Modifier --- Preview Position set to world origin",
"Dither Gradient Modifier --- Preview Size set to (1, 1, 1)",
"Invert Gradient Modifier entity Created",
"Entity has a Invert Gradient Modifier component",
"Invert Gradient Modifier Preview Settings|Pin Preview to Shape: SUCCESS",
"Invert Gradient Modifier --- Preview Position set to world origin",
"Perlin Noise Gradient entity Created",
"Entity has a Perlin Noise Gradient component",
"Perlin Noise Gradient Preview Settings|Pin Preview to Shape: SUCCESS",
"Perlin Noise Gradient --- Preview Position set to world origin",
"Perlin Noise Gradient --- Preview Size set to (1, 1, 1)",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin.py",
expected_lines,
cfg_args=[level]
)

@ -1,86 +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
"""
import os
import pytest
import logging
# Bail on the test if ly_test_tools doesn't exist.
pytest.importorskip("ly_test_tools")
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
test_directory = os.path.join(os.path.dirname(__file__), "EditorScripts")
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['tmp_level'])
@pytest.mark.usefixtures("automatic_process_killer")
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestGradientSampling(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project, level):
def teardown():
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
request.addfinalizer(teardown)
file_system.delete([os.path.join(workspace.paths.engine_root(), project, "Levels", level)], True, True)
@pytest.mark.test_case_id("C3526311")
@pytest.mark.SUITE_periodic
def test_GradientSampling_GradientReferencesAddRemoveSuccessfully(self, request, editor, level, launcher_platform):
expected_lines = [
"Entity has a Random Noise Gradient component",
"Entity has a Gradient Transform Modifier component",
"Entity has a Box Shape component",
"Entity has a Dither Gradient Modifier component",
"Gradient Generator is pinned to the Dither Gradient Modifier successfully",
"Gradient Generator is cleared from the Dither Gradient Modifier successfully",
"Entity has a Invert Gradient Modifier component",
"Gradient Generator is pinned to the Invert Gradient Modifier successfully",
"Gradient Generator is cleared from the Invert Gradient Modifier successfully",
"Entity has a Levels Gradient Modifier component",
"Gradient Generator is pinned to the Levels Gradient Modifier successfully",
"Gradient Generator is cleared from the Levels Gradient Modifier successfully",
"Entity has a Posterize Gradient Modifier component",
"Gradient Generator is pinned to the Posterize Gradient Modifier successfully",
"Gradient Generator is cleared from the Posterize Gradient Modifier successfully",
"Entity has a Smooth-Step Gradient Modifier component",
"Gradient Generator is pinned to the Smooth-Step Gradient Modifier successfully",
"Gradient Generator is cleared from the Smooth-Step Gradient Modifier successfully",
"Entity has a Threshold Gradient Modifier component",
"Gradient Generator is pinned to the Threshold Gradient Modifier successfully",
"Gradient Generator is cleared from the Threshold Gradient Modifier successfully",
]
unexpected_lines = [
"Failed to pin Gradient Generator to the Dither Gradient Modifier",
"Failed to clear Gradient Generator from the Dither Gradient Modifier",
"Failed to pin Gradient Generator to the Invert Gradient Modifier",
"Failed to clear Gradient Generator from the Invert Gradient Modifier",
"Failed to pin Gradient Generator to the Levels Gradient Modifier",
"Failed to clear Gradient Generator from the Levels Gradient Modifier",
"Failed to pin Gradient Generator to the Posterize Gradient Modifier",
"Failed to clear Gradient Generator from the Posterize Gradient Modifier",
"Failed to pin Gradient Generator to the Smooth-Step Gradient Modifier",
"Failed to clear Gradient Generator from the Smooth-Step Gradient Modifier",
"Failed to pin Gradient Generator to the Threshold Gradient Modifier",
"Failed to clear Gradient Generator from the Threshold Gradient Modifier",
]
hydra.launch_and_validate_results(
request,
test_directory,
editor,
"GradientSampling_GradientReferencesAddRemoveSuccessfully.py",
expected_lines,
unexpected_lines,
cfg_args=[level]
)

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save