adapt to pytest changes, add looponfailingdirs ini-option

This commit is contained in:
holger krekel
2010-11-06 09:58:00 +01:00
parent 985c9163b7
commit 9d55d3c831
9 changed files with 48 additions and 37 deletions

View File

@@ -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',

View File

@@ -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")

View File

@@ -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())

View File

@@ -16,5 +16,5 @@ deps=
pytest pytest
pypi pexpect pypi pexpect
[pytest] #[pytest]
addopts = -rf #addopts = -rf

View File

@@ -1,2 +1,2 @@
# #
__version__ = '1.5a4' __version__ = '1.5a5'

View File

@@ -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)
@@ -350,6 +350,6 @@ def enrich_report_with_platform_data(rep, node):
ver = "%s.%s.%s" % d['version_info'][:3] ver = "%s.%s.%s" % d['version_info'][:3]
infoline = "[%s] %s -- Python %s %s" % ( infoline = "[%s] %s -- Python %s %s" % (
d['id'], d['sysplatform'], ver, d['executable']) d['id'], d['sysplatform'], ver, d['executable'])
# XXX more structured longrepr? # XXX more structured longrepr?
rep.longrepr = infoline + "\n\n" + str(rep.longrepr) rep.longrepr = infoline + "\n\n" + str(rep.longrepr)

View File

@@ -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):

View File

@@ -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):

View File

@@ -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):