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

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 0109f2a was 3fbd77b, checked in by Torin Cooper-Bennun <torin.cooper-bennun@…>, 6 years ago

fix testModifyShellsInList

  • Property mode set to 100644
File size: 57.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 3 more param rows now (radius_effective is removed), and a new heading
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 (incl. radius_effective & heading row)?
277        self.assertEqual(self.widget._model_model.rowCount(), 5)
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
499        # open a non-existent file
500        filename = os.path.join("UnitTesting", "testdata_noexist.txt")
501        with self.assertRaises(OSError, msg="testdata_noexist.txt should be a non-existent file"):
502            os.stat(filename)
503        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
504        self.widget.show()
505        # Change the category index so we have a model with polydisp
506        category_index = self.widget.cbCategory.findText("Cylinder")
507        self.widget.cbCategory.setCurrentIndex(category_index)
508        model_index = self.widget.cbModel.findText("barbell")
509        self.widget.cbModel.setCurrentIndex(model_index)
510
511        self.widget.onPolyComboIndexChange('array', 0)
512        # check values - unchanged since the file doesn't exist
513        self.assertTrue(self.widget._poly_model.item(0, 1).isEnabled())
514        with self.assertRaises(AttributeError):
515            self.widget.disp_model()
516
517        # good file
518        # TODO: this depends on the working directory being src/sas/qtgui,
519        # TODO: which isn't convenient if you want to run this test suite
520        # TODO: individually
521        filename = os.path.join("UnitTesting", "testdata.txt")
522        try:
523            os.stat(filename)
524        except OSError:
525            self.assertTrue(False, "testdata.txt does not exist")
526        QtWidgets.QFileDialog.getOpenFileName = MagicMock(return_value=(filename,''))
527
528        self.widget.onPolyComboIndexChange('array', 0)
529        # check values - disabled control, present weights
530        self.assertFalse(self.widget._poly_model.item(0, 1).isEnabled())
531        self.assertEqual(self.widget.disp_model.weights[0], 2.83954)
532        self.assertEqual(len(self.widget.disp_model.weights), 19)
533        self.assertEqual(len(self.widget.disp_model.values), 19)
534        self.assertEqual(self.widget.disp_model.values[0], 0.0)
535        self.assertEqual(self.widget.disp_model.values[18], 3.67347)
536
537    def testSetMagneticModel(self):
538        """
539        Test the magnetic model setup
540        """
541        self.widget.show()
542        # Change the category index so we have a model available
543        category_index = self.widget.cbCategory.findText("Sphere")
544        self.widget.cbCategory.setCurrentIndex(category_index)
545
546        # Check the magnetic model
547        self.assertEqual(self.widget._magnet_model.rowCount(), 9)
548        self.assertEqual(self.widget._magnet_model.columnCount(), 5)
549
550        # Test the header
551        self.assertEqual(self.widget.lstMagnetic.horizontalHeader().count(), 5)
552        self.assertFalse(self.widget.lstMagnetic.horizontalHeader().stretchLastSection())
553
554        #Test tooltips
555        self.assertEqual(len(self.widget._magnet_model.header_tooltips), 5)
556
557        header_tooltips = ['Select parameter for fitting',
558                           'Enter parameter value',
559                           'Enter minimum value for parameter',
560                           'Enter maximum value for parameter',
561                           'Unit of the parameter']
562        for column, tooltip in enumerate(header_tooltips):
563             self.assertEqual(self.widget._magnet_model.headerData(column,
564                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
565                         header_tooltips[column])
566
567        # Test rows
568        for row in range(self.widget._magnet_model.rowCount()):
569            func_index = self.widget._magnet_model.index(row, 0)
570            self.assertIn(':', self.widget._magnet_model.item(row, 0).text())
571
572
573    def testAddExtraShells(self):
574        """
575        Test how the extra shells are presented
576        """
577        pass
578
579    def testModifyShellsInList(self):
580        """
581        Test the additional rows added by modifying the shells combobox
582        """
583        self.widget.show()
584        # Change the model to multi shell
585        category_index = self.widget.cbCategory.findText("Sphere")
586        self.widget.cbCategory.setCurrentIndex(category_index)
587        model_index = self.widget.cbModel.findText("core_multi_shell")
588        self.widget.cbModel.setCurrentIndex(model_index)
589
590        # Assure we have the combobox available
591        cbox_row = self.widget._n_shells_row
592        func_index = self.widget._model_model.index(cbox_row, 1)
593        self.assertIsInstance(self.widget.lstParams.indexWidget(func_index), QtWidgets.QComboBox)
594
595        # get number of rows before changing shell count
596        last_row = self.widget._model_model.rowCount()
597
598        # Change the combo box index
599        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(3)
600
601        # Check that the number of rows increased
602        more_rows = self.widget._model_model.rowCount() - last_row
603        self.assertEqual(more_rows, 6) # 6 new rows: 2 params per index
604
605        # Back to 0
606        self.widget.lstParams.indexWidget(func_index).setCurrentIndex(0)
607        self.assertEqual(self.widget._model_model.rowCount(), last_row)
608
609    def testPlotTheory(self):
610        """
611        See that theory item can produce a chart
612        """
613        # By default, the compute/plot button is disabled
614        self.assertFalse(self.widget.cmdPlot.isEnabled())
615        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
616
617        # Assign a model
618        self.widget.show()
619        # Change the category index so we have a model available
620        category_index = self.widget.cbCategory.findText("Sphere")
621        self.widget.cbCategory.setCurrentIndex(category_index)
622
623        # Check the enablement/text
624        self.assertTrue(self.widget.cmdPlot.isEnabled())
625        self.assertEqual(self.widget.cmdPlot.text(), 'Calculate')
626
627        # Spying on plot update signal
628        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
629
630        # Press Calculate
631        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
632
633        # Observe cmdPlot caption change
634        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
635
636        # Make sure the signal has NOT been emitted
637        self.assertEqual(spy.count(), 0)
638
639        # Click again
640        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
641
642        # This time, we got the update signal
643        self.assertEqual(spy.count(), 0)
644
645    def testPlotData(self):
646        """
647        See that data item can produce a chart
648        """
649        # By default, the compute/plot button is disabled
650        self.assertFalse(self.widget.cmdPlot.isEnabled())
651        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
652
653        self.widget.show()
654
655        # Set data
656        test_data = Data1D(x=[1,2], y=[1,2])
657
658        # Force same data into logic
659        self.widget.logic.data = test_data
660        self.widget.data_is_loaded = True
661
662        # Change the category index so we have a model available
663        category_index = self.widget.cbCategory.findText("Sphere")
664        self.widget.cbCategory.setCurrentIndex(category_index)
665
666        # Check the enablement/text
667        self.assertTrue(self.widget.cmdPlot.isEnabled())
668        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
669
670        # Spying on plot update signal
671        spy = QtSignalSpy(self.widget, self.widget.communicate.plotRequestedSignal)
672
673        # Press Calculate
674        QtTest.QTest.mouseClick(self.widget.cmdPlot, QtCore.Qt.LeftButton)
675
676        # Observe cmdPlot caption did not change
677        self.assertEqual(self.widget.cmdPlot.text(), 'Show Plot')
678
679        # Make sure the signal has been emitted == new plot
680        self.assertEqual(spy.count(), 1)
681
682    def testOnEmptyFit(self):
683        """
684        Test a 1D/2D fit with no parameters
685        """
686        # Set data
687        test_data = Data1D(x=[1,2], y=[1,2])
688        item = QtGui.QStandardItem()
689        updateModelItem(item, test_data, "test")
690        # Force same data into logic
691        self.widget.data = item
692        category_index = self.widget.cbCategory.findText("Sphere")
693        self.widget.cbCategory.setCurrentIndex(category_index)
694
695        self.widget.show()
696
697        # Test no fitting params
698        self.widget.main_params_to_fit = []
699
700        logging.error = MagicMock()
701
702        self.widget.onFit()
703        self.assertTrue(logging.error.called_with('no fitting parameters'))
704        self.widget.close()
705
706        test_data = Data2D(image=[1.0, 2.0, 3.0],
707                           err_image=[0.01, 0.02, 0.03],
708                           qx_data=[0.1, 0.2, 0.3],
709                           qy_data=[0.1, 0.2, 0.3],
710                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
711                           mask=[True, True, True])
712
713        # Force same data into logic
714        item = QtGui.QStandardItem()
715        updateModelItem(item, test_data, "test")
716        # Force same data into logic
717        self.widget.data = item
718        category_index = self.widget.cbCategory.findText("Sphere")
719        self.widget.cbCategory.setCurrentIndex(category_index)
720
721        self.widget.show()
722
723        # Test no fitting params
724        self.widget.main_params_to_fit = []
725
726        logging.error = MagicMock()
727
728        self.widget.onFit()
729        self.assertTrue(logging.error.called_once())
730        self.assertTrue(logging.error.called_with('no fitting parameters'))
731        self.widget.close()
732
733
734    def testOnFit1D(self):
735        """
736        Test the threaded fitting call
737        """
738        # Set data
739        test_data = Data1D(x=[1,2], y=[1,2])
740        item = QtGui.QStandardItem()
741        updateModelItem(item, test_data, "test")
742        # Force same data into logic
743        self.widget.data = item
744        category_index = self.widget.cbCategory.findText("Sphere")
745        self.widget.cbCategory.setCurrentIndex(category_index)
746
747        self.widget.show()
748
749        # Assing fitting params
750        self.widget.main_params_to_fit = ['scale']
751
752        # Spying on status update signal
753        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
754
755        with threads.deferToThread as MagicMock:
756            self.widget.onFit()
757            # thread called
758            self.assertTrue(threads.deferToThread.called)
759            # thread method is 'compute'
760            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
761
762            # the fit button changed caption and got disabled
763            # could fail if machine fast enough to finish
764            #self.assertEqual(self.widget.cmdFit.text(), 'Stop fit')
765            #self.assertFalse(self.widget.cmdFit.isEnabled())
766
767            # Signal pushed up
768            self.assertEqual(update_spy.count(), 1)
769
770        self.widget.close()
771
772    def testOnFit2D(self):
773        """
774        Test the threaded fitting call
775        """
776        # Set data
777        test_data = Data2D(image=[1.0, 2.0, 3.0],
778                           err_image=[0.01, 0.02, 0.03],
779                           qx_data=[0.1, 0.2, 0.3],
780                           qy_data=[0.1, 0.2, 0.3],
781                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
782                           mask=[True, True, True])
783
784        # Force same data into logic
785        item = QtGui.QStandardItem()
786        updateModelItem(item, test_data, "test")
787        # Force same data into logic
788        self.widget.data = item
789        category_index = self.widget.cbCategory.findText("Sphere")
790        self.widget.cbCategory.setCurrentIndex(category_index)
791
792        self.widget.show()
793
794        # Assing fitting params
795        self.widget.main_params_to_fit = ['scale']
796
797        # Spying on status update signal
798        update_spy = QtSignalSpy(self.widget, self.widget.communicate.statusBarUpdateSignal)
799
800        with threads.deferToThread as MagicMock:
801            self.widget.onFit()
802            # thread called
803            self.assertTrue(threads.deferToThread.called)
804            # thread method is 'compute'
805            self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'compute')
806
807            # the fit button changed caption and got disabled
808            #self.assertEqual(self.widget.cmdFit.text(), 'Stop fit')
809            #self.assertFalse(self.widget.cmdFit.isEnabled())
810
811            # Signal pushed up
812            self.assertEqual(update_spy.count(), 1)
813
814    def testOnHelp(self):
815        """
816        Test various help pages shown in this widget
817        """
818        #Mock the webbrowser.open method
819        self.widget.parent.showHelp = MagicMock()
820        #webbrowser.open = MagicMock()
821
822        # Invoke the action on default tab
823        self.widget.onHelp()
824        # Check if show() got called
825        self.assertTrue(self.widget.parent.showHelp.called)
826        # Assure the filename is correct
827        self.assertIn("fitting_help.html", self.widget.parent.showHelp.call_args[0][0])
828
829        # Change the tab to options
830        self.widget.tabFitting.setCurrentIndex(1)
831        self.widget.onHelp()
832        # Check if show() got called
833        self.assertEqual(self.widget.parent.showHelp.call_count, 2)
834        # Assure the filename is correct
835        self.assertIn("residuals_help.html", self.widget.parent.showHelp.call_args[0][0])
836
837        # Change the tab to smearing
838        self.widget.tabFitting.setCurrentIndex(2)
839        self.widget.onHelp()
840        # Check if show() got called
841        self.assertEqual(self.widget.parent.showHelp.call_count, 3)
842        # Assure the filename is correct
843        self.assertIn("resolution.html", self.widget.parent.showHelp.call_args[0][0])
844
845        # Change the tab to poly
846        self.widget.tabFitting.setCurrentIndex(3)
847        self.widget.onHelp()
848        # Check if show() got called
849        self.assertEqual(self.widget.parent.showHelp.call_count, 4)
850        # Assure the filename is correct
851        self.assertIn("polydispersity.html", self.widget.parent.showHelp.call_args[0][0])
852
853        # Change the tab to magnetism
854        self.widget.tabFitting.setCurrentIndex(4)
855        self.widget.onHelp()
856        # Check if show() got called
857        self.assertEqual(self.widget.parent.showHelp.call_count, 5)
858        # Assure the filename is correct
859        self.assertIn("magnetism.html", self.widget.parent.showHelp.call_args[0][0])
860
861    def testReadFitPage(self):
862        """
863        Read in the fitpage object and restore state
864        """
865        # Set data
866        test_data = Data1D(x=[1,2], y=[1,2])
867
868        # Force same data into logic
869        self.widget.logic.data = test_data
870        self.widget.data_is_loaded = True
871        category_index = self.widget.cbCategory.findText('Sphere')
872        self.widget.cbCategory.setCurrentIndex(category_index)
873        self.widget.main_params_to_fit = ['scale']
874        # Invoke the tested method
875        fp = self.widget.currentState()
876
877        # Prepare modified fit page
878        fp.current_model = 'onion'
879        fp.is_polydisperse = True
880
881        # Read in modified state
882        self.widget.readFitPage(fp)
883
884        # Check if the widget got updated accordingly
885        self.assertEqual(self.widget.cbModel.currentText(), 'onion')
886        self.assertTrue(self.widget.chkPolydispersity.isChecked())
887        #Check if polidispersity tab is available
888        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
889
890        #Check if magnetism box and tab are disabled when 1D data is loaded
891        self.assertFalse(self.widget.chkMagnetism.isEnabled())
892        self.assertFalse(self.widget.tabFitting.isTabEnabled(4))
893
894    def testReadFitPage2D(self):
895        """
896        Read in the fitpage object and restore state
897        """
898        # Set data
899
900        test_data = Data2D(image=[1.0, 2.0, 3.0],
901                           err_image=[0.01, 0.02, 0.03],
902                           qx_data=[0.1, 0.2, 0.3],
903                           qy_data=[0.1, 0.2, 0.3],
904                           xmin=0.1, xmax=0.3, ymin=0.1, ymax=0.3,
905                           mask=[True, True, True])
906
907        # Force same data into logic
908        self.widget.logic.data = test_data
909        self.widget.data_is_loaded = True
910
911        #item = QtGui.QStandardItem()
912        #updateModelItem(item, [test_data], "test")
913        # Force same data into logic
914        #self.widget.logic.data = item
915        #self.widget.data_is_loaded = True
916
917        category_index = self.widget.cbCategory.findText("Cylinder")
918        self.widget.cbCategory.setCurrentIndex(category_index)
919
920        # Test no fitting params
921        self.widget.main_params_to_fit = ['scale']
922
923        # Invoke the tested method
924        fp = self.widget.currentState()
925
926        # Prepare modified fit page
927        fp.current_model = 'cylinder'
928        fp.is_polydisperse = True
929        fp.is_magnetic = True
930        fp.is2D = True
931
932        # Read in modified state
933        self.widget.readFitPage(fp)
934
935        # Check if the widget got updated accordingly
936        self.assertEqual(self.widget.cbModel.currentText(), 'cylinder')
937        self.assertTrue(self.widget.chkPolydispersity.isChecked())
938        self.assertTrue(self.widget.chkPolydispersity.isEnabled())
939        #Check if polidispersity tab is available
940        self.assertTrue(self.widget.tabFitting.isTabEnabled(3))
941
942        #Check if magnetism box and tab are disabled when 1D data is loaded
943        self.assertTrue(self.widget.chkMagnetism.isChecked())
944        self.assertTrue(self.widget.chkMagnetism.isEnabled())
945        self.assertTrue(self.widget.tabFitting.isTabEnabled(4))
946
947    def testCurrentState(self):
948        """
949        Set up the fitpage with current state
950        """
951        # Set data
952        test_data = Data1D(x=[1,2], y=[1,2])
953
954        # Force same data into logic
955        self.widget.logic.data = test_data
956        self.widget.data_is_loaded = True
957        category_index = self.widget.cbCategory.findText("Sphere")
958        self.widget.cbCategory.setCurrentIndex(category_index)
959        self.widget.main_params_to_fit = ['scale']
960
961        # Invoke the tested method
962        fp = self.widget.currentState()
963
964        # Test some entries. (Full testing of fp is done in FitPageTest)
965        self.assertIsInstance(fp.data, Data1D)
966        self.assertListEqual(list(fp.data.x), [1,2])
967        self.assertTrue(fp.data_is_loaded)
968        self.assertEqual(fp.current_category, "Sphere")
969        self.assertEqual(fp.current_model, "adsorbed_layer")
970        self.assertListEqual(fp.main_params_to_fit, ['scale'])
971
972    def testPushFitPage(self):
973        """
974        Push current state of fitpage onto stack
975        """
976        # Set data
977        test_data = Data1D(x=[1,2], y=[1,2])
978
979        # Force same data into logic
980        self.widget.logic.data = test_data
981        self.widget.data_is_loaded = True
982        category_index = self.widget.cbCategory.findText("Sphere")
983
984        # Asses the initial state of stack
985        self.assertEqual(self.widget.page_stack, [])
986
987        # Set the undo flag
988        self.widget.undo_supported = True
989        self.widget.cbCategory.setCurrentIndex(category_index)
990        self.widget.main_params_to_fit = ['scale']
991
992        # Check that the stack is updated
993        self.assertEqual(len(self.widget.page_stack), 1)
994
995        # Change another parameter
996        self.widget._model_model.item(2, 1).setText("3.0")
997        # Check that the stack is updated
998        self.assertEqual(len(self.widget.page_stack), 2)
999
1000    def testPopFitPage(self):
1001        """
1002        Pop current state of fitpage from stack
1003        """
1004        # TODO: to be added when implementing UNDO/REDO
1005        pass
1006
1007    def testOnMainPageChange(self):
1008        """
1009        Test update  values of modified parameters in models
1010        """
1011        # select model: cylinder / cylinder
1012        category_index = self.widget.cbCategory.findText("Cylinder")
1013        self.widget.cbCategory.setCurrentIndex(category_index)
1014
1015        model_index = self.widget.cbModel.findText("cylinder")
1016        self.widget.cbModel.setCurrentIndex(model_index)
1017
1018        # modify the initial value of length (different from default)
1019        # print self.widget.kernel_module.details['length']
1020
1021        new_value = "333.0"
1022        self.widget._model_model.item(5, 1).setText(new_value)
1023
1024        # name of modified parameter
1025        name_modified_param = str(self.widget._model_model.item(5, 0).text())
1026
1027         # Check the model
1028        self.assertEqual(self.widget._model_model.rowCount(), 7)
1029        self.assertEqual(self.widget._model_model.columnCount(), 5)
1030
1031        # Test the header
1032        #self.assertEqual(self.widget.lstParams.horizontalHeader().count(), 5)
1033        #self.assertFalse(self.widget.lstParams.horizontalHeader().stretchLastSection())
1034
1035        self.assertEqual(len(self.widget._model_model.header_tooltips), 5)
1036        header_tooltips = ['Select parameter for fitting',
1037                             'Enter parameter value',
1038                             'Enter minimum value for parameter',
1039                             'Enter maximum value for parameter',
1040                             'Unit of the parameter']
1041        for column, tooltip in enumerate(header_tooltips):
1042             self.assertEqual(self.widget._model_model.headerData(column,
1043                QtCore.Qt.Horizontal, QtCore.Qt.ToolTipRole),
1044                         header_tooltips[column])
1045
1046        # check that the value has been modified in kernel_module
1047        self.assertEqual(new_value,
1048                         str(self.widget.kernel_module.params[name_modified_param]))
1049
1050        # check that range of variation for this parameter has NOT been changed
1051        self.assertNotIn(new_value, self.widget.kernel_module.details[name_modified_param] )
1052
1053    def testModelContextMenu(self):
1054        """
1055        Test the right click context menu in the parameter table
1056        """
1057        # select model: cylinder / cylinder
1058        category_index = self.widget.cbCategory.findText("Cylinder")
1059        self.widget.cbCategory.setCurrentIndex(category_index)
1060
1061        model_index = self.widget.cbModel.findText("cylinder")
1062        self.widget.cbModel.setCurrentIndex(model_index)
1063
1064        # no rows selected
1065        menu = self.widget.modelContextMenu([])
1066        self.assertEqual(len(menu.actions()), 0)
1067
1068        # 1 row selected
1069        menu = self.widget.modelContextMenu([1])
1070        self.assertEqual(len(menu.actions()), 4)
1071
1072        # 2 rows selected
1073        menu = self.widget.modelContextMenu([1,3])
1074        self.assertEqual(len(menu.actions()), 5)
1075
1076        # 3 rows selected
1077        menu = self.widget.modelContextMenu([1,2,3])
1078        self.assertEqual(len(menu.actions()), 4)
1079
1080        # over 9000
1081        with self.assertRaises(AttributeError):
1082            menu = self.widget.modelContextMenu([i for i in range(9001)])
1083        self.assertEqual(len(menu.actions()), 4)
1084
1085    def testShowModelContextMenu(self):
1086        # select model: cylinder / cylinder
1087        category_index = self.widget.cbCategory.findText("Cylinder")
1088        self.widget.cbCategory.setCurrentIndex(category_index)
1089
1090        model_index = self.widget.cbModel.findText("cylinder")
1091        self.widget.cbModel.setCurrentIndex(model_index)
1092
1093        # No selection
1094        logging.error = MagicMock()
1095        self.widget.showModelDescription = MagicMock()
1096        # Show the menu
1097        self.widget.showModelContextMenu(QtCore.QPoint(10,20))
1098
1099        # Assure the description menu is shown
1100        self.assertTrue(self.widget.showModelDescription.called)
1101        self.assertFalse(logging.error.called)
1102
1103        # "select" two rows
1104        index1 = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1105        index2 = self.widget.lstParams.model().index(2, 0, QtCore.QModelIndex())
1106        selection_model = self.widget.lstParams.selectionModel()
1107        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1108        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1109
1110        QtWidgets.QMenu.exec_ = MagicMock()
1111        logging.error = MagicMock()
1112        # Show the menu
1113        self.widget.showModelContextMenu(QtCore.QPoint(10,20))
1114
1115        # Assure the menu is shown
1116        self.assertFalse(logging.error.called)
1117        self.assertTrue(QtWidgets.QMenu.exec_.called)
1118
1119    def testShowMultiConstraint(self):
1120        """
1121        Test the widget update on new multi constraint
1122        """
1123        # select model: cylinder / cylinder
1124        category_index = self.widget.cbCategory.findText("Cylinder")
1125        self.widget.cbCategory.setCurrentIndex(category_index)
1126
1127        model_index = self.widget.cbModel.findText("cylinder")
1128        self.widget.cbModel.setCurrentIndex(model_index)
1129
1130        # nothing selected
1131        with self.assertRaises(AssertionError):
1132            self.widget.showMultiConstraint()
1133
1134        # one row selected
1135        index = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1136        selection_model = self.widget.lstParams.selectionModel()
1137        selection_model.select(index, selection_model.Select | selection_model.Rows)
1138        with self.assertRaises(AssertionError):
1139            # should also throw
1140            self.widget.showMultiConstraint()
1141
1142        # two rows selected
1143        index1 = self.widget.lstParams.model().index(1, 0, QtCore.QModelIndex())
1144        index2 = self.widget.lstParams.model().index(3, 0, QtCore.QModelIndex())
1145        selection_model = self.widget.lstParams.selectionModel()
1146        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1147        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1148
1149        # return non-OK from dialog
1150        QtWidgets.QDialog.exec_ = MagicMock()
1151        self.widget.showMultiConstraint()
1152        # Check the dialog called
1153        self.assertTrue(QtWidgets.QDialog.exec_.called)
1154
1155        # return OK from dialog
1156        QtWidgets.QDialog.exec_ = MagicMock(return_value=QtWidgets.QDialog.Accepted)
1157        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1158
1159        self.widget.showMultiConstraint()
1160
1161        # Make sure the signal has been emitted
1162        self.assertEqual(spy.count(), 1)
1163
1164        # Check the argument value - should be row '1'
1165        self.assertEqual(spy.called()[0]['args'][0], [1])
1166
1167    def testGetRowFromName(self):
1168        """
1169        Helper function for parameter table
1170        """
1171        # select model: cylinder / cylinder
1172        category_index = self.widget.cbCategory.findText("Cylinder")
1173        self.widget.cbCategory.setCurrentIndex(category_index)
1174
1175        model_index = self.widget.cbModel.findText("cylinder")
1176        self.widget.cbModel.setCurrentIndex(model_index)
1177
1178        # several random parameters
1179        self.assertEqual(self.widget.getRowFromName('scale'), 0)
1180        self.assertEqual(self.widget.getRowFromName('length'), 6)
1181
1182    def testGetParamNames(self):
1183        """
1184        Helper function for parameter table
1185        """
1186        # select model: cylinder / cylinder
1187        category_index = self.widget.cbCategory.findText("Cylinder")
1188        self.widget.cbCategory.setCurrentIndex(category_index)
1189
1190        model_index = self.widget.cbModel.findText("cylinder")
1191        self.widget.cbModel.setCurrentIndex(model_index)
1192
1193        cylinder_params = ['scale','background','sld','sld_solvent','radius','length']
1194        # assure all parameters are returned
1195        self.assertEqual(self.widget.getParamNames(), cylinder_params)
1196
1197        # Switch to another model
1198        model_index = self.widget.cbModel.findText("pringle")
1199        self.widget.cbModel.setCurrentIndex(model_index)
1200
1201        # make sure the parameters are different than before
1202        self.assertFalse(self.widget.getParamNames() == cylinder_params)
1203
1204    def testAddConstraintToRow(self):
1205        """
1206        Test the constraint row add operation
1207        """
1208        # select model: cylinder / cylinder
1209        category_index = self.widget.cbCategory.findText("Cylinder")
1210        self.widget.cbCategory.setCurrentIndex(category_index)
1211
1212        model_index = self.widget.cbModel.findText("cylinder")
1213        self.widget.cbModel.setCurrentIndex(model_index)
1214
1215        # Create a constraint object
1216        const = Constraint(parent=None, value=7.0)
1217        row = 3
1218
1219        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1220
1221        # call the method tested
1222        self.widget.addConstraintToRow(constraint=const, row=row)
1223
1224        # Make sure the signal has been emitted
1225        self.assertEqual(spy.count(), 1)
1226
1227        # Check the argument value - should be row 'row'
1228        self.assertEqual(spy.called()[0]['args'][0], [row])
1229
1230        # Assure the row has the constraint
1231        self.assertEqual(self.widget.getConstraintForRow(row), const)
1232        self.assertTrue(self.widget.rowHasConstraint(row))
1233
1234        # assign complex constraint now
1235        const = Constraint(parent=None, param='radius', func='5*sld')
1236        row = 5
1237        # call the method tested
1238        self.widget.addConstraintToRow(constraint=const, row=row)
1239
1240        # Make sure the signal has been emitted
1241        self.assertEqual(spy.count(), 2)
1242
1243        # Check the argument value - should be row 'row'
1244        self.assertEqual(spy.called()[1]['args'][0], [row])
1245
1246        # Assure the row has the constraint
1247        self.assertEqual(self.widget.getConstraintForRow(row), const)
1248        # and it is a complex constraint
1249        self.assertTrue(self.widget.rowHasConstraint(row))
1250
1251    def testAddSimpleConstraint(self):
1252        """
1253        Test the constraint add operation
1254        """
1255        # select model: cylinder / cylinder
1256        category_index = self.widget.cbCategory.findText("Cylinder")
1257        self.widget.cbCategory.setCurrentIndex(category_index)
1258
1259        model_index = self.widget.cbModel.findText("cylinder")
1260        self.widget.cbModel.setCurrentIndex(model_index)
1261
1262        # select two rows
1263        row1 = 1
1264        row2 = 4
1265        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1266        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1267        selection_model = self.widget.lstParams.selectionModel()
1268        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1269        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1270
1271        # define the signal spy
1272        spy = QtSignalSpy(self.widget, self.widget.constraintAddedSignal)
1273
1274        # call the method tested
1275        self.widget.addSimpleConstraint()
1276
1277        # Make sure the signal has been emitted
1278        self.assertEqual(spy.count(), 2)
1279
1280        # Check the argument value
1281        self.assertEqual(spy.called()[0]['args'][0], [row1])
1282        self.assertEqual(spy.called()[1]['args'][0], [row2])
1283
1284    def testDeleteConstraintOnParameter(self):
1285        """
1286        Test the constraint deletion in model/view
1287        """
1288        # select model: cylinder / cylinder
1289        category_index = self.widget.cbCategory.findText("Cylinder")
1290        self.widget.cbCategory.setCurrentIndex(category_index)
1291
1292        model_index = self.widget.cbModel.findText("cylinder")
1293        self.widget.cbModel.setCurrentIndex(model_index)
1294
1295        row1 = 1
1296        row2 = 5
1297
1298        param1 = "background"
1299        param2 = "radius"
1300
1301        #default_value1 = "0.001"
1302        default_value2 = "20"
1303
1304        # select two rows
1305        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1306        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1307        selection_model = self.widget.lstParams.selectionModel()
1308        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1309        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1310
1311        # add constraints
1312        self.widget.addSimpleConstraint()
1313
1314        # deselect the model
1315        selection_model.clear()
1316
1317        # select a single row
1318        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1319
1320        # delete one of the constraints
1321        self.widget.deleteConstraintOnParameter(param=param1)
1322
1323        # see that the other constraint is still present
1324        cons = self.widget.getConstraintForRow(row2)
1325        self.assertEqual(cons.param, param2)
1326        self.assertEqual(cons.value, default_value2)
1327
1328        # kill the other constraint
1329        self.widget.deleteConstraint()
1330
1331        # see that the other constraint is still present
1332        self.assertEqual(self.widget.getConstraintsForModel(), [(param2, None)])
1333
1334    def testGetConstraintForRow(self):
1335        """
1336        Helper function for parameter table
1337        """
1338        # tested extensively elsewhere
1339        pass
1340
1341    def testRowHasConstraint(self):
1342        """
1343        Helper function for parameter table
1344        """
1345        # select model: cylinder / cylinder
1346        category_index = self.widget.cbCategory.findText("Cylinder")
1347        self.widget.cbCategory.setCurrentIndex(category_index)
1348
1349        model_index = self.widget.cbModel.findText("cylinder")
1350        self.widget.cbModel.setCurrentIndex(model_index)
1351
1352        row1 = 1
1353        row2 = 5
1354
1355        # select two rows
1356        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1357        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1358        selection_model = self.widget.lstParams.selectionModel()
1359        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1360        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1361
1362        # add constraints
1363        self.widget.addSimpleConstraint()
1364
1365        con_list = [False, True, False, False, False, True, False]
1366        new_list = []
1367        for row in range(self.widget._model_model.rowCount()):
1368            new_list.append(self.widget.rowHasConstraint(row))
1369
1370        self.assertEqual(new_list, con_list)
1371
1372    def testRowHasActiveConstraint(self):
1373        """
1374        Helper function for parameter table
1375        """
1376        # select model: cylinder / cylinder
1377        category_index = self.widget.cbCategory.findText("Cylinder")
1378        self.widget.cbCategory.setCurrentIndex(category_index)
1379
1380        model_index = self.widget.cbModel.findText("cylinder")
1381        self.widget.cbModel.setCurrentIndex(model_index)
1382
1383        row1 = 1
1384        row2 = 5
1385
1386        # select two rows
1387        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1388        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1389        selection_model = self.widget.lstParams.selectionModel()
1390        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1391        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1392
1393        # add constraints
1394        self.widget.addSimpleConstraint()
1395
1396        # deactivate the first constraint
1397        constraint_objects = self.widget.getConstraintObjectsForModel()
1398        constraint_objects[0].active = False
1399
1400        con_list = [False, False, False, False, False, True, False]
1401        new_list = []
1402        for row in range(self.widget._model_model.rowCount()):
1403            new_list.append(self.widget.rowHasActiveConstraint(row))
1404
1405        self.assertEqual(new_list, con_list)
1406
1407    def testGetConstraintsForModel(self):
1408        """
1409        Test the constraint getter for constraint texts
1410        """
1411        # select model: cylinder / cylinder
1412        category_index = self.widget.cbCategory.findText("Cylinder")
1413        self.widget.cbCategory.setCurrentIndex(category_index)
1414
1415        model_index = self.widget.cbModel.findText("cylinder")
1416        self.widget.cbModel.setCurrentIndex(model_index)
1417
1418        # no constraints
1419        self.assertEqual(self.widget.getConstraintsForModel(),[])
1420
1421        row1 = 1
1422        row2 = 5
1423
1424        param1 = "background"
1425        param2 = "radius"
1426
1427        default_value1 = "0.001"
1428        default_value2 = "20"
1429
1430        # select two rows
1431        index1 = self.widget.lstParams.model().index(row1, 0, QtCore.QModelIndex())
1432        index2 = self.widget.lstParams.model().index(row2, 0, QtCore.QModelIndex())
1433        selection_model = self.widget.lstParams.selectionModel()
1434        selection_model.select(index1, selection_model.Select | selection_model.Rows)
1435        selection_model.select(index2, selection_model.Select | selection_model.Rows)
1436
1437        # add constraints
1438        self.widget.addSimpleConstraint()
1439
1440        # simple constraints
1441        # self.assertEqual(self.widget.getConstraintsForModel(), [('background', '0.001'), ('radius', '20')])
1442        cons = self.widget.getConstraintForRow(row1)
1443        self.assertEqual(cons.param, param1)
1444        self.assertEqual(cons.value, default_value1)
1445        cons = self.widget.getConstraintForRow(row2)
1446        self.assertEqual(cons.param, param2)
1447        self.assertEqual(cons.value, default_value2)
1448
1449        objects = self.widget.getConstraintObjectsForModel()
1450        self.assertEqual(len(objects), 2)
1451        self.assertEqual(objects[1].value, default_value2)
1452        self.assertEqual(objects[0].param, param1)
1453
1454        row = 0
1455        param = "scale"
1456        func = "5*sld"
1457
1458        # add complex constraint
1459        const = Constraint(parent=None, param=param, func=func)
1460        self.widget.addConstraintToRow(constraint=const, row=row)
1461        #self.assertEqual(self.widget.getConstraintsForModel(),[('scale', '5*sld'), ('background', '0.001'), ('radius', None)])
1462        cons = self.widget.getConstraintForRow(row2)
1463        self.assertEqual(cons.param, param2)
1464        self.assertEqual(cons.value, default_value2)
1465
1466        objects = self.widget.getConstraintObjectsForModel()
1467        self.assertEqual(len(objects), 3)
1468        self.assertEqual(objects[0].func, func)
1469
1470    def testReplaceConstraintName(self):
1471        """
1472        Test the replacement of constraint moniker
1473        """
1474        # select model: cylinder / cylinder
1475        category_index = self.widget.cbCategory.findText("Cylinder")
1476        self.widget.cbCategory.setCurrentIndex(category_index)
1477
1478        model_index = self.widget.cbModel.findText("cylinder")
1479        self.widget.cbModel.setCurrentIndex(model_index)
1480
1481        old_name = 'M5'
1482        new_name = 'poopy'
1483        # add complex constraint
1484        const = Constraint(parent=None, param='scale', func='%s.5*sld'%old_name)
1485        row = 0
1486        self.widget.addConstraintToRow(constraint=const, row=row)
1487
1488        # Replace 'm5' with 'poopy'
1489        self.widget.replaceConstraintName(old_name, new_name)
1490
1491        self.assertEqual(self.widget.getConstraintsForModel(),[('scale', 'poopy.5*sld')])
1492
1493
1494if __name__ == "__main__":
1495    unittest.main()
Note: See TracBrowser for help on using the repository browser.