[pypy-svn] rev 1103 - in pypy/trunk/src/pypy: interpreter tool

mwh at codespeak.net mwh at codespeak.net
Mon Jul 7 18:10:03 CEST 2003


Author: mwh
Date: Mon Jul  7 18:10:02 2003
New Revision: 1103

Added:
   pypy/trunk/src/pypy/tool/ppdb.py
   pypy/trunk/src/pypy/tool/testpm.py
Modified:
   pypy/trunk/src/pypy/interpreter/unittest_w.py
   pypy/trunk/src/pypy/tool/option.py
   pypy/trunk/src/pypy/tool/test.py
Log:
Some fairly extensive changes to the test rig.

There are two conceptual changes, but they are two textually entangled
to be easily checked in separately :-(

The first change is allowing a test to skip, by raising a
pypy.tool.test.TestSkip exception.

test.objspace() raises TestSkip if it is asked for a specific flavour
of object space and that differs from the specific flavour of object
space that is being tested (as controlled by command-line arguments).

This change is all of the changes to interpreter/unittest_w.py and
some of the work in tool/test.py.

The other change is a new option (-k, for no good reason other than -i
already being taken) that drops you into a interactive session when a
test run has failures.  From this you can list the errors ('l'), print
tracebacks ('tb') and drop into a pdb session for each failure ('d').
Blank lines and 'q' quit.

I've also added a sketal PyPy-specialized subclass of pdb.Pdb.


Modified: pypy/trunk/src/pypy/interpreter/unittest_w.py
==============================================================================
--- pypy/trunk/src/pypy/interpreter/unittest_w.py	(original)
+++ pypy/trunk/src/pypy/interpreter/unittest_w.py	Mon Jul  7 18:10:02 2003
@@ -57,6 +57,52 @@
 
 class IntTestCase(unittest.TestCase):
     """ enrich TestCase with wrapped-methods """
+    def __init__(self, methodName='runTest'):
+        self.methodName = methodName
+        unittest.TestCase.__init__(self, methodName)
+
+    def __call__(self, result=None):
+        from pypy.tool.test import TestSkip
+        if result is None: result = self.defaultTestResult()
+        result.startTest(self)
+        testMethod = getattr(self, self.methodName)
+        try:
+            try:
+                self.setUp()
+            except TestSkip: 
+                result.addSkip(self)
+                return
+            except KeyboardInterrupt:
+                raise
+            except:
+                result.addError(self, self._TestCase__exc_info())
+                return
+
+            ok = 0
+            try:
+                testMethod()
+                ok = 1
+            except self.failureException, e:
+                result.addFailure(self, self._TestCase__exc_info())
+            except TestSkip: 
+                result.addSkip(self)
+                return
+            except KeyboardInterrupt:
+                raise
+            except:
+                result.addError(self, self._TestCase__exc_info())
+
+            try:
+                self.tearDown()
+            except KeyboardInterrupt:
+                raise
+            except:
+                result.addError(self, self._TestCase__exc_info())
+                ok = 0
+            if ok: result.addSuccess(self)
+        finally:
+            result.stopTest(self)
+    
 
     def failUnless_w(self, w_condition, msg=None):
         condition = self.space.is_true(w_condition)
@@ -100,15 +146,11 @@
 
 
 class AppTestCase(IntTestCase):
-    def __init__(self, methodName='runTest'):
-        self.methodName = methodName
-        unittest.TestCase.__init__(self, methodName)
-
     def __call__(self, result=None):
         if type(getattr(self, self.methodName)) != WrappedFunc:
             setattr(self, self.methodName,
                 WrappedFunc(self, getattr(self, self.methodName)))
-        return unittest.TestCase.__call__(self, result)
+        return IntTestCase.__call__(self, result)
         
     def setUp(self):
         from pypy.tool import test

Modified: pypy/trunk/src/pypy/tool/option.py
==============================================================================
--- pypy/trunk/src/pypy/tool/option.py	(original)
+++ pypy/trunk/src/pypy/tool/option.py	Mon Jul  7 18:10:02 2003
@@ -40,8 +40,6 @@
     op = optik.OptionParser()
     op.add_options(optionlist)
     options, args = op.parse_args(argv, input_options)
-    if not input_options.spaces:
-        input_options.spaces.append(os.environ.get('OBJSPACE', 'trivial'))
     return args
 
 def objspace(name='', _spacecache={}):
@@ -49,12 +47,13 @@
 
     this is configured via the environment variable OBJSPACE
     """
+    
     if not name:
-        if hasattr(Options, 'spacename'):
-            name = Options.spacename
-        else:
+        if Options.spaces:
             name = Options.spaces[-1]
-
+        else:
+            name = os.environ.get('OBJSPACE', 'trivial')
+    
     try:
         return _spacecache[name]
     except KeyError:

Added: pypy/trunk/src/pypy/tool/ppdb.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/tool/ppdb.py	Mon Jul  7 18:10:02 2003
@@ -0,0 +1,22 @@
+# it had to happen: a customized version of pdb for pypy; Py Py
+# DeBugger, if you please.
+
+# i only plan to support post morterm debugging!  my head hurts if my
+# thoughts even go near any alternative!
+
+import pdb, sys
+
+class PPdb(pdb.Pdb):
+    def do_bta(self, line):
+        self.operr.print_application_traceback(self.space, sys.stdout)
+
+def post_mortem(space, t, operr):
+    # need app-level tb too?
+    p = PPdb()
+    p.reset()
+    p.space = space
+    p.operr = operr
+    while t.tb_next is not None:
+        t = t.tb_next
+    p.interaction(t.tb_frame, t)
+

Modified: pypy/trunk/src/pypy/tool/test.py
==============================================================================
--- pypy/trunk/src/pypy/tool/test.py	(original)
+++ pypy/trunk/src/pypy/tool/test.py	Mon Jul  7 18:10:02 2003
@@ -1,11 +1,10 @@
 import autopath
-import os, sys, unittest, re, warnings, unittest, traceback
+import os, sys, unittest, re, warnings, unittest, traceback, StringIO
 from unittest import TestCase, TestLoader
 
 import pypy.interpreter.unittest_w
 from pypy.tool.optik import make_option
-from pypy.tool import optik, option
-from pypy.tool.option import objspace
+from pypy.tool import optik, option, ppdb
 
 IntTestCase = pypy.interpreter.unittest_w.IntTestCase
 AppTestCase = pypy.interpreter.unittest_w.AppTestCase
@@ -13,21 +12,13 @@
 
 class MyTestSuite(unittest.TestSuite):
     def __call__(self, result):
-        """ execute the tests, invokes underlyning unittest.__call__"""
+        """ execute the tests, invokes underlying unittest.__call__"""
 
-        # XXX here is probably not the best place 
-        #     to check for test/objspace mismatch 
         count = self.countTestCases()
         if not count:
             return result
 
         fm = getattr(self, 'frommodule','')
-        for spacename  in ('std','trivial','ann'):
-            if fm and fm.startswith('pypy.objspace.' + spacename) and \
-                   Options.spacename != spacename:
-               sys.stderr.write("\n%s skip for objspace %r" % (
-                    fm, Options.spacename))
-               return result
 
         if fm and Options.verbose==0:
             sys.stderr.write('\n%s [%d]' %(fm, count))
@@ -60,21 +51,10 @@
         unittest.TestResult.addError(self, test, err)
     def addSuccess(self, test):
         self.successes.append(test)
+    def addSkip(self, test):
+        self.testsRun -= 1
 
 class MyTextTestResult(unittest._TextTestResult):
-
-    def munge(self, list, test, err):
-        import StringIO
-        from pypy.interpreter.baseobjspace import OperationError
-        text1 = list.pop()[1]
-        if isinstance(err[1], OperationError):
-            sio = StringIO.StringIO()
-            err[1].print_application_traceback(test.space, sio)
-            text2 = sio.getvalue()
-            
-            list.append((test, text1 + "\nand at app-level:\n\n" + text2))
-        else:
-            list.append((test, text1))
         
     def addError(self, test, err):
         from pypy.interpreter.baseobjspace import OperationError
@@ -83,11 +63,71 @@
                 self.addFailure(test, err)
                 return
         unittest._TextTestResult.addError(self, test, err)
-        self.munge(self.errors, test, err)
+        self.errors[-1] = (test, sys.exc_info())
         
     def addFailure(self, test, err):
         unittest._TextTestResult.addFailure(self, test, err)
-        self.munge(self.failures, test, err)
+        self.failures[-1] = (test, sys.exc_info())
+
+    def addSkip(self, test):
+        self.testsRun -= 1
+        if self.showAll:
+            self.stream.writeln("skipped")
+        elif self.dots:
+            self.stream.write('s')
+
+    def interact(self):
+        efs = self.errors + self.failures
+        from pypy.tool.testpm import TestPM
+        c = TestPM(efs)
+        c.cmdloop()
+        return
+        def proc_input(input):
+            r = int(input)
+            if r < 0 or r >= len(efs):
+                raise ValueError
+            return r
+        while 1:
+            i = 0
+            for t, e in efs:
+                print i, t.methodName
+                i += 1
+            while 1:
+                input = raw_input('itr> ')
+                if not input:
+                    return
+                try:
+                    r = proc_input(input)
+                except ValueError:
+                    continue
+                else:
+                    break
+            s, (t, v, tb) = efs[r]
+            ppdb.post_mortem(s.space, tb, v)
+
+    def printErrors(self):
+        if Options.interactive:
+            print
+            if self.errors or self.failures:
+                self.interact()
+        else:
+            unittest._TextTestResult.printErrors(self)
+
+    def printErrorList(self, flavour, errors):
+        from pypy.interpreter.baseobjspace import OperationError
+        for test, err in errors:
+            self.stream.writeln(self.separator1)
+            self.stream.writeln("%s: %s" % (flavour,self.getDescription(test)))
+            self.stream.writeln(self.separator2)
+            t1 = self._exc_info_to_string(err)
+            t2 = ''
+            if isinstance(err[1], OperationError):
+                t2 = '\nand at app-level:\n\n'
+                sio = StringIO.StringIO()
+                err[1].print_application_traceback(test.space, sio)
+                t2 += sio.getvalue()
+
+            self.stream.writeln("%s" % (t1 + t2,))
 
 class CtsTestRunner:
     def run(self, test):
@@ -197,11 +237,20 @@
     testreldir = 0
     runcts = 0
     spacename = ''
-    individualtime=0
+    individualtime = 0
+    interactive = 0
     def ensure_value(*args):
         return 0
     ensure_value = staticmethod(ensure_value)
 
+class TestSkip(Exception):
+    pass
+
+def objspace(name=''):
+    if name and Options.spacename and name != Options.spacename:
+        raise TestSkip
+    return option.objspace(name)
+
 class RegexFilterFunc:
     """ stateful function to filter included/excluded strings via
     a Regular Expression. 
@@ -237,6 +286,9 @@
         '-i', action="store_true", dest="individualtime",
         help="time each test individually"))
     options.append(make_option(
+        '-k', action="store_true", dest="interactive",
+        help="enter an interactive mode on failure or error"))
+    options.append(make_option(
         '-c', action="store_true", dest="runcts",
         help="run CtsTestRunner (catches stdout and prints report "
         "after testing) [unix only, for now]"))

