[py-svn] r65139 - in py/trunk: doc py py/builtin/testing py/cmdline py/log py/log/testing py/magic py/magic/testing py/test py/test/plugin py/test/testing

hpk at codespeak.net hpk at codespeak.net
Thu May 7 16:22:04 CEST 2009


Author: hpk
Date: Thu May  7 16:22:01 2009
New Revision: 65139

Added:
   py/trunk/py/test/plugin/pytest_recwarn.py
Modified:
   py/trunk/doc/conftest.py
   py/trunk/py/builtin/testing/test_exception.py
   py/trunk/py/cmdline/pycleanup.py
   py/trunk/py/conftest.py
   py/trunk/py/log/testing/test_warning.py
   py/trunk/py/log/warning.py
   py/trunk/py/magic/exprinfo.py
   py/trunk/py/magic/testing/test_exprinfo.py
   py/trunk/py/test/collect.py
   py/trunk/py/test/defaultconftest.py
   py/trunk/py/test/plugin/pytest_default.py
   py/trunk/py/test/plugin/pytest_pytester.py
   py/trunk/py/test/plugin/pytest_restdoc.py
   py/trunk/py/test/plugin/pytest_resultdb.py
   py/trunk/py/test/plugin/pytest_terminal.py
   py/trunk/py/test/pycollect.py
   py/trunk/py/test/runner.py
   py/trunk/py/test/testing/acceptance_test.py
   py/trunk/py/test/testing/test_collect.py
   py/trunk/py/test/testing/test_deprecated_api.py
   py/trunk/py/test/testing/test_funcargs.py
   py/trunk/py/test/testing/test_pycollect.py
Log:
merging from mercurial py-trunk

* fix issue 7 (delay in test writing)
* new recwarn plugin (for cool assertions about warnings) 
* have items provide pure metainfo() and leave reporting to
  TerminalReporter
* funcargs are now instantiated during setup phase 
* extend py.cleanup to supply a list of extensions to clean
* python2.6 fixes 



Modified: py/trunk/doc/conftest.py
==============================================================================
--- py/trunk/doc/conftest.py	(original)
+++ py/trunk/doc/conftest.py	Thu May  7 16:22:01 2009
@@ -1,5 +1,5 @@
 #XXX make work: excludedirs = ['_build']
 import py
-py.test.importorskip("pygments")
+#py.test.importorskip("pygments")
 pytest_plugins = ['pytest_restdoc']
 rsyncdirs = ['.']

Modified: py/trunk/py/builtin/testing/test_exception.py
==============================================================================
--- py/trunk/py/builtin/testing/test_exception.py	(original)
+++ py/trunk/py/builtin/testing/test_exception.py	Thu May  7 16:22:01 2009
@@ -15,4 +15,4 @@
 
 def test_GeneratorExit():
     assert py.builtin.GeneratorExit.__module__ == 'exceptions'
-    assert issubclass(py.builtin.GeneratorExit, Exception)
+    assert issubclass(py.builtin.GeneratorExit, py.builtin.BaseException)

Modified: py/trunk/py/cmdline/pycleanup.py
==============================================================================
--- py/trunk/py/cmdline/pycleanup.py	(original)
+++ py/trunk/py/cmdline/pycleanup.py	Thu May  7 16:22:01 2009
@@ -11,11 +11,26 @@
 
 def main():
     parser = py.compat.optparse.OptionParser(usage=__doc__)
+    parser.add_option("-e", "--remove", dest="ext", default=".pyc", action="store",
+        help="remove files with the given comma-separated list of extensions"
+    )
+    parser.add_option("-n", "--dryrun", dest="dryrun", default=False, 
+        action="store_true", 
+        help="display would-be-removed filenames"
+    )
     (options, args) = parser.parse_args()
     if not args:
         args = ["."]
+    ext = options.ext.split(",")
+    def shouldremove(p):
+        return p.ext in ext
+        
     for arg in args:
         path = py.path.local(arg)
-        print "cleaning path", path
-        for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)):
-            x.remove()
+        print "cleaning path", path, "of extensions", ext
+        for x in path.visit(shouldremove, lambda x: x.check(dotfile=0, link=0)):
+            if options.dryrun:
+                print "would remove", x
+            else:
+                print "removing", x
+                x.remove()

Modified: py/trunk/py/conftest.py
==============================================================================
--- py/trunk/py/conftest.py	(original)
+++ py/trunk/py/conftest.py	Thu May  7 16:22:01 2009
@@ -1,7 +1,6 @@
 pytest_plugins = '_pytest doctest pytester'.split()
 
 rsyncdirs = ['../doc']
-rsyncignore = ['c-extension/greenlet/build']
 
 import py
 class PylibTestconfigPlugin:

Modified: py/trunk/py/log/testing/test_warning.py
==============================================================================
--- py/trunk/py/log/testing/test_warning.py	(original)
+++ py/trunk/py/log/testing/test_warning.py	Thu May  7 16:22:01 2009
@@ -26,3 +26,15 @@
     lno = test_stacklevel.func_code.co_firstlineno + 6
     warning = str(err)
     assert warning.find(":%s" % lno) != -1
+
+def test_function():
+    capture = py.io.StdCapture()
+    py.log._apiwarn("x.y.z", "something", function=test_function)
+    out, err = capture.reset()
+    print "out", out
+    print "err", err
+    assert err.find("x.y.z") != -1
+    lno = test_function.func_code.co_firstlineno 
+    exp = "%s:%s" % (mypath, lno)
+    assert err.find(exp) != -1
+

