import sys import subprocess import unittest import webbrowser import logging from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.QtTest import QTest from PyQt5.QtCore import * from PyQt5.QtWebKit import * from unittest.mock import MagicMock # set up import paths import path_prepare # Local from sas.qtgui.MainWindow.DataExplorer import DataExplorerWindow from sas.qtgui.MainWindow.AboutBox import AboutBox from sas.qtgui.MainWindow.WelcomePanel import WelcomePanel from sas.qtgui.Utilities.IPythonWidget import IPythonWidget from sas.qtgui.MainWindow.GuiManager import Acknowledgements, GuiManager from sas.qtgui.MainWindow.MainWindow import MainSasViewWindow from sas.qtgui.UnitTesting.TestUtils import QtSignalSpy if not QApplication.instance(): app = QApplication(sys.argv) class GuiManagerTest(unittest.TestCase): '''Test the Main Window functionality''' def setUp(self): '''Create the tested object''' class MainWindow(MainSasViewWindow): # Main window of the application def __init__(self, reactor, parent=None): super(MainWindow, self).__init__(parent) # define workspace for dialogs. self.workspace = QMdiArea(self) self.setCentralWidget(self.workspace) self.manager = GuiManager(MainWindow(None)) def tearDown(self): '''Destroy the GUI''' self.manager = None def testDefaults(self): """ Test the object in its default state """ self.assertIsInstance(self.manager.filesWidget, DataExplorerWindow) self.assertIsInstance(self.manager.dockedFilesWidget, QDockWidget) self.assertIsInstance(self.manager.dockedFilesWidget.widget(), DataExplorerWindow) self.assertEqual(self.manager.dockedFilesWidget.features(), QDockWidget.NoDockWidgetFeatures) self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.dockedFilesWidget), Qt.LeftDockWidgetArea) self.assertIsInstance(self.manager.logDockWidget, QDockWidget) self.assertIsInstance(self.manager.logDockWidget.widget(), QTextBrowser) self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.logDockWidget), Qt.BottomDockWidgetArea) self.assertIsInstance(self.manager.ackWidget, Acknowledgements) self.assertIsInstance(self.manager.aboutWidget, AboutBox) self.assertIsInstance(self.manager.welcomePanel, WelcomePanel) def skip_testLogging(self): """ Test logging of stdout, stderr and log messages """ # See if the log window is empty self.assertEqual(self.manager.logDockWidget.widget().toPlainText(), "") # Now, send some message to stdout. # We are in the MainWindow scope, so simple 'print' will work message = "from stdout" print(message) self.assertIn(message, self.manager.logDockWidget.widget().toPlainText()) # Send some message to stderr message = "from stderr" sys.stderr.write(message) self.assertIn(message, self.manager.logDockWidget.widget().toPlainText()) # And finally, send a log message import logging message = "from logging" message_logged = "ERROR: " + message logging.error(message) self.assertIn(message_logged, self.manager.logDockWidget.widget().toPlainText()) def testConsole(self): """ Test the docked QtConsole """ # Invoke the console action self.manager.actionPython_Shell_Editor() # Test the widegt properties self.assertIsInstance(self.manager.ipDockWidget, QDockWidget) self.assertIsInstance(self.manager.ipDockWidget.widget(), IPythonWidget) self.assertEqual(self.manager._workspace.dockWidgetArea(self.manager.ipDockWidget), Qt.RightDockWidgetArea) def testUpdatePerspective(self): """ """ pass def testUpdateStatusBar(self): """ """ pass def testSetData(self): """ """ pass def testQuitApplication(self): """ Test that the custom exit method is called on shutdown """ self.manager._workspace.show() # Must mask sys.exit, otherwise the whole testing process stops. sys.exit = MagicMock() # Say No to the close dialog QMessageBox.question = MagicMock(return_value=QMessageBox.No) # Open, then close the manager self.manager.quitApplication() # See that the MessageBox method got called #self.assertTrue(QMessageBox.question.called) # Say Yes to the close dialog QMessageBox.question = MagicMock(return_value=QMessageBox.Yes) # Open, then close the manager self.manager.quitApplication() # See that the MessageBox method got called #self.assertTrue(QMessageBox.question.called) def testCheckUpdate(self): """ Tests the SasView website version polling """ self.manager.processVersion = MagicMock() version = {'update_url' : 'http://www.sasview.org/sasview.latestversion', 'version' : '4.1.2', 'download_url': 'https://github.com/SasView/sasview/releases'} self.manager.checkUpdate() self.manager.processVersion.assert_called_with(version) pass def testProcessVersion(self): """ Tests the version checker logic """ # 1. version = 0.0.0 version_info = {'version' : '0.0.0'} spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal) self.manager.processVersion(version_info) self.assertEqual(spy_status_update.count(), 1) message = 'Could not connect to the application server. Please try again later.' self.assertIn(message, str(spy_status_update.signal(index=0))) # 2. version < LocalConfig.__version__ version_info = {'version' : '0.0.1'} spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal) self.manager.processVersion(version_info) self.assertEqual(spy_status_update.count(), 1) message = 'You have the latest version of SasView' self.assertIn(message, str(spy_status_update.signal(index=0))) # 3. version > LocalConfig.__version__ version_info = {'version' : '999.0.0'} spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal) webbrowser.open = MagicMock() self.manager.processVersion(version_info) self.assertEqual(spy_status_update.count(), 1) message = 'Version 999.0.0 is available!' self.assertIn(message, str(spy_status_update.signal(index=0))) webbrowser.open.assert_called_with("https://github.com/SasView/sasview/releases") # 4. couldn't load version version_info = {} logging.error = MagicMock() spy_status_update = QtSignalSpy(self.manager, self.manager.communicate.statusBarUpdateSignal) self.manager.processVersion(version_info) # Retrieve and compare arguments of the mocked call message = "guiframe: could not get latest application version number" args, _ = logging.error.call_args self.assertIn(message, args[0]) # Check the signal message message = 'Could not connect to the application server.' self.assertIn(message, str(spy_status_update.signal(index=0))) def testActions(self): """ """ pass #### FILE #### def testActionLoadData(self): """ Menu File/Load Data File(s) """ # Mock the system file open method QFileDialog.getOpenFileNames = MagicMock(return_value=('','')) # invoke the action self.manager.actionLoadData() # Test the getOpenFileName() dialog called once self.assertTrue(QFileDialog.getOpenFileNames.called) def testActionLoadDataFolder(self): """ Menu File/Load Data Folder """ # Mock the system file open method QFileDialog.getExistingDirectory = MagicMock(return_value=('','')) # invoke the action self.manager.actionLoad_Data_Folder() # Test the getOpenFileName() dialog called once self.assertTrue(QFileDialog.getExistingDirectory.called) #### VIEW #### def testActionHideToolbar(self): """ Menu View/Hide Toolbar """ # Need to display the main window to initialize the toolbar. self.manager._workspace.show() # Check the initial state self.assertTrue(self.manager._workspace.toolBar.isVisible()) self.assertEqual('Hide Toolbar', self.manager._workspace.actionHide_Toolbar.text()) # Invoke action self.manager.actionHide_Toolbar() # Assure changes propagated correctly self.assertFalse(self.manager._workspace.toolBar.isVisible()) self.assertEqual('Show Toolbar', self.manager._workspace.actionHide_Toolbar.text()) # Revert self.manager.actionHide_Toolbar() # Assure the original values are back self.assertTrue(self.manager._workspace.toolBar.isVisible()) self.assertEqual('Hide Toolbar', self.manager._workspace.actionHide_Toolbar.text()) #### HELP #### # test when PyQt5 works with html def notestActionDocumentation(self): """ Menu Help/Documentation """ #Mock the QWebView method QWebView.show = MagicMock() # Assure the filename is correct self.assertIn("index.html", self.manager._helpLocation) # Invoke the action self.manager.actionDocumentation() # Check if show() got called self.assertTrue(QWebView.show.called) def skip_testActionTutorial(self): """ Menu Help/Tutorial """ # Mock subprocess.Popen subprocess.Popen = MagicMock() tested_location = self.manager._tutorialLocation # Assure the filename is correct self.assertIn("Tutorial.pdf", tested_location) # Invoke the action self.manager.actionTutorial() # Check if popen() got called self.assertTrue(subprocess.Popen.called) #Check the popen() call arguments subprocess.Popen.assert_called_with([tested_location], shell=True) def testActionAcknowledge(self): """ Menu Help/Acknowledge """ self.manager.actionAcknowledge() # Check if the window is actually opened. self.assertTrue(self.manager.ackWidget.isVisible()) self.assertIn("developers@sasview.org", self.manager.ackWidget.label.text()) def testActionCheck_for_update(self): """ Menu Help/Check for update """ # Just make sure checkUpdate is called. self.manager.checkUpdate = MagicMock() self.manager.actionCheck_for_update() self.assertTrue(self.manager.checkUpdate.called) if __name__ == "__main__": unittest.main()