Integrating latest 47acbe8

This commit is contained in:
alexpete
2021-03-25 13:57:57 -07:00
parent 448c549698
commit 75dc720198
10312 changed files with 2711566 additions and 671451 deletions
@@ -0,0 +1,113 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C17211213: Assign Dynamic Slice
https://testrail.agscollab.com/index.php?/cases/view/17211213
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.entity as entity
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
from PySide2 import QtWidgets
from PySide2.QtTest import QTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class AssignDynamicSlice(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="assign dynamic slice", args=["level"])
def run_test(self):
"""
Summary:
Dynamic slice can be assigned to a spawner on an entity
Expected Behavior:
Dynamic slice is present in the file picker, can be added to
components, and is visible in game mode when spawned
Test Steps:
0) Open temporary test level
1) Create an entity with a spawner component
2) Add dynamic slice to the spawner component C17211213_visual_thunderbolt
3) Set Spawn on activate to enabled
4) Enter game mode
5) Verify that the dynamic slice spawned and is visible in game mode
Note:
- This test file must be called from the Lumberyard 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 set_dynamic_slice_by_asset_path(entity_obj, asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, 'GetAssetIdByPath', asset_path, math.Uuid(), False)
entity_obj.get_set_test(0, "Dynamic slice", asset_id)
def set_spawn_checkbox_enabled():
general.idle_wait(0.5)
editor_window = pyside_utils.get_editor_main_window()
entity_inspector = editor_window.findChildren(QtWidgets.QDockWidget, "Entity Inspector")[0]
spawn_activate_frame = entity_inspector.findChildren(QtWidgets.QFrame, "Spawn on activate")[0]
checkbox = spawn_activate_frame.findChildren(QtWidgets.QCheckBox)[0]
checkbox.click()
# 0) Open temporary test level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(3.0)
# 1) Create an entity with a spawner component
entity_postition = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Spawner"]
spawner_entity = editor_utils.Entity("Spawner")
spawner_entity.create_entity(entity_postition, components_to_add)
# 2) Add dynamic slice to the spawner component C17211213_visual_thunderbolt
thunderbolt_path = os.path.join("slices", "Test", "C17211213_visual_thunderbolt.dynamicslice")
set_dynamic_slice_by_asset_path(spawner_entity, thunderbolt_path)
# 3) Set Spawn on activate to enabled
set_spawn_checkbox_enabled()
# 4) Enter game mode
general.enter_game_mode()
general.idle_wait(0.5)
# 5) Verify that the dynamic slice spawned and is visible in game mode
slice_name = "thunderbolt"
thunderbolt_id = general.find_game_entity(slice_name)
check_valid = thunderbolt_id.IsValid()
assert check_valid, f"No entity found for {slice_name}"
if check_valid:
print(f"Entity found for {slice_name}")
general.exit_game_mode()
test = AssignDynamicSlice()
test.run()
@@ -0,0 +1,140 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6376081: Basic Function: Docked/Undocked Tools
https://testrail.agscollab.com/index.php?/cases/view/6376081
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtCore, QtWidgets, QtTest
class TestBasicDockedTools(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_docked_tools: ", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Test that tools still work as expected when docked together.
Expected Behavior:
Multiple tools can be docked together.
Tools function while docked together and the main editor responds appropriately.
Test Steps:
1) Open the tools and dock them together in a floating tabbed widget.
2) Perform actions in the docked tools to verify they still work as expected.
2.1) Select the Entity Outliner in the floating window.
2.2) Select an Entity in the Entity Outliner.
2.3) Select the Entity Inspector in the floating window.
2.4) Change the name of the selected Entity via the Entity Inspector.
2.5) Select the Console inside the floating window.
2.6) Send a console command.
2.7) Check the Editor to verify all changes were made.
: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=True,
)
# Make sure the Entity Outliner, Entity Inspector and Console tools are open
general.open_pane("Entity Outliner (PREVIEW)")
general.open_pane("Entity Inspector")
general.open_pane("Console")
# Create an Entity to test with
entity_original_name = 'MyTestEntity'
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
editor.EditorEntityAPIBus(bus.Event, 'SetName', entity_id, entity_original_name)
editor_window = pyside_utils.get_editor_main_window()
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
# 1) Open the tools and dock them together in a floating tabbed widget.
# We drag/drop it over the viewport since it doesn't allow docking, so this will undock it
render_overlay = editor_window.findChild(QtWidgets.QWidget, "renderOverlay")
pyside_utils.drag_and_drop(entity_outliner, render_overlay)
# We need to grab a new reference to the Entity Outliner QDockWidget because when it gets moved
# to the floating window, its parent changes so the wrapped intance we had becomes invalid
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
# Dock the Entity Inspector tabbed with the floating Entity Outliner
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
pyside_utils.drag_and_drop(entity_inspector, entity_outliner)
# We need to grab a new reference to the Entity Inspector QDockWidget because when it gets moved
# to the floating window, its parent changes so the wrapped intance we had becomes invalid
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
# Dock the Console tabbed with the floating Entity Inspector
console = editor_window.findChild(QtWidgets.QDockWidget, "Console")
pyside_utils.drag_and_drop(console, entity_inspector)
# Check to ensure all the tools are parented to the same QStackedWidget
def check_all_panes_tabbed():
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
console = editor_window.findChild(QtWidgets.QDockWidget, "Console")
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)
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")
# 2.1,2) Select an Entity in the Entity Outliner.
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
console = editor_window.findChild(QtWidgets.QDockWidget, "Console")
object_tree = entity_outliner.findChild(QtWidgets.QTreeView, "m_objectTree")
test_entity_index = pyside_utils.find_child_by_pattern(object_tree, entity_original_name)
object_tree.clearSelection()
object_tree.setCurrentIndex(test_entity_index)
selected_object_names = general.get_names_of_selected_objects()
if len(selected_object_names) == 1 and selected_object_names[0] == entity_original_name:
print("Entity Outliner works when docked, can select an Entity")
# 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")
expected_new_name = "DifferentName"
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}")
# 2.5,6) Send a console command.
console_line_edit = console.findChild(QtWidgets.QLineEdit, "lineEdit")
console_line_edit.setText("e_Vegetation=1")
QtTest.QTest.keyClick(console_line_edit, QtCore.Qt.Key_Enter)
test = TestBasicDockedTools()
test.run()
@@ -0,0 +1,134 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C24064529: Base Edit Menu Options
https://testrail.agscollab.com/index.php?/cases/view/24064529
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestEditMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="edit_menu_options", 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 Lumberyard 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",),
("Hide Selection",),
("Show Selection",),
("Show Last Hidden",),
("Unhide All",),
("Modify", "Parent"),
("Modify", "Un-Parent"),
("Modify", "Align", "Align to grid"),
("Modify", "Align", "Align to object"),
("Modify", "Align", "Align object to surface"),
("Modify", "Constrain", "Constrain to X axis"),
("Modify", "Constrain", "Constrain to Y axis"),
("Modify", "Constrain", "Constrain to Z axis"),
("Modify", "Constrain", "Constrain to XY plane"),
("Modify", "Constrain", "Constrain to terrain/geometry"),
("Modify", "Snap", "Snap To Grid"),
("Modify", "Snap", "Snap angle"),
("Modify", "Fast Rotate", "Rotate X Axis"),
("Modify", "Fast Rotate", "Rotate Y Axis"),
("Modify", "Fast Rotate", "Rotate Z Axis"),
("Modify", "Fast Rotate", "Rotate Angle"),
("Modify", "Transform Mode", "Select mode"),
("Modify", "Transform Mode", "Move"),
("Modify", "Transform Mode", "Rotate"),
("Modify", "Transform Mode", "Scale"),
("Modify", "Transform Mode", "Select terrain"),
("Lock selection",),
("Unlock all",),
("Editor Settings", "Global Preferences"),
("Editor Settings", "Graphics Settings"),
("Editor Settings", "Editor Settings Manager"),
("Editor Settings", "Graphics Performance", "PC", "Very High"),
("Editor Settings", "Graphics Performance", "PC", "High"),
("Editor Settings", "Graphics Performance", "PC", "Medium"),
("Editor Settings", "Graphics Performance", "PC", "Low"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Very High"),
("Editor Settings", "Graphics Performance", "OSX Metal", "High"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Medium"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Low"),
("Editor Settings", "Graphics Performance", "Android", "Very High"),
("Editor Settings", "Graphics Performance", "Android", "High"),
("Editor Settings", "Graphics Performance", "Android", "Medium"),
("Editor Settings", "Graphics Performance", "Android", "Low"),
("Editor Settings", "Graphics Performance", "iOS", "Very High"),
("Editor Settings", "Graphics Performance", "iOS", "High"),
("Editor Settings", "Graphics Performance", "iOS", "Medium"),
("Editor Settings", "Graphics Performance", "iOS", "Low"),
("Editor Settings", "Graphics Performance", "Apple TV"),
("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=True,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with 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)
except Exception as e:
print(e)
test = TestEditMenuOptions()
test.run()
@@ -0,0 +1,118 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C16780783: Base Edit Menu Options (New Viewport Interaction Model)
https://testrail.agscollab.com/index.php?/cases/view/16780783
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestEditMenuOptionsNewViewport(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="edit_menu_options: ", 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 Lumberyard 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",),
("Hide Selection",),
("Show All",),
("Modify", "Snap", "Snap angle"),
("Modify", "Transform Mode", "Move"),
("Modify", "Transform Mode", "Rotate"),
("Modify", "Transform Mode", "Scale"),
("Lock Selection",),
("Unlock All Entities",),
("Editor Settings", "Global Preferences"),
("Editor Settings", "Graphics Settings"),
("Editor Settings", "Editor Settings Manager"),
("Editor Settings", "Graphics Performance", "PC", "Very High"),
("Editor Settings", "Graphics Performance", "PC", "High"),
("Editor Settings", "Graphics Performance", "PC", "Medium"),
("Editor Settings", "Graphics Performance", "PC", "Low"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Very High"),
("Editor Settings", "Graphics Performance", "OSX Metal", "High"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Medium"),
("Editor Settings", "Graphics Performance", "OSX Metal", "Low"),
("Editor Settings", "Graphics Performance", "Android", "Very High"),
("Editor Settings", "Graphics Performance", "Android", "High"),
("Editor Settings", "Graphics Performance", "Android", "Medium"),
("Editor Settings", "Graphics Performance", "Android", "Low"),
("Editor Settings", "Graphics Performance", "iOS", "Very High"),
("Editor Settings", "Graphics Performance", "iOS", "High"),
("Editor Settings", "Graphics Performance", "iOS", "Medium"),
("Editor Settings", "Graphics Performance", "iOS", "Low"),
("Editor Settings", "Graphics Performance", "Apple TV"),
("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=True,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with 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)
except Exception as e:
print(e)
test = TestEditMenuOptionsNewViewport()
test.run()
@@ -0,0 +1,91 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C24064528: The File menu options function normally
https://testrail.agscollab.com/index.php?/cases/view/24064528
C16780778: The File menu options function normally-New view interaction Model enabled
https://testrail.agscollab.com/index.php?/cases/view/16780778
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
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 Lumberyard 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",),
("Project Settings", "Switch Projects"),
("Project Settings", "Configure Gems"),
("Show Log File",),
("Resave All Slices",),
("Upgrade Legacy Entities",),
]
# 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=True,
)
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with 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)
except Exception as e:
print(e)
test = TestFileMenuOptions()
test.run()
@@ -0,0 +1,168 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C17218881: Basic Function: Locking & Hiding
https://testrail.agscollab.com/index.php?/cases/view/17218881
"""
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.editor as editor
from PySide2 import QtWidgets
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class BasicFunctionLockHideTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_function_lock_hide", args=["level"])
def run_test(self):
"""
Summary:
Basic Function: Locking & Hiding
Expected Behavior:
1) Entities can be hidden.
2) Hiding a parent entity hides its children.
3) Entities can be shown.
4) Showing a parent entity shows its children.
5) Entities can be locked.
6) Locking a parent entity locks its children.
7) Locked entities cannot be interacted with in the Viewport.
8) Entities can be unlocked.
9) Unlocking a parent entity unlocks its children.
10)Unlocked entities can be interacted with in the Viewport.
11)Entities can be reordered even when locked or hidden.
Test Steps:
1) Open a new level
2) Create an entity and 3 child entity
3) Click on show/hide button to hide entities and verify if they are hidden
4) Click on show/hide button to show entities and verify if they are hidden
5) Click on lock/unlock entities to lock entities and verify if they are locked
6) Click on lock/unlock entities to unlock entities and verify if they are unlocked
7) Create 2 new entities
8) Hide/Lock the newly created entities
9) Move the newly created entites under parent entity
Note:
- This test file must be called from the Lumberyard 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 is_hidden(id):
return editor.EditorEntityInfoRequestBus(bus.Event, "IsHidden", id)
def is_locked(id):
return editor.EditorEntityInfoRequestBus(bus.Event, "IsLocked", id)
# 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=True,
)
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)").widget()
# 2) Create an entity and 3 child entity
entity_position = math.Vector3(200.0, 200.0, 38.0)
component_to_add = ["Mesh"]
parent = hydra.Entity("Entity2")
parent.create_entity(entity_position, component_to_add)
child_1 = hydra.Entity("Entity3")
child_1.create_entity(entity_position, component_to_add, parent.id)
child_2 = hydra.Entity("Entity4")
child_2.create_entity(entity_position, component_to_add, parent.id)
child_3 = hydra.Entity("Entity5")
child_3.create_entity(entity_position, component_to_add, parent.id)
# 3) Click on show/hide button to hide entities and verify if they are hidden
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 0).data() == "Entity2")
parent_model_index = pyside_utils.find_child_by_pattern(tree, "Entity2")
show_hide_parent = parent_model_index.siblingAtColumn(1)
pyside_utils.item_view_index_mouse_click(tree, show_hide_parent)
self.wait_for_condition(lambda: is_hidden(parent.id))
entities_hidden = (
is_hidden(parent.id) and is_hidden(child_1.id) and is_hidden(child_2.id) and is_hidden(child_3.id)
)
print(f"Child entities are hidden when parent entity is hidden: {entities_hidden}")
# 4) Click on show/hide button to show entities and verify if they are hidden
pyside_utils.item_view_index_mouse_click(tree, show_hide_parent)
self.wait_for_condition(lambda: not is_hidden(parent.id))
entities_hidden = (
not is_hidden(parent.id)
and not is_hidden(child_1.id)
and not is_hidden(child_2.id)
and not is_hidden(child_3.id)
)
print(f"Child entities are shown when parent entity is shown: {entities_hidden}")
# 5) Click on lock/unlock entities to lock entities and verify if they are locked
lock_unlock_parent = parent_model_index.siblingAtColumn(2)
pyside_utils.item_view_index_mouse_click(tree, lock_unlock_parent)
self.wait_for_condition(lambda: is_locked(parent.id))
entities_locked = (
is_locked(parent.id) and is_locked(child_1.id) and is_locked(child_2.id) and is_locked(child_3.id)
)
print(f"Child entities are locked when parent entity is locked: {entities_locked}")
# 6) Click on lock/unlock entities to unlock entities and verify if they are unlocked
pyside_utils.item_view_index_mouse_click(tree, lock_unlock_parent)
self.wait_for_condition(lambda: not is_locked(parent.id))
entities_unlocked = (
not is_locked(parent.id)
and not is_locked(child_1.id)
and not is_locked(child_2.id)
and not is_locked(child_3.id)
)
print(f"Child entities are unlocked when parent entity is unlocked: {entities_unlocked}")
# 7) Create 2 new entities
new_entity_1 = hydra.Entity("Entity6")
new_entity_1.create_entity(entity_position, component_to_add)
new_entity_2 = hydra.Entity("Entity7")
new_entity_2.create_entity(entity_position, component_to_add)
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 0).data() == "Entity6")
# 8) Hide/Lock the newly created entities
new_entity_1_mi = pyside_utils.find_child_by_pattern(tree, "Entity6")
show_hide_new_entity = new_entity_1_mi.siblingAtColumn(1)
new_entity_2_mi = pyside_utils.find_child_by_pattern(tree, "Entity7")
lock_unlock_new_entity = new_entity_2_mi.siblingAtColumn(2)
pyside_utils.item_view_index_mouse_click(tree, show_hide_new_entity)
pyside_utils.item_view_index_mouse_click(tree, lock_unlock_new_entity)
# 9) Move the newly created entites under parent entity
new_entity_1.set_test_parent_entity(parent)
new_entity_2.set_test_parent_entity(parent)
test = BasicFunctionLockHideTest()
test.run()
@@ -0,0 +1,104 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6321491: Basic Function: Global Preferences
https://testrail.agscollab.com/index.php?/cases/view/6321491
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class BasicGlobalPreferencesTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_global_preferences: ")
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Verify if we can change some value in the Global Preferences in Editor Settings
Expected Behavior:
Global preferences can be changed and saved.
Test Steps:
1) Change Toolbar Icon size in Global Preferences
2) Verify if the Toolbar Icon size changed
3) Reset the Toolbar Icon Size
Note:
- This test file must be called from the Lumberyard 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
"""
async def change_tool_button_size(size):
action = pyside_utils.get_action_for_menu_path(
editor_window, "Edit", "Editor Settings", "Global Preferences"
)
print(f"Global Preferences action triggered")
pyside_utils.trigger_action_async(action)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
combo_box = await pyside_utils.wait_for_child_by_hierarchy(
active_modal_widget,
"EditorPreferencesDialog",
...,
"propertyEditor",
...,
dict(type=QtWidgets.QFrame, text="Toolbar Icon Size"),
...,
QtWidgets.QComboBox
)
combo_box.setCurrentIndex(combo_box.findText(size))
button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
print(f"Toolbar Icon size set to {size}")
await pyside_utils.wait_for_destroyed(active_modal_widget)
# 1) Change Toolbar Icon size in Global Preferences
editor_window = pyside_utils.get_editor_main_window()
play_game_button = await pyside_utils.wait_for_child_by_hierarchy(
editor_window,
...,
"EditMode",
...,
dict(type=QtWidgets.QToolButton, text="Play Game")
)
# First, make sure the toolbar icon size is the Default
await change_tool_button_size("Default")
initial_size = play_game_button.size()
await change_tool_button_size("Large")
def check_size():
current_size = play_game_button.size()
return initial_size.height() < current_size.height() and initial_size.width() < current_size.width()
# 2) Verify if the Toolbar Icon size changed
await pyside_utils.wait_for_condition(check_size)
print("Toolbar Icon size changed in Global Preferences")
# 3) Reset the Toolbar Icon Size
await change_tool_button_size("Default")
test = BasicGlobalPreferencesTest()
test.run()
@@ -0,0 +1,140 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6351301: Basic Function: Toolbars
https://testrail.agscollab.com/index.php?/cases/view/6351301
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestBasicToolBarFunction(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_toolbar_function: ", args=["level"])
def run_test(self):
"""
Summary:
Enable all menu toolbars and click between options in each of the toolbars and then disable all the toolbars.
Expected Behavior:
All menu toolbars can be enabled and disbled with each toolbar is functional and options can be selected.
Test Steps:
1) Create new level with an Entity
2) Enable each of the toolbars in the list.
3) Verify options from toolbars can be selected
3.1) "Play Game" option from EditMode toolbar
3.2) "Go to selected object" option from Object toolbar
4) Disable each of the toolbars in the list.
Note:
- This test file must be called from the Lumberyard 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
"""
editor_window = pyside_utils.get_editor_main_window()
def fetch_vector3_parts(vec3):
x = vec3.get_property("x")
y = vec3.get_property("y")
z = vec3.get_property("z")
return (x, y, z)
def enable_disable_toolbar(toolbar, enable=True):
toolbar = editor_window.findChild(QtWidgets.QToolBar, i)
action = toolbar.toggleViewAction()
if enable:
# Enable the toolbar
if not toolbar.isVisible():
action.trigger()
if toolbar.isVisible():
print(f"{i} toolbar is enabled")
else:
# Disable the toolbar
if toolbar.isVisible():
action.trigger()
if not toolbar.isVisible():
print(f"{i} toolbar is disbaled")
menu_toolbar_list = [
"EditMode",
"Object",
"debugViewsToolbar",
"environmentModesToolbar",
"viewModesToolbar",
]
# 1) Create new level with an Entity
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(3.0)
entityId = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
# 2) Enable each of the toolbars in the list.
for i in menu_toolbar_list:
enable_disable_toolbar(i)
# 3) Select options from toolbar
# 3.1) "Play Game" option from EditMode toolbar
editors_toolbar = editor_window.findChild(QtWidgets.QToolBar, "EditMode")
for i in editors_toolbar.children():
if isinstance(i, QtWidgets.QToolButton) and i.text() == "Play Game":
play_game_button = i
break
# Click on the tool button to verify ToolBar functionality
play_game_button.click()
general.idle_wait(1.0)
if general.is_in_game_mode():
print("In game mode: Play game ToolButton is responsive")
general.exit_game_mode()
# 3.2) "Go to selected object" option from Object toolbar
position = fetch_vector3_parts(general.get_current_view_position())
# Click on the tool button to verify ToolBar functionality
object_toolbar = editor_window.findChild(QtWidgets.QToolBar, "Object")
for i in object_toolbar.children():
if isinstance(i, QtWidgets.QToolButton) and i.text() == "Go to selected object":
general.select_object(editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entityId))
general.idle_wait(1.0)
i.click()
break
general.idle_wait(1.0)
new_pos = fetch_vector3_parts(general.get_current_view_position())
if new_pos != position:
print("Go to selected object option is responsive")
# 4) Disable each of the toolbars in the list.
for i in menu_toolbar_list:
enable_disable_toolbar(i, False)
test = TestBasicToolBarFunction()
test.run()
@@ -0,0 +1,127 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6317014: Basic UI Appearance & Function
https://testrail.agscollab.com/index.php?/cases/view/6317014
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestBasicUIAppearence(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_ui_appearence_function: ", args=["level"])
def run_test(self):
"""
Summary:
Open Lumberyard editor and check if basic UI components are loaded and responsive.
Expected Behavior:
Elements below of the UI are loaded and responsive.
Menus
Toolbars
Tools
Viewport
Test Steps:
1) Open level
2) Verify if Tools are responsive - Opens some tool by triggering the Tool Menu actions and
verifies if the tool has been opened and active.
3) Verify if Menu items are responsive - Triggers the MenuBar actions of the Editor window.
4) Verify if Viewport is responsive - Simulates right-click on the viewport to create a new Entity.
5) Verify if Toolbars are responsive - Uses the "EditMode" toolbar to click on the Play Game
ToolButton, and then verifies if the editor is in game mode.
Note:
- This test file must be called from the Lumberyard 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 on_action_triggered(action_name):
print(f"{action_name} Action triggered")
def on_action_trigger():
print("Tools Action triggered")
# 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=True,
)
editor_window = pyside_utils.get_editor_main_window()
# 2) Verify if Tools are responsive
tool_action = pyside_utils.get_action_for_menu_path(editor_window, "Tools", "Script Canvas")
tool_action.triggered.connect(on_action_trigger)
tool_action.trigger()
tool_action.triggered.disconnect(on_action_trigger)
# Verify if the tool is opened
def is_script_canvas_active_window():
tool = editor_window.findChild(QtWidgets.QWidget, "Script Canvas")
if tool:
return tool.isActiveWindow()
return False
if self.wait_for_condition(is_script_canvas_active_window, 2.0):
print("Tool opened")
# 3) Verify if Menu items are responsive
menu_bar = editor_window.menuBar()
for action in menu_bar.actions():
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
# 4) Verify if Viewport is responsive
# Clear the current selection and create a new Entity via the viewport right-click menu
render_overlay = editor_window.findChild(QtWidgets.QWidget, "renderOverlay")
general.clear_selection()
pyside_utils.trigger_context_menu_entry(render_overlay.parent(), "Create entity")
selected_entities = editor.ToolsApplicationRequestBus(bus.Broadcast, 'GetSelectedEntities')
if len(selected_entities) == 1:
entity_id = selected_entities[0]
name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
if name == "Entity2":
print("Viewport is responsive")
# 5) Verify if Toolbars are responsive
editors_toolbar = editor_window.findChild(QtWidgets.QToolBar, "EditMode")
play_game_button = pyside_utils.find_child_by_pattern(editors_toolbar, "Play Game")
# Click on the tool button to verify ToolBar functionality
play_game_button.click()
if self.wait_for_condition(lambda: general.is_in_game_mode()):
print("In game mode: Play game ToolButton is responsive")
general.exit_game_mode()
test = TestBasicUIAppearence()
test.run()
@@ -0,0 +1,75 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6317020: Basic UI Scaling
https://testrail.agscollab.com/index.php?/cases/view/6317020
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestBasicUIScalingFunction(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_ui_scaling: ", args=["level"])
def run_test(self):
"""
Summary:
Resize the editor window and verify if the editor scaling works.
Expected Behavior:
The editor window/docked widgets is scalable/resizable into windowed mode.
Test Steps:
1) Resize the editor window couple of times
2) Resize the docked widget
Note:
- This test file must be called from the Lumberyard 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 check_size(widget, widget_name, width, height):
if widget.geometry().width() == width and widget.geometry().height() == height:
print(f"{widget_name} size is {width} X {height}")
else:
print("Widget size not equal to expected value")
# 1) Resize the editor window couple of times
# wait util the editor opens
general.idle_wait(3.0)
editor_window = pyside_utils.get_editor_main_window()
editor_window.resize(1000, 500)
check_size(editor_window, "Editor", 1000, 500)
editor_window.resize(2000, 1000)
check_size(editor_window, "Editor", 2000, 1000)
# 2) Resize the docked widget
main_window = editor_window.findChild(QtWidgets.QMainWindow)
asset_browser = main_window.findChild(QtWidgets.QDockWidget, "Asset Browser")
asset_browser.resize(100, 100)
check_size(asset_browser, "Docked Widget", 100, 100)
test = TestBasicUIScalingFunction()
test.run()
@@ -0,0 +1,119 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6317035: Basic Viewport Configuration
https://testrail.agscollab.com/index.php?/cases/view/6317035
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from PySide2.QtTest import QTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class BasicViewportConfigurationTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_viewport_configuration", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Verify id the basic viewport configuration works as expected.
Expected Behavior:
1) Layout Configuration dialog opens.
2) Layout configuration can be changed.
3) All viewports function properly and independently of one another.
4) The viewport is reverted to a single, large window.
Test Steps:
1) Open a new level
2) Configure the viewport layer to a different layout
3) Verify if viewport 1 is functional
4) Verify if viewport 2 is functional
5) Reset the layout back to original
Note:
- This test file must be called from the Lumberyard 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 verify_viewport_function(viewport_main_window_object):
"""
To check if the viewport camera controls are working, we can use the key press 'Left Arrow'
which manuallly would have moved the camera to its left. So by simulating the Left Arrow press and
comparing the current view position before and after the key press, we can verify if the view has indeed
been modified and there by confirming if viewport is active.
"""
viewport = viewport_main_window_object.findChildren(QtWidgets.QWidget, "renderOverlay")[0].parent()
initial_x = general.get_current_view_position().x
viewport.setFocus()
QTest.keyPress(viewport, Qt.Key_Left, Qt.NoModifier)
general.idle_wait(0.5)
QTest.keyRelease(viewport, Qt.Key_Left, Qt.NoModifier, delay=2)
current_x = general.get_current_view_position().x
return current_x < initial_x
async def select_layout(layout_index):
action = pyside_utils.get_action_for_menu_path(editor_window, "View", "Viewport", "Configure Layout")
pyside_utils.trigger_action_async(action)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
layout_config_dialog = active_modal_widget.findChild(QtWidgets.QDialog, "CLayoutConfigDialog")
list_view = layout_config_dialog.findChild(QtWidgets.QListView, "m_layouts")
pyside_utils.item_view_mouse_click(list_view, layout_index)
button_box = layout_config_dialog.findChild(QtWidgets.QDialogButtonBox, "m_buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
print(f"Layout set to {layout_index}")
# 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=True,
)
editor_window = pyside_utils.get_editor_main_window()
# 2) Configure the viewport layer to a different layout
# In this case layout with 2 viewports placed side by side is selected
await select_layout(1)
main_window = editor_window.findChild(QtWidgets.QMainWindow).findChild(QtWidgets.QMainWindow)
central_widget = main_window.centralWidget()
await pyside_utils.wait_for_condition(lambda: central_widget.findChild(QtWidgets.QSplitter) is not None)
splitter = central_widget.findChild(QtWidgets.QSplitter)
# 3) Verify if viewport 1 is functional
await pyside_utils.wait_for_condition(lambda: verify_viewport_function(splitter.children()[0]), 3.0)
print(f"Viewport 1 camera controls are functional: {verify_viewport_function(splitter.children()[0])}")
# 4) Verify if viewport 2 is functional
await pyside_utils.wait_for_condition(lambda: verify_viewport_function(splitter.children()[1]), 3.0)
print(f"Viewport 2 camera controls are functional: {verify_viewport_function(splitter.children()[1])}")
# 5) Reset the layout back to original
await select_layout(0)
test = BasicViewportConfigurationTest()
test.run()
@@ -0,0 +1,104 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C18668804: Basic Window Docking System Tests
https://testrail.agscollab.com/index.php?/cases/view/18668804
"""
import azlmbr.legacy.general as general
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtCore, QtWidgets
class TestBasicWindowDocking(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_window_docking: ")
def run_test(self):
"""
Summary:
Test basic docking behavior by undocking the Entity Outliner, and
then docking on the edges around the main Editor window.
Expected Behavior:
The window becomes undocked and floats on its own.
The window can be docked to the main Editor's edges and is resized to fit.
Test Steps:
1) Click on the Entity Outliner's title bar and drag it away to undock it.
2) Click and drag the Entity Outliner to empty borders along the main Editor (top/bottom/left/right)
:return: None
"""
# Make sure the Entity Outliner is open
general.open_pane("Entity Outliner (PREVIEW)")
editor_window = pyside_utils.get_editor_main_window()
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
# 1) Click on the Entity Outliner's title bar and drag it away to undock it.
# We drag/drop it over the viewport since it doesn't allow docking, so this will undock it
render_overlay = editor_window.findChild(QtWidgets.QWidget, "renderOverlay")
pyside_utils.drag_and_drop(entity_outliner, render_overlay)
# Make sure the Entity Outliner is in a different QMainWindow than the main Editor QMainWindow,
# which means it has been properly undocked (in a floating window)
main_window = editor_window.findChild(QtWidgets.QMainWindow)
if entity_outliner.parentWidget() != main_window:
print("Entity Outliner is in a floating window")
# 2) Click and drag the Entity Outliner to empty borders along the main Editor (top/bottom/left/right)
# We need to grab a new reference to the Entity Outliner QDockWidget because when it gets moved
# to the floating window, its parent changes so the wrapped intance we had becomes invalid
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
# Dock to absolute top of main window
edge_offset = 10 # The absolute drop zones are 25px wide, chose 10px as an in between
main_window_rect = main_window.rect()
main_window_center = main_window.rect().center()
top_center = QtCore.QPoint(main_window_center.x(), edge_offset)
pyside_utils.drag_and_drop(entity_outliner, main_window, QtCore.QPoint(), top_center)
# Make sure the Entity Outliner is now in the top area of the main Editor window
if main_window.dockWidgetArea(entity_outliner) == QtCore.Qt.DockWidgetArea.TopDockWidgetArea:
print("Entity Outliner docked in top area")
# Dock to absolute right of main window
right_center = QtCore.QPoint(main_window_rect.right() - edge_offset, main_window_center.y())
pyside_utils.drag_and_drop(entity_outliner, main_window, QtCore.QPoint(), right_center)
# Make sure the Entity Outliner is now in the right area of the main Editor window
if main_window.dockWidgetArea(entity_outliner) == QtCore.Qt.DockWidgetArea.RightDockWidgetArea:
print("Entity Outliner docked in right area")
# Dock to absolute bottom of main window
bottom_center = QtCore.QPoint(main_window_center.x(), main_window_rect.bottom() - edge_offset)
pyside_utils.drag_and_drop(entity_outliner, main_window, QtCore.QPoint(), bottom_center)
# Make sure the Entity Outliner is now in the bottom area of the main Editor window
if main_window.dockWidgetArea(entity_outliner) == QtCore.Qt.DockWidgetArea.BottomDockWidgetArea:
print("Entity Outliner docked in bottom area")
# Dock to absolute left of main window
left_center = QtCore.QPoint(edge_offset, main_window_center.y())
pyside_utils.drag_and_drop(entity_outliner, main_window, QtCore.QPoint(), left_center)
# Make sure the Entity Outliner is now in the left area of the main Editor window
if main_window.dockWidgetArea(entity_outliner) == QtCore.Qt.DockWidgetArea.LeftDockWidgetArea:
print("Entity Outliner docked in left area")
test = TestBasicWindowDocking()
test.run()
@@ -0,0 +1,164 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6384949: Basic Workflow: Layers
https://testrail.agscollab.com/index.php?/cases/view/6384949
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.layers as layers
import azlmbr.math as math
from typing import Optional
from PySide2 import QtWidgets
from PySide2.QtGui import QContextMenuEvent
from PySide2.QtCore import QObject, QEvent, QPoint
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestBasicWorkflowLayers(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="basic_workflow_layers: ", args=["level"])
def run_test(self):
"""
Summary:
Basic Workflow: Layers.
Test Steps:
1) Open level
2) Create layer in the Entity Outliner
3) Create entity and Drag the entity into the layer
4) Create another entity and Drag entities out of the layer
5) Create a second layer and Drag the second layer into the first layer
6) Save layer
Note:
- This test file must be called from the Lumberyard 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 get_entity_count_name(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
def verify_parent(
entity_to_check, expected_parent_entity, child_name, parent_name, additional_pretext: Optional[str] = ""
):
actual_parent_id = editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", entity_to_check)
if actual_parent_id.ToString() == expected_parent_entity.ToString():
print(f"{additional_pretext} The parent entity of {child_name} is : {parent_name}")
def set_parent(chilld_entity, parent_entity):
editor.EditorEntityAPIBus(bus.Event, "SetParent", chilld_entity, parent_entity)
class EventFilter(QObject):
def eventFilter(self, obj, event):
if event.type() == QEvent.Type.ChildPolished:
print("ChildPolished event received")
menu = obj.children()[-1]
action = pyside_utils.find_child_by_property(menu, QtWidgets.QAction, "text", "Save layer")
action.trigger()
menu.clear()
return False
return False
def on_focus_changed(old, new):
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
general.idle_wait(1.0)
button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox, "qt_msgbox_buttonbox")
button_box.button(QtWidgets.QDialogButtonBox.Save).click()
# 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=True,
)
general.idle_wait(3.0)
# 2) Create layer in the Entity Outliner
first_layer_entity_id = layers.EditorLayerComponent_CreateLayerEntityFromName("Layer1")
if get_entity_count_name("Layer1*"):
print("First Layer is created")
# 3) Create entity and Drag the entity into the layer
entity_postition = math.Vector3(125.0, 136.0, 32.0)
first_entity = editor_utils.Entity("Entity1")
first_entity.create_entity(entity_postition, [])
if get_entity_count_name("Entity1"):
print("First Entity is created")
set_parent(first_entity.id, first_layer_entity_id)
verify_parent(first_entity.id, first_layer_entity_id, first_entity.name, "Layer1", "Original Check:")
# 4) Create another entity and Drag entities out of the layer
second_entity = editor_utils.Entity("Entity2")
second_entity.create_entity(entity_postition, [])
if get_entity_count_name("Entity2"):
print("Second Entity is created")
set_parent(second_entity.id, first_layer_entity_id)
verify_parent(second_entity.id, first_layer_entity_id, second_entity.name, "Layer1", "Original Check:")
# Drag entities out of the layer
set_parent(first_entity.id, entity.EntityId())
set_parent(second_entity.id, entity.EntityId())
verify_parent(first_entity.id, entity.EntityId(), first_entity.name, "", "After Drag:")
verify_parent(second_entity.id, entity.EntityId(), second_entity.name, "", "After Drag:")
# 5) Create a second layer and Drag the second layer into the first layer
second_layer_entity_id = layers.EditorLayerComponent_CreateLayerEntityFromName("Layer2")
if get_entity_count_name("Layer2*"):
print("Second Layer is created")
set_parent(second_layer_entity_id, first_layer_entity_id)
verify_parent(second_layer_entity_id, first_layer_entity_id, "Layer2", "Layer1", "After Drag:")
# 6) Save layer
app = QtWidgets.QApplication.instance()
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
entity_outliner = main_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
outliner_main_window = entity_outliner.findChild(QtWidgets.QMainWindow)
wid_1 = outliner_main_window.findChild(QtWidgets.QWidget)
wid_2 = wid_1.findChild(QtWidgets.QWidget)
tree = outliner_main_window.findChildren(QtWidgets.QTreeView, "m_objectTree")[0]
general.clear_selection()
general.select_object(editor.EditorEntityInfoRequestBus(bus.Event, "GetName", first_layer_entity_id))
event_filter = EventFilter()
try:
app.focusChanged.connect(on_focus_changed)
wid_2.installEventFilter(event_filter)
context_menu_event = QContextMenuEvent(QContextMenuEvent.Mouse, QPoint(0, 0))
app.sendEvent(tree, context_menu_event)
general.idle_wait(1.0)
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
wid_2.removeEventFilter(event_filter)
test = TestBasicWorkflowLayers()
test.run()
@@ -0,0 +1,112 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C17219007: Camera Component
https://testrail.agscollab.com/index.php?/cases/view/17219007
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.math as math
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestCameraComponentHelper(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="camera_component: ", args=["level"])
def run_test(self):
"""
Summary:
Ensure that the camera component behaves properly in the editor and the
proper camera settings are being populated.
Expected Behavior:
Camera fov is visible in the Viewport
The Camera entity is populated in the Viewport Selector
The Viewport perspective is changed to the camera
The Viewport perspective is switched back to the default editor camera
Test Steps:
1) Create or open any level
2) Create an entity with a Camera component
3) Click "Be this camera"
4) Save the position of the floating camera viewport and compare against entity
5) Click "Return to default editor camera"
6) Save the position of the current viewport and compare against entity
Note:
- This test file must be called from the Lumberyard 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 get_button(button_text):
return pyside_utils.find_child_by_pattern(entity_inspector, button_text)
# 1) Create or open any level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
editor_window = pyside_utils.get_editor_main_window()
entity_inspector = editor_window.findChildren(QtWidgets.QDockWidget, "Entity Inspector")[0]
# 2) Create an entity with a Camera component
entity_postition = math.Vector3(200.0, 200.0, 32.0)
components_to_add = ["Camera"]
test_entity = editor_utils.Entity("Temp_Entity")
test_entity.create_entity(entity_postition, components_to_add)
if test_entity.id.isValid():
print("Entity created with camera component")
# 3) Click "Be this camera"
button_text = "Be this camera"
self.wait_for_condition(lambda: get_button(button_text) is not None, 2.0)
button = get_button(button_text)
if button:
button.click()
print("Button 'Be this camera' clicked")
# 4) Save the position of the floating camera viewport and compare against entity
self.wait_for_condition(lambda: general.get_current_view_position() == entity_postition, 1.0)
camera_position = general.get_current_view_position()
assert camera_position == entity_postition, "Camera positions did not change upon clicking 'Be this camera'"
print("Camera has shifted after clicking 'Be this camera' button")
# 5) Click "Return to default editor camera"
button_text = "Return to default editor camera"
self.wait_for_condition(lambda: get_button(button_text) is not None, 2.0)
button = get_button(button_text)
if button:
button.click()
print("Button 'Return to default editor camera' clicked")
# 6) Save the position of the current viewport and compare against entity
self.wait_for_condition(lambda: general.get_current_view_position() != entity_postition, 1.0)
camera_position = general.get_current_view_position()
assert camera_position != entity_postition, "Camera positions did not change upon clicking 'Return to default editor camera'"
print("Camera has shifted after clicking 'Return to default editor camera' button")
test = TestCameraComponentHelper()
test.run()
@@ -0,0 +1,154 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C16798661: Component List Contents
https://testrail.agscollab.com/index.php?/cases/view/16798661
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.math as math
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ComponentListContentsTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="component_list_contents", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Select an entity. Click "Add Component" in the Entity Inspector.
Verify components are sorted into categories.
Expand / collapse component categories.
Expected Behavior:
Component categories are expanded / collapsed as expected.
Test Steps:
1) Open level
2) Create entity
3) Select the newly created entity
4) Click "Add Component" in the Entity Inspector.
5) Verify list of components are sorted into categories.
6) Collapse the first category.
7) Expand the category which was just collapsed.
8) Add a valid component to the entity.
Note:
- This test file must be called from the Lumberyard 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
"""
# 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=True,
)
general.idle_wait(3.0)
# Make sure the Entity Inspector is open
general.open_pane('Entity Inspector')
# 2) Create entity
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if entity_id.IsValid():
print("Entity Created")
# 3) Select the newly created entity
general.clear_selection()
general.select_object("Entity2")
# Get the component type ID for our Box Shape
component_name = "Box Shape"
type_ids = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name],
EntityId.EntityType().Game)
component_type_id = type_ids[0]
# Sanity check to make sure our Entity didn't already have the Box Shape component for some reason
had_component = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id, component_type_id)
if had_component:
print("Box Shape already existed on Entity.")
# 4) Click Add Component
general.idle_wait(0.5)
add_comp_btn = pyside_utils.find_child_by_hierarchy(
pyside_utils.get_editor_main_window(), ..., "Entity Inspector", ..., "m_addComponentButton")
add_comp_btn.click()
# Wait 0.5s to ensure component is added and rendered, especially when adding components
# continuously to the entity
general.idle_wait(0.5)
popup = None
search_frame = None
def hasPopup():
nonlocal popup
nonlocal search_frame
popup = QtWidgets.QApplication.activePopupWidget()
focus = QtWidgets.QApplication.focusWidget()
if isinstance(focus, QtWidgets.QLineEdit) and popup:
search_frame = popup.findChild(QtWidgets.QFrame, "SearchFrame")
if search_frame is not None:
return True
await pyside_utils.wait_for_condition(hasPopup)
# To ensure we get an updated tree after the component name is set
general.idle_wait(0)
tree = popup.findChild(QtWidgets.QTreeView, "Tree")
QtTest.QTest.keyClick(tree, Qt.Key_Down, Qt.NoModifier)
while tree.indexBelow(tree.currentIndex()) != QtCore.QModelIndex():
# 6) Collapse the first category (which is "AI")
# "Behavior Tree" is the first component in category "AI"
if tree.currentIndex().data(Qt.DisplayRole) == "Behavior Tree":
QtTest.QTest.keyClick(tree, Qt.Key_Left, Qt.NoModifier)
QtTest.QTest.keyClick(tree, Qt.Key_Left, Qt.NoModifier)
if tree.currentIndex().data(Qt.DisplayRole) == "AI":
print("Successfully collapsed category.")
# 7) Expand the category which was just collapsed.
QtTest.QTest.keyClick(tree, Qt.Key_Right, Qt.NoModifier)
QtTest.QTest.keyClick(tree, Qt.Key_Right, Qt.NoModifier)
if tree.currentIndex().data(Qt.DisplayRole) == "Behavior Tree":
print("Successfully expanded category.")
break
QtTest.QTest.keyClick(tree, Qt.Key_Down, Qt.NoModifier)
# 8) Add the component (based on passed "component_name" variable) to entity
component_index = pyside_utils.find_child_by_pattern(tree, component_name)
tree.setCurrentIndex(component_index)
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
# After selecting the Box Shape from the component palette, make sure the component exists on our Entity now
has_component = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id, component_type_id)
if has_component:
print("Box Shape successfully added.")
test = ComponentListContentsTest()
test.run()
@@ -0,0 +1,144 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C16877221: Component List Filtering
https://testrail.agscollab.com/index.php?/cases/view/16877221
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ComponentListFilteringTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="component_list_filtering", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Select an entity. Click "Add Component" in the Entity Inspector.
Type the name of a valid component in the component search field, clear the search field and finally
type an invalid component name.
The test verifies the component list is filtered correctly in all these cases.
Expected Behavior:
1) Components are filtered as you type narrowing down to the desired component.
2) Components are filtered with each character deleted, eventually restoring the entire component list when the
field is empty.
3) Components are filtered until there are no entries present.
Test Steps:
1) Open level
2) Create entity
3) Select the newly created entity
4) Click "Add Component" in the Entity Inspector.
5) Type component name in search field.
6) Verify list of components are filtered per the typed component name.
7) Delete text in search field.
8) Verify the list is restored.
9) Type an invalid component name in search field.
10) Verify the component list is empty as expected.
Note:
- This test file must be called from the Lumberyard 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
"""
async def search_component(component_name, expected_row_count=None):
# Click the "Add Component" button in the Entity Inspector and
# then enter the specified `component_name` into its search field
pyside_utils.click_button_async(add_comp_btn)
popup = await pyside_utils.wait_for_popup_widget()
search_frame = popup.findChild(QtWidgets.QFrame, "SearchFrame")
search_text = search_frame.findChild(QtWidgets.QLineEdit, "SearchText")
search_text.setText(component_name)
# To ensure we get an updated tree after the component name is set, we
# need to wait until the rowCount matches what we expect, since the
# ComponentPaletteWidget::UpdateSearch is queued.
def row_count_matches():
tree = popup.findChild(QtWidgets.QTreeView, "Tree")
row_count = tree.model().rowCount()
if expected_row_count is not None:
return row_count == expected_row_count
# Else, if expected_row_count was left as None, then just wait until
# the tree model has been populated with anything
return row_count > 0
await pyside_utils.wait_for_condition(row_count_matches)
# Print out the result based on what is populated in the component palette tree
tree = popup.findChild(QtWidgets.QTreeView, "Tree")
row_count = tree.model().rowCount()
component_index = pyside_utils.find_child_by_pattern(tree, text=component_name, type=QtCore.QModelIndex)
if component_index and component_index.isValid():
print(f"{component_name} : Valid filtered component list")
elif row_count == 0:
print(f"{component_name} : Empty component list")
else:
print("Full component list")
# 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=True,
)
# Make sure the Entity Inspector is open
general.open_pane('Entity Inspector')
# 2) Create entity
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntity", entity.EntityId()
)
# 3) Select the newly created entity
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [entity_id])
# Give the Entity Inspector time to fully create its contents
general.idle_wait(0.0)
# 4) Click Add Component
editor_window = pyside_utils.get_editor_main_window()
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
add_comp_btn = entity_inspector.findChild(QtWidgets.QPushButton, "m_addComponentButton")
# 5,6) Search for a valid component name, verify list is properly filtered
component_name_to_search = "Box Shape"
await search_component(component_name_to_search, 1)
# 7,8) Delete the search box, verify full list is loaded
component_name_to_search = ""
await search_component(component_name_to_search)
# 9,10) Search for an invalid component name, verify the list is empty
component_name_to_search = "Invalid Component Name !@#$"
await search_component(component_name_to_search, 0)
test = ComponentListFilteringTest()
test.run()
@@ -0,0 +1,293 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
16929884: Conflicting Components
https://testrail.agscollab.com/index.php?/cases/view/16929884
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.entity as entity
from azlmbr.entity import EntityId
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestConflictingComponents(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="conflicting_components: ", args=["level"])
def run_test(self):
"""
Summary:
Create conflicting components on an entity, test activating and deactivated each.
Expected Behavior:
Conflicting compoennts are deactivated when appropriate.
Test Steps:
1) Create new level
2) Create an entity
3) Create a box shape component on the entity
4) Create a cylinder shape component on the entity
5) Check both components have warnings and activate/delete buttons
6) Select Activate on the cylinder shape
7) Check that the cylinder is active and the box is not active
8) Right click on the box shape and enable it
9) Check delete/activate options exist on both components
10) delete the cylinder shape
11) Check that the cylinder is deleted and the box is enabled
Note:
- This test file must be called from the Lumberyard 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 get_component_type_id(component_type_name):
# Generate List of Component Types
component_list = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentTypeNameList')
if len(component_list) == 0:
print("Component List returned incorrectly.")
return False, 0
# Get Component Types for Mesh and Comment
type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType',
[component_type_name], entity.EntityType().Game)
if len(type_ids_list) == 0:
print("Type Ids List returned incorrectly.")
return False, 0
return True, type_ids_list[0]
def create_component_of_type(entity_id, component_type_name):
result, component_type_id = get_component_type_id(component_type_name)
if not result:
print("Failed to get " + component_type_name + " component id.")
return None
has_component_before_add = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id,
component_type_id)
if not has_component_before_add:
print("Entity does not have " + component_type_name + " component before add.")
# Add an ActorComponent to the entity.
component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entity_id,
[component_type_id])
if component_outcome.IsSuccess():
print(component_type_name + " component added to entity.")
else:
return None
components = component_outcome.GetValue()
component = components[0]
has_component_after_add = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entity_id,
component_type_id)
if has_component_after_add and len(components) == 1:
print("Entity has " + component_type_name + " component after add.")
else:
return None
return component
def get_inspector_component_by_index(index):
inspector_dock = editor_window.findChild(QtWidgets.QWidget, "Entity Inspector")
entity_inspector = inspector_dock.findChild(QtWidgets.QMainWindow)
child_widget = entity_inspector.findChild(QtWidgets.QWidget)
if child_widget is None:
return False, None
general.idle_wait(1.0)
comp_list_contents = (
child_widget.findChild(QtWidgets.QScrollArea, "m_componentList")
.findChild(QtWidgets.QWidget, "qt_scrollarea_viewport")
.findChild(QtWidgets.QWidget, "m_componentListContents")
)
if comp_list_contents is None or index >= len(comp_list_contents.children()):
return False, None
return True, comp_list_contents.children()[index]
def get_box_and_cylinder_component_inspector():
result, box_frame = get_inspector_component_by_index(2)
result, cylinder_frame = get_inspector_component_by_index(3)
return box_frame, cylinder_frame
def is_just_first_component_enabled(component1, component2):
c1_active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', component1)
c2_active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', component2)
if c1_active and not c2_active:
return True
return False
def fail_test(msg):
print(msg)
print("Test Failed.")
sys.exit()
create_level_result = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
if not create_level_result:
fail_test("New level failed.")
EditorTestHelper.after_level_load(self)
# Create a new entity.
new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
if not new_entity_id.IsValid():
print("Failed to create new entity.")
# Select the entity so that it appears in the inspector.
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [new_entity_id])
# Create both shape components.
box_component = create_component_of_type(new_entity_id, "Box Shape")
if box_component is None:
fail_test("Failed to create Box Shape component.")
cylinder_component = create_component_of_type(new_entity_id, "Cylinder Shape")
if cylinder_component is None:
fail_test("Failed to create Box Shape component.")
# Check inspector windows exist for both shapes
box_frame, cylinder_frame = get_box_and_cylinder_component_inspector()
if box_frame is None:
fail_test("Could not find inspector for box shape.")
if cylinder_frame is None:
fail_test("Could not find inspector for cylinder shape.")
# Check both shape inspectors have delete and activate buttons.
box_del_btn = pyside_utils.find_child_by_pattern(box_frame, "Delete component")
box_activate_btn = pyside_utils.find_child_by_pattern(box_frame, "Activate this component")
if box_del_btn is None or box_activate_btn is None:
fail_test("Failed to find buttons on box component.")
cylinder_del_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Delete component")
cylinder_activate_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Activate this component")
if cylinder_del_btn is None or cylinder_activate_btn is None:
fail_test("Failed to find buttons on cylinder component.")
# Check that just the box shape is enabled.
just_box_enabled = is_just_first_component_enabled(box_component, cylinder_component)
if not just_box_enabled:
fail_test("Incorrectly enabled components.")
# Press Activate on cylinder
QtTest.QTest.mouseClick(cylinder_activate_btn, Qt.LeftButton, Qt.NoModifier)
# Re-get the inspector windows to ensure they're up to date.
box_frame, cylinder_frame = get_box_and_cylinder_component_inspector()
# Check that neither shape inspector now has delete or activate buttons.
box_del_btn = pyside_utils.find_child_by_pattern(box_frame, "Delete component")
box_activate_btn = pyside_utils.find_child_by_pattern(box_frame, "Activate this component")
if box_del_btn is not None or box_activate_btn is not None:
fail_test("Found buttons on box component when disabled.")
cylinder_del_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Delete component")
cylinder_activate_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Activate this component")
if cylinder_del_btn is not None or cylinder_activate_btn is not None:
fail_test("Found buttons on cylinder component when active.")
# Check that the cylinder is now enabled and not the box.
just_cylinder_enabled = is_just_first_component_enabled(cylinder_component, box_component)
if not just_cylinder_enabled:
fail_test("Incorrectly enabled components.")
# Click on the box frame to activate it or we might get the wrong context menu
QtTest.QTest.mouseClick(box_frame, Qt.LeftButton, Qt.NoModifier)
general.idle_wait(1.0)
# Pop up box component context menu and select the Enable component option
pyside_utils.trigger_context_menu_entry(box_frame, "Enable component")
# Check that both components have the buttons again
box_frame, cylinder_frame = get_box_and_cylinder_component_inspector()
box_del_btn = pyside_utils.find_child_by_pattern(box_frame, "Delete component")
box_activate_btn = pyside_utils.find_child_by_pattern(box_frame, "Activate this component")
if box_del_btn is None or box_activate_btn is None:
fail_test("Failed to find buttons on box component.")
cylinder_del_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Delete component")
cylinder_activate_btn = pyside_utils.find_child_by_pattern(cylinder_frame, "Activate this component")
if cylinder_del_btn is None or cylinder_activate_btn is None:
fail_test("Failed to find buttons on cylinder component.")
# Press "Delete component" on the Cylinder Shape.
QtTest.QTest.mouseClick(cylinder_del_btn, Qt.LeftButton, Qt.NoModifier)
general.idle_wait(1.0)
# Check it's gone.
result, component_type_id = get_component_type_id("Cylinder Shape")
if not result:
fail_test("Failed to get Cylinder Shape component id.")
has_component_after_delete = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', new_entity_id,
component_type_id)
if has_component_after_delete:
fail_test("Cylinder Shape still exists.")
box_frame, cylinder_frame = get_box_and_cylinder_component_inspector()
# Ensure the activate and delete buttons are gone from the box inspector.
box_del_btn = pyside_utils.find_child_by_pattern(box_frame, "Delete component")
box_activate_btn = pyside_utils.find_child_by_pattern(box_frame, "Activate this component")
if box_del_btn is not None or box_activate_btn is not None:
fail_test("Found buttons on box component when active.")
# Check the box is now enabled.
box_active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', box_component)
if not box_active:
fail_test("Box not active.")
print("Conflicting components test successful.")
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
app = QtWidgets.QApplication.instance()
test = TestConflictingComponents()
test.run()
@@ -0,0 +1,368 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C2603859: Parent Slice should still load on map if a child slice is corrupted.
https://testrail.agscollab.com/index.php?/cases/view/2603859
"""
import os
import sys
import shutil
import azlmbr.qt_helpers
import azlmbr
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.slice as slice
import azlmbr.asset as asset
import azlmbr.math as math
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest, QtCore, QtGui
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
from azlmbr.entity import EntityId
class TestCorruptChildSlice(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="corrupt_child_slice: ", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Carry out various operations to ensure a corrupt child slice does not
prevent a parent slice from loading.
Expected Behavior:
Parent layer of corrupt slice can be loaded.
Test Steps:
1) Create a new entity named "CorruptC".
2) Save as a slice.
3) Create a new entity named "CorruptB".
4) Make CorruptC a child of CorruptB.
5) Save CorruptB as a slice.
6) Create a new entity named CorruptA.
7) Make CorruptB a child of CorruptA.
8) Save CorruptA as a slice.
9) Instantiate an instance of CorruptB.
10) Save the level and close the editor.
11) Rename the CorruptC slice to CorruptC.slice.backup.
12) Make a copy of CorruptB.slice named CorruptB.slice.backup.
13) Open the editor and level.
14) Check that an error is displayed when loading the level.
15) Check that CorruptB and A have loaded but CorruptA is absent.
16) Check that CorruptA has a blue icon.
17) Check that CorruptB is orange.
18) Close the error message.
19) Use the context menu of CorruptB to enter the Advanced Save dialog.
20) Check there is a warning message in the dialog.
21) Check that either CorruptB has "changed" next to it or CorruptB is listed as
having invalid references removed.
22) Press "Save Selected Overrides".
23) Close the editor and reopen the same level again.
24) Check there are no warnings and the level loads correctly.
25) Create a second instance of CorruptA.
26) Check there are no errors.
Note:
- This test file must be called from the Lumberyard 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
"""
Outliner_Name = "Entity Outliner (PREVIEW)"
Console_Name = "Console"
def fail_test(msg):
print(msg)
print("Test failed.")
sys.exit()
async def save_level_as(main_editor_window, level_name_text):
save_action = pyside_utils.get_action_for_menu_path(main_editor_window, "File", "Save As")
pyside_utils.trigger_action_async(save_action)
active_widget = await pyside_utils.wait_for_modal_widget()
save_level_as_dialogue = active_widget.findChild(QtWidgets.QWidget, "LevelFileDialog")
level_name = await pyside_utils.wait_for_child_by_hierarchy(
save_level_as_dialogue,
...,
dict(type=QtWidgets.QLineEdit, objectName="nameLineEdit"),
)
level_name.setText(level_name_text)
button_box = save_level_as_dialogue.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
await pyside_utils.close_modal(active_widget)
async def open_level_check_for_warning(level_name):
general.open_level_no_prompt(level_name)
try:
dialog = await pyside_utils.wait_for_child_by_hierarchy(None,
{"objectName": "ErrorLogDialog", "type": QtWidgets.QDialog},
timeout=2.0)
if dialog is None:
return False
active_widget = QtWidgets.QApplication.activeModalWidget()
button = active_widget.findChild(QtWidgets.QPushButton, "okButton")
if button is not None:
button.click()
except pyside_utils.EventLoopTimeoutException:
# The assertion is a timeout while waiting for the dialog.
return False
return True
async def push_slice_confirm_overwrite(button):
# If a confirm dialog pops up, press the confirm button.
pyside_utils.click_button_async(button)
try:
dialog = await pyside_utils.wait_for_child_by_hierarchy(None,
{"objectName": "SliceUtilities.warningMessageBox",
"type": QtWidgets.QMessageBox},
timeout=1.0)
confirm_button = pyside_utils.find_child_by_pattern(dialog, "Confirm")
pyside_utils.click_button_async(confirm_button)
await pyside_utils.close_modal(dialog)
except pyside_utils.EventLoopTimeoutException:
# No overwrite dialog appeared.
return False
return True
async def push_slice_to_file(slice_entity_id):
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [slice_entity_id])
general.idle_wait(0.0)
pyside_utils.run_soon(lambda: slice.SliceRequestBus(bus.Broadcast, "ShowPushDialog", [slice_entity_id]))
active_widget = await pyside_utils.wait_for_modal_widget()
tree_widget = active_widget.findChild(QtWidgets.QTreeWidget, "SlicePushWidget.m_fieldTree")
if tree_widget is None:
return False
# Check warning message
warning_message = active_widget.findChild(QtWidgets.QLabel, "SlicePushWidget.m_warningTitle")
if warning_message is None:
fail_test("Failed to find warning message in push dialog.")
if not warning_message.text().startswith("1 missing reference(s)"):
fail_test("Failed to find Invalid References warning message.")
# Check for CorruptB in the field selection.
found_corrupt_b_message = False
iterator = QtWidgets.QTreeWidgetItemIterator(tree_widget)
while iterator.value():
item = iterator.value()
# Check it has one of the two possible messages.
if item.text(0) == "CorruptB (invalid references will be removed)" or item.text(
0) == "CorruptB (changed)":
found_corrupt_b_message = True
iterator += 1
if not found_corrupt_b_message:
fail_test("Failed to find CorruptB invalid reference removal message.")
# Press the save button.
save_button = pyside_utils.find_child_by_pattern(active_widget, "Save Selected Overrides")
overwrite_result = await push_slice_confirm_overwrite(save_button)
await pyside_utils.close_modal(active_widget)
return overwrite_result
def create_entity_with_name(entity_name):
new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
if not new_entity_id.IsValid():
fail_test("Failed to create new entity.")
editor.EditorEntityAPIBus(bus.Event, 'SetName', new_entity_id, entity_name)
name = editor.EditorEntityInfoRequestBus(bus.Event, 'GetName', new_entity_id)
return new_entity_id
def path_is_valid_asset(asset_path):
tmp_asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return tmp_asset_id.invoke("IsValid")
def save_entity_as_slice(entity_id, slice_name):
current_level_name = editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName")
slice_file = slice_name + ".slice"
slice_dir = os.path.join("Levels", current_level_name)
full_path = os.path.join(slice_dir, slice_file)
slice_created = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", entity_id, full_path)
self.wait_for_condition(lambda: path_is_valid_asset(full_path), 10.0)
if not slice_created:
fail_test("Failed to create slice.")
general.idle_wait(0.0)
return slice_dir, slice_file
def check_log_for_text_absence(console_text_edit, text_to_check):
# Grab the log text
text = console_text_edit.toPlainText()
# Scan it for failed save lines.
lines = text.split('\n')
for line in lines:
if line.find(text_to_check) >= 0:
return False
return True
EditorTestHelper.after_level_load(self)
app = QtWidgets.QApplication.instance()
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
# Make sure the outliner and console are open.
general.open_pane(Outliner_Name)
entity_outliner = editor_window.findChild(QtWidgets.QDockWidget, Outliner_Name)
if entity_outliner is None:
fail_test("Failed to find the outliner.")
outliner_main_window = entity_outliner.findChild(QtWidgets.QMainWindow)
if outliner_main_window is None:
fail_test("Failed to find the outliner main window.")
outliner_tree_view = outliner_main_window.findChild(QtWidgets.QTreeView, "m_objectTree")
if outliner_tree_view is None:
fail_test("Failed to find the outliner tree view.")
general.open_pane(Console_Name)
console = main_window.findChild(QtWidgets.QDockWidget, Console_Name)
if console is None:
fail_test("Failed to find the console.")
text_edit = console.findChild(QtWidgets.QPlainTextEdit, "textEdit")
if text_edit is None:
fail_test("Failed to find console textEdit.")
# Create a level.
result = general.create_level_no_prompt("test_level_1", 1024, 1, 4096, True)
if result != 0:
fail_test("Failed to create level")
general.idle_wait(2.0)
# Save as a different level name so we can switch between them.
await save_level_as(editor_window, "test_level_2")
# Create the entities.
corrupt_a_id = create_entity_with_name("CorruptA")
corrupt_b_id = create_entity_with_name("CorruptB")
corrupt_c_id = create_entity_with_name("CorruptC")
# Make C a slice.
slice_file_path, slice_c_file = save_entity_as_slice(corrupt_c_id, "CorruptC")
# Make C a child of B.
azlmbr.components.TransformBus(bus.Event, "SetParent", corrupt_c_id, corrupt_b_id)
# Make B a slice.
slice_file_path, slice_b_file = save_entity_as_slice(corrupt_b_id, "CorruptB")
# Make B a child of A.
azlmbr.components.TransformBus(bus.Event, "SetParent", corrupt_b_id, corrupt_a_id)
# Make A a slice.
slice_file_path, slice_a_file = save_entity_as_slice(corrupt_a_id, "CorruptA")
# Instantiate another B
slice_path = os.path.join(slice_file_path, slice_b_file)
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", slice_path, math.Uuid(), False)
transform = math.Transform_CreateIdentity()
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
general.idle_wait(0.0)
# Save.
general.save_level()
# Switch levels while the files are manipulated.
general.open_level_no_prompt("test_level_1")
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") != "test_level_1":
fail_test("Failed to reload test_level_1")
# Rename the slice C file.
game_folder = editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'GetGameFolder')
src = os.path.join(game_folder, slice_file_path, slice_c_file)
dest = src + ".backup"
os.rename(src, dest)
# Duplicate the slice B file and rename that.
src = os.path.join(game_folder, slice_file_path, slice_b_file)
dest = src + ".backup"
shutil.copyfile(src, dest)
# Switch back to the level with the slice instances in and check for error messages.
seen_error_dialog = await open_level_check_for_warning("test_level_2")
if not seen_error_dialog:
fail_test("Failed to find error message dialog.")
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") != "test_level_2":
fail_test("Failed to reload test_level_2")
# Check that A and B are present but not C
corrupt_a_id = general.find_editor_entity("CorruptA")
if not corrupt_a_id.isValid():
fail_test("No CorruptA entity found after reload")
corrupt_b_id = general.find_editor_entity("CorruptB")
if not corrupt_b_id.isValid():
fail_test("No CorruptB entity found after reload")
corrupt_c_id = general.find_editor_entity("CorruptC")
if corrupt_c_id.isValid():
fail_test("CorruptC entity found after reload")
# Pop up the advanced push dialog for CorruptB and push it.
push_success = await push_slice_to_file(corrupt_b_id)
if not push_success:
fail_test("Failed to push slice.")
# Switch levels again so we can reload level2.
general.open_level_no_prompt("test_level_1")
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") != "test_level_1":
fail_test("Failed to reload test_level_1")
# Switch back to level 2 and check the error dialog did not appear.
seen_error_dialog = await open_level_check_for_warning("test_level_2")
if seen_error_dialog:
fail_test("Level failed to load without errors.")
# Instantiate another CorruptA and check there are no errors.
# Any errors will appear in the console so we need to check that.
text_edit.clear()
slice_path = os.path.join(slice_file_path, slice_a_file)
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", slice_path, math.Uuid(), False)
transform = math.Transform_CreateIdentity()
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
general.idle_wait(0.0)
if not check_log_for_text_absence(text_edit, "could not be loaded"):
fail_test("Error occurred instantiating CorruptA.")
print("Corrupt child slice test complete.")
test = TestCorruptChildSlice()
test.run()
@@ -0,0 +1,108 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C17219006: Create Camera Entity from view
https://testrail.agscollab.com/index.php?/cases/view/17219006
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class CreateCameraComponentFromViewTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="create_camera_comp_from_view", args=["level"])
def run_test(self):
"""
Summary:
Create Camera Entity from view - This can be verified by checking if the entity is created with
Camera component and if the Camera is listed in Viewport Camera Selector
Expected Behavior:
An entity is created with a Camera Component attached from the current viewport perspective.
The camera entity is added to the Viewport Camera Selector.
The Camera entity is labeled "Camera #" where # is replaced by the #number of camera entities in the level.
Test Steps:
1) Open level
2) Trigger the action 'Create camera entity from view' from viewport context menu
3) Verify if the entity is created with the name Camera# (Ex: Camera1)
4) Verify if the entity has Camera component added
5) Verify if Camera1 is listed in the Viewport Camera Selector
6) Close Viewport Camera Selector
Note:
- This test file must be called from the Lumberyard 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
"""
# 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=True,
)
general.idle_wait(3.0)
# 2) Trigger the action 'Create camera entity from view' from viewport context menu
# Get the editor window, main window, viewport objects
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
view_port = pyside_utils.find_child_by_hierarchy(main_window, ..., "renderOverlay")
view_port = main_window.findChildren(QtWidgets.QWidget, "renderOverlay")[0]
# Trigger the action
pyside_utils.trigger_context_menu_entry(view_port, "Create camera entity from view")
# 3) Verify if the entity is created with the name Camera# (Ex: Camera1)
camera_entity_id = general.find_editor_entity("Camera1")
assert camera_entity_id.isValid(), "Entity with Camera component is not created"
# 4) Verify if the entity has Camera component added
assert hydra.has_components(camera_entity_id, ["Camera"]), "Entity do not have a Camera component"
# 5) Verify if Camera1 is listed in the Viewport Camera Selector
# Open Viewport Camera Selector if not opened already
if not general.is_pane_visible("Viewport Camera Selector"):
general.open_pane("Viewport Camera Selector")
if general.is_pane_visible("Viewport Camera Selector"):
print("Viewport Camera Selector is opened")
# Get the Camera Selector object and the list of cameras
camera_selector = editor_window.findChildren(QtWidgets.QDockWidget, "Viewport Camera Selector")[0]
list_view = camera_selector.findChildren(QtWidgets.QListView)[0]
# Verify if the Camera1 is present in the list
assert pyside_utils.find_child_by_pattern(
list_view, "Camera1"
), "Camera1 is not listed in the Viewport Camera Selector"
# 6) Close Viewport Camera Selector
camera_selector.close()
if not general.is_pane_visible("Viewport Camera Selector"):
print("Viewport Camera Selector is closed")
test = CreateCameraComponentFromViewTest()
test.run()
@@ -0,0 +1,102 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C1519572: Create a dynamic slice
https://testrail.agscollab.com/index.php?/cases/view/1519572
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.asset as asset
import azlmbr.math as math
import azlmbr.slice as slice
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class CreateDynamicSliceTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="create_dynamic_slice", args=["level"])
def run_test(self):
"""
Summary:
Create a dynamic slice
Expected Behavior:
Dynamic slices can be set/unset
Test Steps:
1) Open level
2) Create new entity
3) Create new slice from the newly created entity
4) Set an existing slice to dynamic
5) Unset the dynamic slice
Note:
- This test file must be called from the Lumberyard 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 is_slice_dynamic(asset_id):
if slice.SliceRequestBus(bus.Broadcast, "IsSliceDynamic", asset_id):
return "Dynamic"
return "Not Dynamic"
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
# 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=True,
)
general.idle_wait(3.0)
# 2) Create new entity
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
# 3) Create new slice from the newly created entity
slice_path = os.path.join("slices", "TestSlice.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", entity_id, slice_path)
self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 10.0)
# 4) Set an existing slice to dynamic
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", slice_path, math.Uuid(), False)
print(f"Slice Status (Before setting to dynamic): {is_slice_dynamic(asset_id)}")
slice.SliceRequestBus(bus.Broadcast, "SetSliceDynamic", asset_id, True)
self.wait_for_condition(lambda: slice.SliceRequestBus(bus.Broadcast, "IsSliceDynamic", asset_id), 5.0)
print(f"Slice Status (After setting to dynamic): {is_slice_dynamic(asset_id)}")
# 5) Unset the dynamic slice
print(f"Slice Status (Before unsetting to dynamic): {is_slice_dynamic(asset_id)}")
slice.SliceRequestBus(bus.Broadcast, "SetSliceDynamic", asset_id, False)
self.wait_for_condition(lambda: not slice.SliceRequestBus(bus.Broadcast, "IsSliceDynamic", asset_id), 5.0)
print(f"Slice Status (After unsetting to dynamic): {is_slice_dynamic(asset_id)}")
test = CreateDynamicSliceTest()
test.run()
@@ -0,0 +1,107 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6317058: Viewport Entity Creation
https://testrail.agscollab.com/index.php?/cases/view/6317058
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.editor as editor
import azlmbr.components as components
import azlmbr.bus as bus
import azlmbr.math as math
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ViewportEntityCreation(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="viewport_entity_creation: ", args=["level"])
def run_test(self):
"""
Summary:
Verify if creating new entities via the viewport context menu works correctly.
Expected Behavior:
Right clicking the viewport and selecting the option in the context menu creates an entity under the cursor.
Test Steps:
1) Open a new level
2) Move the cursor on the viewport
3) Copy the position on the level pointed at by the cursor from the viewport's toolbar
4) Create a new entity via the context menu
5) Verify the entity has been created at the right position
Note:
- This test file must be called from the Lumberyard 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
"""
# 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,
)
# Grab the necessary widgets
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
viewport_window = main_window.findChild(QtWidgets.QMainWindow)
overlay = viewport_window.findChild(QtWidgets.QWidget, "renderOverlay")
posCtrl = viewport_window.findChild(QtWidgets.QWidget, "m_posCtrl")
labels = ["X", "Y", "Z"]
pos = [0.0, 0.0, 0.0]
# 2) Click on the Viewport to focus
QtTest.QTest.mouseClick(overlay, Qt.LeftButton)
# Trigger a Move to update position in viewport toolbar
QtTest.QTest.mouseMove(overlay)
# 3) Get position values
for i, label in enumerate(labels):
vector_element = posCtrl.findChild(QtWidgets.QWidget, label)
line_edit = vector_element.findChild(QtWidgets.QLineEdit)
pos[i] = float(line_edit.text())
# 4) Trigger Context Menu and click Create Entity
pyside_utils.trigger_context_menu_entry(overlay.parent(), "Create entity")
# Get Selected Entity (it will be the newly created one)
selectedTestEntityIds = editor.ToolsApplicationRequestBus(bus.Broadcast, 'GetSelectedEntities')
newEntityId = selectedTestEntityIds[0]
# Get Position of new Entity
position = components.TransformBus(bus.Event, "GetWorldTranslation", newEntityId)
# 5) Verify the positions match (tolerance threshold is 1.0 since this is based on mouseClick
# right-click position which has some room for error)
POSITION_TOLERANCE = 1.0
if (math.Math_IsClose(pos[0], position.x, POSITION_TOLERANCE) and
math.Math_IsClose(pos[1], position.y, POSITION_TOLERANCE) and
math.Math_IsClose(pos[2], position.z, POSITION_TOLERANCE)):
print("New Entity created in the correct position")
test = ViewportEntityCreation()
test.run()
@@ -0,0 +1,76 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1506874: Create Input Bindings File
https://testrail.agscollab.com/index.php?/cases/view/1506874
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.bus as bus
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class CreateInputBindingFileTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="create_inputbindings_file", args=["level"])
def run_test(self):
"""
Summary:
Create a new inputbinding file
Expected Behavior:
The newly created inputbindings file is loaded into the Asset Editor.
Test Steps:
1) Open a new level
2) Create new inputbindings file
3) Verify if file is created
Note:
- This test file must be called from the Lumberyard 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
"""
INPUTBINDING_FILE_PATH = "SamplesProject/temp.inputbindings"
# 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=True,
)
# 2) Create new inputbindings file
input_bindings_type_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetTypeByDisplayName", "Input Bindings"
)
editor.AssetEditorRequestsBus(bus.Broadcast, "CreateNewAsset", input_bindings_type_id)
editor.AssetEditorWidgetRequestsBus(bus.Broadcast, "SaveAssetAs", INPUTBINDING_FILE_PATH)
# 3) Verify if file is created
self.wait_for_condition(lambda: os.path.exists(INPUTBINDING_FILE_PATH), 0.5)
print(f"New inputbindings file created: {os.path.exists(INPUTBINDING_FILE_PATH)}")
test = CreateInputBindingFileTest()
test.run_test()
@@ -0,0 +1,99 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6351273: Create a new level
https://testrail.agscollab.com/index.php?/cases/view/6351273
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestCreateNewLevel(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="create_new_level: ", args=["level"])
def run_test(self):
"""
Summary:
Create a new level and verify if the level is loaded properly.
Expected Behavior:
A new level is created and loaded into the editor.
Test Steps:
1) Create new level
2) Verify if the new level is loaded
Note:
- This test file must be called from the Lumberyard 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 on_focus_changed(old, new):
print("focus Changed")
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
if active_modal_widget:
new_level_dlg = active_modal_widget.findChild(QtWidgets.QWidget, "CNewLevelDialog")
if new_level_dlg:
if new_level_dlg.windowTitle() == "New Level":
print("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_folders = grp_box.findChild(QtWidgets.QComboBox, "LEVEL_FOLDERS")
level_folders.setCurrentText("Levels/")
button_box = new_level_dlg.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
def on_action_triggered():
print("New Level Action triggered")
try:
# 1) Create new level
# Wait 2.0s for editor to load
general.idle_wait(2.0)
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "New Level")
action.triggered.connect(on_action_triggered)
action.trigger()
action.triggered.disconnect(on_action_triggered)
# 2) Verify if the new level is loaded
general.idle_wait(3.0)
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") == self.args["level"]:
print("Create and load new level: SUCCESS")
else:
print("Create and load new level: FAILED")
except Exception as e:
print(e)
finally:
print("Disconnecting focusChanged signal")
app.focusChanged.disconnect(on_focus_changed)
test = TestCreateNewLevel()
test.run()
@@ -0,0 +1,135 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6321576: Basic Function: Customize Keyboard
https://testrail.agscollab.com/index.php?/cases/view/6321576
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtGui, QtTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestCustomizeKeyboard(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="customize_keyboard: ")
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Assign any new shortcut keys to menu command.
Expected Behavior:
Keyboard customizations can be changed, saved, and function properly.
Test Steps:
1) Open the Customize Keyboard dialog and Assign any button combination as new shortcut.
2) Use the newly assigned hotkey.
3) Restore the settings to default
Note:
- This test file must be called from the Lumberyard 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
"""
# 1) Open the Customize Keyboard dialog and Assign any button combination as new shortcut
editor_window = pyside_utils.get_editor_main_window()
action = pyside_utils.find_child_by_pattern(
editor_window, {"text": "Customize &Keyboard...", "type": QtWidgets.QAction}
)
pyside_utils.trigger_action_async(action)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
if active_modal_widget:
keyboard_customizer = active_modal_widget.findChild(QtWidgets.QDialog, "CustomizeKeyboardDialog")
if keyboard_customizer:
print("Customize keyboard window opened successfully")
keySequenceEdit = pyside_utils.find_child_by_pattern(keyboard_customizer, "keySequenceEdit")
assign_button = pyside_utils.find_child_by_pattern(keyboard_customizer, "assignButton")
categories = keyboard_customizer.findChild(QtWidgets.QComboBox, "categories")
commandsView = keyboard_customizer.findChild(QtWidgets.QListView, "commandsView")
# Assign a shortcut to the Tools -> Asset Editor action
categories.setCurrentText("Tools")
asset_editor_index = pyside_utils.find_child_by_pattern(commandsView, "Asset Editor")
commandsView.setCurrentIndex(asset_editor_index)
keySequenceEdit.setKeySequence(QtGui.QKeySequence(Qt.CTRL + Qt.Key_M))
assign_button.setEnabled(True)
if assign_button.isEnabled():
app = QtWidgets.QApplication.instance()
pyside_utils.click_button_async(assign_button)
# We need to handle in case this test had failed previously, the shortcut might
# already be assigned, in which case there will be a second modal dialog
# confirmation to overwrite it
try:
await pyside_utils.wait_for_condition(lambda: app.activeModalWidget() and app.activeModalWidget() != active_modal_widget, timeout=0.1)
confirmation_popup = app.activeModalWidget()
message_box = confirmation_popup.findChild(QtWidgets.QMessageBox)
button = message_box.button(QtWidgets.QMessageBox.Yes)
pyside_utils.click_button_async(button)
await pyside_utils.wait_for_destroyed(confirmation_popup)
except pyside_utils.EventLoopTimeoutException:
# If this timed out, it just means that the shortcut wasn't already assigned
pass
# "Close" button acts as "Save and Close" in this widget
button_box = pyside_utils.find_child_by_pattern(keyboard_customizer, "buttonBox")
button = button_box.button(QtWidgets.QDialogButtonBox.Close)
pyside_utils.click_button_async(button)
await pyside_utils.wait_for_destroyed(active_modal_widget)
# 2) Use the newly assigned hotkey.
general.close_pane("Asset Editor")
QtTest.QTest.keyPress(editor_window, Qt.Key_M, Qt.ControlModifier)
success = await pyside_utils.wait_for_condition(lambda: general.is_pane_visible("Asset Editor"))
if success:
print("New shortcut works : Asset Editor opened")
# Close it now since we are going to verify the shortcut no longer works
# after restoring the default settings
general.close_pane("Asset Editor")
# 3) Restore the settings to default
pyside_utils.trigger_action_async(action)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
if active_modal_widget:
button_box = pyside_utils.find_child_by_pattern(active_modal_widget, "buttonBox")
restore_defaults = pyside_utils.find_child_by_pattern(button_box, "Restore Defaults")
pyside_utils.click_button_async(restore_defaults)
confirmation_modal_widget = await pyside_utils.wait_for_modal_widget()
if confirmation_modal_widget:
message_box = confirmation_modal_widget.findChild(QtWidgets.QMessageBox)
button = message_box.button(QtWidgets.QMessageBox.Yes)
button.click()
# "Close" button acts as "Save and Close" in this widget
close_button = button_box.button(QtWidgets.QDialogButtonBox.Close)
pyside_utils.click_button_async(close_button)
await pyside_utils.wait_for_destroyed(active_modal_widget)
# Verify if setting restored
QtTest.QTest.keyClick(editor_window, Qt.Key_M, Qt.ControlModifier)
if not general.is_pane_visible("Asset Editor"):
print("Default shortcuts restored : Asset Editor stays closed")
test = TestCustomizeKeyboard()
test.run()
@@ -0,0 +1,155 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C1510643: Deleting Entities in the Entity Outliner
https://testrail.agscollab.com/index.php?/cases/view/1510643
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestDeletingEntitiesFromOutliner(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="deleting_entities_from_outliner: ", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Create a nested group of Entities with parent and 2 children.
Right click on each of the entities and select "Delete".
Undo the deletion of those entites.
Expected Behavior:
Deleting a Child entity does not delete the parent.
The child entity is restored as part of the hierarchy.
Deleting the Parent entity deletes all child entities linked to it.
The entity hierarchy is restored.
Test Steps:
1) Create a nested group of Entities and verify deleting the child does not destroy the parent
1.1) Create group of entities
1.2) Delete child
1.3) Verify parent exists
2) Undo the deletion of the child entity and verify the same is restored
2.1) Undo the deletion
2.2) verify child entity exists
3) Delete the parent entity and verify all child entities deleted
3.1) Delete parent
3.2) Verify all child entities deleted
4) Undo the deletion of the parent entity and verify hierarchy is restored
4.1) Undo the deletion
4.2) Verify all entities exist
Note:
- This test file must be called from the Lumberyard 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 get_entity_count_by_id(entity_id):
entity_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
searchFilter = EntityId.SearchFilter()
searchFilter.names = [entity_name]
entities = EntityId.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
async def delete_entity(entity_id, entity_name):
"""
Delete an entity using PySide Right-Click and Delete.
:param entity_id: used to find and delete the entity
:param entity_name: not dynamically grabbed from entity or used in delete code but is used for printing out a comment statement for testrunner
:return None
"""
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
entity_outliner = main_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
object_tree = entity_outliner.findChild(QtWidgets.QTreeView, "m_objectTree")
index_to_delete = pyside_utils.find_child_by_pattern(object_tree, entity_name)
object_tree.setCurrentIndex(index_to_delete)
await pyside_utils.trigger_context_menu_entry(object_tree, "Delete", index=index_to_delete)
if get_entity_count_by_id(entity_id) == 0:
print(f"{entity_name} deleted succesfully")
# Create a level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
# 1) Create a nested group of Entities and verify deleting the child does not destroy the parent
# 1.1) Create group of entities
parent_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
editor.EditorEntityAPIBus(bus.Event, "SetName", parent_entity_id, "Parent entity")
if get_entity_count_by_id(parent_entity_id) ==1:
print("Parent Entity created")
child_entity_1 = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_entity_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_entity_1, "Child entity 1")
child_entity_2 = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_entity_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_entity_2, "Child entity 2")
if get_entity_count_by_id(child_entity_1) == 1 and get_entity_count_by_id(child_entity_1) == 1:
print("Child Entities created")
# 1.2) Delete child
await delete_entity(child_entity_1, "Child entity 1")
# 1.3) Verify parent exists
if get_entity_count_by_id(parent_entity_id) != 0:
print("Deleting a Child entity does not delete the parent")
# 2) Undo the deletion of the child entity and verify the same is restored
# 2.1) Undo the deletion
general.undo()
# 2.2) verify child entity exists
success = await pyside_utils.wait_for_condition(lambda: get_entity_count_by_id(child_entity_1) != 0)
if success:
print("Child entity restored successfully")
# 3) Delete the parent entity and verify all child entities deleted
# 3.1) Delete parent
await delete_entity(parent_entity_id, "Parent entity")
# 3.2) Verify all child entities deleted
if get_entity_count_by_id(child_entity_1) == 0 and get_entity_count_by_id(child_entity_1) == 0:
print("Deleting the Parent entity deletes all child entities linked to it")
# 4) Undo the deletion of the parent entity and verify hierarchy is restored
# 4.1) Undo the deletion
general.undo()
# 4.2) Verify all entities exist
success = await pyside_utils.wait_for_condition(lambda:
(get_entity_count_by_id(parent_entity_id) != 0
and get_entity_count_by_id(child_entity_1) != 0
and get_entity_count_by_id(child_entity_2) != 0))
if success:
print("Entity hierarchy is restored")
test = TestDeletingEntitiesFromOutliner()
test.run()
@@ -0,0 +1,146 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C1564083 : Edit Mode Toolbar
https://testrail.agscollab.com/index.php?/cases/view/1564083
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestEditModeToolbar(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="edit_mode_toolbar_function: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with Edit mode toolbar options and verify if all the options are functional.
Expected Behavior:
All options available in the Edit Mode Toolbar function as intended.
Test Steps:
1) Create/Open a level
2) Open the Edit mode Toolbar
3) Use every option in the toolbar.
Note:
- This test file must be called from the Lumberyard 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
"""
editor_window = pyside_utils.get_editor_main_window()
editors_toolbar = editor_window.findChild(QtWidgets.QToolBar, "EditMode")
def get_entity_count(entity_id):
searchFilter = EntityId.SearchFilter()
searchFilter.names = [editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)]
entities = EntityId.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
def get_tool_button(option):
button = pyside_utils.find_child_by_pattern(editors_toolbar, option)
if button.text() == option:
return button
def wait_condition_print(function, wait_time, print_message=""):
self.wait_for_condition(function, wait_time)
if function() and print_message != "":
print(print_message)
# 1) Create new level with an Entity
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
# 2) Open the Editors Toolbar
if not editors_toolbar.isVisible():
editors_toolbar.toggleViewAction().trigger()
if editors_toolbar.isVisible():
print("Editors tool bar opened successfully")
# 3) Use every option in the toolbar.
# i) Undo
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
self.wait_for_condition(lambda: get_tool_button("Undo").isEnabled(), 2.0)
initial_count = get_entity_count(entity_id)
get_tool_button("Undo").click()
wait_condition_print(lambda: get_entity_count(entity_id) != initial_count, 2.0, "Undo tool button is functional")
# ii) Redo
self.wait_for_condition(lambda: get_tool_button("Redo").isEnabled(), 2.0)
get_tool_button("Redo").click()
wait_condition_print(lambda: get_entity_count(entity_id) == initial_count, 2.0, "Redo tool button is functional")
# iii) Move
move_button = get_tool_button("Move")
if not move_button.isChecked(): move_button.click()
if move_button.isChecked(): print("Move tool button is accesible")
# iv) Rotate
rotate_button = get_tool_button("Rotate")
if not rotate_button.isChecked(): rotate_button.click()
if rotate_button.isChecked(): print("Rotate tool button is accesible")
# v) Scale
scale_button = get_tool_button("Scale")
if not scale_button.isChecked(): scale_button.click()
if scale_button.isChecked(): print("Scale tool button is accesible")
# vi) Play Game
get_tool_button("Play Game").click()
wait_condition_print(lambda : general.is_in_game_mode(), 3.0, "Play game tool button is responsive")
general.exit_game_mode()
# vii) Snap to grid
# Wait for the snap_to_grid_text to become enabled since in the previous step we are leaving game mode,
# so it might still be disabled momentarily
snap_to_grid = get_tool_button("Snap To Grid")
snap_grid_widget = snap_to_grid.parent()
snap_to_grid.setChecked(True)
snap_to_grid_text = snap_grid_widget.findChild(QtWidgets.QDoubleSpinBox).findChild(QtWidgets.QLineEdit)
self.wait_for_condition(lambda: snap_to_grid_text.isEnabled())
if snap_to_grid.isChecked() == snap_to_grid_text.isEnabled():
print("Snap to grid tool button is responsive")
# viii) Snap Angle
# Wait for the snap_angle_text to become enabled since in the previous step we are leaving game mode,
# so it might still be disabled momentarily
snap_angle = get_tool_button("Snap angle")
snap_angle_widget = snap_angle.parent()
snap_angle.setChecked(True)
snap_angle_text = snap_angle_widget.findChild(QtWidgets.QDoubleSpinBox).findChild(QtWidgets.QLineEdit)
self.wait_for_condition(lambda: snap_angle_text.isEnabled())
if snap_angle.isChecked() == snap_angle_text.isEnabled():
print("Snap angle tool button is responsive")
test = TestEditModeToolbar()
test.run_test()
@@ -0,0 +1,156 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C697735: Custom layouts can be saved
C697736: Custom/Default layouts can be loaded
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestLoadLayout(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="load_layout")
def run_test(self):
"""
Summary:
Creates a custom Editor layout, resets to the default options, and then reloads the custom layout.
Expected Behavior:
Pane/tool layout matches expected results for both default and custom layout.
Test Steps:
1) Open Editor
2) Open a few tools and dock them/leave floating
3) Save the custom layout
4) Restore the default layout and verify proper tools/panes are open
5) Load the custom layout and verify proper tools/panes are open
6) Delete the custom layout to cleanup
Note:
- This test file must be called from the Lumberyard 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
"""
custom_layout_name = "custom_layout_test"
editor_window = pyside_utils.get_editor_main_window()
menu_paths = [
("Layouts", "Save Layout"),
("Layouts", "Restore Default Layout"),
("Layouts", custom_layout_name, "Load"),
("Layouts", custom_layout_name, "Delete")
]
def on_save_layout_trigger():
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
if active_modal_widget:
save_level_as_dialogue = active_modal_widget.findChild(QtWidgets.QInputDialog)
if save_level_as_dialogue.windowTitle() == "Layout Name":
print("The 'Layout Name' dialog appeared")
layout_name_field = active_modal_widget.findChild(QtWidgets.QLineEdit)
layout_name_field.setText(custom_layout_name)
button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox)
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
def on_restore_default_layout_trigger():
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
if active_modal_widget:
restore_default_layout_dialog = active_modal_widget.findChild(QtWidgets.QMessageBox)
if restore_default_layout_dialog.windowTitle() == "Restore Default Layout":
print("The 'Restore Default Layout' dialog appeared")
button_box = active_modal_widget.findChildren(QtWidgets.QDialogButtonBox)[0]
button_box.button(QtWidgets.QDialogButtonBox.RestoreDefaults).click()
def on_custom_layout_load_trigger():
print("Restoring default layout")
def on_delete_custom_layout_trigger():
print("Deleting custom layout")
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
if active_modal_widget:
delete_custom_layout_dialog_button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox)
if delete_custom_layout_dialog_button_box:
print("The 'Delete Layout' dialog appeared")
delete_custom_layout_dialog_button_box.button(QtWidgets.QDialogButtonBox.Yes).click()
def trigger_view_menu_option(menu_path, on_trigger):
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_trigger)
try:
action = pyside_utils.get_action_for_menu_path(editor_window, "View", *menu_path)
action.trigger()
print(f"{action.iconText()} Action triggered")
finally:
app.focusChanged.disconnect(on_trigger)
# Open a few floating tools
general.open_pane('Landscape Canvas')
general.open_pane('Track View')
general.open_pane('UI Editor')
# Close a few open tools
general.close_pane('Console')
general.close_pane('Asset Browser')
# Verify proper tools are open in the Editor prior to saving layout
landscape_canvas_open = general.is_pane_visible('Landscape Canvas')
track_view_open = general.is_pane_visible('Track View')
ui_editor_open = general.is_pane_visible('UI Editor')
console_closed = not general.is_pane_visible('Console')
asset_browser_closed = not general.is_pane_visible('Asset Browser')
self.test_success = landscape_canvas_open and track_view_open and ui_editor_open and console_closed \
and asset_browser_closed
if self.test_success:
print('Custom layout setup complete')
# Save layout
trigger_view_menu_option(menu_paths[0], on_save_layout_trigger)
# Trigger Restore Default Layout option and click Restore Defaults on popup dialog
trigger_view_menu_option(menu_paths[1], on_restore_default_layout_trigger)
# Verify proper panes are open with Default Layout
console_open = general.is_pane_visible('Console')
asset_browser_open = general.is_pane_visible('Asset Browser')
self.test_success = self.test_success and console_open and asset_browser_open
general.idle_wait(2.0)
# Trigger Load of saved custom layout
trigger_view_menu_option(menu_paths[2], on_custom_layout_load_trigger)
# Verify proper panes are open with saved custom layout
landscape_canvas_open = general.is_pane_visible('Landscape Canvas')
track_view_open = general.is_pane_visible('Track View')
ui_editor_open = general.is_pane_visible('UI Editor')
self.test_success = self.test_success and landscape_canvas_open and track_view_open and ui_editor_open
general.idle_wait(2.0)
# Restore to defaults and delete custom layout
trigger_view_menu_option(menu_paths[1], on_restore_default_layout_trigger)
trigger_view_menu_option(menu_paths[3], on_delete_custom_layout_trigger)
# Verify that custom layout has been cleaned up
pyside_utils.get_action_for_menu_path(editor_window, "View", *menu_paths[2])
test = TestLoadLayout()
test.run()
@@ -0,0 +1,103 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C2174442: Enter/Exit Component Mode
https://testrail.agscollab.com/index.php?/cases/view/2174442
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as EntityId
import Tests.ly_shared.hydra_editor_utils as hydra
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.editor as editor
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestEnterExitComponentMode(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="enter_exit_component_mode: ", args=["level"])
def run_test(self):
"""
Summary:
Enter/Exit Component Mode
Expected Behavior:
Component Mode can be entered and exited.
Test Steps:
1) Open level
2) Create an entity with Box Shape component
3) Click the "Edit" button on the component to enter into component mode
4) Click the "Done" button on the component to exit from component mode
Note:
- This test file must be called from the Lumberyard 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 on_entered_component_mode(parameters):
print("Entered component mode")
def on_left_component_mode(parameters):
print("Left component mode")
# 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=True,
)
general.idle_wait(3.0)
# Setup the handler to listen for Editor Component Mode notifications
context_id = editor.EditorEntityContextRequestBus(bus.Broadcast, "GetEditorEntityContextId")
handler = bus.NotificationHandler("EditorComponentModeNotificationBus")
handler.connect(context_id)
handler.add_callback("EnteredComponentMode", on_entered_component_mode)
handler.add_callback("LeftComponentMode", on_left_component_mode)
# 2) Create an entity with Box Shape component
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
general.clear_selection()
general.select_object(editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id))
hydra.add_component("Box Shape", entity_id)
# 3) Click the "Edit" button on the component to enter into component mode
# Retrieve the TypeId for the Box Shape component
type_ids = editor.EditorComponentAPIBus(bus.Broadcast, "FindComponentTypeIdsByEntityType", ["Box Shape"],
EntityId.EntityType().Game)
box_shape_type_id = type_ids[0]
# Enter component mode for the Box Shape (Same as pressing the "Edit" button on the Box Shape component)
editor.ComponentModeSystemRequestBus(bus.Broadcast, "EnterComponentMode", box_shape_type_id)
# 4) Click the "Done" button on the component to exit from component mode
# End component mode (Same as pressing the "Done" button on the Box Shape component)
editor.ComponentModeSystemRequestBus(bus.Broadcast, "EndComponentMode")
test = TestEnterExitComponentMode()
test.run()
@@ -0,0 +1,114 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C17218882: Entity duplication in Entity Outliner
https://testrail.agscollab.com/index.php?/cases/view/17218882
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt, QObject, QEvent, QPoint
from PySide2.QtGui import QContextMenuEvent
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
from re import compile as regex
class TestEntityDuplication(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="entity_duplication_entity_outliner: ", args=["level"])
def run_test(self):
"""
Summary:
Verify if the entities in the Entity Outliner can be duplicated.
Expected Behavior:
Entities can be duplicated and can be undone.
Test Steps:
1) Open a new level
2) Create a new entity by clicking "New Entity"
3) Duplicate entity using shortcut CTRL+D
4) Undo entity duplication using CTRL+Z
5) Duplicate entity using Right click "Duplicate" Action
Note:
- This test file must be called from the Lumberyard 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 get_entity_count_name(entity_name="Entity2"):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
# 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=True,
)
# 2) Create a new entity by clicking "New Entity"
general.clear_selection()
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = pyside_utils.find_child_by_hierarchy(editor_window,
...,
dict(windowTitle=regex("Entity Outliner.*")),
...,
"m_objectList").parent()
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
action = pyside_utils.find_child_by_pattern(outliner_widget, "Create Entity")
action.trigger()
if get_entity_count_name() == 1:
print("First entity created")
# 3) Duplicate entity using shortcut CTRL+D
for index in range(3):
QtTest.QTest.keyPress(outliner_widget, Qt.Key_D, Qt.ControlModifier)
success = lambda: get_entity_count_name() == index+2
self.wait_for_condition(success)
print(f"Entity duplicated using shortcut - {index+1} success: {success()}")
# 4) Undo entity duplication using CTRL+Z
QtTest.QTest.keyPress(outliner_widget, Qt.Key_Z, Qt.ControlModifier)
success = lambda: get_entity_count_name() == 3
self.wait_for_condition(success)
print(f"Entity duplication Undo success: {success()}")
# 5) Duplicate entity using Right click "Duplicate" Action
general.select_object("Entity2")
for index in range(3):
index_to_duplicate = pyside_utils.find_child_by_pattern(tree, "Entity2")
pyside_utils.trigger_context_menu_entry(tree, "Duplicate", index=index_to_duplicate)
success = lambda: get_entity_count_name() == index+4
self.wait_for_condition(success)
print(f"Entity duplication using right click - {index+1} success: {success()}")
test = TestEntityDuplication()
test.run()
@@ -0,0 +1,121 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6384955: Basic Workflow: Entity Manipulation in the Outliner
https://testrail.agscollab.com/index.php?/cases/view/6384955
"""
import os
import sys
from typing import Optional
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.entity as entity
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class BasicWorkflow(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="entity_manipulation_in_outliner", args=["level"])
def run_test(self):
"""
Summary:
Manipulate entities in the Entity Outliner
Expected:
Entities/Children can be created/restructured
Test Steps:
0) Open new test level
1) Create the Entities/Child Entities
1.1) Create Parent Entity
1.2) Create Child Entity
1.3) Create Grandchild Entity
1.4) Entities/Children can be created in Outliner and are displayed properly
2) Move the grandchild entity (child of the child entity) to be a child of the Parent (top level) entity
3) Verify Entity Heirarchies can be restructured from the Entity Outliner
4) Undo Entity Restructure (Ctrl+Z)
5) Verify Entity Hierarchy Restructure is undone
Note:
- This test file must be called from the Lumberyard 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 verify_parent(entity_to_check, expected_parent_entity, additional_pretext: Optional[str] = ""):
entity_to_check.get_parent_info()
actual_parent_id = entity_to_check.parent_id
actual_parent_name = entity_to_check.parent_name
# fmt:off
print(f"{additional_pretext} The parent entity of {entity_to_check.name}: Expected: {expected_parent_entity.name}; Actual: {actual_parent_name}")
assert actual_parent_id == expected_parent_entity.id, \
"The IDs of the actual and expected parent entities did not match"
# fmt:on
# 0) Create or open any level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(3.0)
# 1) Create the Entities/Child Entities
entity_postition = math.Vector3(125.0, 136.0, 32.0)
components_to_add = []
# 1.1) Create an entity called "Parent"
parent_entity = editor_utils.Entity("Parent")
parent_entity.create_entity(entity_postition, components_to_add)
# 1.2) Create a child entity for "Parent" named "Child"
child_entity = editor_utils.Entity("Child")
child_entity.create_entity(entity_postition, components_to_add, parent_entity.id)
# 1.3) Create a child entity for "Child" named "Grandchild"
grandchild_entity = editor_utils.Entity("Grandchild")
grandchild_entity.create_entity(entity_postition, components_to_add, child_entity.id)
# 1.4) Verify the hierarchy of the entities (Parent>Child>Grandchild)
# Child should be a child to Parent
verify_parent(child_entity, parent_entity, "Original Check:")
# Grandchild should be a child to Child
verify_parent(grandchild_entity, child_entity, "Original Check:")
# 2) Click and drag the Grandchild from the Child entity to the Parent entity
grandchild_entity.set_test_parent_entity(parent_entity)
# 3) Verify the Granchild entity is now a child of the Parent Entity
verify_parent(grandchild_entity, parent_entity, "After Move:")
# 4) Press Ctrl+Z using hydra utilities to undo the moving of the Grandchild
general.undo()
# 5) Verify the hierarchy has returned to the original orientation
verify_parent(grandchild_entity, child_entity, "After Undo:")
test = BasicWorkflow()
test.run()
@@ -0,0 +1,129 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C2202976: Entity Selection - Entity Outliner
https://testrail.agscollab.com/index.php?/cases/view/2202976
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.slice as slice
import azlmbr.asset as asset
import azlmbr.math as math
import Tests.ly_shared.hydra_editor_utils as hydra
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtCore, QtWidgets, QtGui
from re import compile as regex
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class EntitySelectionEntityOutlinerTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="entity_selection_entity_outliner", args=["level"])
def run_test(self):
"""
Summary:
Entity Selection - Entity Outliner - We need to verify if the Entity outliner works as expected
for the entity selections by verifying the selected items.
Expected Behavior:
Single clicking entities will swap selection between entities.
CTRL+Clicking entities will add to/remove from the selection group.
Test Steps:
1) Open level
2) Create 3 new entities in the outliner
3) Get Model Index for all the entities
4) Verify single click
5) Verify CTRL+click
Note:
- This test file must be called from the Lumberyard 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 verify_selected_index(expected_entity_list):
# NOTE: selectedIndexes() seems to return 2 extra elements for each item seleted whose data() is None
# So removing the items whose data is None and considering only the selected items having data.
selected_indexes = [i.data() for i in tree.selectedIndexes() if i.data()]
return sorted(selected_indexes) == sorted(expected_entity_list)
# 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=True,
)
general.idle_wait(3.0)
# Get the editor window object
editor_window = pyside_utils.get_editor_main_window()
# Get the outliner widget object based on the window title
outliner_widget = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Outliner.*")), ..., "m_objectList"
).parent()
# Get the object tree in the entity outliner
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
# 2) Create 3 new entities in the outliner
entity_postition = math.Vector3(125.0, 136.0, 32.0)
entity_list = ["Entity2", "Entity3", "Entity4"]
[hydra.Entity(name).create_entity(entity_postition, []) for name in entity_list]
# 3) Get Model Index for all the entities
model_index_2, model_index_3, model_index_4 = [
pyside_utils.find_child_by_pattern(tree, name) for name in entity_list
]
# 4) Verify single click
# click on Entity2 and verify if it is selected
pyside_utils.item_view_index_mouse_click(tree, model_index_2)
print(f"Single entity selected on first click: {verify_selected_index(['Entity2'])}")
# click on Entity3 and verify if only that is selected
pyside_utils.item_view_index_mouse_click(tree, model_index_3)
print(f"Single entity selected on second click: {verify_selected_index(['Entity3'])}")
# 5) Verify CTRL+click
# CTRL+click on Entity2 and verify if Entity2 and Entity3 are selected as Entity is already selected
pyside_utils.item_view_index_mouse_click(tree, model_index_2, modifier=QtCore.Qt.ControlModifier)
print(
f"CTRL+click worked for adding selected elements (2 elements): {verify_selected_index(['Entity2', 'Entity3'])}"
)
# CTRL+click on Entity4 and verify if Entity 2, 3, 4 are selected
pyside_utils.item_view_index_mouse_click(tree, model_index_4, modifier=QtCore.Qt.ControlModifier)
print(
f"CTRL+click worked for adding selected elements (3 elements): {verify_selected_index(['Entity2', 'Entity3', 'Entity4'])}"
)
# CTRL+click on already selected element (Entity2) to verify if it is removed from selected items
pyside_utils.item_view_index_mouse_click(tree, model_index_2, modifier=QtCore.Qt.ControlModifier)
print(
f"CTRL+click worked for removing already selected elements: {verify_selected_index(['Entity3', 'Entity4'])}"
)
test = EntitySelectionEntityOutlinerTest()
test.run()
@@ -0,0 +1,80 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C15167491: Export a level
https://testrail.agscollab.com/index.php?/cases/view/15167491
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import Tests.ly_shared.pyside_utils as pyside_utils
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestExportALevel(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="export_level: ", args=["level"])
def run_test(self):
"""
Summary:
Export a Level and verify if the level file is created.
Expected Behavior:
The level is exported and the message "Export to the game was successfully done." appears in the console.
The level.pak file is created after exporting.
Test Steps:
1) Create new level
2) Export level
Note:
- This test file must be called from the Lumberyard 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 on_action_triggered():
print("Game->Export to Engine Action triggered")
# 1) Create 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=True,
)
general.idle_wait(3.0)
# 2) Export level
editor_window = pyside_utils.get_editor_main_window()
action = pyside_utils.get_action_for_menu_path(editor_window, "Game", "Export to Engine")
action.triggered.connect(on_action_triggered)
action.trigger()
action.triggered.disconnect(on_action_triggered)
level_pak_file = os.path.join(
"SamplesProject", "Levels", self.args["level"], "level.pak"
)
general.idle_wait(3.0)
if os.path.exists(level_pak_file):
print("level.pak file exists")
test = TestExportALevel()
test.run()
@@ -0,0 +1,115 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C24064530 : Game Menu Options
https://testrail.agscollab.com/index.php?/cases/view/24064530
C16780793: Game Menu Options (New Viewport Interaction Model)
https://testrail.agscollab.com/index.php?/cases/view/16780793
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestGameMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="game_menu_options: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with Game Menu options and verify if all the options are working.
Expected Behavior:
The Game menu functions normally.
Test Steps:
1) Open level
2) Interact with Game Menu options
Note:
- This test file must be called from the Lumberyard 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
"""
game_menu_options = [
("Play Game",),
("Simulate",),
("Export to Engine",),
("Export Occlusion Mesh",),
("Enable Camera Terrain Collision",),
("Move Player and Camera Separately",),
("AI", "Request a full MNM rebuild",),
("AI", "Show Navigation Areas",),
("AI", "Continuous Update",),
("AI", "Visualize Navigation Accessibility",),
("AI", "View Agent Type",),
("Audio", "Stop All Sounds",),
("Audio", "Refresh Audio",),
("Debugging", "Configure ToolBox Macros",),
("Debugging", "ToolBox Macros",),
("Debugging", "Error Report",),
]
def on_focus_changed(old, new):
print("Focus Changed")
QtWidgets.QApplication.activeModalWidget().close()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
if action_name == "Simulate":
general.exit_simulation_mode()
elif action_name == "Play Game":
general.exit_game_mode()
# 1) Create 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=True,
)
general.idle_wait(3.0)
# 2) Interact with Game Menu options
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
for option in game_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "Game", *option)
if action.isVisible():
print(f"{option} is visible in Game Menu")
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
general.idle_wait(2.0)
else:
print(f"{option} is not visible in Game Menu")
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = TestGameMenuOptions()
test.run()
@@ -0,0 +1,238 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6321557: Basic Function: Graphics Settings
https://testrail.agscollab.com/index.php?/cases/view/6321557
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest, QtCore
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
from enum import Enum, auto
class FocusChangeState(Enum):
IGNORE = auto() # We're not waiting for anything
SETTINGS_WINDOW_APPEAR = auto() # Waiting for the settings window to appear
WARNING_DIALOG_APPEAR = auto() # Waiting for the warning about risks of changing settings
POST_WARNING_DIALOG = auto() # After that, it'll either be a log message or source control warning.
SAVE_WARNING_APPEAR = auto() # Waiting for the "Could not save" dialog if source control stopped us.
LOG_WINDOW_APPEAR = auto() # Waiting for the log window to appear
CLOSE_WITHOUT_SAVING = auto() # Waiting for the close without saving dialog.
class TestGraphicsSettings(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="graphics_settings: ", args=["level"])
def run_test(self):
"""
Summary:
Open the graphics settings, change a value and ensure the setting changes.
Expected Behavior:
Graphics settings can be changed.
Test Steps:
1) Open the graphics settings dialog.
2) Check the Save button is disabled.
3) Change a setting.
4) Check the save button is enabled.
5) Press the save button.
6) Check a warning dialog has appeared.
7) Press Yes.
8) Check the dialog has closed.
Note:
- This test file must be called from the Lumberyard 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 fail_test(msg):
print(msg)
print("Test failed.")
sys.exit()
current_state = FocusChangeState.IGNORE
settings_have_saved = False
last_window_seen = None
def handle_settings_dialog():
nonlocal current_state
current_state = FocusChangeState.IGNORE
general.idle_wait(0.0)
active_widget = QtWidgets.QApplication.activeModalWidget()
if active_widget is None:
fail_test("Failed to find graphics settings dialog.")
save_button = pyside_utils.find_child_by_pattern(active_widget, "Save")
if save_button is None:
fail_test("Failed to find Save button")
if type(save_button) != QtWidgets.QToolButton:
fail_test("Failed to find Save button 2")
if save_button.isEnabled():
fail_test("Save button is enabled before change.")
spin_box = pyside_utils.find_child_by_pattern(active_widget, {"objectName": "e_viewdistratiocustom"})
if spin_box is None:
fail_test("Failed to find spinbox.")
if type(spin_box) != QtWidgets.QDoubleSpinBox:
fail_test("Failed to find spinbox.")
current_value = spin_box.value()
spin_box.setValue(current_value * 2)
new_value = spin_box.value()
if current_value == new_value:
fail_test("Failed to change value.")
if not save_button.isEnabled():
fail_test("Save button is not enabled after change.")
current_state = FocusChangeState.WARNING_DIALOG_APPEAR
save_button.click()
if not settings_have_saved:
current_state = FocusChangeState.CLOSE_WITHOUT_SAVING
active_widget.close()
def handle_warning_dialog():
nonlocal current_state
current_state = FocusChangeState.IGNORE
active_widget = QtWidgets.QApplication.activeModalWidget()
if active_widget is None:
fail_test("Failed to find warning dialog.")
msg_box = active_widget.findChild(QtWidgets.QMessageBox)
if msg_box is None:
fail_test("Unable to find message box in warning dialog")
if not msg_box.text().startswith("A non-tested setting could potentially crash the game"):
fail_test("Unexpected text in warning dialog: " + msg_box.text())
yes_button = pyside_utils.find_child_by_pattern(active_widget, "&Yes")
if yes_button is None:
fail_test("Failed to find yes button.")
current_state = FocusChangeState.POST_WARNING_DIALOG
yes_button.click()
def press_button_in_dialog(button_text, next_state):
nonlocal current_state
current_state = FocusChangeState.IGNORE
active_widget = QtWidgets.QApplication.activeModalWidget()
button = pyside_utils.find_child_by_pattern(active_widget, button_text)
if button is None:
fail_test("Failed to find " + button_text + " button.")
current_state = next_state
button.click()
def check_for_save_success(log_dialog_widget):
nonlocal settings_have_saved
msg_box = log_dialog_widget.findChild(QtWidgets.QMessageBox)
if msg_box is None:
return
if msg_box.text().startswith("Updated the graphics setting correctly"):
settings_have_saved = True
def on_focus_changed(old, new):
nonlocal current_state, settings_have_saved, last_window_seen
if current_state == FocusChangeState.IGNORE:
return
active_widget = QtWidgets.QApplication.activeModalWidget()
if active_widget is None:
return
# Make sure the window has changed.
if active_widget == last_window_seen:
return
last_window_seen = active_widget
title = pyside_utils.find_child_by_pattern(active_widget, "title")
if title is None:
return
if current_state == FocusChangeState.SETTINGS_WINDOW_APPEAR:
# We're waiting for the settings dialog, ensure the right one has appeared.
if title.text() == "Graphics Settings":
handle_settings_dialog()
elif current_state == FocusChangeState.WARNING_DIALOG_APPEAR:
if title.text() == "Warning":
handle_warning_dialog()
elif current_state == FocusChangeState.POST_WARNING_DIALOG:
# This could be a source control dialog, or a log window if the settings file exists and is writable.
if title.text() == "Source Control":
# Just use overwrite for the project settings file.
press_button_in_dialog("Overwrite", FocusChangeState.POST_WARNING_DIALOG)
elif title.text() == "Log":
# After this, there will be another log window for the file that did save.
check_for_save_success(active_widget)
press_button_in_dialog("OK", FocusChangeState.LOG_WINDOW_APPEAR)
elif current_state == FocusChangeState.SAVE_WARNING_APPEAR:
if title.text() == "Warning":
press_button_in_dialog("OK", FocusChangeState.LOG_WINDOW_APPEAR)
elif current_state == FocusChangeState.LOG_WINDOW_APPEAR:
if title.text() == "Log":
check_for_save_success(active_widget)
press_button_in_dialog("OK", FocusChangeState.IGNORE)
elif current_state == FocusChangeState.CLOSE_WITHOUT_SAVING:
if title.text() == "Warning":
press_button_in_dialog("&Yes", FocusChangeState.IGNORE)
EditorTestHelper.after_level_load(self)
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
editor_window = pyside_utils.get_editor_main_window()
action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", "Editor Settings", "Graphics Settings")
if action is None:
fail_test("Failed to find Graphics Settings action.")
current_state = FocusChangeState.SETTINGS_WINDOW_APPEAR
action.trigger()
app.focusChanged.disconnect(on_focus_changed)
if settings_have_saved:
print("Graphics Settings test successful.")
else:
print("Graphics Settings test failed.")
test = TestGraphicsSettings()
test.run()
@@ -0,0 +1,127 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1564080: Help Menu Function
https://testrail.agscollab.com/index.php?/cases/view/1564080
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtCore, QtWidgets, QtGui
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestHelpMenuFunction(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="help_menu_function: ")
def run_test(self):
"""
Summary:
Interact with Help Menu options that open Urls and verify if Urls are valid.
Expected Behavior:
The Help menu Urls are valid.
Test Steps:
1) Interact with Help Menu options that open Urls
Note:
- This test file must be called from the Lumberyard 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
"""
url_help_menu_options = [
("Getting Started",),
("Tutorials",),
("Documentation", "Glossary",),
("Documentation", "Lumberyard Documentation",),
("Documentation", "GameLift Documentation",),
("Documentation", "Release Notes",),
("GameDev Resources", "GameDev Blog",),
("GameDev Resources", "GameDev Twitch Channel",),
("GameDev Resources", "Forums",),
("GameDev Resources", "AWS Support",),
("About Lumberyard",),
]
def validate_url_action(action_name, urlHandler):
if urlHandler.last_opened_url.isEmpty():
print(f"{action_name} didn't open a url with an expected scheme (http/https)")
else:
url_display_string = urlHandler.last_opened_url.toDisplayString()
if urlHandler.last_opened_url.isValid():
print(f"{action_name} triggered Url {url_display_string}")
else:
print(f"{action_name} triggered invalid Url {url_display_string}")
urlHandler.last_opened_url.clear()
def on_focus_changed(old, new):
active_widget = QtWidgets.QApplication.activeModalWidget()
if active_widget:
active_widget.close()
# 1) Interact with the Help Menu options that open urls
try:
# Create a handler to open Urls
class UrlHandler(QtCore.QObject):
self.last_opened_url = QtCore.QUrl()
@QtCore.Slot("QUrl")
def open_url(self, url):
self.last_opened_url = url
urlHandler = UrlHandler()
QtGui.QDesktopServices.setUrlHandler("https", urlHandler, "open_url")
QtGui.QDesktopServices.setUrlHandler("http", urlHandler, "open_url")
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
# Check help menu actions that trigger Urls
for option in url_help_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "Help", *option)
if action.isVisible():
action.trigger()
action_name = action.iconText()
validate_url_action(f"{action_name} Action", urlHandler)
else:
print(f"{option} is not visible in Help Menu")
# Check the documentation search field under the help menu
menu_bar = editor_window.menuBar()
menu_bar_actions = [index.iconText() for index in menu_bar.actions()]
main_menu_item = "Help"
if main_menu_item not in menu_bar_actions:
print(f"QAction not found for main menu item '{main_menu_item}'")
else:
help_action = menu_bar.actions()[menu_bar_actions.index(main_menu_item)]
# Get the first action in the help menu which should be the search action
search_action = help_action.menu().actions()[0]
# Get the search line edit from the search action
search_line_widget = search_action.defaultWidget().findChild(QtWidgets.QLineEdit)
search_string = "component"
search_line_widget.setText(search_string)
search_line_widget.returnPressed.emit()
validate_url_action(f"Documentation search with '{search_string}' text", urlHandler)
search_string = ""
search_line_widget.setText(search_string)
search_line_widget.returnPressed.emit()
validate_url_action(f"Documentation Search with '{search_string}' text", urlHandler)
finally:
app.focusChanged.disconnect(on_focus_changed)
QtGui.QDesktopServices.unsetUrlHandler("https")
QtGui.QDesktopServices.unsetUrlHandler("http")
test = TestHelpMenuFunction()
test.run()
@@ -0,0 +1,105 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C24064533: The Help menu functions normally.
https://testrail.agscollab.com/index.php?/cases/view/24064533
C16780815: Help Menu Options (New Viewport Interaction Model)
https://testrail.agscollab.com/index.php?/cases/view/16780815
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestHelpMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="help_menu_options: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with Help Menu options and verify if all the options are working.
Expected Behavior:
The Help menu functions normally.
Test Steps:
1) Open level
2) Interact with Help Menu options
Note:
- This test file must be called from the Lumberyard 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
"""
help_menu_options = [
("Getting Started",),
("Tutorials",),
("Documentation", "Glossary",),
("Documentation", "Lumberyard Documentation",),
("Documentation", "GameLift Documentation",),
("Documentation", "Release Notes",),
("GameDev Resources", "GameDev Blog",),
("GameDev Resources", "GameDev Twitch Channel",),
("GameDev Resources", "Forums",),
("GameDev Resources", "AWS Support",),
("Give Us Feedback",),
("About Lumberyard",),
]
def on_focus_changed(old, new):
QtWidgets.QApplication.activeModalWidget().close()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 1) Create 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=True,
)
general.idle_wait(3.0)
# 2) Interact with Help Menu options
try:
for option in help_menu_options:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
action = pyside_utils.get_action_for_menu_path(editor_window, "Help", *option)
if action.isVisible():
print(f"{option} is visible in Help Menu")
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
general.idle_wait(2.0)
else:
print(f"{option} is not visible in Help Menu")
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = TestHelpMenuOptions()
test.run()
@@ -0,0 +1,94 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C1564610: LMB and RMB mouse functionality
https://testrail.agscollab.com/index.php?/cases/view/1564610
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestLeftAndRightMouseButtons(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="left_and_right_mouse_buttons: ", args=["level"])
def run_test(self):
"""
Summary:
Use left mouse button to select items in Editor and
Right mouse button to open the item.
Expected Behavior:
LMB interaction is correct and accurate.
RMB functions normally and appropriate context menus are opened.
Test Steps:
1) Open a new level
2) Test LMB and RMB on Viewport of editor to open context menus and select items.
2.1) Create camera entity from view
2.2) Create entity
2.3) Create layer
Note:
- This test file must be called from the Lumberyard 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 get_entity_count_name(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
# 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=True,
)
# 2) Test RMB and LMB on Viewport of editor to open context menus and select items.
# Note: pyside_utils.trigger_context_menu_entry(widget, pattern, pos=None) does same
# functionality as Right click to open context menu and left click to select options in menu.
app = QtWidgets.QApplication.instance()
editor_window = pyside_utils.get_editor_main_window()
viewport = editor_window.findChildren(QtWidgets.QWidget, "renderOverlay")[0]
# 2.1) Create camera entity from view
pyside_utils.trigger_context_menu_entry(viewport, "Create camera entity from view")
if get_entity_count_name("Camera1"):
print("Create camera entity from view option is selected using mouse buttons")
# 2.2) Create entity
pyside_utils.trigger_context_menu_entry(viewport, "Create entity")
if get_entity_count_name("Entity3"):
print("Create entity option is selected using mouse buttons")
# 2.3) Create layer
pyside_utils.trigger_context_menu_entry(viewport, "Create layer")
if get_entity_count_name("Layer4*"):
print("Create layer option is selected using mouse buttons")
test = TestLeftAndRightMouseButtons()
test.run()
@@ -0,0 +1,120 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C5436953: Loading a level with un-pushed slice changes does not effect editor stability
https://testrail.agscollab.com/index.php?/cases/view/5436953
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.slice as slice
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class LoadLevelUnpushedSliceTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="load_level_unpushed_slice_changes", args=["level"])
def run_test(self):
"""
Summary:
Loading a level with un-pushed slice changes does not effect editor stability
Expected Behavior:
The editor remains stable, level loads, and the slice still has pushable changes.
Test Steps:
1) Open a new level
2) Create an entity and add a component to it
3) Create slice from the entity
4) Delete component from the entity
5) Save the level
6) Reload the same level
7) Verify changes after reload
8) Instantiate slice and verify if it has a component
Note:
- This test file must be called from the Lumberyard 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
"""
SLICE_NAME = "TestSlice.slice"
def search_entity(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return entities
# 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=True,
)
# 2) Create an entity and add a component to it
new_entity = hydra.Entity("entity")
new_entity.create_entity(math.Vector3(64.0, 64.0, 32.0), ["Mesh"])
# 3) Create slice from the entity
success = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", new_entity.id, SLICE_NAME)
print(f"Slice Created: {success}")
# 4) Delete component from the entity
success = editor.EditorComponentAPIBus(bus.Broadcast, "RemoveComponents", [new_entity.components[0]])
print(f"Component removed successfully: {success}")
# 5) Save the level
general.save_level()
# 6) Reload the same level
general.reload_current_level()
# 7) Verify changes after reload
# Entity should not have the component
entity_id = search_entity("entity")[0]
print(f"Entity found after level reload: {entity_id.IsValid()}")
print(
f"Level saved even if there are unpushed changes in slice: {not hydra.has_components(entity_id, ['Mesh'])}"
)
# 8) Instantiate slice and verify if it has a component
# Delete existing slice initially
general.delete_object("entity")
# Instantiate slice
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.SetPosition(position)
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", SLICE_NAME, math.Uuid(), False)
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
self.wait_for_condition(lambda: len(search_entity("entity")) > 0, 2.0)
entity_id = search_entity("entity")[0]
print(f"Unpushed changes in Slice are not saved: {hydra.has_components(entity_id, ['Mesh'])}")
test = LoadLevelUnpushedSliceTest()
test.run()
@@ -0,0 +1,108 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C1564082 : Object Toolbar
https://testrail.agscollab.com/index.php?/cases/view/1564082
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as editor_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestObjectToolbar(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="object_toolbar_function: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with Object toolbar options and verify if all the options are functionnal.
Expected Behavior:
All options available in the Object Toolbar function as intended.
Test Steps:
1) Create/Open a level
2) Open the Object Toolbar
3) Verify 'Go to selected object' tool button functionality
i) Create entity and find the entity in viewport
ii) Change viewport position
iii) Click on option and verify viewport position
Note:
- This test file must be called from the Lumberyard 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
"""
editor_window = pyside_utils.get_editor_main_window()
object_toolbar = editor_window.findChild(QtWidgets.QToolBar, "Object")
def get_tool_button(option):
button = pyside_utils.find_child_by_pattern(object_toolbar, option)
if button.text() == option:
return button
# 1) Create new level with an Entity
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(3.0)
# 2) Open the Object Toolbar
if not object_toolbar.isVisible():
object_toolbar.toggleViewAction().trigger()
if object_toolbar.isVisible():
print("Object tool bar opened successfully")
# 3) Verify 'Go to selected object' tool button functionality
# i)Create entity and find the entity in viewport
editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
# Find entity in viewport and get current position
entity_outliner = pyside_utils.find_child_by_pattern(editor_window, "Entity Outliner (PREVIEW)")
action = pyside_utils.find_child_by_pattern(entity_outliner, "Find in viewport")
default_pos = general.get_current_view_position()
action.trigger()
self.wait_for_condition(lambda: default_pos != general.get_current_view_position(), 2.0)
old_pos = general.get_current_view_position()
# ii) Change viewport position
general.set_current_view_position(old_pos.x + 10.0, old_pos.y + 10.0, old_pos.z + 10.0)
self.wait_for_condition(lambda: old_pos != general.get_current_view_position(), 2.0)
current_pos = general.get_current_view_position()
# iii) Click on option and verify viewport position
button = get_tool_button("Go to selected object")
button.click()
self.wait_for_condition(lambda: current_pos != general.get_current_view_position(), 2.0)
new_pos = general.get_current_view_position()
if new_pos != current_pos and new_pos == old_pos:
print("Go to selected object tool button is responsive")
test = TestObjectToolbar()
test.run_test()
+92
View File
@@ -0,0 +1,92 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6351276: Open a level
https://testrail.agscollab.com/index.php?/cases/view/6351276
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
import azlmbr.editor as editor
import azlmbr.bus as bus
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestOpenLevel(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="open_level", args=["level"])
def run_test(self):
"""
Summary:
Open a level and verify if the level is loaded properly.
Expected Behavior:
A level is opened and loaded into the editor.
Test Steps:
1) Open an existing level
2) Verify if the level is loaded
Note:
- This test file must be called from the Lumberyard 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
"""
self.focus_changed = False
def on_focus_changed(old, new):
if not self.focus_changed:
self.focus_changed = True
active_widget = QtWidgets.QApplication.activeModalWidget()
open_level_as_dialogue = active_widget.findChild(QtWidgets.QWidget, "LevelFileDialog")
if open_level_as_dialogue.windowTitle() == "Open a Level":
print("Open a Level dialog opened")
level_name = open_level_as_dialogue.findChild(QtWidgets.QLineEdit, "nameLineEdit")
level_name.setText(self.args["level"])
button_box = open_level_as_dialogue.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 1) Open an existing level
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Open Level")
action_triggered = lambda: on_action_triggered("Open Level")
action.triggered.connect(action_triggered)
action.trigger()
action.triggered.disconnect(action_triggered)
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
# 2) Verify if the new level is loaded
general.idle_wait(2.0)
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") == "Audio_Sample":
print("An existing level opened : SUCCESS")
else:
print("An existing level opened : FAILED")
test = TestOpenLevel()
test.run()
@@ -0,0 +1,168 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1506875: Open Input Bindings File
https://testrail.agscollab.com/index.php?/cases/view/1506875
C1506876: Save Input Bindings File
https://testrail.agscollab.com/index.php?/cases/view/1506876
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class OpenSaveInputBindingFileTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="open_save_input_binding_file", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Verify if we are able to open/save a .inputbindings file in Asset Editor
Expected Behavior:
The .inputbindings asset is loaded into the Asset Editor, replacing any currently open files.
The .inputbindings file is saved correctly.
All changes from step 1. are present in the .inputbindings file.
Test Steps:
1) Open a new level
2) Open Asset Editor
3) Access Asset Editor
4) Open .inputbindings file
5) Verify if the file has loaded by verifying the status bar in Asset Editor
6) Delete all input events initially
7) Add an event and save the file
8) Close Asset editor and reopen the inputbindings file
9) Verify if changes are saved.
10) Close Asset Editor
Note:
- This test file must be called from the Lumberyard 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
"""
INPUTBINDING_FILE_NAME = "test.inputbindings"
def open_asset_editor():
general.open_pane("Asset Editor")
return general.is_pane_visible("Asset Editor")
def close_asset_editor():
general.close_pane("Asset Editor")
return not general.is_pane_visible("Asset Editor")
async def open_input_binding_file():
# Trigger the open asynchronously, since it opens a modal dialog
action = pyside_utils.find_child_by_pattern(asset_editor_widget, {"iconText": "Open"})
pyside_utils.trigger_action_async(action)
# This is to deal with the file picker for .inputbinding
active_modal_widget = await pyside_utils.wait_for_modal_widget()
tree = active_modal_widget.findChild(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")
# Make sure the folder structure tree is expanded so that after giving the searchtext the first
# .inputbinding file is selected
tree.expandAll()
search_text = active_modal_widget.findChild(QtWidgets.QLineEdit, "textSearch")
# Set the searchText so that the first .inputbinding file in the folder structure is selected
search_text.setText(INPUTBINDING_FILE_NAME)
model_index = pyside_utils.find_child_by_pattern(tree, INPUTBINDING_FILE_NAME)
pyside_utils.item_view_index_mouse_click(tree, model_index)
# Click OK
button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox, "m_buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
# 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=True,
)
# 2) Open Asset Editor
print(f"Asset Editor opened: {open_asset_editor()}")
# 3) Access Asset Editor
editor_window = pyside_utils.get_editor_main_window()
asset_editor = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor")
asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "m_assetEditorWidget")
# 4) Open .inputbindings file
await open_input_binding_file()
# 5) Verify if the file has loaded by verifying the status bar in Asset Editor
status_bar = asset_editor_widget.findChild(QtWidgets.QWidget, "AssetEditorStatusBar")
text_edit = status_bar.findChild(QtWidgets.QLabel, "textEdit")
success = await pyside_utils.wait_for_condition(lambda: f"{INPUTBINDING_FILE_NAME} - Asset loaded!" in text_edit.text(), 2.0)
if success:
print("Input Binding File opened in the Asset Editor")
# C1506876
# 6) Delete all input events initially
await pyside_utils.wait_for_condition(lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups") is not None)
input_event_groups = asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups")
delete_all_button = input_event_groups.findChildren(QtWidgets.QToolButton, "")[1]
pyside_utils.click_button_async(delete_all_button)
# Clicking the Delete All button will prompt the user if they are sure they want to
# delete all the entries, so we wait for this modal dialog and then accept it
active_modal_widget = await pyside_utils.wait_for_modal_widget()
message_box = active_modal_widget.findChild(QtWidgets.QMessageBox)
yes_button = message_box.button(QtWidgets.QMessageBox.Yes)
yes_button.click()
# 7) Add an event and save the file
# First QToolButton is +, Second QToolButton is Delete
await pyside_utils.wait_for_condition(lambda: len(asset_editor_widget.findChildren(QtWidgets.QFrame, "Input Event Groups")) > 1, 2.0)
input_event_groups = asset_editor_widget.findChildren(QtWidgets.QFrame, "Input Event Groups")[1]
add_button = input_event_groups.findChild(QtWidgets.QToolButton, "")
add_button.click()
action = pyside_utils.find_child_by_pattern(asset_editor_widget, {"text": "&Save"})
action.trigger()
await pyside_utils.wait_for_condition(lambda: "Asset saved!" in text_edit.text(), 2.0)
# 8) Close Asset editor and reopen the inputbindings file
close_asset_editor()
open_asset_editor()
await open_input_binding_file()
# 9) Verify if changes are saved. (1 Event should be present)
# We need to re-find the Asset Editor and all its child widgets since we
# close/re-opened it, so it is brand new
asset_editor = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor")
asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "m_assetEditorWidget")
input_event_groups = asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups")
no_of_elements_label = input_event_groups.findChild(QtWidgets.QLabel, "DefaultLabel")
success = await pyside_utils.wait_for_condition(lambda: "1 elements" in no_of_elements_label.text(), 3.0)
if success:
print("Changes are saved successfully")
# 6) Close Asset Editor
print(f"Asset Editor closed: {close_asset_editor()}")
test = OpenSaveInputBindingFileTest()
test.run()
@@ -0,0 +1,129 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C17218816: Entity Outliner Searching
https://testrail.agscollab.com/index.php?/cases/view/17218816
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt, QObject, QEvent, QPoint
from PySide2.QtGui import QContextMenuEvent, QCursor
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestEntityOutlinerSearching(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="outliner_search: ", args=["level"])
def run_test(self):
"""
Summary:
Verify the search function of the Entity Outliner works properly
Expected Behavior:
Typing text on the Entity Outliner search field limits the number of entities shown in the tree view
Test Steps:
1) Create new level
2) Create some test entities
3) Write in the Entity Outliner search field (different combinations) and validate
Note:
- This test file must be called from the Lumberyard 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
"""
# 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,
)
general.idle_wait(3.0)
# Grab the Outliner
app = QtWidgets.QApplication.instance()
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
entity_outliner = main_window.findChild(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")
outliner_main_window = entity_outliner.findChild(QtWidgets.QMainWindow)
tree = outliner_main_window.findChild(QtWidgets.QTreeView, "m_objectTree")
search = outliner_main_window.findChild(QtWidgets.QLineEdit, "textSearch")
# Store number of starting entities, just in case
startingEntityCount = tree.model().rowCount()
# 2) Create entities
def CreateEntityWithName(name):
entityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
editor.EditorEntityAPIBus(bus.Event, 'SetName', entityId, name)
CreateEntityWithName("Test_01")
CreateEntityWithName("Test_02")
CreateEntityWithName("Test_03")
CreateEntityWithName("AnotherTest")
CreateEntityWithName("Entity01")
CreateEntityWithName("Entity02")
# Count rows in Outliner
fullEntityCount = tree.model().rowCount()
if((fullEntityCount - startingEntityCount) == 6):
print("Test Entities were set up correctly")
# 3) Write in the Entity Outliner search field (different combinations)
# Write "Test_0" in search box
search.setText("Test_0")
entityCount = tree.model().rowCount()
if(entityCount == 3):
print("Searching Test_0 returns 3 entities")
# Write "Test" in search box
search.setText("Test")
entityCount = tree.model().rowCount()
if(entityCount == 4):
print("Searching Test returns 4 entities")
# Write "asdfgh" in search box
search.setText("asdfgh")
entityCount = tree.model().rowCount()
if(entityCount == 0):
print("Searching asdfgh returns 0 entities")
# Write "" in search box
search.setText("")
entityCount = tree.model().rowCount()
if(entityCount == fullEntityCount):
print("Emptying the search returns all entities")
test = TestEntityOutlinerSearching()
test.run()
@@ -0,0 +1,127 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C2669525: Adding and deleting entities to a slice are able to be quick pushed to slice
https://testrail.agscollab.com/index.php?/cases/view/2669525
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.slice as slice
import azlmbr.entity as EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
from re import compile as regex
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class QuickPushSliceTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="quick_push_slice", args=["level"])
def run_test(self):
"""
Summary:
Adding and deleting entities to a slice are able to be quick pushed to slice
Expected Behavior:
The slice is saved and the second instance of the slice is updated with the
deletion of Child 1 and a addition of Child 2.
Test Steps:
1) Open a new level
2) Create 2 entities as parent and child
3) Create slice from the parent entity
4) Wait until slice is created
5) Instantiate a newly created slice
6) Delete child on original slice
7) Add a child to the original slice entity
8) Save slice overrides
9) Verify if the instantiated slice has a new child
Note:
- This test file must be called from the Lumberyard 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
"""
SLICE_NAME = "temp_slice.slice"
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
# 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=True,
)
# 2) Create 2 entities as parent and child
parent_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
child_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_id, "child1")
# 3) Create slice from the parent entity
success = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", parent_id, SLICE_NAME)
print(f"Slice Created: {success}")
# 4) Wait until slice is created
self.wait_for_condition(lambda: path_is_valid_asset(SLICE_NAME), 3.0)
# 5) Instantiate a newly created slice
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.SetPosition(position)
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", SLICE_NAME, math.Uuid(), False)
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
# 6) Delete child on original slice
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", [child_id])
# 7) Add a child to the original slice entity
child_2_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_2_id, "child2")
# 8) Save slice overrides
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Outliner.*")), ..., "m_objectList"
).parent()
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
# Model index for the original slice
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 2) is not None, 2.0)
model_index_1 = pyside_utils.get_item_view_index(tree, 1)
pyside_utils.trigger_context_menu_entry(tree, SLICE_NAME.lower(), index=model_index_1)
# 9) Verify if the instantiated slice has a new child
# model index for instantiated slice
model_index_0 = pyside_utils.get_item_view_index(tree, 0)
self.wait_for_condition(lambda: pyside_utils.find_child_by_pattern(model_index_0, "child1") is None, 2.0)
if pyside_utils.find_child_by_pattern(model_index_0, "child2"):
print("Newly created child is updated in the instantiated slice")
if pyside_utils.find_child_by_pattern(model_index_0, "child1") is None:
print("Child deletion is updated in the instantiated slice")
test = QuickPushSliceTest()
test.run()
@@ -0,0 +1,135 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C17123213: Quick Saving Changes to Slice
https://testrail.agscollab.com/index.php?/cases/view/17123213
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.asset as asset
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.slice as slice
from azlmbr.entity import EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
import Tests.ly_shared.hydra_editor_utils as hydra
from re import compile as regex
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class QuickSaveSlice(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="quick_save_slice", args=["level"])
def run_test(self):
"""
Summary:
Quick Saving Changes to Slice
Expected:
All changes made to the slice are saved
Test Steps:
1) Create or open a level
2) Create a slice
3) Create child entity
4) Add a component to the slice.
5) Quick save the slice
6) Delete the existing slice before verifying if it is saved
7) Reinstantiate the slice to verify if the changes are saved.
8) Verify the saved changes
Note:
- This test file must be called from the Lumberyard 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
"""
SLICE_NAME = "TempSlice.slice"
def search_entity(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return entities
# 1) Create or open any level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(3.0)
# 2) Create a slice
# Create an Entity to turn into a slice.
new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId())
editor.EditorEntityAPIBus(bus.Event, "SetName", new_entity_id, "Entity2")
success = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", new_entity_id, SLICE_NAME)
print(f"Slice Created: {success}")
# 3) Create child entity
child_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", new_entity_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_entity_id, "Entity3")
print(f"Child entity created: {child_entity_id.IsValid()}")
# 4) Add a component to the slice.
hydra.add_component("Mesh", new_entity_id)
# 5) Quick save the slice
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Outliner.*")), ..., "m_objectList"
).parent()
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
model_index = pyside_utils.find_child_by_pattern(tree, "Entity2")
pyside_utils.item_view_index_mouse_click(tree, model_index)
pyside_utils.trigger_context_menu_entry(tree, SLICE_NAME.lower())
# 6) Delete the existing slice before verifying if it is saved
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntities', [new_entity_id, child_entity_id])
# 7) Reinstantiate the slice to verify if the changes are saved.
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.SetPosition(position)
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", SLICE_NAME, math.Uuid(), False)
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
# 8) Verify the saved changes
tree.expandAll()
self.wait_for_condition(lambda: len(search_entity("Entity3")) > 0, 2.0)
# Parent and child entities instantiated
parent_id = search_entity("Entity2")[0]
child_id = search_entity("Entity3")[0]
# Parent child hierarchy
parent_child_valid = parent_id.Equal(editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", child_id))
# Component in parent entity
has_component = hydra.has_components(parent_id, ["Mesh"])
changes_saved = parent_id.IsValid() and child_id.IsValid() and parent_child_valid and has_component
print(f"Changes in the slice file are saved: {changes_saved}")
test = QuickSaveSlice()
test.run()
+239
View File
@@ -0,0 +1,239 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6130788: Recover a layer
https://testrail.agscollab.com/index.php?/cases/view/6130788
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.bus as bus
import azlmbr.layers as layers
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets, QtTest, QtCore
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestRecoverLayer(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="recover_layer: ", args=["level"])
def run_test(self):
"""
Summary:
Create two layers, delete them and ensure they can be recovered correctly.
Expected Behavior:
Layers can be recovered.
Test Steps:
1) Create two new new levels
2) Create two layers, one with "Save as binary" activated.
3) Save the level.
4) Delete the layers.
5) Save the level again.
6) Load the other level.
7) Reload the first level.
8) Reload the layers using the Asset Browser.
9) Ensure the layers have recovered.
9) Ensure the save options are correct.
10) Undo, check the layers are removed
11) Redo, check the layers are restored.
Note:
- This test file must be called from the Lumberyard 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 fail_test(msg):
print(msg)
print("Test failed.")
sys.exit()
def on_focus_changed_save_as(old, new):
active_widget = QtWidgets.QApplication.activeModalWidget()
save_level_as_dialogue = active_widget.findChild(QtWidgets.QWidget, "LevelFileDialog")
if save_level_as_dialogue.windowTitle() == "Save Level As ":
print("The 'Save Level As' dialog appeared")
level_name = save_level_as_dialogue.findChild(QtWidgets.QLineEdit, "nameLineEdit")
level_name.setText("test_level_2")
button_box = save_level_as_dialogue.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
general.idle_wait(3.0) # wait for new level to load
level_to_load = ""
def get_inspector_window():
nonlocal editor_window
inspector_dock = editor_window.findChild(QtWidgets.QWidget, "Entity Inspector")
entity_inspector = inspector_dock.findChild(QtWidgets.QMainWindow)
child_widget = entity_inspector.findChild(QtWidgets.QWidget)
if child_widget is None:
fail_test("Failed to find inspector window")
general.idle_wait(1.0)
comp_list_contents = (
child_widget.findChild(QtWidgets.QScrollArea, "m_componentList")
.findChild(QtWidgets.QWidget, "qt_scrollarea_viewport")
.findChild(QtWidgets.QWidget, "m_componentListContents")
)
if comp_list_contents is None or len(comp_list_contents.children()) < 1:
return None
return comp_list_contents.children()[1]
def get_binary_save_state_for_layer(layer_name):
layer_id = general.find_editor_entity(layer_name)
if not layer_id.isValid():
fail_test("Failed to find " + " after recovery")
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [layer_id])
layer_inspector = get_inspector_window()
if layer_inspector is None:
fail_test("Failed to find inspector window")
frame = layer_inspector.findChild(QtWidgets.QFrame, "Save as binary")
check_box = frame.findChild(QtWidgets.QCheckBox)
return check_box.isChecked()
def set_binary_save_state_for_layer(layer_name, state):
layer_id = general.find_editor_entity(layer_name)
if not layer_id.isValid():
fail_test("Failed to find " + " after recovery")
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [layer_id])
layer_inspector = get_inspector_window()
if layer_inspector is None:
fail_test("Failed to find inspector window")
frame = layer_inspector.findChild(QtWidgets.QFrame, "Save as binary")
check_box = frame.findChild(QtWidgets.QCheckBox)
check_box.setChecked(state)
EditorTestHelper.after_level_load(self)
editor_window = pyside_utils.get_editor_main_window()
# Create new level.
result = general.create_level_no_prompt("test_level_1", 1024, 1, 4096, True)
if result != 0:
fail_test("Failed to create level")
general.idle_wait(1.0)
# Create two layers
layer1_id = layers.EditorLayerComponent_CreateLayerEntityFromName('Layer1')
if layer1_id.isValid():
print("Layer1 created.")
else:
fail_test("Layer1 creation failed.")
layer2_id = layers.EditorLayerComponent_CreateLayerEntityFromName('Layer2')
if layer2_id.isValid():
print("Layer2 created.")
else:
fail_test("Layer2 creation failed.")
# Set the binary save option on layer 1.
set_binary_save_state_for_layer("Layer1", True)
# Save as a different level name
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed_save_as)
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save As")
action.trigger()
finally:
app.focusChanged.disconnect(on_focus_changed_save_as)
print("Saved level 2 with layers")
# Delete the layers.
general.idle_wait(1.0)
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntities', [layer1_id, layer2_id])
# Save level 2 again.
general.save_level()
print("Saved level 2 without layers")
# Reopen level 1.
general.open_level_no_prompt("test_level_1")
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") != "test_level_1":
fail_test("Failed to reload test_level_1")
print("Reopened level 1")
# Reopen level 2.
general.open_level_no_prompt("test_level_2")
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") != "test_level_2":
fail_test("Failed to reload test_level_2")
print("Reopened level 2")
# Recover the layers
game_folder = editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'GetGameFolder')
layer1_path = game_folder + "\\Levels\\test_level_2\\Layers\\Layer1.layer"
layer2_path = game_folder + "\\Levels\\test_level_2\\Layers\\Layer2.layer"
layers.EditorLayerComponent_RecoverLayer(layer1_path)
layers.EditorLayerComponent_RecoverLayer(layer2_path)
# Check the binary save states are correct.
layer1_binary = get_binary_save_state_for_layer("Layer1")
layer2_binary = get_binary_save_state_for_layer("Layer2")
if not layer1_binary:
fail_test("Layer1 save not set to binary.")
if layer2_binary:
fail_test("Layer2 save set to binary.")
# Undo layer recovery.
general.undo()
general.undo()
id1 = general.find_editor_entity("Layer1")
id2 = general.find_editor_entity("Layer2")
if id1.isValid() or id2.isValid():
fail_test("Failed to undo layer recovery.")
# Redo layer recovery.
general.redo()
general.redo()
id1 = general.find_editor_entity("Layer1")
id2 = general.find_editor_entity("Layer2")
if not id1.isValid() or not id2.isValid():
fail_test("Failed to redo layer recovery.")
print("Recover Layer test complete.")
test = TestRecoverLayer()
test.run()
@@ -0,0 +1,125 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6130896 : Recover a layer containing a deleted slice.
https://testrail.agscollab.com/index.php?/cases/view/6130896
"""
import os
import sys
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.layers as layers
import azlmbr.slice as slice
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestRecoverLayerDeletedSlice(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="recover_layer_deleted_slice: ", args=["level"])
def run_test(self):
"""
Summary:
Recover a layer containing a deleted slice.
Expected Behavior:
The layer is not recovered and a error is given:
This layer contains a reference to a slice that can't be loaded. This layer can't be recovered.
Test Steps:
1) Open level
2) Create a layer on the level as Layer1
3) Create an entity as Entity1
4) Make Entity1 a slice named Slice1
5) Make Slice1 a child of Layer1
6) Save the level
7) Delete Layer1 from the level
8) On disk find Slice1 and delete it
9) Find Layer1 and select the "Recover layer" option for Layer1 in the Asset Browser
Note:
- This test file must be called from the Lumberyard 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
"""
SLICE_NAME = "Slice1.slice"
def get_entity_count_name(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
# 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=True,
)
# 2) Create a layer on the level as Layer1
layer_id = layers.EditorLayerComponent_CreateLayerEntityFromName("Layer1")
if get_entity_count_name("Layer1*"):
print("Layer1 is created")
# 3) Create an entity as Entity1
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", entity.EntityId())
editor.EditorEntityAPIBus(bus.Event, "SetName", entity_id, "Entity1")
if get_entity_count_name("Entity1*"):
print("Entity1 is created")
# 4) Make Entity1 a slice named Slice1
success = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", entity_id, SLICE_NAME)
print(f"Slice Created: {success}")
# 5) Make Slice1 a child of Layer1
editor.EditorEntityAPIBus(bus.Event, "SetParent", entity_id, layer_id)
actual_parent_id = editor.EditorEntityInfoRequestBus(bus.Event, "GetParent", entity_id)
if actual_parent_id.ToString() == layer_id.ToString():
print("Parent of Entity1 is : Layer1")
# 6) Save the level
general.save_level()
# 7) Delete Layer1 from the level
general.idle_wait(1.0)
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntities ', [layer_id])
# 8) On disk find Slice1 and delete it
os.remove(os.path.join("SamplesProject", "Slice1.slice"))
# 9) Find Layer1 and select the "Recover layer" option for Layer1 in the Asset Browser
editor_window = pyside_utils.get_editor_main_window()
general.open_pane("Asset Browser")
asset_browser = editor_window.findChildren(QtWidgets.QDockWidget, "Asset Browser")[0]
search_bar = asset_browser.findChildren(QtWidgets.QLineEdit, "textSearch")[0]
search_bar.setText("Layer1.layer")
asset_browser_tree = asset_browser.findChildren(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")[0]
asset_browser_tree.expandAll()
model_index = pyside_utils.find_child_by_pattern(asset_browser_tree, "Layer1.layer")
asset_browser_tree.setCurrentIndex(model_index)
pyside_utils.trigger_context_menu_entry(asset_browser_tree, "Recover layer", index=model_index)
test = TestRecoverLayerDeletedSlice()
test.run()
@@ -0,0 +1,211 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C16929882: Required Components
https://testrail.agscollab.com/index.php?/cases/view/16929882
"""
import os
import sys
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
from azlmbr.entity import EntityId
import azlmbr.entity as entity
import Tests.ly_shared.hydra_editor_utils as hydra
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class RequiredComponentsTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="required_components: ", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Add required components to an entity.
Expected Behavior:
1) An entity inspector will show a warning if a required component is missing.
2) It will allow that component to be added.
3) The warning will disappear once the component is present..
Test Steps:
1) Open level.
2) Create entity.
3) Add a tube shape.
4) Check there is a required component warning and drop down in the inspector.
5) Click "Add Required Component".
6) Check that a list of valid components has appeared.
7) Click "Spline in the list.
8) Check that a spline component is added to the entity.
9) Check that the warning drop down is gone.
Note:
- This test file must be called from the Lumberyard 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 fail_test(msg):
print(msg)
print("Test failed.")
sys.exit()
def get_component_type_id(component_type_name):
# Get Component Types for required component.
type_ids_list = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType',
[component_type_name], entity.EntityType().Game)
if len(type_ids_list) == 0:
print("Type Ids List returned incorrectly.")
return False, 0
return True, type_ids_list[0]
def find_component_inspector(inspector_list, component_name):
for item in inspector_list:
title = item.findChild(QtWidgets.QLabel, "Title")
if title is None:
continue
print("Checking " + title.text())
if title.text() == component_name:
return item
return None
async def add_spine_to_tube_shape(button):
pyside_utils.click_button_async(button)
list_widget = await pyside_utils.wait_for_popup_widget()
tree = pyside_utils.find_child_by_pattern(list_widget, "Tree")
QtTest.QTest.keyClick(tree, Qt.Key_Down, Qt.NoModifier)
while tree.indexBelow(tree.currentIndex()) != QtCore.QModelIndex():
if tree.currentIndex().data(Qt.DisplayRole) == "Shape":
# Expand the Shape category
QtTest.QTest.keyClick(tree, Qt.Key_Right, Qt.NoModifier)
QtTest.QTest.keyClick(tree, Qt.Key_Right, Qt.NoModifier)
if tree.currentIndex().data(Qt.DisplayRole) == "Spline":
# Add this component type
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
general.idle_wait(0.0)
return
QtTest.QTest.keyClick(tree, Qt.Key_Down, Qt.NoModifier)
# Create level
create_level_result = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
if not create_level_result:
fail_test("New level failed.")
EditorTestHelper.after_level_load(self)
# Ensure the inspector window is open.
general.open_pane('Entity Inspector')
# Create an entity
new_entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId())
# Select the entity.
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [new_entity_id])
# Add a Tube Shape Component
editor_window = pyside_utils.get_editor_main_window()
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
tube_component = hydra.add_component("Tube Shape", new_entity_id)
general.idle_wait(0.0)
result, tube_component_id = get_component_type_id("Tube Shape")
if not result:
fail_test("Failed to find Tube Shape id.")
result, spline_component_id = get_component_type_id("Spline")
if not result:
fail_test("Failed to find Spline id.")
if not editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", new_entity_id, tube_component_id):
fail_test("Failed to add Tube Shape to entity.")
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', tube_component)
if is_enabled:
fail_test("Tube is enabled when first created.")
component_list = pyside_utils.find_child_by_hierarchy(entity_inspector, ..., {"objectName": "m_componentListContents"})
if component_list is None:
fail_test("Failed to find component list.")
# Find the add required component button. It's inside a lot of anonymous frames,
# so find the header_frame and work down from its parent.
tube_frame = find_component_inspector(component_list.children(), "Tube Shape")
if tube_frame is None:
fail_test("Failed to find tube shape inspector.")
header_frame = pyside_utils.find_child_by_hierarchy(tube_frame, ..., {"objectName": "HeaderFrame"})
if header_frame is None:
fail_test("Unable to find tube header frame")
container_frame = header_frame.parent()
if container_frame is None:
fail_test("Unable to find tube container frame")
add_button = container_frame.findChild(QtWidgets.QPushButton)
if add_button is None:
fail_test("Unable to find add required component button")
# Check there is no spline component yet
if editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", new_entity_id, spline_component_id):
fail_test("Spline component already exists.")
await add_spine_to_tube_shape(add_button)
if not editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", new_entity_id, spline_component_id):
fail_test("Failed to add Spline component to entity.")
is_enabled = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', tube_component)
if not is_enabled:
fail_test("Tube is not enabled.")
# Check that the missing component warning is gone.
editor.ToolsApplicationRequestBus(bus.Broadcast, 'SetSelectedEntities', [new_entity_id])
component_list = pyside_utils.find_child_by_hierarchy(entity_inspector, ...,
{"objectName": "m_componentListContents"})
if component_list is None:
fail_test("Failed to find component list.")
tube_frame = find_component_inspector(component_list.children(), "Tube Shape")
if tube_frame is None:
fail_test("Failed to find tube shape inspector.")
header_frame = pyside_utils.find_child_by_hierarchy(tube_frame, ..., {"objectName": "HeaderFrame"})
if header_frame is not None:
fail_test("Missing component warning still exists")
print("Required Component test successful.")
test = RequiredComponentsTest()
test.run()
@@ -0,0 +1,111 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C3712672: Save a Layer with 50,000 entities.
https://testrail.agscollab.com/index.php?/cases/view/3712672
"""
import os
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.layers as layers
import azlmbr.paths
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtTest, QtWidgets
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class SaveLayer50000EntitiesTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="save_layer_50000_entities", args=["level"])
def run_test(self):
"""
Summary:
Save a Layer with 50,000 entities and verify if the editor is stable.
Expected Behavior:
The level and layer are saved.
The Editor remains stable.
Test Steps:
1) Open a new level
2) Delete the DefaultLevelSetup initially to make the selection of entities easier
3) Create a new layer
4) Initially create 2000 entities using 'Duplicate' action
5) Duplicate the already created entities
Note:
- This test file must be called from the Lumberyard 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 search_entity(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
def layer_saved():
return os.path.exists(os.path.join(azlmbr.paths.devroot, "SamplesProject", "Levels", self.args["level"], "Layers", "Layer.layer"))
# 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=True,
)
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = editor_window.findChildren(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")[0].widget()
# 2) Delete the DefaultLevelSetup initially to make the selection of entities easier
default_entity_id = general.find_editor_entity("DefaultLevelSetup")
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", [default_entity_id])
# 3) Create a new layer
layer_id = layers.EditorLayerComponent_CreateLayerEntityFromName("Layer")
print(f"New layer created: {layer_id.isValid()}")
editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", layer_id)
general.select_object("Entity2")
action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", "Select All")
# 4) Initially create 2000 entities using 'Duplicate' action
for _ in range(2000):
QtTest.QTest.keyPress(outliner_widget, Qt.Key_D, Qt.ControlModifier)
# 5) Duplicate the already created entities
action.trigger()
for _ in range(25):
QtTest.QTest.keyPress(outliner_widget, Qt.Key_D, Qt.ControlModifier)
print(f"50000 entities created: {search_entity('Entity2')>=50000}")
# 6) Save/Verify if layer is saved
general.clear_selection()
general.select_object("Layer")
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save")
action.trigger()
self.wait_for_condition(layer_saved, 250.0)
print(f"Layer saved: {layer_saved()}")
test = SaveLayer50000EntitiesTest()
test.run()
+118
View File
@@ -0,0 +1,118 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C15167490: Save a level
https://testrail.agscollab.com/index.php?/cases/view/15167490
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestSaveALevel(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="save_level: ", args=["level"])
def run_test(self):
"""
Summary:
Create or open a level in editor. Create an entity and
Save level with new name using File > Save As..
Expected Behavior:
The "Save Level As" dialog appears.
A new level file is created and saved with the name provided.
The level is saved.
Test Steps:
1) Create or open a level in the editor.
2) Create an entity and Save Level with new name
3) Make any changes to the level and click on save
Note:
- This test file must be called from the Lumberyard 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 on_focus_changed(old, new):
active_widget = QtWidgets.QApplication.activeModalWidget()
save_level_as_dialogue = active_widget.findChild(QtWidgets.QWidget, "LevelFileDialog")
if save_level_as_dialogue.windowTitle() == "Save Level As ":
print("The 'Save Level As' dialog appeared")
level_name = save_level_as_dialogue.findChild(QtWidgets.QLineEdit, "nameLineEdit")
level_name.setText("tmp_new_level")
button_box = save_level_as_dialogue.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
general.idle_wait(3.0) #wait for new level lo load
def on_action_triggered(action):
print(f"{action} Action triggered")
# 1) Create or open a level in the editor.
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=True,
)
general.idle_wait(4.0)
# 2) Create an entity and Save Level with new name
newEntityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId.EntityId())
if newEntityId.IsValid():
print("Entity created")
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save As")
action.triggered.connect(on_action_triggered("Save As"))
action.trigger()
action.triggered.disconnect(on_action_triggered("Save As"))
finally:
app.focusChanged.disconnect(on_focus_changed)
# Verify if the new level is loaded
general.idle_wait(2.0)
if editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName") == "tmp_new_level":
print("A new level file is created and saved with the name provided.: SUCCESS")
else:
print("A new level file is created and saved with the name provided.: FAILED")
# 3) Make any changes to the level and click on save
new_entity = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', EntityId.EntityId())
if new_entity.IsValid():
print("Made a change in level")
editor_window = pyside_utils.get_editor_main_window()
action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save")
action.triggered.connect(on_action_triggered("Save"))
action.trigger()
action.triggered.disconnect(on_action_triggered("Save"))
test = TestSaveALevel()
test.run()
@@ -0,0 +1,131 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C2654251: Adding and deleting entities to a slice should be included on save overrides by default
https://testrail.agscollab.com/index.php?/cases/view/2654251
"""
import os
import sys
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.slice as slice
import azlmbr.entity as EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
from re import compile as regex
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class SaveOverridesDefaultTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="save_overrides_default", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Adding and deleting entities to a slice should be included on save overrides by default
Expected Behavior:
Observe that the Adding of Child 2 and the Deletion of Child 1 are both checked by default
Test Steps:
1) Open a new level
2) Create 2 entities as parent and child
3) Create slice from the parent entity
4) Wait until slice is created
5) Delete child on slice
6) Add a new child to the slice
7) Open Save Slice Overrides (Advanced) and verify if the changes are checked by default
Note:
- This test file must be called from the Lumberyard 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
"""
SLICE_NAME = "temp_slice.slice"
self.focus_changed_flag = False
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
def on_focus_changed(old, new):
active_modal_widget = QtWidgets.QApplication.activeModalWidget()
if active_modal_widget and not self.focus_changed_flag:
self.focus_changed_flag = True
# verify the changes child1 (deleted) and child2 (added) are checked by default
# if the Qt.CheckStateRole of the checkbox is 2, it means it is checked
child1_deleted = pyside_utils.find_child_by_pattern(active_modal_widget, "child1 (deleted)").data(
Qt.CheckStateRole
)
print(f"Child 1 deleted change checked by default: {child1_deleted == 2}")
child2_added = pyside_utils.find_child_by_pattern(active_modal_widget, "child2 (added)").data(
Qt.CheckStateRole
)
print(f"Child 2 added change checked by default: {child2_added == 2}")
active_modal_widget.close()
# 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=True,
)
# 2) Create 2 entities as parent and child
parent_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId.EntityId())
child_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_id, "child1")
# 3) Create slice from the parent entity
success = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", parent_id, SLICE_NAME)
print(f"Slice Created: {success}")
# 4) Wait until slice is created
self.wait_for_condition(lambda: path_is_valid_asset(SLICE_NAME), 3.0)
# 5) Delete child on slice
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", [child_id])
# 6) Add a new child to the slice
child_2_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", parent_id)
editor.EditorEntityAPIBus(bus.Event, "SetName", child_2_id, "child2")
# 7) Open Save Slice Overrides (Advanced) and verify if the changes are checked by default
editor_window = pyside_utils.get_editor_main_window()
outliner_widget = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Outliner.*")), ..., "m_objectList"
).parent()
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 1) is not None)
model_index = pyside_utils.get_item_view_index(tree, 0)
app = QtWidgets.QApplication.instance()
try:
app.focusChanged.connect(on_focus_changed)
await pyside_utils.trigger_context_menu_entry(tree, "Save Slice Overrides (Advanced)...", index=model_index)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = SaveOverridesDefaultTest()
test.run()
@@ -0,0 +1,161 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1508364 : Select over 1,000+ entities that are open in the Entity Outliner at one time
https://testrail.agscollab.com/index.php?/cases/view/1508364
"""
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import azlmbr.editor as editor
from azlmbr.entity import EntityId
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtCore, QtTest, QtWidgets
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
from re import compile as regex
class TestSelectMultipleEntitiesEntityOutliner(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="select_multiple_entities_entityoutliner", args=["level"])
def run_test(self):
"""
Summary:
Select over 1,000+ entities that are open in the Entity Outliner at one time
Expected Behavior:
The engine remains stable while selecting a large number of entities.
Selected entities are highlighted in the Entity Outliner.
The Entity Inspector shows an accurate count of selected entities.
The engine remains stable while selecting entities in the Outliner.
Selected entities are highlighted in the outliner.
The Entity Inspector shows an accurate count of selected entities.
Test Steps:
1) Open a new level
2) Delete the DefaultLevelSetup initially to make the selection of entities easier
3) Create 1000 entities using 'Duplicate' action
4) Verify CTRL+click to select individual entities
5) Select all entities and make sure selected entities are highlighted in the Entity Outliner
6) Verify the count of selected entities in Entity Inspector
Note:
- This test file must be called from the Lumberyard 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 get_entity_count_name(entity_name):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return len(entities)
def verify_selected_index(expected_entity_list):
# NOTE: selectedIndexes() seems to return 2 extra elements for each item seleted whose data() is None
# So removing the items whose data is None and considering only the selected items having data.
selected_indexes = [i for i in tree.selectedIndexes() if i.data()]
return sorted(selected_indexes) == sorted(expected_entity_list)
# 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=True,
)
# Get the editor window object
editor_window = pyside_utils.get_editor_main_window()
# Get the outliner widget object based on the window title
outliner_widget = editor_window.findChildren(QtWidgets.QDockWidget, "Entity Outliner (PREVIEW)")[0].widget()
# Get the entity inspector object based on the window title
entity_inspector_name_editor = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Inspector.*")), ..., "m_entityNameEditor"
)
# Get the object tree in the entity outliner
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
# 2) Delete the DefaultLevelSetup initially to make the selection of entities easier
default_entity_id = general.find_editor_entity("DefaultLevelSetup")
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", [default_entity_id])
# 3) Create 1000 entities using 'Duplicate' action
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, "CreateNewEntity", EntityId())
entity_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
editor.ToolsApplicationRequestBus(bus.Broadcast, "SetSelectedEntities", [entity_id])
for _ in range(999):
QtTest.QTest.keyPress(outliner_widget, Qt.Key_D, Qt.ControlModifier)
if get_entity_count_name(entity_name) == 1000:
print("1000 entities created")
editor.ToolsApplicationRequestBus(bus.Broadcast, "SetSelectedEntities", [])
# 4) Verify CTRL+click to select individual entities
# CTRL+click on First Entity and verify if First Entity is selected
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 1) is not None, 2.0)
model_index_1 = pyside_utils.get_item_view_index(tree, 0)
pyside_utils.item_view_index_mouse_click(tree, model_index_1, modifier=QtCore.Qt.ControlModifier)
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "Entity1", 1.0)
print(
f"The Entity Inspector shows an accurate count of selected entities (1 element): {entity_inspector_name_editor.text() == 'Entity1'}"
)
print(f"CTRL+click worked for adding selected elements (1 element): {verify_selected_index([model_index_1])}")
# CTRL+click on Second Entity and verify if First Entity and Second Entity are selected
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 2) is not None, 2.0)
model_index_2 = pyside_utils.get_item_view_index(tree, 1)
pyside_utils.item_view_index_mouse_click(tree, model_index_2, modifier=QtCore.Qt.ControlModifier)
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "2 entities selected", 1.0)
print(
f"The Entity Inspector shows an accurate count of selected entities (2 elements): {entity_inspector_name_editor.text() == '2 entities selected'}"
)
print(
f"CTRL+click worked for adding selected elements (2 elements): {verify_selected_index([model_index_1, model_index_2])}"
)
# CTRL+click on Third Entity and verify if First Entity, Second Entity and Third Entity are selected
self.wait_for_condition(lambda: pyside_utils.get_item_view_index(tree, 3) is not None, 2.0)
model_index_3 = pyside_utils.get_item_view_index(tree, 2)
pyside_utils.item_view_index_mouse_click(tree, model_index_3, modifier=QtCore.Qt.ControlModifier)
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "3 entities selected", 1.0)
print(
f"The Entity Inspector shows an accurate count of selected entities (3 elements): {entity_inspector_name_editor.text() == '3 entities selected'}"
)
print(
f"CTRL+click worked for adding selected elements (3 elements): {verify_selected_index([model_index_1, model_index_2, model_index_3])}"
)
# 5) Select all entities and make sure selected entities are highlighted in the Entity Outliner
action = pyside_utils.get_action_for_menu_path(editor_window, "Edit", "Select All")
action.trigger()
if len(editor.ToolsApplicationRequestBus(bus.Broadcast, "GetSelectedEntities")) == 1000:
print("Selected entities are highlighted in the Entity Outliner")
# 6) Verify the count of selected entities in Entity Inspector
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "1000 entities selected", 1.0)
print(
f"The Entity Inspector shows an accurate count of selected entities (1000 elements): {entity_inspector_name_editor.text() == '1000 entities selected'}"
)
test = TestSelectMultipleEntitiesEntityOutliner()
test.run()
@@ -0,0 +1,147 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
Test the Hydra API to access Editor Settings
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestSettingsAPI(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="settings_get_set: ", args=["level"])
def run_test(self):
"""
Summary:
Tests the Hydra API for Settings.
Expected Behavior:
Settings can be listed, accessed and set.
Test Steps:
1) Build the list of all settings
2) For each supported type (bool, int, float, string):
2a) Get the current values
2b) Set an altered values
2c) Get the value again
2d) Verify the value has changed
2e) Restore original value
Note:
- This test file must be called from the Lumberyard 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
"""
# Get full list of Editor settings
paths = editor.EditorSettingsAPIBus(bus.Broadcast, 'BuildSettingsList')
if(len(paths) > 0):
print("BuildSettingsList returned a non-empty list")
# Verify a boolean settings
def ParseBoolValue(value):
if(value == "0"):
return False
return True
boolOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|LoadLastLevelAtStartup')
if(boolOutcome.isSuccess()):
startupValue = boolOutcome.GetValue()
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|LoadLastLevelAtStartup', not(ParseBoolValue(startupValue)))
boolOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|LoadLastLevelAtStartup')
if(boolOutcome.isSuccess()):
newStartupValue = boolOutcome.GetValue()
if not(ParseBoolValue(startupValue) == ParseBoolValue(newStartupValue)):
print("Boolean Setting editing works")
# Restore previous value
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|LoadLastLevelAtStartup', ParseBoolValue(startupValue))
# Verify an int settings
intOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|UndoLevels')
if(intOutcome.isSuccess()):
undoLevel = intOutcome.GetValue()
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|UndoLevels', (int(undoLevel) + 10))
intOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|UndoLevels')
if(intOutcome.isSuccess()):
newUndoLevel = intOutcome.GetValue()
if not(int(undoLevel) == int(newUndoLevel)):
print("Int Setting editing works")
# Restore previous value
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|UndoLevels', int(undoLevel))
# Verify a float settings
floatOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|DeepSelectionNearness')
if(floatOutcome.isSuccess()):
deepSelection = floatOutcome.GetValue()
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|DeepSelectionNearness', (float(deepSelection) + 0.5))
floatOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|DeepSelectionNearness')
if(floatOutcome.isSuccess()):
newDeepSelection = floatOutcome.GetValue()
if not(float(deepSelection) == float(newDeepSelection)):
print("Float Setting editing works")
# Restore previous value
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|DeepSelectionNearness', float(deepSelection))
# Verify a string settings
stringOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|TemporaryDirectory')
if(stringOutcome.isSuccess()):
tempDir = stringOutcome.GetValue()
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|TemporaryDirectory', "SomeTempDirectory")
stringOutcome = editor.EditorSettingsAPIBus(bus.Broadcast, 'GetValue', 'Settings|TemporaryDirectory')
if(stringOutcome.isSuccess()):
newTempDir = stringOutcome.GetValue()
if not(tempDir == newTempDir):
print("String Setting editing works")
# Restore previous value
editor.EditorSettingsAPIBus(bus.Broadcast, 'SetValue', 'Settings|TemporaryDirectory', tempDir)
test = TestSettingsAPI()
test.run()
@@ -0,0 +1,188 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6312589: Tool Stability & Workflow: Asset Editor
https://testrail.agscollab.com/index.php?/cases/view/6312589
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.math as math
import Tests.ly_shared.hydra_editor_utils as hydra
from PySide2 import QtWidgets
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ToolStabilityAssetEditorTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tool_stability_asset_editor", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Tool Stability & Workflow: Asset Editor
Expected Behavior:
1) The Asset Editor can be opened/closed.
2) New Input Bindings can be created.
3) Created input bindings populate the Pick Input Bindings list.
4) Input Bindings can be opened.
Test Steps:
1) Open level
2) Open Asset Editor
3) Create new Input Bindings
4) Open Save As dialog
5) Close the Asset Editor
6) Create new entity and add a Input component
7) Assign a .inputbindings file to the input component
8) Open Asset Editor through the newly assigned inputbinding file
Note:
- This test file must be called from the Lumberyard 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 open_asset_editor():
if general.is_pane_visible("Asset Editor"):
return True
general.open_pane("Asset Editor")
return general.is_pane_visible("Asset Editor")
def close_asset_editor():
asset_editor.close()
return not general.is_pane_visible("Asset Editor")
class InputBinding:
input_binding_name = None
# 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=True,
)
editor_window = pyside_utils.get_editor_main_window()
# 2) Open Asset Editor
print(f"Asset Editor opened: {open_asset_editor()}")
# 3) Create new Input Bindings
# Access Asset editor
asset_editor = editor_window.findChild(QtWidgets.QDockWidget, "Asset Editor")
asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "m_assetEditorWidget")
menu_bar = asset_editor_widget.findChild(QtWidgets.QMenuBar)
# Get the action File->New->Input Bindings
action = pyside_utils.find_child_by_pattern(menu_bar, "Input Bindings")
# Trigger the action
action.trigger()
# wait until the trigger loads the frame
await pyside_utils.wait_for_condition(lambda: len(asset_editor_widget.findChildren(QtWidgets.QFrame, "Input Event Groups")) == 1, 3.0)
input_event_groups = asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups")
# Click on + button to assign Input event groups
input_event_groups.findChildren(QtWidgets.QToolButton)[1].click()
# wait until the click action loads the frame
await pyside_utils.wait_for_condition(lambda: len(asset_editor_widget.findChildren(QtWidgets.QFrame, "Event Name")) == 1, 3.0)
new_event_frame = asset_editor_widget.findChild(QtWidgets.QFrame, "<Unspecified Event>")
expander = new_event_frame.findChild(QtWidgets.QCheckBox, "")
expander.click()
event_name_frame = asset_editor_widget.findChild(QtWidgets.QFrame, "Event Name")
event_name = event_name_frame.findChildren(QtWidgets.QLineEdit)[0]
# Set some event name
event_name.setText("tmp_input_binding")
# 4) Open Save As dialog
action = pyside_utils.find_child_by_pattern(menu_bar, {"iconText": "Save As"})
# We use trigger_action_async here since it will open a modal dialog (save as)
pyside_utils.trigger_action_async(action)
# This checks if the Save As dialog is opened
# NOTE: At the moment since we are not able to give a name and save something using QFileDialog
# widget and for this particular case it is not so important to actually save the file, we are
# just checking if the Save As dialog is opened and then closing it immediately
active_modal_widget = await pyside_utils.wait_for_modal_widget()
if active_modal_widget and "Save As" in active_modal_widget.windowTitle():
print("Save as dialog opened")
active_modal_widget.close()
# 5) Close the Asset Editor
print(f"Asset Editor closed: {close_asset_editor()}")
# 6) Create new entity and add a Input component
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
hydra.add_component("Input", entity_id)
entity_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
# 7) Assign a .inputbindings file to the input component
general.clear_selection()
general.select_object(entity_name)
general.open_pane("Entity Inspector")
entity_inspector = editor_window.findChild(QtWidgets.QDockWidget, "Entity Inspector")
await pyside_utils.wait_for_condition(lambda: len(entity_inspector.findChildren(QtWidgets.QPushButton, "attached-button")) == 1, 2.0)
# Assess the file picker button of the "Input" component
button = entity_inspector.findChild(QtWidgets.QPushButton, "attached-button")
# Click on the file picker button and select a .inputbinding file
# We use click_button_async here since it will open a modal dialog
pyside_utils.click_button_async(button)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
# This is to deal with the file picker for .inputbinding
if active_modal_widget:
tree = active_modal_widget.findChild(QtWidgets.QTreeView, "m_assetBrowserTreeViewWidget")
# Make sure the folder structure tree is expanded so that after giving the searchtext the first
# .inputbinding file is selected
tree.expandAll()
search_text = active_modal_widget.findChild(QtWidgets.QLineEdit, "textSearch")
# Set the searchText so that the first .inputbinding file in the folder structure is selected
search_text.setText(".inputbindings")
# Save the file name for later to check when we open the Asset Editor
InputBinding.input_binding_name = tree.currentIndex().data(Qt.DisplayRole)
# Ensure the currently selected item actually is a .inputbinding file
await pyside_utils.wait_for_condition(lambda: ".inputbinding" in str(tree.currentIndex().data(Qt.DisplayRole)))
if ".inputbinding" in str(tree.currentIndex().data(Qt.DisplayRole)):
print(".inputbinding file is selected in the file picker")
# Click OK
button_box = active_modal_widget.findChild(QtWidgets.QDialogButtonBox, "m_buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
# 8) Open Asset Editor through the newly assigned inputbinding file
# Access the popup button to open the Asset Editor
tool_button_parent = entity_inspector.findChild(QtWidgets.QFrame, "browse-edit").parent()
tool_button = tool_button_parent.findChild(QtWidgets.QToolButton)
initial_active_window = QtWidgets.QApplication.activeWindow()
# Click on the "Open in Input Bindings Editor" button to open the selected .inputbindings file in the
# Asset Editor
tool_button.click()
await pyside_utils.wait_for_condition(lambda: initial_active_window != QtWidgets.QApplication.activeWindow(), 3.0)
current_active_window = QtWidgets.QApplication.activeWindow()
# Verify if the newly opened window is actually the Asset Editor to edit the selected inputbinding
if current_active_window.findChildren(QtWidgets.QDockWidget, InputBinding.input_binding_name):
print("Asset Editor for the assigned inputbinding file is opened")
test = ToolStabilityAssetEditorTest()
test.run()
@@ -0,0 +1,94 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6308169: Tool Stability & Workflow: Console
https://testrail.agscollab.com/index.php?/cases/view/6308169
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from PySide2.QtTest import QTest
from PySide2.QtCore import Qt
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ToolStabilityConsoleTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tool_stability_console: ", args=["level"])
def run_test(self):
"""
Summary:
Tool Stability & Workflow: Console
Expected Behavior:
1) Console can be opened
2) Commands can be entered in the console
3) Console can be closed
Test Steps:
1) Open level
2) Open console
3) Close the console
4) Reopen console
Note:
- This test file must be called from the Lumberyard 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 open_console():
general.open_pane("Console")
return general.is_pane_visible("Console")
# 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=True,
)
general.idle_wait(3.0)
# 2) Open console
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
if main_window.findChild(QtWidgets.QDockWidget, "Console") is None:
print(f"Console opened successfully: {open_console()}")
console = main_window.findChild(QtWidgets.QDockWidget, "Console")
console_widget = console.findChild(QtWidgets.QWidget, "Console")
container = console_widget.findChild(QtWidgets.QWidget, "container2")
line_edit = container.findChild(QtWidgets.QLineEdit, "lineEdit")
line_edit.setText("r_GetScreenShot=2")
QTest.keyClick(line_edit, Qt.Key_Enter, Qt.ControlModifier)
general.idle_wait(1.0)
print("Ran the command in console")
# 3) Close the console
general.close_pane("Console")
print(f"Console window closed: {not general.is_pane_visible('Console')}")
# 4) Reopen console
print(f"Console reopened successfully: {open_console()}")
test = ToolStabilityConsoleTest()
test.run()
@@ -0,0 +1,143 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C6308162: Tool Stability: Entity Inspector
https://testrail.agscollab.com/index.php?/cases/view/6308162
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as EntityId
import azlmbr.math as math
import azlmbr.asset as asset
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2.QtCore import Qt
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestToolStabilityEntityInspector(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tool_stability_entity_inspector: ", args=["level"])
def run_test(self):
"""
Summary:
Tool Stability: Entity Inspector
Test Steps:
1) Open level
2) Create an Entity with Mesh component
3) View the entity in the Viewport and Take screenshot
4) Click on the filter icon in Entity Outliner and check the entities filtered by "Mesh" in Entity Outliner
Note:
- This test file must be called from the Lumberyard 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 on_focus_changed(old, new):
print("focus changed")
popup = QtWidgets.QApplication.activePopupWidget()
if popup:
while tree.indexBelow(tree.currentIndex()) != QtCore.QModelIndex():
QtTest.QTest.keyClick(tree, Qt.Key_Down, Qt.NoModifier)
if tree.currentIndex().data(Qt.DisplayRole) == "Mesh":
break
tree.model().setData(tree.currentIndex(), 2, Qt.CheckStateRole)
outliner_tree = outliner_main_window.findChildren(QtWidgets.QTreeView, "m_objectTree")[0]
# make sure only one entity with mesh component is filtered
QtTest.QTest.keyClick(outliner_tree, Qt.Key_Down, Qt.NoModifier)
if tree.currentIndex().data(Qt.DisplayRole) == "Mesh" and outliner_tree.indexBelow(outliner_tree.currentIndex()) == QtCore.QModelIndex():
print("Entity with Mesh component is filtered by Entity outliner")
# 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=True,
)
general.idle_wait(3.0)
# 2) Create an Entity with Mesh component
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
general.clear_selection()
entity_name = editor.EditorEntityInfoRequestBus(bus.Event, "GetName", entity_id)
general.select_object(entity_name)
Entity = hydra.Entity("Entity", entity_id)
Entity.components = []
Entity.components.append(hydra.add_component("Mesh", entity_id))
def get_asset_by_path(path):
return asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", path, math.Uuid(), False)
# Add any valid mesh asset
hydra.get_set_test(
Entity,
0,
"MeshComponentRenderNode|Mesh asset",
get_asset_by_path(os.path.join("Objects", "SamplesAssets", "mover_display_smooth.cgf")),
)
# 3) View the entity in the Viewport and Take screenshot
self.take_viewport_screenshot(125.00, 129.01, 34.90, 0.00, 0.00, 0.00)
screenshot_path = os.path.join("Cache", "SamplesProject", "pc", "user", "screenshots", "screenshot0000.jpg")
if os.path.isfile(screenshot_path):
print("Screenshot Taken")
# 4) Click on the filter icon in Entity Outliner and check the entities filtered by "Mesh" in Entity Outliner
general.idle_wait(2.0)
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
entity_outliner = pyside_utils.find_child_by_property(
main_window, QtWidgets.QDockWidget, "objectName", "Entity Outliner (PREVIEW)"
)
outliner_main_window = entity_outliner.findChild(QtWidgets.QMainWindow)
tool_button = (
outliner_main_window.findChild(QtWidgets.QWidget)
.findChild(QtWidgets.QWidget, "OutlinerWidgetUI")
.findChild(QtWidgets.QFrame, "m_searchWidget")
.findChild(QtWidgets.QFrame, "textSearchContainer")
.findChild(QtWidgets.QToolButton, "assetTypeSelector")
)
try:
menu = tool_button.findChild(QtWidgets.QMenu)
tree = menu.findChild(QtWidgets.QTreeView)
app = QtWidgets.QApplication.instance()
line_edit = menu.findChild(QtWidgets.QLineEdit, "filteredSearchWidget")
line_edit.setFocus()
line_edit.setText("Mesh")
app.focusChanged.connect(on_focus_changed)
tree.setFocus()
tool_button.click()
general.idle_wait(1.0)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = TestToolStabilityEntityInspector()
test.run_test()
@@ -0,0 +1,172 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6308161: Tool Stability: Entity Outliner
https://testrail.agscollab.com/index.php?/cases/view/6308161
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.entity as entity
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.slice as slice
import azlmbr.asset as asset
import azlmbr.math as math
import Tests.ly_shared.pyside_utils as pyside_utils
from re import compile as regex
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ToolStabilityEntityOutlinerTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tool_stability_entity_outliner", args=["level"])
def run_test(self):
"""
Summary:
Tool Stability: Entity Outliner - This can be verified by creating entity, layer, slice and
instantiating a slice.
Expected Behavior:
1) Entities can be created in the Entity Outliner.
2) Created entities populate in other tools properly.
3) Entity creation can be undone and related tools are updated.
4) Entity creation can be redone and related tools are updated.
5) Layers can be created in the Entity Outliner.
5) Created layers populate in the Entity Inspector properly.
6) Slices can be created inside the Entity Outliner.
7) Slices can be instantiated inside the Entity Outliner.
Test Steps:
1) Open level
2) Create Entity and verify if it is created
3) Undo entity creation and verify if entity is deleted
4) Redo the last operation
5) Create layer and verify if it is created
6) Create slice using the first entity
7) Instantiate an existing slice
Note:
- This test file must be called from the Lumberyard 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 get_entities_by_name(entity_name="Entity2"):
searchFilter = entity.SearchFilter()
searchFilter.names = [entity_name]
entities = entity.SearchBus(bus.Broadcast, "SearchEntities", searchFilter)
return entities
# 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=True,
)
general.idle_wait(3.0)
# Get the editor window object
editor_window = pyside_utils.get_editor_main_window()
# Get the outliner widget object based on the window title
outliner_widget = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Outliner.*")), ..., "m_objectList"
).parent()
# Get the object tree in the entity outliner
tree = pyside_utils.find_child_by_hierarchy(outliner_widget, ..., "m_objectTree")
# Get the entity inspector object based on the window title
entity_inspector_name_editor = pyside_utils.find_child_by_hierarchy(
editor_window, ..., dict(windowTitle=regex("Entity Inspector.*")), ..., "m_entityNameEditor"
)
# 2) Create Entity and verify if it is created
general.clear_selection()
# find Create Entity action and trigger it
action = pyside_utils.find_child_by_pattern(outliner_widget, "Create Entity")
action.trigger()
# ensure entity is created
entities = get_entities_by_name()
print(f"New entity created: {len(entities) == 1}")
# get the newly create entity id and check if it is valid
entity_id = entities[0]
print(f"Entity Id is valid: {entity_id.isValid()}")
# check in entity inspector that the newly created entity is present by verifying
# the name populated in the name editor in entity inspector
general.select_object("Entity2")
general.idle_wait(1.0)
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "Entity2", 1.0)
print(f"Entity name populated in Entity Inspector: {entity_inspector_name_editor.text() == 'Entity2'}")
# 3) Undo entity creation and verify if entity is deleted
general.undo()
self.wait_for_condition(lambda: len(get_entities_by_name()) == 0, 3.0)
print(f"Entity creation undone: {len(get_entities_by_name())==0}")
# 4) Redo the last operation
general.idle_wait(1.0)
general.redo()
self.wait_for_condition(lambda: len(get_entities_by_name()) == 1, 3.5)
print(f"Entity appeared after redo: {len(get_entities_by_name())==1}")
# 5) Create layer and verify if it is created
general.idle_wait(2.0)
pyside_utils.trigger_context_menu_entry(tree, "Create layer")
# ensure entity is created
self.wait_for_condition(lambda: len(get_entities_by_name("Layer3*")) == 1, 1.0)
print(f"New layer created: {len(get_entities_by_name('Layer3*'))==1}")
# check in entity inspector that the newly created entity is present by verifying
# the name populated in the name editor in entity inspector
general.clear_selection()
general.select_object("Layer3*")
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "Layer3", 1.0)
print(f"Layer name populated in Entity Inspector: {entity_inspector_name_editor.text() == 'Layer3'}")
# 6) Create slice using the first entity
current_level_name = editor.EditorToolsApplicationRequestBus(bus.Broadcast, "GetCurrentLevelName")
slice_path = os.path.join("Levels", current_level_name, "TestSlice.slice")
slice_created = slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", entity_id, slice_path)
print(f"Slice created successfully: {slice_created}")
# 7) Instantiate an existing slice
# ensure we clear the selection so that the newly instantiated slice is selected after instantiating
# so that we can verify the name of the object in the entity inspector
general.clear_selection()
slice_path = os.path.join("EngineAssets", "Slices", "DefaultLevelSetup.slice")
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", slice_path, math.Uuid(), False)
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.SetPosition(position)
slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
self.wait_for_condition(lambda: entity_inspector_name_editor.text() == "DefaultLevelSetup", 2.0)
print(
f"Instantiated slice populated in Entity Inspector: {entity_inspector_name_editor.text() == 'DefaultLevelSetup'}"
)
test = ToolStabilityEntityOutlinerTest()
test.run()
@@ -0,0 +1,134 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
C6308168: Tool Stability & Workflow: Viewport Camera Selector
https://testrail.agscollab.com/index.php?/cases/view/6308168
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
import Tests.ly_shared.hydra_editor_utils as hydra
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class ToolStabilityViewportCameraSelectorTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tool_stability_viewport_camera_selector", args=["level"])
def run_test(self):
"""
Summary:
Tool Stability & Workflow: Viewport Camera Selector. This can be verified by creating mutiple entities
with Camera component and verifying their functionality by clicking each of them in the Viewport Camera
Selector tool
Expected Behavior:
The Viewport Camera Selector can be opened.
Cameras added to a level will populate the Veiwport Camera Selector.
Selecting cameras in the Viewport Camera Selector will change the perspective camera in the Viewport.
The Viewport Camera Selector can be closed.
Test Steps:
1) Open level
2) Create first Camera component entity
3) Create second Camera component entity at different position
4) Open Viewport Camera Selector
5) Click on both the Cameras in Viewport Camera Selector and verify view position
6) Close Viewport Camera Selector
Note:
- This test file must be called from the Lumberyard 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
"""
CAM_POSITION_1 = (512.0, 512.0, 34.0)
CAM_POSITION_2 = (100.0, 100.0, 34.0)
def create_camera_verify(entity_name, camera_position):
general.set_current_view_position(*camera_position)
# Trigger the action
pyside_utils.trigger_context_menu_entry(view_port, "Create camera entity from view")
# Verify if the entity is created with the name Camera# (Ex: Camera1)
camera_entity_id = general.find_editor_entity(entity_name)
assert camera_entity_id.isValid(), f"Entity {entity_name} with Camera component is not created"
# Verify if the entity has Camera component added
assert hydra.has_components(camera_entity_id, ["Camera"]), "Entity do not have a Camera component"
def select_camera_verify_position(camera_index, expected_position):
# Get the model index of the camera in the list view of Viewport Camera Selector
model_index = pyside_utils.find_child_by_pattern(list_view, f"Camera{camera_index}")
assert model_index, f"Camera{camera_index} not found in Camera View Selctor"
# Click on the camera
pyside_utils.item_view_index_mouse_click(list_view, model_index)
self.wait_for_condition(
lambda: general.get_current_view_position().x == expected_position[0]
and general.get_current_view_position().y == expected_position[1]
and general.get_current_view_position().z == expected_position[2],
1.0,
)
current_position = general.get_current_view_position()
assert (
current_position.x == expected_position[0]
and current_position.y == expected_position[1]
and current_position.z == expected_position[2]
), f"View position incorrect for Camera {camera_index}"
# 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=True,
)
general.idle_wait(3.0)
# Get the editor window, main window, viewport objects
editor_window = pyside_utils.get_editor_main_window()
main_window = editor_window.findChild(QtWidgets.QMainWindow)
view_port = main_window.findChildren(QtWidgets.QWidget, "renderOverlay")[0]
# 2) Create first Camera component entity
create_camera_verify("Camera1", CAM_POSITION_1)
print("done")
# 3) Create second Camera component entity at different position
create_camera_verify("Camera2", CAM_POSITION_2)
# 4) Open Viewport Camera Selector
if not general.is_pane_visible("Viewport Camera Selector"):
general.open_pane("Viewport Camera Selector")
# Get the Camera Selector object and the list of cameras
camera_selector = editor_window.findChildren(QtWidgets.QDockWidget, "Viewport Camera Selector")[0]
list_view = camera_selector.findChildren(QtWidgets.QListView)[0]
# 5) Click on both the Cameras in Viewport Camera Selector and verify view position
select_camera_verify_position(1, CAM_POSITION_1)
select_camera_verify_position(2, CAM_POSITION_2)
# 6) Close Viewport Camera Selector
camera_selector.close()
self.wait_for_condition(lambda: not general.is_pane_visible("Viewport Camera Selector"), 1.0)
if not general.is_pane_visible("Viewport Camera Selector"):
print("Viewport Camera Selector is closed")
test = ToolStabilityViewportCameraSelectorTest()
test.run()
@@ -0,0 +1,117 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1564077: The Tools menu options function normally - New view interaction Model enabled
https://testrail.agscollab.com/index.php?/cases/view/1564077
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestToolsMenuOptionsAfterInteractionModelToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="tools_menu_after_interaction_model_toggle: ", args=["level"])
def run_test(self):
"""
Summary:
Interact with Tools Menu options and verify if all the options are working.
Expected Behavior:
The Tools menu functions normally.
Test Steps:
1) Open level
2) Interact with Tools Menu options
Note:
- This test file must be called from the Lumberyard 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
"""
tools_menu_options = [
("Animation Editor (PREVIEW)",),
("Asset Browser",),
("Asset Editor",),
("Console",),
("Entity Inspector",),
("ImGui Editor",),
("Landscape Canvas",),
("Level Inspector",),
("Lua Editor",),
("Material Editor",),
("Particle Editor",),
("PhysX Configuration (PREVIEW)",),
("Script Canvas",),
("Slice Relationship View (PREVIEW)",),
("UI Editor",),
("Vegetation Editor",),
("Other", "Audio Controls Editor"),
("Other", "Console Variables"),
("Other", "Lens Flare Editor"),
("Other", "Measurement System Tool"),
("Other", "Python Console"),
("Other", "Python Scripts"),
("Other", "Slice Favorites"),
("Other", "Sun Trajectory Tool"),
("Other", "Terrain Texture Layers"),
("Other", "Time Of Day"),
("Plug-Ins", "Substance Editor"),
("Viewport", "Viewport Camera Selector"),
]
# 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=True,
)
general.idle_wait(3.0)
def on_focus_changed(old, new):
print("Focus Changed")
QtWidgets.QApplication.activeModalWidget().close()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with File Menu options
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
for option in tools_menu_options:
action = pyside_utils.get_action_for_menu_path(editor_window, "Tools", *option)
trig_func = lambda: on_action_triggered(action.iconText())
action.triggered.connect(trig_func)
action.trigger()
action.triggered.disconnect(trig_func)
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
general.close_pane("Editor Settings Manager")
test = TestToolsMenuOptionsAfterInteractionModelToggle()
test.run()
@@ -0,0 +1,169 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C1506899: Unsaved Changes Pop-Up
https://testrail.agscollab.com/index.php?/cases/view/1506899
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.bus as bus
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class UnsavedChangesPopupTest(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="unsaved_changes_popup", args=["level"])
@pyside_utils.wrap_async
async def run_test(self):
"""
Summary:
Verify if the unsaved change popup appears and each of the buttons function appropriately.
Expected Behavior:
A prompt appears informing the user there are unsaved changes, with 3 options present: Yes, No, and Cancel.
All options function as expected.
Test Steps:
1) Open a new level
2) Access Editor window
3) Open .inputbindings file in Asset Editor
4) Delete All events initially
5) Add an input event to make a change
6) Click on close - verify CANCEL function
7) Click on close - verify YES function
8) Make a new change for verifying NO button (Add another input event group)
9) Click on close - verify NO function
10) Close Asset Editor
Note:
- This test file must be called from the Lumberyard 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
"""
INPUTBINDING_FILE_NAME = "test.inputbindings"
def close_asset_editor():
general.close_pane("Asset Editor")
general.close_pane(INPUTBINDING_FILE_NAME)
return not general.is_pane_visible("Asset Editor")
async def close_asset_editor_unsaved(button_type):
asset_editor = editor_window.findChild(QtWidgets.QDockWidget, INPUTBINDING_FILE_NAME)
# Trigger the closing of the Asset Editor as async so we can listen for the modal dialog
pyside_utils.run_soon(lambda: asset_editor.close())
active_modal_widget = await pyside_utils.wait_for_modal_widget(timeout=2.0)
if active_modal_widget:
print("Message Box opened")
message_box = active_modal_widget.findChild(QtWidgets.QMessageBox, "")
button = message_box.button(button_type)
pyside_utils.click_button_async(button)
return await pyside_utils.wait_for_destroyed(active_modal_widget)
# 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=True,
)
# 2) Access Editor window
editor_window = pyside_utils.get_editor_main_window()
# 3) Open .inputbindings file in Asset Editor
# Close Asset Editor initially
close_asset_editor()
input_bindings_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", INPUTBINDING_FILE_NAME, math.Uuid(), False
)
editor.AssetEditorRequestsBus(bus.Broadcast, "OpenAssetEditorById", input_bindings_id)
asset_editor = editor_window.findChild(QtWidgets.QDockWidget, INPUTBINDING_FILE_NAME)
asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "m_assetEditorWidget")
input_event_groups = await pyside_utils.wait_for(lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups"))
# 4) Delete All events initially
delete_all_button = input_event_groups.findChildren(QtWidgets.QToolButton, "")[1]
pyside_utils.click_button_async(delete_all_button)
active_modal_widget = await pyside_utils.wait_for_modal_widget()
if active_modal_widget:
message_box = active_modal_widget.findChild(QtWidgets.QMessageBox, "")
button = message_box.button(QtWidgets.QMessageBox.Yes)
button.click()
# 5) Add an input event to make a change
await pyside_utils.wait_for_condition(lambda: len(asset_editor_widget.findChildren(QtWidgets.QFrame, "Input Event Groups")) > 1, 2.0)
input_event_groups = asset_editor_widget.findChildren(QtWidgets.QFrame, "Input Event Groups")[1]
add_button = input_event_groups.findChild(QtWidgets.QToolButton, "")
add_button.click()
# 6) Click on close - verify CANCEL function
await close_asset_editor_unsaved(QtWidgets.QMessageBox.Cancel)
# Cancel should not change anything and the Asset Editor should remain open
print(f"'CANCEL' button working as expected: {general.is_pane_visible(INPUTBINDING_FILE_NAME)}")
# 7) Click on close - verify YES function
await close_asset_editor_unsaved(QtWidgets.QMessageBox.Yes)
# Yes option should save the changes and not close the Asset Editor
print(f"Asset Editor is visible after clicking YES: {general.is_pane_visible(INPUTBINDING_FILE_NAME)}")
status_bar = await pyside_utils.wait_for(lambda: asset_editor_widget.findChild(QtWidgets.QWidget, "AssetEditorStatusBar"))
text_edit = status_bar.findChild(QtWidgets.QLabel, "textEdit")
success = await pyside_utils.wait_for_condition(lambda: f"{INPUTBINDING_FILE_NAME} - Asset saved!" in text_edit.text(), 3.0)
if success:
print("'YES' button working as expected")
# 8) Make a new change for verifying NO button (Add another input event group)
input_event_groups = await pyside_utils.wait_for(lambda: asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups"), 2.0)
add_button = await pyside_utils.wait_for(lambda: input_event_groups.findChild(QtWidgets.QToolButton, ""), 2.0)
add_button.click()
# 9) Click on close - verify NO function
await close_asset_editor_unsaved(QtWidgets.QMessageBox.No)
# No button should close the Asset Editor and changes should not be saved
print(f"Asset Editor is not visible after clicking NO: {not general.is_pane_visible('Asset Editor')}")
# Reopen inputbindings file
input_bindings_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", INPUTBINDING_FILE_NAME, math.Uuid(), False
)
editor.AssetEditorRequestsBus(bus.Broadcast, "OpenAssetEditorById", input_bindings_id)
# Ensure there are no changes saved (only one event group should be there)
asset_editor = await pyside_utils.wait_for(lambda: editor_window.findChild(QtWidgets.QDockWidget, INPUTBINDING_FILE_NAME), 2.0)
asset_editor_widget = asset_editor.findChild(QtWidgets.QWidget, "m_assetEditorWidget")
input_event_groups = asset_editor_widget.findChild(QtWidgets.QFrame, "Input Event Groups")
no_of_elements_label = input_event_groups.findChild(QtWidgets.QLabel, "DefaultLabel")
success = await pyside_utils.wait_for_condition(lambda: "1 elements" in no_of_elements_label.text(), 2.0)
if success:
print("'NO' button working as expected")
# 10) Close Asset Editor
print(f"Asset Editor closed: {close_asset_editor()}")
test = UnsavedChangesPopupTest()
test.run()
@@ -0,0 +1,105 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C16780807: The View menu options function normally - New view interaction Model enabled
https://testrail.agscollab.com/index.php?/cases/view/16780807
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestViewMenuAfterInteractionModelToggle(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="view_menu_after_interaction_model_toggle: ", 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) Open level
2) Interact with View Menu options
Note:
- This test file must be called from the Lumberyard 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",),
("Layouts", "Default Layout"),
("Layouts", "User Legacy Layout"),
("Layouts", "Default Layout"),
("Layouts", "Save Layout"),
("Layouts", "Restore Default Layout"),
("Viewport", "Wireframe"),
("Viewport", "Grid Settings"),
("Viewport", "Configure Layout"),
("Viewport", "Goto Coordinates"),
("Viewport", "Center on Selection"),
("Viewport", "Goto Location"),
("Viewport", "Remember Location"),
("Viewport", "Change Move Speed"),
("Viewport", "Switch Camera"),
("Viewport", "Show/Hide Helpers"),
("Refresh Style",),
]
# 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=True,
)
general.idle_wait(3.0)
def on_focus_changed(old, new):
print("Focus Changed")
QtWidgets.QApplication.activeModalWidget().close()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 2) Interact with View Menu options
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
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)
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = TestViewMenuAfterInteractionModelToggle()
test.run()
@@ -0,0 +1,97 @@
"""
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
its licensors.
For complete copyright and license terms please see the LICENSE at the root of this
distribution (the "License"). All use of this software is governed by the License,
or, if provided, by the license below or the license accompanying this file. Do not
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
"""
C24064534: The View menu options function normally
https://testrail.agscollab.com/index.php?/cases/view/24064534
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.legacy.general as general
import Tests.ly_shared.pyside_utils as pyside_utils
from PySide2 import QtWidgets
from Tests.editor.editor_utils.editor_test_helper import EditorTestHelper
class TestViewMenuOptions(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="view_menu_options: ", 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) Interact with View Menu options
Note:
- This test file must be called from the Lumberyard 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",),
("Layouts", "Default Layout"),
("Layouts", "User Legacy Layout"),
("Layouts", "Default Layout"),
("Layouts", "Save Layout"),
("Layouts", "Restore Default Layout"),
("Viewport", "Wireframe"),
("Viewport", "Ruler"),
("Viewport", "Grid Settings"),
("Viewport", "Configure Layout"),
("Viewport", "Goto Coordinates"),
("Viewport", "Center on Selection"),
("Viewport", "Goto Location"),
("Viewport", "Remember Location"),
("Viewport", "Change Move Speed"),
("Viewport", "Switch Camera"),
("Viewport", "Show/Hide Helpers"),
("Refresh Style",),
]
general.idle_wait(3.0)
def on_focus_changed(old, new):
print("Focus Changed")
QtWidgets.QApplication.activeModalWidget().close()
def on_action_triggered(action_name):
print(f"{action_name} Action triggered")
# 1) Interact with View Menu options
try:
editor_window = pyside_utils.get_editor_main_window()
app = QtWidgets.QApplication.instance()
app.focusChanged.connect(on_focus_changed)
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)
except Exception as e:
print(e)
finally:
app.focusChanged.disconnect(on_focus_changed)
test = TestViewMenuOptions()
test.run()