PyUnit/UnittestDialog.py

changeset 6894
df83ac87e0db
parent 6893
d29a7b8fab0f
child 6896
3716c4af48bb
diff -r d29a7b8fab0f -r df83ac87e0db PyUnit/UnittestDialog.py
--- 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()

eric ide

mercurial