Compare commits

..

38 Commits

Author SHA1 Message Date
Bruno Oliveira
c5f0c34d38 Prepare release 1.22.4 2018-07-27 09:17:18 -03:00
Bruno Oliveira
0b638ef02e Fix ponctuation in CHANGELOG 2018-07-27 09:16:53 -03:00
Bruno Oliveira
a9da3a5306 Update CHANGELOG template to put issue number in the start of the line
This makes using multiple paragraphs per entry possible
2018-07-27 09:15:04 -03:00
Bruno Oliveira
b814db45ce Merge pull request #317 from skirpichev/pypy3-numprocesses
Workaround cpu detection on Travis CI
2018-07-27 09:02:55 -03:00
Bruno Oliveira
ee0754bc35 Improve test for Travis fallback 2018-07-27 08:24:34 -03:00
Sergey B Kirpichev
5c59a30277 Add coverage test for Travis CI workaround 2018-07-26 12:56:10 +03:00
Sergey B Kirpichev
d3ef33ab32 Refactor automatic cpu detection code to auto_detect_cpus() 2018-07-26 12:52:18 +03:00
Sergey B Kirpichev
c950d88204 Workaround cpu detection on Travis CI
Closes #316
2018-07-25 19:05:19 +03:00
Bruno Oliveira
46119eb593 Merge pull request #315 from nicoddemus/pylib-woes
Remove last references to obsolete py.code
2018-07-25 09:07:19 -03:00
Bruno Oliveira
b5abe791ad Drop support to old pytest versions (<3.4)
Let's take the opportunity to reduce the build matrix and test with latest
pytest versions as well
2018-07-23 19:32:56 -03:00
Bruno Oliveira
fe1f239612 Remove last references to obsolete py.code
Also remove some unnecessary references to py.builtin

Fix #305
2018-07-23 17:36:03 -03:00
Bruno Oliveira
55dc257f1c Merge pull request #314 from nicoddemus/skip-tags-appveyor
Skip AppVeyor when building on tags
2018-07-23 17:14:14 -03:00
Bruno Oliveira
46dbc1223b Merge pull request #313 from nicoddemus/conda-forge-badge
Replace anaconda's badge by conda-forge's
2018-07-23 17:14:03 -03:00
Bruno Oliveira
9b46df48a6 Merge pull request #312 from nicoddemus/release-1.22.3
Release 1.22.3
2018-07-23 17:13:26 -03:00
Bruno Oliveira
c6306e4fbe Skip AppVeyor when building on tags 2018-07-23 17:03:06 -03:00
Bruno Oliveira
6ec967a3f2 Prepare release 1.22.3 2018-07-23 16:36:39 -03:00
Bruno Oliveira
0722ea12ef Use a tox environment to update the release files 2018-07-23 16:36:26 -03:00
Bruno Oliveira
a5d51f24c9 Replace anaconda's badge by conda-forge's 2018-07-23 16:33:05 -03:00
Ronny Pfannschmidt
bb91f5459d Merge pull request #309 from greut/cpu-count
plugin: get cpu_count from sched_getaffinity
2018-07-23 20:13:45 +02:00
Bruno Oliveira
00b7c4553a Merge pull request #311 from nicoddemus/skip-deploy
Skip deploy job on Travis if we are not on a tag
2018-07-21 19:20:25 -03:00
Bruno Oliveira
9ff6fc10d0 Skip deploy job on Travis if we are not on a tag 2018-07-21 18:17:21 -03:00
Bruno Oliveira
a06e75cc78 Fix flake8 issue 2018-07-21 12:09:44 -03:00
Yoan Blanc
04882074e1 plugin: get cpu_count from sched_getaffinity
Signed-off-by: Yoan Blanc <yoan.blanc@exoscale.ch>
2018-07-18 08:32:01 +02:00
Bruno Oliveira
06593498cd Merge pull request #306 from asottile/factor_out_py_std
Factor out py.std
2018-07-06 22:21:56 -03:00
Bruno Oliveira
57d09c2622 Merge pull request #308 from scop/dropthedot
Use pytest instead of py.test per upstream recommendation, #dropthedot
2018-07-06 22:20:53 -03:00
Ville Skyttä
663bbd5338 Use pytest instead of py.test per upstream recommendation, #dropthedot
http://blog.pytest.org/2016/whats-new-in-pytest-30/
https://twitter.com/hashtag/dropthedot
2018-07-07 00:01:11 +03:00
Bruno Oliveira
137b939a27 Merge pull request #307 from scop/grammar
Grammar fix
2018-07-06 12:05:26 -03:00
Ville Skyttä
17f6257a24 Grammar fix 2018-07-06 15:45:41 +03:00
Anthony Sottile
78fcf725cf Factor out py.std 2018-07-05 15:35:39 -07:00
Ronny Pfannschmidt
8d94fca964 Merge pull request #297 from nicoddemus/fix-init
Fix __init__ declaration
2018-06-11 06:01:17 +02:00
Bruno Oliveira
b06c52a5d2 Add .pytest_cache to gitignore 2018-06-03 16:10:55 -03:00
Bruno Oliveira
fd2e8a6711 Make all classes subclass from object 2018-06-03 16:10:24 -03:00
Bruno Oliveira
98ae25c12c Fix __init__ declaration 2018-06-03 16:06:52 -03:00
Bruno Oliveira
182d1f6433 Merge pull request #287 from nicoddemus/release-1.22.2
Preparing 1.22.2 release
2018-02-27 07:43:11 -03:00
Bruno Oliveira
c92bbb021a Preparing 1.22.2 release 2018-02-26 18:46:11 -03:00
Bruno Oliveira
710b324902 Merge pull request #286 from nicoddemus/fix-slaveoutput
Add backward compatibility for ``slaveoutput`` attribute to ``WorkerController`` instances
2018-02-26 18:44:24 -03:00
Bruno Oliveira
76e7b8d0c0 Add backward compatibility for `slaveoutput attribute to WorkerController` instances
Fix #285
Fix pytest-dev/pytest-cov#191
2018-02-26 14:52:55 -03:00
Bruno Oliveira
63b329c251 Merge pull request #281 from nicoddemus/release-1.22.1
Release 1.22.1
2018-02-19 19:10:07 -03:00
27 changed files with 202 additions and 116 deletions

