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

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 a3c94b54 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
Line 
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
10from unittest.mock import MagicMock
11from unittest.mock import patch
12from unittest.mock import create_autospec
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
25#if not QtWidgets.QApplication.instance():
26app = QtWidgets.QApplication(sys.argv)
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
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)
148
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)
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
204    def notestHelp(self):
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.