Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e59c609b59 | ||
|
|
de257e6b83 | ||
|
|
51b5a5ace4 | ||
|
|
d86ce20d28 | ||
|
|
58750f5765 | ||
|
|
0789605d30 | ||
|
|
2b57d85550 | ||
|
|
0ce4f32315 | ||
|
|
de7a45ef40 | ||
|
|
e83aae6351 | ||
|
|
a23f572b21 | ||
|
|
36c0e68c23 | ||
|
|
c79a98c624 | ||
|
|
602694b817 | ||
|
|
99f667380f | ||
|
|
82b992a5fd |
33
.travis.yml
33
.travis.yml
@@ -1,30 +1,19 @@
|
|||||||
sudo: false
|
sudo: false
|
||||||
language: python
|
language: python
|
||||||
python:
|
python:
|
||||||
|
- '2.6'
|
||||||
|
- '2.7'
|
||||||
|
- '3.4'
|
||||||
- '3.5'
|
- '3.5'
|
||||||
install: pip install -U tox setuptools_scm
|
- '3.6'
|
||||||
env:
|
install: pip install tox-travis setuptools_scm
|
||||||
matrix:
|
matrix:
|
||||||
|
include:
|
||||||
# note: please use "tox --listenvs" to populate the build matrix
|
# note: please use "tox --listenvs" to populate the build matrix
|
||||||
- TOXENV=py26-pytest27
|
- python: "3.5"
|
||||||
- TOXENV=py26-pytest28
|
env: TOXENV=flakes
|
||||||
- TOXENV=py26-pytest29
|
- python: "3.5"
|
||||||
- TOXENV=py26-pytest30
|
env: TOXENV=readme
|
||||||
- TOXENV=py27-pytest27
|
|
||||||
- TOXENV=py27-pytest28
|
|
||||||
- TOXENV=py27-pytest29
|
|
||||||
- TOXENV=py27-pytest30
|
|
||||||
- TOXENV=py34-pytest27
|
|
||||||
- TOXENV=py34-pytest28
|
|
||||||
- TOXENV=py34-pytest29
|
|
||||||
- TOXENV=py34-pytest30
|
|
||||||
- TOXENV=py35-pytest27
|
|
||||||
- TOXENV=py35-pytest28
|
|
||||||
- TOXENV=py35-pytest29
|
|
||||||
- TOXENV=py27-pytest28-pexpect
|
|
||||||
- TOXENV=py35-pytest28-pexpect
|
|
||||||
- TOXENV=flakes
|
|
||||||
- TOXENV=readme
|
|
||||||
|
|
||||||
script: tox
|
script: tox
|
||||||
notifications:
|
notifications:
|
||||||
|
|||||||
@@ -1,3 +1,10 @@
|
|||||||
|
1.17.0
|
||||||
|
------
|
||||||
|
|
||||||
|
- fix #124: xdist would mark test as complete after 'call' step. As a result,
|
||||||
|
xdist could identify the wrong test as failing when test crashes at teardown.
|
||||||
|
To address this issue, xdist now marks test as complete at teardown.
|
||||||
|
|
||||||
1.16.0
|
1.16.0
|
||||||
------
|
------
|
||||||
|
|
||||||
|
|||||||
10
README.rst
10
README.rst
@@ -1,10 +1,14 @@
|
|||||||
|
|
||||||
|
|
||||||
|
.. image:: http://img.shields.io/pypi/v/pytest-xdist.svg
|
||||||
|
:target: https://pypi.python.org/pypi/pytest-xdist
|
||||||
|
|
||||||
|
.. image:: https://anaconda.org/conda-forge/pytest-xdist/badges/version.svg
|
||||||
|
:target: https://anaconda.org/conda-forge/pytest-xdist
|
||||||
|
|
||||||
.. image:: https://travis-ci.org/pytest-dev/pytest-xdist.svg?branch=master
|
.. image:: https://travis-ci.org/pytest-dev/pytest-xdist.svg?branch=master
|
||||||
:target: https://travis-ci.org/pytest-dev/pytest-xdist
|
:target: https://travis-ci.org/pytest-dev/pytest-xdist
|
||||||
|
|
||||||
.. image:: http://img.shields.io/pypi/v/pytest-xdist.svg
|
|
||||||
:target: https://pypi.python.org/pypi/pytest-xdist
|
|
||||||
|
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/56eq1a1avd4sdd7e/branch/master?svg=true
|
.. image:: https://ci.appveyor.com/api/projects/status/56eq1a1avd4sdd7e/branch/master?svg=true
|
||||||
:target: https://ci.appveyor.com/project/pytestbot/pytest-xdist
|
:target: https://ci.appveyor.com/project/pytestbot/pytest-xdist
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,11 @@ environment:
|
|||||||
- TOXENV: "py35-pytest27"
|
- TOXENV: "py35-pytest27"
|
||||||
- TOXENV: "py35-pytest28"
|
- TOXENV: "py35-pytest28"
|
||||||
- TOXENV: "py35-pytest29"
|
- TOXENV: "py35-pytest29"
|
||||||
|
- TOXENV: "py35-pytest30"
|
||||||
|
- TOXENV: "py36-pytest27"
|
||||||
|
- TOXENV: "py36-pytest28"
|
||||||
|
- TOXENV: "py36-pytest29"
|
||||||
|
- TOXENV: "py36-pytest30"
|
||||||
- TOXENV: "py27-pytest28-pexpect"
|
- TOXENV: "py27-pytest28-pexpect"
|
||||||
- TOXENV: "py35-pytest28-pexpect"
|
- TOXENV: "py35-pytest28-pexpect"
|
||||||
- TOXENV: "flakes"
|
- TOXENV: "flakes"
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ class TestDistribution:
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p1, "-n1")
|
result = testdir.runpytest(p1, "-n1")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*1 passed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 passed*",
|
||||||
|
])
|
||||||
|
|
||||||
def test_n1_fail(self, testdir):
|
def test_n1_fail(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
@@ -19,7 +21,9 @@ class TestDistribution:
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p1, "-n1")
|
result = testdir.runpytest(p1, "-n1")
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
result.stdout.fnmatch_lines(["*1 failed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 failed*",
|
||||||
|
])
|
||||||
|
|
||||||
def test_n1_import_error(self, testdir):
|
def test_n1_import_error(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
@@ -30,7 +34,7 @@ class TestDistribution:
|
|||||||
result = testdir.runpytest(p1, "-n1")
|
result = testdir.runpytest(p1, "-n1")
|
||||||
assert result.ret == 1
|
assert result.ret == 1
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"E ImportError: *__import_of_missing_module*",
|
"E *Error: No module named *__import_of_missing_module*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_n2_import_error(self, testdir):
|
def test_n2_import_error(self, testdir):
|
||||||
@@ -53,7 +57,9 @@ class TestDistribution:
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p1, "-n1")
|
result = testdir.runpytest(p1, "-n1")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*1 skipped*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 skipped*",
|
||||||
|
])
|
||||||
|
|
||||||
def test_manytests_to_one_import_error(self, testdir):
|
def test_manytests_to_one_import_error(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
@@ -64,11 +70,12 @@ class TestDistribution:
|
|||||||
result = testdir.runpytest(p1, '--tx=popen', '--tx=popen')
|
result = testdir.runpytest(p1, '--tx=popen', '--tx=popen')
|
||||||
assert result.ret in (1, 2)
|
assert result.ret in (1, 2)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"E ImportError: *__import_of_missing_module*",
|
"E *Error: No module named *__import_of_missing_module*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_manytests_to_one_popen(self, testdir):
|
def test_manytests_to_one_popen(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile(
|
||||||
|
"""
|
||||||
import py
|
import py
|
||||||
def test_fail0():
|
def test_fail0():
|
||||||
assert 0
|
assert 0
|
||||||
@@ -108,10 +115,13 @@ class TestDistribution:
|
|||||||
""" % str(testdir.tmpdir))
|
""" % str(testdir.tmpdir))
|
||||||
result = testdir.runpytest_subprocess(p1, "-n1")
|
result = testdir.runpytest_subprocess(p1, "-n1")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*1 passed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 passed*",
|
||||||
|
])
|
||||||
|
|
||||||
def test_dist_ini_specified(self, testdir):
|
def test_dist_ini_specified(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile(
|
||||||
|
"""
|
||||||
import py
|
import py
|
||||||
def test_fail0():
|
def test_fail0():
|
||||||
assert 0
|
assert 0
|
||||||
@@ -207,14 +217,14 @@ class TestDistribution:
|
|||||||
""")
|
""")
|
||||||
p1 = testdir.makepyfile("def test_func(): pass")
|
p1 = testdir.makepyfile("def test_func(): pass")
|
||||||
result = testdir.runpytest("-v", p1, '-d', '--tx=popen')
|
result = testdir.runpytest("-v", p1, '-d', '--tx=popen')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines(
|
||||||
"*0*Python*", "*calculated result is 49*", "*1 passed*"
|
["*0*Python*", "*calculated result is 49*", "*1 passed*"])
|
||||||
])
|
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
def test_keyboardinterrupt_hooks_issue79(self, testdir):
|
def test_keyboardinterrupt_hooks_issue79(self, testdir):
|
||||||
testdir.makepyfile(__init__="",
|
testdir.makepyfile(
|
||||||
test_one="""
|
__init__="",
|
||||||
|
test_one="""
|
||||||
def test_hello():
|
def test_hello():
|
||||||
raise KeyboardInterrupt()
|
raise KeyboardInterrupt()
|
||||||
""")
|
""")
|
||||||
@@ -273,8 +283,9 @@ class TestDistEach:
|
|||||||
py.test.skip("%s not found" % name)
|
py.test.skip("%s not found" % name)
|
||||||
interpreters.append(interp)
|
interpreters.append(interp)
|
||||||
|
|
||||||
testdir.makepyfile(__init__="",
|
testdir.makepyfile(
|
||||||
test_one="""
|
__init__="",
|
||||||
|
test_one="""
|
||||||
import sys
|
import sys
|
||||||
def test_hello():
|
def test_hello():
|
||||||
print("%s...%s" % sys.version_info[:2])
|
print("%s...%s" % sys.version_info[:2])
|
||||||
@@ -335,9 +346,8 @@ def test_teardownfails_one_function(testdir):
|
|||||||
assert 0
|
assert 0
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, '-n1', '--tx=popen')
|
result = testdir.runpytest(p, '-n1', '--tx=popen')
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines(
|
||||||
"*def teardown_function(function):*", "*1 passed*1 error*"
|
["*def teardown_function(function):*", "*1 passed*1 error*"])
|
||||||
])
|
|
||||||
|
|
||||||
|
|
||||||
@py.test.mark.xfail
|
@py.test.mark.xfail
|
||||||
@@ -350,7 +360,9 @@ def test_terminate_on_hangingnode(testdir):
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, '--dist=each', '--tx=popen//id=my')
|
result = testdir.runpytest(p, '--dist=each', '--tx=popen//id=my')
|
||||||
assert result.duration < 2.0
|
assert result.duration < 2.0
|
||||||
result.stdout.fnmatch_lines(["*killed*my*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*killed*my*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.xfail(reason="works if run outside test suite", run=False)
|
@pytest.mark.xfail(reason="works if run outside test suite", run=False)
|
||||||
@@ -377,7 +389,10 @@ def test_session_hooks(testdir):
|
|||||||
assert hasattr(sys, 'pytestsessionhooks')
|
assert hasattr(sys, 'pytestsessionhooks')
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p, "--dist=each", "--tx=popen")
|
result = testdir.runpytest(p, "--dist=each", "--tx=popen")
|
||||||
result.stdout.fnmatch_lines(["*ValueError*", "*1 passed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ValueError*",
|
||||||
|
"*1 passed*",
|
||||||
|
])
|
||||||
assert not result.ret
|
assert not result.ret
|
||||||
d = result.parseoutcomes()
|
d = result.parseoutcomes()
|
||||||
assert d['passed'] == 1
|
assert d['passed'] == 1
|
||||||
@@ -422,7 +437,10 @@ def test_funcarg_teardown_failure(testdir):
|
|||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest_subprocess("--debug", p) # , "-n1")
|
result = testdir.runpytest_subprocess("--debug", p) # , "-n1")
|
||||||
result.stdout.fnmatch_lines(["*ValueError*42*", "*1 passed*1 error*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*ValueError*42*",
|
||||||
|
"*1 passed*1 error*",
|
||||||
|
])
|
||||||
assert result.ret
|
assert result.ret
|
||||||
|
|
||||||
|
|
||||||
@@ -436,9 +454,31 @@ def test_crashing_item(testdir):
|
|||||||
pass
|
pass
|
||||||
""")
|
""")
|
||||||
result = testdir.runpytest("-n2", p)
|
result = testdir.runpytest("-n2", p)
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines(
|
||||||
"*crashed*test_crash*", "*1 failed*1 passed*"
|
["*crashed*::test_crash*", "*1 failed*1 passed*"])
|
||||||
])
|
|
||||||
|
|
||||||
|
def test_crashing_item_teardown(testdir):
|
||||||
|
p = testdir.makepyfile("""
|
||||||
|
import py
|
||||||
|
import pytest
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def crash_fixture(request):
|
||||||
|
def kill_me():
|
||||||
|
py.process.kill(os.getpid())
|
||||||
|
request.addfinalizer(kill_me)
|
||||||
|
|
||||||
|
def test_a(crash_fixture):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_b():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-n1", p)
|
||||||
|
result.stdout.fnmatch_lines(["*crashed*::test_a*", "*1 failed*2 passed*"])
|
||||||
|
|
||||||
|
|
||||||
def test_skipping(testdir):
|
def test_skipping(testdir):
|
||||||
@@ -453,7 +493,8 @@ def test_skipping(testdir):
|
|||||||
|
|
||||||
|
|
||||||
def test_issue34_pluginloading_in_subprocess(testdir):
|
def test_issue34_pluginloading_in_subprocess(testdir):
|
||||||
testdir.tmpdir.join("plugin123.py").write(py.code.Source("""
|
testdir.tmpdir.join("plugin123.py").write(
|
||||||
|
py.code.Source("""
|
||||||
def pytest_namespace():
|
def pytest_namespace():
|
||||||
return {'sample_variable': 'testing'}
|
return {'sample_variable': 'testing'}
|
||||||
"""))
|
"""))
|
||||||
@@ -464,7 +505,9 @@ def test_issue34_pluginloading_in_subprocess(testdir):
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest_subprocess("-n1", "-p", "plugin123")
|
result = testdir.runpytest_subprocess("-n1", "-p", "plugin123")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*1 passed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*1 passed*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_fixture_scope_caching_issue503(testdir):
|
def test_fixture_scope_caching_issue503(testdir):
|
||||||
@@ -486,7 +529,9 @@ def test_fixture_scope_caching_issue503(testdir):
|
|||||||
""")
|
""")
|
||||||
result = testdir.runpytest(p1, '-v', '-n1')
|
result = testdir.runpytest(p1, '-v', '-n1')
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
result.stdout.fnmatch_lines(["*2 passed*", ])
|
result.stdout.fnmatch_lines([
|
||||||
|
"*2 passed*",
|
||||||
|
])
|
||||||
|
|
||||||
|
|
||||||
def test_issue_594_random_parametrize(testdir):
|
def test_issue_594_random_parametrize(testdir):
|
||||||
|
|||||||
2
tox.ini
2
tox.ini
@@ -1,7 +1,7 @@
|
|||||||
[tox]
|
[tox]
|
||||||
# if you change the envlist, please update .travis.yml file as well
|
# if you change the envlist, please update .travis.yml file as well
|
||||||
envlist=
|
envlist=
|
||||||
py{26,27,34,35}-pytest{27,28,29,30}
|
py{26,27,34,35,36}-pytest{27,28,29,30}
|
||||||
py{27,35}-pytest28-pexpect
|
py{27,35}-pytest28-pexpect
|
||||||
flakes
|
flakes
|
||||||
readme
|
readme
|
||||||
|
|||||||
@@ -660,7 +660,7 @@ class DSession:
|
|||||||
If the node indicates it is finished with a test item, remove
|
If the node indicates it is finished with a test item, remove
|
||||||
the item from the pending list in the scheduler.
|
the item from the pending list in the scheduler.
|
||||||
"""
|
"""
|
||||||
if rep.when == "call" or (rep.when == "setup" and not rep.passed):
|
if rep.when == "teardown" or (rep.when == "setup" and not rep.passed):
|
||||||
self.sched.mark_test_complete(node, rep.item_index, rep.duration)
|
self.sched.mark_test_complete(node, rep.item_index, rep.duration)
|
||||||
# self.report_line("testreport %s: %s" %(rep.id, rep.status))
|
# self.report_line("testreport %s: %s" %(rep.id, rep.status))
|
||||||
rep.node = node
|
rep.node = node
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ with the worker instance that executed the hook originally:
|
|||||||
You can use this hooks just as you would use normal pytest hooks, but some care
|
You can use this hooks just as you would use normal pytest hooks, but some care
|
||||||
must be taken in plugins in case ``xdist`` is not installed. Please see:
|
must be taken in plugins in case ``xdist`` is not installed. Please see:
|
||||||
|
|
||||||
http://pytest.org/latest/writing_plugins.html#optionally-using-hooks-from-3rd-party-plugins
|
http://pytest.org/en/latest/writing_plugins.html#optionally-using-hooks-from-3rd-party-plugins
|
||||||
"""
|
"""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user