1
.gitignore vendored
View File

@@ -30,5 +30,6 @@ issue/
pytestdebug.log pytestdebug.log
.tox/ .tox/
.cache/ .cache/
.pytest_cache/
.eggs/ .eggs/
.idea/ .idea/

View File

@@ -17,14 +17,18 @@ python:
- '3.5' - '3.5'
- '3.6' - '3.6'
env: env:
- TOXENV=py-pytest30 - TOXENV=py-pytest34
- TOXENV=py-pytest31 - TOXENV=py-pytest35
- TOXENV=py-pytest32 - TOXENV=py-pytest36
- TOXENV=py-pytest33
install: pip install tox setuptools_scm install: pip install tox setuptools_scm
script: tox script: tox
stages:
- test
- name: deploy
if: repo = pytest-dev/pytest-xdist AND tag IS present
jobs: jobs:
include: include:
- stage: test - stage: test

View File

@@ -1,3 +1,42 @@
pytest-xdist 1.22.4 (2018-07-27)
================================
Bug Fixes
---------
- `#305 <https://github.com/pytest-dev/pytest-xdist/issues/305>`_: Remove last references to obsolete ``py.code``.
Remove some unnecessary references to ``py.builtin``.
- `#316 <https://github.com/pytest-dev/pytest-xdist/issues/316>`_: Workaround cpu detection on Travis CI.
pytest-xdist 1.22.3 (2018-07-23)
================================
Bug Fixes
---------
- Fix issue of virtualized or containerized environments not reporting the number of CPUs correctly. (`#9 <https://github.com/pytest-dev/pytest-xdist/issues/9>`_)
Trivial Changes
---------------
- Make all classes subclass from ``object`` and fix ``super()`` call in ``LoadFileScheduling``; (`#297 <https://github.com/pytest-dev/pytest-xdist/issues/297>`_)
pytest-xdist 1.22.2 (2018-02-26)
================================
Bug Fixes
---------
- Add backward compatibility for ``slaveoutput`` attribute to
``WorkerController`` instances. (`#285
<https://github.com/pytest-dev/pytest-xdist/issues/285>`_)
pytest-xdist 1.22.1 (2018-02-19) pytest-xdist 1.22.1 (2018-02-19)
================================ ================================

View File

@@ -24,13 +24,13 @@ 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``. #. Create a new branch named ``release-X.Y.Z`` from the latest ``master``.
#. Install ``pytest-xdist`` and dev requirements in a virtualenv:: #. Install ``tox`` in a virtualenv::
$ pip install -e . -U -r dev-requirements.txt $ pip install tox
#. Update ``CHANGELOG.rst`` file by running:: #. Update the necessary files with::
$ towncrier --version X.Y.Z --yes $ tox -e release -- X.Y.Z
#. Commit and push the branch for review. #. Commit and push the branch for review.

View File

@@ -19,7 +19,7 @@ tag: feature
allow to run xdist own tests using its own mechanism. allow to run xdist own tests using its own mechanism.
currently this doesn't work because the remote side currently this doesn't work because the remote side
has no py.test plugin. How to configure/do has no pytest plugin. How to configure/do
register "xdist.plugin" on the remote side? register "xdist.plugin" on the remote side?
see to avoid any "from _pytest" internal imports see to avoid any "from _pytest" internal imports

View File

