[LYN-2470] Integrate resource mapping tool with engine python environment (#17)

1. Add support to use engine python runtime environment. Ideally we should use project executable directory which contains required qt binaries after build. But currently Qt plugins are not put under proper subdirectory which causes huge overhead of loading plugin binaries (see details in this jira https://jira.agscollab.com/browse/LYN-2669 ). The temporary solution is to use AWSCore.Editor target to create a subdirectory for our use case.   
2. **Minor Fix** After migrating to engine python environment, see boto3 warning while using debug mode, seems like a existing issue for a long time https://github.com/boto/boto3/issues/454. Register a after call event to close connection   
3. **Minor Fix** After migrating to engine python environment, see Qt multithread warning while using debug mode, change direct setter function call to qt signal
This commit is contained in:
Vincent Liu
2021-04-23 11:34:37 -07:00
committed by GitHub
parent 16ca036ea8
commit a4e1abf6df
20 changed files with 343 additions and 126 deletions
@@ -62,7 +62,8 @@ class TestImportResourcesController(TestCase):
self._mocked_proxy_model: MagicMock = self._mocked_tree_view.resource_proxy_model
self._test_import_resources_controller: ImportResourcesController = ImportResourcesController()
self._test_import_resources_controller.add_import_resources = MagicMock()
self._test_import_resources_controller.add_import_resources_sender = MagicMock()
self._test_import_resources_controller.set_notification_frame_text_sender = MagicMock()
self._test_import_resources_controller.setup()
def test_reset_page_resetting_page_with_expected_state(self) -> None:
@@ -116,7 +117,7 @@ class TestImportResourcesController(TestCase):
self._mocked_import_resources_page.typed_resources_search_button.clicked.connect.call_args[0]
mocked_call_args[0]() # triggering search_button connected function
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_tree_view.reset_view.assert_not_called()
self._mocked_import_resources_page.set_current_main_view_index.assert_not_called()
@@ -170,7 +171,7 @@ class TestImportResourcesController(TestCase):
mock_aws_utils.list_cloudformation_stacks.assert_called_once_with(
TestImportResourcesController._expected_region)
mock_aws_utils.list_cloudformation_stack_resources.assert_not_called()
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_proxy_model.emit_source_model_layout_changed.assert_called_once()
self._mocked_import_resources_page.set_current_main_view_index.assert_called_with(
@@ -195,7 +196,7 @@ class TestImportResourcesController(TestCase):
TestImportResourcesController._expected_region)
mock_aws_utils.list_cloudformation_stack_resources.assert_called_once_with(
TestImportResourcesController._expected_cfn_stack_name, TestImportResourcesController._expected_region)
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_proxy_model.emit_source_model_layout_changed.assert_called_once()
self._mocked_import_resources_page.set_current_main_view_index.assert_called_with(
@@ -258,8 +259,8 @@ class TestImportResourcesController(TestCase):
self._mocked_import_resources_page.cfn_stacks_import_button.clicked.connect.call_args[0]
mocked_call_args[0]() # triggering cfn_stacks_import_button connected function
self._test_import_resources_controller.add_import_resources.emit.assert_not_called()
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.add_import_resources_sender.emit.assert_not_called()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
def test_page_cfn_stacks_import_button_emit_signal_with_expected_resources_and_switch_to_expected_page(self) -> None:
self._mocked_proxy_model.deduplicate_selected_import_resources.return_value = \
@@ -268,7 +269,7 @@ class TestImportResourcesController(TestCase):
self._mocked_import_resources_page.cfn_stacks_import_button.clicked.connect.call_args[0]
mocked_call_args[0]() # triggering cfn_stacks_import_button connected function
self._test_import_resources_controller.add_import_resources.emit.assert_called_once_with(
self._test_import_resources_controller.add_import_resources_sender.emit.assert_called_once_with(
[TestImportResourcesController._expected_lambda_resource])
self._mocked_view_manager.switch_to_view_edit_page.assert_called_once()
self._mocked_tree_view.reset_view.assert_called_once()
@@ -329,7 +330,7 @@ class TestImportResourcesController(TestCase):
mock_aws_utils.list_lambda_functions.assert_called_once_with(
TestImportResourcesController._expected_region)
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_proxy_model.emit_source_model_layout_changed.assert_called_once()
self._mocked_import_resources_page.set_current_main_view_index.assert_called_with(
@@ -348,7 +349,7 @@ class TestImportResourcesController(TestCase):
mocked_async_call_args: call = mock_thread_manager.get_instance.return_value.start.call_args[0]
mocked_async_call_args[0].run() # triggering async function
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_proxy_model.emit_source_model_layout_changed.assert_called_once()
self._mocked_import_resources_page.set_current_main_view_index.assert_called_with(
@@ -383,8 +384,8 @@ class TestImportResourcesController(TestCase):
self._mocked_import_resources_page.typed_resources_import_button.clicked.connect.call_args[0]
mocked_call_args[0]() # triggering typed_resources_import_button connected function
self._test_import_resources_controller.add_import_resources.emit.assert_not_called()
self._mocked_import_resources_page.set_notification_frame_text.assert_called_once()
self._test_import_resources_controller.add_import_resources_sender.emit.assert_not_called()
self._test_import_resources_controller.set_notification_frame_text_sender.emit.assert_called_once()
def test_page_typed_resources_import_button_emit_signal_with_expected_resources_and_switch_to_expected_page(self) -> None:
self._mocked_proxy_model.deduplicate_selected_import_resources.return_value = \
@@ -393,7 +394,7 @@ class TestImportResourcesController(TestCase):
self._mocked_import_resources_page.typed_resources_import_button.clicked.connect.call_args[0]
mocked_call_args[0]() # triggering typed_resources_import_button connected function
self._test_import_resources_controller.add_import_resources.emit.assert_called_once_with(
self._test_import_resources_controller.add_import_resources_sender.emit.assert_called_once_with(
[TestImportResourcesController._expected_lambda_resource])
self._mocked_view_manager.switch_to_view_edit_page.assert_called_once()
self._mocked_tree_view.reset_view.assert_called_once()
@@ -62,10 +62,12 @@ class TestViewEditController(TestCase):
self._mocked_proxy_model: MagicMock = self._mocked_table_view.resource_proxy_model
self._test_view_edit_controller: ViewEditController = ViewEditController()
self._test_view_edit_controller.set_notification_frame_text_sender = MagicMock()
self._test_view_edit_controller.set_notification_page_frame_text_sender = MagicMock()
self._test_view_edit_controller.setup()
def test_add_import_resources_expected_resource_gets_loaded_into_model(self) -> None:
self._test_view_edit_controller.add_import_resources([TestViewEditController._expected_resource])
self._test_view_edit_controller.add_import_resources_receiver([TestViewEditController._expected_resource])
self._mocked_proxy_model.add_resource.assert_called_once()
mocked_call_args: call = self._mocked_proxy_model.add_resource.call_args[0] # mock call args index is 0
@@ -128,7 +130,7 @@ class TestViewEditController(TestCase):
self._mocked_proxy_model.emit_source_model_layout_changed.assert_not_called()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_view_edit_page.set_table_view_page_interactions_enabled.assert_called_with(False)
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_view_edit_page.set_current_main_view_index.assert_called_with(
ViewEditPageConstants.TABLE_VIEW_PAGE_INDEX)
@@ -189,7 +191,7 @@ class TestViewEditController(TestCase):
mock_json_utils.validate_json_dict_according_to_json_schema.assert_called_once_with({})
mock_json_utils.convert_json_dict_to_resources.assert_not_called()
self._mocked_proxy_model.load_resource.assert_not_called()
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_view_edit_page.set_table_view_page_interactions_enabled.assert_called_with(False)
@patch("controller.view_edit_controller.file_utils")
@@ -239,7 +241,7 @@ class TestViewEditController(TestCase):
self._mocked_view_edit_page.config_file_combobox.currentText.assert_called_once()
self._mocked_table_view.reset_view.assert_called_once()
self._mocked_proxy_model.override_resource_status.assert_called_once()
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.emit_source_model_layout_changed.assert_has_calls([call(), call()])
self._mocked_view_edit_page.set_current_main_view_index.assert_called_with(
ViewEditPageConstants.TABLE_VIEW_PAGE_INDEX)
@@ -275,7 +277,7 @@ class TestViewEditController(TestCase):
mocked_call_args[0]() # triggering config_location_button connected function
mock_file_dialog.getExistingDirectory.assert_called_once()
self._mocked_view_edit_page.set_notification_page_text.assert_called_with(
self._test_view_edit_controller.set_notification_page_frame_text_sender.emit.assert_called_with(
notification_label_text.NOTIFICATION_LOADING_MESSAGE)
self._mocked_view_edit_page.set_current_main_view_index.assert_called_with(
ViewEditPageConstants.NOTIFICATION_PAGE_INDEX)
@@ -303,7 +305,7 @@ class TestViewEditController(TestCase):
expected_new_config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX)
assert self._mocked_configuration_manager.configuration.config_files == []
self._mocked_view_edit_page.set_config_files.assert_called_with([])
self._mocked_view_edit_page.set_notification_page_text.assert_called_once_with(
self._test_view_edit_controller.set_notification_page_frame_text_sender.emit.assert_called_once_with(
notification_label_text.NOTIFICATION_LOADING_MESSAGE)
@patch("controller.view_edit_controller.ThreadManager")
@@ -325,7 +327,7 @@ class TestViewEditController(TestCase):
expected_new_config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX)
assert self._mocked_configuration_manager.configuration.config_files == []
self._mocked_view_edit_page.set_config_files.assert_called_with([])
self._mocked_view_edit_page.set_notification_page_text.assert_called_once_with(
self._test_view_edit_controller.set_notification_page_frame_text_sender.emit.assert_called_once_with(
notification_label_text.NOTIFICATION_LOADING_MESSAGE)
@patch("controller.view_edit_controller.ThreadManager")
@@ -348,7 +350,7 @@ class TestViewEditController(TestCase):
expected_new_config_directory, constants.RESOURCE_MAPPING_CONFIG_FILE_NAME_SUFFIX)
assert self._mocked_configuration_manager.configuration.config_files == expected_new_config_files
self._mocked_view_edit_page.set_config_files.assert_called_with(expected_new_config_files)
self._mocked_view_edit_page.set_notification_page_text.assert_called_once_with(
self._test_view_edit_controller.set_notification_page_frame_text_sender.emit.assert_called_once_with(
notification_label_text.NOTIFICATION_LOADING_MESSAGE)
def test_page_add_row_button_expected_resource_gets_loaded_into_model(self) -> None:
@@ -395,7 +397,7 @@ class TestViewEditController(TestCase):
mocked_call_args[0]() # triggering save_changes_button connected function
self._mocked_proxy_model.override_resource_status.assert_called_once()
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.override_all_resources_status.assert_not_called()
@patch("controller.view_edit_controller.json_utils")
@@ -451,7 +453,7 @@ class TestViewEditController(TestCase):
mock_json_utils.convert_resources_to_json_dict.assert_called_once()
mock_json_utils.write_into_json_file.assert_called_once_with(
TestViewEditController._expected_config_file_full_path, expected_json_dict)
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
self._mocked_proxy_model.override_all_resources_status.assert_not_called()
def test_page_search_filter_input_invoke_proxy_model_with_expected_filter_text(self) -> None:
@@ -498,7 +500,7 @@ class TestViewEditController(TestCase):
mock_file_utils.join_path.assert_called_once()
mock_json_utils.create_empty_resource_mapping_file.assert_called_once()
mock_file_utils.find_files_with_suffix_under_directory.assert_not_called()
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
@patch("controller.view_edit_controller.file_utils")
def test_page_rescan_button_post_notification_when_find_files_throw_exception(
@@ -509,4 +511,4 @@ class TestViewEditController(TestCase):
mocked_call_args[0]() # triggering rescan_button connected function
mock_file_utils.find_files_with_suffix_under_directory.assert_called_once()
self._mocked_view_edit_page.set_notification_frame_text.assert_called_once()
self._test_view_edit_controller.set_notification_frame_text_sender.emit.assert_called_once()
@@ -55,5 +55,5 @@ class TestControllerManager(TestCase):
TestControllerManager._expected_controller_manager.setup()
mocked_view_edit_controller.setup.assert_called_once()
mocked_import_resources_controller.setup.assert_called_once()
mocked_import_resources_controller.add_import_resources.connect.assert_called_once_with(
mocked_view_edit_controller.add_import_resources)
mocked_import_resources_controller.add_import_resources_sender.connect.assert_called_once_with(
mocked_view_edit_controller.add_import_resources_receiver)
@@ -18,7 +18,7 @@ from manager.view_manager import (ViewManager, ViewManagerConstants)
class TestViewManager(TestCase):
"""
ThreadManager unit test cases
ViewManager unit test cases
"""
_mock_import_resources_page: MagicMock
_mock_view_edit_page: MagicMock
@@ -36,6 +36,8 @@ class TestViewManager(TestCase):
main_window_patcher: patch = patch("manager.view_manager.QMainWindow")
cls._mock_main_window = main_window_patcher.start()
window_icon_patcher: patch = patch("manager.view_manager.QPixmap")
window_icon_patcher.start()
stacked_pages_patcher: patch = patch("manager.view_manager.QStackedWidget")
cls._mock_stacked_pages = stacked_pages_patcher.start()
@@ -0,0 +1,44 @@
"""
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.
"""
from typing import List
from unittest import TestCase
from unittest.mock import (ANY, call, MagicMock, patch)
from model import constants
from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder)
from utils import environment_utils
class TestEnvironmentUtils(TestCase):
"""
environment utils unit test cases
"""
def setUp(self) -> None:
os_environ_patcher: patch = patch("os.environ")
self.addCleanup(os_environ_patcher.stop)
self._mock_os_environ: MagicMock = os_environ_patcher.start()
os_pathsep_patcher: patch = patch("os.pathsep")
self.addCleanup(os_pathsep_patcher.stop)
self._mock_os_pathsep: MagicMock = os_pathsep_patcher.start()
def test_setup_qt_environment_global_flag_is_set(self) -> None:
environment_utils.setup_qt_environment("dummy")
self._mock_os_environ.copy.assert_called_once()
self._mock_os_pathsep.join.assert_called_once()
assert environment_utils.is_qt_linked() is True
def test_cleanup_qt_environment_global_flag_is_set(self) -> None:
environment_utils.setup_qt_environment("dummy")
assert environment_utils.is_qt_linked() is True
environment_utils.cleanup_qt_environment()
assert environment_utils.is_qt_linked() is False
@@ -30,10 +30,6 @@ class TestFileUtils(TestCase):
self.addCleanup(path_patcher.stop)
self._mock_path: MagicMock = path_patcher.start()
windows_path_patcher: patch = patch("pathlib.WindowsPath")
self.addCleanup(windows_path_patcher.stop)
self._mock_windows_path: MagicMock = windows_path_patcher.start()
def test_check_path_exists_returns_true(self) -> None:
mocked_path: MagicMock = self._mock_path.return_value
mocked_path.exists.return_value = True
@@ -105,12 +101,35 @@ class TestFileUtils(TestCase):
assert not actual_files
def test_join_path_return_expected_result(self) -> None:
mocked_windows_path: MagicMock = self._mock_windows_path.return_value
mocked_path: MagicMock = self._mock_path.return_value
expected_join_path_name: str = f"{TestFileUtils._expected_path_name}{TestFileUtils._expected_file_name}"
mocked_windows_path.joinpath.return_value = expected_join_path_name
mocked_path.joinpath.return_value = expected_join_path_name
actual_join_path_name: str = file_utils.join_path(TestFileUtils._expected_path_name,
TestFileUtils._expected_file_name)
self._mock_windows_path.assert_called_once_with(TestFileUtils._expected_path_name)
mocked_windows_path.joinpath.assert_called_once_with(TestFileUtils._expected_file_name)
self._mock_path.assert_called_once_with(TestFileUtils._expected_path_name)
mocked_path.joinpath.assert_called_once_with(TestFileUtils._expected_file_name)
assert actual_join_path_name == expected_join_path_name
def test_normalize_file_path_return_empty_when_input_is_empty(self) -> None:
actual_normalized_path: str = file_utils.normalize_file_path("")
assert actual_normalized_path == ""
def test_normalize_file_path_return_expected_result(self) -> None:
mocked_path: MagicMock = self._mock_path.return_value
expected_resolve_path: str = TestFileUtils._expected_path_name
mocked_path.resolve.return_value = expected_resolve_path
actual_resolve_path: str = file_utils.normalize_file_path("dummy")
self._mock_path.assert_called_once()
mocked_path.resolve.assert_called_once()
assert actual_resolve_path == expected_resolve_path
def test_normalize_file_path_return_empty_when_exception_raised(self) -> None:
mocked_path: MagicMock = self._mock_path.return_value
mocked_path.resolve.side_effect = RuntimeError()
actual_resolve_path: str = file_utils.normalize_file_path("dummy")
self._mock_path.assert_called_once()
mocked_path.resolve.assert_called_once()
assert actual_resolve_path == ""