Integrating latest 47acbe8
This commit is contained in:
@@ -0,0 +1,416 @@
|
||||
"""
|
||||
All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
||||
its licensors.
|
||||
|
||||
For complete copyright and license terms please see the LICENSE at the root of this
|
||||
distribution (the "License"). All use of this software is governed by the License,
|
||||
or, if provided, by the license below or the license accompanying this file. Do not
|
||||
remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
"""
|
||||
|
||||
# Test case ID : C5959808
|
||||
# Test Case Title : Verify Force Region Position Offset
|
||||
# URL of the test case : https://testrail.agscollab.com/index.php?/cases/view/5959808
|
||||
|
||||
|
||||
# fmt:off
|
||||
class Tests:
|
||||
# General tests
|
||||
enter_game_mode = ("Entered game mode", "Failed to enter game mode")
|
||||
exit_game_mode = ("Exited game mode", "Couldn't exit game mode")
|
||||
test_completed = ("The test successfully completed", "The test timed out")
|
||||
# ***** Entities found *****
|
||||
# Force Regions
|
||||
force_region_x_found = ("Force Region for X axis test was found", "Force Region for X axis test was NOT found")
|
||||
force_region_y_found = ("Force Region for Y axis test was found", "Force Region for Y axis test was NOT found")
|
||||
force_region_z_found = ("Force Region for Z axis test was found", "Force Region for Z axis test was NOT found")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_found = ("Force Region Pass Box for X axis test was found", "Force Region Pass Box for X axis test was NOT found")
|
||||
force_region_pass_box_y_found = ("Force Region Pass Box for Y axis test was found", "Force Region Pass Box for Y axis test was NOT found")
|
||||
force_region_pass_box_z_found = ("Force Region Pass Box for Z axis test was found", "Force Region Pass Box for Z axis test was NOT found")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_found = ("External Pass Box for X axis test was found", "External Pass Box for X axis test was NOT found")
|
||||
external_pass_box_y_found = ("External Pass Box for Y axis test was found", "External Pass Box for Y axis test was NOT found")
|
||||
external_pass_box_z_found = ("External Pass Box for Z axis test was found", "External Pass Box for Z axis test was NOT found")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_found = ("Force Region Fail Box for X axis test was found", "Force Region Fail Box for X axis test was NOT found")
|
||||
force_region_fail_box_y_found = ("Force Region Fail Box for Y axis test was found", "Force Region Fail Box for Y axis test was NOT found")
|
||||
force_region_fail_box_z_found = ("Force Region Fail Box for Z axis test was found", "Force Region Fail Box for Z axis test was NOT found")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_found = ("External Fail Box for X axis test was found", "External Fail Box for X axis test was NOT found")
|
||||
external_fail_box_y_found = ("External Fail Box for Y axis test was found", "External Fail Box for Y axis test was NOT found")
|
||||
external_fail_box_z_found = ("External Fail Box for Z axis test was found", "External Fail Box for Z axis test was NOT found")
|
||||
# Pass spheres
|
||||
sphere_pass_x_found = ("Pass Sphere for X axis test was found", "Pass Sphere for X axis test was NOT found")
|
||||
sphere_pass_y_found = ("Pass Sphere for Y axis test was found", "Pass Sphere for Y axis test was NOT found")
|
||||
sphere_pass_z_found = ("Pass Sphere for Z axis test was found", "Pass Sphere for Z axis test was NOT found")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_found = ("Bounce Sphere for X axis test was found", "Bounce Sphere for X axis test was NOT found")
|
||||
sphere_bounce_y_found = ("Bounce Sphere for Y axis test was found", "Bounce Sphere for Y axis test was NOT found")
|
||||
sphere_bounce_z_found = ("Bounce Sphere for Z axis test was found", "Bounce Sphere for Z axis test was NOT found")
|
||||
|
||||
# ****** Entities' results ******
|
||||
# Force Regions
|
||||
force_region_x_mag_result = ("Force Region for X axis magnitude exerted was as expected", "Force Region for X axis magnitude exerted was NOT as expected")
|
||||
force_region_y_mag_result = ("Force Region for Y axis magnitude exerted was as expected", "Force Region for Y axis magnitude exerted was NOT as expected")
|
||||
force_region_z_mag_result = ("Force Region for Z axis magnitude exerted was as expected", "Force Region for Z axis magnitude exerted was NOT as expected")
|
||||
force_region_x_norm_result = ("Force Region for X axis normal exerted was as expected", "Force Region for X axis normal exerted was NOT as expected")
|
||||
force_region_y_norm_result = ("Force Region for Y axis normal exerted was as expected", "Force Region for Y axis normal exerted was NOT as expected")
|
||||
force_region_z_norm_result = ("Force Region for Z axis normal exerted was as expected", "Force Region for Z axis normal exerted was NOT as expected")
|
||||
# Force Region Pass Boxes
|
||||
force_region_pass_box_x_result = ("Force Region Pass Box for X axis collided with exactly one sphere", "Force Region Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_y_result = ("Force Region Pass Box for Y axis collided with exactly one sphere", "Force Region Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
force_region_pass_box_z_result = ("Force Region Pass Box for Z axis collided with exactly one sphere", "Force Region Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# External Pass Boxes
|
||||
external_pass_box_x_result = ("External Pass Box for X axis collided with exactly one sphere", "External Pass Box for X axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_y_result = ("External Pass Box for Y axis collided with exactly one sphere", "External Pass Box for Y axis DID NOT collide with exactly one sphere")
|
||||
external_pass_box_z_result = ("External Pass Box for Z axis collided with exactly one sphere", "External Pass Box for Z axis DID NOT collide with exactly one sphere")
|
||||
# Force Region Fail Boxes
|
||||
force_region_fail_box_x_result = ("Force Region Fail Box for X axis collided with no spheres", "Force Region Fail Box for X axis DID collide with a sphere")
|
||||
force_region_fail_box_y_result = ("Force Region Fail Box for Y axis collided with no spheres", "Force Region Fail Box for Y axis DID collide with a sphere")
|
||||
force_region_fail_box_z_result = ("Force Region Fail Box for Z axis collided with no spheres", "Force Region Fail Box for Z axis DID collide with a sphere")
|
||||
# External Fail Boxes
|
||||
external_fail_box_x_result = ("External Fail Box for X axis collided with no spheres", "External Fail Box for X axis DID collide with a sphere")
|
||||
external_fail_box_y_result = ("External Fail Box for Y axis collided with no spheres", "External Fail Box for Y axis DID collide with a sphere")
|
||||
external_fail_box_z_result = ("External Fail Box for Z axis collided with no spheres", "External Fail Box for Z axis DID collide with a sphere")
|
||||
# Pass spheres
|
||||
sphere_pass_x_result = ("Pass Sphere for X axis collided with expected Box", "Pass Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_pass_y_result = ("Pass Sphere for Y axis collided with expected Box", "Pass Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_pass_z_result = ("Pass Sphere for Z axis collided with expected Box", "Pass Sphere for Z axis DID NOT collide with expected Box")
|
||||
# Bounce Spheres
|
||||
sphere_bounce_x_result = ("Bounce Sphere for X axis collided with expected Box", "Bounce Sphere for X axis DID NOT collide with expected Box")
|
||||
sphere_bounce_y_result = ("Bounce Sphere for Y axis collided with expected Box", "Bounce Sphere for Y axis DID NOT collide with expected Box")
|
||||
sphere_bounce_z_result = ("Bounce Sphere for Z axis collided with expected Box", "Bounce Sphere for Z axis DID NOT collide with expected Box")
|
||||
# fmt:on
|
||||
|
||||
@staticmethod
|
||||
# Test tuple accessor via string
|
||||
def get_test(test_name):
|
||||
if test_name in Tests.__dict__:
|
||||
return Tests.__dict__[test_name]
|
||||
else:
|
||||
return None
|
||||
|
||||
def C5959808_ForceRegion_PositionOffset():
|
||||
"""
|
||||
Summary:
|
||||
Force Region positional offset is tested for each of the 3 axises (X, Y, and Z). Each axis's test has one
|
||||
ForceRegion, two spheres and four boxes. By monitoring which box each sphere collides with we can validate the
|
||||
integrity of the ForceRegions positional offset.
|
||||
|
||||
Level Description:
|
||||
Each axis's test has the following entities:
|
||||
one force region - set for point force and with it's collider set offset (on the axis in test).
|
||||
two spheres - one positioned near the transform of the force region, one positioned near the [offset] collider for
|
||||
the force region
|
||||
four boxes - One box is positioned inside the force region's transform, one inside the force region's [offset]
|
||||
collider. The other two boxes are positioned behind the two spheres (relative to the direction they will be
|
||||
initially traveling)
|
||||
|
||||
Expected Behavior:
|
||||
All three axises' tests run in parallel. when the tests begin, the spheres should move toward their expected
|
||||
force regions. The spheres positioned to collide with their region's [offset] collider should be forced backwards
|
||||
before entering the force region and collide with the box behind it. The spheres positioned by their force region's
|
||||
transforms should pass straight into the transform and collide with the box inside the transform.
|
||||
The boxes inside the Force Regions' [offset] colliders and the boxes behind the spheres set to move into the Force
|
||||
Regions' transforms should not register any collisions.
|
||||
|
||||
Steps:
|
||||
1) Open level and enter game mode
|
||||
2) Set up tests and variables
|
||||
3) Wait for test results (or time out)
|
||||
(Report results)
|
||||
4) Exit game mode and close the editor
|
||||
|
||||
:return: None
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
import ImportPathHelper as imports
|
||||
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
from utils import TestHelper as helper
|
||||
|
||||
import azlmbr.legacy.general as general
|
||||
import azlmbr.math as azmath
|
||||
import azlmbr.bus
|
||||
import azlmbr
|
||||
|
||||
# Constants
|
||||
CLOSE_ENOUGH = 0.01 # Close enough threshold for comparing floats
|
||||
TIME_OUT = 2.0 # Time out (in seconds) until test is aborted
|
||||
FORCE_MAGNITUDE = 1000.0 # Point force magnitude for Force Regions
|
||||
SPEED = 3.0 # Initial speed (in m/s) of the moving spheres.
|
||||
|
||||
# Full list for all spheres. Used for EntityId look up in event handlers
|
||||
all_spheres = []
|
||||
|
||||
# Entity base class handles very general entity initialization
|
||||
# Should be treated as a "virtual" class and all implementing child
|
||||
# classes should implement a "self.result()" function referenced in EntityBase::report(self)
|
||||
class EntityBase:
|
||||
def __init__(self, name):
|
||||
# type: (str) -> None
|
||||
self.name = name
|
||||
self.print_list = []
|
||||
self.id = general.find_game_entity(name)
|
||||
found_test = Tests.get_test(name + "_found")
|
||||
Report.critical_result(found_test, self.id.IsValid())
|
||||
|
||||
# Reports this entity's result. Implicitly calls "get" on result.
|
||||
# Subclasses implement their own definition of a successful result
|
||||
def report(self):
|
||||
# type: () -> None
|
||||
result_test = Tests.get_test(self.name + "_result")
|
||||
Report.result(result_test, self.result())
|
||||
|
||||
# Prints the print queue (with decorated header) if not empty
|
||||
def print_log(self):
|
||||
# type: () -> None
|
||||
if self.print_list:
|
||||
Report.info("*********** {} **********".format(self))
|
||||
for line in self.print_list:
|
||||
Report.info(line)
|
||||
Report.info("")
|
||||
|
||||
# Quick string cast, returns entity name
|
||||
def __str__(self):
|
||||
# type: () -> str
|
||||
return self.name
|
||||
|
||||
# ForceRegion handles all the data and behavior associated with a ForceRegion (for this test)
|
||||
# They simply wait for a Sphere to collide with them. On collision they store the calculated force
|
||||
# magnitude for verification.
|
||||
class ForceRegion(EntityBase):
|
||||
def __init__(self, name, magnitude):
|
||||
# type: (str, float) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.expected_magnitude = magnitude
|
||||
self.actual_magnitude = None
|
||||
self.expected_normal = None
|
||||
self.actual_normal = None
|
||||
# Set point force Magnitude
|
||||
azlmbr.physics.ForcePointRequestBus(azlmbr.bus.Event, "SetMagnitude", self.id, magnitude)
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.ForceRegionNotificationBusHandler()
|
||||
self.handler.connect(None)
|
||||
self.handler.add_callback("OnCalculateNetForce", self.on_calc_force)
|
||||
|
||||
# Callback function for OnCalculateNetForce event
|
||||
def on_calc_force(self, args):
|
||||
# type: ([EntityId, EntityId, azmath.Vector3, float]) -> None
|
||||
if self.id.Equal(args[0]) and self.actual_magnitude is None:
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[1]):
|
||||
# Log event in print queue (for me and for the sphere)
|
||||
self.print_list.append("Exerting force on {}:".format(sphere))
|
||||
sphere.print_list.append("Force exerted by {}".format(self))
|
||||
# Save calculated data to be compared later
|
||||
self.actual_normal = args[2]
|
||||
self.actual_magnitude = args[3]
|
||||
self.expected_normal = sphere.initial_velocity.GetNormalizedSafe().Unary()
|
||||
# Add expected/actual to print queue
|
||||
self.print_list.append("Force Vector: ")
|
||||
self.print_list.append(
|
||||
" Expected: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.expected_normal.x, self.expected_normal.y, self.expected_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append(
|
||||
" Actual: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
self.actual_normal.x, self.actual_normal.y, self.actual_normal.z
|
||||
)
|
||||
)
|
||||
self.print_list.append("Force Magnitude: ")
|
||||
self.print_list.append(" Expected: {}".format(self.expected_magnitude))
|
||||
self.print_list.append(" Actual: {:.2f}".format(self.actual_magnitude))
|
||||
|
||||
# EntityBase::report() overload.
|
||||
# Force regions have 2 test tuples to report on
|
||||
def report(self):
|
||||
magnitude_test = Tests.get_test(self.name + "_mag_result")
|
||||
normal_test = Tests.get_test(self.name + "_norm_result")
|
||||
Report.result(magnitude_test, self.magnitude_result())
|
||||
Report.result(normal_test, self.normal_result())
|
||||
|
||||
# Test result calculations
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
# type: () -> bool
|
||||
return self.magnitude_result() and self.normal_result()
|
||||
|
||||
def magnitude_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_magnitude is not None
|
||||
and abs(self.actual_magnitude - self.expected_magnitude) < CLOSE_ENOUGH
|
||||
)
|
||||
|
||||
def normal_result(self):
|
||||
# type: () -> bool
|
||||
return (
|
||||
self.actual_normal is not None
|
||||
and self.expected_normal is not None
|
||||
and self.expected_normal.IsClose(self.actual_normal, CLOSE_ENOUGH)
|
||||
)
|
||||
|
||||
# Spheres are the objects that test the force regions. They store an expected collision entity and an
|
||||
# actual collision entity
|
||||
class Sphere(EntityBase):
|
||||
def __init__(self, name, initial_velocity, expected_collision):
|
||||
# type: (str, azmath.Vector3, EntityBase, bool) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.initial_velocity = initial_velocity
|
||||
azlmbr.physics.RigidBodyRequestBus(azlmbr.bus.Event, "SetLinearVelocity", self.id, initial_velocity)
|
||||
self.print_list.append(
|
||||
"Initial velocity: ({:.2f}, {:.2f}, {:.2f})".format(
|
||||
initial_velocity.x, initial_velocity.y, initial_velocity.z
|
||||
)
|
||||
)
|
||||
self.expected_collision = expected_collision
|
||||
self.print_list.append("Expected Collision: {}".format(expected_collision))
|
||||
self.actual_collision = None
|
||||
self.active = True
|
||||
self.force_normal = None
|
||||
|
||||
# Registers a collision with this sphere. Saves a reference to the colliding entity for processing later.
|
||||
# Deactivate self after collision is registered.
|
||||
def collide(self, collision_entity):
|
||||
# type: (EntityBase) -> None
|
||||
# Log the event
|
||||
self.print_list.append("Collided with {}".format(collision_entity))
|
||||
self.actual_collision = collision_entity
|
||||
# Deactivate self
|
||||
azlmbr.entity.GameEntityContextRequestBus(azlmbr.bus.Broadcast, "DeactivateGameEntity", self.id)
|
||||
self.active = False
|
||||
|
||||
# Calculates result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
if self.actual_collision is None:
|
||||
return False
|
||||
else:
|
||||
return self.expected_collision.id.Equal(self.actual_collision.id)
|
||||
|
||||
# Box entities wait for a collision with a sphere as a means of validation the force region's offset
|
||||
# worked according to plan.
|
||||
class Box(EntityBase):
|
||||
def __init__(self, name, expected_sphere_collisions):
|
||||
# type: (str, int) -> None
|
||||
EntityBase.__init__(self, name)
|
||||
self.spheres_collided = 0
|
||||
self.expected_sphere_collisions = expected_sphere_collisions
|
||||
# Set up handler
|
||||
self.handler = azlmbr.physics.CollisionNotificationBusHandler()
|
||||
self.handler.connect(self.id)
|
||||
self.handler.add_callback("OnCollisionBegin", self.on_collision_begin)
|
||||
|
||||
# Callback function for OnCollisionBegin event
|
||||
def on_collision_begin(self, args):
|
||||
for sphere in all_spheres:
|
||||
if sphere.id.Equal(args[0]):
|
||||
# Log event
|
||||
self.print_list.append("Collided with {}".format(sphere))
|
||||
# Register collision with sphere
|
||||
sphere.collide(self)
|
||||
self.spheres_collided += 1 # Count collisions for validation later
|
||||
break
|
||||
|
||||
# Calculates test result
|
||||
# Used in EntityBase for reporting results
|
||||
def result(self):
|
||||
return self.spheres_collided == self.expected_sphere_collisions
|
||||
|
||||
# Manages the entities required to run the test for one axis (X, Y, or Z)
|
||||
class AxisTest:
|
||||
def __init__(self, axis, init_velocity):
|
||||
# type: (str, azmath.Vector3) -> None
|
||||
self.name = axis + " axis test"
|
||||
self.force_region = ForceRegion("force_region_" + axis, FORCE_MAGNITUDE)
|
||||
self.spheres = [
|
||||
Sphere("sphere_pass_" + axis, init_velocity, Box("force_region_pass_box_" + axis, 1)),
|
||||
Sphere("sphere_bounce_" + axis, init_velocity, Box("external_pass_box_" + axis, 1)),
|
||||
]
|
||||
self.boxes = [
|
||||
Box("external_fail_box_" + axis, 0),
|
||||
Box("force_region_fail_box_" + axis, 0)
|
||||
] + [
|
||||
# Gets the Boxes passed to spheres on init
|
||||
sphere.expected_collision for sphere in self.spheres
|
||||
]
|
||||
# Full list for all entities this test is responsible for
|
||||
self.all_entities = self.boxes + self.spheres + [self.force_region]
|
||||
# Add spheres to global "lookup" list
|
||||
all_spheres.extend(self.spheres)
|
||||
|
||||
# Checks for all entities' test passing conditions
|
||||
def passed(self):
|
||||
return all([e.result() for e in self.all_entities])
|
||||
|
||||
# Returns true when this test has completed (i.e. when the spheres have collided and are deactivated)
|
||||
def completed(self):
|
||||
return all([not sphere.active for sphere in self.spheres])
|
||||
|
||||
# Reports results for all entities in this test
|
||||
def report(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Results :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.report()
|
||||
|
||||
# Prints the logs for all entities in this test
|
||||
def print_log(self):
|
||||
Report.info("::::::::::::::::::::::::::::: {} Log :::::::::::::::::::::::::::::".format(self.name))
|
||||
for entity in self.all_entities:
|
||||
entity.print_log()
|
||||
|
||||
# *********** Execution Code ***********
|
||||
|
||||
# 1) Open level
|
||||
helper.init_idle()
|
||||
helper.open_level("Physics", "C5959808_ForceRegion_PositionOffset")
|
||||
helper.enter_game_mode(Tests.enter_game_mode)
|
||||
|
||||
# 2) Variable set up
|
||||
# Initial velocities for the three different directions spheres will be moving
|
||||
x_vel = azmath.Vector3(SPEED, 0.0, 0.0)
|
||||
y_vel = azmath.Vector3(0.0, SPEED, 0.0)
|
||||
z_vel = azmath.Vector3(0.0, 0.0, SPEED)
|
||||
|
||||
# The three tests, one for each axis
|
||||
axis_tests = [
|
||||
AxisTest("x", z_vel), # Spheres move in Z direction when testing X axis offset
|
||||
AxisTest("y", x_vel), # Spheres move in X direction when testing Y axis offset
|
||||
AxisTest("z", y_vel), # Spheres move in Y direction when testing Z axis offset
|
||||
]
|
||||
|
||||
# 3) Wait for test results or time out
|
||||
Report.result(
|
||||
Tests.test_completed, helper.wait_for_condition(
|
||||
lambda: all([test.completed() for test in axis_tests]), TIME_OUT
|
||||
)
|
||||
)
|
||||
|
||||
# Report results
|
||||
for test in axis_tests:
|
||||
test.report()
|
||||
|
||||
# Print entity print queues for each failed test
|
||||
for test in axis_tests:
|
||||
if not test.passed():
|
||||
test.print_log()
|
||||
|
||||
# 4) Exit game mode and close editor
|
||||
helper.exit_game_mode(Tests.exit_game_mode)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import ImportPathHelper as imports
|
||||
imports.init()
|
||||
|
||||
from utils import Report
|
||||
Report.start_test(C5959808_ForceRegion_PositionOffset)
|
||||
Reference in New Issue
Block a user