adapt to pytest changes, add looponfailingdirs ini-option
This commit is contained in:
@@ -1,2 +1,2 @@
|
||||
#
|
||||
__version__ = '1.5a4'
|
||||
__version__ = '1.5a5'
|
||||
@@ -301,7 +301,7 @@ class DSession:
|
||||
runner = self.config.pluginmanager.getplugin("runner")
|
||||
fspath = nodeid.split("::")[0]
|
||||
msg = "Slave %r crashed while running %r" %(slave.gateway.id, nodeid)
|
||||
rep = runner.TestReport(nodeid, (), fspath, (fspath, None, fspath), (),
|
||||
rep = runner.TestReport(nodeid, (fspath, None, fspath), (),
|
||||
"failed", msg, "???")
|
||||
enrich_report_with_platform_data(rep, slave)
|
||||
self.config.hook.pytest_runtest_logreport(report=rep)
|
||||
@@ -350,6 +350,6 @@ def enrich_report_with_platform_data(rep, node):
|
||||
ver = "%s.%s.%s" % d['version_info'][:3]
|
||||
infoline = "[%s] %s -- Python %s %s" % (
|
||||
d['id'], d['sysplatform'], ver, d['executable'])
|
||||
# XXX more structured longrepr?
|
||||
# XXX more structured longrepr?
|
||||
rep.longrepr = infoline + "\n\n" + str(rep.longrepr)
|
||||
|
||||
|
||||
@@ -7,21 +7,22 @@
|
||||
the controlling process which should best never happen.
|
||||
"""
|
||||
|
||||
import py
|
||||
import py, pytest
|
||||
import sys
|
||||
import execnet
|
||||
|
||||
def looponfail_main(config):
|
||||
remotecontrol = RemoteControl(config)
|
||||
# XXX better configure rootdir
|
||||
gettopdir = config.pluginmanager.getplugin("session").gettopdir
|
||||
rootdirs = [gettopdir(config.args)]
|
||||
rootdirs = config.getini("looponfailroots")
|
||||
statrecorder = StatRecorder(rootdirs)
|
||||
try:
|
||||
while 1:
|
||||
remotecontrol.loop_once()
|
||||
if not remotecontrol.failures and remotecontrol.wasfailing:
|
||||
continue # the last failures passed, let's immediately rerun all
|
||||
repr_pytest_looponfailinfo(
|
||||
failreports=remotecontrol.failures,
|
||||
rootdirs=rootdirs)
|
||||
statrecorder.waitonchange(checkinterval=2.0)
|
||||
except KeyboardInterrupt:
|
||||
print()
|
||||
@@ -29,7 +30,6 @@ def looponfail_main(config):
|
||||
class RemoteControl(object):
|
||||
def __init__(self, config):
|
||||
self.config = config
|
||||
self.remote_topdir = None
|
||||
self.failures = []
|
||||
|
||||
def trace(self, *args):
|
||||
@@ -70,8 +70,8 @@ class RemoteControl(object):
|
||||
|
||||
def runsession(self):
|
||||
try:
|
||||
self.trace("sending", (self.remote_topdir, self.failures))
|
||||
self.channel.send((self.remote_topdir, self.failures))
|
||||
self.trace("sending", self.failures)
|
||||
self.channel.send(self.failures)
|
||||
try:
|
||||
return self.channel.receive()
|
||||
except self.channel.RemoteError:
|
||||
@@ -85,15 +85,11 @@ class RemoteControl(object):
|
||||
self.setup()
|
||||
self.wasfailing = self.failures and len(self.failures)
|
||||
result = self.runsession()
|
||||
topdir, failures, reports, collection_failed = result
|
||||
failures, reports, collection_failed = result
|
||||
if collection_failed:
|
||||
reports = ["Collection failed, keeping previous failure set"]
|
||||
else:
|
||||
self.remote_topdir, self.failures = topdir, failures
|
||||
|
||||
repr_pytest_looponfailinfo(
|
||||
failreports=reports,
|
||||
rootdirs=[self.remote_topdir],)
|
||||
self.failures = failures
|
||||
|
||||
def repr_pytest_looponfailinfo(failreports, rootdirs):
|
||||
tr = py.io.TerminalWriter()
|
||||
@@ -147,23 +143,29 @@ class SlaveFailSession:
|
||||
|
||||
def pytest_collection(self, session):
|
||||
self.session = session
|
||||
self.collection = session.collection
|
||||
self.topdir, self.trails = self.current_command
|
||||
if self.topdir and self.trails:
|
||||
self.topdir = py.path.local(self.topdir)
|
||||
self.collection.topdir = self.topdir
|
||||
self.collection = collection = session.collection
|
||||
self.trails = self.current_command
|
||||
hook = self.collection.ihook
|
||||
try:
|
||||
items = collection.perform_collect(self.trails or None)
|
||||
except pytest.UsageError:
|
||||
items = collection.perform_collect(None)
|
||||
hook.pytest_collection_modifyitems(config=session.config, items=items)
|
||||
hook.pytest_collection_finish(collection=collection)
|
||||
return True
|
||||
|
||||
if self.trails:
|
||||
col = self.collection
|
||||
items = []
|
||||
for trail in self.trails:
|
||||
names = col._parsearg(trail, base=self.topdir)
|
||||
names = col._parsearg(trail)
|
||||
try:
|
||||
for node in col.matchnodes([col._topcollector], names):
|
||||
items.extend(col.genitems(node))
|
||||
except self.config.Error:
|
||||
except pytest.UsageError:
|
||||
pass # ignore collect errors / vanished tests
|
||||
self.collection.items = items
|
||||
return True
|
||||
self.topdir = session.collection.topdir
|
||||
|
||||
def pytest_runtest_logreport(self, report):
|
||||
if report.failed:
|
||||
@@ -189,8 +191,7 @@ class SlaveFailSession:
|
||||
loc = rep.longrepr
|
||||
loc = str(getattr(loc, 'reprcrash', loc))
|
||||
failreports.append(loc)
|
||||
topdir = str(self.topdir)
|
||||
self.channel.send((topdir, trails, failreports, self.collection_failed))
|
||||
self.channel.send((trails, failreports, self.collection_failed))
|
||||
|
||||
class StatRecorder:
|
||||
def __init__(self, rootdirlist):
|
||||
|
||||
@@ -142,7 +142,7 @@ where the configuration file was found.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import py
|
||||
import py, pytest
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("xdist", "distributed and subprocess testing")
|
||||
@@ -178,6 +178,8 @@ def pytest_addoption(parser):
|
||||
' remote distributed testing.', type="pathlist")
|
||||
parser.addini('rsyncignore', 'list of (relative) paths to be ignored '
|
||||
'for rsyncing.', type="pathlist")
|
||||
parser.addini("looponfailroots", type="pathlist",
|
||||
help="directories to check for changes", default=[py.path.local()])
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# distributed testing hooks
|
||||
@@ -218,10 +220,10 @@ def check_options(config):
|
||||
usepdb = config.option.usepdb # a core option
|
||||
if val("looponfail"):
|
||||
if usepdb:
|
||||
raise config.Error("--pdb incompatible with --looponfail.")
|
||||
raise pytest.UsageError("--pdb incompatible with --looponfail.")
|
||||
elif val("dist") != "no":
|
||||
if usepdb:
|
||||
raise config.Error("--pdb incompatible with distributing tests.")
|
||||
raise pytest.UsageError("--pdb incompatible with distributing tests.")
|
||||
|
||||
|
||||
def pytest_runtest_protocol(item):
|
||||
|
||||
@@ -53,8 +53,8 @@ class SlaveInteractor:
|
||||
if name == "runtests":
|
||||
ids = kwargs['ids']
|
||||
for nodeid in ids:
|
||||
for item in self.collection.getbyid(nodeid):
|
||||
self.config.hook.pytest_runtest_protocol(item=item)
|
||||
item = self._id2item[nodeid]
|
||||
self.config.hook.pytest_runtest_protocol(item=item)
|
||||
elif name == "runtests_all":
|
||||
for item in self.collection.items:
|
||||
self.config.hook.pytest_runtest_protocol(item=item)
|
||||
@@ -63,9 +63,13 @@ class SlaveInteractor:
|
||||
return True
|
||||
|
||||
def pytest_collection_finish(self, collection):
|
||||
ids = [collection.getid(item) for item in collection.items]
|
||||
self._id2item = {}
|
||||
ids = []
|
||||
for item in collection.items:
|
||||
self._id2item[item.nodeid] = item
|
||||
ids.append(item.nodeid)
|
||||
self.sendevent("collectionfinish",
|
||||
topdir=str(collection.topdir),
|
||||
topdir=str(collection.fspath),
|
||||
ids=ids)
|
||||
|
||||
#def pytest_runtest_logstart(self, nodeid, location, fspath):
|
||||
|
||||
Reference in New Issue
Block a user