refine tests and refine code to deal with new xdist semantics.

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-01-17 23:23:02 +01:00
parent f5e9d91f7b
commit 95de17b652
9 changed files with 206 additions and 79 deletions

View File

@ -219,7 +219,7 @@ class TmpTestdir:
if not args:
args = [self.tmpdir]
from py._test import config
oldconfig = py.test.config
oldconfig = config.config_per_process # py.test.config
try:
c = config.config_per_process = py.test.config = pytestConfig()
c.basetemp = oldconfig.mktemp("reparse", numbered=True)

View File

@ -30,16 +30,13 @@ def getproject(path):
return Project(parent)
class ReSTFile(py.test.collect.File):
def __init__(self, fspath, parent, project=None):
def __init__(self, fspath, parent, project):
super(ReSTFile, self).__init__(fspath=fspath, parent=parent)
if project is None:
project = getproject(fspath)
assert project is not None
self.project = project
def collect(self):
return [
ReSTSyntaxTest(self.project, "ReSTSyntax", parent=self),
ReSTSyntaxTest("ReSTSyntax", parent=self, project=self.project),
LinkCheckerMaker("checklinks", parent=self),
DoctestText("doctest", parent=self),
]
@ -63,8 +60,8 @@ def deindent(s, sep='\n'):
return sep.join(lines)
class ReSTSyntaxTest(py.test.collect.Item):
def __init__(self, project, *args, **kwargs):
super(ReSTSyntaxTest, self).__init__(*args, **kwargs)
def __init__(self, name, parent, project):
super(ReSTSyntaxTest, self).__init__(name=name, parent=parent)
self.project = project
def reportinfo(self):

View File

@ -1,6 +1,5 @@
"""
base test collection objects. Collectors and test Items form a tree
that is usually built iteratively.
test collection nodes, forming a tree, Items are leafs.
"""
import py
@ -33,9 +32,9 @@ class Node(object):
self.fspath = getattr(parent, 'fspath', None)
self.ihook = HookProxy(self)
def _checkcollectable(self):
if not hasattr(self, 'fspath'):
self.parent._memocollect() # to reraise exception
def _reraiseunpicklingproblem(self):
if hasattr(self, '_unpickle_exc'):
py.builtin._reraise(*self._unpickle_exc)
#
# note to myself: Pickling is uh.
@ -46,23 +45,25 @@ class Node(object):
name, parent = nameparent
try:
colitems = parent._memocollect()
except KeyboardInterrupt:
raise
except Exception:
# seems our parent can't collect us
# so let's be somewhat operable
# _checkcollectable() is to tell outsiders about the fact
self.name = name
self.parent = parent
self.config = parent.config
#self._obj = "could not unpickle"
else:
for colitem in colitems:
if colitem.name == name:
# we are a copy that will not be returned
# by our parent
self.__dict__ = colitem.__dict__
break
else:
raise ValueError("item %r not found in parent collection %r" %(
name, [x.name for x in colitems]))
except KeyboardInterrupt:
raise
except Exception:
# our parent can't collect us but we want unpickling to
# otherwise continue - self._reraiseunpicklingproblem() will
# reraise the problem
self._unpickle_exc = py.std.sys.exc_info()
self.name = name
self.parent = parent
self.config = parent.config
def __repr__(self):
if getattr(self.config.option, 'debug', False):
@ -268,15 +269,12 @@ class FSCollector(Collector):
self.fspath = fspath
def __getstate__(self):
if isinstance(self.parent, RootCollector):
relpath = self.parent._getrelpath(self.fspath)
return (relpath, self.parent)
else:
return (self.name, self.parent)
def __setstate__(self, picklestate):
name, parent = picklestate
self.__init__(parent.fspath.join(name), parent=parent)
# RootCollector.getbynames() inserts a directory which we need
# to throw out here for proper re-instantiation
if isinstance(self.parent.parent, RootCollector):
assert self.parent.fspath == self.parent.parent.fspath, self.parent
return (self.name, self.parent.parent) # shortcut
return super(Collector, self).__getstate__()
class File(FSCollector):
""" base class for collecting tests from a file. """
@ -382,6 +380,9 @@ class RootCollector(Directory):
def __init__(self, config):
Directory.__init__(self, config.topdir, parent=None, config=config)
self.name = None
def __repr__(self):
return "<RootCollector fspath=%r>" %(self.fspath,)
def getbynames(self, names):
current = self.consider(self.config.topdir)

View File

