Пример #1
0
    def setUp(self):
        self._qapp = mock_widget.mockQapp()
        self.obj = QtGui.QWidget()
        self.context = MuonDataContext()
        self.context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)
Пример #2
0
    def setUp(self):
        self.obj = QWidget()
        setup_context_for_tests(self)
        self.gui_variable_observer = Observer()

        self.gui_context.gui_variables_notifier.add_subscriber(self.gui_variable_observer)
        self.data_context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)
        self.gui_variable_observer.update = mock.MagicMock()
Пример #3
0
    def __init__(self, context, parent):
        self.inst_view = InstrumentWidgetView(parent)
        self.grp_view = HomeGroupingWidgetView(parent)
        self.run_info_view = HomeRunInfoWidgetView(parent)
        self.plot_view = HomePlotWidgetView(parent)

        # keep a handle to the presenters of sub-widgets
        self.instrument_widget = InstrumentWidgetPresenter(self.inst_view,
                                                           InstrumentWidgetModel(context=context))
        self.group_widget = HomeGroupingWidgetPresenter(self.grp_view, HomeGroupingWidgetModel(context=context))
        self.run_info_widget = HomeRunInfoWidgetPresenter(self.run_info_view,
                                                          HomeRunInfoWidgetModel(context=context))
        self.plot_widget = HomePlotWidgetPresenter(self.plot_view, HomePlotWidgetModel(), context)
        self.home_tab_view = HomeTabView(parent=parent,
                                         widget_list=[self.inst_view,
                                                      self.grp_view,
                                                      self.plot_view,
                                                      self.run_info_view])
        self.home_tab_model = HomeTabModel(context=context)
        self.home_tab_widget = HomeTabPresenter(self.home_tab_view, self.home_tab_model,
                                                subwidgets=[self.instrument_widget,
                                                            self.group_widget,
                                                            self.plot_widget,
                                                            self.run_info_widget])

        context.update_view_from_model_notifier.add_subscriber(self.home_tab_widget.update_view_from_model_observer)
Пример #4
0
    def __init__(self, context, parent):
        self.inst_view = InstrumentWidgetView(parent)
        self.grp_view = HomeGroupingWidgetView(parent)
        self.plot_view = HomePlotWidgetView(parent)
        self.run_info_view = HomeRunInfoWidgetView(parent)

        # keep a handle to the presenters of sub-widgets
        self.instrument_widget = InstrumentWidgetPresenter(
            self.inst_view, InstrumentWidgetModel(muon_data=context))
        self.group_widget = HomeGroupingWidgetPresenter(
            self.grp_view, HomeGroupingWidgetModel(muon_data=context))
        self.plot_widget = HomePlotWidgetPresenter(self.plot_view,
                                                   HomePlotWidgetModel())
        self.run_info_widget = HomeRunInfoWidgetPresenter(
            self.run_info_view, HomeRunInfoWidgetModel(muon_data=context))

        self.home_tab_view = HomeTabView(parent=parent,
                                         widget_list=[
                                             self.inst_view, self.grp_view,
                                             self.plot_view, self.run_info_view
                                         ])
        self.home_tab_model = HomeTabModel(muon_data=context)
        self.home_tab_widget = HomeTabPresenter(self.home_tab_view,
                                                self.home_tab_model,
                                                subwidgets=[
                                                    self.instrument_widget,
                                                    self.group_widget,
                                                    self.plot_widget,
                                                    self.run_info_widget
                                                ])
    def setUp(self):
        self._qapp = mock_widget.mockQapp()
        self.obj = QtGui.QWidget()
        setup_context_for_tests(self)
        self.gui_variable_observer = Observer()

        self.gui_context.gui_variables_notifier.add_subscriber(self.gui_variable_observer)
        self.data_context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)
        self.gui_variable_observer.update = mock.MagicMock()