Modified: py/trunk/py/log/warning.py
==============================================================================
--- py/trunk/py/log/warning.py	(original)
+++ py/trunk/py/log/warning.py	Thu May  7 16:22:01 2009
@@ -10,26 +10,30 @@
     def __str__(self):
         return self.msg 
 
-def _apiwarn(startversion, msg, stacklevel=1):
+def _apiwarn(startversion, msg, stacklevel=1, function=None):
     # below is mostly COPIED from python2.4/warnings.py's def warn()
     # Get context information
     msg = "%s (since version %s)" %(msg, startversion)
-    warn(msg, stacklevel=stacklevel+1)
+    warn(msg, stacklevel=stacklevel+1, function=function)
 
-def warn(msg, stacklevel=1):
-    try:
-        caller = sys._getframe(stacklevel)
-    except ValueError:
-        globals = sys.__dict__
-        lineno = 1
+def warn(msg, stacklevel=1, function=None):
+    if function is not None:
+        filename = py.std.inspect.getfile(function)
+        lineno = function.func_code.co_firstlineno
     else:
-        globals = caller.f_globals
-        lineno = caller.f_lineno
-    if '__name__' in globals:
-        module = globals['__name__']
-    else:
-        module = "<string>"
-    filename = globals.get('__file__')
+        try:
+            caller = sys._getframe(stacklevel)
+        except ValueError:
+            globals = sys.__dict__
+            lineno = 1
+        else:
+            globals = caller.f_globals
+            lineno = caller.f_lineno
+        if '__name__' in globals:
+            module = globals['__name__']
+        else:
+            module = "<string>"
+        filename = globals.get('__file__')
     if filename:
         fnl = filename.lower()
         if fnl.endswith(".pyc") or fnl.endswith(".pyo"):

Modified: py/trunk/py/magic/exprinfo.py
==============================================================================
--- py/trunk/py/magic/exprinfo.py	(original)
+++ py/trunk/py/magic/exprinfo.py	Thu May  7 16:22:01 2009
@@ -428,7 +428,9 @@
         import traceback
         traceback.print_exc()
     if should_fail:
-        return "(inconsistently failed then succeeded)"
+        return ("(assertion failed, but when it was re-run for "
+                "printing intermediate values, it did not fail.  Suggestions: "
+                "compute assert expression before the assert or use --nomagic)")
     else:
         return None
 

Modified: py/trunk/py/magic/testing/test_exprinfo.py
==============================================================================
--- py/trunk/py/magic/testing/test_exprinfo.py	(original)
+++ py/trunk/py/magic/testing/test_exprinfo.py	Thu May  7 16:22:01 2009
@@ -119,3 +119,15 @@
             pass
         else:
             raise AssertionError, "ex %s didn't pass through" %(exstr, )
+
+def test_inconsistent_assert_result(testdir):
+    p = testdir.makepyfile("""
+        def test_func():
+            def f(l=[1,0]): 
+                return l.pop()
+            assert f()
+    """)
+    result = testdir.runpytest(p)
+    s = result.stdout.str()
+    assert s.find("re-run") != -1
+

Modified: py/trunk/py/test/collect.py
==============================================================================
--- py/trunk/py/test/collect.py	(original)
+++ py/trunk/py/test/collect.py	Thu May  7 16:22:01 2009
@@ -12,31 +12,6 @@
         return self.config.getvalue(name, self.fspath) 
     return property(fget)
 
-class ReprMetaInfo(object):
-    def __init__(self, fspath=None, lineno=None, modpath=None):
-        self.fspath = fspath
-        self.lineno = lineno 
-        self.modpath = modpath
-
-    def verboseline(self, basedir=None):
-        params = self.__dict__.copy()
-        if self.fspath:
-            if basedir is not None:
-                params['fspath'] = basedir.bestrelpath(self.fspath)
-        if self.lineno is not None:
-            params['lineno'] = self.lineno + 1
-
-        if self.fspath and self.lineno and self.modpath:
-            line = "%(fspath)s:%(lineno)s: %(modpath)s"
-        elif self.fspath and self.modpath:
-            line = "%(fspath)s: %(modpath)s"
-        elif self.fspath and self.lineno:
-            line = "%(fspath)s:%(lineno)s"
-        else:
-            line = "[nometainfo]"
-        return line % params
-        
-
 class Node(object): 
     """ base class for Nodes in the collection tree.  
         Collector nodes have children and 
@@ -50,7 +25,6 @@
         - configuration/options for setup/teardown 
           stdout/stderr capturing and execution of test items 
     """
-    ReprMetaInfo = ReprMetaInfo
     def __init__(self, name, parent=None, config=None):
         self.name = name 
         self.parent = parent
@@ -347,7 +321,7 @@
         setattr(self, attrname, True)
         method = getattr(self.__class__, 'run', None)
         if method is not None and method != Collector.run:
-            warnoldcollect()
+            warnoldcollect(function=method)
             names = self.run()
             return filter(None, [self.join(name) for name in names])
 
