Compare commits

..

63 Commits

Author SHA1 Message Date
Bruno Oliveira
4b487ed5e1 Manually fix changelog title
Some checks failed
build / build (ubuntu-latest, 3.10-dev, py310-pytestlatest) (push) Failing after 3s
build / build (ubuntu-latest, 3.6, py36-pytestlatest) (push) Failing after 2s
build / build (ubuntu-latest, 3.7, py37-pytestlatest) (push) Failing after 2s
build / build (ubuntu-latest, 3.8, py38-psutil) (push) Failing after 2s
build / build (ubuntu-latest, 3.8, py38-pytestlatest) (push) Failing after 2s
build / build (ubuntu-latest, 3.8, py38-pytestmain) (push) Failing after 3s
build / build (ubuntu-latest, 3.8, py38-setproctitle) (push) Failing after 2s
build / build (ubuntu-latest, 3.9, py39-pytestlatest) (push) Failing after 2s
build / build (windows-latest, 3.10-dev, py310-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.6, py36-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.7, py37-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-psutil) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestmain) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-setproctitle) (push) Has been cancelled
build / build (windows-latest, 3.9, py39-pytestlatest) (push) Has been cancelled
build / deploy (push) Has been cancelled
2021-09-20 21:19:04 -03:00
Bruno Oliveira
ecf4d3be31 Release 2.4.0 2021-09-20 21:17:31 -03:00
Bruno Oliveira
87d89795e3 Merge pull request #704 from hugovk/add-3.10 2021-09-20 21:16:42 -03:00
Bruno Oliveira
b4544c8849 Merge pull request #706 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-09-20 21:04:22 -03:00
pre-commit-ci[bot]
66dc3904b4 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.8b0 → 21.9b0](https://github.com/psf/black/compare/21.8b0...21.9b0)
2021-09-20 20:00:03 +00:00
Hugo van Kemenade
e0ce1b7418 Add news file to add support for Python 3.10 2021-09-15 17:28:44 +03:00
Hugo van Kemenade
ed47f0e01a Add support for Python 3.10 2021-09-15 17:14:59 +03:00
Bruno Oliveira
1c8178abc5 Merge pull request #703 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-09-15 08:52:56 -03:00
pre-commit-ci[bot]
9807064f66 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.25.0 → v2.26.0](https://github.com/asottile/pyupgrade/compare/v2.25.0...v2.26.0)
2021-09-13 19:41:01 +00:00
David Lamparter
766e67ce52 Use setproctitle if available to show state (#696)
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2021-09-03 12:58:01 -03:00
Bruno Oliveira
305aeeacec Merge pull request #700 from nicoddemus/fix-pytestmaster
Fix test_fixture_teardown_failure for pytest master
2021-09-03 12:45:44 -03:00
Bruno Oliveira
90fc616906 Fix test_fixture_teardown_failure for pytest master
`--debug` now receives an optional file, so the way the test
was calling pytest made it seem like it was passing the python file
file as the debug file.
2021-09-01 07:54:13 -03:00
Bruno Oliveira
5f4d6ea0e6 Merge pull request #699 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-09-01 07:50:56 -03:00
pre-commit-ci[bot]
a0ad5cf041 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.7b0 → 21.8b0](https://github.com/psf/black/compare/21.7b0...21.8b0)
- [github.com/asottile/pyupgrade: v2.24.0 → v2.25.0](https://github.com/asottile/pyupgrade/compare/v2.24.0...v2.25.0)
2021-08-30 19:26:09 +00:00
Bruno Oliveira
5e0b40cea2 Merge pull request #694 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-08-24 08:08:18 -03:00
pre-commit-ci[bot]
513e203ee1 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.23.0 → v2.24.0](https://github.com/asottile/pyupgrade/compare/v2.23.0...v2.24.0)
2021-08-23 18:59:34 +00:00
Thomas Grainger
a5c65dd1e4 Merge pull request #689 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-07-26 19:55:07 +01:00
pre-commit-ci[bot]
ccb14f24fc [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.21.2 → v2.23.0](https://github.com/asottile/pyupgrade/compare/v2.21.2...v2.23.0)
2021-07-26 18:05:33 +00:00
Ronny Pfannschmidt
76dbde0475 Merge pull request #686 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-07-20 10:44:03 +02:00
pre-commit-ci[bot]
1167e1609c [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.6b0 → 21.7b0](https://github.com/psf/black/compare/21.6b0...21.7b0)
- [github.com/asottile/pyupgrade: v2.21.0 → v2.21.2](https://github.com/asottile/pyupgrade/compare/v2.21.0...v2.21.2)
2021-07-19 22:14:44 +00:00
Bruno Oliveira
348a956724 Merge pull request #678 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-07-13 08:02:41 -03:00
pre-commit-ci[bot]
dba716ba91 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.19.4 → v2.21.0](https://github.com/asottile/pyupgrade/compare/v2.19.4...v2.21.0)
2021-07-12 23:28:12 +00:00
Ronny Pfannschmidt
e7c42f0b3b finish release 2.3.0 2021-06-16 23:53:52 +02:00
Ronny Pfannschmidt
fe57b39563 fixup: add release title underline for 2.3.0
Some checks failed
build / build (ubuntu-latest, 3.6, py36-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.7, py37-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-psutil) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-pytestlatest) (push) Failing after 1s
build / build (ubuntu-latest, 3.8, py38-pytestmain) (push) Failing after 1s
build / build (ubuntu-latest, 3.9, py39-pytestlatest) (push) Failing after 1s
build / build (windows-latest, 3.6, py36-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.7, py37-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-psutil) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestlatest) (push) Has been cancelled
build / build (windows-latest, 3.8, py38-pytestmain) (push) Has been cancelled
build / build (windows-latest, 3.9, py39-pytestlatest) (push) Has been cancelled
build / deploy (push) Has been cancelled
2021-06-16 11:49:15 +02:00
Ronny Pfannschmidt
26e7d953f4 prepare release 2.3.0 2021-06-16 11:46:18 +02:00
Ronny Pfannschmidt
b02a6db771 Merge pull request #667 from graingert/fix-sys-path
fix sys.path for local workers  Fixes #421
2021-06-16 11:40:55 +02:00
Thomas Grainger
b0722675e0 add newsfile 2021-06-16 10:34:19 +01:00
Bruno Oliveira
881cc48271 Merge pull request #672 from pytest-dev/pre-commit-ci-update-config 2021-06-15 09:46:15 -03:00
pre-commit-ci[bot]
958679e9a7 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.5b2 → 21.6b0](https://github.com/psf/black/compare/21.5b2...21.6b0)
- [github.com/asottile/pyupgrade: v2.19.1 → v2.19.4](https://github.com/asottile/pyupgrade/compare/v2.19.1...v2.19.4)
2021-06-14 17:41:19 +00:00
Ronny Pfannschmidt
7f07d50808 Merge pull request #646 from kroeschl/numprocesses-logical
Add explicit 'logical' choice to --numprocesses flag
2021-06-08 08:44:29 +02:00
Ronny Pfannschmidt
fb518def55 Merge pull request #669 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-06-08 08:43:14 +02:00
pre-commit-ci[bot]
0b14d92894 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/asottile/pyupgrade: v2.19.0 → v2.19.1](https://github.com/asottile/pyupgrade/compare/v2.19.0...v2.19.1)
2021-06-08 01:53:05 +00:00
Thomas Grainger
02f971d45f swap docstring 2021-06-03 21:46:21 +01:00
Thomas Grainger
56e631b4bb fix sys.path for local workers Fixes #421 2021-06-03 21:20:11 +01:00
Bruno Oliveira
7f2426b11d Merge pull request #665 from pytest-dev/pre-commit-ci-update-config 2021-05-31 16:35:11 -03:00
pre-commit-ci[bot]
0c84201b40 [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
2021-05-31 19:13:26 +00:00
pre-commit-ci[bot]
08d8a62040 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.5b1 → 21.5b2](https://github.com/psf/black/compare/21.5b1...21.5b2)
- [github.com/asottile/pyupgrade: v2.16.0 → v2.19.0](https://github.com/asottile/pyupgrade/compare/v2.16.0...v2.19.0)
2021-05-31 17:33:03 +00:00
Bruno Oliveira
f36ee80e8c Merge pull request #666 from nicoddemus/296-capture-docs 2021-05-27 12:50:07 -03:00
Bruno Oliveira
f75479f7f3 Document that -s doesn't work with pytest-xdist
Fix #296
2021-05-27 10:49:38 -03:00
Ronny Pfannschmidt
10ec3af8a0 Merge pull request #662 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-05-17 21:34:49 +02:00
pre-commit-ci[bot]
577e24b5bd [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/pre-commit/pre-commit-hooks: v3.4.0 → v4.0.1](https://github.com/pre-commit/pre-commit-hooks/compare/v3.4.0...v4.0.1)
- [github.com/asottile/pyupgrade: v2.15.0 → v2.16.0](https://github.com/asottile/pyupgrade/compare/v2.15.0...v2.16.0)
2021-05-17 17:29:04 +00:00
Bruno Oliveira
e37039ee41 Merge pull request #660 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-05-13 08:11:26 -03:00
pre-commit-ci[bot]
cc828ddbfc [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.4b2 → 21.5b1](https://github.com/psf/black/compare/21.4b2...21.5b1)
- [github.com/PyCQA/flake8: 3.9.1 → 3.9.2](https://github.com/PyCQA/flake8/compare/3.9.1...3.9.2)
- [github.com/asottile/pyupgrade: v2.14.0 → v2.15.0](https://github.com/asottile/pyupgrade/compare/v2.14.0...v2.15.0)
2021-05-10 20:26:00 +00:00
Bruno Oliveira
4f459aa119 Merge pull request #656 from pytest-dev/pre-commit-ci-update-config
[pre-commit.ci] pre-commit autoupdate
2021-05-04 11:24:47 -03:00
pre-commit-ci[bot]
3a7604bdb7 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/psf/black: 21.4b0 → 21.4b2](https://github.com/psf/black/compare/21.4b0...21.4b2)
- [github.com/asottile/pyupgrade: v2.13.0 → v2.14.0](https://github.com/asottile/pyupgrade/compare/v2.13.0...v2.14.0)
2021-05-03 17:21:44 +00:00
Bruno Oliveira
d4d01bba2e Apply suggestions from code review 2021-04-27 11:51:04 -03:00
Kyle Roeschley
4d27110beb Add 'logical' CPU count choice for --numprocesses
This can significantly speed up runs when testing is not CPU-bound.
2021-04-27 08:47:21 -05:00
pre-commit-ci[bot]
260211603e [pre-commit.ci] pre-commit autoupdate (#655)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-04-27 09:30:37 -03:00
xoviat
f861f1667d add new pytest_handlecrashitem hook
allows handling and rescheduling crash tests

PR #651

Co-authored-by: xoviat <xoviat@users.noreply.github.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2021-04-27 09:07:49 -03:00
Bruno Oliveira
f5665436e9 Merge pull request #654 from nicoddemus/drop-py35
Drop Python 3.5 support
2021-04-27 08:44:56 -03:00
Bruno Oliveira
8b35945d54 Drop Python 3.5 support
pytest no longer supports it, and our CI is broken because of it.
2021-04-25 09:31:10 -03:00
Bruno Oliveira
3fe2094197 Merge pull request #649 from pytest-dev/pre-commit-ci-update-config 2021-04-24 16:19:00 -03:00
pre-commit-ci[bot]
04dde88996 [pre-commit.ci] pre-commit autoupdate
updates:
- [github.com/PyCQA/flake8: 3.9.0 → 3.9.1](https://github.com/PyCQA/flake8/compare/3.9.0...3.9.1)
- [github.com/asottile/pyupgrade: v2.11.0 → v2.12.0](https://github.com/asottile/pyupgrade/compare/v2.11.0...v2.12.0)
2021-04-19 17:12:26 +00:00
pre-commit-ci[bot]
9785a316ae [pre-commit.ci] pre-commit autoupdate (#645)
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
2021-04-07 10:13:14 -03:00
Thomas Grainger
1dc019709c Use 'main' to refer to pytest default branch in tox env names. (#643)
Co-authored-by: Thomas Grainger <tagrain@gmail.com>
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2021-03-20 11:43:57 -03:00
Bruno Oliveira
d909dcc61f Merge pull request #642 from adamchainz/upgrade_pre_commit 2021-03-20 11:34:20 -03:00
Adam Johnson
f02aa70c95 Upgrade pre-commit hooks
Use `pre-commit autoupdate` to upgrade, and follow the message from `pre-commit-hooks` to move to the official `flake8` hook.
2021-03-20 11:13:59 -03:00
Bruno Oliveira
1637dc18d0 Merge pull request #638 from AizenGlobal/hotfix/master_to_main
[HOTFIX] rename pytest's branch(master to main)
2021-03-20 11:08:42 -03:00
baekdohyeop
f5342962ab [HOTFIX] rename pytest's branch(master to main) 2021-03-15 13:14:08 +09:00
Ronny Pfannschmidt
1189ae4b91 Merge pull request #624 from pytest-dev/fix-592-use-controller
prepare #592: replace master with controller where we can
2021-02-11 12:27:08 +01:00
Ronny Pfannschmidt
89c86af894 Merge pull request #627 from pytest-dev/release-2.2.1
release 2.2.1
2021-02-09 22:11:18 +01:00
Ronny Pfannschmidt
9e4e8b74e0 Apply suggestions from code review
Co-authored-by: Bruno Oliveira <nicoddemus@gmail.com>
2021-02-09 21:51:30 +01:00
Ronny Pfannschmidt
166bdb4103 prepare #592: replace master with controller where we can 2021-02-07 22:08:22 +01:00
21 changed files with 371 additions and 130 deletions

View File

@@ -11,19 +11,17 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
tox_env: tox_env:
- "py35-pytestlatest"
- "py36-pytestlatest" - "py36-pytestlatest"
- "py37-pytestlatest" - "py37-pytestlatest"
- "py38-pytestlatest" - "py38-pytestlatest"
- "py39-pytestlatest" - "py39-pytestlatest"
- "py38-pytestmaster" - "py310-pytestlatest"
- "py38-pytestmain"
- "py38-psutil" - "py38-psutil"
- "linting" - "py38-setproctitle"
os: [ubuntu-latest, windows-latest] os: [ubuntu-latest, windows-latest]
include: include:
- tox_env: "py35-pytestlatest"
python: "3.5"
- tox_env: "py36-pytestlatest" - tox_env: "py36-pytestlatest"
python: "3.6" python: "3.6"
- tox_env: "py37-pytestlatest" - tox_env: "py37-pytestlatest"
@@ -32,17 +30,19 @@ jobs:
python: "3.8" python: "3.8"
- tox_env: "py39-pytestlatest" - tox_env: "py39-pytestlatest"
python: "3.9" python: "3.9"
- tox_env: "py38-pytestmaster" - tox_env: "py310-pytestlatest"
python: "3.10-dev"
- tox_env: "py38-pytestmain"
python: "3.8" python: "3.8"
- tox_env: "py38-psutil" - tox_env: "py38-psutil"
python: "3.8" python: "3.8"
- tox_env: "linting" - tox_env: "py38-setproctitle"
python: "3.7" python: "3.8"
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v1 uses: actions/setup-python@v2
with: with:
python-version: ${{ matrix.python }} python-version: ${{ matrix.python }}
- name: Install tox - name: Install tox
@@ -53,36 +53,18 @@ jobs:
run: | run: |
tox -e ${{ matrix.tox_env }} tox -e ${{ matrix.tox_env }}
linting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Set up Python
uses: actions/setup-python@v1
with:
python-version: "3.7"
- name: Install tox
run: |
python -m pip install --upgrade pip
pip install tox
- name: Linting
run: |
tox -e linting
deploy: deploy:
if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags') if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags')
runs-on: ubuntu-latest runs-on: ubuntu-latest
needs: [build, linting] needs: build
steps: steps:
- uses: actions/checkout@v1 - uses: actions/checkout@v2
- name: Set up Python - name: Set up Python
uses: actions/setup-python@v1 uses: actions/setup-python@v2
with: with:
python-version: "3.7" python-version: "3.7"
- name: Install wheel - name: Install wheel

View File

@@ -1,20 +1,23 @@
repos: repos:
- repo: https://github.com/ambv/black - repo: https://github.com/psf/black
rev: 19.10b0 rev: 21.9b0
hooks: hooks:
- id: black - id: black
args: [--safe, --quiet, --target-version, py35] args: [--safe, --quiet, --target-version, py35]
language_version: python3.7 language_version: python3.7
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v2.4.0 rev: v4.0.1
hooks: hooks:
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer
- id: check-yaml - id: check-yaml
- id: debug-statements - id: debug-statements
- repo: https://github.com/PyCQA/flake8
rev: 3.9.2
hooks:
- id: flake8 - id: flake8
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/asottile/pyupgrade
rev: v2.7.2 rev: v2.26.0
hooks: hooks:
- id: pyupgrade - id: pyupgrade
args: [--py3-plus] args: [--py3-plus]

View File

@@ -1,3 +1,51 @@
pytest-xdist 2.4.0 (2021-09-20)
===============================
Features
--------
- `#696 <https://github.com/pytest-dev/pytest-xdist/issues/696>`_: On Linux, the process title now changes to indicate the current worker state (running/idle).
Depends on the `setproctitle <https://pypi.org/project/setproctitle/>`__ package, which can be installed with ``pip install pytest-xdist[setproctitle]``.
- `#704 <https://github.com/pytest-dev/pytest-xdist/issues/704>`_: Add support for Python 3.10.
pytest-xdist 2.3.0 (2021-06-16)
===============================
Deprecations and Removals
-------------------------
- `#654 <https://github.com/pytest-dev/pytest-xdist/issues/654>`_: Python 3.5 is no longer supported.
Features
--------
- `#646 <https://github.com/pytest-dev/pytest-xdist/issues/646>`_: Add ``--numprocesses=logical`` flag, which automatically uses the number of logical CPUs available, instead of physical CPUs with ``auto``.
This is very useful for test suites which are not CPU-bound.
- `#650 <https://github.com/pytest-dev/pytest-xdist/issues/650>`_: Added new ``pytest_handlecrashitem`` hook to allow handling and rescheduling crashed items.
Bug Fixes
---------
- `#421 <https://github.com/pytest-dev/pytest-xdist/issues/421>`_: Copy the parent process sys.path into local workers, to work around execnet's python -c adding the current directory to sys.path.
- `#638 <https://github.com/pytest-dev/pytest-xdist/issues/638>`_: Fix issue caused by changing the branch name of the pytest repository.
Trivial Changes
---------------
- `#592 <https://github.com/pytest-dev/pytest-xdist/issues/592>`_: Replace master with controller where ever possible.
- `#643 <https://github.com/pytest-dev/pytest-xdist/issues/643>`_: Use 'main' to refer to pytest default branch in tox env names.
pytest-xdist 2.2.1 (2021-02-09) pytest-xdist 2.2.1 (2021-02-09)
=============================== ===============================

View File

@@ -45,6 +45,9 @@ If you would like to know how pytest-xdist works under the covers, checkout
`OVERVIEW <https://github.com/pytest-dev/pytest-xdist/blob/master/OVERVIEW.md>`_. `OVERVIEW <https://github.com/pytest-dev/pytest-xdist/blob/master/OVERVIEW.md>`_.
**NOTE**: due to how pytest-xdist is implemented, the ``-s/--capture=no`` option does not work.
Installation Installation
------------ ------------
@@ -149,16 +152,16 @@ where executing a high-scope fixture exactly once is important.
Running tests in a Python subprocess Running tests in a Python subprocess
------------------------------------ ------------------------------------
To instantiate a python3.5 subprocess and send tests to it, you may type:: To instantiate a python3.9 subprocess and send tests to it, you may type::
pytest -d --tx popen//python=python3.5 pytest -d --tx popen//python=python3.9
This will start a subprocess which is run with the ``python3.5`` This will start a subprocess which is run with the ``python3.9``
Python interpreter, found in your system binary lookup path. Python interpreter, found in your system binary lookup path.
If you prefix the --tx option value like this:: If you prefix the --tx option value like this::
--tx 3*popen//python=python3.5 --tx 3*popen//python=python3.9
then three subprocesses would be created and tests then three subprocesses would be created and tests
will be load-balanced across these three processes. will be load-balanced across these three processes.
@@ -285,7 +288,18 @@ Since version 2.0, the following functions are also available in the ``xdist`` m
""" """
def is_xdist_master(request_or_session) -> bool: def is_xdist_master(request_or_session) -> bool:
"""Return `True` if this is the xdist master, `False` otherwise """Return `True` if this is the xdist controller, `False` otherwise
Note: this method also returns `False` when distribution has not been
activated at all.
deprecated alias for is_xdist_controller
:param request_or_session: the `pytest` `request` or `session` object
"""
def is_xdist_controller(request_or_session) -> bool:
"""Return `True` if this is the xdist controller, `False` otherwise
Note: this method also returns `False` when distribution has not been Note: this method also returns `False` when distribution has not been
activated at all. activated at all.
@@ -295,7 +309,7 @@ Since version 2.0, the following functions are also available in the ``xdist`` m
def get_xdist_worker_id(request_or_session) -> str: def get_xdist_worker_id(request_or_session) -> str:
"""Return the id of the current worker ('gw0', 'gw1', etc) or 'master' """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
if running on the 'master' node. if running on the controller node.
If not distributing tests (for example passing `-n0` or not passing `-n` at all) also return 'master'. If not distributing tests (for example passing `-n0` or not passing `-n` at all) also return 'master'.
@@ -303,6 +317,26 @@ Since version 2.0, the following functions are also available in the ``xdist`` m
""" """
Identifying workers from the system environment
-----------------------------------------------
*New in version UNRELEASED TBD FIXME*
If the `setproctitle`_ package is installed, ``pytest-xdist`` will use it to
update the process title (command line) on its workers to show their current
state. The titles used are ``[pytest-xdist running] file.py/node::id`` and
``[pytest-xdist idle]``, visible in standard tools like ``ps`` and ``top`` on
Linux, Mac OS X and BSD systems. For Windows, please follow `setproctitle`_'s
pointer regarding the Process Explorer tool.
This is intended purely as an UX enhancement, e.g. to track down issues with
long-running or CPU intensive tests. Errors in changing the title are ignored
silently. Please try not to rely on the title format or title changes in
external scripts.
.. _`setproctitle`: https://pypi.org/project/setproctitle/
Uniquely identifying the current test run Uniquely identifying the current test run
----------------------------------------- -----------------------------------------
@@ -360,7 +394,7 @@ You can also add default environments like this:
.. code-block:: ini .. code-block:: ini
[pytest] [pytest]
addopts = --tx ssh=myhost//python=python3.5 --tx ssh=myhost//python=python3.6 addopts = --tx ssh=myhost//python=python3.9 --tx ssh=myhost//python=python3.6
and then just type:: and then just type::

View File

@@ -18,12 +18,16 @@ setup(
platforms=["linux", "osx", "win32"], platforms=["linux", "osx", "win32"],
packages=find_packages(where="src"), packages=find_packages(where="src"),
package_dir={"": "src"}, package_dir={"": "src"},
extras_require={"testing": ["filelock"], "psutil": ["psutil>=3.0"]}, extras_require={
"testing": ["filelock"],
"psutil": ["psutil>=3.0"],
"setproctitle": ["setproctitle"],
},
entry_points={ entry_points={
"pytest11": ["xdist = xdist.plugin", "xdist.looponfail = xdist.looponfail"] "pytest11": ["xdist = xdist.plugin", "xdist.looponfail = xdist.looponfail"]
}, },
zip_safe=False, zip_safe=False,
python_requires=">=3.5", python_requires=">=3.6",
install_requires=install_requires, install_requires=install_requires,
setup_requires=["setuptools_scm"], setup_requires=["setuptools_scm"],
classifiers=[ classifiers=[
@@ -40,10 +44,10 @@ setup(
"Programming Language :: Python", "Programming Language :: Python",
"Programming Language :: Python :: 3", "Programming Language :: Python :: 3",
"Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
], ],
) )

View File

@@ -1,4 +1,15 @@
from xdist.plugin import is_xdist_worker, is_xdist_master, get_xdist_worker_id from xdist.plugin import (
is_xdist_worker,
is_xdist_master,
get_xdist_worker_id,
is_xdist_controller,
)
from xdist._version import version as __version__ from xdist._version import version as __version__
__all__ = ["__version__", "is_xdist_worker", "is_xdist_master", "get_xdist_worker_id"] __all__ = [
"__version__",
"is_xdist_worker",
"is_xdist_master",
"is_xdist_controller",
"get_xdist_worker_id",
]

View File

@@ -87,7 +87,7 @@ class DSession:
self._session = None self._session = None
def pytest_collection(self): def pytest_collection(self):
# prohibit collection of test items in master process # prohibit collection of test items in controller process
return True return True
@pytest.mark.trylast @pytest.mark.trylast
@@ -240,7 +240,7 @@ class DSession:
return return
self.config.hook.pytest_xdist_node_collection_finished(node=node, ids=ids) self.config.hook.pytest_xdist_node_collection_finished(node=node, ids=ids)
# tell session which items were effectively collected otherwise # tell session which items were effectively collected otherwise
# the master node will finish the session with EXIT_NOTESTSCOLLECTED # the controller node will finish the session with EXIT_NOTESTSCOLLECTED
self._session.testscollected = len(ids) self._session.testscollected = len(ids)
self.sched.add_node_collection(node, ids) self.sched.add_node_collection(node, ids)
if self.terminal: if self.terminal:
@@ -343,6 +343,12 @@ class DSession:
nodeid, (fspath, None, fspath), (), "failed", msg, "???" nodeid, (fspath, None, fspath), (), "failed", msg, "???"
) )
rep.node = worker rep.node = worker
self.config.hook.pytest_handlecrashitem(
crashitem=nodeid,
report=rep,
sched=self.sched,
)
self.config.hook.pytest_runtest_logreport(report=rep) self.config.hook.pytest_runtest_logreport(report=rep)

View File

@@ -61,7 +61,7 @@ class RemoteControl:
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)
print("RemoteControl:", msg) print("RemoteControl:", msg)
def initgateway(self): def initgateway(self):

View File

@@ -48,8 +48,7 @@ def pytest_testnodedown(node, error):
def pytest_xdist_node_collection_finished(node, ids): def pytest_xdist_node_collection_finished(node, ids):
"""called by the master node when a node finishes collecting. """called by the controller node when a worker node finishes collecting."""
"""
@pytest.mark.firstresult @pytest.mark.firstresult
@@ -65,3 +64,20 @@ def pytest_xdist_auto_num_workers(config):
.. versionadded:: 2.1 .. versionadded:: 2.1
""" """
@pytest.mark.firstresult
def pytest_handlecrashitem(crashitem, report, sched):
"""
Handle a crashitem, modifying the report if necessary.
The scheduler is provided as a parameter to reschedule the test if desired with
`sched.mark_test_pending`.
def pytest_handlecrashitem(crashitem, report, sched):
if should_rerun(crashitem):
sched.mark_test_pending(crashitem)
report.outcome = "rerun"
.. versionadded:: 2.2.1
"""

View File

@@ -1,17 +1,21 @@
import os import os
import uuid import uuid
import sys
import py import py
import pytest import pytest
_sys_path = list(sys.path) # freeze a copy of sys.path at interpreter startup
def pytest_xdist_auto_num_workers():
def pytest_xdist_auto_num_workers(config):
try: try:
import psutil import psutil
except ImportError: except ImportError:
pass pass
else: else:
count = psutil.cpu_count(logical=False) or psutil.cpu_count() use_logical = config.option.numprocesses == "logical"
count = psutil.cpu_count(logical=use_logical) or psutil.cpu_count()
if count: if count:
return count return count
try: try:
@@ -36,8 +40,8 @@ def pytest_xdist_auto_num_workers():
def parse_numprocesses(s): def parse_numprocesses(s):
if s == "auto": if s in ("auto", "logical"):
return "auto" return s
elif s is not None: elif s is not None:
return int(s) return int(s)
@@ -51,9 +55,10 @@ def pytest_addoption(parser):
metavar="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'. With 'auto', attempt "
"you can use 'auto' here for auto detection CPUs number on " "to detect physical CPU count. With 'logical', detect logical CPU "
"host system and it will be 0 when used with --pdb", "count. If physical CPU count cannot be found, falls back to logical "
"count. This will be 0 when used with --pdb.",
) )
group.addoption( group.addoption(
"--maxprocesses", "--maxprocesses",
@@ -190,7 +195,7 @@ def pytest_configure(config):
@pytest.mark.tryfirst @pytest.mark.tryfirst
def pytest_cmdline_main(config): def pytest_cmdline_main(config):
usepdb = config.getoption("usepdb", False) # a core option usepdb = config.getoption("usepdb", False) # a core option
if config.option.numprocesses == "auto": if config.option.numprocesses in ("auto", "logical"):
if usepdb: if usepdb:
config.option.numprocesses = 0 config.option.numprocesses = 0
config.option.dist = "no" config.option.dist = "no"
@@ -227,8 +232,8 @@ def is_xdist_worker(request_or_session) -> bool:
return hasattr(request_or_session.config, "workerinput") return hasattr(request_or_session.config, "workerinput")
def is_xdist_master(request_or_session) -> bool: def is_xdist_controller(request_or_session) -> bool:
"""Return `True` if this is the xdist master, `False` otherwise """Return `True` if this is the xdist controller, `False` otherwise
Note: this method also returns `False` when distribution has not been Note: this method also returns `False` when distribution has not been
activated at all. activated at all.
@@ -241,9 +246,13 @@ def is_xdist_master(request_or_session) -> bool:
) )
# ALIAS: TODO, deprecate (#592)
is_xdist_master = is_xdist_controller
def get_xdist_worker_id(request_or_session) -> str: def get_xdist_worker_id(request_or_session) -> str:
"""Return the id of the current worker ('gw0', 'gw1', etc) or 'master' """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
if running on the 'master' node. if running on the controller node.
If not distributing tests (for example passing `-n0` or not passing `-n` at all) If not distributing tests (for example passing `-n0` or not passing `-n` at all)
also return 'master'. also return 'master'.
@@ -253,6 +262,7 @@ def get_xdist_worker_id(request_or_session) -> str:
if hasattr(request_or_session.config, "workerinput"): if hasattr(request_or_session.config, "workerinput"):
return request_or_session.config.workerinput["workerid"] return request_or_session.config.workerinput["workerid"]
else: else:
# TODO: remove "master", ideally for a None
return "master" return "master"
@@ -261,6 +271,7 @@ def worker_id(request):
"""Return the id of the current worker ('gw0', 'gw1', etc) or 'master' """Return the id of the current worker ('gw0', 'gw1', etc) or 'master'
if running on the master node. if running on the master node.
""" """
# TODO: remove "master", ideally for a None
return get_xdist_worker_id(request) return get_xdist_worker_id(request)

View File

@@ -16,6 +16,21 @@ from execnet.gateway_base import dumps, DumpError
from _pytest.config import _prepareconfig, Config from _pytest.config import _prepareconfig, Config
try:
from setproctitle import setproctitle
except ImportError:
def setproctitle(title):
pass
def worker_title(title):
try:
setproctitle(title)
except Exception:
# changing the process name is very optional, no errors please
pass
class WorkerInteractor: class WorkerInteractor:
def __init__(self, config, channel): def __init__(self, config, channel):
@@ -85,9 +100,14 @@ class WorkerInteractor:
else: else:
nextitem = None nextitem = None
worker_title("[pytest-xdist running] %s" % item.nodeid)
start = time.time() start = time.time()
self.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem) self.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
duration = time.time() - start duration = time.time() - start
worker_title("[pytest-xdist idle]")
self.sendevent( self.sendevent(
"runtest_protocol_complete", item_index=self.item_index, duration=duration "runtest_protocol_complete", item_index=self.item_index, duration=duration
) )
@@ -121,7 +141,7 @@ class WorkerInteractor:
self.sendevent("testreport", data=data) self.sendevent("testreport", data=data)
def pytest_collectreport(self, report): def pytest_collectreport(self, report):
# send only reports that have not passed to master as optimization (#330) # send only reports that have not passed to controller as optimization (#330)
if not report.passed: if not report.passed:
data = self.config.hook.pytest_report_to_serializable( data = self.config.hook.pytest_report_to_serializable(
config=self.config, report=report config=self.config, report=report
@@ -144,7 +164,7 @@ def serialize_warning_message(warning_message):
message_class_name = type(warning_message.message).__name__ message_class_name = type(warning_message.message).__name__
message_str = str(warning_message.message) message_str = str(warning_message.message)
# check now if we can serialize the warning arguments (#349) # check now if we can serialize the warning arguments (#349)
# if not, we will just use the exception message on the master node # if not, we will just use the exception message on the controller node
try: try:
dumps(warning_message.message.args) dumps(warning_message.message.args)
except DumpError: except DumpError:
@@ -219,12 +239,14 @@ if __name__ == "__channelexec__":
channel = channel # noqa channel = channel # noqa
workerinput, args, option_dict, change_sys_path = channel.receive() workerinput, args, option_dict, change_sys_path = channel.receive()
if change_sys_path: if change_sys_path is None:
importpath = os.getcwd() importpath = os.getcwd()
sys.path.insert(0, importpath) sys.path.insert(0, importpath)
os.environ["PYTHONPATH"] = ( os.environ["PYTHONPATH"] = (
importpath + os.pathsep + os.environ.get("PYTHONPATH", "") importpath + os.pathsep + os.environ.get("PYTHONPATH", "")
) )
else:
sys.path = change_sys_path
os.environ["PYTEST_XDIST_TESTRUNUID"] = workerinput["testrunuid"] os.environ["PYTEST_XDIST_TESTRUNUID"] = workerinput["testrunuid"]
os.environ["PYTEST_XDIST_WORKER"] = workerinput["workerid"] os.environ["PYTEST_XDIST_WORKER"] = workerinput["workerid"]

View File

@@ -16,5 +16,5 @@ def report_collection_diff(from_collection, to_collection, from_id, to_id):
"The difference is:\n" "The difference is:\n"
"{diff}" "{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

@@ -101,6 +101,14 @@ class EachScheduling:
def mark_test_complete(self, node, item_index, duration=0): def mark_test_complete(self, node, item_index, duration=0):
self.node2pending[node].remove(item_index) self.node2pending[node].remove(item_index)
def mark_test_pending(self, item):
self.pending.insert(
0,
self.collection.index(item),
)
for node in self.node2pending:
self.check_schedule(node)
def remove_node(self, node): def remove_node(self, node):
# KeyError if we didn't get an add_node() yet # KeyError if we didn't get an add_node() yet
pending = self.node2pending.pop(node) pending = self.node2pending.pop(node)

View File

@@ -151,6 +151,14 @@ class LoadScheduling:
self.node2pending[node].remove(item_index) self.node2pending[node].remove(item_index)
self.check_schedule(node, duration=duration) self.check_schedule(node, duration=duration)
def mark_test_pending(self, item):
self.pending.insert(
0,
self.collection.index(item),
)
for node in self.node2pending:
self.check_schedule(node)
def check_schedule(self, node, duration=0): def check_schedule(self, node, duration=0):
"""Maybe schedule new items on the node """Maybe schedule new items on the node

View File

@@ -243,6 +243,9 @@ class LoadScopeScheduling:
self.assigned_work[node][scope][nodeid] = True self.assigned_work[node][scope][nodeid] = True
self._reschedule(node) self._reschedule(node)
def mark_test_pending(self, item):
raise NotImplementedError()
def _assign_work_unit(self, node): def _assign_work_unit(self, node):
"""Assign a work unit to a node.""" """Assign a work unit to a node."""
assert self.workqueue assert self.workqueue

View File

@@ -9,6 +9,7 @@ import pytest
import execnet import execnet
import xdist.remote import xdist.remote
from xdist.plugin import _sys_path
def parse_spec_config(config): def parse_spec_config(config):
@@ -157,8 +158,7 @@ class NodeManager:
class HostRSync(execnet.RSync): class HostRSync(execnet.RSync):
""" RSyncer that filters out common files """RSyncer that filters out common files"""
"""
def __init__(self, sourcedir, *args, **kwargs): def __init__(self, sourcedir, *args, **kwargs):
self._synced = {} self._synced = {}
@@ -262,7 +262,8 @@ class WorkerController:
remote_module = self.config.hook.pytest_xdist_getremotemodule() remote_module = self.config.hook.pytest_xdist_getremotemodule()
self.channel = self.gateway.remote_exec(remote_module) self.channel = self.gateway.remote_exec(remote_module)
# change sys.path only for remote workers # change sys.path only for remote workers
change_sys_path = not self.gateway.spec.popen # restore sys.path from a frozen copy for local workers
change_sys_path = _sys_path if self.gateway.spec.popen else None
self.channel.send((self.workerinput, args, option_dict, change_sys_path)) self.channel.send((self.workerinput, args, option_dict, change_sys_path))
if self.putevent: if self.putevent:

View File

@@ -244,7 +244,7 @@ class TestDistribution:
def test_data_exchange(self, testdir): def test_data_exchange(self, testdir):
testdir.makeconftest( testdir.makeconftest(
""" """
# This hook only called on master. # This hook only called on the controlling process.
def pytest_configure_node(node): def pytest_configure_node(node):
node.workerinput['a'] = 42 node.workerinput['a'] = 42
node.workerinput['b'] = 7 node.workerinput['b'] = 7
@@ -257,7 +257,7 @@ class TestDistribution:
r = a + b r = a + b
config.workeroutput['r'] = r config.workeroutput['r'] = r
# This hook only called on master. # This hook only called on the controlling process.
def pytest_testnodedown(node, error): def pytest_testnodedown(node, error):
node.config.calc_result = node.workeroutput['r'] node.config.calc_result = node.workeroutput['r']
@@ -289,7 +289,7 @@ class TestDistribution:
# on the worker # on the worker
if hasattr(session.config, 'workeroutput'): if hasattr(session.config, 'workeroutput'):
session.config.workeroutput['s2'] = 42 session.config.workeroutput['s2'] = 42
# on the master # on the controller
def pytest_testnodedown(node, error): def pytest_testnodedown(node, error):
assert node.workeroutput['s2'] == 42 assert node.workeroutput['s2'] == 42
print ("s2call-finished") print ("s2call-finished")
@@ -503,7 +503,7 @@ def test_session_hooks(testdir):
if hasattr(session.config, 'workerinput'): if hasattr(session.config, 'workerinput'):
name = "worker" name = "worker"
else: else:
name = "master" name = "controller"
with open(name, "w") as f: with open(name, "w") as f:
f.write("xy") f.write("xy")
# let's fail on the worker # let's fail on the worker
@@ -524,12 +524,12 @@ def test_session_hooks(testdir):
d = result.parseoutcomes() d = result.parseoutcomes()
assert d["passed"] == 1 assert d["passed"] == 1
assert testdir.tmpdir.join("worker").check() assert testdir.tmpdir.join("worker").check()
assert testdir.tmpdir.join("master").check() assert testdir.tmpdir.join("controller").check()
def test_session_testscollected(testdir): def test_session_testscollected(testdir):
""" """
Make sure master node is updating the session object with the number Make sure controller node is updating the session object with the number
of tests collected from the workers. of tests collected from the workers.
""" """
testdir.makepyfile( testdir.makepyfile(
@@ -568,13 +568,13 @@ def test_fixture_teardown_failure(testdir):
pass pass
""" """
) )
result = testdir.runpytest_subprocess("--debug", p) # , "-n1") result = testdir.runpytest_subprocess(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
def test_config_initialization(testdir, monkeypatch, pytestconfig): def test_config_initialization(testdir, monkeypatch, pytestconfig):
"""Ensure workers and master are initialized consistently. Integration test for #445""" """Ensure workers and controller are initialized consistently. Integration test for #445"""
testdir.makepyfile( testdir.makepyfile(
**{ **{
"dir_a/test_foo.py": """ "dir_a/test_foo.py": """
@@ -718,8 +718,7 @@ def test_issue_594_random_parametrize(testdir):
def test_tmpdir_disabled(testdir): def test_tmpdir_disabled(testdir):
"""Test xdist doesn't break if internal tmpdir plugin is disabled (#22). """Test xdist doesn't break if internal tmpdir plugin is disabled (#22)."""
"""
p1 = testdir.makepyfile( p1 = testdir.makepyfile(
""" """
def test_ok(): def test_ok():
@@ -733,8 +732,7 @@ def test_tmpdir_disabled(testdir):
@pytest.mark.parametrize("plugin", ["xdist.looponfail", "xdist.boxed"]) @pytest.mark.parametrize("plugin", ["xdist.looponfail", "xdist.boxed"])
def test_sub_plugins_disabled(testdir, plugin): def test_sub_plugins_disabled(testdir, plugin):
"""Test that xdist doesn't break if we disable any of its sub-plugins. (#32) """Test that xdist doesn't break if we disable any of its sub-plugins. (#32)"""
"""
p1 = testdir.makepyfile( p1 = testdir.makepyfile(
""" """
def test_ok(): def test_ok():
@@ -1138,7 +1136,7 @@ def test_internal_error_with_maxfail(testdir):
assert "INTERNALERROR" not in result.stderr.str() assert "INTERNALERROR" not in result.stderr.str()
def test_internal_errors_propagate_to_master(testdir): def test_internal_errors_propagate_to_controller(testdir):
testdir.makeconftest( testdir.makeconftest(
""" """
def pytest_collection_modifyitems(): def pytest_collection_modifyitems():
@@ -1239,14 +1237,22 @@ class TestFileScope:
"test_b.py::TestB", result.outlines "test_b.py::TestB", result.outlines
) )
assert test_a_workers_and_test_count in ( assert (
test_a_workers_and_test_count
in (
{"gw0": 10}, {"gw0": 10},
{"gw1": 0}, {"gw1": 0},
) or test_a_workers_and_test_count in ({"gw0": 0}, {"gw1": 10}) )
assert test_b_workers_and_test_count in ( or test_a_workers_and_test_count in ({"gw0": 0}, {"gw1": 10})
)
assert (
test_b_workers_and_test_count
in (
{"gw0": 10}, {"gw0": 10},
{"gw1": 0}, {"gw1": 0},
) or test_b_workers_and_test_count in ({"gw0": 0}, {"gw1": 10}) )
or test_b_workers_and_test_count in ({"gw0": 0}, {"gw1": 10})
)
def test_by_class(self, testdir): def test_by_class(self, testdir):
testdir.makepyfile( testdir.makepyfile(
@@ -1271,14 +1277,22 @@ class TestFileScope:
"test_a.py::TestB", result.outlines "test_a.py::TestB", result.outlines
) )
assert test_a_workers_and_test_count in ( assert (
test_a_workers_and_test_count
in (
{"gw0": 10}, {"gw0": 10},
{"gw1": 0}, {"gw1": 0},
) or test_a_workers_and_test_count in ({"gw0": 0}, {"gw1": 10}) )
assert test_b_workers_and_test_count in ( or test_a_workers_and_test_count in ({"gw0": 0}, {"gw1": 10})
)
assert (
test_b_workers_and_test_count
in (
{"gw0": 10}, {"gw0": 10},
{"gw1": 0}, {"gw1": 0},
) or test_b_workers_and_test_count in ({"gw0": 0}, {"gw1": 10}) )
or test_b_workers_and_test_count in ({"gw0": 0}, {"gw1": 10})
)
def test_module_single_start(self, testdir): def test_module_single_start(self, testdir):
"""Fix test suite never finishing in case all workers start with a single test (#277).""" """Fix test suite never finishing in case all workers start with a single test (#277)."""
@@ -1408,12 +1422,18 @@ class TestAPI:
del fake_request.config.workerinput del fake_request.config.workerinput
assert not xdist.is_xdist_worker(fake_request) assert not xdist.is_xdist_worker(fake_request)
def test_is_xdist_master(self, fake_request): def test_is_xdist_controller(self, fake_request):
assert not xdist.is_xdist_master(fake_request) assert not xdist.is_xdist_master(fake_request)
assert not xdist.is_xdist_controller(fake_request)
del fake_request.config.workerinput del fake_request.config.workerinput
assert xdist.is_xdist_master(fake_request) assert xdist.is_xdist_master(fake_request)
assert xdist.is_xdist_controller(fake_request)
fake_request.config.option.dist = "no" fake_request.config.option.dist = "no"
assert not xdist.is_xdist_master(fake_request) assert not xdist.is_xdist_master(fake_request)
assert not xdist.is_xdist_controller(fake_request)
def test_get_xdist_worker_id(self, fake_request): def test_get_xdist_worker_id(self, fake_request):
assert xdist.get_xdist_worker_id(fake_request) == "gw5" assert xdist.get_xdist_worker_id(fake_request) == "gw5"

View File

@@ -46,8 +46,7 @@ class TestHooks:
) )
def test_node_collection_finished(self, testdir): def test_node_collection_finished(self, testdir):
"""Test pytest_xdist_node_collection_finished hook (#8). """Test pytest_xdist_node_collection_finished hook (#8)."""
"""
testdir.makeconftest( testdir.makeconftest(
""" """
def pytest_xdist_node_collection_finished(node, ids): def pytest_xdist_node_collection_finished(node, ids):
@@ -61,3 +60,37 @@ class TestHooks:
["*HOOK: gw0 test_a, test_b, test_c", "*HOOK: gw1 test_a, test_b, test_c"] ["*HOOK: gw0 test_a, test_b, test_c", "*HOOK: gw1 test_a, test_b, test_c"]
) )
res.stdout.fnmatch_lines(["*3 passed*"]) res.stdout.fnmatch_lines(["*3 passed*"])
class TestCrashItem:
@pytest.fixture(autouse=True)
def create_test_file(self, testdir):
testdir.makepyfile(
"""
import os
def test_a(): pass
def test_b(): os._exit(1)
def test_c(): pass
def test_d(): pass
"""
)
def test_handlecrashitem(self, testdir):
"""Test pytest_handlecrashitem hook."""
testdir.makeconftest(
"""
test_runs = 0
def pytest_handlecrashitem(crashitem, report, sched):
global test_runs
if test_runs == 0:
sched.mark_test_pending(crashitem)
test_runs = 1
else:
print("HOOK: pytest_handlecrashitem")
"""
)
res = testdir.runpytest("-n2", "-s")
res.stdout.fnmatch_lines_random(["*HOOK: pytest_handlecrashitem"])
res.stdout.fnmatch_lines(["*3 passed*"])

View File

@@ -69,6 +69,12 @@ def test_auto_detect_cpus(testdir, monkeypatch):
assert config.getoption("numprocesses") == 0 assert config.getoption("numprocesses") == 0
assert config.getoption("dist") == "no" assert config.getoption("dist") == "no"
config = testdir.parseconfigure("-nlogical", "--pdb")
check_options(config)
assert config.getoption("usepdb")
assert config.getoption("numprocesses") == 0
assert config.getoption("dist") == "no"
monkeypatch.delattr(os, "sched_getaffinity", raising=False) monkeypatch.delattr(os, "sched_getaffinity", raising=False)
monkeypatch.setenv("TRAVIS", "true") monkeypatch.setenv("TRAVIS", "true")
config = testdir.parseconfigure("-nauto") config = testdir.parseconfigure("-nauto")
@@ -81,12 +87,16 @@ def test_auto_detect_cpus_psutil(testdir, monkeypatch):
psutil = pytest.importorskip("psutil") psutil = pytest.importorskip("psutil")
monkeypatch.setattr(psutil, "cpu_count", lambda logical=True: 42) monkeypatch.setattr(psutil, "cpu_count", lambda logical=True: 84 if logical else 42)
config = testdir.parseconfigure("-nauto") config = testdir.parseconfigure("-nauto")
check_options(config) check_options(config)
assert config.getoption("numprocesses") == 42 assert config.getoption("numprocesses") == 42
config = testdir.parseconfigure("-nlogical")
check_options(config)
assert config.getoption("numprocesses") == 84
def test_hook_auto_num_workers(testdir, monkeypatch): def test_hook_auto_num_workers(testdir, monkeypatch):
from xdist.plugin import pytest_cmdline_main as check_options from xdist.plugin import pytest_cmdline_main as check_options
@@ -101,6 +111,10 @@ def test_hook_auto_num_workers(testdir, monkeypatch):
check_options(config) check_options(config)
assert config.getoption("numprocesses") == 42 assert config.getoption("numprocesses") == 42
config = testdir.parseconfigure("-nlogical")
check_options(config)
assert config.getoption("numprocesses") == 42
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

@@ -37,7 +37,9 @@ class WorkerSetup:
self.testdir = testdir self.testdir = testdir
self.events = Queue() self.events = Queue()
def setup(self,): def setup(
self,
):
self.testdir.chdir() self.testdir.chdir()
# import os ; os.environ['EXECNET_DEBUG'] = "2" # import os ; os.environ['EXECNET_DEBUG'] = "2"
self.gateway = execnet.makegateway() self.gateway = execnet.makegateway()
@@ -290,3 +292,17 @@ def test_remote_usage_prog(testdir, request):
result = testdir.runpytest_subprocess("-n1") result = testdir.runpytest_subprocess("-n1")
assert result.ret == 1 assert result.ret == 1
result.stdout.fnmatch_lines(["*usage: *", "*error: my_usage_error"]) result.stdout.fnmatch_lines(["*usage: *", "*error: my_usage_error"])
def test_remote_sys_path(testdir):
"""Work around sys.path differences due to execnet using `python -c`."""
testdir.makepyfile(
"""
import sys
def test_sys_path():
assert "" not in sys.path
"""
)
result = testdir.runpytest("-n1")
assert result.ret == 0

21
tox.ini
View File

@@ -1,15 +1,16 @@
[tox] [tox]
envlist= envlist=
linting linting
py{35,36,37,38,39}-pytestlatest py{36,37,38,39,310}-pytestlatest
py38-pytestmaster py38-pytestmain
py38-psutil py38-psutil
py38-setproctitle
[testenv] [testenv]
extras = testing extras = testing
deps = deps =
pytestlatest: pytest pytestlatest: pytest
pytestmaster: git+https://github.com/pytest-dev/pytest.git@master pytestmain: git+https://github.com/pytest-dev/pytest.git
commands= commands=
pytest {posargs} pytest {posargs}
@@ -21,13 +22,13 @@ deps = pytest
commands = commands =
pytest {posargs:-k psutil} pytest {posargs:-k psutil}
[testenv:linting] [testenv:py38-setproctitle]
skip_install = True extras =
usedevelop = True testing
passenv = PRE_COMMIT_HOME setproctitle
deps = deps = pytest
pre-commit commands =
commands = pre-commit run --all-files --show-diff-on-failure pytest {posargs}
[testenv:release] [testenv:release]
changedir= changedir=