[Zope3-checkins] SVN: zope.testing/trunk/src/zope/testing/testrunner Finished debugging support, including tests.

Jim Fulton jim at zope.com
Tue Jun 28 09:59:51 EDT 2005


Log message for revision 30942:
  Finished debugging support, including tests.
  

Changed:
  A   zope.testing/trunk/src/zope/testing/testrunner-edge-cases.txt
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem5.txt
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem6.txt
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/sampletests_d.py
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace5.txt
  A   zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace6.txt
  U   zope.testing/trunk/src/zope/testing/testrunner.py
  U   zope.testing/trunk/src/zope/testing/testrunner.txt

-=-
Added: zope.testing/trunk/src/zope/testing/testrunner-edge-cases.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-edge-cases.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-edge-cases.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,355 @@
+testrunner Edge Cases
+=====================
+
+This document has some edge-case examples to test various aspects of
+the test runner.
+
+    >>> import os, sys
+    >>> directory_with_tests = os.path.join(this_directory, 'testrunner-ex')
+    >>> sys.path.append(directory_with_tests)
+
+    >>> from zope.testing import testrunner
+
+    >>> defaults = [
+    ...     '--path', directory_with_tests,
+    ...     '--tests-pattern', '^sampletestsf?$',
+    ...     ]
+
+Debugging
+---------
+
+    >>> class Input:
+    ...     def __init__(self, src):
+    ...         self.lines = src.split('\n')
+    ...     def readline(self):
+    ...         line = self.lines.pop(0)
+    ...         print line
+    ...         return line+'\n'
+
+    >>> real_stdin = sys.stdin
+
+Using pdb.set_trace in a function called by an ordinary test:
+
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace2').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > /usr/local/python/2.3.5/lib/python2.3/pdb.py(992)set_trace()->None
+    -> Pdb().set_trace()
+    (Pdb) n
+    > testrunner-ex/sample3/sampletests_d.py(42)f()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.001 seconds.
+
+Using pdb.set_trace in a function called by a doctest in a doc string:
+
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace4').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > doctest.py(351)set_trace()->None
+    -> pdb.Pdb.set_trace(self)
+    (Pdb) n
+    > testrunner-ex/sample3/sampletests_d.py(42)f()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.002 seconds.
+
+Using pdb in a docstring-based doctest
+
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace3').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > doctest.py(351)set_trace()->None
+    -> pdb.Pdb.set_trace(self)
+    (Pdb) n
+    > <doctest sample3.sampletests_d.set_trace3[1]>(3)?()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.002 seconds.
+
+Using pdb.set_trace in a doc file:
+
+
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace5').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > doctest.py(351)set_trace()->None
+    -> pdb.Pdb.set_trace(self)
+    (Pdb) n
+    > <doctest set_trace5.txt[1]>(3)?()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.002 seconds.
+
+
+Using pdb.set_trace in a function called by a doctest in a doc file:
+
+
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace6').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > doctest.py(351)set_trace()->None
+    -> pdb.Pdb.set_trace(self)
+    (Pdb) n
+    > testrunner-ex/sample3/sampletests_d.py(42)f()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.002 seconds.
+
+Post-mortem debugging function called from ordinary test:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem2 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test test_post_mortem2 (sample3.sampletests_d.TestSomething)
+    Traceback (most recent call last):
+      File "testrunner-ex/sample3/sampletests_d.py", 
+           line 37, in test_post_mortem2
+        g()
+      File "testrunner-ex/sample3/sampletests_d.py", line 46, in g
+        raise ValueError
+    ValueError
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > testrunner-ex/sample3/sampletests_d.py(46)g()
+    -> raise ValueError
+    (Pdb) p x
+    1
+    (Pdb) c
+
+
+Post-mortem debugging docstring-based doctest:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem3 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test post_mortem3 (sample3.sampletests_d)
+    Traceback (most recent call last):
+      File "zope/testing/doctest.py", line 2276, in debug
+        runner.run(self._dt_test)
+      File "zope/testing/doctest.py", line 1731, in run
+        r = DocTestRunner.run(self, test, compileflags, out, False)
+      File "zope/testing/doctest.py", line 1389, in run
+        return self.__run(test, compileflags, out)
+      File "zope/testing/doctest.py", line 1310, in __run
+        exc_info)
+      File "zope/testing/doctest.py", line 1737, in report_unexpected_exception
+        raise UnexpectedException(test, example, exc_info)
+    UnexpectedException: 
+       from testrunner-ex/sample3/sampletests_d.py:61 (2 examples)>
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > <doctest sample3.sampletests_d.post_mortem3[1]>(1)?()
+    (Pdb) p x
+    1
+    (Pdb) c
+
+Post-mortem debugging function called from docstring-based doctest:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem4 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test post_mortem4 (sample3.sampletests_d)
+    Traceback (most recent call last):
+      File "zope/testing/doctest.py", line 2276, in debug
+        runner.run(self._dt_test)
+      File "zope/testing/doctest.py", line 1731, in run
+        r = DocTestRunner.run(self, test, compileflags, out, False)
+      File "zope/testing/doctest.py", line 1389, in run
+        return self.__run(test, compileflags, out)
+      File "zope/testing/doctest.py", line 1310, in __run
+        exc_info)
+      File "zope/testing/doctest.py", line 1737, in report_unexpected_exception
+        raise UnexpectedException(test, example, exc_info)
+    UnexpectedException: testrunner-ex/sample3/sampletests_d.py:67 (1 example)>
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > testrunner-ex/sample3/sampletests_d.py(46)g()
+    -> raise ValueError
+    (Pdb) p x
+    1
+    (Pdb) c
+
+Post-mortem debugging file-based doctest:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem5 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test zope/testing/testrunner-ex/sample3/post_mortem5.txt
+    Traceback (most recent call last):
+      File "zope/testing/doctest.py", line 2276, in debug
+        runner.run(self._dt_test)
+      File "zope/testing/doctest.py", line 1731, in run
+        r = DocTestRunner.run(self, test, compileflags, out, False)
+      File "zope/testing/doctest.py", line 1389, in run
+        return self.__run(test, compileflags, out)
+      File "zope/testing/doctest.py", line 1310, in __run
+        exc_info)
+      File "zope/testing/doctest.py", line 1737, in report_unexpected_exception
+        raise UnexpectedException(test, example, exc_info)
+    UnexpectedException: testrunner-ex/sample3/post_mortem5.txt:0 (2 examples)>
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > <doctest post_mortem5.txt[1]>(1)?()
+    (Pdb) p x
+    1
+    (Pdb) c
+
+
+Post-mortem debugging function called from file-based doctest:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem6 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test zope/testing/testrunner-ex/sample3/post_mortem6.txt
+    Traceback (most recent call last):
+      File "zope/testing/doctest.py", line 2276, in debug
+        runner.run(self._dt_test)
+      File "zope/testing/doctest.py", line 1731, in run
+        r = DocTestRunner.run(self, test, compileflags, out, False)
+      File "zope/testing/doctest.py", line 1389, in run
+        return self.__run(test, compileflags, out)
+      File "zope/testing/doctest.py", line 1310, in __run
+        exc_info)
+      File "zope/testing/doctest.py", line 1737, in report_unexpected_exception
+        raise UnexpectedException(test, example, exc_info)
+    UnexpectedException: testrunner-ex/sample3/post_mortem6.txt:0 (2 examples)>
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > testrunner-ex/sample3/sampletests_d.py(46)g()
+    -> raise ValueError
+    (Pdb) p x
+    1
+    (Pdb) c
+
+Post-mortem debugging of a docstring doctest failure:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem_failure2 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test post_mortem_failure2 (sample3.sampletests_d)
+    <BLANKLINE>
+    File "testrunner-ex/sample3/sampletests_d.py", 
+                   line 81, in sample3.sampletests_d.post_mortem_failure2
+    <BLANKLINE>
+    x
+    <BLANKLINE>
+    Want:
+    2
+    <BLANKLINE>
+    Got:
+    1
+    <BLANKLINE>
+    <BLANKLINE>
+    > testrunner-ex/sample3/sampletests_d.py(81)_()
+    exceptions.ValueError:
+    Expected and actual output are different
+    > <string>(1)?()
+    (Pdb) p x
+    1
+    (Pdb) c
+
+
+Post-mortem debugging of a docfile doctest failure:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem_failure.txt -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test /home/jim/z3/zope.testing/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt
+    <BLANKLINE>
+    File "testrunner-ex/sample3/post_mortem_failure.txt",
+                                      line 2, in post_mortem_failure.txt
+    <BLANKLINE>
+    x
+    <BLANKLINE>
+    Want:
+    2
+    <BLANKLINE>
+    Got:
+    1
+    <BLANKLINE>
+    <BLANKLINE>
+    > testrunner-ex/sample3/post_mortem_failure.txt(2)_()
+    exceptions.ValueError:
+    Expected and actual output are different
+    > <string>(1)?()
+    (Pdb) p x
+    1
+    (Pdb) c


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-edge-cases.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem5.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem5.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem5.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,2 @@
+    >>> x = 1
+    >>> raise ValueError


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem5.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem6.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem6.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem6.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,2 @@
+    >>> from sample3.sampletests_d import g
+    >>> g()


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem6.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,3 @@
+    >>> x = 1
+    >>> x
+    2


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/post_mortem_failure.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/sampletests_d.py
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/sampletests_d.py	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/sampletests_d.py	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,99 @@
+##############################################################################
+#
+# Copyright (c) 2004 Zope Corporation and Contributors.
+# All Rights Reserved.
+#
+# This software is subject to the provisions of the Zope Public License,
+# Version 2.0 (ZPL).  A copy of the ZPL should accompany this distribution.
+# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
+# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
+# FOR A PARTICULAR PURPOSE.
+#
+##############################################################################
+"""XXX short summary goes here.
+
+$Id$
+"""
+
+import unittest
+from zope.testing import doctest
+
+class TestSomething(unittest.TestCase):
+
+    def test_set_trace1(self):
+        x = 1
+        import pdb; pdb.set_trace()
+        y = x
+
+    def test_set_trace2(self):
+        f()
+
+    def test_post_mortem1(self):
+        x = 1
+        raise ValueError
+
+    def test_post_mortem2(self):
+        g()
+
+    def test_post_mortem_failure1(self):
+        x = 1
+        y = 2
+        self.assertEqual(x, y)
+
+def f():
+    x = 1
+    import pdb; pdb.set_trace()
+    y = x
+
+def g():
+    x = 1
+    raise ValueError
+
+def set_trace3(self):
+    """
+    >>> x = 1
+    >>> if 1:
+    ...     import pdb; pdb.set_trace()
+    ...     y = x
+    """
+
+def set_trace4(self):
+    """
+    >>> f()
+    """
+
+def post_mortem3(self):
+    """
+    >>> x = 1
+    >>> raise ValueError
+    """
+
+def post_mortem4(self):
+    """
+    >>> g()
+    """
+
+
+def post_mortem_failure2():
+    """
+    >>> x = 1
+    >>> x
+    2
+    """
+
+
+def test_suite():
+    return unittest.TestSuite((
+        doctest.DocTestSuite(),
+        unittest.makeSuite(TestSomething),
+        doctest.DocFileSuite('set_trace5.txt'),
+        doctest.DocFileSuite('set_trace6.txt'),
+        doctest.DocFileSuite('post_mortem5.txt'),
+        doctest.DocFileSuite('post_mortem6.txt'),
+        doctest.DocFileSuite('post_mortem_failure.txt'),
+        ))
+
+if __name__ == '__main__':
+    unittest.main(defaultTest='test_suite')
+


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/sampletests_d.py
___________________________________________________________________
Name: svn:keywords
   + Id
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace5.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace5.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace5.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,4 @@
+    >>> x = 1
+    >>> if 1:
+    ...     import pdb; pdb.set_trace()
+    ...     y = x


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace5.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Added: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace6.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace6.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace6.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -0,0 +1,2 @@
+    >>> from sample3.sampletests_d import f
+    >>> f()


