--- a/eric7/Unittest/UnittestWidget.py Sat May 14 18:56:52 2022 +0200 +++ b/eric7/Unittest/UnittestWidget.py Sun May 15 18:08:31 2022 +0200 @@ -100,29 +100,23 @@ "Start the selected testsuite")) self.__startButton.setWhatsThis(self.tr( """<b>Start Test</b>""" - """<p>This button starts the selected testsuite.</p>""")) + """<p>This button starts the test run.</p>""")) - # TODO: implement "Rerun Failed" -## 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 selected""" -## """ testsuite.</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 unittest")) self.__stopButton.setWhatsThis(self.tr( """<b>Stop Test</b>""" - """<p>This button stops a running unittest.</p>""")) - - self.__stopButton.setEnabled(False) - self.__startButton.setDefault(True) - self.__startButton.setEnabled(False) -## self.__startFailedButton.setEnabled(False) + """<p>This button stops a running test.</p>""")) self.setWindowFlags( self.windowFlags() | @@ -148,21 +142,22 @@ self.__testNameHistory = [] self.__recentFramework = "" self.__recentEnvironment = "" - - self.__failedTests = set() + self.__failedTests = [] self.__editors = [] self.__testExecutor = None # connect some signals self.frameworkComboBox.currentIndexChanged.connect( - self.__updateButtonBoxButtons) + self.__resetResults) self.discoverCheckBox.toggled.connect( - self.__updateButtonBoxButtons) + self.__resetResults) self.discoveryPicker.editTextChanged.connect( - self.__updateButtonBoxButtons) + self.__resetResults) self.testsuitePicker.editTextChanged.connect( - self.__updateButtonBoxButtons) + self.__resetResults) + self.testComboBox.editTextChanged.connect( + self.__resetResults) self.__frameworkRegistry = UTFrameworkRegistry() for framework in Frameworks: @@ -235,6 +230,25 @@ 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 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): """ @@ -370,11 +384,30 @@ # 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 method to update the state of the buttons of the button box. + Private slot to update the state of the buttons of the button box. """ - failedAvailable = bool(self.__failedTests) + failedAvailable = bool(self.__resultsModel.getFailedTests()) # Start button if self.__mode in ( @@ -398,7 +431,14 @@ self.__startButton.setDefault(False) # Start Failed button - # TODO: not implemented yet (Start Failed button) + self.__startFailedButton.setEnabled( + self.__mode == UnittestWidgetModes.STOPPED and + failedAvailable + ) + self.__startFailedButton.setDefault( + self.__mode == UnittestWidgetModes.STOPPED and + failedAvailable + ) # Stop button self.__stopButton.setEnabled( @@ -413,9 +453,10 @@ UnittestWidgetModes.IDLE, UnittestWidgetModes.STOPPED )) + @pyqtSlot() def __updateProgress(self): """ - Private method update the progress indicators. + Private slot update the progress indicators. """ self.progressCounterRunCount.setText( str(self.__runCount)) @@ -424,17 +465,20 @@ self.progressProgressBar.setMaximum(self.__totalCount) self.progressProgressBar.setValue(self.__runCount) + @pyqtSlot() def __setIdleMode(self): """ - Private method to switch the widget to idle mode. + Private slot to switch the widget to idle mode. """ self.__mode = UnittestWidgetModes.IDLE self.__updateButtonBoxButtons() + self.progressGroupBox.hide() self.tabWidget.setCurrentIndex(0) + @pyqtSlot() def __setRunningMode(self): """ - Private method to switch the widget to running mode. + Private slot to switch the widget to running mode. """ self.__mode = UnittestWidgetModes.RUNNING @@ -449,19 +493,51 @@ self.__updateButtonBoxButtons() self.__updateProgress() - self.__resultsModel.clear() + self.progressGroupBox.show() + @pyqtSlot() def __setStoppedMode(self): """ - Private method to switch the widget to stopped mode. + Private slot to switch the widget to stopped mode. """ self.__mode = UnittestWidgetModes.STOPPED + if self.__totalCount == 0: + self.progressProgressBar.setMaximum(100) + + self.progressGroupBox.hide() self.__updateButtonBoxButtons() self.raise_() self.activateWindow() + @pyqtSlot() + def on_testsuitePicker_aboutToShowPathPickerDialog(self): + """ + Private slot called before the test file selection dialog is shown. + """ + # TODO: implement eric-ide mode +# if self.__dbs: +# py3Extensions = ' '.join( +# ["*{0}".format(ext) +# for ext in self.__dbs.getExtensions('Python3')] +# ) +# fileFilter = self.tr( +# "Python3 Files ({0});;All Files (*)" +# ).format(py3Extensions) +# else: + fileFilter = self.tr("Python Files (*.py);;All Files (*)") + self.testsuitePicker.setFilters(fileFilter) + + defaultDirectory = Preferences.getMultiProject("Workspace") + if not defaultDirectory: + defaultDirectory = os.path.expanduser("~") +# if self.__dbs: +# project = ericApp().getObject("Project") +# if self.__forProject and project.isOpen(): +# defaultDirectory = project.getProjectPath() + self.testsuitePicker.setDefaultDirectory(defaultDirectory) + @pyqtSlot(QAbstractButton) def on_buttonBox_clicked(self, button): """ @@ -475,8 +551,8 @@ self.__saveRecent() elif button == self.__stopButton: self.__stopTests() -# elif button == self.__startFailedButton: -# self.startTests(failedOnly=True) + elif button == self.__startFailedButton: + self.startTests(failedOnly=True) @pyqtSlot(int) def on_venvComboBox_currentIndexChanged(self, index): @@ -548,6 +624,11 @@ 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() @@ -579,11 +660,11 @@ testFilename=testFileName, testName=testName, failFast=self.failfastCheckBox.isChecked(), + failedOnly=failedOnly, collectCoverage=self.coverageCheckBox.isChecked(), eraseCoverage=self.coverageEraseCheckBox.isChecked(), ) - self.__resultsModel.clear() self.__testExecutor = self.__frameworkRegistry.createExecutor( self.__recentFramework, self) self.__testExecutor.collected.connect(self.__testsCollected) @@ -594,6 +675,8 @@ 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, []) @@ -736,6 +819,14 @@ 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): """