Merge remote-tracking branch 'upstream/development' into thin-transmission

This commit is contained in:
Santi Paprika
2022-02-02 16:15:00 +01:00
3446 changed files with 80320 additions and 118964 deletions
+1 -8
View File
@@ -2,18 +2,14 @@
.vs/
.vscode/
__pycache__
AssetProcessorTemp/**
[Bb]uild/
[Oo]ut/**
CMakeUserPresets.json
[Cc]ache/
/[Ii]nstall/
Editor/EditorEventLog.xml
Editor/EditorLayout.xml
**/*egg-info/**
**/*egg-link
**/[Rr]estricted
UserSettings.xml
[Uu]ser/
FrameCapture/**
.DS_Store
@@ -22,9 +18,6 @@ client*.cfg
server*.cfg
.mayaSwatches/
_savebackup/
#Output folder for test results when running Automated Tests
TestResults/**
*.swatches
/imgui.ini
/scripts/project_manager/logs/
/AutomatedTesting/Gem/PythonTests/scripting/TestResults
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<path d="M3.145,8.433c0-1.47,1.196-2.666,2.666-2.666h9.544c-0.158-0.819-0.88-1.443-1.744-1.443H3.487
c-0.978,0-1.778,0.8-1.778,1.778v5.356c0,0.861,0.62,1.582,1.436,1.743V8.433z" fill="#FFFFFF"/>
<g>
<path d="M6.833,11.654c0-1.47,1.196-2.666,2.666-2.666h9.069c-0.158-0.819-0.88-1.443-1.744-1.443H6.7
c-0.978,0-1.778,0.8-1.778,1.778v5.356c0,0.978,0.8,1.778,1.778,1.778h0.133V11.654z" fill="#FFFFFF"/>
</g>
<path d="M20.513,10.765H10.388c-0.978,0-1.778,0.8-1.778,1.777v5.356c0,0.978,0.8,1.778,1.778,1.778h10.125
c0.978,0,1.778-0.8,1.778-1.778v-5.356C22.29,11.565,21.49,10.765,20.513,10.765z M19.332,15.967h-7.763
c-0.264,0-0.478-0.355-0.478-0.793c0-0.438,0.214-0.793,0.478-0.793h7.763c0.264,0,0.478,0.355,0.478,0.793
C19.81,15.612,19.597,15.967,19.332,15.967z" fill="#FFFFFF"/>
</svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:bfc2ef8c6dbf2fba078e27e4e94384099e090468e679327dd826a5cbf22b04ed
size 1019
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:708b12d41229afab78e0f7d59097ae3de855fea8525a920c5c214fc0ce79f1bd
size 1209
@@ -1,3 +0,0 @@
version https://git-lfs.github.com/spec/v1
oid sha256:fab63af9b50790dca25330058e70517987ea8bf11c00f9353dd951ebdbd1dbe5
size 5008
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:7f94f2634eacb4d7bee20dacc45edef96e4d268f1adb7960b8aa8f3b6e2906ed
size 6867609
+129
View File
@@ -0,0 +1,129 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "Bush",
"Components": {
"Component_[1140272189295067758]": {
"$type": "EditorInspectorComponent",
"Id": 1140272189295067758
},
"Component_[13437832196484687256]": {
"$type": "EditorOnlyEntityComponent",
"Id": 13437832196484687256
},
"Component_[1553903646452669645]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 1553903646452669645
},
"Component_[15914009348632444632]": {
"$type": "EditorEntitySortComponent",
"Id": 15914009348632444632,
"Child Entity Order": [
"Entity_[7511491868318]"
]
},
"Component_[18046340308818780248]": {
"$type": "EditorPrefabComponent",
"Id": 18046340308818780248
},
"Component_[1948833233489872938]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 1948833233489872938,
"Parent Entity": ""
},
"Component_[2903632350157981339]": {
"$type": "SelectionComponent",
"Id": 2903632350157981339
},
"Component_[48827510535192710]": {
"$type": "EditorPendingCompositionComponent",
"Id": 48827510535192710
},
"Component_[5609536793322429681]": {
"$type": "EditorLockComponent",
"Id": 5609536793322429681
},
"Component_[5859168386298620990]": {
"$type": "EditorEntityIconComponent",
"Id": 5859168386298620990
},
"Component_[6604616929271524505]": {
"$type": "EditorVisibilityComponent",
"Id": 6604616929271524505
}
}
},
"Entities": {
"Entity_[7511491868318]": {
"Id": "Entity_[7511491868318]",
"Name": "Bush",
"Components": {
"Component_[10227459330338484901]": {
"$type": "EditorInspectorComponent",
"Id": 10227459330338484901,
"ComponentOrderEntryArray": [
{
"ComponentId": 4998941225335869157
},
{
"ComponentId": 9922994635792843826,
"SortIndex": 1
}
]
},
"Component_[10972351222359420947]": {
"$type": "EditorOnlyEntityComponent",
"Id": 10972351222359420947
},
"Component_[12101122374155214392]": {
"$type": "EditorPendingCompositionComponent",
"Id": 12101122374155214392
},
"Component_[1535264614652988260]": {
"$type": "SelectionComponent",
"Id": 1535264614652988260
},
"Component_[16367811417907891218]": {
"$type": "EditorVisibilityComponent",
"Id": 16367811417907891218
},
"Component_[17044216787716682880]": {
"$type": "EditorEntitySortComponent",
"Id": 17044216787716682880
},
"Component_[2129822594969629430]": {
"$type": "EditorEntityIconComponent",
"Id": 2129822594969629430
},
"Component_[2838015156782745450]": {
"$type": "EditorLockComponent",
"Id": 2838015156782745450
},
"Component_[4998941225335869157]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 4998941225335869157,
"Parent Entity": "ContainerEntity"
},
"Component_[8773358049076362578]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 8773358049076362578
},
"Component_[9922994635792843826]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 9922994635792843826,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{1201406D-FB20-5B5F-B9B5-6A6E8DE00A14}",
"subId": 276506120
},
"assetHint": "assets/objects/foliage/bush_privet_01.azmodel"
}
}
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,129 @@
{
"ContainerEntity": {
"Id": "ContainerEntity",
"Name": "PurpleFlower",
"Components": {
"Component_[10519928302743666073]": {
"$type": "EditorPrefabComponent",
"Id": 10519928302743666073
},
"Component_[13894087802180240181]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 13894087802180240181,
"Parent Entity": ""
},
"Component_[15788541052719571801]": {
"$type": "EditorEntityIconComponent",
"Id": 15788541052719571801
},
"Component_[15842981265136092481]": {
"$type": "SelectionComponent",
"Id": 15842981265136092481
},
"Component_[16360384897559021149]": {
"$type": "EditorInspectorComponent",
"Id": 16360384897559021149
},
"Component_[16713545675046303279]": {
"$type": "EditorVisibilityComponent",
"Id": 16713545675046303279
},
"Component_[1806734194268113785]": {
"$type": "EditorPendingCompositionComponent",
"Id": 1806734194268113785
},
"Component_[5392020700593853313]": {
"$type": "EditorEntitySortComponent",
"Id": 5392020700593853313,
"Child Entity Order": [
"Entity_[14335611090324]"
]
},
"Component_[5995854518752659458]": {
"$type": "EditorLockComponent",
"Id": 5995854518752659458
},
"Component_[6963022284400845376]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 6963022284400845376
},
"Component_[8055275578170091546]": {
"$type": "EditorOnlyEntityComponent",
"Id": 8055275578170091546
}
}
},
"Entities": {
"Entity_[14335611090324]": {
"Id": "Entity_[14335611090324]",
"Name": "PurpleFlower",
"Components": {
"Component_[10887353073528055802]": {
"$type": "EditorPendingCompositionComponent",
"Id": 10887353073528055802
},
"Component_[12641127425852859189]": {
"$type": "AZ::Render::EditorMeshComponent",
"Id": 12641127425852859189,
"Controller": {
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{D493A670-6D82-5AE9-A2C8-A2EB02684F71}",
"subId": 284799939
},
"assetHint": "assets/objects/foliage/grass_flower_purple.azmodel"
}
}
}
},
"Component_[14406733303466080015]": {
"$type": "EditorInspectorComponent",
"Id": 14406733303466080015,
"ComponentOrderEntryArray": [
{
"ComponentId": 9231452352781000222
},
{
"ComponentId": 12641127425852859189,
"SortIndex": 1
}
]
},
"Component_[1452384341905923012]": {
"$type": "EditorLockComponent",
"Id": 1452384341905923012
},
"Component_[2215454016415585892]": {
"$type": "EditorDisabledCompositionComponent",
"Id": 2215454016415585892
},
"Component_[4104108067383423623]": {
"$type": "EditorVisibilityComponent",
"Id": 4104108067383423623
},
"Component_[4197335450471807917]": {
"$type": "SelectionComponent",
"Id": 4197335450471807917
},
"Component_[6877680739064997650]": {
"$type": "EditorOnlyEntityComponent",
"Id": 6877680739064997650
},
"Component_[7372550507186490390]": {
"$type": "EditorEntityIconComponent",
"Id": 7372550507186490390
},
"Component_[7673532337364366244]": {
"$type": "EditorEntitySortComponent",
"Id": 7673532337364366244
},
"Component_[9231452352781000222]": {
"$type": "{27F1E1A1-8D9D-4C3B-BD3A-AFB9762449C0} TransformComponent",
"Id": 9231452352781000222,
"Parent Entity": "ContainerEntity"
}
}
}
}
}
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:3789abdf439a6d70438fd4bb1e06881ae6686a4699209c6bc371d22d161e5347
size 26476
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:c987d7d79685fda83efcffb7e1afbcd356c37fc68ec5c663a89b02d4df10caea
size 46412
@@ -2,11 +2,11 @@
<Class name="DescriptorListAsset" type="{60961B36-E3CA-4877-B197-1462C1363F6E}">
<Class name="AZStd::vector" field="Descriptors" type="{FC36C5E0-6152-5B06-AF30-3FC494B85FAB}">
<Class name="Descriptor" field="element" version="8" type="{A5A5E7F7-FC36-4BD1-8A93-21362574B9DA}">
<Class name="AZ::Uuid" field="SpawnerType" value="{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="AZ::Uuid" field="SpawnerType" value="{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}" type="{E152C105-A133-4D03-BBF8-3D4B2FBA3E2A}"/>
<Class name="AZStd::shared_ptr" field="InstanceSpawner" type="{7C7046DE-F8B1-529D-AD8C-829C6C0E2FCD}">
<Class name="DynamicSliceInstanceSpawner" field="element" type="{BBA5CC1E-B4CA-4792-89F7-93711E98FBD1}">
<Class name="PrefabInstanceSpawner" field="element" type="{74BEEDB5-81CF-409F-B375-0D93D81EF2E3}">
<Class name="InstanceSpawner" field="BaseClass1" type="{01AD0758-B04A-4B43-BC2B-BDCD77F4EF6A}"/>
<Class name="Asset" field="SliceAsset" value="id={EBFE34EB-D0A8-5E51-8234-3BE38082B28C}:2,type={78802ABF-9595-463A-8D2B-D022F906F9B1},hint={slices/pinkflower.dynamicslice}" version="1" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
<Class name="Asset" field="SpawnableAsset" value="id={80C0CF4E-9A5E-544B-B89E-BC980175A259}:e6f903d2,type={855E3021-D305-4845-B284-20C3F7FDF16B},hint={Assets/Prefabs/PinkFlower.prefab},loadBehavior=1" version="2" type="{77A19D40-8731-4D3C-9041-1B43047366A4}"/>
</Class>
</Class>
<Class name="float" field="Weight" value="1.0000000" type="{EA2C3E90-AFBE-44D4-A90D-FAAF79BAF93D}"/>
-1
View File
@@ -27,5 +27,4 @@ else()
set_property(GLOBAL APPEND PROPERTY LY_PROJECTS_TARGET_NAME ${project_target_name})
add_subdirectory(Gem)
endif()
+5 -2
View File
@@ -6,6 +6,9 @@
#
#
set(gem_path ${CMAKE_CURRENT_LIST_DIR})
set(gem_json ${gem_path}/gem.json)
o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
add_subdirectory(Code)
add_subdirectory(PythonTests)
add_subdirectory(PythonCoverage)
add_subdirectory(PythonTests)
+1 -1
View File
@@ -6,7 +6,7 @@
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${gem_restricted_path} ${gem_path} ${gem_parent_relative_path})
ly_add_target(
NAME AutomatedTesting ${PAL_TRAIT_MONOLITHIC_DRIVEN_MODULE_TYPE}
@@ -0,0 +1,15 @@
<?xml version="1.0"?>
<Component
Name="NetworkTestLevelEntityComponent"
Namespace="AutomatedTesting"
OverrideComponent="false"
OverrideController="false"
OverrideInclude=""
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<RemoteProcedure Name="AuthorityToClientNoParams_PlayFx" InvokeFrom="Authority" HandleOn="Client" IsPublic="false" IsReliable="true" GenerateEventBindings="true" Description="" />
</Component>
@@ -29,8 +29,6 @@
<Param Type="float" Name="SomeFloat" />
</RemoteProcedure>
<RemoteProcedure Name="AuthorityToClientNoParams_PlayFx" InvokeFrom="Authority" HandleOn="Client" IsPublic="false" IsReliable="true" GenerateEventBindings="true" Description="" />
<RemoteProcedure Name="ServerToAuthority_DealDamage" InvokeFrom="Server" HandleOn="Authority" IsPublic="false" IsReliable="true" GenerateEventBindings="true" Description="" >
<Param Type="float" Name="damage" />
</RemoteProcedure>
@@ -12,4 +12,5 @@ set(FILES
Source/AutomatedTestingSystemComponent.cpp
Source/AutomatedTestingSystemComponent.h
Source/AutoGen/NetworkTestPlayerComponent.AutoComponent.xml
Source/AutoGen/NetworkTestLevelEntityComponent.AutoComponent.xml
)
@@ -6,4 +6,8 @@
#
#
set(gem_path ${CMAKE_CURRENT_LIST_DIR})
set(gem_json ${gem_path}/gem.json)
o3de_restricted_path(${gem_json} gem_restricted_path gem_parent_relative_path)
add_subdirectory(Code)
@@ -6,7 +6,7 @@
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${o3de_gem_restricted_path} ${o3de_gem_path} ${o3de_gem_name})
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${gem_restricted_path} ${gem_path} ${gem_parent_relative_path})
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
if(PAL_TRAIT_PYTHONCOVERAGE_SUPPORTED)
+5 -1
View File
@@ -4,6 +4,7 @@
"license": "Apache-2.0 Or MIT",
"license_url": "https://github.com/o3de/o3de/blob/development/LICENSE.txt",
"origin": "Open 3D Engine - o3de.org",
"origin_url": "https://github.com/o3de/o3de",
"type": "Tool",
"summary": "A tool for generating gem coverage for Python tests.",
"canonical_tags": [
@@ -13,5 +14,8 @@
"PythonCoverage"
],
"icon_path": "preview.png",
"requirements": ""
"requirements": "",
"documentation_url": "",
"dependencies": [
]
}
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:6d6204c6730e5675791765ca194e9b1cbec282208e280507de830afc2805e5fa
size 41127
oid sha256:de0e6e480ece5b423222f4feacf56553d73713fe9afea8bbc9a2660a3cd54ec7
size 1232
@@ -12,12 +12,6 @@
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# Only enable AWS automated tests on Windows
set(SUPPORTED_PLATFORMS "Windows" "Linux")
if (NOT "${PAL_PLATFORM_NAME}" IN_LIST SUPPORTED_PLATFORMS)
return()
endif()
ly_add_pytest(
NAME AutomatedTesting::AWSTests
TEST_SUITE awsi
@@ -3,7 +3,7 @@
## Prerequisites
1. Build the O3DE Editor and AutomatedTesting.GameLauncher in Profile.
2. Install the latest version of NodeJs.
3. AWS CLI is installed and configured following [Configuration and Credential File Settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
3. AWS CLI is installed and AWS crendentials are configured via [environment variables](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-envvars.html) or [default profile](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html).
4. [AWS Cloud Development Kit (CDK)](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install) is installed.
## Deploy CDK Applications
@@ -68,6 +68,10 @@ class AwsCredentials:
if (len(self._credentials.sections()) == 0) and (not self._credentials_file_exists):
os.remove(self._credentials_path)
return
credentials_file_dir = os.path.dirname(self._credentials_path)
if not os.path.isdir(credentials_file_dir):
os.makedirs(credentials_file_dir)
with open(self._credentials_path, 'w+') as credential_file:
self._credentials.write(credential_file)
@@ -16,7 +16,7 @@ logging.getLogger('nose').setLevel(logging.WARNING)
class AwsUtils:
def __init__(self, arn: str, session_name: str, region_name: str):
local_session = boto3.Session(profile_name='default')
local_session = boto3.Session()
local_sts_client = local_session.client('sts')
self._local_account_id = local_sts_client.get_caller_identity()["Account"]
logger.info(f'Local Account Id: {self._local_account_id}')
@@ -6,11 +6,13 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import platform
# ARN of the IAM role to assume for retrieving temporary AWS credentials
ASSUME_ROLE_ARN = os.environ.get('ASSUME_ROLE_ARN', 'arn:aws:iam::645075835648:role/o3de-automation-tests')
# Name of the AWS project deployed by the CDK applications
AWS_PROJECT_NAME = os.environ.get('O3DE_AWS_PROJECT_NAME', 'AWSAUTO')
AWS_PROJECT_NAME = os.environ.get('O3DE_AWS_PROJECT_NAME').upper() if os.environ.get('O3DE_AWS_PROJECT_NAME') else \
(os.environ.get('BRANCH_NAME', '') + '-' + os.environ.get('PIPELINE_NAME', '') + '-' + platform.system()).upper()
# Region for the existing CloudFormation stacks used by the automation tests
AWS_REGION = os.environ.get('O3DE_AWS_DEPLOY_REGION', 'us-east-1')
# Name of the default resource mapping config file used by the automation tests
@@ -4,16 +4,28 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import logging
import os
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
logger = logging.getLogger(__name__)
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
enable_prefab_system = True
@pytest.mark.test_case_id("C36529679")
class AtomLevelLoadTest_Editor(EditorSharedTest):
from Atom.tests import hydra_Atom_LevelLoadTest as test_module
@pytest.mark.test_case_id("C36525657")
class AtomEditorComponents_BloomAdded(EditorSharedTest):
@@ -112,5 +124,85 @@ class TestAutomation(EditorTestSuite):
class AtomEditorComponents_SSAOAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_SSAOAdded as test_module
@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
class ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges(EditorSharedTest):
from Atom.tests import hydra_ShaderAssetBuilder_RecompilesShaderAsChainOfDependenciesChanges as test_module
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
class TestMaterialEditorBasicTests(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project):
def delete_files():
file_system.delete(
[
os.path.join(workspace.paths.project(), "Materials", "test_material.material"),
os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"),
os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"),
],
True,
True,
)
# Cleanup our newly created materials
delete_files()
def teardown():
# Cleanup our newly created materials
delete_files()
request.addfinalizer(teardown)
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
@pytest.mark.test_case_id("C34448113") # Creating a New Asset.
@pytest.mark.test_case_id("C34448114") # Opening an Existing Asset.
@pytest.mark.test_case_id("C34448115") # Closing Selected Material.
@pytest.mark.test_case_id("C34448116") # Closing All Materials.
@pytest.mark.test_case_id("C34448117") # Closing all but Selected Material.
@pytest.mark.test_case_id("C34448118") # Saving Material.
@pytest.mark.test_case_id("C34448119") # Saving as a New Material.
@pytest.mark.test_case_id("C34448120") # Saving as a Child Material.
@pytest.mark.test_case_id("C34448121") # Saving all Open Materials.
def test_MaterialEditorBasicTests(
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name):
expected_lines = [
"Material opened: True",
"Test asset doesn't exist initially: True",
"New asset created: True",
"New Material opened: True",
"Material closed: True",
"All documents closed: True",
"Close All Except Selected worked as expected: True",
"Actual Document saved with changes: True",
"Document saved as copy is saved with changes: True",
"Document saved as child is saved with changes: True",
"Save All worked as expected: True",
"P1: Asset Browser visibility working as expected: True",
"P1: Inspector visibility working as expected: True",
]
unexpected_lines = [
# Including any lines in unexpected_lines will cause the test to run for the duration of the timeout
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
generic_launcher,
"hydra_AtomMaterialEditor_BasicTests.py",
run_python="--runpython",
timeout=43,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
null_renderer=True,
log_file_name="MaterialEditor.log",
enable_prefab_system=False,
)
@@ -9,168 +9,20 @@ import os
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__)
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "tests")
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("level", ["auto_test"])
class TestAtomEditorComponentsMain(object):
"""Holds tests for Atom components."""
@pytest.mark.test_case_id("C34525095")
def test_AtomEditorComponents_LightComponent(
self, request, editor, workspace, project, launcher_platform, level):
"""
Please review the hydra script run by this test for more specific test info.
Tests that the Light component has the expected property options available to it.
"""
cfg_args = [level]
expected_lines = [
"light_entity Entity successfully created",
"Entity has a Light component",
"light_entity_test: Component added to the entity: True",
f"light_entity_test: Property value is {LIGHT_TYPES['sphere']} which matches {LIGHT_TYPES['sphere']}",
"Controller|Configuration|Shadows|Enable shadow set to True",
"light_entity Controller|Configuration|Shadows|Shadowmap size: SUCCESS",
"Controller|Configuration|Shadows|Shadow filter method set to 1", # PCF
"Controller|Configuration|Shadows|Filtering sample count set to 4",
"Controller|Configuration|Shadows|Filtering sample count set to 64",
"Controller|Configuration|Shadows|Shadow filter method set to 2", # ESM
"Controller|Configuration|Shadows|ESM exponent set to 50.0",
"Controller|Configuration|Shadows|ESM exponent set to 5000.0",
"Controller|Configuration|Shadows|Shadow filter method set to 3", # ESM+PCF
f"light_entity_test: Property value is {LIGHT_TYPES['spot_disk']} which matches {LIGHT_TYPES['spot_disk']}",
f"light_entity_test: Property value is {LIGHT_TYPES['capsule']} which matches {LIGHT_TYPES['capsule']}",
f"light_entity_test: Property value is {LIGHT_TYPES['quad']} which matches {LIGHT_TYPES['quad']}",
"light_entity Controller|Configuration|Fast approximation: SUCCESS",
"light_entity Controller|Configuration|Both directions: SUCCESS",
f"light_entity_test: Property value is {LIGHT_TYPES['polygon']} which matches {LIGHT_TYPES['polygon']}",
f"light_entity_test: Property value is {LIGHT_TYPES['simple_point']} "
f"which matches {LIGHT_TYPES['simple_point']}",
"Controller|Configuration|Attenuation radius|Mode set to 0",
"Controller|Configuration|Attenuation radius|Radius set to 100.0",
f"light_entity_test: Property value is {LIGHT_TYPES['simple_spot']} "
f"which matches {LIGHT_TYPES['simple_spot']}",
"Controller|Configuration|Shutters|Outer angle set to 45.0",
"Controller|Configuration|Shutters|Outer angle set to 90.0",
"light_entity_test: Component added to the entity: True",
"Light component test (non-GPU) completed.",
]
unexpected_lines = ["Traceback (most recent call last):"]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
editor,
"hydra_AtomEditorComponents_LightComponent.py",
timeout=120,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
null_renderer=True,
cfg_args=cfg_args,
enable_prefab_system=False,
)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_generic'])
@pytest.mark.system
class TestMaterialEditorBasicTests(object):
@pytest.fixture(autouse=True)
def setup_teardown(self, request, workspace, project):
def delete_files():
file_system.delete(
[
os.path.join(workspace.paths.project(), "Materials", "test_material.material"),
os.path.join(workspace.paths.project(), "Materials", "test_material_1.material"),
os.path.join(workspace.paths.project(), "Materials", "test_material_2.material"),
],
True,
True,
)
# Cleanup our newly created materials
delete_files()
def teardown():
# Cleanup our newly created materials
delete_files()
request.addfinalizer(teardown)
@pytest.mark.parametrize("exe_file_name", ["MaterialEditor"])
@pytest.mark.test_case_id("C34448113") # Creating a New Asset.
@pytest.mark.test_case_id("C34448114") # Opening an Existing Asset.
@pytest.mark.test_case_id("C34448115") # Closing Selected Material.
@pytest.mark.test_case_id("C34448116") # Closing All Materials.
@pytest.mark.test_case_id("C34448117") # Closing all but Selected Material.
@pytest.mark.test_case_id("C34448118") # Saving Material.
@pytest.mark.test_case_id("C34448119") # Saving as a New Material.
@pytest.mark.test_case_id("C34448120") # Saving as a Child Material.
@pytest.mark.test_case_id("C34448121") # Saving all Open Materials.
def test_MaterialEditorBasicTests(
self, request, workspace, project, launcher_platform, generic_launcher, exe_file_name):
expected_lines = [
"Material opened: True",
"Test asset doesn't exist initially: True",
"New asset created: True",
"New Material opened: True",
"Material closed: True",
"All documents closed: True",
"Close All Except Selected worked as expected: True",
"Actual Document saved with changes: True",
"Document saved as copy is saved with changes: True",
"Document saved as child is saved with changes: True",
"Save All worked as expected: True",
]
unexpected_lines = [
"Traceback (most recent call last):"
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
generic_launcher,
"hydra_AtomMaterialEditor_BasicTests.py",
run_python="--runpython",
timeout=120,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
null_renderer=True,
log_file_name="MaterialEditor.log",
enable_prefab_system=False,
)
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
class TestAutomation(EditorTestSuite):
enable_prefab_system = False
#this test is intermittently timing out without ever having executed. sandboxing while we investigate cause.
# this test is intermittently timing out without ever having executed. sandboxing while we investigate cause.
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponents_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponents_DisplayMapperAdded as test_module
# this test causes editor to crash when using slices. once automation transitions to prefabs it should pass
@pytest.mark.test_case_id("C36529666")
class AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DiffuseGlobalIlluminationAdded as test_module
# this test causes editor to crash when using slices. once automation transitions to prefabs it should pass
@pytest.mark.test_case_id("C36525660")
class AtomEditorComponentsLevel_DisplayMapperAdded(EditorSharedTest):
from Atom.tests import hydra_AtomEditorComponentsLevel_DisplayMapperAdded as test_module
@@ -32,6 +32,8 @@ GLOBAL_ILLUMINATION_QUALITY = {
'High': 2,
}
# Level list used in Editor Level Load Test
LEVEL_LIST = ["hermanubis", "hermanubis_high", "macbeth_shaderballs", "PbrMaterialChart", "ShadowTest", "Sponza"]
class AtomComponentProperties:
"""
@@ -195,11 +197,13 @@ class AtomComponentProperties:
def entity_reference(property: str = 'name') -> str:
"""
Entity Reference component properties.
- 'EntityIdReferences' component container of entityId references. Initially empty.
: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': 'Entity Reference',
'EntityIdReferences': 'Controller|Configuration|EntityIdReferences',
}
return properties[property]
@@ -125,11 +125,11 @@ def is_pane_visible(pane_name):
"""
:return: bool
"""
return atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
return atomtools.AtomToolsMainWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
def set_pane_visibility(pane_name, value):
atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "SetDockWidgetVisible", pane_name, value)
atomtools.AtomToolsMainWindowRequestBus(bus.Broadcast, "SetDockWidgetVisible", pane_name, value)
def select_lighting_config(config_name):
@@ -162,6 +162,13 @@ def select_model_config(configname):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
def destroy_main_window():
"""
Closes the Material Editor window
"""
azlmbr.atomtools.AtomToolsMainWindowFactoryRequestBus(azlmbr.bus.Broadcast, "DestroyMainWindow")
def wait_for_condition(function, timeout_in_seconds=1.0):
# type: (function, float) -> bool
"""
@@ -60,7 +60,7 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Add Diffuse Global Illumination level component to the level entity.
@@ -86,10 +86,10 @@ def AtomEditorComponentsLevel_DiffuseGlobalIllumination_AddedToEntity():
# 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']))
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'])
Report.result(Tests.diffuse_global_illumination_quality, quality == GLOBAL_ILLUMINATION_QUALITY['Low'])
# 5. Enter/Exit game mode.
TestHelper.enter_game_mode(Tests.enter_game_mode)
@@ -67,7 +67,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Add Display Mapper level component to the level entity.
@@ -102,7 +102,7 @@ def AtomEditorComponentsLevel_DisplayMapper_AddedToEntity():
display_mapper_component.set_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT'), True)
Report.result(
Test.enable_ldr_color_grading_lut,
Tests.enable_ldr_color_grading_lut,
display_mapper_component.get_component_property_value(
AtomComponentProperties.display_mapper('Enable LDR color grading LUT')) is True)
@@ -97,7 +97,7 @@ def AtomEditorComponents_Bloom_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Bloom entity with no components.
@@ -170,10 +170,12 @@ def AtomEditorComponents_Bloom_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, bloom_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not bloom_entity.exists())
# 15. Look for errors and asserts.
@@ -95,7 +95,7 @@ def AtomEditorComponents_Decal_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Decal entity with no components.
@@ -162,6 +162,7 @@ def AtomEditorComponents_Decal_AddedToEntity():
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not decal_entity.exists())
# 12. Look for errors and asserts.
@@ -97,7 +97,7 @@ def AtomEditorComponents_DeferredFog_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Deferred Fog entity with no components.
@@ -174,10 +174,12 @@ def AtomEditorComponents_DeferredFog_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, deferred_fog_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not deferred_fog_entity.exists())
# 15. Look for errors and asserts.
@@ -107,7 +107,7 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a DepthOfField entity with no components.
@@ -189,10 +189,12 @@ def AtomEditorComponents_DepthOfField_AddedToEntity():
# 15. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, depth_of_field_entity.exists())
# 16. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not depth_of_field_entity.exists())
# 17. Look for errors and asserts.
@@ -90,7 +90,7 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Diffuse Probe Grid entity with no components.
@@ -168,10 +168,12 @@ def AtomEditorComponents_DiffuseProbeGrid_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, diffuse_probe_grid_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not diffuse_probe_grid_entity.exists())
# 14. Look for errors or asserts.
@@ -95,7 +95,7 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Directional Light entity with no components.
@@ -168,10 +168,12 @@ def AtomEditorComponents_DirectionalLight_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, directional_light_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not directional_light_entity.exists())
# 14. Look for errors and asserts.
@@ -91,7 +91,7 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Display Mapper entity with no components.
@@ -166,10 +166,12 @@ def AtomEditorComponents_DisplayMapper_AddedToEntity():
# 11. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, display_mapper_entity.exists())
# 12. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not display_mapper_entity.exists())
# 13. Look for errors and asserts.
@@ -9,37 +9,58 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
class Tests:
creation_undo = (
"UNDO Entity creation success",
"UNDO Entity creation failed")
"P0: UNDO Entity creation failed")
creation_redo = (
"REDO Entity creation success",
"REDO Entity creation failed")
"P0: REDO Entity creation failed")
entity_reference_creation = (
"Entity Reference Entity successfully created",
"Entity Reference Entity failed to be created")
"P0: Entity Reference Entity failed to be created")
entity_reference_component = (
"Entity has an Entity Reference component",
"Entity failed to find Entity Reference component")
"P0: Entity failed to find Entity Reference component")
enter_game_mode = (
"Entered game mode",
"Failed to enter game mode")
"P0: Failed to enter game mode")
exit_game_mode = (
"Exited game mode",
"Couldn't exit game mode")
"P0: Couldn't exit game mode")
is_visible = (
"Entity is visible",
"Entity was not visible")
"P0: Entity was not visible")
is_hidden = (
"Entity is hidden",
"Entity was not hidden")
"P0: Entity was not hidden")
entity_deleted = (
"Entity deleted",
"Entity was not deleted")
"P0: Entity was not deleted")
deletion_undo = (
"UNDO deletion success",
"UNDO deletion failed")
"P0: UNDO deletion failed")
deletion_redo = (
"REDO deletion success",
"REDO deletion failed")
"P0: REDO deletion failed")
entity_id_references_is_container = (
"EntityIdReferences is a container property",
"P1: EntityIdReferences is NOT a container property")
container_append = (
"EntityIdReferences append succeeded",
"P1: EntityIdReferences append did not succeed")
container_add = (
"EntityIdReferences add succeeded",
"P1: EntityIdReferences add did not succeed")
container_update = (
"EntityIdReferences update succeeded",
"P1: EntityIdReferences update did not succeed")
container_remove = (
"EntityIdReferences remove succeeded",
"P1: EntityIdReferences remove did not succeed")
container_reset = (
"EntityIdReferences reset succeeded",
"P1: EntityIdReferences reset did not succeed")
entity_reference_component_removed = (
"Entity Reference component removed from entity",
"P1: Entity Reference component NOT removed from entity")
def AtomEditorComponents_EntityReference_AddedToEntity():
@@ -60,13 +81,21 @@ def AtomEditorComponents_EntityReference_AddedToEntity():
2) Add Entity Reference component to Entity Reference 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) Delete Entity Reference entity.
9) UNDO deletion.
10) REDO deletion.
11) Look for errors.
5) 'EntityIdReferences' is a container property
6) Append item to 'EntityIdReferences'
7) Add item to 'EntityIdReferences'
8) Update item in 'EntityIdReferences'
9) Remove item from 'EntityIdReferences'
10) Reset the container property then put one entity reference back for further tests
11) Remove component
12) UNDO component remove
13) Enter/Exit game mode.
14) Test IsHidden.
15) Test IsVisible.
16) Delete Entity Reference entity.
17) UNDO deletion.
18) REDO deletion.
19) Look for errors.
:return: None
"""
@@ -81,7 +110,7 @@ def AtomEditorComponents_EntityReference_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Entity Reference entity with no components.
@@ -119,33 +148,107 @@ def AtomEditorComponents_EntityReference_AddedToEntity():
general.idle_wait_frames(1)
Report.result(Tests.creation_redo, entity_reference_entity.exists())
# 5. Enter/Exit game mode.
# Entities for EntityIdReferences tests
test_1 = EditorEntity.create_editor_entity('test_1')
test_2 = EditorEntity.create_editor_entity('test_2')
test_3 = EditorEntity.create_editor_entity('test_3')
# 5. 'EntityIdReferences' is a container property
Report.result(
Tests.entity_id_references_is_container,
entity_reference_component.is_property_container(
AtomComponentProperties.entity_reference('EntityIdReferences')))
# 6. Append item to 'EntityIdReferences'
entity_reference_component.append_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), test_1.id)
Report.result(
Tests.container_append,
entity_reference_component.get_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 0) == test_1.id)
# 7. Add item to 'EntityIdReferences'
entity_reference_component.add_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 1, test_1.id)
Report.result(
Tests.container_add,
entity_reference_component.get_container_count(
AtomComponentProperties.entity_reference('EntityIdReferences')) == 2)
# 8. Update item in 'EntityIdReferences'
entity_reference_component.update_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 1, test_2.id)
Report.result(
Tests.container_update,
entity_reference_component.get_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 1) == test_2.id)
# 9. Remove item from 'EntityIdReferences'
entity_reference_component.append_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), test_3.id)
count_before = entity_reference_component.get_container_count(
AtomComponentProperties.entity_reference('EntityIdReferences'))
entity_reference_component.remove_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 1)
count_after = entity_reference_component.get_container_count(
AtomComponentProperties.entity_reference('EntityIdReferences'))
Report.result(
Tests.container_remove,
((count_before == 3) and (count_after == 2) and
(entity_reference_component.get_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), 1) == test_3.id)))
# 10. Reset the container property then put one entity reference back for further tests
entity_reference_component.reset_container(AtomComponentProperties.entity_reference('EntityIdReferences'))
general.idle_wait_frames(1)
Report.result(
Tests.container_reset,
entity_reference_component.get_container_count(
AtomComponentProperties.entity_reference('EntityIdReferences')) == 0)
entity_reference_component.append_container_item(
AtomComponentProperties.entity_reference('EntityIdReferences'), test_1.id)
# 11. Remove component
entity_reference_entity.remove_component(AtomComponentProperties.entity_reference())
general.idle_wait_frames(1)
Report.result(Tests.entity_reference_component_removed, not entity_reference_entity.has_component(
AtomComponentProperties.entity_reference()))
# 12. UNDO component remove
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.entity_reference_component, entity_reference_entity.has_component(
AtomComponentProperties.entity_reference()))
# 13. 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.
# 14. Test IsHidden.
entity_reference_entity.set_visibility_state(False)
Report.result(Tests.is_hidden, entity_reference_entity.is_hidden() is True)
# 7. Test IsVisible.
# 15. Test IsVisible.
entity_reference_entity.set_visibility_state(True)
general.idle_wait_frames(1)
Report.result(Tests.is_visible, entity_reference_entity.is_visible() is True)
# 8. Delete Entity Reference entity.
# 16. Delete Entity Reference entity.
entity_reference_entity.delete()
Report.result(Tests.entity_deleted, not entity_reference_entity.exists())
# 9. UNDO deletion.
# 17. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, entity_reference_entity.exists())
# 10. REDO deletion.
# 18. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not entity_reference_entity.exists())
# 11. Look for errors and asserts.
# 19. 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}")
@@ -101,7 +101,7 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Creation of Exposure Control entity with no components.
@@ -169,10 +169,12 @@ def AtomEditorComponents_ExposureControl_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, exposure_control_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not exposure_control_entity.exists())
# 14. Look for errors and asserts.
@@ -99,7 +99,7 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Global Skylight (IBL) entity with no components.
@@ -176,10 +176,12 @@ def AtomEditorComponents_GlobalSkylightIBL_AddedToEntity():
# 11. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, global_skylight_entity.exists())
# 12. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not global_skylight_entity.exists())
# 13. Look for errors and asserts.
@@ -82,7 +82,7 @@ def AtomEditorComponents_Grid_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Grid entity with no components.
@@ -139,10 +139,12 @@ def AtomEditorComponents_Grid_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, grid_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not grid_entity.exists())
# 11. Look for errors or asserts.
@@ -96,7 +96,7 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an HDR Color Grading entity with no components.
@@ -173,10 +173,12 @@ def AtomEditorComponents_HDRColorGrading_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, hdr_color_grading_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not hdr_color_grading_entity.exists())
# 15. Look for errors and asserts.
@@ -87,7 +87,7 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an HDRi Skybox with no components.
@@ -158,10 +158,12 @@ def AtomEditorComponents_HDRiSkybox_AddedToEntity():
# 10. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, hdri_skybox_entity.exists())
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not hdri_skybox_entity.exists())
# 12. Look for errors or asserts.
@@ -89,7 +89,7 @@ def AtomEditorComponents_Light_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Light entity with no components.
@@ -144,10 +144,12 @@ def AtomEditorComponents_Light_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, light_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not light_entity.exists())
# 11. Look for errors asserts.
@@ -1,213 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.projectroot, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from Atom.atom_utils.atom_constants import LIGHT_TYPES
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES = [
("Controller|Configuration|Shadows|Enable shadow", True),
("Controller|Configuration|Shadows|Shadowmap size", 0), # 256
("Controller|Configuration|Shadows|Shadowmap size", 1), # 512
("Controller|Configuration|Shadows|Shadowmap size", 2), # 1024
("Controller|Configuration|Shadows|Shadowmap size", 3), # 2048
("Controller|Configuration|Shadows|Shadow filter method", 1), # PCF
("Controller|Configuration|Shadows|Filtering sample count", 4.0),
("Controller|Configuration|Shadows|Filtering sample count", 64.0),
("Controller|Configuration|Shadows|Shadow filter method", 2), # ECM
("Controller|Configuration|Shadows|ESM exponent", 50),
("Controller|Configuration|Shadows|ESM exponent", 5000),
("Controller|Configuration|Shadows|Shadow filter method", 3), # ESM+PCF
]
QUAD_LIGHT_PROPERTIES = [
("Controller|Configuration|Both directions", True),
("Controller|Configuration|Fast approximation", True),
]
SIMPLE_POINT_LIGHT_PROPERTIES = [
("Controller|Configuration|Attenuation radius|Mode", 0),
("Controller|Configuration|Attenuation radius|Radius", 100.0),
]
SIMPLE_SPOT_LIGHT_PROPERTIES = [
("Controller|Configuration|Shutters|Inner angle", 45.0),
("Controller|Configuration|Shutters|Outer angle", 90.0),
]
def verify_required_component_property_value(entity_name, component, property_path, expected_property_value):
"""
Compares the property value of component against the expected_property_value.
:param entity_name: name of the entity to use (for test verification purposes).
:param component: component to check on a given entity for its current property value.
:param property_path: the path to the property inside the component.
:param expected_property_value: The value expected from the value inside property_path.
:return: None, but prints to general.log() which the test uses to verify against.
"""
property_value = editor.EditorComponentAPIBus(
bus.Broadcast, "GetComponentProperty", component, property_path).GetValue()
general.log(f"{entity_name}_test: Property value is {property_value} "
f"which matches {expected_property_value}")
def run():
"""
Test Case - Light Component
1. Creates a "light_entity" Entity and attaches a "Light" component to it.
2. Updates the Light component to each light type option from the LIGHT_TYPES constant.
3. The test will check the Editor log to ensure each light type was selected.
4. Prints the string "Light component test (non-GPU) completed" after completion.
Tests will fail immediately if any of these log lines are found:
1. Trace::Assert
2. Trace::Error
3. Traceback (most recent call last):
:return: None
"""
# Create a "light_entity" entity with "Light" component.
light_entity_name = "light_entity"
light_component = "Light"
light_entity = hydra.Entity(light_entity_name)
light_entity.create_entity(math.Vector3(-1.0, -2.0, 3.0), [light_component])
general.log(
f"{light_entity_name}_test: Component added to the entity: "
f"{hydra.has_components(light_entity.id, [light_component])}")
# Populate the light_component_id_pair value so that it can be used to select all Light component options.
light_component_id_pair = None
component_type_id_list = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', [light_component], 0)
if len(component_type_id_list) < 1:
general.log(f"ERROR: A component class with name {light_component} doesn't exist")
light_component_id_pair = None
elif len(component_type_id_list) > 1:
general.log(f"ERROR: Found more than one component classes with same name: {light_component}")
light_component_id_pair = None
entity_component_id_pair = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', light_entity.id, component_type_id_list[0])
if entity_component_id_pair.IsSuccess():
light_component_id_pair = entity_component_id_pair.GetValue()
# Test each Light component option can be selected and it's properties updated.
# Point (sphere) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['sphere'],
light_properties=SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Spot (disk) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['spot_disk'],
light_properties=SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Capsule light type checks.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
LIGHT_TYPES['capsule']
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=LIGHT_TYPES['capsule']
)
# Quad light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['quad'],
light_properties=QUAD_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Polygon light type checks.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
LIGHT_TYPES['polygon']
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=LIGHT_TYPES['polygon']
)
# Point (simple punctual) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['simple_point'],
light_properties=SIMPLE_POINT_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
# Spot (simple punctual) light type checks.
light_type_property_test(
light_type=LIGHT_TYPES['simple_spot'],
light_properties=SIMPLE_SPOT_LIGHT_PROPERTIES,
light_component_id_pair=light_component_id_pair,
light_entity_name=light_entity_name,
light_entity=light_entity
)
general.log("Light component test (non-GPU) completed.")
def light_type_property_test(light_type, light_properties, light_component_id_pair, light_entity_name, light_entity):
"""
Updates the current light type and modifies its properties, then verifies they are accurate to what was set.
:param light_type: The type of light to update, must match a value in LIGHT_TYPES
:param light_properties: List of tuples detailing properties to modify with update values.
:param light_component_id_pair: Entity + component ID pair for updating the light component on a given entity.
:param light_entity_name: the name of the Entity holding the light component.
:param light_entity: the Entity object containing the light component.
:return: None
"""
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
light_type
)
verify_required_component_property_value(
entity_name=light_entity_name,
component=light_entity.components[0],
property_path=LIGHT_TYPE_PROPERTY,
expected_property_value=light_type
)
for light_property in light_properties:
light_entity.get_set_test(0, light_property[0], light_property[1])
if __name__ == "__main__":
run()
@@ -104,7 +104,7 @@ def AtomEditorComponents_LookModification_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create an Look Modification entity with no components.
@@ -192,10 +192,12 @@ def AtomEditorComponents_LookModification_AddedToEntity():
# 14. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, look_modification_entity.exists())
# 15. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not look_modification_entity.exists())
# 16. Look for errors and asserts.
@@ -102,7 +102,7 @@ def AtomEditorComponents_Material_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Material entity with no components.
@@ -184,10 +184,12 @@ def AtomEditorComponents_Material_AddedToEntity():
# 16. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, material_entity.exists())
# 17. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not material_entity.exists())
# 18. Look for errors or asserts.
@@ -87,7 +87,7 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Mesh entity with no components.
@@ -151,10 +151,12 @@ def AtomEditorComponents_Mesh_AddedToEntity():
# 10. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, mesh_entity.exists())
# 11. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not mesh_entity.exists())
# 12. Look for errors or asserts.
@@ -80,7 +80,7 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a occlusion culling plane entity with no components.
@@ -140,10 +140,12 @@ def AtomEditorComponents_OcclusionCullingPlane_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, occlusion_culling_plane_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not occlusion_culling_plane_entity.exists())
# 11. Look for errors or asserts.
@@ -89,7 +89,7 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Physical Sky entity with no components.
@@ -146,10 +146,12 @@ def AtomEditorComponents_PhysicalSky_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, physical_sky_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not physical_sky_entity.exists())
# 11. Look for errors and asserts.
@@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFX Gradient Weight Modifier entity with no components.
@@ -162,10 +162,12 @@ def AtomEditorComponents_PostFXGradientWeightModifier_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_gradient_weight_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_gradient_weight_entity.exists())
# 14. Look for errors or asserts.
@@ -80,7 +80,7 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFX Layer entity with no components.
@@ -137,10 +137,12 @@ def AtomEditorComponents_postfx_layer_AddedToEntity():
# 9. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_layer_entity.exists())
# 10. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_layer_entity.exists())
# 11. Look for errors or asserts.
@@ -92,7 +92,7 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Post FX Radius Weight Modifier entity with no components.
@@ -161,10 +161,12 @@ def AtomEditorComponents_PostFXRadiusWeightModifier_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_radius_weight_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_radius_weight_entity.exists())
# 14. Look for errors and asserts.
@@ -98,7 +98,7 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a PostFx Shape Weight Modifier entity with no components.
@@ -188,10 +188,12 @@ def AtomEditorComponents_postfx_shape_weight_AddedToEntity():
# 15. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, postfx_shape_weight_entity.exists())
# 16. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not postfx_shape_weight_entity.exists())
# 17. Look for errors or asserts.
@@ -97,7 +97,7 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a Reflection Probe entity with no components.
@@ -183,10 +183,12 @@ def AtomEditorComponents_ReflectionProbe_AddedToEntity():
# 13. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, reflection_probe_entity.exists())
# 14. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not reflection_probe_entity.exists())
# 15. Look for errors or asserts.
@@ -94,7 +94,7 @@ def AtomEditorComponents_SSAO_AddedToEntity():
# 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")
TestHelper.open_level("Graphics", "base_empty")
# Test steps begin.
# 1. Create a SSAO entity with no components.
@@ -163,10 +163,12 @@ def AtomEditorComponents_SSAO_AddedToEntity():
# 12. UNDO deletion.
general.undo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_undo, ssao_entity.exists())
# 13. REDO deletion.
general.redo()
general.idle_wait_frames(1)
Report.result(Tests.deletion_redo, not ssao_entity.exists())
# 14. Look for errors and asserts.
@@ -36,6 +36,19 @@ MATERIAL_TYPE_PATH = os.path.join(
CACHE_FILE_EXTENSION = ".azmaterial"
def verify_pane_visibility(pane_name: str):
"""
print log lines indicating Material Editor pane visibility function
:param pane_name: Name of the pane to be tested
"""
initial_value = material_editor.is_pane_visible(pane_name)
material_editor.set_pane_visibility(pane_name, not initial_value)
result = (material_editor.is_pane_visible(pane_name) is not initial_value)
material_editor.set_pane_visibility(pane_name, initial_value)
result = result and (initial_value is material_editor.is_pane_visible(pane_name))
print(f"P1: {pane_name} visibility working as expected: {result}")
def run():
"""
Summary:
@@ -49,9 +62,12 @@ def run():
7. Saving as a New Material
8. Saving as a Child Material
9. Saving all Open Materials
10. Verify Asset Browser pane visibility
11. Verify Material Inspector pane visibility
Expected Result:
All the above functions work as expected in Material Editor.
Pane visibility functions as expected
:return: None
"""
@@ -187,6 +203,19 @@ def run():
material_editor.save_all()
material_editor.close_all_documents()
# 10) Verify Asset Browser pane visibility
verify_pane_visibility("Asset Browser")
# 11) Verify Material Inspector pane visibility
verify_pane_visibility("Inspector")
# Confirm documents closed and exit Material Editor
material_editor.wait_for_condition(lambda:
(not material_editor.is_open(document1_id)) and
(not material_editor.is_open(document2_id)) and
(not material_editor.is_open(document3_id)), 2.0)
material_editor.destroy_main_window()
if __name__ == "__main__":
run()
@@ -0,0 +1,72 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def Atom_LevelLoadTest():
"""
Summary:
Loads all graphics levels within the AutomatedTesting project in editor. For each level this script will verify that
the level loads, and can enter/exit gameplay without crashing the editor.
Test setup:
- Store all available levels in a list.
- Set up a for loop to run all checks for each level.
Expected Behavior:
Test verifies that each level loads, enters/exits game mode, and reports success for all test actions.
Test Steps for each level:
1) Create tuple with level load success and failure messages
2) Open the level using the python test tools command
3) Verify level is loaded using a separate command, and report success/failure
4) Enter gameplay and report result using a tuple
5) Exit Gameplay and report result using a tuple
6) Look for errors or asserts.
:return: None
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report, Tracer, TestHelper
from Atom.atom_utils.atom_constants import LEVEL_LIST
with Tracer() as error_tracer:
for level in LEVEL_LIST:
# 1. Create tuple with level load success and failure messages
level_check_tuple = (f"loaded {level}", f"failed to load {level}")
# 2. Open the level using the python test tools command
TestHelper.init_idle()
TestHelper.open_level("Graphics", level)
# 3. Verify level is loaded using a separate command, and report success/failure
Report.result(level_check_tuple, level == general.get_current_level_name())
# 4. Enter gameplay and report result using a tuple
enter_game_mode_tuple = (f"{level} entered gameplay successfully ", f"{level} failed to enter gameplay")
TestHelper.enter_game_mode(enter_game_mode_tuple)
general.idle_wait_frames(1)
# 5. Exit gameplay and report result using a tuple
exit_game_mode_tuple = (f"{level} exited gameplay successfully ", f"{level} failed to exit gameplay")
TestHelper.exit_game_mode(exit_game_mode_tuple)
# 6. Look for errors or 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(Atom_LevelLoadTest)
@@ -6,7 +6,7 @@
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${gem_restricted_path} ${gem_path} ${gem_parent_relative_path})
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_BLAST Traits
@@ -10,7 +10,7 @@
# Automated Tests
################################################################################
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${gem_restricted_path} ${gem_path} ${gem_parent_relative_path})
include(${pal_dir}/PAL_traits_${PAL_PLATFORM_NAME_LOWERCASE}.cmake)
@@ -8,7 +8,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
# Built-in Imports
from __future__ import annotations
from typing import List, Tuple, Union
from enum import Enum
import warnings
# Open 3D Engine Imports
import azlmbr
@@ -21,14 +22,25 @@ import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
class EditorEntityType(Enum):
GAME = azlmbr.entity.EntityType().Game
LEVEL = azlmbr.entity.EntityType().Level
class EditorComponent:
"""
EditorComponent class used to set and get the component property value using path
EditorComponent object is returned from either of
EditorEntity.add_component() or Entity.add_components() or EditorEntity.get_components_of_type()
which also assigns self.id and self.type_id to the EditorComponent object.
self.type_id is the UUID for the component type as provided by an ebus call.
"""
def __init__(self, type_id: uuid):
self.type_id = type_id
self.id = None
self.property_tree_editor = None
def get_component_name(self) -> str:
"""
Used to get name of component
@@ -38,9 +50,9 @@ class EditorComponent:
assert len(type_names) != 0, "Component object does not have type id"
return type_names[0]
def get_property_tree(self):
def get_property_tree(self, force_get: bool = False):
"""
Used to get the property tree object of component that has following functions associated with it:
Used to get and cache the property tree editor of component that has following functions associated with it:
1. prop_tree.is_container(path)
2. prop_tree.get_container_count(path)
3. prop_tree.reset_container(path)
@@ -48,17 +60,161 @@ class EditorComponent:
5. prop_tree.remove_container_item(path, key)
6. prop_tree.update_container_item(path, key, value)
7. prop_tree.get_container_item(path, key)
:return: Property tree object of a component
:param force_get: Force a fresh property tree editor rather than the cached self.property_tree_editor
:return: Property tree editor of the component
"""
if (not force_get) and (self.property_tree_editor is not None):
return self.property_tree_editor
build_prop_tree_outcome = editor.EditorComponentAPIBus(
bus.Broadcast, "BuildComponentPropertyTreeEditor", self.id
)
assert (
build_prop_tree_outcome.IsSuccess()
), f"Failure: Could not build property tree of component: '{self.get_component_name()}'"
), f"Failure: Could not build property tree editor of component: '{self.get_component_name()}'"
prop_tree = build_prop_tree_outcome.GetValue()
Report.info(prop_tree.build_paths_list())
return prop_tree
self.property_tree_editor = prop_tree
return self.property_tree_editor
def is_property_container(self, component_property_path: str) -> bool:
"""
Used to determine if a component property is a container.
Containers are a collection of same typed values that can expand/shrink to contain more or less.
There are two types of containers; indexed and associative.
Indexed containers use integer key and are something like a linked list
Associative containers utilize keys of the same type which could be any supported type.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:return: Boolean True if the property is a container False if it is not.
"""
if self.property_tree_editor is None:
self.get_property_tree()
result = self.property_tree_editor.is_container(component_property_path)
if not result:
Report.info(f"{self.get_component_name()}: '{component_property_path}' is not a container")
return result
def get_container_count(self, component_property_path: str) -> int:
"""
Used to get the count of items in the container.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:return: Count of items in the container as unsigned integer
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
container_count_outcome = self.property_tree_editor.get_container_count(component_property_path)
assert (
container_count_outcome.IsSuccess()
), f"Failure: get_container_count did not return success for '{component_property_path}'"
return container_count_outcome.GetValue()
def reset_container(self, component_property_path: str):
"""
Used to reset a container to empty
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:return: None
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
reset_outcome = self.property_tree_editor.reset_container(component_property_path)
assert (
reset_outcome.IsSuccess()
), f"Failure: could not reset_container on '{component_property_path}'"
def append_container_item(self, component_property_path: str, value: any):
"""
Used to append a value to an indexed container item without providing an index key.
Append will fail on an associative container
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:param value: Value to be set
:return: None
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
append_outcome = self.property_tree_editor.append_container_item(component_property_path, value)
assert (
append_outcome.IsSuccess()
), f"Failure: could not append_container_item to '{component_property_path}'"
def add_container_item(self, component_property_path: str, key: any, value: any):
"""
Used to add a container item at a specified key.
There are two types of containers; indexed and associative.
Indexed containers use integer key.
Associative containers utilize keys of the same type which could be any supported type.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:param key: Zero index integer key or any supported type for associative container
:param value: Value to be set
:return: None
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
add_outcome = self.property_tree_editor.add_container_item(component_property_path, key, value)
assert (
add_outcome.IsSuccess()
), f"Failure: could not add_container_item '{key}' to '{component_property_path}'"
def get_container_item(self, component_property_path: str, key: any) -> any:
"""
Used to retrieve a container item value at the specified key.
There are two types of containers; indexed and associative.
Indexed containers use integer key.
Associative containers utilize keys of the same type which could be any supported type.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:param key: Zero index integer key or any supported type for associative container
:return: Value stored at the key specified
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
get_outcome = self.property_tree_editor.get_container_item(component_property_path, key)
assert (
get_outcome.IsSuccess()
), (
f"Failure: could not get a value for {self.get_component_name()}: '{component_property_path}' [{key}]. "
f"Error returned by get_container_item: {get_outcome.GetError()}")
return get_outcome.GetValue()
def remove_container_item(self, component_property_path: str, key: any):
"""
Used to remove a container item value at the specified key.
There are two types of containers; indexed and associative.
Indexed containers use integer key.
Associative containers utilize keys of the same type which could be any supported type.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:param key: Zero index integer key or any supported type for associative container
:return: None
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
remove_outcome = self.property_tree_editor.remove_container_item(component_property_path, key)
assert (
remove_outcome.IsSuccess()
), f"Failure: could not remove_container_item '{key}' from '{component_property_path}'"
def update_container_item(self, component_property_path: str, key: any, value: any):
"""
Used to update a container item at a specified key.
There are two types of containers; indexed and associative.
Indexed containers use integer key.
Associative containers utilize keys of the same type which could be any supported type.
:param component_property_path: String of component property. (e.g. 'Settings|Visible')
:param key: Zero index integer key or any supported type for associative container
:param value: Value to be set
:return: None
"""
assert (
self.is_property_container(component_property_path)
), f"Failure: '{component_property_path}' is not a property container"
update_outcome = self.property_tree_editor.update_container_item(component_property_path, key, value)
assert (
update_outcome.IsSuccess()
), f"Failure: could not update '{key}' in '{component_property_path}'"
def get_component_property_value(self, component_property_path: str):
"""
@@ -94,23 +250,36 @@ class EditorComponent:
"""
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", self.id)
def set_enabled(self, new_state: bool):
"""
Used to set the component enabled state
:param new_state: Boolean enabled True, disabled False
:return: None
"""
if new_state:
editor.EditorComponentAPIBus(bus.Broadcast, "EnableComponents", [self.id])
else:
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [self.id])
def disable_component(self):
"""
Used to disable the component using its id value.
Deprecation warning! Use set_enabled(False) instead as this method is in deprecation
:return: None
"""
warnings.warn("disable_component is deprecated, use set_enabled(False) instead.", DeprecationWarning)
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [self.id])
@staticmethod
def get_type_ids(component_names: list) -> list:
def get_type_ids(component_names: list, entity_type: EditorEntityType = EditorEntityType.GAME) -> list:
"""
Used to get type ids of given components list
:param: component_names: List of components to get type ids
:return: List of type ids of given components.
:param component_names: List of components to get type ids
:param entity_type: Entity_Type enum value Entity_Type.GAME is the default
:return: List of type ids of given components. Type id is a UUID as provided by the ebus call
"""
type_ids = editor.EditorComponentAPIBus(
bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, azlmbr.entity.EntityType().Game
)
bus.Broadcast, "FindComponentTypeIdsByEntityType", component_names, entity_type.value)
return type_ids
@@ -131,7 +300,7 @@ class EditorEntity:
"""
Entity class is used to create and interact with Editor Entities.
Example: To create Editor Entity, Use the code:
test_entity = Entity.create_editor_entity("TestEntity")
test_entity = EditorEntity.create_editor_entity("TestEntity")
# This creates a python object with 'test_entity' linked to entity name "TestEntity" in Editor.
# To add component, use:
test_entity.add_component(<COMPONENT_NAME>)
@@ -276,10 +445,9 @@ class EditorEntity:
:return: List of newly added components to the entity
"""
components = []
type_ids = EditorComponent.get_type_ids(component_names)
type_ids = EditorComponent.get_type_ids(component_names, EditorEntityType.GAME)
for type_id in type_ids:
new_comp = EditorComponent()
new_comp.type_id = type_id
new_comp = EditorComponent(type_id)
add_component_outcome = editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", self.id, [type_id]
)
@@ -291,6 +459,26 @@ class EditorEntity:
self.components.append(new_comp)
return components
def remove_component(self, component_name: str) -> None:
"""
Used to remove a component from Entity
:param component_name: String of component name to remove
:return: None
"""
self.remove_components([component_name])
def remove_components(self, component_names: list):
"""
Used to remove a list of components from Entity
:param component_names: List of component names to remove
:return: None
"""
component_ids = [component.id for component in self.get_components_of_type(component_names)]
remove_success = editor.EditorComponentAPIBus(bus.Broadcast, "RemoveComponents", component_ids)
assert (
remove_success
), f"Failure: could not remove component from entity '{self.get_name()}'"
def get_components_of_type(self, component_names: list) -> List[EditorComponent]:
"""
Used to get components of type component_name that already exists on Entity
@@ -298,10 +486,9 @@ class EditorEntity:
:return: List of Entity Component objects of given component name
"""
component_list = []
type_ids = EditorComponent.get_type_ids(component_names)
type_ids = EditorComponent.get_type_ids(component_names, EditorEntityType.GAME)
for type_id in type_ids:
component = EditorComponent()
component.type_id = type_id
component = EditorComponent(type_id)
get_component_of_type_outcome = editor.EditorComponentAPIBus(
bus.Broadcast, "GetComponentOfType", self.id, type_id
)
@@ -319,7 +506,7 @@ class EditorEntity:
:param component_name: Name of component to check for
:return: True, if entity has specified component. Else, False
"""
type_ids = EditorComponent.get_type_ids([component_name])
type_ids = EditorComponent.get_type_ids([component_name], EditorEntityType.GAME)
return editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", self.id, type_ids[0])
def get_start_status(self) -> int:
@@ -359,6 +546,21 @@ class EditorEntity:
set_status = self.get_start_status()
assert set_status == status_to_set, f"Failed to set start status of {desired_start_status} to {self.get_name}"
def is_locked(self) -> bool:
"""
Used to get the locked status of the entity
:return: Boolean True if locked False if not locked
"""
return editor.EditorEntityInfoRequestBus(bus.Event, "IsLocked", self.id)
def set_lock_state(self, is_locked: bool) -> None:
"""
Sets the lock state on the object to locked or not locked.
:param is_locked: True for locking, False to unlock.
:return: None
"""
editor.EditorEntityAPIBus(bus.Event, "SetLockState", self.id, is_locked)
def delete(self) -> None:
"""
Used to delete the Entity.
@@ -488,18 +690,6 @@ class EditorLevelEntity:
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:
"""
@@ -518,10 +708,9 @@ class EditorLevelEntity:
:return: List of newly added components to the level
"""
components = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
type_ids = EditorComponent.get_type_ids(component_names, EditorEntityType.LEVEL)
for type_id in type_ids:
new_comp = EditorComponent()
new_comp.type_id = type_id
new_comp = EditorComponent(type_id)
add_component_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", [type_id]
)
@@ -540,10 +729,9 @@ class EditorLevelEntity:
:return: List of Level Component objects of given component name
"""
component_list = []
type_ids = EditorLevelEntity.get_type_ids(component_names)
type_ids = EditorComponent.get_type_ids(component_names, EditorEntityType.LEVEL)
for type_id in type_ids:
component = EditorComponent()
component.type_id = type_id
component = EditorComponent(type_id)
get_component_of_type_outcome = editor.EditorLevelComponentAPIBus(
bus.Broadcast, "GetComponentOfType", type_id
)
@@ -562,7 +750,7 @@ class EditorLevelEntity:
: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])
type_ids = EditorComponent.get_type_ids([component_name], EditorEntityType.LEVEL)
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "HasComponentOfType", type_ids[0])
@staticmethod
@@ -572,5 +760,5 @@ class EditorLevelEntity:
: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])
type_ids = EditorComponent.get_type_ids([component_name], EditorEntityType.LEVEL)
return editor.EditorLevelComponentAPIBus(bus.Broadcast, "CountComponentsOfType", type_ids[0])
@@ -5,15 +5,22 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import collections.abc
from typing import List
from math import isclose
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.object
from typing import List
from math import isclose
import collections.abc
from editor_python_test_tools.utils import TestHelper as helper
def open_base_level():
helper.init_idle()
helper.open_level("Prefab", "Base")
def find_entity_by_name(entity_name):
@@ -48,8 +48,8 @@ def wait_for_propagation():
# This is a helper class which contains some of the useful information about a prefab instance.
class PrefabInstance:
def __init__(self, prefab_file_name: str = None, container_entity: EditorEntity = None):
self.prefab_file_name: str = prefab_file_name
def __init__(self, prefab_file_path: str = None, container_entity: EditorEntity = None):
self.prefab_file_path: str = prefab_file_path
self.container_entity: EditorEntity = container_entity
def __eq__(self, other):
@@ -66,7 +66,7 @@ class PrefabInstance:
See if this instance is valid to be used with other prefab operations.
:return: Whether the target instance is valid or not.
"""
return self.container_entity.id.IsValid() and self.prefab_file_name in Prefab.existing_prefabs
return self.container_entity.id.IsValid() and self.prefab_file_path in Prefab.existing_prefabs
def has_editor_prefab_component(self) -> bool:
"""
@@ -131,7 +131,7 @@ class PrefabInstance:
has_correct_parent = reparented_container_entity_parent_id.ToString() == parent_entity_id.ToString()
assert has_correct_parent, "Prefab Instance reparented is *not* under the expected parent entity"
current_instance_prefab = Prefab.get_prefab(self.prefab_file_name)
current_instance_prefab = Prefab.get_prefab(self.prefab_file_path)
current_instance_prefab.instances.remove(self)
self.container_entity = reparented_container_entity
@@ -161,46 +161,48 @@ class Prefab:
:param file_path: A unique file path of the target prefab.
:return: Whether the target prefab is loaded or not.
"""
return file_path in Prefab.existing_prefabs
for entry in Prefab.existing_prefabs:
Report.info(f"PrefabPath: '{entry}'")
return get_prefab_file_path(file_path) in Prefab.existing_prefabs
@classmethod
def prefab_exists(cls, file_path: str) -> bool:
"""
Check if a prefab exists in the directory for files of prefab tests.
:param file_name: A unique file name of the target prefab.
:param file_path: A unique file path of the target prefab.
:return: Whether the target prefab exists or not.
"""
return path.exists(get_prefab_file_path(file_path))
@classmethod
def get_prefab(cls, file_name: str) -> Prefab:
def get_prefab(cls, file_path: str) -> Prefab:
"""
Return a prefab which can be used immediately.
:param file_name: A unique file name of the target prefab.
:param file_path: A unique file path of the target prefab.
:return: The prefab with given file name.
"""
assert file_name, "Received an empty file_name"
if Prefab.is_prefab_loaded(file_name):
return Prefab.existing_prefabs[file_name]
assert file_path, "Received an empty file_path"
if Prefab.is_prefab_loaded(file_path):
return Prefab.existing_prefabs[get_prefab_file_path(file_path)]
else:
assert Prefab.prefab_exists(file_name), f"Attempted to get a prefab \"{file_name}\" that doesn't exist"
new_prefab = Prefab(file_name)
Prefab.existing_prefabs[file_name] = Prefab(file_name)
assert Prefab.prefab_exists(file_path), f"Attempted to get a prefab \"{file_path}\" that doesn't exist"
new_prefab = Prefab(file_path)
Prefab.existing_prefabs[new_prefab.file_path] = new_prefab
return new_prefab
@classmethod
def create_prefab(cls, entities: list[EditorEntity], file_name: str, prefab_instance_name: str=None) -> tuple(Prefab, PrefabInstance):
def create_prefab(cls, entities: list[EditorEntity], file_path: str, prefab_instance_name: str=None) -> tuple(Prefab, PrefabInstance):
"""
Create a prefab in memory and return it. The very first instance of this prefab will also be created.
:param entities: The entities that should form the new prefab (along with their descendants).
:param file_name: A unique file name of new prefab.
:param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as file_name.
:param file_path: A unique file path for new prefab.
:param prefab_instance_name: A name for the very first instance generated while prefab creation. The default instance name is the same as the file name in file_path.
:return: Created Prefab object and the very first PrefabInstance object owned by the prefab.
"""
assert not Prefab.is_prefab_loaded(file_name), f"Can't create Prefab '{file_name}' since the prefab already exists"
assert not Prefab.is_prefab_loaded(file_path), f"Can't create Prefab '{file_path}' since the prefab already exists"
new_prefab = Prefab(file_name)
new_prefab = Prefab(file_path)
entity_ids = [entity.id for entity in entities]
create_prefab_result = prefab.PrefabPublicRequestBus(bus.Broadcast, 'CreatePrefabInMemory', entity_ids, new_prefab.file_path)
assert create_prefab_result.IsSuccess(), f"Prefab operation 'CreatePrefab' failed. Error: {create_prefab_result.GetError()}"
@@ -210,15 +212,14 @@ class Prefab:
children_entity_ids = container_entity.get_children_ids()
assert len(children_entity_ids) == len(entities), f"Entity count of created prefab instance does *not* match the count of given entities."
if prefab_instance_name:
container_entity.set_name(prefab_instance_name)
wait_for_propagation()
new_prefab_instance = PrefabInstance(file_name, EditorEntity(container_entity_id))
new_prefab_instance = PrefabInstance(new_prefab.file_path, EditorEntity(container_entity_id))
if prefab_instance_name:
new_prefab_instance.container_entity.set_name(prefab_instance_name)
new_prefab.instances.add(new_prefab_instance)
Prefab.existing_prefabs[file_name] = new_prefab
Prefab.existing_prefabs[new_prefab.file_path] = new_prefab
return new_prefab, new_prefab_instance
@classmethod
@@ -250,7 +251,7 @@ class Prefab:
assert False, "Not all entities and descendants in target prefabs are deleted."
for instance in prefab_instances:
instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_name)
instance_deleted_prefab = Prefab.get_prefab(instance.prefab_file_path)
instance_deleted_prefab.instances.remove(instance)
instance = PrefabInstance()
@@ -290,8 +291,7 @@ class Prefab:
prefab_file_path = prefab.PrefabPublicRequestBus(bus.Broadcast, 'GetOwningInstancePrefabPath', duplicate_container_entity_id)
assert prefab_file_path, "Returned file path should *not* be empty."
prefab_file_name = Path(prefab_file_path).stem
duplicate_instance_prefab = Prefab.get_prefab(prefab_file_name)
duplicate_instance_prefab = Prefab.get_prefab(prefab_file_path)
duplicate_instance = PrefabInstance(prefab_file_path, EditorEntity(duplicate_container_entity_id))
duplicate_instance_prefab.instances.add(duplicate_instance)
duplicate_instances.append(duplicate_instance)
@@ -324,7 +324,7 @@ class Prefab:
wait_for_propagation()
instance_owner_prefab = Prefab.get_prefab(prefab_instance.prefab_file_name)
instance_owner_prefab = Prefab.get_prefab(prefab_instance.prefab_file_path)
instance_owner_prefab.instances.remove(prefab_instance)
prefab_instance = PrefabInstance()
@@ -346,13 +346,13 @@ class Prefab:
container_entity_id = instantiate_prefab_result.GetValue()
container_entity = EditorEntity(container_entity_id)
if name:
container_entity.set_name(name)
wait_for_propagation()
new_prefab_instance = PrefabInstance(self.file_path, EditorEntity(container_entity_id))
assert not new_prefab_instance in self.instances, "This prefab instance is already existed before this instantiation."
assert not new_prefab_instance in self.instances, "This prefab instance already existed before this instantiation."
if name:
new_prefab_instance.container_entity.set_name(name)
self.instances.add(new_prefab_instance)
assert new_prefab_instance.is_at_position(prefab_position), "This prefab instance is *not* at expected position."
@@ -35,3 +35,7 @@ class TestAutomation(TestAutomationBase):
def test_Multiplayer_AutoComponent_RPC(self, request, workspace, editor, launcher_platform):
from .tests import Multiplayer_AutoComponent_RPC as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_Multiplayer_SimpleNetworkLevelEntity(self, request, workspace, editor, launcher_platform):
from .tests import Multiplayer_SimpleNetworkLevelEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
@@ -62,15 +62,16 @@ def Multiplayer_AutoComponent_RPC():
Report.critical_result(TestSuccessFailTuples.find_network_player, player_id.IsValid())
# 4) Check the editor logs for expected and unexpected log output
# Authority->Autonomous RPC
PLAYERID_RPC_WAIT_TIME_SECONDS = 1.0 # The player id is sent from the server as soon as the player script is spawned. 1 second should be more than enough time to send/receive that RPC.
helper.succeed_if_log_line_found('EditorServer', 'Script: AutoComponent_RPC: Sending client PlayerNumber 1', section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS)
helper.succeed_if_log_line_found('Script', "AutoComponent_RPC: I'm Player #1", section_tracer.prints, PLAYERID_RPC_WAIT_TIME_SECONDS)
# Uncomment once editor game-play mode supports level entities with net-binding
#PLAYFX_RPC_WAIT_TIME_SECONDS = 1.1 # The server will send an RPC to play an fx on the client every second.
#helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity Activated on entity: NetLevelEntity", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
#helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
#helper.succeed_if_log_line_found('Script', "AutoComponent_RPC_NetLevelEntity: I'm a client playing some superficial fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
# Authority->Client RPC
PLAYFX_RPC_WAIT_TIME_SECONDS = 1.1 # The server will send an RPC to play an fx on the client every second.
helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity Activated on entity: NetLevelEntity", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
helper.succeed_if_log_line_found('EditorServer', "Script: AutoComponent_RPC_NetLevelEntity: Authority sending RPC to play some fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
helper.succeed_if_log_line_found('Script', "AutoComponent_RPC_NetLevelEntity: I'm a client playing some fx.", section_tracer.prints, PLAYFX_RPC_WAIT_TIME_SECONDS)
# Exit game mode
@@ -0,0 +1,76 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
# Test Case Title : Check that level entities with network bindings are properly replicated.
# Note: This test should be ran on a fresh editor run; some bugs with spawnables occur only on the first editor play-mode.
# fmt: off
class TestSuccessFailTuples():
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
find_network_player = ("Found network player", "Couldn't find network player")
# fmt: on
def Multiplayer_SimpleNetworkLevelEntity():
r"""
Summary:
Test to make sure that network entities in a level function and are replicated to clients as expected
Level Description:
- Static
1. NetLevelEntity. This is a networked entity which has a script attached which prints logs to ensure it's replicated.
Expected Outcome:
We should see logs stating that the net-sync'd level entity exists on both server and client.
:return:
"""
import azlmbr.legacy.general as general
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import Tracer
from editor_python_test_tools.utils import TestHelper as helper
from ly_remote_console.remote_console_commands import RemoteConsole as RemoteConsole
level_name = "SimpleNetworkLevelEntity"
player_prefab_name = "Player"
player_prefab_path = f"levels/multiplayer/{level_name}/{player_prefab_name}.network.spawnable"
helper.init_idle()
# 1) Open Level
helper.open_level("Multiplayer", level_name)
with Tracer() as section_tracer:
# 2) Enter game mode
helper.multiplayer_enter_game_mode(TestSuccessFailTuples.enter_game_mode, player_prefab_path.lower())
# 3) Make sure the network player was spawned
player_id = general.find_game_entity(player_prefab_name)
Report.critical_result(TestSuccessFailTuples.find_network_player, player_id.IsValid())
# 4) Check the editor logs for network spawnable errors
ATTEMPTING_INVALID_NETSPAWN_WAIT_TIME_SECONDS = 0.0 # The editor will try to net-spawn its networked level entity before it's even a client. Make sure this didn't happen.
helper.fail_if_log_line_found('NetworkEntityManager', "RequestNetSpawnableInstantiation: Requested spawnable Root.network.spawnable doesn't exist in the NetworkSpawnableLibrary. Please make sure it is a network spawnable", section_tracer.errors, ATTEMPTING_INVALID_NETSPAWN_WAIT_TIME_SECONDS)
# 5) Ensure the script graph attached to the level entity is running on the server
SCRIPTGRAPH_ENABLED_WAIT_TIME_SECONDS = 0.25
helper.succeed_if_log_line_found('EditorServer', "Script: SimpleNetworkLevelEntity: On Graph Start", section_tracer.prints, SCRIPTGRAPH_ENABLED_WAIT_TIME_SECONDS)
# Exit game mode
helper.exit_game_mode(TestSuccessFailTuples.exit_game_mode)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Multiplayer_SimpleNetworkLevelEntity)
@@ -127,7 +127,7 @@ def Collider_SameCollisionGroupSameCustomLayerCollide():
# Main Script
# 1) Load the level
helper.init_idle()
helper.open_level("physics", "Collider_SameCollisionGroupSameCustomLayerCollide")
helper.open_level("Physics", "Collider_SameCollisionGroupSameCustomLayerCollide")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
@@ -162,7 +162,7 @@ def ForceRegion_MultipleForcesInSameComponentCombineForces():
helper.init_idle()
# 1) Load Level
helper.open_level("physics", "ForceRegion_MultipleForcesInSameComponentCombineForces")
helper.open_level("Physics", "ForceRegion_MultipleForcesInSameComponentCombineForces")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
@@ -229,8 +229,8 @@ def Material_DefaultLibraryUpdatedAcrossLevels_after():
for test in test_list:
# 1) Open the correct level is open
helper.open_level(
"physics",
f"Material_DefaultLibraryUpdatedAcrossLevels\\{test.level}"
"Physics",
os.path.join("Material_DefaultLibraryUpdatedAcrossLevels", str(test.level))
)
# 2) Enter Game Mode
@@ -189,7 +189,7 @@ def Material_DefaultLibraryUpdatedAcrossLevels_before():
# 1) Open the correct level is open
helper.open_level(
"Physics",
f"Material_DefaultLibraryUpdatedAcrossLevels\\{test.level}"
os.path.join("Material_DefaultLibraryUpdatedAcrossLevels", str(test.level))
)
# 2) Enter Game Mode
@@ -252,10 +252,8 @@ def Material_LibraryUpdatedAcrossLevels():
for test in test_list:
# 1) Open the correct level for the test
helper.open_level(
"physics",
"Material_LibraryUpdatedAcrossLevels\\Material_LibraryUpdatedAcrossLevels_{}".format(
test.level_index
),
"Physics",
os.path.join("Material_LibraryUpdatedAcrossLevels", str(test.level_index))
)
# 2) Open Game Mode
@@ -106,7 +106,7 @@ def ScriptCanvas_SpawnEntityWithPhysComponents():
# Main Script
helper.init_idle()
# 1) Open Level
helper.open_level("physics", "ScriptCanvas_SpawnEntityWithPhysComponents")
helper.open_level("Physics", "ScriptCanvas_SpawnEntityWithPhysComponents")
# 2) Enter Game Mode
helper.enter_game_mode(Tests.enter_game_mode)
@@ -38,6 +38,10 @@ class TestAutomation(TestAutomationBase):
from Prefab.tests.instantiate_prefab import InstantiatePrefab_ContainingASingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_InstantiatePrefab_FromCreatedPrefabWithSingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.instantiate_prefab import InstantiatePrefab_FromCreatedPrefabWithSingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
def test_DeletePrefab_ContainingASingleEntity(self, request, workspace, editor, launcher_platform):
from Prefab.tests.delete_prefab import DeletePrefab_ContainingASingleEntity as test_module
self._run_prefab_test(request, workspace, editor, test_module)
@@ -45,8 +45,12 @@ class TestAutomationNoAutoTestMode(EditorTestSuite):
class test_InstantiatePrefab_ContainingASingleEntity(EditorSharedTest):
from .tests.instantiate_prefab import InstantiatePrefab_ContainingASingleEntity as test_module
class test_InstantiatePrefab_FromCreatedPrefabWithSingleEntity(EditorSharedTest):
from .tests.instantiate_prefab import InstantiatePrefab_FromCreatedPrefabWithSingleEntity as test_module
class test_DeletePrefab_ContainingASingleEntity(EditorSharedTest):
from .tests.delete_prefab import DeletePrefab_ContainingASingleEntity as test_module
class test_DuplicatePrefab_ContainingASingleEntity(EditorSharedTest):
from .tests.duplicate_prefab import DuplicatePrefab_ContainingASingleEntity as test_module
from .tests.duplicate_prefab import DuplicatePrefab_ContainingASingleEntity as test_module
@@ -13,7 +13,8 @@ def CreatePrefab_UnderAnEntity():
Test is successful if the new instanced prefab of the child has the parent entity id
"""
CAR_PREFAB_FILE_NAME = 'car_prefab'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
@@ -7,7 +7,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
def CreatePrefab_WithSingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report
@@ -7,7 +7,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
def DeletePrefab_ContainingASingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
@@ -7,8 +7,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
def DetachPrefab_UnderAnotherPrefab():
CAR_PREFAB_FILE_NAME = 'car_prefab2'
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab2'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
WHEEL_PREFAB_FILE_NAME = Path(__file__).stem + 'wheel_prefab'
import editor_python_test_tools.pyside_utils as pyside_utils
@@ -7,7 +7,8 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
def DuplicatePrefab_ContainingASingleEntity():
CAR_PREFAB_FILE_NAME = 'car_prefab'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.prefab_utils import Prefab
@@ -0,0 +1,34 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
def InstantiatePrefab_FromCreatedPrefabWithSingleEntity():
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
from editor_python_test_tools.editor_entity_utils import EditorEntity
from editor_python_test_tools.utils import Report
from editor_python_test_tools.prefab_utils import Prefab
import Prefab.tests.PrefabTestUtils as prefab_test_utils
prefab_test_utils.open_base_tests_level()
# Creates a new entity at the root level
car_entity = EditorEntity.create_editor_entity()
car_prefab_entities = [car_entity]
# Creates a prefab from the new entity
car_prefab, car_prefab_instance = Prefab.create_prefab(car_prefab_entities, CAR_PREFAB_FILE_NAME)
# Instantiate another instance and verify
car_prefab_instance_2 = car_prefab.instantiate()
assert car_prefab_instance_2.is_valid(), "Failed to instantiate prefab"
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(InstantiatePrefab_FromCreatedPrefabWithSingleEntity)
@@ -7,8 +7,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
def ReparentPrefab_UnderAnotherPrefab():
CAR_PREFAB_FILE_NAME = 'car_prefab'
WHEEL_PREFAB_FILE_NAME = 'wheel_prefab'
from pathlib import Path
CAR_PREFAB_FILE_NAME = Path(__file__).stem + 'car_prefab'
WHEEL_PREFAB_FILE_NAME = Path(__file__).stem + 'wheel_prefab'
import editor_python_test_tools.pyside_utils as pyside_utils
@@ -93,7 +93,7 @@ def Terrain_World_ConfigurationWorks():
# 5) Set the base Terrain World values
world_bounds_max = azmath.Vector3(1100.0, 1100.0, 1100.0)
world_bounds_min = azmath.Vector3(10.0, 10.0, 10.0)
height_query_resolution = azmath.Vector2(1.0, 1.0)
height_query_resolution = 1.0
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Max)", world_bounds_max)
hydra.set_component_property_value(terrain_world_component, "Configuration|World Bounds (Min)", world_bounds_min)
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
@@ -148,7 +148,7 @@ def Terrain_World_ConfigurationWorks():
# 13) Check height value is the expected one when query resolution is changed
testpoint = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
height_query_resolution = azmath.Vector2(0.5, 0.5)
height_query_resolution = 0.5
hydra.set_component_property_value(terrain_world_component, "Configuration|Height Query Resolution (m)", height_query_resolution)
general.idle_wait_frames(1)
testpoint2 = terrain.TerrainDataRequestBus(bus.Broadcast, 'GetHeightFromFloats', 10.5, 10.5, CLAMP)
@@ -165,4 +165,3 @@ if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Terrain_World_ConfigurationWorks)
@@ -6,7 +6,7 @@
#
#
ly_get_list_relative_pal_filename(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME})
o3de_pal_dir(pal_dir ${CMAKE_CURRENT_LIST_DIR}/Platform/${PAL_PLATFORM_NAME} ${gem_restricted_path} ${gem_path} ${gem_parent_relative_path})
include(${pal_dir}/PAL_${PAL_PLATFORM_NAME_LOWERCASE}.cmake) # for PAL_TRAIT_WHITEBOX Traits
@@ -93,15 +93,19 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
)
ly_add_pytest(
NAME AssetPipelineTests.AssetBundler
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_bundler_batch_tests.py
NAME AssetPipelineTests.AssetBuilder
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_builder_tests.py
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::AssetBundlerBatch
)
set(SUPPORTED_PLATFORMS "Windows" "Mac")
if (NOT "${PAL_PLATFORM_NAME}" IN_LIST SUPPORTED_PLATFORMS)
return()
endif()
ly_add_pytest(
NAME AssetPipelineTests.BundleMode
@@ -117,16 +121,16 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
)
ly_add_pytest(
NAME AssetPipelineTests.AssetBuilder
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_builder_tests.py
NAME AssetPipelineTests.AssetBundler
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_bundler_batch_tests.py
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::AssetBundlerBatch
)
ly_add_pytest(
NAME AssetPipelineTests.MissingDependency
PATH ${CMAKE_CURRENT_LIST_DIR}/missing_dependency_tests.py
@@ -136,5 +140,5 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
RUNTIME_DEPENDENCIES
AZ::AssetProcessorBatch
)
endif()
@@ -108,7 +108,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
"""
helper = bundler_batch_helper
seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list
asset = r"levels\testdependencieslevel\level.pak"
asset = r"levels\testdependencieslevel\testdependencieslevel.spawnable"
# Create Asset list
helper.call_assetLists(
@@ -191,7 +191,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
"""
helper = bundler_batch_helper
seed_list = os.path.join(workspace.paths.engine_root(), "Assets", "Engine", "SeedAssetList.seed") # Engine seed list
asset = r"levels\testdependencieslevel\level.pak"
asset = r"levels\testdependencieslevel\testdependencieslevel.spawnable"
# Useful bundle locations / names (2 for comparing contents)
# fmt:off
@@ -924,7 +924,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
# Create a seed file
helper.call_seeds(
seedListFile=helper["seed_list_file"],
addSeed=r"levels\testdependencieslevel\level.pak",
addSeed=r"levels\testdependencieslevel\testdependencieslevel.spawnable",
platform="pc",
)
@@ -947,9 +947,9 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
# Specifying platform but not "add" or "remove" should fail
result, _ = helper.call_assetLists(
assetListFile=helper["asset_info_file_request"],
allowOverwrites="",
seedListFile=helper["seed_list_file"],
platform="pc",
allowOverwrites="",
)
assert result, "Overwriting with override threw an error"
@@ -982,7 +982,7 @@ class TestsAssetBundlerBatch_WindowsAndMac(object):
request.addfinalizer(lambda: fs.delete([bundle_result_path], True, False))
bundles_folder = os.path.join(workspace.paths.project(), "Bundles")
level_pak = r"levels\testdependencieslevel\level.pak"
level_pak = r"levels\testdependencieslevel\testdependencieslevel.spawnable"
bundle_request_path = os.path.join(bundles_folder, "bundle.pak")
bundle_result_path = os.path.join(bundles_folder,
helper.platform_file_name("bundle.pak", workspace.asset_processor_platform))
@@ -23,12 +23,11 @@ from ..ap_fixtures.timeout_option_fixture import timeout_option_fixture as timeo
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize('launcher_platform', ['windows_editor'])
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['auto_test'])
@pytest.mark.parametrize('level', ['TestDependenciesLevel'])
class TestBundleMode(object):
def test_bundle_mode_with_levels_mounts_bundles_correctly(self, request, editor, level, launcher_platform,
asset_processor, workspace, bundler_batch_helper):
level_pak = os.path.join("levels", level, "level.pak")
level_pak = os.path.join("levels", level, "TestDependenciesLevel.spawnable")
bundles_folder = os.path.join(workspace.paths.project(), "Bundles")
bundle_request_path = os.path.join(bundles_folder, "bundle.pak")
bundle_result_path = os.path.join(bundles_folder,
@@ -182,7 +182,7 @@ class TestAutomationBase:
@staticmethod
def _kill_ly_processes(include_asset_processor=True):
LY_PROCESSES = [
'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher'
'Editor', 'Profiler', 'RemoteConsole', 'AutomatedTesting.ServerLauncher', 'o3de'
]
AP_PROCESSES = [
'AssetProcessor', 'AssetProcessorBatch', 'AssetBuilder', 'CrySCompileServer',
@@ -19,12 +19,16 @@ def find_nodes_matching_entity_component(component_name, entity_id):
:param entity_id: The entity ID to search for the given component node on
:return List of matching nodes
"""
component_type_id = hydra.get_component_type_id(component_name)
component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', entity_id,
hydra.get_component_type_id(component_name))
component_type_id)
if component.IsSuccess():
component_id = component.GetValue()
component_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
component_id)
if component_node:
print(f"{component_name} node found on entity {entity_id.ToString()}")
else:
print(f"Failed to find {component_name} node on entity {entity_id.ToString()}")
return component_node
@@ -9,10 +9,10 @@
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_FOUNDATION_TEST_SUPPORTED)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Main_Optimized
NAME AutomatedTesting::EditorTests_Main
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
PYTEST_MARKS "not REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
@@ -27,7 +27,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE main
TEST_SERIAL
TEST_REQUIRES gpu
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main_Optimized.py
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Main.py
PYTEST_MARKS "REQUIRES_gpu"
RUNTIME_DEPENDENCIES
Legacy::Editor
@@ -37,17 +37,4 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
Editor
)
ly_add_pytest(
NAME AutomatedTesting::EditorTests_Sandbox_Optimized
TEST_SUITE sandbox
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Sandbox_Optimized.py
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
AutomatedTesting.Assets
COMPONENT
Editor
)
endif()
@@ -59,8 +59,8 @@ def AssetBrowser_SearchFiltering():
import azlmbr.legacy.general as general
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def verify_files_appeared(model, allowed_asset_extensions, parent_index=QtCore.QModelIndex()):
indexes = [parent_index]
@@ -80,8 +80,7 @@ def AssetBrowser_SearchFiltering():
return True
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
hydra.open_base_level()
# 2) Open Asset Browser (if not opened already)
editor_window = pyside_utils.get_editor_main_window()
@@ -53,6 +53,7 @@ def AssetBrowser_TreeNavigation():
import azlmbr.legacy.general as general
import editor_python_test_tools.pyside_utils as pyside_utils
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
@@ -69,8 +70,7 @@ def AssetBrowser_TreeNavigation():
file_path = ("AutomatedTesting", "Assets", "ImageGradients", "image_grad_test_gsi.png")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
hydra.open_base_level()
# 2) Open Asset Browser (if not opened already)
editor_window = pyside_utils.get_editor_main_window()
@@ -215,8 +215,7 @@ def AssetPicker_UI_UX():
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
hydra.open_base_level()
# 2) Create entity and add Mesh component
entity_position = math.Vector3(125.0, 136.0, 32.0)
@@ -56,19 +56,17 @@ def BasicEditorWorkflows_ExistingLevel_EntityComponentCRUD():
06. delete parent entity
"""
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.editor_entity_utils import EditorEntity
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.object
# 01. load an existing level
test_level = 'Simple'
general.open_level_no_prompt(test_level)
Report.result(Tests.load_level, general.get_current_level_name() == test_level)
hydra.open_base_level()
# 02. create parent entity and set name
# Delete any exiting entity and Create a new Entity at the root level
@@ -31,7 +31,7 @@ class Tests:
"Component removed from entity successfully",
"Failed to remove component from entity"
)
level_saved_and_exported = (
saved_and_exported = (
"Level saved and exported successfully",
"Failed to save/export level"
)
@@ -52,8 +52,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD():
- A new entity can be created
- Entity hierarchy can be adjusted
- Components can be added/removed/updated
- Level can be saved
- Level can be exported
- Level can be saved/exported
Note:
- This test file must be called from the O3DE Editor command terminal
@@ -70,7 +69,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD():
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math
import azlmbr.paths
import azlmbr.paths as paths
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
@@ -84,7 +83,7 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD():
return None
# 1) Create a new level
level = "tmp_level"
lvl_name = "tmp_level"
editor_window = pyside_utils.get_editor_main_window()
new_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "New Level")
pyside_utils.trigger_action_async(new_level_action)
@@ -95,23 +94,24 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD():
Report.info("New Level dialog opened")
grp_box = new_level_dlg.findChild(QtWidgets.QGroupBox, "STATIC_GROUP1")
level_name = grp_box.findChild(QtWidgets.QLineEdit, "LEVEL")
level_name.setText(level)
level_name.setText(lvl_name)
button_box = new_level_dlg.findChild(QtWidgets.QDialogButtonBox, "buttonBox")
button_box.button(QtWidgets.QDialogButtonBox.Ok).click()
# Verify new level was created successfully
level_create_success = await pyside_utils.wait_for_condition(lambda: editor.EditorToolsApplicationRequestBus(
bus.Broadcast, "GetCurrentLevelName") == level, 5.0)
bus.Broadcast, "GetCurrentLevelName") == lvl_name, 5.0)
Report.critical_result(Tests.level_created, level_create_success)
# 2) Delete existing entities, and create and manipulate new entities via Entity Inspector
search_filter = azlmbr.entity.SearchFilter()
all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
entity_outliner_widget = editor_window.findChild(QtWidgets.QWidget, "OutlinerWidgetUI")
entity_outliner_widget = editor_window.findChild(QtWidgets.QWidget, "EntityOutlinerWidgetUI")
outliner_object_list = entity_outliner_widget.findChild(QtWidgets.QWidget, "m_objectList_Contents")
outliner_tree = outliner_object_list.findChild(QtWidgets.QWidget, "m_objectTree")
await pyside_utils.trigger_context_menu_entry(outliner_tree, "Create entity")
outliner_viewport = outliner_tree.findChild(QtWidgets.QWidget, "qt_scrollarea_viewport")
await pyside_utils.trigger_context_menu_entry(outliner_viewport, "Create entity")
# Find the new entity
parent_entity_id = find_entity_by_name("Entity1")
@@ -153,14 +153,10 @@ def BasicEditorWorkflows_LevelEntityComponentCRUD():
save_level_action = pyside_utils.get_action_for_menu_path(editor_window, "File", "Save")
pyside_utils.trigger_action_async(save_level_action)
# 5) Export the level
export_action = pyside_utils.get_action_for_menu_path(editor_window, "Game", "Export to Engine")
pyside_utils.trigger_action_async(export_action)
level_pak_file = os.path.join(
"AutomatedTesting", "Levels", level, "level.pak"
)
export_success = await pyside_utils.wait_for_condition(lambda: os.path.exists(level_pak_file), 5.0)
Report.result(Tests.level_saved_and_exported, export_success)
# 5) Verify the save/export of the level
level_prefab_path = os.path.join(paths.products, "levels", lvl_name, f"{lvl_name}.spawnable")
success = await pyside_utils.wait_for_condition(lambda: os.path.exists(level_prefab_path), 5.0)
Report.result(Tests.saved_and_exported, success)
run_test()
@@ -63,7 +63,7 @@ def ComponentCRUD_Add_Delete_Components():
:return: None
"""
from PySide2 import QtWidgets, QtTest, QtCore
from PySide2 import QtWidgets, QtTest
from PySide2.QtCore import Qt
import azlmbr.legacy.general as general
@@ -74,7 +74,6 @@ def ComponentCRUD_Add_Delete_Components():
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
async def add_component(component_name):
pyside_utils.click_button_async(add_comp_btn)
@@ -88,8 +87,7 @@ def ComponentCRUD_Add_Delete_Components():
QtTest.QTest.keyClick(tree, Qt.Key_Enter, Qt.NoModifier)
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
hydra.open_base_level()
# 2) Create entity
entity_position = math.Vector3(125.0, 136.0, 32.0)

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