Property changes on: zope.testing/trunk/src/zope/testing/testrunner-ex/sample3/set_trace6.txt
___________________________________________________________________
Name: svn:eol-style
   + native

Modified: zope.testing/trunk/src/zope/testing/testrunner.py
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner.py	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner.py	2005-06-28 13:59:49 UTC (rev 30942)
@@ -32,13 +32,27 @@
 import threading
 import unittest
 
+real_pdb_set_trace = pdb.set_trace
 
+class EndRun(Exception):
+    """Indicate that the existing run call should stop
+
+    Used to prevent additional test output after post-mortem debugging.
+    """
+
 def run(defaults=None, args=None):
     # Control reporting flags during run
     old_reporting_flags = doctest.set_unittest_reportflags(0)
+
+    # Make sure we start with real pdb.set_trace.  This is needed
+    # to make tests of the test runner work properly. :)
+    pdb.set_trace = real_pdb_set_trace
     
     options = get_options(args, defaults)
-    run_with_options(options)
+    try:
+        run_with_options(options)
+    except EndRun:
+        pass
 
     doctest.set_unittest_reportflags(old_reporting_flags)
 
@@ -152,7 +166,23 @@
             print '    ',
         result = TestResult(options, tests)
         t = time.time()
-        tests(result)
+
+
+        if options.post_mortem:
+            # post-mortem debugging
+            for test in tests:
+                try:
+                    test.debug()
+                except:
+                    result.addError(
+                        test,
+                        sys.exc_info()[:2] + (sys.exc_info()[2].tb_next, ),
+                        )
+                else:
+                    result.addSuccess(test)
+        else:
+            # normal
+            tests(result)
         t = time.time() - t
         if options.verbose == 1 or options.progress:
             print
