adapt to pytest changes, add looponfailingdirs ini-option
This commit is contained in:
2
setup.py
2
setup.py
@@ -10,7 +10,7 @@ from setuptools import setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="pytest-xdist",
|
name="pytest-xdist",
|
||||||
version='1.5a4',
|
version='1.5a5',
|
||||||
description='py.test xdist plugin for distributed testing and loop-on-failing modes',
|
description='py.test xdist plugin for distributed testing and loop-on-failing modes',
|
||||||
long_description=__doc__,
|
long_description=__doc__,
|
||||||
license='GPLv2 or later',
|
license='GPLv2 or later',
|
||||||
|
|||||||
@@ -156,6 +156,8 @@ class TestSlaveInteractor:
|
|||||||
assert not ev.kwargs
|
assert not ev.kwargs
|
||||||
ev = slave.popevent()
|
ev = slave.popevent()
|
||||||
assert ev.name == "collectreport"
|
assert ev.name == "collectreport"
|
||||||
|
ev = slave.popevent()
|
||||||
|
assert ev.name == "collectreport"
|
||||||
rep = unserialize_report(ev.name, ev.kwargs['data'])
|
rep = unserialize_report(ev.name, ev.kwargs['data'])
|
||||||
assert rep.skipped
|
assert rep.skipped
|
||||||
ev = slave.popevent("collectionfinish")
|
ev = slave.popevent("collectionfinish")
|
||||||
@@ -168,6 +170,8 @@ class TestSlaveInteractor:
|
|||||||
assert not ev.kwargs
|
assert not ev.kwargs
|
||||||
ev = slave.popevent()
|
ev = slave.popevent()
|
||||||
assert ev.name == "collectreport"
|
assert ev.name == "collectreport"
|
||||||
|
ev = slave.popevent()
|
||||||
|
assert ev.name == "collectreport"
|
||||||
rep = unserialize_report(ev.name, ev.kwargs['data'])
|
rep = unserialize_report(ev.name, ev.kwargs['data'])
|
||||||
assert rep.failed
|
assert rep.failed
|
||||||
ev = slave.popevent("collectionfinish")
|
ev = slave.popevent("collectionfinish")
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ def pytest_funcarg__hookrecorder(request):
|
|||||||
|
|
||||||
def pytest_funcarg__hook(request):
|
def pytest_funcarg__hook(request):
|
||||||
from xdist import newhooks
|
from xdist import newhooks
|
||||||
from pytest._core import HookRelay, PluginManager
|
from pytest.main import HookRelay, PluginManager
|
||||||
from pytest import hookspec
|
from pytest import hookspec
|
||||||
return HookRelay([hookspec, newhooks], PluginManager())
|
return HookRelay([hookspec, newhooks], PluginManager())
|
||||||
|
|
||||||
|
|||||||
4
tox.ini
4
tox.ini
@@ -16,5 +16,5 @@ deps=
|
|||||||
pytest
|
pytest
|
||||||
pypi pexpect
|
pypi pexpect
|
||||||
|
|
||||||
[pytest]
|
#[pytest]
|
||||||
addopts = -rf
|
#addopts = -rf
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
#
|
#
|
||||||
__version__ = '1.5a4'
|
__version__ = '1.5a5'
|
||||||
@@ -301,7 +301,7 @@ class DSession:
|
|||||||
runner = self.config.pluginmanager.getplugin("runner")
|
runner = self.config.pluginmanager.getplugin("runner")
|
||||||
fspath = nodeid.split("::")[0]
|
fspath = nodeid.split("::")[0]
|
||||||
msg = "Slave %r crashed while running %r" %(slave.gateway.id, nodeid)
|
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, "???")
|
"failed", msg, "???")
|
||||||
enrich_report_with_platform_data(rep, slave)
|
enrich_report_with_platform_data(rep, slave)
|
||||||
self.config.hook.pytest_runtest_logreport(report=rep)
|
self.config.hook.pytest_runtest_logreport(report=rep)
|
||||||
|
|||||||
@@ -7,21 +7,22 @@
|
|||||||
the controlling process which should best never happen.
|
the controlling process which should best never happen.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import py
|
import py, pytest
|
||||||
import sys
|
import sys
|
||||||
import execnet
|
import execnet
|
||||||
|
|
||||||
def looponfail_main(config):
|
def looponfail_main(config):
|
||||||
remotecontrol = RemoteControl(config)
|
remotecontrol = RemoteControl(config)
|
||||||
# XXX better configure rootdir
|
rootdirs = config.getini("looponfailroots")
|
||||||
gettopdir = config.pluginmanager.getplugin("session").gettopdir
|
|
||||||
rootdirs = [gettopdir(config.args)]
|
|
||||||
statrecorder = StatRecorder(rootdirs)
|
statrecorder = StatRecorder(rootdirs)
|
||||||
try:
|
try:
|
||||||
while 1:
|
while 1:
|
||||||
remotecontrol.loop_once()
|
remotecontrol.loop_once()
|
||||||
if not remotecontrol.failures and remotecontrol.wasfailing:
|
if not remotecontrol.failures and remotecontrol.wasfailing:
|
||||||
continue # the last failures passed, let's immediately rerun all
|
continue # the last failures passed, let's immediately rerun all
|
||||||
|
repr_pytest_looponfailinfo(
|
||||||
|
failreports=remotecontrol.failures,
|
||||||
|
rootdirs=rootdirs)
|
||||||
statrecorder.waitonchange(checkinterval=2.0)
|
statrecorder.waitonchange(checkinterval=2.0)
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
print()
|
print()
|
||||||
@@ -29,7 +30,6 @@ def looponfail_main(config):
|
|||||||
class RemoteControl(object):
|
class RemoteControl(object):
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.remote_topdir = None
|
|
||||||
self.failures = []
|
self.failures = []
|
||||||
|
|
||||||
def trace(self, *args):
|
def trace(self, *args):
|
||||||
@@ -70,8 +70,8 @@ class RemoteControl(object):
|
|||||||
|
|
||||||
def runsession(self):
|
def runsession(self):
|
||||||
try:
|
try:
|
||||||
self.trace("sending", (self.remote_topdir, self.failures))
|
self.trace("sending", self.failures)
|
||||||
self.channel.send((self.remote_topdir, self.failures))
|
self.channel.send(self.failures)
|
||||||
try:
|
try:
|
||||||
return self.channel.receive()
|
return self.channel.receive()
|
||||||
except self.channel.RemoteError:
|
except self.channel.RemoteError:
|
||||||
@@ -85,15 +85,11 @@ class RemoteControl(object):
|
|||||||
self.setup()
|
self.setup()
|
||||||
self.wasfailing = self.failures and len(self.failures)
|
self.wasfailing = self.failures and len(self.failures)
|
||||||
result = self.runsession()
|
result = self.runsession()
|
||||||
topdir, failures, reports, collection_failed = result
|
failures, reports, collection_failed = result
|
||||||
if collection_failed:
|
if collection_failed:
|
||||||
reports = ["Collection failed, keeping previous failure set"]
|
reports = ["Collection failed, keeping previous failure set"]
|
||||||
else:
|
else:
|
||||||
self.remote_topdir, self.failures = topdir, failures
|
self.failures = failures
|
||||||
|
|
||||||
repr_pytest_looponfailinfo(
|
|
||||||
failreports=reports,
|
|
||||||
rootdirs=[self.remote_topdir],)
|
|
||||||
|
|
||||||
def repr_pytest_looponfailinfo(failreports, rootdirs):
|
def repr_pytest_looponfailinfo(failreports, rootdirs):
|
||||||
tr = py.io.TerminalWriter()
|
tr = py.io.TerminalWriter()
|
||||||
@@ -147,23 +143,29 @@ class SlaveFailSession:
|
|||||||
|
|
||||||
def pytest_collection(self, session):
|
def pytest_collection(self, session):
|
||||||
self.session = session
|
self.session = session
|
||||||
self.collection = session.collection
|
self.collection = collection = session.collection
|
||||||
self.topdir, self.trails = self.current_command
|
self.trails = self.current_command
|
||||||
if self.topdir and self.trails:
|
hook = self.collection.ihook
|
||||||
self.topdir = py.path.local(self.topdir)
|
try:
|
||||||
self.collection.topdir = self.topdir
|
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
|
col = self.collection
|
||||||
items = []
|
items = []
|
||||||
for trail in self.trails:
|
for trail in self.trails:
|
||||||
names = col._parsearg(trail, base=self.topdir)
|
names = col._parsearg(trail)
|
||||||
try:
|
try:
|
||||||
for node in col.matchnodes([col._topcollector], names):
|
for node in col.matchnodes([col._topcollector], names):
|
||||||
items.extend(col.genitems(node))
|
items.extend(col.genitems(node))
|
||||||
except self.config.Error:
|
except pytest.UsageError:
|
||||||
pass # ignore collect errors / vanished tests
|
pass # ignore collect errors / vanished tests
|
||||||
self.collection.items = items
|
self.collection.items = items
|
||||||
return True
|
return True
|
||||||
self.topdir = session.collection.topdir
|
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report):
|
||||||
if report.failed:
|
if report.failed:
|
||||||
@@ -189,8 +191,7 @@ class SlaveFailSession:
|
|||||||
loc = rep.longrepr
|
loc = rep.longrepr
|
||||||
loc = str(getattr(loc, 'reprcrash', loc))
|
loc = str(getattr(loc, 'reprcrash', loc))
|
||||||
failreports.append(loc)
|
failreports.append(loc)
|
||||||
topdir = str(self.topdir)
|
self.channel.send((trails, failreports, self.collection_failed))
|
||||||
self.channel.send((topdir, trails, failreports, self.collection_failed))
|
|
||||||
|
|
||||||
class StatRecorder:
|
class StatRecorder:
|
||||||
def __init__(self, rootdirlist):
|
def __init__(self, rootdirlist):
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ where the configuration file was found.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import py
|
import py, pytest
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
group = parser.getgroup("xdist", "distributed and subprocess testing")
|
group = parser.getgroup("xdist", "distributed and subprocess testing")
|
||||||
@@ -178,6 +178,8 @@ def pytest_addoption(parser):
|
|||||||
' remote distributed testing.', type="pathlist")
|
' remote distributed testing.', type="pathlist")
|
||||||
parser.addini('rsyncignore', 'list of (relative) paths to be ignored '
|
parser.addini('rsyncignore', 'list of (relative) paths to be ignored '
|
||||||
'for rsyncing.', type="pathlist")
|
'for rsyncing.', type="pathlist")
|
||||||
|
parser.addini("looponfailroots", type="pathlist",
|
||||||
|
help="directories to check for changes", default=[py.path.local()])
|
||||||
|
|
||||||
# -------------------------------------------------------------------------
|
# -------------------------------------------------------------------------
|
||||||
# distributed testing hooks
|
# distributed testing hooks
|
||||||
@@ -218,10 +220,10 @@ def check_options(config):
|
|||||||
usepdb = config.option.usepdb # a core option
|
usepdb = config.option.usepdb # a core option
|
||||||
if val("looponfail"):
|
if val("looponfail"):
|
||||||
if usepdb:
|
if usepdb:
|
||||||
raise config.Error("--pdb incompatible with --looponfail.")
|
raise pytest.UsageError("--pdb incompatible with --looponfail.")
|
||||||
elif val("dist") != "no":
|
elif val("dist") != "no":
|
||||||
if usepdb:
|
if usepdb:
|
||||||
raise config.Error("--pdb incompatible with distributing tests.")
|
raise pytest.UsageError("--pdb incompatible with distributing tests.")
|
||||||
|
|
||||||
|
|
||||||
def pytest_runtest_protocol(item):
|
def pytest_runtest_protocol(item):
|
||||||
|
|||||||
@@ -53,8 +53,8 @@ class SlaveInteractor:
|
|||||||
if name == "runtests":
|
if name == "runtests":
|
||||||
ids = kwargs['ids']
|
ids = kwargs['ids']
|
||||||
for nodeid in ids:
|
for nodeid in ids:
|
||||||
for item in self.collection.getbyid(nodeid):
|
item = self._id2item[nodeid]
|
||||||
self.config.hook.pytest_runtest_protocol(item=item)
|
self.config.hook.pytest_runtest_protocol(item=item)
|
||||||
elif name == "runtests_all":
|
elif name == "runtests_all":
|
||||||
for item in self.collection.items:
|
for item in self.collection.items:
|
||||||
self.config.hook.pytest_runtest_protocol(item=item)
|
self.config.hook.pytest_runtest_protocol(item=item)
|
||||||
@@ -63,9 +63,13 @@ class SlaveInteractor:
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def pytest_collection_finish(self, collection):
|
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",
|
self.sendevent("collectionfinish",
|
||||||
topdir=str(collection.topdir),
|
topdir=str(collection.fspath),
|
||||||
ids=ids)
|
ids=ids)
|
||||||
|
|
||||||
#def pytest_runtest_logstart(self, nodeid, location, fspath):
|
#def pytest_runtest_logstart(self, nodeid, location, fspath):
|
||||||
|
|||||||
Reference in New Issue
Block a user