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

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

More unit test fixes.

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