@@ -356,14 +330,12 @@
             You can return an empty list.  Callers of this method
             must take care to catch exceptions properly.  
         """
-        warnoldcollect()
         return [colitem.name for colitem in self._memocollect()]
 
     def join(self, name): 
         """  DEPRECATED: return a child collector or item for the given name.  
              If the return value is None there is no such child. 
         """
-        warnoldcollect()
         return self.collect_by_name(name)
 
 class FSCollector(Collector): 
@@ -451,40 +423,33 @@
     """ a basic test item. """
     def _deprecated_testexecution(self):
         if self.__class__.run != Item.run:
-            warnoldtestrun()
-            self.run()
-            return True
+            warnoldtestrun(function=self.run)
         elif self.__class__.execute != Item.execute:
-            warnoldtestrun()
-            self.execute(self.obj, *self._args)
-            return True
+            warnoldtestrun(function=self.execute)
+        else:
+            return False
+        self.run()
+        return True
 
     def run(self):
-        warnoldtestrun()
+        """ deprecated, here because subclasses might call it. """
         return self.execute(self.obj, *self._args)
 
     def execute(self, obj, *args):
-        warnoldtestrun()
+        """ deprecated, here because subclasses might call it. """
         return obj(*args)
 
-    def repr_metainfo(self):
-        try:
-            return self.ReprMetaInfo(self.fspath, modpath=self.__class__.__name__)
-        except AttributeError:
-            code = py.code.Code(self.execute)
-            return self.ReprMetaInfo(code.path, code.firstlineno)
-      
-    def runtest(self):
-        """ execute this test item."""
+    def metainfo(self):
+        return self.fspath, None, ""
         
-def warnoldcollect():
+def warnoldcollect(function=None):
     py.log._apiwarn("1.0", 
         "implement collector.collect() instead of "
         "collector.run() and collector.join()",
-        stacklevel=2)
+        stacklevel=2, function=function)
 
-def warnoldtestrun():
+def warnoldtestrun(function=None):
     py.log._apiwarn("1.0", 
         "implement item.runtest() instead of "
         "item.run() and item.execute()",
-        stacklevel=2)
+        stacklevel=2, function=function)

Modified: py/trunk/py/test/defaultconftest.py
==============================================================================
--- py/trunk/py/test/defaultconftest.py	(original)
+++ py/trunk/py/test/defaultconftest.py	Thu May  7 16:22:01 2009
@@ -10,5 +10,5 @@
 Function = py.test.collect.Function
 Instance = py.test.collect.Instance
 
-pytest_plugins = "default terminal xfail tmpdir execnetcleanup monkeypatch pdb".split()
+pytest_plugins = "default terminal xfail tmpdir execnetcleanup monkeypatch recwarn pdb".split()
 

Modified: py/trunk/py/test/plugin/pytest_default.py
==============================================================================
--- py/trunk/py/test/plugin/pytest_default.py	(original)
+++ py/trunk/py/test/plugin/pytest_default.py	Thu May  7 16:22:01 2009
@@ -191,10 +191,12 @@
     
 def test_plugin_specify(testdir):
     testdir.chdir()
-    config = testdir.parseconfig("-p", "nqweotexistent")
-    py.test.raises(ImportError, 
-        "config.pluginmanager.do_configure(config)"
-    )
+    config = py.test.raises(ImportError, """
+            testdir.parseconfig("-p", "nqweotexistent")
+    """)
+    #py.test.raises(ImportError, 
+    #    "config.pluginmanager.do_configure(config)"
+    #)
 
 def test_plugin_already_exists(testdir):
     config = testdir.parseconfig("-p", "default")

Modified: py/trunk/py/test/plugin/pytest_pytester.py
==============================================================================
--- py/trunk/py/test/plugin/pytest_pytester.py	(original)
+++ py/trunk/py/test/plugin/pytest_pytester.py	Thu May  7 16:22:01 2009
@@ -3,6 +3,7 @@
 """
 
 import py
+import os
 import inspect
 from py.__.test import runner
 from py.__.test.config import Config as pytestConfig
@@ -222,6 +223,9 @@
     def popen(self, cmdargs, stdout, stderr, **kw):
         if not hasattr(py.std, 'subprocess'):
             py.test.skip("no subprocess module")
+        env = os.environ.copy()
+        env['PYTHONPATH'] = "%s:%s" % (os.getcwd(), env['PYTHONPATH'])
+        kw['env'] = env
         return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw)
 
     def run(self, *cmdargs):
@@ -250,10 +254,14 @@
         return RunResult(ret, out, err)
 
     def runpybin(self, scriptname, *args):
+        fullargs = self._getpybinargs(scriptname) + args
+        return self.run(*fullargs)
+
+    def _getpybinargs(self, scriptname):
         bindir = py.path.local(py.__file__).dirpath("bin")
         script = bindir.join(scriptname)
         assert script.check()
-        return self.run(py.std.sys.executable, script, *args)
+        return py.std.sys.executable, script
 
     def runpytest(self, *args):
         p = py.path.local.make_numbered_dir(prefix="runpytest-", 
@@ -261,6 +269,15 @@
         args = ('--basetemp=%s' % p, ) + args 
         return self.runpybin("py.test", *args)
 
+    def spawn_pytest(self, string, expect_timeout=10.0):
+        pexpect = py.test.importorskip("pexpect", "2.3")
+        basetemp = self.tmpdir.mkdir("pexpect")
+        invoke = "%s %s" % self._getpybinargs("py.test")
+        cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string)
+        child = pexpect.spawn(cmd, logfile=basetemp.join("spawn.out").open("w"))
+        child.timeout = expect_timeout
+        return child
+
 class Event:
     def __init__(self, name, args, kwargs):
         self.name = name

