split up the plugin and extend tox test matrix
This commit is contained in:
15
CHANGELOG
15
CHANGELOG
@@ -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
|
||||||
|
|
||||||
|
|||||||
8
setup.py
8
setup.py
@@ -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=[
|
||||||
|
|||||||
@@ -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
43
tox.ini
@@ -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
56
xdist/boxed.py
Normal 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
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|||||||
Reference in New Issue
Block a user