@@ -281,6 +311,7 @@
 
     def addFailure(self, test, exc_info):
 
+
         if self.options.verbose > 2:
             print " (%.3f ms)" % (time.time() - self._start_time)
 
@@ -324,6 +355,16 @@
     def _print_traceback(self, msg, exc_info):
         print_traceback(msg, exc_info)
 
+doctest_template = """
+File "%s", line %s, in %s
+
+%s
+Want:
+%s
+Got:
+%s
+"""
+
 def print_traceback(msg, exc_info):
     print
     print msg
@@ -331,6 +372,15 @@
     v = exc_info[1]
     if isinstance(v, doctest.DocTestFailureException):
         tb = v.args[0]
+    elif isinstance(v, doctest.DocTestFailure):
+        tb = doctest_template % (
+            v.test.filename,
+            v.test.lineno + v.example.lineno + 1,
+            v.test.name,
+            v.example.source,
+            v.example.want,
+            v.got,
+            )
     else:
         tb = "".join(traceback.format_exception(*exc_info))
         
@@ -361,7 +411,7 @@
     print "%s:" % (exc_info[0], )
     print exc_info[1]
     pdb.post_mortem(exc_info[2])
-    sys.exit()
+    raise EndRun
 
 def print_doctest_location(err):
     # This mimicks pdb's output, which gives way cool results in emacs :)
