Commits

Anonymous committed da26302

Added printing of tracebacks

Comments (0)

Files changed (8)

examples/frobber.py

 #   describe that has been frazzled
 #     - should not be ready to frob
 #     - should be ready to frob -> FAIL (AssertionError @ 28)
+#
+# Traceback (most recent call last):
+#   File "$ROOT/examples/frobber.py", line 28, in should_be_ready_to_frob
+#     assert frobber.ready_to_frob
+# AssertionError
+#
 #   describe that has been frobbed
 #     - should have spam
 # Specs failed
 import unittest
 from optparse import OptionParser
 import sys
-from itertools import chain
+from itertools import chain, count
 import re
+import traceback
+
 from mote.localfunctions import LocalFunctions
 
 
         try:
             case_function()
         except Exception, e:
-            exc_type, exc_value, traceback = sys.exc_info()
-            self.exception_line = traceback.tb_next.tb_lineno
+            self.failure = Failure(sys.exc_info())
             self.success = False
             self.exception = e
         else:
             self.success = True
 
 
+class Failure:
+    def __init__(self, exc_info):
+        self.exc_type, self.exc_value, self.exc_traceback = exc_info
+        self.exception_line = self.exc_traceback.tb_next.tb_lineno
+
+    def format_exception(self):
+        traceback_lines = traceback.format_exception(
+            self.exc_type,
+            self.exc_value,
+            self.exc_traceback.tb_next)
+        return '\n%s\n' % ''.join(traceback_lines)
+
+
 class PythonFilesInDirectory(list):
     def __init__(self, parent):
         if not os.path.isdir(parent):
 
     def _failing_case_status(self, case):
         exception_name = case.exception.__class__.__name__
-        exception_line = case.exception_line
-        return ' -> FAIL (%s @ %i)' % (exception_name, exception_line)
+        failure = case.failure
+        return ' -> FAIL (%s @ %i)' % (exception_name,
+                                       failure.exception_line)
 
     def _print_cases(self, cases, padding):
+        failure_numbers = count(1)
+
         for case in cases:
-            result = '' if case.success else self._failing_case_status(case)
+            if case.success:
+                result = ''
+            else:
+                case.failure.number = failure_numbers.next()
+                result = self._failing_case_status(case)
+
             sys.stdout.write('%s  - %s%s\n' % (padding,
-                                             case.pretty_name,
-                                             result))
+                                               case.pretty_name,
+                                               result))
+
+            if not case.success:
+                sys.stdout.write(case.failure.format_exception())
 
     def _print_contexts(self, contexts, padding):
         for context in contexts:
 
 if __name__ == '__main__':
     nose_args = sys.argv + [r'-m',
-                            r'((?:^|[b_.-])(:?[Tt]est|When|should))']
+                            r'((?:^|[b_.-])(:?[Tt]est|Describe|When|should))']
     nose.run(argv=nose_args)
 

tests/system/test_examples.py

         output_lines = file_contents[output_marker + 2:]
         output_lines = [line[2:] for line in output_lines]
         output = '\n'.join(output_lines)
+        output = output.replace('$ROOT', os.getcwd())
         self._assert_output_equals(output)
 
     def should_match_output_in_example_comments(self):

tests/system/test_mote.py

         self._assert_fails()
 
     def should_output_spec_with_failures(self):
-        expected = dedent(
-            '''\
-            describe integers incorrectly
-              - should add correctly
-              - should add incorrectly -> FAIL (AssertionError @ 5)
-            Specs failed
-            ''')
-        self._assert_output_equals(expected)
+        output = self._output()
+        assert 'should add incorrectly -> FAIL (AssertionError @ 5)' in output
 
 
 class WhenRunningMote(SystemTest):

tests/unit/test_case.py

     def should_store_exception(self):
         assert isinstance(self.case.exception, AssertionError)
 
-    def should_store_line_number(self):
-        assert self.case.exception_line == 3
+    def should_create_failure(self):
+        assert mote.Failure.calls('()', mote.sys.exc_info.return_value).one()
 
+    def should_store_failure_globally(self):
+        assert self.case.failure is mote.Failure.return_value
+

tests/unit/test_failure.py

+from dingus import Dingus, DingusTestCase, exception_raiser, DontCare
+import mote as mod
+from mote import Failure
+
+
+class DescribeFailure(DingusTestCase(Failure)):
+    def setup(self):
+        super(DescribeFailure, self).setup()
+        exc_info = Dingus.many(3)
+        self.exc_type, self.exc_value, self.tb = exc_info
+        mod.traceback.format_exception.return_value = ['tb-line-1',
+                                                       'tb-line-2']
+        self.failure = Failure(exc_info)
+
+    def should_have_traceback_line_number(self):
+        assert self.failure.exception_line is self.tb.tb_next.tb_lineno
+
+    def should_format_exception(self):
+        self.failure.format_exception()
+        traceback_start = self.tb.tb_next
+        assert mod.traceback.calls('format_exception',
+                                   self.exc_type,
+                                   self.exc_value,
+                                   traceback_start).one()
+
+    def should_return_formatted_exception(self):
+        formatted_exception = mod.traceback.format_exception.return_value
+        assert self.failure.format_exception() == '\ntb-line-1tb-line-2\n'
+

tests/unit/test_result_printer.py

 from mote import ResultPrinter
 
 
-class WithPatchedStdOut(DingusTestCase(ResultPrinter)):
+class WithPatchedStdOut(DingusTestCase(ResultPrinter, 'count')):
     def setup(self):
         super(WithPatchedStdOut, self).setup()
         mote.sys = Dingus()
 class WhenCasesFail(WithPatchedStdOut):
     def setup(self):
         super(WhenCasesFail, self).setup()
-        self.case = Dingus(pretty_name='should frob',
-                           success=False,
-                           exception=AssertionError(),
-                           exception_line=3)
+        self.case1 = Dingus(pretty_name='should 1',
+                            success=False,
+                            exception=AssertionError(),
+                            failure=Dingus(exception_line=3))
+        self.case2 = Dingus(pretty_name='should 2',
+                            success=False,
+                            exception=AssertionError(),
+                            failure=Dingus(exception_line=5))
         self.context = Dingus(pretty_name='describe frobber',
-                              cases=[self.case],
+                              cases=[self.case1, self.case2],
                               contexts=[])
         ResultPrinter([self.context])
 
     def should_print_failure_message(self):
-        assert self._wrote('  - should frob -> FAIL (AssertionError @ 3)\n')
+        assert self._wrote('  - should 1 -> FAIL (AssertionError @ 3)\n')
+        assert self._wrote('  - should 2 -> FAIL (AssertionError @ 5)\n')
+
+    def should_number_failures(self):
+        assert self.case1.failure.number == 1
+        assert self.case2.failure.number == 2
 
 
 class WhenCasesAreInNestedContexts(WithPatchedStdOut):