[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