Пример #6
0
class HomeTabInstrumentPresenterTest(unittest.TestCase):
    def setUp(self):
        self._qapp = mock_widget.mockQapp()
        self.obj = QtGui.QWidget()
        self.context = MuonDataContext()
        self.context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)

    def tearDown(self):
        self.obj = None

    def test_that_on_instrument_change_model_instrument_updated(self):
        self.view.set_instrument('MUSR')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_to_non_Muon_instrument_fails(self):
        self.view.set_instrument('MUSR')
        self.view.set_instrument('RANDOM')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_with_signal_blocking_does_not_update_model(
            self):
        self.view.set_instrument('CHRONUS', block=False)
        self.view.set_instrument('MUSR', block=True)

        self.assertEqual(self.model._data.instrument, 'CHRONUS')

    def test_that_subscribers_notified_when_instrument_changed(self):
        observer = Observer()
        observer.update = mock.MagicMock()
        self.context.instrumentNotifier.add_subscriber(observer)

        self.view.set_instrument('MUSR', block=True)
        self.view.set_instrument('CHRONUS')

        observer.update.assert_called_once_with(
            self.context.instrumentNotifier, 'CHRONUS')

    def test_that_changeing_time_zero_updates_model(self):
        time_zero = 1.23456
        self.view.timezero_checkbox.setChecked(False)
        self.view.set_time_zero(time_zero)
        self.view.timezero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), round(time_zero, 3))

    def test_that_changeing_time_zero_does_nothing_if_checkbox_is_checked(
            self):
        time_zero = 1.23456
        self.view.timezero_checkbox.setChecked(True)
        self.view.set_time_zero(time_zero)
        self.view.timezero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)

    def test_that_changeing_time_zero_checkbox_toggles_between_user_and_file_time_zero_displayed(
            self):
        user_time_zero = 1.234
        self.model.set_user_time_zero(user_time_zero)

        self.view.timezero_checkbox.setChecked(True)
        self.assertEqual(self.view.get_time_zero(), 0.0)

        self.view.timezero_checkbox.setChecked(False)
        self.assertEqual(self.view.get_time_zero(), user_time_zero)

    def test_that_changeing_first_good_data_updates_model(self):
        time_zero = 1.23456
        self.view.firstgooddata_checkbox.setChecked(False)
        self.view.set_first_good_data(time_zero)
        self.view.firstgooddata_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_first_good_data(),
                         round(time_zero, 3))

    def test_that_changeing_first_good_data_does_nothing_if_checkbox_is_checked(
            self):
        time_zero = 1.23456
        self.view.firstgooddata_checkbox.setChecked(True)
        self.view.set_first_good_data(time_zero)
        self.view.firstgooddata_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)

    def test_that_changeing_first_good_data_checkbox_toggles_between_user_and_file_time_zero_displayed(
            self):
        user_time_zero = 1.234
        self.model.set_user_first_good_data(user_time_zero)

        self.view.firstgooddata_checkbox.setChecked(True)
        self.assertEqual(self.view.get_first_good_data(), 0.0)

        self.view.firstgooddata_checkbox.setChecked(False)
        self.assertEqual(self.view.get_first_good_data(), user_time_zero)

    def test_that_changeing_steps_rebin_updates_fixed_binning_in_model(self):
        self.view.rebin_steps_edit.setText('50')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.model._data.gui_variables['RebinFixed'], '50')

    def test_that_changeing_variable_rebin_updates_variable_binning_in_model(
            self):
        self.view.rebin_variable_edit.setText('1,5,21')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.model._data.gui_variables['RebinVariable'],
                         '1,5,21')

    def test_that_steps_only_accepts_a_single_integer(self):
        self.view.rebin_steps_edit.insert('1,0.1,70')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_steps_edit.text(), '')
        self.assertEqual(self.model._data.gui_variables['RebinFixed'], '')

    def test_that_variable_rebin_only_accepts_a_valid_rebin_string(self):
        self.view.rebin_variable_edit.insert('1-0.1-80')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_variable_edit.text(), '')

    def test_that_rebin_type_starts_as_none(self):
        self.assertEqual(self.model._data.gui_variables['RebinType'], 'None')

    def test_that_updating_rebin_combobox_updates_context(self):
        self.view.rebin_selector.setCurrentIndex(1)

        self.assertEqual(self.model._data.gui_variables['RebinType'], 'Fixed')

        self.view.rebin_selector.setCurrentIndex(2)

        self.assertEqual(self.model._data.gui_variables['RebinType'],
                         'Variable')

        self.view.rebin_selector.setCurrentIndex(0)

        self.assertEqual(self.model._data.gui_variables['RebinType'], 'None')

    def test_that_on_dead_time_unselected_deadtime_model_set_to_none(self):
        self.view.deadtime_selector.setCurrentIndex(1)
        self.view.deadtime_selector.setCurrentIndex(0)

        self.assertEqual(self.view.deadtime_label_3.text(),
                         self.presenter.dead_time_from_data_text([0.0]))
        self.assertEqual(self.model._data.current_data["DeadTimeTable"], None)

    def test_that_on_deadtime_data_selected_updates_with_no_loaded_data(self):
        self.view.deadtime_selector.setCurrentIndex(1)

        self.assertEqual(self.view.deadtime_label_3.text(),
                         "No loaded dead time")

    def test_that_on_deadtime_data_selected_updates_with_loaded_data(self):
        dead_time_data = mock.MagicMock()
        dead_time_data.toDict.return_value = {
            'dead-time': [0.001, 0.002, 0.003]
        }
        self.presenter._model.get_dead_time_table_from_data = mock.MagicMock(
            return_value=dead_time_data)

        self.view.deadtime_selector.setCurrentIndex(1)

        self.assertEqual(self.view.deadtime_label_3.text(),
                         'From 0.001 to 0.003 (ave. 0.002)')

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.get_table_workspace_names_from_ADS'
    )
    def test_that_selecting_from_table_workspace_deadtime_option_enables_table_workspace_combo_box(
            self, get_table_names_mock):
        get_table_names_mock.return_value = ['table_1', 'table_2', 'table_3']
        self.assertTrue(self.view.deadtime_file_selector.isHidden())

        self.view.deadtime_selector.setCurrentIndex(2)

        self.assertEqual(self.view.deadtime_label_3.text(),
                         "From 0.000 to 0.000 (ave. 0.000)")
        self.assertFalse(self.view.deadtime_file_selector.isHidden())
        self.assertEqual(self.view.deadtime_file_selector.count(), 4)
        self.assertEqual(self.view.deadtime_file_selector.itemText(0), 'None')
        self.assertEqual(self.view.deadtime_file_selector.itemText(1),
                         'table_1')
        self.assertEqual(self.view.deadtime_file_selector.itemText(2),
                         'table_2')
        self.assertEqual(self.view.deadtime_file_selector.itemText(3),
                         'table_3')

    def test_that_returning_to_None_options_hides_table_workspace_selector(
            self):
        self.view.deadtime_selector.setCurrentIndex(2)
        self.view.deadtime_selector.setCurrentIndex(0)

        self.assertEqual(self.view.deadtime_label_3.text(),
                         "From 0.000 to 0.000 (ave. 0.000)")
        self.assertTrue(self.view.deadtime_file_selector.isHidden())

    def test_browse_button_displayed_when_from_other_file_selected(self):
        self.assertTrue(self.view.deadtime_browse_button.isHidden())

        self.view.deadtime_selector.setCurrentIndex(3)

        self.assertFalse(self.view.deadtime_browse_button.isHidden())

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename'
    )
    def test_browse_clicked_displays_warning_popup_if_file_does_not_contain_table(
            self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock()
        load_deadtime_mock.return_value = ''
        self.view.deadtime_selector.setCurrentIndex(3)

        self.view.deadtime_browse_button.clicked.emit(True)

        self.view.show_file_browser_and_return_selection.assert_called_once_with(
            'Files (*.nxs)', [''], multiple_files=False)
        self.view.warning_popup.assert_called_once_with(
            "File does not appear to contain dead time data.")

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename'
    )
    def test_browse_clicked_does_nothing_if_no_file_selected(
            self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock(
            return_value=[''])
        self.view.deadtime_selector.setCurrentIndex(3)

        self.view.deadtime_browse_button.clicked.emit(True)

        load_deadtime_mock.assert_not_called()
        self.view.warning_popup.assert_not_called()

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename'
    )
    def test_browse_clicked_fails_if_table_not_loaded_into_ADS(
            self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock(
            return_value=['filename'])
        load_deadtime_mock.return_value = 'dead_time_table_name'
        self.view.deadtime_selector.setCurrentIndex(3)

        self.view.deadtime_browse_button.clicked.emit(True)

        self.assertEqual(self.view.deadtime_selector.currentIndex(), 2)
        self.view.warning_popup.assert_called_once_with(
            "Dead time table cannot be loaded")

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename'
    )
    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.get_table_workspace_names_from_ADS'
    )
    def test_browse_clicked_suceeds_if_table_in_ADS(self,
                                                    get_table_workspace_mock,
                                                    load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock(
            return_value=['filename'])
        get_table_workspace_mock.return_value = [
            'table_1', 'dead_time_table_name'
        ]
        load_deadtime_mock.return_value = 'dead_time_table_name'
        handle_table_changed_mock = mock.MagicMock()
        self.view.on_dead_time_file_option_changed(handle_table_changed_mock)

        self.view.deadtime_browse_button.clicked.emit(True)

        self.assertEqual(self.view.deadtime_selector.currentIndex(), 2)
        self.view.warning_popup.assert_not_called()
        self.assertEqual(self.view.deadtime_file_selector.currentText(),
                         'dead_time_table_name')
        self.assertEqual(handle_table_changed_mock.call_count, 3)

    def test_validate_variable_rebin_string_allows_single_number(self):
        result, message = self.model.validate_variable_rebin_string('0.034')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_does_not_allow_empty(self):
        result, message = self.model.validate_variable_rebin_string('')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_does_not_allow_non_numbers(self):
        result, message = self.model.validate_variable_rebin_string('abc')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_requires_second_number_of_two_to_be_greater(
            self):
        result, message = self.model.validate_variable_rebin_string('4,2')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('2,4')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_of_length_greater_than_three_must_have_bins_line_up(
            self):
        result, messag = self.model.validate_variable_rebin_string('1,5,21')

        self.assertTrue(result)

        result, message = self.model.validate_variable_rebin_string('1,5,20')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,5,21,4')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,-5,19')

        self.assertTrue(result)
