Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cee78e9b20 | ||
|
|
6e1154881d | ||
|
|
a96f6c9038 | ||
|
|
47b65716a3 | ||
|
|
dbaf49cd69 | ||
|
|
41c00ca47e | ||
|
|
f0dee10a0d | ||
|
|
6256608669 | ||
|
|
4a1f1d879b | ||
|
|
2094157950 | ||
|
|
7473683cdb | ||
|
|
c48834aa69 | ||
|
|
6d9d6c6a34 | ||
|
|
1542aff326 | ||
|
|
cc95cf26e1 | ||
|
|
eb90a7c82c | ||
|
|
afe8bad9ff | ||
|
|
95a62460aa | ||
|
|
8e2fdeee3e | ||
|
|
fae1e02c1d | ||
|
|
3440f08a40 | ||
|
|
a9b064d992 | ||
|
|
47875cbea9 | ||
|
|
e59c609b59 | ||
|
|
de257e6b83 | ||
|
|
51b5a5ace4 | ||
|
|
d86ce20d28 | ||
|
|
58750f5765 | ||
|
|
0789605d30 | ||
|
|
2b57d85550 | ||
|
|
0ce4f32315 | ||
|
|
de7a45ef40 | ||
|
|
e83aae6351 | ||
|
|
a23f572b21 | ||
|
|
36c0e68c23 | ||
|
|
c79a98c624 | ||
|
|
602694b817 | ||
|
|
99f667380f | ||
|
|
82b992a5fd |
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
17
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
Thanks for submitting a PR, your contribution is really appreciated!
|
||||||
|
|
||||||
|
Here's a quick checklist that should be present in PRs:
|
||||||
|
|
||||||
|
- [ ] Make sure to include reasonable tests for your change if necessary
|
||||||
|
|
||||||
|
- [ ] Add a *news* file into the `changelog` folder, following these guidelines:
|
||||||
|
* Name it `$issue_id.$type` for example `588.bug`
|
||||||
|
* If you don't have an issue_id change it to the PR id after creating it
|
||||||
|
* Ensure type is one of `removal`, `feature`, `bugfix`, `vendor`, `doc` or `trivial`
|
||||||
|
* Make sure to use full sentences with correct case and punctuation, for example:
|
||||||
|
|
||||||
|
```
|
||||||
|
Fix issue with non-ascii contents in doctest text files.
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
34
.travis.yml
34
.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:
|
||||||
@@ -39,6 +28,7 @@ notifications:
|
|||||||
deploy:
|
deploy:
|
||||||
provider: pypi
|
provider: pypi
|
||||||
user: ronny
|
user: ronny
|
||||||
|
skip_upload_docs: true
|
||||||
password:
|
password:
|
||||||
secure: cxmSDho5d+PYKEM4ZCg8ms1P4lzhYkrw6fEOm2HtTcsuCyY6aZMSgImWAnEYbJHSkdzgcxlXK9UKJ9B0YenXmBCkAr7UjdnpNXNmkySr0sYzlH/sfqt/dDATCHFaRKxnkOSOVywaDYhT9n8YudbXI77pXwD12i/CeSSJDbHhsu0JYUfAcb+D6YjRYoA2SEGCnzSzg+gDDfwXZx4ZiODCGLVwieNp1klCg88YROUE1BaYYNuUOONvfXX8+TWowbCF6ChH1WL/bZ49OStEYQNuYxZQZr4yClIqu9VJbchrU8j860K9ott2kkGTgfB/dDrQB/XncBubyIX9ikzCQAmmBXWAI3eyvWLPDk2Jz7kW2l2RT7syct80tCq3JhvQ1qdwr5ap7siocTLgnBW0tF4tkHSTFN3510fkc43npnp6FThebESQpnI24vqpwJ9hI/kW5mYi014Og2E/cpCXnz2XO8iZPDbqAMQpDsqEQoyhfGNgPTGp4K30TxRtwZBI5hHhDKnnR16fXtRgt1gYPvz/peUQvvpOm4JzIzGXPzluuutpnCBy75v5+oiwT3YRrLL/Meims9FtDDXL3qQubAE/ezIOOpm0N5XXV8DxIom8EN71yq5ab1tqhM+tBX7owRjy4FR4If2Q8feBdmTuh26DIQt/y+qSG8VkB9Sw/JCjc7c=
|
secure: cxmSDho5d+PYKEM4ZCg8ms1P4lzhYkrw6fEOm2HtTcsuCyY6aZMSgImWAnEYbJHSkdzgcxlXK9UKJ9B0YenXmBCkAr7UjdnpNXNmkySr0sYzlH/sfqt/dDATCHFaRKxnkOSOVywaDYhT9n8YudbXI77pXwD12i/CeSSJDbHhsu0JYUfAcb+D6YjRYoA2SEGCnzSzg+gDDfwXZx4ZiODCGLVwieNp1klCg88YROUE1BaYYNuUOONvfXX8+TWowbCF6ChH1WL/bZ49OStEYQNuYxZQZr4yClIqu9VJbchrU8j860K9ott2kkGTgfB/dDrQB/XncBubyIX9ikzCQAmmBXWAI3eyvWLPDk2Jz7kW2l2RT7syct80tCq3JhvQ1qdwr5ap7siocTLgnBW0tF4tkHSTFN3510fkc43npnp6FThebESQpnI24vqpwJ9hI/kW5mYi014Og2E/cpCXnz2XO8iZPDbqAMQpDsqEQoyhfGNgPTGp4K30TxRtwZBI5hHhDKnnR16fXtRgt1gYPvz/peUQvvpOm4JzIzGXPzluuutpnCBy75v5+oiwT3YRrLL/Meims9FtDDXL3qQubAE/ezIOOpm0N5XXV8DxIom8EN71yq5ab1tqhM+tBX7owRjy4FR4If2Q8feBdmTuh26DIQt/y+qSG8VkB9Sw/JCjc7c=
|
||||||
on:
|
on:
|
||||||
|
|||||||
@@ -1,3 +1,55 @@
|
|||||||
|
pytest-xdist 1.18.0 (2017-06-26)
|
||||||
|
================================
|
||||||
|
|
||||||
|
Features
|
||||||
|
--------
|
||||||
|
|
||||||
|
- Add long option `--numprocesses` as alternative for `-n`. (#168)
|
||||||
|
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Fix serialization and deserialization dropping longrepr details. (#133)
|
||||||
|
|
||||||
|
|
||||||
|
pytest-xdist 1.17.1 (2017-06-10)
|
||||||
|
================================
|
||||||
|
|
||||||
|
Bug Fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
- Hot fix release reverting the change introduced by #124, unfortunately it
|
||||||
|
broke a number of test suites so we are reversing this change while we
|
||||||
|
investigate the problem. (#157)
|
||||||
|
|
||||||
|
|
||||||
|
Improved Documentation
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
- Introduced ``towncrier`` for ``CHANGELOG`` management. (#154)
|
||||||
|
|
||||||
|
- Added ``HOWTORELEASE`` documentation. (#155)
|
||||||
|
|
||||||
|
|
||||||
|
..
|
||||||
|
You should *NOT* be adding new change log entries to this file, this
|
||||||
|
file is managed by towncrier. You *may* edit previous change logs to
|
||||||
|
fix problems like typo corrections or such.
|
||||||
|
To add a new change log entry, please see
|
||||||
|
https://pip.pypa.io/en/latest/development/#adding-a-news-entry
|
||||||
|
We named the news folder ``changelog``
|
||||||
|
|
||||||
|
.. towncrier release notes start
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
------
|
------
|
||||||
|
|
||||||
45
HOWTORELEASE.rst
Normal file
45
HOWTORELEASE.rst
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
======================
|
||||||
|
Releasing pytest-xdist
|
||||||
|
======================
|
||||||
|
|
||||||
|
This document describes the steps to make a new ``pytest-xdist`` release.
|
||||||
|
|
||||||
|
Version
|
||||||
|
-------
|
||||||
|
|
||||||
|
``master`` should always be green and a potential release candidate. ``pytest-xdist`` follows
|
||||||
|
semantic versioning, so given that the current version is ``X.Y.Z``, to find the next version number
|
||||||
|
one needs to look at the ``changelog`` folder:
|
||||||
|
|
||||||
|
- If there is any file named ``*.feature``, then we must make a new **minor** release: next
|
||||||
|
release will be ``X.Y+1.0``.
|
||||||
|
|
||||||
|
- Otherwise it is just a **bug fix** release: ``X.Y.Z+1``.
|
||||||
|
|
||||||
|
|
||||||
|
Steps
|
||||||
|
-----
|
||||||
|
|
||||||
|
To publish a new release ``X.Y.Z``, the steps are as follows:
|
||||||
|
|
||||||
|
#. Create a new branch named ``release-X.Y.Z`` from the latest ``master``.
|
||||||
|
|
||||||
|
#. Install ``pytest-xdist`` and dev requirements in a virtualenv::
|
||||||
|
|
||||||
|
$ pip install -e . -r dev-requirements.txt
|
||||||
|
|
||||||
|
#. Update ``CHANGELOG.rst`` file by running::
|
||||||
|
|
||||||
|
$ towncrier --version X.Y.Z
|
||||||
|
|
||||||
|
It might ask for confirmation to remove news fragments; answer yes.
|
||||||
|
|
||||||
|
#. Commit and push the branch for review.
|
||||||
|
|
||||||
|
#. Once PR is **green** and **approved**, create and push a tag::
|
||||||
|
|
||||||
|
$ export VERSION=X.Y.Z
|
||||||
|
$ git tag v$VERSION release-$VERSION
|
||||||
|
$ git push git@github.com:pytest-dev/pytest-xdist.git v$VERSION
|
||||||
|
|
||||||
|
That will build the package and publish it on ``PyPI`` automatically.
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
include CHANGELOG
|
|
||||||
include LICENSE
|
|
||||||
include README.txt
|
|
||||||
include setup.py
|
|
||||||
include tox.ini
|
|
||||||
graft testing
|
|
||||||
prune .git
|
|
||||||
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
|
||||||
|
|
||||||
|
|||||||
18
appveyor.yml
18
appveyor.yml
@@ -1,23 +1,13 @@
|
|||||||
environment:
|
environment:
|
||||||
matrix:
|
matrix:
|
||||||
# note: please use "tox --listenvs" to populate the build matrix
|
# note: please use "tox --listenvs" to populate the build matrix
|
||||||
- TOXENV: "py26-pytest27"
|
|
||||||
- TOXENV: "py26-pytest28"
|
|
||||||
- TOXENV: "py26-pytest29"
|
|
||||||
- TOXENV: "py26-pytest30"
|
- TOXENV: "py26-pytest30"
|
||||||
- TOXENV: "py27-pytest27"
|
|
||||||
- TOXENV: "py27-pytest28"
|
|
||||||
- TOXENV: "py27-pytest29"
|
|
||||||
- TOXENV: "py27-pytest30"
|
- TOXENV: "py27-pytest30"
|
||||||
- TOXENV: "py34-pytest27"
|
|
||||||
- TOXENV: "py34-pytest28"
|
|
||||||
- TOXENV: "py34-pytest29"
|
|
||||||
- TOXENV: "py34-pytest30"
|
- TOXENV: "py34-pytest30"
|
||||||
- TOXENV: "py35-pytest27"
|
- TOXENV: "py35-pytest30"
|
||||||
- TOXENV: "py35-pytest28"
|
- TOXENV: "py36-pytest30"
|
||||||
- TOXENV: "py35-pytest29"
|
- TOXENV: "py27-pytest30-pexpect"
|
||||||
- TOXENV: "py27-pytest28-pexpect"
|
- TOXENV: "py35-pytest30-pexpect"
|
||||||
- TOXENV: "py35-pytest28-pexpect"
|
|
||||||
- TOXENV: "flakes"
|
- TOXENV: "flakes"
|
||||||
- TOXENV: "readme"
|
- TOXENV: "readme"
|
||||||
|
|
||||||
|
|||||||
39
changelog/_template.rst
Normal file
39
changelog/_template.rst
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
{% for section in sections %}
|
||||||
|
{% set underline = "-" %}
|
||||||
|
{% if section %}
|
||||||
|
{{section}}
|
||||||
|
{{ underline * section|length }}{% set underline = "~" %}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if sections[section] %}
|
||||||
|
{% for category, val in definitions.items() if category in sections[section] and category != 'trivial' %}
|
||||||
|
|
||||||
|
{{ definitions[category]['name'] }}
|
||||||
|
{{ underline * definitions[category]['name']|length }}
|
||||||
|
|
||||||
|
{% if definitions[category]['showcontent'] %}
|
||||||
|
{% for text, values in sections[section][category]|dictsort(by='value') %}
|
||||||
|
- {{ text }}{% if category != 'vendor' %} ({{ values|sort|join(', ') }}){% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
- {{ sections[section][category]['']|sort|join(', ') }}
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if sections[section][category]|length == 0 %}
|
||||||
|
|
||||||
|
No significant changes.
|
||||||
|
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
|
||||||
|
No significant changes.
|
||||||
|
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
1
dev-requirements.txt
Normal file
1
dev-requirements.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
towncrier
|
||||||
36
pyproject.toml
Normal file
36
pyproject.toml
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
[tool.towncrier]
|
||||||
|
package = "xdist"
|
||||||
|
filename = "CHANGELOG.rst"
|
||||||
|
directory = "changelog/"
|
||||||
|
title_format = "pytest-xdist {version} ({project_date})"
|
||||||
|
template = "changelog/_template.rst"
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "removal"
|
||||||
|
name = "Deprecations and Removals"
|
||||||
|
showcontent = true
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "feature"
|
||||||
|
name = "Features"
|
||||||
|
showcontent = true
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "bugfix"
|
||||||
|
name = "Bug Fixes"
|
||||||
|
showcontent = true
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "vendor"
|
||||||
|
name = "Vendored Libraries"
|
||||||
|
showcontent = true
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "doc"
|
||||||
|
name = "Improved Documentation"
|
||||||
|
showcontent = true
|
||||||
|
|
||||||
|
[[tool.towncrier.type]]
|
||||||
|
directory = "trivial"
|
||||||
|
name = "Trivial Changes"
|
||||||
|
showcontent = false
|
||||||
2
setup.py
2
setup.py
@@ -20,7 +20,7 @@ setup(
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
install_requires=['execnet>=1.1', 'pytest>=2.7.0', 'py>=1.4.22'],
|
install_requires=['execnet>=1.1', 'pytest>=3.0.0', 'py>=1.4.22'],
|
||||||
setup_requires=['setuptools_scm'],
|
setup_requires=['setuptools_scm'],
|
||||||
classifiers=[
|
classifiers=[
|
||||||
'Development Status :: 5 - Production/Stable',
|
'Development Status :: 5 - Production/Stable',
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
@@ -453,7 +471,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 +483,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 +507,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):
|
||||||
|
|||||||
@@ -268,7 +268,7 @@ class TestFunctional:
|
|||||||
""")
|
""")
|
||||||
child = testdir.spawn_pytest("-f %s" % p)
|
child = testdir.spawn_pytest("-f %s" % p)
|
||||||
child.expect("1 xpass")
|
child.expect("1 xpass")
|
||||||
child.expect("### LOOPONFAILING ####")
|
# child.expect("### LOOPONFAILING ####")
|
||||||
child.expect("waiting for changes")
|
child.expect("waiting for changes")
|
||||||
child.kill(15)
|
child.kill(15)
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ def test_dist_options(testdir):
|
|||||||
check_options(config)
|
check_options(config)
|
||||||
assert config.option.dist == "load"
|
assert config.option.dist == "load"
|
||||||
assert config.option.tx == ['popen'] * 2
|
assert config.option.tx == ['popen'] * 2
|
||||||
|
config = testdir.parseconfigure("--numprocesses", "2")
|
||||||
|
check_options(config)
|
||||||
|
assert config.option.dist == "load"
|
||||||
|
assert config.option.tx == ['popen'] * 2
|
||||||
config = testdir.parseconfigure("-d")
|
config = testdir.parseconfigure("-d")
|
||||||
check_options(config)
|
check_options(config)
|
||||||
assert config.option.dist == "load"
|
assert config.option.dist == "load"
|
||||||
|
|||||||
@@ -79,6 +79,20 @@ def test_remoteinitconfig(testdir):
|
|||||||
|
|
||||||
|
|
||||||
class TestReportSerialization:
|
class TestReportSerialization:
|
||||||
|
def test_xdist_report_longrepr_reprcrash_130(self, testdir):
|
||||||
|
reprec = testdir.inline_runsource("""
|
||||||
|
import py
|
||||||
|
def test_fail(): assert False, 'Expected Message'
|
||||||
|
""")
|
||||||
|
reports = reprec.getreports("pytest_runtest_logreport")
|
||||||
|
assert len(reports) == 3
|
||||||
|
initial_failure_report = reports[1]
|
||||||
|
d = serialize_report(initial_failure_report)
|
||||||
|
check_marshallable(d)
|
||||||
|
processed_report = unserialize_report("testreport", d)
|
||||||
|
assert 'Expected Message' \
|
||||||
|
in processed_report.longrepr.reprcrash.message
|
||||||
|
|
||||||
def test_itemreport_outcomes(self, testdir):
|
def test_itemreport_outcomes(self, testdir):
|
||||||
reprec = testdir.inline_runsource("""
|
reprec = testdir.inline_runsource("""
|
||||||
import py
|
import py
|
||||||
@@ -108,7 +122,7 @@ class TestReportSerialization:
|
|||||||
assert newrep.when == rep.when
|
assert newrep.when == rep.when
|
||||||
assert newrep.keywords == rep.keywords
|
assert newrep.keywords == rep.keywords
|
||||||
if rep.failed:
|
if rep.failed:
|
||||||
assert newrep.longrepr == str(rep.longrepr)
|
assert newrep.longreprtext == rep.longreprtext
|
||||||
|
|
||||||
def test_collectreport_passed(self, testdir):
|
def test_collectreport_passed(self, testdir):
|
||||||
reprec = testdir.inline_runsource("def test_func(): pass")
|
reprec = testdir.inline_runsource("def test_func(): pass")
|
||||||
|
|||||||
7
tox.ini
7
tox.ini
@@ -1,8 +1,8 @@
|
|||||||
[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{30}
|
||||||
py{27,35}-pytest28-pexpect
|
py{27,35}-pytest{30}-pexpect
|
||||||
flakes
|
flakes
|
||||||
readme
|
readme
|
||||||
|
|
||||||
@@ -14,9 +14,6 @@ deps =
|
|||||||
pycmd
|
pycmd
|
||||||
# to avoid .eggs
|
# to avoid .eggs
|
||||||
setuptools_scm
|
setuptools_scm
|
||||||
pytest27: pytest~=2.7.2
|
|
||||||
pytest28: pytest~=2.8.7
|
|
||||||
pytest29: pytest~=2.9.2
|
|
||||||
pytest30: pytest~=3.0.5
|
pytest30: pytest~=3.0.5
|
||||||
pexpect: pexpect
|
pexpect: pexpect
|
||||||
platform=
|
platform=
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ def parse_numprocesses(s):
|
|||||||
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(
|
group._addoption(
|
||||||
'-n', dest="numprocesses", metavar="numprocesses",
|
'-n', '--numprocesses', dest="numprocesses", metavar="numprocesses",
|
||||||
action="store",
|
action="store",
|
||||||
type=parse_numprocesses,
|
type=parse_numprocesses,
|
||||||
help="shortcut for '--dist=load --tx=NUM*popen', "
|
help="shortcut for '--dist=load --tx=NUM*popen', "
|
||||||
|
|||||||
@@ -98,10 +98,33 @@ class SlaveInteractor:
|
|||||||
|
|
||||||
|
|
||||||
def serialize_report(rep):
|
def serialize_report(rep):
|
||||||
|
def disassembled_report(rep):
|
||||||
|
reprtraceback = rep.longrepr.reprtraceback.__dict__
|
||||||
|
reprcrash = rep.longrepr.reprcrash.__dict__
|
||||||
|
|
||||||
|
new_entries = []
|
||||||
|
for entry in reprtraceback['reprentries']:
|
||||||
|
new_entry = entry.__dict__
|
||||||
|
for key, value in new_entry.items():
|
||||||
|
if hasattr(value, '__dict__'):
|
||||||
|
new_entry[key] = value.__dict__
|
||||||
|
new_entries.append(new_entry)
|
||||||
|
|
||||||
|
reprtraceback['reprentries'] = new_entries
|
||||||
|
|
||||||
|
return {
|
||||||
|
'reprcrash': reprcrash,
|
||||||
|
'reprtraceback': reprtraceback
|
||||||
|
}
|
||||||
|
|
||||||
import py
|
import py
|
||||||
d = rep.__dict__.copy()
|
d = rep.__dict__.copy()
|
||||||
if hasattr(rep.longrepr, 'toterminal'):
|
if hasattr(rep.longrepr, 'toterminal'):
|
||||||
d['longrepr'] = str(rep.longrepr)
|
if hasattr(rep.longrepr, 'reprtraceback') \
|
||||||
|
and hasattr(rep.longrepr, 'reprcrash'):
|
||||||
|
d['longrepr'] = disassembled_report(rep)
|
||||||
|
else:
|
||||||
|
d['longrepr'] = str(rep.longrepr)
|
||||||
else:
|
else:
|
||||||
d['longrepr'] = rep.longrepr
|
d['longrepr'] = rep.longrepr
|
||||||
for name in d:
|
for name in d:
|
||||||
|
|||||||
@@ -327,7 +327,45 @@ class SlaveController(object):
|
|||||||
|
|
||||||
|
|
||||||
def unserialize_report(name, reportdict):
|
def unserialize_report(name, reportdict):
|
||||||
|
def assembled_report(reportdict):
|
||||||
|
from _pytest._code.code import (
|
||||||
|
ReprExceptionInfo,
|
||||||
|
ReprFileLocation,
|
||||||
|
ReprEntry,
|
||||||
|
ReprFuncArgs,
|
||||||
|
ReprTraceback
|
||||||
|
)
|
||||||
|
if reportdict['longrepr']:
|
||||||
|
if 'reprcrash' and 'reprtraceback' in reportdict['longrepr']:
|
||||||
|
|
||||||
|
reprtraceback = reportdict['longrepr']['reprtraceback']
|
||||||
|
reprcrash = reportdict['longrepr']['reprcrash']
|
||||||
|
|
||||||
|
unserialized_entries = []
|
||||||
|
for entry in reprtraceback['reprentries']:
|
||||||
|
reprfuncargs, reprfileloc = None, None
|
||||||
|
if entry['reprfuncargs']:
|
||||||
|
reprfuncargs = ReprFuncArgs(**entry['reprfuncargs'])
|
||||||
|
if entry['reprfileloc']:
|
||||||
|
reprfileloc = ReprFileLocation(**entry['reprfileloc'])
|
||||||
|
|
||||||
|
reprentry = ReprEntry(
|
||||||
|
lines=entry['lines'],
|
||||||
|
reprfuncargs=reprfuncargs,
|
||||||
|
reprlocals=entry['reprlocals'],
|
||||||
|
filelocrepr=reprfileloc,
|
||||||
|
style=entry['style']
|
||||||
|
)
|
||||||
|
unserialized_entries.append(reprentry)
|
||||||
|
reprtraceback['reprentries'] = unserialized_entries
|
||||||
|
|
||||||
|
reportdict['longrepr'] = ReprExceptionInfo(
|
||||||
|
reprtraceback=ReprTraceback(**reprtraceback),
|
||||||
|
reprcrash=ReprFileLocation(**reprcrash),
|
||||||
|
)
|
||||||
|
return reportdict
|
||||||
|
|
||||||
if name == "testreport":
|
if name == "testreport":
|
||||||
return runner.TestReport(**reportdict)
|
return runner.TestReport(**assembled_report(reportdict))
|
||||||
elif name == "collectreport":
|
elif name == "collectreport":
|
||||||
return runner.CollectReport(**reportdict)
|
return runner.CollectReport(**assembled_report(reportdict))
|
||||||
|
|||||||
Reference in New Issue
Block a user