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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 8faac15 was 8faac15, checked in by Piotr Rozyczko <piotrrozyczko@…>, 6 years ago

Fixed the tooltip SASVIEW-1067

  • Property mode set to 100644
File size: 58.7 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.Perspectives.Fitting.Constraint import Constraint
20import sas.qtgui.Utilities.LocalConfig
21from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy
22from sas.qtgui.Perspectives.Fitting.ModelThread import Calc1D
23from sas.qtgui.Perspectives.Fitting.ModelThread import Calc2D
24
25from sas.qtgui.Plotting.PlotterData import Data1D
26from sas.qtgui.Plotting.PlotterData import Data2D
27
28if not QtWidgets.QApplication.instance():
29    app = QtWidgets.QApplication(sys.argv)
30
31class dummy_manager(object):
32    HELP_DIRECTORY_LOCATION = "html"
33    communicate = Communicate()
34
35class FittingWidgetTest(unittest.TestCase):
36    """Test the fitting widget GUI"""
37
38    def setUp(self):
39        """Create the GUI"""
40        self.widget = FittingWidget(dummy_manager())
41
42    def tearDown(self):
43        """Destroy the GUI"""
44        self.widget.close()
45        del self.widget
46
47    def testDefaults(self):
48        """Test the GUI in its default state"""
49        self.assertIsInstance(self.widget, QtWidgets.QWidget)
50        self.assertEqual(self.widget.windowTitle(), "Fitting")
51        self.assertEqual(self.widget.sizePolicy().Policy(), QtWidgets.QSizePolicy.Fixed)
52        self.assertIsInstance(self.widget.lstParams.model(), QtGui.QStandardItemModel)
53        self.assertIsInstance(self.widget.lstPoly.model(), QtGui.QStandardItemModel)
54        self.assertIsInstance(self.widget.lstMagnetic.model(), QtGui.QStandardItemModel)
55        self.assertFalse(self.widget.cbModel.isEnabled())
56        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
57        self.assertFalse(self.widget.cmdFit.isEnabled())
58        self.assertTrue(self.widget.acceptsData())
59        self.assertFalse(self.widget.data_is_loaded)
60
61    def testSelectCategoryDefault(self):
62        """
63        Test if model categories have been loaded properly
64        """
65        fittingWindow =  self.widget
66
67        #Test loading from json categories
68        category_list = list(fittingWindow.master_category_dict.keys())
69
70        for category in category_list:
71            self.assertNotEqual(fittingWindow.cbCategory.findText(category),-1)
72
73        #Test what is current text in the combobox
74        self.assertEqual(fittingWindow.cbCategory.currentText(), CATEGORY_DEFAULT)
75
76    def testWidgetWithData(self):
77        """
78        Test the instantiation of the widget with initial data
79        """
80        data = Data1D(x=[1,2], y=[1,2])
81        GuiUtils.dataFromItem = MagicMock(return_value=data)
82        item = QtGui.QStandardItem("test")
83
84        widget_with_data = FittingWidget(dummy_manager(), data=item, tab_id=3)
85
86        self.assertEqual(widget_with_data.data, data)
87        self.assertTrue(widget_with_data.data_is_loaded)
88        # self.assertTrue(widget_with_data.cmdFit.isEnabled())
89        self.assertFalse(widget_with_data.acceptsData())
90
91    def testSelectPolydispersity(self):
92        """
93        Test if models have been loaded properly
94        """
95        fittingWindow =  self.widget
96
97        self.assertIsInstance(fittingWindow.lstPoly.itemDelegate(), QtWidgets.QStyledItemDelegate)
98        #Test loading from json categories
99        fittingWindow.SASModelToQModel("cylinder")
100        pd_index = fittingWindow.lstPoly.model().index(0,0)
101        self.assertEqual(str(pd_index.data()), "Distribution of radius")
102        pd_index = fittingWindow.lstPoly.model().index(1,0)
103        self.assertEqual(str(pd_index.data()), "Distribution of length")
104
105        # test the delegate a bit
106        delegate = fittingWindow.lstPoly.itemDelegate()
107        self.assertEqual(len(delegate.POLYDISPERSE_FUNCTIONS), 5)
108        self.assertEqual(delegate.editableParameters(), [1, 2, 3, 4, 5])
109        self.assertEqual(delegate.poly_function, 6)
110        self.assertIsInstance(delegate.combo_updated, QtCore.pyqtBoundSignal)
111
112    def testSelectMagnetism(self):
113        """
114        Test if models have been loaded properly
115        """
116        fittingWindow =  self.widget
117
118        self.assertIsInstance(fittingWindow.lstMagnetic.itemDelegate(), QtWidgets.QStyledItemDelegate)
119        #Test loading from json categories
120        fittingWindow.SASModelToQModel("cylinder")
121        mag_index = fittingWindow.lstMagnetic.model().index(0,0)
122        self.assertEqual(mag_index.data(), "up:frac_i")
123        mag_index = fittingWindow.lstMagnetic.model().index(1,0)
124        self.assertEqual(mag_index.data(), "up:frac_f")
125        mag_index = fittingWindow.lstMagnetic.model().index(2,0)
126        self.assertEqual(mag_index.data(), "up:angle")
127        mag_index = fittingWindow.lstMagnetic.model().index(3,0)
128        self.assertEqual(mag_index.data(), "M0:sld")
129        mag_index = fittingWindow.lstMagnetic.model().index(4,0)
130        self.assertEqual(mag_index.data(), "mtheta:sld")
131        mag_index = fittingWindow.lstMagnetic.model().index(5,0)
132        self.assertEqual(mag_index.data(), "mphi:sld")
133        mag_index = fittingWindow.lstMagnetic.model().index(6,0)
134        self.assertEqual(mag_index.data(), "M0:sld_solvent")
135        mag_index = fittingWindow.lstMagnetic.model().index(7,0)
136        self.assertEqual(mag_index.data(), "mtheta:sld_solvent")
137        mag_index = fittingWindow.lstMagnetic.model().index(8,0)
138        self.assertEqual(mag_index.data(), "mphi:sld_solvent")
139
140        # test the delegate a bit
141        delegate = fittingWindow.lstMagnetic.itemDelegate()
142        self.assertEqual(delegate.editableParameters(), [1, 2, 3])
143
144    def testSelectStructureFactor(self):
145        """
146        Test if structure factors have been loaded properly
147        """
148        fittingWindow =  self.widget
149
150        #Test for existence in combobox
151        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("stickyhardsphere"),-1)
152        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hayter_msa"),-1)
153        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("squarewell"),-1)
154        self.assertNotEqual(fittingWindow.cbStructureFactor.findText("hardsphere"),-1)
155
156        #Test what is current text in the combobox
157        self.assertTrue(fittingWindow.cbCategory.currentText(), "None")
158
159    def testSignals(self):
160        """
161        Test the widget emitted signals
162        """
163        pass
164
165    def testSelectCategory(self):
166        """
167        Assure proper behaviour on changing category
168        """
169        self.widget.show()
170        self.assertEqual(self.widget._previous_category_index, 0)
171        # confirm the model combo contains no models
172        self.assertEqual(self.widget.cbModel.count(), 0)
173
174        # invoke the method by changing the index
175        category_index = self.widget.cbCategory.findText("Shape Independent")
176        self.widget.cbCategory.setCurrentIndex(category_index)
177
178        # test the model combo content
179        self.assertEqual(self.widget.cbModel.count(), 29)
180
181        # Try to change back to default
182        self.widget.cbCategory.setCurrentIndex(0)
183
184        # Observe no such luck
185        self.assertEqual(self.widget.cbCategory.currentIndex(), 7)
186        self.assertEqual(self.widget.cbModel.count(), 29)
187
188        # Set the structure factor
189        structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
190        self.widget.cbCategory.setCurrentIndex(structure_index)
191        # check the enablement of controls
192        self.assertFalse(self.widget.cbModel.isEnabled())
193        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
194
195    def testSelectModel(self):
196        """
197        Assure proper behaviour on changing model
198        """
199        self.widget.show()
200        # Change the category index so we have some models
201        category_index = self.widget.cbCategory.findText("Shape Independent")
202        self.widget.cbCategory.setCurrentIndex(category_index)
203
204        # check the enablement of controls
205        self.assertTrue(self.widget.cbModel.isEnabled())
206        self.assertFalse(self.widget.cbStructureFactor.isEnabled())
207
208        # set up the model update spy
209        # spy = QtSignalSpy(self.widget._model_model, self.widget._model_model.itemChanged)
210
211        # mock the tested methods
212        self.widget.SASModelToQModel = MagicMock()
213        self.widget.createDefaultDataset = MagicMock()
214        self.widget.calculateQGridForModel = MagicMock()
215        #
216        # Now change the model
217        self.widget.cbModel.setCurrentIndex(3)
218        self.assertEqual(self.widget.cbModel.currentText(),'dab')
219
220        # No data sent -> no index set, only createDefaultDataset called
221        self.assertTrue(self.widget.createDefaultDataset.called)
222        self.assertTrue(self.widget.SASModelToQModel.called)
223        self.assertFalse(self.widget.calculateQGridForModel.called)
224
225        # Let's tell the widget that data has been loaded
226        self.widget.data_is_loaded = True
227        # Reset the sasmodel index
228        self.widget.cbModel.setCurrentIndex(1)
229        self.assertEqual(self.widget.cbModel.currentText(),'broad_peak')
230
231        # Observe calculateQGridForModel called
232        self.assertTrue(self.widget.calculateQGridForModel.called)
233
234    def testSelectFactor(self):
235        """
236        Assure proper behaviour on changing structure factor
237        """
238        self.widget.show()
239        # Change the category index so we have some models
240        category_index = self.widget.cbCategory.findText("Shape Independent")
241        self.widget.cbCategory.setCurrentIndex(category_index)
242        # Change the model to one that supports structure factors
243        model_index = self.widget.cbModel.findText('fractal_core_shell')
244        self.widget.cbModel.setCurrentIndex(model_index)
245
246        # Check that the factor combo is active and the default is chosen
247        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
248        self.assertEqual(self.widget.cbStructureFactor.currentText(), STRUCTURE_DEFAULT)
249
250        # We have this many rows in the model
251        rowcount = self.widget._model_model.rowCount()
252        #self.assertEqual(self.widget._model_model.rowCount(), 8)
253
254        # Change structure factor to something more exciting
255        structure_index = self.widget.cbStructureFactor.findText('squarewell')
256        self.widget.cbStructureFactor.setCurrentIndex(structure_index)
257
258        # We have 3 more param rows now (radius_effective is removed), and a new heading
259        self.assertEqual(self.widget._model_model.rowCount(), rowcount+4)
260
261        # Switch models
262        self.widget.cbModel.setCurrentIndex(0)
263
264        # Observe factor doesn't reset to None
265        self.assertEqual(self.widget.cbStructureFactor.currentText(), 'squarewell')
266
267        # Switch category to structure factor
268        structure_index=self.widget.cbCategory.findText(CATEGORY_STRUCTURE)
269        self.widget.cbCategory.setCurrentIndex(structure_index)
270        # Observe the correct enablement
271        self.assertTrue(self.widget.cbStructureFactor.isEnabled())
272        self.assertFalse(self.widget.cbModel.isEnabled())
273        self.assertEqual(self.widget._model_model.rowCount(), 0)
274
275        # Choose the last factor
276        last_index = self.widget.cbStructureFactor.count()
277        self.widget.cbStructureFactor.setCurrentIndex(last_index-1)
278        # Do we have all the rows (incl. radius_effective & heading row)?
279        self.assertEqual(self.widget._model_model.rowCount(), 5)
280
281        # Are the command buttons properly enabled?
282        self.assertTrue(self.widget.cmdPlot.isEnabled())
283        self.assertFalse(self.widget.cmdFit.isEnabled())
284
285    def testReadCategoryInfo(self):
286        """
287        Check the category file reader
288        """
289        # Tested in default checks
290        pass
291
292    def testUpdateParamsFromModel(self):
293        """
294        Checks the sasmodel parameter update from QModel items
295        """
296        # Tested in default checks
297        pass
298
299    def testCreateTheoryIndex(self):
300        """
301        Test the data->QIndex conversion
302        """
303        # set up the model update spy
304        spy = QtSignalSpy(self.widget._model_model, self.widget.communicate.updateTheoryFromPerspectiveSignal)
305
306        self.widget.show()
307        # Change the category index so we have some models
308        self.widget.cbCategory.setCurrentIndex(1)
309
310        # Create the index
311        self.widget.createTheoryIndex(Data1D(x=[1,2], y=[1,2]))
312
313        # Make sure the signal has been emitted
314        self.assertEqual(spy.count(), 1)
315
316        # Check the argument type
317        self.assertIsInstance(spy.called()[0]['args'][0], QtGui.QStandardItem)
318
319    def testCalculateQGridForModel(self):
320        """
321        Check that the fitting 1D data object is ready
322        """
323
324        if LocalConfig.USING_TWISTED:
325            # Mock the thread creation
326            threads.deferToThread = MagicMock()
327            # Model for theory
328            self.widget.SASModelToQModel("cylinder")
329            # Call the tested method
330            self.widget.calculateQGridForModel()
331            time.sleep(1)
332            # Test the mock
333            self.assertTrue(threads.deferToThread.called)
334            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, "compute")
335        else:
336            Calc2D.queue = MagicMock()
337            # Model for theory
338            self.widget.SASModelToQModel("cylinder")
339            # Call the tested method
340            self.widget.calculateQGridForModel()
341            time.sleep(1)
342            # Test the mock
343            self.assertTrue(Calc2D.queue.called)
344
345    def testCalculateResiduals(self):
346        """
347        Check that the residuals are calculated and plots updated
348        """
349        test_data = Data1D(x=[1,2], y=[1,2])
350
351        # Model for theory
352        self.widget.SASModelToQModel("cylinder")
353        # Invoke the tested method
354        self.widget.calculateResiduals(test_data)
355        # Check the Chi2 value - should be undetermined
356        self.assertEqual(self.widget.lblChi2Value.text(), '---')
357
358        # Force same data into logic
359        self.widget.logic.data = test_data
360        self.widget.calculateResiduals(test_data)
361        # Now, the difference is 0, as data is the same
362        self.assertEqual(self.widget.lblChi2Value.text(), '0')
363
364        # Change data
365        test_data_2 = Data1D(x=[1,2], y=[2.1,3.49])
366        self.widget.logic.data = test_data_2
367        self.widget.calculateResiduals(test_data)
368        # Now, the difference is non-zero
369        self.assertEqual(float(self.widget.lblChi2Value.text()), 1.7151)
370
371    def testSetPolyModel(self):
372        """
373        Test the polydispersity model setup
374        """
375        self.widget.show()
376        # Change the category index so we have a model with no poly
377        category_index = self.widget.cbCategory.findText("Shape Independent")
378        self.widget.cbCategory.setCurrentIndex(category_index)
379        # Check the poly model
380        self.assertEqual(self.widget._poly_model.rowCount(), 0)
381        self.assertEqual(self.widget._poly_model.columnCount(), 0)
382
383        # Change the category index so we have a model available
384        self.widget.cbCategory.setCurrentIndex(2)
385
386        # Check the poly model
387        self.assertEqual(self.widget._poly_model.rowCount(), 4)
388        self.assertEqual(self.widget._poly_model.columnCount(), 8)
389
390        # Test the header
391        self.assertEqual(self.widget.lstPoly.horizontalHeader().count(), 8)
392        self.assertFalse(self.widget.lstPoly.horizontalHeader().stretchLastSection())
393
394        # Test tooltips
395        self.assertEqual(len(self.widget._poly_model.header_tooltips), 9)
396
397        header_tooltips = ['Select parameter for fitting',
398                            'Enter polydispersity ratio (Std deviation/mean).\n'+
399                            'For angles this can be either std deviation or half width (for uniform distributions) in degrees',
400                            'Enter minimum value for parameter',
401                            'Enter maximum value for parameter',
402                            'Enter number of points for parameter',
403                            'Enter number of sigmas parameter',
404                            'Select distribution function',
405                            'Select filename with user-definable distribution']
406        for column, tooltip in enumerate(header_tooltips):
407             self.assertEqual(self.widget._poly_model.headerData( column,
408                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
409                         header_tooltips[column])
410
411        # Test presence of comboboxes in last column
412        for row in range(self.widget._poly_model.rowCount()):
413            func_index = self.widget._poly_model.index(row, 6)
414            self.assertTrue(isinstance(self.widget.lstPoly.indexWidget(func_index), QtWidgets.QComboBox))
415            self.assertIn('Distribution of', self.widget._poly_model.item(row, 0).text())
416        #self.widget.close()
417
418    def testPolyModelChange(self):
419        """
420        Polydispersity model changed - test all possible scenarios
421        """
422        self.widget.show()
423        # Change the category index so we have a model with polydisp
424        category_index = self.widget.cbCategory.findText("Cylinder")
425        self.widget.cbCategory.setCurrentIndex(category_index)
426        model_index = self.widget.cbModel.findText("barbell")
427        self.widget.cbModel.setCurrentIndex(model_index)
428
429        # click on a poly parameter checkbox
430        index = self.widget._poly_model.index(0,0)
431
432        # Set the checbox
433        self.widget._poly_model.item(0,0).setCheckState(2)
434        # Assure the parameter is added
435        self.assertEqual(self.widget.poly_params_to_fit, ['radius_bell.width'])
436
437        # Add another parameter
438        self.widget._poly_model.item(2,0).setCheckState(2)
439        # Assure the parameters are added
440        self.assertEqual(self.widget.poly_params_to_fit, ['radius_bell.width', 'length.width'])
441
442        # Change the min/max values
443        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 0.0)
444        self.widget._poly_model.item(0,2).setText("1.0")
445        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 1.0)
446
447        #self.widget.show()
448        #QtWidgets.QApplication.exec_()
449
450        # Change the number of points
451        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 35)
452        self.widget._poly_model.item(0,4).setText("22")
453        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 22)
454        # try something stupid
455        self.widget._poly_model.item(0,4).setText("butt")
456        # see that this didn't annoy the control at all
457        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 22)
458
459        # Change the number of sigmas
460        self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 3)
461        self.widget._poly_model.item(0,5).setText("222")
462        self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 222)
463        # try something stupid again
464        self.widget._poly_model.item(0,4).setText("beer")
465        # no efect
466        self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 222)
467
468    def testOnPolyComboIndexChange(self):
469        """
470        Test the slot method for polydisp. combo box index change
471        """
472        self.widget.show()
473        # Change the category index so we have a model with polydisp
474        category_index = self.widget.cbCategory.findText("Cylinder")
475        self.widget.cbCategory.setCurrentIndex(category_index)
476        model_index = self.widget.cbModel.findText("barbell")
477        self.widget.cbModel.setCurrentIndex(model_index)
478
479        # call method with default settings
480        self.widget.onPolyComboIndexChange('gaussian', 0)
481        # check values
482        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
483        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 3)
484        # Change the index
485        self.widget.onPolyComboIndexChange('rectangle', 0)
486        # check values
487        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 35)
488        self.assertAlmostEqual(self.widget.poly_params['radius_bell.nsigmas'], 1.73205, 5)
489        # Change the index
490        self.widget.onPolyComboIndexChange('lognormal', 0)
491        # check values
492        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 80)
493        self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 8)
494        # Change the index
495        self.widget.onPolyComboIndexChange('schulz', 0)
496        # check values
497        self.assertEqual(self.widget.poly_params['radius_bell.npts'], 80)
498        self.assertEqual(self.widget.poly_params['radius_bell.nsigmas'], 8)
499
500        # mock up file load
501        self.widget.loadPolydispArray = MagicMock()
502        # Change to 'array'
503        self.widget.onPolyComboIndexChange('array', 0)
504        # See the mock fire
505        self.assertTrue(self.widget.loadPolydispArray.called)
506
507    def testLoadPolydispArray(self):
508        """
509        Test opening of the load file dialog for 'array' polydisp. function
510        """
511
512        # open a non-existent file
513        filename = os.path.join("UnitTesting", "testdata_noexist.txt")
514        with self.assertRaises(OSError, msg="testdata_noexist.txt should be a non-existent file"):
515            os.stat(filename)
516        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
517        self.widget.show()
518        # Change the category index so we have a model with polydisp
519        category_index = self.widget.cbCategory.findText("Cylinder")
520        self.widget.cbCategory.setCurrentIndex(category_index)
521        model_index = self.widget.cbModel.findText("barbell")
522        self.widget.cbModel.setCurrentIndex(model_index)
523
524        self.widget.onPolyComboIndexChange('array', 0)
525        # check values - unchanged since the file doesn't exist
526        self.assertTrue(self.widget._poly_model.item(0, 1).isEnabled())
527        with self.assertRaises(AttributeError):
528            self.widget.disp_model()
529
530        # good file
531        # TODO: this depends on the working directory being src/sas/qtgui,
532        # TODO: which isn't convenient if you want to run this test suite
533        # TODO: individually
534        filename = os.path.join("UnitTesting", "testdata.txt")
535        try:
536            os.stat(filename)
537        except OSError:
538            self.assertTrue(False, "testdata.txt does not exist")
539        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
540
541        self.widget.onPolyComboIndexChange('array', 0)
542        # check values - disabled control, present weights
543        self.assertFalse(self.widget._poly_model.item(0, 1).isEnabled())
544        self.assertEqual(self.widget.disp_model.weights[0], 2.83954)
545        self.assertEqual(len(self.widget.disp_model.weights), 19)
546        self.assertEqual(len(self.widget.disp_model.values), 19)
547        self.assertEqual(self.widget.disp_model.values[0], 0.0)
548        self.assertEqual(self.widget.disp_model.values[18], 3.67347)
549
550    def testSetMagneticModel(self):
551        """
552        Test the magnetic model setup
553        """
554        self.widget.show()
555        # Change the category index so we have a model available
556        category_index = self.widget.cbCategory.findText("Sphere")
557        self.widget.cbCategory.setCurrentIndex(category_index)
558
559        # Check the magnetic model
560        self.assertEqual(self.widget._magnet_model.rowCount(), 9)
561        self.assertEqual(self.widget._magnet_model.columnCount(), 5)
562
563        # Test the header
564        self.assertEqual(self.widget.lstMagnetic.horizontalHeader().count(), 5)
565        self.assertFalse(self.widget.lstMagnetic.horizontalHeader().stretchLastSection())
566
567        #Test tooltips
568        self.assertEqual(len(self.widget._magnet_model.header_tooltips), 5)
569
570        header_tooltips = ['Select parameter for fitting',
571                           'Enter parameter value',
572                           'Enter minimum value for parameter',
573                           'Enter maximum value for parameter',
574                           'Unit of the parameter']
575        for column, tooltip in enumerate(header_tooltips):
576             self.assertEqual(self.widget._magnet_model.headerData(column,
577                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
578                         header_tooltips[column])
579
580        # Test rows
581        for row in range(self.widget._magnet_model.rowCount()):
582            func_index = self.widget._magnet_model.index(row, 0)
583            self.assertIn(':', self.widget._magnet_model.item(row, 0).text())
584
585
586    def testAddExtraShells(self):
587        """
588        Test how the extra shells are presented
589        """
590        pass
591
592    def testModifyShellsInList(self):
593        """
594        Test the additional rows added by modifying the shells combobox
595        """
596        self.widget.show()
597        # Change the model to multi shell
598        category_index = self.widget.cbCategory.findText("Sphere")
599        self.widget.cbCategory.setCurrentIndex(category_index)
600        model_index = self.widget.cbModel.findText("core_multi_shell")
601        self.widget.cbModel.setCurrentIndex(model_index)
602
603        # Assure we have the combobox available
604        cbox_row = self.widget._n_shells_row
605        func_index = self.widget._model_model.index(cbox_row, 1)
606        self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtWidgets.QComboBox)
607
608        # get number of rows before changing shell count
609        last_row = self.widget._model_model.rowCount()
610
611        # Change the combo box index
612        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(3)
613
614        # Check that the number of rows increased
615        # (note that n == 1 by default in core_multi_shell so this increases index by 2)
616        more_rows = self.widget._model_model.rowCount() - last_row
617        self.assertEqual(more_rows, 4) # 4 new rows: 2 params per index
618
619        # Set to 0
620        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0)
621        self.assertEqual(self.widget._model_model.rowCount(), last_row - 2)
622
623    def testPlotTheory(self):
624        """
625        See that theory item can produce a chart
626        """
627        # By default, the compute/plot button is disabled
628        self.assertFalse(self.widget.cmdPlot.isEnabled())
629        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
630
631        # Assign a model
632        self.widget.show()
633        # Change the category index so we have a model available
634        category_index = self.widget.cbCategory.findText("Sphere")
635        self.widget.cbCategory.setCurrentIndex(category_index)
636
637        # Check the enablement/text
638        self.assertTrue(self.widget.cmdPlot.isEnabled())
639        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate')
640
641        # Spying on plot update signal
642        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
643
644        # Press Calculate
645        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
646
647        # Observe cmdPlot caption change
648        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
649
650        # Make sure the signal has NOT been emitted
651        self.assertEqual(spy.count(), 0)
652
653        # Click again
654        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
655
656        # This time, we got the update signal
657        self.assertEqual(spy.count(), 0)
658
659    def notestPlotData(self):
660        """
661        See that data item can produce a chart
662        """
663        # By default, the compute/plot button is disabled
664        self.assertFalse(self.widget.cmdPlot.isEnabled())
665        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
666
667        # Set data
668        test_data = Data1D(x=[1,2], y=[1,2])
669        item = QtGui.QStandardItem()
670        updateModelItem(item, test_data, "test")
671        # Force same data into logic
672        self.widget.data = item
673
674        # Change the category index so we have a model available
675        category_index = self.widget.cbCategory.findText("Sphere")
676        self.widget.cbCategory.setCurrentIndex(category_index)
677
678        # Check the enablement/text
679        self.assertTrue(self.widget.cmdPlot.isEnabled())
680        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
681
682        # Spying on plot update signal
683        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
684
685        # Press Calculate
686        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
687
688        # Observe cmdPlot caption did not change
689        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
690
691        # Make sure the signal has been emitted == new plot
692        self.assertEqual(spy.count(), 1)
693
694    def testOnEmptyFit(self):
695        """
696        Test a 1D/2D fit with no parameters
697        """
698        # Set data
699        test_data = Data1D(x=[1,2], y=[1,2])
700        item = QtGui.QStandardItem()
701        updateModelItem(item, test_data, "test")
702        # Force same data into logic
703        self.widget.data = item
704        category_index = self.widget.cbCategory.findText("Sphere")
705        self.widget.cbCategory.setCurrentIndex(category_index)
706
707        self.widget.show()
708
709        # Test no fitting params
710        self.widget.main_params_to_fit = []
711
712        logging.error = MagicMock()
713
714        self.widget.onFit()
715        self.assertTrue(logging.error.called_with('no fitting parameters'))
716        self.widget.close()
717
718    def testOnEmptyFit2(self):
719        test_data = Data2D(image=[1.0, 2.0, 3.0],
720                           err_image=[0.01, 0.02, 0.03],
721                           qx_data=[0.1, 0.2, 0.3],
722                           qy_data=[0.1, 0.2, 0.3],
723                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
724                           mask=[True, True, True])
725
726        # Force same data into logic
727        item = QtGui.QStandardItem()
728        updateModelItem(item, test_data, "test")
729
730        # Force same data into logic
731        self.widget.data = item
732        category_index = self.widget.cbCategory.findText("Sphere")
733        self.widget.cbCategory.setCurrentIndex(category_index)
734
735        self.widget.show()
736
737        # Test no fitting params
738        self.widget.main_params_to_fit = []
739
740        logging.error = MagicMock()
741
742        self.widget.onFit()
743        self.assertTrue(logging.error.called_once())
744        self.assertTrue(logging.error.called_with('no fitting parameters'))
745        self.widget.close()
746
747    def notestOnFit1D(self):
748        """
749        Test the threaded fitting call
750        """
751        # Set data
752        test_data = Data1D(x=[1,2], y=[1,2])
753        item = QtGui.QStandardItem()
754        updateModelItem(item, test_data, "test")
755        # Force same data into logic
756        self.widget.data = item
757        category_index = self.widget.cbCategory.findText("Sphere")
758        self.widget.cbCategory.setCurrentIndex(category_index)
759
760        self.widget.show()
761
762        # Assing fitting params
763        self.widget.main_params_to_fit = ['scale']
764
765        # Spying on status update signal
766        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
767
768        with threads.deferToThread as MagicMock:
769            self.widget.onFit()
770            # thread called
771            self.assertTrue(threads.deferToThread.called)
772            # thread method is 'compute'
773            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
774
775            # the fit button changed caption and got disabled
776            # could fail if machine fast enough to finish
777            #self.assertEqual(self.widget.cmdFit.text(), 'Stop fit')
778            #self.assertFalse(self.widget.cmdFit.isEnabled())
779
780            # Signal pushed up
781            self.assertEqual(update_spy.count(), 1)
782
783        self.widget.close()
784
785    def notestOnFit2D(self):
786        """
787        Test the threaded fitting call
788        """
789        # Set data
790        test_data = Data2D(image=[1.0, 2.0, 3.0],
791                           err_image=[0.01, 0.02, 0.03],
792                           qx_data=[0.1, 0.2, 0.3],
793                           qy_data=[0.1, 0.2, 0.3],
794                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
795                           mask=[True, True, True])
796
797        # Force same data into logic
798        item = QtGui.QStandardItem()
799        updateModelItem(item, test_data, "test")
800        # Force same data into logic
801        self.widget.data = item
802        category_index = self.widget.cbCategory.findText("Sphere")
803        self.widget.cbCategory.setCurrentIndex(category_index)
804
805        self.widget.show()
806
807        # Assing fitting params
808        self.widget.main_params_to_fit = ['scale']
809
810        # Spying on status update signal
811        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
812
813        with threads.deferToThread as MagicMock:
814            self.widget.onFit()
815            # thread called
816            self.assertTrue(threads.deferToThread.called)
817            # thread method is 'compute'
818            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
819
820            # the fit button changed caption and got disabled
821            #self.assertEqual(self.widget.cmdFit.text(), 'Stop fit')
822            #self.assertFalse(self.widget.cmdFit.isEnabled())
823
824            # Signal pushed up
825            self.assertEqual(update_spy.count(), 1)
826
827    def testOnHelp(self):
828        """
829        Test various help pages shown in this widget
830        """
831        #Mock the webbrowser.open method
832        self.widget.parent.showHelp = MagicMock()
833        #webbrowser.open = MagicMock()
834
835        # Invoke the action on default tab
836        self.widget.onHelp()
837        # Check if show() got called
838        self.assertTrue(self.widget.parent.showHelp.called)
839        # Assure the filename is correct
840        self.assertIn("fitting_help.html", self.widget.parent.showHelp.call_args[0][0])
841
842        # Change the tab to options
843        self.widget.tabFitting.setCurrentIndex(1)
844        self.widget.onHelp()
845        # Check if show() got called
846        self.assertEqual(self.widget.parent.showHelp.call_count, 2)
847        # Assure the filename is correct
848        self.assertIn("residuals_help.html", self.widget.parent.showHelp.call_args[0][0])
849
850        # Change the tab to smearing
851        self.widget.tabFitting.setCurrentIndex(2)
852        self.widget.onHelp()
853        # Check if show() got called
854        self.assertEqual(self.widget.parent.showHelp.call_count, 3)
855        # Assure the filename is correct
856        self.assertIn("resolution.html", self.widget.parent.showHelp.call_args[0][0])
857
858        # Change the tab to poly
859        self.widget.tabFitting.setCurrentIndex(3)
860        self.widget.onHelp()
861        # Check if show() got called
862        self.assertEqual(self.widget.parent.showHelp.call_count, 4)
863        # Assure the filename is correct
864        self.assertIn("polydispersity.html", self.widget.parent.showHelp.call_args[0][0])
865
866        # Change the tab to magnetism
867        self.widget.tabFitting.setCurrentIndex(4)
868        self.widget.onHelp()
869        # Check if show() got called
870        self.assertEqual(self.widget.parent.showHelp.call_count, 5)
871        # Assure the filename is correct
872        self.assertIn("magnetism.html", self.widget.parent.showHelp.call_args[0][0])
873
874    def testReadFitPage(self):
875        """
876        Read in the fitpage object and restore state
877        """
878        # Set data
879        test_data = Data1D(x=[1,2], y=[1,2])
880        item = QtGui.QStandardItem()
881        updateModelItem(item, test_data, "test")
882        # Force same data into logic
883        self.widget.data = item
884
885        # Force same data into logic
886        category_index = self.widget.cbCategory.findText('Sphere')
887
888        self.widget.cbCategory.setCurrentIndex(category_index)
889        self.widget.main_params_to_fit = ['scale']
890        # Invoke the tested method
891        fp = self.widget.currentState()
892
893        # Prepare modified fit page
894        fp.current_model = 'onion'
895        fp.is_polydisperse = True
896
897        # Read in modified state
898        self.widget.readFitPage(fp)
899
900        # Check if the widget got updated accordingly
901        self.assertEqual(self.widget.cbModel.currentText(), 'onion')
902        self.assertTrue(self.widget.chkPolydispersity.isChecked())
903        #Check if polidispersity tab is available
904        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
905
906        #Check if magnetism box and tab are disabled when 1D data is loaded
907        self.assertFalse(self.widget.chkMagnetism.isEnabled())
908        self.assertFalse(self.widget.tabFitting.isTabEnabled(4))
909
910    # to be fixed after functionality is ready
911    def notestReadFitPage2D(self):
912        """
913        Read in the fitpage object and restore state
914        """
915        # Set data
916
917        test_data = Data2D(image=[1.0, 2.0, 3.0],
918                           err_image=[0.01, 0.02, 0.03],
919                           qx_data=[0.1, 0.2, 0.3],
920                           qy_data=[0.1, 0.2, 0.3],
921                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
922                           mask=[True, True, True])
923
924        # Force same data into logic
925        self.widget.logic.data = test_data
926        self.widget.data_is_loaded = True
927
928        #item = QtGui.QStandardItem()
929        #updateModelItem(item, [test_data], "test")
930        # Force same data into logic
931        #self.widget.logic.data = item
932        #self.widget.data_is_loaded = True
933
934        category_index = self.widget.cbCategory.findText("Cylinder")
935        self.widget.cbCategory.setCurrentIndex(category_index)
936
937        # Test no fitting params
938        self.widget.main_params_to_fit = ['scale']
939
940        # Invoke the tested method
941        fp = self.widget.currentState()
942
943        # Prepare modified fit page
944        fp.current_model = 'cylinder'
945        fp.is_polydisperse = True
946        fp.is_magnetic = True
947        fp.is2D = True
948
949        # Read in modified state
950        self.widget.readFitPage(fp)
951
952        # Check if the widget got updated accordingly
953        self.assertEqual(self.widget.cbModel.currentText(), 'cylinder')
954        self.assertTrue(self.widget.chkPolydispersity.isChecked())
955        self.assertTrue(self.widget.chkPolydispersity.isEnabled())
956        #Check if polidispersity tab is available
957        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
958
959        #Check if magnetism box and tab are disabled when 1D data is loaded
960        self.assertTrue(self.widget.chkMagnetism.isChecked())
961        self.assertTrue(self.widget.chkMagnetism.isEnabled())
962        self.assertTrue(self.widget.tabFitting.isTabEnabled(4))
963
964    def testCurrentState(self):
965        """
966        Set up the fitpage with current state
967        """
968        # Set data
969        test_data = Data1D(x=[1,2], y=[1,2])
970        item = QtGui.QStandardItem()
971        updateModelItem(item, test_data, "test")
972        # Force same data into logic
973        self.widget.data = item
974        category_index = self.widget.cbCategory.findText("Sphere")
975        self.widget.cbCategory.setCurrentIndex(category_index)
976        self.widget.main_params_to_fit = ['scale']
977
978        # Invoke the tested method
979        fp = self.widget.currentState()
980
981        # Test some entries. (Full testing of fp is done in FitPageTest)
982        self.assertIsInstance(fp.data, Data1D)
983        self.assertListEqual(list(fp.data.x), [1,2])
984        self.assertTrue(fp.data_is_loaded)
985        self.assertEqual(fp.current_category, "Sphere")
986        self.assertEqual(fp.current_model, "adsorbed_layer")
987        self.assertListEqual(fp.main_params_to_fit, ['scale'])
988
989    def testPushFitPage(self):
990        """
991        Push current state of fitpage onto stack
992        """
993        # Set data
994        test_data = Data1D(x=[1,2], y=[1,2])
995        item = QtGui.QStandardItem()
996        updateModelItem(item, test_data, "test")
997        # Force same data into logic
998        self.widget.data = item
999        category_index = self.widget.cbCategory.findText("Sphere")
1000
1001        # Asses the initial state of stack
1002        self.assertEqual(self.widget.page_stack, [])
1003
1004        # Set the undo flag
1005        self.widget.undo_supported = True
1006        self.widget.cbCategory.setCurrentIndex(category_index)
1007        self.widget.main_params_to_fit = ['scale']
1008
1009        # Check that the stack is updated
1010        self.assertEqual(len(self.widget.page_stack), 1)
1011
1012        # Change another parameter
1013        self.widget._model_model.item(2, 1).setText("3.0")
1014        # Check that the stack is updated
1015        self.assertEqual(len(self.widget.page_stack), 2)
1016
1017    def testPopFitPage(self):
1018        """
1019        Pop current state of fitpage from stack
1020        """
1021        # TODO: to be added when implementing UNDO/REDO
1022        pass
1023
1024    def testOnMainPageChange(self):
1025        """
1026        Test update  values of modified parameters in models
1027        """
1028        # select model: cylinder / cylinder
1029        category_index = self.widget.cbCategory.findText("Cylinder")
1030        self.widget.cbCategory.setCurrentIndex(category_index)
1031
1032        model_index = self.widget.cbModel.findText("cylinder")
1033        self.widget.cbModel.setCurrentIndex(model_index)
1034
1035        # modify the initial value of length (different from default)
1036        # print self.widget.kernel_module.details['length']
1037
1038        new_value = "333.0"
1039        self.widget._model_model.item(5, 1).setText(new_value)
1040
1041        # name of modified parameter
1042        name_modified_param = str(self.widget._model_model.item(5, 0).text())
1043
1044         # Check the model
1045        self.assertEqual(self.widget._model_model.rowCount(), 7)
1046        self.assertEqual(self.widget._model_model.columnCount(), 5)
1047
1048        # Test the header
1049        #self.assertEqual(self.widget.lstParams.horizontalHeader().count(), 5)
1050        #self.assertFalse(self.widget.lstParams.horizontalHeader().stretchLastSection())
1051
1052        self.assertEqual(len(self.widget._model_model.header_tooltips), 5)
1053        header_tooltips = ['Select parameter for fitting',
1054                             'Enter parameter value',
1055                             'Enter minimum value for parameter',
1056                             'Enter maximum value for parameter',
1057                             'Unit of the parameter']
1058        for column, tooltip in enumerate(header_tooltips):
1059             self.assertEqual(self.widget._model_model.headerData(column,
1060                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
1061                         header_tooltips[column])
1062
1063        # check that the value has been modified in kernel_module
1064        self.assertEqual(new_value,
1065                         str(self.widget.kernel_module.params[name_modified_param]))
1066
1067        # check that range of variation for this parameter has NOT been changed
1068        self.assertNotIn(new_value, self.widget.kernel_module.details[name_modified_param] )
1069
1070    def testModelContextMenu(self):
1071        """
1072        Test the right click context menu in the parameter table
1073        """
1074        # select model: cylinder / cylinder
1075        category_index = self.widget.cbCategory.findText("Cylinder")
1076        self.widget.cbCategory.setCurrentIndex(category_index)
1077
1078        model_index = self.widget.cbModel.findText("cylinder")
1079        self.widget.cbModel.setCurrentIndex(model_index)
1080
1081        # no rows selected
1082        menu = self.widget.modelContextMenu([])
1083        self.assertEqual(len(menu.actions()), 0)
1084
1085        # 1 row selected
1086        menu = self.widget.modelContextMenu([1])
1087        self.assertEqual(len(menu.actions()), 4)
1088
1089        # 2 rows selected
1090        menu = self.widget.modelContextMenu([1,3])
1091        self.assertEqual(len(menu.actions()), 5)
1092
1093        # 3 rows selected
1094        menu = self.widget.modelContextMenu([1,2,3])
1095        self.assertEqual(len(menu.actions()), 4)
1096
1097        # over 9000
1098        with self.assertRaises(AttributeError):
1099            menu = self.widget.modelContextMenu([i for i in range(9001)])
1100        self.assertEqual(len(menu.actions()), 4)
1101
1102    def testShowModelContextMenu(self):
1103        # select model: cylinder / cylinder
1104        category_index = self.widget.cbCategory.findText("Cylinder")
1105        self.widget.cbCategory.setCurrentIndex(category_index)
1106
1107        model_index = self.widget.cbModel.findText("cylinder")
1108        self.widget.cbModel.setCurrentIndex(model_index)
1109
1110        # No selection
1111        logging.error = MagicMock()
1112        self.widget.showModelDescription = MagicMock()
1113        # Show the menu
1114        self.widget.showModelContextMenu(QtCore.QPoint(10,20))
1115
1116        # Assure the description menu is shown
1117        self.assertTrue(self.widget.showModelDescription.called)
1118        self.assertFalse(logging.error.called)
1119
1120        # "select" two rows
1121        index1 = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1122        index2 = self.widget.lstParams.model().index(2, 0, QtCore.QModelIndex())
1123        selection_model = self.widget.lstParams.selectionModel()
1124        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1125        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1126
1127        QtWidgets.QMenu.exec_ = MagicMock()
1128        logging.error = MagicMock()
1129        # Show the menu
1130        self.widget.showModelContextMenu(QtCore.QPoint(10,20))
1131
1132        # Assure the menu is shown
1133        self.assertFalse(logging.error.called)
1134        self.assertTrue(QtWidgets.QMenu.exec_.called)
1135
1136    def testShowMultiConstraint(self):
1137        """
1138        Test the widget update on new multi constraint
1139        """
1140        # select model: cylinder / cylinder
1141        category_index = self.widget.cbCategory.findText("Cylinder")
1142        self.widget.cbCategory.setCurrentIndex(category_index)
1143
1144        model_index = self.widget.cbModel.findText("cylinder")
1145        self.widget.cbModel.setCurrentIndex(model_index)
1146
1147        # nothing selected
1148        with self.assertRaises(AssertionError):
1149            self.widget.showMultiConstraint()
1150
1151        # one row selected
1152        index = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1153        selection_model = self.widget.lstParams.selectionModel()
1154        selection_model.select(index, selection_model.Select | selection_model.Rows)
1155        with self.assertRaises(AssertionError):
1156            # should also throw
1157            self.widget.showMultiConstraint()
1158
1159        # two rows selected
1160        index1 = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1161        index2 = self.widget.lstParams.model().index(3, 0, QtCore.QModelIndex())
1162        selection_model = self.widget.lstParams.selectionModel()
1163        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1164        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1165
1166        # return non-OK from dialog
1167        QtWidgets.QDialog.exec_ = MagicMock()
1168        self.widget.showMultiConstraint()
1169        # Check the dialog called
1170        self.assertTrue(QtWidgets.QDialog.exec_.called)
1171
1172        # return OK from dialog
1173        QtWidgets.QDialog.exec_ = MagicMock(return_value=QtWidgets.QDialog.Accepted)
1174        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1175
1176        self.widget.showMultiConstraint()
1177
1178        # Make sure the signal has been emitted
1179        self.assertEqual(spy.count(), 1)
1180
1181        # Check the argument value - should be row '1'
1182        self.assertEqual(spy.called()[0]['args'][0], [1])
1183
1184    def testGetRowFromName(self):
1185        """
1186        Helper function for parameter table
1187        """
1188        # select model: cylinder / cylinder
1189        category_index = self.widget.cbCategory.findText("Cylinder")
1190        self.widget.cbCategory.setCurrentIndex(category_index)
1191
1192        model_index = self.widget.cbModel.findText("cylinder")
1193        self.widget.cbModel.setCurrentIndex(model_index)
1194
1195        # several random parameters
1196        self.assertEqual(self.widget.getRowFromName('scale'), 0)
1197        self.assertEqual(self.widget.getRowFromName('length'), 6)
1198
1199    def testGetParamNames(self):
1200        """
1201        Helper function for parameter table
1202        """
1203        # select model: cylinder / cylinder
1204        category_index = self.widget.cbCategory.findText("Cylinder")
1205        self.widget.cbCategory.setCurrentIndex(category_index)
1206
1207        model_index = self.widget.cbModel.findText("cylinder")
1208        self.widget.cbModel.setCurrentIndex(model_index)
1209
1210        cylinder_params = ['scale','background','sld','sld_solvent','radius','length']
1211        # assure all parameters are returned
1212        self.assertEqual(self.widget.getParamNames(), cylinder_params)
1213
1214        # Switch to another model
1215        model_index = self.widget.cbModel.findText("pringle")
1216        self.widget.cbModel.setCurrentIndex(model_index)
1217
1218        # make sure the parameters are different than before
1219        self.assertFalse(self.widget.getParamNames() == cylinder_params)
1220
1221    def testAddConstraintToRow(self):
1222        """
1223        Test the constraint row add operation
1224        """
1225        # select model: cylinder / cylinder
1226        category_index = self.widget.cbCategory.findText("Cylinder")
1227        self.widget.cbCategory.setCurrentIndex(category_index)
1228
1229        model_index = self.widget.cbModel.findText("cylinder")
1230        self.widget.cbModel.setCurrentIndex(model_index)
1231
1232        # Create a constraint object
1233        const = Constraint(parent=None, value=7.0)
1234        row = 3
1235
1236        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1237
1238        # call the method tested
1239        self.widget.addConstraintToRow(constraint=const, row=row)
1240
1241        # Make sure the signal has been emitted
1242        self.assertEqual(spy.count(), 1)
1243
1244        # Check the argument value - should be row 'row'
1245        self.assertEqual(spy.called()[0]['args'][0], [row])
1246
1247        # Assure the row has the constraint
1248        self.assertEqual(self.widget.getConstraintForRow(row), const)
1249        self.assertTrue(self.widget.rowHasConstraint(row))
1250
1251        # assign complex constraint now
1252        const = Constraint(parent=None, param='radius', func='5*sld')
1253        row = 5
1254        # call the method tested
1255        self.widget.addConstraintToRow(constraint=const, row=row)
1256
1257        # Make sure the signal has been emitted
1258        self.assertEqual(spy.count(), 2)
1259
1260        # Check the argument value - should be row 'row'
1261        self.assertEqual(spy.called()[1]['args'][0], [row])
1262
1263        # Assure the row has the constraint
1264        self.assertEqual(self.widget.getConstraintForRow(row), const)
1265        # and it is a complex constraint
1266        self.assertTrue(self.widget.rowHasConstraint(row))
1267
1268    def testAddSimpleConstraint(self):
1269        """
1270        Test the constraint add operation
1271        """
1272        # select model: cylinder / cylinder
1273        category_index = self.widget.cbCategory.findText("Cylinder")
1274        self.widget.cbCategory.setCurrentIndex(category_index)
1275
1276        model_index = self.widget.cbModel.findText("cylinder")
1277        self.widget.cbModel.setCurrentIndex(model_index)
1278
1279        # select two rows
1280        row1 = 1
1281        row2 = 4
1282        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1283        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1284        selection_model = self.widget.lstParams.selectionModel()
1285        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1286        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1287
1288        # define the signal spy
1289        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1290
1291        # call the method tested
1292        self.widget.addSimpleConstraint()
1293
1294        # Make sure the signal has been emitted
1295        self.assertEqual(spy.count(), 2)
1296
1297        # Check the argument value
1298        self.assertEqual(spy.called()[0]['args'][0], [row1])
1299        self.assertEqual(spy.called()[1]['args'][0], [row2])
1300
1301    def testDeleteConstraintOnParameter(self):
1302        """
1303        Test the constraint deletion in model/view
1304        """
1305        # select model: cylinder / cylinder
1306        category_index = self.widget.cbCategory.findText("Cylinder")
1307        self.widget.cbCategory.setCurrentIndex(category_index)
1308
1309        model_index = self.widget.cbModel.findText("cylinder")
1310        self.widget.cbModel.setCurrentIndex(model_index)
1311
1312        row1 = 1
1313        row2 = 5
1314
1315        param1 = "background"
1316        param2 = "radius"
1317
1318        #default_value1 = "0.001"
1319        default_value2 = "20"
1320
1321        # select two rows
1322        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1323        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1324        selection_model = self.widget.lstParams.selectionModel()
1325        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1326        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1327
1328        # add constraints
1329        self.widget.addSimpleConstraint()
1330
1331        # deselect the model
1332        selection_model.clear()
1333
1334        # select a single row
1335        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1336
1337        # delete one of the constraints
1338        self.widget.deleteConstraintOnParameter(param=param1)
1339
1340        # see that the other constraint is still present
1341        cons = self.widget.getConstraintForRow(row2)
1342        self.assertEqual(cons.param, param2)
1343        self.assertEqual(cons.value, default_value2)
1344
1345        # kill the other constraint
1346        self.widget.deleteConstraint()
1347
1348        # see that the other constraint is still present
1349        self.assertEqual(self.widget.getConstraintsForModel(), [(param2, None)])
1350
1351    def testGetConstraintForRow(self):
1352        """
1353        Helper function for parameter table
1354        """
1355        # tested extensively elsewhere
1356        pass
1357
1358    def testRowHasConstraint(self):
1359        """
1360        Helper function for parameter table
1361        """
1362        # select model: cylinder / cylinder
1363        category_index = self.widget.cbCategory.findText("Cylinder")
1364        self.widget.cbCategory.setCurrentIndex(category_index)
1365
1366        model_index = self.widget.cbModel.findText("cylinder")
1367        self.widget.cbModel.setCurrentIndex(model_index)
1368
1369        row1 = 1
1370        row2 = 5
1371
1372        # select two rows
1373        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1374        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1375        selection_model = self.widget.lstParams.selectionModel()
1376        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1377        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1378
1379        # add constraints
1380        self.widget.addSimpleConstraint()
1381
1382        con_list = [False, True, False, False, False, True, False]
1383        new_list = []
1384        for row in range(self.widget._model_model.rowCount()):
1385            new_list.append(self.widget.rowHasConstraint(row))
1386
1387        self.assertEqual(new_list, con_list)
1388
1389    def testRowHasActiveConstraint(self):
1390        """
1391        Helper function for parameter table
1392        """
1393        # select model: cylinder / cylinder
1394        category_index = self.widget.cbCategory.findText("Cylinder")
1395        self.widget.cbCategory.setCurrentIndex(category_index)
1396
1397        model_index = self.widget.cbModel.findText("cylinder")
1398        self.widget.cbModel.setCurrentIndex(model_index)
1399
1400        row1 = 1
1401        row2 = 5
1402
1403        # select two rows
1404        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1405        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1406        selection_model = self.widget.lstParams.selectionModel()
1407        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1408        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1409
1410        # add constraints
1411        self.widget.addSimpleConstraint()
1412
1413        # deactivate the first constraint
1414        constraint_objects = self.widget.getConstraintObjectsForModel()
1415        constraint_objects[0].active = False
1416
1417        con_list = [False, False, False, False, False, True, False]
1418        new_list = []
1419        for row in range(self.widget._model_model.rowCount()):
1420            new_list.append(self.widget.rowHasActiveConstraint(row))
1421
1422        self.assertEqual(new_list, con_list)
1423
1424    def testGetConstraintsForModel(self):
1425        """
1426        Test the constraint getter for constraint texts
1427        """
1428        # select model: cylinder / cylinder
1429        category_index = self.widget.cbCategory.findText("Cylinder")
1430        self.widget.cbCategory.setCurrentIndex(category_index)
1431
1432        model_index = self.widget.cbModel.findText("cylinder")
1433        self.widget.cbModel.setCurrentIndex(model_index)
1434
1435        # no constraints
1436        self.assertEqual(self.widget.getConstraintsForModel(),[])
1437
1438        row1 = 1
1439        row2 = 5
1440
1441        param1 = "background"
1442        param2 = "radius"
1443
1444        default_value1 = "0.001"
1445        default_value2 = "20"
1446
1447        # select two rows
1448        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1449        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1450        selection_model = self.widget.lstParams.selectionModel()
1451        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1452        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1453
1454        # add constraints
1455        self.widget.addSimpleConstraint()
1456
1457        # simple constraints
1458        # self.assertEqual(self.widget.getConstraintsForModel(), [('background', '0.001'), ('radius', '20')])
1459        cons = self.widget.getConstraintForRow(row1)
1460        self.assertEqual(cons.param, param1)
1461        self.assertEqual(cons.value, default_value1)
1462        cons = self.widget.getConstraintForRow(row2)
1463        self.assertEqual(cons.param, param2)
1464        self.assertEqual(cons.value, default_value2)
1465
1466        objects = self.widget.getConstraintObjectsForModel()
1467        self.assertEqual(len(objects), 2)
1468        self.assertEqual(objects[1].value, default_value2)
1469        self.assertEqual(objects[0].param, param1)
1470
1471        row = 0
1472        param = "scale"
1473        func = "5*sld"
1474
1475        # add complex constraint
1476        const = Constraint(parent=None, param=param, func=func)
1477        self.widget.addConstraintToRow(constraint=const, row=row)
1478        #self.assertEqual(self.widget.getConstraintsForModel(),[('scale', '5*sld'), ('background', '0.001'), ('radius', None)])
1479        cons = self.widget.getConstraintForRow(row2)
1480        self.assertEqual(cons.param, param2)
1481        self.assertEqual(cons.value, default_value2)
1482
1483        objects = self.widget.getConstraintObjectsForModel()
1484        self.assertEqual(len(objects), 3)
1485        self.assertEqual(objects[0].func, func)
1486
1487    def testReplaceConstraintName(self):
1488        """
1489        Test the replacement of constraint moniker
1490        """
1491        # select model: cylinder / cylinder
1492        category_index = self.widget.cbCategory.findText("Cylinder")
1493        self.widget.cbCategory.setCurrentIndex(category_index)
1494
1495        model_index = self.widget.cbModel.findText("cylinder")
1496        self.widget.cbModel.setCurrentIndex(model_index)
1497
1498        old_name = 'M5'
1499        new_name = 'poopy'
1500        # add complex constraint
1501        const = Constraint(parent=None, param='scale', func='%s.5*sld'%old_name)
1502        row = 0
1503        self.widget.addConstraintToRow(constraint=const, row=row)
1504
1505        # Replace 'm5' with 'poopy'
1506        self.widget.replaceConstraintName(old_name, new_name)
1507
1508        self.assertEqual(self.widget.getConstraintsForModel(),[('scale', 'poopy.5*sld')])
1509
1510
1511if __name__ == "__main__":
1512    unittest.main()
Note: See TracBrowser for help on using the repository browser.