class HomeTabInstrumentPresenterTest(unittest.TestCase):
    def setUp(self):
        self._qapp = mock_widget.mockQapp()
        self.obj = QtGui.QWidget()
        setup_context_for_tests(self)
        self.gui_variable_observer = Observer()

        self.gui_context.gui_variables_notifier.add_subscriber(self.gui_variable_observer)
        self.data_context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)
        self.gui_variable_observer.update = mock.MagicMock()

    def tearDown(self):
        self.obj = None

    def test_that_on_instrument_change_model_instrument_updated(self):
        self.view.set_instrument('MUSR')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_to_non_Muon_instrument_fails(self):
        self.view.set_instrument('MUSR')
        self.view.set_instrument('RANDOM')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_with_signal_blocking_does_not_update_model(self):
        self.view.set_instrument('CHRONUS', block=False)
        self.view.set_instrument('MUSR', block=True)

        self.assertEqual(self.model._data.instrument, 'CHRONUS')

    def test_that_subscribers_notified_when_instrument_changed(self):
        observer = Observer()
        observer.update = mock.MagicMock()
        self.data_context.instrumentNotifier.add_subscriber(observer)

        self.view.set_instrument('MUSR', block=True)
        self.view.set_instrument('CHRONUS')

        observer.update.assert_called_once_with(self.data_context.instrumentNotifier, 'CHRONUS')

    def test_that_changeing_time_zero_updates_model(self):
        time_zero = 1.23456
        self.view.time_zero_checkbox.setChecked(False)
        self.view.set_time_zero(time_zero)
        self.view.time_zero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), round(time_zero, 3))
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changeing_time_zero_does_nothing_if_checkbox_is_checked(self):
        time_zero = 1.23456
        self.view.time_zero_checkbox.setChecked(True)
        self.view.set_time_zero(time_zero)
        self.view.time_zero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_changeing_time_zero_checkbox_toggles_between_user_and_file_time_zero_displayed(self):
        user_time_zero = 1.234
        self.model.set_user_time_zero(user_time_zero)

        self.view.time_zero_checkbox.setChecked(True)
        self.assertEqual(self.view.get_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.time_zero_checkbox.setChecked(False)
        self.assertEqual(self.view.get_time_zero(), user_time_zero)
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changeing_first_good_data_updates_model(self):
        time_zero = 1.23456
        self.view.first_good_data_checkbox.setChecked(False)
        self.view.set_first_good_data(time_zero)
        self.view.first_good_data_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_first_good_data(), round(time_zero, 3))
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changeing_first_good_data_does_nothing_if_checkbox_is_checked(self):
        time_zero = 1.23456
        self.view.first_good_data_checkbox.setChecked(True)
        self.view.set_first_good_data(time_zero)
        self.view.first_good_data_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_changeing_first_good_data_checkbox_toggles_between_user_and_file_time_zero_displayed(self):
        user_time_zero = 1.234
        self.model.set_user_first_good_data(user_time_zero)

        self.view.first_good_data_checkbox.setChecked(True)
        self.assertEqual(self.view.get_first_good_data(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.first_good_data_checkbox.setChecked(False)
        self.assertEqual(self.view.get_first_good_data(), user_time_zero)
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changeing_steps_rebin_updates_fixed_binning_in_model(self):
        self.view.rebin_steps_edit.setText('50')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.model._context.gui_context['RebinFixed'], '50')
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_that_changeing_variable_rebin_updates_variable_binning_in_model(self):
        self.view.rebin_variable_edit.setText('1,5,21')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.model._context.gui_context['RebinVariable'], '1,5,21')
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_that_steps_only_accepts_a_single_integer(self):
        self.view.rebin_steps_edit.insert('1,0.1,70')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_steps_edit.text(), '')
        self.assertEqual(self.model._context.gui_context['RebinFixed'], '')
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_that_variable_rebin_only_accepts_a_valid_rebin_string(self):
        self.view.rebin_variable_edit.insert('1-0.1-80')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_variable_edit.text(), '')

    def test_that_rebin_type_starts_as_none(self):
        self.assertEqual(self.model._context.gui_context['RebinType'], 'None')

    def test_that_updating_rebin_combobox_updates_context(self):
        self.view.rebin_selector.setCurrentIndex(1)

        self.assertEqual(self.model._context.gui_context['RebinType'], 'Fixed')
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.rebin_selector.setCurrentIndex(2)

        self.assertEqual(self.model._context.gui_context['RebinType'], 'Variable')
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

        self.view.rebin_selector.setCurrentIndex(0)

        self.assertEqual(self.model._context.gui_context['RebinType'], 'None')
        self.assertEqual(self.gui_variable_observer.update.call_count, 3)

    def test_that_on_dead_time_unselected_deadtime_model_set_to_none(self):
        self.view.dead_time_selector.setCurrentIndex(1)
        self.context.gui_context['DeadTimeSource'] = 'FromFile'
        self.view.dead_time_selector.setCurrentIndex(0)

        self.assertEqual(self.view.dead_time_label_3.text(), self.presenter.dead_time_from_data_text([0.0]))
        self.assertEqual(self.model._data.current_data["DeadTimeTable"], None)
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_that_on_deadtime_data_selected_updates_with_no_loaded_data(self):
        self.view.dead_time_selector.setCurrentIndex(1)

        self.assertEqual(self.view.dead_time_label_3.text(), "No loaded dead time")
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_on_deadtime_data_selected_updates_with_loaded_data(self):
        dead_time_data = mock.MagicMock()
        dead_time_data.toDict.return_value = {'dead-time': [0.001, 0.002, 0.003]}
        self.presenter._model.get_dead_time_table_from_data = mock.MagicMock(return_value=dead_time_data)

        self.view.dead_time_selector.setCurrentIndex(1)

        self.assertEqual(self.view.dead_time_label_3.text(), 'From 0.001 to 0.003 (ave. 0.002)')
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.get_table_workspace_names_from_ADS')
    def test_that_selecting_from_table_workspace_deadtime_option_enables_table_workspace_combo_box(self,
                                                                                                   get_table_names_mock):
        get_table_names_mock.return_value = ['table_1', 'table_2', 'table_3']
        self.assertTrue(self.view.dead_time_file_selector.isHidden())

        self.view.dead_time_selector.setCurrentIndex(2)

        self.assertEqual(self.view.dead_time_label_3.text(), "From 0.000 to 0.000 (ave. 0.000)")
        self.assertFalse(self.view.dead_time_file_selector.isHidden())
        self.assertEqual(self.view.dead_time_file_selector.count(), 4)
        self.assertEqual(self.view.dead_time_file_selector.itemText(0), 'None')
        self.assertEqual(self.view.dead_time_file_selector.itemText(1), 'table_1')
        self.assertEqual(self.view.dead_time_file_selector.itemText(2), 'table_2')
        self.assertEqual(self.view.dead_time_file_selector.itemText(3), 'table_3')
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_returning_to_None_options_hides_table_workspace_selector(self):
        self.view.dead_time_selector.setCurrentIndex(2)
        self.context.gui_context['DeadTimeSource'] = 'FromADS'
        self.view.dead_time_selector.setCurrentIndex(0)

        self.assertEqual(self.view.dead_time_label_3.text(), "From 0.000 to 0.000 (ave. 0.000)")
        self.assertTrue(self.view.dead_time_file_selector.isHidden())
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_browse_button_displayed_when_from_other_file_selected(self):
        self.assertTrue(self.view.dead_time_browse_button.isHidden())

        self.view.dead_time_selector.setCurrentIndex(3)

        self.assertFalse(self.view.dead_time_browse_button.isHidden())
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename')
    def test_browse_clicked_displays_warning_popup_if_file_does_not_contain_table(self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock()
        load_deadtime_mock.return_value = ''
        self.view.dead_time_selector.setCurrentIndex(3)

        self.view.dead_time_browse_button.clicked.emit(True)

        self.view.show_file_browser_and_return_selection.assert_called_once_with('Files (*.nxs)', [''],
                                                                                 multiple_files=False)
        self.view.warning_popup.assert_called_once_with("File does not appear to contain dead time data.")
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)


    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename')
    def test_browse_clicked_does_nothing_if_no_file_selected(self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock(return_value=[''])
        self.view.dead_time_selector.setCurrentIndex(3)

        self.view.dead_time_browse_button.clicked.emit(True)

        load_deadtime_mock.assert_not_called()
        self.view.warning_popup.assert_not_called()
        self.gui_variable_observer.update.assert_not_called()

    @mock.patch(
        'Muon.GUI.Common.home_instrument_widget.home_instrument_widget_presenter.load_utils.load_dead_time_from_filename')
    def test_browse_clicked_fails_if_table_not_loaded_into_ADS(self, load_deadtime_mock):
        self.view.show_file_browser_and_return_selection = mock.MagicMock(return_value=['filename'])
        load_deadtime_mock.return_value = 'dead_time_table_name'
        self.view.dead_time_selector.setCurrentIndex(3)

        self.view.dead_time_browse_button.clicked.emit(True)

        self.assertEqual(self.view.dead_time_selector.currentIndex(), 2)
        self.view.warning_popup.assert_called_once_with("Dead time table cannot be loaded")
        self.gui_variable_observer.update.assert_not_called()

    def test_browse_clicked_suceeds_if_table_in_ADS(self):
        filename = FileFinder.findRuns('MUSR00015196.nxs')[0]
        self.view.show_file_browser_and_return_selection = mock.MagicMock(return_value=[filename])
        self.model.check_dead_time_file_selection = mock.MagicMock(return_value=True)

        self.view.dead_time_browse_button.clicked.emit(True)

        self.assertEqual(self.view.dead_time_selector.currentIndex(), 2)
        self.view.warning_popup.assert_not_called()
        self.assertEqual(self.view.dead_time_file_selector.currentText(), 'MUSR00015196_deadTimes')
        self.gui_variable_observer.update.assert_called_once_with(self.gui_context.gui_variables_notifier, None)

    def test_validate_variable_rebin_string_allows_single_number(self):
        result, message = self.model.validate_variable_rebin_string('0.034')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_does_not_allow_empty(self):
        result, message = self.model.validate_variable_rebin_string('')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_does_not_allow_non_numbers(self):
        result, message = self.model.validate_variable_rebin_string('abc')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_requires_second_number_of_two_to_be_greater(self):
        result, message = self.model.validate_variable_rebin_string('4,2')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('2,4')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_of_length_greater_than_three_must_have_bins_line_up(self):
        result, messag = self.model.validate_variable_rebin_string('1,5,21')

        self.assertTrue(result)

        result, message = self.model.validate_variable_rebin_string('1,5,20')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,5,21,4')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,-5,19')

        self.assertTrue(result)