Added: py/trunk/py/test/plugin/pytest_recwarn.py
==============================================================================
--- (empty file)
+++ py/trunk/py/test/plugin/pytest_recwarn.py	Thu May  7 16:22:01 2009
@@ -0,0 +1,95 @@
+"""
+
+"recwarn" funcarg plugin that helps to assert 
+that warnings are shown to a user.  See the test
+at the bottom for an example. 
+
+"""
+import py
+import os
+
+class RecwarnPlugin:
+    def pytest_funcarg__recwarn(self, request):
+        """ check that warnings have been raised. """ 
+        warnings = WarningsRecorder()
+        request.addfinalizer(warnings.finalize)
+        return warnings
+
+class RecordedWarning:
+    def __init__(self, message, category, filename, lineno, line):
+        self.message = message
+        self.category = category
+        self.filename = filename
+        self.lineno = lineno
+        self.line = line
+
+class WarningsRecorder:
+    def __init__(self):
+        warningmodule = py.std.warnings
+        self.list = []
+        def showwarning(message, category, filename, lineno, line=0):
+            self.list.append(RecordedWarning(
+                message, category, filename, lineno, line))
+            try:
+                self.old_showwarning(message, category, 
+                    filename, lineno, line=line)
+            except TypeError:
+                # < python2.6 
+                self.old_showwarning(message, category, filename, lineno)
+        self.old_showwarning = warningmodule.showwarning
+        warningmodule.showwarning = showwarning
+
+    def pop(self, cls=Warning):
+        """ pop the first recorded warning, raise exception if not exists."""
+        for i, w in py.builtin.enumerate(self.list):
+            if issubclass(w.category, cls):
+                return self.list.pop(i)
+        __tracebackhide__ = True
+        assert 0, "%r not found in %r" %(cls, self.list)
+
+    #def resetregistry(self):
+    #    import warnings
+    #    warnings.onceregistry.clear()
+    #    warnings.__warningregistry__.clear()
+
+    def clear(self): 
+        self.list[:] = []
+
+    def finalize(self):
+        py.std.warnings.showwarning = self.old_showwarning
+
+def test_WarningRecorder():
+    showwarning = py.std.warnings.showwarning
+    rec = WarningsRecorder()
+    assert py.std.warnings.showwarning != showwarning
+    assert not rec.list
+    py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13)
+    assert len(rec.list) == 1
+    py.std.warnings.warn(DeprecationWarning("hello"))
+    assert len(rec.list) == 2
+    warn = rec.pop()
+    assert str(warn.message) == "hello"
+    l = rec.list
+    rec.clear()
+    assert len(rec.list) == 0
+    assert l is rec.list
+    py.test.raises(AssertionError, "rec.pop()")
+    rec.finalize()
+    assert showwarning == py.std.warnings.showwarning
+
+def test_recwarn_functional(testdir):
+    sorter = testdir.inline_runsource("""
+        pytest_plugins = 'pytest_recwarn', 
+        import warnings
+        oldwarn = warnings.showwarning
+        def test_method(recwarn):
+            assert warnings.showwarning != oldwarn
+            warnings.warn("hello")
+            warn = recwarn.pop()
+            assert isinstance(warn.message, UserWarning)
+        def test_finalized():
+            assert warnings.showwarning == oldwarn
+    """)
+    res = sorter.countoutcomes()
+    assert tuple(res) == (2, 0, 0), res
+        

Modified: py/trunk/py/test/plugin/pytest_restdoc.py
==============================================================================
--- py/trunk/py/test/plugin/pytest_restdoc.py	(original)
+++ py/trunk/py/test/plugin/pytest_restdoc.py	Thu May  7 16:22:01 2009
@@ -64,6 +64,9 @@
         super(ReSTSyntaxTest, self).__init__(*args, **kwargs)
         self.project = project
 
+    def metainfo(self):
+        return self.fspath, None, "syntax check"
+
     def runtest(self):
         self.restcheck(py.path.svnwc(self.fspath))
 
@@ -193,6 +196,9 @@
                     #return [] # no need to rebuild 
 
 class DoctestText(py.test.collect.Item): 
+    def metainfo(self):
+        return self.fspath, None, "doctest"
+
     def runtest(self): 
         content = self._normalize_linesep()
         newcontent = self.config.api.pytest_doctest_prepare_content(content=content)
@@ -276,9 +282,9 @@
                             args=(tryfn, path, lineno), callobj=localrefcheck)
         
 class CheckLink(py.test.collect.Function): 
-    def repr_metainfo(self):
-        return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2],
-            modpath="checklink: %s" % (self._args[0],))
+    def metainfo(self, basedir=None):
+        return (self.fspath, self._args[2], "checklink: %s" % self._args[0])
+
     def setup(self): 
         pass 
     def teardown(self): 

Modified: py/trunk/py/test/plugin/pytest_resultdb.py
==============================================================================
--- py/trunk/py/test/plugin/pytest_resultdb.py	(original)
+++ py/trunk/py/test/plugin/pytest_resultdb.py	Thu May  7 16:22:01 2009
@@ -6,6 +6,8 @@
 
     Saves test results to a datastore.
 
