--- a/src/eric7/Testing/TestingWidget.py Wed Jul 13 11:16:20 2022 +0200 +++ b/src/eric7/Testing/TestingWidget.py Wed Jul 13 14:55:47 2022 +0200 @@ -13,9 +13,7 @@ import os from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QCoreApplication -from PyQt6.QtWidgets import ( - QAbstractButton, QComboBox, QDialogButtonBox, QWidget -) +from PyQt6.QtWidgets import QAbstractButton, QComboBox, QDialogButtonBox, QWidget from EricWidgets import EricMessageBox from EricWidgets.EricApplication import ericApp @@ -26,18 +24,18 @@ from .TestResultsTree import TestResultsModel, TestResultsTreeView from .Interfaces import Frameworks -from .Interfaces.TestExecutorBase import ( - TestConfig, TestResult, TestResultCategory -) +from .Interfaces.TestExecutorBase import TestConfig, TestResult, TestResultCategory from .Interfaces.TestFrameworkRegistry import TestFrameworkRegistry import Preferences import UI.PixmapCache from Globals import ( - recentNameTestDiscoverHistory, recentNameTestFileHistory, - recentNameTestNameHistory, recentNameTestFramework, - recentNameTestEnvironment + recentNameTestDiscoverHistory, + recentNameTestFileHistory, + recentNameTestNameHistory, + recentNameTestFramework, + recentNameTestEnvironment, ) @@ -45,26 +43,28 @@ """ Class defining the various modes of the testing widget. """ - IDLE = 0 # idle, no test were run yet - RUNNING = 1 # test run being performed - STOPPED = 2 # test run finished + + IDLE = 0 # idle, no test were run yet + RUNNING = 1 # test run being performed + STOPPED = 2 # test run finished class TestingWidget(QWidget, Ui_TestingWidget): """ Class implementing a widget to orchestrate unit test execution. - + @signal testFile(str, int, bool) emitted to show the source of a test file @signal testRunStopped() emitted after a test run has finished """ + testFile = pyqtSignal(str, int, bool) testRunStopped = pyqtSignal() - + def __init__(self, testfile=None, parent=None): """ Constructor - + @param testfile file name of the test to load @type str @param parent reference to the parent widget (defaults to None) @@ -72,86 +72,101 @@ """ super().__init__(parent) self.setupUi(self) - + self.__resultsModel = TestResultsModel(self) self.__resultsModel.summary.connect(self.__setStatusLabel) self.__resultsTree = TestResultsTreeView(self) self.__resultsTree.setModel(self.__resultsModel) self.__resultsTree.goto.connect(self.__showSource) self.resultsGroupBox.layout().addWidget(self.__resultsTree) - - self.versionsButton.setIcon( - UI.PixmapCache.getIcon("info")) - self.clearHistoriesButton.setIcon( - UI.PixmapCache.getIcon("clearPrivateData")) - + + self.versionsButton.setIcon(UI.PixmapCache.getIcon("info")) + self.clearHistoriesButton.setIcon(UI.PixmapCache.getIcon("clearPrivateData")) + self.testsuitePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) - self.testsuitePicker.setInsertPolicy( - QComboBox.InsertPolicy.InsertAtTop) + self.testsuitePicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) self.testsuitePicker.setSizeAdjustPolicy( - QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) - + QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon + ) + self.discoveryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) - self.discoveryPicker.setInsertPolicy( - QComboBox.InsertPolicy.InsertAtTop) + self.discoveryPicker.setInsertPolicy(QComboBox.InsertPolicy.InsertAtTop) self.discoveryPicker.setSizeAdjustPolicy( - QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) - + QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon + ) + self.testComboBox.lineEdit().setClearButtonEnabled(True) - + # create some more dialog buttons for orchestration self.__showLogButton = self.buttonBox.addButton( - self.tr("Show Output..."), - QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Show Output..."), QDialogButtonBox.ButtonRole.ActionRole + ) self.__showLogButton.setToolTip( - self.tr("Show the output of the test runner process")) - self.__showLogButton.setWhatsThis(self.tr( - """<b>Show Output...</b""" - """<p>This button opens a dialog containing the output of the""" - """ test runner process of the most recent run.</p>""")) - + self.tr("Show the output of the test runner process") + ) + self.__showLogButton.setWhatsThis( + self.tr( + """<b>Show Output...</b""" + """<p>This button opens a dialog containing the output of the""" + """ test runner process of the most recent run.</p>""" + ) + ) + self.__showCoverageButton = self.buttonBox.addButton( - self.tr("Show Coverage..."), - QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Show Coverage..."), QDialogButtonBox.ButtonRole.ActionRole + ) self.__showCoverageButton.setToolTip( - self.tr("Show code coverage in a new dialog")) - self.__showCoverageButton.setWhatsThis(self.tr( - """<b>Show Coverage...</b>""" - """<p>This button opens a dialog containing the collected code""" - """ coverage data.</p>""")) - + self.tr("Show code coverage in a new dialog") + ) + self.__showCoverageButton.setWhatsThis( + self.tr( + """<b>Show Coverage...</b>""" + """<p>This button opens a dialog containing the collected code""" + """ coverage data.</p>""" + ) + ) + self.__startButton = self.buttonBox.addButton( - self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole) - - self.__startButton.setToolTip(self.tr( - "Start the selected testsuite")) - self.__startButton.setWhatsThis(self.tr( - """<b>Start Test</b>""" - """<p>This button starts the test run.</p>""")) - + self.tr("Start"), QDialogButtonBox.ButtonRole.ActionRole + ) + + self.__startButton.setToolTip(self.tr("Start the selected testsuite")) + self.__startButton.setWhatsThis( + self.tr( + """<b>Start Test</b>""" """<p>This button starts the test run.</p>""" + ) + ) + self.__startFailedButton = self.buttonBox.addButton( - self.tr("Rerun Failed"), QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Rerun Failed"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__startFailedButton.setToolTip( - self.tr("Reruns failed tests of the selected testsuite")) - self.__startFailedButton.setWhatsThis(self.tr( - """<b>Rerun Failed</b>""" - """<p>This button reruns all failed tests of the most recent""" - """ test run.</p>""")) - + self.tr("Reruns failed tests of the selected testsuite") + ) + self.__startFailedButton.setWhatsThis( + self.tr( + """<b>Rerun Failed</b>""" + """<p>This button reruns all failed tests of the most recent""" + """ test run.</p>""" + ) + ) + self.__stopButton = self.buttonBox.addButton( - self.tr("Stop"), QDialogButtonBox.ButtonRole.ActionRole) + self.tr("Stop"), QDialogButtonBox.ButtonRole.ActionRole + ) self.__stopButton.setToolTip(self.tr("Stop the running test")) - self.__stopButton.setWhatsThis(self.tr( - """<b>Stop Test</b>""" - """<p>This button stops a running test.</p>""")) - + self.__stopButton.setWhatsThis( + self.tr( + """<b>Stop Test</b>""" """<p>This button stops a running test.</p>""" + ) + ) + self.setWindowFlags( - self.windowFlags() | - Qt.WindowType.WindowContextHelpButtonHint + self.windowFlags() | Qt.WindowType.WindowContextHelpButtonHint ) self.setWindowIcon(UI.PixmapCache.getIcon("eric")) self.setWindowTitle(self.tr("Testing")) - + try: # we are called from within the eric IDE self.__venvManager = ericApp().getObject("VirtualEnvManager") @@ -161,63 +176,65 @@ except KeyError: # we were called as a standalone application from VirtualEnv.VirtualenvManager import VirtualenvManager + self.__venvManager = VirtualenvManager(self) self.__venvManager.virtualEnvironmentAdded.connect( - self.__populateVenvComboBox) + self.__populateVenvComboBox + ) self.__venvManager.virtualEnvironmentRemoved.connect( - self.__populateVenvComboBox) + self.__populateVenvComboBox + ) self.__venvManager.virtualEnvironmentChanged.connect( - self.__populateVenvComboBox) + self.__populateVenvComboBox + ) ericApp().registerObject("VirtualEnvManager", self.__venvManager) - + self.__project = None - + self.__discoverHistory = [] self.__fileHistory = [] self.__testNameHistory = [] self.__recentFramework = "" self.__recentEnvironment = "" self.__failedTests = [] - + self.__coverageFile = "" self.__coverageDialog = None - + self.__editors = [] self.__testExecutor = None self.__recentLog = "" - + # connect some signals - self.discoveryPicker.editTextChanged.connect( - self.__resetResults) - self.testsuitePicker.editTextChanged.connect( - self.__resetResults) - self.testComboBox.editTextChanged.connect( - self.__resetResults) - + self.discoveryPicker.editTextChanged.connect(self.__resetResults) + self.testsuitePicker.editTextChanged.connect(self.__resetResults) + self.testComboBox.editTextChanged.connect(self.__resetResults) + self.__frameworkRegistry = TestFrameworkRegistry() for framework in Frameworks: self.__frameworkRegistry.register(framework) - + self.__setIdleMode() - + self.__loadRecent() self.__populateVenvComboBox() - + if self.__project and self.__project.isOpen(): self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) self.frameworkComboBox.setCurrentText( - self.__project.getProjectTestingFramework()) + self.__project.getProjectTestingFramework() + ) self.__insertDiscovery(self.__project.getProjectPath()) else: self.__insertDiscovery("") - + self.__insertTestFile(testfile) self.__insertTestName("") - + self.clearHistoriesButton.clicked.connect(self.clearRecent) - + self.tabWidget.setCurrentIndex(0) - + def __populateVenvComboBox(self): """ Private method to (re-)populate the virtual environments selector. @@ -225,13 +242,12 @@ currentText = self.venvComboBox.currentText() if not currentText: currentText = self.__recentEnvironment - + self.venvComboBox.clear() self.venvComboBox.addItem("") - self.venvComboBox.addItems( - sorted(self.__venvManager.getVirtualenvNames())) + self.venvComboBox.addItems(sorted(self.__venvManager.getVirtualenvNames())) self.venvComboBox.setCurrentText(currentText) - + def __populateTestFrameworkComboBox(self): """ Private method to (re-)populate the test framework selector. @@ -239,62 +255,59 @@ currentText = self.frameworkComboBox.currentText() if not currentText: currentText = self.__recentFramework - + self.frameworkComboBox.clear() - + if bool(self.venvComboBox.currentText()): interpreter = self.__venvManager.getVirtualenvInterpreter( - self.venvComboBox.currentText()) + self.venvComboBox.currentText() + ) self.frameworkComboBox.addItem("") for index, (name, executor) in enumerate( - sorted(self.__frameworkRegistry.getFrameworks().items()), - start=1 + sorted(self.__frameworkRegistry.getFrameworks().items()), start=1 ): isInstalled = executor.isInstalled(interpreter) entry = ( - name - if isInstalled else - self.tr("{0} (not available)").format(name) + name if isInstalled else self.tr("{0} (not available)").format(name) ) self.frameworkComboBox.addItem(entry) - self.frameworkComboBox.model().item(index).setEnabled( - isInstalled) - + self.frameworkComboBox.model().item(index).setEnabled(isInstalled) + self.frameworkComboBox.setCurrentText(self.__recentFramework) - + def getResultsModel(self): """ Public method to get a reference to the model containing the test result data. - + @return reference to the test results model @rtype TestResultsModel """ return self.__resultsModel - + def hasFailedTests(self): """ Public method to check for failed tests. - + @return flag indicating the existence of failed tests @rtype bool """ return bool(self.__resultsModel.getFailedTests()) - + def getFailedTests(self): """ Public method to get the list of failed tests (if any). - + @return list of IDs of failed tests @rtype list of str """ return self.__failedTests[:] - + @pyqtSlot(str) def __insertHistory(self, widget, history, item): """ Private slot to insert an item into a history object. - + @param widget reference to the widget @type QComboBox or EricComboPathPicker @param history array containing the history @@ -311,24 +324,23 @@ widget.clear() widget.addItems(history) widget.setEditText(item) - + @pyqtSlot(str) def __insertDiscovery(self, start): """ Private slot to insert the discovery start directory into the discoveryPicker object. - + @param start start directory name to be inserted @type str """ - self.__insertHistory(self.discoveryPicker, self.__discoverHistory, - start) - + self.__insertHistory(self.discoveryPicker, self.__discoverHistory, start) + @pyqtSlot(str) def setTestFile(self, testFile, forProject=False): """ Public slot to set the given test file as the current one. - + @param testFile path of the test file @type str @param forProject flag indicating that this call is for a project @@ -337,93 +349,92 @@ """ if testFile: self.__insertTestFile(testFile) - + self.discoverCheckBox.setChecked(forProject or not bool(testFile)) - + if forProject: self.__projectOpened() - + self.tabWidget.setCurrentIndex(0) - + @pyqtSlot(str) def __insertTestFile(self, prog): """ Private slot to insert a test file name into the testsuitePicker object. - + @param prog test file name to be inserted @type str """ - self.__insertHistory(self.testsuitePicker, self.__fileHistory, - prog) - + self.__insertHistory(self.testsuitePicker, self.__fileHistory, prog) + @pyqtSlot(str) def __insertTestName(self, testName): """ Private slot to insert a test name into the testComboBox object. - + @param testName name of the test to be inserted @type str """ - self.__insertHistory(self.testComboBox, self.__testNameHistory, - testName) - + self.__insertHistory(self.testComboBox, self.__testNameHistory, testName) + def __loadRecent(self): """ Private method to load the most recently used lists. """ Preferences.Prefs.rsettings.sync() - + # 1. recently selected test framework and virtual environment self.__recentEnvironment = Preferences.Prefs.rsettings.value( - recentNameTestEnvironment, "") + recentNameTestEnvironment, "" + ) self.__recentFramework = Preferences.Prefs.rsettings.value( - recentNameTestFramework, "") - + recentNameTestFramework, "" + ) + # 2. discovery history self.__discoverHistory = [] - rs = Preferences.Prefs.rsettings.value( - recentNameTestDiscoverHistory) + rs = Preferences.Prefs.rsettings.value(recentNameTestDiscoverHistory) if rs is not None: recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] - self.__discoverHistory = recent[ - :Preferences.getDebugger("RecentNumber")] - + self.__discoverHistory = recent[: Preferences.getDebugger("RecentNumber")] + # 3. test file history self.__fileHistory = [] - rs = Preferences.Prefs.rsettings.value( - recentNameTestFileHistory) + rs = Preferences.Prefs.rsettings.value(recentNameTestFileHistory) if rs is not None: recent = [f for f in Preferences.toList(rs) if os.path.exists(f)] - self.__fileHistory = recent[ - :Preferences.getDebugger("RecentNumber")] - + self.__fileHistory = recent[: Preferences.getDebugger("RecentNumber")] + # 4. test name history self.__testNameHistory = [] - rs = Preferences.Prefs.rsettings.value( - recentNameTestNameHistory) + rs = Preferences.Prefs.rsettings.value(recentNameTestNameHistory) if rs is not None: recent = [n for n in Preferences.toList(rs) if n] - self.__testNameHistory = recent[ - :Preferences.getDebugger("RecentNumber")] - + self.__testNameHistory = recent[: Preferences.getDebugger("RecentNumber")] + def __saveRecent(self): """ Private method to save the most recently used lists. """ Preferences.Prefs.rsettings.setValue( - recentNameTestEnvironment, self.__recentEnvironment) + recentNameTestEnvironment, self.__recentEnvironment + ) Preferences.Prefs.rsettings.setValue( - recentNameTestFramework, self.__recentFramework) + recentNameTestFramework, self.__recentFramework + ) Preferences.Prefs.rsettings.setValue( - recentNameTestDiscoverHistory, self.__discoverHistory) + recentNameTestDiscoverHistory, self.__discoverHistory + ) Preferences.Prefs.rsettings.setValue( - recentNameTestFileHistory, self.__fileHistory) + recentNameTestFileHistory, self.__fileHistory + ) Preferences.Prefs.rsettings.setValue( - recentNameTestNameHistory, self.__testNameHistory) - + recentNameTestNameHistory, self.__testNameHistory + ) + Preferences.Prefs.rsettings.sync() - + @pyqtSlot() def clearRecent(self): """ @@ -433,15 +444,15 @@ self.__discoverHistory = [] self.__fileHistory = [] self.__testNameHistory = [] - + # clear widgets with histories self.discoveryPicker.clear() self.testsuitePicker.clear() self.testComboBox.clear() - + # sync histories self.__saveRecent() - + @pyqtSlot() def __resetResults(self): """ @@ -449,94 +460,87 @@ """ self.__totalCount = 0 self.__runCount = 0 - + self.progressCounterRunCount.setText("0") self.progressCounterRemCount.setText("0") self.progressProgressBar.setMaximum(100) self.progressProgressBar.setValue(0) - + self.statusLabel.clear() - + self.__resultsModel.clear() self.__updateButtonBoxButtons() - + @pyqtSlot() def __updateButtonBoxButtons(self): """ Private slot to update the state of the buttons of the button box. """ failedAvailable = bool(self.__resultsModel.getFailedTests()) - + # Start button - if self.__mode in ( - TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED - ): + if self.__mode in (TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED): self.__startButton.setEnabled( - bool(self.venvComboBox.currentText()) and - bool(self.frameworkComboBox.currentText()) and - ( - (self.discoverCheckBox.isChecked() and - bool(self.discoveryPicker.currentText())) or - bool(self.testsuitePicker.currentText()) + bool(self.venvComboBox.currentText()) + and bool(self.frameworkComboBox.currentText()) + and ( + ( + self.discoverCheckBox.isChecked() + and bool(self.discoveryPicker.currentText()) + ) + or bool(self.testsuitePicker.currentText()) ) ) self.__startButton.setDefault( - self.__mode == TestingWidgetModes.IDLE or - not failedAvailable + self.__mode == TestingWidgetModes.IDLE or not failedAvailable ) else: self.__startButton.setEnabled(False) self.__startButton.setDefault(False) - + # Start Failed button self.__startFailedButton.setEnabled( - self.__mode == TestingWidgetModes.STOPPED and - failedAvailable + self.__mode == TestingWidgetModes.STOPPED and failedAvailable ) self.__startFailedButton.setDefault( - self.__mode == TestingWidgetModes.STOPPED and - failedAvailable + self.__mode == TestingWidgetModes.STOPPED and failedAvailable ) - + # Stop button - self.__stopButton.setEnabled( - self.__mode == TestingWidgetModes.RUNNING) - self.__stopButton.setDefault( - self.__mode == TestingWidgetModes.RUNNING) - + self.__stopButton.setEnabled(self.__mode == TestingWidgetModes.RUNNING) + self.__stopButton.setDefault(self.__mode == TestingWidgetModes.RUNNING) + # Code coverage button self.__showCoverageButton.setEnabled( - self.__mode == TestingWidgetModes.STOPPED and - bool(self.__coverageFile) and - ( - (self.discoverCheckBox.isChecked() and - bool(self.discoveryPicker.currentText())) or - bool(self.testsuitePicker.currentText()) + self.__mode == TestingWidgetModes.STOPPED + and bool(self.__coverageFile) + and ( + ( + self.discoverCheckBox.isChecked() + and bool(self.discoveryPicker.currentText()) + ) + or bool(self.testsuitePicker.currentText()) ) ) - + # Log output button self.__showLogButton.setEnabled(bool(self.__recentLog)) - + # Close button - self.buttonBox.button( - QDialogButtonBox.StandardButton.Close - ).setEnabled(self.__mode in ( - TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED - )) - + self.buttonBox.button(QDialogButtonBox.StandardButton.Close).setEnabled( + self.__mode in (TestingWidgetModes.IDLE, TestingWidgetModes.STOPPED) + ) + @pyqtSlot() def __updateProgress(self): """ Private slot update the progress indicators. """ - self.progressCounterRunCount.setText( - str(self.__runCount)) - self.progressCounterRemCount.setText( - str(self.__totalCount - self.__runCount)) + self.progressCounterRunCount.setText(str(self.__runCount)) + self.progressCounterRemCount.setText(str(self.__totalCount - self.__runCount)) self.progressProgressBar.setMaximum(self.__totalCount) self.progressProgressBar.setValue(self.__runCount) - + @pyqtSlot() def __setIdleMode(self): """ @@ -546,26 +550,26 @@ self.__updateButtonBoxButtons() self.progressGroupBox.hide() self.tabWidget.setCurrentIndex(0) - + @pyqtSlot() def __setRunningMode(self): """ Private slot to switch the widget to running mode. """ self.__mode = TestingWidgetModes.RUNNING - + self.__totalCount = 0 self.__runCount = 0 - + self.__coverageFile = "" - + self.sbLabel.setText(self.tr("Running")) self.tabWidget.setCurrentIndex(1) self.__updateButtonBoxButtons() self.__updateProgress() - + self.progressGroupBox.show() - + @pyqtSlot() def __setStoppedMode(self): """ @@ -574,21 +578,21 @@ self.__mode = TestingWidgetModes.STOPPED if self.__totalCount == 0: self.progressProgressBar.setMaximum(100) - + self.progressGroupBox.hide() - + self.__updateButtonBoxButtons() - + self.testRunStopped.emit() - + self.raise_() self.activateWindow() - + @pyqtSlot(bool) def on_discoverCheckBox_toggled(self, checked): """ Private slot handling state changes of the 'discover' checkbox. - + @param checked state of the checkbox @type bool """ @@ -596,11 +600,10 @@ if self.__project and self.__project.isOpen(): self.__insertDiscovery(self.__project.getProjectPath()) else: - self.__insertDiscovery( - Preferences.getMultiProject("Workspace")) - + self.__insertDiscovery(Preferences.getMultiProject("Workspace")) + self.__resetResults() - + @pyqtSlot() def on_testsuitePicker_aboutToShowPathPickerDialog(self): """ @@ -608,33 +611,36 @@ """ if self.__project: # we were called from within eric - py3Extensions = ' '.join([ - "*{0}".format(ext) - for ext in - ericApp().getObject("DebugServer").getExtensions('Python3') - ]) - fileFilter = self.tr( - "Python3 Files ({0});;All Files (*)" - ).format(py3Extensions) + py3Extensions = " ".join( + [ + "*{0}".format(ext) + for ext in ericApp() + .getObject("DebugServer") + .getExtensions("Python3") + ] + ) + fileFilter = self.tr("Python3 Files ({0});;All Files (*)").format( + py3Extensions + ) else: # standalone application fileFilter = self.tr("Python Files (*.py);;All Files (*)") self.testsuitePicker.setFilters(fileFilter) - + defaultDirectory = ( self.__project.getProjectPath() - if self.__project and self.__project.isOpen() else - Preferences.getMultiProject("Workspace") + if self.__project and self.__project.isOpen() + else Preferences.getMultiProject("Workspace") ) if not defaultDirectory: defaultDirectory = os.path.expanduser("~") self.testsuitePicker.setDefaultDirectory(defaultDirectory) - + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. - + @param button button that was clicked @type QAbstractButton """ @@ -649,33 +655,33 @@ self.__showCoverageDialog() elif button == self.__showLogButton: self.__showLogOutput() - + @pyqtSlot(int) def on_venvComboBox_currentIndexChanged(self, index): """ Private slot handling the selection of a virtual environment. - + @param index index of the selected environment @type int """ self.__populateTestFrameworkComboBox() self.__updateButtonBoxButtons() - + self.versionsButton.setEnabled(bool(self.venvComboBox.currentText())) - + self.__updateCoverage() - + @pyqtSlot(int) def on_frameworkComboBox_currentIndexChanged(self, index): """ Private slot handling the selection of a test framework. - + @param index index of the selected framework @type int """ self.__resetResults() self.__updateCoverage() - + @pyqtSlot() def __updateCoverage(self): """ @@ -683,21 +689,19 @@ the selected framework's capabilities. """ hasCoverage = False - + venvName = self.venvComboBox.currentText() if venvName: framework = self.frameworkComboBox.currentText() if framework: - interpreter = self.__venvManager.getVirtualenvInterpreter( - venvName) - executor = self.__frameworkRegistry.createExecutor( - framework, self) + interpreter = self.__venvManager.getVirtualenvInterpreter(venvName) + executor = self.__frameworkRegistry.createExecutor(framework, self) hasCoverage = executor.hasCoverage(interpreter) - + self.coverageCheckBox.setEnabled(hasCoverage) if not hasCoverage: self.coverageCheckBox.setChecked(False) - + @pyqtSlot() def on_versionsButton_clicked(self): """ @@ -705,69 +709,58 @@ """ venvName = self.venvComboBox.currentText() if venvName: - headerText = self.tr("<h3>Versions of Frameworks and their" - " Plugins</h3>") + headerText = self.tr("<h3>Versions of Frameworks and their" " Plugins</h3>") versionsText = "" interpreter = self.__venvManager.getVirtualenvInterpreter(venvName) - for framework in sorted( - self.__frameworkRegistry.getFrameworks().keys() - ): - executor = self.__frameworkRegistry.createExecutor( - framework, self) + for framework in sorted(self.__frameworkRegistry.getFrameworks().keys()): + executor = self.__frameworkRegistry.createExecutor(framework, self) versions = executor.getVersions(interpreter) if versions: txt = "<p><strong>{0} {1}</strong>".format( - versions["name"], versions["version"]) - + versions["name"], versions["version"] + ) + if versions["plugins"]: txt += "<table>" for pluginVersion in versions["plugins"]: - txt += self.tr( - "<tr><td>{0}</td><td>{1}</td></tr>" - ).format( + txt += self.tr("<tr><td>{0}</td><td>{1}</td></tr>").format( pluginVersion["name"], pluginVersion["version"] ) txt += "</table>" txt += "</p>" - + versionsText += txt - + if not versionsText: versionsText = self.tr("No version information available.") - + EricMessageBox.information( - self, - self.tr("Versions"), - headerText + versionsText + self, self.tr("Versions"), headerText + versionsText ) - + @pyqtSlot() def startTests(self, failedOnly=False): """ Public slot to start the test run. - + @param failedOnly flag indicating to run only failed tests @type bool """ if self.__mode == TestingWidgetModes.RUNNING: return - + self.__recentLog = "" - + self.__recentEnvironment = self.venvComboBox.currentText() self.__recentFramework = self.frameworkComboBox.currentText() - - self.__failedTests = ( - self.__resultsModel.getFailedTests() - if failedOnly else - [] - ) + + self.__failedTests = self.__resultsModel.getFailedTests() if failedOnly else [] discover = self.discoverCheckBox.isChecked() if discover: discoveryStart = self.discoveryPicker.currentText() testFileName = "" testName = "" - + if discoveryStart: self.__insertDiscovery(discoveryStart) else: @@ -778,21 +771,20 @@ testName = self.testComboBox.currentText() if testName: self.__insertTestName(testName) - + self.sbLabel.setText(self.tr("Preparing Testsuite")) QCoreApplication.processEvents() - + if self.__project: mainScript = self.__project.getMainScript(True) coverageFile = ( - os.path.splitext(mainScript)[0] + ".coverage" - if mainScript else - "" + os.path.splitext(mainScript)[0] + ".coverage" if mainScript else "" ) else: coverageFile = "" interpreter = self.__venvManager.getVirtualenvInterpreter( - self.__recentEnvironment) + self.__recentEnvironment + ) config = TestConfig( interpreter=interpreter, discover=discover, @@ -805,9 +797,10 @@ eraseCoverage=self.coverageEraseCheckBox.isChecked(), coverageFile=coverageFile, ) - + self.__testExecutor = self.__frameworkRegistry.createExecutor( - self.__recentFramework, self) + self.__recentFramework, self + ) self.__testExecutor.collected.connect(self.__testsCollected) self.__testExecutor.collectError.connect(self.__testsCollectError) self.__testExecutor.startTest.connect(self.__testStarted) @@ -817,23 +810,24 @@ self.__testExecutor.stop.connect(self.__testsStopped) self.__testExecutor.coverageDataSaved.connect(self.__coverageData) self.__testExecutor.testRunAboutToBeStarted.connect( - self.__testRunAboutToBeStarted) - + self.__testRunAboutToBeStarted + ) + self.__setRunningMode() self.__testExecutor.start(config, []) - + @pyqtSlot() def __stopTests(self): """ Private slot to stop the current test run. """ self.__testExecutor.stopIfRunning() - + @pyqtSlot(list) def __testsCollected(self, testNames): """ Private slot handling the 'collected' signal of the executor. - + @param testNames list of tuples containing the test id, the test name and a description of collected tests @type list of tuple of (str, str, str) @@ -845,85 +839,89 @@ name=name, id=id, message=desc, - ) for id, name, desc in testNames + ) + for id, name, desc in testNames ] self.__resultsModel.addTestResults(testResults) - + self.__totalCount += len(testResults) self.__updateProgress() - + @pyqtSlot(list) def __testsCollectError(self, errors): """ Private slot handling the 'collectError' signal of the executor. - + @param errors list of tuples containing the test name and a description of the error @type list of tuple of (str, str) """ testResults = [] - + for testFile, error in errors: if testFile: - testResults.append(TestResult( - category=TestResultCategory.FAIL, - status=self.tr("Failure"), - name=testFile, - id=testFile, - message=self.tr("Collection Error"), - extra=error.splitlines() - )) + testResults.append( + TestResult( + category=TestResultCategory.FAIL, + status=self.tr("Failure"), + name=testFile, + id=testFile, + message=self.tr("Collection Error"), + extra=error.splitlines(), + ) + ) else: EricMessageBox.critical( self, self.tr("Collection Error"), self.tr( - "<p>There was an error while collecting tests." - "</p><p>{0}</p>" - ).format("<br/>".join(error.splitlines())) + "<p>There was an error while collecting tests." "</p><p>{0}</p>" + ).format("<br/>".join(error.splitlines())), ) - + if testResults: self.__resultsModel.addTestResults(testResults) - + @pyqtSlot(tuple) def __testStarted(self, test): """ Private slot handling the 'startTest' signal of the executor. - + @param test tuple containing the id, name and short description of the tests about to be run @type tuple of (str, str, str) """ - self.__resultsModel.updateTestResults([ - TestResult( - category=TestResultCategory.RUNNING, - status=self.tr("running"), - id=test[0], - name=test[1], - message="" if test[2] is None else test[2], - ) - ]) - + self.__resultsModel.updateTestResults( + [ + TestResult( + category=TestResultCategory.RUNNING, + status=self.tr("running"), + id=test[0], + name=test[1], + message="" if test[2] is None else test[2], + ) + ] + ) + @pyqtSlot(TestResult) def __processTestResult(self, result): """ Private slot to handle the receipt of a test result object. - + @param result test result object @type TestResult """ if not result.subtestResult: self.__runCount += 1 self.__updateProgress() - + self.__resultsModel.updateTestResults([result]) - + @pyqtSlot(list, str) def __testProcessFinished(self, results, output): """ Private slot to handle the 'testFinished' signal of the executor. - + @param results list of test result objects (if not sent via the 'testResult' signal @type list of TestResult @@ -931,17 +929,17 @@ @type str """ self.__recentLog = output - + self.__setStoppedMode() self.__testExecutor = None - + self.__adjustPendingState() - + @pyqtSlot(int, float) def __testRunFinished(self, noTests, duration): """ Private slot to handle the 'testRunFinished' signal of the executor. - + @param noTests number of tests run by the executor @type int @param duration time needed in seconds to run the tests @@ -952,18 +950,18 @@ locale.format_string("%.3f", duration, grouping=True) ) ) - + self.__setStoppedMode() - + @pyqtSlot() def __testsStopped(self): """ Private slot to handle the 'stop' signal of the executor. """ self.sbLabel.setText(self.tr("Ran %n test(s)", "", self.__runCount)) - + self.__setStoppedMode() - + @pyqtSlot() def __testRunAboutToBeStarted(self): """ @@ -971,7 +969,7 @@ executor. """ self.__resultsModel.clear() - + def __adjustPendingState(self): """ Private method to change the status indicator of all still pending @@ -983,20 +981,20 @@ result.category = TestResultCategory.SKIP result.status = self.tr("not run") newResults.append(result) - + if newResults: self.__resultsModel.updateTestResults(newResults) - + @pyqtSlot(str) def __coverageData(self, coverageFile): """ Private slot to handle the 'coverageData' signal of the executor. - + @param coverageFile file containing the coverage data @type str """ self.__coverageFile = coverageFile - + @pyqtSlot() def __showCoverageDialog(self): """ @@ -1005,40 +1003,41 @@ """ if self.__coverageDialog is None: from DataViews.PyCoverageDialog import PyCoverageDialog + self.__coverageDialog = PyCoverageDialog(self) self.__coverageDialog.openFile.connect(self.__openEditor) - + testDir = ( self.discoveryPicker.currentText() - if self.discoverCheckBox.isChecked() else - os.path.dirname(self.testsuitePicker.currentText()) + if self.discoverCheckBox.isChecked() + else os.path.dirname(self.testsuitePicker.currentText()) ) if testDir: self.__coverageDialog.show() self.__coverageDialog.start(self.__coverageFile, testDir) - + @pyqtSlot() def __showLogOutput(self): """ Private slot to show the output of the most recent test run. """ from EricWidgets.EricPlainTextDialog import EricPlainTextDialog + dlg = EricPlainTextDialog( - title=self.tr("Test Run Output"), - text=self.__recentLog + title=self.tr("Test Run Output"), text=self.__recentLog ) dlg.exec() - + @pyqtSlot(str) def __setStatusLabel(self, statusText): """ Private slot to set the status label to the text sent by the model. - + @param statusText text to be shown @type str """ self.statusLabel.setText(f"<b>{statusText}</b>") - + @pyqtSlot() def __projectOpened(self): """ @@ -1046,9 +1045,10 @@ """ self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) self.frameworkComboBox.setCurrentText( - self.__project.getProjectTestingFramework()) + self.__project.getProjectTestingFramework() + ) self.__insertDiscovery(self.__project.getProjectPath()) - + @pyqtSlot() def __projectClosed(self): """ @@ -1057,12 +1057,12 @@ self.venvComboBox.setCurrentText("") self.frameworkComboBox.setCurrentText("") self.__insertDiscovery("") - + @pyqtSlot(str, int) def __showSource(self, filename, lineno): """ Private slot to show the source of a traceback in an editor. - + @param filename file name of the file to be shown @type str @param lineno line number to go to in the file @@ -1073,35 +1073,36 @@ self.testFile.emit(filename, lineno, True) else: self.__openEditor(filename, lineno) - + def __openEditor(self, filename, linenumber=1): """ Private method to open an editor window for the given file. - + Note: This method opens an editor window when the testing dialog is called as a standalone application. - + @param filename path of the file to be opened @type str @param linenumber line number to place the cursor at (defaults to 1) @type int (optional) """ from QScintilla.MiniEditor import MiniEditor + editor = MiniEditor(filename, "Python3", self) editor.gotoLine(linenumber) editor.show() - + self.__editors.append(editor) - + def closeEvent(self, event): """ Protected method to handle the close event. - + @param event close event @type QCloseEvent """ event.accept() - + for editor in self.__editors: with contextlib.suppress(Exception): editor.close() @@ -1111,10 +1112,11 @@ """ Main window class for the standalone dialog. """ + def __init__(self, testfile=None, parent=None): """ Constructor - + @param testfile file name of the test script to open @type str @param parent reference to the parent widget @@ -1126,17 +1128,16 @@ size = self.__cw.size() self.setCentralWidget(self.__cw) self.resize(size) - - self.setStyle(Preferences.getUI("Style"), - Preferences.getUI("StyleSheet")) - + + self.setStyle(Preferences.getUI("Style"), Preferences.getUI("StyleSheet")) + self.__cw.buttonBox.accepted.connect(self.close) self.__cw.buttonBox.rejected.connect(self.close) - + def eventFilter(self, obj, event): """ Public method to filter events. - + @param obj reference to the object the event is meant for (QObject) @param event reference to the event object (QEvent) @return flag indicating, whether the event was handled (boolean) @@ -1144,7 +1145,7 @@ if event.type() == QEvent.Type.Close: QCoreApplication.exit(0) return True - + return False @@ -1152,11 +1153,8 @@ """ Function to clear the saved history lists. """ - Preferences.Prefs.rsettings.setValue( - recentNameTestDiscoverHistory, []) - Preferences.Prefs.rsettings.setValue( - recentNameTestFileHistory, []) - Preferences.Prefs.rsettings.setValue( - recentNameTestNameHistory, []) - + Preferences.Prefs.rsettings.setValue(recentNameTestDiscoverHistory, []) + Preferences.Prefs.rsettings.setValue(recentNameTestFileHistory, []) + Preferences.Prefs.rsettings.setValue(recentNameTestNameHistory, []) + Preferences.Prefs.rsettings.sync()