source: sasview/src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py @ 53c771e

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 53c771e was 53c771e, checked in by Piotr Rozyczko <rozyczko@…>, 4 years ago

Converted unit tests

  • Property mode set to 100755
File size: 40.4 KB
Line 
1import sys
2import unittest
3import time
4import logging
5
6from PyQt5 import QtGui
7from PyQt5 import QtWidgets
8from PyQt5 import QtTest
9from PyQt5 import QtCore
10from unittest.mock import MagicMock
11from twisted.internet import threads
12
13# set up import paths
14import sas.qtgui.path_prepare
15
16# Local
17from sas.qtgui.Utilities.GuiUtils import *
18from sas.qtgui.Perspectives.Fitting.FittingWidget import *
19from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
20
21from sas.qtgui.Plotting.PlotterData import Data1D
22from sas.qtgui.Plotting.PlotterData import Data2D
23
24if not QtWidgets.QApplication.instance():
25    app = QtWidgets.QApplication(sys.argv)
26
27class dummy_manager(object):
28    HELP_DIRECTORY_LOCATION = "html"
29    communicate = Communicate()
30
31class FittingWidgetTest(unittest.TestCase):
32    """Test the fitting widget GUI"""
33
34    def setUp(self):
35        """Create the GUI"""
36        self.widget = FittingWidget(dummy_manager())
37
38    def tearDown(self):
39        """Destroy the GUI"""
40        self.widget.close()
41        del self.widget
42
43    def testDefaults(self):
44        """Test the GUI in its default state"""
45        self.assertIsInstance(self.widget, QtWidgets.QWidget)
46        self.assertEqual(self.widget.windowTitle(), "Fitting")
47        self.assertEqual(self.widget.sizePolicy().Policy(), QtWidgets.QSizePolicy.Fixed)
48        self.assertIsInstance(self.widget.lstParams.model(), QtGui.QStandardItemModel)
49        self.assertIsInstance(self.widget.lstPoly.model(), QtGui.QStandardItemModel)
50        self.assertIsInstance(self.widget.lstMagnetic.model(), QtGui.QStandardItemModel)
51        self.assertFalse(self.widget.cbModel.isEnabled())
52        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
53        self.assertFalse(self.widget.cmdFit.isEnabled())
54        self.assertTrue(self.widget.acceptsData())
55        self.assertFalse(self.widget.data_is_loaded)
56
57    def testSelectCategoryDefault(self):
58        """
59        Test if model categories have been loaded properly
60        """
61        fittingWindow =  self.widget
62
63        #Test loading from json categories
64        category_list = list(fittingWindow.master_category_dict.keys())
65
66        for category in category_list:
67            self.assertNotEqual(fittingWindow.cbCategory.findText(category),-1)
68
69        #Test what is current text in the combobox
70        self.assertEqual(fittingWindow.cbCategory.currentText(), CATEGORY_DEFAULT)
71
72    def testWidgetWithData(self):
73        """
74        Test the instantiation of the widget with initial data
75        """
76        data = Data1D(x=[1,2], y=[1,2])
77        GuiUtils.dataFromItem = MagicMock(return_value=data)
78        item = QtGui.QStandardItem("test")
79
80        widget_with_data = FittingWidget(dummy_manager(), data=item, tab_id=3)
81
82        self.assertEqual(widget_with_data.data, data)
83        self.assertTrue(widget_with_data.data_is_loaded)
84        # self.assertTrue(widget_with_data.cmdFit.isEnabled())
85        self.assertFalse(widget_with_data.acceptsData())
86
87    def testSelectPolydispersity(self):
88        """
89        Test if models have been loaded properly
90        """
91        fittingWindow =  self.widget
92
93        self.assertIsInstance(fittingWindow.lstPoly.itemDelegate(), QtWidgets.QStyledItemDelegate)
94        #Test loading from json categories
95        fittingWindow.SASModelToQModel("cylinder")
96        pd_index = fittingWindow.lstPoly.model().index(0,0)
97        self.assertEqual(str(pd_index.data()), "Distribution of radius")
98        pd_index = fittingWindow.lstPoly.model().index(1,0)
99        self.assertEqual(str(pd_index.data()), "Distribution of length")
100
101        # test the delegate a bit
102        delegate = fittingWindow.lstPoly.itemDelegate()
103        self.assertEqual(len(delegate.POLYDISPERSE_FUNCTIONS), 5)
104        self.assertEqual(delegate.editableParameters(), [1, 2, 3, 4, 5])
105        self.assertEqual(delegate.poly_function, 6)
106        self.assertIsInstance(delegate.combo_updated, QtCore.pyqtBoundSignal)
107
108    def testSelectMagnetism(self):
109        """
110        Test if models have been loaded properly
111        """
112        fittingWindow =  self.widget
113
114        self.assertIsInstance(fittingWindow.lstMagnetic.itemDelegate(), QtGui.QStyledItemDelegate)
115        #Test loading from json categories
116        fittingWindow.SASModelToQModel("cylinder")
117        mag_index = fittingWindow.lstMagnetic.model().index(0,0)
118        self.assertEqual(str(mag_index.data().toString()), "up:frac_i")
119        mag_index = fittingWindow.lstMagnetic.model().index(1,0)
120        self.assertEqual(str(mag_index.data().toString()), "up:frac_f")
121        mag_index = fittingWindow.lstMagnetic.model().index(2,0)
122        self.assertEqual(str(mag_index.data().toString()), "up:angle")
123        mag_index = fittingWindow.lstMagnetic.model().index(3,0)
124        self.assertEqual(str(mag_index.data().toString()), "M0:sld")
125        mag_index = fittingWindow.lstMagnetic.model().index(4,0)
126        self.assertEqual(str(mag_index.data().toString()), "mtheta:sld")
127        mag_index = fittingWindow.lstMagnetic.model().index(5,0)
128        self.assertEqual(str(mag_index.data().toString()), "mphi:sld")
129        mag_index = fittingWindow.lstMagnetic.model().index(6,0)
130        self.assertEqual(str(mag_index.data().toString()), "M0:sld_solvent")
131        mag_index = fittingWindow.lstMagnetic.model().index(7,0)
132        self.assertEqual(str(mag_index.data().toString()), "mtheta:sld_solvent")
133        mag_index = fittingWindow.lstMagnetic.model().index(8,0)
134        self.assertEqual(str(mag_index.data().toString()), "mphi:sld_solvent")
135
136        # test the delegate a bit
137        delegate = fittingWindow.lstMagnetic.itemDelegate()
138        self.assertEqual(delegate.editableParameters(), [1, 2, 3])
139        self.assertIsInstance(delegate.combo_updated, QtCore.pyqtBoundSignal)
140
141    def testSelectStructureFactor(self):
142        """
143        Test if structure factors have been loaded properly
144        """
145        fittingWindow =  self.widget
146
147        #Test for existence in combobox
148        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("stickyhardsphere"),-1)
149        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hayter_msa"),-1)
150        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("squarewell"),-1)
151        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hardsphere"),-1)
152
153        #Test what is current text in the combobox
154        self.assertTrue(fittingWindow.cbCategory.currentText(), "None")
155
156    def testSignals(self):
157        """
158        Test the widget emitted signals
159        """
160        pass
161
162    def testSelectCategory(self):
163        """
164        Assure proper behaviour on changing category
165        """
166        self.widget.show()
167        self.assertEqual(self.widget._previous_category_index, 0)
168        # confirm the model combo contains no models
169        self.assertEqual(self.widget.cbModel.count(), 0)
170
171        # invoke the method by changing the index
172        category_index = self.widget.cbCategory.findText("Shape Independent")
173        self.widget.cbCategory.setCurrentIndex(category_index)
174
175        # test the model combo content
176        self.assertEqual(self.widget.cbModel.count(), 29)
177
178        # Try to change back to default
179        self.widget.cbCategory.setCurrentIndex(0)
180
181        # Observe no such luck
182        self.assertEqual(self.widget.cbCategory.currentIndex(), 6)
183        self.assertEqual(self.widget.cbModel.count(), 29)
184
185        # Set the structure factor
186        structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
187        self.widget.cbCategory.setCurrentIndex(structure_index)
188        # check the enablement of controls
189        self.assertFalse(self.widget.cbModel.isEnabled())
190        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
191
192    def testSelectModel(self):
193        """
194        Assure proper behaviour on changing model
195        """
196        self.widget.show()
197        # Change the category index so we have some models
198        category_index = self.widget.cbCategory.findText("Shape Independent")
199        self.widget.cbCategory.setCurrentIndex(category_index)
200
201        # check the enablement of controls
202        self.assertTrue(self.widget.cbModel.isEnabled())
203        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
204
205        # set up the model update spy
206        # spy = QtSignalSpy(self.widget._model_model, self.widget._model_model.itemChanged)
207
208        # mock the tested methods
209        self.widget.SASModelToQModel = MagicMock()
210        self.widget.createDefaultDataset = MagicMock()
211        self.widget.calculateQGridForModel = MagicMock()
212        #
213        # Now change the model
214        self.widget.cbModel.setCurrentIndex(3)
215        self.assertEqual(self.widget.cbModel.currentText(),'dab')
216
217        # No data sent -> no index set, only createDefaultDataset called
218        self.assertTrue(self.widget.createDefaultDataset.called)
219        self.assertTrue(self.widget.SASModelToQModel.called)
220        self.assertFalse(self.widget.calculateQGridForModel.called)
221
222        # Let's tell the widget that data has been loaded
223        self.widget.data_is_loaded = True
224        # Reset the sasmodel index
225        self.widget.cbModel.setCurrentIndex(1)
226        self.assertEqual(self.widget.cbModel.currentText(),'broad_peak')
227
228        # Observe calculateQGridForModel called
229        self.assertTrue(self.widget.calculateQGridForModel.called)
230
231    def testSelectFactor(self):
232        """
233        Assure proper behaviour on changing structure factor
234        """
235        self.widget.show()
236        # Change the category index so we have some models
237        category_index = self.widget.cbCategory.findText("Shape Independent")
238        self.widget.cbCategory.setCurrentIndex(category_index)
239        # Change the model to one that supports structure factors
240        model_index = self.widget.cbModel.findText('fractal_core_shell')
241        self.widget.cbModel.setCurrentIndex(model_index)
242
243        # Check that the factor combo is active and the default is chosen
244        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
245        self.assertEqual(self.widget.cbStructureFactor.currentText(), STRUCTURE_DEFAULT)
246
247        # We have this many rows in the model
248        rowcount = self.widget._model_model.rowCount()
249        #self.assertEqual(self.widget._model_model.rowCount(), 8)
250
251        # Change structure factor to something more exciting
252        structure_index = self.widget.cbStructureFactor.findText('squarewell')
253        self.widget.cbStructureFactor.setCurrentIndex(structure_index)
254
255        # We have 4 more rows now
256        self.assertEqual(self.widget._model_model.rowCount(), rowcount+4)
257
258        # Switch models
259        self.widget.cbModel.setCurrentIndex(0)
260
261        # Observe factor reset to None
262        self.assertEqual(self.widget.cbStructureFactor.currentText(), STRUCTURE_DEFAULT)
263
264        # Switch category to structure factor
265        structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
266        self.widget.cbCategory.setCurrentIndex(structure_index)
267        # Observe the correct enablement
268        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
269        self.assertFalse(self.widget.cbModel.isEnabled())
270        self.assertEqual(self.widget._model_model.rowCount(), 0)
271
272        # Choose the last factor
273        last_index = self.widget.cbStructureFactor.count()
274        self.widget.cbStructureFactor.setCurrentIndex(last_index-1)
275        # Do we have all the rows?
276        self.assertEqual(self.widget._model_model.rowCount(), 4)
277
278        # Are the command buttons properly enabled?
279        self.assertTrue(self.widget.cmdPlot.isEnabled())
280        self.assertFalse(self.widget.cmdFit.isEnabled())
281
282    def testReadCategoryInfo(self):
283        """
284        Check the category file reader
285        """
286        # Tested in default checks
287        pass
288
289    def testUpdateParamsFromModel(self):
290        """
291        Checks the sasmodel parameter update from QModel items
292        """
293        # Tested in default checks
294        pass
295
296    def testCreateTheoryIndex(self):
297        """
298        Test the data->QIndex conversion
299        """
300        # set up the model update spy
301        spy = QtSignalSpy(self.widget._model_model, self.widget.communicate.updateTheoryFromPerspectiveSignal)
302
303        self.widget.show()
304        # Change the category index so we have some models
305        self.widget.cbCategory.setCurrentIndex(1)
306
307        # Create the index
308        self.widget.createTheoryIndex(Data1D(x=[1,2], y=[1,2]))
309
310        # Make sure the signal has been emitted
311        self.assertEqual(spy.count(), 1)
312
313        # Check the argument type
314        self.assertIsInstance(spy.called()[0]['args'][0], QtGui.QStandardItem)
315
316    def testCalculateQGridForModel(self):
317        """
318        Check that the fitting 1D data object is ready
319        """
320        # Mock the thread creation
321        threads.deferToThread = MagicMock()
322        # Model for theory
323        self.widget.SASModelToQModel("cylinder")
324        # Call the tested method
325        self.widget.calculateQGridForModel()
326        time.sleep(1)
327        # Test the mock
328        self.assertTrue(threads.deferToThread.called)
329        self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, "compute")
330
331    def testCalculateResiduals(self):
332        """
333        Check that the residuals are calculated and plots updated
334        """
335        test_data = Data1D(x=[1,2], y=[1,2])
336
337        # Model for theory
338        self.widget.SASModelToQModel("cylinder")
339        # Invoke the tested method
340        self.widget.calculateResiduals(test_data)
341        # Check the Chi2 value - should be undetermined
342        self.assertEqual(self.widget.lblChi2Value.text(), '---')
343
344        # Force same data into logic
345        self.widget.logic.data = test_data
346        self.widget.calculateResiduals(test_data)
347        # Now, the difference is 0, as data is the same
348        self.assertEqual(self.widget.lblChi2Value.text(), '0')
349
350        # Change data
351        test_data_2 = Data1D(x=[1,2], y=[2.1,3.49])
352        self.widget.logic.data = test_data_2
353        self.widget.calculateResiduals(test_data)
354        # Now, the difference is non-zero
355        self.assertEqual(float(self.widget.lblChi2Value.text()), 1.7151)
356
357    def testSetPolyModel(self):
358        """
359        Test the polydispersity model setup
360        """
361        self.widget.show()
362        # Change the category index so we have a model with no poly
363        category_index = self.widget.cbCategory.findText("Shape Independent")
364        self.widget.cbCategory.setCurrentIndex(category_index)
365        # Check the poly model
366        self.assertEqual(self.widget._poly_model.rowCount(), 0)
367        self.assertEqual(self.widget._poly_model.columnCount(), 0)
368
369        # Change the category index so we have a model available
370        self.widget.cbCategory.setCurrentIndex(2)
371
372        # Check the poly model
373        self.assertEqual(self.widget._poly_model.rowCount(), 4)
374        self.assertEqual(self.widget._poly_model.columnCount(), 8)
375
376        # Test the header
377        self.assertEqual(self.widget.lstPoly.horizontalHeader().count(), 8)
378        self.assertFalse(self.widget.lstPoly.horizontalHeader().stretchLastSection())
379
380        # Test tooltips
381        self.assertEqual(len(self.widget._poly_model.header_tooltips), 8)
382
383        header_tooltips = ['Select parameter for fitting',
384                             'Enter polydispersity ratio (STD/mean). '
385                             'STD: standard deviation from the mean value',
386                             'Enter minimum value for parameter',
387                             'Enter maximum value for parameter',
388                             'Enter number of points for parameter',
389                             'Enter number of sigmas parameter',
390                             'Select distribution function',
391                             'Select filename with user-definable distribution']
392        for column, tooltip in enumerate(header_tooltips):
393             self.assertEqual(self.widget._poly_model.headerData( column,
394                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
395                         header_tooltips[column])
396
397        # Test presence of comboboxes in last column
398        for row in range(self.widget._poly_model.rowCount()):
399            func_index = self.widget._poly_model.index(row, 6)
400            self.assertTrue(isinstance(self.widget.lstPoly.indexWidget(func_index), QtWidgets.QComboBox))
401            self.assertIn('Distribution of', self.widget._poly_model.item(row, 0).text())
402        #self.widget.close()
403
404    def testPolyModelChange(self):
405        """
406        Polydispersity model changed - test all possible scenarios
407        """
408        self.widget.show()
409        # Change the category index so we have a model with polydisp
410        category_index = self.widget.cbCategory.findText("Cylinder")
411        self.widget.cbCategory.setCurrentIndex(category_index)
412
413        # click on a poly parameter checkbox
414        index = self.widget._poly_model.index(0,0)
415        # Set the checbox
416        self.widget._poly_model.item(0,0).setCheckState(2)
417        # Assure the parameter is added
418        self.assertEqual(self.widget.parameters_to_fit, ['radius_bell.width'])
419
420        # Add another parameter
421        self.widget._poly_model.item(2,0).setCheckState(2)
422        # Assure the parameters are added
423        self.assertEqual(self.widget.parameters_to_fit, ['radius_bell.width', 'length.width'])
424
425        # Change the min/max values
426        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 0.0)
427        self.widget._poly_model.item(0,2).setText("1.0")
428        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 1.0)
429
430        # Change the number of points
431        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
432        self.widget._poly_model.item(0,4).setText("22")
433        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 22)
434        # try something stupid
435        self.widget._poly_model.item(0,4).setText("butt")
436        # see that this didn't annoy the control at all
437        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 22)
438
439        # Change the number of sigmas
440        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 3)
441        self.widget._poly_model.item(0,5).setText("222")
442        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 222)
443        # try something stupid again
444        self.widget._poly_model.item(0,4).setText("beer")
445        # no efect
446        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 222)
447
448    def testOnPolyComboIndexChange(self):
449        """
450        Test the slot method for polydisp. combo box index change
451        """
452        self.widget.show()
453        # Change the category index so we have a model with polydisp
454        category_index = self.widget.cbCategory.findText("Cylinder")
455        self.widget.cbCategory.setCurrentIndex(category_index)
456
457        # call method with default settings
458        self.widget.onPolyComboIndexChange('gaussian', 0)
459        # check values
460        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
461        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 3)
462        # Change the index
463        self.widget.onPolyComboIndexChange('rectangle', 0)
464        # check values
465        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
466        self.assertAlmostEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 1.70325, 5)
467        # Change the index
468        self.widget.onPolyComboIndexChange('lognormal', 0)
469        # check values
470        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 80)
471        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 8)
472        # Change the index
473        self.widget.onPolyComboIndexChange('schulz', 0)
474        # check values
475        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 80)
476        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 8)
477
478        # mock up file load
479        self.widget.loadPolydispArray = MagicMock()
480        # Change to 'array'
481        self.widget.onPolyComboIndexChange('array', 0)
482        # See the mock fire
483        self.assertTrue(self.widget.loadPolydispArray.called)
484
485    def testLoadPolydispArray(self):
486        """
487        Test opening of the load file dialog for 'array' polydisp. function
488        """
489        filename = os.path.join("UnitTesting", "testdata_noexist.txt")
490        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
491        self.widget.show()
492        # Change the category index so we have a model with polydisp
493        category_index = self.widget.cbCategory.findText("Cylinder")
494        self.widget.cbCategory.setCurrentIndex(category_index)
495
496        self.widget.onPolyComboIndexChange('array', 0)
497        # check values - unchanged since the file doesn't exist
498        self.assertTrue(self.widget._poly_model.item(0, 1).isEnabled())
499        with self.assertRaises(AttributeError):
500            self.widget.disp_model()
501
502        # good file
503        filename = os.path.join("UnitTesting", "testdata.txt")
504        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
505
506        self.widget.onPolyComboIndexChange('array', 0)
507        # check values - disabled control, present weights
508        self.assertFalse(self.widget._poly_model.item(0, 1).isEnabled())
509        self.assertEqual(self.widget.disp_model.weights[0], 2.83954)
510        self.assertEqual(len(self.widget.disp_model.weights), 19)
511        self.assertEqual(len(self.widget.disp_model.values), 19)
512        self.assertEqual(self.widget.disp_model.values[0], 0.0)
513        self.assertEqual(self.widget.disp_model.values[18], 3.67347)
514
515    def testSetMagneticModel(self):
516        """
517        Test the magnetic model setup
518        """
519        self.widget.show()
520        # Change the category index so we have a model available
521        category_index = self.widget.cbCategory.findText("Sphere")
522        self.widget.cbCategory.setCurrentIndex(category_index)
523
524        # Check the magnetic model
525        self.assertEqual(self.widget._magnet_model.rowCount(), 9)
526        self.assertEqual(self.widget._magnet_model.columnCount(), 5)
527
528        # Test the header
529        self.assertEqual(self.widget.lstMagnetic.horizontalHeader().count(), 5)
530        self.assertFalse(self.widget.lstMagnetic.horizontalHeader().stretchLastSection())
531
532        #Test tooltips
533        self.assertEqual(len(self.widget._magnet_model.header_tooltips), 5)
534
535        header_tooltips = ['Select parameter for fitting',
536                             'Enter parameter value',
537                             'Enter minimum value for parameter',
538                             'Enter maximum value for parameter',
539                             'Unit of the parameter']
540        for column, tooltip in enumerate(header_tooltips):
541             self.assertEqual(self.widget._magnet_model.headerData(column,
542                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
543                         header_tooltips[column])
544
545        # Test rows
546        for row in range(self.widget._magnet_model.rowCount()):
547            func_index = self.widget._magnet_model.index(row, 0)
548            self.assertIn(':', self.widget._magnet_model.item(row, 0).text())
549
550
551    def testAddExtraShells(self):
552        """
553        Test how the extra shells are presented
554        """
555        pass
556
557    def testModifyShellsInList(self):
558        """
559        Test the additional rows added by modifying the shells combobox
560        """
561        self.widget.show()
562        # Change the model to multi shell
563        category_index = self.widget.cbCategory.findText("Sphere")
564        self.widget.cbCategory.setCurrentIndex(category_index)
565        model_index = self.widget.cbModel.findText("core_multi_shell")
566        self.widget.cbModel.setCurrentIndex(model_index)
567
568        # Assure we have the combobox available
569        last_row = self.widget._last_model_row
570        func_index = self.widget._model_model.index(last_row-1, 1)
571        self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtWidgets.QComboBox)
572
573        # Change the combo box index
574        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(3)
575
576        # Check that the number of rows increased
577        more_rows = self.widget._model_model.rowCount() - last_row
578        self.assertEqual(more_rows, 6) # 6 new rows: 2 params per index
579
580        # Back to 0
581        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0)
582        self.assertEqual(self.widget._model_model.rowCount(), last_row)
583
584    def testPlotTheory(self):
585        """
586        See that theory item can produce a chart
587        """
588        # By default, the compute/plot button is disabled
589        self.assertFalse(self.widget.cmdPlot.isEnabled())
590        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
591
592        # Assign a model
593        self.widget.show()
594        # Change the category index so we have a model available
595        category_index = self.widget.cbCategory.findText("Sphere")
596        self.widget.cbCategory.setCurrentIndex(category_index)
597
598        # Check the enablement/text
599        self.assertTrue(self.widget.cmdPlot.isEnabled())
600        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate')
601
602        # Spying on plot update signal
603        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
604
605        # Press Calculate
606        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
607
608        # Observe cmdPlot caption change
609        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
610
611        # Make sure the signal has NOT been emitted
612        self.assertEqual(spy.count(), 0)
613
614        # Click again
615        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
616
617        # This time, we got the update signal
618        self.assertEqual(spy.count(), 0)
619
620    def testPlotData(self):
621        """
622        See that data item can produce a chart
623        """
624        # By default, the compute/plot button is disabled
625        self.assertFalse(self.widget.cmdPlot.isEnabled())
626        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
627
628        self.widget.show()
629
630        # Set data
631        test_data = Data1D(x=[1,2], y=[1,2])
632
633        # Force same data into logic
634        self.widget.logic.data = test_data
635        self.widget.data_is_loaded = True
636
637        # Change the category index so we have a model available
638        category_index = self.widget.cbCategory.findText("Sphere")
639        self.widget.cbCategory.setCurrentIndex(category_index)
640
641        # Check the enablement/text
642        self.assertTrue(self.widget.cmdPlot.isEnabled())
643        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
644
645        # Spying on plot update signal
646        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
647
648        # Press Calculate
649        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
650
651        # Observe cmdPlot caption did not change
652        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
653
654        # Make sure the signal has been emitted == new plot
655        self.assertEqual(spy.count(), 1)
656
657    def testOnEmptyFit(self):
658        """
659        Test a 1D/2D fit with no parameters
660        """
661        # Set data
662        test_data = Data1D(x=[1,2], y=[1,2])
663        item = QtGui.QStandardItem()
664        updateModelItem(item, [test_data], "test")
665        # Force same data into logic
666        self.widget.data = item
667        category_index = self.widget.cbCategory.findText("Sphere")
668        self.widget.cbCategory.setCurrentIndex(category_index)
669
670        self.widget.show()
671
672        # Test no fitting params
673        self.widget.parameters_to_fit = []
674
675        logging.error = MagicMock()
676
677        self.widget.onFit()
678        self.assertTrue(logging.error.called_with('no fitting parameters'))
679        self.widget.close()
680
681        test_data = Data2D(image=[1.0, 2.0, 3.0],
682                           err_image=[0.01, 0.02, 0.03],
683                           qx_data=[0.1, 0.2, 0.3],
684                           qy_data=[0.1, 0.2, 0.3],
685                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
686                           mask=[True, True, True])
687
688        # Force same data into logic
689        item = QtGui.QStandardItem()
690        updateModelItem(item, [test_data], "test")
691        # Force same data into logic
692        self.widget.data = item
693        category_index = self.widget.cbCategory.findText("Sphere")
694        self.widget.cbCategory.setCurrentIndex(category_index)
695
696        self.widget.show()
697
698        # Test no fitting params
699        self.widget.parameters_to_fit = []
700
701        logging.error = MagicMock()
702
703        self.widget.onFit()
704        self.assertTrue(logging.error.called_once())
705        self.assertTrue(logging.error.called_with('no fitting parameters'))
706        self.widget.close()
707
708
709    def testOnFit1D(self):
710        """
711        Test the threaded fitting call
712        """
713        # Set data
714        test_data = Data1D(x=[1,2], y=[1,2])
715        item = QtGui.QStandardItem()
716        updateModelItem(item, [test_data], "test")
717        # Force same data into logic
718        self.widget.data = item
719        category_index = self.widget.cbCategory.findText("Sphere")
720        self.widget.cbCategory.setCurrentIndex(category_index)
721
722        self.widget.show()
723
724        # Assing fitting params
725        self.widget.parameters_to_fit = ['scale']
726
727        # Spying on status update signal
728        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
729
730        with threads.deferToThread as MagicMock:
731            self.widget.onFit()
732            # thread called
733            self.assertTrue(threads.deferToThread.called)
734            # thread method is 'compute'
735            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
736
737            # the fit button changed caption and got disabled
738            self.assertEqual(self.widget.cmdFit.text(), 'Running...')
739            self.assertFalse(self.widget.cmdFit.isEnabled())
740
741            # Signal pushed up
742            self.assertEqual(update_spy.count(), 1)
743
744        self.widget.close()
745
746    def testOnFit2D(self):
747        """
748        Test the threaded fitting call
749        """
750        # Set data
751        test_data = Data2D(image=[1.0, 2.0, 3.0],
752                           err_image=[0.01, 0.02, 0.03],
753                           qx_data=[0.1, 0.2, 0.3],
754                           qy_data=[0.1, 0.2, 0.3],
755                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
756                           mask=[True, True, True])
757
758        # Force same data into logic
759        item = QtGui.QStandardItem()
760        updateModelItem(item, [test_data], "test")
761        # Force same data into logic
762        self.widget.data = item
763        category_index = self.widget.cbCategory.findText("Sphere")
764        self.widget.cbCategory.setCurrentIndex(category_index)
765
766        self.widget.show()
767
768        # Assing fitting params
769        self.widget.parameters_to_fit = ['scale']
770
771        # Spying on status update signal
772        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
773
774        with threads.deferToThread as MagicMock:
775            self.widget.onFit()
776            # thread called
777            self.assertTrue(threads.deferToThread.called)
778            # thread method is 'compute'
779            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
780
781            # the fit button changed caption and got disabled
782            self.assertEqual(self.widget.cmdFit.text(), 'Running...')
783            self.assertFalse(self.widget.cmdFit.isEnabled())
784
785            # Signal pushed up
786            self.assertEqual(update_spy.count(), 1)
787
788    # test disabled until pyqt5 deals with html properly
789    def notestOnHelp(self):
790        """
791        Test various help pages shown in this widget
792        """
793        #Mock the QWebView method
794        QtWebKit.QWebView.show = MagicMock()
795        QtWebKit.QWebView.load = MagicMock()
796
797        # Invoke the action on default tab
798        self.widget.onHelp()
799        # Check if show() got called
800        self.assertTrue(QtWebKit.QWebView.show.called)
801        # Assure the filename is correct
802        self.assertIn("fitting_help.html", QtWebKit.QWebView.load.call_args[0][0].toString())
803
804        # Change the tab to options
805        self.widget.tabFitting.setCurrentIndex(1)
806        self.widget.onHelp()
807        # Check if show() got called
808        self.assertEqual(QtWebKit.QWebView.show.call_count, 2)
809        # Assure the filename is correct
810        self.assertIn("residuals_help.html", QtWebKit.QWebView.load.call_args[0][0].toString())
811
812        # Change the tab to smearing
813        self.widget.tabFitting.setCurrentIndex(2)
814        self.widget.onHelp()
815        # Check if show() got called
816        self.assertEqual(QtWebKit.QWebView.show.call_count, 3)
817        # Assure the filename is correct
818        self.assertIn("sm_help.html", QtWebKit.QWebView.load.call_args[0][0].toString())
819
820        # Change the tab to poly
821        self.widget.tabFitting.setCurrentIndex(3)
822        self.widget.onHelp()
823        # Check if show() got called
824        self.assertEqual(QtWebKit.QWebView.show.call_count, 4)
825        # Assure the filename is correct
826        self.assertIn("pd_help.html", QtWebKit.QWebView.load.call_args[0][0].toString())
827
828        # Change the tab to magnetism
829        self.widget.tabFitting.setCurrentIndex(4)
830        self.widget.onHelp()
831        # Check if show() got called
832        self.assertEqual(QtWebKit.QWebView.show.call_count, 5)
833        # Assure the filename is correct
834        self.assertIn("mag_help.html", QtWebKit.QWebView.load.call_args[0][0].toString())
835
836    def testReadFitPage(self):
837        """
838        Read in the fitpage object and restore state
839        """
840        # Set data
841        test_data = Data1D(x=[1,2], y=[1,2])
842
843        # Force same data into logic
844        self.widget.logic.data = test_data
845        self.widget.data_is_loaded = True
846        category_index = self.widget.cbCategory.findText('Sphere')
847        self.widget.cbCategory.setCurrentIndex(category_index)
848        self.widget.parameters_to_fit = ['scale']
849        # Invoke the tested method
850        fp = self.widget.currentState()
851
852        # Prepare modified fit page
853        fp.current_model = 'onion'
854        fp.is_polydisperse = True
855
856        # Read in modified state
857        self.widget.readFitPage(fp)
858
859        # Check if the widget got updated accordingly
860        self.assertEqual(self.widget.cbModel.currentText(), 'onion')
861        self.assertTrue(self.widget.chkPolydispersity.isChecked())
862        #Check if polidispersity tab is available
863        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
864
865        #Check if magnetism box and tab are disabled when 1D data is loaded
866        self.assertFalse(self.widget.chkMagnetism.isEnabled())
867        self.assertFalse(self.widget.tabFitting.isTabEnabled(4))
868
869    def testReadFitPage2D(self):
870        """
871        Read in the fitpage object and restore state
872        """
873        # Set data
874
875        test_data = Data2D(image=[1.0, 2.0, 3.0],
876                           err_image=[0.01, 0.02, 0.03],
877                           qx_data=[0.1, 0.2, 0.3],
878                           qy_data=[0.1, 0.2, 0.3],
879                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
880                           mask=[True, True, True])
881
882        # Force same data into logic
883        self.widget.logic.data = test_data
884        self.widget.data_is_loaded = True
885
886        #item = QtGui.QStandardItem()
887        #updateModelItem(item, [test_data], "test")
888        # Force same data into logic
889        #self.widget.logic.data = item
890        #self.widget.data_is_loaded = True
891
892        category_index = self.widget.cbCategory.findText("Cylinder")
893        self.widget.cbCategory.setCurrentIndex(category_index)
894
895        # Test no fitting params
896        self.widget.parameters_to_fit = ['scale']
897
898        # Invoke the tested method
899        fp = self.widget.currentState()
900
901        # Prepare modified fit page
902        fp.current_model = 'cylinder'
903        fp.is_polydisperse = True
904        fp.is_magnetic = True
905        fp.is2D = True
906
907        # Read in modified state
908        self.widget.readFitPage(fp)
909
910        # Check if the widget got updated accordingly
911        self.assertEqual(self.widget.cbModel.currentText(), 'cylinder')
912        self.assertTrue(self.widget.chkPolydispersity.isChecked())
913        self.assertTrue(self.widget.chkPolydispersity.isEnabled())
914        #Check if polidispersity tab is available
915        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
916
917        #Check if magnetism box and tab are disabled when 1D data is loaded
918        self.assertTrue(self.widget.chkMagnetism.isChecked())
919        self.assertTrue(self.widget.chkMagnetism.isEnabled())
920        self.assertTrue(self.widget.tabFitting.isTabEnabled(4))
921
922    def testCurrentState(self):
923        """
924        Set up the fitpage with current state
925        """
926        # Set data
927        test_data = Data1D(x=[1,2], y=[1,2])
928
929        # Force same data into logic
930        self.widget.logic.data = test_data
931        self.widget.data_is_loaded = True
932        category_index = self.widget.cbCategory.findText("Sphere")
933        self.widget.cbCategory.setCurrentIndex(category_index)
934        self.widget.parameters_to_fit = ['scale']
935
936        # Invoke the tested method
937        fp = self.widget.currentState()
938
939        # Test some entries. (Full testing of fp is done in FitPageTest)
940        self.assertIsInstance(fp.data, Data1D)
941        self.assertListEqual(list(fp.data.x), [1,2])
942        self.assertTrue(fp.data_is_loaded)
943        self.assertEqual(fp.current_category, "Sphere")
944        self.assertEqual(fp.current_model, "adsorbed_layer")
945        self.assertListEqual(fp.parameters_to_fit, ['scale'])
946
947    def testPushFitPage(self):
948        """
949        Push current state of fitpage onto stack
950        """
951        # Set data
952        test_data = Data1D(x=[1,2], y=[1,2])
953
954        # Force same data into logic
955        self.widget.logic.data = test_data
956        self.widget.data_is_loaded = True
957        category_index = self.widget.cbCategory.findText("Sphere")
958
959        # Asses the initial state of stack
960        self.assertEqual(self.widget.page_stack, [])
961
962        # Set the undo flag
963        self.widget.undo_supported = True
964        self.widget.cbCategory.setCurrentIndex(category_index)
965        self.widget.parameters_to_fit = ['scale']
966
967        # Check that the stack is updated
968        self.assertEqual(len(self.widget.page_stack), 1)
969
970        # Change another parameter
971        self.widget._model_model.item(2, 1).setText("3.0")
972        # Check that the stack is updated
973        self.assertEqual(len(self.widget.page_stack), 2)
974
975    def testPopFitPage(self):
976        """
977        Pop current state of fitpage from stack
978        """
979        # TODO: to be added when implementing UNDO/REDO
980        pass
981
982    def testOnMainPageChange(self):
983        """
984        Test update  values of modified parameters in models
985        """
986        # select model: cylinder / cylinder
987        category_index = self.widget.cbCategory.findText("Cylinder")
988        self.widget.cbCategory.setCurrentIndex(category_index)
989
990        model_index = self.widget.cbModel.findText("cylinder")
991        self.widget.cbModel.setCurrentIndex(model_index)
992
993        # modify the initial value of length (different from default)
994        # print self.widget.kernel_module.details['length']
995
996        new_value = "333.0"
997        self.widget._model_model.item(5, 1).setText(new_value)
998
999        # name of modified parameter
1000        name_modified_param = str(self.widget._model_model.item(5, 0).text())
1001
1002         # Check the model
1003        self.assertEqual(self.widget._model_model.rowCount(), 6)
1004        self.assertEqual(self.widget._model_model.columnCount(), 5)
1005
1006        # Test the header
1007        #self.assertEqual(self.widget.lstParams.horizontalHeader().count(), 5)
1008        #self.assertFalse(self.widget.lstParams.horizontalHeader().stretchLastSection())
1009
1010        self.assertEqual(len(self.widget._model_model.header_tooltips), 5)
1011        header_tooltips = ['Select parameter for fitting',
1012                             'Enter parameter value',
1013                             'Enter minimum value for parameter',
1014                             'Enter maximum value for parameter',
1015                             'Unit of the parameter']
1016        for column, tooltip in enumerate(header_tooltips):
1017             self.assertEqual(self.widget._model_model.headerData(column,
1018                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
1019                         header_tooltips[column])
1020
1021        # check that the value has been modified in kernel_module
1022        self.assertEqual(new_value,
1023                         str(self.widget.kernel_module.params[name_modified_param]))
1024
1025        # check that range of variation for this parameter has NOT been changed
1026        self.assertNotIn(new_value, self.widget.kernel_module.details[name_modified_param] )
1027
1028
1029if __name__ == "__main__":
1030    unittest.main()
Note: See TracBrowser for help on using the repository browser.