PyUnit/UnittestDialog.py

changeset 1499
b4d0457afb15
parent 1491
985c5abc8226
child 1509
c0b5e693b0eb
--- a/PyUnit/UnittestDialog.py	Sat Dec 17 16:03:53 2011 +0100
+++ b/PyUnit/UnittestDialog.py	Sat Dec 17 19:13:19 2011 +0100
@@ -37,8 +37,10 @@
     Class implementing the UI to the pyunit package.
     
     @signal unittestFile(str, int, int) emitted to show the source of a unittest file
+    @signal unittestStopped() emitted after a unit test was run
     """
     unittestFile = pyqtSignal(str, int, int)
+    unittestStopped = pyqtSignal()
     
     def __init__(self, prog=None, dbs=None, ui=None, fromEric=False, parent=None,
                  name=None):
@@ -65,6 +67,13 @@
         self.startButton.setWhatsThis(self.trUtf8(
             """<b>Start Test</b>"""
             """<p>This button starts the selected testsuite.</p>"""))
+        self.startFailedButton = self.buttonBox.addButton(
+            self.trUtf8("Rerun Failed"), QDialogButtonBox.ActionRole)
+        self.startFailedButton.setToolTip(
+            self.trUtf8("Reruns failed tests of the selected testsuite"))
+        self.startFailedButton.setWhatsThis(self.trUtf8(
+            """<b>Rerun Failed</b>"""
+            """<p>This button reruns all failed tests of the selected testsuite.</p>"""))
         self.stopButton = self.buttonBox.addButton(
             self.trUtf8("Stop"), QDialogButtonBox.ActionRole)
         self.stopButton.setToolTip(self.trUtf8("Stop the running unittest"))
@@ -73,6 +82,7 @@
             """<p>This button stops a running unittest.</p>"""))
         self.stopButton.setEnabled(False)
         self.startButton.setDefault(True)
+        self.startFailedButton.setEnabled(False)
         
         self.dbs = dbs
         self.__fromEric = fromEric
@@ -104,6 +114,8 @@
             self.trUtf8("^Error: "),
         ]
         
+        self.__failedTests = []
+        
         # now connect the debug server signals if called from the eric5 IDE
         if self.dbs:
             self.dbs.utPrepared.connect(self.__UTPrepared)
@@ -200,7 +212,10 @@
         """
         if self.dbs:
             exts = self.dbs.getExtensions("Python2")
-            if txt.endswith(exts):
+            flags = Utilities.extractFlagsFromFile(txt)
+            if txt.endswith(exts) or \
+               ("FileType" in flags and
+                flags["FileType"] in ["Python", "Python2"]):
                 self.coverageCheckBox.setChecked(False)
                 self.coverageCheckBox.setEnabled(False)
                 self.localCheckBox.setChecked(False)
@@ -220,11 +235,15 @@
             self.on_startButton_clicked()
         elif button == self.stopButton:
             self.on_stopButton_clicked()
+        elif button == self.startFailedButton:
+            self.on_startButton_clicked(failedOnly=True)
         
     @pyqtSlot()
-    def on_startButton_clicked(self):
+    def on_startButton_clicked(self, failedOnly=False):
         """
         Public slot to start the test.
+        
+        @keyparam failedOnly flag indicating to run only failed tests (boolean)
         """
         if self.running:
             return
@@ -266,7 +285,13 @@
                     clientType = "Python2"
                 else:
                     clientType = ""
+            if failedOnly and self.__failedTests:
+                failed = [t.split(".", 1)[1] for t in self.__failedTests]
+            else:
+                failed = []
+            self.__failedTests = []
             self.dbs.remoteUTPrepare(prog, self.testName, testFunctionName,
+                failed,
                 self.coverageCheckBox.isChecked(), mainScript,
                 self.coverageEraseCheckBox.isChecked(), clientType=clientType)
         else:
@@ -285,8 +310,13 @@
             try:
                 module = __import__(self.testName)
                 try:
-                    test = unittest.defaultTestLoader.loadTestsFromName(
-                        testFunctionName, module)
+                    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)
             except:
@@ -318,6 +348,7 @@
             
             self.testResult = QtTestResult(self)
             self.totalTests = test.countTestCases()
+            self.__failedTests = []
             self.__setRunningMode()
             if cover:
                 cover.start()
@@ -420,8 +451,14 @@
         self.running = False
         
         self.startButton.setEnabled(True)
+        self.startFailedButton.setEnabled(bool(self.__failedTests))
         self.stopButton.setEnabled(False)
-        self.startButton.setDefault(True)
+        if self.__failedTests:
+            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.trUtf8("Ran {0} test in {1:.3f}s")
                 .format(self.runCount, self.timeTaken))
@@ -429,39 +466,46 @@
             self.sbLabel.setText(self.trUtf8("Ran {0} tests in {1:.3f}s")
                 .format(self.runCount, self.timeTaken))
         self.progressLed.off()
