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

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 f50d170 was f50d170, checked in by Torin Cooper-Bennun <torin.cooper-bennun@…>, 11 months ago

fix testCurrentState (adsorbed_layer is the first Sphere-category model in sasmodels, alphabetically)

  • 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
423        # Set the checbox
424        self.widget._poly_model.item(0,0).setCheckState(2)
425        # Assure the parameter is added
426        self.assertEqual(self.widget.parameters_to_fit, ['radius_bell.width'])
427
428        # Add another parameter
429        self.widget._poly_model.item(2,0).setCheckState(2)
430        # Assure the parameters are added
431        self.assertEqual(self.widget.parameters_to_fit, ['radius_bell.width', 'length.width'])
432
433        # Change the min/max values
434        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 0.0)
435        self.widget._poly_model.item(0,2).setText("1.0")
436        self.assertEqual(self.widget.kernel_module.details['radius_bell'][1], 1.0)
437
438        # Change the number of points
439        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
440        self.widget._poly_model.item(0,4).setText("22")
441        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 22)
442        # try something stupid
443        self.widget._poly_model.item(0,4).setText("butt")
444        # see that this didn't annoy the control at all
445        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 22)
446
447        # Change the number of sigmas
448        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 3)
449        self.widget._poly_model.item(0,5).setText("222")
450        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 222)
451        # try something stupid again
452        self.widget._poly_model.item(0,4).setText("beer")
453        # no efect
454        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 222)
455
456    def testOnPolyComboIndexChange(self):
457        """
458        Test the slot method for polydisp. combo box index change
459        """
460        self.widget.show()
461        # Change the category index so we have a model with polydisp
462        category_index = self.widget.cbCategory.findText("Cylinder")
463        self.widget.cbCategory.setCurrentIndex(category_index)
464        model_index = self.widget.cbModel.findText("barbell")
465        self.widget.cbModel.setCurrentIndex(model_index)
466
467        # call method with default settings
468        self.widget.onPolyComboIndexChange('gaussian', 0)
469        # check values
470        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
471        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 3)
472        # Change the index
473        self.widget.onPolyComboIndexChange('rectangle', 0)
474        # check values
475        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 35)
476        self.assertAlmostEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 1.73205, 5)
477        # Change the index
478        self.widget.onPolyComboIndexChange('lognormal', 0)
479        # check values
480        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 80)
481        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 8)
482        # Change the index
483        self.widget.onPolyComboIndexChange('schulz', 0)
484        # check values
485        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.npts'), 80)
486        self.assertEqual(self.widget.kernel_module.getParam('radius_bell.nsigmas'), 8)
487
488        # mock up file load
489        self.widget.loadPolydispArray = MagicMock()
490        # Change to 'array'
491        self.widget.onPolyComboIndexChange('array', 0)
492        # See the mock fire
493        self.assertTrue(self.widget.loadPolydispArray.called)
494
495    def testLoadPolydispArray(self):
496        """
497        Test opening of the load file dialog for 'array' polydisp. function
498        """
499        filename = os.path.join("UnitTesting", "testdata_noexist.txt")
500        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
501        self.widget.show()
502        # Change the category index so we have a model with polydisp
503        category_index = self.widget.cbCategory.findText("Cylinder")
504        self.widget.cbCategory.setCurrentIndex(category_index)
505        model_index = self.widget.cbModel.findText("barbell")
506        self.widget.cbModel.setCurrentIndex(model_index)
507
508        self.widget.onPolyComboIndexChange('array', 0)
509        # check values - unchanged since the file doesn't exist
510        self.assertTrue(self.widget._poly_model.item(0, 1).isEnabled())
511        with self.assertRaises(AttributeError):
512            self.widget.disp_model()
513
514        # good file
515        filename = os.path.join("UnitTesting", "testdata.txt")
516        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
517
518        self.widget.onPolyComboIndexChange('array', 0)
519        # check values - disabled control, present weights
520        self.assertFalse(self.widget._poly_model.item(0, 1).isEnabled())
521        self.assertEqual(self.widget.disp_model.weights[0], 2.83954)
522        self.assertEqual(len(self.widget.disp_model.weights), 19)
523        self.assertEqual(len(self.widget.disp_model.values), 19)
524        self.assertEqual(self.widget.disp_model.values[0], 0.0)
525        self.assertEqual(self.widget.disp_model.values[18], 3.67347)
526
527    def testSetMagneticModel(self):
528        """
529        Test the magnetic model setup
530        """
531        self.widget.show()
532        # Change the category index so we have a model available
533        category_index = self.widget.cbCategory.findText("Sphere")
534        self.widget.cbCategory.setCurrentIndex(category_index)
535
536        # Check the magnetic model
537        self.assertEqual(self.widget._magnet_model.rowCount(), 12)
538        self.assertEqual(self.widget._magnet_model.columnCount(), 5)
539
540        # Test the header
541        self.assertEqual(self.widget.lstMagnetic.horizontalHeader().count(), 5)
542        self.assertFalse(self.widget.lstMagnetic.horizontalHeader().stretchLastSection())
543
544        #Test tooltips
545        self.assertEqual(len(self.widget._magnet_model.header_tooltips), 5)
546
547        header_tooltips = ['Select parameter for fitting',
548                           'Enter parameter value',
549                           'Enter minimum value for parameter',
550                           'Enter maximum value for parameter',
551                           'Unit of the parameter']
552        for column, tooltip in enumerate(header_tooltips):
553             self.assertEqual(self.widget._magnet_model.headerData(column,
554                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
555                         header_tooltips[column])
556
557        # Test rows
558        for row in range(self.widget._magnet_model.rowCount()):
559            func_index = self.widget._magnet_model.index(row, 0)
560            self.assertIn(':', self.widget._magnet_model.item(row, 0).text())
561
562
563    def testAddExtraShells(self):
564        """
565        Test how the extra shells are presented
566        """
567        pass
568
569    def testModifyShellsInList(self):
570        """
571        Test the additional rows added by modifying the shells combobox
572        """
573        self.widget.show()
574        # Change the model to multi shell
575        category_index = self.widget.cbCategory.findText("Sphere")
576        self.widget.cbCategory.setCurrentIndex(category_index)
577        model_index = self.widget.cbModel.findText("core_multi_shell")
578        self.widget.cbModel.setCurrentIndex(model_index)
579
580        # Assure we have the combobox available
581        last_row = self.widget._last_model_row
582        func_index = self.widget._model_model.index(last_row-1, 1)
583        self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtWidgets.QComboBox)
584
585        # Change the combo box index
586        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(3)
587
588        # Check that the number of rows increased
589        more_rows = self.widget._model_model.rowCount() - last_row
590        self.assertEqual(more_rows, 6) # 6 new rows: 2 params per index
591
592        # Back to 0
593        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0)
594        self.assertEqual(self.widget._model_model.rowCount(), last_row)
595
596    def testPlotTheory(self):
597        """
598        See that theory item can produce a chart
599        """
600        # By default, the compute/plot button is disabled
601        self.assertFalse(self.widget.cmdPlot.isEnabled())
602        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
603
604        # Assign a model
605        self.widget.show()
606        # Change the category index so we have a model available
607        category_index = self.widget.cbCategory.findText("Sphere")
608        self.widget.cbCategory.setCurrentIndex(category_index)
609
610        # Check the enablement/text
611        self.assertTrue(self.widget.cmdPlot.isEnabled())
612        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate')
613
614        # Spying on plot update signal
615        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
616
617        # Press Calculate
618        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
619
620        # Observe cmdPlot caption change
621        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
622
623        # Make sure the signal has NOT been emitted
624        self.assertEqual(spy.count(), 0)
625
626        # Click again
627        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
628
629        # This time, we got the update signal
630        self.assertEqual(spy.count(), 0)
631
632    def testPlotData(self):
633        """
634        See that data item can produce a chart
635        """
636        # By default, the compute/plot button is disabled
637        self.assertFalse(self.widget.cmdPlot.isEnabled())
638        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
639
640        self.widget.show()
641
642        # Set data
643        test_data = Data1D(x=[1,2], y=[1,2])
644
645        # Force same data into logic
646        self.widget.logic.data = test_data
647        self.widget.data_is_loaded = True
648
649        # Change the category index so we have a model available
650        category_index = self.widget.cbCategory.findText("Sphere")
651        self.widget.cbCategory.setCurrentIndex(category_index)
652
653        # Check the enablement/text
654        self.assertTrue(self.widget.cmdPlot.isEnabled())
655        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
656
657        # Spying on plot update signal
658        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
659
660        # Press Calculate
661        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
662
663        # Observe cmdPlot caption did not change
664        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
665
666        # Make sure the signal has been emitted == new plot
667        self.assertEqual(spy.count(), 1)
668
669    def testOnEmptyFit(self):
670        """
671        Test a 1D/2D fit with no parameters
672        """
673        # Set data
674        test_data = Data1D(x=[1,2], y=[1,2])
675        item = QtGui.QStandardItem()
676        updateModelItem(item, test_data, "test")
677        # Force same data into logic
678        self.widget.data = item
679        category_index = self.widget.cbCategory.findText("Sphere")
680        self.widget.cbCategory.setCurrentIndex(category_index)
681
682        self.widget.show()
683
684        # Test no fitting params
685        self.widget.parameters_to_fit = []
686
687        logging.error = MagicMock()
688
689        self.widget.onFit()
690        self.assertTrue(logging.error.called_with('no fitting parameters'))
691        self.widget.close()
692
693        test_data = Data2D(image=[1.0, 2.0, 3.0],
694                           err_image=[0.01, 0.02, 0.03],
695                           qx_data=[0.1, 0.2, 0.3],
696                           qy_data=[0.1, 0.2, 0.3],
697                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
698                           mask=[True, True, True])
699
700        # Force same data into logic
701        item = QtGui.QStandardItem()
702        updateModelItem(item, test_data, "test")
703        # Force same data into logic
704        self.widget.data = item
705        category_index = self.widget.cbCategory.findText("Sphere")
706        self.widget.cbCategory.setCurrentIndex(category_index)
707
708        self.widget.show()
709
710        # Test no fitting params
711        self.widget.parameters_to_fit = []
712
713        logging.error = MagicMock()
714
715        self.widget.onFit()
716        self.assertTrue(logging.error.called_once())
717        self.assertTrue(logging.error.called_with('no fitting parameters'))
718        self.widget.close()
719
720
721    def testOnFit1D(self):
722        """
723        Test the threaded fitting call
724        """
725        # Set data
726        test_data = Data1D(x=[1,2], y=[1,2])
727        item = QtGui.QStandardItem()
728        updateModelItem(item, test_data, "test")
729        # Force same data into logic
730        self.widget.data = item
731        category_index = self.widget.cbCategory.findText("Sphere")
732        self.widget.cbCategory.setCurrentIndex(category_index)
733
734        self.widget.show()
735
736        # Assing fitting params
737        self.widget.parameters_to_fit = ['scale']
738
739        # Spying on status update signal
740        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
741
742        with threads.deferToThread as MagicMock:
743            self.widget.onFit()
744            # thread called
745            self.assertTrue(threads.deferToThread.called)
746            # thread method is 'compute'
747            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
748
749            # the fit button changed caption and got disabled
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.parameters_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.parameters_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.parameters_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.parameters_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.parameters_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.parameters_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.