class HomeTabInstrumentPresenterTest(unittest.TestCase):
    def setUp(self):
        self.obj = QWidget()
        setup_context_for_tests(self)
        self.gui_variable_observer = Observer()

        self.gui_context.gui_variables_notifier.add_subscriber(
            self.gui_variable_observer)
        self.data_context.instrument = 'MUSR'
        self.view = InstrumentWidgetView(self.obj)
        self.view.set_instrument('MUSR', block=True)
        self.model = InstrumentWidgetModel(self.context)
        self.presenter = InstrumentWidgetPresenter(self.view, self.model)

        self.view.warning_popup = mock.MagicMock()
        self.view.instrument_changed_warning = mock.MagicMock(return_value=1)
        self.gui_variable_observer.update = mock.MagicMock()

    def tearDown(self):
        self.obj = None

    def test_that_on_instrument_change_model_instrument_updated(self):
        self.view.set_instrument('MUSR')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_to_non_Muon_instrument_fails(self):
        self.view.set_instrument('MUSR')
        self.view.set_instrument('RANDOM')

        self.assertEqual(self.model._data.instrument, 'MUSR')

    def test_that_setting_instrument_with_signal_blocking_does_not_update_model(
            self):
        self.view.set_instrument('CHRONUS', block=False)
        self.view.set_instrument('MUSR', block=True)

        self.assertEqual(self.model._data.instrument, 'CHRONUS')

    def test_that_subscribers_notified_when_instrument_changed(self):
        observer = Observer()
        observer.update = mock.MagicMock()
        self.data_context.instrumentNotifier.add_subscriber(observer)

        self.view.set_instrument('MUSR', block=True)
        self.view.set_instrument('CHRONUS')

        observer.update.assert_called_once_with(
            self.data_context.instrumentNotifier, 'CHRONUS')

    def test_that_changing_time_zero_updates_model(self):
        time_zero = 1.23456
        self.view.time_zero_checkbox.setChecked(False)
        self.view.set_time_zero(time_zero)
        self.view.time_zero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), round(time_zero, 3))
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changing_time_zero_does_nothing_if_checkbox_is_checked(self):
        time_zero = 1.23456
        self.view.time_zero_checkbox.setChecked(True)
        self.view.set_time_zero(time_zero)
        self.view.time_zero_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_changing_time_zero_checkbox_toggles_between_user_and_file_time_zero_displayed(
            self):
        user_time_zero = 1.234
        self.model.set_user_time_zero(user_time_zero)

        self.view.time_zero_checkbox.setChecked(True)
        self.assertEqual(self.view.get_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.time_zero_checkbox.setChecked(False)
        self.assertEqual(self.view.get_time_zero(), user_time_zero)
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changing_first_good_data_updates_model(self):
        time_zero = 1.23456
        self.view.first_good_data_checkbox.setChecked(False)
        self.view.set_first_good_data(time_zero)
        self.view.first_good_data_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_first_good_data(),
                         round(time_zero, 3))
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changing_first_good_data_does_nothing_if_checkbox_is_checked(
            self):
        time_zero = 1.23456
        self.view.first_good_data_checkbox.setChecked(True)
        self.view.set_first_good_data(time_zero)
        self.view.first_good_data_edit.editingFinished.emit()

        self.assertEqual(self.model.get_user_time_zero(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 0)

    def test_that_changing_first_good_data_checkbox_toggles_between_user_and_file_time_zero_displayed(
            self):
        user_time_zero = 1.234
        self.model.set_user_first_good_data(user_time_zero)

        self.view.first_good_data_checkbox.setChecked(True)
        self.assertEqual(self.view.get_first_good_data(), 0.0)
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.first_good_data_checkbox.setChecked(False)
        self.assertEqual(self.view.get_first_good_data(), user_time_zero)
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

    def test_that_changing_steps_rebin_updates_fixed_binning_in_model(self):
        self.view.rebin_steps_edit.setText('50')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.model._context.gui_context['RebinFixed'], '50')
        self.gui_variable_observer.update.assert_called_once_with(
            self.gui_context.gui_variables_notifier, {'RebinFixed': '50'})

    def test_that_changing_variable_rebin_updates_variable_binning_in_model(
            self):
        self.view.rebin_variable_edit.setText('1,5,21')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.model._context.gui_context['RebinVariable'],
                         '1,5,21')
        self.gui_variable_observer.update.assert_called_once_with(
            self.gui_context.gui_variables_notifier,
            {'RebinVariable': '1,5,21'})

    def test_that_steps_only_accepts_a_single_integer(self):
        self.view.rebin_steps_edit.insert('1,0.1,70')
        self.view.rebin_steps_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_steps_edit.text(), '')
        self.assertEqual(self.model._context.gui_context['RebinFixed'], '')
        self.gui_variable_observer.update.assert_called_once_with(
            self.gui_context.gui_variables_notifier, {'RebinFixed': ''})

    def test_that_variable_rebin_only_accepts_a_valid_rebin_string(self):
        self.view.rebin_variable_edit.insert('1-0.1-80')
        self.view.rebin_variable_edit.editingFinished.emit()

        self.assertEqual(self.view.rebin_variable_edit.text(), '')

    def test_that_rebin_type_starts_as_none(self):
        self.assertEqual(self.model._context.gui_context['RebinType'], 'None')

    def test_that_updating_rebin_combobox_updates_context(self):
        self.view.rebin_selector.setCurrentIndex(1)

        self.assertEqual(self.model._context.gui_context['RebinType'], 'Fixed')
        self.assertEqual(self.gui_variable_observer.update.call_count, 1)

        self.view.rebin_selector.setCurrentIndex(2)

        self.assertEqual(self.model._context.gui_context['RebinType'],
                         'Variable')
        self.assertEqual(self.gui_variable_observer.update.call_count, 2)

        self.view.rebin_selector.setCurrentIndex(0)

        self.assertEqual(self.model._context.gui_context['RebinType'], 'None')
        self.assertEqual(self.gui_variable_observer.update.call_count, 3)

    def test_validate_variable_rebin_string_allows_single_number(self):
        result, message = self.model.validate_variable_rebin_string('0.034')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_does_not_allow_empty(self):
        result, message = self.model.validate_variable_rebin_string('')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_does_not_allow_non_numbers(self):
        result, message = self.model.validate_variable_rebin_string('abc')

        self.assertFalse(result)

    def test_validate_variable_rebin_string_requires_second_number_of_two_to_be_greater(
            self):
        result, message = self.model.validate_variable_rebin_string('4,2')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('2,4')

        self.assertTrue(result)

    def test_validate_variable_rebin_string_of_length_greater_than_three_must_have_bins_line_up(
            self):
        result, messag = self.model.validate_variable_rebin_string('1,5,21')

        self.assertTrue(result)

        result, message = self.model.validate_variable_rebin_string('1,5,20')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,5,21,4')

        self.assertFalse(result)

        result, message = self.model.validate_variable_rebin_string('1,-5,19')

        self.assertTrue(result)

    def test_last_good_data_from_file(self):
        self.view.last_good_data_state = mock.Mock(return_value=True)
        self.view.set_last_good_data = mock.MagicMock()
        self.presenter.update_view_from_model()

        self.assertEqual(0.0, self.model.get_file_last_good_data())
        self.view.set_last_good_data.assert_called_once_with(0.0)

    def test_last_good_data_from_gui(self):
        self.view.last_good_data_state = mock.Mock(return_value=False)
        self.view.set_last_good_data = mock.MagicMock()
        self.context.gui_context["LastGoodData"] = 2.0
        self.presenter.update_view_from_model()

        self.assertEqual(2.0, self.model.get_last_good_data())
        self.view.set_last_good_data.assert_called_once_with(2.0)