Added: pypy/trunk/src/pypy/tool/testpm.py
==============================================================================
--- (empty file)
+++ pypy/trunk/src/pypy/tool/testpm.py	Mon Jul  7 18:10:02 2003
@@ -0,0 +1,79 @@
+# this file implements a little interactive loop that can be
+# optionally entered at the end of a test run to allow inspection (and
+# pdb-ing) of failures and/or errors.
+
+import autopath
+from pypy.tool import ppdb
+import cmd, traceback
+
+class TestPM(cmd.Cmd):
+    def __init__(self, efs):
+        cmd.Cmd.__init__(self)
+        self.efs = efs
+        self.prompt = 'tbi> '
+    def emptyline(self):
+        return 1
+
+    def do_EOF(self, line):
+        return 1
+    do_q = do_EOF
+
+    def print_tb(self, ef):
+        from pypy.interpreter.baseobjspace import OperationError
+        err = ef[1]
+        print ''.join(traceback.format_exception(*err))
+        if isinstance(err[1], OperationError):
+            print 'and at app-level:'
+            print
+            err[1].print_application_traceback(ef[0].space)
+    
+    def do_l(self, line):
+        i = 0
+        for t, e in self.efs:
+            print i, t.__class__.__module__, t.__class__.__name__, t.methodName
+            i += 1
+
+    def do_tb(self, arg):
+        args = arg.split()
+        if len(args) == 0:
+            for x in self.efs:
+                t = x[0]
+                print t.__class__.__module__, t.__class__.__name__, t.methodName
+                print
+                self.print_tb(x)
+                print
+        elif len(args) == 1:
+            try:
+                tbi = int(args[0])
+            except ValueError:
+                print "error2"
+            else:
+                if 0 <= tbi < len(self.efs):
+                    self.print_tb(self.efs[tbi])
+                else:
+                    print "error3"
+        else:
+            print "error"
+
+    def do_d(self, arg):
+        args = arg.split()
+        if len(args) == 1:
+            try:
+                efi = int(args[0])
+            except ValueError:
+                print "error2"
+            else:
+                if 0 <= efi < len(self.efs):
+                    s, (t, v, tb) = self.efs[efi]
+                    ppdb.post_mortem(s.space, tb, v)
+                else:
+                    print "error3"
+        else:
+            print "error"
+
+
+if __name__ == '__main__':
+    # just for testing
+    c = TestPM([])
+    c.cmdloop()
+


More information about the Pypy-commit mailing list