+        
+        self.unittestStopped.emit()
 
-    def testFailed(self, test, exc):
+    def testFailed(self, test, exc, id):
         """
         Public method called if a test fails.
         
-        @param test name of the failed test (string)
+        @param test name of the test (string)
         @param exc string representation of the exception (string)
+        @param id id of the test (string)
         """
         self.failCount += 1
         self.progressCounterFailureCount.setText(str(self.failCount))
         itm = QListWidgetItem(self.trUtf8("Failure: {0}").format(test))
         itm.setData(Qt.UserRole, (test, exc))
         self.errorsListWidget.insertItem(0, itm)
+        self.__failedTests.append(id)
         
-    def testErrored(self, test, exc):
+    def testErrored(self, test, exc, id):
         """
         Public method called if a test errors.
         
-        @param test name of the failed test (string)
+        @param test name of the test (string)
         @param exc string representation of the exception (string)
+        @param id id of the test (string)
         """
         self.errorCount += 1
         self.progressCounterErrorCount.setText(str(self.errorCount))
         itm = QListWidgetItem(self.trUtf8("Error: {0}").format(test))
         itm.setData(Qt.UserRole, (test, exc))
         self.errorsListWidget.insertItem(0, itm)
+        self.__failedTests.append(id)
         
-    def testSkipped(self, test, reason):
+    def testSkipped(self, test, reason, id):
         """
         Public method called if a test was skipped.
         
-        @param test name of the failed test (string)
+        @param test name of the test (string)
         @param reason reason for skipping the test (string)
+        @param id id of the test (string)
         """
         self.skippedCount += 1
         self.progressCounterSkippedCount.setText(str(self.skippedCount))
@@ -469,12 +513,13 @@
         itm.setForeground(Qt.blue)
         self.testsListWidget.insertItem(1, itm)
         
-    def testFailedExpected(self, test, exc):
+    def testFailedExpected(self, test, exc, id):
         """
-        Public method called if a test fails expected.
+        Public method called if a test fails expectedly.
         
-        @param test name of the failed test (string)
+        @param test name of the test (string)
         @param exc string representation of the exception (string)
+        @param id id of the test (string)
         """
         self.expectedFailureCount += 1
         self.progressCounterExpectedFailureCount.setText(str(self.expectedFailureCount))
@@ -482,11 +527,12 @@
         itm.setForeground(Qt.blue)
         self.testsListWidget.insertItem(1, itm)
         
-    def testSucceededUnexpected(self, test):
+    def testSucceededUnexpected(self, test, id):
         """
         Public method called if a test succeeds unexpectedly.
         
-        @param test name of the failed test (string)
+        @param test name of the test (string)
+        @param id id of the test (string)
         """
         self.unexpectedSuccessCount += 1
         self.progressCounterUnexpectedSuccessCount.setText(
@@ -586,6 +632,14 @@
         if fmatch:
             fn, ln = fmatch.group(1, 2)
             self.unittestFile.emit(fn, int(ln), 1)
+    
+    def hasFailedTests(self):
+        """
+        Public method to check, if there are failed tests from the last run.
+        
+        @return flag indicating the presence of failed tests (boolean)
+        """
+        return bool(self.__failedTests)
 
 
 class QtTestResult(unittest.TestResult):
@@ -612,7 +666,7 @@
         """
         super().addFailure(test, err)
         tracebackLines = self._exc_info_to_string(err, test)
-        self.parent.testFailed(str(test), tracebackLines)
+        self.parent.testFailed(str(test), tracebackLines, test.id())
         
     def addError(self, test, err):
         """
@@ -623,7 +677,7 @@
         """
         super().addError(test, err)
         tracebackLines = self._exc_info_to_string(err, test)
-        self.parent.testErrored(str(test), tracebackLines)
+        self.parent.testErrored(str(test), tracebackLines, test.id())
         
     def addSkip(self, test, reason):
         """
@@ -633,7 +687,7 @@
         @param reason reason for skipping the test (string)
         """
         super().addSkip(test, reason)
-        self.parent.testSkipped(str(test), reason)
+        self.parent.testSkipped(str(test), reason, test.id())
         
     def addExpectedFailure(self, test, err):
         """
@@ -644,7 +698,7 @@
         """
         super().addExpectedFailure(test, err)
         tracebackLines = self._exc_info_to_string(err, test)
-        self.parent.testFailedExpected(str(test), tracebackLines)
+        self.parent.testFailedExpected(str(test), tracebackLines, test.id())
         
     def addUnexpectedSuccess(self, test):
         """
@@ -653,7 +707,7 @@
         @param test reference to the test object
         """
         super().addUnexpectedSuccess(test)
-        self.parent.testSucceededUnexpected(str(test))
+        self.parent.testSucceededUnexpected(str(test), test.id())
         
     def startTest(self, test):
         """

eric ide

mercurial