refine tests and refine code to deal with new xdist semantics.
--HG-- branch : trunk
This commit is contained in:
parent
f5e9d91f7b
commit
95de17b652
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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)):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
Loading…
Reference in New Issue