@@ -4,14 +4,13 @@
:alt: PyPI version :alt: PyPI version
:target: https://pypi.python.org/pypi/pytest-xdist :target: https://pypi.python.org/pypi/pytest-xdist
.. image:: https://img.shields.io/conda/vn/conda-forge/pytest-xdist.svg
:target: https://anaconda.org/conda-forge/pytest-xdist
.. image:: https://img.shields.io/pypi/pyversions/pytest-xdist.svg .. image:: https://img.shields.io/pypi/pyversions/pytest-xdist.svg
:alt: Python versions :alt: Python versions
:target: https://pypi.python.org/pypi/pytest-xdist :target: https://pypi.python.org/pypi/pytest-xdist
.. image:: https://anaconda.org/conda-forge/pytest-xdist/badges/version.svg
:alt: Anaconda version
: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
:alt: Travis CI build status :alt: Travis CI build status
:target: https://travis-ci.org/pytest-dev/pytest-xdist :target: https://travis-ci.org/pytest-dev/pytest-xdist
@@ -23,7 +22,7 @@
xdist: pytest distributed testing plugin xdist: pytest distributed testing plugin
======================================== ========================================
The `pytest-xdist`_ plugin extends py.test with some unique The `pytest-xdist`_ plugin extends pytest with some unique
test execution modes: test execution modes:
* test run parallelization_: if you have multiple CPUs or hosts you can use * test run parallelization_: if you have multiple CPUs or hosts you can use
@@ -32,14 +31,14 @@ test execution modes:
* ``--looponfail``: run your tests repeatedly in a subprocess. After each run * ``--looponfail``: run your tests repeatedly in a subprocess. After each run
py.test waits until a file in your project changes and then re-runs pytest waits until a file in your project changes and then re-runs
the previously failing tests. This is repeated until all tests pass the previously failing tests. This is repeated until all tests pass
after which again a full run is performed. after which again a full run is performed.
* `Multi-Platform`_ coverage: you can specify different Python interpreters * `Multi-Platform`_ coverage: you can specify different Python interpreters
or different platforms and run tests in parallel on all of them. or different platforms and run tests in parallel on all of them.
Before running tests remotely, ``py.test`` efficiently "rsyncs" your Before running tests remotely, ``pytest`` efficiently "rsyncs" your
program source code to the remote place. All test results program source code to the remote place. All test results
are reported back and displayed to your local terminal. are reported back and displayed to your local terminal.
You may specify different Python versions and interpreters. You may specify different Python versions and interpreters.
@@ -67,7 +66,7 @@ Speed up test runs by sending tests to multiple CPUs
To send tests to multiple CPUs, type:: To send tests to multiple CPUs, type::
py.test -n NUM pytest -n NUM
Especially for longer running tests or tests requiring Especially for longer running tests or tests requiring
a lot of I/O this can lead to considerable speed ups. This option can a lot of I/O this can lead to considerable speed ups. This option can
@@ -98,7 +97,7 @@ Running tests in a Python subprocess
To instantiate a python3.5 subprocess and send tests to it, you may type:: To instantiate a python3.5 subprocess and send tests to it, you may type::
py.test -d --tx popen//python=python3.5 pytest -d --tx popen//python=python3.5
This will start a subprocess which is run with the ``python3.5`` This will start a subprocess which is run with the ``python3.5``
Python interpreter, found in your system binary lookup path. Python interpreter, found in your system binary lookup path.
@@ -129,10 +128,10 @@ tests that you can successfully run locally. And you
have a ssh-reachable machine ``myhost``. Then have a ssh-reachable machine ``myhost``. Then
you can ad-hoc distribute your tests by typing:: you can ad-hoc distribute your tests by typing::
py.test -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg pytest -d --tx ssh=myhostpopen --rsyncdir mypkg mypkg
This will synchronize your :code:`mypkg` package directory This will synchronize your :code:`mypkg` package directory
to an remote ssh account and then locally collect tests to a remote ssh account and then locally collect tests
and send them to remote places for execution. and send them to remote places for execution.
You can specify multiple :code:`--rsyncdir` directories You can specify multiple :code:`--rsyncdir` directories
@@ -140,11 +139,11 @@ to be sent to the remote side.
.. note:: .. note::
For py.test to collect and send tests correctly For pytest to collect and send tests correctly
you not only need to make sure all code and tests you not only need to make sure all code and tests
directories are rsynced, but that any test (sub) directory directories are rsynced, but that any test (sub) directory
also has an :code:`__init__.py` file because internally also has an :code:`__init__.py` file because internally
py.test references tests as a fully qualified python pytest references tests as a fully qualified python
module path. **You will otherwise get strange errors** module path. **You will otherwise get strange errors**
during setup of the remote side. during setup of the remote side.
@@ -168,7 +167,7 @@ It will tell you that it starts listening on the default
port. You can now on your home machine specify this port. You can now on your home machine specify this
new socket host with something like this:: new socket host with something like this::
py.test -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg pytest -d --tx socket=192.168.1.102:8888 --rsyncdir mypkg mypkg
.. _`atonce`: .. _`atonce`:
@@ -180,7 +179,7 @@ Running tests on many platforms at once
The basic command to run tests on multiple platforms is:: The basic command to run tests on multiple platforms is::
py.test --dist=each --tx=spec1 --tx=spec2 pytest --dist=each --tx=spec1 --tx=spec2
If you specify a windows host, an OSX host and a Linux If you specify a windows host, an OSX host and a Linux
environment this command will send each tests to all environment this command will send each tests to all
@@ -242,7 +241,7 @@ You can also add default environments like this:
and then just type:: and then just type::
py.test --dist=each pytest --dist=each
to run tests in each of the environments. to run tests in each of the environments.

View File

@@ -1,19 +1,22 @@
environment: environment:
matrix: matrix:
# note: please use "tox --listenvs" to populate the build matrix - TOXENV: "py27-pytest36"
- TOXENV: "py27-pytest33" - TOXENV: "py34-pytest36"
- TOXENV: "py34-pytest33" - TOXENV: "py35-pytest36"
- TOXENV: "py35-pytest33" - TOXENV: "py36-pytest36"
- TOXENV: "py36-pytest33" - TOXENV: "py27-pytest36-pexpect"
- TOXENV: "py27-pytest33-pexpect" - TOXENV: "py36-pytest36-pexpect"
- TOXENV: "py36-pytest33-pexpect"
- TOXENV: "flakes" - TOXENV: "flakes"
- TOXENV: "readme" - TOXENV: "readme"
install: install:
- C:\Python35\python -m pip install -U tox setuptools_scm pip - C:\Python36\python -m pip install -U tox setuptools_scm pip
build: false # Not a C# project, build stuff at the test step instead. build: false # Not a C# project, build stuff at the test step instead.
test_script: test_script:
- C:\Python35\python -m tox - C:\Python36\python -m tox
# We don't deploy anything on tags with AppVeyor, we use Travis instead, so we
# might as well save resources
skip_tags: true

View File

