def setUpClass(cls): cls.main_window = MainWindow()
def create_and_launch_workbench(app, command_line_options): """Given an application instance create the MainWindow, show it and start the main event loop """ exit_value = 0 try: # MainWindow needs to be imported locally to ensure the matplotlib # backend is not imported too early. from workbench.app.mainwindow import MainWindow # The ordering here is very delicate. Test thoroughly when # changing anything! main_window = MainWindow() # Set the mainwindow as the parent for additional QMainWindow instances from workbench.config import set_additional_windows_parent set_additional_windows_parent(main_window) # decorates the excepthook callback with the reference to the main window # this is used in case the user wants to terminate the workbench from the error window shown sys.excepthook = partial(exception_logger, main_window) # Load matplotlib as early as possible and set our defaults # Setup our custom backend and monkey patch in custom current figure manager main_window.set_splash('Preloading matplotlib') from workbench.plotting.config import initialize_matplotlib # noqa initialize_matplotlib() # Setup widget layouts etc. mantid.simple cannot be used before this # or the log messages don't get through to the widget main_window.setup() # start mantid main_window.set_splash('Initializing mantid framework') FrameworkManagerImpl.Instance() main_window.post_mantid_init() if main_window.splash: main_window.splash.hide() if command_line_options.script is not None: main_window.editor.open_file_in_new_tab( command_line_options.script) editor_task = None if command_line_options.execute: # if the quit flag is not specified, this task reference will be # GC'ed, and the task will be finished alongside the GUI startup editor_task = main_window.editor.execute_current_async() if command_line_options.quit: # wait for the code interpreter thread to finish executing the script editor_task.join() main_window.close() # for task exit code descriptions see the classes AsyncTask and TaskExitCode return int(editor_task.exit_code) if editor_task else 0 main_window.show() main_window.setWindowIcon(QIcon(':/images/MantidIcon.ico')) # Project Recovery on startup main_window.project_recovery.repair_checkpoints() if main_window.project_recovery.check_for_recover_checkpoint(): main_window.project_recovery.attempt_recovery() else: main_window.project_recovery.start_recovery_thread() if not (command_line_options.execute or command_line_options.quit): if AboutPresenter.should_show_on_startup(): AboutPresenter(main_window).show() # lift-off! exit_value = app.exec_() except BaseException: # We count this as a crash import traceback # This is type of thing we want to capture and have reports # about. Prints to stderr as we can't really count on anything # else traceback.print_exc(file=ORIGINAL_STDERR) try: print_file_path = os.path.join(ConfigService.getAppDataDirectory(), STACKTRACE_FILE) with open(print_file_path, 'w') as print_file: traceback.print_exc(file=print_file) except OSError: pass exit_value = -1 finally: ORIGINAL_SYS_EXIT(exit_value)
class MainWindowTest(unittest.TestCase): def setUp(self): from workbench.app.mainwindow import MainWindow self.main_window = MainWindow() def tearDown(self): self.main_window.close() @patch("workbench.app.mainwindow.find_window") def test_launch_custom_cpp_gui_creates_interface_if_not_already_open( self, mock_find_window): mock_find_window.return_value = None interface_name = 'ISIS Reflectometry' with patch.object(self.main_window, 'interface_manager') as mock_interface_manager: self.main_window.launch_custom_cpp_gui(interface_name) mock_interface_manager.createSubWindow.assert_called_once_with( interface_name) @patch("workbench.app.mainwindow.find_window") def test_different_interfaces_simultaneously_created( self, mock_find_window): mock_find_window.return_value = None interface_name = 'Data Reduction' second_interface_name = 'Settings' with patch.object(self.main_window, 'interface_manager') as mock_interface_manager: self.main_window.launch_custom_cpp_gui(interface_name) mock_interface_manager.createSubWindow.assert_called_with( interface_name) self.main_window.launch_custom_cpp_gui(second_interface_name) mock_interface_manager.createSubWindow.assert_called_with( second_interface_name) @patch("workbench.app.mainwindow.FrameworkManager") @patch("workbench.app.mainwindow.QMessageBox") def test_clear_all_memory_calls_frameworkmanager_when_user_presses_ok( self, mock_msg_box, mock_fm): mock_msg_box_instance = MagicMock(spec=QMessageBox) mock_msg_box.return_value = mock_msg_box_instance mock_msg_box_instance.exec.return_value = mock_msg_box.Ok mock_fm_instance = MagicMock(spec=FrameworkManager) mock_fm.Instance.return_value = mock_fm_instance self.main_window.clear_all_memory_action() mock_fm_instance.clear.assert_called_once() @patch("workbench.app.mainwindow.FrameworkManager") @patch("workbench.app.mainwindow.QMessageBox") def test_clear_all_memory_does_not_call_frameworkmanager_when_user_presses_cancel( self, mock_msg_box, mock_fm): mock_msg_box_instance = MagicMock(spec=QMessageBox) mock_msg_box.return_value = mock_msg_box_instance mock_msg_box_instance.exec.return_value = mock_msg_box.Cancel mock_fm_instance = MagicMock(spec=FrameworkManager) mock_fm.Instance.return_value = mock_fm_instance self.main_window.clear_all_memory_action() mock_fm_instance.clear.assert_not_called() @patch('workbench.plugins.logmessagedisplay.ORIGINAL_STDOUT', new=StringIO()) @patch('workbench.plugins.logmessagedisplay.ORIGINAL_STDERR', new=StringIO()) @patch( 'workbench.plugins.logmessagedisplay.MessageDisplay.append_script_notice' ) @patch( 'workbench.plugins.logmessagedisplay.MessageDisplay.append_script_error' ) def test_after_setup_stdout_and_stderr_are_captured( self, append_script_notice, append_script_error): original_stdout = sys.stdout original_stderr = sys.stderr try: matplotlib.use("agg") self.main_window.setup() print('test stdout') print('test stderr', file=sys.stderr) finally: # whatever happened, we need to reset these so unittest can report it! sys.stdout = original_stdout sys.stderr = original_stderr append_script_notice.assert_called() append_script_error.assert_called() def test_menus_exist(self): self.main_window.menuBar().addMenu = MagicMock() expected_calls = [ call("&File"), call("&View"), call("&Interfaces"), call("&Help") ] self.main_window.create_menus() self.main_window.menuBar().addMenu.assert_has_calls(expected_calls, any_order=False) @patch('workbench.app.mainwindow.add_actions') def test_file_view_and_help_menus_are_correct(self, mock_add_actions): def convert_action_to_text(menu_item): """Takes an item on a mainwindow menu, and returns a representation of the item so we can assert whether menus look right to the user. """ if isinstance(menu_item, QAction): return menu_item.text() if not menu_item: return None return type(menu_item) self.main_window.editor = Mock() self.main_window.populate_interfaces_menu = Mock() expected_file_menu_items = [ 'Open Script', 'Open Project', None, 'Save Script', 'Save Script as...', RecentlyClosedScriptsMenu, 'Generate Recovery Script', None, 'Save Project', 'Save Project as...', None, 'Settings', None, 'Manage User Directories', None, 'Script Repository', None, 'Clear All Memory', None, '&Quit' ] # There are no wigets on this instance of MainWindow, so they will not appear on the view menu. expected_view_menu_items = ['Restore Default Layout', None] expected_help_menu_items = [ 'Mantid Help', 'Mantid Concepts', 'Algorithm Descriptions', None, 'Mantid Homepage', 'Mantid Forum', None, 'About Mantid Workbench' ] self.main_window.create_menus() self.main_window.create_actions() self.main_window.populate_menus() actual_file_menu_items = list( map(convert_action_to_text, self.main_window.file_menu_actions)) actual_view_menu_items = list( map(convert_action_to_text, self.main_window.view_menu_actions)) actual_help_menu_items = list( map(convert_action_to_text, self.main_window.help_menu_actions)) self.assertEqual(expected_file_menu_items, actual_file_menu_items) self.assertEqual(expected_view_menu_items, actual_view_menu_items) self.assertEqual(expected_help_menu_items, actual_help_menu_items) mock_add_actions.assert_has_calls([ call(self.main_window.file_menu, self.main_window.file_menu_actions), call(self.main_window.view_menu, self.main_window.view_menu_actions), call(self.main_window.help_menu, self.main_window.help_menu_actions), ]) @patch('workbench.app.mainwindow.add_actions') def test_interfaces_menu_texts_are_correct(self, _): interface_dir = './interfaces/' example_interfaces = { 'General': ['TOFCalculator'], 'Direct': [ 'DGS_Reduction.py', 'DGSPlanner.py', 'PyChop.py', 'MSlice.py', 'ALF View' ] } with patch('workbench.app.mainwindow.ConfigService', new={ 'interfaces.categories.hidden': '', 'mantidqt.python_interfaces_directory': interface_dir }): self.main_window._discover_python_interfaces = Mock( return_value=(example_interfaces, {})) self.main_window._discover_cpp_interfaces = Mock() self.main_window.create_menus() self.main_window.populate_interfaces_menu() expected_menu_texts = ['Direct', 'General'] # Alphabetical order actual_menu_texts = [ action.text() for action in self.main_window.interfaces_menu.actions() ] self.assertEqual(expected_menu_texts, actual_menu_texts) expected_direct_texts = [ 'ALF View', 'DGSPlanner', 'DGS Reduction', 'MSlice', 'PyChop' ] expected_general_texts = ['TOFCalculator'] submenus = list( filter(lambda child: isinstance(child, QMenu), self.main_window.interfaces_menu.children())) actual_direct_texts = [ action.text() for action in submenus[0].actions() ] actual_general_texts = [ action.text() for action in submenus[1].actions() ] self.assertEqual(expected_direct_texts, actual_direct_texts) self.assertEqual(expected_general_texts, actual_general_texts) @patch('workbench.app.mainwindow.add_actions') def test_that_populate_interfaces_menu_discovers_interfaces(self, _): interface_dir = './interfaces/' interfaces = {'category': ['interface.py']} self.main_window._discover_python_interfaces = Mock( return_value=(interfaces, {})) self.main_window._discover_cpp_interfaces = Mock() with patch('workbench.app.mainwindow.ConfigService', new={ 'interfaces.categories.hidden': '', 'mantidqt.python_interfaces_directory': interface_dir }): self.main_window.create_menus() self.main_window.populate_interfaces_menu() self.main_window._discover_python_interfaces.assert_called_with( interface_dir) self.main_window._discover_cpp_interfaces.assert_called_with( interfaces) def test_that_populate_interfaces_menu_ignores_hidden_interfaces(self): interface_dir = './interfaces/' self.main_window._discover_python_interfaces = Mock(return_value=({ 'category1': ['interface1.py'], 'category2': ['interface2.py'] }, {})) self.main_window._discover_cpp_interfaces = Mock() self.main_window.interfaces_menu = Mock() ConfigService_dict = { 'interfaces.categories.hidden': 'category1;category2', 'mantidqt.python_interfaces_directory': interface_dir } with patch.object(self.main_window, 'interfaces_menu') as mock_interfaces_menu: with patch('workbench.app.mainwindow.ConfigService', new=ConfigService_dict): self.main_window.populate_interfaces_menu() mock_interfaces_menu.addMenu.assert_not_called() def test_main_window_does_not_close_when_project_is_saving(self): mock_event = Mock() mock_project = Mock() mock_project.is_saving = True self.main_window.project = mock_project self.main_window.closeEvent(mock_event) mock_event.ignore.assert_called() mock_project.inform_user_not_possible.assert_called() def test_main_window_does_not_close_when_project_is_loading(self): mock_event = Mock() mock_project = Mock() mock_project.is_loading = True self.main_window.project = mock_project self.main_window.closeEvent(mock_event) mock_event.ignore.assert_called() mock_project.inform_user_not_possible.assert_called() def test_main_window_does_not_close_if_project_not_saved_and_user_cancels_project_save( self): mock_event = Mock() mock_project = Mock() mock_project.is_saving, mock_project.is_loading, mock_project.saved, = False, False, False mock_project.offer_save = Mock( return_value=True) # user cancels when save offered self.main_window.project = mock_project self.main_window.closeEvent(mock_event) mock_project.offer_save.assert_called() mock_event.ignore.assert_called() @patch('workbench.app.mainwindow.ConfigService') @patch('workbench.app.mainwindow.QApplication') @patch('matplotlib.pyplot.close') def test_main_window_close_behavior_correct_when_workbench_able_to_be_closed( self, mock_plt_close, mock_QApplication, mock_ConfigService): mock_event = Mock() mock_project = Mock() mock_project.is_saving, mock_project.is_loading, mock_project.saved = False, False, True mock_editor = Mock() mock_editor.app_closing = Mock( return_value=True) # Editors can be closed mock_project_recovery = Mock() mock_project_recovery.stop_recovery_thread, mock_project_recovery.remove_current_pid_folder =\ Mock(), Mock() self.main_window.editor = mock_editor self.main_window.writeSettings = Mock() self.main_window.project_recovery = mock_project_recovery self.main_window.interface_manager = Mock() self.main_window.writeSettings = Mock() self.main_window.project = mock_project self.main_window.closeEvent(mock_event) mock_ConfigService.saveConfig.assert_called_with( mock_ConfigService.getUserFilename()) self.main_window.writeSettings.assert_called() mock_plt_close.assert_called_with('all') mock_QApplication.instance().closeAllWindows.assert_called() self.main_window.project_recovery.assert_has_calls( [call.stop_recovery_thread(), call.remove_current_pid_folder()]) self.assertTrue(self.main_window.project_recovery.closing_workbench) self.main_window.interface_manager.closeHelpWindow.assert_called() mock_event.accept.assert_called() @patch('workbench.app.mainwindow.logger') @patch('os.path.exists') def test_python_interfaces_are_discovered_correctly( self, mock_os_path_exists, _): interfaces = ['Muon/Frequency_Domain_Analysis.py', 'ILL/Drill.py'] interfaces_str = " ".join( interfaces) # config service returns them as a whole string. mock_os_path_exists.return_value = lambda path: path in interfaces self.main_window.PYTHON_GUI_BLACKLIST = [] with patch('workbench.app.mainwindow.ConfigService', new={'mantidqt.python_interfaces': interfaces_str}): returned_interfaces, registration_files = self.main_window._discover_python_interfaces( '') expected_interfaces = { 'Muon': ['Frequency_Domain_Analysis.py'], 'ILL': ['Drill.py'] } self.assertDictEqual(expected_interfaces, returned_interfaces) self.assertDictEqual({}, registration_files) @patch('workbench.app.mainwindow.logger') @patch('os.path.exists') def test_that_non_existent_python_interface_is_ignored_gracefully( self, mock_os_path_exists, mock_logger): interface_str = 'fake/interface.py' mock_os_path_exists.return_value = False self.main_window.PYTHON_GUI_BLACKLIST = [] with patch('workbench.app.mainwindow.ConfigService', new={'mantidqt.python_interfaces': interface_str}): returned_interfaces, registration_files = self.main_window._discover_python_interfaces( '') self.assertDictEqual({}, returned_interfaces) self.assertDictEqual({}, registration_files) mock_logger.warning.assert_called() @patch('workbench.app.mainwindow.UserSubWindowFactory') def test_cpp_interfaces_are_discovered_correctly( self, mock_UserSubWindowFactory): """Assuming we have already found some python interfaces, test that cpp interfaces are discovered correctly using the Direct interfaces as an example.""" cpp_interface_factory = Mock() cpp_interface_factory.keys.return_value = ['ALF View', 'TOFCalculator'] cpp_interface_factory.categories.side_effect = lambda name: [ 'Direct' ] if name == 'ALF View' else [] mock_UserSubWindowFactory.Instance.return_value = cpp_interface_factory all_interfaces = self.main_window._discover_cpp_interfaces({ 'Direct': ['DGS_Reduction.py', 'DGSPlanner.py', 'PyChop.py', 'MSlice.py'] }) expected_interfaces = { 'Direct': [ 'DGS_Reduction.py', 'DGSPlanner.py', 'PyChop.py', 'MSlice.py', 'ALF View' ], 'General': ['TOFCalculator'] } self.assertDictEqual(expected_interfaces, all_interfaces) @patch('workbench.app.mainwindow.input_qinputdialog') def test_override_python_input_replaces_input_with_qinputdialog( self, mock_input): self.main_window.override_python_input() input("prompt") mock_input.assert_called_with("prompt")
def setUp(self): from workbench.app.mainwindow import MainWindow self.main_window = MainWindow()
def setUpClass(cls): from workbench.app.mainwindow import MainWindow cls.main_window = MainWindow()