split up the plugin and extend tox test matrix

This commit is contained in:
Ronny Pfannschmidt
2015-08-08 11:57:08 +02:00
parent 6bab1dab75
commit ec89a3c36b
7 changed files with 106 additions and 98 deletions

View File

@@ -1,6 +1,14 @@
1.13.dev1 1.13.dev
------------------------- -------------------------
- extended the tox matrix with the supported py.test versions
- split up the plugin into 3 plugin's
to prepare the departure of boxed and looponfail
looponfail will become apart of core
and forked boxed will be replaced
with a more reliable primitive based on xdist
- conforming with new pytest-2.8 behavior of returning non-zero when all - conforming with new pytest-2.8 behavior of returning non-zero when all
tests were skipped or deselected. tests were skipped or deselected.
@@ -34,7 +42,7 @@
Thanks to Andreas Pelme for bug analysis and failing test. Thanks to Andreas Pelme for bug analysis and failing test.
- restart crashed nodes by internally refactoring setup handling - restart crashed nodes by internally refactoring setup handling
of nodes. Also includes better code documentation. of nodes. Also includes better code documentation.
Many thanks to Floris Bruynooghe for the complete PR. Many thanks to Floris Bruynooghe for the complete PR.
@@ -47,7 +55,7 @@
- fix pytest issue382 - produce "pytest_runtest_logstart" event again - fix pytest issue382 - produce "pytest_runtest_logstart" event again
in master. Thanks Aron Curzon. in master. Thanks Aron Curzon.
- fix pytest issue419 by sending/receiving indices into the test - fix pytest issue419 by sending/receiving indices into the test
collection instead of node ids (which are not neccessarily unique collection instead of node ids (which are not neccessarily unique
for functions parametrized with duplicate values) for functions parametrized with duplicate values)
@@ -160,4 +168,3 @@
- cleaned up termination handling - cleaned up termination handling
- make -x cause hard killing of test nodes to decrease wait time - make -x cause hard killing of test nodes to decrease wait time
until the traceback shows up on first failure until the traceback shows up on first failure

View File

