source: sasview/src/sas/qtgui/Perspectives/Invariant/UnitTesting/InvariantPerspectiveTest.py @ 50bfab0

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalc
Last change on this file since 50bfab0 was 7c487846, checked in by Piotr Rozyczko <rozyczko@…>, 7 years ago

Fixes to the Invariant perspective

  • Property mode set to 100755
File size: 18.7 KB
RevLine 
[f1f3e6a]1import sys
2import time
3import numpy
4import logging
5import unittest
6from PyQt5 import QtGui, QtWidgets
7from PyQt5 import QtCore
8from PyQt5.QtTest import QTest
9from PyQt5.QtCore import Qt
[7c487846]10from unittest.mock import MagicMock
11from unittest.mock import patch
12from unittest.mock import create_autospec
[f1f3e6a]13
14from twisted.internet import threads
15
16from sas.qtgui.Perspectives.Invariant.InvariantPerspective import InvariantWindow
17from sas.qtgui.Perspectives.Invariant.InvariantDetails import DetailsDialog
18from sas.qtgui.Perspectives.Invariant.InvariantUtils import WIDGETS
19from sas.qtgui.Plotting.PlotterData import Data1D
20from sas.qtgui.MainWindow.GuiManager import GuiManager
21from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow
22
23import sas.qtgui.Utilities.GuiUtils as GuiUtils
24
[7c487846]25#if not QtWidgets.QApplication.instance():
26app = QtWidgets.QApplication(sys.argv)
[f1f3e6a]27
28BG_COLOR_ERR = 'background-color: rgb(244, 170, 164);'
29
30class InvariantPerspectiveTest(unittest.TestCase):
31    """Test the Invariant Perspective Window"""
32    def setUp(self):
33        """Create the Invariant Perspective Window"""
34
35        class MainWindow(object):
36            def __init__(self):
37                self.model = QtGui.QStandardItemModel()
38
39        class dummy_manager(object):
40            def __init__(self):
41                self.filesWidget = MainWindow()
42
43            def communicator(self):
44                return GuiUtils.Communicate()
45
46            def communicate(self):
47                return GuiUtils.Communicate()
48
49        self.widget = InvariantWindow(dummy_manager())
50
51    def tearDown(self):
52        """Destroy the DataOperationUtility"""
53        self.widget.close()
54        self.widget = None
55
56    def testDefaults(self):
57        """Test the GUI in its default state"""
58
59        self.assertIsInstance(self.widget, QtWidgets.QDialog)
60
61        self.assertEqual(self.widget.windowTitle(), "Invariant Perspective")
62
63        # name for displaying in the DataExplorer combo box
64        self.assertEqual(self.widget.name, "Invariant")
65
66        self.assertIsInstance(self.widget.model, QtGui.QStandardItemModel)
67
68        self.assertIsNone(self.widget._data)
69        self.assertEqual(self.widget._path, '')
70        self.assertFalse(self.widget._allow_close)
71
72        # disabled pushbuttons
73        self.assertFalse(self.widget.cmdStatus.isEnabled())
74        self.assertFalse(self.widget.cmdCalculate.isEnabled())
75
76        # disabled, read only line edits
77        self.assertFalse(self.widget.txtName.isEnabled())
78        self.assertTrue(self.widget.txtVolFract.isReadOnly())
79        self.assertTrue(self.widget.txtVolFractErr.isReadOnly())
80
81        self.assertTrue(self.widget.txtSpecSurf.isReadOnly())
82        self.assertTrue(self.widget.txtSpecSurfErr.isReadOnly())
83
84        self.assertTrue(self.widget.txtInvariantTot.isReadOnly())
85        self.assertTrue(self.widget.txtInvariantTotErr.isReadOnly())
86
87        self.assertFalse(self.widget.txtBackgd.isReadOnly())
88        self.assertFalse(self.widget.txtScale.isReadOnly())
89        self.assertFalse(self.widget.txtContrast.isReadOnly())
90        self.assertFalse(self.widget.txtPorodCst.isReadOnly())
91
92        self.assertFalse(self.widget.txtExtrapolQMin.isEnabled())
93        self.assertFalse(self.widget.txtExtrapolQMax.isEnabled())
94
95        self.assertFalse(self.widget.txtNptsLowQ.isReadOnly())
96        self.assertFalse(self.widget.txtNptsHighQ.isReadOnly())
97
98        # content of line edits
99        self.assertEqual(self.widget.txtName.text(), '')
100        self.assertEqual(self.widget.txtTotalQMin.text(), '0.0')
101        self.assertEqual(self.widget.txtTotalQMax.text(), '0.0')
102        self.assertEqual(self.widget.txtBackgd.text(), '0.0')
103        self.assertEqual(self.widget.txtScale.text(), '1.0')
104        self.assertEqual(self.widget.txtContrast.text(), '1.0')
105        self.assertEqual(self.widget.txtExtrapolQMin.text(), '1e-05')
106        self.assertEqual(self.widget.txtExtrapolQMax.text(), '10')
107        self.assertEqual(self.widget.txtPowerLowQ.text(), '4')
108        self.assertEqual(self.widget.txtPowerHighQ.text(), '4')
109
110        # unchecked checkboxes
111        self.assertFalse(self.widget.chkLowQ.isChecked())
112        self.assertFalse(self.widget.chkHighQ.isChecked())
113
114        # number of tabs
115        self.assertEqual(self.widget.tabWidget.count(), 2)
116        # default tab
117        self.assertEqual(self.widget.tabWidget.currentIndex(), 0)
118        # tab's title
119        self.assertEqual(self.widget.tabWidget.tabText(0), 'Invariant')
120        self.assertEqual(self.widget.tabWidget.tabText(1), 'Options')
121
122        # Tooltips
123        self.assertEqual(self.widget.cmdStatus.toolTip(), "Get more details of computation such as fraction from extrapolation" )
124
125        self.assertEqual(self.widget.txtInvariantTot.toolTip(), "Total invariant [Q*], including extrapolated regions.")
126
127        self.assertEqual(self.widget.txtExtrapolQMin.toolTip(), "The minimum extrapolated q value.")
128
129        self.assertEqual(self.widget.txtPowerHighQ.toolTip(),"Exponent to apply to the Power_law function.")
130
131        self.assertEqual(self.widget.txtNptsHighQ.toolTip(), "Number of Q points to consider\n while extrapolating the high-Q region")
132
133        self.assertEqual(self.widget.chkHighQ.toolTip(), "Check to extrapolate data at high-Q")
134
135        self.assertEqual(self.widget.txtNptsLowQ.toolTip(), "Number of Q points to consider\nwhile extrapolating the low-Q region")
136
137        self.assertEqual(self.widget.chkLowQ.toolTip(), "Check to extrapolate data at low-Q")
138
139        self.assertEqual(self.widget.cmdCalculate.toolTip(), "Compute invariant")
140
141        self.assertEqual(self.widget.txtPowerLowQ.toolTip(), "Exponent to apply to the Power_law function." )
142
143        # Validators
[7c487846]144        self.assertIsInstance(self.widget.txtNptsLowQ.validator(), QtGui.QIntValidator)
145        self.assertIsInstance(self.widget.txtNptsHighQ.validator(), QtGui.QIntValidator)
146        self.assertIsInstance(self.widget.txtPowerLowQ.validator(), GuiUtils.DoubleValidator)
147        self.assertIsInstance(self.widget.txtPowerHighQ.validator(), GuiUtils.DoubleValidator)
[f1f3e6a]148
[7c487846]149        self.assertIsInstance(self.widget.txtBackgd.validator(), GuiUtils.DoubleValidator)
150        self.assertIsInstance(self.widget.txtContrast.validator(), GuiUtils.DoubleValidator)
151        self.assertIsInstance(self.widget.txtScale.validator(), GuiUtils.DoubleValidator)
152        self.assertIsInstance(self.widget.txtPorodCst.validator(), GuiUtils.DoubleValidator)
[f1f3e6a]153
154        # Test autoexclusivity of radiobuttons
155        # Low Q
156        self.assertFalse(self.widget.rbGuinier.autoExclusive())
157        self.assertFalse(self.widget.rbPowerLawLowQ.autoExclusive())
158        self.assertTrue(self.widget.rbFixLowQ.autoExclusive())
159        self.assertTrue(self.widget.rbFitLowQ.autoExclusive())
160        # High Q
161        self.assertTrue(self.widget.rbFixHighQ.autoExclusive())
162        self.assertTrue(self.widget.rbFitHighQ.autoExclusive())
163
164    def testOnCalculate(self):
165        """ Test onCompute function """
166        self.widget.calculateInvariant = MagicMock()
167        self.widget.cmdCalculate.setEnabled(True)
168        QTest.mouseClick(self.widget.cmdCalculate, Qt.LeftButton)
169        self.assertTrue(self.widget.calculateInvariant.called_once())
170
171    def testCalculateInvariant(self):
172        """ """
173        threads.deferToThread = MagicMock()
174        self.widget.calculateInvariant()
175        self.assertTrue(threads.deferToThread.called)
176        self.assertEqual(threads.deferToThread.call_args_list[0][0][0].__name__, 'calculateThread')
177
178        self.assertEqual(self.widget.cmdCalculate.text(), 'Calculating...')
179        self.assertFalse(self.widget.cmdCalculate.isEnabled())
180
181    # TODO
182    def testPlotResult(self):
183        """ """
184        pass
185        # create fake input
186        # data = Data1D(x=[1, 2], y=[3, 4])
187        # GuiUtils.dataFromItem = MagicMock(return_value=data)
188        # # self.widget._manager.filesWidget.model = MagicMock()
189        # item = QtGui.QStandardItem("test")
190
191        # run function
192        # self.widget.plotResult = MagicMock(return_value=None) # (item)
193        # self.widget.plotResult(item)
194        # self.assertTrue(self.widget.plotResult.called_once())
195
196
197        # self.assertTrue(self.widget.cmdCalculate.isEnabled())
198        # self.assertEqual(self.widget.cmdCalculate.text(), 'Calculate')
199        # self.assertEqual(self.widget._data.x[0], 1)
200        # self.assertEqual(self.widget._data.x[1], 2)
201        # self.assertEqual(self.widget._data.y[0], 3)
202        # self.assertEqual(self.widget._data.y[1], 4)
203
[7c487846]204    def notestHelp(self):
[f1f3e6a]205        """ Assure help file is shown """
206        # this should not rise
207        self.widget.onHelp()
208
209    def testAllowBatch(self):
210        """ """
211        self.assertFalse(self.widget.allowBatch())
212
213    def testTitle(self):
214        """
215        Test Perspective name
216        """
217        self.assertEqual(self.widget.title(), "Invariant panel")
218
219    def testOnStatus(self):
220        """
221        Test Display of Invariant Details
222        """
223        # enable click on Calculate button
224        self.widget.cmdStatus.setEnabled(True)
225
226        invariant_details_dialog = create_autospec(DetailsDialog)
227
228        self.widget.detailsDialog = invariant_details_dialog
229
230        # click on button
231        QTest.mouseClick(self.widget.cmdStatus, Qt.LeftButton)
232
233        invariant_details_dialog.showDialog.assert_called_once_with()
234
235    def testUpdateFromModel(self):
236        """
237        update the globals based on the data in the model
238        """
239        self.widget.updateFromModel()
240        self.assertEqual(self.widget._background,
241                         float(self.widget.model.item(WIDGETS.W_BACKGROUND).text()))
242        self.assertEqual(self.widget._contrast,
243                         float(self.widget.model.item(WIDGETS.W_CONTRAST).text()))
244        self.assertEqual(self.widget._scale, float(self.widget.model.item(WIDGETS.W_SCALE).text()))
245        self.assertEqual(self.widget._low_extrapolate,
246                         str(self.widget.model.item(WIDGETS.W_ENABLE_LOWQ).text()) == 'true')
247        self.assertEqual(self.widget._low_points,
248                         float(self.widget.model.item(WIDGETS.W_NPTS_LOWQ).text()))
249
250        self.assertEqual(self.widget._low_guinier, str(self.widget.model.item(WIDGETS.W_LOWQ_GUINIER).text()) == 'true' )
251
252        self.assertEqual(self.widget._low_fit,str(self.widget.model.item(WIDGETS.W_LOWQ_FIT).text()) == 'true')
253        self.assertEqual(self.widget._low_power_value, float(self.widget.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text()))
254
255        self.assertEqual(self.widget._high_extrapolate,str(self.widget.model.item(WIDGETS.W_ENABLE_HIGHQ).text()) == 'true')
256        self.assertEqual(self.widget._high_points,
257                         float(self.widget.model.item(WIDGETS.W_NPTS_HIGHQ).text()))
258        self.assertEqual(self.widget._high_fit,str(self.widget.model.item(WIDGETS.W_HIGHQ_FIT).text()) == 'true')
259
260        self.assertEqual(self.widget._high_power_value, float(self.widget.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text()))
261
262    def testEnabling(self):
263        """ """
264
265        self.widget.cmdStatus.setEnabled(False)
266
267        self.widget.enabling()
268
269        self.assertTrue(self.widget.cmdStatus.isEnabled())
270
271    def testCheckLength(self):
272        """
273        Test validator for number of points for extrapolation
274         Error if it is larger than the distribution length
275        """
276        logging.warning = MagicMock()
277        self.widget.txtNptsLowQ.setEnabled(True)
278
279        self.widget._data = Data1D(x=[1, 2], y=[1, 2])
280        self.widget.txtNptsLowQ.setText('9')
281
282        # QTest.keyClicks(self.widget.txtNptsLowQ, '9')
283        # QTest.keyClick(self.widget.txtNptsLowQ, QtCore.Qt.Key_Return)
284
285        BG_COLOR_ERR = 'background-color: rgb(244, 170, 164);'
286        # print 'style ',self.widget.txtNptsLowQ.styleSheet()
287        self.assertIn(BG_COLOR_ERR, self.widget.txtNptsLowQ.styleSheet())
288        self.assertTrue(logging.warning.called_once_with())
289        self.assertFalse(self.widget.cmdCalculate.isEnabled())
290
291    def testModelChanged(self):
292        """ """
293        self.widget.lowQToggle = MagicMock()
294        status_ini = self.widget.model.item(WIDGETS.W_ENABLE_LOWQ).text()
295        if status_ini == 'true':
296            status_fin = 'false'
297        else:
298            status_fin = 'true'
299
300        self.widget.model.setItem(WIDGETS.W_ENABLE_LOWQ, QtGui.QStandardItem(status_fin))
301
302        if status_fin:
303            self.assertTrue(self.widget._low_extrapolate)
304        else:
305            self.assertFalse(self.widget._low_extrapolate)
306
307        self.assertTrue(self.widget.lowQToggle.called_once_with())
308
309    def testUpdateFromGui(self):
310        """ """
311        self.widget.txtBackgd.setText('0.22')
312        self.assertEqual(str(self.widget.model.item(WIDGETS.W_BACKGROUND).text()), '0.22')
313
314    def testLowGuinierAndPowerToggle(self):
315        """ """
316        # enable all tested radiobuttons
317        self.widget.rbGuinier.setEnabled(True)
318        self.widget.rbPowerLawLowQ.setEnabled(True)
319        self.widget.txtNptsLowQ.setEnabled(True)
320
321        # record initial status
322        status_ini = self.widget.rbGuinier.isChecked()
323
324        # mouse click to run function
325        QTest.mouseClick(self.widget.rbGuinier, Qt.LeftButton)
326
327        # check that status changed
328        self.assertNotEqual(self.widget.rbGuinier.isChecked(), status_ini)
329
330        status_fin = self.widget.rbGuinier.isChecked()
331
332        self.assertEqual(self.widget.rbPowerLawLowQ.isChecked(), not status_fin)
333
334        self.assertEqual(self.widget.txtPowerLowQ.isEnabled(),
335                         all([not status_fin, not self.widget._low_fit]))
336
337    def testLowFitAndFixToggle(self):
338        """ """
339        status = True
340        # run function to test
341        self.widget.lowFitAndFixToggle(status)
342        self.assertEqual(self.widget._low_fit, status)
343        self.assertNotEqual(self.widget.txtPowerLowQ.isEnabled(), status)
344
345    def testHiFitAndFixToggle(self):
346        status = True
347        self.widget.hiFitAndFixToggle(status)
348        self.assertEqual(self.widget.txtPowerHighQ.isEnabled(), not status)
349
350    def testHighQToggle(self):
351        """ Test enabling / disabling for check box High Q extrapolation """
352        status_chkHighQ = self.widget.chkHighQ.isChecked()
353        self.widget.highQToggle(status_chkHighQ)
354
355        self.assertEqual(self.widget.rbFitHighQ.isEnabled(), status_chkHighQ)
356        self.assertEqual(self.widget.rbFixHighQ.isEnabled(), status_chkHighQ)
357        self.assertEqual(self.widget.txtNptsHighQ.isEnabled(), status_chkHighQ)
358        self.assertEqual(self.widget.txtPowerHighQ.isEnabled(), status_chkHighQ)
359
360        # change checked status of chkHighQ
361        self.widget.chkHighQ.setChecked(True)
362        status_chkHighQ = self.widget.chkHighQ.isChecked()
363        self.assertEqual(self.widget.rbFitHighQ.isEnabled(), status_chkHighQ)
364        self.assertEqual(self.widget.rbFixHighQ.isEnabled(), status_chkHighQ)
365        self.assertEqual(self.widget.txtNptsHighQ.isEnabled(), status_chkHighQ)
366        self.assertEqual(self.widget.txtPowerHighQ.isEnabled(),
367                         status_chkHighQ)
368
369    def testLowQToggle(self):
370        """ Test enabling / disabling for check box Low Q extrapolation """
371        status_chkLowQ = self.widget.chkLowQ.isChecked()
372
373        self.assertEqual(self.widget.rbGuinier.isEnabled(), status_chkLowQ)
374        self.assertEqual(self.widget.rbPowerLawLowQ.isEnabled(), status_chkLowQ)
375        self.assertEqual(self.widget.txtNptsLowQ.isEnabled(), status_chkLowQ)
376
377        self.assertEqual(self.widget.rbFitLowQ.isVisible(), self.widget.rbPowerLawLowQ.isChecked())
378        self.assertEqual(self.widget.rbFixLowQ.isVisible(), self.widget.rbPowerLawLowQ.isChecked())
379        self.assertEqual(self.widget.rbFitLowQ.isEnabled(), status_chkLowQ)
380        self.assertEqual(self.widget.rbFixLowQ.isEnabled(), status_chkLowQ)
381
382        self.assertEqual(self.widget.txtNptsLowQ.isEnabled(),
383                         all([status_chkLowQ, not self.widget._low_guinier, not self.widget._low_fit]))
384
385    def testSetupModel(self):
386        """ Test default settings of model"""
387        self.assertEqual(self.widget.model.item(WIDGETS.W_FILENAME).text(),
388                         self.widget._path)
389
390        self.assertEqual(self.widget.model.item(WIDGETS.W_QMIN).text(), '0.0')
391
392        self.assertEqual(self.widget.model.item(WIDGETS.W_QMAX).text(), '0.0')
393
394        self.assertEqual(self.widget.model.item(WIDGETS.W_BACKGROUND).text(),
395                         str(self.widget._background))
396
397        self.assertEqual(self.widget.model.item(WIDGETS.W_CONTRAST).text(),
398                         str(self.widget._contrast))
399
400        self.assertEqual(self.widget.model.item(WIDGETS.W_SCALE).text(),
401                         str(self.widget._scale))
402
403        self.assertIn(str(self.widget.model.item(WIDGETS.W_POROD_CST).text()),
404                      ['', str(self.widget._porod)])
405
406        self.assertEqual(
407            str(self.widget.model.item(WIDGETS.W_ENABLE_HIGHQ).text()).lower(),
408            'false')
409
410        self.assertEqual(
411            str(self.widget.model.item(WIDGETS.W_ENABLE_LOWQ).text()).lower(),
412            'false')
413
414        self.assertEqual(
415            str(self.widget.model.item(WIDGETS.W_NPTS_LOWQ).text()),
416            str(10))
417
418        self.assertEqual(self.widget.model.item(WIDGETS.W_NPTS_HIGHQ).text(),
419                         str(10))
420
421        self.assertEqual(
422            str(self.widget.model.item(WIDGETS.W_LOWQ_GUINIER).text()).lower(),
423            'true')
424
425        self.assertEqual(
426            str(self.widget.model.item(WIDGETS.W_LOWQ_FIT).text()).lower(),
427            'true')
428
429        self.assertEqual(
430            str(self.widget.model.item(WIDGETS.W_LOWQ_POWER_VALUE).text()), '4')
431
432        self.assertEqual(
433            str(self.widget.model.item(WIDGETS.W_HIGHQ_FIT).text()).lower(),
434            'true')
435
436        self.assertEqual(
437            str(self.widget.model.item(WIDGETS.W_HIGHQ_POWER_VALUE).text()),
438            '4')
439
440    # TODO
441    def testSetupMapper(self):
442        """ """
443        self.assertIsInstance(self.widget.mapper, QtWidgets.QDataWidgetMapper)
444
445        self.assertEqual(self.widget.mapper.orientation(), 2)
446
447        self.assertEqual(self.widget.mapper.model(), self.widget.model)
448
449    def testSetData(self):
450        """ """
451        self.widget.updateGuiFromFile = MagicMock()
452
453        data = Data1D(x=[1, 2], y=[1, 2])
454        GuiUtils.dataFromItem = MagicMock(return_value=data)
455        item = QtGui.QStandardItem("test")
456        self.widget.setData([item])
457
458        self.assertTrue(self.widget.updateGuiFromFile.called_once())
459
460    def TestCheckQExtrapolatedData(self):
461        """
462        Test Match status of low or high-Q extrapolated data checkbox in
463        DataExplorer with low or high-Q extrapolation checkbox in invariant
464        panel
465        """
466        # Low-Q check box ticked
467        self.widget.chkLowQ.setCheckStatus(QtCore.Qt.Checked)
468        GuiUtils.updateModelItemStatus = MagicMock()
469
470        self.assertTrue(GuiUtils.updateModelItemStatus.called_once())
471
472
473if __name__ == "__main__":
474    unittest.main()
Note: See TracBrowser for help on using the repository browser.