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

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

Compute/Show? Plot button logic: SASVIEW-271
Unit tests for plotting in fitting: SASVIEW-501

  • Property mode set to 100644
File size: 19.3 KB
Line 
1import sys
2import unittest
3import time
4
5from PyQt4 import QtGui
6from PyQt4 import QtTest
7from PyQt4 import QtCore
8from mock import MagicMock
9from twisted.internet import threads
10
11# set up import paths
12import sas.qtgui.path_prepare
13
14# Local
15from sas.qtgui.Utilities.GuiUtils import *
16from sas.qtgui.Perspectives.Fitting.FittingWidget import *
17from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
18
19from sas.sasgui.guiframe.dataFitting import Data1D
20
21app = QtGui.QApplication(sys.argv)
22
23class dummy_manager(object):
24    communicate = Communicate()
25
26class FittingWidgetTest(unittest.TestCase):
27    """Test the fitting widget GUI"""
28
29    def setUp(self):
30        """Create the GUI"""
31        self.widget = FittingWidget(dummy_manager())
32
33    def tearDown(self):
34        """Destroy the GUI"""
35        self.widget.close()
36        self.widget = None
37
38    def testDefaults(self):
39        """Test the GUI in its default state"""
40        self.assertIsInstance(self.widget, QtGui.QWidget)
41        self.assertEqual(self.widget.windowTitle(), "Fitting")
42        self.assertEqual(self.widget.sizePolicy().Policy(), QtGui.QSizePolicy.Fixed)
43        self.assertIsInstance(self.widget.lstParams.model(), QtGui.QStandardItemModel)
44        self.assertIsInstance(self.widget.lstPoly.model(), QtGui.QStandardItemModel)
45        self.assertIsInstance(self.widget.lstMagnetic.model(), QtGui.QStandardItemModel)
46        self.assertFalse(self.widget.cbModel.isEnabled())
47        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
48        self.assertFalse(self.widget.cmdFit.isEnabled())
49        self.assertTrue(self.widget.acceptsData())
50        self.assertFalse(self.widget.data_is_loaded)
51
52    def testSelectCategory(self):
53        """
54        Test if model categories have been loaded properly
55        """
56        fittingWindow =  self.widget
57
58        #Test loading from json categories
59        category_list = fittingWindow.master_category_dict.keys()
60
61        for category in category_list:
62            self.assertNotEqual(fittingWindow.cbCategory.findText(category),-1)
63
64        #Test what is current text in the combobox
65        self.assertEqual(fittingWindow.cbCategory.currentText(), CATEGORY_DEFAULT)
66
67    def testWidgetWithData(self):
68        """
69        Test the instantiation of the widget with initial data
70        """
71        data = Data1D(x=[1,2], y=[1,2])
72        GuiUtils.dataFromItem = MagicMock(return_value=data)
73        item = QtGui.QStandardItem("test")
74
75        widget_with_data = FittingWidget(dummy_manager(), data=item, id=3)
76
77        self.assertEqual(widget_with_data.data, data)
78        self.assertTrue(widget_with_data.data_is_loaded)
79        # self.assertTrue(widget_with_data.cmdFit.isEnabled())
80        self.assertFalse(widget_with_data.acceptsData())
81
82    def testSelectModel(self):
83        """
84        Test if models have been loaded properly
85        """
86        fittingWindow =  self.widget
87
88        #Test loading from json categories
89        model_list = fittingWindow.master_category_dict["Cylinder"]
90        self.assertTrue(['cylinder', True] in model_list)
91        self.assertTrue(['core_shell_cylinder', True] in model_list)
92        self.assertTrue(['barbell', True] in model_list)
93        self.assertTrue(['core_shell_bicelle', True] in model_list)
94        self.assertTrue(['flexible_cylinder', True] in model_list)
95        self.assertTrue(['flexible_cylinder_elliptical', True] in model_list)
96        self.assertTrue(['pearl_necklace', True] in model_list)
97        self.assertTrue(['capped_cylinder', True] in model_list)
98        self.assertTrue(['elliptical_cylinder', True] in model_list)
99        self.assertTrue(['pringle', True] in model_list)
100        self.assertTrue(['hollow_cylinder', True] in model_list)
101        self.assertTrue(['core_shell_bicelle_elliptical', True] in model_list)
102        self.assertTrue(['stacked_disks', True] in model_list)
103
104        #Test for existence in combobox
105        self.assertNotEqual(fittingWindow.cbModel.findText("cylinder"),-1)
106        self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_cylinder"),-1)
107        self.assertNotEqual(fittingWindow.cbModel.findText("barbell"),-1)
108        self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_bicelle"),-1)
109        self.assertNotEqual(fittingWindow.cbModel.findText("flexible_cylinder"),-1)
110        self.assertNotEqual(fittingWindow.cbModel.findText("flexible_cylinder_elliptical"),-1)
111        self.assertNotEqual(fittingWindow.cbModel.findText("pearl_necklace"),-1)
112        self.assertNotEqual(fittingWindow.cbModel.findText("capped_cylinder"),-1)
113        self.assertNotEqual(fittingWindow.cbModel.findText("elliptical_cylinder"),-1)
114        self.assertNotEqual(fittingWindow.cbModel.findText("pringle"),-1)
115        self.assertNotEqual(fittingWindow.cbModel.findText("hollow_cylinder"),-1)
116        self.assertNotEqual(fittingWindow.cbModel.findText("core_shell_bicelle_elliptical"),-1)
117        self.assertNotEqual(fittingWindow.cbModel.findText("stacked_disks"),-1)
118
119
120    def testSelectPolydispersity(self):
121        """
122        Test if models have been loaded properly
123        :return:
124        """
125        fittingWindow =  self.widget
126
127        #Test loading from json categories
128        fittingWindow.SASModelToQModel("cylinder")
129        pd_index = fittingWindow.lstPoly.model().index(0,0)
130        self.assertEqual(str(pd_index.data().toString()), "Distribution of radius")
131        pd_index = fittingWindow.lstPoly.model().index(1,0)
132        self.assertEqual(str(pd_index.data().toString()), "Distribution of length")
133
134    def testSelectStructureFactor(self):
135        """
136        Test if structure factors have been loaded properly
137        :return:
138        """
139        fittingWindow =  self.widget
140
141        #Test for existence in combobox
142        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("stickyhardsphere"),-1)
143        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hayter_msa"),-1)
144        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("squarewell"),-1)
145        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hardsphere"),-1)
146
147        #Test what is current text in the combobox
148        self.assertTrue(fittingWindow.cbCategory.currentText(), "None")
149
150    def testSignals(self):
151        """
152        Test the widget emitted signals
153        """
154        pass
155
156    def  testSelectCategory(self):
157        """
158        Assure proper behaviour on changing category
159        """
160        self.widget.show()
161        self.assertEqual(self.widget._previous_category_index, 0)
162        # confirm the model combo contains no models
163        self.assertEqual(self.widget.cbModel.count(), 0)
164
165        # invoke the method by changing the index
166        category_index = self.widget.cbCategory.findText("Shape Independent")
167        self.widget.cbCategory.setCurrentIndex(category_index)
168
169        # test the model combo content
170        self.assertEqual(self.widget.cbModel.count(), 29)
171
172        # Try to change back to default
173        self.widget.cbCategory.setCurrentIndex(0)
174
175        # Observe no such luck
176        self.assertEqual(self.widget.cbCategory.currentIndex(), 6)
177        self.assertEqual(self.widget.cbModel.count(), 29)
178
179        # Set the structure factor
180        structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
181        self.widget.cbCategory.setCurrentIndex(structure_index)
182        # check the enablement of controls
183        self.assertFalse(self.widget.cbModel.isEnabled())
184        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
185
186    def testSelectModel(self):
187        """
188        Assure proper behaviour on changing model
189        """
190        self.widget.show()
191        # Change the category index so we have some models
192        category_index = self.widget.cbCategory.findText("Shape Independent")
193        self.widget.cbCategory.setCurrentIndex(category_index)
194
195        # check the enablement of controls
196        self.assertTrue(self.widget.cbModel.isEnabled())
197        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
198
199        # set up the model update spy
200        # spy = QtSignalSpy(self.widget._model_model, self.widget._model_model.itemChanged)
201
202        # mock the tested methods
203        self.widget.SASModelToQModel = MagicMock()
204        self.widget.createDefaultDataset = MagicMock()
205        self.widget.calculateQGridForModel = MagicMock()
206        #
207        # Now change the model
208        self.widget.cbModel.setCurrentIndex(3)
209        self.assertEqual(self.widget.cbModel.currentText(),'dab')
210
211        # No data sent -> no index set, only createDefaultDataset called
212        self.assertTrue(self.widget.createDefaultDataset.called)
213        self.assertTrue(self.widget.SASModelToQModel.called)
214        self.assertFalse(self.widget.calculateQGridForModel.called)
215
216        # Let's tell the widget that data has been loaded
217        self.widget.data_is_loaded = True
218        # Reset the sasmodel index
219        self.widget.cbModel.setCurrentIndex(1)
220        self.assertEqual(self.widget.cbModel.currentText(),'broad_peak')
221
222        # Observe calculateQGridForModel called
223        self.assertTrue(self.widget.calculateQGridForModel.called)
224
225    def testSelectFactor(self):
226        """
227        Assure proper behaviour on changing structure factor
228        """
229        self.widget.show()
230        # Change the category index so we have some models
231        category_index = self.widget.cbCategory.findText("Shape Independent")
232        self.widget.cbCategory.setCurrentIndex(category_index)
233        # Change the model to one that supports structure factors
234        model_index = self.widget.cbModel.findText('fractal_core_shell')
235        self.widget.cbModel.setCurrentIndex(model_index)
236
237        # Check that the factor combo is active and the default is chosen
238        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
239        self.assertEqual(self.widget.cbStructureFactor.currentText(), STRUCTURE_DEFAULT)
240
241        # We have this many rows in the model
242        rowcount = self.widget._model_model.rowCount()
243        #self.assertEqual(self.widget._model_model.rowCount(), 8)
244
245        # Change structure factor to something more exciting
246        structure_index = self.widget.cbStructureFactor.findText('squarewell')
247        self.widget.cbStructureFactor.setCurrentIndex(structure_index)
248
249        # We have 4 more rows now
250        self.assertEqual(self.widget._model_model.rowCount(), rowcount+4)
251
252        # Switch models
253        self.widget.cbModel.setCurrentIndex(0)
254
255        # Observe factor reset to None
256        self.assertEqual(self.widget.cbStructureFactor.currentText(), STRUCTURE_DEFAULT)
257
258
259        # TODO once functionality fixed
260        ## Switch category to structure factor
261        #structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
262        #self.widget.cbCategory.setCurrentIndex(structure_index)
263        ## Choose the last factor
264        #last_index = self.widget.cbStructureFactor.count()
265        #self.widget.cbStructureFactor.setCurrentIndex(last_index-1)
266
267    def testReadCategoryInfo(self):
268        """
269        Check the category file reader
270        """
271        # Tested in default checks
272        pass
273
274    def testUpdateParamsFromModel(self):
275        """
276        Checks the sasmodel parameter update from QModel items
277        """
278        # Tested in default checks
279        pass
280
281    def testCreateTheoryIndex(self):
282        """
283        Test the data->QIndex conversion
284        """
285        # set up the model update spy
286        spy = QtSignalSpy(self.widget._model_model, self.widget.communicate.updateTheoryFromPerspectiveSignal)
287
288        self.widget.show()
289        # Change the category index so we have some models
290        self.widget.cbCategory.setCurrentIndex(1)
291
292        # Create the index
293        self.widget.createTheoryIndex(Data1D(x=[1,2], y=[1,2]))
294
295        # Make sure the signal has been emitted
296        self.assertEqual(spy.count(), 1)
297
298        # Check the argument type
299        self.assertIsInstance(spy.called()[0]['args'][0], QtGui.QStandardItem)
300
301    def testCalculateQGridForModel(self):
302        """
303        Check that the fitting 1D data object is ready
304        """
305        # Mock the thread creation
306        threads.deferToThread = MagicMock()
307        # Model for theory
308        self.widget.SASModelToQModel("cylinder")
309        # Call the tested method
310        self.widget.calculateQGridForModel()
311        time.sleep(1)
312        # Test the mock
313        self.assertTrue(threads.deferToThread.called)
314        self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, "compute")
315
316    def testCalculateResiduals(self):
317        """
318        Check that the residuals are calculated and plots updated
319        """
320        test_data = Data1D(x=[1,2], y=[1,2])
321
322        # Model for theory
323        self.widget.SASModelToQModel("cylinder")
324        # Invoke the tested method
325        self.widget.calculateResiduals(test_data)
326        # Check the Chi2 value - should be undetermined
327        self.assertEqual(self.widget.lblChi2Value.text(), '---')
328
329        # Force same data into logic
330        self.widget.logic.data = test_data
331        self.widget.calculateResiduals(test_data)
332        # Now, the difference is 0, as data is the same
333        self.assertEqual(self.widget.lblChi2Value.text(), '0')
334
335        # Change data
336        test_data_2 = Data1D(x=[1,2], y=[2.1,3.49])
337        self.widget.logic.data = test_data_2
338        self.widget.calculateResiduals(test_data)
339        # Now, the difference is non-zero
340        self.assertEqual(float(self.widget.lblChi2Value.text()), 1.715)
341
342    def testSetPolyModel(self):
343        """
344        Test the polydispersity model setup
345        """
346        self.widget.show()
347        # Change the category index so we have a model with no poly
348        category_index = self.widget.cbCategory.findText("Shape Independent")
349        self.widget.cbCategory.setCurrentIndex(category_index)
350        # Check the poly model
351        self.assertEqual(self.widget._poly_model.rowCount(), 0)
352        self.assertEqual(self.widget._poly_model.columnCount(), 0)
353
354        # Change the category index so we have a model available
355        self.widget.cbCategory.setCurrentIndex(2)
356
357        # Check the poly model
358        self.assertEqual(self.widget._poly_model.rowCount(), 4)
359        self.assertEqual(self.widget._poly_model.columnCount(), 7)
360
361        # Test the header
362        self.assertEqual(self.widget.lstPoly.horizontalHeader().count(), 7)
363        self.assertFalse(self.widget.lstPoly.horizontalHeader().stretchLastSection())
364
365        # Test presence of comboboxes in last column
366        for row in xrange(self.widget._poly_model.rowCount()):
367            func_index = self.widget._poly_model.index(row, 6)
368            self.assertTrue(isinstance(self.widget.lstPoly.indexWidget(func_index), QtGui.QComboBox))
369            self.assertIn('Distribution of', self.widget._poly_model.item(row, 0).text())
370
371    def testSetMagneticModel(self):
372        """
373        Test the magnetic model setup
374        """
375        self.widget.show()
376        # Change the category index so we have a model available
377        category_index = self.widget.cbCategory.findText("Sphere")
378        self.widget.cbCategory.setCurrentIndex(category_index)
379
380        # Check the magnetic model
381        self.assertEqual(self.widget._magnet_model.rowCount(), 9)
382        self.assertEqual(self.widget._magnet_model.columnCount(), 5)
383
384        # Test the header
385        self.assertEqual(self.widget.lstMagnetic.horizontalHeader().count(), 5)
386        self.assertFalse(self.widget.lstMagnetic.horizontalHeader().stretchLastSection())
387
388        # Test rows
389        for row in xrange(self.widget._magnet_model.rowCount()):
390            func_index = self.widget._magnet_model.index(row, 0)
391            self.assertIn(':', self.widget._magnet_model.item(row, 0).text())
392
393
394    def testAddExtraShells(self):
395        """
396        Test how the extra shells are presented
397        """
398        pass
399
400    def testModifyShellsInList(self):
401        """
402        Test the additional rows added by modifying the shells combobox
403        """
404        self.widget.show()
405        # Change the model to multi shell
406        category_index = self.widget.cbCategory.findText("Sphere")
407        self.widget.cbCategory.setCurrentIndex(category_index)
408        model_index = self.widget.cbModel.findText("core_multi_shell")
409        self.widget.cbModel.setCurrentIndex(model_index)
410
411        # Assure we have the combobox available
412        last_row = self.widget._last_model_row
413        func_index = self.widget._model_model.index(last_row-1, 1)
414        self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtGui.QComboBox)
415
416        # Change the combo box index
417        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(3)
418
419        # Check that the number of rows increased
420        more_rows = self.widget._model_model.rowCount() - last_row
421        self.assertEqual(more_rows, 6) # 6 new rows: 2 params per index
422
423        # Back to 0
424        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0)
425        self.assertEqual(self.widget._model_model.rowCount(), last_row)
426
427    def testPlotTheory(self):
428        """
429        See that theory item can produce a chart
430        """
431        # By default, the compute/plot button is disabled
432        self.assertFalse(self.widget.cmdPlot.isEnabled())
433        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
434
435        # Assign a model
436        self.widget.show()
437        # Change the category index so we have a model available
438        category_index = self.widget.cbCategory.findText("Sphere")
439        self.widget.cbCategory.setCurrentIndex(category_index)
440
441        # Check the enablement/text
442        self.assertTrue(self.widget.cmdPlot.isEnabled())
443        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate')
444
445        # Spying on plot update signal
446        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
447
448        # Press Calculate
449        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
450
451        # Observe cmdPlot caption change
452        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
453
454        # Make sure the signal has NOT been emitted
455        self.assertEqual(spy.count(), 0)
456
457        # Click again
458        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
459
460        # This time, we got the update signal
461        self.assertEqual(spy.count(), 0)
462
463    def testPlotData(self):
464        """
465        See that data item can produce a chart
466        """
467        # By default, the compute/plot button is disabled
468        self.assertFalse(self.widget.cmdPlot.isEnabled())
469        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
470
471        self.widget.show()
472
473        # Set data
474        test_data = Data1D(x=[1,2], y=[1,2])
475
476        # Force same data into logic
477        self.widget.logic.data = test_data
478        self.widget.data_is_loaded = True
479
480        # Change the category index so we have a model available
481        category_index = self.widget.cbCategory.findText("Sphere")
482        self.widget.cbCategory.setCurrentIndex(category_index)
483
484        # Check the enablement/text
485        self.assertTrue(self.widget.cmdPlot.isEnabled())
486        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
487
488        # Spying on plot update signal
489        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
490
491        # Press Calculate
492        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
493
494        # Observe cmdPlot caption did not change
495        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
496
497        # Make sure the signal has been emitted == new plot
498        self.assertEqual(spy.count(), 1)
499
500
501if __name__ == "__main__":
502    unittest.main()
Note: See TracBrowser for help on using the repository browser.