--- a/PyUnit/UnittestDialog.py Fri Mar 22 19:31:56 2019 +0100 +++ b/PyUnit/UnittestDialog.py Sat Mar 23 16:45:26 2019 +0100 @@ -70,6 +70,11 @@ self.testsuitePicker.setSizeAdjustPolicy( QComboBox.AdjustToMinimumContentsLength) + self.discoveryPicker.setMode(E5PathPickerModes.DirectoryMode) + self.discoveryPicker.setInsertPolicy(QComboBox.InsertAtTop) + self.discoveryPicker.setSizeAdjustPolicy( + QComboBox.AdjustToMinimumContentsLength) + self.startButton = self.buttonBox.addButton( self.tr("Start"), QDialogButtonBox.ActionRole) self.startButton.setToolTip(self.tr( @@ -105,16 +110,19 @@ self.setWindowTitle(self.tr("Unittest")) if dbs: self.ui = ui + + self.venvComboBox.addItem("") + self.venvComboBox.addItems( + sorted(e5App().getObject("VirtualEnvManager") + .getVirtualenvNames())) + self.venvComboBox.setVisible(bool(self.__dbs)) + self.venvLabel.setVisible(bool(self.__dbs)) + self.__setProgressColor("green") self.progressLed.setDarkFactor(150) self.progressLed.off() - self.venvComboBox.addItem("") - self.venvComboBox.addItems( - sorted(e5App().getObject("VirtualEnvManager") - .getVirtualenvNames())) - self.venvComboBox.setEnabled(bool(self.__dbs)) - + self.discoverHistory = [] self.fileHistory = [] self.testNameHistory = [] self.running = False @@ -173,6 +181,31 @@ @type bool """ self.__forProject = forProject + if forProject: + project = e5App().getObject("Project") + if project.isOpen(): + self.insertDiscovery(project.getProjectPath()) + else: + self.insertDiscovery("") + else: + self.insertDiscovery("") + + def insertDiscovery(self, start): + """ + Public slot to insert the discovery start directory into the + discoveryPicker object. + + @param start start directory name to be inserted + @type str + """ + # prepend the given directory to the discovery picker + if start is None: + start = "" + if start in self.discoverHistory: + self.discoverHistory.remove(start) + self.discoverHistory.insert(0, start) + self.discoveryPicker.clear() + self.discoveryPicker.addItems(self.discoverHistory) def insertProg(self, prog): """ @@ -242,6 +275,27 @@ """ self.insertProg(suite) + @pyqtSlot(str) + def on_testsuitePicker_editTextChanged(self, path): + """ + Private slot handling changes of the test suite path. + + @param path path of the test suite file + @type str + """ + self.startFailedButton.setEnabled(False) + + @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 + """ + self.startFailedButton.setEnabled( + bool(self.__failedTests) and not checked) + def on_buttonBox_clicked(self, button): """ Private slot called by a button of the button box clicked. @@ -265,12 +319,20 @@ if self.running: return - prog = self.testsuitePicker.currentText() - if not prog: + discover = self.discoverCheckBox.isChecked() + if discover: + discoveryStart = self.discoveryPicker.currentText() + prog = "" + else: + discoveryStart = "" + prog = self.testsuitePicker.currentText() + + if not prog and not discover: E5MessageBox.critical( self, self.tr("Unittest"), - self.tr("You must enter a test suite file.")) + self.tr("You must enter a test suite file or select" + " auto-discovery.")) return # prepend the selected file to the testsuite combobox @@ -278,36 +340,60 @@ self.sbLabel.setText(self.tr("Preparing Testsuite")) QApplication.processEvents() - testFunctionName = self.testComboBox.currentText() - if testFunctionName: - self.insertTestName(testFunctionName) + if discover: + testFunctionName = "" + self.testName = self.tr("Unittest with auto-discovery") else: - testFunctionName = "suite" - - # build the module name from the filename without extension - self.testName = os.path.splitext(os.path.basename(prog))[0] + testFunctionName = self.testComboBox.currentText() + if testFunctionName: + self.insertTestName(testFunctionName) + else: + testFunctionName = "suite" + + # build the module name from the filename without extension + self.testName = os.path.splitext(os.path.basename(prog))[0] if self.__dbs: # we are cooperating with the eric6 IDE project = e5App().getObject("Project") if self.__forProject and project.isOpen() and \ - project.isProjectSource(prog): + (discover or project.isProjectSource(prog)): mainScript = os.path.abspath(project.getMainScript(True)) clientType = project.getProjectLanguage() - sysPath = [os.path.dirname(mainScript)] + if mainScript: + workdir = os.path.dirname(mainScript) + else: + workdir = project.getProjectPath() + sysPath = [workdir] + if discover and not discoveryStart: + discoveryStart = workdir else: - mainScript = os.path.abspath(prog) + if discover: + if not discoveryStart: + E5MessageBox.critical( + self, + self.tr("Unittest"), + self.tr("You must enter a start directory for" + " auto-discovery.")) + return + mainScript = os.path.join(discoveryStart, "unittest") + workdir = "" + # assume Python3 for auto-discovery + clientType = "Python3" + else: + mainScript = os.path.abspath(prog) + workdir = os.path.dirname(mainScript) + flags = Utilities.extractFlagsFromFile(mainScript) + if mainScript.endswith( + tuple(Preferences.getPython("PythonExtensions"))) or \ + ("FileType" in flags and + flags["FileType"] in ["Python", "Python2"]): + clientType = "Python2" + else: + # if it is not Python2 it must be Python3! + clientType = "Python3" sysPath = [] - flags = Utilities.extractFlagsFromFile(mainScript) - if mainScript.endswith( - tuple(Preferences.getPython("PythonExtensions"))) or \ - ("FileType" in flags and - flags["FileType"] in ["Python", "Python2"]): - clientType = "Python2" - else: - # if it is not Python2 it must be Python3! - clientType = "Python3" - if failedOnly and self.__failedTests: + if not discover and failedOnly and self.__failedTests: failed = [t.split(".", 1)[1] for t in self.__failedTests] else: failed = [] @@ -316,9 +402,11 @@ prog, self.testName, testFunctionName, failed, self.coverageCheckBox.isChecked(), mainScript, self.coverageEraseCheckBox.isChecked(), clientType=clientType, - forProject=self.__forProject, - venvName=self.venvComboBox.currentText(), syspath=sysPath) + forProject=self.__forProject, workdir=workdir, + venvName=self.venvComboBox.currentText(), syspath=sysPath, + discover=discover, discoveryStart=discoveryStart) else: + # TODO: add discovery # we are running as an application sys.path = [os.path.dirname(os.path.abspath(prog))] + \ self.savedSysPath @@ -334,18 +422,32 @@ # now try to generate the testsuite try: - module = __import__(self.testName) - try: - if failedOnly and self.__failedTests: - test = unittest.defaultTestLoader.loadTestsFromNames( - [t.split(".", 1)[1] for t in self.__failedTests], + if discover: + if not discoveryStart: + E5MessageBox.critical( + self, + self.tr("Unittest"), + self.tr("You must enter a start directory for" + " auto-discovery.")) + return + + test = unittest.defaultTestLoader.discover(discoveryStart) + else: + module = __import__(self.testName) + try: + if failedOnly and self.__failedTests: + test = \ + unittest.defaultTestLoader.loadTestsFromNames( + [t.split(".", 1)[1] + for t in self.__failedTests], + module) + else: + test = \ + unittest.defaultTestLoader.loadTestsFromName( + testFunctionName, module) + except AttributeError: + test = unittest.defaultTestLoader.loadTestsFromModule( module) - else: - test = unittest.defaultTestLoader.loadTestsFromName( - testFunctionName, module) - except AttributeError: - test = unittest.defaultTestLoader.loadTestsFromModule( - module) except Exception: exc_type, exc_value, exc_tb = sys.exc_info() E5MessageBox.critical( @@ -360,22 +462,15 @@ # now set up the coverage stuff if self.coverageCheckBox.isChecked(): - if self.__dbs: - # we are cooperating with the eric6 IDE - project = e5App().getObject("Project") - if project.isOpen() and project.isProjectSource(prog): - mainScript = project.getMainScript(True) - if not mainScript: - mainScript = os.path.abspath(prog) - else: - mainScript = os.path.abspath(prog) + if discover: + covname = os.path.join(discoveryStart, "unittest") + elif prog: + covname = os.path.splitext(os.path.abspath(prog))[0] else: - mainScript = os.path.abspath(prog) + covname = "unittest" from DebugClients.Python.coverage import coverage - cover = coverage( - data_file="{0}.coverage".format( - os.path.splitext(mainScript)[0])) + cover = coverage(data_file="{0}.coverage".format(covname)) if self.coverageEraseCheckBox.isChecked(): cover.erase() else: @@ -413,7 +508,7 @@ "<p>Unable to run test <b>{0}</b>.<br>{1}<br>{2}</p>") .format(self.testName, exc_type, exc_value)) return - + self.totalTests = nrTests self.__setRunningMode() self.__dbs.remoteUTRun() @@ -450,6 +545,7 @@ Private method to set the GUI in running mode. """ self.running = True + self.tabWidget.setCurrentIndex(1) # reset counters and error infos self.runCount = 0 @@ -470,14 +566,19 @@ str(self.expectedFailureCount)) self.progressCounterUnexpectedSuccessCount.setText( str(self.unexpectedSuccessCount)) + self.errorsListWidget.clear() self.testsListWidget.clear() + self.progressProgressBar.setRange(0, self.totalTests) self.__setProgressColor("green") self.progressProgressBar.reset() + self.stopButton.setEnabled(True) self.startButton.setEnabled(False) + self.startFailedButton.setEnabled(False) self.stopButton.setDefault(True) + self.sbLabel.setText(self.tr("Running")) self.progressLed.on() QApplication.processEvents() @@ -492,23 +593,20 @@ self.timeTaken = float(self.stopTime - self.startTime) self.running = False + failedAvailable = bool(self.__failedTests) and \ + not self.discoverCheckBox.isChecked() self.startButton.setEnabled(True) - self.startFailedButton.setEnabled(bool(self.__failedTests)) + self.startFailedButton.setEnabled(failedAvailable) self.stopButton.setEnabled(False) - if self.__failedTests: + if failedAvailable: self.startFailedButton.setDefault(True) self.startButton.setDefault(False) else: self.startFailedButton.setDefault(False) self.startButton.setDefault(True) - if self.runCount == 1: - self.sbLabel.setText( - self.tr("Ran {0} test in {1:.3f}s") - .format(self.runCount, self.timeTaken)) - else: - self.sbLabel.setText( - self.tr("Ran {0} tests in {1:.3f}s") - .format(self.runCount, self.timeTaken)) + self.sbLabel.setText( + self.tr("Ran %n test(s) in {0:.3f}s", "", self.runCount) + .format(self.timeTaken)) self.progressLed.off() self.unittestStopped.emit()