testtools: Import new upstream snapshot.
authorJelmer Vernooij <jelmer@samba.org>
Thu, 30 Sep 2010 07:18:01 +0000 (09:18 +0200)
committerJelmer Vernooij <jelmer@samba.org>
Thu, 30 Sep 2010 07:18:01 +0000 (09:18 +0200)
lib/testtools/MANUAL
lib/testtools/NEWS
lib/testtools/testtools/__init__.py
lib/testtools/testtools/testcase.py
lib/testtools/testtools/tests/test_testtools.py

index db213669c9ad5b11f66d15af8a5dba2680c07bf0..1a43e70f23713fd85d51a93e1da873f9badaee4a 100644 (file)
@@ -42,6 +42,11 @@ logic in a try/finally block or tearDown method.  e.g.::
         self.addCleanup(foo.unlock)
         ...
 
+Cleanups can also report multiple errors, if appropriate by wrapping them in
+a testtools.MultipleExceptions object::
+
+    raise MultipleExceptions(exc_info1, exc_info2)
+
 
 TestCase.addOnException
 ~~~~~~~~~~~~~~~~~~~~~~~
index 596df0d6a6b7ce13af84753d3c61f447d32c719b..89b942fdbe37bf50306e741bb0c0d0366c602680 100644 (file)
@@ -7,6 +7,10 @@ NEXT
 Improvements
 ------------
 
+* Cleanups can raise ``testtools.MultipleExceptions`` if they have multiple
+  exceptions to report. For instance, a cleanup which is itself responsible for
+  running several different internal cleanup routines might use this.
+
 * Code duplication between assertEqual and the matcher Equals has been removed.
 
 * In normal circumstances, a TestCase will no longer share details with clones
index b1c9b661ed4be06abf3d94e7b313a9e462ba6760..2b76a5eef7ff0556ee213df66f7d4bdeb060681b 100644 (file)
@@ -8,6 +8,7 @@ __all__ = [
     'ErrorHolder',
     'ExtendedToOriginalDecorator',
     'iterate_tests',
+    'MultipleExceptions',
     'MultiTestResult',
     'PlaceHolder',
     'TestCase',
@@ -28,6 +29,7 @@ from testtools.runtest import (
     )
 from testtools.testcase import (
     ErrorHolder,
+    MultipleExceptions,
     PlaceHolder,
     TestCase,
     clone_test_with_new_id,
index 959c129691b040cd82b95008bccbb71b25a9cec6..573cd84dc2f4127a23c72ece37cc535d87557a8b 100644 (file)
@@ -5,6 +5,7 @@
 __metaclass__ = type
 __all__ = [
     'clone_test_with_new_id',
+    'MultipleExceptions',
     'TestCase',
     'skip',
     'skipIf',
@@ -60,6 +61,13 @@ except ImportError:
         """
 
 
+class MultipleExceptions(Exception):
+    """Represents many exceptions raised from some operation.
+
+    :ivar args: The sys.exc_info() tuples for each exception.
+    """
+
+
 class TestCase(unittest.TestCase):
     """Extensions to the basic TestCase.
 
@@ -188,9 +196,14 @@ class TestCase(unittest.TestCase):
             except KeyboardInterrupt:
                 raise
             except:
-                exc_info = sys.exc_info()
-                self._report_traceback(exc_info)
-                last_exception = exc_info[1]
+                exceptions = [sys.exc_info()]
+                while exceptions:
+                    exc_info = exceptions.pop()
+                    if exc_info[0] is MultipleExceptions:
+                        exceptions.extend(exc_info[1].args)
+                        continue
+                    self._report_traceback(exc_info)
+                    last_exception = exc_info[1]
         return last_exception
 
     def addCleanup(self, function, *arguments, **keywordArguments):
index 5dfb3559905dda2f8fceff26c9060de347214651..8e253e63117cb5684b566d02ca71c50a615f113c 100644 (file)
@@ -8,6 +8,7 @@ import unittest
 
 from testtools import (
     ErrorHolder,
+    MultipleExceptions,
     PlaceHolder,
     TestCase,
     clone_test_with_new_id,
@@ -608,6 +609,27 @@ class TestAddCleanup(TestCase):
         self.assertRaises(
             KeyboardInterrupt, self.test.run, self.logging_result)
 
+    def test_all_errors_from_MultipleExceptions_reported(self):
+        # When a MultipleExceptions exception is caught, all the errors are
+        # reported.
+        def raiseMany():
+            try:
+                1/0
+            except Exception:
+                exc_info1 = sys.exc_info()
+            try:
+                1/0
+            except Exception:
+                exc_info2 = sys.exc_info()
+            raise MultipleExceptions(exc_info1, exc_info2)
+        self.test.addCleanup(raiseMany)
+        self.logging_result = ExtendedTestResult()
+        self.test.run(self.logging_result)
+        self.assertEqual(['startTest', 'addError', 'stopTest'],
+            [event[0] for event in self.logging_result._events])
+        self.assertEqual(set(['traceback', 'traceback-1']),
+            set(self.logging_result._events[1][2].keys()))
+
     def test_multipleCleanupErrorsReported(self):
         # Errors from all failing cleanups are reported as separate backtraces.
         self.test.addCleanup(lambda: 1/0)