@@ -972,13 +1022,18 @@
         (re.compile(r'\d+[.]\d\d\d ms'), 'N.NNN ms'),
         (re.compile('( |")[^\n]+testrunner-ex'), r'\1testrunner-ex'),
         (re.compile('( |")[^\n]+testrunner.py'), r'\1testrunner.py'),
+        (re.compile('> [^\n]*(doc|unit)test[.]py\(\d+\)'),
+                       r'\1doctest.py(NNN)'),
+        (re.compile('[.]py\(\d+\)'), r'.py(NNN)'),
+        (re.compile('[.]py:\d+'), r'.py:NNN'),
+        (re.compile(' line \d+,', re.IGNORECASE), r' Line NNN,'),
 
         # omit traceback entries for unittest.py or doctest.py from
         # output:
-        (re.compile(r'\n +File "[^\n]+(doc|unit)test.py", [^\n]+\n[^\n]+\n'),
-         r'\n'),
+        (re.compile(r'^ +File "[^\n]+(doc|unit)test.py", [^\n]+\n[^\n]+\n',
+                    re.MULTILINE),
+         r''),
 
-
         ])
 
     def setUp(test):
@@ -988,7 +1043,7 @@
     def tearDown(test):
         sys.path, sys.argv = test.globs['saved-sys-info']
 
