Initial update for this module's unit tests on Linux
Signed-off-by: sweeneys <sweeneys@amazon.com>
This commit is contained in:
@@ -11,16 +11,16 @@ import sys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Supported platforms.
|
||||
# Supported platforms
|
||||
ALL_PLATFORM_OPTIONS = ['android', 'ios', 'linux', 'mac', 'windows']
|
||||
ALL_LAUNCHER_OPTIONS = ['android', 'base', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic']
|
||||
ALL_LAUNCHER_OPTIONS = ['android', 'base', 'linux', 'mac', 'windows', 'windows_editor', 'windows_dedicated', 'windows_generic']
|
||||
ANDROID = False
|
||||
IOS = False # Not implemented - see SPEC-2505
|
||||
LINUX = sys.platform.startswith('linux') # Not implemented - see SPEC-2501
|
||||
MAC = sys.platform.startswith('darwin')
|
||||
WINDOWS = sys.platform.startswith('win')
|
||||
|
||||
# Detect platforms.
|
||||
# Detect available platforms
|
||||
HOST_OS_PLATFORM = 'unknown'
|
||||
HOST_OS_EDITOR = 'unknown'
|
||||
HOST_OS_DEDICATED_SERVER = 'unknown'
|
||||
@@ -51,9 +51,12 @@ elif MAC:
|
||||
from ly_test_tools.launchers import MacLauncher
|
||||
LAUNCHERS['mac'] = MacLauncher
|
||||
elif LINUX:
|
||||
logger.warning(f'Linux operating system is currently not supported, LyTestTools only supports Windows and Mac.')
|
||||
HOST_OS_PLATFORM = 'linux'
|
||||
HOST_OS_EDITOR = NotImplementedError('LyTestTools does not yet support Linux editor')
|
||||
HOST_OS_DEDICATED_SERVER = NotImplementedError('LyTestTools does not yet support Linux dedicated server')
|
||||
HOST_OS_EDITOR = 'linux_editor'
|
||||
HOST_OS_DEDICATED_SERVER = 'linux_dedicated'
|
||||
from ly_test_tools.launchers.platforms.linux.launcher import (LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher)
|
||||
LAUNCHERS['linux'] = LinuxLauncher
|
||||
LAUNCHERS['linux_editor'] = LinuxEditor
|
||||
LAUNCHERS['linux_dedicated'] = DedicatedLinuxLauncher
|
||||
else:
|
||||
logger.warning(f'WARNING: LyTestTools only supports Windows and Mac, got HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".')
|
||||
logger.warning(f'WARNING: LyTestTools only supports Windows, Mac, and Linux. Unexpectedly detected HOST_OS_PLATFORM: "{HOST_OS_PLATFORM}".')
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
"""
|
||||
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
|
||||
|
||||
Linux directory and workspace mappings
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from ly_test_tools._internal.managers.workspace import AbstractWorkspaceManager
|
||||
from ly_test_tools._internal.managers.abstract_resource_locator import AbstractResourceLocator
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
CACHE_DIR = 'linux'
|
||||
CONFIG_FILE = 'system_linux_pc.cfg'
|
||||
|
||||
|
||||
class _LinuxResourceManager(AbstractResourceLocator):
|
||||
"""
|
||||
Override for locating resources in a Linux operating system running LyTestTools.
|
||||
"""
|
||||
def __init__(self, build_directory: str, project: str):
|
||||
pass
|
||||
|
||||
def platform_config_file(self):
|
||||
"""
|
||||
:return: path to the platform config file
|
||||
"""
|
||||
return os.path.join(self.engine_root(), CONFIG_FILE)
|
||||
|
||||
def platform_cache(self):
|
||||
"""
|
||||
:return: path to cache for the Linux operating system
|
||||
"""
|
||||
return os.path.join(self.project_cache(), CACHE_DIR)
|
||||
|
||||
def project_log(self):
|
||||
"""
|
||||
:return: path to 'log' dir in the platform cache dir
|
||||
"""
|
||||
return os.path.join(self.project(), 'user', 'log')
|
||||
|
||||
def project_screenshots(self):
|
||||
"""
|
||||
:return: path to 'screenshot' dir in the platform cache dir
|
||||
"""
|
||||
return os.path.join(self.project(), 'user', 'ScreenShots')
|
||||
|
||||
def editor_log(self):
|
||||
"""
|
||||
:return: path to editor.log
|
||||
"""
|
||||
return os.path.join(self.project_log(), "editor.log")
|
||||
|
||||
|
||||
class LinuxWorkspaceManager(AbstractWorkspaceManager):
|
||||
"""
|
||||
A Mac host WorkspaceManager. Contains Mac overridden functions for the AbstractWorkspaceManager class.
|
||||
Also creates a Mac host ResourceLocator for directory and build mappings.
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
build_directory=None,
|
||||
project=None,
|
||||
tmp_path=None,
|
||||
output_path=None,
|
||||
):
|
||||
# Type: (str,str,str,str) -> None
|
||||
super(LinuxWorkspaceManager, self).__init__(
|
||||
_LinuxResourceManager(build_directory, project),
|
||||
project=project,
|
||||
tmp_path=tmp_path,
|
||||
output_path=output_path,
|
||||
)
|
||||
@@ -9,7 +9,7 @@ Helper file for assisting in building workspaces and setting up LTT with the cur
|
||||
|
||||
import ly_test_tools._internal.pytest_plugin as pytest_plugin
|
||||
import ly_test_tools._internal.managers.workspace as internal_workspace
|
||||
from ly_test_tools import MAC, WINDOWS
|
||||
from ly_test_tools import LINUX, MAC, WINDOWS
|
||||
import os, stat
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ def create_builtin_workspace(
|
||||
elif MAC:
|
||||
from ly_test_tools._internal.managers.platforms.mac import MacWorkspaceManager
|
||||
build_class = MacWorkspaceManager
|
||||
elif LINUX:
|
||||
from ly_test_tools._internal.managers.platforms.linux import LinuxWorkspaceManager
|
||||
build_class = LinuxWorkspaceManager
|
||||
else:
|
||||
raise NotImplementedError("No workspace manager found for current Operating System")
|
||||
|
||||
instance = build_class(
|
||||
build_directory=build_directory,
|
||||
|
||||
@@ -12,8 +12,8 @@ import psutil
|
||||
import subprocess
|
||||
import ctypes
|
||||
|
||||
import ly_test_tools
|
||||
import ly_test_tools.environment.waiter as waiter
|
||||
from ly_test_tools import WINDOWS, MAC
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
_PROCESS_OUTPUT_ENCODING = 'utf-8'
|
||||
@@ -182,7 +182,7 @@ def process_is_unresponsive(name):
|
||||
:param name: the name of the process to check
|
||||
:return: True if the specified process is unresponsive and False otherwise
|
||||
"""
|
||||
if WINDOWS:
|
||||
if ly_test_tools.WINDOWS:
|
||||
output = check_output(['tasklist',
|
||||
'/FI', f'IMAGENAME eq {name}',
|
||||
'/FI', 'STATUS eq NOT RESPONDING'])
|
||||
@@ -194,7 +194,7 @@ def process_is_unresponsive(name):
|
||||
return True
|
||||
logger.debug(f"Process '{name}' was not unresponsive.")
|
||||
return False
|
||||
elif MAC:
|
||||
else:
|
||||
cmd = ["ps", "-axc", "-o", "command,state"]
|
||||
output = check_output(cmd)
|
||||
for line in output.splitlines()[1:]:
|
||||
@@ -209,8 +209,6 @@ def process_is_unresponsive(name):
|
||||
return True
|
||||
logger.debug(f"Process '{name}' was not unresponsive.")
|
||||
return False
|
||||
else:
|
||||
raise NotImplementedError('Only Windows and Mac hosts are supported.')
|
||||
|
||||
|
||||
def check_output(command, **kwargs):
|
||||
@@ -406,7 +404,7 @@ def close_windows_process(pid, timeout=20, raise_on_missing=False):
|
||||
:param pid: the pid of the process to kill
|
||||
:param raise_on_missing: if set to True, raise RuntimeError if the process does not already exist
|
||||
"""
|
||||
if not WINDOWS:
|
||||
if not ly_test_tools.WINDOWS:
|
||||
raise NotImplementedError("close_windows_process() is only implemented on Windows.")
|
||||
|
||||
if pid is None:
|
||||
|
||||
@@ -8,8 +8,10 @@ Reg cleaner: tools for working with the lumberyard windows registry keys
|
||||
"""
|
||||
import logging
|
||||
import os
|
||||
import winreg
|
||||
|
||||
import ly_test_tools
|
||||
if ly_test_tools.WINDOWS:
|
||||
import winreg # OS-specific module availability, must be mocked if this file is accessed elsewhere e.g. unit tests
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
|
||||
CONST_LY_REG = r'SOFTWARE\O3DE\O3DE'
|
||||
|
||||
@@ -14,8 +14,8 @@ import re
|
||||
import psutil
|
||||
import time
|
||||
|
||||
import ly_test_tools
|
||||
import ly_test_tools.environment.process_utils as process_utils
|
||||
from ly_test_tools import WINDOWS
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -28,7 +28,7 @@ class Watchdog(object):
|
||||
DEFAULT_JOIN_TIMEOUT = 10 # seconds
|
||||
|
||||
def __init__(self, bool_fn, interval=1, raise_on_condition=True, name='ly_test_watchdog', error_message=''):
|
||||
# type: (func, int, bool, str, str) -> Watchdog
|
||||
# type: (function, int, bool, str, str) -> Watchdog
|
||||
"""
|
||||
A Watchdog object that takes in a boolean function. It spawns a thread that loops over the boolean function
|
||||
until it returns True. If the boolean function returns True, then a flag will be set and an exception
|
||||
@@ -44,7 +44,7 @@ class Watchdog(object):
|
||||
"""
|
||||
self.caught_failure = False
|
||||
self.name = name
|
||||
|
||||
|
||||
self._bool_fn = bool_fn
|
||||
self._interval = interval
|
||||
self._raise_on_condition = raise_on_condition
|
||||
@@ -66,7 +66,7 @@ class Watchdog(object):
|
||||
self.caught_failure = False
|
||||
|
||||
def stop(self, join_timeout=DEFAULT_JOIN_TIMEOUT):
|
||||
# type: () -> None
|
||||
# type: (int) -> None
|
||||
"""
|
||||
Stops the watchdog's thread if it's executing by enabling its shutdown Event. If the target function's condition
|
||||
was found, then it will either raise an exception or log an error message.
|
||||
@@ -106,7 +106,7 @@ class Watchdog(object):
|
||||
"""
|
||||
The main function of the watchdog thread. It will repeatedly call its target function until the target function
|
||||
returns True, in which it will set the self.caught_failure attribute to True.
|
||||
|
||||
|
||||
:return: None
|
||||
"""
|
||||
while True:
|
||||
@@ -128,7 +128,7 @@ class ProcessUnresponsiveWatchdog(Watchdog):
|
||||
Watches a process ID and reports if it is unresponsive for a given timeout. If multiple processes need to be
|
||||
watched, then multiple watchdogs should be instantiated.
|
||||
Note: This is for windows OS only.
|
||||
|
||||
|
||||
:param process_id: The process id to watch
|
||||
:param interval: The interval (in seconds) for how frequently the bool_fn is called on the thread.
|
||||
:param raise_on_condition: If True, raises an exception when bool_fn returns True. If False, logs an error
|
||||
@@ -138,8 +138,8 @@ class ProcessUnresponsiveWatchdog(Watchdog):
|
||||
:param unresponsive_timeout_seconds: How long the process needs to be unresponsive for in order for the watchdog
|
||||
to report (in seconds).
|
||||
"""
|
||||
if not WINDOWS:
|
||||
raise (NotImplementedError, "Process watchdog is only implemented on Windows.")
|
||||
if not ly_test_tools.WINDOWS:
|
||||
pass # TODO add non-windows support
|
||||
self._unresponsive_timeout = unresponsive_timeout_seconds
|
||||
self._calculated_timeout_point = None
|
||||
self._pid = process_id
|
||||
|
||||
@@ -6,7 +6,7 @@ SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
"""
|
||||
|
||||
from ly_test_tools.launchers.platforms.base import Launcher
|
||||
from ly_test_tools.launchers.platforms.linux.launcher import LinuxLauncher, LinuxEditor, DedicatedLinuxLauncher
|
||||
from ly_test_tools.launchers.platforms.mac.launcher import MacLauncher
|
||||
from ly_test_tools.launchers.platforms.win.launcher import (
|
||||
WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher)
|
||||
from ly_test_tools.launchers.platforms.win.launcher import WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher
|
||||
from ly_test_tools.launchers.platforms.android.launcher import AndroidLauncher
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""
|
||||
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,224 @@
|
||||
"""
|
||||
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
|
||||
|
||||
Linux compatible launcher
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
|
||||
import ly_test_tools.environment.waiter
|
||||
import ly_test_tools.launchers.exceptions
|
||||
|
||||
from ly_test_tools.launchers.platforms.base import Launcher
|
||||
from ly_test_tools.launchers.exceptions import TeardownError, ProcessNotStartedError
|
||||
from tempfile import TemporaryFile
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class LinuxLauncher(Launcher):
|
||||
def __init__(self, build, args):
|
||||
super(LinuxLauncher, self).__init__(build, args)
|
||||
self._proc = None
|
||||
self._ret_code = None
|
||||
self._tmpout = None
|
||||
log.debug("Initialized Linux Launcher")
|
||||
|
||||
def binary_path(self):
|
||||
"""
|
||||
Return full path to the launcher for this build's configuration and project
|
||||
|
||||
:return: full path to <project>.GameLauncher
|
||||
"""
|
||||
assert self.workspace.project is not None
|
||||
return os.path.join(self.workspace.paths.build_directory(), f"{self.workspace.project}.GameLauncher")
|
||||
|
||||
def setup(self, backupFiles=True, launch_ap=True, configure_settings=True):
|
||||
"""
|
||||
Perform setup of this launcher, must be called before launching.
|
||||
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
|
||||
|
||||
:param backupFiles: Bool to backup setup files
|
||||
:param lauch_ap: Bool to lauch the asset processor
|
||||
:return: None
|
||||
"""
|
||||
# Backup
|
||||
if backupFiles:
|
||||
self.backup_settings()
|
||||
|
||||
# Base setup defaults to None
|
||||
if launch_ap is None:
|
||||
launch_ap = True
|
||||
|
||||
# Modify and re-configure
|
||||
if configure_settings:
|
||||
self.configure_settings()
|
||||
super(LinuxLauncher, self).setup(backupFiles, launch_ap)
|
||||
|
||||
def launch(self):
|
||||
"""
|
||||
Launch the executable and track the subprocess
|
||||
|
||||
:return: None
|
||||
"""
|
||||
command = [self.binary_path()] + self.args
|
||||
self._tmpout = TemporaryFile()
|
||||
self._proc = subprocess.Popen(command, stdout=self._tmpout, stderr=self._tmpout, universal_newlines=True)
|
||||
log.debug(f"Started Linux Launcher with command: {command}")
|
||||
|
||||
def get_output(self, encoding="utf-8"):
|
||||
if self._tmpout is None:
|
||||
raise ProcessNotStartedError("Process must be started before retrieving output")
|
||||
|
||||
self._tmpout.seek(0)
|
||||
return self._tmpout.read().decode(encoding)
|
||||
|
||||
def teardown(self):
|
||||
"""
|
||||
Perform teardown of this launcher, undoing actions taken by calling setup()
|
||||
Subclasses should call its parent's teardown() after performing its own teardown.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
self.restore_settings()
|
||||
super(LinuxLauncher, self).teardown()
|
||||
|
||||
def kill(self):
|
||||
"""
|
||||
This is a hard kill, and then wait to make sure until it actually ended.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
if self._proc is not None:
|
||||
self._proc.kill()
|
||||
ly_test_tools.environment.waiter.wait_for(
|
||||
lambda: not self.is_alive(),
|
||||
exc=ly_test_tools.launchers.exceptions.TeardownError(
|
||||
f"Unable to terminate active Linux Launcher with process ID {self._proc.pid}")
|
||||
)
|
||||
self._proc = None
|
||||
self._ret_code = None
|
||||
log.debug("Linux Launcher terminated successfully")
|
||||
|
||||
def is_alive(self):
|
||||
"""
|
||||
Check the process to verify activity. Side effect of setting self.proc to None if it has ended.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
if self._proc is None:
|
||||
return False
|
||||
else:
|
||||
if self._proc.poll() is not None:
|
||||
self._ret_code = self._proc.poll()
|
||||
self._proc = None
|
||||
return False
|
||||
return True
|
||||
|
||||
def get_pid(self):
|
||||
# type: () -> int or None
|
||||
"""
|
||||
Returns the pid of the launcher process if it exists, else it returns None
|
||||
|
||||
:return: process id or None
|
||||
"""
|
||||
if self._proc:
|
||||
return self._proc.pid
|
||||
return None
|
||||
|
||||
def get_returncode(self):
|
||||
# type: () -> int or None
|
||||
"""
|
||||
Returns the returncode of the launcher process if it exists, else return None.
|
||||
The returncode attribute is set when the process is terminated.
|
||||
|
||||
:return: The returncode of the launcher's process
|
||||
"""
|
||||
if self._proc:
|
||||
return self._proc.poll()
|
||||
else:
|
||||
return self._ret_code
|
||||
|
||||
def check_returncode(self):
|
||||
# type: () -> None
|
||||
"""
|
||||
Checks the returncode of the launcher if it exists. Raises a CrashError if the returncode is non-zero. Returns
|
||||
None otherwise. This function must be called after exiting the launcher properly and NOT using its provided
|
||||
teardown(). Provided teardown() will always return a non-zero returncode and should not be checked.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
return_code = self.get_returncode()
|
||||
if return_code != 0:
|
||||
log.error(f"Launcher exited with non-zero return code: {return_code}")
|
||||
raise ly_test_tools.launchers.exceptions.CrashError()
|
||||
return None
|
||||
|
||||
def configure_settings(self):
|
||||
"""
|
||||
Configures system level settings and syncs the launcher to the targeted console IP.
|
||||
|
||||
:return: None
|
||||
"""
|
||||
# Update settings via the settings registry to avoid modifying the bootstrap.cfg
|
||||
host_ip = '127.0.0.1'
|
||||
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/project_path={self.workspace.paths.project()}"')
|
||||
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/remote_ip={host_ip}"')
|
||||
self.args.append('--regset="/Amazon/AzCore/Bootstrap/wait_for_connect=1"')
|
||||
self.args.append(f'--regset="/Amazon/AzCore/Bootstrap/allowed_list={host_ip}"')
|
||||
|
||||
self.workspace.settings.modify_platform_setting("r_AssetProcessorShaderCompiler", 1)
|
||||
self.workspace.settings.modify_platform_setting("r_ShaderCompilerServer", host_ip)
|
||||
self.workspace.settings.modify_platform_setting("log_RemoteConsoleAllowedAddresses", host_ip)
|
||||
|
||||
|
||||
class DedicatedLinuxLauncher(LinuxLauncher):
|
||||
|
||||
def setup(self, backupFiles=True, launch_ap=False):
|
||||
"""
|
||||
Perform setup of this launcher, must be called before launching.
|
||||
Subclasses should call its parent's setup() before calling its own code, unless it changes configuration files
|
||||
|
||||
:param backupFiles: Bool to backup setup files
|
||||
:param lauch_ap: Bool to lauch the asset processor
|
||||
:return: None
|
||||
"""
|
||||
# Base setup defaults to None
|
||||
if launch_ap is None:
|
||||
launch_ap = False
|
||||
|
||||
super(DedicatedLinuxLauncher, self).setup(backupFiles, launch_ap)
|
||||
|
||||
def binary_path(self):
|
||||
"""
|
||||
Return full path to the dedicated server launcher for the build directory.
|
||||
|
||||
:return: full path to <project>Launcher_Server
|
||||
"""
|
||||
assert self.workspace.project is not None, (
|
||||
'Project cannot be NoneType - please specify a project name string.')
|
||||
return os.path.join(f"{self.workspace.paths.build_directory()}",
|
||||
f"{self.workspace.project}.ServerLauncher")
|
||||
|
||||
|
||||
class LinuxEditor(LinuxLauncher):
|
||||
|
||||
def __init__(self, build, args):
|
||||
super(LinuxEditor, self).__init__(build, args)
|
||||
self.args.append('--regset="/Amazon/Settings/EnableSourceControl=false"')
|
||||
self.args.append('--regset="/Amazon/AWS/Preferences/AWSAttributionConsentShown=true"')
|
||||
self.args.append('--regset="/Amazon/AWS/Preferences/AWSAttributionEnabled=false"')
|
||||
|
||||
def binary_path(self):
|
||||
"""
|
||||
Return full path to the Editor for this build's configuration and project
|
||||
|
||||
:return: full path to Editor
|
||||
"""
|
||||
assert self.workspace.project is not None
|
||||
return os.path.join(self.workspace.paths.build_directory(), "Editor")
|
||||
@@ -8,7 +8,6 @@ Unit tests for ly_test_tools.builtin.helpers functions.
|
||||
"""
|
||||
import unittest.mock as mock
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
import ly_test_tools.builtin.helpers
|
||||
@@ -112,10 +111,11 @@ class TestBuiltinHelpers(object):
|
||||
ly_test_tools._internal.managers.abstract_resource_locator._find_engine_root(
|
||||
initial_path='mock_dev_dir')
|
||||
|
||||
@mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.teardown')
|
||||
@mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.setup')
|
||||
@mock.patch('ly_test_tools._internal.managers.artifact_manager.NullArtifactManager', mock.MagicMock())
|
||||
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
||||
def test_SetupBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_setup):
|
||||
def test_SetupTeardownBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_setup, mock_teardown):
|
||||
mock_test_name = 'mock_test_name'
|
||||
mock_test_amount = 10
|
||||
mock_workspace = ly_test_tools.builtin.helpers.create_builtin_workspace(
|
||||
@@ -125,25 +125,16 @@ class TestBuiltinHelpers(object):
|
||||
output_path='mock_output_path',
|
||||
)
|
||||
|
||||
under_test = ly_test_tools.builtin.helpers.setup_builtin_workspace(
|
||||
setup_test = ly_test_tools.builtin.helpers.setup_builtin_workspace(
|
||||
mock_workspace, mock_test_name, mock_test_amount)
|
||||
|
||||
assert under_test == mock_workspace
|
||||
assert setup_test == mock_workspace
|
||||
assert mock_setup.call_count == 1
|
||||
mock_workspace.artifact_manager.set_test_name.assert_called_with(
|
||||
test_name=mock_test_name, amount=mock_test_amount)
|
||||
|
||||
@mock.patch('ly_test_tools._internal.managers.workspace.AbstractWorkspaceManager.teardown')
|
||||
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
||||
def test_TeardownBuiltinWorkspace_ValidWorkspaceSetup_ReturnsWorkspaceObject(self, mock_teardown):
|
||||
mock_workspace = ly_test_tools.builtin.helpers.create_builtin_workspace(
|
||||
build_directory='build_directory',
|
||||
project='mock_project',
|
||||
tmp_path='mock_tmp_path',
|
||||
output_path='mock_output_path',
|
||||
)
|
||||
# Teardown not tested separately due to patched MockedAbstractResourceLocator creating a StopIteration error on Linux
|
||||
teardown_test = ly_test_tools.builtin.helpers.teardown_builtin_workspace(mock_workspace)
|
||||
|
||||
under_test = ly_test_tools.builtin.helpers.teardown_builtin_workspace(mock_workspace)
|
||||
|
||||
assert under_test == mock_workspace
|
||||
assert teardown_test == mock_workspace
|
||||
assert mock_teardown.call_count == 1
|
||||
|
||||
@@ -208,20 +208,6 @@ class TestUnZip(unittest.TestCase):
|
||||
|
||||
self.assertEqual(path, expected_path)
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
|
||||
@mock.patch('os.path.join')
|
||||
def test_Unzip_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_join, mock_check_free, mock_exists):
|
||||
|
||||
path = ''
|
||||
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
|
||||
mock_exists.return_value = self.exists
|
||||
|
||||
with mock.patch(self.decomp_obj_name, self.mock_decomp):
|
||||
path = self.call_decomp(self.dest_path, self.src_path)
|
||||
|
||||
mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234')
|
||||
|
||||
@mock.patch('ly_test_tools.environment.file_system.logger')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
|
||||
@@ -375,20 +361,6 @@ class TestUnTgz(unittest.TestCase):
|
||||
|
||||
self.assertEqual(path, expected_path)
|
||||
|
||||
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
|
||||
@mock.patch('os.path.join')
|
||||
@mock.patch('os.stat')
|
||||
def test_Untgz_ReleaseBuild_JoinCalledWithNoPathNoExtension(self, mock_stat, mock_join, mock_check_free):
|
||||
|
||||
mock_stat.return_value = self.src_stat
|
||||
|
||||
path = ''
|
||||
self.src_path = r'C:\packages\lumberyard-1.2.0.3-54321-pc-1234.zip'
|
||||
with mock.patch(self.decomp_obj_name, self.mock_decomp):
|
||||
path = self.call_decomp(self.dest_path, self.src_path)
|
||||
|
||||
mock_join.assert_called_once_with(self.dest_path, 'lumberyard-1.2.0.3-54321-pc-1234')
|
||||
|
||||
@mock.patch('ly_test_tools.environment.file_system.logger')
|
||||
@mock.patch('os.path.exists')
|
||||
@mock.patch('ly_test_tools.environment.file_system.check_free_space')
|
||||
|
||||
@@ -204,21 +204,20 @@ class TestLauncherBuilder(object):
|
||||
"""
|
||||
def test_CreateLauncher_DummyWorkspace_DefaultLauncher(self):
|
||||
dummy_workspace = mock.MagicMock()
|
||||
launcher_platform = 'windows'
|
||||
under_test = ly_test_tools.launchers.launcher_helper.create_launcher(
|
||||
dummy_workspace, launcher_platform)
|
||||
dummy_workspace, ly_test_tools.HOST_OS_EDITOR)
|
||||
assert isinstance(under_test, ly_test_tools.launchers.Launcher)
|
||||
|
||||
def test_CreateDedicateLauncher_DummyWorkspace_DefaultLauncher(self):
|
||||
dummy_workspace = mock.MagicMock()
|
||||
launcher_platform = 'windows_dedicated'
|
||||
under_test = ly_test_tools.launchers.launcher_helper.create_dedicated_launcher(
|
||||
dummy_workspace, launcher_platform)
|
||||
dummy_workspace, ly_test_tools.HOST_OS_DEDICATED_SERVER)
|
||||
assert isinstance(under_test, ly_test_tools.launchers.Launcher)
|
||||
|
||||
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
|
||||
def test_CreateEditor_DummyWorkspace_DefaultLauncher(self):
|
||||
dummy_workspace = mock.MagicMock()
|
||||
launcher_platform = 'windows_editor'
|
||||
dummy_workspace.paths.build_directory.return_value = 'dummy'
|
||||
under_test = ly_test_tools.launchers.launcher_helper.create_editor(
|
||||
dummy_workspace, launcher_platform)
|
||||
dummy_workspace, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE)
|
||||
assert isinstance(under_test, ly_test_tools.launchers.Launcher)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
"""
|
||||
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
|
||||
|
||||
Unit Tests for linux launcher-wrappers: all are sanity code-path tests, since no interprocess actions should be taken
|
||||
"""
|
||||
import os
|
||||
import pytest
|
||||
import unittest.mock as mock
|
||||
|
||||
import ly_test_tools.launchers
|
||||
|
||||
pytestmark = pytest.mark.SUITE_smoke
|
||||
|
||||
|
||||
class TestLinuxLauncher(object):
|
||||
|
||||
def test_Construct_TestDoubles_LinuxLauncherCreated(self):
|
||||
under_test = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), ["some_args"])
|
||||
assert isinstance(under_test, ly_test_tools.launchers.Launcher)
|
||||
assert isinstance(under_test, ly_test_tools.launchers.LinuxLauncher)
|
||||
|
||||
def test_BinaryPath_DummyPath_AddPathToApp(self):
|
||||
dummy_path = "dummy_workspace_path"
|
||||
dummy_project = "dummy_project"
|
||||
mock_workspace = mock.MagicMock()
|
||||
mock_workspace.paths.build_directory.return_value = dummy_path
|
||||
mock_workspace.project = dummy_project
|
||||
launcher = ly_test_tools.launchers.LinuxLauncher(mock_workspace, ["some_args"])
|
||||
|
||||
under_test = launcher.binary_path()
|
||||
expected = os.path.join(dummy_path, f"{dummy_project}.GameLauncher")
|
||||
|
||||
assert under_test == expected
|
||||
|
||||
@mock.patch('ly_test_tools.launchers.LinuxLauncher.binary_path', mock.MagicMock)
|
||||
@mock.patch('subprocess.Popen')
|
||||
def test_Launch_DummyArgs_ArgsPassedToPopen(self, mock_subprocess):
|
||||
dummy_args = ["some_args"]
|
||||
launcher = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), dummy_args)
|
||||
|
||||
launcher.launch()
|
||||
|
||||
mock_subprocess.assert_called_once()
|
||||
name, args, kwargs = mock_subprocess.mock_calls[0]
|
||||
unpacked_args = args[0] # args is a list inside a tuple
|
||||
assert len(dummy_args) > 0, "accidentally removed dummy_args"
|
||||
for expected_arg in dummy_args:
|
||||
assert expected_arg in unpacked_args
|
||||
|
||||
@mock.patch('ly_test_tools.launchers.LinuxLauncher.is_alive')
|
||||
def test_Kill_MockAliveFalse_SilentSuccess(self, mock_alive):
|
||||
mock_alive.return_value = False
|
||||
mock_proc = mock.MagicMock()
|
||||
launcher = ly_test_tools.launchers.LinuxLauncher(mock.MagicMock(), ["dummy"])
|
||||
launcher._proc = mock_proc
|
||||
|
||||
launcher.kill()
|
||||
|
||||
mock_proc.kill.assert_called_once()
|
||||
mock_alive.assert_called_once()
|
||||
@@ -191,7 +191,7 @@ class TestSubprocessCheckCallWrapperSafe(unittest.TestCase):
|
||||
reason="tests.unit.test_process_utils is restricted to the Windows platform.")
|
||||
class TestCloseWindowsProcess(unittest.TestCase):
|
||||
|
||||
@mock.patch('ly_test_tools.environment.process_utils.WINDOWS', False)
|
||||
@mock.patch('ly_test_tools.WINDOWS', False)
|
||||
def test_CloseWindowsProccess_NotOnWindows_Error(self):
|
||||
with pytest.raises(NotImplementedError):
|
||||
process_utils.close_windows_process(1)
|
||||
|
||||
Reference in New Issue
Block a user