Merge branch 'upstream/development' into LYN-6769_TestingRPCs

This commit is contained in:
Gene Walters
2021-12-06 11:56:19 -08:00
79 changed files with 3264 additions and 3794 deletions
@@ -12,6 +12,7 @@ import pytest
import ly_test_tools.environment.file_system as file_system
import editor_python_test_tools.hydra_test_utils as hydra
from ly_test_tools.o3de.editor_test import EditorSharedTest, EditorTestSuite
from Atom.atom_utils.atom_constants import LIGHT_TYPES
logger = logging.getLogger(__name__)
@@ -159,3 +160,17 @@ class TestMaterialEditorBasicTests(object):
enable_prefab_system=False,
)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
@pytest.mark.test_case_id("C36529666")
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
@@ -18,6 +18,13 @@ LIGHT_TYPES = {
'simple_spot': 7,
}
# Qualiity Level settings for Diffuse Global Illumination level component
GLOBAL_ILLUMINATION_QUALITY = {
'Low': 0,
'Medium': 1,
'High': 2,
}
class AtomComponentProperties:
"""
@@ -116,6 +123,21 @@ class AtomComponentProperties:
}
return properties[property]
@staticmethod
def diffuse_global_illumination(property: str = 'name') -> str:
"""
Diffuse Global Illumination level component properties.
Controls global settings for Diffuse Probe Grid components.
- 'Quality Level' from atom_constants.py GLOBAL_ILLUMINATION_QUALITY
:param property: From the last element of the property tree path. Default 'name' for component name string.
:return: Full property path OR component name if no property specified.
"""
properties = {
'name': 'Diffuse Global Illumination',
'Quality Level': 'Controller|Configuration|Quality Level'
}
return properties[property]
@staticmethod
def diffuse_probe_grid(property: str = 'name') -> str:
"""
@@ -148,7 +170,8 @@ class AtomComponentProperties:
@staticmethod
def display_mapper(property: str = 'name') -> str:
"""
Display Mapper component properties.
Display Mapper level component properties.
- 'Enable LDR color grading LUT' toggles the use of LDR color grading LUT
- 'LDR color Grading LUT' is the Low Definition Range (LDR) color grading for Look-up Textures (LUT) which is
an Asset.id value corresponding to a lighting asset file.
:param property: From the last element of the property tree path. Default 'name' for component name string.
@@ -156,6 +179,7 @@ class AtomComponentProperties:
"""
properties = {
'name': 'Display Mapper',
'Enable LDR color grading LUT': 'Controller|Configuration|Enable LDR color grading LUT',
'LDR color Grading LUT': 'Controller|Configuration|LDR color Grading LUT',
}
return properties[property]
@@ -0,0 +1,109 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
creation_undo = (
"UNDO Level component addition success",
"UNDO Level component addition failed")
creation_redo = (
"REDO Level component addition success",
"REDO Level component addition failed")
diffuse_global_illumination_component = (
"Level has a Diffuse Global Illumination component",
"Level failed to find Diffuse Global Illumination component")
diffuse_global_illumination_quality = (
"Quality Level set",
"Quality Level could not be set")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
"""
Summary:
Tests the Diffuse Global Illumination level component can be added to the level entity and is stable.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Add Diffuse Global Illumination level component to the level entity.
2) UNDO the level component addition.
3) REDO the level component addition.
4) Set Quality Level property to Low
5) Enter/Exit game mode.
6) Look for errors and asserts.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import AtomComponentProperties, GLOBAL_ILLUMINATION_QUALITY
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Add Diffuse Global Illumination level component to the level entity.
diffuse_global_illumination_component = EditorLevelEntity.add_component(
AtomComponentProperties.diffuse_global_illumination())
Report.critical_result(
Tests.diffuse_global_illumination_component,
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 2. UNDO the level component addition.
# -> UNDO component addition.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo,
not EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 3. REDO the level component addition.
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo,
EditorLevelEntity.has_component(AtomComponentProperties.diffuse_global_illumination()))
# 4. Set Quality Level property to Low
diffuse_global_illumination_component.set_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level', GLOBAL_ILLUMINATION_QUALITY['Low']))
quality = diffuse_global_illumination_component.get_component_property_value(
AtomComponentProperties.diffuse_global_illumination('Quality Level'))
Report.result(diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity)
@@ -0,0 +1,124 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
creation_undo = (
"UNDO level component addition success",
"UNDO level component addition failed")
creation_redo = (
"REDO Level component addition success",
"REDO Level component addition failed")
display_mapper_component = (
"Level has a Display Mapper component",
"Level failed to find Display Mapper component")
ldr_color_grading_lut = (
"LDR color Grading LUT asset set",
"LDR color Grading LUT asset could not be set")
enable_ldr_color_grading_lut = (
"Enable LDR color grading LUT set",
"Enable LDR color grading LUT could not be set")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
"""
Summary:
Tests the Display Mapper level component can be added to the level entity and has the expected functionality.
Test setup:
- Wait for Editor idle loop.
- Open the "Base" level.
Expected Behavior:
The component can be added, used in game mode, and has accurate required components.
Creation and deletion undo/redo should also work.
Test Steps:
1) Add Display Mapper level component to the level entity.
2) UNDO the level component addition.
3) REDO the level component addition.
4) Set LDR color Grading LUT asset.
5) Set Enable LDR color grading LUT property True
6) Enter/Exit game mode.
7) Look for errors and asserts.
:return: None
"""
import os
import azlmbr.legacy.general as general
from editor_python_test_tools.asset_utils import Asset
from editor_python_test_tools.editor_entity_utils import EditorLevelEntity
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import AtomComponentProperties
with Tracer() as error_tracer:
# Test setup begins.
# Setup: Wait for Editor idle loop before executing Python hydra scripts then open "Base" level.
TestHelper.init_idle()
TestHelper.open_level("", "Base")
# Test steps begin.
# 1. Add Display Mapper level component to the level entity.
display_mapper_component = EditorLevelEntity.add_component(AtomComponentProperties.display_mapper())
Report.critical_result(
Tests.display_mapper_component,
EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 2. UNDO the level component addition.
# -> UNDO component addition.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.creation_undo, not EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 3. REDO the level component addition.
# -> REDO component addition.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, EditorLevelEntity.has_component(AtomComponentProperties.display_mapper()))
# 4. Set LDR color Grading LUT asset.
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('LDR color Grading LUT'), display_mapper_asset.id)
Report.result(
Tests.ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('LDR color Grading LUT')) == display_mapper_asset.id)
# 5. Set Enable LDR color grading LUT property True
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Test.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
# 6. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 7. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
for assert_info in error_tracer.asserts:
Report.info(f"Assert: {assert_info.filename} {assert_info.function} | {assert_info.message}")
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AtomEditorComponentsLevel_DisplayMapper_AddedToEntity)
@@ -7,15 +7,6 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
class Tests:
camera_creation = (
"Camera Entity successfully created",
"Camera Entity failed to be created")
camera_component_added = (
"Camera component was added to entity",
"Camera component failed to be added to entity")
camera_component_check = (
"Entity has a Camera component",
"Entity failed to find Camera component")
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
@@ -43,6 +34,9 @@ class Tests:
ldr_color_grading_lut = (
"LDR color Grading LUT asset set",
"LDR color Grading LUT asset could not be set")
enable_ldr_color_grading_lut = (
"Enable LDR color grading LUT set",
"Enable LDR color grading LUT could not be set")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
@@ -72,14 +66,15 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
2) Add Display Mapper component to Display Mapper entity.
3) UNDO the entity creation and component addition.
4) REDO the entity creation and component addition.
5) Enter/Exit game mode.
6) Test IsHidden.
7) Test IsVisible.
8) Set LDR color Grading LUT asset.
9) Delete Display Mapper entity.
10) UNDO deletion.
11) REDO deletion.
12) Look for errors and asserts.
5) Set LDR color Grading LUT asset.
6) Set Enable LDR color grading LUT property True
7) Enter/Exit game mode.
8) Test IsHidden.
9) Test IsVisible.
10) Delete Display Mapper entity.
11) UNDO deletion.
12) REDO deletion.
13) Look for errors and asserts.
:return: None
"""
@@ -133,21 +128,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, display_mapper_entity.exists())
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 6. Test IsHidden.
display_mapper_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
# 7. Test IsVisible.
display_mapper_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
# 8. Set LDR color Grading LUT asset.
# 5. Set LDR color Grading LUT asset.
display_mapper_asset_path = os.path.join("TestData", "test.lightingpreset.azasset")
display_mapper_asset = Asset.find_asset_by_path(display_mapper_asset_path, False)
display_mapper_component.set_component_property_value(
@@ -157,19 +138,41 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper("LDR color Grading LUT")) == display_mapper_asset.id)
# 9. Delete Display Mapper entity.
# 6. Set Enable LDR color grading LUT property True
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Tests.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
# 7. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
general.idle_wait_frames(1)
TestHelper.exit_game_mode(Tests.exit_game_mode)
# 8. Test IsHidden.
display_mapper_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, display_mapper_entity.is_hidden() is True)
# 9. Test IsVisible.
display_mapper_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, display_mapper_entity.is_visible() is True)
# 10. Delete Display Mapper entity.
display_mapper_entity.delete()
Report.result(Tests.entity_deleted, not display_mapper_entity.exists())
# 10. UNDO deletion.
# 11. UNDO deletion.
general.undo()
Report.result(Tests.deletion_undo, display_mapper_entity.exists())
# 11. REDO deletion.
# 12. REDO deletion.
general.redo()
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
# 12. Look for errors and asserts.
# 13. Look for errors and asserts.
TestHelper.wait_for_condition(lambda: error_tracer.has_errors or error_tracer.has_asserts, 1.0)
for error_info in error_tracer.errors:
Report.info(f"Error: {error_info.filename} {error_info.function} | {error_info.message}")
@@ -107,7 +107,6 @@ class EditorComponent:
return type_ids
def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
"""
Converts a vector3-like element into a azlmbr.math.Vector3
@@ -120,6 +119,7 @@ def convert_to_azvector3(xyz) -> azlmbr.math.Vector3:
else:
raise ValueError("vector must be a 3 element list/tuple or azlmbr.math.Vector3")
class EditorEntity:
"""
Entity class is used to create and interact with Editor Entities.
@@ -470,3 +470,99 @@ class EditorEntity:
assert self.id.isValid(), "A valid entity id is required to focus on its owning prefab."
focus_prefab_result = azlmbr.prefab.PrefabFocusPublicRequestBus(bus.Broadcast, "FocusOnOwningPrefab", self.id)
assert focus_prefab_result.IsSuccess(), f"Prefab operation 'FocusOnOwningPrefab' failed. Error: {focus_prefab_result.GetError()}"
class EditorLevelEntity:
"""
EditorLevel class used to add and fetch level components.
Level entity is a special entity that you do not create/destroy independently of larger systems of level creation.
This collects a number of staticmethods that do not rely on entityId since Level entity is found internally by
EditorLevelComponentAPIBus requests.
"""
@staticmethod
def get_type_ids(component_names: list) -> list:
"""
Used to get type ids of given components list for EntityType Level
:param: component_names: List of components to get type ids
:return: List of type ids of given components.
"""
type_ids = editor.EditorComponentAPIBus(
bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Level
)
return type_ids
@staticmethod
def add_component(component_name: str) -> EditorComponent:
"""
Used to add new component to Level.
:param component_name: String of component name to add.
:return: Component object of newly added component.
"""
component = EditorLevelEntity.add_components([component_name])[0]
return component
@staticmethod
def add_components(component_names: list) -> List[EditorComponent]:
"""
Used to add multiple components
:param: component_names: List of components to add to level
:return: List of newly added components to the level
"""
components = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
for type_id in type_ids:
new_comp = EditorComponent()
new_comp.type_id = type_id
add_component_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", [type_id]
)
assert (
add_component_outcome.IsSuccess()
), f"Failure: Could not add component: '{new_comp.get_component_name()}' to level"
new_comp.id = add_component_outcome.GetValue()[0]
components.append(new_comp)
return components
@staticmethod
def get_components_of_type(component_names: list) -> List[EditorComponent]:
"""
Used to get components of type component_name that already exists on the level
:param component_names: List of names of components to check
:return: List of Level Component objects of given component name
"""
component_list = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
for type_id in type_ids:
component = EditorComponent()
component.type_id = type_id
get_component_of_type_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "GetComponentOfType", type_id
)
assert (
get_component_of_type_outcome.IsSuccess()
), f"Failure: Level does not have component:'{component.get_component_name()}'"
component.id = get_component_of_type_outcome.GetValue()
component_list.append(component)
return component_list
@staticmethod
def has_component(component_name: str) -> bool:
"""
Used to verify if the level has the specified component
:param component_name: Name of component to check for
:return: True, if level has specified component. Else, False
"""
type_ids = EditorLevelEntity.get_type_ids([component_name])
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "HasComponentOfType", type_ids[0])
@staticmethod
def count_components_of_type(component_name: str) -> int:
"""
Used to get a count of the specified level component attached to the level
:param component_name: Name of component to check for
:return: integer count of occurences of level component attached to level or zero if none are present
"""
type_ids = EditorLevelEntity.get_type_ids([component_name])
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "CountComponentsOfType", type_ids[0])
@@ -98,38 +98,32 @@ class FileManagement:
"""
file_map = FileManagement._load_file_map()
backup_path = FileManagement.backup_folder_path
backup_file_name = "{}.bak".format(file_name)
backup_file = os.path.join(backup_path, backup_file_name)
# If backup directory DNE, make one
if not os.path.exists(backup_path):
os.mkdir(backup_path)
# If "traditional" backup file exists, delete it (myFile.txt.bak)
if os.path.exists(backup_file):
fs.delete([backup_file], True, False)
# Find my next storage name (myFile_1.txt.bak)
backup_storage_file_name = FileManagement._next_available_name(backup_file_name, file_map)
if backup_storage_file_name is None:
# Find my next storage name (myFile_1.txt)
backup_file_name = FileManagement._next_available_name(file_name, file_map)
if backup_file_name is None:
# If _next_available_name returns None, we have backed up MAX_BACKUPS of files name [file_name]
raise Exception(
"FileManagement class ran out of backups per name. Max: {}".format(FileManagement.MAX_BACKUPS)
)
backup_storage_file = os.path.join(backup_path, backup_storage_file_name)
# If this backup file already exists, delete it.
backup_storage_file = "{}.bak".format(os.path.normpath(os.path.join(backup_path, backup_file_name)))
if os.path.exists(backup_storage_file):
# This file should not exists, but if it does it's about to get clobbered!
fs.unlock_file(backup_storage_file)
# Create "traditional" backup file (myFile.txt.bak)
fs.create_backup(os.path.join(file_path, file_name), backup_path)
# Copy "traditional" backup file into storage backup (myFile_1.txt.bak)
FileManagement._copy_file(backup_file_name, backup_path, backup_storage_file_name, backup_path)
fs.lock_file(backup_storage_file)
# Delete "traditional" back up file
fs.unlock_file(backup_file)
fs.delete([backup_file], True, False)
fs.delete([backup_storage_file], True, False)
# Create backup file (myFile_1.txt.bak)
original_file = os.path.normpath(os.path.join(file_path, file_name))
fs.create_backup(original_file, backup_path, backup_file_name)
# Update file map with new file
file_map[os.path.join(file_path, file_name)] = backup_storage_file_name
file_map[original_file] = backup_file_name
FileManagement._save_file_map(file_map)
# Unlock original file to get it ready to be edited by the test
fs.unlock_file(os.path.join(file_path, file_name))
fs.unlock_file(original_file)
@staticmethod
def _restore_file(file_name, file_path):
@@ -143,20 +137,15 @@ class FileManagement:
"""
file_map = FileManagement._load_file_map()
backup_path = FileManagement.backup_folder_path
src_file = os.path.join(file_path, file_name)
src_file = os.path.normpath(os.path.join(file_path, file_name))
if src_file in file_map:
backup_file = os.path.join(backup_path, file_map[src_file])
if os.path.exists(backup_file):
fs.unlock_file(backup_file)
fs.unlock_file(src_file)
# Make temporary copy of backed up file to restore from
temp_file = "{}.bak".format(file_name)
FileManagement._copy_file(file_map[src_file], backup_path, temp_file, backup_path)
fs.restore_backup(src_file, backup_path)
fs.lock_file(src_file)
# Delete backup file
fs.delete([os.path.join(backup_path, temp_file)], True, False)
backup_file_name = file_map[src_file]
backup_file = "{}.bak".format(os.path.join(backup_path, backup_file_name))
fs.unlock_file(src_file)
if fs.restore_backup(src_file, backup_path, backup_file_name):
fs.delete([backup_file], True, False)
# Remove from file map
del file_map[src_file]
FileManagement._save_file_map(file_map)
@@ -24,6 +24,7 @@ def CreatePrefab_WithSingleEntity():
# Creates a prefab from the new entity
Prefab.create_prefab(car_prefab_entities, CAR_PREFAB_FILE_NAME)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(CreatePrefab_WithSingleEntity)
@@ -8,6 +8,8 @@ General Asset Processor Batch Tests
"""
# Import builtin libraries
from os import listdir
import pytest
import logging
import os
@@ -724,3 +726,11 @@ class TestsAssetProcessorBatch_AllPlatforms(object):
assert error_line_found, "The error could not be found in the newest run of the AP Batch log."
@pytest.mark.assetpipeline
def test_AssetProcessor_Log_On_Failure(self, asset_processor, ap_setup_fixture, workspace):
asset_processor.prepare_test_environment(ap_setup_fixture["tests_dir"], "test_AP_Logs")
result, output = asset_processor.batch_process(expect_failure=True, capture_output=True)
assert result == False, f'AssetProcessorBatch should have failed because there is a bad asset, output was {output}'
jobLogs = listdir(workspace.paths.ap_job_logs() + "/test_AP_Logs")
assert not len(jobLogs) == 0, 'No job logs where output during failure.'
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7c6b33c6137d6bd8c696f180c30a23089c95c1af398a630b4b13e080bec3254d
size 18220
@@ -8,7 +8,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
import os
import logging
import subprocess
import sys
import pytest
import time
@@ -128,7 +128,8 @@ class TestAutomationBase:
errors.append(TestRunError("FAILED TEST", error_str))
if return_code and return_code != TestAutomationBase.TEST_FAIL_RETCODE: # Crashed
crash_info = "-- No crash log available --"
crash_log = os.path.join(workspace.paths.project_log(), 'error.log')
crash_log = workspace.paths.crash_log()
try:
waiter.wait_for(lambda: os.path.exists(crash_log), timeout=TestAutomationBase.WAIT_FOR_CRASH_LOG)
except AssertionError:
@@ -914,11 +914,11 @@ namespace UnitTest
m_testAssetManager->SetParallelDependentLoadingEnabled(true);
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_LoadTest_SameAsset_DifferentFilters)
#else
TEST_F(AssetJobsFloodTest, LoadTest_SameAsset_DifferentFilters)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
@@ -1263,11 +1263,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_NoLoadNotLoaded)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -1304,11 +1304,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadContainerDependencies_LoadAllLoadsNoLoad)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -1343,11 +1343,11 @@ namespace UnitTest
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusDisconnect();
}
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#if AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
TEST_F(AssetJobsFloodTest, DISABLED_AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
#else
TEST_F(AssetJobsFloodTest, AssetWithNoLoadReference_LoadDependencies_BehaviorObeyed)
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS || AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS
#endif // AZ_TRAIT_DISABLE_FAILED_ASSET_MANAGER_TESTS
{
m_assetHandlerAndCatalog->AssetCatalogRequestBus::Handler::BusConnect();
// Setup has already created/destroyed assets
@@ -13,6 +13,13 @@
namespace UnitTest
{
//! Null implementation of DebugDisplayRequests for dummy draw calls.
class NullDebugDisplayRequests : public AzFramework::DebugDisplayRequests
{
public:
virtual ~NullDebugDisplayRequests() = default;
};
//! Minimal implementation of DebugDisplayRequests to support testing shapes.
//! Stores a list of points based on received draw calls to delineate the exterior of the object requested to be drawn.
class TestDebugDisplayRequests : public AzFramework::DebugDisplayRequests
@@ -29,7 +29,8 @@ namespace UnitTest
void SetUpEditorFixtureImpl() override
{
ToolsApplicationFixtureT::SetUpEditorFixtureImpl();
m_viewportManipulatorInteraction = AZStd::make_unique<IndirectCallManipulatorViewportInteraction>();
m_viewportManipulatorInteraction =
AZStd::make_unique<IndirectCallManipulatorViewportInteraction>(ToolsApplicationFixtureT::CreateDebugDisplayRequests());
m_actionDispatcher = AZStd::make_unique<ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction);
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
@@ -17,11 +17,10 @@ namespace AzManipulatorTestFramework
class ViewportInteraction;
//! Implementation of manipulator viewport interaction that manipulates the manager directly.
class DirectCallManipulatorViewportInteraction
: public ManipulatorViewportInteraction
class DirectCallManipulatorViewportInteraction : public ManipulatorViewportInteraction
{
public:
DirectCallManipulatorViewportInteraction();
explicit DirectCallManipulatorViewportInteraction(AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests);
~DirectCallManipulatorViewportInteraction();
// ManipulatorViewportInteractionInterface ...
@@ -21,7 +21,7 @@ namespace AzManipulatorTestFramework
class IndirectCallManipulatorViewportInteraction : public ManipulatorViewportInteraction
{
public:
IndirectCallManipulatorViewportInteraction();
explicit IndirectCallManipulatorViewportInteraction(AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests);
~IndirectCallManipulatorViewportInteraction();
// ManipulatorViewportInteractionInterface ...
@@ -11,10 +11,13 @@
#include <AzFramework/Visibility/EntityVisibilityQuery.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
namespace AzFramework
{
class DebugDisplayRequests;
}
namespace AzManipulatorTestFramework
{
class NullDebugDisplayRequests;
//! Implementation of the viewport interaction model to handle viewport interaction requests.
class ViewportInteraction
: public ViewportInteractionInterface
@@ -23,7 +26,7 @@ namespace AzManipulatorTestFramework
, private AzToolsFramework::ViewportInteraction::EditorEntityViewportInteractionRequestBus::Handler
{
public:
ViewportInteraction();
explicit ViewportInteraction(AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests);
~ViewportInteraction();
// ViewportInteractionInterface overrides ...
@@ -63,7 +66,7 @@ namespace AzManipulatorTestFramework
static constexpr AzFramework::ViewportId m_viewportId = 1234; //!< Arbitrary viewport id for manipulator tests.
AzFramework::EntityVisibilityQuery m_entityVisibilityQuery;
AZStd::unique_ptr<NullDebugDisplayRequests> m_nullDebugDisplayRequests;
AZStd::shared_ptr<AzFramework::DebugDisplayRequests> m_debugDisplayRequests;
AzFramework::CameraState m_cameraState;
bool m_gridSnapping = false;
bool m_angularSnapping = false;
@@ -118,10 +118,11 @@ namespace AzManipulatorTestFramework
return m_manipulatorManager->Interacting();
}
DirectCallManipulatorViewportInteraction::DirectCallManipulatorViewportInteraction()
DirectCallManipulatorViewportInteraction::DirectCallManipulatorViewportInteraction(
AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests)
: m_customManager(
AZStd::make_unique<CustomManipulatorManager>(AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"))))
, m_viewportInteraction(AZStd::make_unique<ViewportInteraction>())
, m_viewportInteraction(AZStd::make_unique<ViewportInteraction>(AZStd::move(debugDisplayRequests)))
, m_manipulatorManager(AZStd::make_unique<DirectCallManipulatorManager>(m_viewportInteraction.get(), m_customManager))
{
}
@@ -76,8 +76,9 @@ namespace AzManipulatorTestFramework
return manipulatorInteracting;
}
IndirectCallManipulatorViewportInteraction::IndirectCallManipulatorViewportInteraction()
: m_viewportInteraction(AZStd::make_unique<ViewportInteraction>())
IndirectCallManipulatorViewportInteraction::IndirectCallManipulatorViewportInteraction(
AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests)
: m_viewportInteraction(AZStd::make_unique<ViewportInteraction>(AZStd::move(debugDisplayRequests)))
, m_manipulatorManager(AZStd::make_unique<IndirectCallManipulatorManager>(*m_viewportInteraction))
{
}
@@ -13,15 +13,8 @@
namespace AzManipulatorTestFramework
{
// Null debug display for dummy draw calls
class NullDebugDisplayRequests : public AzFramework::DebugDisplayRequests
{
public:
virtual ~NullDebugDisplayRequests() = default;
};
ViewportInteraction::ViewportInteraction()
: m_nullDebugDisplayRequests(AZStd::make_unique<NullDebugDisplayRequests>())
ViewportInteraction::ViewportInteraction(AZStd::shared_ptr<AzFramework::DebugDisplayRequests> debugDisplayRequests)
: m_debugDisplayRequests(AZStd::move(debugDisplayRequests))
{
AzToolsFramework::ViewportInteraction::ViewportInteractionRequestBus::Handler::BusConnect(m_viewportId);
AzToolsFramework::ViewportInteraction::ViewportSettingsRequestBus::Handler::BusConnect(m_viewportId);
@@ -102,7 +95,7 @@ namespace AzManipulatorTestFramework
AzFramework::DebugDisplayRequests& ViewportInteraction::GetDebugDisplay()
{
return *m_nullDebugDisplayRequests;
return *m_debugDisplayRequests;
}
void ViewportInteraction::SetGridSnapping(const bool enabled)
@@ -26,7 +26,8 @@ namespace UnitTest
{
public:
GridSnappingFixture()
: m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>())
: m_viewportManipulatorInteraction(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>(
AZStd::make_shared<NullDebugDisplayRequests>()))
, m_actionDispatcher(
AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(*m_viewportManipulatorInteraction))
{
@@ -15,7 +15,8 @@ namespace UnitTest
{
public:
AValidViewportInteraction()
: m_viewportInteraction(AZStd::make_unique<AzManipulatorTestFramework::ViewportInteraction>())
: m_viewportInteraction(
AZStd::make_unique<AzManipulatorTestFramework::ViewportInteraction>(AZStd::make_shared<NullDebugDisplayRequests>()))
{
}
@@ -75,9 +75,11 @@ namespace UnitTest
void SetUpEditorFixtureImpl() override
{
m_directState =
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>());
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>(
AZStd::make_shared<NullDebugDisplayRequests>()));
m_busState =
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction>());
AZStd::make_unique<State>(AZStd::make_unique<AzManipulatorTestFramework::IndirectCallManipulatorViewportInteraction>(
AZStd::make_shared<NullDebugDisplayRequests>()));
m_cameraState =
AzFramework::CreateIdentityDefaultCamera(AZ::Vector3::CreateZero(), AzManipulatorTestFramework::DefaultViewportSize);
}
@@ -14,7 +14,6 @@
#define AZ_TRAIT_UNIT_TEST_DILLER_TRIGGER_EVENT_COUNT 100000
#define AZ_TRAIT_DISABLE_FAILED_AP_CONNECTION_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ASSET_LOAD_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ATOM_RPI_TESTS true
#define AZ_TRAIT_DISABLE_FAILED_ARCHIVE_TESTS true
@@ -52,7 +52,7 @@ namespace AzToolsFramework
void ReadOnlyEntitySystemComponent::RefreshReadOnlyState(const EntityIdList& entityIds)
{
for (const AZ::EntityId entityId : entityIds)
for (const AZ::EntityId& entityId : entityIds)
{
bool wasReadOnly = m_readOnlystates[entityId];
QueryReadOnlyStateForEntity(entityId);
@@ -67,10 +67,10 @@ namespace AzToolsFramework
void ReadOnlyEntitySystemComponent::RefreshReadOnlyStateForAllEntities()
{
for (auto elem : m_readOnlystates)
for (auto& elem : m_readOnlystates)
{
AZ::EntityId entityId = elem.first;
bool wasReadOnly = m_readOnlystates[entityId];
bool wasReadOnly = elem.second;
QueryReadOnlyStateForEntity(entityId);
if (bool isReadOnly = m_readOnlystates[entityId]; wasReadOnly != isReadOnly)
@@ -50,7 +50,7 @@ namespace AzToolsFramework
return false;
}
void TraceLogger::PrepareLogFile(const AZStd::string& logFileName)
void TraceLogger::OpenLogFile(const AZStd::string& logFileName, bool clearLogFile)
{
using namespace AzFramework;
@@ -73,7 +73,7 @@ namespace AzToolsFramework
AZStd::string logPath;
StringFunc::Path::Join(logDirectory.c_str(), logFileName.c_str(), logPath);
m_logFile.reset(aznew LogFile(logPath.c_str()));
m_logFile.reset(aznew LogFile(logPath.c_str(), clearLogFile));
if (m_logFile)
{
m_logFile->SetMachineReadable(false);
@@ -81,7 +81,7 @@ namespace AzToolsFramework
{
m_logFile->AppendLog(LogFile::SEV_NORMAL, message.window.c_str(), message.message.c_str());
}
m_startupLogSink = {};
m_startupLogSink.clear();
m_logFile->FlushLog();
}
}
@@ -23,7 +23,7 @@ namespace AzToolsFramework
~TraceLogger();
//! Open log file and dump log sink into it
void PrepareLogFile(const AZStd::string& logFileName);
void OpenLogFile(const AZStd::string& logFileName, bool clearLogFile);
//! Add filter to ignore messages for windows with matching names
void AddWindowFilter(const AZStd::string& filter);
@@ -55,7 +55,8 @@ namespace AzToolsFramework
AZStd::string window;
AZStd::string message;
};
AZStd::vector<LogMessage> m_startupLogSink;
AZStd::list<LogMessage> m_startupLogSink;
AZStd::unordered_set<AZStd::string> m_windowFilters;
AZStd::unordered_set<AZStd::string> m_messageFilters;
AZStd::unique_ptr<AzFramework::LogFile> m_logFile;
@@ -191,8 +191,8 @@ namespace AzToolsFramework
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
ManipulatorState{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
}
void AngularManipulator::SetAxis(const AZ::Vector3& axis)
@@ -116,8 +116,8 @@ namespace AzToolsFramework
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ TransformUniformScale(GetSpace()), GetNonUniformScale(), m_localStart, MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), m_localStart, MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
}
}
@@ -239,8 +239,8 @@ namespace AzToolsFramework
view->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ ApplySpace(localTransform), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
ManipulatorState{ ApplySpace(localTransform), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
}
}
@@ -146,7 +146,7 @@ namespace AzToolsFramework
for (const auto& pair : m_manipulatorIdToPtrMap)
{
pair.second->Draw({ Interacting() }, debugDisplay, cameraState, mouseInteraction);
pair.second->Draw(ManipulatorManagerState{ Interacting() }, debugDisplay, cameraState, mouseInteraction);
}
RefreshMouseOverState(mouseInteraction.m_mousePick);
@@ -10,6 +10,15 @@
namespace AzToolsFramework
{
AZ::Transform ApplySpace(const AZ::Transform& localTransform, const AZ::Transform& space, const AZ::Vector3& nonUniformScale)
{
AZ::Transform result;
result.SetRotation(space.GetRotation() * localTransform.GetRotation());
result.SetTranslation(space.TransformPoint(nonUniformScale * localTransform.GetTranslation()));
result.SetUniformScale(space.GetUniformScale() * localTransform.GetUniformScale());
return result;
}
const AZ::Transform& ManipulatorSpace::GetSpace() const
{
return m_space;
@@ -32,11 +41,7 @@ namespace AzToolsFramework
AZ::Transform ManipulatorSpace::ApplySpace(const AZ::Transform& localTransform) const
{
AZ::Transform result;
result.SetRotation(m_space.GetRotation() * localTransform.GetRotation());
result.SetTranslation(m_space.TransformPoint(m_nonUniformScale * localTransform.GetTranslation()));
result.SetUniformScale(m_space.GetUniformScale() * localTransform.GetUniformScale());
return result;
return AzToolsFramework::ApplySpace(localTransform, m_space, m_nonUniformScale);
}
const AZ::Vector3& ManipulatorSpaceWithLocalPosition::GetLocalPosition() const
@@ -17,6 +17,8 @@ namespace AZ
namespace AzToolsFramework
{
AZ::Transform ApplySpace(const AZ::Transform& localTransform, const AZ::Transform& space, const AZ::Vector3& nonUniformScale);
//! Handles location for manipulators which have a global space but no local transformation.
class ManipulatorSpace
{
@@ -383,8 +383,8 @@ namespace AzToolsFramework
debugDisplay.DrawLine(quadBoundVisual.m_corner1, quadBoundVisual.m_corner2);
debugDisplay.SetColor(ViewColor(manipulatorState.m_mouseOver, m_axis2Color, m_mouseOverColor).GetAsVector4());
debugDisplay.DrawLine(quadBoundVisual.m_corner4, quadBoundVisual.m_corner1);
debugDisplay.DrawLine(quadBoundVisual.m_corner2, quadBoundVisual.m_corner3);
debugDisplay.DrawLine(quadBoundVisual.m_corner1, quadBoundVisual.m_corner4);
if (manipulatorState.m_mouseOver)
{
@@ -738,15 +738,16 @@ namespace AzToolsFramework
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
AZStd::unique_ptr<ManipulatorViewQuad> CreateManipulatorViewQuad(
const PlanarManipulator& planarManipulator,
const AZ::Vector3& axis1,
const AZ::Vector3& axis2,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const AZ::Vector3& offset,
const float size)
{
AZStd::unique_ptr<ManipulatorViewQuad> viewQuad = AZStd::make_unique<ManipulatorViewQuad>();
viewQuad->m_axis1 = planarManipulator.GetAxis1();
viewQuad->m_axis2 = planarManipulator.GetAxis2();
viewQuad->m_axis1 = axis1;
viewQuad->m_axis2 = axis2;
viewQuad->m_size = size;
viewQuad->m_offset = offset;
viewQuad->m_axis1Color = axis1Color;
@@ -382,7 +382,8 @@ namespace AzToolsFramework
// Helpers to create various manipulator views.
AZStd::unique_ptr<ManipulatorViewQuad> CreateManipulatorViewQuad(
const PlanarManipulator& planarManipulator,
const AZ::Vector3& axis1,
const AZ::Vector3& axis2,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const AZ::Vector3& offset,
@@ -145,8 +145,8 @@ namespace AzToolsFramework
{
view->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
ManipulatorState{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() },
debugDisplay, cameraState, mouseInteraction);
}
}
@@ -202,8 +202,8 @@ namespace AzToolsFramework
{
view->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
ManipulatorState{ ApplySpace(GetLocalTransform()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() },
debugDisplay, cameraState, mouseInteraction);
}
}
@@ -90,8 +90,8 @@ namespace AzToolsFramework
{
view->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
}
}
@@ -94,8 +94,8 @@ namespace AzToolsFramework
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ TransformUniformScale(GetSpace()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), AZ::Vector3::CreateZero(), MouseOver() },
debugDisplay, cameraState, mouseInteraction);
}
}
@@ -166,8 +166,8 @@ namespace AzToolsFramework
{
m_manipulatorView->Draw(
GetManipulatorManagerId(), managerState, GetManipulatorId(),
{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay, cameraState,
mouseInteraction);
ManipulatorState{ TransformUniformScale(GetSpace()), GetNonUniformScale(), GetLocalPosition(), MouseOver() }, debugDisplay,
cameraState, mouseInteraction);
}
void SurfaceManipulator::InvalidateImpl()
@@ -19,6 +19,21 @@ namespace AzToolsFramework
static const AZ::Color LinearManipulatorZAxisColor = AZ::Color(0.0f, 0.0f, 1.0f, 1.0f);
static const AZ::Color SurfaceManipulatorColor = AZ::Color(1.0f, 1.0f, 0.0f, 0.5f);
static TranslationManipulatorsViewCreateInfo DefaultTranslationManipulatorViewCreateInfo()
{
TranslationManipulatorsViewCreateInfo createInfo;
createInfo.axis1Color = LinearManipulatorXAxisColor;
createInfo.axis2Color = LinearManipulatorYAxisColor;
createInfo.axis3Color = LinearManipulatorZAxisColor;
createInfo.surfaceColor = SurfaceManipulatorColor;
createInfo.linearAxisLength = LinearManipulatorAxisLength();
createInfo.linearConeLength = LinearManipulatorConeLength();
createInfo.linearConeRadius = LinearManipulatorConeRadius();
createInfo.planarAxisLength = PlanarManipulatorAxisLength();
createInfo.surfaceRadius = SurfaceManipulatorRadius();
return createInfo;
}
TranslationManipulators::TranslationManipulators(
const Dimensions dimensions, const AZ::Transform& worldFromLocal, const AZ::Vector3& nonUniformScale)
: m_dimensions(dimensions)
@@ -231,17 +246,36 @@ namespace AzToolsFramework
}
}
void TranslationManipulators::ConfigureView2d(const TranslationManipulatorsViewCreateInfo& translationManipulatorViewCreateInfo)
{
ConfigureLinearView(
translationManipulatorViewCreateInfo.linearAxisLength, translationManipulatorViewCreateInfo.linearConeLength,
translationManipulatorViewCreateInfo.linearConeRadius, translationManipulatorViewCreateInfo.axis1Color,
translationManipulatorViewCreateInfo.axis2Color, translationManipulatorViewCreateInfo.axis3Color);
ConfigurePlanarView(
translationManipulatorViewCreateInfo.planarAxisLength, translationManipulatorViewCreateInfo.linearAxisLength,
translationManipulatorViewCreateInfo.linearConeLength, translationManipulatorViewCreateInfo.axis1Color,
translationManipulatorViewCreateInfo.axis2Color, translationManipulatorViewCreateInfo.axis3Color);
}
void TranslationManipulators::ConfigureView3d(const TranslationManipulatorsViewCreateInfo& translationManipulatorViewCreateInfo)
{
ConfigureView2d(translationManipulatorViewCreateInfo);
ConfigureSurfaceView(translationManipulatorViewCreateInfo.surfaceRadius, translationManipulatorViewCreateInfo.surfaceColor);
}
void TranslationManipulators::ConfigureLinearView(
const float axisLength,
const float coneLength,
const float coneRadius,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const AZ::Color& axis3Color /*= AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)*/)
{
const AZ::Color axesColor[] = { axis1Color, axis2Color, axis3Color };
const auto configureLinearView =
[lineBoundWidth = m_lineBoundWidth, coneLength = LinearManipulatorConeLength(), axisLength,
coneRadius = LinearManipulatorConeRadius()](LinearManipulator* linearManipulator, const AZ::Color& color)
const auto configureLinearView = [lineBoundWidth = m_lineBoundWidth, coneLength, axisLength,
coneRadius](LinearManipulator* linearManipulator, const AZ::Color& color)
{
const auto lineLength = axisLength - coneLength;
@@ -259,25 +293,21 @@ namespace AzToolsFramework
}
void TranslationManipulators::ConfigurePlanarView(
const float planeSize,
const float planarAxisLength,
const float linearAxisLength,
const float linearConeLength,
const AZ::Color& plane1Color,
const AZ::Color& plane2Color /*= AZ::Color(0.0f, 1.0f, 0.0f, 0.5f)*/,
const AZ::Color& plane3Color /*= AZ::Color(0.0f, 0.0f, 1.0f, 0.5f)*/)
{
const AZ::Color planesColor[] = { plane1Color, plane2Color, plane3Color };
const float linearAxisLength = LinearManipulatorAxisLength();
const float linearConeLength = LinearManipulatorConeLength();
for (size_t manipulatorIndex = 0; manipulatorIndex < m_planarManipulators.size(); ++manipulatorIndex)
{
const auto& planarManipulator = *m_planarManipulators[manipulatorIndex];
const AZStd::shared_ptr<ManipulatorViewQuad> manipulatorView = CreateManipulatorViewQuad(
*m_planarManipulators[manipulatorIndex], planesColor[manipulatorIndex], planesColor[(manipulatorIndex + 1) % 3],
(planarManipulator.GetAxis1() + planarManipulator.GetAxis2()) *
(((linearAxisLength - linearConeLength) * 0.5f) - (planeSize * 0.5f)),
planeSize);
m_planarManipulators[manipulatorIndex]->SetViews(ManipulatorViews{ manipulatorView });
m_planarManipulators[manipulatorIndex]->SetViews(ManipulatorViews{ CreateManipulatorViewQuadForPlanarTranslationManipulator(
planarManipulator.GetAxis1(), planarManipulator.GetAxis2(), planesColor[manipulatorIndex],
planesColor[(manipulatorIndex + 1) % 3], linearAxisLength, linearConeLength, planarAxisLength) });
}
}
@@ -325,19 +355,25 @@ namespace AzToolsFramework
void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators)
{
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Vector3::CreateAxisZ());
translationManipulators->ConfigurePlanarView(
PlanarManipulatorAxisLength(), LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureLinearView(
LinearManipulatorAxisLength(), LinearManipulatorXAxisColor, LinearManipulatorYAxisColor, LinearManipulatorZAxisColor);
translationManipulators->ConfigureSurfaceView(SurfaceManipulatorRadius(), SurfaceManipulatorColor);
translationManipulators->ConfigureView3d(DefaultTranslationManipulatorViewCreateInfo());
}
void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators)
{
translationManipulators->SetAxes(AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY());
translationManipulators->ConfigurePlanarView(
PlanarManipulatorAxisLength(), LinearManipulatorXAxisColor, LinearManipulatorYAxisColor);
translationManipulators->ConfigureLinearView(
LinearManipulatorAxisLength(), LinearManipulatorXAxisColor, LinearManipulatorYAxisColor);
translationManipulators->ConfigureView2d(DefaultTranslationManipulatorViewCreateInfo());
}
AZStd::shared_ptr<ManipulatorViewQuad> CreateManipulatorViewQuadForPlanarTranslationManipulator(
const AZ::Vector3& axis1,
const AZ::Vector3& axis2,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const float linearAxisLength,
const float linearConeLength,
const float planarAxisLength)
{
const AZ::Vector3 offset = (axis1 + axis2) * (((linearAxisLength - linearConeLength) * 0.5f) - (planarAxisLength * 0.5f));
return CreateManipulatorViewQuad(axis1, axis2, axis1Color, axis2Color, offset, planarAxisLength);
}
} // namespace AzToolsFramework
@@ -15,6 +15,20 @@
namespace AzToolsFramework
{
//! Parameters to configure the appearance of the TranslationManipulators view(s).
struct TranslationManipulatorsViewCreateInfo
{
float linearAxisLength;
float linearConeLength;
float linearConeRadius;
float planarAxisLength;
float surfaceRadius;
AZ::Color axis1Color;
AZ::Color axis2Color;
AZ::Color axis3Color;
AZ::Color surfaceColor;
};
//! TranslationManipulators is an aggregation of 3 linear manipulators, 3 planar manipulators
//! and one surface manipulator who share the same transform.
class TranslationManipulators : public Manipulators
@@ -23,6 +37,9 @@ namespace AzToolsFramework
AZ_RTTI(TranslationManipulators, "{D5E49EA2-30E0-42BC-A51D-6A7F87818260}")
AZ_CLASS_ALLOCATOR(TranslationManipulators, AZ::SystemAllocator, 0)
TranslationManipulators(TranslationManipulators&&) = delete;
TranslationManipulators& operator=(TranslationManipulators&&) = delete;
//! How many dimensions does this translation manipulator have.
enum class Dimensions
{
@@ -52,26 +69,31 @@ namespace AzToolsFramework
void SetAxes(const AZ::Vector3& axis1, const AZ::Vector3& axis2, const AZ::Vector3& axis3 = AZ::Vector3::CreateAxisZ());
void ConfigureView2d(const TranslationManipulatorsViewCreateInfo& translationManipulatorViewCreateInfo);
void ConfigureView3d(const TranslationManipulatorsViewCreateInfo& translationManipulatorViewCreateInfo);
//! Sets the bound width to use for the line/axis of a linear manipulator.
void SetLineBoundWidth(float lineBoundWidth);
private:
void ConfigurePlanarView(
float planeSize,
float linearAxisLength,
float linearConeLength,
const AZ::Color& plane1Color,
const AZ::Color& plane2Color = AZ::Color(0.0f, 1.0f, 0.0f, 0.5f),
const AZ::Color& plane3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
void ConfigureLinearView(
float axisLength,
float coneLength,
float coneRadius,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
const AZ::Color& axis3Color = AZ::Color(0.0f, 0.0f, 1.0f, 0.5f));
void ConfigureSurfaceView(float radius, const AZ::Color& color);
//! Sets the bound width to use for the line/axis of a linear manipulator.
void SetLineBoundWidth(float lineBoundWidth);
private:
AZ_DISABLE_COPY_MOVE(TranslationManipulators)
// Manipulators
void ProcessManipulators(const AZStd::function<void(BaseManipulator*)>&) override;
@@ -131,4 +153,12 @@ namespace AzToolsFramework
void ConfigureTranslationManipulatorAppearance3d(TranslationManipulators* translationManipulators);
void ConfigureTranslationManipulatorAppearance2d(TranslationManipulators* translationManipulators);
AZStd::shared_ptr<ManipulatorViewQuad> CreateManipulatorViewQuadForPlanarTranslationManipulator(
const AZ::Vector3& axis1,
const AZ::Vector3& axis2,
const AZ::Color& axis1Color,
const AZ::Color& axis2Color,
float linearAxisLength,
float linearConeLength,
float planarAxisLength);
} // namespace AzToolsFramework
@@ -8,10 +8,12 @@
#include <API/ToolsApplicationAPI.h>
#include <AzCore/Component/ComponentApplicationBus.h>
#include <AzCore/Component/Entity.h>
#include <AzCore/RTTI/BehaviorContext.h>
#include <AzToolsFramework/ToolsComponents/EditorLockComponent.h>
#include <AzToolsFramework/ToolsComponents/EditorVisibilityComponent.h>
#include <Prefab/PrefabSystemComponentInterface.h>
#include <Prefab/PrefabSystemScriptingHandler.h>
#include <AzCore/Component/Entity.h>
#include <Prefab/EditorPrefabComponent.h>
#include <ToolsComponents/TransformComponent.h>
@@ -72,6 +74,9 @@ namespace AzToolsFramework::Prefab
entities, commonRoot, &topLevelEntities);
auto containerEntity = AZStd::make_unique<AZ::Entity>();
containerEntity->CreateComponent<Components::TransformComponent>();
containerEntity->CreateComponent<Components::EditorLockComponent>();
containerEntity->CreateComponent<Components::EditorVisibilityComponent>();
containerEntity->CreateComponent<Prefab::EditorPrefabComponent>();
for (AZ::Entity* entity : topLevelEntities)
@@ -565,9 +565,7 @@ namespace AzToolsFramework
EditorRequestBus::BroadcastResult(position, &EditorRequestBus::Events::GetWorldPositionAtViewportCenter);
}
// Instantiating from context menu always puts the instance at the root level
auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabFilePath, parentId, position);
if (!createPrefabOutcome.IsSuccess())
{
WarnUserOfError("Prefab Instantiation Error",createPrefabOutcome.GetError());
@@ -594,15 +592,13 @@ namespace AzToolsFramework
}
else
{
// otherwise return since it needs to be inside an authored prefab
return;
EditorRequestBus::BroadcastResult(position, &EditorRequestBus::Events::GetWorldPositionAtViewportCenter);
}
// Instantiating from context menu always puts the instance at the root level
auto createPrefabOutcome = s_prefabPublicInterface->InstantiatePrefab(prefabAssetPath, parentId, position);
if (!createPrefabOutcome.IsSuccess())
{
WarnUserOfError("Prefab Instantiation Error", createPrefabOutcome.GetError());
WarnUserOfError("Procedural Prefab Instantiation Error", createPrefabOutcome.GetError());
}
}
}
@@ -45,6 +45,7 @@ AZ_POP_DISABLE_WARNING
#include <AzToolsFramework/AssetBrowser/EBusFindAssetTypeByName.h>
#include <AzToolsFramework/ComponentMode/ComponentModeDelegate.h>
#include <AzToolsFramework/Entity/EditorEntityHelpers.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityInterface.h>
#include <AzToolsFramework/Prefab/PrefabFocusPublicInterface.h>
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <AzToolsFramework/Slice/SliceDataFlagsCommand.h>
@@ -497,6 +498,9 @@ namespace AzToolsFramework
m_prefabPublicInterface = AZ::Interface<Prefab::PrefabPublicInterface>::Get();
AZ_Assert(m_prefabPublicInterface != nullptr, "EntityPropertyEditor requires a PrefabPublicInterface instance on Initialize.");
m_readOnlyEntityPublicInterface = AZ::Interface<ReadOnlyEntityPublicInterface>::Get();
AZ_Assert(m_readOnlyEntityPublicInterface != nullptr, "EntityPropertyEditor requires a ReadOnlyEntityPublicInterface instance on Initialize.");
setObjectName("EntityPropertyEditor");
setAcceptDrops(true);
@@ -535,10 +539,6 @@ namespace AzToolsFramework
model->setItem(row, 0, m_comboItems[row]);
}
m_gui->m_statusComboBox->setModel(model);
m_gui->m_statusComboBox->setStyleSheet("QComboBox {border: 0px; border-radius:3px; background-color:#555555; color:white}"
"QComboBox:on {background-color:#e9e9e9; color:black; border:0px}"
"QComboBox::down-arrow:on {image: url(:/stylesheet/img/dropdowns/black_down_arrow.png)}"
"QComboBox::drop-down {border-radius: 3p}");
AzQtComponents::ComboBox::addCustomCheckStateStyle(m_gui->m_statusComboBox);
EnableEditor(true);
m_sceneIsNew = true;
@@ -565,6 +565,12 @@ namespace AzToolsFramework
AZ::EntitySystemBus::Handler::BusConnect();
EntityPropertyEditorRequestBus::Handler::BusConnect();
EditorWindowUIRequestBus::Handler::BusConnect();
AzFramework::EntityContextId editorEntityContextId = AzFramework::EntityContextId::CreateNull();
EditorEntityContextRequestBus::BroadcastResult(
editorEntityContextId, &EditorEntityContextRequests::GetEditorEntityContextId);
ReadOnlyEntityPublicNotificationBus::Handler::BusConnect(editorEntityContextId);
m_spacer = nullptr;
m_emptyIcon = QIcon();
@@ -614,6 +620,7 @@ namespace AzToolsFramework
{
qApp->removeEventFilter(this);
ReadOnlyEntityPublicNotificationBus::Handler::BusDisconnect();
EditorWindowUIRequestBus::Handler::BusDisconnect();
EntityPropertyEditorRequestBus::Handler::BusDisconnect();
ToolsApplicationEvents::Bus::Handler::BusDisconnect();
@@ -973,7 +980,7 @@ namespace AzToolsFramework
m_gui->m_entityDetailsLabel->setVisible(false);
// If we're in edit mode, make the name field editable.
m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled());
m_gui->m_entityNameEditor->setReadOnly(!m_gui->m_componentListContents->isEnabled() || m_selectionContainsReadOnlyEntity);
// get the name of the entity.
auto entity = GetSelectedEntityById(entityId);
@@ -1062,6 +1069,12 @@ namespace AzToolsFramework
bool EntityPropertyEditor::CanAddComponentsToSelection(const SelectionEntityTypeInfo& selectionEntityTypeInfo) const
{
if (m_selectionContainsReadOnlyEntity)
{
// Can't add components if there is a read only entity in the selection
return false;
}
if (selectionEntityTypeInfo == SelectionEntityTypeInfo::Mixed ||
selectionEntityTypeInfo == SelectionEntityTypeInfo::None)
{
@@ -1126,6 +1139,17 @@ namespace AzToolsFramework
m_selectedEntityIds.clear();
GetSelectedEntities(m_selectedEntityIds);
// Check if any of the selected entities are marked as read only
m_selectionContainsReadOnlyEntity = false;
for (const auto& entityId : m_selectedEntityIds)
{
if (m_readOnlyEntityPublicInterface->IsReadOnly(entityId))
{
m_selectionContainsReadOnlyEntity = true;
break;
}
}
SourceControlFileInfo scFileInfo;
ToolsApplicationRequests::Bus::BroadcastResult(scFileInfo, &ToolsApplicationRequests::GetSceneSourceControlInfo);
@@ -1681,6 +1705,12 @@ namespace AzToolsFramework
componentEditor->UpdateExpandability();
componentEditor->InvalidateAll(!componentInFilter ? m_filterString.c_str() : nullptr);
// If we are in read only mode, then show the components as disabled
if (m_selectionContainsReadOnlyEntity)
{
componentEditor->mockDisabledState(true);
}
if (!componentEditor->GetPropertyEditor()->HasFilteredOutNodes() || componentEditor->GetPropertyEditor()->HasVisibleNodes())
{
for (AZ::Component* componentInstance : componentInstances)
@@ -3077,6 +3107,7 @@ namespace AzToolsFramework
}
}
m_gui->m_statusComboBox->setDisabled(m_selectionContainsReadOnlyEntity);
m_gui->m_statusComboBox->setVisible(!m_isSystemEntityEditor && !m_isLevelEntityEditor);
m_gui->m_statusComboBox->style()->unpolish(m_gui->m_statusComboBox);
m_gui->m_statusComboBox->style()->polish(m_gui->m_statusComboBox);
@@ -3304,7 +3335,8 @@ namespace AzToolsFramework
const auto& componentsToEdit = GetSelectedComponents();
const bool hasComponents = !m_selectedEntityIds.empty() && !componentsToEdit.empty();
const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit);
// Don't allow components to be removed/cut/enabled/disabled if read only
const bool allowRemove = hasComponents && AreComponentsRemovable(componentsToEdit) && !m_selectionContainsReadOnlyEntity;
const bool allowCopy = hasComponents && AreComponentsCopyable(componentsToEdit);
m_actionToDeleteComponents->setEnabled(allowRemove);
@@ -3366,6 +3398,12 @@ namespace AzToolsFramework
return false;
}
if (m_selectionContainsReadOnlyEntity)
{
// Can't paste components if there is a read only entity in the selection
return false;
}
// Grab component data from clipboard, if exists
const QMimeData* mimeData = ComponentMimeData::GetComponentMimeDataFromClipboard();
@@ -5727,6 +5765,14 @@ namespace AzToolsFramework
SaveComponentEditorState();
}
void EntityPropertyEditor::OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, [[maybe_unused]] bool readOnly)
{
if (IsEntitySelected(entityId))
{
UpdateContents();
}
}
void EntityPropertyEditor::OnEditorModeActivated(
[[maybe_unused]] const AzToolsFramework::ViewportEditorModesInterface& editorModeState, AzToolsFramework::ViewportEditorMode mode)
{
@@ -29,6 +29,7 @@
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
#include <AzToolsFramework/ComponentMode/EditorComponentModeBus.h>
#include <AzToolsFramework/Entity/EditorEntityContextBus.h>
#include <AzToolsFramework/Entity/ReadOnly/ReadOnlyEntityBus.h>
#include <AzToolsFramework/ToolsComponents/ComponentMimeData.h>
#include <AzToolsFramework/ToolsComponents/EditorInspectorComponentBus.h>
#include <AzQtComponents/Components/O3DEStylesheet.h>
@@ -62,6 +63,7 @@ namespace AzToolsFramework
class ComponentPaletteWidget;
class ComponentModeCollectionInterface;
struct SourceControlFileInfo;
class ReadOnlyEntityPublicInterface;
namespace AssetBrowser
{
@@ -116,6 +118,7 @@ namespace AzToolsFramework
, public AZ::EntitySystemBus::Handler
, public AZ::TickBus::Handler
, private EditorWindowUIRequestBus::Handler
, private ReadOnlyEntityPublicNotificationBus::Handler
{
Q_OBJECT;
public:
@@ -253,6 +256,9 @@ namespace AzToolsFramework
// EditorWindowRequestBus overrides
void SetEditorUiEnabled(bool enable) override;
// ReadOnlyEntityPublicNotificationBus overrides ...
void OnReadOnlyEntityStatusChanged(const AZ::EntityId& entityId, bool readOnly) override;
bool IsEntitySelected(const AZ::EntityId& id) const;
bool IsSingleEntitySelected(const AZ::EntityId& id) const;
@@ -623,6 +629,9 @@ namespace AzToolsFramework
Prefab::PrefabPublicInterface* m_prefabPublicInterface = nullptr;
bool m_prefabsAreEnabled = false;
ReadOnlyEntityPublicInterface* m_readOnlyEntityPublicInterface = nullptr;
bool m_selectionContainsReadOnlyEntity = false;
// Reordering row widgets within the RPE.
static constexpr float MoveFadeSeconds = 0.5f;
@@ -191,7 +191,7 @@
</size>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgb(51, 51, 51)</string>
<string notr="true">QWidget#m_darkBox { background-color:rgb(51, 51, 51) }</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_4">
<property name="spacing">
@@ -444,6 +444,9 @@
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="styleSheet">
<string notr="true">background-color:rgb(51, 51, 51)</string>
</property>
</widget>
</item>
<item>
@@ -15,12 +15,14 @@
#include <AzCore/Slice/SliceComponent.h>
#include <AzCore/std/containers/unordered_map.h>
#include <AzCore/std/parallel/binary_semaphore.h>
#include <AzCore/std/smart_ptr/make_shared.h>
#include <AzCore/std/smart_ptr/unique_ptr.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzCore/UserSettings/UserSettingsComponent.h>
#include <AzTest/AzTest.h>
#include <AZTestShared/Math/MathTestHelpers.h>
#include <AZTestShared/Utils/Utils.h>
#include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerInterface.h>
#include <AzToolsFramework/API/ViewportEditorModeTrackerNotificationBus.h>
@@ -235,6 +237,13 @@ namespace UnitTest
return toolsApp;
}
//! It is possible to override this in classes deriving from ToolsApplicationFixture to provide alternate
//! implementations of the DebugDisplayRequests interface (e.g. TestDebugDisplayRequests).
virtual AZStd::shared_ptr<AzFramework::DebugDisplayRequests> CreateDebugDisplayRequests()
{
return AZStd::make_shared<NullDebugDisplayRequests>();
}
protected:
TestEditorActions m_editorActions;
ToolsApplicationMessageHandler m_messageHandler; // used to suppress trace messages in test output
@@ -7,12 +7,16 @@
*/
#include <AzCore/Serialization/SerializeContext.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/DirectManipulatorViewportInteraction.h>
#include <AzManipulatorTestFramework/ImmediateModeActionDispatcher.h>
#include <AzTest/AzTest.h>
#include <AzToolsFramework/Application/ToolsApplication.h>
#include <AzToolsFramework/Manipulators/RotationManipulators.h>
#include <AzToolsFramework/Manipulators/ManipulatorManager.h>
#include <AzToolsFramework/Manipulators/ManipulatorView.h>
#include <AzCore/UnitTest/TestTypes.h>
#include <AzToolsFramework/Manipulators/RotationManipulators.h>
#include <AzToolsFramework/Manipulators/TranslationManipulators.h>
#include <AzToolsFramework/UnitTest/AzToolsFrameworkTestHelpers.h>
#include <AzToolsFramework/UnitTest/ToolsTestApplication.h>
#include <AzToolsFramework/ViewportSelection/EditorSelectionUtil.h>
@@ -21,8 +25,7 @@ namespace UnitTest
{
using namespace AzToolsFramework;
class ManipulatorViewTest
: public AllocatorsTestFixture
class ManipulatorViewTest : public AllocatorsTestFixture
{
AZStd::unique_ptr<AZ::SerializeContext> m_serializeContext;
@@ -32,7 +35,7 @@ namespace UnitTest
m_serializeContext = AZStd::make_unique<AZ::SerializeContext>();
m_app.Start(AzFramework::Application::Descriptor());
// Without this, the user settings component would attempt to save on finalize/shutdown. Since the file is
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// shared across the whole engine, if multiple tests are run in parallel, the saving could cause a crash
// in the unit tests.
AZ::UserSettingsComponentRequestBus::Broadcast(&AZ::UserSettingsComponentRequests::DisableSaveOnFinalize);
}
@@ -51,12 +54,9 @@ namespace UnitTest
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Given
const AZ::Transform orientation =
AZ::Transform::CreateFromQuaternion(
AZ::Quaternion::CreateFromAxisAngle(
AZ::Vector3::CreateAxisX(), AZ::DegToRad(-90.0f)));
AZ::Transform::CreateFromQuaternion(AZ::Quaternion::CreateRotationX(AZ::DegToRad(-90.0f)));
const AZ::Transform translation =
AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 0.0f, 10.0f));
const AZ::Transform translation = AZ::Transform::CreateTranslation(AZ::Vector3(5.0f, 0.0f, 10.0f));
const AZ::Transform manipulatorSpace = translation * orientation;
// create a rotation manipulator in an arbitrary space
@@ -67,8 +67,7 @@ namespace UnitTest
// When
const AZ::Vector3 worldCameraPosition = AZ::Vector3(5.0f, -10.0f, 10.0f);
// transform the view direction to the space of the manipulator (space + local transform)
const AZ::Vector3 viewDirection =
CalculateViewDirection(rotationManipulators, worldCameraPosition);
const AZ::Vector3 viewDirection = CalculateViewDirection(rotationManipulators, worldCameraPosition);
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -84,8 +83,7 @@ namespace UnitTest
cameraState.m_position = AZ::Vector3::CreateAxisY(20.0f);
cameraState.m_forward = -AZ::Vector3::CreateAxisY();
const float scale =
AzToolsFramework::CalculateScreenToWorldMultiplier(AZ::Vector3::CreateZero(), cameraState);
const float scale = AzToolsFramework::CalculateScreenToWorldMultiplier(AZ::Vector3::CreateZero(), cameraState);
EXPECT_NEAR(scale, 2.0f, std::numeric_limits<float>::epsilon());
}
@@ -96,9 +94,57 @@ namespace UnitTest
cameraState.m_position = AZ::Vector3::CreateAxisY(20.0f);
cameraState.m_forward = -AZ::Vector3::CreateAxisY();
const float scale =
AzToolsFramework::CalculateScreenToWorldMultiplier(AZ::Vector3::CreateAxisX(-10.0f), cameraState);
const float scale = AzToolsFramework::CalculateScreenToWorldMultiplier(AZ::Vector3::CreateAxisX(-10.0f), cameraState);
EXPECT_NEAR(scale, 2.0f, std::numeric_limits<float>::epsilon());
}
TEST_F(ManipulatorViewTest, ManipulatorViewQuadDrawsAtCorrectPositionWhenManipulatorSpaceIsScaledUniformlyAndNonUniformly)
{
// Given
// simulate a custom manipulator space (e.g. entity transform) and a local offset within that space (e.g. spline vertex position)
const AZ::Transform space =
AZ::Transform::CreateTranslation(AZ::Vector3(2.0f, -3.0f, -4.0f)) * AZ::Transform::CreateUniformScale(2.0f);
const AZ::Vector3 localPosition = AZ::Vector3(2.0f, -2.0f, 0.0f);
const AZ::Vector3 nonUniformScale = AZ::Vector3(2.0f, 3.0f, 4.0f);
const AZ::Transform combinedTransform =
AzToolsFramework::ApplySpace(AZ::Transform::CreateTranslation(localPosition), space, nonUniformScale);
// create a manipulator state based on the space and local position
AzToolsFramework::ManipulatorState manipulatorState{};
manipulatorState.m_worldFromLocal = combinedTransform;
manipulatorState.m_nonUniformScale = nonUniformScale;
// note: This is zero as the localPosition is already encoded in the combinedTransform
manipulatorState.m_localPosition = AZ::Vector3::CreateZero();
// camera (go to position format) - 10.00, -15.00, 6.00, -90.00, 0.00
const AzFramework::CameraState cameraState = AzFramework::CreateDefaultCamera(
AZ::Transform::CreateFromMatrix3x3AndTranslation(
AZ::Matrix3x3::CreateRotationX(AZ::DegToRad(-90.0f)), AZ::Vector3(10.0f, -15.0f, 6.0f)),
AZ::Vector2(1280, 720));
// test debug display instance to record vertices that were output
auto testDebugDisplayRequests = AZStd::make_shared<TestDebugDisplayRequests>();
auto planarTranslationViewQuad = CreateManipulatorViewQuadForPlanarTranslationManipulator(
AZ::Vector3::CreateAxisX(), AZ::Vector3::CreateAxisY(), AZ::Color::CreateZero(), AZ::Color::CreateZero(), 2.2f, 0.2f, 1.0f);
// When
// draw the quad as it would be for a manipulator
planarTranslationViewQuad->Draw(
AzToolsFramework::ManipulatorManagerId(1), AzToolsFramework::ManipulatorManagerState{ false },
AzToolsFramework::ManipulatorId(1), manipulatorState, *testDebugDisplayRequests, cameraState,
AzToolsFramework::ViewportInteraction::MouseInteraction{});
const AZStd::vector<AZ::Vector3> expectedDisplayPositions = {
AZ::Vector3(10.5f, -13.5f, -4.0f), AZ::Vector3(11.5f, -13.5f, -4.0f), AZ::Vector3(10.5f, -14.5f, -4.0f),
AZ::Vector3(11.5f, -14.5f, -4.0f), AZ::Vector3(10.5f, -13.5f, -4.0f), AZ::Vector3(10.5f, -14.5f, -4.0f),
AZ::Vector3(11.5f, -14.5f, -4.0f), AZ::Vector3(11.5f, -13.5f, -4.0f)
};
// Then
const auto points = testDebugDisplayRequests->GetPoints();
// quad vertices appear in the expected position (not offset or scaled incorrectly by space scale)
using ::testing::UnorderedPointwise;
EXPECT_THAT(points, UnorderedPointwise(ContainerIsClose(), expectedDisplayPositions));
}
} // namespace UnitTest
@@ -299,7 +299,7 @@ void android_main(android_app* appState)
{
// Adding a start up banner so you can see when the game is starting up in amongst the logcat spam
LOGI("****************************************************************");
LOGI("* Amazon Lumberyard - Launching Game... *");
LOGI("* Launching Game... *");
LOGI("****************************************************************");
// setup the system command handler which are guaranteed to be called on the same
@@ -294,7 +294,7 @@ namespace O3DE::ProjectManager
}
else if (numChangedDependencies > 1)
{
notification += tr("%1 Gem %2").arg(QString(numChangedDependencies), tr("dependencies"));
notification += tr("%1 Gem %2").arg(numChangedDependencies).arg(tr("dependencies"));
}
notification += (added ? tr(" activated") : tr(" deactivated"));
@@ -28,6 +28,7 @@
#include <QFileInfo>
#include <QDesktopServices>
#include <QMessageBox>
#include <QMouseEvent>
namespace O3DE::ProjectManager
{
@@ -109,11 +110,11 @@ namespace O3DE::ProjectManager
vLayout->addWidget(m_progressBar);
}
void LabelButton::mousePressEvent([[maybe_unused]] QMouseEvent* event)
void LabelButton::mousePressEvent(QMouseEvent* event)
{
if(m_enabled)
{
emit triggered();
emit triggered(event);
}
}
@@ -201,52 +202,64 @@ namespace O3DE::ProjectManager
projectNameLabel->setToolTip(m_projectInfo.m_path);
hLayout->addWidget(projectNameLabel);
QMenu* menu = new QMenu(this);
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
menu->addAction(tr("Configure Gems..."), this, [this]() { emit EditProjectGems(m_projectInfo.m_path); });
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
menu->addAction(tr("Open CMake GUI..."), this, [this]() { emit OpenCMakeGUI(m_projectInfo); });
menu->addSeparator();
menu->addAction(tr("Open Project folder..."), this, [this]()
{
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
});
#if AZ_TRAIT_PROJECT_MANAGER_CREATE_DESKTOP_SHORTCUT
menu->addAction(tr("Create Editor desktop shortcut..."), this, [this]()
{
AZ::IO::FixedMaxPath editorExecutablePath = ProjectUtils::GetEditorExecutablePath(m_projectInfo.m_path.toUtf8().constData());
const QString shortcutName = QString("%1 Editor").arg(m_projectInfo.m_displayName);
const QString arg = QString("--regset=\"/Amazon/AzCore/Bootstrap/project_path=%1\"").arg(m_projectInfo.m_path);
auto result = ProjectUtils::CreateDesktopShortcut(shortcutName, editorExecutablePath.c_str(), { arg });
if(result.IsSuccess())
{
QMessageBox::information(this, tr("Desktop Shortcut Created"), result.GetValue());
}
else
{
QMessageBox::critical(this, tr("Failed to create shortcut"), result.GetError());
}
});
#endif // AZ_TRAIT_PROJECT_MANAGER_CREATE_DESKTOP_SHORTCUT
menu->addSeparator();
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
menu->addSeparator();
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
m_projectMenuButton = new QPushButton(this);
m_projectMenuButton->setObjectName("projectMenuButton");
m_projectMenuButton->setMenu(menu);
m_projectMenuButton->setMenu(CreateProjectMenu());
hLayout->addWidget(m_projectMenuButton);
}
vLayout->addWidget(projectFooter);
connect(m_projectImageLabel->GetOpenEditorButton(), &QPushButton::clicked, [this](){ emit OpenProject(m_projectInfo.m_path); });
connect(m_projectImageLabel, &LabelButton::triggered, [this](QMouseEvent* event) {
if (event->button() == Qt::RightButton)
{
m_projectMenuButton->menu()->move(event->globalPos());
m_projectMenuButton->menu()->show();
}
});
}
QMenu* ProjectButton::CreateProjectMenu()
{
QMenu* menu = new QMenu(this);
menu->addAction(tr("Edit Project Settings..."), this, [this]() { emit EditProject(m_projectInfo.m_path); });
menu->addAction(tr("Configure Gems..."), this, [this]() { emit EditProjectGems(m_projectInfo.m_path); });
menu->addAction(tr("Build"), this, [this]() { emit BuildProject(m_projectInfo); });
menu->addAction(tr("Open CMake GUI..."), this, [this]() { emit OpenCMakeGUI(m_projectInfo); });
menu->addSeparator();
menu->addAction(tr("Open Project folder..."), this, [this]()
{
AzQtComponents::ShowFileOnDesktop(m_projectInfo.m_path);
});
#if AZ_TRAIT_PROJECT_MANAGER_CREATE_DESKTOP_SHORTCUT
menu->addAction(tr("Create Editor desktop shortcut..."), this, [this]()
{
AZ::IO::FixedMaxPath editorExecutablePath = ProjectUtils::GetEditorExecutablePath(m_projectInfo.m_path.toUtf8().constData());
const QString shortcutName = QString("%1 Editor").arg(m_projectInfo.m_displayName);
const QString arg = QString("--regset=\"/Amazon/AzCore/Bootstrap/project_path=%1\"").arg(m_projectInfo.m_path);
auto result = ProjectUtils::CreateDesktopShortcut(shortcutName, editorExecutablePath.c_str(), { arg });
if(result.IsSuccess())
{
QMessageBox::information(this, tr("Desktop Shortcut Created"), result.GetValue());
}
else
{
QMessageBox::critical(this, tr("Failed to create shortcut"), result.GetError());
}
});
#endif // AZ_TRAIT_PROJECT_MANAGER_CREATE_DESKTOP_SHORTCUT
menu->addSeparator();
menu->addAction(tr("Duplicate"), this, [this]() { emit CopyProject(m_projectInfo); });
menu->addSeparator();
menu->addAction(tr("Remove from O3DE"), this, [this]() { emit RemoveProject(m_projectInfo.m_path); });
menu->addAction(tr("Delete this Project"), this, [this]() { emit DeleteProject(m_projectInfo.m_path); });
return menu;
}
const ProjectInfo& ProjectButton::GetProjectInfo() const
@@ -24,6 +24,7 @@ QT_FORWARD_DECLARE_CLASS(QProgressBar)
QT_FORWARD_DECLARE_CLASS(QLayout)
QT_FORWARD_DECLARE_CLASS(QVBoxLayout)
QT_FORWARD_DECLARE_CLASS(QEvent)
QT_FORWARD_DECLARE_CLASS(QMenu)
namespace O3DE::ProjectManager
{
@@ -49,7 +50,7 @@ namespace O3DE::ProjectManager
QLayout* GetBuildOverlayLayout();
signals:
void triggered();
void triggered(QMouseEvent* event);
public slots:
void mousePressEvent(QMouseEvent* event) override;
@@ -108,6 +109,8 @@ namespace O3DE::ProjectManager
void ShowWarning(bool show, const QString& warning);
void ShowDefaultBuildButton();
QMenu* CreateProjectMenu();
ProjectInfo m_projectInfo;
LabelButton* m_projectImageLabel = nullptr;
+2 -1
View File
@@ -63,8 +63,9 @@ namespace AZ
#if defined(USE_RENDERDOC)
// If RenderDoc is requested, we need to load the library as early as possible (before device queries/factories are made)
bool enableRenderDoc = RHI::QueryCommandLineOption("enableRenderDoc");
#if defined(USE_PIX)
s_pixGpuMarkersEnabled = s_pixGpuMarkersEnabled || enableRenderDoc;
#endif
if (enableRenderDoc && AZ_TRAIT_RENDERDOC_MODULE && !s_renderDocModule)
{
s_renderDocModule = DynamicModuleHandle::Create(AZ_TRAIT_RENDERDOC_MODULE);
@@ -43,6 +43,10 @@ namespace AZ
// Rendering -> Idle
// -> Queued (Rendering will transition to Queued if a pass was queued with the PassSystem during Rendering)
//
// Any State -> Orphaned (transition to Orphaned state can be outside the jurisdiction of the pass and so can happen from any state)
// Orphaned -> Queued (When coming out of Orphaned state, pass will queue itself for build. In practice this
// (almost?) never happens as orphaned passes are re-created in most if not all cases.)
//
enum class PassState : u8
{
// Default value, you should only ever see this in the Pass constructor
@@ -92,7 +96,10 @@ namespace AZ
// |
// V
// Pass is currently rendering. Pass must be in Idle state before entering this state
Rendering
Rendering,
// Special state: Orphaned State, pass was removed from it's parent and is awaiting deletion
Orphaned
};
// This enum keeps track of what actions the pass is queued for with the pass system
@@ -147,6 +147,11 @@ namespace AZ
m_treeDepth = m_parent->m_treeDepth + 1;
m_path = ConcatPassName(m_parent->m_path, m_name);
m_flags.m_partOfHierarchy = m_parent->m_flags.m_partOfHierarchy;
if (m_state == PassState::Orphaned)
{
QueueForBuildAndInitialization();
}
}
void Pass::RemoveFromParent()
@@ -154,7 +159,7 @@ namespace AZ
AZ_RPI_PASS_ASSERT(m_parent != nullptr, "Trying to remove pass from parent but pointer to the parent pass is null.");
m_parent->RemoveChild(Ptr<Pass>(this));
m_queueState = PassQueueState::NoQueue;
m_state = PassState::Idle;
m_state = PassState::Orphaned;
}
void Pass::OnOrphan()
@@ -162,6 +167,8 @@ namespace AZ
m_parent = nullptr;
m_flags.m_partOfHierarchy = false;
m_treeDepth = 0;
m_queueState = PassQueueState::NoQueue;
m_state = PassState::Orphaned;
}
// --- Getters & Setters ---
@@ -175,7 +175,8 @@ namespace AtomToolsFramework
Base::StartCommon(systemEntity);
m_traceLogger.PrepareLogFile(GetBuildTargetName() + ".log");
const bool clearLogFile = GetSettingOrDefault("/O3DE/AtomToolsFramework/Application/ClearLogOnStart", false);
m_traceLogger.OpenLogFile(GetBuildTargetName() + ".log", clearLogFile);
AzToolsFramework::AssetDatabase::AssetDatabaseRequestsBus::Handler::BusConnect();
AzToolsFramework::AssetBrowser::AssetDatabaseLocationNotificationBus::Broadcast(
@@ -225,6 +225,36 @@ namespace AudioControls
{
pItem->setFlags(pItem->flags() & ~Qt::ItemIsDragEnabled);
}
if (compatibleType == EACEControlType::eACET_SWITCH_STATE)
{
IAudioSystemControl* pControl = pAudioSystemEditorImpl->GetControl(GetItemId(pItem));
if (pControl && !pControl->IsLocalized())
{
size_t nConnect = 0;
for (int i = 0; i < pControl->GetParent()->ChildCount(); ++i)
{
IAudioSystemControl* child = pControl->GetParent()->GetChildAt(i);
if (child && child->IsConnected())
{
++nConnect;
}
}
QTreeWidgetItem* pParentItem = GetItem(pControl->GetParent()->GetId(), pControl->GetParent()->IsLocalized());
if (pParentItem)
{
if (nConnect > 0 && nConnect == pControl->GetParent()->ChildCount())
{
pParentItem->setForeground(0, m_connectedColor);
}
else
{
pParentItem->setForeground(0, m_disconnectedColor);
}
}
}
}
}
}
@@ -15,39 +15,39 @@ namespace GraphCanvas
void AddEntryToDatabase(const AZStd::string& baseKey, const AZStd::string& name, const rapidjson::Value& it, TranslationFormat* translationFormat)
{
AZStd::string finalKey = baseKey;
if (it.IsString())
{
if (translationFormat->m_database.find(finalKey) == translationFormat->m_database.end())
auto translationDbItr = translationFormat->m_database.find(baseKey);
if (translationDbItr == translationFormat->m_database.end())
{
translationFormat->m_database[finalKey] = it.GetString();
translationFormat->m_database[baseKey] = it.GetString();
}
else
{
AZStd::string existingValue = translationFormat->m_database[finalKey.c_str()];
const AZStd::string& existingValue = translationDbItr->second;
// There is a name collision
AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", finalKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString());
const AZStd::string error = AZStd::string::format("Unable to store key: %s with value: %s because that key already exists with value: %s (proposed: %s)", baseKey.c_str(), it.GetString(), existingValue.c_str(), it.GetString());
AZ_Error("TranslationSerializer", false, error.c_str());
}
}
else if (it.IsObject())
{
AZStd::string finalKey = baseKey;
if (!name.empty())
{
finalKey.append(".");
finalKey.append(name);
}
AZStd::string itemKey = finalKey;
AZStd::string itemKey;
for (auto objIt = it.MemberBegin(); objIt != it.MemberEnd(); ++objIt)
{
itemKey = finalKey;
itemKey.append(".");
itemKey.append(objIt->name.GetString());
AddEntryToDatabase(itemKey, name, objIt->value, translationFormat);
itemKey = finalKey;
}
}
@@ -60,18 +60,21 @@ namespace GraphCanvas
key.append(name);
}
AZStd::string itemKey = key;
AZStd::string itemKey;
const rapidjson::Value& array = it;
for (rapidjson::SizeType i = 0; i < array.Size(); ++i)
{
itemKey = key;
// if there is a "base" member within the object, then use it, otherwise use the index
if (array[i].IsObject())
const auto& element = array[i];
if (element.IsObject())
{
if (array[i].HasMember(Schema::Field::key))
rapidjson::Value::ConstMemberIterator innerKeyItr = element.FindMember(Schema::Field::key);
if (innerKeyItr != element.MemberEnd())
{
AZStd::string innerKey = array[i].FindMember(Schema::Field::key)->value.GetString();
itemKey.append(AZStd::string::format(".%s", innerKey.c_str()));
itemKey.append(AZStd::string::format(".%s", innerKeyItr->value.GetString()));
}
else
{
@@ -79,9 +82,7 @@ namespace GraphCanvas
}
}
AddEntryToDatabase(itemKey, "", array[i], translationFormat);
itemKey = key;
AddEntryToDatabase(itemKey, "", element, translationFormat);
}
}
}
@@ -114,42 +115,32 @@ namespace GraphCanvas
{
const rapidjson::Value::ConstMemberIterator entries = inputValue.FindMember(Schema::Field::entries);
AZStd::string keyStr;
AZStd::string contextStr;
AZStd::string variantStr;
AZStd::string baseKey;
rapidjson::SizeType entryCount = entries->value.Size();
for (rapidjson::SizeType i = 0; i < entryCount; ++i)
{
const rapidjson::Value& entry = entries->value[i];
AZStd::string keyStr;
rapidjson::Value::ConstMemberIterator keyValue;
if (entry.HasMember(Schema::Field::key))
{
keyValue = entry.FindMember(Schema::Field::key);
keyStr = keyValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator keyItr = entry.FindMember(Schema::Field::key);
keyStr = keyItr != entry.MemberEnd() ? keyItr->value.GetString() : "";
AZStd::string contextStr;
rapidjson::Value::ConstMemberIterator contextValue;
if (entry.HasMember(Schema::Field::context))
{
contextValue = entry.FindMember(Schema::Field::context);
contextStr = contextValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator contextItr = entry.FindMember(Schema::Field::context);
contextStr = contextItr != entry.MemberEnd() ? contextItr->value.GetString() : "";
AZStd::string variantStr;
rapidjson::Value::ConstMemberIterator variantValue;
if (entry.HasMember(Schema::Field::variant))
{
variantValue = entry.FindMember(Schema::Field::variant);
variantStr = variantValue->value.GetString();
}
rapidjson::Value::ConstMemberIterator variantItr = entry.FindMember(Schema::Field::variant);
variantStr = variantItr != entry.MemberEnd() ? variantItr->value.GetString() : "";
AZStd::string baseKey = contextStr;
if (keyStr.empty())
{
AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", baseKey.c_str());
AZ_Error("TranslationDatabase", false, "Every entry in the Translation data must have a key: %s", contextStr.c_str());
return context.Report(JSR::Tasks::ReadField, JSR::Outcomes::Unsupported, "Every entry in the Translation data must have a key");
}
baseKey = contextStr;
if (!baseKey.empty())
{
baseKey.append(".");
@@ -167,7 +158,7 @@ namespace GraphCanvas
for (auto it = entry.MemberBegin(); it != entry.MemberEnd(); ++it)
{
// Skip the fixed elements
if (it == keyValue || it == contextValue || it == variantValue)
if (it == keyItr || it == contextItr || it == variantItr)
{
continue;
}
@@ -198,7 +198,7 @@ namespace Multiplayer
}
};
class BenchmarkNetworkEntityManager : public MockNetworkEntityManager
class BenchmarkNetworkEntityManager : public Multiplayer::INetworkEntityManager
{
public:
BenchmarkNetworkEntityManager() : m_authorityTracker(*this) {}
@@ -235,6 +235,54 @@ namespace Multiplayer
return InvalidNetEntityId;
}
void Initialize([[maybe_unused]] const HostId& hostId, [[maybe_unused]] AZStd::unique_ptr<IEntityDomain> entityDomain) override {}
bool IsInitialized() const override { return false; }
IEntityDomain* GetEntityDomain() const override { return nullptr; }
EntityList CreateEntitiesImmediate(
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
[[maybe_unused]] NetEntityRole netEntityRole,
[[maybe_unused]] const AZ::Transform& transform,
[[maybe_unused]] AutoActivate autoActivate) override {
return {};
}
EntityList CreateEntitiesImmediate(
[[maybe_unused]] const PrefabEntityId& prefabEntryId,
[[maybe_unused]] NetEntityId netEntityId,
[[maybe_unused]] NetEntityRole netEntityRole,
[[maybe_unused]] AutoActivate autoActivate,
[[maybe_unused]] const AZ::Transform& transform) override {
return {};
}
[[nodiscard]] AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
[[maybe_unused]] const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable,
[[maybe_unused]] const AZ::Transform& transform) override {
return {};
}
void SetupNetEntity([[maybe_unused]] AZ::Entity* netEntity, [[maybe_unused]] PrefabEntityId prefabEntityId, [[maybe_unused]] NetEntityRole netEntityRole) override {}
uint32_t GetEntityCount() const override {
return 0;
}
void MarkForRemoval([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
bool IsMarkedForRemoval([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) const override {
return false;
}
void ClearEntityFromRemovalList([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
void ClearAllEntities() override {}
void AddEntityMarkedDirtyHandler([[maybe_unused]] AZ::Event<>::Handler& entityMarkedDirtyHandle) override {}
void AddEntityNotifyChangesHandler([[maybe_unused]] AZ::Event<>::Handler& entityNotifyChangesHandle) override {}
void AddEntityExitDomainHandler([[maybe_unused]] EntityExitDomainEvent::Handler& entityExitDomainHandler) override {}
void AddControllersActivatedHandler([[maybe_unused]] ControllersActivatedEvent::Handler& controllersActivatedHandler) override {}
void AddControllersDeactivatedHandler([[maybe_unused]] ControllersDeactivatedEvent::Handler& controllersDeactivatedHandler) override {}
void NotifyEntitiesDirtied() override {}
void NotifyEntitiesChanged() override {}
void NotifyControllersActivated([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
void NotifyControllersDeactivated([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle, [[maybe_unused]] EntityIsMigrating entityIsMigrating) override {}
void HandleLocalRpcMessage([[maybe_unused]] NetworkEntityRpcMessage& message) override {}
void HandleEntitiesExitDomain([[maybe_unused]] const NetEntityIdSet& entitiesNotInDomain) override {}
void ForceAssumeAuthority([[maybe_unused]] const ConstNetworkEntityHandle& entityHandle) override {}
void SetMigrateTimeoutTimeMs([[maybe_unused]] AZ::TimeMs timeoutTimeMs) override {}
void DebugDraw() const override {}
NetworkEntityTracker m_tracker;
NetworkEntityAuthorityTracker m_authorityTracker;
MultiplayerComponentRegistry m_multiplayerComponentRegistry;
@@ -347,8 +347,9 @@ namespace PhysX
void JointsSubComponentModeAngleCone::ConfigurePlanarView(const AZ::Color& planeColor, const AZ::Color& plane2Color)
{
AzToolsFramework::ManipulatorViews views;
views.emplace_back(CreateManipulatorViewQuad(
*m_yzPlanarManipulator, planeColor, plane2Color, AZ::Vector3::CreateZero(), AzToolsFramework::PlanarManipulatorAxisLength()));
views.emplace_back(AzToolsFramework::CreateManipulatorViewQuad(
m_yzPlanarManipulator->GetAxis1(), m_yzPlanarManipulator->GetAxis2(), planeColor, plane2Color, AZ::Vector3::CreateZero(),
AzToolsFramework::PlanarManipulatorAxisLength()));
m_yzPlanarManipulator->SetViews(AZStd::move(views));
}
@@ -22,6 +22,7 @@
#include <AzCore/IO/Path/Path.h>
#include <AzCore/Math/Matrix3x3.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/UnitTest/TestDebugDisplayRequests.h>
#include <AzFramework/Viewport/CameraState.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFramework.h>
#include <AzManipulatorTestFramework/AzManipulatorTestFrameworkUtils.h>
@@ -38,10 +39,6 @@ namespace UnitTest
static const AzToolsFramework::ManipulatorManagerId TestManipulatorManagerId =
AzToolsFramework::ManipulatorManagerId(AZ::Crc32("TestManipulatorManagerId"));
class NullDebugDisplayRequests : public AzFramework::DebugDisplayRequests
{
};
class WhiteBoxManipulatorFixture : public WhiteBoxTestFixture
{
public:
@@ -67,7 +64,8 @@ namespace UnitTest
// create the direct call manipulator viewport interaction and an immediate mode dispatcher
AZStd::unique_ptr<AzManipulatorTestFramework::ManipulatorViewportInteraction> viewportManipulatorInteraction =
AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>();
AZStd::make_unique<AzManipulatorTestFramework::DirectCallManipulatorViewportInteraction>(
AZStd::make_shared<NullDebugDisplayRequests>());
AZStd::unique_ptr<AzManipulatorTestFramework::ImmediateModeActionDispatcher> actionDispatcher =
AZStd::make_unique<AzManipulatorTestFramework::ImmediateModeActionDispatcher>(
*viewportManipulatorInteraction);
@@ -384,3 +384,14 @@ class AbstractResourceLocator(object):
"editor_log() is not implemented on the base AbstractResourceLocator() class. "
"It must be defined by the inheriting class - "
"i.e. _WindowsResourceLocator(AbstractResourceLocator).editor_log()")
@abstractmethod
def crash_log(self):
"""
Return path to the project's crash log dir using the builds project and platform
:return: path to error.log/crash.log
"""
raise NotImplementedError(
"crash_log() is not implemented on the base AbstractResourceLocator() class. "
"It must be defined by the inheriting class - "
"i.e. _WindowsResourceLocator(AbstractResourceLocator).crash_log()")
@@ -53,6 +53,12 @@ class _LinuxResourceManager(AbstractResourceLocator):
"""
return os.path.join(self.project_log(), "Editor.log")
def crash_log(self):
"""
Return path to the project's crash log dir using the builds project and platform
:return: path to Crash.log
"""
return os.path.join(self.project_log(), "crash.log")
class LinuxWorkspaceManager(AbstractWorkspaceManager):
"""
@@ -62,6 +62,12 @@ class _MacResourceLocator(AbstractResourceLocator):
"""
return os.path.join(self.project_log(), "Editor.log")
def crash_log(self):
"""
Return path to the project's crash log dir using the builds project and platform
:return: path to crash.log
"""
return os.path.join(self.project_log(), "crash.log")
class MacWorkspaceManager(AbstractWorkspaceManager):
"""
@@ -68,6 +68,12 @@ class _WindowsResourceLocator(AbstractResourceLocator):
"""
return os.path.join(self.project_log(), "Editor.log")
def crash_log(self):
"""
Return path to the project's crash log dir using the builds project and platform
:return: path to Error.log
"""
return os.path.join(self.project_log(), "error.log")
class WindowsWorkspaceManager(AbstractWorkspaceManager):
"""
@@ -293,61 +293,75 @@ def delete(file_list, del_files, del_dirs):
return True
def create_backup(source, backup_dir):
def create_backup(source, backup_dir, backup_name=None):
"""
Creates a backup of a single source file by creating a copy of it with the same name + '.bak' in backup_dir
e.g.: foo.txt is stored as backup_dir/foo.txt.bak
If backup_name is provided, it will create a copy of the source file named "backup_name + .bak" instead.
:param source: Full path to file to backup
:param backup_dir: Path to the directory to store backup.
:param backup_name: [Optional] Name of the backed up file to use instead or the source name.
"""
if not backup_dir or not os.path.isdir(backup_dir):
logger.error(f'Cannot create backup due to invalid backup directory {backup_dir}')
return
return False
if not os.path.exists(source):
logger.warning(f'Source file {source} does not exist, aborting backup creation.')
return
return False
source_filename = os.path.basename(source)
dest = os.path.join(backup_dir, f'{source_filename}.bak')
dest = None
if backup_name is None:
source_filename = os.path.basename(source)
dest = os.path.join(backup_dir, f'{source_filename}.bak')
else:
dest = os.path.join(backup_dir, f'{backup_name}.bak')
logger.info(f'Saving backup of {source} in {dest}')
if os.path.exists(dest):
logger.warning(f'Backup file already exists at {dest}, it will be overwritten.')
try:
shutil.copy(source, dest)
shutil.copy2(source, dest)
except Exception: # intentionally broad
logger.warning('Could not create backup, exception occurred while copying.', exc_info=True)
return False
return True
def restore_backup(original_file, backup_dir):
def restore_backup(original_file, backup_dir, backup_name=None):
"""
Restores a backup file to its original location. Works with a single file only.
:param original_file: Full path to file to overwrite.
:param backup_dir: Path to the directory storing the backup.
:param backup_name: [Optional] Provide if the backup file name is different from source. eg backup file = myFile_1.txt.bak original file = myfile.txt
"""
if not backup_dir or not os.path.isdir(backup_dir):
logger.error(f'Cannot restore backup due to invalid or nonexistent directory {backup_dir}.')
return
return False
source_filename = os.path.basename(original_file)
backup = os.path.join(backup_dir, f'{source_filename}.bak')
backup = None
if backup_name is None:
source_filename = os.path.basename(original_file)
backup = os.path.join(backup_dir, f'{source_filename}.bak')
else:
backup = os.path.join(backup_dir, f'{backup_name}.bak')
if not os.path.exists(backup):
logger.warning(f'Backup file {backup} does not exist, aborting backup restoration.')
return
return False
logger.info(f'Restoring backup of {original_file} from {backup}')
try:
shutil.copy(backup, original_file)
shutil.copy2(backup, original_file)
except Exception: # intentionally broad
logger.warning('Could not restore backup, exception occurred while copying.', exc_info=True)
return False
return True
def delete_oldest(path_glob, keep_num, del_files=True, del_dirs=False):
""" Delete oldest builds, keeping a specific number """
@@ -163,9 +163,23 @@ class AndroidLauncher(Launcher):
return True
def setup(self):
def setup(self, backupFiles=True, launch_ap=True, configure_settings=True):
"""
Perform setup of this launcher, must be called before launching.
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
:param backupFiles: Bool to backup setup files
:param launch_ap: Bool to launch the asset processor
:param configure_settings: Bool to update settings caches
:return: None
"""
# Backup
self.backup_settings()
if backupFiles:
self.backup_settings()
# None reverts to function default
if launch_ap is None:
launch_ap = True
# Enable Android capabilities and verify environment is setup before continuing.
self._is_valid_android_environment()
@@ -174,7 +188,7 @@ class AndroidLauncher(Launcher):
# Modify and re-configure
self.configure_settings()
self.workspace.shader_compiler.start()
super(AndroidLauncher, self).setup()
super(AndroidLauncher, self).setup(backupFiles, launch_ap, configure_settings)
def teardown(self):
ly_test_tools.mobile.android.undo_tcp_port_changes(self._device_id)
@@ -75,6 +75,8 @@ class Launcher(object):
~/ly_test_tools/devices.ini (a.k.a. %USERPROFILE%/ly_test_tools/devices.ini)
:param backupFiles: Bool to backup setup files
:param launch_ap: Bool to launch the asset processor
:param configure_settings: Bool to update settings caches
:return: None
"""
# Remove existing logs and dmp files before launching for self.save_project_log_files()
@@ -153,7 +155,6 @@ class Launcher(object):
:return: None
"""
self.workspace.asset_processor.stop()
self.save_project_log_files()
def save_project_log_files(self):
# type: () -> None
@@ -52,7 +52,7 @@ class LinuxLauncher(Launcher):
if backupFiles:
self.backup_settings()
# Base setup defaults to None
# None reverts to function default
if launch_ap is None:
launch_ap = True
@@ -162,7 +162,7 @@ class LinuxLauncher(Launcher):
def configure_settings(self):
"""
Configures system level settings and syncs the launcher to the targeted console IP.
Configures system level settings
:return: None
"""
@@ -170,7 +170,6 @@ class LinuxLauncher(Launcher):
host_ip = '127.0.0.1'
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.paths.project()}"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={host_ip}"')
self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"')
self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip)
@@ -179,20 +178,25 @@ class LinuxLauncher(Launcher):
class DedicatedLinuxLauncher(LinuxLauncher):
def setup(self, backupFiles=True, launch_ap=False):
def setup(self, backupFiles=True, launch_ap=False, configure_settings=True):
"""
Perform setup of this launcher, must be called before launching.
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
:param backupFiles: Bool to backup setup files
:param lauch_ap: Bool to lauch the asset processor
:param launch_ap: Bool to launch the asset processor
:param configure_settings: Bool to update settings caches
:return: None
"""
# Base setup defaults to None
# Backup
if backupFiles:
self.backup_settings()
# None reverts to function default
if launch_ap is None:
launch_ap = False
super(DedicatedLinuxLauncher, self).setup(backupFiles, launch_ap)
super(DedicatedLinuxLauncher, self).setup(backupFiles, launch_ap, configure_settings)
def binary_path(self):
"""
@@ -44,21 +44,22 @@ class WinLauncher(Launcher):
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
:param backupFiles: Bool to backup setup files
:param lauch_ap: Bool to lauch the asset processor
:param launch_ap: Bool to lauch the asset processor
:param configure_settings: Bool to update settings caches
:return: None
"""
# Backup
if backupFiles:
self.backup_settings()
# Base setup defaults to None
# None reverts to function default
if launch_ap is None:
launch_ap = True
# Modify and re-configure
if configure_settings:
self.configure_settings()
super(WinLauncher, self).setup(backupFiles, launch_ap)
super(WinLauncher, self).setup(backupFiles, launch_ap, configure_settings)
def launch(self):
"""
@@ -161,7 +162,7 @@ class WinLauncher(Launcher):
def configure_settings(self):
"""
Configures system level settings and syncs the launcher to the targeted console IP.
Configures system level settings
:return: None
"""
@@ -169,7 +170,6 @@ class WinLauncher(Launcher):
host_ip = '127.0.0.1'
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.paths.project()}"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={host_ip}"')
self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"')
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"')
self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip)
@@ -177,20 +177,25 @@ class WinLauncher(Launcher):
class DedicatedWinLauncher(WinLauncher):
def setup(self, backupFiles=True, launch_ap=False):
def setup(self, backupFiles=True, launch_ap=False, configure_settings=True):
"""
Perform setup of this launcher, must be called before launching.
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
:param backupFiles: Bool to backup setup files
:param lauch_ap: Bool to lauch the asset processor
:param launch_ap: Bool to launch the asset processor
:param configure_settings: Bool to update settings caches
:return: None
"""
# Base setup defaults to None
# Backup
if backupFiles:
self.backup_settings()
# None reverts to function default
if launch_ap is None:
launch_ap = False
super(DedicatedWinLauncher, self).setup(backupFiles, launch_ap)
super(DedicatedWinLauncher, self).setup(backupFiles, launch_ap, configure_settings)
def binary_path(self):
"""
@@ -48,6 +48,7 @@ import ly_test_tools.environment.waiter as waiter
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.o3de.editor_test
import ly_test_tools.o3de.editor_test_utils as editor_utils
import ly_test_tools._internal.pytest_plugin
from ly_test_tools.o3de.asset_processor import AssetProcessor
from ly_test_tools.launchers.exceptions import WaitTimeoutError
@@ -757,7 +758,7 @@ class EditorTestSuite():
cmdline = [
"--runpythontest", test_filename,
"-logfile", f"@log@/{log_name}",
"-project-log-path", editor_utils.retrieve_log_path(run_id, workspace)] + test_cmdline_args
"-project-log-path", ly_test_tools._internal.pytest_plugin.output_path] + test_cmdline_args
editor.args.extend(cmdline)
editor.start(backupFiles = False, launch_ap = False, configure_settings=False)
@@ -823,7 +824,7 @@ class EditorTestSuite():
cmdline = [
"--runpythontest", test_filenames_str,
"-logfile", f"@log@/{log_name}",
"-project-log-path", editor_utils.retrieve_log_path(run_id, workspace)] + test_cmdline_args
"-project-log-path", ly_test_tools._internal.pytest_plugin.output_path] + test_cmdline_args
editor.args.extend(cmdline)
editor.start(backupFiles = False, launch_ap = False, configure_settings=False)
@@ -900,7 +901,6 @@ class EditorTestSuite():
results[test_spec_name] = Result.Timeout.create(timed_out_result.test_spec,
results[test_spec_name].output,
self.timeout_editor_shared_test, result.editor_log)
return results
def _run_single_test(self, request: Request, workspace: AbstractWorkspace, editor: Editor,
@@ -751,7 +751,7 @@ class TestFileBackup(unittest.TestCase):
self._dummy_file = 'dummy.txt'
self._dummy_backup_file = os.path.join(self._dummy_dir, '{}.bak'.format(self._dummy_file))
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_SourceExists_BackupCreated(self, mock_path_isdir, mock_backup_exists, mock_copy):
@@ -763,7 +763,7 @@ class TestFileBackup(unittest.TestCase):
mock_copy.assert_called_with(self._dummy_file, self._dummy_backup_file)
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_BackupExists_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
@@ -776,7 +776,7 @@ class TestFileBackup(unittest.TestCase):
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_SourceNotExists_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
@@ -789,7 +789,7 @@ class TestFileBackup(unittest.TestCase):
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_BackupSettings_CannotCopy_WarningLogged(self, mock_path_isdir, mock_backup_exists, mock_copy, mock_logger_warning):
@@ -821,7 +821,7 @@ class TestFileBackupRestore(unittest.TestCase):
self._dummy_file = 'dummy.txt'
self._dummy_backup_file = os.path.join(self._dummy_dir, '{}.bak'.format(self._dummy_file))
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_BackupRestore_Success(self, mock_path_isdir, mock_exists, mock_copy):
@@ -832,7 +832,7 @@ class TestFileBackupRestore(unittest.TestCase):
mock_copy.assert_called_with(self._dummy_backup_file, self._dummy_file)
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_CannotCopy_WarningLogged(self, mock_path_isdir, mock_exists, mock_copy, mock_logger_warning):
@@ -846,7 +846,7 @@ class TestFileBackupRestore(unittest.TestCase):
mock_logger_warning.assert_called_once()
@mock.patch('ly_test_tools.environment.file_system.logger.warning')
@mock.patch('shutil.copy')
@mock.patch('shutil.copy2')
@mock.patch('os.path.exists')
@mock.patch('os.path.isdir')
def test_RestoreSettings_BackupNotExists_WarningLogged(self, mock_path_isdir, mock_exists, mock_copy, mock_logger_warning):
@@ -146,15 +146,6 @@ class TestBaseLauncher:
mock_stop_ap.assert_called_once()
@mock.patch('ly_test_tools.launchers.platforms.base.Launcher.save_project_log_files')
def test_Teardown_TeardownCalled_CallsSaveProjectLogFiles(self, under_test):
mock_workspace = mock.MagicMock()
mock_args = ['foo']
mock_launcher = ly_test_tools.launchers.Launcher(mock_workspace, mock_args)
mock_launcher.teardown()
under_test.assert_called_once()
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
@mock.patch('ly_test_tools._internal.managers.artifact_manager.ArtifactManager.save_artifact')
@mock.patch('os.listdir')
+3 -1
View File
@@ -32,6 +32,7 @@ endif()
# Set and create folders for PyTest and GTest xml output
ly_set(PYTEST_XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/Testing/Pytest)
ly_set(GTEST_XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/Testing/Gtest)
ly_set(LYTESTTOOLS_OUTPUT_DIR ${CMAKE_BINARY_DIR}/Testing/LyTestTools)
file(MAKE_DIRECTORY ${PYTEST_XML_OUTPUT_DIR})
file(MAKE_DIRECTORY ${GTEST_XML_OUTPUT_DIR})
@@ -309,6 +310,7 @@ function(ly_add_pytest)
endif()
string(REPLACE "::" "_" pytest_report_directory "${PYTEST_XML_OUTPUT_DIR}/${ly_add_pytest_NAME}.xml")
string(REPLACE "::" "_" pytest_output_directory "${LYTESTTOOLS_OUTPUT_DIR}/${ly_add_pytest_NAME}")
# Add the script path to the test target params
set(LY_TEST_PARAMS "${ly_add_pytest_PATH}")
@@ -318,7 +320,7 @@ function(ly_add_pytest)
PARENT_NAME ${ly_add_pytest_NAME}
TEST_SUITE ${ly_add_pytest_TEST_SUITE}
LABELS FRAMEWORK_pytest
TEST_COMMAND ${LY_PYTEST_EXECUTABLE} ${ly_add_pytest_PATH} ${ly_add_pytest_EXTRA_ARGS} --junitxml=${pytest_report_directory} ${custom_marks_args}
TEST_COMMAND ${LY_PYTEST_EXECUTABLE} ${ly_add_pytest_PATH} ${ly_add_pytest_EXTRA_ARGS} --output-path ${pytest_output_directory} --junitxml=${pytest_report_directory} ${custom_marks_args}
TEST_LIBRARY pytest
COMPONENT ${ly_add_pytest_COMPONENT}
${ly_add_pytest_UNPARSED_ARGUMENTS}
@@ -84,7 +84,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"test_profile_nounity": {
@@ -97,7 +97,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "all",
"CTEST_OPTIONS": "-E (AutomatedTesting::Atom_TestSuite_Main|AutomatedTesting::TerrainTests_Main|Gem::EMotionFX.Editor.Tests) -L (SUITE_smoke|SUITE_main) -LE (REQUIRES_gpu) --no-tests=error",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"asset_profile": {
@@ -146,7 +146,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_periodic",
"CTEST_OPTIONS": "-L (SUITE_periodic) --no-tests=error",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"sandbox_test_profile": {
@@ -182,7 +182,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_benchmark",
"CTEST_OPTIONS": "-L (SUITE_benchmark) --no-tests=error",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"release": {
+2 -2
View File
@@ -103,7 +103,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_periodic",
"CTEST_OPTIONS": "-L \"(SUITE_periodic)\"",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"benchmark_test_profile": {
@@ -120,7 +120,7 @@
"CMAKE_LY_PROJECTS": "AutomatedTesting",
"CMAKE_TARGET": "TEST_SUITE_benchmark",
"CTEST_OPTIONS": "-L \"(SUITE_benchmark)\"",
"TEST_RESULTS": "True"
"TEST_RESULTS": "False"
}
},
"release": {
+1 -1
View File
@@ -411,7 +411,7 @@ def register_project_path(json_data: dict,
if not remove:
# registering a project has the additional step of setting the project.json 'engine' field
this_engine_json = manifest.get_engine_json_data(engine_path=manifest.get_this_engine_path())
this_engine_json = manifest.get_engine_json_data(engine_path=engine_path if engine_path else manifest.get_this_engine_path())
if not this_engine_json:
return 1
project_json_data = manifest.get_project_json_data(project_path=project_path)