@@ -13,8 +13,7 @@
{% if definitions[category]['showcontent'] %} {% if definitions[category]['showcontent'] %}
{% for text, values in sections[section][category]|dictsort(by='value') %} {% for text, values in sections[section][category]|dictsort(by='value') %}
- {{ text }}{% if category != 'vendor' %} (`{{ values[0] }} <https://github.com/pytest-dev/pytest-xdist/issues/{{ values[0][1:] }}>`_){% endif %} - `{{ values[0] }} <https://github.com/pytest-dev/pytest-xdist/issues/{{ values[0][1:] }}>`_: {{ text }}
{% endfor %} {% endfor %}
{% else %} {% else %}

View File

@@ -1 +0,0 @@
towncrier

View File

@@ -25,7 +25,7 @@ to run each test in a controlled subprocess. Here is a basic example::
If you run this with:: If you run this with::
$ py.test -n1 $ pytest -n1
=========================== test session starts ============================ =========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8 platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov
@@ -46,7 +46,7 @@ You'll see that a couple of tests are reported as crashing, indicated
by lower-case ``f`` and the respective failure summary. You can also use by lower-case ``f`` and the respective failure summary. You can also use
the xdist-provided parallelization feature to speed up your testing:: the xdist-provided parallelization feature to speed up your testing::
$ py.test -n3 $ pytest -n3
=========================== test session starts ============================ =========================== test session starts ============================
platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8 platform linux2 -- Python 2.7.3 -- pytest-2.3.0.dev8
plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov plugins: xdist, bugzilla, cache, oejskit, cli, pep8, cov

View File

@@ -8,7 +8,7 @@ passenv = http_proxy https_proxy
deps = -rrequirements.txt deps = -rrequirements.txt
changedir = {envtmpdir} changedir = {envtmpdir}
commands = commands =
py.test -s -v \ pytest -s -v \
--doctest-modules \ --doctest-modules \
--junitxml=tests.xml \ --junitxml=tests.xml \
--dist=loadscope \ --dist=loadscope \

View File