-    return doctest.DocFileSuite('testrunner.txt',
+    return doctest.DocFileSuite('testrunner.txt', 'testrunner-edge-cases.txt',
                                 setUp=setUp, tearDown=tearDown,
                                 checker=checker)
 
@@ -1000,6 +1055,10 @@
     run(default)
 
 if __name__ == '__main__':
+    # Hm, when run as a script, we need to import the testrunner under
+    # it's own name, so that there's the imported flavor has the right
+    # real_pdb_set_trace.
+    import zope.testing.testrunner
     main()
 
 # Test the testrunner

Modified: zope.testing/trunk/src/zope/testing/testrunner.txt
===================================================================
--- zope.testing/trunk/src/zope/testing/testrunner.txt	2005-06-28 12:18:49 UTC (rev 30941)
+++ zope.testing/trunk/src/zope/testing/testrunner.txt	2005-06-28 13:59:49 UTC (rev 30942)
@@ -1496,3 +1496,106 @@
       sample2.sample21.sampletests_i
       sample2.sample22.sampletests_i
       sample2.sample23.sampletests_i
+
+Debugging
+---------
+
+The testrunner module supports post-mortem debugging and debugging
+using `pdb.set_trace`.  Let's look first at using `pdb.set_trace`.
+To demonstrate this, we'll provide input via helper Input objects:
+
+    >>> class Input:
+    ...     def __init__(self, src):
+    ...         self.lines = src.split('\n')
+    ...     def readline(self):
+    ...         line = self.lines.pop(0)
+    ...         print line
+    ...         return line+'\n'
+
+If a test or code called by a test calls pdb.set_trace, then the
+runner will enter pdb at that point:
+
+    >>> import sys
+    >>> real_stdin = sys.stdin
+    >>> sys.stdin = Input('n\np x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t set_trace1').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    Running unit tests:
+    --Return--
+    > /usr/local/python/2.3.5/lib/python2.3/pdb.py(992)set_trace()->None
+    -> Pdb().set_trace()
+    (Pdb) n
+    > testrunner-ex/sample3/sampletests_d.py(27)test_set_trace1()
+    -> y = x
+    (Pdb) p x
+    1
+    (Pdb) c
+      Ran 1 tests with 0 failures and 0 errors in 0.002 seconds.
+
+You can also do post-mortem debugging, using the --post-mortem (-D)
+option:
+
+    >>> sys.stdin = Input('p x\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem1 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test test_post_mortem1 (sample3.sampletests_d.TestSomething)
+    Traceback (most recent call last):
+      File "testrunner-ex/sample3/sampletests_d.py", 
+              line 34, in test_post_mortem1
+        raise ValueError
+    ValueError
+    <BLANKLINE>
+    exceptions.ValueError:
+    <BLANKLINE>
+    > testrunner-ex/sample3/sampletests_d.py(34)test_post_mortem1()
+    -> raise ValueError
+    (Pdb) p x
+    1
+    (Pdb) c
+
+Note that the test runner exits after post-mortem debugging (as
+indicated by the SystemExit above.
+
+In the example above, we debugged an error.  Failures are actually
+converted to errors and can be debugged the same way:
+
+    >>> sys.stdin = Input('up\np x\np y\nc')
+    >>> sys.argv = ('test -ssample3 --tests-pattern ^sampletests_d$'
+    ...             ' -t post_mortem_failure1 -D').split()
+    >>> try: testrunner.run(defaults)
+    ... finally: sys.stdin = real_stdin
+    ... # doctest: +NORMALIZE_WHITESPACE +REPORT_NDIFF
+    Running unit tests:
+    <BLANKLINE>
+    <BLANKLINE>
+    Error in test test_post_mortem_failure1 (sample3.sampletests_d.TestSomething)
+    Traceback (most recent call last):
+      File ".../unittest.py",  line 252, in debug
+        getattr(self, self.__testMethodName)()
+      File "testrunner-ex/sample3/sampletests_d.py",
+        line 42, in test_post_mortem_failure1
+        self.assertEqual(x, y)
+      File ".../unittest.py", line 302, in failUnlessEqual
+        raise self.failureException, \
+    AssertionError: 1 != 2
+    <BLANKLINE>
+    exceptions.AssertionError:
+    1 != 2
+    > .../unittest.py(302)failUnlessEqual()
+    -> raise self.failureException, \
+    (Pdb) up
+    > testrunner-ex/sample3/sampletests_d.py(42)test_post_mortem_failure1()
+    -> self.assertEqual(x, y)
+    (Pdb) p x
+    1
+    (Pdb) p y
+    2
+    (Pdb) c



More information about the Zope3-Checkins mailing list