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.
@@ -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