Add --max-slave-restart option

Also changed wording used from "failed node" to "crashed slave", to conform
with other messages ("slave sw0 crashed")
This commit is contained in:
Bruno Oliveira
2015-07-11 13:12:12 -03:00
parent 9d4afbdfec
commit 4b1ddb9c81
5 changed files with 65 additions and 6 deletions

View File

@@ -1,6 +1,9 @@
1.13.dev1 1.13.dev1
------------------------- -------------------------
- new "--max-slave-restart" option that can be used to control maximum
number of times pytest-xdist can restart slaves due to crashes. Thanks to
Anatoly Bubenkov for the report and Bruno Oliveira for the PR.
- release as wheel - release as wheel
- "-n" option now can be set to "auto" for automatic detection of number - "-n" option now can be set to "auto" for automatic detection of number
of cpus in the host system. Thanks Suloev Dmitry for the PR. of cpus in the host system. Thanks Suloev Dmitry for the PR.

View File

@@ -62,6 +62,11 @@ Especially for longer running tests or tests requiring
a lot of IO this can lead to considerable speed ups. This option can a lot of IO this can lead to considerable speed ups. This option can
also be set to ``auto`` for automatic detection of the number of CPUs. also be set to ``auto`` for automatic detection of the number of CPUs.
If a test crashes the interpreter, pytest-xdist will automatically restart
that slave and report the failure as usual. You can use the
``--max-slave-restart`` option to limit the number of slaves that can
be restarted, or disable restarting altogether using ``--max-slave-restart=0``.
Running tests in a Python subprocess Running tests in a Python subprocess
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

View File

@@ -529,7 +529,7 @@ class TestNodeFailure:
""") """)
res = testdir.runpytest(f, '-n1') res = testdir.runpytest(f, '-n1')
res.stdout.fnmatch_lines([ res.stdout.fnmatch_lines([
"*Replacing failed node*", "*Replacing crashed slave*",
"*Slave*crashed while running*", "*Slave*crashed while running*",
"*1 failed*1 passed*", "*1 failed*1 passed*",
]) ])
@@ -544,7 +544,7 @@ class TestNodeFailure:
""") """)
res = testdir.runpytest(f, '-n2') res = testdir.runpytest(f, '-n2')
res.stdout.fnmatch_lines([ res.stdout.fnmatch_lines([
"*Replacing failed node*", "*Replacing crashed slave*",
"*Slave*crashed while running*", "*Slave*crashed while running*",
"*1 failed*3 passed*", "*1 failed*3 passed*",
]) ])
@@ -557,7 +557,7 @@ class TestNodeFailure:
""") """)
res = testdir.runpytest(f, '--dist=each', '--tx=popen') res = testdir.runpytest(f, '--dist=each', '--tx=popen')
res.stdout.fnmatch_lines([ res.stdout.fnmatch_lines([
"*Replacing failed node*", "*Replacing crashed slave*",
"*Slave*crashed while running*", "*Slave*crashed while running*",
"*1 failed*1 passed*", "*1 failed*1 passed*",
]) ])
@@ -570,7 +570,39 @@ class TestNodeFailure:
""") """)
res = testdir.runpytest(f, '--dist=each', '--tx=2*popen') res = testdir.runpytest(f, '--dist=each', '--tx=2*popen')
res.stdout.fnmatch_lines([ res.stdout.fnmatch_lines([
"*Replacing failed node*", "*Replacing crashed slave*",
"*Slave*crashed while running*", "*Slave*crashed while running*",
"*2 failed*2 passed*", "*2 failed*2 passed*",
]) ])
def test_max_slave_restart(self, testdir):
f = testdir.makepyfile("""
import os
def test_a(): pass
def test_b(): os._exit(1)
def test_c(): os._exit(1)
def test_d(): pass
""")
res = testdir.runpytest(f, '-n4', '--max-slave-restart=1')
res.stdout.fnmatch_lines([
"*Replacing crashed slave*",
"*Maximum crashed slaves reached: 1*",
"*Slave*crashed while running*",
"*Slave*crashed while running*",
"*2 failed*2 passed*",
])
def test_disable_restart(self, testdir):
f = testdir.makepyfile("""
import os
def test_a(): pass
def test_b(): os._exit(1)
def test_c(): pass
""")
res = testdir.runpytest(f, '-n4', '--max-slave-restart=0')
res.stdout.fnmatch_lines([
"*Slave restarting disabled*",
"*Slave*crashed while running*",
"*1 failed*2 passed*",
])

View File

@@ -458,6 +458,10 @@ class DSession:
self._session = None self._session = None
self._failed_collection_errors = {} self._failed_collection_errors = {}
self._active_nodes = set() self._active_nodes = set()
self._failed_nodes_count = 0
self._max_slave_restart = self.config.getoption('max_slave_restart')
if self._max_slave_restart is not None:
self._max_slave_restart = int(self._max_slave_restart)
try: try:
self.terminal = config.pluginmanager.getplugin("terminalreporter") self.terminal = config.pluginmanager.getplugin("terminalreporter")
except KeyError: except KeyError:
@@ -583,8 +587,20 @@ class DSession:
else: else:
if crashitem: if crashitem:
self.handle_crashitem(crashitem, node) self.handle_crashitem(crashitem, node)
self.report_line("Replacing failed node %s" % node.gateway.id)
self._clone_node(node) self._failed_nodes_count += 1
maximum_reached = (self._max_slave_restart is not None and
self._failed_nodes_count > self._max_slave_restart)
if maximum_reached:
if self._max_slave_restart == 0:
msg = 'Slave restarting disabled'
else:
msg = "Maximum crashed slaves reached: %d" % \
self._max_slave_restart
self.report_line(msg)
else:
self.report_line("Replacing crashed slave %s" % node.gateway.id)
self._clone_node(node)
self._active_nodes.remove(node) self._active_nodes.remove(node)
def slave_collectionfinish(self, node, ids): def slave_collectionfinish(self, node, ids):

View File

@@ -14,6 +14,9 @@ def pytest_addoption(parser):
help="shortcut for '--dist=load --tx=NUM*popen', " help="shortcut for '--dist=load --tx=NUM*popen', "
"you can use 'auto' here for auto detection CPUs number on " "you can use 'auto' here for auto detection CPUs number on "
"host system") "host system")
group._addoption('--max-slave-restart', action="store", default=None,
help="maximum number of slaves that can be restarted "
"when crashed (set to zero to disable this feature)")
group.addoption('--boxed', group.addoption('--boxed',
action="store_true", dest="boxed", default=False, action="store_true", dest="boxed", default=False,
help="box each test run in a separate process (unix)") help="box each test run in a separate process (unix)")