+    XXX this needs to be merged with resultlog plugin
+
     Also mixes in some early ideas about an archive abstraction for test 
     results.
     """ 
@@ -262,7 +264,11 @@
 class TestSQLiteResultArchive(BaseResultArchiveTests):
     cls = SQLiteResultArchive
 
+    def setup_method(self, method):
+        py.test.importorskip("sqlite3")
+
     def test_init_db_sql(self, testdir):
+        py.test.importorskip("sqlite3")
         tempdb_path = unicode(testdir.tmpdir.join(self.tempdb))
         archive = self.cls(tempdb_path)
         archive.init_db()
@@ -287,6 +293,7 @@
 
 class TestWithFunctionIntegration:
     def getarchive(self, testdir, arg):
+        py.test.importorskip("sqlite3")
         py.test.importorskip("simplejson")
         resultdb = testdir.tmpdir.join("resultdb")
         args = ["--resultdb=%s" % resultdb, "--resultdb_format=sqlite"] + [arg]

Modified: py/trunk/py/test/plugin/pytest_terminal.py
==============================================================================
--- py/trunk/py/test/plugin/pytest_terminal.py	(original)
+++ py/trunk/py/test/plugin/pytest_terminal.py	Thu May  7 16:22:01 2009
@@ -138,8 +138,7 @@
 
     def pytest_itemstart(self, item, node=None):
         if self.config.option.debug:
-            info = item.repr_metainfo()
-            line = info.verboseline(basedir=self.curdir) + " "
+            line = self._metainfoline(item)
             extra = ""
             if node:
                 extra = "-> " + str(node.gateway.id)
@@ -149,10 +148,11 @@
         elif self.config.option.verbose and self.config.option.dist == "no":
             # ensure that the path is printed before the 1st test of
             # a module starts running
-            info = item.repr_metainfo()
-            line = info.verboseline(basedir=self.curdir) + " "
-            #self.write_fspath_result(fspath, "")
+            line = self._metainfoline(item)
             self.write_ensure_prefix(line, "") 
+        else:
+            fspath, lineno, msg = item.metainfo()
+            self.write_fspath_result(fspath, "")
 
     def pytest_rescheduleitems(self, items):
         if self.config.option.debug:
@@ -172,8 +172,7 @@
         if not self.config.option.verbose:
             self.write_fspath_result(fspath, letter)
         else:
-            info = rep.colitem.repr_metainfo()
-            line = info.verboseline(basedir=self.curdir) + " "
+            line = self._metainfoline(rep.colitem)
             if not hasattr(rep, 'node'):
                 self.write_ensure_prefix(line, word, **markup)
             else:
@@ -250,6 +249,22 @@
         for rootdir in rootdirs:
             self.write_line("### Watching:   %s" %(rootdir,), bold=True)
 
+    def _metainfoline(self, item):
+        fspath, lineno, msg = item.metainfo()
+        if fspath:
+            fspath = self.curdir.bestrelpath(fspath)
+        if lineno is not None:
+            lineno += 1
+        if fspath and lineno and msg:
+            line = "%(fspath)s:%(lineno)s: %(msg)s"
+        elif fspath and msg:
+            line = "%(fspath)s: %(msg)s"
+        elif fspath and lineno:
+            line = "%(fspath)s:%(lineno)s"
+        else:
+            line = "[nometainfo]"
+        return line % locals() + " "
+
     #
     # summaries for testrunfinish 
     #
@@ -543,16 +558,10 @@
             linecomp.stringio.truncate(0)
 
     def test_show_path_before_running_test(self, testdir, linecomp):
-        modcol = testdir.getmodulecol("""
-            def test_foobar():
-                pass
-        """)
-        rep = TerminalReporter(modcol.config, file=linecomp.stringio)
-        modcol.config.pluginmanager.register(rep)
-        l = list(testdir.genitems([modcol]))
-        assert len(l) == 1
-        modcol.config.option.debug = True
-        rep.config.api.pytest_itemstart(item=l[0])
+        item = testdir.getitem("def test_func(): pass")
+        rep = TerminalReporter(item.config, file=linecomp.stringio)
+        item.config.pluginmanager.register(rep)
+        rep.config.api.pytest_itemstart(item=item)
         linecomp.assert_contains_lines([
             "*test_show_path_before_running_test.py*"
         ])

Modified: py/trunk/py/test/pycollect.py
==============================================================================
--- py/trunk/py/test/pycollect.py	(original)
+++ py/trunk/py/test/pycollect.py	Thu May  7 16:22:01 2009
@@ -87,15 +87,10 @@
         self._fslineno = fspath, lineno
         return fspath, lineno
 
-    def repr_metainfo(self):
+    def metainfo(self):
         fspath, lineno = self.getfslineno()
         modpath = self.getmodpath()
-        return self.ReprMetaInfo(
-            fspath=fspath, 
-            lineno=lineno,
-            modpath=modpath,
-        )
-
+        return fspath, lineno, modpath 
 
 class PyCollectorMixin(PyobjMixin, py.test.collect.Collector): 
     Class = configproperty('Class')
@@ -235,6 +230,7 @@
 class FunctionMixin(PyobjMixin):
     """ mixin for the code common to Function and Generator.
     """
+
     def _getsortvalue(self):  
         return self.getfslineno()
 
@@ -251,7 +247,7 @@
             obj = self.parent.obj 
         setup_func_or_method = getattr(obj, name, None)
         if setup_func_or_method is not None: 
-            return setup_func_or_method(self.obj) 
+            setup_func_or_method(self.obj) 
 
     def teardown(self): 
         """ perform teardown for this test function. """
@@ -283,9 +279,9 @@
 
 class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector): 
     def collect(self):
-        # test generators are collectors yet participate in 
-        # the test-item setup and teardown protocol. 
-        # otherwise we could avoid global setupstate
+        # test generators are seen as collectors but they also 
+        # invoke setup/teardown on popular request 
+        # (induced by the common "test_*" naming shared with normal tests)
         self.config._setupstate.prepare(self) 
         l = []
         seen = {}
@@ -314,6 +310,7 @@
             name = None
         call, args = obj[0], obj[1:]
         return name, call, args 
+    
 
 #
 #  Test Items 
@@ -348,14 +345,18 @@
 
     def runtest(self):
         """ execute the given test function. """
-        if not self._deprecated_testexecution():
-            self.setupargs() # XXX move to setup() / consider funcargs plugin
-            ret = self.config.api.pytest_pyfunc_call(
-                pyfuncitem=self, args=self._args, kwargs=self.funcargs)
+        self.config.api.pytest_pyfunc_call(pyfuncitem=self, 
+            args=self._args, kwargs=self.funcargs)
 
-    def setupargs(self):
+    def setup(self):
+        super(Function, self).setup()
+        self._setupfuncargs()
+
+    def _setupfuncargs(self):
         if self._args:
-            # generator case: we don't do anything then
+            # functions yielded from a generator: we don't want
+            # to support that because we want to go here anyway: 
+            # http://bitbucket.org/hpk42/py-trunk/issue/2/next-generation-generative-tests
             pass
         else:
             # standard Python Test function/method case  
@@ -389,7 +390,7 @@
 
     def getrequest(self, argname):
         return FuncargRequest(pyfuncitem=self, argname=argname) 
-        
+
 
 class FuncargRequest:
     _argprefix = "pytest_funcarg__"
@@ -447,8 +448,9 @@
                     name = name[len(self._argprefix):]
                     if name not in available:
                         available.append(name) 
-        metainfo = self._pyfuncitem.repr_metainfo()
-        msg = "funcargument %r not found for: %s" %(self.argname,metainfo.verboseline())
+        fspath, lineno, msg = self._pyfuncitem.metainfo()
+        line = "%s:%s" %(fspath, lineno)
+        msg = "funcargument %r not found for: %s" %(self.argname, line)
         msg += "\n available funcargs: %s" %(", ".join(available),)
         raise LookupError(msg)
 

Modified: py/trunk/py/test/runner.py
==============================================================================
--- py/trunk/py/test/runner.py	(original)
+++ py/trunk/py/test/runner.py	Thu May  7 16:22:01 2009
@@ -20,7 +20,8 @@
             item.config._setupstate.prepare(item)
             try:
                 when = "execute"
-                res = item.runtest()
+                if not item._deprecated_testexecution():
+                    item.runtest()
             finally:
                 when = "teardown"
                 item.config._setupstate.teardown_exact(item)

Modified: py/trunk/py/test/testing/acceptance_test.py
==============================================================================
--- py/trunk/py/test/testing/acceptance_test.py	(original)
+++ py/trunk/py/test/testing/acceptance_test.py	Thu May  7 16:22:01 2009
@@ -1,7 +1,5 @@
 import py
 
-pydir = py.path.local(py.__file__).dirpath()
-pytestpath = pydir.join("bin", "py.test")
 EXPECTTIMEOUT=10.0
 
 class TestGeneralUsage:
@@ -445,30 +443,13 @@
 
 
 class TestInteractive:
-    def getspawn(self, tmpdir):
-        pexpect = py.test.importorskip("pexpect")
-        basetemp = tmpdir.mkdir("basetemp")
-        def spawn(cmd):
-            cmd = cmd + " --basetemp=" + str(basetemp)
-            return pexpect.spawn(cmd, logfile=tmpdir.join("spawn.out").open("w"))
-        return spawn
-
-    def requirespexpect(self, version_needed):
-        pexpect = py.test.importorskip("pexpect")
-        ver = tuple(map(int, pexpect.__version__.split(".")))
-        if ver < version_needed:
-            py.test.skip("pexpect version %s needed" %(".".join(map(str, version_needed))))
-       
     def test_pdb_interaction(self, testdir):
-        self.requirespexpect((2,3))
-        spawn = self.getspawn(testdir.tmpdir)
         p1 = testdir.makepyfile("""
             def test_1():
                 i = 0
                 assert i == 1
         """)
-        child = spawn("%s %s --pdb %s" % (py.std.sys.executable, pytestpath, p1))
-        child.timeout = EXPECTTIMEOUT
+        child = testdir.spawn_pytest("--pdb %s" % p1)
         #child.expect(".*def test_1.*")
         child.expect(".*i = 0.*")
         child.expect("(Pdb)")
@@ -478,14 +459,12 @@
             child.wait()
 
     def test_simple_looponfail_interaction(self, testdir):
-        spawn = self.getspawn(testdir.tmpdir)
         p1 = testdir.makepyfile("""
             def test_1():
                 assert 1 == 0 
         """)
         p1.setmtime(p1.mtime() - 50.0)  
-        child = spawn("%s %s --looponfail %s" % (py.std.sys.executable, pytestpath, p1))
-        child.timeout = EXPECTTIMEOUT
+        child = testdir.spawn_pytest("--looponfail %s" % p1)
         child.expect("assert 1 == 0")
         child.expect("test_simple_looponfail_interaction.py:")
         child.expect("1 failed")

Modified: py/trunk/py/test/testing/test_collect.py
==============================================================================
--- py/trunk/py/test/testing/test_collect.py	(original)
+++ py/trunk/py/test/testing/test_collect.py	Thu May  7 16:22:01 2009
@@ -131,12 +131,6 @@
         names = [x.name for x in col.collect()]
         assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"]
 
-    def test_collector_deprecated_run_method(self, testdir):
-        modcol = testdir.getmodulecol("pass")
-        res1 = py.test.deprecated_call(modcol.run)
-        res2 = modcol.collect()
-        assert res1 == [x.name for x in res2]
-
 class TestCollectPluginHooks:
     def test_pytest_collect_file(self, testdir):
         tmpdir = testdir.tmpdir
@@ -212,63 +206,3 @@
         evrec = testdir.inline_run(testdir.tmpdir, "--XX")
         names = [rep.colitem.name for rep in evrec.getreports("collectreport")]
         assert 'hello' in names 
-
-class TestCollectorReprs:
-    def test_repr_metainfo_basic_item(self, testdir):
-        modcol = testdir.getmodulecol("")
-        Item = py.test.collect.Item
-        item = Item("virtual", parent=modcol)
-        info = item.repr_metainfo() 
-        assert info.fspath == modcol.fspath
-        assert not info.lineno
-        assert info.modpath == "Item"
-        
-    def test_repr_metainfo_func(self, testdir):
-        item = testdir.getitem("def test_func(): pass")
-        info = item.repr_metainfo()
-        assert info.fspath == item.fspath 
-        assert info.lineno == 0
-        assert info.modpath == "test_func"
-
-    def test_repr_metainfo_class(self, testdir):
-        modcol = testdir.getmodulecol("""
-            # lineno 0
-            class TestClass:
-                def test_hello(self): pass
-        """)
-        classcol = modcol.collect_by_name("TestClass")
-        info = classcol.repr_metainfo()
-        assert info.fspath == modcol.fspath 
-        assert info.lineno == 1
-        assert info.modpath == "TestClass"
-
-    def test_repr_metainfo_generator(self, testdir):
-        modcol = testdir.getmodulecol("""
-            # lineno 0
-            def test_gen(): 
-                def check(x): 
-                    assert x
-                yield check, 3
-        """)
-        gencol = modcol.collect_by_name("test_gen")
-        info = gencol.repr_metainfo()
-        assert info.fspath == modcol.fspath
-        assert info.lineno == 1
-        assert info.modpath == "test_gen"
-
-        genitem = gencol.collect()[0]
-        info = genitem.repr_metainfo()
-        assert info.fspath == modcol.fspath
-        assert info.lineno == 2
-        assert info.modpath == "test_gen[0]"
-        """
-            def test_func():
-                pass
-            def test_genfunc():
-                def check(x):
-                    pass
-                yield check, 3
-            class TestClass:
-                def test_method(self):
-                    pass
-       """

Modified: py/trunk/py/test/testing/test_deprecated_api.py
==============================================================================
--- py/trunk/py/test/testing/test_deprecated_api.py	(original)
+++ py/trunk/py/test/testing/test_deprecated_api.py	Thu May  7 16:22:01 2009
@@ -2,15 +2,8 @@
 import py
 
 class TestCollectDeprecated:
-    def test_directory_run_join_warnings(self, testdir):
-        p = testdir.makepyfile(test_one="")
-        config = testdir.parseconfig(p)  
-        dirnode = config.getfsnode(p.dirpath())
-        py.test.deprecated_call(dirnode.run)
-        # XXX for directories we still have join()
-        #py.test.deprecated_call(dirnode.join, 'test_one')
         
-    def test_collect_with_deprecated_run_and_join(self, testdir):
+    def test_collect_with_deprecated_run_and_join(self, testdir, recwarn):
         testdir.makepyfile(conftest="""
             import py
 
