From 8766790f48dae79da59fd60c73b124d0e9b191a2 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Mon, 10 May 2021 14:28:02 -0700 Subject: [PATCH 1/4] re-add WinGenericLauncher for custom .exe launchers instead of the hard-coded ones (i.e. able to use 'MaterialEditor.exe' instead of 'Editor.exe' for a WinLauncher class) --- Tools/LyTestTools/ly_test_tools/__init__.py | 6 ++++-- .../pytest_plugin/test_tools_fixtures.py | 15 +++++++++++++++ .../ly_test_tools/launchers/launcher_helper.py | 16 ++++++++++++++++ .../launchers/platforms/win/launcher.py | 16 ++++++++++++++++ 4 files changed, 51 insertions(+), 2 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index 1c78176b87..febb89b541 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -17,7 +17,7 @@ logger = logging.getLogger(__name__) # Supported platforms. ALL_PLATFORM_OPTIONS = ['android', 'ios', 'linux', 'mac', 'windows'] -ALL_LAUNCHER_OPTIONS = ['android', 'base', 'mac', 'windows', 'windows_editor', 'windows_dedicated'] +ALL_LAUNCHER_OPTIONS = ['android', 'base', '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 @@ -38,11 +38,13 @@ if WINDOWS: HOST_OS_EDITOR = 'windows_editor' HOST_OS_DEDICATED_SERVER = 'windows_dedicated' import ly_test_tools.mobile.android - from ly_test_tools.launchers import AndroidLauncher, WinLauncher, DedicatedWinLauncher, WinEditor + from ly_test_tools.launchers import ( + AndroidLauncher, WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher) ANDROID = ly_test_tools.mobile.android.can_run_android() LAUNCHERS['windows'] = WinLauncher LAUNCHERS['windows_editor'] = WinEditor LAUNCHERS['windows_dedicated'] = DedicatedWinLauncher + LAUNCHERS['windows_generic'] = WinGenericLauncher LAUNCHERS['android'] = AndroidLauncher elif MAC: HOST_OS_PLATFORM = 'mac' diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py index c4249441b0..02b574ddf9 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py @@ -281,6 +281,21 @@ def _dedicated_launcher(request, workspace, launcher_platform, level=""): return launcher + +@pytest.fixture(scope="function") +def generic_launcher(workspace, request, crash_log_watchdog): + # type: (...) -> ly_test_tools.launchers.platforms.base.Launcher + return _generic_launcher( + workspace=workspace, + launcher_platform=get_fixture_argument(request, 'launcher_platform', HOST_OS_PLATFORM), + exe_file_name=get_fixture_argument(request, 'exe_file_name', '')) + + +def _generic_launcher(workspace, launcher_platform, exe_file_name): + """Separate implementation to call directly during unit tests""" + return ly_test_tools.launchers.launcher_helper.create_generic_launcher(workspace, launcher_platform, exe_file_name) + + @pytest.fixture def automatic_process_killer(request): # type: (_pytest.fixtures.SubRequest) -> ly_process_killer diff --git a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py index 4ed4c43dee..63b1eac1d3 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py @@ -61,3 +61,19 @@ def create_editor(workspace, launcher_platform=ly_test_tools.HOST_OS_EDITOR, arg """ launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_EDITOR) return launcher_class(workspace, args) + + +def create_generic_launcher(workspace, launcher_platform, exe_file_name, args=None): + # type: (ly_test_tools.managers.workspace.WorkspaceManager, str, str, List[str]) -> Launcher + """ + Create a generic launcher compatible with the specified workspace. + Allows custom .exe files to serve as the launcher instead of ones listed in the ly_test_tools.LAUNCHERS constant + + :param workspace: lumberyard workspace to use + :param launcher_platform: the platform to target for a launcher (i.e. 'windows' for WinLauncher) + :param exe_file_name: .exe file name which has to be launched for this launcher (i.e. 'MaterialEditor.exe') + :param args: List of arguments to pass to the launcher's 'args' argument during construction + :return: Launcher instance. + """ + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_PLATFORM) + return launcher_class(workspace, exe_file_name, args) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py index 800771c9bc..492af0e612 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py @@ -203,3 +203,19 @@ class WinEditor(WinLauncher): """ assert self.workspace.project is not None return os.path.join(self.workspace.paths.build_directory(), "Editor.exe") + + +class WinGenericLauncher(WinLauncher): + + def __init__(self, build, exe_file_name, args=None): + super(WinGenericLauncher, self).__init__(build, args) + self.exe_file_name = exe_file_name + + def binary_path(self): + """ + Return full path to the .exe file for this build's configuration and project + + :return: full path to the given exe file + """ + assert self.workspace.project is not None + return os.path.join(self.workspace.paths.build_directory(), f"{self.exe_file_name}.exe") From 58858994970fb1ce004215f055202d6251c12e92 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Mon, 10 May 2021 14:44:47 -0700 Subject: [PATCH 2/4] fix tabbing, add WinGenericLauncher class import to ly_test_tools.launchers.__init__ --- .../_internal/pytest_plugin/test_tools_fixtures.py | 4 ++-- Tools/LyTestTools/ly_test_tools/launchers/__init__.py | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py index 02b574ddf9..c3a9ffdc22 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py @@ -287,8 +287,8 @@ def generic_launcher(workspace, request, crash_log_watchdog): # type: (...) -> ly_test_tools.launchers.platforms.base.Launcher return _generic_launcher( workspace=workspace, - launcher_platform=get_fixture_argument(request, 'launcher_platform', HOST_OS_PLATFORM), - exe_file_name=get_fixture_argument(request, 'exe_file_name', '')) + launcher_platform=get_fixture_argument(request, 'launcher_platform', HOST_OS_PLATFORM), + exe_file_name=get_fixture_argument(request, 'exe_file_name', '')) def _generic_launcher(workspace, launcher_platform, exe_file_name): diff --git a/Tools/LyTestTools/ly_test_tools/launchers/__init__.py b/Tools/LyTestTools/ly_test_tools/launchers/__init__.py index 2203d2eb39..5f7a391afb 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/__init__.py @@ -11,5 +11,6 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. from ly_test_tools.launchers.platforms.base import Launcher from ly_test_tools.launchers.platforms.mac.launcher import MacLauncher -from ly_test_tools.launchers.platforms.win.launcher import WinLauncher, DedicatedWinLauncher, WinEditor +from ly_test_tools.launchers.platforms.win.launcher import ( + WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher) from ly_test_tools.launchers.platforms.android.launcher import AndroidLauncher From 757a26825ac5a4e09892a9af817c45a13eb44552 Mon Sep 17 00:00:00 2001 From: jromnoa Date: Mon, 10 May 2021 16:27:30 -0700 Subject: [PATCH 3/4] adds new HOST_OS_GENERIC_EXECUTABLE constant and adds error handling if the executable doesn't exist in the path provided --- Tools/LyTestTools/ly_test_tools/__init__.py | 1 + .../_internal/pytest_plugin/test_tools_fixtures.py | 4 ++-- .../ly_test_tools/launchers/launcher_helper.py | 2 +- .../launchers/platforms/win/launcher.py | 13 +++++++++++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/Tools/LyTestTools/ly_test_tools/__init__.py b/Tools/LyTestTools/ly_test_tools/__init__.py index febb89b541..d534f5e0d2 100755 --- a/Tools/LyTestTools/ly_test_tools/__init__.py +++ b/Tools/LyTestTools/ly_test_tools/__init__.py @@ -37,6 +37,7 @@ if WINDOWS: HOST_OS_PLATFORM = 'windows' HOST_OS_EDITOR = 'windows_editor' HOST_OS_DEDICATED_SERVER = 'windows_dedicated' + HOST_OS_GENERIC_EXECUTABLE = 'windows_generic' import ly_test_tools.mobile.android from ly_test_tools.launchers import ( AndroidLauncher, WinLauncher, DedicatedWinLauncher, WinEditor, WinGenericLauncher) diff --git a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py index c3a9ffdc22..dc2e92f518 100755 --- a/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py +++ b/Tools/LyTestTools/ly_test_tools/_internal/pytest_plugin/test_tools_fixtures.py @@ -29,7 +29,7 @@ import ly_test_tools.environment.file_system import ly_test_tools.launchers.launcher_helper import ly_test_tools.launchers.platforms.base import ly_test_tools.environment.watchdog -from ly_test_tools import ALL_PLATFORM_OPTIONS, HOST_OS_PLATFORM +from ly_test_tools import ALL_PLATFORM_OPTIONS, HOST_OS_PLATFORM, HOST_OS_GENERIC_EXECUTABLE logger = logging.getLogger(__name__) @@ -287,7 +287,7 @@ def generic_launcher(workspace, request, crash_log_watchdog): # type: (...) -> ly_test_tools.launchers.platforms.base.Launcher return _generic_launcher( workspace=workspace, - launcher_platform=get_fixture_argument(request, 'launcher_platform', HOST_OS_PLATFORM), + launcher_platform=get_fixture_argument(request, 'launcher_platform', HOST_OS_GENERIC_EXECUTABLE), exe_file_name=get_fixture_argument(request, 'exe_file_name', '')) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py index 63b1eac1d3..45f5e6e73f 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/launcher_helper.py @@ -75,5 +75,5 @@ def create_generic_launcher(workspace, launcher_platform, exe_file_name, args=No :param args: List of arguments to pass to the launcher's 'args' argument during construction :return: Launcher instance. """ - launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_PLATFORM) + launcher_class = ly_test_tools.LAUNCHERS.get(launcher_platform, ly_test_tools.HOST_OS_GENERIC_EXECUTABLE) return launcher_class(workspace, exe_file_name, args) diff --git a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py index 492af0e612..ebb3566806 100755 --- a/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py +++ b/Tools/LyTestTools/ly_test_tools/launchers/platforms/win/launcher.py @@ -210,12 +210,21 @@ class WinGenericLauncher(WinLauncher): def __init__(self, build, exe_file_name, args=None): super(WinGenericLauncher, self).__init__(build, args) self.exe_file_name = exe_file_name + self.expected_executable_path = os.path.join( + self.workspace.paths.build_directory(), f"{self.exe_file_name}.exe") + + if not os.path.exists(self.expected_executable_path): + raise ProcessNotStartedError( + f"Unable to locate executable '{self.exe_file_name}.exe' " + f"in path: '{self.expected_executable_path}'") def binary_path(self): """ Return full path to the .exe file for this build's configuration and project + Relies on the build_directory() in self.workspace.paths to be accurate :return: full path to the given exe file """ - assert self.workspace.project is not None - return os.path.join(self.workspace.paths.build_directory(), f"{self.exe_file_name}.exe") + assert self.workspace.project is not None, ( + 'Project cannot be NoneType - please specify a project name string.') + return self.expected_executable_path From d26c4614ba270c32357ef6f2fd2a4e02c54a72fd Mon Sep 17 00:00:00 2001 From: jromnoa Date: Mon, 10 May 2021 16:48:38 -0700 Subject: [PATCH 4/4] add unit tests for WinGenericLauncher class --- .../tests/unit/test_launcher_win.py | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Tools/LyTestTools/tests/unit/test_launcher_win.py b/Tools/LyTestTools/tests/unit/test_launcher_win.py index 193589f457..37e04ac99e 100755 --- a/Tools/LyTestTools/tests/unit/test_launcher_win.py +++ b/Tools/LyTestTools/tests/unit/test_launcher_win.py @@ -184,3 +184,31 @@ class TestDedicatedWinLauncher(object): launcher.workspace.build(dedicated=True) mock_workspace.build.assert_called_once_with(dedicated=True) + + +class TestWinGenericLauncher(object): + + @mock.patch('ly_test_tools.launchers.platforms.win.launcher.os.path.exists') + def test_BinaryPath_DummyPathExeExists_AddPathToExe(self, mock_os_path_exists): + dummy_path = "dummy_workspace_path" + dummy_executable = 'SomeCustomLauncher.exe' + mock_os_path_exists.return_value = True + mock_workspace = mock.MagicMock() + mock_workspace.paths.build_directory.return_value = dummy_path + launcher = ly_test_tools.launchers.WinGenericLauncher(mock_workspace, dummy_executable, ["some_args"]) + + under_test = launcher.binary_path() + + assert dummy_executable in under_test, f"executable named {dummy_executable} not found" + assert dummy_path in under_test, "workspace path unexpectedly missing " + + @mock.patch('ly_test_tools.launchers.platforms.win.launcher.os.path.exists') + def test_BinaryPath_DummyPathExeDoesNotExist_RaiseProcessNotStartedError(self, mock_os_path_exists): + dummy_path = "dummy_workspace_path" + dummy_executable = 'SomeCustomLauncher.exe' + mock_os_path_exists.return_value = False + mock_workspace = mock.MagicMock() + mock_workspace.paths.build_directory.return_value = dummy_path + + with pytest.raises(ly_test_tools.launchers.exceptions.ProcessNotStartedError): + ly_test_tools.launchers.WinGenericLauncher(mock_workspace, dummy_executable, ["some_args"])