@@ -11,7 +11,13 @@ setup(
url='http://bitbucket.org/hpk42/pytest-xdist', url='http://bitbucket.org/hpk42/pytest-xdist',
platforms=['linux', 'osx', 'win32'], platforms=['linux', 'osx', 'win32'],
packages = ['xdist'], packages = ['xdist'],
entry_points = {'pytest11': ['xdist = xdist.plugin'],}, entry_points = {
'pytest11': [
'xdist = xdist.plugin',
'xdist.looponfail = xdist.looponfail',
'xdist.boxed = xdist.boxed',
],
},
zip_safe=False, zip_safe=False,
install_requires = ['execnet>=1.1', 'pytest>=2.4.2', 'py>=1.4.22'], install_requires = ['execnet>=1.1', 'pytest>=2.4.2', 'py>=1.4.22'],
classifiers=[ classifiers=[

View File

@@ -13,7 +13,7 @@ def test_dist_incompatibility_messages(testdir):
assert "incompatible" in result.stderr.str() assert "incompatible" in result.stderr.str()
def test_dist_options(testdir): def test_dist_options(testdir):
from xdist.plugin import check_options from xdist.plugin import pytest_cmdline_main as check_options
config = testdir.parseconfigure("-n 2") config = testdir.parseconfigure("-n 2")
check_options(config) check_options(config)
assert config.option.dist == "load" assert config.option.dist == "load"
@@ -67,4 +67,3 @@ class TestDistOptions:
assert py.path.local('y') in roots assert py.path.local('y') in roots
assert py.path.local('z') in roots assert py.path.local('z') in roots
assert testdir.tmpdir.join('x') in roots assert testdir.tmpdir.join('x') in roots

43
tox.ini
View File

@@ -1,43 +1,28 @@
[tox] [tox]
envlist=py26,py33,py34,py27,py27-pexpect,py33-pexpect,py26-old,py33-old,flakes envlist=
py{26,33,34,27}-pytest2{4,5,6,7},py{27,34}-pytest27-pexpect,flakes
[testenv] [testenv]
changedir=testing changedir=testing
deps=pytest>=2.5.1 deps =
commands= py.test {posargs} pycmd
pytest24: pytest~=2.4.0
pytest25: pytest~=2.5.0
[testenv:py27-pexpect] pytest26: pytest~=2.6.1
deps={[testenv]deps} pytest27: pytest~=2.7.2
pexpect pexpect: pexpect
[testenv:py33-pexpect] commands=
deps={[testenv]deps} # always clean to avoid code unmarshal mismatch on old python/pytest
pexpect py.cleanup -aq
py.test {posargs}
[testenv:flakes] [testenv:flakes]
changedir= changedir=
deps = pytest-flakes>=0.2 deps = pytest-flakes>=0.2
commands = py.test --flakes -m flakes testing xdist commands = py.test --flakes -m flakes testing xdist
[testenv:py26-old]
basepython = python2.6
deps=
pytest==2.5.2
pycmd
commands=
py.cleanup -a
py.test {posargs}
[testenv:py33-old]
basepython = python3.3
deps=
pytest==2.5.2
pycmd
commands=
py.cleanup -a
py.test {posargs}
[pytest] [pytest]
addopts = -rsfxX addopts = -rsfxX
;; hello ;; hello

56
xdist/boxed.py Normal file
View File

@@ -0,0 +1,56 @@
import py
def pytest_addoption(parser):
group = parser.getgroup("xdist", "distributed and subprocess testing")
group.addoption('--boxed',
action="store_true", dest="boxed", default=False,
help="box each test run in a separate process (unix)")
def pytest_runtest_protocol(item):
if item.config.getvalue("boxed"):
reports = forked_run_report(item)
for rep in reports:
item.ihook.pytest_runtest_logreport(report=rep)
return True
def forked_run_report(item):
# for now, we run setup/teardown in the subprocess
# XXX optionally allow sharing of setup/teardown
from _pytest.runner import runtestprotocol
EXITSTATUS_TESTEXIT = 4
import marshal
from xdist.remote import serialize_report
from xdist.slavemanage import unserialize_report
def runforked():
try:
reports = runtestprotocol(item, log=False)
except KeyboardInterrupt:
py.std.os._exit(EXITSTATUS_TESTEXIT)
return marshal.dumps([serialize_report(x) for x in reports])
ff = py.process.ForkedFunc(runforked)
result = ff.waitfinish()
if result.retval is not None:
report_dumps = marshal.loads(result.retval)
return [unserialize_report("testreport", x) for x in report_dumps]
else:
if result.exitstatus == EXITSTATUS_TESTEXIT:
py.test.exit("forked test item %s raised Exit" %(item,))
return [report_process_crash(item, result)]
def report_process_crash(item, result):
path, lineno = item._getfslineno()
info = ("%s:%s: running the test CRASHED with signal %d" %
(path, lineno, result.signal))
from _pytest import runner
call = runner.CallInfo(lambda: 0/0, "???")
call.excinfo = info
rep = runner.pytest_runtest_makereport(item, call)
if result.out:
rep.sections.append(("captured stdout", result.out))
if result.err:
rep.sections.append(("captured stderr", result.err))
return rep

View File

@@ -11,6 +11,21 @@ import py, pytest
import sys import sys
import execnet import execnet
def pytest_addoption(parser):
group = parser.getgroup("xdist", "distributed and subprocess testing")
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests in subprocess, wait for modified files "
"and re-run failing test set until all pass.")
def pytest_cmdline_main(config):
if config.getoption("looponfail"):
looponfail_main(config)
return 2 # looponfail only can get stop with ctrl-C anyway
def looponfail_main(config): def looponfail_main(config):
remotecontrol = RemoteControl(config) remotecontrol = RemoteControl(config)
rootdirs = config.getini("looponfailroots") rootdirs = config.getini("looponfailroots")
@@ -227,4 +242,3 @@ class StatRecorder:
changed = True changed = True
self.statcache = newstat self.statcache = newstat
return changed return changed

View File

@@ -5,10 +5,6 @@ import 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")
group._addoption('-f', '--looponfail',
action="store_true", dest="looponfail", default=False,
help="run tests in subprocess, wait for modified files "
"and re-run failing test set until all pass.")
group._addoption('-n', dest="numprocesses", metavar="numprocesses", group._addoption('-n', dest="numprocesses", metavar="numprocesses",
action="store", action="store",
help="shortcut for '--dist=load --tx=NUM*popen', " help="shortcut for '--dist=load --tx=NUM*popen', "
@@ -17,9 +13,6 @@ def pytest_addoption(parser):
group._addoption('--max-slave-restart', action="store", default=None, group._addoption('--max-slave-restart', action="store", default=None,
help="maximum number of slaves that can be restarted " help="maximum number of slaves that can be restarted "
"when crashed (set to zero to disable this feature)") "when crashed (set to zero to disable this feature)")
group.addoption('--boxed',
action="store_true", dest="boxed", default=False,
help="box each test run in a separate process (unix)")
group._addoption('--dist', metavar="distmode", group._addoption('--dist', metavar="distmode",
action="store", choices=['load', 'each', 'no'], action="store", choices=['load', 'each', 'no'],
type="choice", dest="dist", default="no", type="choice", dest="dist", default="no",
@@ -62,12 +55,6 @@ def pytest_addhooks(pluginmanager):
# distributed testing initialization # distributed testing initialization
# ------------------------------------------------------------------------- # -------------------------------------------------------------------------
def pytest_cmdline_main(config):
check_options(config)
if config.getoption("looponfail"):
from xdist.looponfail import looponfail_main
looponfail_main(config)
return 2 # looponfail only can get stop with ctrl-C anyway
@pytest.mark.trylast @pytest.mark.trylast
def pytest_configure(config): def pytest_configure(config):
@@ -78,7 +65,8 @@ def pytest_configure(config):
tr = config.pluginmanager.getplugin("terminalreporter") tr = config.pluginmanager.getplugin("terminalreporter")
tr.showfspath = False tr.showfspath = False
def check_options(config): @pytest.mark.tryfirst
def pytest_cmdline_main(config):
if config.option.numprocesses: if config.option.numprocesses:
if config.option.numprocesses == 'auto': if config.option.numprocesses == 'auto':
config.option.numprocesses = multiprocessing.cpu_count() config.option.numprocesses = multiprocessing.cpu_count()
@@ -100,50 +88,3 @@ def check_options(config):
elif val("dist") != "no": elif val("dist") != "no":
if usepdb: if usepdb:
raise pytest.UsageError("--pdb incompatible with distributing tests.") raise pytest.UsageError("--pdb incompatible with distributing tests.")
def pytest_runtest_protocol(item):
if item.config.getvalue("boxed"):
reports = forked_run_report(item)
for rep in reports:
item.ihook.pytest_runtest_logreport(report=rep)
return True
def forked_run_report(item):
# for now, we run setup/teardown in the subprocess
# XXX optionally allow sharing of setup/teardown
from _pytest.runner import runtestprotocol
EXITSTATUS_TESTEXIT = 4
import marshal
from xdist.remote import serialize_report
from xdist.slavemanage import unserialize_report
def runforked():
try:
reports = runtestprotocol(item, log=False)
except KeyboardInterrupt:
py.std.os._exit(EXITSTATUS_TESTEXIT)
return marshal.dumps([serialize_report(x) for x in reports])
ff = py.process.ForkedFunc(runforked)
result = ff.waitfinish()
if result.retval is not None:
report_dumps = marshal.loads(result.retval)
return [unserialize_report("testreport", x) for x in report_dumps]
else:
if result.exitstatus == EXITSTATUS_TESTEXIT:
py.test.exit("forked test item %s raised Exit" %(item,))
return [report_process_crash(item, result)]
def report_process_crash(item, result):
path, lineno = item._getfslineno()
info = ("%s:%s: running the test CRASHED with signal %d" %
(path, lineno, result.signal))
from _pytest import runner
call = runner.CallInfo(lambda: 0/0, "???")
call.excinfo = info
rep = runner.pytest_runtest_makereport(item, call)
if result.out:
rep.sections.append(("captured stdout", result.out))
if result.err:
rep.sections.append(("captured stderr", result.err))
return rep