@@ -52,23 +45,31 @@
         """)
         config = testdir.parseconfig()
         dirnode = config.getfsnode(p.dirpath())
-        colitems = py.test.deprecated_call(dirnode.collect)
+        colitems = dirnode.collect()
+        w = recwarn.pop(DeprecationWarning)
+        assert w.filename.find("conftest.py") != -1
+        #recwarn.resetregistry()
+        #assert 0, (w.message, w.filename, w.lineno)
         assert len(colitems) == 1
         modcol = colitems[0]
         assert modcol.name == "somefile.py"
-        colitems = py.test.deprecated_call(modcol.collect)
+        colitems = modcol.collect()
+        recwarn.pop(DeprecationWarning)
         assert len(colitems) == 2
         assert colitems[0].name == 'check'
         assert colitems[1].name == 'Cls'
         clscol = colitems[1] 
-        colitems = py.test.deprecated_call(clscol.collect)
+
+        colitems = clscol.collect()
+        recwarn.pop(DeprecationWarning)
         assert len(colitems) == 1
         icol = colitems[0] 
-        colitems = py.test.deprecated_call(icol.collect)
+        colitems = icol.collect()
+        recwarn.pop(DeprecationWarning)
         assert len(colitems) == 1
         assert colitems[0].name == 'check2'
 
-    def test_collect_with_deprecated_join_but_no_run(self, testdir):
+    def test_collect_with_deprecated_join_but_no_run(self, testdir, recwarn):
         testdir.makepyfile(conftest="""
             import py
 
