source: sasview/src/sas/qtgui/Utilities/UnitTesting/AddMultEditorTest.py @ 01ef3f7

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 01ef3f7 was 01ef3f7, checked in by celinedurniak <celine.durniak@…>, 6 years ago

Implemented new GUI for Add/Multiply? models

  • Property mode set to 100644
File size: 12.7 KB
Line 
1import sys
2import os
3import unittest
4import webbrowser
5import tempfile
6from unittest.mock import MagicMock, patch
7
8from PyQt5 import QtGui
9from PyQt5 import QtWidgets
10
11# set up import paths
12import path_prepare
13
14from sas.qtgui.Utilities.GuiUtils import Communicate
15
16# Local
17from sas.qtgui.Utilities.AddMultEditor import AddMultEditor
18
19if not QtWidgets.QApplication.instance():
20    app = QtWidgets.QApplication(sys.argv)
21
22class dummy_manager(object):
23    HELP_DIRECTORY_LOCATION = "html"
24    communicate = Communicate()
25
26class AddMultEditorTest(unittest.TestCase):
27    """ Test the simple AddMultEditor dialog """
28    @patch.object(AddMultEditor, 'readModels')
29    def setUp(self, mock_list_models):
30        """ Create AddMultEditor dialog """
31
32        # mock models from plugin folder
33        mock_list_models.return_value = ['cylinder', 'rpa',
34                                         'core_shell_cylinder', 'sphere']
35
36        self.widget = AddMultEditor(dummy_manager())
37
38    def tearDown(self):
39        """ Destroy the GUI """
40
41        self.widget.close()
42        self.widget = None
43
44    def testDefaults(self):
45        """ Test the GUI in its default state """
46
47        self.assertIsInstance(self.widget, QtWidgets.QDialog)
48
49        self.assertEqual(self.widget.sizePolicy().verticalPolicy(), 0)
50        self.assertEqual(self.widget.sizePolicy().horizontalPolicy(), 5)
51
52        # Default title
53        self.assertEqual(self.widget.windowTitle(), "Easy Add/Multiply Editor")
54
55        # Default types
56        self.assertIsInstance(self.widget.cbOperator, QtWidgets.QComboBox)
57        self.assertIsInstance(self.widget.cbModel1, QtWidgets.QComboBox)
58        self.assertIsInstance(self.widget.cbModel2, QtWidgets.QComboBox)
59
60        # Modal window
61        self.assertFalse(self.widget.isModal())
62
63        # Checkbox not tristate, not checked
64        self.assertFalse(self.widget.chkOverwrite.isTristate())
65        self.assertFalse(self.widget.chkOverwrite.isChecked())
66
67        # Push buttons disabled
68        self.assertFalse(self.widget.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).isEnabled())
69
70        # Text of labels...
71        self.assertEqual(self.widget.chkOverwrite.text(),
72                         "Overwrite existing model")
73
74        # Allowed operators
75        self.assertListEqual([str(self.widget.cbOperator.itemText(i)) for i in
76                              range(self.widget.cbOperator.count())],
77                             ['+', '*'])
78
79        # Default operator
80        self.assertEqual(self.widget.cbOperator.currentText(), '+')
81
82        # checkbox unchecked
83        self.assertFalse(self.widget.chkOverwrite.isChecked())
84
85        # Default 'good_name' flag, name for plugin file and plugin dir
86        self.assertFalse(self.widget.good_name)
87        self.assertIsNone(self.widget.plugin_filename)
88
89        # default content of displayed equation (to create the new model)
90        self.assertEqual(self.widget.lblEquation.text(),
91                         "<html><head/><body><p>Plugin_model = "
92                         "scale_factor * (model_1 + model_2) + background"
93                         "</p></body></html>")
94
95        # Tooltips
96        self.assertEqual(self.widget.cbOperator.toolTip(),
97                         "Add: +\nMultiply: *")
98        self.assertEqual(self.widget.txtName.toolTip(),
99                         "Sum / Multiply model function name.")
100        self.widget.chkOverwrite.setChecked(True)
101
102        self.assertNotEqual(len(self.widget.chkOverwrite.toolTip()), 0)
103
104    def testOnModelComboboxes(self):
105        """ Test on Model1 and Model2 comboboxes """
106
107        # content of model_1 and model_2 comboboxes
108        # same content for the two comboboxes
109        self.assertEqual(self.widget.cbModel1.count(),
110                         self.widget.cbModel2.count())
111
112        self.assertEqual(self.widget.cbModel1.count(), 4)
113
114        # default of cbModel1 and cbModel2
115        self.assertEqual(self.widget.cbModel1.currentText(), 'sphere')
116        self.assertEqual(self.widget.cbModel2.currentText(), 'cylinder')
117
118    def testValidateName(self):
119        """ Test validity of output name (syntax only) """
120
121        # Invalid plugin name
122        self.widget.txtName.setText('+++')
123
124        state = self.widget.txtName.validator().validate(self.widget.txtName.text(), 0)[0]
125        self.assertEqual(state, QtGui.QValidator.Invalid)
126
127        # Valid new plugin name
128        self.widget.txtName.setText('cylinder_test_case')
129        state = \
130        self.widget.txtName.validator().validate(self.widget.txtName.text(),
131                                                 0)[0]
132        self.assertEqual(state, QtGui.QValidator.Acceptable)
133
134    def testOnApply(self):
135        """ Test onApply """
136
137        self.widget.txtName.setText("new_model")
138        self.widget.updateModels = MagicMock()
139
140        # make sure the flag is set correctly
141        self.widget.is_modified = True
142
143        # Mock self.write_new_model_to_file
144        self.widget.plugin_dir = tempfile.gettempdir()
145        self.widget.plugin_filename = \
146            os.path.join(self.widget.plugin_dir, 'new_model.py')
147
148        self.widget.write_new_mode_to_file = MagicMock()
149
150        # invoke the tested method
151        self.widget.onApply()
152
153        self.assertTrue(self.widget.write_new_mode_to_file.called_once_with(
154        os.path.join(self.widget.plugin_dir, 'new_model.py'),
155        self.widget.cbModel1.currentText(),
156        self.widget.cbModel2.currentText(),
157        self.widget.cbOperator.currentText()))
158
159        self.assertFalse(self.widget.buttonBox.button(QtWidgets.QDialogButtonBox.Apply).isEnabled())
160
161        self.assertTrue(self.widget.checkModel(self.widget.plugin_filename))
162
163        self.assertTrue(self.widget.updateModels.called_once())
164
165    def testWriteNewModelToFile(self):
166        """ Test content of generated plugin"""
167
168        dummy_file_path = os.path.join(tempfile.gettempdir(),
169                                       "test_datafile.py")
170
171        # prepare data to create file and check its content: names of 2 models,
172        # operator and description (default or not)
173        model1_name = self.widget.cbModel1.currentText()
174        model2_name = self.widget.cbModel2.currentText()
175        symbol_operator = self.widget.cbOperator.currentText()
176
177        # default description
178        desc_line = "{} {} {}".format(model1_name,
179                                      symbol_operator,
180                                      model2_name)
181
182        self.widget.write_new_model_to_file(dummy_file_path, model1_name,
183                                            model2_name, symbol_operator)
184        # check content of dummy file path
185        with open(dummy_file_path, 'r') as f_in:
186            lines_from_generated_file = [line.strip() for line in f_in]
187
188        # SUM_TEMPLATE with updated entries
189        completed_template = ["from sasmodels.core import load_model_info",
190                        "from sasmodels.sasview_model import make_model_from_info",
191                        "model_info = load_model_info('{model1}{operator}{model2}')".
192                            format(model1=model1_name,
193                                   operator=symbol_operator,
194                                   model2=model2_name),
195                        "model_info.name = '{}'".format("test_datafile"),
196                        "model_info.description = '{}'".format(desc_line),
197                        "Model = make_model_from_info(model_info)"]
198
199        for item in completed_template:
200            self.assertIn(item, lines_from_generated_file)
201
202        # check content with description entered by user
203        self.widget.txtDescription.setText('New description for test  ')
204
205        new_desc_line = "model_info.description = '{}'".format('New description for test')
206
207        # re-run function to test
208        self.widget.write_new_model_to_file(dummy_file_path, model1_name,
209                                            model2_name, symbol_operator)
210
211        # check content of dummy file path
212        with open(dummy_file_path, 'r') as f_in:
213            lines_from_generated_file = [line.strip() for line in f_in]
214
215        # update completed template
216        completed_template[4] = new_desc_line
217
218        # check content of generated file
219        for item in completed_template:
220            self.assertIn(item, lines_from_generated_file)
221
222    def testOnOperatorChange(self):
223        """
224        Test modification of displayed equation
225        when selected operator is changed
226        """
227
228        # check default
229        self.assertIn(self.widget.cbOperator.currentText(),
230                      self.widget.lblEquation.text())
231
232        # change operator
233        if self.widget.cbOperator.currentIndex() == 0:
234            self.widget.cbOperator.setCurrentIndex(1)
235        else:
236            self.widget.cbOperator.setCurrentIndex(0)
237
238        self.assertIn(self.widget.cbOperator.currentText(),
239                      self.widget.lblEquation.text())
240
241    def testOnHelp(self):
242        """ Test the default help renderer """
243
244        webbrowser.open = MagicMock()
245
246        # invoke the tested method
247        self.widget.onHelp()
248
249        # see that webbrowser open was attempted
250        webbrowser.open.assert_called()
251
252    def testOnNameCheck(self):
253        """ Test onNameCheck """
254
255        # Enter plugin name already present in list of existing models
256        self.widget.txtName.setText("cylinder")
257
258        # Scenario 1
259        # overwrite not checked -> message box should appear
260        # and good_name set to False, 'Apply' button disabled
261
262        # mock QMessageBox
263        QtWidgets.QMessageBox.critical = MagicMock()
264
265        self.widget.chkOverwrite.setChecked(False)
266        self.widget.txtName.editingFinished.emit()
267        self.assertFalse(self.widget.good_name)
268        self.assertFalse(self.widget.buttonBox.button(
269            QtWidgets.QDialogButtonBox.Apply).isEnabled())
270
271        self.assertTrue(QtWidgets.QMessageBox.critical.called_once())
272
273        msg = "Plugin with specified name already exists.\n"
274        msg += "Please specify different filename or allow file overwrite."
275
276        self.assertIn('Plugin Error', QtWidgets.QMessageBox.critical.call_args[0][1])
277        self.assertIn(msg, QtWidgets.QMessageBox.critical.call_args[0][2])
278
279        # Scenario 2
280        # overwrite checked -> no message box displayed
281        # and good_name set to True, Apply button enabled, output name created
282
283        # mock QMessageBox
284        QtWidgets.QMessageBox.critical = MagicMock()
285        # create dummy plugin_dir for output file
286        self.widget.plugin_dir = tempfile.gettempdir()
287
288        self.widget.chkOverwrite.setChecked(True)
289        self.widget.txtName.editingFinished.emit()
290        self.assertTrue(self.widget.good_name)
291        self.assertTrue(self.widget.buttonBox.button(
292            QtWidgets.QDialogButtonBox.Apply).isEnabled())
293
294        self.assertFalse(QtWidgets.QMessageBox.critical.called)
295
296        self.assertIn('cylinder.py', self.widget.plugin_filename)
297
298        # Scenario 3 Enter valid new plugin name -> filename created and Apply
299        # button enabled
300        # forbidding overwriting should not change anything
301        # since it's a new name
302        self.widget.txtName.setText("   cylinder0   ")
303        self.widget.chkOverwrite.setChecked(False)
304        self.widget.txtName.editingFinished.emit()
305        self.assertIn("cylinder0.py", self.widget.plugin_filename)
306        self.assertTrue(self.widget.buttonBox.button(
307            QtWidgets.QDialogButtonBox.Apply).isEnabled())
308
309    @patch.object(AddMultEditor, 'readModels')
310    def testOnUpdateModels(self, mock_new_list_models):
311        """ Test onUpdateModels """
312
313        ini_count_models = self.widget.cbModel1.count()
314
315        mock_new_list_models.return_value = ['cylinder', 'rpa',
316                                             'core_shell_cylinder', 'sphere',
317                                             'cylinder0']
318
319        self.widget.updateModels()
320        # check that the number of models in the comboboxes
321        # has been incremented by 1
322        self.assertEqual(self.widget.cbModel1.count(), ini_count_models + 1)
323        self.assertEqual(self.widget.cbModel2.count(), ini_count_models + 1)
324
325        # check that the new model is in the list
326        combobox = self.widget.cbModel1
327        self.assertIn('cylinder0', [combobox.itemText(indx)
328                                    for indx in range(combobox.count())])
329
330    def testOnReadModels(self):
331        """ The output of ReadModels is the return value of MagicMock defined
332        in the SetUp of these tests. """
333
334        self.assertEqual(self.widget.list_models, ['cylinder', 'rpa',
335                                                   'core_shell_cylinder',
336                                                   'sphere'])
337
338    def testCheckModel(self):
339        """ Test CheckModel"""
340
341        # TODO first: solve problem with empty 'test'
342        pass
Note: See TracBrowser for help on using the repository browser.