Merge branch 'development' into ExposeLodControls

This commit is contained in:
Kyle Birnbaum
2021-08-20 12:45:00 -07:00
committed by GitHub
3092 changed files with 61511 additions and 87536 deletions
+1
View File
@@ -25,3 +25,4 @@ TestResults/**
*.swatches
/imgui.ini
/scripts/project_manager/logs/
/AutomatedTesting/Gem/PythonTests/scripting/TestResults

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

+3 -3
View File
@@ -212,10 +212,10 @@
"Configuration": {
"ModelAsset": {
"assetId": {
"guid": "{935F694A-8639-515B-8133-81CDC7948E5B}",
"subId": 277333723
"guid": "{0CD745C0-6AA8-569A-A68A-73A3270986C4}",
"subId": 277889906
},
"assetHint": "objects/groudplane/groundplane_521x521m.azmodel"
"assetHint": "objects/groudplane/groundplane_512x512m.azmodel"
}
}
}
@@ -2771,6 +2771,80 @@
<source>VECTOR2_CREATEONE_OUTPUT0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_NAME">
<source>VECTOR2_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector2 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_CATEGORY">
<source>VECTOR2_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUT_NAME">
<source>VECTOR2_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTOL_IN_NAME">
<source>VECTOR2_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR2_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector2</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR2_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR2_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector2 C++ Type: Vector2*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR2_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR2_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR2_GETPROJECTED_NAME">
<source>VECTOR2_GETPROJECTED_NAME</source>
<comment>Class/Bus: Vector2 Event/Method: GetProjected</comment>
@@ -32262,6 +32336,80 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<source>VECTOR4_GETRECIPROCAL_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_NAME">
<source>VECTOR4_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector4 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_CATEGORY">
<source>VECTOR4_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUT_NAME">
<source>VECTOR4_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTOL_IN_NAME">
<source>VECTOR4_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR4_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector4</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR4_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR4_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector4 C++ Type: Vector4*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR4_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR4_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR4_AXISX_NAME">
<source>VECTOR4_AXISX_NAME</source>
<comment>Class/Bus: Vector4 Event/Method: CreateAxisX</comment>
@@ -37469,6 +37617,80 @@ An Entity can be selected by using the pick button, or by dragging an Entity fro
<source>VECTOR3_GETRECIPROCAL_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_NAME">
<source>VECTOR3_DIRECTIONTO_NAME</source>
<comment>Class/Bus: Vector3 Event/Method: DirectionTo</comment>
<translation type="unfinished">Get Direction Vector</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_TOOLTIP</source>
<translation>Returns a direction vector between two points and the distance between them, by default the direction will be normalized, it may be optionally scaled using the Scale parameter if different from 1.0</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_CATEGORY">
<source>VECTOR3_DIRECTIONTO_CATEGORY</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUT_NAME">
<source>VECTOR3_DIRECTIONTO_OUT_NAME</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUT_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUT_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTOL_IN_NAME">
<source>VECTOR3_DIRECTIONTOL_IN_NAME</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_IN_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_IN_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT0_NAME">
<source>VECTOR3_DIRECTIONTO_OUTPUT0_NAME</source>
<comment>C++ Type: const Vector3</comment>
<translation type="unfinished">Direction</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT0_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUTPUT0_TOOLTIP</source>
<translation>The direction between To and From normalized and optionally scaled</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT1_NAME">
<source>VECTOR3_DIRECTIONTO_OUTPUT1_NAME</source>
<comment>C++ Type: float</comment>
<translation type="unfinished">Distance</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_OUTPUT1_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_OUTPUT1_TOOLTIP</source>
<translation>The distance between To and From</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM0_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM0_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">From</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM0_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM0_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM1_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM1_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">To</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM1_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM1_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM2_NAME">
<source>VECTOR3_DIRECTIONTO_PARAM2_NAME</source>
<comment>Simple Type: Vector3 C++ Type: Vector3*</comment>
<translation type="unfinished">Scale</translation>
</message>
<message id="VECTOR3_DIRECTIONTO_PARAM2_TOOLTIP">
<source>VECTOR3_DIRECTIONTO_PARAM2_TOOLTIP</source>
<translation></translation>
</message>
<message id="VECTOR3_PROJECT_NAME">
<source>VECTOR3_PROJECT_NAME</source>
<comment>Class/Bus: Vector3 Event/Method: Project</comment>
@@ -0,0 +1,19 @@
{
"Amazon": {
"AssetProcessor": {
"Settings": {
"RC cgf": {
"ignore": true
},
"RC fbx": {
"ignore": true
},
"ScanFolder AtomTestData": {
"watch": "@ENGINEROOT@/Gems/Atom/TestData",
"recursive": 1,
"order": 1000
}
}
}
}
}
+6
View File
@@ -57,6 +57,12 @@ if (PAL_TRAIT_BUILD_HOST_TOOLS)
TARGETS Editor
VARIANTS Tools)
# The Material Editor needs the Lyshine "Tools" gem variant for the custom LyShine pass
ly_enable_gems(
PROJECT_NAME AutomatedTesting GEMS LyShine
TARGETS MaterialEditor
VARIANTS Tools)
# The pipeline tools use "Builders" gem variants:
ly_enable_gems(
PROJECT_NAME AutomatedTesting GEM_FILE enabled_gems.cmake
@@ -12,6 +12,11 @@
################################################################################
if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
# Only enable AWS automated tests on Windows
if(NOT "${PAL_PLATFORM_NAME}" STREQUAL "Windows")
return()
endif()
# Enable after installing NodeJS and CDK on jenkins Windows AMI.
ly_add_pytest(
NAME AutomatedTesting::AWSTests
@@ -0,0 +1,26 @@
# AWS Gem Automation Tests
## Prerequisites
1. Build the O3DE Editor and AutomatedTesting.GameLauncher in Profile.
2. 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 Cloud Development Kit (CDK)](https://docs.aws.amazon.com/cdk/latest/guide/getting_started.html#getting_started_install) is installed.
## Deploy CDK Applications
1. Go to the AWS IAM console and create an IAM role called o3de-automation-tests which adds your own account as as a trusted entity and uses the "AdministratorAccess" permissions policy.
2. Copy {engine_root}\scripts\build\Platform\Windows\deploy_cdk_applications.cmd to your engine root folder.
3. Open a Command Prompt window at the engine root and set the following environment variables:
Set O3DE_AWS_PROJECT_NAME=AWSAUTO
Set O3DE_AWS_DEPLOY_REGION=us-east-1
Set ASSUME_ROLE_ARN="arn:aws:iam::{your_aws_account_id}:role/o3de-automation-tests"
Set COMMIT_ID=HEAD
4. Deploy the CDK applications for AWS gems by running deploy_cdk_applications.cmd in the same Command Prompt window.
5. Edit AWS\common\constants.py to replace the assume role ARN with your own:
arn:aws:iam::{your_aws_account_id}:role/o3de-automation-tests
## Run Automation Tests
### CLI
Open a Command Prompt window at the engine root and run the following CLI command:
python\python.cmd -m pytest {path_to_the_test_file} --build-directory {directory_to_the_profile_build}
### Pycharm
You can also run any specific automation test directly from Pycharm by providing the "--build-directory" argument in the Run Configuration.
@@ -9,18 +9,18 @@ import logging
import os
import pytest
import typing
from datetime import datetime
import ly_test_tools.log.log_monitor
from AWS.common import constants
from .aws_metrics_custom_thread import AWSMetricsThread
# fixture imports
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
from .aws_metrics_utils import aws_metrics_utils
from .aws_metrics_custom_thread import AWSMetricsThread
AWS_METRICS_FEATURE_NAME = 'AWSMetrics'
GAME_LOG_NAME = 'Game.log'
CONTEXT_VARIABLE = ['-c', 'batch_processing=true']
logger = logging.getLogger(__name__)
@@ -36,7 +36,7 @@ def setup(launcher: pytest.fixture,
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
# Initialize the log monitor.
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
@@ -73,23 +73,26 @@ def monitor_metrics_submission(log_monitor: pytest.fixture) -> None:
f'unexpected_lines values: {unexpected_lines}')
def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, stack_name: str) -> None:
def query_metrics_from_s3(aws_metrics_utils: pytest.fixture, resource_mappings: pytest.fixture, stack_name: str) -> None:
"""
Verify that the metrics events are delivered to the S3 bucket and can be queried.
aws_metrics_utils: aws_metrics_utils fixture.
stack_name: name of the CloudFormation stack.
:param aws_metrics_utils: aws_metrics_utils fixture.
:param resource_mappings: resource_mappings fixture.
:param stack_name: name of the CloudFormation stack.
"""
analytics_bucket_name = aws_metrics_utils.get_analytics_bucket_name(stack_name)
aws_metrics_utils.verify_s3_delivery(analytics_bucket_name)
aws_metrics_utils.verify_s3_delivery(
resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsBucketName')
)
logger.info('Metrics are sent to S3.')
aws_metrics_utils.run_glue_crawler(f'{stack_name}-EventsCrawler')
aws_metrics_utils.run_glue_crawler(
resource_mappings.get_resource_name_id('AWSMetrics.EventsCrawlerName'))
# Remove the events_json table if exists so that the sample query can create a table with the same name.
aws_metrics_utils.delete_table(f'{stack_name}-eventsdatabase', 'events_json')
aws_metrics_utils.run_named_queries(f'{stack_name}-AthenaWorkGroup')
logger.info('Query metrics from S3 successfully.')
# Empty the S3 bucket. S3 buckets can only be deleted successfully when it doesn't contain any object.
aws_metrics_utils.empty_batch_analytics_bucket(analytics_bucket_name)
def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: str, start_time: datetime) -> None:
"""
@@ -102,7 +105,7 @@ def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: st
'AWS/Lambda',
'Invocations',
[{'Name': 'FunctionName',
'Value': f'{stack_name}-AnalyticsProcessingLambdaName'}],
'Value': f'{stack_name}-AnalyticsProcessingLambda'}],
start_time)
logger.info('AnalyticsProcessingLambda metrics are sent to CloudWatch.')
@@ -115,50 +118,59 @@ def verify_operational_metrics(aws_metrics_utils: pytest.fixture, stack_name: st
logger.info('EventsProcessingLambda metrics are sent to CloudWatch.')
def start_kinesis_analytics_application(aws_metrics_utils: pytest.fixture, stack_name: str) -> None:
def update_kinesis_analytics_application_status(aws_metrics_utils: pytest.fixture,
resource_mappings: pytest.fixture, start_application: bool) -> None:
"""
Start the Kinesis analytics application for real-time analytics.
aws_metrics_utils: aws_metrics_utils fixture.
stack_name: name of the CloudFormation stack.
Update the Kinesis analytics application to start or stop it.
:param aws_metrics_utils: aws_metrics_utils fixture.
:param resource_mappings: resource_mappings fixture.
:param start_application: whether to start or stop the application.
"""
analytics_application_name = f'{stack_name}-AnalyticsApplication'
aws_metrics_utils.start_kinesis_data_analytics_application(analytics_application_name)
if start_application:
aws_metrics_utils.start_kinesis_data_analytics_application(
resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsApplicationName'))
else:
aws_metrics_utils.stop_kinesis_data_analytics_application(
resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsApplicationName'))
@pytest.mark.SUITE_periodic
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['AWS/Metrics'])
@pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME])
@pytest.mark.usefixtures('resource_mappings')
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
@pytest.mark.usefixtures('aws_credentials')
@pytest.mark.usefixtures('resource_mappings')
@pytest.mark.parametrize('assume_role_arn', [constants.ASSUME_ROLE_ARN])
@pytest.mark.parametrize('feature_name', [AWS_METRICS_FEATURE_NAME])
@pytest.mark.parametrize('level', ['AWS/Metrics'])
@pytest.mark.parametrize('profile_name', ['AWSAutomationTest'])
@pytest.mark.parametrize('region_name', ['us-west-2'])
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
@pytest.mark.usefixtures('cdk')
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
@pytest.mark.parametrize('deployment_params', [CONTEXT_VARIABLE])
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('region_name', [constants.AWS_REGION])
@pytest.mark.parametrize('resource_mappings_filename', [constants.AWS_RESOURCE_MAPPING_FILE_NAME])
@pytest.mark.parametrize('session_name', [constants.SESSION_NAME])
@pytest.mark.parametrize('stacks', [[f'{constants.AWS_PROJECT_NAME}-{AWS_METRICS_FEATURE_NAME}-{constants.AWS_REGION}']])
class TestAWSMetricsWindows(object):
"""
Test class to verify the real-time and batch analytics for metrics.
"""
@pytest.mark.parametrize('destroy_stacks_on_teardown', [False])
def test_realtime_and_batch_analytics(self,
level: str,
launcher: pytest.fixture,
asset_processor: pytest.fixture,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
cdk: pytest.fixture,
resource_mappings: pytest.fixture,
stacks: typing.List,
aws_metrics_utils: pytest.fixture):
"""
Verify that the metrics events are sent to CloudWatch and S3 for analytics.
"""
# Start Kinesis analytics application on a separate thread to avoid blocking the test.
kinesis_analytics_application_thread = AWSMetricsThread(target=start_kinesis_analytics_application,
args=(aws_metrics_utils, cdk.stacks[0]))
kinesis_analytics_application_thread = AWSMetricsThread(target=update_kinesis_analytics_application_status,
args=(aws_metrics_utils, resource_mappings, True))
kinesis_analytics_application_thread.start()
# Clear the analytics bucket objects before sending new metrics.
aws_metrics_utils.empty_bucket(
resource_mappings.get_resource_name_id('AWSMetrics.AnalyticsBucketName'))
log_monitor = setup(launcher, asset_processor)
# Kinesis analytics application needs to be in the running state before we start the game launcher.
@@ -177,18 +189,22 @@ class TestAWSMetricsWindows(object):
start_time)
logger.info('Real-time metrics are sent to CloudWatch.')
# Run time-consuming verifications on separate threads to avoid blocking the test.
verification_threads = list()
verification_threads.append(
AWSMetricsThread(target=query_metrics_from_s3, args=(aws_metrics_utils, cdk.stacks[0])))
verification_threads.append(
AWSMetricsThread(target=verify_operational_metrics, args=(aws_metrics_utils, cdk.stacks[0], start_time)))
for thread in verification_threads:
# Run time-consuming operations on separate threads to avoid blocking the test.
operational_threads = list()
operational_threads.append(
AWSMetricsThread(target=query_metrics_from_s3,
args=(aws_metrics_utils, resource_mappings, stacks[0])))
operational_threads.append(
AWSMetricsThread(target=verify_operational_metrics,
args=(aws_metrics_utils, stacks[0], start_time)))
operational_threads.append(
AWSMetricsThread(target=update_kinesis_analytics_application_status,
args=(aws_metrics_utils, resource_mappings, False)))
for thread in operational_threads:
thread.start()
for thread in verification_threads:
for thread in operational_threads:
thread.join()
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
def test_unauthorized_user_request_rejected(self,
level: str,
launcher: pytest.fixture,
@@ -198,7 +198,7 @@ class AWSMetricsUtils:
assert state == 'SUCCEEDED', f'Failed to run the named query {named_query.get("Name", {})}'
def empty_batch_analytics_bucket(self, bucket_name: str) -> None:
def empty_bucket(self, bucket_name: str) -> None:
"""
Empty the S3 bucket following:
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/migrations3.html
@@ -211,25 +211,18 @@ class AWSMetricsUtils:
for key in bucket.objects.all():
key.delete()
def get_analytics_bucket_name(self, stack_name: str) -> str:
def delete_table(self, database_name: str, table_name: str) -> None:
"""
Get the name of the deployed S3 bucket.
:param stack_name: Name of the CloudFormation stack.
:return: Name of the deployed S3 bucket.
Delete an existing Glue table.
:param database_name: Name of the Glue database.
:param table_name: Name of the table to delete.
"""
client = self._aws_util.client('cloudformation')
response = client.describe_stack_resources(
StackName=stack_name
client = self._aws_util.client('glue')
client.delete_table(
DatabaseName=database_name,
Name=table_name
)
resources = response.get('StackResources', [])
for resource in resources:
if resource.get('ResourceType') == 'AWS::S3::Bucket':
return resource.get('PhysicalResourceId', '')
return ''
@pytest.fixture(scope='function')
@@ -45,7 +45,8 @@ class KinesisAnalyticsApplicationUpdatedWaiter(CustomWaiter):
class GlueCrawlerReadyWaiter(CustomWaiter):
"""
Subclass of the base custom waiter class.
Wait for the Glue crawler to finish its processing.
Wait for the Glue crawler to finish its processing. Return when the crawler is in the "Stopping" status
to avoid wasting too much time in the automation tests on its shutdown process.
"""
def __init__(self, client: botocore.client):
"""
@@ -57,7 +58,7 @@ class GlueCrawlerReadyWaiter(CustomWaiter):
'GlueCrawlerReady',
'GetCrawler',
'Crawler.State',
{'READY': WaitState.SUCCESS},
{'STOPPING': WaitState.SUCCESS},
client)
def wait(self, crawler_name):
@@ -1,7 +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
"""
@@ -1,248 +0,0 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import boto3
import uuid
import logging
import subprocess
import botocore
import ly_test_tools.environment.process_utils as process_utils
from typing import List
BOOTSTRAP_STACK_NAME = 'CDKToolkit'
BOOTSTRAP_STAGING_BUCKET_LOGIC_ID = 'StagingBucket'
logger = logging.getLogger(__name__)
class Cdk:
"""
Cdk class that provides methods to run cdk application commands.
Expects system to have NodeJS, AWS CLI and CDK installed globally and have their paths setup as env variables.
"""
def __init__(self):
self._cdk_env = ''
self._stacks = []
self._cdk_path = os.path.dirname(os.path.realpath(__file__))
self._session = ''
cdk_npm_latest_version_cmd = ['npm', 'view', 'aws-cdk', 'version']
output = process_utils.check_output(
cdk_npm_latest_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_npm_latest_version = output.split()[0]
cdk_version_cmd = ['cdk', 'version']
output = process_utils.check_output(
cdk_version_cmd,
cwd=self._cdk_path,
shell=True)
cdk_version = output.split()[0]
logger.info(f'Current CDK version {cdk_version}')
if cdk_version != cdk_npm_latest_version:
try:
logger.info(f'Updating CDK to latest')
# uninstall and reinstall cdk in case npm has been updated.
output = process_utils.check_output(
'npm uninstall -g aws-cdk',
cwd=self._cdk_path,
shell=True)
logger.info(f'Uninstall CDK output: {output}')
output = process_utils.check_output(
'npm install -g aws-cdk@latest',
cwd=self._cdk_path,
shell=True)
logger.info(f'Install CDK output: {output}')
except subprocess.CalledProcessError as error:
logger.warning(f'Failed reinstalling latest CDK on npm'
f'\nError:{error.stderr}')
def setup(self, cdk_path: str, project: str, account_id: str,
workspace: pytest.fixture, session: boto3.session.Session):
"""
:param cdk_path: Path where cdk app.py is stored.
:param project: Project name used for cdk project name env variable.
:param account_id: AWS account id to use with cdk application.
:param workspace: ly_test_tools workspace fixture.
:param session: Current boto3 session, provides credentials and region.
"""
self._cdk_env = os.environ.copy()
unique_id = uuid.uuid4().hex[-4:]
self._cdk_env['O3DE_AWS_PROJECT_NAME'] = project[:4] + unique_id if len(project) > 4 else project + unique_id
self._cdk_env['O3DE_AWS_DEPLOY_REGION'] = session.region_name
self._cdk_env['O3DE_AWS_DEPLOY_ACCOUNT'] = account_id
self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH']
credentials = session.get_credentials().get_frozen_credentials()
self._cdk_env['AWS_ACCESS_KEY_ID'] = credentials.access_key
self._cdk_env['AWS_SECRET_ACCESS_KEY'] = credentials.secret_key
self._cdk_env['AWS_SESSION_TOKEN'] = credentials.token
self._cdk_path = cdk_path
self._session = session
output = process_utils.check_output(
'python -m pip install -r requirements.txt',
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
logger.info(f'Installing cdk python dependencies: {output}')
self.bootstrap()
def bootstrap(self) -> None:
"""
Deploy the bootstrap stack.
"""
try:
bootstrap_cmd = ['cdk', 'bootstrap',
f'aws://{self._cdk_env["O3DE_AWS_DEPLOY_ACCOUNT"]}/{self._cdk_env["O3DE_AWS_DEPLOY_REGION"]}']
process_utils.check_call(
bootstrap_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except botocore.exceptions.ClientError as clientError:
logger.warning(f'Failed creating Bootstrap stack {BOOTSTRAP_STACK_NAME} not found. '
f'\nError:{clientError["Error"]["Message"]}')
def list(self, deployment_params: List[str] = None) -> List[str]:
"""
lists cdk stack names.
:param deployment_params: Deployment parameters like --all can be passed in this way.
:return List of cdk stack names.
"""
if not self._cdk_path:
return []
list_cdk_application_cmd = ['cdk', 'list']
if deployment_params:
list_cdk_application_cmd.extend(deployment_params)
output = process_utils.check_output(
list_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
return output.splitlines()
def synthesize(self, deployment_params: List[str] = None) -> None:
"""
Synthesizes all cdk stacks.
:param deployment_params: Deployment parameters like --all can be passed in this way.
"""
if not self._cdk_path:
return
synth_cdk_application_cmd = ['cdk', 'synth']
if deployment_params:
synth_cdk_application_cmd.extend(deployment_params)
process_utils.check_output(
synth_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
def deploy(self, deployment_params: List[str] = None) -> List[str]:
"""
Deploys all the CDK stacks.
:param deployment_params: Deployment parameters like --all can be passed in this way.
:return List of deployed stack arns.
"""
if not self._cdk_path:
return []
deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never']
if deployment_params:
deploy_cdk_application_cmd.extend(deployment_params)
output = process_utils.check_output(
deploy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
for line in output.splitlines():
line_sections = line.split('/')
assert len(line_sections), 3
self._stacks.append(line.split('/')[-2])
return self._stacks
def destroy(self, deployment_params: List[str] = None) -> None:
"""
Destroys the cdk application.
:param deployment_params: Deployment parameters like --all can be passed in this way.
"""
logger.info(f'CDK Path {self._cdk_path}')
destroy_cdk_application_cmd = ['cdk', 'destroy', '-f']
if deployment_params:
destroy_cdk_application_cmd.extend(deployment_params)
try:
process_utils.check_output(
destroy_cdk_application_cmd,
cwd=self._cdk_path,
env=self._cdk_env,
shell=True)
except subprocess.CalledProcessError as e:
logger.error(e.output)
raise e
self._stacks = []
def remove_bootstrap_stack(self) -> None:
"""
Remove the CDK bootstrap stack.
:param aws_utils: aws_utils fixture.
"""
# Check if the bootstrap stack exists.
response = self._session.client('cloudformation').describe_stacks(
StackName=BOOTSTRAP_STACK_NAME
)
stacks = response.get('Stacks', [])
if not stacks or len(stacks) is 0:
return
# Clear the bootstrap staging bucket before deleting the bootstrap stack.
response = self._session.client('cloudformation').describe_stack_resource(
StackName=BOOTSTRAP_STACK_NAME,
LogicalResourceId=BOOTSTRAP_STAGING_BUCKET_LOGIC_ID
)
staging_bucket_name = response.get('StackResourceDetail', {}).get('PhysicalResourceId', '')
if staging_bucket_name:
s3 = self._session.resource('s3')
bucket = s3.Bucket(staging_bucket_name)
for key in bucket.objects.all():
key.delete()
# Delete the bootstrap stack.
# Should not need to delete the stack if S3 bucket can be cleaned.
# self._session.client('cloudformation').delete_stack(
# StackName=BOOTSTRAP_STACK_NAME
# )
@property
def stacks(self):
return self._stacks
@@ -4,45 +4,42 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import pytest
import os
import logging
import os
import pytest
import ly_test_tools.log.log_monitor
from AWS.common import constants
# fixture imports
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
AWS_PROJECT_NAME = 'AWS-AutomationTest'
AWS_CLIENT_AUTH_FEATURE_NAME = 'AWSClientAuth'
AWS_CLIENT_AUTH_DEFAULT_PROFILE_NAME = 'default'
GAME_LOG_NAME = 'Game.log'
logger = logging.getLogger(__name__)
@pytest.mark.SUITE_periodic
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('asset_processor')
@pytest.mark.usefixtures('workspace')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.usefixtures('cdk')
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
@pytest.mark.usefixtures('resource_mappings')
@pytest.mark.parametrize('resource_mappings_filename', ['default_aws_resource_mappings.json'])
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('aws_utils')
@pytest.mark.parametrize('region_name', ['us-west-2'])
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
@pytest.mark.usefixtures('cdk')
@pytest.mark.parametrize('deployment_params', [[]])
@pytest.mark.usefixtures('workspace')
@pytest.mark.parametrize('assume_role_arn', [constants.ASSUME_ROLE_ARN])
@pytest.mark.parametrize('feature_name', [AWS_CLIENT_AUTH_FEATURE_NAME])
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.usefixtures('resource_mappings')
@pytest.mark.parametrize('resource_mappings_filename', [constants.AWS_RESOURCE_MAPPING_FILE_NAME])
@pytest.mark.parametrize('region_name', [constants.AWS_REGION])
@pytest.mark.parametrize('session_name', [constants.SESSION_NAME])
@pytest.mark.parametrize('stacks', [[f'{constants.AWS_PROJECT_NAME}-{AWS_CLIENT_AUTH_FEATURE_NAME}-Stack-{constants.AWS_REGION}']])
class TestAWSClientAuthWindows(object):
"""
Test class to verify AWS Client Auth gem features on Windows.
"""
@pytest.mark.parametrize('level', ['AWS/ClientAuth'])
@pytest.mark.parametrize('destroy_stacks_on_teardown', [False])
def test_anonymous_credentials(self,
level: str,
launcher: pytest.fixture,
@@ -53,14 +50,14 @@ class TestAWSClientAuthWindows(object):
"""
Test to verify AWS Cognito Identity pool anonymous authorization.
Setup: Deploys cdk and updates resource mapping file.
Setup: Updates resource mapping file using existing CloudFormation stacks.
Tests: Getting credentials when no credentials are configured
Verification: Log monitor looks for success credentials log.
"""
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
launcher.args = ['+LoadLevel', level]
@@ -74,10 +71,8 @@ class TestAWSClientAuthWindows(object):
)
assert result, 'Anonymous credentials fetched successfully.'
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
def test_password_signin_credentials(self,
launcher: pytest.fixture,
cdk: pytest.fixture,
resource_mappings: pytest.fixture,
workspace: pytest.fixture,
asset_processor: pytest.fixture,
@@ -86,16 +81,29 @@ class TestAWSClientAuthWindows(object):
"""
Test to verify AWS Cognito IDP Password sign in and Cognito Identity pool authenticated authorization.
Setup: Deploys cdk and updates resource mapping file.
Setup: Updates resource mapping file using existing CloudFormation stacks.
Tests: Sign up new test user, admin confirm the user, sign in and get aws credentials.
Verification: Log monitor looks for success credentials log.
"""
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
cognito_idp = aws_utils.client('cognito-idp')
user_pool_id = resource_mappings.get_resource_name_id(f'{AWS_CLIENT_AUTH_FEATURE_NAME}.CognitoUserPoolId')
logger.info(f'UserPoolId:{user_pool_id}')
# Remove the user if already exists
try:
cognito_idp.admin_delete_user(
UserPoolId=user_pool_id,
Username='test1'
)
except cognito_idp.exceptions.UserNotFoundException:
pass
launcher.args = ['+LoadLevel', 'AWS/ClientAuthPasswordSignUp']
launcher.args.extend(['-rhi=null'])
@@ -109,9 +117,6 @@ class TestAWSClientAuthWindows(object):
launcher.stop()
cognito_idp = aws_utils.client('cognito-idp')
user_pool_id = resource_mappings.get_resource_name_id(f'{AWS_CLIENT_AUTH_FEATURE_NAME}.CognitoUserPoolId')
print(f'UserPoolId:{user_pool_id}')
cognito_idp.admin_confirm_sign_up(
UserPoolId=user_pool_id,
Username='test1'
@@ -5,10 +5,11 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import logging
import typing
import os
import shutil
import typing
from botocore.exceptions import ClientError
import pytest
import ly_test_tools
@@ -16,16 +17,15 @@ import ly_test_tools.log.log_monitor
import ly_test_tools.environment.process_utils as process_utils
import ly_test_tools.o3de.asset_processor_utils as asset_processor_utils
from botocore.exceptions import ClientError
from AWS.common import constants
# fixture imports
from assetpipeline.ap_fixtures.asset_processor_fixture import asset_processor
AWS_CORE_FEATURE_NAME = 'AWSCore'
AWS_RESOURCE_MAPPING_FILE_NAME = 'default_aws_resource_mappings.json'
process_utils.kill_processes_named("o3de", ignore_extensions=True) # Kill ProjectManager windows
GAME_LOG_NAME = 'Game.log'
logger = logging.getLogger(__name__)
@@ -46,7 +46,7 @@ def setup(launcher: pytest.fixture, asset_processor: pytest.fixture) -> typing.T
asset_processor.start()
asset_processor.wait_for_idle()
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), GAME_LOG_NAME)
file_to_monitor = os.path.join(launcher.workspace.paths.project_log(), constants.GAME_LOG_NAME)
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=launcher, log_file_path=file_to_monitor)
return log_monitor, s3_download_dir
@@ -58,7 +58,7 @@ def write_test_data_to_dynamodb_table(resource_mappings: pytest.fixture, aws_uti
:param resource_mappings: resource_mappings fixture.
:param aws_utils: aws_utils fixture.
"""
table_name = resource_mappings.get_resource_name_id("AWSCore.ExampleDynamoTableOutput")
table_name = resource_mappings.get_resource_name_id(f'{AWS_CORE_FEATURE_NAME}.ExampleDynamoTableOutput')
try:
aws_utils.client('dynamodb').put_item(
TableName=table_name,
@@ -77,21 +77,19 @@ def write_test_data_to_dynamodb_table(resource_mappings: pytest.fixture, aws_uti
@pytest.mark.SUITE_periodic
@pytest.mark.usefixtures('automatic_process_killer')
@pytest.mark.usefixtures('asset_processor')
@pytest.mark.usefixtures('cdk')
@pytest.mark.parametrize('feature_name', [AWS_CORE_FEATURE_NAME])
@pytest.mark.parametrize('region_name', ['us-west-2'])
@pytest.mark.parametrize('assume_role_arn', ['arn:aws:iam::645075835648:role/o3de-automation-tests'])
@pytest.mark.parametrize('session_name', ['o3de-Automation-session'])
@pytest.mark.parametrize('region_name', [constants.AWS_REGION])
@pytest.mark.parametrize('assume_role_arn', [constants.ASSUME_ROLE_ARN])
@pytest.mark.parametrize('session_name', [constants.SESSION_NAME])
@pytest.mark.usefixtures('workspace')
@pytest.mark.parametrize('project', ['AutomatedTesting'])
@pytest.mark.parametrize('level', ['AWS/Core'])
@pytest.mark.usefixtures('resource_mappings')
@pytest.mark.parametrize('resource_mappings_filename', [AWS_RESOURCE_MAPPING_FILE_NAME])
@pytest.mark.parametrize('resource_mappings_filename', [constants.AWS_RESOURCE_MAPPING_FILE_NAME])
@pytest.mark.parametrize('stacks', [[f'{constants.AWS_PROJECT_NAME}-{AWS_CORE_FEATURE_NAME}',
f'{constants.AWS_PROJECT_NAME}-{AWS_CORE_FEATURE_NAME}-Example-{constants.AWS_REGION}']])
@pytest.mark.usefixtures('aws_credentials')
@pytest.mark.parametrize('profile_name', ['AWSAutomationTest'])
@pytest.mark.usefixtures('cdk')
@pytest.mark.parametrize('deployment_params', [['--all']])
@pytest.mark.parametrize('destroy_stacks_on_teardown', [True])
class TestAWSCoreAWSResourceInteraction(object):
"""
Test class to verify the scripting behavior for the AWSCore gem.
@@ -119,7 +117,7 @@ class TestAWSCoreAWSResourceInteraction(object):
expected_lines: typing.List[str],
unexpected_lines: typing.List[str]):
"""
Setup: Deploys cdk and updates resource mapping file.
Setup: Updates resource mapping file using existing CloudFormation stacks.
Tests: Interact with AWS S3, DynamoDB and Lambda services.
Verification: Script canvas nodes can communicate with AWS services successfully.
"""
@@ -1,6 +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
"""
@@ -0,0 +1,19 @@
"""
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
"""
# ARN of the IAM role to assume for retrieving temporary AWS credentials
ASSUME_ROLE_ARN = 'arn:aws:iam::645075835648:role/o3de-automation-tests'
# Name of the AWS project deployed by the CDK applications
AWS_PROJECT_NAME = 'AWSAUTO'
# Region for the existing CloudFormation stacks used by the automation tests
AWS_REGION = 'us-east-1'
# Name of the default resource mapping config file used by the automation tests
AWS_RESOURCE_MAPPING_FILE_NAME = 'default_aws_resource_mappings.json'
# Name of the game launcher log
GAME_LOG_NAME = 'Game.log'
# Name of the IAM role session for retrieving temporary AWS credentials
SESSION_NAME = 'o3de-Automation-session'
@@ -6,9 +6,9 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import json
import logging
from AWS.common import constants
logger = logging.getLogger(__name__)
@@ -22,18 +22,14 @@ class ResourceMappings:
ResourceMappings class that handles writing Cloud formation outputs to resource mappings json file in a project.
"""
def __init__(self, file_path: str, region: str, feature_name: str, account_id: str, workspace: pytest.fixture,
cloud_formation_client):
def __init__(self, file_path: str, region: str, feature_name: str, account_id: str, cloud_formation_client):
"""
:param file_path: Path for the resource mapping file.
:param region: Region value for the resource mapping file.
:param feature_name: Feature gem name to use to append name to mappings key.
:param account_id: AWS account id value for the resource mapping file.
:param workspace: ly_test_tools workspace fixture.
:param cloud_formation_client: AWS cloud formation client.
"""
self._cdk_env = os.environ.copy()
self._cdk_env['PATH'] = f'{workspace.paths.engine_root()}\\python;' + self._cdk_env['PATH']
self._resource_mapping_file_path = file_path
self._region = region
self._feature_name = feature_name
@@ -44,7 +40,7 @@ class ResourceMappings:
f'Invalid resource mapping file path {self._resource_mapping_file_path}'
self._client = cloud_formation_client
def populate_output_keys(self, stacks=[]) -> None:
def populate_output_keys(self, stacks=None) -> None:
"""
Calls describe stacks on cloud formation service and persists outputs to resource mappings file.
:param stacks List of stack arns to describe and populate resource mappings with.
@@ -58,7 +54,7 @@ class ResourceMappings:
self._write_resource_mappings(stacks[0].get('Outputs', []))
def _write_resource_mappings(self, outputs, append_feature_name = True) -> None:
def _write_resource_mappings(self, outputs, append_feature_name=True) -> None:
with open(self._resource_mapping_file_path) as file_content:
resource_mappings = json.load(file_content)
@@ -91,7 +87,7 @@ class ResourceMappings:
resource_mappings = json.load(file_content)
resource_mappings[AWS_RESOURCE_MAPPINGS_ACCOUNT_ID_KEY] = ''
resource_mappings[AWS_RESOURCE_MAPPINGS_REGION_KEY] = 'us-west-2'
resource_mappings[AWS_RESOURCE_MAPPINGS_REGION_KEY] = constants.AWS_REGION
# Append new mappings.
resource_mappings[AWS_RESOURCE_MAPPINGS_KEY] = resource_mappings.get(AWS_RESOURCE_MAPPINGS_KEY, {})
@@ -11,8 +11,7 @@ import typing
from AWS.common.aws_utils import AwsUtils
from AWS.common.aws_credentials import AwsCredentials
from AWS.Windows.cdk.cdk_utils import Cdk
from AWS.Windows.resource_mappings.resource_mappings import ResourceMappings
from AWS.common.resource_mappings import ResourceMappings
logger = logging.getLogger(__name__)
@@ -52,6 +51,7 @@ def resource_mappings(
project: str,
feature_name: str,
resource_mappings_filename: str,
stacks: typing.List,
workspace: pytest.fixture,
aws_utils: pytest.fixture) -> ResourceMappings:
"""
@@ -61,6 +61,7 @@ def resource_mappings(
:param project: Project to find resource mapping file.
:param feature_name: AWS Gem name that is prepended to resource mapping keys.
:param resource_mappings_filename: Name of resource mapping file.
:param stacks: List of stack names to describe and populate resource mappings with.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: AWS utils fixture.
:return: ResourceMappings class object.
@@ -70,8 +71,8 @@ def resource_mappings(
logger.info(f'Resource mapping path : {path}')
logger.info(f'Resource mapping resolved path : {abspath(path)}')
resource_mappings_obj = ResourceMappings(abspath(path), aws_utils.assume_session().region_name, feature_name,
aws_utils.assume_account_id(), workspace,
aws_utils.client('cloudformation'))
aws_utils.assume_account_id(), aws_utils.client('cloudformation'))
resource_mappings_obj.populate_output_keys(stacks)
def teardown():
resource_mappings_obj.clear_output_keys()
@@ -81,56 +82,6 @@ def resource_mappings(
return resource_mappings_obj
@pytest.fixture(scope='function')
def cdk(
request: pytest.fixture,
project: str,
feature_name: str,
workspace: pytest.fixture,
aws_utils: pytest.fixture,
resource_mappings: pytest.fixture,
deployment_params: typing.List[str],
destroy_stacks_on_teardown: bool) -> Cdk:
"""
Fixture for setting up a Cdk
:param request: _pytest.fixtures.SubRequest class that handles getting
a pytest fixture from a pytest function/fixture.
:param project: Project name used for cdk project name env variable.
:param feature_name: Feature gem name to expect cdk folder in.
:param workspace: ly_test_tools workspace fixture.
:param aws_utils: aws_utils fixture.
:param resource_mappings: resource_mappings fixture.
:param deployment_params: Parameters for the CDK application deployment.
:param destroy_stacks_on_teardown: option to control calling destroy ot the end of test.
:return Cdk class object.
"""
cdk_path = f'{workspace.paths.engine_root()}/Gems/{feature_name}/cdk'
logger.info(f'CDK Path {cdk_path}')
if pytest.cdk_obj is None:
pytest.cdk_obj = Cdk()
pytest.cdk_obj.setup(cdk_path, project, aws_utils.assume_account_id(), workspace, aws_utils.assume_session())
stacks = pytest.cdk_obj.deploy(deployment_params=deployment_params)
logger.info(f'Cdk stack names:\n{stacks}')
resource_mappings.populate_output_keys(stacks)
def teardown():
if destroy_stacks_on_teardown:
pytest.cdk_obj.destroy(deployment_params=deployment_params)
# Enable after https://github.com/aws/aws-cdk/issues/986 is fixed.
# Until then clean the bootstrap bucket manually.
# pytest.cdk_obj.remove_bootstrap_stack()
pytest.cdk_obj = None
request.addfinalizer(teardown)
return pytest.cdk_obj
@pytest.fixture(scope='function')
def aws_credentials(request: pytest.fixture, aws_utils: pytest.fixture, profile_name: str):
"""
@@ -12,7 +12,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE main
TEST_SERIAL TRUE
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -8,7 +8,7 @@ INTRODUCTION
------------
EditorPythonBindings is a Python project that contains a collection of editor testing tools
developed by the Lumberyard feature teams. The project contains tools for system level
developed by the O3DE feature teams. The project contains tools for system level
editor tests.
@@ -23,7 +23,7 @@ installed on your system.
INSTALL
-----------
It is recommended to set up these these tools with Lumberyard's CMake build commands.
It is recommended to set up these these tools with O3DE's CMake build commands.
Assuming CMake is already setup on your operating system, below are some sample build commands:
cd /path/to/od3e/
mkdir windows_vs2019
@@ -8,6 +8,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
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
@@ -428,3 +429,32 @@ def get_component_type_id_map(component_name_list):
type_ids_by_component[component_names[i]] = typeId
return type_ids_by_component
def attach_component_to_entity(entity_id, component_name):
# type: (azlmbr.entity.EntityId, str) -> azlmbr.entity.EntityComponentIdPair
"""
Adds the component if not added already.
:param entity_id: EntityId of the entity to attach the component to
:param component_name: name of the component
:return: If successful, returns the EntityComponentIdPair, otherwise returns None.
"""
type_ids_list = editor.EditorComponentAPIBus(
bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component_name], 0)
general.log(f"Components found = {len(type_ids_list)}")
if len(type_ids_list) < 1:
general.log(f"ERROR: A component class with name {component_name} doesn't exist")
return None
elif len(type_ids_list) > 1:
general.log(f"ERROR: Found more than one component classes with same name: {component_name}")
return None
# Before adding the component let's check if it is already attached to the entity.
component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', entity_id, type_ids_list[0])
if component_outcome.IsSuccess():
return component_outcome.GetValue() # In this case the value is not a list.
component_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entity_id, type_ids_list)
if component_outcome.IsSuccess():
general.log(f"{component_name} Component added to entity.")
return component_outcome.GetValue()[0]
general.log(f"ERROR: Failed to add component [{component_name}] to entity")
return None
@@ -29,7 +29,7 @@ def teardown_editor(editor):
def launch_and_validate_results(request, test_directory, editor, editor_script, expected_lines, unexpected_lines=[],
halt_on_unexpected=False, run_python="--runpythontest", auto_test_mode=True, null_renderer=True, cfg_args=[],
timeout=300):
timeout=300, log_file_name="Editor.log"):
"""
Runs the Editor with the specified script, and monitors for expected log lines.
:param request: Special fixture providing information of the requesting test function.
@@ -44,6 +44,7 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
:param null_renderer: Specifies the test does not require the renderer. Defaults to True.
:param cfg_args: Additional arguments for CFG, such as LevelName.
:param timeout: Length of time for test to run. Default is 60.
:param log_file_name: Name of the log file created by the editor. Defaults to 'Editor.log'
"""
test_case = os.path.join(test_directory, editor_script)
request.addfinalizer(lambda: teardown_editor(editor))
@@ -58,7 +59,17 @@ def launch_and_validate_results(request, test_directory, editor, editor_script,
with editor.start():
editorlog_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log')
editorlog_file = os.path.join(editor.workspace.paths.project_log(), log_file_name)
# Log monitor requires the file to exist.
logger.debug(f"Waiting until log file <{editorlog_file}> exists...")
waiter.wait_for(
lambda: os.path.exists(editorlog_file),
timeout=60,
exc=f"Log file '{editorlog_file}' was never created by another process.",
interval=1,
)
logger.debug(f"Done! log file <{editorlog_file}> exists.")
# Initialize the log monitor and set time to wait for log creation
log_monitor = ly_test_tools.log.log_monitor.LogMonitor(launcher=editor, log_file_path=editorlog_file)
@@ -13,7 +13,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_REQUIRES gpu
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -27,19 +27,19 @@ class TestPythonAssetProcessing(object):
unexpected_lines = []
expected_lines = [
'Mock asset exists',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel) found',
'Expected subId for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel) found'
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel) found',
'AssetId found for asset (gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel) found'
]
timeout = 180
halt_on_unexpected = False
test_directory = os.path.join(os.path.dirname(__file__))
testFile = os.path.join(test_directory, 'AssetBuilder_test_case.py')
editor.args.extend(['-NullRenderer', "--skipWelcomeScreenDialog", "--autotest_mode", "--runpythontest", testFile])
editor.args.extend(['-NullRenderer', '-rhi=Null', "--skipWelcomeScreenDialog", "--autotest_mode", "--runpythontest", testFile])
with editor.start():
editorlog_file = os.path.join(editor.workspace.paths.project_log(), 'Editor.log')
@@ -29,21 +29,21 @@ if (assetIdString.endswith(':528cca58') is False):
print ('Mock asset exists')
# These tests detect if the geom_group.fbx file turns into a number of azmodel product assets
def test_azmodel_product(generatedModelAssetPath, expectedSubId):
def test_azmodel_product(generatedModelAssetPath):
azModelAssetType = azlmbr.math.Uuid_CreateString('{2C7477B6-69C5-45BE-8163-BCD6A275B6D8}', 0)
assetId = azlmbr.asset.AssetCatalogRequestBus(azlmbr.bus.Broadcast, 'GetAssetIdByPath', generatedModelAssetPath, azModelAssetType, False)
assetIdString = assetId.to_string()
if (assetIdString.endswith(':' + expectedSubId) is False):
raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString}) for ({generatedModelAssetPath}), expected {expectedSubId}!')
if (assetId.is_valid()):
print(f'AssetId found for asset ({generatedModelAssetPath}) found')
else:
print(f'Expected subId for asset ({generatedModelAssetPath}) found')
raise_and_stop(f'Asset at path {generatedModelAssetPath} has unexpected asset ID ({assetIdString})!')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive.azmodel', '1024be55')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative.azmodel', '1052c94e')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive.azmodel', '10130556')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative.azmodel', '1065724d')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive.azmodel', '10d16e68')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative.azmodel', '10a71973')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center.azmodel', '10412075')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_z_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_y_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_positive_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_x_negative_1.azmodel')
test_azmodel_product('gem/pythontests/pythonassetbuilder/geom_group_fbx_cube_100cm_center_1.azmodel')
azlmbr.editor.EditorToolsApplicationRequestBus(azlmbr.bus.Broadcast, 'ExitNoPrompt')
@@ -12,7 +12,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE periodic
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -12,7 +12,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
TEST_SUITE main
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}/TestSuite_Active.py
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -92,17 +92,17 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
AZ::AssetProcessor
)
ly_add_pytest(
NAME AssetPipelineTests.AssetBundler
PATH ${CMAKE_CURRENT_LIST_DIR}/asset_bundler_batch_tests.py
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TIMEOUT 2400
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::AssetBundlerBatch
)
# Issue #3017
#ly_add_pytest(
# 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.AssetBundler_SandBox
@@ -111,7 +111,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
PYTEST_MARKS "SUITE_sandbox" # run only sandbox tests in this file
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
AZ::AssetBundlerBatch
@@ -133,7 +132,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS)
PATH ${CMAKE_CURRENT_LIST_DIR}/missing_dependency_tests.py
EXCLUDE_TEST_RUN_TARGET_FROM_IDE
TEST_SERIAL
TIMEOUT 1500
TEST_SUITE periodic
RUNTIME_DEPENDENCIES
AZ::AssetProcessorBatch
@@ -39,7 +39,7 @@ if(PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_BUILD_TESTS_SUPPORTED AND AutomatedT
TEST_SUITE main
TEST_REQUIRES gpu
TEST_SERIAL
TIMEOUT 800
TIMEOUT 1200
PATH ${CMAKE_CURRENT_LIST_DIR}/test_Atom_GPUTests.py
RUNTIME_DEPENDENCIES
AssetProcessor
@@ -206,8 +206,8 @@ def run():
# PostFX Layer Component
ComponentTests("PostFX Layer")
# Radius Weight Modifier Component
ComponentTests("Radius Weight Modifier")
# PostFX Radius Weight Modifier Component
ComponentTests("PostFX Radius Weight Modifier")
# Light Component
ComponentTests("Light")
@@ -19,7 +19,7 @@ import azlmbr.legacy.general as general
sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from atom_renderer.atom_utils.atom_component_helper import LIGHT_TYPES
from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
SPHERE_AND_SPOT_DISK_LIGHT_PROPERTIES = [
@@ -0,0 +1,183 @@
"""
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 azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe
This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe
You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear.
"""
import os
import sys
import time
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devassets, "Gem", "PythonTests"))
import atom_renderer.atom_utils.material_editor_utils as material_editor
NEW_MATERIAL = "test_material.material"
NEW_MATERIAL_1 = "test_material_1.material"
NEW_MATERIAL_2 = "test_material_2.material"
TEST_MATERIAL_1 = "001_DefaultWhite.material"
TEST_MATERIAL_2 = "002_BaseColorLerp.material"
TEST_MATERIAL_3 = "003_MetalMatte.material"
TEST_DATA_PATH = os.path.join(
azlmbr.paths.devroot, "Gems", "Atom", "TestData", "TestData", "Materials", "StandardPbrTestCases"
)
MATERIAL_TYPE_PATH = os.path.join(
azlmbr.paths.devroot, "Gems", "Atom", "Feature", "Common", "Assets",
"Materials", "Types", "StandardPBR.materialtype",
)
def run():
"""
Summary:
Material Editor basic tests including the below
1. Opening an Existing Asset
2. Creating a New Asset
3. Closing Selected Material
4. Closing All Materials
5. Closing all but Selected Material
6. Saving Material
7. Saving as a New Material
8. Saving as a Child Material
9. Saving all Open Materials
Expected Result:
All the above functions work as expected in Material Editor.
:return: None
"""
# 1) Test Case: Opening an Existing Asset
document_id = material_editor.open_material(MATERIAL_TYPE_PATH)
print(f"Material opened: {material_editor.is_open(document_id)}")
# Verify if the test material exists initially
target_path = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL)
print(f"Test asset doesn't exist initially: {not os.path.exists(target_path)}")
# 2) Test Case: Creating a New Material Using Existing One
material_editor.save_document_as_child(document_id, target_path)
material_editor.wait_for_condition(lambda: os.path.exists(target_path), 2.0)
print(f"New asset created: {os.path.exists(target_path)}")
# Verify if the newly created document is open
new_document_id = material_editor.open_material(target_path)
material_editor.wait_for_condition(lambda: material_editor.is_open(new_document_id))
print(f"New Material opened: {material_editor.is_open(new_document_id)}")
# 3) Test Case: Closing Selected Material
print(f"Material closed: {material_editor.close_document(new_document_id)}")
# Open materials initially
document1_id, document2_id, document3_id = (
material_editor.open_material(os.path.join(TEST_DATA_PATH, material))
for material in [TEST_MATERIAL_1, TEST_MATERIAL_2, TEST_MATERIAL_3]
)
# 4) Test Case: Closing All Materials
print(f"All documents closed: {material_editor.close_all_documents()}")
# 5) Test Case: Closing all but Selected Material
document1_id, document2_id, document3_id = (
material_editor.open_material(os.path.join(TEST_DATA_PATH, material))
for material in [TEST_MATERIAL_1, TEST_MATERIAL_2, TEST_MATERIAL_3]
)
result = material_editor.close_all_except_selected(document1_id)
print(f"Close All Except Selected worked as expected: {result and material_editor.is_open(document1_id)}")
# 6) Test Case: Saving Material
document_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
property_name = azlmbr.name.Name("baseColor.color")
initial_color = material_editor.get_property(document_id, property_name)
# Assign new color to the material file and save the actual material
expected_color = math.Color(0.25, 0.25, 0.25, 1.0)
material_editor.set_property(document_id, property_name, expected_color)
material_editor.save_document(document_id)
# 7) Test Case: Saving as a New Material
# Assign new color to the material file and save the document as copy
expected_color_1 = math.Color(0.5, 0.5, 0.5, 1.0)
material_editor.set_property(document_id, property_name, expected_color_1)
target_path_1 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_1)
material_editor.save_document_as_copy(document_id, target_path_1)
time.sleep(2.0)
# 8) Test Case: Saving as a Child Material
# Assign new color to the material file save the document as child
expected_color_2 = math.Color(0.75, 0.75, 0.75, 1.0)
material_editor.set_property(document_id, property_name, expected_color_2)
target_path_2 = os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Materials", NEW_MATERIAL_2)
material_editor.save_document_as_child(document_id, target_path_2)
time.sleep(2.0)
# Close/Reopen documents
material_editor.close_all_documents()
document_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
document1_id = material_editor.open_material(target_path_1)
document2_id = material_editor.open_material(target_path_2)
# Verify if the changes are saved in the actual document
actual_color = material_editor.get_property(document_id, property_name)
print(f"Actual Document saved with changes: {material_editor.compare_colors(actual_color, expected_color)}")
# Verify if the changes are saved in the document saved as copy
actual_color = material_editor.get_property(document1_id, property_name)
result_copy = material_editor.compare_colors(actual_color, expected_color_1)
print(f"Document saved as copy is saved with changes: {result_copy}")
# Verify if the changes are saved in the document saved as child
actual_color = material_editor.get_property(document2_id, property_name)
result_child = material_editor.compare_colors(actual_color, expected_color_2)
print(f"Document saved as child is saved with changes: {result_child}")
# Revert back the changes in the actual document
material_editor.set_property(document_id, property_name, initial_color)
material_editor.save_document(document_id)
material_editor.close_all_documents()
# 9) Test Case: Saving all Open Materials
# Open first material and make change to the values
document1_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
property1_name = azlmbr.name.Name("metallic.factor")
initial_metallic_factor = material_editor.get_property(document1_id, property1_name)
expected_metallic_factor = 0.444
material_editor.set_property(document1_id, property1_name, expected_metallic_factor)
# Open second material and make change to the values
document2_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_2))
property2_name = azlmbr.name.Name("baseColor.color")
initial_color = material_editor.get_property(document2_id, property2_name)
expected_color = math.Color(0.4156, 0.0196, 0.6862, 1.0)
material_editor.set_property(document2_id, property2_name, expected_color)
# Save all and close all documents
material_editor.save_all()
material_editor.close_all_documents()
# Reopen materials and verify values
document1_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_1))
result = material_editor.is_close(
material_editor.get_property(document1_id, property1_name), expected_metallic_factor, 0.00001
)
document2_id = material_editor.open_material(os.path.join(TEST_DATA_PATH, TEST_MATERIAL_2))
result = result and material_editor.compare_colors(
expected_color, material_editor.get_property(document2_id, property2_name))
print(f"Save All worked as expected: {result}")
# Revert the changes made
material_editor.set_property(document1_id, property1_name, initial_metallic_factor)
material_editor.set_property(document2_id, property2_name, initial_color)
material_editor.save_all()
material_editor.close_all_documents()
if __name__ == "__main__":
run()
@@ -93,6 +93,7 @@ def run():
general.idle_wait_frames(100)
for i in range(1, 101):
benchmarker.capture_pass_timestamp(i)
benchmarker.capture_cpu_frame_time(i)
general.exit_game_mode()
helper.wait_for_condition(function=lambda: not general.is_in_game_mode(), timeout_in_seconds=2.0)
general.log("Capturing complete.")
@@ -0,0 +1,267 @@
"""
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
Hydra script that is used to create an entity with a Light component attached.
It then updates the property values of the Light component and takes a screenshot.
The screenshot is compared against an expected golden image for test verification.
See the run() function for more in-depth test info.
"""
import os
import sys
import azlmbr.asset as asset
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.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from atom_renderer.atom_utils import atom_component_helper, atom_constants, screenshot_utils
from editor_python_test_tools.editor_test_helper import EditorTestHelper
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
LEVEL_NAME = "auto_test"
LIGHT_COMPONENT = "Light"
LIGHT_TYPE_PROPERTY = 'Controller|Configuration|Light type'
DEGREE_RADIAN_FACTOR = 0.0174533
def run():
"""
Sets up the tests by making sure the required level is created & setup correctly.
It then executes 2 test cases - see each associated test function's docstring for more info.
Finally prints the string "Light component tests 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
"""
atom_component_helper.create_basic_atom_level(level_name=LEVEL_NAME)
# Run tests.
area_light_test()
spot_light_test()
general.log("Light component tests completed.")
def area_light_test():
"""
Basic test for the "Light" component attached to an "area_light" entity.
Test Case - Light Component: Capsule, Spot (disk), and Point (sphere):
1. Creates "area_light" entity w/ a Light component that has a Capsule Light type w/ the color set to 255, 0, 0
2. Enters game mode to take a screenshot for comparison, then exits game mode.
3. Sets the Light component Intensity Mode to Lumens (default).
4. Ensures the Light component Mode is Automatic (default).
5. Sets the Intensity value of the Light component to 0.0
6. Enters game mode again, takes another screenshot for comparison, then exits game mode.
7. Updates the Intensity value of the Light component to 1000.0
8. Enters game mode again, takes another screenshot for comparison, then exits game mode.
9. Swaps the Capsule light type option to Spot (disk) light type on the Light component
10. Updates "area_light" entity Transform rotate value to x: 90.0, y:0.0, z:0.0
11. Enters game mode again, takes another screenshot for comparison, then exits game mode.
12. Swaps the Spot (disk) light type for the Point (sphere) light type in the Light component.
13. Enters game mode again, takes another screenshot for comparison, then exits game mode.
14. Deletes the Light component from the "area_light" entity and verifies its successful.
"""
# Create an "area_light" entity with "Light" component using Light type of "Capsule"
area_light_entity_name = "area_light"
area_light = hydra.Entity(area_light_entity_name)
area_light.create_entity(math.Vector3(-1.0, -2.0, 3.0), [LIGHT_COMPONENT])
general.log(
f"{area_light_entity_name}_test: Component added to the entity: "
f"{hydra.has_components(area_light.id, [LIGHT_COMPONENT])}")
light_component_id_pair = hydra.attach_component_to_entity(area_light.id, LIGHT_COMPONENT)
# Select the "Capsule" light type option.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
atom_constants.LIGHT_TYPES['capsule']
)
# Update color and take screenshot in game mode
color = math.Color(255.0, 0.0, 0.0, 0.0)
area_light.get_set_test(0, "Controller|Configuration|Color", color)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("AreaLight_1", area_light_entity_name)
# Update intensity value to 0.0 and take screenshot in game mode
area_light.get_set_test(0, "Controller|Configuration|Attenuation Radius|Mode", 1)
area_light.get_set_test(0, "Controller|Configuration|Intensity", 0.0)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("AreaLight_2", area_light_entity_name)
# Update intensity value to 1000.0 and take screenshot in game mode
area_light.get_set_test(0, "Controller|Configuration|Intensity", 1000.0)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("AreaLight_3", area_light_entity_name)
# Swap the "Capsule" light type option to "Spot (disk)" light type
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
atom_constants.LIGHT_TYPES['spot_disk']
)
area_light_rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 90.0, 0.0, 0.0)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", area_light.id, area_light_rotation)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("AreaLight_4", area_light_entity_name)
# Swap the "Spot (disk)" light type to the "Point (sphere)" light type and take screenshot.
azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_id_pair,
LIGHT_TYPE_PROPERTY,
atom_constants.LIGHT_TYPES['sphere']
)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("AreaLight_5", area_light_entity_name)
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntityById", area_light.id)
def spot_light_test():
"""
Basic test for the Light component attached to a "spot_light" entity.
Test Case - Light Component: Spot (disk) with shadows & colors:
1. Creates "spot_light" entity w/ a Light component attached to it.
2. Selects the "directional_light" entity already present in the level and disables it.
3. Selects the "global_skylight" entity already present in the level and disables the HDRi Skybox component,
as well as the Global Skylight (IBL) component.
4. Enters game mode to take a screenshot for comparison, then exits game mode.
5. Selects the "ground_plane" entity and changes updates the material to a new material.
6. Enters game mode to take a screenshot for comparison, then exits game mode.
7. Selects the "spot_light" entity and increases the Light component Intensity to 800 lm
8. Enters game mode to take a screenshot for comparison, then exits game mode.
9. Selects the "spot_light" entity and sets the Light component Color to 47, 75, 37
10. Enters game mode to take a screenshot for comparison, then exits game mode.
11. Selects the "spot_light" entity and modifies the Shutter controls to the following values:
- Enable shutters: True
- Inner Angle: 60.0
- Outer Angle: 75.0
12. Enters game mode to take a screenshot for comparison, then exits game mode.
13. Selects the "spot_light" entity and modifies the Shadow controls to the following values:
- Enable Shadow: True
- ShadowmapSize: 256
14. Modifies the world translate position of the "spot_light" entity to 0.7, -2.0, 1.9 (for casting shadows better)
15. Enters game mode to take a screenshot for comparison, then exits game mode.
"""
# Disable "Directional Light" component for the "directional_light" entity
# "directional_light" entity is created by the create_basic_atom_level() function by default.
directional_light_entity_id = hydra.find_entity_by_name("directional_light")
directional_light = hydra.Entity(name='directional_light', id=directional_light_entity_id)
directional_light_component_type = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Directional Light"], 0)[0]
directional_light_component = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', directional_light.id, directional_light_component_type
).GetValue()
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [directional_light_component])
general.idle_wait(0.5)
# Disable "Global Skylight (IBL)" and "HDRi Skybox" components for the "global_skylight" entity
global_skylight_entity_id = hydra.find_entity_by_name("global_skylight")
global_skylight = hydra.Entity(name='global_skylight', id=global_skylight_entity_id)
global_skylight_component_type = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Global Skylight (IBL)"], 0)[0]
global_skylight_component = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, global_skylight_component_type
).GetValue()
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [global_skylight_component])
hdri_skybox_component_type = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["HDRi Skybox"], 0)[0]
hdri_skybox_component = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', global_skylight.id, hdri_skybox_component_type
).GetValue()
editor.EditorComponentAPIBus(bus.Broadcast, "DisableComponents", [hdri_skybox_component])
general.idle_wait(0.5)
# Create a "spot_light" entity with "Light" component using Light Type of "Spot (disk)"
spot_light_entity_name = "spot_light"
spot_light = hydra.Entity(spot_light_entity_name)
spot_light.create_entity(math.Vector3(0.7, -2.0, 1.0), [LIGHT_COMPONENT])
general.log(
f"{spot_light_entity_name}_test: Component added to the entity: "
f"{hydra.has_components(spot_light.id, [LIGHT_COMPONENT])}")
rotation = math.Vector3(DEGREE_RADIAN_FACTOR * 300.0, 0.0, 0.0)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", spot_light.id, rotation)
light_component_type = hydra.attach_component_to_entity(spot_light.id, LIGHT_COMPONENT)
editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
light_component_type,
LIGHT_TYPE_PROPERTY,
atom_constants.LIGHT_TYPES['spot_disk']
)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_1", spot_light_entity_name)
# Change default material of ground plane entity and take screenshot
ground_plane_entity_id = hydra.find_entity_by_name("ground_plane")
ground_plane = hydra.Entity(name='ground_plane', id=ground_plane_entity_id)
ground_plane_asset_path = os.path.join("Materials", "Presets", "MacBeth", "22_neutral_5-0_0-70d.azmaterial")
ground_plane_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_asset_path, math.Uuid(), False)
material_property_path = "Default Material|Material Asset"
material_component_type = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Material"], 0)[0]
material_component = azlmbr.editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast, 'GetComponentOfType', ground_plane.id, material_component_type).GetValue()
editor.EditorComponentAPIBus(
azlmbr.bus.Broadcast,
'SetComponentProperty',
material_component,
material_property_path,
ground_plane_asset_value
)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_2", spot_light_entity_name)
# Increase intensity value of the Spot light and take screenshot in game mode
spot_light.get_set_test(0, "Controller|Configuration|Intensity", 800.0)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_3", spot_light_entity_name)
# Update the Spot light color and take screenshot in game mode
color_value = math.Color(47.0 / 255.0, 75.0 / 255.0, 37.0 / 255.0, 255.0 / 255.0)
spot_light.get_set_test(0, "Controller|Configuration|Color", color_value)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_4", spot_light_entity_name)
# Update the Shutter controls of the Light component and take screenshot
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Enable shutters", True)
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Inner angle", 60.0)
spot_light.get_set_test(0, "Controller|Configuration|Shutters|Outer angle", 75.0)
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_5", spot_light_entity_name)
# Update the Shadow controls, move the spot_light entity world translate position and take screenshot
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Enable shadow", True)
spot_light.get_set_test(0, "Controller|Configuration|Shadows|Shadowmap size", 256.0)
azlmbr.components.TransformBus(
azlmbr.bus.Event, "SetWorldTranslation", spot_light.id, math.Vector3(0.7, -2.0, 1.9))
general.idle_wait(1.0)
screenshot_utils.take_screenshot_game_mode("SpotLight_6", spot_light_entity_name)
if __name__ == "__main__":
run()
@@ -3,17 +3,184 @@ Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright
SPDX-License-Identifier: Apache-2.0 OR MIT
File to assist with common hydra component functions or constants used across various Atom tests.
File to assist with common hydra component functions used across various Atom tests.
"""
import os
# Light type options for the Light component.
LIGHT_TYPES = {
'unknown': 0,
'sphere': 1,
'spot_disk': 2,
'capsule': 3,
'quad': 4,
'polygon': 5,
'simple_point': 6,
'simple_spot': 7,
}
from editor_python_test_tools.editor_test_helper import EditorTestHelper
helper = EditorTestHelper(log_prefix="Atom_EditorTestHelper")
def create_basic_atom_level(level_name):
"""
Creates a new level inside the Editor matching level_name & adds the following:
1. "default_level" entity to hold all other entities.
2. Adds Grid, Global Skylight (IBL), ground Mesh, Directional Light, Sphere w/ material+mesh, & Camera components.
3. Each of these components has its settings tweaked slightly to match the ideal scene to test Atom rendering.
:param level_name: name of the level to create and apply this basic setup to.
:return: None
"""
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.camera as camera
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.object
import editor_python_test_tools.hydra_editor_utils as hydra
# Create a new level.
new_level_name = level_name
heightmap_resolution = 512
heightmap_meters_per_pixel = 1
terrain_texture_resolution = 412
use_terrain = False
# Return codes are ECreateLevelResult defined in CryEdit.h
return_code = general.create_level_no_prompt(
new_level_name, heightmap_resolution, heightmap_meters_per_pixel, terrain_texture_resolution, use_terrain)
if return_code == 1:
general.log(f"{new_level_name} level already exists")
elif return_code == 2:
general.log("Failed to create directory")
elif return_code == 3:
general.log("Directory length is too long")
elif return_code != 0:
general.log("Unknown error, failed to create level")
else:
general.log(f"{new_level_name} level created successfully")
# Enable idle and update viewport.
general.idle_enable(True)
general.idle_wait(1.0)
general.update_viewport()
general.idle_wait(0.5) # half a second is more than enough for updating the viewport.
# Close out problematic windows, FPS meters, and anti-aliasing.
if general.is_helpers_shown(): # Turn off the helper gizmos if visible
general.toggle_helpers()
general.idle_wait(1.0)
if general.is_pane_visible("Error Report"): # Close Error Report windows that block focus.
general.close_pane("Error Report")
if general.is_pane_visible("Error Log"): # Close Error Log windows that block focus.
general.close_pane("Error Log")
general.idle_wait(1.0)
general.run_console("r_displayInfo=0")
general.run_console("r_antialiasingmode=0")
general.idle_wait(1.0)
# Delete all existing entities & create default_level entity
search_filter = azlmbr.entity.SearchFilter()
all_entities = entity.SearchBus(azlmbr.bus.Broadcast, "SearchEntities", search_filter)
editor.ToolsApplicationRequestBus(bus.Broadcast, "DeleteEntities", all_entities)
default_level = hydra.Entity("default_level")
default_position = math.Vector3(0.0, 0.0, 0.0)
default_level.create_entity(default_position, ["Grid"])
default_level.get_set_test(0, "Controller|Configuration|Secondary Grid Spacing", 1.0)
# Set the viewport up correctly after adding the parent default_level entity.
screen_width = 1280
screen_height = 720
degree_radian_factor = 0.0174533 # Used by "Rotation" property for the Transform component.
general.set_viewport_size(screen_width, screen_height)
general.update_viewport()
helper.wait_for_condition(
function=lambda: helper.isclose(a=general.get_viewport_size().x, b=screen_width, rel_tol=0.1)
and helper.isclose(a=general.get_viewport_size().y, b=screen_height, rel_tol=0.1),
timeout_in_seconds=4.0
)
result = helper.isclose(a=general.get_viewport_size().x, b=screen_width, rel_tol=0.1) and helper.isclose(
a=general.get_viewport_size().y, b=screen_height, rel_tol=0.1)
general.log(general.get_viewport_size().x)
general.log(general.get_viewport_size().y)
general.log(general.get_viewport_size().z)
general.log(f"Viewport is set to the expected size: {result}")
general.log("Basic level created")
general.run_console("r_DisplayInfo = 0")
# Create global_skylight entity and set the properties
global_skylight = hydra.Entity("global_skylight")
global_skylight.create_entity(
entity_position=default_position,
components=["HDRi Skybox", "Global Skylight (IBL)"],
parent_id=default_level.id)
global_skylight_asset_path = os.path.join(
"LightingPresets", "greenwich_park_02_4k_iblskyboxcm_iblspecular.exr.streamingimage")
global_skylight_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", global_skylight_asset_path, math.Uuid(), False)
global_skylight.get_set_test(0, "Controller|Configuration|Cubemap Texture", global_skylight_asset_value)
global_skylight.get_set_test(1, "Controller|Configuration|Diffuse Image", global_skylight_asset_value)
global_skylight.get_set_test(1, "Controller|Configuration|Specular Image", global_skylight_asset_value)
# Create ground_plane entity and set the properties
ground_plane = hydra.Entity("ground_plane")
ground_plane.create_entity(
entity_position=default_position,
components=["Material"],
parent_id=default_level.id)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalUniformScale", ground_plane.id, 32.0)
ground_plane_material_asset_path = os.path.join(
"Materials", "Presets", "PBR", "metal_chrome.azmaterial")
ground_plane_material_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_material_asset_path, math.Uuid(), False)
ground_plane.get_set_test(0, "Default Material|Material Asset", ground_plane_material_asset_value)
# Work around to add the correct Atom Mesh component
mesh_type_id = azlmbr.globals.property.EditorMeshComponentTypeId
ground_plane.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", ground_plane.id, [mesh_type_id]
).GetValue()[0]
)
ground_plane_mesh_asset_path = os.path.join("Models", "plane.azmodel")
ground_plane_mesh_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", ground_plane_mesh_asset_path, math.Uuid(), False)
ground_plane.get_set_test(1, "Controller|Configuration|Mesh Asset", ground_plane_mesh_asset_value)
# Create directional_light entity and set the properties
directional_light = hydra.Entity("directional_light")
directional_light.create_entity(
entity_position=math.Vector3(0.0, 0.0, 10.0),
components=["Directional Light"],
parent_id=default_level.id)
directional_light_rotation = math.Vector3(degree_radian_factor * -90.0, 0.0, 0.0)
azlmbr.components.TransformBus(
azlmbr.bus.Event, "SetLocalRotation", directional_light.id, directional_light_rotation)
# Create sphere entity and set the properties
sphere_entity = hydra.Entity("sphere")
sphere_entity.create_entity(
entity_position=math.Vector3(0.0, 0.0, 1.0),
components=["Material"],
parent_id=default_level.id)
sphere_material_asset_path = os.path.join("Materials", "Presets", "PBR", "metal_brass_polished.azmaterial")
sphere_material_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", sphere_material_asset_path, math.Uuid(), False)
sphere_entity.get_set_test(0, "Default Material|Material Asset", sphere_material_asset_value)
# Work around to add the correct Atom Mesh component
sphere_entity.components.append(
editor.EditorComponentAPIBus(
bus.Broadcast, "AddComponentsOfType", sphere_entity.id, [mesh_type_id]
).GetValue()[0]
)
sphere_mesh_asset_path = os.path.join("Models", "sphere.azmodel")
sphere_mesh_asset_value = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", sphere_mesh_asset_path, math.Uuid(), False)
sphere_entity.get_set_test(1, "Controller|Configuration|Mesh Asset", sphere_mesh_asset_value)
# Create camera component and set the properties
camera_entity = hydra.Entity("camera")
camera_entity.create_entity(
entity_position=math.Vector3(5.5, -12.0, 9.0),
components=["Camera"],
parent_id=default_level.id)
rotation = math.Vector3(
degree_radian_factor * -27.0, degree_radian_factor * -12.0, degree_radian_factor * 25.0
)
azlmbr.components.TransformBus(azlmbr.bus.Event, "SetLocalRotation", camera_entity.id, rotation)
camera_entity.get_set_test(0, "Controller|Configuration|Field of view", 60.0)
camera.EditorCameraViewRequestBus(azlmbr.bus.Event, "ToggleCameraAsActiveView", camera_entity.id)
@@ -0,0 +1,19 @@
"""
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
Hold constants used across both hydra and non-hydra scripts.
"""
# Light type options for the Light component.
LIGHT_TYPES = {
'unknown': 0,
'sphere': 1,
'spot_disk': 2,
'capsule': 3,
'quad': 4,
'polygon': 5,
'simple_point': 6,
'simple_spot': 7,
}
@@ -61,6 +61,25 @@ class BenchmarkHelper(object):
general.log('Failed to capture pass timestamps.')
return self.capturedData
def capture_cpu_frame_time(self, frame_number):
"""
Capture CPU frame times and block further execution until it has been written to the disk.
"""
self.handler = azlmbr.atom.ProfilingCaptureNotificationBusHandler()
self.handler.connect()
self.handler.add_callback('OnCaptureCpuFrameTimeFinished', self.on_data_captured)
self.done = False
self.capturedData = False
success = azlmbr.atom.ProfilingCaptureRequestBus(
azlmbr.bus.Broadcast, "CaptureCpuFrameTime", f'{self.output_path}/cpu_frame{frame_number}_time.json')
if success:
self.wait_until_data()
general.log('CPU frame time captured.')
else:
general.log('Failed to capture CPU frame time.')
return self.capturedData
def on_data_captured(self, parameters):
# the parameters come in as a tuple
if parameters[0]:
@@ -0,0 +1,274 @@
"""
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 azlmbr.materialeditor will fail with a ModuleNotFound error when using this script with Editor.exe
This is because azlmbr.materialeditor only binds to MaterialEditor.exe and not Editor.exe
You need to launch this script with MaterialEditor.exe in order for azlmbr.materialeditor to appear.
"""
import os
import sys
import time
import azlmbr.atom
import azlmbr.atomtools as atomtools
import azlmbr.materialeditor as materialeditor
import azlmbr.bus as bus
def is_close(actual, expected, buffer=sys.float_info.min):
"""
:param actual: actual value
:param expected: expected value
:param buffer: acceptable variation from expected
:return: bool
"""
return abs(actual - expected) < buffer
def compare_colors(color1, color2, buffer=0.00001):
"""
Compares the red, green and blue properties of a color allowing a slight variance of buffer
:param color1: first color to compare
:param color2: second color
:param buffer: allowed variance in individual color value
:return: bool
"""
return (
is_close(color1.r, color2.r, buffer)
and is_close(color1.g, color2.g, buffer)
and is_close(color1.b, color2.b, buffer)
)
def open_material(file_path):
"""
:return: uuid of material document opened
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "OpenDocument", file_path)
def is_open(document_id):
"""
:return: bool
"""
return azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "IsOpen", document_id)
def save_document(document_id):
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "SaveDocument", document_id)
def save_document_as_copy(document_id, target_path):
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
bus.Broadcast, "SaveDocumentAsCopy", document_id, target_path
)
def save_document_as_child(document_id, target_path):
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(
bus.Broadcast, "SaveDocumentAsChild", document_id, target_path
)
def save_all():
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "SaveAllDocuments")
def close_document(document_id):
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseDocument", document_id)
def close_all_documents():
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocuments")
def close_all_except_selected(document_id):
"""
:return: bool success
"""
return azlmbr.atomtools.AtomToolsDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocumentsExcept", document_id)
def get_property(document_id, property_name):
"""
:return: property value or invalid value if the document is not open or the property_name can't be found
"""
return azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "GetPropertyValue", document_id, property_name)
def set_property(document_id, property_name, value):
azlmbr.atomtools.AtomToolsDocumentRequestBus(bus.Event, "SetPropertyValue", document_id, property_name, value)
def is_pane_visible(pane_name):
"""
:return: bool
"""
return atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
def set_pane_visibility(pane_name, value):
atomtools.AtomToolsWindowRequestBus(bus.Broadcast, "SetDockWidgetVisible", pane_name, value)
def select_lighting_config(config_name):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectLightingPresetByName", config_name)
def set_grid_enable_disable(value):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetGridEnabled", value)
def get_grid_enable_disable():
"""
:return: bool
"""
return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetGridEnabled")
def set_shadowcatcher_enable_disable(value):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SetShadowCatcherEnabled", value)
def get_shadowcatcher_enable_disable():
"""
:return: bool
"""
return azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "GetShadowCatcherEnabled")
def select_model_config(configname):
azlmbr.materialeditor.MaterialViewportRequestBus(azlmbr.bus.Broadcast, "SelectModelPresetByName", configname)
def wait_for_condition(function, timeout_in_seconds=1.0):
# type: (function, float) -> bool
"""
Function to run until it returns True or timeout is reached
the function can have no parameters and
waiting idle__wait_* is handled here not in the function
:param function: a function that returns a boolean indicating a desired condition is achieved
:param timeout_in_seconds: when reached, function execution is abandoned and False is returned
"""
with Timeout(timeout_in_seconds) as t:
while True:
try:
azlmbr.atomtools.general.idle_wait_frames(1)
except Exception:
print("WARNING: Couldn't wait for frame")
if t.timed_out:
return False
ret = function()
if not isinstance(ret, bool):
raise TypeError("return value for wait_for_condition function must be a bool")
if ret:
return True
class Timeout:
# type: (float) -> None
"""
contextual timeout
:param seconds: float seconds to allow before timed_out is True
"""
def __init__(self, seconds):
self.seconds = seconds
def __enter__(self):
self.die_after = time.time() + self.seconds
return self
def __exit__(self, type, value, traceback):
pass
@property
def timed_out(self):
return time.time() > self.die_after
screenshotsFolder = os.path.join(azlmbr.paths.devroot, "AtomTest", "Cache" "pc", "Screenshots")
class ScreenshotHelper:
"""
A helper to capture screenshots and wait for them.
"""
def __init__(self, idle_wait_frames_callback):
super().__init__()
self.done = False
self.capturedScreenshot = False
self.max_frames_to_wait = 60
self.idle_wait_frames_callback = idle_wait_frames_callback
def capture_screenshot_blocking(self, filename):
"""
Capture a screenshot and block the execution until the screenshot has been written to the disk.
"""
self.handler = azlmbr.atom.FrameCaptureNotificationBusHandler()
self.handler.connect()
self.handler.add_callback("OnCaptureFinished", self.on_screenshot_captured)
self.done = False
self.capturedScreenshot = False
success = azlmbr.atom.FrameCaptureRequestBus(azlmbr.bus.Broadcast, "CaptureScreenshot", filename)
if success:
self.wait_until_screenshot()
print("Screenshot taken.")
else:
print("screenshot failed")
return self.capturedScreenshot
def on_screenshot_captured(self, parameters):
# the parameters come in as a tuple
if parameters[0]:
print("screenshot saved: {}".format(parameters[1]))
self.capturedScreenshot = True
else:
print("screenshot failed: {}".format(parameters[1]))
self.done = True
self.handler.disconnect()
def wait_until_screenshot(self):
frames_waited = 0
while self.done == False:
self.idle_wait_frames_callback(1)
if frames_waited > self.max_frames_to_wait:
print("timeout while waiting for the screenshot to be written")
self.handler.disconnect()
break
else:
frames_waited = frames_waited + 1
print("(waited {} frames)".format(frames_waited))
def capture_screenshot(file_path):
return ScreenshotHelper(azlmbr.atomtools.general.idle_wait_frames).capture_screenshot_blocking(
os.path.join(file_path)
)
@@ -91,3 +91,20 @@ class ScreenshotHelper(object):
else:
frames_waited = frames_waited + 1
general.log(f"(waited {frames_waited} frames)")
def take_screenshot_game_mode(screenshot_name, entity_name=None):
"""
Enters game mode & takes a screenshot, then exits game mode after.
:param screenshot_name: name to give the captured screenshot .ppm file.
:param entity_name: name of the entity being tested (for generating unique log lines).
:return: None
"""
general.enter_game_mode()
helper.wait_for_condition(lambda: general.is_in_game_mode(), 2.0)
general.log(f"{entity_name}_test: Entered game mode: {general.is_in_game_mode()}")
ScreenshotHelper(general.idle_wait_frames).capture_screenshot_blocking(f"{screenshot_name}.ppm")
general.idle_wait(1.0)
general.exit_game_mode()
helper.wait_for_condition(lambda: not general.is_in_game_mode(), 2.0)
general.log(f"{entity_name}_test: Exit game mode: {not general.is_in_game_mode()}")
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:954d7d0df47c840a24e313893800eb3126d0c0d47c3380926776b51833778db7
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e81c19128f42ba362a2d5f3ccf159dfbc942d67ceeb1ac8c21f295a6fd9d2ce5
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:5e20801213e065b6ea8c95ede81c23faa9b6dc70a2002dc5bced293e1bed989f
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:e250f812e594e5152bf2d6f23caa8b53b78276bfdf344d7a8d355dd96cb995c0
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:95be359041f8291c74b335297a4dfe9902a180510f24a181b15e1a5ba4d3b024
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:118e43e4b915e262726183467cc4b82f244565213fea5b6bfe02be07f0851ab1
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:dc2ce3256a6552975962c9e113c52c1a22bf3817d417151f6f60640dd568e0fa
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:287d98890b35427688999760f9d066bcbff1a3bc9001534241dc212b32edabd8
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:66e91c92c868167c850078cd91714db47e10a96e23cc30191994486bd79c353f
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:d950d173f5101820c5e18205401ca08ce5feeff2302ac2920b292750d86a8fa4
size 6220817
@@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:72eddb7126eae0c839b933886e0fb69d78229f72d49ef13199de28df2b7879db
size 6220817
@@ -15,11 +15,11 @@ import pytest
import ly_test_tools.environment.file_system as file_system
from ly_test_tools.image.screenshot_compare_qssim import qssim as compare_screenshots
from ly_test_tools.benchmark.data_aggregator import BenchmarkDataAggregator
import editor_python_test_tools.hydra_test_utils as hydra
logger = logging.getLogger(__name__)
DEFAULT_SUBFOLDER_PATH = 'user/PythonTests/Automated/Screenshots'
EDITOR_TIMEOUT = 600
TEST_DIRECTORY = os.path.join(os.path.dirname(__file__), "atom_hydra_scripts")
@@ -67,6 +67,7 @@ class TestAllComponentsIndepthTests(object):
"Trace::Assert",
"Trace::Error",
"Traceback (most recent call last):",
"Screenshot failed"
]
hydra.launch_and_validate_results(
@@ -74,7 +75,7 @@ class TestAllComponentsIndepthTests(object):
TEST_DIRECTORY,
editor,
"hydra_GPUTest_BasicLevelSetup.py",
timeout=EDITOR_TIMEOUT,
timeout=180,
expected_lines=level_creation_expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
@@ -85,6 +86,60 @@ class TestAllComponentsIndepthTests(object):
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
compare_screenshots(test_screenshot, golden_screenshot)
def test_LightComponent_ScreenshotMatchesGoldenImage(
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 screenshots in a rendered level appear the same as the golden images.
"""
screenshot_names = [
"AreaLight_1.ppm",
"AreaLight_2.ppm",
"AreaLight_3.ppm",
"AreaLight_4.ppm",
"AreaLight_5.ppm",
"SpotLight_1.ppm",
"SpotLight_2.ppm",
"SpotLight_3.ppm",
"SpotLight_4.ppm",
"SpotLight_5.ppm",
"SpotLight_6.ppm",
]
test_screenshots = []
for screenshot in screenshot_names:
screenshot_path = os.path.join(workspace.paths.project(), DEFAULT_SUBFOLDER_PATH, screenshot)
test_screenshots.append(screenshot_path)
file_system.delete(test_screenshots, True, True)
golden_images = []
for golden_image in screenshot_names:
golden_image_path = os.path.join(golden_images_directory(), golden_image)
golden_images.append(golden_image_path)
expected_lines = ["Light component tests completed."]
unexpected_lines = [
"Trace::Assert",
"Trace::Error",
"Traceback (most recent call last):",
"Screenshot failed",
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
editor,
"hydra_GPUTest_LightComponent.py",
timeout=180,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
cfg_args=[level],
null_renderer=False,
)
for test_screenshot, golden_screenshot in zip(test_screenshots, golden_images):
compare_screenshots(test_screenshot, golden_screenshot)
@pytest.mark.parametrize('rhi', ['dx12', 'vulkan'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
@pytest.mark.parametrize("launcher_platform", ["windows_editor"])
@@ -99,6 +154,7 @@ class TestPerformanceBenchmarkSuite(object):
expected_lines = [
"Benchmark metadata captured.",
"Pass timestamps captured.",
"CPU frame time captured.",
"Capturing complete.",
"Captured data successfully."
]
@@ -106,6 +162,7 @@ class TestPerformanceBenchmarkSuite(object):
unexpected_lines = [
"Failed to capture data.",
"Failed to capture pass timestamps.",
"Failed to capture CPU frame time.",
"Failed to capture benchmark metadata."
]
@@ -114,7 +171,7 @@ class TestPerformanceBenchmarkSuite(object):
TEST_DIRECTORY,
editor,
"hydra_GPUTest_AtomFeatureIntegrationBenchmark.py",
timeout=EDITOR_TIMEOUT,
timeout=600,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
@@ -11,8 +11,9 @@ 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 atom_renderer.atom_utils.atom_component_helper import LIGHT_TYPES
from atom_renderer.atom_utils.atom_constants import LIGHT_TYPES
logger = logging.getLogger(__name__)
EDITOR_TIMEOUT = 120
@@ -31,7 +32,7 @@ class TestAtomEditorComponentsMain(object):
Tests the following Atom components and verifies all "expected_lines" appear in Editor.log:
1. Display Mapper
2. Light
3. Radius Weight Modifier
3. PostFX Radius Weight Modifier
4. PostFX Layer
5. Physical Sky
6. Global Skylight (IBL)
@@ -125,18 +126,18 @@ class TestAtomEditorComponentsMain(object):
"PostFX Layer_test: Entity deleted: True",
"PostFX Layer_test: UNDO entity deletion works: True",
"PostFX Layer_test: REDO entity deletion works: True",
# Radius Weight Modifier Component
"Radius Weight Modifier Entity successfully created",
"Radius Weight Modifier_test: Component added to the entity: True",
"Radius Weight Modifier_test: Component removed after UNDO: True",
"Radius Weight Modifier_test: Component added after REDO: True",
"Radius Weight Modifier_test: Entered game mode: True",
"Radius Weight Modifier_test: Exit game mode: True",
"Radius Weight Modifier_test: Entity is hidden: True",
"Radius Weight Modifier_test: Entity is shown: True",
"Radius Weight Modifier_test: Entity deleted: True",
"Radius Weight Modifier_test: UNDO entity deletion works: True",
"Radius Weight Modifier_test: REDO entity deletion works: True",
# PostFX Radius Weight Modifier Component
"PostFX Radius Weight Modifier Entity successfully created",
"PostFX Radius Weight Modifier_test: Component added to the entity: True",
"PostFX Radius Weight Modifier_test: Component removed after UNDO: True",
"PostFX Radius Weight Modifier_test: Component added after REDO: True",
"PostFX Radius Weight Modifier_test: Entered game mode: True",
"PostFX Radius Weight Modifier_test: Exit game mode: True",
"PostFX Radius Weight Modifier_test: Entity is hidden: True",
"PostFX Radius Weight Modifier_test: Entity is shown: True",
"PostFX Radius Weight Modifier_test: Entity deleted: True",
"PostFX Radius Weight Modifier_test: UNDO entity deletion works: True",
"PostFX Radius Weight Modifier_test: REDO entity deletion works: True",
# Light Component
"Light Entity successfully created",
"Light_test: Component added to the entity: True",
@@ -242,3 +243,65 @@ class TestAtomEditorComponentsMain(object):
null_renderer=True,
cfg_args=cfg_args,
)
@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"])
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 = [
# "Trace::Assert",
# "Trace::Error",
"Traceback (most recent call last):"
]
hydra.launch_and_validate_results(
request,
TEST_DIRECTORY,
generic_launcher,
"hydra_AtomMaterialEditor_BasicTests.py",
run_python="--runpython",
timeout=80,
expected_lines=expected_lines,
unexpected_lines=unexpected_lines,
halt_on_unexpected=True,
log_file_name="MaterialEditor.log",
)
@@ -13,7 +13,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_main and not REQUIRES_gpu"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -28,7 +27,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_periodic and not REQUIRES_gpu"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -44,7 +42,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_REQUIRES gpu
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_main and REQUIRES_gpu"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -59,7 +56,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SERIAL
PATH ${CMAKE_CURRENT_LIST_DIR}
PYTEST_MARKS "SUITE_sandbox"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
Legacy::Editor
AZ::AssetProcessor
@@ -61,7 +61,7 @@ class AssetPickerUIUXTest(EditorTestHelper):
5) Verify if Mesh Asset is assigned via both OK/Enter options
Note:
- This test file must be called from the Lumberyard Editor command terminal
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
@@ -37,7 +37,7 @@ class TestBasicEditorWorkflows(EditorTestHelper):
async def run_test(self):
"""
Summary:
Open Lumberyard editor and check if basic Editor workflows are completable.
Open O3DE editor and check if basic Editor workflows are completable.
Expected Behavior:
- A new level can be created
@@ -48,7 +48,7 @@ class TestBasicEditorWorkflows(EditorTestHelper):
- Level can be exported
Note:
- This test file must be called from the Lumberyard Editor command terminal
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
@@ -36,7 +36,7 @@ class TestEditMenuOptions(EditorTestHelper):
2) Interact with Edit Menu options
Note:
- This test file must be called from the Lumberyard Editor command terminal
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
@@ -32,7 +32,7 @@ class TestFileMenuOptions(EditorTestHelper):
2) Interact with File Menu options
Note:
- This test file must be called from the Lumberyard Editor command terminal
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
@@ -36,7 +36,7 @@ class TestViewMenuOptions(EditorTestHelper):
2) Interact with View Menu options
Note:
- This test file must be called from the Lumberyard Editor command terminal
- This test file must be called from the O3DE Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
@@ -16,7 +16,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -33,7 +32,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE sandbox
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_sandbox"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -49,7 +47,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_filter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -64,7 +61,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_modifier"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -79,7 +75,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_regression"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -94,7 +89,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_area"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -109,7 +103,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_misc"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -124,7 +117,6 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/dyn_veg
PYTEST_MARKS "SUITE_periodic and dynveg_surfacetagemitter"
TIMEOUT 1500
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -138,9 +130,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::LandscapeCanvasTests_Main
TEST_SERIAL
TEST_SUITE main
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas
PYTEST_MARKS "not SUITE_sandbox and not SUITE_periodic and not SUITE_benchmark"
TIMEOUT 1500
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/test_LandscapeCanvas_Main.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -153,9 +143,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::LandscapeCanvasTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas
PYTEST_MARKS "SUITE_periodic"
TIMEOUT 1500
PATH ${CMAKE_CURRENT_LIST_DIR}/landscape_canvas/test_LandscapeCanvas_Periodic.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -169,8 +157,7 @@ if(PAL_TRAIT_BUILD_TESTS_SUPPORTED AND PAL_TRAIT_BUILD_HOST_TOOLS AND PAL_TRAIT_
NAME AutomatedTesting::GradientSignalTests_Periodic
TEST_SERIAL
TEST_SUITE periodic
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal
TIMEOUT 1500
PATH ${CMAKE_CURRENT_LIST_DIR}/gradient_signal/test_GradientSignal_Periodic.py
RUNTIME_DEPENDENCIES
AZ::AssetProcessor
Legacy::Editor
@@ -10,6 +10,7 @@ import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.editor as editor
import azlmbr.legacy.general as general
import azlmbr.bus as bus
import azlmbr.math as math
@@ -107,7 +108,7 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
# Create an entity with a PhysX Collider and our PhysX Collider Surface Tag Emitter
collider_entity = hydra.Entity("Collider Surface")
collider_entity.create_entity(
entity_center_point,
entity_center_point,
["PhysX Collider", "PhysX Collider Surface Tag Emitter"]
)
if collider_entity.id.IsValid():
@@ -153,23 +154,29 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
self.test_success = self.test_success and baseline_success
# Setup collider entity with a PhysX Mesh
test_physx_mesh_asset_path = asset.AssetCatalogRequestBus(
test_physx_mesh_asset_id = asset.AssetCatalogRequestBus(
bus.Broadcast, "GetAssetIdByPath", os.path.join("levels", "physics",
"c4044697_material_perfacematerialvalidation",
"test.pxmesh"), math.Uuid(), False)
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Shape", 7)
hydra.get_set_test(collider_entity, 0, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_path)
# Remove/re-add component due to LYN-5496
collider_entity.remove_component("PhysX Collider")
collider_entity.add_component("PhysX Collider")
self.wait_for_condition(lambda: editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled',
collider_entity.components[1]), 5.0)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Shape", 7)
hydra.get_set_test(collider_entity, 1, "Shape Configuration|Asset|PhysX Mesh", test_physx_mesh_asset_id)
# Set the asset scale to match the test heights of the shapes tested
asset_scale = math.Vector3(1.0, 1.0, 9.0)
collider_entity.get_set_test(0, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale)
collider_entity.get_set_test(1, "Shape Configuration|Asset|Configuration|Asset Scale", asset_scale)
# Test: Generate a new surface on the collider.
# There should be one instance at the very top of the collider mesh, and none on the baseline surface
# (We use a small query box to only check for one placed instance point)
self.log("Starting PhysX Mesh Collider Test")
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [surface_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [invalid_tag])
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 1), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
@@ -180,8 +187,8 @@ class TestPhysXColliderSurfaceTagEmitter(EditorTestHelper):
# There should be no instances at the very top of the collider mesh, and none on the baseline surface within
# our query box as PhysX meshes are treated as hollow shells, not solid volumes.
# (We use a small query box to only check for one placed instance point)
hydra.get_set_test(collider_entity, 1, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 1, "Configuration|Extended Tags", [surface_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Generated Tags", [invalid_tag])
hydra.get_set_test(collider_entity, 0, "Configuration|Extended Tags", [surface_tag])
top_point_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(top_point, 0.25, 0), 5.0)
self.test_success = self.test_success and top_point_success
baseline_success = self.wait_for_condition(lambda: dynveg.validate_instance_count(baseline_surface_point,
@@ -4,124 +4,118 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestGradientGeneratorIncompatibilities(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientGeneratorIncompatibilities", args=["level"])
def GradientGenerators_Incompatibilities():
"""
Summary:
This test verifies that components are disabled when conflicting components are present on the same entity.
def run_test(self):
"""
Summary:
This test verifies that components are disabled when conflicting components are present on the same entity.
Expected Behavior:
Gradient Generator components are incompatible with Vegetation area components.
Expected Behavior:
Gradient Generator components are incompatible with Vegetation area components.
Test Steps:
1) Open a simple level
2) Create a new entity in the level
3) Add each Gradient Generator component to an entity, and add a Vegetation Area component to the same entity
4) Verify that components are only enabled when entity is free of a conflicting component
Test Steps:
1) Create a new level
2) Create a new entity in the level
3) Add each Gradient Generator component to an entity, and add a Vegetation Area component to the same entity
4) Verify that components are only enabled when entity is free of a conflicting component
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
gradient_generators = [
'Altitude Gradient',
'Constant Gradient',
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient',
'Shape Falloff Gradient',
'Slope Gradient',
'Surface Mask Gradient'
]
require_transform_modifiers = [
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient'
]
vegetation_areas = [
'Vegetation Layer Spawner',
'Vegetation Layer Blender',
'Vegetation Layer Blocker',
'Vegetation Layer Blocker (Mesh)'
]
area_dependencies = {
'Vegetation Layer Spawner': 'Vegetation Asset List',
'Vegetation Layer Blocker (Mesh)': 'Mesh'
}
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
gradient_generators = [
'Altitude Gradient',
'Constant Gradient',
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient',
'Shape Falloff Gradient',
'Slope Gradient',
'Surface Mask Gradient'
]
require_transform_modifiers = [
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient'
]
vegetation_areas = [
'Vegetation Layer Spawner',
'Vegetation Layer Blender',
'Vegetation Layer Blocker',
'Vegetation Layer Blocker (Mesh)'
]
area_dependencies = {
'Vegetation Layer Spawner': 'Vegetation Asset List',
'Vegetation Layer Blocker (Mesh)': 'Mesh'
}
# For every gradient generator component, verify that they are incompatible
# which each vegetation area component
for component_name in gradient_generators:
for vegetation_area_name in vegetation_areas:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
# For every gradient generator component, verify that they are incompatible
# which each vegetation area component
for component_name in gradient_generators:
for vegetation_area_name in vegetation_areas:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add the specific vegetation area dependencies (if necessary)
if vegetation_area_name in area_dependencies:
hydra.add_component(area_dependencies[vegetation_area_name], entity_id)
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
# Add the vegetation area component we are validating against, then add the
# gradient generator afterwards, so that the gradient generator will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the vegetation area)
area_component = hydra.add_component(vegetation_area_name, entity_id)
gradient_component = hydra.add_component(component_name, entity_id)
# Add the specific vegetation area dependencies (if necessary)
if vegetation_area_name in area_dependencies:
hydra.add_component(area_dependencies[vegetation_area_name], entity_id)
# Verify the gradient generator component is disabled since the vegetation area is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and not active
if not active:
self.log(f"{component_name} is disabled before removing {vegetation_area_name} component")
# Add the vegetation area component we are validating against, then add the
# gradient generator afterwards, so that the gradient generator will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the vegetation area)
area_component = hydra.add_component(vegetation_area_name, entity_id)
gradient_component = hydra.add_component(component_name, entity_id)
# Remove the vegetation area component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [area_component])
# Verify the gradient generator component is disabled since the vegetation area is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_disabled = (
f"{component_name} is disabled before removing {vegetation_area_name} component",
f"{component_name} is unexpectedly enabled before removing {vegetation_area_name} component"
)
Report.result(component_is_disabled, not active)
# Add required dependencies for our gradient generators after the vegetation
# area has been removed, because the transform modifier is also incompatible
# with the vegetation areas
if component_name in require_transform_modifiers:
hydra.add_component('Gradient Transform Modifier', entity_id)
# Remove the vegetation area component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [area_component])
# Verify the gradient generator component is enabled now that the vegetation area is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and active
if active:
self.log(f"{component_name} is enabled after removing {vegetation_area_name} component")
# Add required dependencies for our gradient generators after the vegetation
# area has been removed, because the transform modifier is also incompatible
# with the vegetation areas
if component_name in require_transform_modifiers:
hydra.add_component('Gradient Transform Modifier', entity_id)
# Verify the gradient generator component is enabled now that the vegetation area is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_enabled = (
f"{component_name} is enabled after removing {vegetation_area_name} component",
f"{component_name} is unexpectedly disabled after removing {vegetation_area_name} component"
)
Report.result(component_is_enabled, active)
test = TestGradientGeneratorIncompatibilities()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientGenerators_Incompatibilities)
@@ -4,164 +4,162 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestGradientModifiersIncompatibilities(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientModifiersIncompatibilities", args=["level"])
def GradientModifiers_Incompatibilities():
"""
Summary:
This test verifies that components are disabled when conflicting components are present on the same entity.
def run_test(self):
"""
Summary:
This test verifies that components are disabled when conflicting components are present on the same entity.
Expected Behavior:
Gradient Modifier components are incompatible with Vegetation area components.
Expected Behavior:
Gradient Modifier components are incompatible with Vegetation area components.
Test Steps:
1) Open a simple level
2) Create a new entity in the level
3) Add each Gradient Modifier component to an entity, and add a Vegetation Area component to the same entity
4) Verify that components are only enabled when entity is free of a conflicting component
Test Steps:
1) Create a new level
2) Create a new entity in the level
3) Add each Gradient Modifier component to an entity, and add a Vegetation Area component to the same entity
4) Verify that components are only enabled when entity is free of a conflicting component
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
gradient_generators = [
'Altitude Gradient',
'Constant Gradient',
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient',
'Shape Falloff Gradient',
'Slope Gradient',
'Surface Mask Gradient'
]
require_transform_modifiers = [
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient'
]
gradient_modifiers = [
'Dither Gradient Modifier',
'Gradient Mixer',
'Invert Gradient Modifier',
'Levels Gradient Modifier',
'Posterize Gradient Modifier',
'Smooth-Step Gradient Modifier',
'Threshold Gradient Modifier'
]
vegetation_areas = [
'Vegetation Layer Spawner',
'Vegetation Layer Blender',
'Vegetation Layer Blocker',
'Vegetation Layer Blocker (Mesh)'
]
area_dependencies = {
'Vegetation Layer Spawner': 'Vegetation Asset List',
'Vegetation Layer Blocker (Mesh)': 'Mesh'
}
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
gradient_generators = [
'Altitude Gradient',
'Constant Gradient',
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient',
'Shape Falloff Gradient',
'Slope Gradient',
'Surface Mask Gradient'
]
require_transform_modifiers = [
'FastNoise Gradient',
'Image Gradient',
'Perlin Noise Gradient',
'Random Noise Gradient'
]
gradient_modifiers = [
'Dither Gradient Modifier',
'Gradient Mixer',
'Invert Gradient Modifier',
'Levels Gradient Modifier',
'Posterize Gradient Modifier',
'Smooth-Step Gradient Modifier',
'Threshold Gradient Modifier'
]
vegetation_areas = [
'Vegetation Layer Spawner',
'Vegetation Layer Blender',
'Vegetation Layer Blocker',
'Vegetation Layer Blocker (Mesh)'
]
area_dependencies = {
'Vegetation Layer Spawner': 'Vegetation Asset List',
'Vegetation Layer Blocker (Mesh)': 'Mesh'
}
# For every gradient modifier component, verify that they are incompatible
# which each vegetation area and gradient generator/modifier component
all_gradients = gradient_modifiers + gradient_generators
for component_name in gradient_modifiers:
for vegetation_area_name in vegetation_areas:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
# For every gradient modifier component, verify that they are incompatible
# which each vegetation area and gradient generator/modifier component
all_gradients = gradient_modifiers + gradient_generators
for component_name in gradient_modifiers:
for vegetation_area_name in vegetation_areas:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add the specific vegetation area dependencies (if necessary)
if vegetation_area_name in area_dependencies:
hydra.add_component(area_dependencies[vegetation_area_name], entity_id)
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
# Add the vegetation area component we are validating against, then add the
# gradient modifier afterwards, so that the gradient modifier will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the vegetation area)
area_component = hydra.add_component(vegetation_area_name, entity_id)
gradient_component = hydra.add_component(component_name, entity_id)
# Add the specific vegetation area dependencies (if necessary)
if vegetation_area_name in area_dependencies:
hydra.add_component(area_dependencies[vegetation_area_name], entity_id)
# Verify the gradient modifier component is disabled since the vegetation area is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and not active
if not active:
self.log("{gradient} is disabled before removing {vegetation_area} component".format(gradient=component_name, vegetation_area=vegetation_area_name))
# Add the vegetation area component we are validating against, then add the
# gradient modifier afterwards, so that the gradient modifier will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the vegetation area)
area_component = hydra.add_component(vegetation_area_name, entity_id)
gradient_component = hydra.add_component(component_name, entity_id)
# Remove the vegetation area component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [area_component])
# Verify the gradient modifier component is disabled since the vegetation area is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_disabled = (
f"{component_name} is disabled before removing {vegetation_area_name} component",
f"{component_name} is unexpectedly enabled before removing {vegetation_area_name} component"
)
Report.result(component_is_disabled, not active)
# Verify the gradient modifier component is enabled now that the vegetation area is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and active
if active:
self.log("{gradient} is enabled after removing {vegetation_area} component".format(gradient=component_name, vegetation_area=vegetation_area_name))
# Remove the vegetation area component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [area_component])
for gradient_name in all_gradients:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Verify the gradient modifier component is enabled now that the vegetation area is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_enabled = (
f"{component_name} is enabled after removing {vegetation_area_name} component",
f"{component_name} is unexpectedly disabled after removing {vegetation_area_name} component"
)
Report.result(component_is_enabled, active)
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
for gradient_name in all_gradients:
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add the specific gradient generator dependencies (if necessary)
conflicting_components = []
if gradient_name in require_transform_modifiers:
component = hydra.add_component('Gradient Transform Modifier', entity_id)
conflicting_components.append(component)
# Most of these need a shape, so use a Box Shape
hydra.add_component('Box Shape', entity_id)
# Add the gradient component we are validating against, then add the
# gradient modifier afterwards, so that the gradient modifier will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the other gradient)
component = hydra.add_component(gradient_name, entity_id)
# Add the specific gradient generator dependencies (if necessary)
conflicting_components = []
if gradient_name in require_transform_modifiers:
component = hydra.add_component('Gradient Transform Modifier', entity_id)
conflicting_components.append(component)
gradient_component = hydra.add_component(component_name, entity_id)
# Verify the gradient modifier component is disabled since the other gradient is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and not active
if not active:
self.log("{gradient} is disabled before removing {conflicting_gradient} component".format(gradient=component_name, conflicting_gradient=gradient_name))
# Add the gradient component we are validating against, then add the
# gradient modifier afterwards, so that the gradient modifier will actually
# be disabled (if it was present before, it would only get deactivated instead of disabled
# by the other gradient)
component = hydra.add_component(gradient_name, entity_id)
conflicting_components.append(component)
gradient_component = hydra.add_component(component_name, entity_id)
# Remove the conflicting gradient component (and transform modifier if it was added)
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', conflicting_components)
# Verify the gradient modifier component is disabled since the other gradient is incompatible
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_disabled = (
f"{component_name} is disabled before removing {gradient_name} component",
f"{component_name} is unexpectedly enabled before removing {gradient_name} component"
)
Report.result(component_is_disabled, not active)
# Verify the gradient modifier component is enabled now that the other gradient is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
self.test_success = self.test_success and active
if active:
self.log("{gradient} is enabled after removing {conflicting_gradient} component".format(gradient=component_name, conflicting_gradient=gradient_name))
# Remove the conflicting gradient component (and transform modifier if it was added)
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', conflicting_components)
# Verify the gradient modifier component is enabled now that the other gradient is gone
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_component)
component_is_enabled = (
f"{component_name} is enabled after removing {gradient_name} component",
f"{component_name} is unexpectedly disabled after removing {gradient_name} component"
)
Report.result(component_is_enabled, active)
test = TestGradientModifiersIncompatibilities()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientModifiers_Incompatibilities)
@@ -5,129 +5,125 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
import azlmbr.entity as EntityId
def GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin():
"""
Summary:
A temporary level is created. An entity for each test case is created and added with the corresponding
components to verify if the gradient transform is set to the world origin.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
1) Preview image updates to reflect change in transform of the gradient sampler.
2) New Preview Position property is exposed, and set to 0,0,0 (world origin).
3) Preview Size is set to 1,1,1 by default.
Test Steps:
1) Open level
2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity
3) Create entity with Levels Gradient Modifier and verify gradient position after clearing pinned entity
4) Create entity with Posterize Gradient Modifier and verify gradient position after clearing pinned entity
5) Create entity with Smooth-Step Gradient Modifier and verify gradient position after clearing pinned entity
6) Create entity with Threshold Gradient Modifier and verify gradient position after clearing pinned entity
7) Create entity with FastNoise Gradient and verify gradient position after clearing pinned entity
8) Create entity with Dither Gradient Modifier and verify gradient position after clearing pinned entity
9) Create entity with Invert Gradient Modifier and verify gradient position after clearing pinned entity
10) Create entity with Perlin Noise Gradient and verify gradient position after clearing pinned entity
class TestGradientPreviewSettings(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientPreviewSettings_ClearPinnedEntity", args=["level"])
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def run_test(self):
"""
Summary:
A temporary level is created. An entity for each test case is created and added with the corresponding
components to verify if the gradient transform is set to the world origin.
:return: None
"""
Expected Behavior:
1) Preview image updates to reflect change in transform of the gradient sampler.
2) New Preview Position property is exposed, and set to 0,0,0 (world origin).
3) Preview Size is set to 1,1,1 by default.
import sys
Test Steps:
1) Open level
2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity
3) Create entity with Levels Gradient Modifier and verify gradient position after clearing pinned entity
4) Create entity with Posterize Gradient Modifier and verify gradient position after clearing pinned entity
5) Create entity with Smooth-Step Gradient Modifier and verify gradient position after clearing pinned entity
6) Create entity with Threshold Gradient Modifier and verify gradient position after clearing pinned entity
7) Create entity with FastNoise Gradient and verify gradient position after clearing pinned entity
8) Create entity with Dither Gradient Modifier and verify gradient position after clearing pinned entity
9) Create entity with Invert Gradient Modifier and verify gradient position after clearing pinned entity
10) Create entity with Perlin Noise Gradient and verify gradient position after clearing pinned entity
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
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
:return: None
"""
WORLD_ORIGIN = math.Vector3(0.0, 0.0, 0.0)
EXPECTED_SIZE = math.Vector3(1.0, 1.0, 1.0)
CLOSE_THRESHOLD = sys.float_info.min
WORLD_ORIGIN = math.Vector3(0.0, 0.0, 0.0)
EXPECTED_SIZE = math.Vector3(1.0, 1.0, 1.0)
CLOSE_THRESHOLD = sys.float_info.min
def create_entity(entity_name, components_to_add):
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
entity = hydra.Entity(entity_name, entity_id)
if entity_id.IsValid():
print(f"{entity_name} entity Created")
entity.components = []
for component in components_to_add:
entity.components.append(hydra.add_component(component, entity_id))
return entity
def create_entity(enity_name, components_to_add):
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
def clear_entityid_check_position(entity_name, components_to_add, check_preview_size=False):
entity = create_entity(entity_name, components_to_add)
hydra.get_set_test(entity, 0, "Preview Settings|Pin Preview to Shape", EntityId.EntityId())
preview_position = hydra.get_component_property_value(
entity.components[0], "Preview Settings|Preview Position"
)
preview_set_to_origin = (
f"{entity_name}: Preview Position set to world origin",
f"{entity_name}: Preview Position set to unexpected coords"
)
Report.result(preview_set_to_origin, preview_position.IsClose(WORLD_ORIGIN, CLOSE_THRESHOLD))
if check_preview_size:
preview_size = hydra.get_component_property_value(entity.components[0], "Preview Settings|Preview Size")
preview_size_default_set = (
f"{entity_name}: Preview Size set as expected",
f"{entity_name}: Preview Size set to unexpected value. Expected {EXPECTED_SIZE}, Found {preview_size}"
)
entity = hydra.Entity(enity_name, entity_id)
if entity_id.IsValid():
print(f"{enity_name} entity Created")
entity.components = []
for component in components_to_add:
entity.components.append(hydra.add_component(component, entity_id))
return entity
Report.result(preview_size_default_set, preview_size.IsClose(EXPECTED_SIZE, CLOSE_THRESHOLD))
return entity
def clear_entityid_check_position(entity_name, components_to_add, check_preview_size=False):
entity = create_entity(entity_name, components_to_add)
hydra.get_set_test(entity, 0, "Preview Settings|Pin Preview to Shape", EntityId.EntityId())
preview_position = hydra.get_component_property_value(
entity.components[0], "Preview Settings|Preview Position"
)
if preview_position.IsClose(WORLD_ORIGIN, CLOSE_THRESHOLD):
print(f"{entity_name} --- Preview Position set to world origin")
if check_preview_size:
preview_size = hydra.get_component_property_value(entity.components[0], "Preview Settings|Preview Size")
if preview_size.IsClose(EXPECTED_SIZE, CLOSE_THRESHOLD):
print(f"{entity_name} --- Preview Size set to (1, 1, 1)")
return entity
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"Random Noise Gradient", ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
# 2) Create entity with Random Noise gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"Random Noise Gradient", ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
# 3) Create entity with Levels Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Levels Gradient Modifier", ["Levels Gradient Modifier"])
# 3) Create entity with Levels Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Levels Gradient Modifier", ["Levels Gradient Modifier"])
# 4) Create entity with Posterize Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Posterize Gradient Modifier", ["Posterize Gradient Modifier"])
# 4) Create entity with Posterize Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Posterize Gradient Modifier", ["Posterize Gradient Modifier"])
# 5) Create entity with Smooth-Step Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Smooth-Step Gradient Modifier", ["Smooth-Step Gradient Modifier"])
# 5) Create entity with Smooth-Step Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Smooth-Step Gradient Modifier", ["Smooth-Step Gradient Modifier"])
# 6) Create entity with Threshold Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Threshold Gradient Modifier", ["Threshold Gradient Modifier"])
# 6) Create entity with Threshold Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Threshold Gradient Modifier", ["Threshold Gradient Modifier"])
# 7) Create entity with FastNoise Gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"FastNoise Gradient", ["FastNoise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
# 7) Create entity with FastNoise Gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"FastNoise Gradient", ["FastNoise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
# 8) Create entity with Dither Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Dither Gradient Modifier", ["Dither Gradient Modifier"], True)
# 8) Create entity with Dither Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Dither Gradient Modifier", ["Dither Gradient Modifier"], True)
# 9) Create entity with Invert Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Invert Gradient Modifier", ["Invert Gradient Modifier"])
# 9) Create entity with Invert Gradient Modifier and verify gradient position after clearing pinned entity
clear_entityid_check_position("Invert Gradient Modifier", ["Invert Gradient Modifier"])
# 10) Create entity with Perlin Noise Gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"Perlin Noise Gradient", ["Perlin Noise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
# 10) Create entity with Perlin Noise Gradient and verify gradient position after clearing pinned entity
clear_entityid_check_position(
"Perlin Noise Gradient", ["Perlin Noise Gradient", "Gradient Transform Modifier", "Box Shape"], True
)
test = TestGradientPreviewSettings()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin)
@@ -5,18 +5,6 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Scoped:
def __init__(self, constructor, destructor, *args):
@@ -33,81 +21,84 @@ class TestParams:
self.accessed_component = accessed_component
class TestGradientPreviewSettings(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientPreviewSettings_DefaultPinnedEntity", args=["level"])
def GradientPreviewSettings_DefaultPinnedEntityIsSelf():
"""
Summary:
This test verifies default values for the pinned entity for Gradient Preview settings.
def run_test(self):
"""
Summary:
This test verifies default values for the pinned entity for Gradient Preview settings.
Expected Behavior:
Pinned entity is self for all gradient generator/modifiers.
Expected Behavior:
Pinned entity is self for all gradient generator/modifiers.
Test Steps:
1) Open a simple level
2) Create a new entity in the level
3) Add each Gradient Generator component to an entity, and verify the Pin Preview to Shape property is set to
self
Test Steps:
1) Create a new level
2) Create a new entity in the level
3) Add each Gradient Generator component to an entity, and verify the Pin Preview to Shape property is set to
self
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
def execute_test(test_id, function, *args):
if function(*args):
self.log(test_id + ' has Preview pinned to own Entity result: SUCCESS')
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 create_entity():
return editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
def delete_entity(entity_id):
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityAndAllDescendants', entity_id)
def attach_components(component_list, entity_id):
components = []
for i in component_list:
components.append(hydra.add_component(i, entity_id))
return components
def validate_id_is_current(param):
entity_ptr = Scoped(create_entity, delete_entity)
added_components = attach_components(param.required_components, entity_ptr.data)
value = hydra.get_component_property_value(added_components[param.accessed_component],
'Preview Settings|Pin Preview to Shape')
self.test_success = self.test_success and entity_ptr.data.Equal(value)
return entity_ptr.data.Equal(value)
param_list = [
TestParams(['Gradient Transform Modifier', 'Box Shape', 'Perlin Noise Gradient'], 2),
TestParams(['Random Noise Gradient', 'Gradient Transform Modifier', 'Box Shape'], 0),
TestParams(['FastNoise Gradient', 'Gradient Transform Modifier', 'Box Shape'], 0),
TestParams(['Dither Gradient Modifier'], 0),
TestParams(['Invert Gradient Modifier'], 0),
TestParams(['Levels Gradient Modifier'], 0),
TestParams(['Posterize Gradient Modifier'], 0),
TestParams(['Smooth-Step Gradient Modifier'], 0),
TestParams(['Threshold Gradient Modifier'], 0)
]
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
def execute_test(test_id, function, *args):
pinned_to_self = (
f"{test_id} has Preview pinned to self",
f"{test_id} has Preview pinned to a different entity"
)
Report.result(pinned_to_self, function(*args))
for param in param_list:
execute_test(param.required_components[param.accessed_component],
validate_id_is_current, param)
def create_entity():
return editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
def delete_entity(entity_id):
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityAndAllDescendants', entity_id)
def attach_components(component_list, entity_id):
components = []
for i in component_list:
components.append(hydra.add_component(i, entity_id))
return components
def validate_id_is_current(param):
entity_ptr = Scoped(create_entity, delete_entity)
added_components = attach_components(param.required_components, entity_ptr.data)
value = hydra.get_component_property_value(added_components[param.accessed_component],
'Preview Settings|Pin Preview to Shape')
return entity_ptr.data.Equal(value)
param_list = [
TestParams(['Gradient Transform Modifier', 'Box Shape', 'Perlin Noise Gradient'], 2),
TestParams(['Random Noise Gradient', 'Gradient Transform Modifier', 'Box Shape'], 0),
TestParams(['FastNoise Gradient', 'Gradient Transform Modifier', 'Box Shape'], 0),
TestParams(['Dither Gradient Modifier'], 0),
TestParams(['Invert Gradient Modifier'], 0),
TestParams(['Levels Gradient Modifier'], 0),
TestParams(['Posterize Gradient Modifier'], 0),
TestParams(['Smooth-Step Gradient Modifier'], 0),
TestParams(['Threshold Gradient Modifier'], 0)
]
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
for param in param_list:
execute_test(param.required_components[param.accessed_component],
validate_id_is_current, param)
test = TestGradientPreviewSettings()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientPreviewSettings_DefaultPinnedEntityIsSelf)
@@ -5,92 +5,82 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.math as math
import azlmbr.paths
import azlmbr.entity as EntityId
def GradientSampling_GradientReferencesAddRemoveSuccessfully():
"""
Summary:
An existing gradient generator can be pinned and cleared to/from the Gradient Entity Id field
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
Gradient generator is assigned to the Gradient Entity Id field.
Gradient generator is removed from the field.
Test Steps:
1) Open level
2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape"
3) Create a new entity with Gradient Modifier's, pin and clear the random noise entity id to the Gradient Id
field in Gradient Modifier
class TestGradientSampling(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientSampling_GradientReferences", args=["level"])
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def run_test(self):
"""
Summary:
An existing gradient generator can be pinned and cleared to/from the Gradient Entity Id field
:return: None
"""
Expected Behavior:
Gradient generator is assigned to the Gradient Entity Id field.
Gradient generator is removed from the field.
import azlmbr.math as math
import azlmbr.entity as EntityId
Test Steps:
1) Open level
2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape"
3) Create a new entity with Gradient Modifier's, pin and clear the random noise entity id to the Gradient Id
field in Gradient Modifier
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
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
def modifier_pin_clear_to_gradiententityid(modifier):
entity_position = math.Vector3(125.0, 136.0, 32.0)
component_to_add = [modifier]
gradient_modifier = hydra.Entity(modifier)
gradient_modifier.create_entity(entity_position, component_to_add)
gradient_modifier.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", random_noise.id)
entity = hydra.get_component_property_value(
gradient_modifier.components[0], "Configuration|Gradient|Gradient Entity Id"
)
if entity.Equal(random_noise.id):
print(f"Gradient Generator is pinned to the {modifier} successfully")
else:
print(f"Failed to pin Gradient Generator to the {modifier}")
hydra.get_set_test(gradient_modifier, 0, "Configuration|Gradient|Gradient Entity Id", EntityId.EntityId())
entity = hydra.get_component_property_value(
gradient_modifier.components[0], "Configuration|Gradient|Gradient Entity Id"
)
if entity.Equal(EntityId.EntityId()):
print(f"Gradient Generator is cleared from the {modifier} successfully")
else:
print(f"Failed to clear Gradient Generator from the {modifier}")
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape"
def modifier_pin_clear_to_gradiententityid(modifier):
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
random_noise = hydra.Entity("Random_Noise")
random_noise.create_entity(entity_position, components_to_add)
component_to_add = [modifier]
gradient_modifier = hydra.Entity(modifier)
gradient_modifier.create_entity(entity_position, component_to_add)
gradient_modifier.get_set_test(0, "Configuration|Gradient|Gradient Entity Id", random_noise.id)
entity = hydra.get_component_property_value(
gradient_modifier.components[0], "Configuration|Gradient|Gradient Entity Id"
)
gradient_pinned_to_modifier = (
f"Gradient Generator is pinned to the {modifier} successfully",
f"Failed to pin Gradient Generator to the {modifier}"
)
Report.result(gradient_pinned_to_modifier, entity.Equal(random_noise.id))
hydra.get_set_test(gradient_modifier, 0, "Configuration|Gradient|Gradient Entity Id", EntityId.EntityId())
entity = hydra.get_component_property_value(
gradient_modifier.components[0], "Configuration|Gradient|Gradient Entity Id"
)
gradient_cleared_from_modifier = (
f"Gradient Generator is cleared from the {modifier} successfully",
f"Failed to clear Gradient Generator from the {modifier}"
)
Report.result(gradient_cleared_from_modifier, entity.Equal(EntityId.EntityId()))
# 3) Create a new entity with Gradient Modifier's, pin and clear the random noise entity id to the Gradient Id
# field in Gradient Modifier
modifier_pin_clear_to_gradiententityid("Dither Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Invert Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Levels Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Posterize Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Smooth-Step Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Threshold Gradient Modifier")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Create a new entity with components "Random Noise Gradient", "Gradient Transform Modifier" and "Box Shape"
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
random_noise = hydra.Entity("Random_Noise")
random_noise.create_entity(entity_position, components_to_add)
# 3) Create a new entity with Gradient Modifier's, pin and clear the random noise entity id to the Gradient Id
# field in Gradient Modifier
modifier_pin_clear_to_gradiententityid("Dither Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Invert Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Levels Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Posterize Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Smooth-Step Gradient Modifier")
modifier_pin_clear_to_gradiententityid("Threshold Gradient Modifier")
test = TestGradientSampling()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientSampling_GradientReferencesAddRemoveSuccessfully)
@@ -5,114 +5,104 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.entity as entity
import azlmbr.paths
import azlmbr.editor as editor
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestGradientSurfaceTagEmitterDependencies(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(
self, log_prefix="GradientSurfaceTagEmitter_ComponentDependencies", args=["level"]
def GradientSurfaceTagEmitter_ComponentDependencies():
"""
Summary:
This test verifies that the Gradient Surface Tag Emitter component is dependent on a gradient component.
Expected Result:
Gradient Surface Tag Emitter component is disabled until a Gradient Generator, Modifier or Gradient Reference
component (and any sub-dependencies) is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Gradient Surface Tag Emitter component
3) Verify the component is disabled until a dependent component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.math as math
import azlmbr.bus as bus
import azlmbr.entity as entity
import azlmbr.editor as editor
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 is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create an entity with Gradient Surface Tag Emitter component
position = math.Vector3(512.0, 512.0, 32.0)
gradient = hydra.Entity("gradient")
gradient.create_entity(position, ["Gradient Surface Tag Emitter"])
# Make sure Gradient Surface Tag Emitter is disabled
gradient_surface_tag_disabled = (
"Gradient Surface Tag Emitter is Disabled",
"Gradient Surface Tag Emitter is Enabled, but should be Disabled without dependencies met"
)
Report.result(gradient_surface_tag_disabled, not is_enabled(gradient.components[0]))
# Verify Gradient Surface Tag Emitter component is enabled after adding Gradient, Generator, Modifier
# or Reference component
new_components_to_add = [
"Dither Gradient Modifier",
"Gradient Mixer",
"Invert Gradient Modifier",
"Levels Gradient Modifier",
"Posterize Gradient Modifier",
"Smooth-Step Gradient Modifier",
"Threshold Gradient Modifier",
"Altitude Gradient",
"Constant Gradient",
"FastNoise Gradient",
"Image Gradient",
"Perlin Noise Gradient",
"Random Noise Gradient",
"Reference Gradient",
"Shape Falloff Gradient",
"Slope Gradient",
"Surface Mask Gradient",
]
for component in new_components_to_add:
component_list = ["FastNoise Gradient", "Image Gradient", "Perlin Noise Gradient", "Random Noise Gradient"]
if component in component_list:
for Component in ["Gradient Transform Modifier", "Box Shape"]:
hydra.add_component(Component, gradient.id)
typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component],
entity.EntityType().Game)
ComponentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', gradient.id, [typeIdsList[0]])
Components = ComponentOutcome.GetValue()
ComponentIdPair = Components[0]
gradient_enabled = is_enabled(gradient.components[0])
new_components_enabled = is_enabled(ComponentIdPair)
dependencies_met = (
f"{component} and Gradient Surface Tag Emitter are enabled",
f"{component} and Gradient Surface Tag Emitter are disabled"
)
Report.result(dependencies_met, new_components_enabled and gradient_enabled)
def run_test(self):
"""
Summary:
This test verifies that the Gradient Surface Tag Emitter component is dependent on a gradient component.
Expected Result:
Gradient Surface Tag Emitter component is disabled until a Gradient Generator, Modifier or Gradient Reference
component (and any sub-dependencies) is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Gradient Surface Tag Emitter component
3) Verify the component is disabled until a dependent component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
def is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Create an entity with Gradient Surface Tag Emitter component
position = math.Vector3(512.0, 512.0, 32.0)
gradient = hydra.Entity("gradient")
gradient.create_entity(position, ["Gradient Surface Tag Emitter"])
# Make sure Gradient Surface Tag Emitter is disabled
is_enable = is_enabled(gradient.components[0])
if not is_enable:
self.log("Gradient Surface Tag Emitter is Disabled")
elif not is_enable:
self.log("Gradient Surface Tag Emitter is Enabled, but should be Disabled without dependencies met")
# Verify Gradient Surface Tag Emitter component is enabled after adding Gradient, Generator, Modifier
# or Reference component
new_components_to_add = [
"Dither Gradient Modifier",
"Gradient Mixer",
"Invert Gradient Modifier",
"Levels Gradient Modifier",
"Posterize Gradient Modifier",
"Smooth-Step Gradient Modifier",
"Threshold Gradient Modifier",
"Altitude Gradient",
"Constant Gradient",
"FastNoise Gradient",
"Image Gradient",
"Perlin Noise Gradient",
"Random Noise Gradient",
"Reference Gradient",
"Shape Falloff Gradient",
"Slope Gradient",
"Surface Mask Gradient",
]
for component in new_components_to_add:
component_list = ["FastNoise Gradient", "Image Gradient", "Perlin Noise Gradient", "Random Noise Gradient"]
if component in component_list:
for Component in ["Gradient Transform Modifier", "Box Shape"]:
hydra.add_component(Component, gradient.id)
typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', [component],
entity.EntityType().Game)
ComponentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', gradient.id, [typeIdsList[0]])
Components = ComponentOutcome.GetValue()
ComponentIdPair = Components[0]
gradient_enabled = new_components_enabled = False
gradient_enabled = is_enabled(gradient.components[0])
new_components_enabled = is_enabled(ComponentIdPair)
if new_components_enabled and gradient_enabled:
self.log(f"{component} and Gradient Surface Tag Emitter are enabled")
else:
self.log(f"{component} and Gradient Surface Tag Emitter are disabled")
if component in component_list:
hydra.remove_component("Gradient Transform Modifier", gradient.id)
hydra.remove_component("Box Shape", gradient.id)
hydra.remove_component(component, gradient.id)
if component in component_list:
hydra.remove_component("Gradient Transform Modifier", gradient.id)
hydra.remove_component("Box Shape", gradient.id)
hydra.remove_component(component, gradient.id)
test = TestGradientSurfaceTagEmitterDependencies()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientSurfaceTagEmitter_ComponentDependencies)
@@ -5,75 +5,69 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.math as math
import azlmbr.paths
import azlmbr.surface_data as surface_data
def GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully():
"""
Summary:
Entity with Gradient Surface Tag Emitter and Reference Gradient components is created.
And new surface tag has been added and removed.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
A new Surface Tag can be added and removed from the component
Test Steps:
1) Open level
2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components.
3) Add/ remove Surface Tags
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.math as math
import azlmbr.surface_data as surface_data
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
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components.
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Surface Tag Emitter", "Reference Gradient"]
entity = hydra.Entity("entity")
entity.create_entity(entity_position, components_to_add)
# 3) Add/ remove Surface Tags
tag = surface_data.SurfaceTag()
tag.SetTag("water")
pte = hydra.get_property_tree(entity.components[0])
path = "Configuration|Extended Tags"
pte.add_container_item(path, 0, tag)
success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1, 1.0)
tag_added_to_container = (
"Successfully added surface tag",
"Failed to add surface tag"
)
Report.result(tag_added_to_container, success)
pte.remove_container_item(path, 0)
success = helper.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0, 1.0)
tag_removed_from_container = (
"Successfully removed surface tag",
"Failed to remove surface tag"
)
Report.result(tag_removed_from_container, success)
class TestGradientSurfaceTagEmitter(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSucessfully",
args=["level"])
if __name__ == "__main__":
def run_test(self):
"""
Summary:
Entity with Gradient Surface Tag Emitter and Reference Gradient components is created.
And new surface tag has been added and removed.
Expected Behavior:
A new Surface Tag can be added and removed from the component
from editor_python_test_tools.utils import Report
Report.start_test(GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully)
Test Steps:
1) Open level
2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components.
3) Add/ remove Surface Tags
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# 2) Create an entity with Gradient Surface Tag Emitter and Reference Gradient components.
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Surface Tag Emitter", "Reference Gradient"]
entity = hydra.Entity("entity")
entity.create_entity(entity_position, components_to_add)
# 3) Add/ remove Surface Tags
tag = surface_data.SurfaceTag()
tag.SetTag("water")
pte = hydra.get_property_tree(entity.components[0])
path = "Configuration|Extended Tags"
pte.add_container_item(path, 0, tag)
success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 1)
self.test_success = self.test_success and success
print(f"Added SurfaceTag: container count is {pte.get_container_count(path).GetValue()}")
pte.remove_container_item(path, 0)
success = self.wait_for_condition(lambda: pte.get_container_count(path).GetValue() == 0)
self.test_success = self.test_success and success
print(f"Removed SurfaceTag: container count is {pte.get_container_count(path).GetValue()}")
test = TestGradientSurfaceTagEmitter()
test.run()
@@ -5,118 +5,102 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
def GradientTransform_ComponentIncompatibleWithExpectedGradients():
"""
Summary:
A New level is created. A New entity is created with components Gradient Transform Modifier and Box Shape.
Adding components Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
All added components are disabled and inform the user that they are incompatible with the Gradient Transform
Modifier
Test Steps:
1) Create level
2) Create a new entity with components Gradient Transform Modifier and Box Shape
3) Make sure all components are enabled in Entity
4) Add Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity
5) Make sure all newly added components are disabled
class TestGradientTransform_ComponentIncompatibleWithExpectedGradients(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientTransform_ComponentIncompatibleWithExpectedGradients",
args=["level"])
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def run_test(self):
"""
Summary:
A New level is created. A New entity is created with components Gradient Transform Modifier and Box Shape.
Adding components Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity.
:return: None
"""
Expected Behavior:
All added components are disabled and inform the user that they are incompatible with the Gradient Transform
Modifier
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
Test Steps:
1) Create level
2) Create a new entity with components Gradient Transform Modifier and Box Shape
3) Make sure all components are enabled in Entity
4) Add Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity
5) Make sure all newly added components are disabled
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
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
:return: None
"""
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
def is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
# 2) Create a new entity with components Gradient Transform Modifier and Box Shape
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Transform Modifier", "Box Shape"]
gradient_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
gradient = hydra.Entity("gradient", gradient_id)
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
gradient.components = []
for component in components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
entity_created = (
"Entity created successfully",
"Failed to create entity"
)
Report.critical_result(entity_created, gradient_id.isValid())
# 3) Make sure all components are enabled in Entity
index = 0
for component in components_to_add:
components_enabled = (
f"{component} is enabled",
f"{component} is unexpectedly disabled"
)
Report.critical_result(components_enabled, is_enabled(gradient.components[index]))
index += 1
# 2) Create a new entity with components Gradient Transform Modifier and Box Shape
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Transform Modifier", "Box Shape"]
gradient_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
# 4) Add Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
# Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity
new_components_to_add = [
"Constant Gradient",
"Altitude Gradient",
"Gradient Mixer",
"Reference Gradient",
"Shape Falloff Gradient",
"Slope Gradient",
"Surface Mask Gradient",
]
new_components_enabled = False
for component in new_components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
gradient_components_disabled = (
f"{component} is disabled",
f"{component} is enabled, but should be disabled"
)
gradient = hydra.Entity("gradient", gradient_id)
gradient.components = []
for component in components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
if gradient_id.isValid():
self.log("New Entity Created")
# 3) Make sure all components are enabled in Entity
index = 0
for component in components_to_add:
is_enable = is_enabled(gradient.components[index])
if is_enable:
self.log(f"{component} is Enabled")
self.test_success = self.test_success and is_enable
elif not is_enable:
self.log(f"{component} is disabled, but it should be enabled")
self.test_success = self.test_success and is_enable
break
index += 1
# 4) Add Constant Gradient, Altitude Gradient, Gradient Mixer, Reference Gradient, Shape
# Falloff Gradient, Slope Gradient and Surface Mask Gradient to the same entity
new_components_to_add = [
"Constant Gradient",
"Altitude Gradient",
"Gradient Mixer",
"Reference Gradient",
"Shape Falloff Gradient",
"Slope Gradient",
"Surface Mask Gradient",
]
index = 2
new_components_enabled = False
for component in new_components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
new_components_enabled = is_enabled(gradient.components[index])
if new_components_enabled:
self.log(f"{component} is enabled, but should be disabled")
break
Report.result(gradient_components_disabled, not is_enabled(gradient.components[2]))
if not is_enabled(gradient.components[2]):
editor.EditorComponentAPIBus(bus.Broadcast, "RemoveComponents", component)
# 5) Make sure all newly added components are disabled
if not new_components_enabled:
self.log("All newly added components are incompatible and disabled")
self.test_success = self.test_success and not new_components_enabled
if __name__ == "__main__":
test = TestGradientTransform_ComponentIncompatibleWithExpectedGradients()
test.run()
from editor_python_test_tools.utils import Report
Report.start_test(GradientTransform_ComponentIncompatibleWithExpectedGradients)
@@ -5,104 +5,88 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
def GradientTransform_ComponentIncompatibleWithSpawners():
"""
Summary:
A simple level is opened. A New entity is created with components Gradient Transform Modifier and Box Shape.
Adding a component Vegetation Layer Spawner to the same entity.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
The Vegetation Layer Spawner is deactivated and it is communicated that it is incompatible with Gradient
Transform Modifier
Test Steps:
1) Open a simple level
2) Create a new entity with components Gradient Transform Modifier and Box Shape
3) Make sure all components are enabled in Entity
4) Add Vegetation Layer Spawner to the same entity
5) Make sure newly added component is disabled
class TestGradientTransform_ComponentIncompatibleWithSpawners(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientTransform_ComponentIncompatibleWithSpawners",
args=["level"])
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def run_test(self):
"""
Summary:
A New level is created. A New entity is created with components Gradient Transform Modifier and Box Shape.
Adding a component Vegetation Layer Spawner to the same entity.
:return: None
"""
Expected Behavior:
The Vegetation Layer Spawner is deactivated and it is communicated that it is incompatible with Gradient
Transform Modifier
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
Test Steps:
1) Create level
2) Create a new entity with components Gradient Transform Modifier and Box Shape
3) Make sure all components are enabled in Entity
4) Add Vegetation Layer Spawner to the same entity
5) Make sure newly added component is disabled
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
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
def is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
:return: None
"""
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
def is_enabled(EntityComponentIdPair):
return editor.EditorComponentAPIBus(bus.Broadcast, "IsComponentEnabled", EntityComponentIdPair)
# 2) Create a new entity with components Gradient Transform Modifier and Box Shape
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Transform Modifier", "Box Shape"]
gradient_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
gradient = hydra.Entity("gradient", gradient_id)
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
gradient.components = []
for component in components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
entity_created = (
"Entity created successfully",
"Failed to create entity"
)
Report.critical_result(entity_created, gradient_id.isValid())
# 3) Make sure all components are enabled in Entity
index = 0
for component in components_to_add:
components_enabled = (
f"{component} is enabled",
f"{component} is unexpectedly disabled"
)
Report.critical_result(components_enabled, is_enabled(gradient.components[index]))
index += 1
# 2) Create a new entity with components Gradient Transform Modifier and Box Shape
entity_position = math.Vector3(125.0, 136.0, 32.0)
components_to_add = ["Gradient Transform Modifier", "Box Shape"]
gradient_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
gradient = hydra.Entity("gradient", gradient_id)
# 4) Add Vegetation Layer Spawner to the same entity
gradient.components.append(hydra.add_component("Vegetation Layer Spawner", gradient_id))
gradient.components = []
for component in components_to_add:
gradient.components.append(hydra.add_component(component, gradient_id))
if gradient_id.isValid():
self.log("New Entity Created")
# 3) Make sure all components are enabled in Entity
index = 0
for component in components_to_add:
is_enable = is_enabled(gradient.components[index])
if is_enable:
self.log(f"{component} is Enabled")
self.test_success = self.test_success and is_enable
elif not is_enable:
self.log(f"{component} is Disabled. But It should be Enabled in an Entity")
self.test_success = self.test_success and is_enable
break
index += 1
# 4) Add Vegetation Layer Spawner to the same entity
new_component_to_add = "Vegetation Layer Spawner"
index = 2
gradient.components.append(hydra.add_component(new_component_to_add, gradient_id))
new_component_enabled = is_enabled(gradient.components[index])
# 5) Make sure newly added component is disabled
if not new_component_enabled:
self.log(f"{new_component_to_add} is incompatible and disabled")
self.test_success = self.test_success and not new_component_enabled
elif new_component_enabled:
self.log(f"{new_component_to_add} is compatible and enabled. But It should be Incompatible and disabled")
self.test_success = self.test_success and new_component_enabled
# 5) Make sure newly added component is disabled
spawner_component_disabled = (
"Spawner component is disabled",
"Spawner component is unexpectedly enabled"
)
Report.result(spawner_component_disabled, not is_enabled(gradient.components[2]))
test = TestGradientTransform_ComponentIncompatibleWithSpawners()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientTransform_ComponentIncompatibleWithSpawners)
@@ -5,87 +5,82 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
"""
C3430292: Frequency Zoom can manually be set higher than 8.
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
import azlmbr.entity as EntityId
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
entity_created = (
"Entity created successfully",
"Failed to create entity"
)
components_added = (
"All expected components added to entity",
"Failed to add expected components to entity"
)
higher_zoom_value_set = (
"Frequency Zoom is equal to expected value",
"Frequency Zoom is not equal to expected value"
)
class TestGradientTransformFrequencyZoom(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientTransform_FrequencyZoomBeyondSliders", args=["level"])
def GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange():
"""
Summary:
Frequency Zoom can manually be set higher than 8 in a random noise gradient
def run_test(self):
"""
Summary:
Frequency Zoom can manually be set higher than 8 in a random noise gradient
Expected Behavior:
The value properly changes, despite the value being outside of the slider limit
Expected Behavior:
The value properly changes, despite the value being outside of the slider limit
Test Steps:
1) Open level
2) Create entity
3) Add components to the entity
4) Set the frequency value of the component
5) Verify if the frequency value is set to higher value
Test Steps:
1) Open level
2) Create entity
3) Add components to the entity
4) Set the frequency value of the component
5) Verify if the frequency value is set to higher value
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.entity as EntityId
# 1) Open level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
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
# 2) Create entity
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if entity_id.IsValid():
print("Entity Created")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# 3) Add components to the entity
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
entity = hydra.Entity("entity", entity_id)
entity.components = []
for component in components_to_add:
entity.components.append(hydra.add_component(component, entity_id))
print("Components added to the entity")
# 2) Create entity
entity_position = math.Vector3(125.0, 136.0, 32.0)
entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
Report.critical_result(Tests.entity_created, entity_id.IsValid())
# 4) Set the frequency value of the component
hydra.get_set_test(entity, 1, "Configuration|Frequency Zoom", 10)
# 3) Add components to the entity
components_to_add = ["Random Noise Gradient", "Gradient Transform Modifier", "Box Shape"]
entity = hydra.Entity("entity", entity_id)
entity.components = []
for component in components_to_add:
entity.components.append(hydra.add_component(component, entity_id))
Report.critical_result(Tests.components_added, len(entity.components) == 3)
# 5) Verify if the frequency value is set to higher value
curr_value = hydra.get_component_property_value(entity.components[1], "Configuration|Frequency Zoom")
if curr_value == 10.0:
print("Frequency Zoom is equal to expected value")
else:
print("Frequency Zoom is not equal to expected value")
# 4) Set the frequency value of the component
hydra.get_set_test(entity, 1, "Configuration|Frequency Zoom", 10)
# 5) Verify if the frequency value is set to higher value
curr_value = hydra.get_component_property_value(entity.components[1], "Configuration|Frequency Zoom")
Report.result(Tests.higher_zoom_value_set, curr_value == 10.0)
test = TestGradientTransformFrequencyZoom()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange)
@@ -4,73 +4,71 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestGradientTransformRequiresShape(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientTransformRequiresShape", args=["level"])
def run_test(self):
"""
Summary:
This test verifies that the Gradient Transform Modifier component is dependent on a shape component.
Expected Result:
Gradient Transform Modifier component is disabled until a shape component is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Gradient Transform Modifier component
3) Verify the component is disabled until a shape component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add a Gradient Transform Component (that will be disabled since there is no shape on the Entity)
gradient_transform_component = hydra.add_component('Gradient Transform Modifier', entity_id)
# Verify the Gradient Transform Component is not active before adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_transform_component)
self.test_success = self.test_success and not active
if not active:
self.log("Gradient Transform component is not active without a Shape component on the Entity")
# Add a Shape component to the same Entity
hydra.add_component('Box Shape', entity_id)
# Check if the Gradient Transform Component is active now after adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_transform_component)
self.test_success = self.test_success and active
if active:
self.log("Gradient Transform Modifier component is active now that the Entity has a Shape")
class Tests:
disabled_without_shape = (
"Gradient Transform Modifier component is disabled without a Shape component on the Entity",
"Gradient Transform Modifier component is unexpectedly enabled without a Shape component on the Entity",
)
enabled_with_shape = (
"Gradient Transform Modifier component is enabled now that the Entity has a Shape",
"Gradient Transform Modifier component is still disabled alongside a Shape component",
)
test = TestGradientTransformRequiresShape()
test.run()
def GradientTransform_RequiresShape():
"""
Summary:
This test verifies that the Gradient Transform Modifier component is dependent on a shape component.
Expected Result:
Gradient Transform Modifier component is disabled until a shape component is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Gradient Transform Modifier component
3) Verify the component is disabled until a shape component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
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
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add a Gradient Transform Component (that will be disabled since there is no shape on the Entity)
gradient_transform_component = hydra.add_component('Gradient Transform Modifier', entity_id)
# Verify the Gradient Transform Component is not active before adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_transform_component)
Report.result(Tests.disabled_without_shape, not active)
# Add a Shape component to the same Entity
hydra.add_component('Box Shape', entity_id)
# Check if the Gradient Transform Component is active now after adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', gradient_transform_component)
Report.result(Tests.enabled_with_shape, active)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientTransform_RequiresShape)
@@ -5,89 +5,91 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)))
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.editor as editor
import azlmbr.math as math
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
image_gradient_entity_created = (
"Image Gradient entity created",
"Failed to create Image Gradient entity",
)
image_gradient_asset_found = (
"image_grad_test_gsi.png was found in the workspace",
"image_grad_test_gsi.png was not found in the workspace"
)
image_gradient_assigned = (
"Successfully assigned image gradient asset",
"Failed to assign image gradient asset"
)
class TestImageGradient(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ImageGradient_ProcessedImageAssignedSucessfully",
args=["level"])
def ImageGradient_ProcessedImageAssignedSuccessfully():
"""
Summary:
Level created with Entity having Image Gradient and Gradient Transform Modifier components.
Save any new image to your workspace with the suffix "_gsi" and assign as image asset.
def run_test(self):
"""
Summary:
Level created with Entity having Image Gradient and Gradient Transform Modifier components.
Save any new image to your workspace with the suffix "_gsi" and assign as image asset.
Expected Behavior:
Image can be assigned as the Image Asset for the Image as Gradient component.
Expected Behavior:
Image can be assigned as the Image Asset for the Image as Gradient component.
Test Steps:
1) Create level
2) Create an entity with Image Gradient and Gradient Transform Modifier components.
3) Assign the newly processed gradient image as Image asset.
Test Steps:
1) Open a level
2) Create an entity with Image Gradient and Gradient Transform Modifier components.
3) Assign the newly processed gradient image as Image asset.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# 1) Create level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
import os
# 2) Create an entity with Image Gradient and Gradient Transform Modifier components
components_to_add = ["Image Gradient", "Gradient Transform Modifier", "Box Shape"]
entity_position = math.Vector3(512.0, 512.0, 32.0)
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
if new_entity_id.IsValid():
print("Image Gradient Entity created")
image_gradient_entity = hydra.Entity("Image Gradient Entity", new_entity_id)
image_gradient_entity.components = []
for component in components_to_add:
image_gradient_entity.add_component(component)
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.entity as EntityId
import azlmbr.editor as editor
import azlmbr.math as math
# 3) Assign the processed gradient signal image as the Image Gradient's image asset and verify success
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
# First, check for the base image in the workspace
base_image = "image_grad_test_gsi.png"
base_image_path = os.path.join("AutomatedTesting", "Assets", "ImageGradients", base_image)
if os.path.isfile(base_image_path):
print(f"{base_image} was found in the workspace")
# 1) Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Next, assign the processed image to the Image Gradient's Image Asset property
processed_image_path = os.path.join("Assets", "ImageGradients", "image_grad_test_gsi.gradimage")
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", processed_image_path, math.Uuid(),
False)
hydra.get_set_test(image_gradient_entity, 0, "Configuration|Image Asset", asset_id)
# 2) Create an entity with Image Gradient and Gradient Transform Modifier components
components_to_add = ["Image Gradient", "Gradient Transform Modifier", "Box Shape"]
entity_position = math.Vector3(512.0, 512.0, 32.0)
new_entity_id = editor.ToolsApplicationRequestBus(
bus.Broadcast, "CreateNewEntityAtPosition", entity_position, EntityId.EntityId()
)
Report.critical_result(Tests.image_gradient_entity_created, new_entity_id.IsValid())
image_gradient_entity = hydra.Entity("Image Gradient Entity", new_entity_id)
image_gradient_entity.components = []
for component in components_to_add:
image_gradient_entity.add_component(component)
# Finally, verify if the gradient image is assigned as the Image Asset
success = hydra.get_component_property_value(image_gradient_entity.components[0], "Configuration|Image Asset") == asset_id
self.test_success = self.test_success and success
# 3) Assign the processed gradient signal image as the Image Gradient's image asset and verify success
# First, check for the base image in the workspace
base_image = "image_grad_test_gsi.png"
base_image_path = os.path.join("AutomatedTesting", "Assets", "ImageGradients", base_image)
Report.critical_result(Tests.image_gradient_asset_found, os.path.isfile(base_image_path))
# Next, assign the processed image to the Image Gradient's Image Asset property
processed_image_path = os.path.join("Assets", "ImageGradients", "image_grad_test_gsi.gradimage")
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", processed_image_path, math.Uuid(),
False)
hydra.get_set_test(image_gradient_entity, 0, "Configuration|Image Asset", asset_id)
# Finally, verify if the gradient image is assigned as the Image Asset
success = hydra.get_component_property_value(image_gradient_entity.components[0], "Configuration|Image Asset") == asset_id
Report.result(Tests.image_gradient_assigned, success)
test = TestImageGradient()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ImageGradient_ProcessedImageAssignedSuccessfully)
@@ -4,74 +4,72 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestImageGradientRequiresShape(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ImageGradientRequiresShape", args=["level"])
def run_test(self):
"""
Summary:
This test verifies that the Image Gradient component is dependent on a shape component.
Expected Result:
Gradient Transform Modifier component is disabled until a shape component is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Image Gradient component
3) Verify the component is disabled until a shape component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add an Image Gradient and Gradient Transform Component (should be disabled until a Shape exists on the Entity)
image_gradient_component = hydra.add_component('Image Gradient', entity_id)
hydra.add_component('Gradient Transform Modifier', entity_id)
# Verify the Image Gradient Component is not active before adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', image_gradient_component)
self.test_success = self.test_success and not active
if not active:
self.log("Image Gradient component is not active without a Shape component on the Entity")
# Add a Shape component to the same Entity
hydra.add_component('Box Shape', entity_id)
# Check if the Image Gradient Component is active now after adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', image_gradient_component)
self.test_success = self.test_success and active
if active:
self.log("Image Gradient component is active now that the Entity has a Shape")
class Tests:
disabled_without_shape = (
"Image Gradient component is disabled without a Shape component on the Entity",
"Image Gradient component is unexpectedly enabled without a Shape component on the Entity",
)
enabled_with_shape = (
"Image Gradient component is enabled now that the Entity has a Shape",
"Image Gradient component is still disabled alongside a Shape component",
)
test = TestImageGradientRequiresShape()
test.run()
def ImageGradient_RequiresShape():
"""
Summary:
This test verifies that the Image Gradient component is dependent on a shape component.
Expected Result:
Gradient Transform Modifier component is disabled until a shape component is added to the entity.
Test Steps:
1) Open level
2) Create a new entity with a Image Gradient component
3) Verify the component is disabled until a shape component is also added to the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
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
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a new Entity in the level
entity_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Add an Image Gradient and Gradient Transform Component (should be disabled until a Shape exists on the Entity)
image_gradient_component = hydra.add_component('Image Gradient', entity_id)
hydra.add_component('Gradient Transform Modifier', entity_id)
# Verify the Image Gradient Component is not active before adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', image_gradient_component)
Report.result(Tests.disabled_without_shape, not active)
# Add a Shape component to the same Entity
hydra.add_component('Box Shape', entity_id)
# Check if the Image Gradient Component is active now after adding the Shape
active = editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled', image_gradient_component)
Report.result(Tests.enabled_with_shape, active)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ImageGradient_RequiresShape)
@@ -0,0 +1,71 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import pytest
import sys
sys.path.append(os.path.dirname(os.path.abspath(__file__)) + '/../../automatedtesting_shared')
from base import TestAutomationBase
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(TestAutomationBase):
def test_GradientGenerators_Incompatibilities(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientGenerators_Incompatibilities as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientModifiers_Incompatibilities(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientModifiers_Incompatibilities as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientSampling_GradientReferencesAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientSurfaceTagEmitter_ComponentDependencies(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientTransform_RequiresShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_RequiresShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientTransform_ComponentIncompatibleWithSpawners(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module
self._run_test(request, workspace, editor, test_module)
def test_GradientTransform_ComponentIncompatibleWithExpectedGradients(self, request, workspace, editor, launcher_platform):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module
self._run_test(request, workspace, editor, test_module)
def test_ImageGradient_RequiresShape(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ImageGradient_RequiresShape as test_module
self._run_test(request, workspace, editor, test_module)
def test_ImageGradient_ProcessedImageAssignedSuccessfully(self, request, workspace, editor, launcher_platform):
from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module
self._run_test(request, workspace, editor, test_module)
@@ -0,0 +1,55 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import pytest
from ly_test_tools.o3de.editor_test import EditorSingleTest, EditorSharedTest, EditorParallelTest, EditorTestSuite
@pytest.mark.SUITE_periodic
@pytest.mark.parametrize("launcher_platform", ['windows_editor'])
@pytest.mark.parametrize("project", ["AutomatedTesting"])
class TestAutomation(EditorTestSuite):
class test_GradientGenerators_Incompatibilities(EditorSharedTest):
from .EditorScripts import GradientGenerators_Incompatibilities as test_module
class test_GradientModifiers_Incompatibilities(EditorSharedTest):
from .EditorScripts import GradientModifiers_Incompatibilities as test_module
class test_GradientPreviewSettings_DefaultPinnedEntityIsSelf(EditorSharedTest):
from .EditorScripts import GradientPreviewSettings_DefaultPinnedEntityIsSelf as test_module
class test_GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin(EditorSharedTest):
from .EditorScripts import GradientPreviewSettings_ClearingPinnedEntitySetsPreviewToOrigin as test_module
class test_GradientSampling_GradientReferencesAddRemoveSuccessfully(EditorSharedTest):
from .EditorScripts import GradientSampling_GradientReferencesAddRemoveSuccessfully as test_module
class test_GradientSurfaceTagEmitter_ComponentDependencies(EditorSharedTest):
from .EditorScripts import GradientSurfaceTagEmitter_ComponentDependencies as test_module
class test_GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully(EditorSharedTest):
from .EditorScripts import GradientSurfaceTagEmitter_SurfaceTagsAddRemoveSuccessfully as test_module
class test_GradientTransform_RequiresShape(EditorSharedTest):
from .EditorScripts import GradientTransform_RequiresShape as test_module
class test_GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange(EditorSharedTest):
from .EditorScripts import GradientTransform_FrequencyZoomCanBeSetBeyondSliderRange as test_module
class test_GradientTransform_ComponentIncompatibleWithSpawners(EditorSharedTest):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithSpawners as test_module
class test_GradientTransform_ComponentIncompatibleWithExpectedGradients(EditorSharedTest):
from .EditorScripts import GradientTransform_ComponentIncompatibleWithExpectedGradients as test_module
class test_ImageGradient_RequiresShape(EditorSharedTest):
from .EditorScripts import ImageGradient_RequiresShape as test_module
class test_ImageGradient_ProcessedImageAssignedSuccessfully(EditorSharedTest):
from .EditorScripts import ImageGradient_ProcessedImageAssignedSuccessfully as test_module
@@ -5,144 +5,147 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
dependencies_added = (
"Node created new Entity with all required components",
"Failed to create node with all required components"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestAreaNodeComponentDependency(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AreaNodeComponentDependency", args=["level"])
def AreaNodes_DependentComponentsAdded():
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with
proper dependent components.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with
proper dependent components.
Expected Behavior:
All expected component dependencies are met when adding an area node to a graph.
Expected Behavior:
All expected component dependencies are met when adding an area node to a graph.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure the proper dependent components are added
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure the proper dependent components are added
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding these vegetation area nodes has the main target Vegetation Layer Component
# as well as automatically adding all required dependency components
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Vegetation area mapping with the key being the node name and the value is the
# expected Components that should be added to the Entity created for the node
areas = {
'SpawnerAreaNode': [
'Vegetation Layer Spawner',
'Vegetation Asset List',
'Vegetation Reference Shape'
],
'MeshBlockerAreaNode': [
'Vegetation Layer Blocker (Mesh)',
'Mesh'
],
'BlockerAreaNode': [
'Vegetation Layer Blocker',
'Vegetation Reference Shape'
]
}
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in areas:
componentNames.extend(areas[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Listen for entity creation notifications so we can check if the entity created
# from adding these vegetation area nodes has the main target Vegetation Layer Component
# as well as automatically adding all required dependency components
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create nodes for the vegetation areas that have additional required dependencies and check if
# the Entity created by adding the node has the appropriate component and required
# additional components added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Vegetation area mapping with the key being the node name and the value is the
# expected Components that should be added to the Entity created for the node
areas = {
'SpawnerAreaNode': [
'Vegetation Layer Spawner',
'Vegetation Asset List',
'Vegetation Reference Shape'
],
'MeshBlockerAreaNode': [
'Vegetation Layer Blocker (Mesh)',
'Mesh'
],
'BlockerAreaNode': [
'Vegetation Layer Blocker',
'Vegetation Reference Shape'
]
}
components = areas[nodeName]
success = False
for component in components:
componentTypeId = componentTypeIds[component]
success = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
if not success:
break
self.test_success = self.test_success and success
if success:
self.log("{node} created new Entity with all required components".format(node=nodeName))
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in areas:
componentNames.extend(areas[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
x += 40.0
y += 40.0
# Create nodes for the vegetation areas that have additional required dependencies and check if
# the Entity created by adding the node has the appropriate component and required
# additional components added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Stop listening for entity creation notifications
handler.disconnect()
components = areas[nodeName]
success = False
for component in components:
componentTypeId = componentTypeIds[component]
success = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
if not success:
break
Report.info(nodeName)
Report.result(Tests.dependencies_added, success)
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestAreaNodeComponentDependency()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AreaNodes_DependentComponentsAdded)
@@ -5,125 +5,129 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
component_added = (
"New entity created with the expected component",
"Expected component was not found on entity"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestGradientNodeEntityCreate(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityCreate", args=["level"])
def AreaNodes_EntityCreatedOnNodeAdd():
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
Expected Behavior:
New entities are created when dragging area nodes to graph area.
Expected Behavior:
New entities are created when dragging area nodes to graph area.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure a new entity is created
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure a new entity is created
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
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
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Listen for entity creation notifications so we can check if the entity created
# from adding vegetation area nodes has the appropriate Vegetation Layer Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
areas = {
'AreaBlenderNode': 'Vegetation Layer Blender',
'BlockerAreaNode': 'Vegetation Layer Blocker',
'MeshBlockerAreaNode': 'Vegetation Layer Blocker (Mesh)',
'SpawnerAreaNode': 'Vegetation Layer Spawner'
}
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in areas:
componentNames.append(areas[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Create nodes for all the vegetation areas we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Listen for entity creation notifications so we can check if the entity created
# from adding vegetation area nodes has the appropriate Vegetation Layer Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
areaComponent = areas[nodeName]
componentTypeId = componentTypeIds[areaComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
self.test_success = self.test_success and hasComponent
if hasComponent:
self.log("{node} created new Entity with {component} Component".format(node=nodeName, component=areaComponent))
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
areas = {
'AreaBlenderNode': 'Vegetation Layer Blender',
'BlockerAreaNode': 'Vegetation Layer Blocker',
'MeshBlockerAreaNode': 'Vegetation Layer Blocker (Mesh)',
'SpawnerAreaNode': 'Vegetation Layer Spawner'
}
x += 40.0
y += 40.0
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in areas:
componentNames.append(areas[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Stop listening for entity creation notifications
handler.disconnect()
# Create nodes for all the vegetation areas we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
areaComponent = areas[nodeName]
componentTypeId = componentTypeIds[areaComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
Report.info(f"Node: {nodeName} | Component: {areaComponent}")
Report.result(Tests.component_added, hasComponent)
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientNodeEntityCreate()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AreaNodes_EntityCreatedOnNodeAdd)
@@ -5,123 +5,128 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
entity_deleted = (
"Entity was deleted when node was removed",
"Entity was not deleted as expected when node was removed"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
createdEntityId = None
deletedEntityId = None
class TestAreaNodeEntityDelete(EditorTestHelper):
def AreaNodes_EntityRemovedOnNodeDelete():
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="AreaNodeEntityDelete", args=["level"])
Expected Behavior:
Entities are removed when area nodes are deleted from a graph.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
Expected Behavior:
Entities are removed when area nodes are deleted from a graph.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the area nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
:return: None
"""
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
areas = [
'AreaBlenderNode',
'BlockerAreaNode',
'MeshBlockerAreaNode',
'SpawnerAreaNode',
]
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
areas = [
'AreaBlenderNode',
'BlockerAreaNode',
'MeshBlockerAreaNode',
'SpawnerAreaNode',
]
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
self.test_success = self.test_success and removed and createdEntityId.invoke("Equal", deletedEntityId)
if removed and createdEntityId.invoke("Equal", deletedEntityId):
self.log("{node} corresponding Entity was deleted when node is removed".format(node=nodeName))
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in areas:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Stop listening for entity creation notifications
handler.disconnect()
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
Report.info(f"Node: {nodeName}")
Report.result(Tests.entity_deleted, removed and createdEntityId.invoke("Equal", deletedEntityId))
# Stop listening for entity creation notifications
handler.disconnect()
test = TestAreaNodeEntityDelete()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(AreaNodes_EntityRemovedOnNodeDelete)
@@ -5,197 +5,209 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class Tests:
slice_instantiated = (
"Slice instantiated successfully",
"Failed to instantiate slice"
)
lc_entity_found = (
"LandscapeCanvas entity found",
"Failed to find LandscapeCanvas entity"
)
spawner_entity_found = (
"BushSpawner entity found",
"Failed to find BushSpawner entity"
)
dist_filter_component_found = (
"Vegetation Distribution Filter component on BushSpawner entity found",
"Failed to find Distribution Filter component on BushSpawner entity"
)
existing_graph_opened = (
"Opened existing graph from slice",
"Failed to open existing graph"
)
dist_filter_node_found = (
"Vegetation Distribution Filter node found on graph",
"Failed to find Distribution Filter node on graph"
)
alt_filter_component_found = (
"Vegetation Altitude Filter component on BushSpawner entity found",
"Failed to find Altitude Filter component on BushSpawner entity"
)
alt_filter_node_found = (
"Vegetation Altitude Filter node found on graph",
"Failed to find Altitude Filter node on graph"
)
dist_filter_component_removed = (
"Vegetation Distribution Filter component removed from BushSpawner entity",
"Failed to remove Distribution Filter component from BushSpawner entity"
)
dist_filter_node_removed = (
"Vegetation Distribution Filter node removed from graph",
"Failed to remove Distribution Filter node from graph"
)
child_entity_added = (
"New entity successfully added as a child of the BushSpawner entity",
"New entity added with an unexpected parent"
)
box_shape_component_found = (
"Box Shape component on Box entity found",
"Failed to find Box Shape component on Box entity"
)
box_shape_node_found = (
"Box Shape node found on graph",
"Failed to find Box Shape node on graph"
)
class TestComponentUpdatesUpdateGraph(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="ComponentUpdatesUpdateGraph", args=["level"])
def ComponentUpdates_UpdateGraph():
"""
Summary:
This test verifies that the Landscape Canvas graphs update properly when components are added/removed outside of
Landscape Canvas.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas graphs update properly when components are added/removed outside of
Landscape Canvas.
Expected Behavior:
Graphs properly reflect component changes made to entities outside of Landscape Canvas.
Expected Behavior:
Graphs properly reflect component changes made to entities outside of Landscape Canvas.
Test Steps:
1. Open Level
2. Find LandscapeCanvas named entity
3. Ensure Vegetation Distribution Component is present on the BushSpawner entity
4. Open graph and ensure Distribution Filter wrapped node is present
5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector
6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is
no longer present in the graph
7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector
8. Ensure Altitude Filter was added to the BushSpawner node in the open graph
9. Add a new entity with unique name as a child of the Landscape Canvas entity
10. Add a Box Shape component to the new child entity
11. Ensure Box Shape node is present on the open graph
Test Steps:
1. Open Level
2. Find LandscapeCanvas named entity
3. Ensure Vegetation Distribution Component is present on the BushSpawner entity
4. Open graph and ensure Distribution Filter wrapped node is present
5. Delete the Vegetation Distribution Filter component from the BushSpawner entity via Entity Inspector
6. Ensure the Vegetation Distribution Filter component was deleted from the BushSpawner entity and node is
no longer present in the graph
7. Add Vegetation Altitude Filter to the BushSpawner entity through Entity Inspector
8. Ensure Altitude Filter was added to the BushSpawner node in the open graph
9. Add a new entity with unique name as a child of the Landscape Canvas entity
10. Add a Box Shape component to the new child entity
11. Ensure Box Shape node is present on the open graph
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import os
# Create a new empty level and instantiate LC_BushFlowerBlender.slice
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
self.test_success = self.test_success and test_slice.IsValid()
if test_slice.IsValid():
self.log("Slice spawned!")
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
# Find root entity in the loaded level
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
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
# Allow a few seconds for matching entity to be found
self.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0, 5.0)
lc_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
slice_root_id = lc_matching_entities[0] #Entity with Landscape Canvas component
self.test_success = self.test_success and slice_root_id.IsValid()
if slice_root_id.IsValid():
self.log("LandscapeCanvas entity found")
# Open a simple level and instantiate LC_BushFlowerBlender.slice
helper.init_idle()
helper.open_level("Physics", "Base")
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
Report.critical_result(Tests.slice_instantiated, test_slice.IsValid())
# Find the BushSpawner entity
search_filter.names = ["BushSpawner"]
spawner_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
spawner_id = spawner_matching_entities[0] #Entity with Vegetation Layer Spawner component
self.test_success = self.test_success and spawner_id.IsValid()
if spawner_id.IsValid():
self.log("BushSpawner entity found")
# Find root entity in the loaded level
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
# Get needed component type ids
distribution_filter_type_id = hydra.get_component_type_id("Vegetation Distribution Filter")
altitude_filter_type_id = hydra.get_component_type_id("Vegetation Altitude Filter")
box_shape_type_id = hydra.get_component_type_id("Box Shape")
# Allow a few seconds for matching entity to be found
helper.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0, 5.0)
lc_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
slice_root_id = lc_matching_entities[0] #Entity with Landscape Canvas component
Report.critical_result(Tests.lc_entity_found, slice_root_id.IsValid())
# Verify the BushSpawner entity has a Distribution Filter
has_distribution_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
distribution_filter_type_id)
self.test_success = self.test_success and has_distribution_filter
if has_distribution_filter:
self.log("Vegetation Distribution Filter on BushSpawner entity found")
# Find the BushSpawner entity
search_filter.names = ["BushSpawner"]
spawner_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
spawner_id = spawner_matching_entities[0] #Entity with Vegetation Layer Spawner component
Report.critical_result(Tests.spawner_entity_found, spawner_id.IsValid())
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
self.test_success = self.test_success and open_graph.IsValid()
if open_graph.IsValid():
self.log("Graph opened")
# Get needed component type ids
distribution_filter_type_id = hydra.get_component_type_id("Vegetation Distribution Filter")
altitude_filter_type_id = hydra.get_component_type_id("Vegetation Altitude Filter")
box_shape_type_id = hydra.get_component_type_id("Box Shape")
# Verify that Distribution Filter node is present on the graph
spawner_distribution_filter_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
distribution_filter_type_id)
spawner_distribution_filter_component_id = spawner_distribution_filter_component.GetValue()
distribution_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast,
'GetNodeMatchingEntityComponentInGraph',
open_graph,
spawner_distribution_filter_component_id)
self.test_success = self.test_success and distribution_filter_node is not None
if distribution_filter_node is not None:
self.log("Distribution Filter node found on graph")
else:
self.log("Distribution Filter node not found on graph")
# Verify the BushSpawner entity has a Distribution Filter
has_distribution_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
distribution_filter_type_id)
Report.critical_result(Tests.dist_filter_component_found, has_distribution_filter)
# Add a Vegetation Altitude Filter component to the BushSpawner entity, and verify the node is added to the graph
spawner_altitude_filter_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
altitude_filter_type_id)
spawner_altitude_filter_component_id = spawner_altitude_filter_component.GetValue()
editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentOfType', spawner_id, altitude_filter_type_id)
has_altitude_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
altitude_filter_type_id)
altitude_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetNodeMatchingEntityComponentInGraph',
open_graph, spawner_altitude_filter_component_id)
self.test_success = self.test_success and has_altitude_filter and altitude_filter_node is not None
if has_altitude_filter:
self.log("Vegetation Altitude Filter on BushSpawner entity found")
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
Report.critical_result(Tests.existing_graph_opened, open_graph.IsValid())
if altitude_filter_node is not None:
self.log("Altitude Filter node found on graph")
else:
self.log("Altitude Filter node not found on graph")
# Verify that Distribution Filter node is present on the graph
spawner_distribution_filter_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
distribution_filter_type_id)
spawner_distribution_filter_component_id = spawner_distribution_filter_component.GetValue()
distribution_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast,
'GetNodeMatchingEntityComponentInGraph',
open_graph,
spawner_distribution_filter_component_id)
Report.critical_result(Tests.dist_filter_node_found, distribution_filter_node is not None)
# Remove the Distribution Filter
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [spawner_distribution_filter_component_id])
general.idle_wait(1.0)
# Add a Vegetation Altitude Filter component to the BushSpawner entity, and verify the node is added to the graph
spawner_altitude_filter_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
altitude_filter_type_id)
spawner_altitude_filter_component_id = spawner_altitude_filter_component.GetValue()
editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentOfType', spawner_id, altitude_filter_type_id)
has_altitude_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
altitude_filter_type_id)
altitude_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetNodeMatchingEntityComponentInGraph',
open_graph, spawner_altitude_filter_component_id)
Report.result(Tests.dist_filter_component_found, has_altitude_filter)
Report.result(Tests.dist_filter_node_found, altitude_filter_node is not None)
# Verify the Distribution Filter was successfully removed from entity and the node was likewise removed
has_distribution_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
distribution_filter_type_id)
self.test_success = self.test_success and not has_distribution_filter
if not has_distribution_filter:
self.log("Vegetation Distribution Filter removed from BushSpawner entity")
# Remove the Distribution Filter
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [spawner_distribution_filter_component_id])
general.idle_wait(1.0)
distribution_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast,
'GetAllNodesMatchingEntityComponent',
spawner_distribution_filter_component_id)
self.test_success = self.test_success and not distribution_filter_node
if distribution_filter_node:
self.log("Distribution Filter node is still present on the graph")
else:
self.log("Distribution Filter node was removed from the graph")
# Verify the Distribution Filter was successfully removed from entity and the node was likewise removed
has_distribution_filter = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', spawner_id,
distribution_filter_type_id)
Report.result(Tests.dist_filter_component_removed, not has_distribution_filter)
# Add a new child entity of BushSpawner entity
box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', spawner_id)
if editor.EditorEntityInfoRequestBus(bus.Event, 'GetParent', box_id) == spawner_id:
self.log("New entity successfully added as a child of the BushSpawner entity")
else:
self.log("New entity added with an unexpected parent")
distribution_filter_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast,
'GetAllNodesMatchingEntityComponent',
spawner_distribution_filter_component_id)
Report.result(Tests.dist_filter_node_removed, not distribution_filter_node)
# Add a Box Shape component to the new entity and verify it was properly added
editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentOfType', box_id, box_shape_type_id)
has_box_shape = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', box_id,
box_shape_type_id)
self.test_success = self.test_success and has_box_shape
if has_box_shape:
self.log("Box Shape on Box entity found")
# Add a new child entity of BushSpawner entity
box_id = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', spawner_id)
Report.result(Tests.child_entity_added, editor.EditorEntityInfoRequestBus(bus.Event, 'GetParent', box_id) ==
spawner_id)
# Verify the Box Shape node appear on the graph
box_shape_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', box_id,
box_shape_type_id)
# Add a Box Shape component to the new entity and verify it was properly added
editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentOfType', box_id, box_shape_type_id)
has_box_shape = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', box_id,
box_shape_type_id)
Report.result(Tests.box_shape_component_found, has_box_shape)
box_shape_component_id = box_shape_component.GetValue()
box_shape_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
box_shape_component_id)
self.test_success = self.test_success and box_shape_node is not None
if box_shape_node is not None:
self.log("Box Shape node found on graph")
else:
self.log("Box Shape node not found on graph")
# Verify the Box Shape node appears on the graph
box_shape_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', box_id,
box_shape_type_id)
box_shape_component_id = box_shape_component.GetValue()
box_shape_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
box_shape_component_id)
Report.result(Tests.box_shape_node_found, box_shape_node is not None)
test = TestComponentUpdatesUpdateGraph()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(ComponentUpdates_UpdateGraph)
@@ -0,0 +1,80 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
lc_component_added = (
"Landscape Canvas component successfully added to entity",
"Failed to add Landscape Canvas component to entity"
)
lc_component_removed = (
"Landscape Canvas component successfully removed from entity",
"Failed to remove Landscape Canvas component from entity"
)
def Component_AddedRemoved():
"""
Summary:
This test verifies that the Landscape Canvas component can be added to/removed from an entity.
Expected Behavior:
Closing a tabbed graph only closes the appropriate graph.
Test Steps:
1) Open a simple level
2) Create a new entity
3) Add a Landscape Canvas component to the entity
4) Remove the Landscape Canvas component from the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
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
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create an Entity at the root of the level
newEntityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Find the component TypeId for our Landscape Canvas component
landscape_canvas_type_id = hydra.get_component_type_id("Landscape Canvas")
# Add the Landscape Canvas Component to our Entity
componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', newEntityId, [landscape_canvas_type_id])
components = componentOutcome.GetValue()
landscapeCanvasComponent = components[0]
# Validate the Landscape Canvas Component exists
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, landscape_canvas_type_id)
Report.result(Tests.lc_component_added, hasComponent)
# Remove the Landscape Canvas Component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [landscapeCanvasComponent])
# Validate the Landscape Canvas Component is no longer on our Entity
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, landscape_canvas_type_id)
Report.result(Tests.lc_component_removed, not hasComponent)
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Component_AddedRemoved)
@@ -1,106 +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.editor.graph as graph
import azlmbr.legacy.general as general
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
new_root_entity_id = None
class TestCreateNewGraph(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="CreateNewGraph", args=["level"])
def on_entity_created(self, parameters):
global new_root_entity_id
new_root_entity_id = parameters[0]
print("New root entity created")
def run_test(self):
"""
Summary:
This test verifies that new graphs can be created in Landscape Canvas.
Expected Behavior:
New graphs can be created, and proper entity is created to hold graph data with a Landscape Canvas component.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Ensures the root entity created contains a Landscape Canvas component
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
# Open Landscape Canvas tool and verify
general.open_pane("Landscape Canvas")
self.test_success = self.test_success and general.is_pane_visible("Landscape Canvas")
if general.is_pane_visible("Landscape Canvas"):
self.log("Landscape Canvas pane is open")
# Listen for entity creation notifications so we can check if the entity created
# with the new graph has our Landscape Canvas component automatically added
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback("OnEditorEntityCreated", self.on_entity_created)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, "CreateNewGraph", editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, "ContainsGraph", editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Check if the entity created when we create a new graph has the
# Landscape Canvas component already added to it
landscape_canvas_type_id = hydra.get_component_type_id("Landscape Canvas")
success = editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", new_root_entity_id,
landscape_canvas_type_id)
self.test_success = self.test_success and success
if success:
self.log("Root entity has Landscape Canvas component")
# Close Landscape Canvas tool and verify
general.close_pane("Landscape Canvas")
self.test_success = self.test_success and not general.is_pane_visible("Landscape Canvas")
if not general.is_pane_visible("Landscape Canvas"):
self.log("Landscape Canvas pane is closed")
# Stop listening for entity creation notifications
handler.disconnect()
test = TestCreateNewGraph()
test.run()
@@ -5,131 +5,132 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestDisabledNodeDuplication(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="DisabledNodeDuplication", args=["level"])
def Edit_DisabledNodeDuplication():
"""
Summary:
This test verifies Editor stability after duplicating disabled Landscape Canvas nodes.
def run_test(self):
"""
Summary:
This test verifies Editor stability after duplicating disabled Landscape Canvas nodes.
Expected Behavior:
Editor remains stable and free of crashes.
Expected Behavior:
Editor remains stable and free of crashes.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Create several new nodes, disable the nodes via disabling/deleting components, and duplicate the nodes
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Create several new nodes, disable the nodes via disabling/deleting components, and duplicate the nodes
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
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
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Listen for entity creation notifications so when we add a new node
# we can access the corresponding Entity that was created so that we
# can disable/remove components on that Entity
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Mapping of our Landscape Canvas nodes with corresponding dependent components
# that we can disable/remove to reproduce the crash
nodes = {
'SpawnerAreaNode': 'Vegetation Asset List',
'MeshBlockerAreaNode': 'Mesh',
'BlockerAreaNode': 'Vegetation Reference Shape',
'FastNoiseGradientNode': 'Gradient Transform Modifier',
'ImageGradientNode': 'Gradient Transform Modifier',
'PerlinNoiseGradientNode': 'Gradient Transform Modifier',
'RandomNoiseGradientNode': 'Gradient Transform Modifier'
}
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = list(set(nodes.values())) # Convert to set then back to list to remove any duplicates
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Iterate through creating our nodes and then disabling/deleting required components
# and then duplicating the node to reproduce the crash
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in nodes:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Listen for entity creation notifications so when we add a new node
# we can access the corresponding Entity that was created so that we
# can disable/remove components on that Entity
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
dependentComponentName = nodes[nodeName]
componentTypeId = componentTypeIds[dependentComponentName]
componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', newEntityId, componentTypeId)
component = componentOutcome.GetValue()
# Mapping of our Landscape Canvas nodes with corresponding dependent components
# that we can disable/remove to reproduce the crash
nodes = {
'SpawnerAreaNode': 'Vegetation Asset List',
'MeshBlockerAreaNode': 'Mesh',
'BlockerAreaNode': 'Vegetation Reference Shape',
'FastNoiseGradientNode': 'Gradient Transform Modifier',
'ImageGradientNode': 'Gradient Transform Modifier',
'PerlinNoiseGradientNode': 'Gradient Transform Modifier',
'RandomNoiseGradientNode': 'Gradient Transform Modifier'
}
# First make sure we can duplicate a node with a dependent component that is disabled
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [component])
general.idle_wait(1.0)
graph.SceneRequestBus(bus.Event, 'DuplicateSelection', newGraphId) # This duplication would cause a crash without the fix
self.log("{node} duplicated with disabled component".format(node=nodeName))
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = list(set(nodes.values())) # Convert to set then back to list to remove any duplicates
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Then, make sure we can duplicate the node with a dependent component that is deleted
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component])
general.idle_wait(1.0)
graph.SceneRequestBus(bus.Event, 'DuplicateSelection', newGraphId) # This duplication would cause a crash without the fix
self.log("{node} duplicated with deleted component".format(node=nodeName))
# Iterate through creating our nodes and then disabling/deleting required components
# and then duplicating the node to reproduce the crash
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in nodes:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
x += 40.0
y += 40.0
dependentComponentName = nodes[nodeName]
componentTypeId = componentTypeIds[dependentComponentName]
componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', newEntityId, componentTypeId)
component = componentOutcome.GetValue()
# Stop listening for entity creation notifications
handler.disconnect()
# First make sure we can duplicate a node with a dependent component that is disabled
editor.EditorComponentAPIBus(bus.Broadcast, 'DisableComponents', [component])
helper.wait_for_condition(lambda: not editor.EditorComponentAPIBus(bus.Broadcast, 'IsComponentEnabled',
[component]), 1.0)
graph.SceneRequestBus(bus.Event, 'DuplicateSelection', newGraphId) # This duplication would cause a crash without the fix
Report.info("{node} duplicated with disabled component".format(node=nodeName))
# Then, make sure we can duplicate the node with a dependent component that is deleted
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [component])
helper.wait_for_condition(lambda: not editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType',
componentTypeIds[dependentComponentName]), 1.0)
graph.SceneRequestBus(bus.Event, 'DuplicateSelection', newGraphId) # This duplication would cause a crash without the fix
Report.info("{node} duplicated with deleted component".format(node=nodeName))
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestDisabledNodeDuplication()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(Edit_DisabledNodeDuplication)
@@ -5,133 +5,134 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
import azlmbr.paths
class Tests:
slice_spawned = (
"Slice instantiated successfully",
"Failed to instantiate slice"
)
lc_entity_found = (
"Landscape Canvas entity found",
"Failed to find Landscape Canvas entity"
)
spawner_entity_found = (
"Spawner entity found",
"Failed to find Spawner entity"
)
graph_opened = (
"Graph successfully opened",
"Graph failed to open"
)
spawner_node_found = (
"Vegetation Layer Spawner node found on graph",
"Failed to find Vegetation Layer Spawner node on graph"
)
spawner_node_removed = (
"Vegetation Layer Spawner node was successfully removed",
"Failed to remove Vegetation Layer Spawner node"
)
def Edit_UndoNodeDelete_SliceEntity():
"""
Summary:
This test verifies Editor stability after undoing the deletion of nodes on a slice entity.
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
Expected Behavior:
Editor remains stable and free of crashes.
Test Steps:
1) Open a simple level
2) Instantiate a slice with a Landscape Canvas setup
3) Find a specific node on the graph, and delete it
4) Restore the node with Undo
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import os
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
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
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Instantiate slice
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
Report.result(Tests.slice_spawned, test_slice.IsValid())
# Find root entity in the loaded level
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
# Allow a few seconds for matching entity to be found
helper.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0, 5.0)
lc_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
slice_root_id = lc_matching_entities[0] # Entity with Landscape Canvas component
Report.result(Tests.lc_entity_found, slice_root_id.IsValid())
# Find the BushSpawner entity
search_filter.names = ["BushSpawner"]
spawner_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
spawner_id = spawner_matching_entities[0] # Entity with Vegetation Layer Spawner component
Report.result(Tests.spawner_entity_found, spawner_id.IsValid())
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
Report.result(Tests.graph_opened, open_graph.IsValid())
# Get needed component type ids
layer_spawner_type_id = hydra.get_component_type_id("Vegetation Layer Spawner")
# Find the Vegetation Layer Spawner node on the BushSpawner entity
layer_spawner_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
layer_spawner_type_id)
layer_spawner_component_component_id = layer_spawner_component.GetValue()
layer_spawner_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
layer_spawner_component_component_id)
Report.result(Tests.spawner_node_found, layer_spawner_node is not None)
# Remove the Layer Spawner node
graph.GraphControllerRequestBus(bus.Event, "RemoveNode", open_graph, layer_spawner_node[0])
# Verify node was removed
layer_spawner_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
layer_spawner_component_component_id)
Report.result(Tests.spawner_node_removed, not layer_spawner_node)
# Undo the Node deletion. This is required to be executed twice to hit the node removal.
general.undo()
general.undo()
# self.log a line to the Console to verify the Editor is still active
Report.info("Editor is still responsive")
class TestUndoNodeDeleteSlice(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="UndoNodeDeleteSlice", args=["level"])
if __name__ == "__main__":
def run_test(self):
"""
Summary:
This test verifies Editor stability after undoing the deletion of nodes on a slice entity.
from editor_python_test_tools.utils import Report
Report.start_test(Edit_UndoNodeDelete_SliceEntity)
Expected Behavior:
Editor remains stable and free of crashes.
Test Steps:
1) Create a new level
2) Instantiate a slice with a Landscape Canvas setup
3) Find a specific node on the graph, and delete it
4) Restore the node with Undo
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# Create a new empty level and instantiate LC_BushFlowerBlender.slice
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
self.test_success = self.test_success and test_slice.IsValid()
if test_slice.IsValid():
self.log("Slice spawned!")
# Find root entity in the loaded level
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
# Allow a few seconds for matching entity to be found
self.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0,
5.0)
lc_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
slice_root_id = lc_matching_entities[0] # Entity with Landscape Canvas component
self.test_success = self.test_success and slice_root_id.IsValid()
if slice_root_id.IsValid():
self.log("LandscapeCanvas entity found")
# Find the BushSpawner entity
search_filter.names = ["BushSpawner"]
spawner_matching_entities = entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)
spawner_id = spawner_matching_entities[0] # Entity with Vegetation Layer Spawner component
self.test_success = self.test_success and spawner_id.IsValid()
if spawner_id.IsValid():
self.log("BushSpawner entity found")
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
self.test_success = self.test_success and open_graph.IsValid()
if open_graph.IsValid():
self.log("Graph opened")
# Get needed component type ids
layer_spawner_type_id = hydra.get_component_type_id("Vegetation Layer Spawner")
# Find the Vegetation Layer Spawner node on the BushSpawner entity
layer_spawner_component = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', spawner_id,
layer_spawner_type_id)
layer_spawner_component_component_id = layer_spawner_component.GetValue()
layer_spawner_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
layer_spawner_component_component_id)
self.test_success = self.test_success and layer_spawner_node
if layer_spawner_node:
self.log("Vegetation Layer Spawner node found on graph")
else:
self.log("Vegetation Layer Spawner node not found")
# Remove the Layer Spawner node
graph.GraphControllerRequestBus(bus.Event, "RemoveNode", open_graph, layer_spawner_node[0])
# Verify node was removed
layer_spawner_node = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'GetAllNodesMatchingEntityComponent',
layer_spawner_component_component_id)
self.test_success = self.test_success and not layer_spawner_node
if not layer_spawner_node:
self.log("Vegetation Layer Spawner node was removed")
else:
self.log("Vegetation Layer Spawner node was not removed")
# Undo the Node deletion. This is required to be executed twice to hit the node removal.
general.undo()
general.undo()
# self.log a line to the Console to verify the Editor is still active
self.log("Editor is still responsive")
test = TestUndoNodeDeleteSlice()
test.run()
@@ -5,190 +5,205 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.entity as entity
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
preview_entity_set = (
"Perlin Noise Gradient component Preview Entity property set to Box Shape EntityId",
"Unexpected entity set in Perlin Noise Gradient Preview Entity property"
)
mixer_inbound_gradient_set_a = (
"Gradient Mixer component Inbound Gradient extendable property set to Perlin Noise Gradient EntityId",
"Unexpected entity set in Gradient Mixer's Inbound Gradient property"
)
mixer_inbound_gradient_set_b = (
"Gradient Mixer component Inbound Gradient extendable property set to FastNoise Gradient EntityId",
"Unexpected entity set in Gradient Mixer's Inbound Gradient property"
)
mixer_operation_a = (
"Layer 1 Operation is set to Initialize",
"Layer 1 Operation is not set to Initialize as expected"
)
mixer_operation_b = (
"Layer 2 Operation is set to Average",
"Layer 2 Operation is not set to Average as expected"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestGradientMixerNodeConstruction(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientMixerNodeConstruction", args=["level"])
def GradientMixer_NodeConstruction():
"""
Summary:
This test verifies a Gradient Mixer vegetation setup can be constructed through Landscape Canvas.
def run_test(self):
"""
Summary:
This test verifies a Gradient Mixer vegetation setup can be constructed through Landscape Canvas.
Expected Behavior:
Entities contain all required components and component references after creating nodes and setting connections
on a Landscape Canvas graph.
Expected Behavior:
Entities contain all required components and component references after creating nodes and setting connections
on a Landscape Canvas graph.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Add all necessary nodes to the graph and set connections to form a Gradient Mixer setup
4) Verify all components and component references were properly set during graph construction
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Add all necessary nodes to the graph and set connections to form a Gradient Mixer setup
4) Verify all components and component references were properly set during graph construction
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can verify the component EntityId
# references are set correctly when connecting slots on the nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
positionX = 10.0
positionY = 10.0
offsetX = 340.0
offsetY = 100.0
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Add a Box Shape node to the graph
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
boxShapeNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'BoxShapeNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, boxShapeNode, math.Vector2(positionX, positionY))
boxShapeEntityId = newEntityId
# Listen for entity creation notifications so we can verify the component EntityId
# references are set correctly when connecting slots on the nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
positionX += offsetX
positionY += offsetY
positionX = 10.0
positionY = 10.0
offsetX = 340.0
offsetY = 100.0
# Add a Random Noise Gradient node to the graph
perlinNoiseNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'PerlinNoiseGradientNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, perlinNoiseNode, math.Vector2(positionX, positionY))
perlinNoiseEntityId = newEntityId
# Add a Box Shape node to the graph
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
boxShapeNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'BoxShapeNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, boxShapeNode, math.Vector2(positionX, positionY))
boxShapeEntityId = newEntityId
positionX += offsetX
positionY += offsetY
positionX += offsetX
positionY += offsetY
# Add a FastNoise Gradient node to the graph
fastNoiseNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'FastNoiseGradientNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, fastNoiseNode, math.Vector2(positionX, positionY))
fastNoiseEntityId = newEntityId
# Add a Random Noise Gradient node to the graph
perlinNoiseNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'PerlinNoiseGradientNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, perlinNoiseNode, math.Vector2(positionX, positionY))
perlinNoiseEntityId = newEntityId
positionX += offsetX
positionY += offsetY
positionX += offsetX
positionY += offsetY
# Add a Gradient Mixer node to the graph
gradientMixerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'GradientMixerNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, gradientMixerNode, math.Vector2(positionX, positionY))
gradientMixerEntityId = newEntityId
# Add a FastNoise Gradient node to the graph
fastNoiseNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'FastNoiseGradientNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, fastNoiseNode, math.Vector2(positionX, positionY))
fastNoiseEntityId = newEntityId
boundsSlotId = graph.GraphModelSlotId('Bounds')
previewBoundsSlotId = graph.GraphModelSlotId('PreviewBounds')
inboundGradientSlotId = graph.GraphModelSlotId('InboundGradient')
outboundGradientSlotId = graph.GraphModelSlotId('OutboundGradient')
inboundGradientSlotId2 = graph.GraphControllerRequestBus(bus.Event, 'ExtendSlot', newGraphId, gradientMixerNode,
'InboundGradient')
positionX += offsetX
positionY += offsetY
# Connect slots on our nodes to construct a Gradient Mixer hierarchy
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
perlinNoiseNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
fastNoiseNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
gradientMixerNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, perlinNoiseNode, outboundGradientSlotId,
gradientMixerNode, inboundGradientSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, fastNoiseNode, outboundGradientSlotId,
gradientMixerNode, inboundGradientSlotId2)
# Add a Gradient Mixer node to the graph
gradientMixerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph,
'GradientMixerNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, gradientMixerNode, math.Vector2(positionX, positionY))
gradientMixerEntityId = newEntityId
# Delay to allow all the underlying component properties to be updated after the slot connections are made
general.idle_wait(1.0)
boundsSlotId = graph.GraphModelSlotId('Bounds')
previewBoundsSlotId = graph.GraphModelSlotId('PreviewBounds')
inboundGradientSlotId = graph.GraphModelSlotId('InboundGradient')
outboundGradientSlotId = graph.GraphModelSlotId('OutboundGradient')
inboundGradientSlotId2 = graph.GraphControllerRequestBus(bus.Event, 'ExtendSlot', newGraphId, gradientMixerNode,
'InboundGradient')
# Get component info
gradientMixerTypeId = hydra.get_component_type_id("Gradient Mixer")
perlinNoiseTypeId = hydra.get_component_type_id("Perlin Noise Gradient")
gradientMixerOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', gradientMixerEntityId,
gradientMixerTypeId)
gradientMixerComponent = gradientMixerOutcome.GetValue()
perlinNoiseOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', perlinNoiseEntityId,
perlinNoiseTypeId)
perlinNoiseComponent = perlinNoiseOutcome.GetValue()
# Connect slots on our nodes to construct a Gradient Mixer hierarchy
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
perlinNoiseNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
fastNoiseNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, boxShapeNode, boundsSlotId,
gradientMixerNode, previewBoundsSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, perlinNoiseNode, outboundGradientSlotId,
gradientMixerNode, inboundGradientSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, fastNoiseNode, outboundGradientSlotId,
gradientMixerNode, inboundGradientSlotId2)
# Verify the Preview EntityId property on our Perlin Noise Gradient component has been set to our Box Shape's EntityId
previewEntityId = hydra.get_component_property_value(perlinNoiseComponent, 'Preview Settings|Pin Preview to Shape')
self.test_success = self.test_success and previewEntityId and boxShapeEntityId.invoke("Equal", previewEntityId)
if previewEntityId and boxShapeEntityId.invoke("Equal", previewEntityId):
self.log("Perlin Noise Gradient component Preview Entity property set to Box Shape EntityId")
# Delay to allow all the underlying component properties to be updated after the slot connections are made
general.idle_wait(1.0)
# Verify the 1st Inbound Gradient EntityId property on our Gradient Mixer component has been set to our Perlin Noise
# Gradient's EntityId
inboundGradientEntityId = hydra.get_component_property_value(gradientMixerComponent,
'Configuration|Layers|[0]|Gradient|Gradient Entity Id')
self.test_success = self.test_success and inboundGradientEntityId and perlinNoiseEntityId.invoke("Equal", inboundGradientEntityId)
if inboundGradientEntityId and perlinNoiseEntityId.invoke("Equal", inboundGradientEntityId):
self.log("Gradient Mixer component Inbound Gradient extendable property set to Perlin Noise Gradient EntityId")
# Get component info
gradientMixerTypeId = hydra.get_component_type_id("Gradient Mixer")
perlinNoiseTypeId = hydra.get_component_type_id("Perlin Noise Gradient")
gradientMixerOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', gradientMixerEntityId,
gradientMixerTypeId)
gradientMixerComponent = gradientMixerOutcome.GetValue()
perlinNoiseOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', perlinNoiseEntityId,
perlinNoiseTypeId)
perlinNoiseComponent = perlinNoiseOutcome.GetValue()
# Verify the 2nd Inbound Gradient EntityId property on our Gradient Mixer component has been set to our FastNoise
# Gradient Modifier's EntityId
inboundGradientEntityId2 = hydra.get_component_property_value(gradientMixerComponent,
'Configuration|Layers|[1]|Gradient|Gradient Entity Id')
self.test_success = self.test_success and inboundGradientEntityId2 and fastNoiseEntityId.invoke("Equal", inboundGradientEntityId2)
if inboundGradientEntityId2 and fastNoiseEntityId.invoke("Equal", inboundGradientEntityId2):
self.log("Gradient Mixer component Inbound Gradient extendable property set to FastNoise Gradient EntityId")
# Verify the Preview EntityId property on our Perlin Noise Gradient component has been set to our Box Shape's EntityId
previewEntityId = hydra.get_component_property_value(perlinNoiseComponent, 'Preview Settings|Pin Preview to Shape')
Report.result(Tests.preview_entity_set, previewEntityId and boxShapeEntityId.invoke("Equal", previewEntityId))
# Verify that Gradient Mixer Layer Operations are properly set
hydra.get_component_property_value(gradientMixerComponent, 'Configuration|Layers|[0]|Operation')
hydra.get_component_property_value(gradientMixerComponent, 'Configuration|Layers|[1]|Operation')
# Verify the 1st Inbound Gradient EntityId property on our Gradient Mixer component has been set to our Perlin Noise
# Gradient's EntityId
inboundGradientEntityId = hydra.get_component_property_value(gradientMixerComponent,
'Configuration|Layers|[0]|Gradient|Gradient Entity Id')
Report.result(Tests.mixer_inbound_gradient_set_a, inboundGradientEntityId and perlinNoiseEntityId.invoke("Equal", inboundGradientEntityId))
# Stop listening for entity creation notifications
handler.disconnect()
# Verify the 2nd Inbound Gradient EntityId property on our Gradient Mixer component has been set to our FastNoise
# Gradient Modifier's EntityId
inboundGradientEntityId2 = hydra.get_component_property_value(gradientMixerComponent,
'Configuration|Layers|[1]|Gradient|Gradient Entity Id')
Report.result(Tests.mixer_inbound_gradient_set_b, inboundGradientEntityId2 and fastNoiseEntityId.invoke("Equal", inboundGradientEntityId2))
# Verify that Gradient Mixer Layer Operations are properly set
mixer_operation_a = hydra.get_component_property_value(gradientMixerComponent, 'Configuration|Layers|[0]|Operation')
mixer_operation_b = hydra.get_component_property_value(gradientMixerComponent, 'Configuration|Layers|[1]|Operation')
Report.result(Tests.mixer_operation_a, mixer_operation_a == 0)
Report.result(Tests.mixer_operation_b, mixer_operation_b == 6)
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientMixerNodeConstruction()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientMixer_NodeConstruction)
@@ -5,132 +5,134 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
component_added = (
"New entity created with the expected component",
"Expected component was not found on entity"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestGradientModifierNodeEntityCreate(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityCreate", args=["level"])
def GradientModifierNodes_EntityCreatedOnNodeAdd():
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
Expected Behavior:
New entities are created when dragging Gradient Modifier nodes to graph area.
Expected Behavior:
New entities are created when dragging Gradient Modifier nodes to graph area.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient modifier nodes has the appropriate Gradient Modifier Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Gradient modifier mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradientModifiers = {
'DitherGradientModifierNode': 'Dither Gradient Modifier',
'GradientMixerNode': 'Gradient Mixer',
'InvertGradientModifierNode': 'Invert Gradient Modifier',
'LevelsGradientModifierNode': 'Levels Gradient Modifier',
'PosterizeGradientModifierNode': 'Posterize Gradient Modifier',
'SmoothStepGradientModifierNode': 'Smooth-Step Gradient Modifier',
'ThresholdGradientModifierNode': 'Threshold Gradient Modifier'
}
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in gradientModifiers:
componentNames.append(gradientModifiers[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient modifier nodes has the appropriate Gradient Modifier Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create nodes for all the gradients modifiers we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradientModifiers:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Gradient modifier mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradientModifiers = {
'DitherGradientModifierNode': 'Dither Gradient Modifier',
'GradientMixerNode': 'Gradient Mixer',
'InvertGradientModifierNode': 'Invert Gradient Modifier',
'LevelsGradientModifierNode': 'Levels Gradient Modifier',
'PosterizeGradientModifierNode': 'Posterize Gradient Modifier',
'SmoothStepGradientModifierNode': 'Smooth-Step Gradient Modifier',
'ThresholdGradientModifierNode': 'Threshold Gradient Modifier'
}
gradientComponent = gradientModifiers[nodeName]
componentTypeId = componentTypeIds[gradientComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
self.test_success = self.test_success and hasComponent
if hasComponent:
self.log("{node} created new Entity with {component} Component".format(node=nodeName,
component=gradientComponent))
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in gradientModifiers:
componentNames.append(gradientModifiers[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
x += 40.0
y += 40.0
# Create nodes for all the gradients modifiers we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradientModifiers:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Stop listening for entity creation notifications
handler.disconnect()
gradientComponent = gradientModifiers[nodeName]
componentTypeId = componentTypeIds[gradientComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
Report.info(f"Node: {nodeName} | Component: {gradientComponent}")
Report.result(Tests.component_added, hasComponent)
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientModifierNodeEntityCreate()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientModifierNodes_EntityCreatedOnNodeAdd)
@@ -5,126 +5,129 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
entity_deleted = (
"Entity was deleted when node was removed",
"Entity was not deleted as expected when node was removed"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
createdEntityId = None
deletedEntityId = None
class TestGradientModifierNodeEntityDelete(EditorTestHelper):
def GradientModifierNodes_EntityRemovedOnNodeDelete():
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientModifierNodeEntityDelete", args=["level"])
Expected Behavior:
Entities are removed when Gradient Modifier nodes are deleted from a graph.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
Expected Behavior:
Entities are removed when Gradient Modifier nodes are deleted from a graph.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
:return: None
"""
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradientModifiers = [
'DitherGradientModifierNode',
'GradientMixerNode',
'InvertGradientModifierNode',
'LevelsGradientModifierNode',
'PosterizeGradientModifierNode',
'SmoothStepGradientModifierNode',
'ThresholdGradientModifierNode'
]
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradientModifiers:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Vegetation Area mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradientModifiers = [
'DitherGradientModifierNode',
'GradientMixerNode',
'InvertGradientModifierNode',
'LevelsGradientModifierNode',
'PosterizeGradientModifierNode',
'SmoothStepGradientModifierNode',
'ThresholdGradientModifierNode'
]
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
self.test_success = self.test_success and removed and createdEntityId.invoke("Equal", deletedEntityId)
if removed and createdEntityId.invoke("Equal", deletedEntityId):
self.log("{node} corresponding Entity was deleted when node is removed".format(node=nodeName))
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradientModifiers:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Stop listening for entity creation notifications
handler.disconnect()
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
Report.info(f"Node: {nodeName}")
Report.result(Tests.entity_deleted, removed and createdEntityId.invoke("Equal", deletedEntityId))
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientModifierNodeEntityDelete()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientModifierNodes_EntityRemovedOnNodeDelete)
@@ -5,141 +5,143 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
dependencies_added = (
"Node created new Entity with all required components",
"Failed to create node with all required components"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestGradientNodeComponentDependency(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientNodeComponentDependency", args=["level"])
def GradientNodes_DependentComponentsAdded():
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with
proper dependent components.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities with
proper dependent components.
Expected Behavior:
All expected component dependencies are met when adding a Gradient Modifier node to a graph.
Expected Behavior:
All expected component dependencies are met when adding a Gradient Modifier node to a graph.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure the proper dependent components are
added
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient Modifier nodes to the graph area, and ensure the proper dependent components are
added
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding these gradients nodes has the main target Gradient Component
# as well as automatically adding all required dependency components
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = {
'FastNoiseGradientNode': 'FastNoise Gradient',
'ImageGradientNode': 'Image Gradient',
'PerlinNoiseGradientNode': 'Perlin Noise Gradient',
'RandomNoiseGradientNode': 'Random Noise Gradient'
}
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
commonComponents = [
'Gradient Transform Modifier',
'Vegetation Reference Shape'
]
componentNames = []
for name in gradients:
componentNames.append(gradients[name])
componentNames.extend(commonComponents)
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Listen for entity creation notifications so we can check if the entity created
# from adding these gradients nodes has the main target Gradient Component
# as well as automatically adding all required dependency components
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create nodes for the gradients that have additional required dependencies and check if
# the Entity created by adding the node has the appropriate Component and required
# Gradient Transform Modifier and Vegetation Reference Shape components added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = {
'FastNoiseGradientNode': 'FastNoise Gradient',
'ImageGradientNode': 'Image Gradient',
'PerlinNoiseGradientNode': 'Perlin Noise Gradient',
'RandomNoiseGradientNode': 'Random Noise Gradient'
}
gradientComponent = gradients[nodeName]
components = [gradientComponent] + commonComponents
success = False
for component in components:
componentTypeId = componentTypeIds[component]
success = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
self.test_success = self.test_success and success
if not success:
break
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
commonComponents = [
'Gradient Transform Modifier',
'Vegetation Reference Shape'
]
componentNames = []
for name in gradients:
componentNames.append(gradients[name])
componentNames.extend(commonComponents)
componentTypeIds = hydra.get_component_type_id_map(componentNames)
if success:
self.log("{node} created new Entity with all required components".format(node=nodeName))
# Create nodes for the gradients that have additional required dependencies and check if
# the Entity created by adding the node has the appropriate Component and required
# Gradient Transform Modifier and Vegetation Reference Shape components added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
x += 40.0
y += 40.0
gradientComponent = gradients[nodeName]
components = [gradientComponent] + commonComponents
success = False
for component in components:
componentTypeId = componentTypeIds[component]
success = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
if not success:
break
Report.info(nodeName)
Report.result(Tests.dependencies_added, success)
# Stop listening for entity creation notifications
handler.disconnect()
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientNodeComponentDependency()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientNodes_DependentComponentsAdded)
@@ -5,130 +5,134 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os, sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
component_added = (
"New entity created with the expected component",
"Expected component was not found on entity"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestGradientNodeEntityCreate(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityCreate", args=["level"])
def GradientNodes_EntityCreatedOnNodeAdd():
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas nodes can be added to a graph, and correctly create entities.
Expected Behavior:
New entities are created when dragging Gradient nodes to graph area.
Expected Behavior:
New entities are created when dragging Gradient nodes to graph area.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = {
'AltitudeGradientNode': 'Altitude Gradient',
'ConstantGradientNode': 'Constant Gradient',
'FastNoiseGradientNode': 'FastNoise Gradient',
'ImageGradientNode': 'Image Gradient',
'PerlinNoiseGradientNode': 'Perlin Noise Gradient',
'RandomNoiseGradientNode': 'Random Noise Gradient',
'ShapeAreaFalloffGradientNode': 'Shape Falloff Gradient',
'SlopeGradientNode': 'Slope Gradient',
'SurfaceMaskGradientNode': 'Surface Mask Gradient',
}
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in gradients:
componentNames.append(gradients[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = {
'AltitudeGradientNode': 'Altitude Gradient',
'ConstantGradientNode': 'Constant Gradient',
'FastNoiseGradientNode': 'FastNoise Gradient',
'ImageGradientNode': 'Image Gradient',
'PerlinNoiseGradientNode': 'Perlin Noise Gradient',
'RandomNoiseGradientNode': 'Random Noise Gradient',
'ShapeAreaFalloffGradientNode': 'Shape Falloff Gradient',
'SlopeGradientNode': 'Slope Gradient',
'SurfaceMaskGradientNode': 'Surface Mask Gradient',
}
gradientComponent = gradients[nodeName]
componentTypeId = componentTypeIds[gradientComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
self.test_success = self.test_success and hasComponent
if hasComponent:
self.log("{node} created new Entity with {component} Component".format(node=nodeName, component=gradientComponent))
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in gradients:
componentNames.append(gradients[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
x += 40.0
y += 40.0
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Stop listening for entity creation notifications
handler.disconnect()
gradientComponent = gradients[nodeName]
componentTypeId = componentTypeIds[gradientComponent]
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, componentTypeId)
Report.info(f"Node: {nodeName} | Component: {gradientComponent}")
Report.result(Tests.component_added, hasComponent)
x += 40.0
y += 40.0
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientNodeEntityCreate()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientNodes_EntityCreatedOnNodeAdd)
@@ -5,129 +5,131 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
entity_deleted = (
"Entity was deleted when node was removed",
"Entity was not deleted as expected when node was removed"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
createdEntityId = None
deletedEntityId = None
class TestGradientNodeEntityDelete(EditorTestHelper):
def GradientNodes_EntityRemovedOnNodeDelete():
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GradientNodeEntityDelete", args=["level"])
Expected Behavior:
Entities are removed when Gradient nodes are deleted from a graph.
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas node deletion properly cleans up entities in the Editor.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
Expected Behavior:
Entities are removed when Gradient nodes are deleted from a graph.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Drag each of the Gradient nodes to the graph area, and ensure a new entity is created
4) Delete the nodes, and ensure the newly created entities are removed
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
:return: None
"""
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
def onEntityCreated(parameters):
global createdEntityId
createdEntityId = parameters[0]
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
def onEntityDeleted(parameters):
global deletedEntityId
deletedEntityId = parameters[0]
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = [
'AltitudeGradientNode',
'ConstantGradientNode',
'FastNoiseGradientNode',
'ImageGradientNode',
'PerlinNoiseGradientNode',
'RandomNoiseGradientNode',
'ShapeAreaFalloffGradientNode',
'SlopeGradientNode',
'SurfaceMaskGradientNode',
]
# Listen for entity creation notifications so we can check if the entity created
# from adding gradient nodes has the appropriate Gradient Component
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
handler.add_callback('OnEditorEntityDeleted', onEntityDeleted)
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Gradient mapping with the key being the node name and the value is the
# expected Component that should be added to the Entity created for the node
gradients = [
'AltitudeGradientNode',
'ConstantGradientNode',
'FastNoiseGradientNode',
'ImageGradientNode',
'PerlinNoiseGradientNode',
'RandomNoiseGradientNode',
'ShapeAreaFalloffGradientNode',
'SlopeGradientNode',
'SurfaceMaskGradientNode',
]
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Create nodes for all the gradients we support and check if the Entity created by
# adding the node has the appropriate Component added automatically to it
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for nodeName in gradients:
nodePosition = math.Vector2(x, y)
node = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName', newGraph, nodeName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, node, nodePosition)
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
self.test_success = self.test_success and removed and createdEntityId.invoke("Equal", deletedEntityId)
if removed and createdEntityId.invoke("Equal", deletedEntityId):
self.log("{node} corresponding Entity was deleted when node is removed".format(node=nodeName))
removed = graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, node)
# Stop listening for entity creation notifications
handler.disconnect()
# Verify that the created Entity for this node matches the Entity that gets
# deleted when the node is removed
Report.info(f"Node: {nodeName}")
Report.result(Tests.entity_deleted, removed and createdEntityId.invoke("Equal", deletedEntityId))
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGradientNodeEntityDelete()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GradientNodes_EntityRemovedOnNodeDelete)
@@ -5,100 +5,99 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
graph_closed = (
"Graph closed on entity delete",
"Graph is still open after entity delete"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newRootEntityId = None
class TestGraphClosedOnEntityDelete(EditorTestHelper):
def GraphClosed_OnEntityDelete():
"""
Summary:
This test verifies that Landscape Canvas graphs are auto-closed when the corresponding entity is deleted.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GraphClosedOnEntityDelete", args=["level"])
Expected Behavior:
When a Landscape Canvas root entity is deleted, the corresponding graph automatically closes.
def run_test(self):
"""
Summary:
This test verifies that Landscape Canvas graphs are auto-closed when the corresponding entity is deleted.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Delete the automatically created entity
4) Verify the open graph is closed
Expected Behavior:
When a Landscape Canvas root entity is deleted, the corresponding graph automatically closes.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Delete the automatically created entity
4) Verify the open graph is closed
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
:return: None
"""
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
def onEntityCreated(parameters):
global newRootEntityId
newRootEntityId = parameters[0]
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
def onEntityCreated(parameters):
global newRootEntityId
newRootEntityId = parameters[0]
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Listen for entity creation notifications so we can store the top-level Entity created
# when a new graph is created, and then delete it to test if the graph is closed
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Create a new graph in Landscape Canvas and verify
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
graphIsOpen = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and graphIsOpen
if graphIsOpen:
self.log("Graph registered with Landscape Canvas")
# Listen for entity creation notifications so we can store the top-level Entity created
# when a new graph is created, and then delete it to test if the graph is closed
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Delete the top-level Entity created by the new graph
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', newRootEntityId)
# Create a new graph in Landscape Canvas and verify
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
graphIsOpen = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graphIsOpen)
# We need to delay here because the closing of the graph due to Entity deletion
# is actually queued in order to workaround an undo/redo issue
# Alternatively, we could add a notifications bus for AssetEditorRequests
# that could trigger when graphs are opened/closed and then do the check there
general.idle_enable(True)
general.idle_wait(1.0)
# Delete the top-level Entity created by the new graph
editor.ToolsApplicationRequestBus(bus.Broadcast, 'DeleteEntityById', newRootEntityId)
# Verify that the corresponding graph is no longer open
graphIsClosed = not graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and graphIsClosed
if graphIsClosed:
self.log("The graph is no longer open after deleting the Entity")
# We need to delay here because the closing of the graph due to Entity deletion
# is actually queued in order to workaround an undo/redo issue
# Alternatively, we could add a notifications bus for AssetEditorRequests
# that could trigger when graphs are opened/closed and then do the check there
general.idle_enable(True)
general.idle_wait(1.0)
# Stop listening for entity creation notifications
handler.disconnect()
# Verify that the corresponding graph is no longer open
graphIsClosed = not graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_closed, graphIsClosed)
# Stop listening for entity creation notifications
handler.disconnect()
test = TestGraphClosedOnEntityDelete()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GraphClosed_OnEntityDelete)
@@ -5,82 +5,82 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
graph_closed = (
"Graph closed on level change",
"Graph is still open after level change"
)
class TestGraphClosedOnLevelChange(EditorTestHelper):
def GraphClosed_OnLevelChange():
"""
Summary:
This test verifies that Landscape Canvas graphs are auto-closed when the currently open level changes.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GraphClosedOnLevelChange", args=["level"])
Expected Behavior:
When a new level is loaded in the Editor, open Landscape Canvas graphs are automatically closed.
def run_test(self):
"""
Summary:
This test verifies that Landscape Canvas graphs are auto-closed when the currently open level changes.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Open a different level
4) Verify the open graph is closed
Expected Behavior:
When a new level is loaded in the Editor, open Landscape Canvas graphs are automatically closed.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Open a different level
4) Verify the open graph is closed
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
:return: None
"""
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
graphIsOpen = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and graphIsOpen
if graphIsOpen:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Open a different level, which should close any open Landscape Canvas graphs
general.open_level_no_prompt('WhiteBox/EmptyLevel')
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Make sure the graph we created is now closed
graphIsOpen = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and not graphIsOpen
if not graphIsOpen:
self.log("Graph is no longer open in Landscape Canvas")
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Open a different level, which should close any open Landscape Canvas graphs
general.open_level_no_prompt('WhiteBox/EmptyLevel')
# Make sure the graph we created is now closed
graphIsOpen = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_closed, not graphIsOpen)
test = TestGraphClosedOnLevelChange()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GraphClosed_OnLevelChange)
@@ -5,95 +5,93 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_open = (
"Graph is open in Landscape Canvas",
"Graph is not open in Landscape Canvas"
)
tabbed_graph_closed = (
"Tabbed graph closed independently",
"Closing tabbed graph resulted in unexpected graphs closing"
)
class TestGraphClosedTabbedGraph(EditorTestHelper):
def GraphClosed_TabbedGraphClosesIndependently():
"""
Summary:
This test verifies that Landscape Canvas tabbed graphs can be independently closed.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GraphClosedTabbedGraph", args=["level"])
Expected Behavior:
Closing a tabbed graph only closes the appropriate graph.
def run_test(self):
"""
Summary:
This test verifies that Landscape Canvas tabbed graphs can be independently closed.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create several new graphs
3) Close one of the open graphs
4) Ensure the graph properly closed, and other open graphs remain open
Expected Behavior:
Closing a tabbed graph only closes the appropriate graph.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create several new graphs
3) Close one of the open graphs
4) Ensure the graph properly closed, and other open graphs remain open
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor.graph as graph
import azlmbr.legacy.general as general
:return: None
"""
from editor_python_test_tools.utils import Report
from editor_python_test_tools.utils import TestHelper as helper
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
editor_id = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def create_new_graph():
new_graph_id = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editor_id)
return new_graph_id
# Create 3 new graphs in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
def is_graph_open(graph_id):
graph_open = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editor_id, graph_id)
return graph_open
newGraphId2 = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId2
if newGraphId2:
self.log("2nd new graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
newGraphId3 = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId3
if newGraphId3:
self.log("3rd new graph created")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Make sure the graphs we created are open in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
success2 = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId2)
success3 = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId3)
self.test_success = self.test_success and success and success2 and success3
if success and success2 and success3:
self.log("Graphs registered with Landscape Canvas")
# Create 3 new graphs in Landscape Canvas and make sure the graphs we created are open in Landscape Canvas
graph1 = create_new_graph()
Report.result(Tests.new_graph_created, graph1 is not None)
Report.result(Tests.graph_open, is_graph_open(graph1))
# Close a single graph and verify it was properly closed and other graphs remain open
success4 = graph.AssetEditorRequestBus(bus.Event, 'CloseGraph', editorId, newGraphId2)
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
success2 = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId2)
success3 = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId3)
self.test_success = self.test_success and success and success3 and success4 and not success2
if success and success3 and success4 and not success2:
self.log("Graph 2 was successfully closed")
graph2 = create_new_graph()
Report.result(Tests.new_graph_created, graph2 is not None)
Report.result(Tests.graph_open, is_graph_open(graph2))
graph3 = create_new_graph()
Report.result(Tests.new_graph_created, graph3 is not None)
Report.result(Tests.graph_open, is_graph_open(graph3))
# Close a single graph and verify it was properly closed and other graphs remain open
graph.AssetEditorRequestBus(bus.Event, 'CloseGraph', editor_id, graph2)
Report.result(Tests.tabbed_graph_closed, not is_graph_open(graph2) and is_graph_open(graph1) and
is_graph_open(graph3))
test = TestGraphClosedTabbedGraph()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GraphClosed_TabbedGraphClosesIndependently)
@@ -5,155 +5,151 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
import azlmbr.paths
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import automatedtesting_shared.landscape_canvas_utils as lc
class Tests:
slice_instantiated = (
"Slice instantiated successfully",
"Failed to instantiate slice"
)
existing_graph_opened = (
"Opened existing graph from slice",
"Failed to open existing graph"
)
node_removed = (
"Rotation Modifier node was removed",
"Failed to remove Rotation Modifier node"
)
component_removed = (
"Rotation Modifier component was removed from entity",
"Rotation Modifier component is still present on entity"
)
entity_deleted = (
"BushSpawner entity was deleted",
"Failed to delete BushSpawner entity"
)
entity_reference_updated = (
"Gradient Entity Id reference was properly updated",
"Gradient Entity Id reference was not updated properly"
)
class TestGraphUpdatesUpdateComponents(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="GraphUpdatesUpdateComponents", args=["level"])
def GraphUpdates_UpdateComponent():
"""
Summary:
This test verifies that components are properly updated as nodes are added/removed/updated.
def run_test(self):
"""
Summary:
This test verifies that components are properly updated as nodes are added/removed/updated.
Expected Behavior:
Landscape Canvas node CRUD properly updates component entities.
Expected Behavior:
Landscape Canvas node CRUD properly updates component entities.
Test Steps:
1. Open Level.
2. Open the graph on LC_BushFlowerBlender.slice
3. Find the Rotation Modifier node on the BushSpawner entity
4. Delete the Rotation Modifier node
5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity
6. Delete the Vegetation Layer Spawner node from the graph
7. Ensure BushSpawner entity is deleted
8. Change connection from second Rotation Modifier node to a different Gradient
9. Ensure Gradient reference on component is updated
Test Steps:
1. Open Level.
2. Open the graph on LC_BushFlowerBlender.slice
3. Find the Rotation Modifier node on the BushSpawner entity
4. Delete the Rotation Modifier node
5. Ensure the Vegetation Rotation Modifier component is removed from the BushSpawner entity
6. Delete the Vegetation Layer Spawner node from the graph
7. Ensure BushSpawner entity is deleted
8. Change connection from second Rotation Modifier node to a different Gradient
9. Ensure Gradient reference on component is updated
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
# Create a new empty level and instantiate LC_BushFlowerBlender.slice
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
self.test_success = self.test_success and test_slice.IsValid()
if test_slice.IsValid():
self.log("Slice spawned!")
import os
# Search for root entity to ensure slice is loaded
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
self.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0, 5.0)
import azlmbr.asset as asset
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.legacy.general as general
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.math as math
import azlmbr.slice as slice
# Find needed entities in the loaded level
slice_root_id = hydra.find_entity_by_name('LandscapeCanvas')
bush_spawner_id = hydra.find_entity_by_name('BushSpawner')
flower_spawner_id = hydra.find_entity_by_name('FlowerSpawner')
inverted_perlin_noise_id = hydra.find_entity_by_name('Invert')
import automatedtesting_shared.landscape_canvas_utils as lc
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
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph_id = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
self.test_success = self.test_success and open_graph_id.IsValid()
if open_graph_id.IsValid():
self.log('Graph opened')
# Open a simple level and instantiate LC_BushFlowerBlender.slice
helper.init_idle()
helper.open_level("Physics", "Base")
transform = math.Transform_CreateIdentity()
position = math.Vector3(64.0, 64.0, 32.0)
transform.invoke('SetPosition', position)
test_slice_path = os.path.join("Slices", "LC_BushFlowerBlender.slice")
test_slice_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", test_slice_path, math.Uuid(),
False)
test_slice = slice.SliceRequestBus(bus.Broadcast, 'InstantiateSliceFromAssetId', test_slice_id, transform)
Report.critical_result(Tests.slice_instantiated, test_slice.IsValid())
# Find the Rotation Modifier node on the BushSpawner entity
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', bush_spawner_id)
# Search for root entity to ensure slice is loaded
search_filter = entity.SearchFilter()
search_filter.names = ["LandscapeCanvas"]
helper.wait_for_condition(lambda: len(entity.SearchBus(bus.Broadcast, 'SearchEntities', search_filter)) > 0, 5.0)
# Remove the Rotation Modifier node
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', open_graph_id, rotation_modifier_node[0])
# Find needed entities in the loaded level
slice_root_id = hydra.find_entity_by_name('LandscapeCanvas')
bush_spawner_id = hydra.find_entity_by_name('BushSpawner')
flower_spawner_id = hydra.find_entity_by_name('FlowerSpawner')
inverted_perlin_noise_id = hydra.find_entity_by_name('Invert')
# Verify node was removed
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', bush_spawner_id)
self.test_success = self.test_success and not rotation_modifier_node
if not rotation_modifier_node:
self.log('Rotation Modifier node was removed')
else:
self.log('Rotation Modifier node was not removed')
# Open Landscape Canvas and the existing graph
general.open_pane('Landscape Canvas')
open_graph_id = landscapecanvas.LandscapeCanvasRequestBus(bus.Broadcast, 'OnGraphEntity', slice_root_id)
Report.critical_result(Tests.existing_graph_opened, open_graph_id.IsValid())
# Verify the component was removed from the BushSpawner entity
has_rotation_modifier = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', bush_spawner_id,
hydra.get_component_type_id('Vegetation Rotation Modifier'))
self.test_success = self.test_success and not has_rotation_modifier
if not has_rotation_modifier:
self.log('Rotation Modifier component was removed from entity')
else:
self.log('Rotation Modifier component is still present on entity')
# Find the Rotation Modifier node on the BushSpawner entity
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', bush_spawner_id)
# Find the Vegetation Layer Spawner node on the BushSpawner entity
layer_spawner_node = lc.find_nodes_matching_entity_component('Vegetation Layer Spawner', bush_spawner_id)
# Remove the Rotation Modifier node
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', open_graph_id, rotation_modifier_node[0])
# Remove the Vegetation Layer Spawner node and verify the corresponding entity is deleted
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', open_graph_id, layer_spawner_node[0])
bush_spawner_id = hydra.find_entity_by_name('BushSpawner')
self.test_success = self.test_success and not bush_spawner_id
if not bush_spawner_id:
self.log('BushSpawner entity was deleted')
else:
self.log('Failed to delete BushSpawner entity')
# Verify node was removed
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', bush_spawner_id)
Report.result(Tests.node_removed, not rotation_modifier_node)
# Connect the FlowerSpawner's Rotation Modifier node to the Invert Gradient Modifier node
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', flower_spawner_id)
invert_node = lc.find_nodes_matching_entity_component('Invert Gradient Modifier', inverted_perlin_noise_id)
# Verify the component was removed from the BushSpawner entity
has_rotation_modifier = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', bush_spawner_id,
hydra.get_component_type_id('Vegetation Rotation Modifier'))
Report.result(Tests.component_removed, not has_rotation_modifier)
inbound_gradient_z_slot = graph.GraphModelSlotId('InboundGradientZ')
outbound_gradient_slot = graph.GraphModelSlotId('OutboundGradient')
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', open_graph_id, invert_node[0],
outbound_gradient_slot, rotation_modifier_node[0], inbound_gradient_z_slot)
# Find the Vegetation Layer Spawner node on the BushSpawner entity
layer_spawner_node = lc.find_nodes_matching_entity_component('Vegetation Layer Spawner', bush_spawner_id)
general.idle_wait(1.0) # Add a small wait to ensure component property has time to update
# Remove the Vegetation Layer Spawner node and verify the corresponding entity is deleted
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', open_graph_id, layer_spawner_node[0])
bush_spawner_id = hydra.find_entity_by_name('BushSpawner')
Report.result(Tests.entity_deleted, not bush_spawner_id)
# Verify the Gradient Entity Id reference on the Rotation Modifier component was properly set
rotation_type_id = hydra.get_component_type_id('Vegetation Rotation Modifier')
rotation_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', flower_spawner_id,
rotation_type_id)
rotation_component = rotation_outcome.GetValue()
gradient_reference = hydra.get_component_property_value(rotation_component,
'Configuration|Rotation Z|Gradient|Gradient Entity Id')
gradient_reference_success = gradient_reference == inverted_perlin_noise_id
self.test_success = self.test_success and gradient_reference_success
if gradient_reference_success:
self.log('Gradient Entity Id reference was properly updated')
else:
self.log(f'Gradient Entity Id was not updated properly: Expected {inverted_perlin_noise_id.ToString()} -- Got '
f'{gradient_reference.ToString()}.')
# Connect the FlowerSpawner's Rotation Modifier node to the Invert Gradient Modifier node
rotation_modifier_node = lc.find_nodes_matching_entity_component('Vegetation Rotation Modifier', flower_spawner_id)
invert_node = lc.find_nodes_matching_entity_component('Invert Gradient Modifier', inverted_perlin_noise_id)
inbound_gradient_z_slot = graph.GraphModelSlotId('InboundGradientZ')
outbound_gradient_slot = graph.GraphModelSlotId('OutboundGradient')
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', open_graph_id, invert_node[0],
outbound_gradient_slot, rotation_modifier_node[0], inbound_gradient_z_slot)
general.idle_wait(1.0) # Add a small wait to ensure component property has time to update
# Verify the Gradient Entity Id reference on the Rotation Modifier component was properly set
rotation_type_id = hydra.get_component_type_id('Vegetation Rotation Modifier')
rotation_outcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType', flower_spawner_id,
rotation_type_id)
rotation_component = rotation_outcome.GetValue()
gradient_reference = hydra.get_component_property_value(rotation_component,
'Configuration|Rotation Z|Gradient|Gradient Entity Id')
Report.result(Tests.entity_reference_updated, gradient_reference == inverted_perlin_noise_id)
test = TestGraphUpdatesUpdateComponents()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(GraphUpdates_UpdateComponent)
@@ -1,87 +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.entity as entity
import azlmbr.paths
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
class TestLandscapeCanvasComponentAddedRemoved(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LandscapeCanvasComponentAddedRemoved", args=["level"])
def run_test(self):
"""
Summary:
This test verifies that the Landscape Canvas component can be added to/removed from an entity.
Expected Behavior:
Closing a tabbed graph only closes the appropriate graph.
Test Steps:
1) Create a new level
2) Create a new entity
3) Add a Landscape Canvas component to the entity
4) Remove the Landscape Canvas component from the entity
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
# Create an Entity at the root of the level
newEntityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())
# Find the component TypeId for our Landscape Canvas component
landscape_canvas_type_id = hydra.get_component_type_id("Landscape Canvas")
# Add the Landscape Canvas Component to our Entity
componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', newEntityId, [landscape_canvas_type_id])
components = componentOutcome.GetValue()
landscapeCanvasComponent = components[0]
# Validate the Landscape Canvas Component exists
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, landscape_canvas_type_id)
self.test_success = self.test_success and hasComponent
if hasComponent:
self.log("Landscape Canvas Component added to Entity")
# Remove the Landscape Canvas Component
editor.EditorComponentAPIBus(bus.Broadcast, 'RemoveComponents', [landscapeCanvasComponent])
# Validate the Landscape Canvas Component is no longer on our Entity
hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId, landscape_canvas_type_id)
self.test_success = self.test_success and not hasComponent
if not hasComponent:
self.log("Landscape Canvas Component removed from Entity")
test = TestLandscapeCanvasComponentAddedRemoved()
test.run()
@@ -1,82 +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.math as math
import azlmbr.paths
import azlmbr.bus as bus
import azlmbr.asset as asset
import azlmbr.slice as slice
sys.path.append(os.path.join(azlmbr.paths.devroot, "AutomatedTesting", "Gem", "PythonTests"))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
class TestLandscapeCanvasSliceCreateInstantiate(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LandscapeCanvas_SliceCreateInstantiate", args=["level"])
def run_test(self):
"""
Summary:
A slice containing the LandscapeCanvas component can be created/instantiated.
Expected Result:
Slice is created/processed/instantiated successfully and free of errors/warnings.
Test Steps:
1) Create a new level
2) Create a new entity with a Landscape Canvas component
3) Create a slice of the new entity
4) Instantiate a new copy of the slice
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
def path_is_valid_asset(asset_path):
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", asset_path, math.Uuid(), False)
return asset_id.invoke("IsValid")
# Create empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
# Create entity with LandScape Canvas component
position = math.Vector3(512.0, 512.0, 32.0)
landscape_canvas = hydra.Entity("landscape_canvas_entity")
landscape_canvas.create_entity(position, ["Landscape Canvas"])
# Create slice from the created entity
slice_path = os.path.join("slices", "TestSlice.slice")
slice.SliceRequestBus(bus.Broadcast, "CreateNewSlice", landscape_canvas.id, slice_path)
# Verify if slice is created
self.wait_for_condition(lambda: path_is_valid_asset(slice_path), 5.0)
self.log(f"Slice has been created successfully: {path_is_valid_asset(slice_path)}")
# Instantiate slice
transform = math.Transform_CreateIdentity()
asset_id = asset.AssetCatalogRequestBus(bus.Broadcast, "GetAssetIdByPath", slice_path, math.Uuid(), False)
test_slice = slice.SliceRequestBus(bus.Broadcast, "InstantiateSliceFromAssetId", asset_id, transform)
self.wait_for_condition(lambda: test_slice.IsValid(), 3.0)
self.log(f"Slice instantiated: {test_slice.IsValid()}")
test = TestLandscapeCanvasSliceCreateInstantiate()
test.run()
@@ -5,157 +5,160 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.entity as entity
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
blender_first_layer_set = (
"Spawner entity set as the first layer of the Vegetation Layer Blender component",
"Unexpected entity set as the first layer of the Vegetation Layer Blender component"
)
blender_second_layer_set = (
"Blocker entity set as the second layer of the Vegetation Layer Blender component",
"Unexpected entity set as the second layer of the Vegetation Layer Blender component"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestLayerBlenderNodeConstruction(EditorTestHelper):
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerBlenderNodeConstruction", args=["level"])
def LayerBlender_NodeConstruction():
"""
Summary:
This test verifies a Layer Blender vegetation setup can be constructed through Landscape Canvas.
def run_test(self):
"""
Summary:
This test verifies a Layer Blender vegetation setup can be constructed through Landscape Canvas.
Expected Behavior:
Entities contain all required components and component references after creating nodes and setting connections
on a Landscape Canvas graph.
Expected Behavior:
Entities contain all required components and component references after creating nodes and setting connections
on a Landscape Canvas graph.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Add all necessary nodes to the graph and set connections to form a Layer Blender setup
4) Verify all components and component references were properly set during graph construction
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Add all necessary nodes to the graph and set connections to form a Layer Blender setup
4) Verify all components and component references were properly set during graph construction
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
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
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=1024,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=4096,
use_terrain=False,
)
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Listen for entity creation notifications so we can verify the component EntityId
# references are set correctly when connecting slots on the nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
positionX = 10.0
positionY = 10.0
offsetX = 340.0
offsetY = 100.0
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Add a Vegetation Layer Spawner node to the graph
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
layerSpawnerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'SpawnerAreaNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerSpawnerNode, math.Vector2(positionX, positionY))
layerSpawnerEntityId = newEntityId
# Listen for entity creation notifications so we can verify the component EntityId
# references are set correctly when connecting slots on the nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
positionX += offsetX
positionY += offsetY
positionX = 10.0
positionY = 10.0
offsetX = 340.0
offsetY = 100.0
# Add a Vegetation Layer Blocker node to the graph
layerBlockerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'BlockerAreaNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerBlockerNode, math.Vector2(positionX, positionY))
layerBlockerEntityId = newEntityId
# Add a Vegetation Layer Spawner node to the graph
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
layerSpawnerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'SpawnerAreaNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerSpawnerNode, math.Vector2(positionX, positionY))
layerSpawnerEntityId = newEntityId
positionX += offsetX
positionY += offsetY
positionX += offsetX
positionY += offsetY
# Add a Vegetation Layer Blender node to the graph
layerBlenderNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'AreaBlenderNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerBlenderNode, math.Vector2(positionX, positionY))
layerBlenderNodeEntityId = newEntityId
# Add a Vegetation Layer Blocker node to the graph
layerBlockerNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'BlockerAreaNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerBlockerNode, math.Vector2(positionX, positionY))
layerBlockerEntityId = newEntityId
positionX += offsetX
positionY += offsetY
positionX += offsetX
positionY += offsetY
outboundAreaSlotId = graph.GraphModelSlotId('OutboundArea')
inboundAreaSlotId = graph.GraphModelSlotId('InboundArea')
inboundAreaSlotId2 = graph.GraphControllerRequestBus(bus.Event, 'ExtendSlot', newGraphId, layerBlenderNode,
'InboundArea')
# Add a Vegetation Layer Blender node to the graph
layerBlenderNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, 'AreaBlenderNode')
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, layerBlenderNode, math.Vector2(positionX, positionY))
layerBlenderNodeEntityId = newEntityId
# Connect slots on our nodes to construct a Vegetation Layer Blender hierarchy
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, layerSpawnerNode, outboundAreaSlotId,
layerBlenderNode, inboundAreaSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, layerBlockerNode, outboundAreaSlotId,
layerBlenderNode, inboundAreaSlotId2)
positionX += offsetX
positionY += offsetY
# Delay to allow all the underlying component properties to be updated after the slot connections are made
general.idle_wait(1.0)
outboundAreaSlotId = graph.GraphModelSlotId('OutboundArea')
inboundAreaSlotId = graph.GraphModelSlotId('InboundArea')
inboundAreaSlotId2 = graph.GraphControllerRequestBus(bus.Event, 'ExtendSlot', newGraphId, layerBlenderNode,
'InboundArea')
# Get component info
layerBlenderTypeId = hydra.get_component_type_id("Vegetation Layer Blender")
vegetationLayerBlenderOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType',
layerBlenderNodeEntityId, layerBlenderTypeId)
layerBlenderComponent = vegetationLayerBlenderOutcome.GetValue()
# Connect slots on our nodes to construct a Vegetation Layer Blender hierarchy
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, layerSpawnerNode, outboundAreaSlotId,
layerBlenderNode, inboundAreaSlotId)
graph.GraphControllerRequestBus(bus.Event, 'AddConnectionBySlotId', newGraphId, layerBlockerNode, outboundAreaSlotId,
layerBlenderNode, inboundAreaSlotId2)
# Verify the Vegetation Areas properties on our Vegetation Layer Blender component have been set to our area EntityIds
area1EntityId = hydra.get_component_property_value(layerBlenderComponent, 'Configuration|Vegetation Areas|[0]')
self.test_success = self.test_success and area1EntityId and layerSpawnerEntityId.invoke("Equal", area1EntityId)
if area1EntityId and layerSpawnerEntityId.invoke("Equal", area1EntityId):
self.log("Vegetation Layer Blender component Vegetation Areas[0] property set to Vegetation Layer Spawner EntityId")
# Delay to allow all the underlying component properties to be updated after the slot connections are made
general.idle_wait(1.0)
area2EntityId = hydra.get_component_property_value(layerBlenderComponent, 'Configuration|Vegetation Areas|[1]')
self.test_success = self.test_success and area2EntityId and layerBlockerEntityId.invoke("Equal", area2EntityId)
if area2EntityId and layerBlockerEntityId.invoke("Equal", area2EntityId):
self.log("Vegetation Layer Blender component Vegetation Areas[1] property set to Vegetation Layer Blocker EntityId")
# Get component info
layerBlenderTypeId = hydra.get_component_type_id("Vegetation Layer Blender")
vegetationLayerBlenderOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentOfType',
layerBlenderNodeEntityId, layerBlenderTypeId)
layerBlenderComponent = vegetationLayerBlenderOutcome.GetValue()
# Stop listening for entity creation notifications
handler.disconnect()
# Verify the Vegetation Areas properties on our Vegetation Layer Blender component have been set to our area EntityIds
area1EntityId = hydra.get_component_property_value(layerBlenderComponent, 'Configuration|Vegetation Areas|[0]')
Report.result(Tests.blender_first_layer_set, area1EntityId and layerSpawnerEntityId.invoke("Equal", area1EntityId))
area2EntityId = hydra.get_component_property_value(layerBlenderComponent, 'Configuration|Vegetation Areas|[1]')
Report.result(Tests.blender_second_layer_set, area2EntityId and layerBlockerEntityId.invoke("Equal", area2EntityId))
# Stop listening for entity creation notifications
handler.disconnect()
test = TestLayerBlenderNodeConstruction()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerBlender_NodeConstruction)
@@ -5,164 +5,170 @@ For complete copyright and license terms please see the LICENSE at the root of t
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
import os
import sys
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
component_added = (
"Expected component is present on entity",
"Expected component was not found on entity"
)
component_removed = (
"Expected component was removed from entity",
"Component is unexpectedly still present on entity"
)
sys.path.append(os.path.join(azlmbr.paths.devroot, 'AutomatedTesting', 'Gem', 'PythonTests'))
import editor_python_test_tools.hydra_editor_utils as hydra
from editor_python_test_tools.editor_test_helper import EditorTestHelper
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
newEntityId = None
class TestLayerExtenderNodeComponentEntitySync(EditorTestHelper):
def LayerExtenderNodes_ComponentEntitySync():
"""
Summary:
This test verifies that all wrapped nodes can be successfully added to/removed from parent nodes.
def __init__(self):
EditorTestHelper.__init__(self, log_prefix="LayerExtenderNodeComponentEntitySync", args=["level"])
Expected Behavior:
All wrapped extender nodes can be added to/removed from appropriate parent nodes.
def run_test(self):
"""
Summary:
This test verifies that all wrapped nodes can be successfully added to/removed from parent nodes.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Add Area Blender and Layer Spawner nodes to the graph, and add/remove each extender node to/from each
Expected Behavior:
All wrapped extender nodes can be added to/removed from appropriate parent nodes.
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
Test Steps:
1) Create a new level
2) Open Landscape Canvas and create a new graph
3) Add Area Blender and Layer Spawner nodes to the graph, and add/remove each extender node to/from each
:return: None
"""
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
import azlmbr.landscapecanvas as landscapecanvas
import azlmbr.legacy.general as general
import azlmbr.math as math
import azlmbr.paths
:return: None
"""
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 onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
# Create a new empty level
self.test_success = self.create_level(
self.args["level"],
heightmap_resolution=128,
heightmap_meters_per_pixel=1,
terrain_texture_resolution=128,
use_terrain=False,
)
def onEntityCreated(parameters):
global newEntityId
newEntityId = parameters[0]
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
self.test_success = self.test_success and general.is_pane_visible('Landscape Canvas')
if general.is_pane_visible('Landscape Canvas'):
self.log('Landscape Canvas pane is open')
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
self.test_success = self.test_success and newGraphId
if newGraphId:
self.log("New graph created")
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Make sure the graph we created is in Landscape Canvas
success = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
self.test_success = self.test_success and success
if success:
self.log("Graph registered with Landscape Canvas")
# Create a new graph in Landscape Canvas
newGraphId = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, newGraphId is not None)
# Listen for entity creation notifications so we can check if the
# proper components are added when we add nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, newGraphId)
Report.result(Tests.graph_registered, graph_registered)
# Extender mapping with the key being the node name and the value is the
# expected Component that should be added to the layer Entity for that wrapped node
extenders = {
'AltitudeFilterNode': 'Vegetation Altitude Filter',
'DistanceBetweenFilterNode': 'Vegetation Distance Between Filter',
'DistributionFilterNode': 'Vegetation Distribution Filter',
'ShapeIntersectionFilterNode': 'Vegetation Shape Intersection Filter',
'SlopeFilterNode': 'Vegetation Slope Filter',
'SurfaceMaskDepthFilterNode': 'Vegetation Surface Mask Depth Filter',
'SurfaceMaskFilterNode': 'Vegetation Surface Mask Filter',
'PositionModifierNode': 'Vegetation Position Modifier',
'RotationModifierNode': 'Vegetation Rotation Modifier',
'ScaleModifierNode': 'Vegetation Scale Modifier',
'SlopeAlignmentModifierNode': 'Vegetation Slope Alignment Modifier',
'AssetWeightSelectorNode': 'Vegetation Asset Weight Selector'
}
# Listen for entity creation notifications so we can check if the
# proper components are added when we add nodes
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback('OnEditorEntityCreated', onEntityCreated)
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in extenders:
componentNames.append(extenders[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Extender mapping with the key being the node name and the value is the
# expected Component that should be added to the layer Entity for that wrapped node
extenders = {
'AltitudeFilterNode': 'Vegetation Altitude Filter',
'DistanceBetweenFilterNode': 'Vegetation Distance Between Filter',
'DistributionFilterNode': 'Vegetation Distribution Filter',
'ShapeIntersectionFilterNode': 'Vegetation Shape Intersection Filter',
'SlopeFilterNode': 'Vegetation Slope Filter',
'SurfaceMaskDepthFilterNode': 'Vegetation Surface Mask Depth Filter',
'SurfaceMaskFilterNode': 'Vegetation Surface Mask Filter',
'PositionModifierNode': 'Vegetation Position Modifier',
'RotationModifierNode': 'Vegetation Rotation Modifier',
'ScaleModifierNode': 'Vegetation Scale Modifier',
'SlopeAlignmentModifierNode': 'Vegetation Slope Alignment Modifier',
'AssetWeightSelectorNode': 'Vegetation Asset Weight Selector'
}
areas = [
'AreaBlenderNode',
'SpawnerAreaNode'
]
# Retrieve a mapping of the TypeIds for all the components
# we will be checking for
componentNames = []
for name in extenders:
componentNames.append(extenders[name])
componentTypeIds = hydra.get_component_type_id_map(componentNames)
# Add/remove all our supported extender nodes to the Layer Areas and check if the appropriate
# Components are added/removed to the wrapper node's Entity
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for areaName in areas:
nodePosition = math.Vector2(x, y)
areaNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, areaName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, areaNode, nodePosition)
areas = [
'AreaBlenderNode',
'SpawnerAreaNode'
]
success = True
for extenderName in extenders:
# Add the wrapped node for the extender
extenderNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast,
'CreateNodeForTypeName', newGraph,
extenderName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, extenderNode, nodePosition)
graph.GraphControllerRequestBus(bus.Event, 'WrapNode', newGraphId, areaNode, extenderNode)
# Add/remove all our supported extender nodes to the Layer Areas and check if the appropriate
# Components are added/removed to the wrapper node's Entity
newGraph = graph.GraphManagerRequestBus(bus.Broadcast, 'GetGraph', newGraphId)
x = 10.0
y = 10.0
for areaName in areas:
nodePosition = math.Vector2(x, y)
areaNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast, 'CreateNodeForTypeName',
newGraph, areaName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, areaNode, nodePosition)
# Check that the appropriate Component was added when the extender node was added
extenderComponent = extenders[extenderName]
componentTypeId = componentTypeIds[extenderComponent]
success = success and editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
success = True
for extenderName in extenders:
# Add the wrapped node for the extender
extenderNode = landscapecanvas.LandscapeCanvasNodeFactoryRequestBus(bus.Broadcast,
'CreateNodeForTypeName', newGraph,
extenderName)
graph.GraphControllerRequestBus(bus.Event, 'AddNode', newGraphId, extenderNode, nodePosition)
graph.GraphControllerRequestBus(bus.Event, 'WrapNode', newGraphId, areaNode, extenderNode)
# Check that the appropriate Component was added when the extender node was added
extenderComponent = extenders[extenderName]
componentTypeId = componentTypeIds[extenderComponent]
success = success and editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
Report.info(f"Component: {extenderComponent}")
Report.result(Tests.component_added, success)
if not success:
break
# Check that the appropriate Component was removed when the extender node was removed
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, extenderNode)
success = success and not editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
self.test_success = self.test_success and success
if not success:
self.log("{node} failed to add {component} Component".format(node=areaName,
component=extenderComponent))
break
Report.info(f"Component: {extenderComponent}")
Report.result(Tests.component_removed, success)
if not success:
break
# Check that the appropriate Component was removed when the extender node was removed
graph.GraphControllerRequestBus(bus.Event, 'RemoveNode', newGraphId, extenderNode)
success = success and not editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', newEntityId,
componentTypeId)
self.test_success = self.test_success and success
if not success:
self.log("{node} failed to remove {component} Component".format(node=areaName,
component=extenderComponent))
break
if success:
Report.info(f"{areaName} successfully added and removed all filters/modifiers/selectors")
if success:
self.log("{node} successfully added and removed all filters/modifiers/selectors".format(node=areaName))
# Stop listening for entity creation notifications
handler.disconnect()
# Stop listening for entity creation notifications
handler.disconnect()
test = TestLayerExtenderNodeComponentEntitySync()
test.run()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(LayerExtenderNodes_ComponentEntitySync)
@@ -0,0 +1,111 @@
"""
Copyright (c) Contributors to the Open 3D Engine Project.
For complete copyright and license terms please see the LICENSE at the root of this distribution.
SPDX-License-Identifier: Apache-2.0 OR MIT
"""
class Tests:
lc_tool_opened = (
"Landscape Canvas tool opened",
"Failed to open Landscape Canvas tool"
)
new_graph_created = (
"Successfully created new graph",
"Failed to create new graph"
)
graph_registered = (
"Graph registered with Landscape Canvas",
"Failed to register graph"
)
lc_component_added = (
"Root entity created with the Landscape Canvas component",
"Landscape Canvas component was not found on the root entity"
)
lc_tool_closed = (
"Landscape Canvas tool closed",
"Failed to close Landscape Canvas tool"
)
new_root_entity_id = None
def NewGraph_CreatedSuccessfully():
"""
Summary:
This test verifies that new graphs can be created in Landscape Canvas.
Expected Behavior:
New graphs can be created, and proper entity is created to hold graph data with a Landscape Canvas component.
Test Steps:
1) Open a simple level
2) Open Landscape Canvas and create a new graph
3) Ensures the root entity created contains a Landscape Canvas component
Note:
- This test file must be called from the Open 3D Engine Editor command terminal
- Any passed and failed tests are written to the Editor.log file.
Parsing the file or running a log_monitor are required to observe the test results.
:return: None
"""
import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.editor.graph as graph
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
editorId = azlmbr.globals.property.LANDSCAPE_CANVAS_EDITOR_ID
def on_entity_created(parameters):
global new_root_entity_id
new_root_entity_id = parameters[0]
# Open an existing simple level
helper.init_idle()
helper.open_level("Physics", "Base")
# Listen for entity creation notifications so we can check if the entity created
# with the new graph has our Landscape Canvas component automatically added
handler = editor.EditorEntityContextNotificationBusHandler()
handler.connect()
handler.add_callback("OnEditorEntityCreated", on_entity_created)
# Open Landscape Canvas tool and verify
general.open_pane('Landscape Canvas')
Report.critical_result(Tests.lc_tool_opened, general.is_pane_visible('Landscape Canvas'))
# Create a new graph in Landscape Canvas
new_graph_id = graph.AssetEditorRequestBus(bus.Event, 'CreateNewGraph', editorId)
Report.critical_result(Tests.new_graph_created, new_graph_id is not None)
# Make sure the graph we created is in Landscape Canvas
graph_registered = graph.AssetEditorRequestBus(bus.Event, 'ContainsGraph', editorId, new_graph_id)
Report.result(Tests.graph_registered, graph_registered)
# Check if the entity created when we create a new graph has the
# Landscape Canvas component already added to it
landscape_canvas_type_id = hydra.get_component_type_id("Landscape Canvas")
success = editor.EditorComponentAPIBus(bus.Broadcast, "HasComponentOfType", new_root_entity_id,
landscape_canvas_type_id)
Report.result(Tests.lc_component_added, success)
# Close Landscape Canvas tool and verify
general.close_pane("Landscape Canvas")
Report.result(Tests.lc_tool_closed, not general.is_pane_visible("Landscape Canvas"))
# Stop listening for entity creation notifications
handler.disconnect()
if __name__ == "__main__":
from editor_python_test_tools.utils import Report
Report.start_test(NewGraph_CreatedSuccessfully)

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