@@ -87,42 +88,57 @@
             def check_one(): pass
             class SomeClass: pass
         """)
-        colitems = py.test.deprecated_call(col.collect)
+        colitems = col.collect()
+        recwarn.pop(DeprecationWarning) 
         assert len(colitems) == 1
         funcitem = colitems[0]
         assert funcitem.name == "check_one"
 
-    def test_function_custom_run(self, testdir):
+    def test_function_custom_run(self, testdir, recwarn):
         testdir.makepyfile(conftest="""
             import py
-            class MyFunction(py.test.collect.Function):
+            class Function(py.test.collect.Function):
                 def run(self):
                     pass
-            Function=MyFunction 
         """)
         modcol = testdir.getmodulecol("def test_func(): pass")
         funcitem = modcol.collect()[0]
         assert funcitem.name == 'test_func'
-        py.test.deprecated_call(funcitem.runtest)
+        recwarn.clear()
+        funcitem._deprecated_testexecution()
+        recwarn.pop(DeprecationWarning)
 
-    def test_function_custom_execute(self, testdir):
+    def test_function_custom_execute(self, testdir, recwarn):
         testdir.makepyfile(conftest="""
             import py
+
             class MyFunction(py.test.collect.Function):
                 def execute(self, obj, *args):
                     pass
             Function=MyFunction 
         """)
-        modcol = testdir.getmodulecol("def test_func(): pass")
+        modcol = testdir.getmodulecol("def test_func2(): pass")
         funcitem = modcol.collect()[0]
-        assert funcitem.name == 'test_func'
-        py.test.deprecated_call(funcitem.runtest)
+        assert funcitem.name == 'test_func2'
+        funcitem._deprecated_testexecution()
+        w = recwarn.pop(DeprecationWarning)
+        assert w.filename.find("conftest.py") != -1
 
-    def test_function_deprecated_run_execute(self, testdir):
-        modcol = testdir.getmodulecol("def test_some(): pass")
+    def test_function_deprecated_run_execute(self, testdir, recwarn):
+        testdir.makepyfile(conftest="""
+            import py
+
+            class Function(py.test.collect.Function):
+
+                def run(self):
+                    pass
+        """)
+        modcol = testdir.getmodulecol("def test_some2(): pass")
         funcitem = modcol.collect()[0]
-        py.test.deprecated_call(funcitem.run)
-        py.test.deprecated_call(funcitem.execute, funcitem.obj)
+
+        recwarn.clear()
+        funcitem._deprecated_testexecution()
+        recwarn.pop(DeprecationWarning)
 
     def test_function_deprecated_run_recursive(self, testdir):
         testdir.makepyfile(conftest="""

