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

ESS_GUIESS_GUI_opencl
Last change on this file since c02721c4 was c02721c4, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 6 years ago

Unit test update after latest round of commits.

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