source: sasview/src/sas/qtgui/UnitTesting/DataExplorerTest.py @ e540cd2

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 e540cd2 was e540cd2, checked in by Piotr Rozyczko <piotr.rozyczko@…>, 8 years ago

Status bar, progress bar, initial treeview context menu + minor cleanup

  • Property mode set to 100755
File size: 19.0 KB
Line 
1import sys
2import unittest
3
4from PyQt4.QtGui import *
5from PyQt4.QtTest import QTest
6from PyQt4.QtCore import *
7from mock import MagicMock
8
9# Local
10from sas.sasgui.guiframe.dataFitting import Data1D
11from sas.sascalc.dataloader.loader import Loader
12from sas.sasgui.guiframe.data_manager import DataManager
13
14from DataExplorer import DataExplorerWindow
15from GuiManager import GuiManager
16from GuiUtils import *
17from UnitTesting.TestUtils import QtSignalSpy
18from Plotter import Plotter
19
20app = QApplication(sys.argv)
21
22class DataExplorerTest(unittest.TestCase):
23    '''Test the Data Explorer GUI'''
24    def setUp(self):
25        '''Create the GUI'''
26        class MyPerspective(object):
27            def communicator(self):
28                return Communicate()
29            def allowBatch(self):
30                return False
31            def setData(self, data_item=None):
32                return None
33            def title(self):
34                return "Dummy Perspective"
35
36        class dummy_manager(object):
37            def communicator(self):
38                return Communicate()
39            def perspective(self):
40                return MyPerspective()
41
42        self.form = DataExplorerWindow(None, dummy_manager())
43
44    def tearDown(self):
45        '''Destroy the GUI'''
46        self.form.close()
47        self.form = None
48
49    def testDefaults(self):
50        '''Test the GUI in its default state'''
51        # Tab widget
52        self.assertIsInstance(self.form, QTabWidget)
53        self.assertEqual(self.form.count(), 2)
54
55        # Buttons - data tab
56        self.assertEqual(self.form.cmdLoad.text(), "Load")
57        self.assertEqual(self.form.cmdDeleteData.text(), "Delete")
58        self.assertEqual(self.form.cmdDeleteTheory.text(), "Delete")
59        self.assertEqual(self.form.cmdFreeze.text(), "Freeze Theory")
60        self.assertEqual(self.form.cmdSendTo.text(), "...")
61        self.assertEqual(self.form.cmdSendTo.iconSize(), QSize(32, 32))
62        self.assertIsInstance(self.form.cmdSendTo.icon(), QIcon)
63        self.assertEqual(self.form.chkBatch.text(), "Batch mode")
64        self.assertFalse(self.form.chkBatch.isChecked())
65
66        # Buttons - theory tab
67
68        # Combo boxes
69        self.assertEqual(self.form.cbSelect.count(), 6)
70        self.assertEqual(self.form.cbSelect.currentIndex(), 0)
71
72        # Models - data
73        self.assertIsInstance(self.form.model, QStandardItemModel)
74        self.assertEqual(self.form.treeView.model().rowCount(), 0)
75        self.assertEqual(self.form.treeView.model().columnCount(), 0)
76        self.assertEqual(self.form.model.rowCount(), 0)
77        self.assertEqual(self.form.model.columnCount(), 0)
78        self.assertIsInstance(self.form.data_proxy, QSortFilterProxyModel)
79        self.assertEqual(self.form.data_proxy.sourceModel(), self.form.model)
80        self.assertEqual("[^()]", str(self.form.data_proxy.filterRegExp().pattern()))
81        self.assertIsInstance(self.form.treeView, QTreeView)
82
83        # Models - theory
84        self.assertIsInstance(self.form.theory_model, QStandardItemModel)
85        self.assertEqual(self.form.freezeView.model().rowCount(), 0)
86        self.assertEqual(self.form.freezeView.model().columnCount(), 0)
87        self.assertEqual(self.form.theory_model.rowCount(), 0)
88        self.assertEqual(self.form.theory_model.columnCount(), 0)
89        self.assertIsInstance(self.form.theory_proxy, QSortFilterProxyModel)
90        self.assertEqual(self.form.theory_proxy.sourceModel(), self.form.theory_model)
91        self.assertEqual("[^()]", str(self.form.theory_proxy.filterRegExp().pattern()))
92        self.assertIsInstance(self.form.freezeView, QTreeView)
93
94    def testWidgets(self):
95        """
96        Test if all required widgets got added
97        """       
98    def testLoadButton(self):
99        loadButton = self.form.cmdLoad
100
101        filename = "cyl_400_20.txt"
102        # Initialize signal spy instances
103        spy_file_read = QtSignalSpy(self.form, self.form.communicator.fileReadSignal)
104
105        # Return no files.
106        QtGui.QFileDialog.getOpenFileNames = MagicMock(return_value=None)
107
108        # Click on the Load button
109        QTest.mouseClick(loadButton, Qt.LeftButton)
110
111        # Test the getOpenFileName() dialog called once
112        self.assertTrue(QtGui.QFileDialog.getOpenFileNames.called)
113        QtGui.QFileDialog.getOpenFileNames.assert_called_once()
114
115        # Make sure the signal has not been emitted
116        self.assertEqual(spy_file_read.count(), 0)
117
118        # Now, return a single file
119        QtGui.QFileDialog.getOpenFileNames = MagicMock(return_value=filename)
120
121        # Click on the Load button
122        QTest.mouseClick(loadButton, Qt.LeftButton)
123        QtGui.qApp.processEvents()
124
125        # Test the getOpenFileName() dialog called once
126        self.assertTrue(QtGui.QFileDialog.getOpenFileNames.called)
127        QtGui.QFileDialog.getOpenFileNames.assert_called_once()
128
129        # Expected one spy instance
130        #self.assertEqual(spy_file_read.count(), 1)
131        #self.assertIn(filename, str(spy_file_read.called()[0]['args'][0]))
132
133    def testLoadFiles(self):
134        """
135        Test progress bar update while loading of multiple files
136        """
137        # Set up the spy on progress bar update signal
138        spy_progress_bar_update = QtSignalSpy(self.form,
139            self.form.communicator.progressBarUpdateSignal)
140
141        # Populate the model
142        filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"]
143        self.form.readData(filename)
144
145        # 0, 0, 33, 66, -1 -> 5 signals reaching progressBar
146        self.assertEqual(spy_progress_bar_update.count(), 5)
147
148        expected_list = [0, 0, 33, 66, -1]
149        spied_list = [spy_progress_bar_update.called()[i]['args'][0] for i in xrange(5)]
150        self.assertEqual(expected_list, spied_list)
151       
152    def testDeleteButton(self):
153        """
154        Functionality of the delete button
155        """
156        deleteButton = self.form.cmdDeleteData
157
158        # Mock the confirmation dialog with return=No
159        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No)
160
161        # Populate the model
162        filename = ["cyl_400_20.txt", "Dec07031.ASC", "cyl_400_20.txt"]
163        self.form.readData(filename)
164
165        # Assure the model contains three items
166        self.assertEqual(self.form.model.rowCount(), 3)
167
168        # Assure the checkboxes are on
169        item1 = self.form.model.item(0)
170        item2 = self.form.model.item(1)
171        item3 = self.form.model.item(2)
172        self.assertTrue(item1.checkState() == QtCore.Qt.Checked)
173        self.assertTrue(item2.checkState() == QtCore.Qt.Checked)
174        self.assertTrue(item3.checkState() == QtCore.Qt.Checked)
175
176        # Click on the delete  button
177        QTest.mouseClick(deleteButton, Qt.LeftButton)
178
179        # Test the warning dialog called once
180        self.assertTrue(QtGui.QMessageBox.question.called)
181
182        # Assure the model still contains the items
183        self.assertEqual(self.form.model.rowCount(), 3)
184
185        # Now, mock the confirmation dialog with return=Yes
186        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
187
188        # Click on the delete  button
189        QTest.mouseClick(deleteButton, Qt.LeftButton)
190
191        # Test the warning dialog called once
192        self.assertTrue(QtGui.QMessageBox.question.called)
193
194        # Assure the model contains no items
195        self.assertEqual(self.form.model.rowCount(), 0)
196
197        # Click delete once again to assure no nasty behaviour on empty model
198        QTest.mouseClick(deleteButton, Qt.LeftButton)
199
200    def testDeleteTheory(self):
201        """
202        Test that clicking "Delete" in theories tab removes selected indices
203        """
204        deleteButton = self.form.cmdDeleteTheory
205
206        # Mock the confirmation dialog with return=No
207        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.No)
208
209        # Populate the model
210        item1 = QtGui.QStandardItem(True)
211        item1.setCheckable(True)
212        item1.setCheckState(QtCore.Qt.Checked)
213        item1.setText("item 1")
214        self.form.theory_model.appendRow(item1)
215        item2 = QtGui.QStandardItem(True)
216        item2.setCheckable(True)
217        item2.setCheckState(QtCore.Qt.Unchecked)
218        item2.setText("item 2")
219        self.form.theory_model.appendRow(item2)
220
221        # Assure the model contains two items
222        self.assertEqual(self.form.theory_model.rowCount(), 2)
223
224        # Assure the checkboxes are on
225        self.assertTrue(item1.checkState() == QtCore.Qt.Checked)
226        self.assertTrue(item2.checkState() == QtCore.Qt.Unchecked)
227
228        # Click on the delete  button
229        QTest.mouseClick(deleteButton, Qt.LeftButton)
230
231        # Test the warning dialog called once
232        self.assertTrue(QtGui.QMessageBox.question.called)
233
234        # Assure the model still contains the items
235        self.assertEqual(self.form.theory_model.rowCount(), 2)
236
237        # Now, mock the confirmation dialog with return=Yes
238        QtGui.QMessageBox.question = MagicMock(return_value=QtGui.QMessageBox.Yes)
239
240        # Click on the delete  button
241        QTest.mouseClick(deleteButton, Qt.LeftButton)
242
243        # Test the warning dialog called once
244        self.assertTrue(QtGui.QMessageBox.question.called)
245
246        # Assure the model contains 1 item
247        self.assertEqual(self.form.theory_model.rowCount(), 1)
248
249        # Set the remaining item to checked
250        self.form.theory_model.item(0).setCheckState(QtCore.Qt.Checked)
251
252        # Click on the delete button again
253        QTest.mouseClick(deleteButton, Qt.LeftButton)
254
255        # Assure the model contains no items
256        self.assertEqual(self.form.theory_model.rowCount(), 0)
257
258        # Click delete once again to assure no nasty behaviour on empty model
259        QTest.mouseClick(deleteButton, Qt.LeftButton)
260
261
262    def testSendToButton(self):
263        """
264        Test that clicking the Send To button sends checked data to a perspective
265        """
266        # Send empty data
267        mocked_perspective = self.form.parent.perspective()
268        mocked_perspective.setData = MagicMock()
269
270        # Click on the Send To  button
271        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
272
273        # The set_data method not called
274        self.assertFalse(mocked_perspective.setData.called)
275               
276        # Populate the model
277        filename = ["cyl_400_20.txt"]
278        self.form.readData(filename)
279
280        # setData is the method we want to see called
281        mocked_perspective = self.form.parent.perspective()
282        mocked_perspective.setData = MagicMock(filename)
283
284        # Assure the checkbox is on
285        self.form.cbSelect.setCurrentIndex(0)
286
287        # Click on the Send To  button
288        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
289
290        # Test the set_data method called once
291        #self.assertTrue(mocked_perspective.setData.called)
292
293        # open another file
294        filename = ["cyl_400_20.txt"]
295        self.form.readData(filename)
296
297        # Mock the warning message
298        QtGui.QMessageBox = MagicMock()
299
300        # Click on the button
301        QTest.mouseClick(self.form.cmdSendTo, Qt.LeftButton)
302
303        # Assure the message box popped up
304        QtGui.QMessageBox.assert_called_once()
305
306    def testDataSelection(self):
307        """
308        Tests the functionality of the Selection Option combobox
309        """
310        # Populate the model with 1d and 2d data
311        filename = ["cyl_400_20.txt", "Dec07031.ASC"]
312        self.form.readData(filename)
313
314        # Unselect all data
315        self.form.cbSelect.setCurrentIndex(1)
316
317        # Test the current selection
318        item1D = self.form.model.item(0)
319        item2D = self.form.model.item(1)
320        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
321        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)       
322
323        # Select all data
324        self.form.cbSelect.setCurrentIndex(0)
325
326        # Test the current selection
327        self.assertTrue(item1D.checkState() == QtCore.Qt.Checked)
328        self.assertTrue(item2D.checkState() == QtCore.Qt.Checked)       
329
330        # select 1d data
331        self.form.cbSelect.setCurrentIndex(2)
332
333        # Test the current selection
334        self.assertTrue(item1D.checkState() == QtCore.Qt.Checked)
335        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)       
336
337        # unselect 1d data
338        self.form.cbSelect.setCurrentIndex(3)
339
340        # Test the current selection
341        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
342        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)       
343
344        # select 2d data
345        self.form.cbSelect.setCurrentIndex(4)
346
347        # Test the current selection
348        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
349        self.assertTrue(item2D.checkState() == QtCore.Qt.Checked)       
350
351        # unselect 2d data
352        self.form.cbSelect.setCurrentIndex(5)
353
354        # Test the current selection
355        self.assertTrue(item1D.checkState() == QtCore.Qt.Unchecked)
356        self.assertTrue(item2D.checkState() == QtCore.Qt.Unchecked)       
357
358        # choose impossible index and assure the code raises
359        #with self.assertRaises(Exception):
360        #    self.form.cbSelect.setCurrentIndex(6)
361
362    def testFreezeTheory(self):
363        """
364        Assure theory freeze functionality works
365        """
366        # Not yet tested - agree on design first.
367        pass
368
369    def testRecursivelyCloneItem(self):
370        """
371        Test the rescursive QAbstractItem/QStandardItem clone
372        """
373        # Create an item with several branches
374        item1 = QtGui.QStandardItem()
375        item2 = QtGui.QStandardItem()
376        item3 = QtGui.QStandardItem()
377        item4 = QtGui.QStandardItem()
378        item5 = QtGui.QStandardItem()
379        item6 = QtGui.QStandardItem()
380
381        item4.appendRow(item5)
382        item2.appendRow(item4)
383        item2.appendRow(item6)
384        item1.appendRow(item2)
385        item1.appendRow(item3)
386
387        # Clone
388        new_item = self.form.recursivelyCloneItem(item1)
389
390        # assure the trees look identical
391        self.assertEqual(item1.rowCount(), new_item.rowCount())
392        self.assertEqual(item1.child(0).rowCount(), new_item.child(0).rowCount())
393        self.assertEqual(item1.child(1).rowCount(), new_item.child(1).rowCount())
394        self.assertEqual(item1.child(0).child(0).rowCount(), new_item.child(0).child(0).rowCount())
395
396    def testReadData(self):
397        """
398        Test the low level readData() method
399        """
400        filename = ["cyl_400_20.txt"]
401        self.form.manager.add_data = MagicMock()
402
403        # Initialize signal spy instances
404        spy_status_update = QtSignalSpy(self.form, self.form.communicator.statusBarUpdateSignal)
405        spy_data_received = QtSignalSpy(self.form, self.form.communicator.fileDataReceivedSignal)
406
407        # Read in the file
408        self.form.readData(filename)
409
410        # Expected two status bar updates
411        self.assertEqual(spy_status_update.count(), 1)
412        self.assertIn(filename[0], str(spy_status_update.called()[0]['args'][0]))
413
414
415        # Check that the model contains the item
416        self.assertEqual(self.form.model.rowCount(), 1)
417        self.assertEqual(self.form.model.columnCount(), 1)
418
419        # The 0th item header should be the name of the file
420        model_item = self.form.model.index(0,0)
421        model_name = str(self.form.model.data(model_item).toString())
422        self.assertEqual(model_name, filename[0])
423
424    def testDisplayHelp(self):
425        """
426        Test that the Help window gets shown correctly
427        """
428        partial_url = "sasgui/guiframe/data_explorer_help.html"
429        button1 = self.form.cmdHelp
430        button2 = self.form.cmdHelp_2
431
432        # Click on the Help button
433        QTest.mouseClick(button1, Qt.LeftButton)
434        qApp.processEvents()
435
436        # Check the browser
437        self.assertIn(partial_url, str(self.form._helpView.url()))
438        # Close the browser
439        self.form._helpView.close()
440
441        # Click on the Help_2 button
442        QTest.mouseClick(button2, Qt.LeftButton)
443        qApp.processEvents()
444        # Check the browser
445        self.assertIn(partial_url, str(self.form._helpView.url()))
446
447    def testLoadFile(self):
448        """
449        Test the threaded call to readData()
450        """
451        #self.form.loadFile()
452        pass
453
454    def testGetWList(self):
455        """
456        Test the list of known extensions
457        """
458        w_list = self.form.getWlist()
459
460        defaults = 'All (*.*);;canSAS files (*.xml);;SESANS files' +\
461            ' (*.ses);;ASCII files (*.txt);;IGOR 2D files (*.asc);;' +\
462            'IGOR/DAT 2D Q_map files (*.dat);;IGOR 1D files (*.abs);;'+\
463            'HFIR 1D files (*.d1d);;DANSE files (*.sans);;NXS files (*.nxs)'
464        default_list = defaults.split(';;')
465
466        for def_format in default_list:
467            self.assertIn(def_format, w_list)
468       
469    def testLoadComplete(self):
470        """
471        Test the callback method updating the data object
472        """
473        message="Loading Data Complete"
474        data_dict = {"a1":Data1D()}
475        output_data = (data_dict, message)
476
477        self.form.manager.add_data = MagicMock()
478
479        # Initialize signal spy instances
480        spy_status_update = QtSignalSpy(self.form, self.form.communicator.statusBarUpdateSignal)
481        spy_data_received = QtSignalSpy(self.form, self.form.communicator.fileDataReceivedSignal)
482
483        # Read in the file
484        self.form.loadComplete(output_data)
485
486        # "Loading data complete" no longer sent in LoadFile but in callback
487        self.assertIn("Loading Data Complete", str(spy_status_update.called()[0]['args'][0]))
488
489        # Expect one Data Received signal
490        self.assertEqual(spy_data_received.count(), 1)
491
492        # Assure returned dictionary has correct data
493        # We don't know the data ID, so need to iterate over dict
494        data_dict = spy_data_received.called()[0]['args'][0]
495        for data_key, data_value in data_dict.iteritems():
496            self.assertIsInstance(data_value, Data1D)
497
498        # Assure add_data on data_manager was called (last call)
499        self.assertTrue(self.form.manager.add_data.called)
500
501    def testNewPlot(self):
502        """
503        Creating new plots from Data1D/2D
504        """
505        loader = Loader()
506        manager = DataManager()
507
508        # get Data1D
509        p_file="cyl_400_20.txt"
510        output_object = loader.load(p_file)
511        new_data = [manager.create_gui_data(output_object, p_file)]
512
513        # Mask the plot show call
514        Plotter.show = MagicMock()
515
516        # Mask retrieval of the data
517        self.form.plotsFromCheckedItems = MagicMock(return_value=new_data)
518
519        # Call the plotting method
520        self.form.newPlot()
521
522        # The plot was displayed
523        self.assertTrue(Plotter.show.called)
524
525    def testUpdateModelFromPerspective(self):
526        """
527        Assure the model update is correct
528        """
529        good_item = QtGui.QStandardItem()
530        bad_item = "I'm so bad"
531
532        self.form.model.reset = MagicMock()
533
534        self.form.updateModelFromPerspective(good_item)
535
536        # See that the model got reset
537        self.form.model.reset.assert_called_once()
538
539        # See that the bad item causes raise
540        with self.assertRaises(Exception):
541            self.form.updateModelFromPerspective(bad_item)
542
543if __name__ == "__main__":
544    unittest.main()
Note: See TracBrowser for help on using the repository browser.