@@ -1,12 +1,12 @@
from setuptools import setup, find_packages from setuptools import setup, find_packages
install_requires = ['execnet>=1.1', 'pytest>=3.0.0', 'pytest-forked'] install_requires = ['execnet>=1.1', 'pytest>=3.4', 'pytest-forked']
setup( setup(
name="pytest-xdist", name="pytest-xdist",
use_scm_version={'write_to': 'xdist/_version.py'}, use_scm_version={'write_to': 'xdist/_version.py'},
description='py.test xdist plugin for distributed testing' description='pytest xdist plugin for distributed testing'
' and loop-on-failing modes', ' and loop-on-failing modes',
long_description=open('README.rst').read(), long_description=open('README.rst').read(),
license='MIT', license='MIT',

View File

@@ -1,4 +1,6 @@
import os
import re import re
import textwrap
import py import py
import pytest import pytest
@@ -54,8 +56,8 @@ class TestDistribution:
def test_n1_skip(self, testdir): def test_n1_skip(self, testdir):
p1 = testdir.makepyfile(""" p1 = testdir.makepyfile("""
def test_skip(): def test_skip():
import py import pytest
py.test.skip("myreason") pytest.skip("myreason")
""") """)
result = testdir.runpytest(p1, "-n1") result = testdir.runpytest(p1, "-n1")
assert result.ret == 0 assert result.ret == 0
@@ -78,7 +80,7 @@ class TestDistribution:
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 pytest
def test_fail0(): def test_fail0():
assert 0 assert 0
def test_fail1(): def test_fail1():
@@ -86,7 +88,7 @@ class TestDistribution:
def test_ok(): def test_ok():
pass pass
def test_skip(): def test_skip():
py.test.skip("hello") pytest.skip("hello")
""", ) """, )
result = testdir.runpytest(p1, "-v", '-d', '--tx=popen', '--tx=popen') result = testdir.runpytest(p1, "-v", '-d', '--tx=popen', '--tx=popen')
result.stdout.fnmatch_lines([ result.stdout.fnmatch_lines([
@@ -124,7 +126,7 @@ class TestDistribution:
def test_dist_ini_specified(self, testdir): def test_dist_ini_specified(self, testdir):
p1 = testdir.makepyfile( p1 = testdir.makepyfile(
""" """
import py import pytest
def test_fail0(): def test_fail0():
assert 0 assert 0
def test_fail1(): def test_fail1():
@@ -132,7 +134,7 @@ class TestDistribution:
def test_ok(): def test_ok():
pass pass
def test_skip(): def test_skip():
py.test.skip("hello") pytest.skip("hello")
""", ) """, )
testdir.makeini(""" testdir.makeini("""
[pytest] [pytest]
@@ -145,13 +147,13 @@ class TestDistribution:
]) ])
assert result.ret == 1 assert result.ret == 1
@py.test.mark.xfail("sys.platform.startswith('java')", run=False) @pytest.mark.xfail("sys.platform.startswith('java')", run=False)
def test_dist_tests_with_crash(self, testdir): def test_dist_tests_with_crash(self, testdir):
if not hasattr(py.std.os, 'kill'): if not hasattr(os, 'kill'):
py.test.skip("no os.kill") pytest.skip("no os.kill")
p1 = testdir.makepyfile(""" p1 = testdir.makepyfile("""
import py import pytest
def test_fail0(): def test_fail0():
assert 0 assert 0
def test_fail1(): def test_fail1():
@@ -159,7 +161,7 @@ class TestDistribution:
def test_ok(): def test_ok():
pass pass
def test_skip(): def test_skip():
py.test.skip("hello") pytest.skip("hello")
def test_crash(): def test_crash():
import time import time
import os import os
@@ -299,7 +301,7 @@ class TestDistEach:
assert not result.ret assert not result.ret
result.stdout.fnmatch_lines(["*2 pass*"]) result.stdout.fnmatch_lines(["*2 pass*"])
@py.test.mark.xfail( @pytest.mark.xfail(
run=False, run=False,
reason="other python versions might not have py.test installed") reason="other python versions might not have py.test installed")
def test_simple_diffoutput(self, testdir): def test_simple_diffoutput(self, testdir):
@@ -307,7 +309,7 @@ class TestDistEach:
for name in ("python2.5", "python2.6"): for name in ("python2.5", "python2.6"):
interp = py.path.local.sysfind(name) interp = py.path.local.sysfind(name)
if interp is None: if interp is None:
py.test.skip("%s not found" % name) pytest.skip("%s not found" % name)
interpreters.append(interp) interpreters.append(interp)
testdir.makepyfile( testdir.makepyfile(
@@ -330,11 +332,11 @@ class TestDistEach:
class TestTerminalReporting: class TestTerminalReporting:
def test_pass_skip_fail(self, testdir): def test_pass_skip_fail(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""
import py import pytest
def test_ok(): def test_ok():
pass pass
def test_skip(): def test_skip():
py.test.skip("xx") pytest.skip("xx")
def test_func(): def test_func():
assert 0 assert 0
""") """)
@@ -421,7 +423,7 @@ def test_teardownfails_one_function(testdir):
["*def teardown_function(function):*", "*1 passed*1 error*"]) ["*def teardown_function(function):*", "*1 passed*1 error*"])
@py.test.mark.xfail @pytest.mark.xfail
def test_terminate_on_hangingnode(testdir): def test_terminate_on_hangingnode(testdir):
p = testdir.makeconftest(""" p = testdir.makeconftest("""
def pytest_sessionfinish(session): def pytest_sessionfinish(session):
@@ -585,7 +587,7 @@ def test_skipping(testdir):
def test_issue34_pluginloading_in_subprocess(testdir): def test_issue34_pluginloading_in_subprocess(testdir):
testdir.tmpdir.join("plugin123.py").write( testdir.tmpdir.join("plugin123.py").write(
py.code.Source(""" textwrap.dedent("""
def pytest_namespace(): def pytest_namespace():
return {'sample_variable': 'testing'} return {'sample_variable': 'testing'}
""")) """))

View File

@@ -31,7 +31,9 @@ def test_dist_options(testdir):
def test_auto_detect_cpus(testdir, monkeypatch): def test_auto_detect_cpus(testdir, monkeypatch):
import os import os
if hasattr(os, 'cpu_count'): if hasattr(os, 'sched_getaffinity'):
monkeypatch.setattr(os, 'sched_getaffinity', lambda _pid: set(range(99)))
elif hasattr(os, 'cpu_count'):
monkeypatch.setattr(os, 'cpu_count', lambda: 99) monkeypatch.setattr(os, 'cpu_count', lambda: 99)
else: else:
import multiprocessing import multiprocessing
@@ -43,6 +45,11 @@ def test_auto_detect_cpus(testdir, monkeypatch):
config = testdir.parseconfigure("-nauto") config = testdir.parseconfigure("-nauto")
assert config.getoption('numprocesses') == 99 assert config.getoption('numprocesses') == 99
monkeypatch.delattr(os, 'sched_getaffinity', raising=False)
monkeypatch.setenv('TRAVIS', 'true')
config = testdir.parseconfigure("-nauto")
assert config.getoption('numprocesses') == 2
def test_boxed_with_collect_only(testdir): def test_boxed_with_collect_only(testdir):
from xdist.plugin import pytest_cmdline_main as check_options from xdist.plugin import pytest_cmdline_main as check_options

View File

@@ -1,11 +1,12 @@
import py import py
import pprint
import pytest import pytest
from xdist.workermanage import WorkerController, unserialize_report from xdist.workermanage import WorkerController, unserialize_report
from xdist.remote import serialize_report from xdist.remote import serialize_report
import execnet import execnet
import marshal import marshal
queue = py.builtin._tryimport("queue", "Queue") from six.moves.queue import Queue
WAIT_TIMEOUT = 10.0 WAIT_TIMEOUT = 10.0
@@ -14,7 +15,7 @@ def check_marshallable(d):
try: try:
marshal.dumps(d) marshal.dumps(d)
except ValueError: except ValueError:
py.std.pprint.pprint(d) pprint.pprint(d)
raise ValueError("not marshallable") raise ValueError("not marshallable")
@@ -32,7 +33,7 @@ class WorkerSetup:
def __init__(self, request, testdir): def __init__(self, request, testdir):
self.request = request self.request = request
self.testdir = testdir self.testdir = testdir
self.events = queue.Queue() self.events = Queue()
def setup(self, ): def setup(self, ):
self.testdir.chdir() self.testdir.chdir()
@@ -344,7 +345,7 @@ class TestWorkerInteractor:
for data in worker.slp.channel: for data in worker.slp.channel:
worker.slp.process_from_remote(data) worker.slp.process_from_remote(data)
worker.slp.process_from_remote(worker.slp.ENDMARK) worker.slp.process_from_remote(worker.slp.ENDMARK)
py.std.pprint.pprint(hookrec.hookrecorder.calls) pprint.pprint(hookrec.hookrecorder.calls)
hookrec.hookrecorder.contains([ hookrec.hookrecorder.contains([
("pytest_collectstart", "collector.fspath == aaa"), ("pytest_collectstart", "collector.fspath == aaa"),
("pytest_pycollect_makeitem", "name == 'test_func'"), ("pytest_pycollect_makeitem", "name == 'test_func'"),

View File

@@ -1,5 +1,7 @@
from __future__ import print_function
import py import py
import pytest import pytest
import textwrap
import execnet import execnet
from _pytest.pytester import HookRecorder from _pytest.pytester import HookRecorder
from xdist import workermanage, newhooks from xdist import workermanage, newhooks
@@ -169,7 +171,7 @@ class TestNodeManager:
p, = nodemanager.gwmanager.multi_exec( p, = nodemanager.gwmanager.multi_exec(
"import os ; channel.send(os.getcwd())").receive_each() "import os ; channel.send(os.getcwd())").receive_each()
p = py.path.local(p) p = py.path.local(p)
py.builtin.print_("remote curdir", p) print("remote curdir", p)
assert p == mysetup.dest.join(config.topdir.basename) assert p == mysetup.dest.join(config.topdir.basename)
assert p.join("dir1").check() assert p.join("dir1").check()
assert p.join("dir1", "file1").check() assert p.join("dir1", "file1").check()
@@ -198,7 +200,7 @@ class TestNodeManager:
source.ensure("dir1", "somefile", dir=1) source.ensure("dir1", "somefile", dir=1)
dir2.ensure("hello") dir2.ensure("hello")
source.ensure("bogusdir", "file") source.ensure("bogusdir", "file")
source.join("tox.ini").write(py.std.textwrap.dedent(""" source.join("tox.ini").write(textwrap.dedent("""
[pytest] [pytest]
rsyncdirs=dir1/dir2 rsyncdirs=dir1/dir2
""")) """))
@@ -217,7 +219,7 @@ class TestNodeManager:
dir2.ensure("hello") dir2.ensure("hello")
source.ensure("foo", "bar") source.ensure("foo", "bar")
source.ensure("bar", "foo") source.ensure("bar", "foo")
source.join("tox.ini").write(py.std.textwrap.dedent(""" source.join("tox.ini").write(textwrap.dedent("""
[pytest] [pytest]
rsyncdirs = dir1 dir5 rsyncdirs = dir1 dir5
rsyncignore = dir1/dir2 dir5/dir6 foo* rsyncignore = dir1/dir2 dir5/dir6 foo*

26
tox.ini
View File

@@ -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{27,34,35,36}-pytest{30,31,32,33} py{27,34,35,36}-pytest{34,35,36}
py{27,36}-pytest{30,31,32,33}-pexpect py{27,36}-pytest36-pexpect
py{27,36}-pytest{master,features} py{27,36}-pytest{master,features}
flakes flakes
readme readme
@@ -15,10 +15,9 @@ deps =
pycmd pycmd
# to avoid .eggs # to avoid .eggs
setuptools_scm setuptools_scm
pytest30: pytest~=3.0.5 pytest34: pytest~=3.4.0
pytest31: pytest~=3.1.0 pytest35: pytest~=3.5.0
pytest32: pytest~=3.2.0 pytest36: pytest~=3.6.0
pytest33: pytest~=3.3.0
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master pytestmaster: git+https://github.com/pytest-dev/pytest.git@master
pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features pytestfeatures: git+https://github.com/pytest-dev/pytest.git@features
pexpect: pexpect pexpect: pexpect
@@ -27,7 +26,7 @@ platform=
commands= commands=
# always clean to avoid code unmarshal mismatch on old python/pytest # always clean to avoid code unmarshal mismatch on old python/pytest
py.cleanup -aq py.cleanup -aq
py.test {posargs} pytest {posargs}
[testenv:flakes] [testenv:flakes]
changedir= changedir=
@@ -40,7 +39,18 @@ deps = readme
skip_install = true skip_install = true
commands = python setup.py check -r -s commands = python setup.py check -r -s
[testenv:release]
changedir=
decription = do a release, required posarg of the version number
basepython = python3.6
skipsdist = True
usedevelop = True
passenv = *
deps =
towncrier
commands =
towncrier --version {posargs} --yes
[pytest] [pytest]
addopts = -rsfxX addopts = -rsfxX
;; hello

View File

@@ -10,15 +10,15 @@ from xdist.scheduler import (
) )
queue = py.builtin._tryimport('queue', 'Queue') from six.moves.queue import Empty, Queue
class Interrupted(KeyboardInterrupt): class Interrupted(KeyboardInterrupt):
""" signals an immediate interruption. """ """ signals an immediate interruption. """
class DSession: class DSession(object):
"""A py.test plugin which runs a distributed test session """A pytest plugin which runs a distributed test session
At the beginning of the test session this creates a NodeManager At the beginning of the test session this creates a NodeManager
instance which creates and starts all nodes. Nodes then emit instance which creates and starts all nodes. Nodes then emit
@@ -26,7 +26,7 @@ class DSession:
methods. methods.
Once a node is started it will automatically start running the Once a node is started it will automatically start running the
py.test mainloop with some custom hooks. This means a node pytest mainloop with some custom hooks. This means a node
automatically starts collecting tests. Once tests are collected automatically starts collecting tests. Once tests are collected
it will wait for instructions. it will wait for instructions.
""" """
@@ -41,7 +41,7 @@ class DSession:
self.shuttingdown = False self.shuttingdown = False
self.countfailures = 0 self.countfailures = 0
self.maxfail = config.getvalue("maxfail") self.maxfail = config.getvalue("maxfail")
self.queue = queue.Queue() self.queue = Queue()
self._session = None self._session = None
self._failed_collection_errors = {} self._failed_collection_errors = {}
self._active_nodes = set() self._active_nodes = set()
@@ -129,7 +129,7 @@ class DSession:
try: try:
eventcall = self.queue.get(timeout=2.0) eventcall = self.queue.get(timeout=2.0)
break break
except queue.Empty: except Empty:
continue continue
callname, kwargs = eventcall callname, kwargs = eventcall
assert callname, kwargs assert callname, kwargs
@@ -316,7 +316,7 @@ class DSession:
self.config.hook.pytest_runtest_logreport(report=rep) self.config.hook.pytest_runtest_logreport(report=rep)
class TerminalDistReporter: class TerminalDistReporter(object):
def __init__(self, config): def __init__(self, config):
self.config = config self.config = config
self.tr = config.pluginmanager.getplugin("terminalreporter") self.tr = config.pluginmanager.getplugin("terminalreporter")

View File

@@ -1,15 +1,16 @@
""" """
Implement -f aka looponfailing for py.test. Implement -f aka looponfailing for pytest.
NOTE that we try to avoid loading and depending on application modules NOTE that we try to avoid loading and depending on application modules
within the controlling process (the one that starts repeatedly test within the controlling process (the one that starts repeatedly test
processes) otherwise changes to source code can crash processes) otherwise changes to source code can crash
the controlling process which should best never happen. the controlling process which should best never happen.
""" """
from __future__ import print_function
import py import py
import pytest import pytest
import sys import sys
import time
import execnet import execnet
@@ -59,7 +60,7 @@ class RemoteControl(object):
def trace(self, *args): def trace(self, *args):
if self.config.option.debug: if self.config.option.debug:
msg = " ".join([str(x) for x in args]) msg = " ".join([str(x) for x in args])
py.builtin.print_("RemoteControl:", msg) print("RemoteControl:", msg)
def initgateway(self): def initgateway(self):
return execnet.makegateway("popen") return execnet.makegateway("popen")
@@ -157,7 +158,7 @@ def init_worker_session(channel, args, option_dict):
WorkerFailSession(config, channel).main() WorkerFailSession(config, channel).main()
class WorkerFailSession: class WorkerFailSession(object):
def __init__(self, config, channel): def __init__(self, config, channel):
self.config = config self.config = config
self.channel = channel self.channel = channel
@@ -211,7 +212,7 @@ class WorkerFailSession:
self.channel.send((trails, failreports, self.collection_failed)) self.channel.send((trails, failreports, self.collection_failed))
class StatRecorder: class StatRecorder(object):
def __init__(self, rootdirlist): def __init__(self, rootdirlist):
self.rootdirlist = rootdirlist self.rootdirlist = rootdirlist
self.statcache = {} self.statcache = {}
@@ -228,7 +229,7 @@ class StatRecorder:
changed = self.check() changed = self.check()
if changed: if changed:
return return
py.std.time.sleep(checkinterval) time.sleep(checkinterval)
def check(self, removepycfiles=True): # noqa, too complex def check(self, removepycfiles=True): # noqa, too complex
changed = False changed = False
@@ -247,7 +248,7 @@ class StatRecorder:
if oldstat.mtime != curstat.mtime or \ if oldstat.mtime != curstat.mtime or \
oldstat.size != curstat.size: oldstat.size != curstat.size:
changed = True changed = True
py.builtin.print_("# MODIFIED", path) print("# MODIFIED", path)
if removepycfiles and path.ext == ".py": if removepycfiles and path.ext == ".py":
pycfile = path + "c" pycfile = path + "c"
if pycfile.check(): if pycfile.check():

View File

@@ -1,18 +1,34 @@
import os
import py import py
import pytest import pytest
def parse_numprocesses(s): def auto_detect_cpus():
if s == 'auto': try:
from os import sched_getaffinity
except ImportError:
if os.environ.get('TRAVIS') == 'true':
# workaround https://bitbucket.org/pypy/pypy/issues/2375
return 2
try: try:
from os import cpu_count from os import cpu_count
except ImportError: except ImportError:
from multiprocessing import cpu_count from multiprocessing import cpu_count
try: else:
n = cpu_count() def cpu_count():
except NotImplementedError: return len(sched_getaffinity(0))
return 1
return n if n else 1 try:
n = cpu_count()
except NotImplementedError:
return 1
return n if n else 1
def parse_numprocesses(s):
if s == 'auto':
return auto_detect_cpus()
else: else:
return int(s) return int(s)

View File

@@ -14,7 +14,7 @@ import _pytest.hookspec
import pytest import pytest
class WorkerInteractor: class WorkerInteractor(object):
def __init__(self, config, channel): def __init__(self, config, channel):
self.config = config self.config = config
self.workerid = config.workerinput.get('workerid', "?") self.workerid = config.workerinput.get('workerid', "?")

View File

@@ -1,4 +1,4 @@
import py from __future__ import print_function
from difflib import unified_diff from difflib import unified_diff
@@ -17,10 +17,10 @@ def report_collection_diff(from_collection, to_collection, from_id, to_id):
fromfile=from_id, fromfile=from_id,
tofile=to_id, tofile=to_id,
) )
error_message = py.builtin._totext( error_message = (
'Different tests were collected between {from_id} and {to_id}. ' u'Different tests were collected between {from_id} and {to_id}. '
'The difference is:\n' u'The difference is:\n'
'{diff}' u'{diff}'
).format(from_id=from_id, to_id=to_id, diff='\n'.join(diff)) ).format(from_id=from_id, to_id=to_id, diff='\n'.join(diff))
msg = "\n".join([x.rstrip() for x in error_message.split("\n")]) msg = "\n".join([x.rstrip() for x in error_message.split("\n")])
return msg return msg

View File

@@ -4,7 +4,7 @@ from xdist.workermanage import parse_spec_config
from xdist.report import report_collection_diff from xdist.report import report_collection_diff
class EachScheduling: class EachScheduling(object):
"""Implement scheduling of test items on all nodes """Implement scheduling of test items on all nodes
If a node gets added after the test run is started then it is If a node gets added after the test run is started then it is

View File

@@ -20,7 +20,7 @@ class LoadFileScheduling(LoadScopeScheduling):
This class behaves very much like LoadScopeScheduling, but with a file-level scope. This class behaves very much like LoadScopeScheduling, but with a file-level scope.
""" """
def __init(self, config, log=None): def __init__(self, config, log=None):
super(LoadFileScheduling, self).__init__(config, log) super(LoadFileScheduling, self).__init__(config, log)
if log is None: if log is None:
self.log = Producer('loadfilesched') self.log = Producer('loadfilesched')

View File

@@ -7,7 +7,7 @@ from xdist.workermanage import parse_spec_config
from xdist.report import report_collection_diff from xdist.report import report_collection_diff
class LoadScheduling: class LoadScheduling(object):
"""Implement load scheduling across nodes. """Implement load scheduling across nodes.
This distributes the tests collected across all nodes so each test This distributes the tests collected across all nodes so each test

View File

@@ -6,7 +6,7 @@ from xdist.report import report_collection_diff
from xdist.workermanage import parse_spec_config from xdist.workermanage import parse_spec_config
class LoadScopeScheduling: class LoadScopeScheduling(object):
"""Implement load scheduling across nodes, but grouping test by scope. """Implement load scheduling across nodes, but grouping test by scope.
This distributes the tests collected across all nodes so each test is run This distributes the tests collected across all nodes so each test is run

View File

@@ -1,10 +1,13 @@
from __future__ import print_function
import fnmatch import fnmatch
import os import os
import re import re
import threading
import py import py
import pytest import pytest
import execnet import execnet
import xdist.remote import xdist.remote
from _pytest import runner # XXX load dynamically from _pytest import runner # XXX load dynamically
@@ -32,7 +35,7 @@ class NodeManager(object):
def __init__(self, config, specs=None, defaultchdir="pyexecnetcache"): def __init__(self, config, specs=None, defaultchdir="pyexecnetcache"):
self.config = config self.config = config
self._nodesready = py.std.threading.Event() self._nodesready = threading.Event()
self.trace = self.config.trace.get("nodemanager") self.trace = self.config.trace.get("nodemanager")
self.group = execnet.Group() self.group = execnet.Group()
if specs is None: if specs is None:
@@ -47,7 +50,7 @@ class NodeManager(object):
self.specs.append(spec) self.specs.append(spec)
self.roots = self._getrsyncdirs() self.roots = self._getrsyncdirs()
self.rsyncoptions = self._getrsyncoptions() self.rsyncoptions = self._getrsyncoptions()
self._rsynced_specs = py.builtin.set() self._rsynced_specs = set()
def rsync_roots(self, gateway): def rsync_roots(self, gateway):
"""Rsync the set of roots to the node's gateway cwd.""" """Rsync the set of roots to the node's gateway cwd."""
@@ -179,12 +182,11 @@ class HostRSync(execnet.RSync):
if self._verbose: if self._verbose:
path = os.path.basename(self._sourcedir) + "/" + modified_rel_path path = os.path.basename(self._sourcedir) + "/" + modified_rel_path
remotepath = gateway.spec.chdir remotepath = gateway.spec.chdir
py.builtin.print_('%s:%s <= %s' % print('%s:%s <= %s' % (gateway.spec, remotepath, path))
(gateway.spec, remotepath, path))
def make_reltoroot(roots, args): def make_reltoroot(roots, args):
# XXX introduce/use public API for splitting py.test args # XXX introduce/use public API for splitting pytest args
splitcode = "::" splitcode = "::"
result = [] result = []
for arg in args: for arg in args:
@@ -307,7 +309,7 @@ class WorkerController(object):
self.notify_inproc(eventname, node=self, **kwargs) self.notify_inproc(eventname, node=self, **kwargs)
elif eventname == "workerfinished": elif eventname == "workerfinished":
self._down = True self._down = True
self.workeroutput = kwargs['workeroutput'] self.workeroutput = self.slaveoutput = kwargs['workeroutput']
self.notify_inproc("workerfinished", node=self) self.notify_inproc("workerfinished", node=self)
elif eventname in ("logstart", "logfinish"): elif eventname in ("logstart", "logfinish"):
self.notify_inproc(eventname, node=self, **kwargs) self.notify_inproc(eventname, node=self, **kwargs)
@@ -332,8 +334,9 @@ class WorkerController(object):
# should not land in receiver-thread # should not land in receiver-thread
raise raise
except: # noqa except: # noqa
excinfo = py.code.ExceptionInfo() from _pytest._code import ExceptionInfo
py.builtin.print_("!" * 20, excinfo) excinfo = ExceptionInfo()
print("!" * 20, excinfo)
self.config.notify_exception(excinfo) self.config.notify_exception(excinfo)
self.shutdown() self.shutdown()
self.notify_inproc("errordown", node=self, error=excinfo) self.notify_inproc("errordown", node=self, error=excinfo)