@ -15,9 +15,9 @@ def ensuretemp(string, dir=1):
return py.test.config.ensuretemp(string, dir=dir)
class CmdOptions(object):
""" pure container instance for holding cmdline options
as attributes.
"""
""" holds cmdline options as attributes."""
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
return "<CmdOptions %r>" %(self.__dict__,)
@ -31,8 +31,8 @@ class Config(object):
basetemp = None
_sessionclass = None
def __init__(self, topdir=None):
self.option = CmdOptions()
def __init__(self, topdir=None, option=None):
self.option = option or CmdOptions()
self.topdir = topdir
self._parser = parseopt.Parser(
usage="usage: %prog [options] [file_or_dir] [file_or_dir] [...]",
@ -47,9 +47,9 @@ class Config(object):
self.pluginmanager.consider_conftest(conftestmodule)
def _getmatchingplugins(self, fspath):
conftests = self._conftest._conftestpath2mod.values()
allconftests = self._conftest._conftestpath2mod.values()
plugins = [x for x in self.pluginmanager.getplugins()
if x not in conftests]
if x not in allconftests]
plugins += self._conftest.getconftestmodules(fspath)
return plugins
@ -114,20 +114,20 @@ class Config(object):
for path in self.args:
path = py.path.local(path)
l.append(path.relto(self.topdir))
return l, vars(self.option)
return l, self.option.__dict__
def __setstate__(self, repr):
# we have to set py.test.config because loading
# of conftest files may use it (deprecated)
# mainly by py.test.config.addoptions()
py.test.config = self
# next line will registers default plugins
self.__init__(topdir=py.path.local())
self._rootcol = RootCollector(config=self)
global config_per_process
py.test.config = config_per_process = self
args, cmdlineopts = repr
cmdlineopts = CmdOptions(**cmdlineopts)
# next line will registers default plugins
self.__init__(topdir=py.path.local(), option=cmdlineopts)
self._rootcol = RootCollector(config=self)
args = [str(self.topdir.join(x)) for x in args]
self.option = CmdOptions()
self.option.__dict__.update(cmdlineopts)
self._preparse(args)
self._setargs(args)
@ -177,7 +177,7 @@ class Config(object):
def _getcollectclass(self, name, path):
try:
cls = self.getvalue(name, path)
cls = self._conftest.rget(name, path)
except KeyError:
return getattr(py.test.collect, name)
else:

View File

@ -24,6 +24,8 @@ class Session(object):
def genitems(self, colitems, keywordexpr=None):
""" yield Items from iterating over the given colitems. """
if colitems:
colitems = list(colitems)
while colitems:
next = colitems.pop(0)
if isinstance(next, (tuple, list)):

View File

@ -33,21 +33,16 @@ class Option:
def pytest_generate_tests(metafunc):
if "option" in metafunc.funcargnames:
metafunc.addcall(
id="default",
funcargs={'option': Option(verbose=False)}
)
metafunc.addcall(
id="verbose",
funcargs={'option': Option(verbose=True)}
)
if metafunc.config.pluginmanager.hasplugin("xdist"):
nodist = getattr(metafunc.function, 'nodist', False)
if not nodist:
metafunc.addcall(
id="verbose-dist",
funcargs={'option': Option(dist='each', verbose=True)}
)
metafunc.addcall(id="default", param=Option(verbose=False))
metafunc.addcall(id="verbose", param=Option(verbose=True))
if not getattr(metafunc.function, 'nodist', False):
metafunc.addcall(id="verbose-dist",
param=Option(dist='each', verbose=True))
def pytest_funcarg__option(request):
if request.param.dist:
request.config.pluginmanager.skipifmissing("xdist")
return request.param
class TestTerminal:
def test_pass_skip_fail(self, testdir, option):
@ -255,12 +250,19 @@ class TestTerminal:
])
def test_keyboard_interrupt_dist(self, testdir, option):
# xxx could be refined to check for return code
p = testdir.makepyfile("""
raise KeyboardInterrupt
def test_sleep():
import time
time.sleep(10)
""")
result = testdir.runpytest(*option._getcmdargs())
assert result.ret == 2
result.stdout.fnmatch_lines(['*KEYBOARD INTERRUPT*'])
child = testdir.spawn_pytest(" ".join(option._getcmdargs()))
child.expect(".*test session starts.*")
child.kill(2) # keyboard interrupt
child.expect(".*KeyboardInterrupt.*")
#child.expect(".*seconds.*")
child.close()
#assert ret == 2
@py.test.mark.nodist
def test_keyboard_interrupt(self, testdir, option):
@ -593,9 +595,10 @@ def test_terminalreporter_reportopt_conftestsetting(testdir):
assert result.stdout.fnmatch_lines([
"*1 passed*"
])
def test_trace_reporting(self, testdir):
result = testdir.runpytest("--trace")
assert result.stdout.fnmatch_lines([
"*active plugins*"
])
assert result.ret == 0
def test_trace_reporting(testdir):
result = testdir.runpytest("--traceconfig")
assert result.stdout.fnmatch_lines([
"*active plugins*"
])
assert result.ret == 0

View File

