integrate xdist related reporting into the plugin

This commit is contained in:
holger krekel
2010-07-07 12:43:19 +02:00
parent 51b0ba55e9
commit fb31aa882d
9 changed files with 155 additions and 11 deletions

View File

@@ -1,3 +1,10 @@
1.4a1
-------------------------
- perform distributed testing related reporting in the plugin
rather than having dist-related code in the generic py.test
distribution
1.3
-------------------------

View File

@@ -22,7 +22,7 @@ setup(
packages = ['xdist'],
entry_points = {'pytest11': ['xdist = xdist.plugin'],},
zip_safe=False,
install_requires = ['execnet>=1.0.6', 'py>=1.3.1'],
install_requires = ['execnet>=1.0.6', 'py>1.3.1'],
classifiers=[
'Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers',

View File

@@ -1,4 +1,5 @@
import py
import sys
class TestDistribution:
def test_manytests_to_one_popen(self, testdir):
@@ -173,3 +174,57 @@ class TestDistribution:
assert result.ret
assert 'SIGINT' in s
assert 's2call' in s
def test_keyboard_interrupt_dist(self, testdir):
# xxx could be refined to check for return code
p = testdir.makepyfile("""
def test_sleep():
import time
time.sleep(10)
""")
child = testdir.spawn_pytest("-n1")
child.expect(".*test session starts.*")
child.kill(2) # keyboard interrupt
child.expect(".*KeyboardInterrupt.*")
#child.expect(".*seconds.*")
child.close()
#assert ret == 2
class TestTerminalReporting:
def test_pass_skip_fail(self, testdir):
p = testdir.makepyfile("""
import py
def test_ok():
pass
def test_skip():
py.test.skip("xx")
def test_func():
assert 0
""")
result = testdir.runpytest("-n1", "-v")
expected = [
"*PASS*test_pass_skip_fail.py:2: *test_ok*",
"*SKIP*test_pass_skip_fail.py:4: *test_skip*",
"*FAIL*test_pass_skip_fail.py:6: *test_func*",
]
for line in expected:
result.stdout.fnmatch_lines([line])
result.stdout.fnmatch_lines([
" def test_func():",
"> assert 0",
"E assert 0",
])
def test_fail_platinfo(self, testdir):
p = testdir.makepyfile("""
def test_func():
assert 0
""")
result = testdir.runpytest("-n1", "-v")
result.stdout.fnmatch_lines([
"*FAIL*test_fail_platinfo.py:1: *test_func*",
"*popen*Python*",
" def test_func():",
"> assert 0",
"E assert 0",
])

View File

@@ -2,7 +2,6 @@ import py
import execnet
pytest_plugins = "pytester"
option_report = 'skipped'
#rsyncdirs = ['.', '../xdist', py.path.local(execnet.__file__).dirpath()]

View File

@@ -132,7 +132,7 @@ class TestLooponFailing:
x = 0
assert x == 1
""")
child = testdir.spawn_pytest("-f %s" % p)
child = testdir.spawn_pytest("-f %s --traceconfig" % p)
child.expect("def test_one")
child.expect("x == 1")
child.expect("1 failed")

17
tox.ini
View File

@@ -1,5 +1,6 @@
[tox]
distshare={homedir}/.tox/distshare
envlist=py26,py31,py27,py25,py24
[tox:hudson]
distshare={toxworkdir}/distshare
sdistsrc={distshare}/pytest-xdist-**LATEST**
@@ -9,15 +10,21 @@ changedir=testing
deps=
{distshare}/py-**LATEST**
{distshare}/execnet-**LATEST**
commands=py.test -rsfxX --junitxml={envlogdir}/junit-{envname}.xml []
commands=
py.test -rsfxX --tools-on-path \
--junitxml={envlogdir}/junit-{envname}.xml []
[testenv:py27]
basepython=python2.7
[testenv:py26]
basepython=python2.6
[testenv:py26-py131]
basepython=python2.6
deps= py==1.3.1
execnet==1.0.6
deps=
{distshare}/py-**LATEST**
{distshare}/execnet-**LATEST**
pexpect
#[testenv:py26-py132]
#basepython=python2.6
#deps= py==1.3.2
# execnet==1.0.6
[testenv:py25]
basepython=python2.5
[testenv:py24]

View File

@@ -20,6 +20,4 @@ def pytest_testnodedown(node, error):
def pytest_rescheduleitems(items):
""" reschedule Items from a node that went down. """
def pytest_looponfailinfo(failreports, rootdirs):
""" info for repeating failing tests. """

View File

@@ -197,11 +197,23 @@ def pytest_configure(config):
raise config.Error("--pdb incompatible with --looponfail.")
from xdist.remote import LooponfailingSession
config.setsessionclass(LooponfailingSession)
config._isdistsession = True
elif val("dist") != "no":
if usepdb:
raise config.Error("--pdb incompatible with distributing tests.")
from xdist.dsession import DSession
config.setsessionclass(DSession)
config._isdistsession = True
def pytest_sessionstart(session):
config = session.config
if hasattr(config, '_isdistsession'):
if not config.pluginmanager.hasplugin("terminal") or \
not config.pluginmanager.hasplugin("terminalreporter"):
return
trdist = TerminalDistReporter(config)
config.pluginmanager.register(trdist, "terminaldistreporter")
def pytest_runtest_protocol(item):
if item.config.getvalue("boxed"):
@@ -244,3 +256,56 @@ def report_process_crash(item, result):
from py._plugin.pytest_runner import ItemTestReport
return ItemTestReport(item, excinfo=info, when="???")
class TerminalDistReporter:
def __init__(self, config):
self.gateway2info = {}
self.config = config
self.tplugin = config.pluginmanager.getplugin("terminal")
self.tr = config.pluginmanager.getplugin("terminalreporter")
def write_line(self, msg):
self.tr.write_line(msg)
def pytest_itemstart(self, __multicall__):
try:
__multicall__.methods.remove(self.tr.pytest_itemstart)
except KeyError:
pass
def pytest_runtest_logreport(self, report):
if hasattr(report, 'node'):
report.headerlines.append(self.gateway2info.get(
report.node.gateway,
"node %r (platinfo not found? strange)"))
def pytest_gwmanage_newgateway(self, gateway, platinfo):
#self.write_line("%s instantiated gateway from spec %r" %(gateway.id, gateway.spec._spec))
d = {}
d['version'] = self.tplugin.repr_pythonversion(platinfo.version_info)
d['id'] = gateway.id
d['spec'] = gateway.spec._spec
d['platform'] = platinfo.platform
if self.config.option.verbose:
d['extra'] = "- " + platinfo.executable
else:
d['extra'] = ""
d['cwd'] = platinfo.cwd
infoline = ("[%(id)s] %(spec)s -- platform %(platform)s, "
"Python %(version)s "
"cwd: %(cwd)s"
"%(extra)s" % d)
self.write_line(infoline)
self.gateway2info[gateway] = infoline
def pytest_testnodeready(self, node):
self.write_line("[%s] txnode ready to receive tests" %(node.gateway.id,))
def pytest_testnodedown(self, node, error):
if not error:
return
self.write_line("[%s] node down, error: %s" %(node.gateway.id, error))
def pytest_rescheduleitems(self, items):
if self.config.option.debug:
self.write_sep("!", "RESCHEDULING %s " %(items,))

View File

@@ -169,8 +169,21 @@ def slave_runsession(channel, config, fullwidth, hasmarkup):
DEBUG("SLAVE: starting session.main()")
session.main(colitems)
session.config.hook.pytest_looponfailinfo(
repr_pytest_looponfailinfo(
failreports=list(failreports),
rootdirs=[config.topdir])
rootcol = session.config._rootcol
channel.send([rootcol.totrail(rep.getnode()) for rep in failreports])
def repr_pytest_looponfailinfo(failreports, rootdirs):
tr = py.io.TerminalWriter()
if failreports:
tr.sep("#", "LOOPONFAILING", red=True)
for report in failreports:
loc = report._getcrashline()
if loc:
tr.line(loc, red=True)
tr.sep("#", "waiting for changes")
for rootdir in rootdirs:
tr.line("### Watching: %s" %(rootdir,), bold=True)