You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Tools/LyTestTools/tests/unit/test_fixtures.py

371 lines
16 KiB
Python

"""
Copyright (c) Contributors to the Open 3D Engine Project
SPDX-License-Identifier: Apache-2.0 OR MIT
Unit tests for ly_test_tools._internal.pytest_plugin.test_tools_fixtures
"""
import datetime
import os
import unittest.mock as mock
import pytest
import ly_test_tools._internal.pytest_plugin.test_tools_fixtures as test_tools_fixtures
pytestmark = pytest.mark.SUITE_smoke
class TestFixtures(object):
def test_AddOptionForLogs_MockParser_OptionAdded(self):
mock_parser = mock.MagicMock()
test_tools_fixtures.pytest_addoption(mock_parser)
mock_call = mock_parser.addoption.mock_calls[0]
"""
mock_call is the same as: call('--output_path', help='Set the folder name for the output logs.')
which results in a 3-tuple with (name, args, kwargs), so --output_path is in the list of args
"""
assert mock_call[1][0] == '--output-path'
def test_RecordSuiteProperty_MockRequestWithEmptyXml_PropertyAdded(self):
mock_request = mock.MagicMock()
mock_xml = mock.MagicMock()
mock_request.config._xml = mock_xml
func = test_tools_fixtures._record_suite_property(mock_request)
func('NewProperty', 'value')
mock_xml.add_global_property.assert_called_once_with('NewProperty', 'value')
def test_RecordSuiteProperty_MockRequestWithPropertyXml_PropertyUpdated(self):
mock_request = mock.MagicMock()
mock_xml = mock.MagicMock()
mock_xml.global_properties = [('ExistingProperty', 'old value')]
mock_request.config._xml = mock_xml
func = test_tools_fixtures._record_suite_property(mock_request)
func('ExistingProperty', 'new value')
assert mock_xml.global_properties[0] == ('ExistingProperty', 'new value')
mock_xml.add_global_property.assert_not_called()
@mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.logger')
def test_RecordSuiteProperty_MockRequestWithoutXml_NoOpReturned(self, mock_logger):
mock_request = mock.MagicMock()
mock_request.config._xml = None
func = test_tools_fixtures._record_suite_property(mock_request)
mock_logger.debug.assert_not_called()
func('SomeProperty', 'some value')
mock_logger.debug.assert_called_once()
@mock.patch('os.makedirs')
def test_LogsPath_CustomPathOption_CustomPathCreated(self, mock_makedirs):
expected = 'SomePath'
mock_config = mock.MagicMock()
mock_config.getoption.return_value = expected
actual = test_tools_fixtures._get_output_path(mock_config)
assert actual == expected
mock_makedirs.assert_called_once_with('SomePath', exist_ok=True)
@mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.datetime',
mock.Mock(now=lambda: datetime.datetime(2019, 10, 11)))
@mock.patch('os.getcwd')
@mock.patch('os.makedirs')
def test_LogsPath_NoPathOption_DefaultPathCreated(self, mock_makedirs, mock_getcwd):
mock_config = mock.MagicMock()
mock_cwd = 'C:/foo'
mock_getcwd.return_value = mock_cwd
mock_config.getoption.return_value = None
expected = os.path.join(mock_cwd,
'TestResults',
'2019-10-11T00-00-00-000000',
'pytest_results')
actual = test_tools_fixtures._get_output_path(mock_config)
assert actual == expected
mock_makedirs.assert_called_once_with(expected, exist_ok=True)
def test_RecordBuildName_MockRecordFunction_MockFunctionCalled(self):
mock_fn = mock.MagicMock()
func = test_tools_fixtures._record_build_name(mock_fn)
func('MyBuild')
mock_fn.assert_called_once_with('build', 'MyBuild')
@mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.datetime',
mock.Mock(now=lambda: datetime.datetime(2019, 10, 11)))
def test_RecordTimeStamp_MockRecordFunction_MockFunctionCalled(self):
mock_fn = mock.MagicMock()
test_tools_fixtures._record_test_timestamp(mock_fn)
mock_fn.assert_called_once_with('timestamp', '2019-10-11T00-00-00-000000')
@mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.datetime',
mock.Mock(now=lambda: datetime.datetime(2019, 10, 11)))
@mock.patch('socket.gethostname')
@mock.patch('getpass.getuser')
def test_RecordSuiteData_MockRecordFunction_MockFunctionCalled(self, mock_getuser, mock_gethostname):
mock_getuser.return_value = 'foo@bar.baz'
mock_gethostname.return_value = 'bar.baz'
mock_fn = mock.MagicMock()
test_tools_fixtures._record_suite_data(mock_fn)
expected_calls = [mock.call('timestamp', '2019-10-11T00-00-00-000000'),
mock.call('hostname', 'bar.baz'),
mock.call('username', 'foo@bar.baz')]
mock_fn.assert_has_calls(expected_calls)
@mock.patch("ly_test_tools._internal.log.py_logging_util.initialize_logging", mock.MagicMock())
@mock.patch("ly_test_tools.builtin.helpers.setup_builtin_workspace")
@mock.patch("ly_test_tools.builtin.helpers.create_builtin_workspace")
def test_Workspace_MockFixturesAndExecTeardown_ReturnWorkspaceRegisterTeardown(self, mock_create, mock_setup):
test_module = 'example.tests.test_system_example'
test_class = ('TestSystemExample.test_SystemTestExample_AllSupportedPlatforms_LaunchAutomatedTesting'
'[120-simple_jacklocomotion-AutomatedTesting-all-profile-win_x64_vs2017]')
test_method = 'test_SystemTestExample_AllSupportedPlatforms_LaunchAutomatedTesting'
artifact_folder_name = 'TheArtifactFolder'
artifact_path = "PathToArtifacts"
mock_request = mock.MagicMock()
mock_request.addfinalizer = mock.MagicMock()
mock_request.node.module.__name__ = test_module
mock_request.node.getmodpath.return_value = test_class
mock_request.node.originalname = test_method
mock_workspace = mock.MagicMock()
mock_workspace.artifact_manager.generate_folder_name.return_value = artifact_folder_name
mock_workspace.artifact_manager.artifact_path = artifact_path
mock_workspace.artifact_manager.gather_artifacts.return_value = os.path.join(artifact_path, 'foo.zip')
mock_create.return_value = mock_workspace
mock_property = mock.MagicMock()
mock_build_name = mock.MagicMock()
mock_logs = "foo"
under_test = test_tools_fixtures._workspace(
request=mock_request,
build_directory='foo',
project="",
record_property=mock_property,
record_build_name=mock_build_name,
output_path=mock_logs,
asset_processor_platform='ap_platform'
)
assert under_test is mock_workspace
# verify additional commands are called
mock_create.assert_called_once()
mock_setup.assert_called_once_with(under_test, artifact_folder_name, mock_request.session.testscollected)
# verify teardown was hooked but not called
mock_request.addfinalizer.assert_called_once()
mock_workspace.teardown.assert_not_called()
# execute teardown hook from recorded call, and verify called
mock_request.addfinalizer.call_args[0][0]()
mock_workspace.teardown.assert_called_once()
mock_workspace.artifact_manager.generate_folder_name.assert_called_with(
test_module.split('.')[-1], # 'example.tests.test_system_example' -> 'test_system_example'
test_class.split('.')[0], # 'TestSystemExample.test_SystemTestExample_...' -> 'TestSystemExample'
test_method # 'test_SystemTestExample_AllSupportedPlatforms_LaunchAutomatedTesting'
)
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
@mock.patch("ly_test_tools.launchers.launcher_helper.create_launcher")
def test_Launcher_MockHelper_Passthrough(self, mock_create):
retval = mock.MagicMock()
mock_create.return_value = retval
mock_workspace = mock.MagicMock()
mock_workspace.paths.waf.return_value = "dummy"
mock_workspace.paths.autoexec_file.return_value = "dummy2"
file_handler = mock.mock_open()
with mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.open', file_handler, create=True):
with open(mock_workspace.paths.autoexec_file(), 'w') as autoexec_file:
autoexec_file.write('map ' + 'level')
under_test = test_tools_fixtures._launcher(mock.MagicMock(), mock_workspace, 'windows', 'level')
mock_create.assert_called_once()
assert retval is under_test
@mock.patch('os.path.exists', mock.MagicMock(return_value=True))
@mock.patch("ly_test_tools.launchers.launcher_helper.create_launcher")
def test_Launcher_MockHelper_TeardownCalled(self, mock_create):
retval = mock.MagicMock()
retval.stop = mock.MagicMock()
mock_create.return_value = retval
mock_request = mock.MagicMock()
mock_workspace = mock.MagicMock()
mock_workspace.paths.waf.return_value = "dummy"
mock_workspace.paths.autoexec_file.return_value = "dummy2"
file_handler = mock.mock_open()
def _fail_finalizer():
assert False, "teardown should have been added to finalizer"
def _capture_finalizer(func):
nonlocal _finalizer
_finalizer = func
_finalizer = _fail_finalizer
mock_request.addfinalizer = _capture_finalizer
with mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.open', file_handler, create=True):
with open(mock_workspace.paths.autoexec_file(), 'w') as autoexec_file:
autoexec_file.write('map ' + 'level')
under_test = test_tools_fixtures._launcher(mock_request, mock_workspace, 'windows', 'level')
assert retval is under_test
assert _finalizer is not None
_finalizer()
retval.stop.assert_called_once()
@mock.patch("ly_test_tools.launchers.launcher_helper.create_dedicated_launcher")
def test_DedicatedLauncher_MockHelper_Passthrough(self, mock_create):
retval = mock.MagicMock()
mock_create.return_value = retval
under_test = test_tools_fixtures._dedicated_launcher(mock.MagicMock(), mock.MagicMock(), 'windows')
mock_create.assert_called_once()
assert retval is under_test
@mock.patch("ly_test_tools.launchers.launcher_helper.create_dedicated_launcher")
def test_DedicatedLauncher_MockHelper_TeardownCalled(self, mock_create):
retval = mock.MagicMock()
retval.stop = mock.MagicMock()
mock_request = mock.MagicMock()
mock_create.return_value = retval
def _fail_finalizer():
assert False, "teardown should have been added to finalizer"
def _capture_finalizer(func):
nonlocal _finalizer
_finalizer = func
_finalizer = _fail_finalizer
mock_request.addfinalizer = _capture_finalizer
under_test = test_tools_fixtures._dedicated_launcher(mock_request, mock.MagicMock(), 'windows')
mock_create.assert_called_once()
assert retval is under_test
assert _finalizer is not None
_finalizer()
retval.stop.assert_called_once()
@mock.patch("ly_test_tools.launchers.launcher_helper.create_editor")
def test_Editor_MockHelper_Passthrough(self, mock_create):
retval = mock.MagicMock()
mock_create.return_value = retval
under_test = test_tools_fixtures._editor(mock.MagicMock(), mock.MagicMock(), 'windows_editor')
mock_create.assert_called_once()
assert retval is under_test
@mock.patch("ly_test_tools.launchers.launcher_helper.create_editor")
def test_Editor_MockHelper_TeardownCalled(self, mock_create):
retval = mock.MagicMock()
retval.stop = mock.MagicMock()
mock_request = mock.MagicMock()
mock_create.return_value = retval
def _fail_finalizer():
assert False, "teardown should have been added to finalizer"
def _capture_finalizer(func):
nonlocal _finalizer
_finalizer = func
_finalizer = _fail_finalizer
mock_request.addfinalizer = _capture_finalizer
under_test = test_tools_fixtures._editor(mock_request, mock.MagicMock(), 'windows_editor')
mock_create.assert_called_once()
assert retval is under_test
assert _finalizer is not None
_finalizer()
retval.stop.assert_called_once()
@mock.patch('ly_test_tools._internal.pytest_plugin.test_tools_fixtures.get_fixture_argument')
@mock.patch('ly_test_tools._internal.managers.ly_process_killer.detect_lumberyard_processes')
@mock.patch('ly_test_tools._internal.managers.ly_process_killer.kill_processes', mock.MagicMock())
def test_AutomaticProcessKiller_ProcessKillList_KillsDetectedProcesses(self, mock_detect_processes,
mock_get_fixture_argument):
mock_processes_list = ['foo', 'bar', 'foobar']
mock_detected_processes = ['foo', 'bar']
mock_detect_processes.return_value = mock_detected_processes
mock_get_fixture_argument.return_value = mock_processes_list
under_test = test_tools_fixtures._automatic_process_killer(mock_processes_list)
under_test.detect_lumberyard_processes.assert_called_with(processes_list=mock_processes_list)
under_test.kill_processes.assert_called_with(processes_list=mock_detected_processes)
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog.start', mock.MagicMock())
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog')
def test_CrashLogWatchdog_Instantiates_CreatesWatchdog(self, under_test):
mock_workspace = mock.MagicMock()
mock_path = 'C:/foo'
mock_workspace.paths.project_log.return_value = mock_path
mock_request = mock.MagicMock()
mock_request.addfinalizer = mock.MagicMock()
mock_raise_on_crash = mock.MagicMock()
mock_watchdog = test_tools_fixtures._crash_log_watchdog(mock_request, mock_workspace, mock_raise_on_crash)
under_test.assert_called_once_with(os.path.join(mock_path, 'error.log'), raise_on_condition=mock_raise_on_crash)
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog.start')
def test_CrashLogWatchdog_Instantiates_StartsThread(self, under_test):
mock_workspace = mock.MagicMock()
mock_path = 'C:/foo'
mock_workspace.paths.project_log.return_value = mock_path
mock_request = mock.MagicMock()
mock_request.addfinalizer = mock.MagicMock()
mock_raise_on_crash = mock.MagicMock()
test_tools_fixtures._crash_log_watchdog(mock_request, mock_workspace, mock_raise_on_crash)
under_test.assert_called_once()
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog.start', mock.MagicMock())
def test_CrashLogWatchdog_Instantiates_AddsTeardown(self):
mock_workspace = mock.MagicMock()
mock_path = 'C:/foo'
mock_workspace.paths.project_log.return_value = mock_path
mock_request = mock.MagicMock()
mock_request.addfinalizer = mock.MagicMock()
mock_raise_on_crash = mock.MagicMock()
mock_watchdog = test_tools_fixtures._crash_log_watchdog(mock_request, mock_workspace, mock_raise_on_crash)
mock_request.addfinalizer.assert_called_once()
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog.start', mock.MagicMock())
@mock.patch('ly_test_tools.environment.watchdog.CrashLogWatchdog.stop')
def test_CrashLogWatchdog_Teardown_CallsStop(self, mock_stop):
mock_workspace = mock.MagicMock()
mock_path = 'C:/foo'
mock_workspace.paths.project_log.return_value = mock_path
mock_request = mock.MagicMock()
mock_request.addfinalizer = mock.MagicMock()
mock_raise_condition = mock.MagicMock()
mock_watchdog = test_tools_fixtures._crash_log_watchdog(mock_request, mock_workspace, mock_raise_condition)
mock_request.addfinalizer.call_args[0][0]()
mock_stop.assert_called_once()