@ -51,7 +51,7 @@ def test_new_instances(testdir):
reprec.assertoutcome(passed=2)
def test_teardown(testdir):
testpath = testdir.makepyfile(test_three="""
testpath = testdir.makepyfile("""
import unittest
pytest_plugins = "pytest_unittest" # XXX
class MyTestCase(unittest.TestCase):

View File

@ -246,3 +246,132 @@ def test_preparse_ordering(testdir, monkeypatch):
plugin = config.pluginmanager.getplugin("mytestplugin")
assert plugin.x == 42
import pickle
class TestConfigPickling:
def pytest_funcarg__testdir(self, request):
oldconfig = py.test.config
print("setting py.test.config to None")
py.test.config = None
def resetglobals():
py.builtin.print_("setting py.test.config to", oldconfig)
py.test.config = oldconfig
request.addfinalizer(resetglobals)
return request.getfuncargvalue("testdir")
def test_config_getstate_setstate(self, testdir):
from py._test.config import Config
testdir.makepyfile(__init__="", conftest="x=1; y=2")
hello = testdir.makepyfile(hello="")
tmp = testdir.tmpdir
testdir.chdir()
config1 = testdir.parseconfig(hello)
config2 = Config()
config2.__setstate__(config1.__getstate__())
assert config2.topdir == py.path.local()
config2_relpaths = [py.path.local(x).relto(config2.topdir)
for x in config2.args]
config1_relpaths = [py.path.local(x).relto(config1.topdir)
for x in config1.args]
assert config2_relpaths == config1_relpaths
for name, value in config1.option.__dict__.items():
assert getattr(config2.option, name) == value
assert config2.getvalue("x") == 1
def test_config_pickling_customoption(self, testdir):
testdir.makeconftest("""
def pytest_addoption(parser):
group = parser.getgroup("testing group")
group.addoption('-G', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value.")
""")
config = testdir.parseconfig("-G", "11")
assert config.option.gdest == 11
repr = config.__getstate__()
config = testdir.Config()
py.test.raises(AttributeError, "config.option.gdest")
config2 = testdir.Config()
config2.__setstate__(repr)
assert config2.option.gdest == 11
def test_config_pickling_and_conftest_deprecated(self, testdir):
tmp = testdir.tmpdir.ensure("w1", "w2", dir=1)
tmp.ensure("__init__.py")
tmp.join("conftest.py").write(py.code.Source("""
def pytest_addoption(parser):
group = parser.getgroup("testing group")
group.addoption('-G', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value.")
"""))
config = testdir.parseconfig(tmp, "-G", "11")
assert config.option.gdest == 11
repr = config.__getstate__()
config = testdir.Config()
py.test.raises(AttributeError, "config.option.gdest")
config2 = testdir.Config()
config2.__setstate__(repr)
assert config2.option.gdest == 11
option = config2.addoptions("testing group",
config2.Option('-G', '--glong', action="store", default=42,
type="int", dest="gdest", help="g value."))
assert option.gdest == 11
def test_config_picklability(self, testdir):
config = testdir.parseconfig()
s = pickle.dumps(config)
newconfig = pickle.loads(s)
assert hasattr(newconfig, "topdir")
assert newconfig.topdir == py.path.local()
def test_collector_implicit_config_pickling(self, testdir):
tmpdir = testdir.tmpdir
testdir.chdir()
testdir.makepyfile(hello="def test_x(): pass")
config = testdir.parseconfig(tmpdir)
col = config.getnode(config.topdir)
io = py.io.BytesIO()
pickler = pickle.Pickler(io)
pickler.dump(col)
io.seek(0)
unpickler = pickle.Unpickler(io)
col2 = unpickler.load()
assert col2.name == col.name
assert col2.listnames() == col.listnames()
def test_config_and_collector_pickling(self, testdir):
tmpdir = testdir.tmpdir
dir1 = tmpdir.ensure("sourcedir", "somedir", dir=1)
config = testdir.parseconfig()
assert config.topdir == tmpdir
col = config.getnode(dir1.dirpath())
col1 = config.getnode(dir1)
assert col1.parent == col
io = py.io.BytesIO()
pickler = pickle.Pickler(io)
pickler.dump(col)
pickler.dump(col1)
pickler.dump(col)
io.seek(0)
unpickler = pickle.Unpickler(io)
newtopdir = tmpdir.ensure("newtopdir", dir=1)
newtopdir.mkdir("sourcedir").mkdir("somedir")
old = newtopdir.chdir()
try:
newcol = unpickler.load()
newcol2 = unpickler.load()
newcol3 = unpickler.load()
assert newcol2.config is newcol.config
assert newcol2.parent == newcol
assert newcol2.config.topdir.realpath() == newtopdir.realpath()
newsourcedir = newtopdir.join("sourcedir")
assert newcol.fspath.realpath() == newsourcedir.realpath()
assert newcol2.fspath.basename == dir1.basename
assert newcol2.fspath.relto(newcol2.config.topdir)
finally:
old.chdir()

View File

@ -193,8 +193,3 @@ class TestNewSession(SessionTests):
colfail = [x for x in finished if x.failed]
assert len(colfail) == 1
class TestNewSessionDSession(SessionTests):
def parseconfig(self, *args):
args = ('-n1',) + args
return SessionTests.parseconfig(self, *args)