source: sasview/src/sas/qtgui/Perspectives/Fitting/UnitTesting/FittingWidgetTest.py @ 3e8dee3

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 3e8dee3 was 3e8dee3, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Converted unit tests

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