Modified: py/trunk/py/test/testing/test_funcargs.py
==============================================================================
--- py/trunk/py/test/testing/test_funcargs.py	(original)
+++ py/trunk/py/test/testing/test_funcargs.py	Thu May  7 16:22:01 2009
@@ -8,7 +8,7 @@
                     return 42
         """)
         item = testdir.getitem("def test_func(some): pass")
-        exc = py.test.raises(LookupError, "item.setupargs()")
+        exc = py.test.raises(LookupError, "item._setupfuncargs()")
         s = str(exc.value)
         assert s.find("xyzsomething") != -1
 
@@ -18,7 +18,7 @@
             def pytest_funcarg__some(self, request):
                 return request.function.__name__
         item.config.pluginmanager.register(Provider())
-        item.setupargs()
+        item._setupfuncargs()
         assert len(item.funcargs) == 1
 
     def test_funcarg_lookup_default_gets_overriden(self, testdir):
@@ -27,7 +27,7 @@
             def pytest_funcarg__other(self, request):
                 return request.function.__name__
         item.config.pluginmanager.register(Provider())
-        item.setupargs()
+        item._setupfuncargs()
         assert len(item.funcargs) == 1
         name, value = item.funcargs.popitem()
         assert name == "other"
@@ -41,7 +41,7 @@
             def pytest_funcarg__other(self, request):
                 return 42
         item.config.pluginmanager.register(Provider())
-        item.setupargs()
+        item._setupfuncargs()
         assert len(item.funcargs) == 2
         assert item.funcargs['some'] == "test_func"
         assert item.funcargs['other'] == 42
@@ -58,9 +58,9 @@
                 pass 
         """)
         item1, item2 = testdir.genitems([modcol])
-        item1.setupargs()
+        item1._setupfuncargs()
         assert item1.funcargs['something'] ==  "test_method"
-        item2.setupargs()
+        item2._setupfuncargs()
         assert item2.funcargs['something'] ==  "test_func"
 
 class TestRequest:

Modified: py/trunk/py/test/testing/test_pycollect.py
==============================================================================
--- py/trunk/py/test/testing/test_pycollect.py	(original)
+++ py/trunk/py/test/testing/test_pycollect.py	Thu May  7 16:22:01 2009
@@ -339,3 +339,55 @@
         assert len(colitems) == 1
         assert colitems[0].name == "check_method"
 
+
+class TestMetaInfo:
+        
+    def test_func_metainfo(self, testdir):
+        item = testdir.getitem("def test_func(): pass")
+        fspath, lineno, modpath = item.metainfo()
+        assert fspath == item.fspath 
+        assert lineno == 0
+        assert modpath == "test_func"
+
+    def test_class_metainfo(self, testdir):
+        modcol = testdir.getmodulecol("""
+            # lineno 0
+            class TestClass:
+                def test_hello(self): pass
+        """)
+        classcol = modcol.collect_by_name("TestClass")
+        fspath, lineno, msg = classcol.metainfo()
+        assert fspath == modcol.fspath 
+        assert lineno == 1
+        assert msg == "TestClass"
+
+    def test_generator_metainfo(self, testdir):
+        modcol = testdir.getmodulecol("""
+            # lineno 0
+            def test_gen(): 
+                def check(x): 
+                    assert x
+                yield check, 3
+        """)
+        gencol = modcol.collect_by_name("test_gen")
+        fspath, lineno, modpath = gencol.metainfo()
+        assert fspath == modcol.fspath
+        assert lineno == 1
+        assert modpath == "test_gen"
+
+        genitem = gencol.collect()[0]
+        fspath, lineno, modpath = genitem.metainfo()
+        assert fspath == modcol.fspath
+        assert lineno == 2
+        assert modpath == "test_gen[0]"
+        """
+            def test_func():
+                pass
+            def test_genfunc():
+                def check(x):
+                    pass
+                yield check, 3
+            class TestClass:
+                def test_method(self):
+                    pass
+       """



More information about the pytest-commit mailing list