Wed, 18 May 2022 08:43:36 +0200
Modified the 'getTestFileName()' function of the Utilities module to allow for additional test file name patterns (currently 'test...' and 'test_...').
# -*- coding: utf-8 -*- # Copyright (c) 2022 Detlev Offenbach <detlev@die-offenbachs.de> # """ Module implementing a widget to orchestrate unit test execution. """ import contextlib import enum import locale import os from PyQt6.QtCore import pyqtSignal, pyqtSlot, Qt, QEvent, QCoreApplication from PyQt6.QtWidgets import ( QAbstractButton, QComboBox, QDialogButtonBox, QWidget ) from EricWidgets import EricMessageBox from EricWidgets.EricApplication import ericApp from EricWidgets.EricMainWindow import EricMainWindow from EricWidgets.EricPathPicker import EricPathPickerModes from .Ui_TestingWidget import Ui_TestingWidget from .TestResultsTree import TestResultsModel, TestResultsTreeView from .Interfaces import Frameworks 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 ) class TestingWidgetModes(enum.Enum): """ 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 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) @type QWidget (optional) """ 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.testsuitePicker.setMode(EricPathPickerModes.OPEN_FILE_MODE) self.testsuitePicker.setInsertPolicy( QComboBox.InsertPolicy.InsertAtTop) self.testsuitePicker.setSizeAdjustPolicy( QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) self.discoveryPicker.setMode(EricPathPickerModes.DIRECTORY_MODE) self.discoveryPicker.setInsertPolicy( QComboBox.InsertPolicy.InsertAtTop) self.discoveryPicker.setSizeAdjustPolicy( QComboBox.SizeAdjustPolicy.AdjustToMinimumContentsLengthWithIcon) self.testComboBox.lineEdit().setClearButtonEnabled(True) # create some more dialog buttons for orchestration self.__showCoverageButton = self.buttonBox.addButton( 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.__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.__startFailedButton = self.buttonBox.addButton( 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.__stopButton = self.buttonBox.addButton( 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.setWindowFlags( 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") self.__project = ericApp().getObject("Project") self.__project.projectOpened.connect(self.__projectOpened) self.__project.projectClosed.connect(self.__projectClosed) 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.__venvManager.virtualEnvironmentRemoved.connect( self.__populateVenvComboBox) self.__venvManager.virtualEnvironmentChanged.connect( self.__populateVenvComboBox) 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 # connect some signals self.frameworkComboBox.currentIndexChanged.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.__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. """ 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.setCurrentText(currentText) def __populateTestFrameworkComboBox(self): """ Private method to (re-)populate the test framework selector. """ 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.frameworkComboBox.addItem("") for index, (name, executor) in enumerate( sorted(self.__frameworkRegistry.getFrameworks().items()), start=1 ): isInstalled = executor.isInstalled(interpreter) entry = ( name if isInstalled else self.tr("{0} (not available)").format(name) ) self.frameworkComboBox.addItem(entry) 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 @type list of str @param item item to be inserted @type str """ # prepend the given directory to the discovery picker if item is None: item = "" if item in history: history.remove(item) history.insert(0, item) 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) @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 (defaults to False) @type bool (optional) """ if testFile: self.__insertTestFile(testFile) self.discoverCheckBox.setChecked(forProject or not bool(testFile)) 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) @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) 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, "") self.__recentFramework = Preferences.Prefs.rsettings.value( recentNameTestFramework, "") # 2. discovery history self.__discoverHistory = [] 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")] # 3. test file history self.__fileHistory = [] 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")] # 4. test name history self.__testNameHistory = [] 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")] def __saveRecent(self): """ Private method to save the most recently used lists. """ Preferences.Prefs.rsettings.setValue( recentNameTestEnvironment, self.__recentEnvironment) Preferences.Prefs.rsettings.setValue( recentNameTestFramework, self.__recentFramework) Preferences.Prefs.rsettings.setValue( recentNameTestDiscoverHistory, self.__discoverHistory) Preferences.Prefs.rsettings.setValue( recentNameTestFileHistory, self.__fileHistory) Preferences.Prefs.rsettings.setValue( recentNameTestNameHistory, self.__testNameHistory) Preferences.Prefs.rsettings.sync() @pyqtSlot() def clearRecent(self): """ Public slot to clear the recently used lists. """ # clear histories 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): """ Private slot to reset the test results tab and data. """ 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 ): 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()) ) ) self.__startButton.setDefault( 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.__startFailedButton.setDefault( self.__mode == TestingWidgetModes.STOPPED and failedAvailable ) # Stop button 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()) ) ) # Close button 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.progressProgressBar.setMaximum(self.__totalCount) self.progressProgressBar.setValue(self.__runCount) @pyqtSlot() def __setIdleMode(self): """ Private slot to switch the widget to idle mode. """ self.__mode = TestingWidgetModes.IDLE 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): """ Private slot to switch the widget to stopped mode. """ 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 """ if not bool(self.discoveryPicker.currentText()): if self.__project and self.__project.isOpen(): self.__insertDiscovery(self.__project.getProjectPath()) else: self.__insertDiscovery( Preferences.getMultiProject("Workspace")) self.__resetResults() @pyqtSlot() def on_testsuitePicker_aboutToShowPathPickerDialog(self): """ Private slot called before the test file selection dialog is shown. """ 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) 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 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 """ if button == self.__startButton: self.startTests() self.__saveRecent() elif button == self.__stopButton: self.__stopTests() elif button == self.__startFailedButton: self.startTests(failedOnly=True) elif button == self.__showCoverageButton: self.__showCoverageDialog() @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())) @pyqtSlot() def on_versionsButton_clicked(self): """ Private slot to show the versions of available plugins. """ venvName = self.venvComboBox.currentText() if venvName: 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) versions = executor.getVersions(interpreter) if versions: txt = "<p><strong>{0} {1}</strong>".format( 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( 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 ) @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.__recentEnvironment = self.venvComboBox.currentText() self.__recentFramework = self.frameworkComboBox.currentText() 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: discoveryStart = "" testFileName = self.testsuitePicker.currentText() if testFileName: self.__insertTestFile(testFileName) testName = self.testComboBox.currentText() if testName: self.__insertTestName(testName) if testFileName and not testName: testName = "suite" self.sbLabel.setText(self.tr("Preparing Testsuite")) QCoreApplication.processEvents() if self.__project: mainScript = self.__project.getMainScript(True) coverageFile = os.path.splitext(mainScript)[0] + ".coverage" else: coverageFile = "" interpreter = self.__venvManager.getVirtualenvInterpreter( self.__recentEnvironment) config = TestConfig( interpreter=interpreter, discover=discover, discoveryStart=discoveryStart, testFilename=testFileName, testName=testName, failFast=self.failfastCheckBox.isChecked(), failedOnly=failedOnly, collectCoverage=self.coverageCheckBox.isChecked(), eraseCoverage=self.coverageEraseCheckBox.isChecked(), coverageFile=coverageFile, ) self.__testExecutor = self.__frameworkRegistry.createExecutor( self.__recentFramework, self) self.__testExecutor.collected.connect(self.__testsCollected) self.__testExecutor.collectError.connect(self.__testsCollectError) self.__testExecutor.startTest.connect(self.__testStarted) self.__testExecutor.testResult.connect(self.__processTestResult) self.__testExecutor.testFinished.connect(self.__testProcessFinished) self.__testExecutor.testRunFinished.connect(self.__testRunFinished) self.__testExecutor.stop.connect(self.__testsStopped) self.__testExecutor.coverageDataSaved.connect(self.__coverageData) self.__testExecutor.testRunAboutToBeStarted.connect( 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 and test name of collected tests @type list of tuple of (str, str) """ testResults = [ TestResult( category=TestResultCategory.PENDING, status=self.tr("pending"), name=name, id=id, message=desc, ) for id, name, desc in testNames ] self.__resultsModel.setTestResults(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() )) else: EricMessageBox.critical( self, self.tr("Collection Error"), self.tr( "<p>There was an error while collecting unit 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], ) ]) @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 @param output string containing the test process output (if any) @type str """ self.__setStoppedMode() self.__testExecutor = None @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 @type float """ self.sbLabel.setText( self.tr("Ran %n test(s) in {0}s", "", noTests).format( 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): """ Private slot to handle the 'testRunAboutToBeStarted' signal of the executor. """ self.__resultsModel.clear() @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): """ Private slot to show a code coverage dialog for the most recent test run. """ if self.__coverageDialog is None: from DataViews.PyCoverageDialog import PyCoverageDialog self.__coverageDialog = PyCoverageDialog(self) self.__coverageDialog.openFile.connect(self.__openEditor) if self.discoverCheckBox.isChecked(): testDir = self.discoveryPicker.currentText() else: testDir = os.path.dirname(self.testsuitePicker.currentText()) if testDir: self.__coverageDialog.show() self.__coverageDialog.start(self.__coverageFile, testDir) @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): """ Private slot to handle a project being opened. """ self.venvComboBox.setCurrentText(self.__project.getProjectVenv()) self.frameworkComboBox.setCurrentText( self.__project.getProjectTestingFramework()) self.__insertDiscovery(self.__project.getProjectPath()) @pyqtSlot() def __projectClosed(self): """ Private slot to handle a project being closed. """ 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 @type int """ if self.__project: # running as part of eric IDE 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() class TestingWindow(EricMainWindow): """ 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 @type QWidget """ super().__init__(parent) self.__cw = TestingWidget(testfile=testfile, parent=self) self.__cw.installEventFilter(self) size = self.__cw.size() self.setCentralWidget(self.__cw) self.resize(size) 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) """ if event.type() == QEvent.Type.Close: QCoreApplication.exit(0) return True return False def clearSavedHistories(self): """ 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.sync()