MaterialEditor BasicTests added to AutomatedTesting for AR (#3022)

* MaterialEditor BasicTests added to AutomatedTesting for AR

Signed-off-by: Scott Murray <scottmur@amazon.com>

* launch_and_validate_results adding a waiter.wait_for to the log monitor so the log file exists

Signed-off-by: Scott Murray <scottmur@amazon.com>
This commit is contained in:
smurly
2021-08-11 12:15:31 -07:00
committed by GitHub
parent 6d1bb8e439
commit 2564e8f8dc
5 changed files with 552 additions and 2 deletions
@@ -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.materialeditor as materialeditor
import azlmbr.bus as bus
import azlmbr.atomtools.general as general
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 materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "OpenDocument", file_path)
def is_open(document_id):
"""
:return: bool
"""
return materialeditor.MaterialDocumentRequestBus(bus.Event, "IsOpen", document_id)
def save_document(document_id):
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "SaveDocument", document_id)
def save_document_as_copy(document_id, target_path):
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(
bus.Broadcast, "SaveDocumentAsCopy", document_id, target_path
)
def save_document_as_child(document_id, target_path):
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(
bus.Broadcast, "SaveDocumentAsChild", document_id, target_path
)
def save_all():
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "SaveAllDocuments")
def close_document(document_id):
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "CloseDocument", document_id)
def close_all_documents():
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(bus.Broadcast, "CloseAllDocuments")
def close_all_except_selected(document_id):
"""
:return: bool success
"""
return materialeditor.MaterialDocumentSystemRequestBus(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 materialeditor.MaterialDocumentRequestBus(bus.Event, "GetPropertyValue", document_id, property_name)
def set_property(document_id, property_name, value):
materialeditor.MaterialDocumentRequestBus(bus.Event, "SetPropertyValue", document_id, property_name, value)
def is_pane_visible(pane_name):
"""
:return: bool
"""
return materialeditor.MaterialEditorWindowRequestBus(bus.Broadcast, "IsDockWidgetVisible", pane_name)
def set_pane_visibility(pane_name, value):
materialeditor.MaterialEditorWindowRequestBus(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:
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)
)