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

ESS_GUIESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since f53bc04 was 6dbff18, checked in by Piotr Rozyczko <rozyczko@…>, 6 years ago

Restructure parameters to fit into three separate lists to enable proper processing. SASVIEW-1008

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