Compare commits
215 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aea962dc21 | ||
|
|
4345efaffc | ||
|
|
bf47033169 | ||
|
|
37a65684d6 | ||
|
|
eab5020e24 | ||
|
|
8ef21f56d3 | ||
|
|
103d980b2d | ||
|
|
28c3ef1c77 | ||
|
|
67c3c28877 | ||
|
|
e040fd20a3 | ||
|
|
00e0b43010 | ||
|
|
f19cfbb825 | ||
|
|
bde3d1a0cd | ||
|
|
2e090896d5 | ||
|
|
b0a32da0b5 | ||
|
|
10c1c7c41a | ||
|
|
16f452ef98 | ||
|
|
b77e533693 | ||
|
|
82a7ca9615 | ||
|
|
32575f92c9 | ||
|
|
a260e58020 | ||
|
|
b2f7e02a02 | ||
|
|
29e114b463 | ||
|
|
2a059b1c1b | ||
|
|
cdc72bf5a3 | ||
|
|
f786335dbb | ||
|
|
ab5af524a4 | ||
|
|
9620b167d9 | ||
|
|
10544c4cb8 | ||
|
|
1e8e17c01e | ||
|
|
47bb53f5cb | ||
|
|
6991a16edb | ||
|
|
a31967431f | ||
|
|
e74ad4ff9b | ||
|
|
70bdacf01a | ||
|
|
b69f853acb | ||
|
|
c31018d9bc | ||
|
|
7ae23901d3 | ||
|
|
4d19b94347 | ||
|
|
c15b537e3d | ||
|
|
2577a6ce8a | ||
|
|
dd5f5ca4cb | ||
|
|
508774742e | ||
|
|
d3f5324386 | ||
|
|
3da88d794f | ||
|
|
71b4995775 | ||
|
|
b0541e9d31 | ||
|
|
415fcb912b | ||
|
|
f872fcb5d0 | ||
|
|
de6f2c0336 | ||
|
|
be4b359c74 | ||
|
|
72a58bbafe | ||
|
|
c336449729 | ||
|
|
1e4ecda884 | ||
|
|
8cf0e46bbf | ||
|
|
f0226e9329 | ||
|
|
dce8df45d5 | ||
|
|
f6948597e4 | ||
|
|
e3df1031ca | ||
|
|
14ffadf004 | ||
|
|
459b040d21 | ||
|
|
3396225f74 | ||
|
|
c82906105c | ||
|
|
4c14740798 | ||
|
|
72e6482994 | ||
|
|
5f8b50c094 | ||
|
|
99e31f6fb1 | ||
|
|
f2e35c8c4f | ||
|
|
40b4fe64af | ||
|
|
d10d59c013 | ||
|
|
d54aa8ce13 | ||
|
|
52fa8c98bb | ||
|
|
3f336869e2 | ||
|
|
85482d575e | ||
|
|
6f7365509d | ||
|
|
7099ea9bb0 | ||
|
|
dccac69d82 | ||
|
|
c2cd337886 | ||
|
|
0fc4a806e5 | ||
|
|
4d3c1ab4f0 | ||
|
|
e4f76f6350 | ||
|
|
0d65783dce | ||
|
|
8bb8b91357 | ||
|
|
8804c7333a | ||
|
|
17eec5b97e | ||
|
|
cd07c4d4ff | ||
|
|
917b99e438 | ||
|
|
b08e156b79 | ||
|
|
8e2c7b4979 | ||
|
|
5a7aa123ea | ||
|
|
a12eadd9ef | ||
|
|
2137e2b15b | ||
|
|
89446af51e | ||
|
|
3b521bedf8 | ||
|
|
5b2c8fa007 | ||
|
|
eb8d145195 | ||
|
|
80bea79512 | ||
|
|
07a560ff24 | ||
|
|
717775a1c6 | ||
|
|
672f4bb5aa | ||
|
|
f1079a8222 | ||
|
|
223eef6261 | ||
|
|
43657f252f | ||
|
|
70ebab3537 | ||
|
|
d3bdfc704b | ||
|
|
4de247cfa0 | ||
|
|
d611b03589 | ||
|
|
308d789d92 | ||
|
|
e4bea9068b | ||
|
|
e620798d33 | ||
|
|
7ea4992f16 | ||
|
|
0564b52c0e | ||
|
|
8b2c91836b | ||
|
|
9e382e8d29 | ||
|
|
2255892d65 | ||
|
|
7d9b198f73 | ||
|
|
f4c5994d27 | ||
|
|
c24c7e75e2 | ||
|
|
273670b2a2 | ||
|
|
28aff051ab | ||
|
|
29975e5b37 | ||
|
|
5cf7d1dba2 | ||
|
|
2fe824b8c4 | ||
|
|
f674217c43 | ||
|
|
9f7345d663 | ||
|
|
eb2d074530 | ||
|
|
9fa7745795 | ||
|
|
14db2f91ba | ||
|
|
c3e494f6cf | ||
|
|
090f67a980 | ||
|
|
3059bfb1b3 | ||
|
|
e391c47ed8 | ||
|
|
f66764e1c0 | ||
|
|
e0b088b52e | ||
|
|
e5a3c870b4 | ||
|
|
2b71cb9c38 | ||
|
|
da9d814da4 | ||
|
|
7d4c4c66d4 | ||
|
|
939a792c41 | ||
|
|
17644ff285 | ||
|
|
64faa41d06 | ||
|
|
ca1bb9a3a1 | ||
|
|
78ef531420 | ||
|
|
212ee450b7 | ||
|
|
c1c08852f9 | ||
|
|
e06a077ac2 | ||
|
|
cb77e65c97 | ||
|
|
6367f0f5f1 | ||
|
|
b88e09a697 | ||
|
|
68bbd42213 | ||
|
|
22ee2093b8 | ||
|
|
87a99275fb | ||
|
|
abbd7c30a4 | ||
|
|
abae60c8d0 | ||
|
|
e92893ed24 | ||
|
|
27b5435a40 | ||
|
|
50db718a6a | ||
|
|
bfd0addaeb | ||
|
|
be11d3e195 | ||
|
|
266f05c4c4 | ||
|
|
d0bd01beca | ||
|
|
220288ac77 | ||
|
|
4d8903fd0b | ||
|
|
67106f056b | ||
|
|
5d3c5123f8 | ||
|
|
74d9f56d0f | ||
|
|
051db6a33d | ||
|
|
aa358433b0 | ||
|
|
e723069165 | ||
|
|
855fd17014 | ||
|
|
d11781920b | ||
|
|
2c0d2eef40 | ||
|
|
ef8ec01e39 | ||
|
|
0a1c2a7ca1 | ||
|
|
fe0a76e1a6 | ||
|
|
dcafb8c48c | ||
|
|
a76cc8f8c4 | ||
|
|
4d2fa581e1 | ||
|
|
f7a3f45a18 | ||
|
|
dff7b203f7 | ||
|
|
4705fd2bbe | ||
|
|
ca0476953e | ||
|
|
7e92930fa9 | ||
|
|
33769d0328 | ||
|
|
5db2e6c7a1 | ||
|
|
804fc4063a | ||
|
|
82a2174867 | ||
|
|
a80e031c62 | ||
|
|
452e5c1cf0 | ||
|
|
c6b11b9f62 | ||
|
|
b8255308d6 | ||
|
|
a5c0fb7f6b | ||
|
|
f25683354e | ||
|
|
7d13599ba1 | ||
|
|
43664d7841 | ||
|
|
2a2f888909 | ||
|
|
ad5ddaf55a | ||
|
|
4588130c1e | ||
|
|
5003bae0de | ||
|
|
6e32a1f73d | ||
|
|
611d254ed5 | ||
|
|
57a8f208bc | ||
|
|
fcdc1d867e | ||
|
|
098dca3a9f | ||
|
|
8e2ed76227 | ||
|
|
bf7c188cc0 | ||
|
|
8c9efd8608 | ||
|
|
e1ad1a14af | ||
|
|
327fe4cfcc | ||
|
|
d02491931a | ||
|
|
032db159c9 | ||
|
|
cd2085ee71 | ||
|
|
ad305e71d7 | ||
|
|
7d8688d54b | ||
|
|
253419316c |
@@ -5,13 +5,13 @@ repos:
|
||||
hooks:
|
||||
- id: black
|
||||
args: [--safe, --quiet]
|
||||
language_version: python3.6
|
||||
language_version: python3
|
||||
- repo: https://github.com/asottile/blacken-docs
|
||||
rev: v0.2.0
|
||||
hooks:
|
||||
- id: blacken-docs
|
||||
additional_dependencies: [black==18.6b4]
|
||||
language_version: python3.6
|
||||
language_version: python3
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v1.3.0
|
||||
hooks:
|
||||
@@ -37,4 +37,8 @@ repos:
|
||||
files: ^(CHANGELOG.rst|HOWTORELEASE.rst|README.rst|changelog/.*)$
|
||||
language: python
|
||||
additional_dependencies: [pygments, restructuredtext_lint]
|
||||
python_version: python3.6
|
||||
- id: changelogs-rst
|
||||
name: changelog files must end in .rst
|
||||
entry: ./scripts/fail
|
||||
language: script
|
||||
files: 'changelog/.*(?<!\.rst)$'
|
||||
|
||||
34
.travis.yml
34
.travis.yml
@@ -1,7 +1,7 @@
|
||||
sudo: false
|
||||
language: python
|
||||
stages:
|
||||
- linting
|
||||
- baseline
|
||||
- test
|
||||
- name: deploy
|
||||
if: repo = pytest-dev/pytest AND tag IS present
|
||||
@@ -15,9 +15,6 @@ env:
|
||||
- TOXENV=coveralls
|
||||
# note: please use "tox --listenvs" to populate the build matrix below
|
||||
# please remove the linting env in all cases
|
||||
- TOXENV=py27
|
||||
- TOXENV=py34
|
||||
- TOXENV=py36
|
||||
- TOXENV=py27-pexpect
|
||||
- TOXENV=py27-xdist
|
||||
- TOXENV=py27-trial
|
||||
@@ -44,6 +41,27 @@ jobs:
|
||||
python: '3.7'
|
||||
sudo: required
|
||||
dist: xenial
|
||||
- &test-macos
|
||||
language: generic
|
||||
os: osx
|
||||
osx_image: xcode9.4
|
||||
sudo: required
|
||||
install:
|
||||
- python -m pip install --pre tox
|
||||
env: TOXENV=py27
|
||||
- <<: *test-macos
|
||||
env: TOXENV=py37
|
||||
before_install:
|
||||
- brew update
|
||||
- brew upgrade python
|
||||
- brew unlink python
|
||||
- brew link python
|
||||
|
||||
- stage: baseline
|
||||
env: TOXENV=py27
|
||||
- env: TOXENV=py34
|
||||
- env: TOXENV=py36
|
||||
- env: TOXENV=linting
|
||||
|
||||
- stage: deploy
|
||||
python: '3.6'
|
||||
@@ -60,14 +78,6 @@ jobs:
|
||||
on:
|
||||
tags: true
|
||||
repo: pytest-dev/pytest
|
||||
- stage: linting
|
||||
python: '3.6'
|
||||
env:
|
||||
install:
|
||||
- pip install pre-commit
|
||||
- pre-commit install-hooks
|
||||
script:
|
||||
- pre-commit run --all-files
|
||||
|
||||
script: tox --recreate
|
||||
|
||||
|
||||
3
AUTHORS
3
AUTHORS
@@ -98,6 +98,7 @@ Javier Domingo Cansino
|
||||
Javier Romero
|
||||
Jeff Rackauckas
|
||||
Jeff Widman
|
||||
Jenni Rinker
|
||||
John Eddie Ayson
|
||||
John Towler
|
||||
Jon Sonesen
|
||||
@@ -182,6 +183,7 @@ Russel Winder
|
||||
Ryan Wooden
|
||||
Samuel Dion-Girardeau
|
||||
Samuele Pedroni
|
||||
Sankt Petersbug
|
||||
Segev Finer
|
||||
Serhii Mozghovyi
|
||||
Simon Gomizelj
|
||||
@@ -205,6 +207,7 @@ Trevor Bekolay
|
||||
Tyler Goodlet
|
||||
Tzu-ping Chung
|
||||
Vasily Kuznetsov
|
||||
Victor Maryama
|
||||
Victor Uriarte
|
||||
Vidar T. Fauske
|
||||
Vitaly Lashmanov
|
||||
|
||||
152
CHANGELOG.rst
152
CHANGELOG.rst
@@ -1,3 +1,13 @@
|
||||
=================
|
||||
Changelog history
|
||||
=================
|
||||
|
||||
Versions follow `Semantic Versioning <https://semver.org/>`_ (``<major>.<minor>.<patch>``).
|
||||
|
||||
Backward incompatible (breaking) changes will only be introduced in major versions
|
||||
with advance notice in the **Deprecations** section of releases.
|
||||
|
||||
|
||||
..
|
||||
You should *NOT* be adding new change log entries to this file, this
|
||||
file is managed by towncrier. You *may* edit previous change logs to
|
||||
@@ -8,6 +18,148 @@
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
pytest 3.7.4 (2018-08-29)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3506 <https://github.com/pytest-dev/pytest/issues/3506>`_: Fix possible infinite recursion when writing ``.pyc`` files.
|
||||
|
||||
|
||||
- `#3853 <https://github.com/pytest-dev/pytest/issues/3853>`_: Cache plugin now obeys the ``-q`` flag when ``--last-failed`` and ``--failed-first`` flags are used.
|
||||
|
||||
|
||||
- `#3883 <https://github.com/pytest-dev/pytest/issues/3883>`_: Fix bad console output when using ``console_output_style=classic``.
|
||||
|
||||
|
||||
- `#3888 <https://github.com/pytest-dev/pytest/issues/3888>`_: Fix macOS specific code using ``capturemanager`` plugin in doctests.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#3902 <https://github.com/pytest-dev/pytest/issues/3902>`_: Fix pytest.org links
|
||||
|
||||
|
||||
pytest 3.7.3 (2018-08-26)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3033 <https://github.com/pytest-dev/pytest/issues/3033>`_: Fixtures during teardown can again use ``capsys`` and ``capfd`` to inspect output captured during tests.
|
||||
|
||||
|
||||
- `#3773 <https://github.com/pytest-dev/pytest/issues/3773>`_: Fix collection of tests from ``__init__.py`` files if they match the ``python_files`` configuration option.
|
||||
|
||||
|
||||
- `#3796 <https://github.com/pytest-dev/pytest/issues/3796>`_: Fix issue where teardown of fixtures of consecutive sub-packages were executed once, at the end of the outer
|
||||
package.
|
||||
|
||||
|
||||
- `#3816 <https://github.com/pytest-dev/pytest/issues/3816>`_: Fix bug where ``--show-capture=no`` option would still show logs printed during fixture teardown.
|
||||
|
||||
|
||||
- `#3819 <https://github.com/pytest-dev/pytest/issues/3819>`_: Fix ``stdout/stderr`` not getting captured when real-time cli logging is active.
|
||||
|
||||
|
||||
- `#3843 <https://github.com/pytest-dev/pytest/issues/3843>`_: Fix collection error when specifying test functions directly in the command line using ``test.py::test`` syntax together with ``--doctest-modules``.
|
||||
|
||||
|
||||
- `#3848 <https://github.com/pytest-dev/pytest/issues/3848>`_: Fix bugs where unicode arguments could not be passed to ``testdir.runpytest`` on Python 2.
|
||||
|
||||
|
||||
- `#3854 <https://github.com/pytest-dev/pytest/issues/3854>`_: Fix double collection of tests within packages when the filename starts with a capital letter.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#3824 <https://github.com/pytest-dev/pytest/issues/3824>`_: Added example for multiple glob pattern matches in ``python_files``.
|
||||
|
||||
|
||||
- `#3833 <https://github.com/pytest-dev/pytest/issues/3833>`_: Added missing docs for ``pytester.Testdir``.
|
||||
|
||||
|
||||
- `#3870 <https://github.com/pytest-dev/pytest/issues/3870>`_: Correct documentation for setuptools integration.
|
||||
|
||||
|
||||
|
||||
Trivial/Internal Changes
|
||||
------------------------
|
||||
|
||||
- `#3826 <https://github.com/pytest-dev/pytest/issues/3826>`_: Replace broken type annotations with type comments.
|
||||
|
||||
|
||||
- `#3845 <https://github.com/pytest-dev/pytest/issues/3845>`_: Remove a reference to issue `#568 <https://github.com/pytest-dev/pytest/issues/568>`_ from the documentation, which has since been
|
||||
fixed.
|
||||
|
||||
|
||||
pytest 3.7.2 (2018-08-16)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3671 <https://github.com/pytest-dev/pytest/issues/3671>`_: Fix ``filterwarnings`` not being registered as a builtin mark.
|
||||
|
||||
|
||||
- `#3768 <https://github.com/pytest-dev/pytest/issues/3768>`_, `#3789 <https://github.com/pytest-dev/pytest/issues/3789>`_: Fix test collection from packages mixed with normal directories.
|
||||
|
||||
|
||||
- `#3771 <https://github.com/pytest-dev/pytest/issues/3771>`_: Fix infinite recursion during collection if a ``pytest_ignore_collect`` hook returns ``False`` instead of ``None``.
|
||||
|
||||
|
||||
- `#3774 <https://github.com/pytest-dev/pytest/issues/3774>`_: Fix bug where decorated fixtures would lose functionality (for example ``@mock.patch``).
|
||||
|
||||
|
||||
- `#3775 <https://github.com/pytest-dev/pytest/issues/3775>`_: Fix bug where importing modules or other objects with prefix ``pytest_`` prefix would raise a ``PluginValidationError``.
|
||||
|
||||
|
||||
- `#3788 <https://github.com/pytest-dev/pytest/issues/3788>`_: Fix ``AttributeError`` during teardown of ``TestCase`` subclasses which raise an exception during ``__init__``.
|
||||
|
||||
|
||||
- `#3804 <https://github.com/pytest-dev/pytest/issues/3804>`_: Fix traceback reporting for exceptions with ``__cause__`` cycles.
|
||||
|
||||
|
||||
|
||||
Improved Documentation
|
||||
----------------------
|
||||
|
||||
- `#3746 <https://github.com/pytest-dev/pytest/issues/3746>`_: Add documentation for ``metafunc.config`` that had been mistakenly hidden.
|
||||
|
||||
|
||||
pytest 3.7.1 (2018-08-02)
|
||||
=========================
|
||||
|
||||
Bug Fixes
|
||||
---------
|
||||
|
||||
- `#3473 <https://github.com/pytest-dev/pytest/issues/3473>`_: Raise immediately if ``approx()`` is given an expected value of a type it doesn't understand (e.g. strings, nested dicts, etc.).
|
||||
|
||||
|
||||
- `#3712 <https://github.com/pytest-dev/pytest/issues/3712>`_: Correctly represent the dimensions of an numpy array when calling ``repr()`` on ``approx()``.
|
||||
|
||||
- `#3742 <https://github.com/pytest-dev/pytest/issues/3742>`_: Fix incompatibility with third party plugins during collection, which produced the error ``object has no attribute '_collectfile'``.
|
||||
|
||||
- `#3745 <https://github.com/pytest-dev/pytest/issues/3745>`_: Display the absolute path if ``cache_dir`` is not relative to the ``rootdir`` instead of failing.
|
||||
|
||||
|
||||
- `#3747 <https://github.com/pytest-dev/pytest/issues/3747>`_: Fix compatibility problem with plugins and the warning code issued by fixture functions when they are called directly.
|
||||
|
||||
|
||||
- `#3748 <https://github.com/pytest-dev/pytest/issues/3748>`_: Fix infinite recursion in ``pytest.approx`` with arrays in ``numpy<1.13``.
|
||||
|
||||
|
||||
- `#3757 <https://github.com/pytest-dev/pytest/issues/3757>`_: Pin pathlib2 to ``>=2.2.0`` as we require ``__fspath__`` support.
|
||||
|
||||
|
||||
- `#3763 <https://github.com/pytest-dev/pytest/issues/3763>`_: Fix ``TypeError`` when the assertion message is ``bytes`` in python 3.
|
||||
|
||||
|
||||
pytest 3.7.0 (2018-07-30)
|
||||
=========================
|
||||
|
||||
|
||||
20
README.rst
20
README.rst
@@ -1,5 +1,5 @@
|
||||
.. image:: http://docs.pytest.org/en/latest/_static/pytest1.png
|
||||
:target: http://docs.pytest.org
|
||||
.. image:: https://docs.pytest.org/en/latest/_static/pytest1.png
|
||||
:target: https://docs.pytest.org/en/latest/
|
||||
:align: center
|
||||
:alt: pytest
|
||||
|
||||
@@ -66,23 +66,23 @@ To execute it::
|
||||
========================== 1 failed in 0.04 seconds ===========================
|
||||
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <https://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||
|
||||
|
||||
Features
|
||||
--------
|
||||
|
||||
- Detailed info on failing `assert statements <http://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
|
||||
- Detailed info on failing `assert statements <https://docs.pytest.org/en/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
|
||||
|
||||
- `Auto-discovery
|
||||
<http://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
|
||||
<https://docs.pytest.org/en/latest/goodpractices.html#python-test-discovery>`_
|
||||
of test modules and functions;
|
||||
|
||||
- `Modular fixtures <http://docs.pytest.org/en/latest/fixture.html>`_ for
|
||||
- `Modular fixtures <https://docs.pytest.org/en/latest/fixture.html>`_ for
|
||||
managing small or parametrized long-lived test resources;
|
||||
|
||||
- Can run `unittest <http://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
|
||||
`nose <http://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
|
||||
- Can run `unittest <https://docs.pytest.org/en/latest/unittest.html>`_ (or trial),
|
||||
`nose <https://docs.pytest.org/en/latest/nose.html>`_ test suites out of the box;
|
||||
|
||||
- Python 2.7, Python 3.4+, PyPy 2.3, Jython 2.5 (untested);
|
||||
|
||||
@@ -92,7 +92,7 @@ Features
|
||||
Documentation
|
||||
-------------
|
||||
|
||||
For full documentation, including installation, tutorials and PDF documents, please see http://docs.pytest.org.
|
||||
For full documentation, including installation, tutorials and PDF documents, please see https://docs.pytest.org/en/latest/.
|
||||
|
||||
|
||||
Bugs/Requests
|
||||
@@ -104,7 +104,7 @@ Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issue
|
||||
Changelog
|
||||
---------
|
||||
|
||||
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
|
||||
Consult the `Changelog <https://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
|
||||
|
||||
|
||||
License
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import py
|
||||
import six
|
||||
|
||||
for i in range(1000):
|
||||
py.builtin.exec_("def test_func_%d(): pass" % i)
|
||||
six.exec_("def test_func_%d(): pass" % i)
|
||||
|
||||
@@ -6,6 +6,10 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-3.7.4
|
||||
release-3.7.3
|
||||
release-3.7.2
|
||||
release-3.7.1
|
||||
release-3.7.0
|
||||
release-3.6.4
|
||||
release-3.6.3
|
||||
|
||||
21
doc/en/announce/release-3.7.1.rst
Normal file
21
doc/en/announce/release-3.7.1.rst
Normal file
@@ -0,0 +1,21 @@
|
||||
pytest-3.7.1
|
||||
=======================================
|
||||
|
||||
pytest 3.7.1 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Kale Kundert
|
||||
* Ronny Pfannschmidt
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
25
doc/en/announce/release-3.7.2.rst
Normal file
25
doc/en/announce/release-3.7.2.rst
Normal file
@@ -0,0 +1,25 @@
|
||||
pytest-3.7.2
|
||||
=======================================
|
||||
|
||||
pytest 3.7.2 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Josh Holland
|
||||
* Ronny Pfannschmidt
|
||||
* Sankt Petersbug
|
||||
* Wes Thomas
|
||||
* turturica
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
33
doc/en/announce/release-3.7.3.rst
Normal file
33
doc/en/announce/release-3.7.3.rst
Normal file
@@ -0,0 +1,33 @@
|
||||
pytest-3.7.3
|
||||
=======================================
|
||||
|
||||
pytest 3.7.3 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Andrew Champion
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Gandalf Saxe
|
||||
* Jennifer Rinker
|
||||
* Natan Lao
|
||||
* Ondřej Súkup
|
||||
* Ronny Pfannschmidt
|
||||
* Sankt Petersbug
|
||||
* Tyler Richard
|
||||
* Victor
|
||||
* Vlad Shcherbina
|
||||
* turturica
|
||||
* victor
|
||||
* wim glenn
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
22
doc/en/announce/release-3.7.4.rst
Normal file
22
doc/en/announce/release-3.7.4.rst
Normal file
@@ -0,0 +1,22 @@
|
||||
pytest-3.7.4
|
||||
=======================================
|
||||
|
||||
pytest 3.7.4 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Anthony Sottile
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Jiri Kuncar
|
||||
* Steve Piercy
|
||||
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
.. _changelog:
|
||||
|
||||
Changelog history
|
||||
=================================
|
||||
|
||||
.. include:: ../../CHANGELOG.rst
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from pytest import raises
|
||||
import _pytest._code
|
||||
import py
|
||||
import six
|
||||
|
||||
|
||||
def otherfunc(a, b):
|
||||
@@ -177,7 +177,7 @@ def test_dynamic_compile_shows_nicely():
|
||||
name = "abc-123"
|
||||
module = imp.new_module(name)
|
||||
code = _pytest._code.compile(src, name, "exec")
|
||||
py.builtin.exec_(code, module.__dict__)
|
||||
six.exec_(code, module.__dict__)
|
||||
sys.modules[name] = module
|
||||
module.foo()
|
||||
|
||||
|
||||
@@ -10,4 +10,4 @@ def pytest_runtest_setup(item):
|
||||
return
|
||||
mod = item.getparent(pytest.Module).obj
|
||||
if hasattr(mod, "hello"):
|
||||
print("mod.hello %r" % (mod.hello,))
|
||||
print("mod.hello {!r}".format(mod.hello))
|
||||
|
||||
@@ -200,15 +200,17 @@ You can ask which markers exist for your test suite - the list includes our just
|
||||
$ pytest --markers
|
||||
@pytest.mark.webtest: mark a test as a webtest.
|
||||
|
||||
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings
|
||||
|
||||
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
|
||||
|
||||
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
|
||||
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see https://docs.pytest.org/en/latest/skipping.html
|
||||
|
||||
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
|
||||
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/latest/skipping.html
|
||||
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/latest/parametrize.html for more info and examples.
|
||||
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/latest/fixture.html#usefixtures
|
||||
|
||||
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
|
||||
|
||||
@@ -374,15 +376,17 @@ The ``--markers`` option always gives you a list of available markers::
|
||||
$ pytest --markers
|
||||
@pytest.mark.env(name): mark test to run only on named environment
|
||||
|
||||
@pytest.mark.filterwarnings(warning): add a warning filter to the given test. see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings
|
||||
|
||||
@pytest.mark.skip(reason=None): skip the given test function with an optional reason. Example: skip(reason="no way of currently testing this") skips the test.
|
||||
|
||||
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see http://pytest.org/latest/skipping.html
|
||||
@pytest.mark.skipif(condition): skip the given test function if eval(condition) results in a True value. Evaluation happens within the module global context. Example: skipif('sys.platform == "win32"') skips the test if we are on the win32 platform. see https://docs.pytest.org/en/latest/skipping.html
|
||||
|
||||
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See http://pytest.org/latest/skipping.html
|
||||
@pytest.mark.xfail(condition, reason=None, run=True, raises=None, strict=False): mark the test function as an expected failure if eval(condition) has a True value. Optionally specify a reason for better reporting and run=False if you don't even want to execute the test function. If only specific exception(s) are expected, you can list them in raises, and if the test fails in other ways, it will be reported as a true failure. See https://docs.pytest.org/en/latest/skipping.html
|
||||
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see http://pytest.org/latest/parametrize.html for more info and examples.
|
||||
@pytest.mark.parametrize(argnames, argvalues): call a test function multiple times passing in different arguments in turn. argvalues generally needs to be a list of values if argnames specifies only one name or a list of tuples of values if argnames specifies multiple names. Example: @parametrize('arg1', [1,2]) would lead to two calls of the decorated test function, one with arg1=1 and another with arg1=2.see https://docs.pytest.org/en/latest/parametrize.html for more info and examples.
|
||||
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures
|
||||
@pytest.mark.usefixtures(fixturename1, fixturename2, ...): mark tests as needing all of the specified fixtures. see https://docs.pytest.org/en/latest/fixture.html#usefixtures
|
||||
|
||||
@pytest.mark.tryfirst: mark a hook implementation function such that the plugin machinery will try to call it first/as early as possible.
|
||||
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
module containing a parametrized tests testing cross-python
|
||||
serialization via the pickle module.
|
||||
"""
|
||||
import textwrap
|
||||
|
||||
import py
|
||||
import pytest
|
||||
import _pytest._code
|
||||
|
||||
pythonlist = ["python2.7", "python3.4", "python3.5"]
|
||||
|
||||
@@ -24,42 +25,44 @@ class Python(object):
|
||||
def __init__(self, version, picklefile):
|
||||
self.pythonpath = py.path.local.sysfind(version)
|
||||
if not self.pythonpath:
|
||||
pytest.skip("%r not found" % (version,))
|
||||
pytest.skip("{!r} not found".format(version))
|
||||
self.picklefile = picklefile
|
||||
|
||||
def dumps(self, obj):
|
||||
dumpfile = self.picklefile.dirpath("dump.py")
|
||||
dumpfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pickle
|
||||
f = open(%r, 'wb')
|
||||
s = pickle.dump(%r, f, protocol=2)
|
||||
f.close()
|
||||
"""
|
||||
% (str(self.picklefile), obj)
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pickle
|
||||
f = open({!r}, 'wb')
|
||||
s = pickle.dump({!r}, f, protocol=2)
|
||||
f.close()
|
||||
""".format(
|
||||
str(self.picklefile), obj
|
||||
)
|
||||
)
|
||||
)
|
||||
py.process.cmdexec("%s %s" % (self.pythonpath, dumpfile))
|
||||
py.process.cmdexec("{} {}".format(self.pythonpath, dumpfile))
|
||||
|
||||
def load_and_is_true(self, expression):
|
||||
loadfile = self.picklefile.dirpath("load.py")
|
||||
loadfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pickle
|
||||
f = open(%r, 'rb')
|
||||
obj = pickle.load(f)
|
||||
f.close()
|
||||
res = eval(%r)
|
||||
if not res:
|
||||
raise SystemExit(1)
|
||||
"""
|
||||
% (str(self.picklefile), expression)
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pickle
|
||||
f = open({!r}, 'rb')
|
||||
obj = pickle.load(f)
|
||||
f.close()
|
||||
res = eval({!r})
|
||||
if not res:
|
||||
raise SystemExit(1)
|
||||
""".format(
|
||||
str(self.picklefile), expression
|
||||
)
|
||||
)
|
||||
)
|
||||
print(loadfile)
|
||||
py.process.cmdexec("%s %s" % (self.pythonpath, loadfile))
|
||||
py.process.cmdexec("{} {}".format(self.pythonpath, loadfile))
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj", [42, {}, {1: 3}])
|
||||
|
||||
@@ -413,7 +413,7 @@ Running it results in some skips if we don't have all the python interpreters in
|
||||
. $ pytest -rs -q multipython.py
|
||||
...sss...sssssssss...sss... [100%]
|
||||
========================= short test summary info ==========================
|
||||
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
|
||||
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:29: 'python3.4' not found
|
||||
12 passed, 15 skipped in 0.12 seconds
|
||||
|
||||
Indirect parametrization of optional implementations/imports
|
||||
|
||||
@@ -100,19 +100,21 @@ Changing naming conventions
|
||||
|
||||
You can configure different naming conventions by setting
|
||||
the :confval:`python_files`, :confval:`python_classes` and
|
||||
:confval:`python_functions` configuration options. Example::
|
||||
:confval:`python_functions` configuration options.
|
||||
Here is an example::
|
||||
|
||||
# content of pytest.ini
|
||||
# Example 1: have pytest look for "check" instead of "test"
|
||||
# can also be defined in tox.ini or setup.cfg file, although the section
|
||||
# name in setup.cfg files should be "tool:pytest"
|
||||
[pytest]
|
||||
python_files=check_*.py
|
||||
python_classes=Check
|
||||
python_functions=*_check
|
||||
python_files = check_*.py
|
||||
python_classes = Check
|
||||
python_functions = *_check
|
||||
|
||||
This would make ``pytest`` look for tests in files that match the ``check_*
|
||||
.py`` glob-pattern, ``Check`` prefixes in classes, and functions and methods
|
||||
that match ``*_check``. For example, if we have::
|
||||
that match ``*_check``. For example, if we have::
|
||||
|
||||
# content of check_myapp.py
|
||||
class CheckMyApp(object):
|
||||
@@ -121,7 +123,7 @@ that match ``*_check``. For example, if we have::
|
||||
def complex_check(self):
|
||||
pass
|
||||
|
||||
then the test collection looks like this::
|
||||
The test collection would look like this::
|
||||
|
||||
$ pytest --collect-only
|
||||
=========================== test session starts ============================
|
||||
@@ -136,11 +138,19 @@ then the test collection looks like this::
|
||||
|
||||
======================= no tests ran in 0.12 seconds =======================
|
||||
|
||||
You can check for multiple glob patterns by adding a space between the patterns::
|
||||
|
||||
# Example 2: have pytest look for files with "test" and "example"
|
||||
# content of pytest.ini, tox.ini, or setup.cfg file (replace "pytest"
|
||||
# with "tool:pytest" for setup.cfg)
|
||||
[pytest]
|
||||
python_files = test_*.py example_*.py
|
||||
|
||||
.. note::
|
||||
|
||||
the ``python_functions`` and ``python_classes`` options has no effect
|
||||
for ``unittest.TestCase`` test discovery because pytest delegates
|
||||
detection of test case methods to unittest code.
|
||||
discovery of test case methods to unittest code.
|
||||
|
||||
Interpreting cmdline arguments as Python packages
|
||||
-----------------------------------------------------
|
||||
|
||||
@@ -363,7 +363,7 @@ get on the terminal - we are working on that)::
|
||||
> int(s)
|
||||
E ValueError: invalid literal for int() with base 10: 'qwe'
|
||||
|
||||
<0-codegen $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/python_api.py:635>:1: ValueError
|
||||
<0-codegen $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/python_api.py:682>:1: ValueError
|
||||
______________________ TestRaises.test_raises_doesnt _______________________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
@@ -423,7 +423,7 @@ get on the terminal - we are working on that)::
|
||||
name = "abc-123"
|
||||
module = imp.new_module(name)
|
||||
code = _pytest._code.compile(src, name, "exec")
|
||||
py.builtin.exec_(code, module.__dict__)
|
||||
six.exec_(code, module.__dict__)
|
||||
sys.modules[name] = module
|
||||
> module.foo()
|
||||
|
||||
@@ -617,5 +617,5 @@ get on the terminal - we are working on that)::
|
||||
Metafunc.addcall is deprecated and scheduled to be removed in pytest 4.0.
|
||||
Please use Metafunc.parametrize instead.
|
||||
|
||||
-- Docs: http://doc.pytest.org/en/latest/warnings.html
|
||||
-- Docs: https://docs.pytest.org/en/latest/warnings.html
|
||||
================== 42 failed, 1 warnings in 0.12 seconds ===================
|
||||
|
||||
@@ -7,7 +7,7 @@ pytest-2.3: reasoning for fixture/funcarg evolution
|
||||
|
||||
**Target audience**: Reading this document requires basic knowledge of
|
||||
python testing, xUnit setup methods and the (previous) basic pytest
|
||||
funcarg mechanism, see http://pytest.org/2.2.4/funcargs.html
|
||||
funcarg mechanism, see https://docs.pytest.org/en/latest/historical-notes.html#funcargs-and-pytest-funcarg.
|
||||
If you are new to pytest, then you can simply ignore this
|
||||
section and read the other sections.
|
||||
|
||||
|
||||
@@ -4,6 +4,27 @@
|
||||
Good Integration Practices
|
||||
=================================================
|
||||
|
||||
Install package with pip
|
||||
-------------------------------------------------
|
||||
|
||||
For development, we recommend to use virtualenv_ environments and pip_
|
||||
for installing your application and any dependencies
|
||||
as well as the ``pytest`` package itself. This ensures your code and
|
||||
dependencies are isolated from the system Python installation.
|
||||
|
||||
First you need to place a ``setup.py`` file in the root of your package with the following minimum content::
|
||||
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(name="PACKAGENAME", packages=find_packages())
|
||||
|
||||
Where ``PACKAGENAME`` is the name of your package. You can then install your package in "editable" mode by running from the same directory::
|
||||
|
||||
pip install -e .
|
||||
|
||||
which lets you change your source code (both tests and application) and rerun tests at will.
|
||||
This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs
|
||||
your package using a symlink to your development code.
|
||||
|
||||
.. _`test discovery`:
|
||||
.. _`Python test discovery`:
|
||||
@@ -177,19 +198,6 @@ Note that this layout also works in conjunction with the ``src`` layout mentione
|
||||
tox
|
||||
------
|
||||
|
||||
For development, we recommend to use virtualenv_ environments and pip_
|
||||
for installing your application and any dependencies
|
||||
as well as the ``pytest`` package itself. This ensures your code and
|
||||
dependencies are isolated from the system Python installation.
|
||||
|
||||
You can then install your package in "editable" mode::
|
||||
|
||||
pip install -e .
|
||||
|
||||
which lets you change your source code (both tests and application) and rerun tests at will.
|
||||
This is similar to running ``python setup.py develop`` or ``conda develop`` in that it installs
|
||||
your package using a symlink to your development code.
|
||||
|
||||
Once you are done with your work and want to make sure that your actual
|
||||
package passes all tests you may want to look into `tox`_, the
|
||||
virtualenv test automation tool and its `pytest support
|
||||
@@ -282,7 +290,7 @@ your own setuptools Test command for invoking pytest.
|
||||
setup(
|
||||
# ...,
|
||||
tests_require=["pytest"],
|
||||
cmdclass={"test": PyTest},
|
||||
cmdclass={"pytest": PyTest},
|
||||
)
|
||||
|
||||
Now if you run::
|
||||
|
||||
@@ -460,7 +460,7 @@ To use it, include in your top-most ``conftest.py`` file::
|
||||
|
||||
|
||||
.. autoclass:: Testdir()
|
||||
:members: runpytest,runpytest_subprocess,runpytest_inprocess,makeconftest,makepyfile
|
||||
:members:
|
||||
|
||||
.. autoclass:: RunResult()
|
||||
:members:
|
||||
@@ -1229,7 +1229,8 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
.. confval:: python_classes
|
||||
|
||||
One or more name prefixes or glob-style patterns determining which classes
|
||||
are considered for test collection. By default, pytest will consider any
|
||||
are considered for test collection. Search for multiple glob patterns by
|
||||
adding a space between patterns. By default, pytest will consider any
|
||||
class prefixed with ``Test`` as a test collection. Here is an example of how
|
||||
to collect tests from classes that end in ``Suite``:
|
||||
|
||||
@@ -1246,15 +1247,23 @@ passed multiple times. The expected format is ``name=value``. For example::
|
||||
.. confval:: python_files
|
||||
|
||||
One or more Glob-style file patterns determining which python files
|
||||
are considered as test modules. By default, pytest will consider
|
||||
any file matching with ``test_*.py`` and ``*_test.py`` globs as a test
|
||||
module.
|
||||
are considered as test modules. Search for multiple glob patterns by
|
||||
adding a space between patterns::
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
[pytest]
|
||||
python_files = test_*.py check_*.py example_*.py
|
||||
|
||||
By default, pytest will consider any file matching with ``test_*.py``
|
||||
and ``*_test.py`` globs as a test module.
|
||||
|
||||
|
||||
.. confval:: python_functions
|
||||
|
||||
One or more name prefixes or glob-patterns determining which test functions
|
||||
and methods are considered tests. By default, pytest will consider any
|
||||
and methods are considered tests. Search for multiple glob patterns by
|
||||
adding a space between patterns. By default, pytest will consider any
|
||||
function prefixed with ``test`` as a test. Here is an example of how
|
||||
to collect test functions and methods that end in ``_test``:
|
||||
|
||||
|
||||
@@ -136,12 +136,6 @@ You can use the ``skipif`` marker (as any other marker) on classes::
|
||||
If the condition is ``True``, this marker will produce a skip result for
|
||||
each of the test methods of that class.
|
||||
|
||||
.. warning::
|
||||
|
||||
The use of ``skipif`` on classes that use inheritance is strongly
|
||||
discouraged. `A Known bug <https://github.com/pytest-dev/pytest/issues/568>`_
|
||||
in pytest's markers may cause unexpected behavior in super classes.
|
||||
|
||||
If you want to skip all test functions of a module, you may use
|
||||
the ``pytestmark`` name on the global level:
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ Running pytest now produces this output::
|
||||
$REGENDOC_TMPDIR/test_show_warnings.py:4: UserWarning: api v1, should use functions from v2
|
||||
warnings.warn(UserWarning("api v1, should use functions from v2"))
|
||||
|
||||
-- Docs: http://doc.pytest.org/en/latest/warnings.html
|
||||
-- Docs: https://docs.pytest.org/en/latest/warnings.html
|
||||
=================== 1 passed, 1 warnings in 0.12 seconds ===================
|
||||
|
||||
Pytest by default catches all warnings except for ``DeprecationWarning`` and ``PendingDeprecationWarning``.
|
||||
|
||||
@@ -422,7 +422,7 @@ additionally it is possible to copy examples for a example folder before running
|
||||
$REGENDOC_TMPDIR/test_example.py:4: PytestExerimentalApiWarning: testdir.copy_example is an experimental api that may change over time
|
||||
testdir.copy_example("test_example.py")
|
||||
|
||||
-- Docs: http://doc.pytest.org/en/latest/warnings.html
|
||||
-- Docs: https://docs.pytest.org/en/latest/warnings.html
|
||||
=================== 2 passed, 1 warnings in 0.12 seconds ===================
|
||||
|
||||
For more information about the result object that ``runpytest()`` returns, and
|
||||
|
||||
7
scripts/fail
Executable file
7
scripts/fail
Executable file
@@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
"""Used by .pre-commit-config.yaml"""
|
||||
import sys
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(" ".join(sys.argv[1:]))
|
||||
sys.exit(1)
|
||||
@@ -9,11 +9,11 @@ against itself, passing on many different interpreters and platforms.
|
||||
This release contains a number of bugs fixes and improvements, so users are encouraged
|
||||
to take a look at the CHANGELOG:
|
||||
|
||||
http://doc.pytest.org/en/latest/changelog.html
|
||||
https://docs.pytest.org/en/latest/changelog.html
|
||||
|
||||
For complete documentation, please visit:
|
||||
|
||||
http://docs.pytest.org
|
||||
https://docs.pytest.org/en/latest/
|
||||
|
||||
As usual, you can upgrade from pypi via:
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
The full changelog is available at https://docs.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
|
||||
8
setup.py
8
setup.py
@@ -73,11 +73,11 @@ def main():
|
||||
environment_marker_support_level = get_environment_marker_support_level()
|
||||
if environment_marker_support_level >= 2:
|
||||
install_requires.append('funcsigs;python_version<"3.0"')
|
||||
install_requires.append('pathlib2;python_version<"3.6"')
|
||||
install_requires.append('pathlib2>=2.2.0;python_version<"3.6"')
|
||||
install_requires.append('colorama;sys_platform=="win32"')
|
||||
elif environment_marker_support_level == 1:
|
||||
extras_require[':python_version<"3.0"'] = ["funcsigs"]
|
||||
extras_require[':python_version<"3.6"'] = ["pathlib2"]
|
||||
extras_require[':python_version<"3.6"'] = ["pathlib2>=2.2.0"]
|
||||
extras_require[':sys_platform=="win32"'] = ["colorama"]
|
||||
else:
|
||||
if sys.platform == "win32":
|
||||
@@ -85,14 +85,14 @@ def main():
|
||||
if sys.version_info < (3, 0):
|
||||
install_requires.append("funcsigs")
|
||||
if sys.version_info < (3, 6):
|
||||
install_requires.append("pathlib2")
|
||||
install_requires.append("pathlib2>=2.2.0")
|
||||
|
||||
setup(
|
||||
name="pytest",
|
||||
description="pytest: simple powerful testing with Python",
|
||||
long_description=long_description,
|
||||
use_scm_version={"write_to": "src/_pytest/_version.py"},
|
||||
url="http://pytest.org",
|
||||
url="https://docs.pytest.org/en/latest/",
|
||||
project_urls={
|
||||
"Source": "https://github.com/pytest-dev/pytest",
|
||||
"Tracker": "https://github.com/pytest-dev/pytest/issues",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
# CHANGES:
|
||||
# - some_str is replaced, trying to create unicode strings
|
||||
#
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from __future__ import absolute_import, division, print_function, unicode_literals
|
||||
import types
|
||||
from six import text_type
|
||||
|
||||
@@ -51,17 +51,17 @@ def format_exception_only(etype, value):
|
||||
pass
|
||||
else:
|
||||
filename = filename or "<string>"
|
||||
lines.append(' File "%s", line %d\n' % (filename, lineno))
|
||||
lines.append(' File "{}", line {}\n'.format(filename, lineno))
|
||||
if badline is not None:
|
||||
if isinstance(badline, bytes): # python 2 only
|
||||
badline = badline.decode("utf-8", "replace")
|
||||
lines.append(u" %s\n" % badline.strip())
|
||||
lines.append(" {}\n".format(badline.strip()))
|
||||
if offset is not None:
|
||||
caretspace = badline.rstrip("\n")[:offset].lstrip()
|
||||
# non-space whitespace (likes tabs) must be kept for alignment
|
||||
caretspace = ((c.isspace() and c or " ") for c in caretspace)
|
||||
# only three spaces to account for offset1 == pos 0
|
||||
lines.append(" %s^\n" % "".join(caretspace))
|
||||
lines.append(" {}^\n".format("".join(caretspace)))
|
||||
value = msg
|
||||
|
||||
lines.append(_format_final_exc_line(stype, value))
|
||||
@@ -72,9 +72,9 @@ def _format_final_exc_line(etype, value):
|
||||
"""Return a list of a single line -- normal case for format_exception_only"""
|
||||
valuestr = _some_str(value)
|
||||
if value is None or not valuestr:
|
||||
line = "%s\n" % etype
|
||||
line = "{}\n".format(etype)
|
||||
else:
|
||||
line = "%s: %s\n" % (etype, valuestr)
|
||||
line = "{}: {}\n".format(etype, valuestr)
|
||||
return line
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ def _some_str(value):
|
||||
return text_type(value)
|
||||
except Exception:
|
||||
try:
|
||||
return str(value)
|
||||
return bytes(value).decode("UTF-8", "replace")
|
||||
except Exception:
|
||||
pass
|
||||
return "<unprintable %s object>" % type(value).__name__
|
||||
return "<unprintable {} object>".format(type(value).__name__)
|
||||
|
||||
@@ -11,6 +11,7 @@ from weakref import ref
|
||||
from _pytest.compat import _PY2, _PY3, PY35, safe_str
|
||||
from six import text_type
|
||||
import py
|
||||
import six
|
||||
|
||||
builtin_repr = repr
|
||||
|
||||
@@ -128,7 +129,7 @@ class Frame(object):
|
||||
"""
|
||||
f_locals = self.f_locals.copy()
|
||||
f_locals.update(vars)
|
||||
py.builtin.exec_(code, self.f_globals, f_locals)
|
||||
six.exec_(code, self.f_globals, f_locals)
|
||||
|
||||
def repr(self, object):
|
||||
""" return a 'safe' (non-recursive, one-line) string repr for 'object'
|
||||
@@ -719,7 +720,9 @@ class FormattedExcinfo(object):
|
||||
repr_chain = []
|
||||
e = excinfo.value
|
||||
descr = None
|
||||
while e is not None:
|
||||
seen = set()
|
||||
while e is not None and id(e) not in seen:
|
||||
seen.add(id(e))
|
||||
if excinfo:
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
|
||||
@@ -64,11 +64,16 @@ class AssertionRewritingHook(object):
|
||||
self._rewritten_names = set()
|
||||
self._register_with_pkg_resources()
|
||||
self._must_rewrite = set()
|
||||
# flag to guard against trying to rewrite a pyc file while we are already writing another pyc file,
|
||||
# which might result in infinite recursion (#3506)
|
||||
self._writing_pyc = False
|
||||
|
||||
def set_session(self, session):
|
||||
self.session = session
|
||||
|
||||
def find_module(self, name, path=None):
|
||||
if self._writing_pyc:
|
||||
return None
|
||||
state = self.config._assertstate
|
||||
state.trace("find_module called for: %s" % name)
|
||||
names = name.rsplit(".", 1)
|
||||
@@ -151,7 +156,11 @@ class AssertionRewritingHook(object):
|
||||
# Probably a SyntaxError in the test.
|
||||
return None
|
||||
if write:
|
||||
_write_pyc(state, co, source_stat, pyc)
|
||||
self._writing_pyc = True
|
||||
try:
|
||||
_write_pyc(state, co, source_stat, pyc)
|
||||
finally:
|
||||
self._writing_pyc = False
|
||||
else:
|
||||
state.trace("found cached rewritten pyc for %r" % (fn,))
|
||||
self.modules[name] = co, pyc
|
||||
@@ -223,7 +232,7 @@ class AssertionRewritingHook(object):
|
||||
mod.__loader__ = self
|
||||
# Normally, this attribute is 3.4+
|
||||
mod.__spec__ = spec_from_file_location(name, co.co_filename, loader=self)
|
||||
py.builtin.exec_(co, mod.__dict__)
|
||||
six.exec_(co, mod.__dict__)
|
||||
except: # noqa
|
||||
if name in sys.modules:
|
||||
del sys.modules[name]
|
||||
@@ -402,12 +411,11 @@ def _saferepr(obj):
|
||||
JSON reprs.
|
||||
|
||||
"""
|
||||
repr = py.io.saferepr(obj)
|
||||
if isinstance(repr, six.text_type):
|
||||
t = six.text_type
|
||||
r = py.io.saferepr(obj)
|
||||
if isinstance(r, six.text_type):
|
||||
return r.replace(u"\n", u"\\n")
|
||||
else:
|
||||
t = six.binary_type
|
||||
return repr.replace(t("\n"), t("\\n"))
|
||||
return r.replace(b"\n", b"\\n")
|
||||
|
||||
|
||||
from _pytest.assertion.util import format_explanation as _format_explanation # noqa
|
||||
@@ -425,20 +433,18 @@ def _format_assertmsg(obj):
|
||||
# contains a newline it gets escaped, however if an object has a
|
||||
# .__repr__() which contains newlines it does not get escaped.
|
||||
# However in either case we want to preserve the newline.
|
||||
if isinstance(obj, six.text_type) or isinstance(obj, six.binary_type):
|
||||
s = obj
|
||||
is_repr = False
|
||||
else:
|
||||
s = py.io.saferepr(obj)
|
||||
is_repr = True
|
||||
if isinstance(s, six.text_type):
|
||||
t = six.text_type
|
||||
else:
|
||||
t = six.binary_type
|
||||
s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%"))
|
||||
if is_repr:
|
||||
s = s.replace(t("\\n"), t("\n~"))
|
||||
return s
|
||||
replaces = [(u"\n", u"\n~"), (u"%", u"%%")]
|
||||
if not isinstance(obj, six.string_types):
|
||||
obj = py.io.saferepr(obj)
|
||||
replaces.append((u"\\n", u"\n~"))
|
||||
|
||||
if isinstance(obj, bytes):
|
||||
replaces = [(r1.encode(), r2.encode()) for r1, r2 in replaces]
|
||||
|
||||
for r1, r2 in replaces:
|
||||
obj = obj.replace(r1, r2)
|
||||
|
||||
return obj
|
||||
|
||||
|
||||
def _should_repr_global_name(obj):
|
||||
@@ -448,10 +454,9 @@ def _should_repr_global_name(obj):
|
||||
def _format_boolop(explanations, is_or):
|
||||
explanation = "(" + (is_or and " or " or " and ").join(explanations) + ")"
|
||||
if isinstance(explanation, six.text_type):
|
||||
t = six.text_type
|
||||
return explanation.replace(u"%", u"%%")
|
||||
else:
|
||||
t = six.binary_type
|
||||
return explanation.replace(t("%"), t("%%"))
|
||||
return explanation.replace(b"%", b"%%")
|
||||
|
||||
|
||||
def _call_reprcompare(ops, results, expls, each_obj):
|
||||
|
||||
@@ -187,9 +187,9 @@ def _diff_text(left, right, verbose=False):
|
||||
r = r.replace(r"\r", "\r")
|
||||
return r
|
||||
|
||||
if isinstance(left, six.binary_type):
|
||||
if isinstance(left, bytes):
|
||||
left = escape_for_readable_diff(left)
|
||||
if isinstance(right, six.binary_type):
|
||||
if isinstance(right, bytes):
|
||||
right = escape_for_readable_diff(right)
|
||||
if not verbose:
|
||||
i = 0 # just in case left or right has zero length
|
||||
|
||||
@@ -132,7 +132,7 @@ class LFPlugin(object):
|
||||
self._no_failures_behavior = self.config.getoption("last_failed_no_failures")
|
||||
|
||||
def pytest_report_collectionfinish(self):
|
||||
if self.active:
|
||||
if self.active and self.config.getoption("verbose") >= 0:
|
||||
if not self._previously_failed_count:
|
||||
mode = "run {} (no recorded failures)".format(
|
||||
self._no_failures_behavior
|
||||
@@ -312,8 +312,15 @@ def cache(request):
|
||||
|
||||
def pytest_report_header(config):
|
||||
if config.option.verbose:
|
||||
relpath = config.cache._cachedir.relative_to(config.rootdir)
|
||||
return "cachedir: {}".format(relpath)
|
||||
cachedir = config.cache._cachedir
|
||||
# TODO: evaluate generating upward relative paths
|
||||
# starting with .., ../.. if sensible
|
||||
|
||||
try:
|
||||
displaypath = cachedir.relative_to(config.rootdir)
|
||||
except ValueError:
|
||||
displaypath = cachedir
|
||||
return "cachedir: {}".format(displaypath)
|
||||
|
||||
|
||||
def cacheshow(config, session):
|
||||
|
||||
@@ -16,7 +16,6 @@ import six
|
||||
import pytest
|
||||
from _pytest.compat import CaptureIO
|
||||
|
||||
|
||||
patchsysdict = {0: "stdin", 1: "stdout", 2: "stderr"}
|
||||
|
||||
|
||||
@@ -63,8 +62,9 @@ def pytest_load_initial_conftests(early_config, parser, args):
|
||||
# finally trigger conftest loading but while capturing (issue93)
|
||||
capman.start_global_capturing()
|
||||
outcome = yield
|
||||
out, err = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
if outcome.excinfo is not None:
|
||||
out, err = capman.read_global_capture()
|
||||
sys.stdout.write(out)
|
||||
sys.stderr.write(err)
|
||||
|
||||
@@ -85,6 +85,7 @@ class CaptureManager(object):
|
||||
def __init__(self, method):
|
||||
self._method = method
|
||||
self._global_capturing = None
|
||||
self._current_item = None
|
||||
|
||||
def _getcapture(self, method):
|
||||
if method == "fd":
|
||||
@@ -96,6 +97,8 @@ class CaptureManager(object):
|
||||
else:
|
||||
raise ValueError("unknown capturing method: %r" % method)
|
||||
|
||||
# Global capturing control
|
||||
|
||||
def start_global_capturing(self):
|
||||
assert self._global_capturing is None
|
||||
self._global_capturing = self._getcapture(self._method)
|
||||
@@ -110,16 +113,15 @@ class CaptureManager(object):
|
||||
def resume_global_capture(self):
|
||||
self._global_capturing.resume_capturing()
|
||||
|
||||
def suspend_global_capture(self, item=None, in_=False):
|
||||
if item is not None:
|
||||
self.deactivate_fixture(item)
|
||||
def suspend_global_capture(self, in_=False):
|
||||
cap = getattr(self, "_global_capturing", None)
|
||||
if cap is not None:
|
||||
try:
|
||||
outerr = cap.readouterr()
|
||||
finally:
|
||||
cap.suspend_capturing(in_=in_)
|
||||
return outerr
|
||||
cap.suspend_capturing(in_=in_)
|
||||
|
||||
def read_global_capture(self):
|
||||
return self._global_capturing.readouterr()
|
||||
|
||||
# Fixture Control (its just forwarding, think about removing this later)
|
||||
|
||||
def activate_fixture(self, item):
|
||||
"""If the current item is using ``capsys`` or ``capfd``, activate them so they take precedence over
|
||||
@@ -135,12 +137,53 @@ class CaptureManager(object):
|
||||
if fixture is not None:
|
||||
fixture.close()
|
||||
|
||||
def suspend_fixture(self, item):
|
||||
fixture = getattr(item, "_capture_fixture", None)
|
||||
if fixture is not None:
|
||||
fixture._suspend()
|
||||
|
||||
def resume_fixture(self, item):
|
||||
fixture = getattr(item, "_capture_fixture", None)
|
||||
if fixture is not None:
|
||||
fixture._resume()
|
||||
|
||||
# Helper context managers
|
||||
|
||||
@contextlib.contextmanager
|
||||
def global_and_fixture_disabled(self):
|
||||
"""Context manager to temporarily disables global and current fixture capturing."""
|
||||
# Need to undo local capsys-et-al if exists before disabling global capture
|
||||
self.suspend_fixture(self._current_item)
|
||||
self.suspend_global_capture(in_=False)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.resume_global_capture()
|
||||
self.resume_fixture(self._current_item)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def item_capture(self, when, item):
|
||||
self.resume_global_capture()
|
||||
self.activate_fixture(item)
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self.deactivate_fixture(item)
|
||||
self.suspend_global_capture(in_=False)
|
||||
|
||||
out, err = self.read_global_capture()
|
||||
item.add_report_section(when, "stdout", out)
|
||||
item.add_report_section(when, "stderr", err)
|
||||
|
||||
# Hooks
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_make_collect_report(self, collector):
|
||||
if isinstance(collector, pytest.File):
|
||||
self.resume_global_capture()
|
||||
outcome = yield
|
||||
out, err = self.suspend_global_capture()
|
||||
self.suspend_global_capture()
|
||||
out, err = self.read_global_capture()
|
||||
rep = outcome.get_result()
|
||||
if out:
|
||||
rep.sections.append(("Captured stdout", out))
|
||||
@@ -150,29 +193,25 @@ class CaptureManager(object):
|
||||
yield
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_setup(self, item):
|
||||
self.resume_global_capture()
|
||||
# no need to activate a capture fixture because they activate themselves during creation; this
|
||||
# only makes sense when a fixture uses a capture fixture, otherwise the capture fixture will
|
||||
# be activated during pytest_runtest_call
|
||||
def pytest_runtest_protocol(self, item):
|
||||
self._current_item = item
|
||||
yield
|
||||
self.suspend_capture_item(item, "setup")
|
||||
self._current_item = None
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_setup(self, item):
|
||||
with self.item_capture("setup", item):
|
||||
yield
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_call(self, item):
|
||||
self.resume_global_capture()
|
||||
# it is important to activate this fixture during the call phase so it overwrites the "global"
|
||||
# capture
|
||||
self.activate_fixture(item)
|
||||
yield
|
||||
self.suspend_capture_item(item, "call")
|
||||
with self.item_capture("call", item):
|
||||
yield
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_runtest_teardown(self, item):
|
||||
self.resume_global_capture()
|
||||
self.activate_fixture(item)
|
||||
yield
|
||||
self.suspend_capture_item(item, "teardown")
|
||||
with self.item_capture("teardown", item):
|
||||
yield
|
||||
|
||||
@pytest.hookimpl(tryfirst=True)
|
||||
def pytest_keyboard_interrupt(self, excinfo):
|
||||
@@ -182,11 +221,6 @@ class CaptureManager(object):
|
||||
def pytest_internalerror(self, excinfo):
|
||||
self.stop_global_capturing()
|
||||
|
||||
def suspend_capture_item(self, item, when, in_=False):
|
||||
out, err = self.suspend_global_capture(item, in_=in_)
|
||||
item.add_report_section(when, "stdout", out)
|
||||
item.add_report_section(when, "stderr", err)
|
||||
|
||||
|
||||
capture_fixtures = {"capfd", "capfdbinary", "capsys", "capsysbinary"}
|
||||
|
||||
@@ -290,40 +324,54 @@ class CaptureFixture(object):
|
||||
def __init__(self, captureclass, request):
|
||||
self.captureclass = captureclass
|
||||
self.request = request
|
||||
self._capture = None
|
||||
self._captured_out = self.captureclass.EMPTY_BUFFER
|
||||
self._captured_err = self.captureclass.EMPTY_BUFFER
|
||||
|
||||
def _start(self):
|
||||
self._capture = MultiCapture(
|
||||
out=True, err=True, in_=False, Capture=self.captureclass
|
||||
)
|
||||
self._capture.start_capturing()
|
||||
# Start if not started yet
|
||||
if getattr(self, "_capture", None) is None:
|
||||
self._capture = MultiCapture(
|
||||
out=True, err=True, in_=False, Capture=self.captureclass
|
||||
)
|
||||
self._capture.start_capturing()
|
||||
|
||||
def close(self):
|
||||
cap = self.__dict__.pop("_capture", None)
|
||||
if cap is not None:
|
||||
self._outerr = cap.pop_outerr_to_orig()
|
||||
cap.stop_capturing()
|
||||
if self._capture is not None:
|
||||
out, err = self._capture.pop_outerr_to_orig()
|
||||
self._captured_out += out
|
||||
self._captured_err += err
|
||||
self._capture.stop_capturing()
|
||||
self._capture = None
|
||||
|
||||
def readouterr(self):
|
||||
"""Read and return the captured output so far, resetting the internal buffer.
|
||||
|
||||
:return: captured content as a namedtuple with ``out`` and ``err`` string attributes
|
||||
"""
|
||||
try:
|
||||
return self._capture.readouterr()
|
||||
except AttributeError:
|
||||
return self._outerr
|
||||
captured_out, captured_err = self._captured_out, self._captured_err
|
||||
if self._capture is not None:
|
||||
out, err = self._capture.readouterr()
|
||||
captured_out += out
|
||||
captured_err += err
|
||||
self._captured_out = self.captureclass.EMPTY_BUFFER
|
||||
self._captured_err = self.captureclass.EMPTY_BUFFER
|
||||
return CaptureResult(captured_out, captured_err)
|
||||
|
||||
def _suspend(self):
|
||||
"""Suspends this fixture's own capturing temporarily."""
|
||||
self._capture.suspend_capturing()
|
||||
|
||||
def _resume(self):
|
||||
"""Resumes this fixture's own capturing temporarily."""
|
||||
self._capture.resume_capturing()
|
||||
|
||||
@contextlib.contextmanager
|
||||
def disabled(self):
|
||||
"""Temporarily disables capture while inside the 'with' block."""
|
||||
self._capture.suspend_capturing()
|
||||
capmanager = self.request.config.pluginmanager.getplugin("capturemanager")
|
||||
capmanager.suspend_global_capture(item=None, in_=False)
|
||||
try:
|
||||
with capmanager.global_and_fixture_disabled():
|
||||
yield
|
||||
finally:
|
||||
capmanager.resume_global_capture()
|
||||
self._capture.resume_capturing()
|
||||
|
||||
|
||||
def safe_text_dupfile(f, mode, default_encoding="UTF8"):
|
||||
@@ -440,6 +488,7 @@ class MultiCapture(object):
|
||||
|
||||
|
||||
class NoCapture(object):
|
||||
EMPTY_BUFFER = None
|
||||
__init__ = start = done = suspend = resume = lambda *args: None
|
||||
|
||||
|
||||
@@ -449,6 +498,8 @@ class FDCaptureBinary(object):
|
||||
snap() produces `bytes`
|
||||
"""
|
||||
|
||||
EMPTY_BUFFER = bytes()
|
||||
|
||||
def __init__(self, targetfd, tmpfile=None):
|
||||
self.targetfd = targetfd
|
||||
try:
|
||||
@@ -522,6 +573,8 @@ class FDCapture(FDCaptureBinary):
|
||||
snap() produces text
|
||||
"""
|
||||
|
||||
EMPTY_BUFFER = str()
|
||||
|
||||
def snap(self):
|
||||
res = FDCaptureBinary.snap(self)
|
||||
enc = getattr(self.tmpfile, "encoding", None)
|
||||
@@ -531,6 +584,9 @@ class FDCapture(FDCaptureBinary):
|
||||
|
||||
|
||||
class SysCapture(object):
|
||||
|
||||
EMPTY_BUFFER = str()
|
||||
|
||||
def __init__(self, fd, tmpfile=None):
|
||||
name = patchsysdict[fd]
|
||||
self._old = getattr(sys, name)
|
||||
@@ -568,6 +624,8 @@ class SysCapture(object):
|
||||
|
||||
|
||||
class SysCaptureBinary(SysCapture):
|
||||
EMPTY_BUFFER = bytes()
|
||||
|
||||
def snap(self):
|
||||
res = self.tmpfile.buffer.getvalue()
|
||||
self.tmpfile.seek(0)
|
||||
|
||||
@@ -8,6 +8,7 @@ import functools
|
||||
import inspect
|
||||
import re
|
||||
import sys
|
||||
from contextlib import contextmanager
|
||||
|
||||
import py
|
||||
|
||||
@@ -151,6 +152,13 @@ def getfuncargnames(function, is_method=False, cls=None):
|
||||
return arg_names
|
||||
|
||||
|
||||
@contextmanager
|
||||
def dummy_context_manager():
|
||||
"""Context manager that does nothing, useful in situations where you might need an actual context manager or not
|
||||
depending on some condition. Using this allow to keep the same code"""
|
||||
yield
|
||||
|
||||
|
||||
def get_default_arg_names(function):
|
||||
# Note: this code intentionally mirrors the code at the beginning of getfuncargnames,
|
||||
# to get the arguments which were excluded from its result because they had default values
|
||||
@@ -228,12 +236,31 @@ else:
|
||||
return val.encode("unicode-escape")
|
||||
|
||||
|
||||
class _PytestWrapper(object):
|
||||
"""Dummy wrapper around a function object for internal use only.
|
||||
|
||||
Used to correctly unwrap the underlying function object
|
||||
when we are creating fixtures, because we wrap the function object ourselves with a decorator
|
||||
to issue warnings when the fixture function is called directly.
|
||||
"""
|
||||
|
||||
def __init__(self, obj):
|
||||
self.obj = obj
|
||||
|
||||
|
||||
def get_real_func(obj):
|
||||
""" gets the real function object of the (possibly) wrapped object by
|
||||
functools.wraps or functools.partial.
|
||||
"""
|
||||
start_obj = obj
|
||||
for i in range(100):
|
||||
# __pytest_wrapped__ is set by @pytest.fixture when wrapping the fixture function
|
||||
# to trigger a warning if it gets called directly instead of by pytest: we don't
|
||||
# want to unwrap further than this otherwise we lose useful wrappings like @mock.patch (#3774)
|
||||
new_obj = getattr(obj, "__pytest_wrapped__", None)
|
||||
if isinstance(new_obj, _PytestWrapper):
|
||||
obj = new_obj.obj
|
||||
break
|
||||
new_obj = getattr(obj, "__wrapped__", None)
|
||||
if new_obj is None:
|
||||
break
|
||||
@@ -249,6 +276,21 @@ def get_real_func(obj):
|
||||
return obj
|
||||
|
||||
|
||||
def get_real_method(obj, holder):
|
||||
"""
|
||||
Attempts to obtain the real function object that might be wrapping ``obj``, while at the same time
|
||||
returning a bound method to ``holder`` if the original object was a bound method.
|
||||
"""
|
||||
try:
|
||||
is_method = hasattr(obj, "__func__")
|
||||
obj = get_real_func(obj)
|
||||
except Exception:
|
||||
return obj
|
||||
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
|
||||
obj = obj.__get__(holder)
|
||||
return obj
|
||||
|
||||
|
||||
def getfslineno(obj):
|
||||
# xxx let decorators etc specify a sane ordering
|
||||
obj = get_real_func(obj)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
""" command line options, ini-file and conftest.py processing. """
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import argparse
|
||||
import inspect
|
||||
import shlex
|
||||
import traceback
|
||||
import types
|
||||
@@ -252,6 +253,10 @@ class PytestPluginManager(PluginManager):
|
||||
method = getattr(plugin, name)
|
||||
opts = super(PytestPluginManager, self).parse_hookimpl_opts(plugin, name)
|
||||
|
||||
# consider only actual functions for hooks (#3775)
|
||||
if not inspect.isroutine(method):
|
||||
return
|
||||
|
||||
# collect unmarked hooks as long as they have the `pytest_' prefix
|
||||
if opts is None and name.startswith("pytest_"):
|
||||
opts = {}
|
||||
|
||||
@@ -2,6 +2,8 @@ import six
|
||||
import warnings
|
||||
import argparse
|
||||
|
||||
import py
|
||||
|
||||
FILE_OR_DIR = "file_or_dir"
|
||||
|
||||
|
||||
@@ -70,7 +72,8 @@ class Parser(object):
|
||||
|
||||
self.optparser = self._getparser()
|
||||
try_argcomplete(self.optparser)
|
||||
return self.optparser.parse_args([str(x) for x in args], namespace=namespace)
|
||||
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return self.optparser.parse_args(args, namespace=namespace)
|
||||
|
||||
def _getparser(self):
|
||||
from _pytest._argcomplete import filescompleter
|
||||
@@ -106,7 +109,7 @@ class Parser(object):
|
||||
the remaining arguments unknown at this point.
|
||||
"""
|
||||
optparser = self._getparser()
|
||||
args = [str(x) for x in args]
|
||||
args = [str(x) if isinstance(x, py.path.local) else x for x in args]
|
||||
return optparser.parse_known_args(args, namespace=namespace)
|
||||
|
||||
def addini(self, name, help, type=None, default=None):
|
||||
@@ -174,23 +177,23 @@ class Argument(object):
|
||||
if isinstance(typ, six.string_types):
|
||||
if typ == "choice":
|
||||
warnings.warn(
|
||||
"type argument to addoption() is a string %r."
|
||||
" For parsearg this is optional and when supplied"
|
||||
" should be a type."
|
||||
"`type` argument to addoption() is the string %r."
|
||||
" For choices this is optional and can be omitted, "
|
||||
" but when supplied should be a type (for example `str` or `int`)."
|
||||
" (options: %s)" % (typ, names),
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
stacklevel=4,
|
||||
)
|
||||
# argparse expects a type here take it from
|
||||
# the type of the first element
|
||||
attrs["type"] = type(attrs["choices"][0])
|
||||
else:
|
||||
warnings.warn(
|
||||
"type argument to addoption() is a string %r."
|
||||
" For parsearg this should be a type."
|
||||
"`type` argument to addoption() is the string %r, "
|
||||
" but when supplied should be a type (for example `str` or `int`)."
|
||||
" (options: %s)" % (typ, names),
|
||||
DeprecationWarning,
|
||||
stacklevel=3,
|
||||
stacklevel=4,
|
||||
)
|
||||
attrs["type"] = Argument._typ_map[typ]
|
||||
# used in test_parseopt -> test_parse_defaultgetter
|
||||
|
||||
@@ -102,7 +102,8 @@ class PdbInvoke(object):
|
||||
def pytest_exception_interact(self, node, call, report):
|
||||
capman = node.config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
out, err = capman.suspend_global_capture(in_=True)
|
||||
capman.suspend_global_capture(in_=True)
|
||||
out, err = capman.read_global_capture()
|
||||
sys.stdout.write(out)
|
||||
sys.stdout.write(err)
|
||||
_enter_pdb(node, call.excinfo, report)
|
||||
|
||||
@@ -203,7 +203,8 @@ class DoctestItem(pytest.Item):
|
||||
return
|
||||
capman = self.config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
out, err = capman.suspend_global_capture(in_=True)
|
||||
capman.suspend_global_capture(in_=True)
|
||||
out, err = capman.read_global_capture()
|
||||
sys.stdout.write(out)
|
||||
sys.stderr.write(err)
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ from __future__ import absolute_import, division, print_function
|
||||
|
||||
import functools
|
||||
import inspect
|
||||
import os
|
||||
import sys
|
||||
import warnings
|
||||
from collections import OrderedDict, deque, defaultdict
|
||||
@@ -30,6 +29,8 @@ from _pytest.compat import (
|
||||
getfuncargnames,
|
||||
safe_getattr,
|
||||
FuncargnamesCompatAttr,
|
||||
get_real_method,
|
||||
_PytestWrapper,
|
||||
)
|
||||
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
|
||||
from _pytest.outcomes import fail, TEST_OUTCOME
|
||||
@@ -91,7 +92,7 @@ def get_scope_package(node, fixturedef):
|
||||
|
||||
cls = pytest.Package
|
||||
current = node
|
||||
fixture_package_name = os.path.join(fixturedef.baseid, "__init__.py")
|
||||
fixture_package_name = "%s/%s" % (fixturedef.baseid, "__init__.py")
|
||||
while current and (
|
||||
type(current) is not cls or fixture_package_name != current.nodeid
|
||||
):
|
||||
@@ -305,8 +306,8 @@ class FuncFixtureInfo(object):
|
||||
# fixture names specified via usefixtures and via autouse=True in fixture
|
||||
# definitions.
|
||||
initialnames = attr.ib(type=tuple)
|
||||
names_closure = attr.ib(type="List[str]")
|
||||
name2fixturedefs = attr.ib(type="List[str, List[FixtureDef]]")
|
||||
names_closure = attr.ib() # type: List[str]
|
||||
name2fixturedefs = attr.ib() # type: List[str, List[FixtureDef]]
|
||||
|
||||
def prune_dependency_tree(self):
|
||||
"""Recompute names_closure from initialnames and name2fixturedefs
|
||||
@@ -856,7 +857,7 @@ class FixtureDef(object):
|
||||
if exceptions:
|
||||
e = exceptions[0]
|
||||
del exceptions # ensure we don't keep all frames alive because of the traceback
|
||||
py.builtin._reraise(*e)
|
||||
six.reraise(*e)
|
||||
|
||||
finally:
|
||||
hook = self._fixturemanager.session.gethookproxy(request.node.fspath)
|
||||
@@ -883,7 +884,7 @@ class FixtureDef(object):
|
||||
result, cache_key, err = cached_result
|
||||
if my_cache_key == cache_key:
|
||||
if err is not None:
|
||||
py.builtin._reraise(*err)
|
||||
six.reraise(*err)
|
||||
else:
|
||||
return result
|
||||
# we have a previous but differently parametrized fixture instance
|
||||
@@ -931,13 +932,6 @@ def pytest_fixture_setup(fixturedef, request):
|
||||
request._check_scope(argname, request.scope, fixdef.scope)
|
||||
kwargs[argname] = result
|
||||
|
||||
# if function has been defined with @pytest.fixture, we want to
|
||||
# pass the special __being_called_by_pytest parameter so we don't raise a warning
|
||||
# this is an ugly hack, see #3720 for an opportunity to improve this
|
||||
defined_using_fixture_decorator = hasattr(fixturedef.func, "_pytestfixturefunction")
|
||||
if defined_using_fixture_decorator:
|
||||
kwargs["__being_called_by_pytest"] = True
|
||||
|
||||
fixturefunc = resolve_fixture_function(fixturedef, request)
|
||||
my_cache_key = request.param_index
|
||||
try:
|
||||
@@ -960,9 +954,6 @@ def _ensure_immutable_ids(ids):
|
||||
def wrap_function_to_warning_if_called_directly(function, fixture_marker):
|
||||
"""Wrap the given fixture function so we can issue warnings about it being called directly, instead of
|
||||
used as an argument in a test function.
|
||||
|
||||
The warning is emitted only in Python 3, because I didn't find a reliable way to make the wrapper function
|
||||
keep the original signature, and we probably will drop Python 2 in Pytest 4 anyway.
|
||||
"""
|
||||
is_yield_function = is_generator(function)
|
||||
msg = FIXTURE_FUNCTION_CALL.format(name=fixture_marker.name or function.__name__)
|
||||
@@ -973,9 +964,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
|
||||
@functools.wraps(function)
|
||||
def result(*args, **kwargs):
|
||||
__tracebackhide__ = True
|
||||
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
|
||||
if not __being_called_by_pytest:
|
||||
warnings.warn(warning, stacklevel=3)
|
||||
warnings.warn(warning, stacklevel=3)
|
||||
for x in function(*args, **kwargs):
|
||||
yield x
|
||||
|
||||
@@ -984,14 +973,16 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
|
||||
@functools.wraps(function)
|
||||
def result(*args, **kwargs):
|
||||
__tracebackhide__ = True
|
||||
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
|
||||
if not __being_called_by_pytest:
|
||||
warnings.warn(warning, stacklevel=3)
|
||||
warnings.warn(warning, stacklevel=3)
|
||||
return function(*args, **kwargs)
|
||||
|
||||
if six.PY2:
|
||||
result.__wrapped__ = function
|
||||
|
||||
# keep reference to the original function in our own custom attribute so we don't unwrap
|
||||
# further than this point and lose useful wrappings like @mock.patch (#3774)
|
||||
result.__pytest_wrapped__ = _PytestWrapper(function)
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@@ -1279,9 +1270,9 @@ class FixtureManager(object):
|
||||
# The attribute can be an arbitrary descriptor, so the attribute
|
||||
# access below can raise. safe_getatt() ignores such exceptions.
|
||||
obj = safe_getattr(holderobj, name, None)
|
||||
marker = getfixturemarker(obj)
|
||||
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
|
||||
# or are "@pytest.fixture" marked
|
||||
marker = getfixturemarker(obj)
|
||||
if marker is None:
|
||||
if not name.startswith(self._argprefix):
|
||||
continue
|
||||
@@ -1303,6 +1294,15 @@ class FixtureManager(object):
|
||||
name = marker.name
|
||||
assert not name.startswith(self._argprefix), FIXTURE_MSG.format(name)
|
||||
|
||||
# during fixture definition we wrap the original fixture function
|
||||
# to issue a warning if called directly, so here we unwrap it in order to not emit the warning
|
||||
# when pytest itself calls the fixture function
|
||||
if six.PY2 and unittest:
|
||||
# hack on Python 2 because of the unbound methods
|
||||
obj = get_real_func(obj)
|
||||
else:
|
||||
obj = get_real_method(obj, holderobj)
|
||||
|
||||
fixture_def = FixtureDef(
|
||||
self,
|
||||
nodeid,
|
||||
|
||||
@@ -6,6 +6,7 @@ from contextlib import closing, contextmanager
|
||||
import re
|
||||
import six
|
||||
|
||||
from _pytest.compat import dummy_context_manager
|
||||
from _pytest.config import create_terminal_writer
|
||||
import pytest
|
||||
import py
|
||||
@@ -369,11 +370,6 @@ def pytest_configure(config):
|
||||
config.pluginmanager.register(LoggingPlugin(config), "logging-plugin")
|
||||
|
||||
|
||||
@contextmanager
|
||||
def _dummy_context_manager():
|
||||
yield
|
||||
|
||||
|
||||
class LoggingPlugin(object):
|
||||
"""Attaches to the logging module and captures log messages for each test.
|
||||
"""
|
||||
@@ -537,7 +533,7 @@ class LoggingPlugin(object):
|
||||
log_cli_handler, formatter=log_cli_formatter, level=log_cli_level
|
||||
)
|
||||
else:
|
||||
self.live_logs_context = _dummy_context_manager()
|
||||
self.live_logs_context = dummy_context_manager()
|
||||
|
||||
|
||||
class _LiveLoggingStreamHandler(logging.StreamHandler):
|
||||
@@ -572,9 +568,12 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
|
||||
self._test_outcome_written = False
|
||||
|
||||
def emit(self, record):
|
||||
if self.capture_manager is not None:
|
||||
self.capture_manager.suspend_global_capture()
|
||||
try:
|
||||
ctx_manager = (
|
||||
self.capture_manager.global_and_fixture_disabled()
|
||||
if self.capture_manager
|
||||
else dummy_context_manager()
|
||||
)
|
||||
with ctx_manager:
|
||||
if not self._first_record_emitted:
|
||||
self.stream.write("\n")
|
||||
self._first_record_emitted = True
|
||||
@@ -586,6 +585,3 @@ class _LiveLoggingStreamHandler(logging.StreamHandler):
|
||||
self.stream.section("live log " + self._when, sep="-", bold=True)
|
||||
self._section_name_shown = True
|
||||
logging.StreamHandler.emit(self, record)
|
||||
finally:
|
||||
if self.capture_manager is not None:
|
||||
self.capture_manager.resume_global_capture()
|
||||
|
||||
@@ -482,6 +482,8 @@ class Session(nodes.FSCollector):
|
||||
self.trace.root.indent -= 1
|
||||
|
||||
def _collect(self, arg):
|
||||
from _pytest.python import Package
|
||||
|
||||
names = self._parsearg(arg)
|
||||
argpath = names.pop(0)
|
||||
paths = []
|
||||
@@ -504,7 +506,8 @@ class Session(nodes.FSCollector):
|
||||
else:
|
||||
col = root._collectfile(pkginit)
|
||||
if col:
|
||||
root = col[0]
|
||||
if isinstance(col[0], Package):
|
||||
root = col[0]
|
||||
self._node_cache[root.fspath] = root
|
||||
|
||||
# If it's a directory argument, recurse and look for any Subpackages.
|
||||
@@ -622,11 +625,12 @@ class Session(nodes.FSCollector):
|
||||
resultnodes.append(node)
|
||||
continue
|
||||
assert isinstance(node, nodes.Collector)
|
||||
if node.nodeid in self._node_cache:
|
||||
rep = self._node_cache[node.nodeid]
|
||||
key = (type(node), node.nodeid)
|
||||
if key in self._node_cache:
|
||||
rep = self._node_cache[key]
|
||||
else:
|
||||
rep = collect_one_node(node)
|
||||
self._node_cache[node.nodeid] = rep
|
||||
self._node_cache[key] = rep
|
||||
if rep.passed:
|
||||
has_matched = False
|
||||
for x in rep.result:
|
||||
|
||||
@@ -3,7 +3,6 @@ exception classes and constants handling test outcomes
|
||||
as well as functions creating them
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import py
|
||||
import sys
|
||||
|
||||
|
||||
@@ -21,7 +20,7 @@ class OutcomeException(BaseException):
|
||||
if self.msg:
|
||||
val = self.msg
|
||||
if isinstance(val, bytes):
|
||||
val = py._builtin._totext(val, errors="replace")
|
||||
val = val.decode("UTF-8", errors="replace")
|
||||
return val
|
||||
return "<%s instance>" % (self.__class__.__name__,)
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@ import pytest
|
||||
from _pytest.main import Session, EXIT_OK
|
||||
from _pytest.assertion.rewrite import AssertionRewritingHook
|
||||
from _pytest.compat import Path
|
||||
from _pytest.compat import safe_str
|
||||
|
||||
IGNORE_PAM = [ # filenames added when obtaining details about the current user
|
||||
u"/var/lib/sss/mc/passwd"
|
||||
@@ -34,7 +35,7 @@ def pytest_addoption(parser):
|
||||
action="store_true",
|
||||
dest="lsof",
|
||||
default=False,
|
||||
help=("run FD checks if lsof is available"),
|
||||
help="run FD checks if lsof is available",
|
||||
)
|
||||
|
||||
parser.addoption(
|
||||
@@ -273,7 +274,7 @@ class HookRecorder(object):
|
||||
del self.calls[i]
|
||||
return call
|
||||
lines = ["could not find call %r, in:" % (name,)]
|
||||
lines.extend([" %s" % str(x) for x in self.calls])
|
||||
lines.extend([" %s" % x for x in self.calls])
|
||||
pytest.fail("\n".join(lines))
|
||||
|
||||
def getcall(self, name):
|
||||
@@ -550,18 +551,22 @@ class Testdir(object):
|
||||
return ret
|
||||
|
||||
def makefile(self, ext, *args, **kwargs):
|
||||
"""Create a new file in the testdir.
|
||||
r"""Create new file(s) in the testdir.
|
||||
|
||||
ext: The extension the file should use, including the dot, e.g. `.py`.
|
||||
|
||||
args: All args will be treated as strings and joined using newlines.
|
||||
:param str ext: The extension the file(s) should use, including the dot, e.g. `.py`.
|
||||
:param list[str] args: All args will be treated as strings and joined using newlines.
|
||||
The result will be written as contents to the file. The name of the
|
||||
file will be based on the test function requesting this fixture.
|
||||
E.g. "testdir.makefile('.txt', 'line1', 'line2')"
|
||||
|
||||
kwargs: Each keyword is the name of a file, while the value of it will
|
||||
:param kwargs: Each keyword is the name of a file, while the value of it will
|
||||
be written as contents of the file.
|
||||
E.g. "testdir.makefile('.ini', pytest='[pytest]\naddopts=-rs\n')"
|
||||
|
||||
Examples:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
testdir.makefile(".txt", "line1", "line2")
|
||||
|
||||
testdir.makefile(".ini", pytest="[pytest]\naddopts=-rs\n")
|
||||
|
||||
"""
|
||||
return self._makefile(ext, args, kwargs)
|
||||
@@ -667,7 +672,9 @@ class Testdir(object):
|
||||
example_path.copy(result)
|
||||
return result
|
||||
else:
|
||||
raise LookupError("example is not found as a file or directory")
|
||||
raise LookupError(
|
||||
'example "{}" is not found as a file or directory'.format(example_path)
|
||||
)
|
||||
|
||||
Session = Session
|
||||
|
||||
@@ -881,14 +888,12 @@ class Testdir(object):
|
||||
return self._runpytest_method(*args, **kwargs)
|
||||
|
||||
def _ensure_basetemp(self, args):
|
||||
args = [str(x) for x in args]
|
||||
args = list(args)
|
||||
for x in args:
|
||||
if str(x).startswith("--basetemp"):
|
||||
# print("basedtemp exists: %s" %(args,))
|
||||
if safe_str(x).startswith("--basetemp"):
|
||||
break
|
||||
else:
|
||||
args.append("--basetemp=%s" % self.tmpdir.dirpath("basetemp"))
|
||||
# print("added basetemp: %s" %(args,))
|
||||
return args
|
||||
|
||||
def parseconfig(self, *args):
|
||||
@@ -1014,7 +1019,7 @@ class Testdir(object):
|
||||
"""
|
||||
env = os.environ.copy()
|
||||
env["PYTHONPATH"] = os.pathsep.join(
|
||||
filter(None, [str(os.getcwd()), env.get("PYTHONPATH", "")])
|
||||
filter(None, [os.getcwd(), env.get("PYTHONPATH", "")])
|
||||
)
|
||||
kw["env"] = env
|
||||
|
||||
@@ -1033,14 +1038,13 @@ class Testdir(object):
|
||||
Returns a :py:class:`RunResult`.
|
||||
|
||||
"""
|
||||
return self._run(*cmdargs)
|
||||
|
||||
def _run(self, *cmdargs):
|
||||
cmdargs = [str(x) for x in cmdargs]
|
||||
cmdargs = [
|
||||
str(arg) if isinstance(arg, py.path.local) else arg for arg in cmdargs
|
||||
]
|
||||
p1 = self.tmpdir.join("stdout")
|
||||
p2 = self.tmpdir.join("stderr")
|
||||
print("running:", " ".join(cmdargs))
|
||||
print(" in:", str(py.path.local()))
|
||||
print("running:", *cmdargs)
|
||||
print(" in:", py.path.local())
|
||||
f1 = codecs.open(str(p1), "w", encoding="utf8")
|
||||
f2 = codecs.open(str(p2), "w", encoding="utf8")
|
||||
try:
|
||||
@@ -1072,7 +1076,7 @@ class Testdir(object):
|
||||
print("couldn't print to %s because of encoding" % (fp,))
|
||||
|
||||
def _getpytestargs(self):
|
||||
return (sys.executable, "-mpytest")
|
||||
return sys.executable, "-mpytest"
|
||||
|
||||
def runpython(self, script):
|
||||
"""Run a python script using sys.executable as interpreter.
|
||||
|
||||
@@ -173,13 +173,14 @@ def pytest_configure(config):
|
||||
"or a list of tuples of values if argnames specifies multiple names. "
|
||||
"Example: @parametrize('arg1', [1,2]) would lead to two calls of the "
|
||||
"decorated test function, one with arg1=1 and another with arg1=2."
|
||||
"see http://pytest.org/latest/parametrize.html for more info and "
|
||||
"examples.",
|
||||
"see https://docs.pytest.org/en/latest/parametrize.html for more info "
|
||||
"and examples.",
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers",
|
||||
"usefixtures(fixturename1, fixturename2, ...): mark tests as needing "
|
||||
"all of the specified fixtures. see http://pytest.org/latest/fixture.html#usefixtures ",
|
||||
"all of the specified fixtures. see "
|
||||
"https://docs.pytest.org/en/latest/fixture.html#usefixtures ",
|
||||
)
|
||||
|
||||
|
||||
@@ -201,33 +202,25 @@ def pytest_collect_file(path, parent):
|
||||
ext = path.ext
|
||||
if ext == ".py":
|
||||
if not parent.session.isinitpath(path):
|
||||
for pat in parent.config.getini("python_files") + ["__init__.py"]:
|
||||
if path.fnmatch(pat):
|
||||
break
|
||||
else:
|
||||
if not path_matches_patterns(
|
||||
path, parent.config.getini("python_files") + ["__init__.py"]
|
||||
):
|
||||
return
|
||||
ihook = parent.session.gethookproxy(path)
|
||||
return ihook.pytest_pycollect_makemodule(path=path, parent=parent)
|
||||
|
||||
|
||||
def path_matches_patterns(path, patterns):
|
||||
"""Returns True if the given py.path.local matches one of the patterns in the list of globs given"""
|
||||
return any(path.fnmatch(pattern) for pattern in patterns)
|
||||
|
||||
|
||||
def pytest_pycollect_makemodule(path, parent):
|
||||
if path.basename == "__init__.py":
|
||||
return Package(path, parent)
|
||||
return Module(path, parent)
|
||||
|
||||
|
||||
def pytest_ignore_collect(path, config):
|
||||
# Skip duplicate packages.
|
||||
keepduplicates = config.getoption("keepduplicates")
|
||||
if keepduplicates:
|
||||
duplicate_paths = config.pluginmanager._duplicatepaths
|
||||
if path.basename == "__init__.py":
|
||||
if path in duplicate_paths:
|
||||
return True
|
||||
else:
|
||||
duplicate_paths.add(path)
|
||||
|
||||
|
||||
@hookimpl(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem(collector, name, obj):
|
||||
outcome = yield
|
||||
@@ -554,14 +547,12 @@ class Package(Module):
|
||||
self.name = fspath.dirname
|
||||
self.trace = session.trace
|
||||
self._norecursepatterns = session._norecursepatterns
|
||||
for path in list(session.config.pluginmanager._duplicatepaths):
|
||||
if path.dirname == fspath.dirname and path != fspath:
|
||||
session.config.pluginmanager._duplicatepaths.remove(path)
|
||||
self.fspath = fspath
|
||||
|
||||
def _recurse(self, path):
|
||||
ihook = self.gethookproxy(path.dirpath())
|
||||
if ihook.pytest_ignore_collect(path=path, config=self.config):
|
||||
return
|
||||
return False
|
||||
for pat in self._norecursepatterns:
|
||||
if path.check(fnmatch=pat):
|
||||
return False
|
||||
@@ -594,15 +585,43 @@ class Package(Module):
|
||||
return path in self.session._initialpaths
|
||||
|
||||
def collect(self):
|
||||
path = self.fspath.dirpath()
|
||||
pkg_prefix = None
|
||||
for path in path.visit(fil=lambda x: 1, rec=self._recurse, bf=True, sort=True):
|
||||
if pkg_prefix and pkg_prefix in path.parts():
|
||||
# XXX: HACK!
|
||||
# Before starting to collect any files from this package we need
|
||||
# to cleanup the duplicate paths added by the session's collect().
|
||||
# Proper fix is to not track these as duplicates in the first place.
|
||||
for path in list(self.session.config.pluginmanager._duplicatepaths):
|
||||
# if path.parts()[:len(self.fspath.dirpath().parts())] == self.fspath.dirpath().parts():
|
||||
if path.dirname.startswith(self.name):
|
||||
self.session.config.pluginmanager._duplicatepaths.remove(path)
|
||||
|
||||
this_path = self.fspath.dirpath()
|
||||
init_module = this_path.join("__init__.py")
|
||||
if init_module.check(file=1) and path_matches_patterns(
|
||||
init_module, self.config.getini("python_files")
|
||||
):
|
||||
yield Module(init_module, self)
|
||||
pkg_prefixes = set()
|
||||
for path in this_path.visit(rec=self._recurse, bf=True, sort=True):
|
||||
# we will visit our own __init__.py file, in which case we skip it
|
||||
skip = False
|
||||
if path.basename == "__init__.py" and path.dirpath() == this_path:
|
||||
continue
|
||||
|
||||
for pkg_prefix in pkg_prefixes:
|
||||
if (
|
||||
pkg_prefix in path.parts()
|
||||
and pkg_prefix.join("__init__.py") != path
|
||||
):
|
||||
skip = True
|
||||
|
||||
if skip:
|
||||
continue
|
||||
|
||||
if path.isdir() and path.join("__init__.py").check(file=1):
|
||||
pkg_prefixes.add(path)
|
||||
|
||||
for x in self._collectfile(path):
|
||||
yield x
|
||||
if isinstance(x, Package):
|
||||
pkg_prefix = path.dirpath()
|
||||
|
||||
|
||||
def _get_xunit_setup_teardown(holder, attr_name, param_obj=None):
|
||||
@@ -743,7 +762,7 @@ class FunctionMixin(PyobjMixin):
|
||||
def _repr_failure_py(self, excinfo, style="long"):
|
||||
if excinfo.errisinstance(fail.Exception):
|
||||
if not excinfo.value.pytrace:
|
||||
return py._builtin._totext(excinfo.value)
|
||||
return six.text_type(excinfo.value)
|
||||
return super(FunctionMixin, self)._repr_failure_py(excinfo, style=style)
|
||||
|
||||
def repr_failure(self, excinfo, outerr=None):
|
||||
@@ -880,12 +899,13 @@ class Metafunc(fixtures.FuncargnamesCompatAttr):
|
||||
"""
|
||||
|
||||
def __init__(self, definition, fixtureinfo, config, cls=None, module=None):
|
||||
#: access to the :class:`_pytest.config.Config` object for the test session
|
||||
assert (
|
||||
isinstance(definition, FunctionDefinition)
|
||||
or type(definition).__name__ == "DefinitionMock"
|
||||
)
|
||||
self.definition = definition
|
||||
|
||||
#: access to the :class:`_pytest.config.Config` object for the test session
|
||||
self.config = config
|
||||
|
||||
#: the module object where the test function is defined in.
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import math
|
||||
import pprint
|
||||
import sys
|
||||
from numbers import Number
|
||||
from decimal import Decimal
|
||||
|
||||
import py
|
||||
import six
|
||||
from six.moves import zip, filterfalse
|
||||
from more_itertools.more import always_iterable
|
||||
|
||||
@@ -30,6 +33,15 @@ def _cmp_raises_type_error(self, other):
|
||||
)
|
||||
|
||||
|
||||
def _non_numeric_type_error(value, at):
|
||||
at_str = " at {}".format(at) if at else ""
|
||||
return TypeError(
|
||||
"cannot make approximate comparisons to non-numeric values: {!r} {}".format(
|
||||
value, at_str
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
# builtin pytest.approx helper
|
||||
|
||||
|
||||
@@ -39,15 +51,17 @@ class ApproxBase(object):
|
||||
or sequences of numbers.
|
||||
"""
|
||||
|
||||
# Tell numpy to use our `__eq__` operator instead of its
|
||||
# Tell numpy to use our `__eq__` operator instead of its.
|
||||
__array_ufunc__ = None
|
||||
__array_priority__ = 100
|
||||
|
||||
def __init__(self, expected, rel=None, abs=None, nan_ok=False):
|
||||
__tracebackhide__ = True
|
||||
self.expected = expected
|
||||
self.abs = abs
|
||||
self.rel = rel
|
||||
self.nan_ok = nan_ok
|
||||
self._check_type()
|
||||
|
||||
def __repr__(self):
|
||||
raise NotImplementedError
|
||||
@@ -75,21 +89,32 @@ class ApproxBase(object):
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _check_type(self):
|
||||
"""
|
||||
Raise a TypeError if the expected value is not a valid type.
|
||||
"""
|
||||
# This is only a concern if the expected value is a sequence. In every
|
||||
# other case, the approx() function ensures that the expected value has
|
||||
# a numeric type. For this reason, the default is to do nothing. The
|
||||
# classes that deal with sequences should reimplement this method to
|
||||
# raise if there are any non-numeric elements in the sequence.
|
||||
pass
|
||||
|
||||
|
||||
def _recursive_list_map(f, x):
|
||||
if isinstance(x, list):
|
||||
return list(_recursive_list_map(f, xi) for xi in x)
|
||||
else:
|
||||
return f(x)
|
||||
|
||||
|
||||
class ApproxNumpy(ApproxBase):
|
||||
"""
|
||||
Perform approximate comparisons for numpy arrays.
|
||||
Perform approximate comparisons where the expected value is numpy array.
|
||||
"""
|
||||
|
||||
def __repr__(self):
|
||||
# It might be nice to rewrite this function to account for the
|
||||
# shape of the array...
|
||||
import numpy as np
|
||||
|
||||
list_scalars = []
|
||||
for x in np.ndindex(self.expected.shape):
|
||||
list_scalars.append(self._approx_scalar(np.asscalar(self.expected[x])))
|
||||
|
||||
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
|
||||
return "approx({!r})".format(list_scalars)
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
@@ -128,8 +153,8 @@ class ApproxNumpy(ApproxBase):
|
||||
|
||||
class ApproxMapping(ApproxBase):
|
||||
"""
|
||||
Perform approximate comparisons for mappings where the values are numbers
|
||||
(the keys can be anything).
|
||||
Perform approximate comparisons where the expected value is a mapping with
|
||||
numeric values (the keys can be anything).
|
||||
"""
|
||||
|
||||
def __repr__(self):
|
||||
@@ -147,10 +172,20 @@ class ApproxMapping(ApproxBase):
|
||||
for k in self.expected.keys():
|
||||
yield actual[k], self.expected[k]
|
||||
|
||||
def _check_type(self):
|
||||
__tracebackhide__ = True
|
||||
for key, value in self.expected.items():
|
||||
if isinstance(value, type(self.expected)):
|
||||
msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}"
|
||||
raise TypeError(msg.format(key, value, pprint.pformat(self.expected)))
|
||||
elif not isinstance(value, Number):
|
||||
raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
|
||||
|
||||
|
||||
class ApproxSequence(ApproxBase):
|
||||
"""
|
||||
Perform approximate comparisons for sequences of numbers.
|
||||
Perform approximate comparisons where the expected value is a sequence of
|
||||
numbers.
|
||||
"""
|
||||
|
||||
def __repr__(self):
|
||||
@@ -169,10 +204,21 @@ class ApproxSequence(ApproxBase):
|
||||
def _yield_comparisons(self, actual):
|
||||
return zip(actual, self.expected)
|
||||
|
||||
def _check_type(self):
|
||||
__tracebackhide__ = True
|
||||
for index, x in enumerate(self.expected):
|
||||
if isinstance(x, type(self.expected)):
|
||||
msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}"
|
||||
raise TypeError(msg.format(x, index, pprint.pformat(self.expected)))
|
||||
elif not isinstance(x, Number):
|
||||
raise _non_numeric_type_error(
|
||||
self.expected, at="index {}".format(index)
|
||||
)
|
||||
|
||||
|
||||
class ApproxScalar(ApproxBase):
|
||||
"""
|
||||
Perform approximate comparisons for single numbers only.
|
||||
Perform approximate comparisons where the expected value is a single number.
|
||||
"""
|
||||
|
||||
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
|
||||
@@ -211,7 +257,9 @@ class ApproxScalar(ApproxBase):
|
||||
the pre-specified tolerance.
|
||||
"""
|
||||
if _is_numpy_array(actual):
|
||||
return all(a == self for a in actual.flat)
|
||||
# Call ``__eq__()`` manually to prevent infinite-recursion with
|
||||
# numpy<1.13. See #3748.
|
||||
return all(self.__eq__(a) for a in actual.flat)
|
||||
|
||||
# Short-circuit exact equality.
|
||||
if actual == self.expected:
|
||||
@@ -286,7 +334,9 @@ class ApproxScalar(ApproxBase):
|
||||
|
||||
|
||||
class ApproxDecimal(ApproxScalar):
|
||||
from decimal import Decimal
|
||||
"""
|
||||
Perform approximate comparisons where the expected value is a decimal.
|
||||
"""
|
||||
|
||||
DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
|
||||
DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
|
||||
@@ -445,32 +495,35 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
|
||||
__ https://docs.python.org/3/reference/datamodel.html#object.__ge__
|
||||
"""
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
# Delegate the comparison to a class that knows how to deal with the type
|
||||
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
|
||||
#
|
||||
# This architecture is really driven by the need to support numpy arrays.
|
||||
# The only way to override `==` for arrays without requiring that approx be
|
||||
# the left operand is to inherit the approx object from `numpy.ndarray`.
|
||||
# But that can't be a general solution, because it requires (1) numpy to be
|
||||
# installed and (2) the expected value to be a numpy array. So the general
|
||||
# solution is to delegate each type of expected value to a different class.
|
||||
# The primary responsibility of these classes is to implement ``__eq__()``
|
||||
# and ``__repr__()``. The former is used to actually check if some
|
||||
# "actual" value is equivalent to the given expected value within the
|
||||
# allowed tolerance. The latter is used to show the user the expected
|
||||
# value and tolerance, in the case that a test failed.
|
||||
#
|
||||
# This has the advantage that it made it easy to support mapping types
|
||||
# (i.e. dict). The old code accepted mapping types, but would only compare
|
||||
# their keys, which is probably not what most people would expect.
|
||||
# The actual logic for making approximate comparisons can be found in
|
||||
# ApproxScalar, which is used to compare individual numbers. All of the
|
||||
# other Approx classes eventually delegate to this class. The ApproxBase
|
||||
# class provides some convenient methods and overloads, but isn't really
|
||||
# essential.
|
||||
|
||||
if _is_numpy_array(expected):
|
||||
cls = ApproxNumpy
|
||||
__tracebackhide__ = True
|
||||
|
||||
if isinstance(expected, Decimal):
|
||||
cls = ApproxDecimal
|
||||
elif isinstance(expected, Number):
|
||||
cls = ApproxScalar
|
||||
elif isinstance(expected, Mapping):
|
||||
cls = ApproxMapping
|
||||
elif isinstance(expected, Sequence) and not isinstance(expected, STRING_TYPES):
|
||||
cls = ApproxSequence
|
||||
elif isinstance(expected, Decimal):
|
||||
cls = ApproxDecimal
|
||||
elif _is_numpy_array(expected):
|
||||
cls = ApproxNumpy
|
||||
else:
|
||||
cls = ApproxScalar
|
||||
raise _non_numeric_type_error(expected, at=None)
|
||||
|
||||
return cls(expected, rel, abs, nan_ok)
|
||||
|
||||
@@ -480,17 +533,11 @@ def _is_numpy_array(obj):
|
||||
Return true if the given object is a numpy array. Make a special effort to
|
||||
avoid importing numpy unless it's really necessary.
|
||||
"""
|
||||
import inspect
|
||||
|
||||
for cls in inspect.getmro(type(obj)):
|
||||
if cls.__module__ == "numpy":
|
||||
try:
|
||||
import numpy as np
|
||||
|
||||
return isinstance(obj, np.ndarray)
|
||||
except ImportError:
|
||||
pass
|
||||
import sys
|
||||
|
||||
np = sys.modules.get("numpy")
|
||||
if np is not None:
|
||||
return isinstance(obj, np.ndarray)
|
||||
return False
|
||||
|
||||
|
||||
@@ -633,8 +680,8 @@ def raises(expected_exception, *args, **kwargs):
|
||||
# print "raises frame scope: %r" % frame.f_locals
|
||||
try:
|
||||
code = _pytest._code.Source(code).compile()
|
||||
py.builtin.exec_(code, frame.f_globals, loc)
|
||||
# XXX didn'T mean f_globals == f_locals something special?
|
||||
six.exec_(code, frame.f_globals, loc)
|
||||
# XXX didn't mean f_globals == f_locals something special?
|
||||
# this is destroyed here ...
|
||||
except expected_exception:
|
||||
return _pytest._code.ExceptionInfo()
|
||||
|
||||
@@ -4,11 +4,11 @@ from __future__ import absolute_import, division, print_function
|
||||
import inspect
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import re
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
import re
|
||||
import six
|
||||
|
||||
from _pytest.fixtures import yield_fixture
|
||||
from _pytest.outcomes import fail
|
||||
@@ -130,7 +130,7 @@ def warns(expected_warning, *args, **kwargs):
|
||||
|
||||
with WarningsChecker(expected_warning, match_expr=match_expr):
|
||||
code = _pytest._code.Source(code).compile()
|
||||
py.builtin.exec_(code, frame.f_globals, loc)
|
||||
six.exec_(code, frame.f_globals, loc)
|
||||
else:
|
||||
func = args[0]
|
||||
with WarningsChecker(expected_warning, match_expr=match_expr):
|
||||
|
||||
@@ -6,7 +6,7 @@ import os
|
||||
import sys
|
||||
from time import time
|
||||
|
||||
import py
|
||||
import six
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
from _pytest.outcomes import skip, Skipped, TEST_OUTCOME
|
||||
|
||||
@@ -317,7 +317,7 @@ class SetupState(object):
|
||||
if exc is None:
|
||||
exc = sys.exc_info()
|
||||
if exc:
|
||||
py.builtin._reraise(*exc)
|
||||
six.reraise(*exc)
|
||||
|
||||
def _teardown_with_finalization(self, colitem):
|
||||
self._callfinalizers(colitem)
|
||||
@@ -352,7 +352,7 @@ class SetupState(object):
|
||||
if exc is None:
|
||||
exc = sys.exc_info()
|
||||
if exc:
|
||||
py.builtin._reraise(*exc)
|
||||
six.reraise(*exc)
|
||||
|
||||
def prepare(self, colitem):
|
||||
""" setup objects along the collector chain to the test-method
|
||||
@@ -363,7 +363,7 @@ class SetupState(object):
|
||||
# check if the last collection node has raised an error
|
||||
for col in self.stack:
|
||||
if hasattr(col, "_prepare_exc"):
|
||||
py.builtin._reraise(*col._prepare_exc)
|
||||
six.reraise(*col._prepare_exc)
|
||||
for col in needed_collectors[len(self.stack) :]:
|
||||
self.stack.append(col)
|
||||
try:
|
||||
|
||||
@@ -51,7 +51,8 @@ def _show_fixture_action(fixturedef, msg):
|
||||
config = fixturedef._fixturemanager.config
|
||||
capman = config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
out, err = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
out, err = capman.read_global_capture()
|
||||
|
||||
tw = config.get_terminal_writer()
|
||||
tw.line()
|
||||
|
||||
@@ -51,7 +51,7 @@ def pytest_configure(config):
|
||||
"results in a True value. Evaluation happens within the "
|
||||
"module global context. Example: skipif('sys.platform == \"win32\"') "
|
||||
"skips the test if we are on the win32 platform. see "
|
||||
"http://pytest.org/latest/skipping.html",
|
||||
"https://docs.pytest.org/en/latest/skipping.html",
|
||||
)
|
||||
config.addinivalue_line(
|
||||
"markers",
|
||||
@@ -61,7 +61,7 @@ def pytest_configure(config):
|
||||
"and run=False if you don't even want to execute the test function. "
|
||||
"If only specific exception(s) are expected, you can list them in "
|
||||
"raises, and if the test fails in other ways, it will be reported as "
|
||||
"a true failure. See http://pytest.org/latest/skipping.html",
|
||||
"a true failure. See https://docs.pytest.org/en/latest/skipping.html",
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -263,7 +263,7 @@ class TerminalReporter(object):
|
||||
def write_fspath_result(self, nodeid, res):
|
||||
fspath = self.config.rootdir.join(nodeid.split("::")[0])
|
||||
if fspath != self.currentfspath:
|
||||
if self.currentfspath is not None:
|
||||
if self.currentfspath is not None and self._show_progress_info:
|
||||
self._write_progress_information_filling_space()
|
||||
self.currentfspath = fspath
|
||||
fspath = self.startdir.bestrelpath(fspath)
|
||||
@@ -358,12 +358,12 @@ class TerminalReporter(object):
|
||||
def pytest_runtest_logreport(self, report):
|
||||
rep = report
|
||||
res = self.config.hook.pytest_report_teststatus(report=rep)
|
||||
cat, letter, word = res
|
||||
category, letter, word = res
|
||||
if isinstance(word, tuple):
|
||||
word, markup = word
|
||||
else:
|
||||
markup = None
|
||||
self.stats.setdefault(cat, []).append(rep)
|
||||
self.stats.setdefault(category, []).append(rep)
|
||||
self._tests_ran = True
|
||||
if not letter and not word:
|
||||
# probably passed setup/teardown
|
||||
@@ -691,7 +691,7 @@ class TerminalReporter(object):
|
||||
indented = "\n".join(" " + x for x in lines)
|
||||
self._tw.line(indented)
|
||||
self._tw.line()
|
||||
self._tw.line("-- Docs: http://doc.pytest.org/en/latest/warnings.html")
|
||||
self._tw.line("-- Docs: https://docs.pytest.org/en/latest/warnings.html")
|
||||
|
||||
def summary_passes(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
@@ -706,7 +706,12 @@ class TerminalReporter(object):
|
||||
self._outrep_summary(rep)
|
||||
|
||||
def print_teardown_sections(self, rep):
|
||||
showcapture = self.config.option.showcapture
|
||||
if showcapture == "no":
|
||||
return
|
||||
for secname, content in rep.sections:
|
||||
if showcapture != "all" and showcapture not in secname:
|
||||
continue
|
||||
if "teardown" in secname:
|
||||
self._tw.sep("-", secname)
|
||||
if content[-1:] == "\n":
|
||||
|
||||
@@ -69,6 +69,7 @@ class UnitTestCase(Class):
|
||||
class TestCaseFunction(Function):
|
||||
nofuncargs = True
|
||||
_excinfo = None
|
||||
_testcase = None
|
||||
|
||||
def setup(self):
|
||||
self._testcase = self.parent.obj(self.name)
|
||||
|
||||
@@ -49,6 +49,14 @@ def pytest_addoption(parser):
|
||||
)
|
||||
|
||||
|
||||
def pytest_configure(config):
|
||||
config.addinivalue_line(
|
||||
"markers",
|
||||
"filterwarnings(warning): add a warning filter to the given test. "
|
||||
"see https://docs.pytest.org/en/latest/warnings.html#pytest-mark-filterwarnings ",
|
||||
)
|
||||
|
||||
|
||||
@contextmanager
|
||||
def catch_warnings_for_item(item):
|
||||
"""
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import types
|
||||
|
||||
import six
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR
|
||||
@@ -131,7 +131,7 @@ class TestGeneralUsage(object):
|
||||
p2 = testdir.makefile(".pyc", "123")
|
||||
result = testdir.runpytest(p1, p2)
|
||||
assert result.ret
|
||||
result.stderr.fnmatch_lines(["*ERROR: not found:*%s" % (p2.basename,)])
|
||||
result.stderr.fnmatch_lines(["*ERROR: not found:*{}".format(p2.basename)])
|
||||
|
||||
def test_issue486_better_reporting_on_conftest_load_failure(self, testdir):
|
||||
testdir.makepyfile("")
|
||||
@@ -201,16 +201,16 @@ class TestGeneralUsage(object):
|
||||
testdir.tmpdir.join("py").mksymlinkto(py._pydir)
|
||||
p = testdir.tmpdir.join("main.py")
|
||||
p.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import sys, os
|
||||
sys.path.insert(0, '')
|
||||
import py
|
||||
print(py.__file__)
|
||||
print(py.__path__)
|
||||
os.chdir(os.path.dirname(os.getcwd()))
|
||||
print(py.log)
|
||||
"""
|
||||
import sys, os
|
||||
sys.path.insert(0, '')
|
||||
import py
|
||||
print (py.__file__)
|
||||
print (py.__path__)
|
||||
os.chdir(os.path.dirname(os.getcwd()))
|
||||
print (py.log)
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpython(p)
|
||||
@@ -453,7 +453,7 @@ class TestInvocationVariants(object):
|
||||
@pytest.mark.xfail("sys.platform.startswith('java')")
|
||||
def test_pydoc(self, testdir):
|
||||
for name in ("py.test", "pytest"):
|
||||
result = testdir.runpython_c("import %s;help(%s)" % (name, name))
|
||||
result = testdir.runpython_c("import {};help({})".format(name, name))
|
||||
assert result.ret == 0
|
||||
s = result.stdout.str()
|
||||
assert "MarkGenerator" in s
|
||||
@@ -660,6 +660,16 @@ class TestInvocationVariants(object):
|
||||
["*test_world.py::test_other*PASSED*", "*1 passed*"]
|
||||
)
|
||||
|
||||
def test_invoke_test_and_doctestmodules(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
def test():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(str(p) + "::test", "--doctest-modules")
|
||||
result.stdout.fnmatch_lines(["*1 passed*"])
|
||||
|
||||
@pytest.mark.skipif(not hasattr(os, "symlink"), reason="requires symlinks")
|
||||
def test_cmdline_python_package_symlink(self, testdir, monkeypatch):
|
||||
"""
|
||||
@@ -826,7 +836,7 @@ class TestDurations(object):
|
||||
if ("test_%s" % x) in line and y in line:
|
||||
break
|
||||
else:
|
||||
raise AssertionError("not found %s %s" % (x, y))
|
||||
raise AssertionError("not found {} {}".format(x, y))
|
||||
|
||||
def test_with_deselected(self, testdir):
|
||||
testdir.makepyfile(self.source)
|
||||
@@ -1044,3 +1054,10 @@ def test_frame_leak_on_failing_test(testdir):
|
||||
)
|
||||
result = testdir.runpytest_subprocess()
|
||||
result.stdout.fnmatch_lines(["*1 failed, 1 passed in*"])
|
||||
|
||||
|
||||
def test_fixture_mock_integration(testdir):
|
||||
"""Test that decorators applied to fixture are left working (#3774)"""
|
||||
p = testdir.copy_example("acceptance/fixture_mock_integration.py")
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines("*1 passed*")
|
||||
|
||||
@@ -3,8 +3,8 @@ from __future__ import absolute_import, division, print_function
|
||||
import sys
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
import mock
|
||||
from test_excinfo import TWMock
|
||||
from six import text_type
|
||||
|
||||
@@ -32,10 +32,8 @@ def test_code_with_class():
|
||||
pytest.raises(TypeError, "_pytest._code.Code(A)")
|
||||
|
||||
|
||||
if True:
|
||||
|
||||
def x():
|
||||
pass
|
||||
def x():
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
def test_code_fullsource():
|
||||
@@ -48,7 +46,7 @@ def test_code_source():
|
||||
code = _pytest._code.Code(x)
|
||||
src = code.source()
|
||||
expected = """def x():
|
||||
pass"""
|
||||
raise NotImplementedError()"""
|
||||
assert str(src) == expected
|
||||
|
||||
|
||||
@@ -68,12 +66,8 @@ def test_getstatement_empty_fullsource():
|
||||
|
||||
f = func()
|
||||
f = _pytest._code.Frame(f)
|
||||
prop = f.code.__class__.fullsource
|
||||
try:
|
||||
f.code.__class__.fullsource = None
|
||||
assert f.statement == _pytest._code.Source("")
|
||||
finally:
|
||||
f.code.__class__.fullsource = prop
|
||||
with mock.patch.object(f.code.__class__, "fullsource", None):
|
||||
assert f.statement == ""
|
||||
|
||||
|
||||
def test_code_from_func():
|
||||
@@ -83,20 +77,20 @@ def test_code_from_func():
|
||||
|
||||
|
||||
def test_unicode_handling():
|
||||
value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8")
|
||||
value = u"ąć".encode("UTF-8")
|
||||
|
||||
def f():
|
||||
raise Exception(value)
|
||||
|
||||
excinfo = pytest.raises(Exception, f)
|
||||
str(excinfo)
|
||||
if sys.version_info[0] < 3:
|
||||
text_type(excinfo)
|
||||
text_type(excinfo)
|
||||
if sys.version_info < (3,):
|
||||
bytes(excinfo)
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info[0] >= 3, reason="python 2 only issue")
|
||||
def test_unicode_handling_syntax_error():
|
||||
value = py.builtin._totext("\xc4\x85\xc4\x87\n", "utf-8").encode("utf8")
|
||||
value = u"ąć".encode("UTF-8")
|
||||
|
||||
def f():
|
||||
raise SyntaxError("invalid syntax", (None, 1, 3, value))
|
||||
@@ -109,25 +103,25 @@ def test_unicode_handling_syntax_error():
|
||||
|
||||
def test_code_getargs():
|
||||
def f1(x):
|
||||
pass
|
||||
raise NotImplementedError()
|
||||
|
||||
c1 = _pytest._code.Code(f1)
|
||||
assert c1.getargs(var=True) == ("x",)
|
||||
|
||||
def f2(x, *y):
|
||||
pass
|
||||
raise NotImplementedError()
|
||||
|
||||
c2 = _pytest._code.Code(f2)
|
||||
assert c2.getargs(var=True) == ("x", "y")
|
||||
|
||||
def f3(x, **z):
|
||||
pass
|
||||
raise NotImplementedError()
|
||||
|
||||
c3 = _pytest._code.Code(f3)
|
||||
assert c3.getargs(var=True) == ("x", "z")
|
||||
|
||||
def f4(x, *y, **z):
|
||||
pass
|
||||
raise NotImplementedError()
|
||||
|
||||
c4 = _pytest._code.Code(f4)
|
||||
assert c4.getargs(var=True) == ("x", "y", "z")
|
||||
@@ -192,11 +186,14 @@ class TestReprFuncArgs(object):
|
||||
|
||||
tw = TWMock()
|
||||
|
||||
args = [("unicode_string", u"São Paulo"), ("utf8_string", "S\xc3\xa3o Paulo")]
|
||||
args = [("unicode_string", u"São Paulo"), ("utf8_string", b"S\xc3\xa3o Paulo")]
|
||||
|
||||
r = ReprFuncArgs(args)
|
||||
r.toterminal(tw)
|
||||
if sys.version_info[0] >= 3:
|
||||
assert tw.lines[0] == "unicode_string = São Paulo, utf8_string = São Paulo"
|
||||
assert (
|
||||
tw.lines[0]
|
||||
== r"unicode_string = São Paulo, utf8_string = b'S\xc3\xa3o Paulo'"
|
||||
)
|
||||
else:
|
||||
assert tw.lines[0] == "unicode_string = São Paulo, utf8_string = São Paulo"
|
||||
|
||||
@@ -4,9 +4,11 @@ from __future__ import absolute_import, division, print_function
|
||||
import operator
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
import _pytest
|
||||
import py
|
||||
import pytest
|
||||
import six
|
||||
from _pytest._code.code import (
|
||||
ExceptionInfo,
|
||||
FormattedExcinfo,
|
||||
@@ -147,7 +149,7 @@ class TestTraceback_f_g_h(object):
|
||||
except somenoname:
|
||||
pass
|
||||
xyz()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
try:
|
||||
exec(source.compile())
|
||||
@@ -250,7 +252,7 @@ class TestTraceback_f_g_h(object):
|
||||
import sys
|
||||
|
||||
exc, val, tb = sys.exc_info()
|
||||
py.builtin._reraise(exc, val, tb)
|
||||
six.reraise(exc, val, tb)
|
||||
|
||||
def f(n):
|
||||
try:
|
||||
@@ -268,7 +270,7 @@ class TestTraceback_f_g_h(object):
|
||||
decorator = pytest.importorskip("decorator").decorator
|
||||
|
||||
def log(f, *k, **kw):
|
||||
print("%s %s" % (k, kw))
|
||||
print("{} {}".format(k, kw))
|
||||
f(*k, **kw)
|
||||
|
||||
log = decorator(log)
|
||||
@@ -424,7 +426,7 @@ class TestFormattedExcinfo(object):
|
||||
@pytest.fixture
|
||||
def importasmod(self, request):
|
||||
def importasmod(source):
|
||||
source = _pytest._code.Source(source)
|
||||
source = textwrap.dedent(source)
|
||||
tmpdir = request.getfixturevalue("tmpdir")
|
||||
modpath = tmpdir.join("mod.py")
|
||||
tmpdir.ensure("__init__.py")
|
||||
@@ -448,10 +450,10 @@ class TestFormattedExcinfo(object):
|
||||
def test_repr_source(self):
|
||||
pr = FormattedExcinfo()
|
||||
source = _pytest._code.Source(
|
||||
"""
|
||||
"""\
|
||||
def f(x):
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
).strip()
|
||||
pr.flow_marker = "|"
|
||||
lines = pr.get_source(source, 0)
|
||||
@@ -883,10 +885,10 @@ raise ValueError()
|
||||
|
||||
class MyRepr(TerminalRepr):
|
||||
def toterminal(self, tw):
|
||||
tw.line(py.builtin._totext("я", "utf-8"))
|
||||
tw.line(u"я")
|
||||
|
||||
x = py.builtin._totext(MyRepr())
|
||||
assert x == py.builtin._totext("я", "utf-8")
|
||||
x = six.text_type(MyRepr())
|
||||
assert x == u"я"
|
||||
|
||||
def test_toterminal_long(self, importasmod):
|
||||
mod = importasmod(
|
||||
@@ -1265,6 +1267,50 @@ raise ValueError()
|
||||
]
|
||||
)
|
||||
|
||||
@pytest.mark.skipif("sys.version_info[0] < 3")
|
||||
def test_exc_chain_repr_cycle(self, importasmod):
|
||||
mod = importasmod(
|
||||
"""
|
||||
class Err(Exception):
|
||||
pass
|
||||
def fail():
|
||||
return 0 / 0
|
||||
def reraise():
|
||||
try:
|
||||
fail()
|
||||
except ZeroDivisionError as e:
|
||||
raise Err() from e
|
||||
def unreraise():
|
||||
try:
|
||||
reraise()
|
||||
except Err as e:
|
||||
raise e.__cause__
|
||||
"""
|
||||
)
|
||||
excinfo = pytest.raises(ZeroDivisionError, mod.unreraise)
|
||||
r = excinfo.getrepr(style="short")
|
||||
tw = TWMock()
|
||||
r.toterminal(tw)
|
||||
out = "\n".join(line for line in tw.lines if isinstance(line, str))
|
||||
expected_out = textwrap.dedent(
|
||||
"""\
|
||||
:13: in unreraise
|
||||
reraise()
|
||||
:10: in reraise
|
||||
raise Err() from e
|
||||
E test_exc_chain_repr_cycle0.mod.Err
|
||||
|
||||
During handling of the above exception, another exception occurred:
|
||||
:15: in unreraise
|
||||
raise e.__cause__
|
||||
:8: in reraise
|
||||
fail()
|
||||
:5: in fail
|
||||
return 0 / 0
|
||||
E ZeroDivisionError: division by zero"""
|
||||
)
|
||||
assert out == expected_out
|
||||
|
||||
|
||||
@pytest.mark.parametrize("style", ["short", "long"])
|
||||
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# flake8: noqa
|
||||
# disable flake check on this file because some constructs are strange
|
||||
# or redundant on purpose and can't be disable on a line-by-line basis
|
||||
@@ -6,8 +7,8 @@ import inspect
|
||||
import sys
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
import six
|
||||
from _pytest._code import Source
|
||||
from _pytest._code.source import ast
|
||||
|
||||
@@ -41,15 +42,11 @@ def test_source_str_function():
|
||||
|
||||
|
||||
def test_unicode():
|
||||
try:
|
||||
unicode
|
||||
except NameError:
|
||||
return
|
||||
x = Source(unicode("4"))
|
||||
x = Source(u"4")
|
||||
assert str(x) == "4"
|
||||
co = _pytest._code.compile(unicode('u"\xc3\xa5"', "utf8"), mode="eval")
|
||||
co = _pytest._code.compile(u'u"å"', mode="eval")
|
||||
val = eval(co)
|
||||
assert isinstance(val, unicode)
|
||||
assert isinstance(val, six.text_type)
|
||||
|
||||
|
||||
def test_source_from_function():
|
||||
@@ -323,7 +320,7 @@ class TestSourceParsingAndCompiling(object):
|
||||
|
||||
def test_compile_and_getsource(self):
|
||||
co = self.source.compile()
|
||||
py.builtin.exec_(co, globals())
|
||||
six.exec_(co, globals())
|
||||
f(7)
|
||||
excinfo = pytest.raises(AssertionError, "f(6)")
|
||||
frame = excinfo.traceback[-1].frame
|
||||
@@ -392,7 +389,7 @@ def test_getfuncsource_dynamic():
|
||||
def g(): pass
|
||||
"""
|
||||
co = _pytest._code.compile(source)
|
||||
py.builtin.exec_(co, globals())
|
||||
six.exec_(co, globals())
|
||||
assert str(_pytest._code.Source(f)).strip() == "def f():\n raise ValueError"
|
||||
assert str(_pytest._code.Source(g)).strip() == "def g(): pass"
|
||||
|
||||
@@ -632,7 +629,7 @@ def test_issue55():
|
||||
assert str(s) == ' round_trip("""\n""")'
|
||||
|
||||
|
||||
def XXXtest_multiline():
|
||||
def test_multiline():
|
||||
source = getstatement(
|
||||
0,
|
||||
"""\
|
||||
|
||||
@@ -116,7 +116,7 @@ def test_resultlog_is_deprecated(testdir):
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*--result-log is deprecated and scheduled for removal in pytest 4.0*",
|
||||
"*See https://docs.pytest.org/*/usage.html#creating-resultlog-format-files for more information*",
|
||||
"*See https://docs.pytest.org/en/latest/usage.html#creating-resultlog-format-files for more information*",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -267,7 +267,6 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives(
|
||||
)
|
||||
|
||||
|
||||
# @pytest.mark.skipif(six.PY2, reason="We issue the warning in Python 3 only")
|
||||
def test_call_fixture_function_deprecated():
|
||||
"""Check if a warning is raised if a fixture function is called directly (#3661)"""
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Reproduces issue #3774"""
|
||||
|
||||
try:
|
||||
import mock
|
||||
except ImportError:
|
||||
import unittest.mock as mock
|
||||
|
||||
import pytest
|
||||
|
||||
config = {"mykey": "ORIGINAL"}
|
||||
|
||||
|
||||
@pytest.fixture(scope="function")
|
||||
@mock.patch.dict(config, {"mykey": "MOCKED"})
|
||||
def my_fixture():
|
||||
return config["mykey"]
|
||||
|
||||
|
||||
def test_foobar(my_fixture):
|
||||
assert my_fixture == "MOCKED"
|
||||
@@ -0,0 +1,2 @@
|
||||
[pytest]
|
||||
python_files = *.py
|
||||
@@ -0,0 +1,2 @@
|
||||
def test_init():
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
def test_foo():
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
def pytest_ignore_collect(path):
|
||||
return False
|
||||
@@ -0,0 +1,2 @@
|
||||
def test():
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
class pytest_something(object):
|
||||
pass
|
||||
@@ -0,0 +1,2 @@
|
||||
def test_foo():
|
||||
pass
|
||||
10
testing/example_scripts/fixtures/custom_item/conftest.py
Normal file
10
testing/example_scripts/fixtures/custom_item/conftest.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import pytest
|
||||
|
||||
|
||||
class CustomItem(pytest.Item, pytest.File):
|
||||
def runtest(self):
|
||||
pass
|
||||
|
||||
|
||||
def pytest_collect_file(path, parent):
|
||||
return CustomItem(path, parent)
|
||||
@@ -0,0 +1,2 @@
|
||||
def test():
|
||||
pass
|
||||
@@ -876,22 +876,18 @@ def test_live_logging_suspends_capture(has_capture_manager, request):
|
||||
is installed.
|
||||
"""
|
||||
import logging
|
||||
import contextlib
|
||||
from functools import partial
|
||||
from _pytest.capture import CaptureManager
|
||||
from _pytest.logging import _LiveLoggingStreamHandler
|
||||
|
||||
class MockCaptureManager:
|
||||
calls = []
|
||||
|
||||
def suspend_global_capture(self):
|
||||
self.calls.append("suspend_global_capture")
|
||||
|
||||
def resume_global_capture(self):
|
||||
self.calls.append("resume_global_capture")
|
||||
|
||||
# sanity check
|
||||
assert CaptureManager.suspend_capture_item
|
||||
assert CaptureManager.resume_global_capture
|
||||
@contextlib.contextmanager
|
||||
def global_and_fixture_disabled(self):
|
||||
self.calls.append("enter disabled")
|
||||
yield
|
||||
self.calls.append("exit disabled")
|
||||
|
||||
class DummyTerminal(six.StringIO):
|
||||
def section(self, *args, **kwargs):
|
||||
@@ -908,10 +904,7 @@ def test_live_logging_suspends_capture(has_capture_manager, request):
|
||||
|
||||
logger.critical("some message")
|
||||
if has_capture_manager:
|
||||
assert MockCaptureManager.calls == [
|
||||
"suspend_global_capture",
|
||||
"resume_global_capture",
|
||||
]
|
||||
assert MockCaptureManager.calls == ["enter disabled", "exit disabled"]
|
||||
else:
|
||||
assert MockCaptureManager.calls == []
|
||||
assert out_file.getvalue() == "\nsome message\n"
|
||||
|
||||
@@ -59,17 +59,21 @@ class TestApprox(object):
|
||||
),
|
||||
)
|
||||
|
||||
def test_repr_0d_array(self, plus_minus):
|
||||
@pytest.mark.parametrize(
|
||||
"value, repr_string",
|
||||
[
|
||||
(5., "approx(5.0 {pm} 5.0e-06)"),
|
||||
([5.], "approx([5.0 {pm} 5.0e-06])"),
|
||||
([[5.]], "approx([[5.0 {pm} 5.0e-06]])"),
|
||||
([[5., 6.]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
|
||||
([[5.], [6.]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
|
||||
],
|
||||
)
|
||||
def test_repr_nd_array(self, plus_minus, value, repr_string):
|
||||
"""Make sure that arrays of all different dimensions are repr'd correctly."""
|
||||
np = pytest.importorskip("numpy")
|
||||
np_array = np.array(5.)
|
||||
assert approx(np_array) == 5.0
|
||||
string_expected = "approx([5.0 {} 5.0e-06])".format(plus_minus)
|
||||
|
||||
assert repr(approx(np_array)) == string_expected
|
||||
|
||||
np_array = np.array([5.])
|
||||
assert approx(np_array) == 5.0
|
||||
assert repr(approx(np_array)) == string_expected
|
||||
np_array = np.array(value)
|
||||
assert repr(approx(np_array)) == repr_string.format(pm=plus_minus)
|
||||
|
||||
def test_operator_overloading(self):
|
||||
assert 1 == approx(1, rel=1e-6, abs=1e-12)
|
||||
@@ -439,6 +443,21 @@ class TestApprox(object):
|
||||
["*At index 0 diff: 3 != 4 * {}".format(expected), "=* 1 failed in *="]
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"x",
|
||||
[
|
||||
pytest.param(None),
|
||||
pytest.param("string"),
|
||||
pytest.param(["string"], id="nested-str"),
|
||||
pytest.param([[1]], id="nested-list"),
|
||||
pytest.param({"key": "string"}, id="dict-with-string"),
|
||||
pytest.param({"key": {"key": 1}}, id="nested-dict"),
|
||||
],
|
||||
)
|
||||
def test_expected_value_type_error(self, x):
|
||||
with pytest.raises(TypeError):
|
||||
approx(x)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"op",
|
||||
[
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
import textwrap
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
@@ -47,13 +47,14 @@ class TestModule(object):
|
||||
p = root2.join("test_x456.py")
|
||||
monkeypatch.syspath_prepend(str(root1))
|
||||
p.write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import x456
|
||||
def test():
|
||||
assert x456.__file__.startswith(%r)
|
||||
"""
|
||||
% str(root2)
|
||||
import x456
|
||||
def test():
|
||||
assert x456.__file__.startswith({!r})
|
||||
""".format(
|
||||
str(root2)
|
||||
)
|
||||
)
|
||||
)
|
||||
with root2.as_cwd():
|
||||
@@ -929,23 +930,23 @@ class TestConftestCustomization(object):
|
||||
def test_customized_pymakemodule_issue205_subdir(self, testdir):
|
||||
b = testdir.mkdir("a").mkdir("b")
|
||||
b.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_pycollect_makemodule():
|
||||
outcome = yield
|
||||
mod = outcome.get_result()
|
||||
mod.obj.hello = "world"
|
||||
"""
|
||||
import pytest
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_pycollect_makemodule():
|
||||
outcome = yield
|
||||
mod = outcome.get_result()
|
||||
mod.obj.hello = "world"
|
||||
"""
|
||||
)
|
||||
)
|
||||
b.join("test_module.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_hello():
|
||||
assert hello == "world"
|
||||
"""
|
||||
def test_hello():
|
||||
assert hello == "world"
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
@@ -954,31 +955,31 @@ class TestConftestCustomization(object):
|
||||
def test_customized_pymakeitem(self, testdir):
|
||||
b = testdir.mkdir("a").mkdir("b")
|
||||
b.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem():
|
||||
outcome = yield
|
||||
if outcome.excinfo is None:
|
||||
result = outcome.get_result()
|
||||
if result:
|
||||
for func in result:
|
||||
func._some123 = "world"
|
||||
"""
|
||||
import pytest
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
def pytest_pycollect_makeitem():
|
||||
outcome = yield
|
||||
if outcome.excinfo is None:
|
||||
result = outcome.get_result()
|
||||
if result:
|
||||
for func in result:
|
||||
func._some123 = "world"
|
||||
"""
|
||||
)
|
||||
)
|
||||
b.join("test_module.py").write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pytest
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture()
|
||||
def obj(request):
|
||||
return request.node._some123
|
||||
def test_hello(obj):
|
||||
assert obj == "world"
|
||||
"""
|
||||
@pytest.fixture()
|
||||
def obj(request):
|
||||
return request.node._some123
|
||||
def test_hello(obj):
|
||||
assert obj == "world"
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
@@ -1033,7 +1034,7 @@ class TestConftestCustomization(object):
|
||||
)
|
||||
testdir.makefile(
|
||||
".narf",
|
||||
"""
|
||||
"""\
|
||||
def test_something():
|
||||
assert 1 + 1 == 2""",
|
||||
)
|
||||
@@ -1046,29 +1047,29 @@ def test_setup_only_available_in_subdir(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
"""
|
||||
import pytest
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub1"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub2.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
"""
|
||||
import pytest
|
||||
def pytest_runtest_setup(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_call(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
def pytest_runtest_teardown(item):
|
||||
assert item.fspath.purebasename == "test_in_sub2"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
@@ -1547,12 +1548,12 @@ def test_skip_duplicates_by_default(testdir):
|
||||
a = testdir.mkdir("a")
|
||||
fh = a.join("test_a.py")
|
||||
fh.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
def test_real():
|
||||
pass
|
||||
"""
|
||||
import pytest
|
||||
def test_real():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest(a.strpath, a.strpath)
|
||||
@@ -1567,13 +1568,94 @@ def test_keep_duplicates(testdir):
|
||||
a = testdir.mkdir("a")
|
||||
fh = a.join("test_a.py")
|
||||
fh.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
def test_real():
|
||||
pass
|
||||
"""
|
||||
import pytest
|
||||
def test_real():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--keep-duplicates", a.strpath, a.strpath)
|
||||
result.stdout.fnmatch_lines(["*collected 2 item*"])
|
||||
|
||||
|
||||
def test_package_collection_infinite_recursion(testdir):
|
||||
testdir.copy_example("collect/package_infinite_recursion")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("*1 passed*")
|
||||
|
||||
|
||||
def test_package_with_modules(testdir):
|
||||
"""
|
||||
.
|
||||
└── root
|
||||
├── __init__.py
|
||||
├── sub1
|
||||
│ ├── __init__.py
|
||||
│ └── sub1_1
|
||||
│ ├── __init__.py
|
||||
│ └── test_in_sub1.py
|
||||
└── sub2
|
||||
└── test
|
||||
└── test_in_sub2.py
|
||||
|
||||
"""
|
||||
root = testdir.mkpydir("root")
|
||||
sub1 = root.mkdir("sub1")
|
||||
sub1.ensure("__init__.py")
|
||||
sub1_test = sub1.mkdir("sub1_1")
|
||||
sub1_test.ensure("__init__.py")
|
||||
sub2 = root.mkdir("sub2")
|
||||
sub2_test = sub2.mkdir("sub2")
|
||||
|
||||
sub1_test.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
sub2_test.join("test_in_sub2.py").write("def test_2(): pass")
|
||||
|
||||
# Execute from .
|
||||
result = testdir.runpytest("-v", "-s")
|
||||
result.assert_outcomes(passed=2)
|
||||
|
||||
# Execute from . with one argument "root"
|
||||
result = testdir.runpytest("-v", "-s", "root")
|
||||
result.assert_outcomes(passed=2)
|
||||
|
||||
# Chdir into package's root and execute with no args
|
||||
root.chdir()
|
||||
result = testdir.runpytest("-v", "-s")
|
||||
result.assert_outcomes(passed=2)
|
||||
|
||||
|
||||
def test_package_ordering(testdir):
|
||||
"""
|
||||
.
|
||||
└── root
|
||||
├── Test_root.py
|
||||
├── __init__.py
|
||||
├── sub1
|
||||
│ ├── Test_sub1.py
|
||||
│ └── __init__.py
|
||||
└── sub2
|
||||
└── test
|
||||
└── test_sub2.py
|
||||
|
||||
"""
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
python_files=*.py
|
||||
"""
|
||||
)
|
||||
root = testdir.mkpydir("root")
|
||||
sub1 = root.mkdir("sub1")
|
||||
sub1.ensure("__init__.py")
|
||||
sub2 = root.mkdir("sub2")
|
||||
sub2_test = sub2.mkdir("sub2")
|
||||
|
||||
root.join("Test_root.py").write("def test_1(): pass")
|
||||
sub1.join("Test_sub1.py").write("def test_2(): pass")
|
||||
sub2_test.join("test_sub2.py").write("def test_3(): pass")
|
||||
|
||||
# Execute from .
|
||||
result = testdir.runpytest("-v", "-s")
|
||||
result.assert_outcomes(passed=3)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from textwrap import dedent
|
||||
# -*- coding: utf-8 -*-
|
||||
import textwrap
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
from _pytest.pytester import get_public_names
|
||||
from _pytest.fixtures import FixtureLookupError, FixtureRequest
|
||||
@@ -208,23 +208,23 @@ class TestFillFixtures(object):
|
||||
)
|
||||
subdir = testdir.mkpydir("subdir")
|
||||
subdir.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pytest
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def spam():
|
||||
return 'spam'
|
||||
"""
|
||||
@pytest.fixture
|
||||
def spam():
|
||||
return 'spam'
|
||||
"""
|
||||
)
|
||||
)
|
||||
testfile = subdir.join("test_spam.py")
|
||||
testfile.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_spam(spam):
|
||||
assert spam == "spam"
|
||||
"""
|
||||
def test_spam(spam):
|
||||
assert spam == "spam"
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -276,26 +276,26 @@ class TestFillFixtures(object):
|
||||
)
|
||||
subdir = testdir.mkpydir("subdir")
|
||||
subdir.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pytest
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(params=[1, 2, 3])
|
||||
def spam(request):
|
||||
return request.param
|
||||
"""
|
||||
@pytest.fixture(params=[1, 2, 3])
|
||||
def spam(request):
|
||||
return request.param
|
||||
"""
|
||||
)
|
||||
)
|
||||
testfile = subdir.join("test_spam.py")
|
||||
testfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
params = {'spam': 1}
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
params = {'spam': 1}
|
||||
|
||||
def test_spam(spam):
|
||||
assert spam == params['spam']
|
||||
params['spam'] += 1
|
||||
"""
|
||||
def test_spam(spam):
|
||||
assert spam == params['spam']
|
||||
params['spam'] += 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -320,26 +320,26 @@ class TestFillFixtures(object):
|
||||
)
|
||||
subdir = testdir.mkpydir("subdir")
|
||||
subdir.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pytest
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(params=[1, 2, 3])
|
||||
def spam(request):
|
||||
return request.param
|
||||
"""
|
||||
@pytest.fixture(params=[1, 2, 3])
|
||||
def spam(request):
|
||||
return request.param
|
||||
"""
|
||||
)
|
||||
)
|
||||
testfile = subdir.join("test_spam.py")
|
||||
testfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
params = {'spam': 1}
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
params = {'spam': 1}
|
||||
|
||||
def test_spam(spam):
|
||||
assert spam == params['spam']
|
||||
params['spam'] += 1
|
||||
"""
|
||||
def test_spam(spam):
|
||||
assert spam == params['spam']
|
||||
params['spam'] += 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -807,13 +807,13 @@ class TestRequestBasic(object):
|
||||
# this tests that normalization of nodeids takes place
|
||||
b = testdir.mkdir("tests").mkdir("unit")
|
||||
b.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
pass
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
p = b.join("test_module.py")
|
||||
@@ -1470,10 +1470,9 @@ class TestFixtureManagerParseFactories(object):
|
||||
print (faclist)
|
||||
assert len(faclist) == 3
|
||||
|
||||
kwargs = {'__being_called_by_pytest': True}
|
||||
assert faclist[0].func(item._request, **kwargs) == "conftest"
|
||||
assert faclist[1].func(item._request, **kwargs) == "module"
|
||||
assert faclist[2].func(item._request, **kwargs) == "class"
|
||||
assert faclist[0].func(item._request) == "conftest"
|
||||
assert faclist[1].func(item._request) == "module"
|
||||
assert faclist[2].func(item._request) == "class"
|
||||
"""
|
||||
)
|
||||
reprec = testdir.inline_run("-s")
|
||||
@@ -1485,41 +1484,41 @@ class TestFixtureManagerParseFactories(object):
|
||||
runner = testdir.mkdir("runner")
|
||||
package = testdir.mkdir("package")
|
||||
package.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def one():
|
||||
return 1
|
||||
"""
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.join("test_x.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_x(one):
|
||||
assert one == 1
|
||||
"""
|
||||
def test_x(one):
|
||||
assert one == 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub = package.mkdir("sub")
|
||||
sub.join("__init__.py").ensure()
|
||||
sub.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def one():
|
||||
return 2
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def one():
|
||||
return 2
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub.join("test_y.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_x(one):
|
||||
assert one == 2
|
||||
"""
|
||||
def test_x(one):
|
||||
assert one == 2
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
@@ -1536,44 +1535,44 @@ class TestFixtureManagerParseFactories(object):
|
||||
)
|
||||
package = testdir.mkdir("package")
|
||||
package.join("__init__.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def setup_module():
|
||||
values.append("package")
|
||||
def teardown_module():
|
||||
values[:] = []
|
||||
"""
|
||||
from .. import values
|
||||
def setup_module():
|
||||
values.append("package")
|
||||
def teardown_module():
|
||||
values[:] = []
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.join("test_x.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def test_x():
|
||||
assert values == ["package"]
|
||||
"""
|
||||
from .. import values
|
||||
def test_x():
|
||||
assert values == ["package"]
|
||||
"""
|
||||
)
|
||||
)
|
||||
package = testdir.mkdir("package2")
|
||||
package.join("__init__.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def setup_module():
|
||||
values.append("package2")
|
||||
def teardown_module():
|
||||
values[:] = []
|
||||
"""
|
||||
from .. import values
|
||||
def setup_module():
|
||||
values.append("package2")
|
||||
def teardown_module():
|
||||
values[:] = []
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.join("test_x.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def test_x():
|
||||
assert values == ["package2"]
|
||||
"""
|
||||
from .. import values
|
||||
def test_x():
|
||||
assert values == ["package2"]
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
@@ -1585,40 +1584,46 @@ class TestFixtureManagerParseFactories(object):
|
||||
values = []
|
||||
"""
|
||||
)
|
||||
testdir.syspathinsert(testdir.tmpdir.dirname)
|
||||
package = testdir.mkdir("package")
|
||||
package.join("__init__.py").write("")
|
||||
package.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
from .. import values
|
||||
@pytest.fixture(scope="package")
|
||||
def one():
|
||||
values.append("package")
|
||||
yield values
|
||||
values.pop()
|
||||
@pytest.fixture(scope="package", autouse=True)
|
||||
def two():
|
||||
values.append("package-auto")
|
||||
yield values
|
||||
values.pop()
|
||||
"""
|
||||
import pytest
|
||||
from .. import values
|
||||
@pytest.fixture(scope="package")
|
||||
def one():
|
||||
values.append("package")
|
||||
yield values
|
||||
values.pop()
|
||||
@pytest.fixture(scope="package", autouse=True)
|
||||
def two():
|
||||
values.append("package-auto")
|
||||
yield values
|
||||
values.pop()
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.join("test_x.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def test_package_autouse():
|
||||
assert values == ["package-auto"]
|
||||
def test_package(one):
|
||||
assert values == ["package-auto", "package"]
|
||||
"""
|
||||
from .. import values
|
||||
def test_package_autouse():
|
||||
assert values == ["package-auto"]
|
||||
def test_package(one):
|
||||
assert values == ["package-auto", "package"]
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
def test_collect_custom_items(self, testdir):
|
||||
testdir.copy_example("fixtures/custom_item")
|
||||
result = testdir.runpytest("foo")
|
||||
result.stdout.fnmatch_lines("*passed*")
|
||||
|
||||
|
||||
class TestAutouseDiscovery(object):
|
||||
@pytest.fixture
|
||||
@@ -1800,24 +1805,24 @@ class TestAutouseManagement(object):
|
||||
def test_autouse_conftest_mid_directory(self, testdir):
|
||||
pkgdir = testdir.mkpydir("xyz123")
|
||||
pkgdir.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture(autouse=True)
|
||||
def app():
|
||||
import sys
|
||||
sys._myapp = "hello"
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture(autouse=True)
|
||||
def app():
|
||||
import sys
|
||||
sys._myapp = "hello"
|
||||
"""
|
||||
)
|
||||
)
|
||||
t = pkgdir.ensure("tests", "test_app.py")
|
||||
t.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import sys
|
||||
def test_app():
|
||||
assert sys._myapp == "hello"
|
||||
"""
|
||||
import sys
|
||||
def test_app():
|
||||
assert sys._myapp == "hello"
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run("-s")
|
||||
@@ -2711,17 +2716,17 @@ class TestFixtureMarker(object):
|
||||
)
|
||||
b = testdir.mkdir("subdir")
|
||||
b.join("test_overridden_fixture_finalizer.py").write(
|
||||
dedent(
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def browser(browser):
|
||||
browser['visited'] = True
|
||||
return browser
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def browser(browser):
|
||||
browser['visited'] = True
|
||||
return browser
|
||||
|
||||
def test_browser(browser):
|
||||
assert browser['visited'] is True
|
||||
"""
|
||||
def test_browser(browser):
|
||||
assert browser['visited'] is True
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.runpytest("-s")
|
||||
@@ -3213,120 +3218,119 @@ class TestShowFixtures(object):
|
||||
|
||||
def test_show_fixtures_trimmed_doc(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
'''\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
"""
|
||||
line1
|
||||
line2
|
||||
|
||||
"""
|
||||
@pytest.fixture
|
||||
def arg2():
|
||||
"""
|
||||
line1
|
||||
line2
|
||||
|
||||
"""
|
||||
'''
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def arg1():
|
||||
"""
|
||||
line1
|
||||
line2
|
||||
|
||||
"""
|
||||
@pytest.fixture
|
||||
def arg2():
|
||||
"""
|
||||
line1
|
||||
line2
|
||||
|
||||
"""
|
||||
'''
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--fixtures", p)
|
||||
result.stdout.fnmatch_lines(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_trimmed_doc *
|
||||
arg2
|
||||
line1
|
||||
line2
|
||||
arg1
|
||||
line1
|
||||
line2
|
||||
"""
|
||||
* fixtures defined from test_show_fixtures_trimmed_doc *
|
||||
arg2
|
||||
line1
|
||||
line2
|
||||
arg1
|
||||
line1
|
||||
line2
|
||||
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
def test_show_fixtures_indented_doc(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
'''\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fixture1():
|
||||
"""
|
||||
line1
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fixture1():
|
||||
"""
|
||||
line1
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--fixtures", p)
|
||||
result.stdout.fnmatch_lines(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_doc *
|
||||
fixture1
|
||||
line1
|
||||
indented line
|
||||
"""
|
||||
* fixtures defined from test_show_fixtures_indented_doc *
|
||||
fixture1
|
||||
line1
|
||||
indented line
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
def test_show_fixtures_indented_doc_first_line_unindented(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
'''\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fixture1():
|
||||
"""line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fixture1():
|
||||
"""line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--fixtures", p)
|
||||
result.stdout.fnmatch_lines(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
|
||||
fixture1
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
* fixtures defined from test_show_fixtures_indented_doc_first_line_unindented *
|
||||
fixture1
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
def test_show_fixtures_indented_in_class(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
'''\
|
||||
import pytest
|
||||
class TestClass(object):
|
||||
@pytest.fixture
|
||||
def fixture1(self):
|
||||
"""line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
import pytest
|
||||
class TestClass(object):
|
||||
@pytest.fixture
|
||||
def fixture1(self):
|
||||
"""line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
'''
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--fixtures", p)
|
||||
result.stdout.fnmatch_lines(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
* fixtures defined from test_show_fixtures_indented_in_class *
|
||||
fixture1
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
* fixtures defined from test_show_fixtures_indented_in_class *
|
||||
fixture1
|
||||
line1
|
||||
line2
|
||||
indented line
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3663,26 +3667,26 @@ class TestParameterizedSubRequest(object):
|
||||
fixdir = testdir.mkdir("fixtures")
|
||||
fixfile = fixdir.join("fix.py")
|
||||
fixfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
import pytest
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(params=[0, 1, 2])
|
||||
def fix_with_param(request):
|
||||
return request.param
|
||||
"""
|
||||
@pytest.fixture(params=[0, 1, 2])
|
||||
def fix_with_param(request):
|
||||
return request.param
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
testfile = tests_dir.join("test_foos.py")
|
||||
testfile.write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
from fix import fix_with_param
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from fix import fix_with_param
|
||||
|
||||
def test_foo(request):
|
||||
request.getfixturevalue('fix_with_param')
|
||||
"""
|
||||
def test_foo(request):
|
||||
request.getfixturevalue('fix_with_param')
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -3694,9 +3698,9 @@ class TestParameterizedSubRequest(object):
|
||||
E*Failed: The requested fixture has no parameter defined for the current test.
|
||||
E*
|
||||
E*Requested fixture 'fix_with_param' defined in:
|
||||
E*fix.py:5
|
||||
E*fix.py:4
|
||||
E*Requested here:
|
||||
E*test_foos.py:5
|
||||
E*test_foos.py:4
|
||||
*1 failed*
|
||||
"""
|
||||
)
|
||||
@@ -3975,3 +3979,71 @@ class TestScopeOrdering(object):
|
||||
items, _ = testdir.inline_genitems()
|
||||
request = FixtureRequest(items[0])
|
||||
assert request.fixturenames == "s1 p1 m1 m2 c1 f2 f1".split()
|
||||
|
||||
def test_multiple_packages(self, testdir):
|
||||
"""Complex test involving multiple package fixtures. Make sure teardowns
|
||||
are executed in order.
|
||||
.
|
||||
└── root
|
||||
├── __init__.py
|
||||
├── sub1
|
||||
│ ├── __init__.py
|
||||
│ ├── conftest.py
|
||||
│ └── test_1.py
|
||||
└── sub2
|
||||
├── __init__.py
|
||||
├── conftest.py
|
||||
└── test_2.py
|
||||
"""
|
||||
root = testdir.mkdir("root")
|
||||
root.join("__init__.py").write("values = []")
|
||||
sub1 = root.mkdir("sub1")
|
||||
sub1.ensure("__init__.py")
|
||||
sub1.join("conftest.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
from .. import values
|
||||
@pytest.fixture(scope="package")
|
||||
def fix():
|
||||
values.append("pre-sub1")
|
||||
yield values
|
||||
assert values.pop() == "pre-sub1"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub1.join("test_1.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def test_1(fix):
|
||||
assert values == ["pre-sub1"]
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub2 = root.mkdir("sub2")
|
||||
sub2.ensure("__init__.py")
|
||||
sub2.join("conftest.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
from .. import values
|
||||
@pytest.fixture(scope="package")
|
||||
def fix():
|
||||
values.append("pre-sub2")
|
||||
yield values
|
||||
assert values.pop() == "pre-sub2"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub2.join("test_2.py").write(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
from .. import values
|
||||
def test_2(fix):
|
||||
assert values == ["pre-sub2"]
|
||||
"""
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=2)
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import re
|
||||
import sys
|
||||
import attr
|
||||
import _pytest._code
|
||||
import py
|
||||
import textwrap
|
||||
import pytest
|
||||
from _pytest import python, fixtures
|
||||
|
||||
@@ -295,9 +294,7 @@ class TestMetafunc(object):
|
||||
)
|
||||
assert result == ["a0-1.0", "a1-b1"]
|
||||
# unicode mixing, issue250
|
||||
result = idmaker(
|
||||
(py.builtin._totext("a"), "b"), [pytest.param({}, b"\xc3\xb4")]
|
||||
)
|
||||
result = idmaker((u"a", "b"), [pytest.param({}, b"\xc3\xb4")])
|
||||
assert result == ["a0-\\xc3\\xb4"]
|
||||
|
||||
def test_idmaker_with_bytes_regex(self):
|
||||
@@ -309,7 +306,6 @@ class TestMetafunc(object):
|
||||
def test_idmaker_native_strings(self):
|
||||
from _pytest.python import idmaker
|
||||
|
||||
totext = py.builtin._totext
|
||||
result = idmaker(
|
||||
("a", "b"),
|
||||
[
|
||||
@@ -324,7 +320,7 @@ class TestMetafunc(object):
|
||||
pytest.param({7}, set("seven")),
|
||||
pytest.param(tuple("eight"), (8, -8, 8)),
|
||||
pytest.param(b"\xc3\xb4", b"name"),
|
||||
pytest.param(b"\xc3\xb4", totext("other")),
|
||||
pytest.param(b"\xc3\xb4", u"other"),
|
||||
],
|
||||
)
|
||||
assert result == [
|
||||
@@ -1275,19 +1271,19 @@ class TestMetafuncFunctional(object):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub2 = testdir.mkpydir("sub2")
|
||||
sub1.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_1"
|
||||
"""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_1"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub2.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_2"
|
||||
"""
|
||||
def pytest_generate_tests(metafunc):
|
||||
assert metafunc.function.__name__ == "test_2"
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub1.join("test_in_sub1.py").write("def test_1(): pass")
|
||||
|
||||
@@ -11,7 +11,7 @@ def equal_with_bash(prefix, ffc, fc, out=None):
|
||||
res_bash = set(fc(prefix))
|
||||
retval = set(res) == res_bash
|
||||
if out:
|
||||
out.write("equal_with_bash %s %s\n" % (retval, res))
|
||||
out.write("equal_with_bash {} {}\n".format(retval, res))
|
||||
if not retval:
|
||||
out.write(" python - bash: %s\n" % (set(res) - res_bash))
|
||||
out.write(" bash - python: %s\n" % (res_bash - set(res)))
|
||||
|
||||
@@ -6,6 +6,7 @@ import textwrap
|
||||
import _pytest.assertion as plugin
|
||||
import py
|
||||
import pytest
|
||||
import six
|
||||
from _pytest.assertion import util
|
||||
from _pytest.assertion import truncate
|
||||
|
||||
@@ -509,12 +510,12 @@ class TestAssert_reprcompare(object):
|
||||
assert "raised in repr()" not in expl
|
||||
|
||||
def test_unicode(self):
|
||||
left = py.builtin._totext("£€", "utf-8")
|
||||
right = py.builtin._totext("£", "utf-8")
|
||||
left = u"£€"
|
||||
right = u"£"
|
||||
expl = callequal(left, right)
|
||||
assert expl[0] == py.builtin._totext("'£€' == '£'", "utf-8")
|
||||
assert expl[1] == py.builtin._totext("- £€", "utf-8")
|
||||
assert expl[2] == py.builtin._totext("+ £", "utf-8")
|
||||
assert expl[0] == u"'£€' == '£'"
|
||||
assert expl[1] == u"- £€"
|
||||
assert expl[2] == u"+ £"
|
||||
|
||||
def test_nonascii_text(self):
|
||||
"""
|
||||
@@ -541,8 +542,8 @@ class TestAssert_reprcompare(object):
|
||||
right = bytes(right, "utf-8")
|
||||
expl = callequal(left, right)
|
||||
for line in expl:
|
||||
assert isinstance(line, py.builtin.text)
|
||||
msg = py.builtin._totext("\n").join(expl)
|
||||
assert isinstance(line, six.text_type)
|
||||
msg = u"\n".join(expl)
|
||||
assert msg
|
||||
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import textwrap
|
||||
import zipfile
|
||||
import py
|
||||
import pytest
|
||||
import six
|
||||
|
||||
import _pytest._code
|
||||
from _pytest.assertion import util
|
||||
@@ -49,7 +50,7 @@ def getmsg(f, extra_ns=None, must_pass=False):
|
||||
ns = {}
|
||||
if extra_ns is not None:
|
||||
ns.update(extra_ns)
|
||||
py.builtin.exec_(code, ns)
|
||||
six.exec_(code, ns)
|
||||
func = ns[f.__name__]
|
||||
try:
|
||||
func()
|
||||
@@ -246,6 +247,15 @@ class TestAssertionRewrite(object):
|
||||
["*AssertionError: To be escaped: %", "*assert 1 == 2"]
|
||||
)
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info < (3,), reason="bytes is a string type in python 2"
|
||||
)
|
||||
def test_assertion_messages_bytes(self, testdir):
|
||||
testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n")
|
||||
result = testdir.runpytest()
|
||||
assert result.ret == 1
|
||||
result.stdout.fnmatch_lines(["*AssertionError: b'ohai!'", "*assert False"])
|
||||
|
||||
def test_boolop(self):
|
||||
def f():
|
||||
f = g = False
|
||||
@@ -551,7 +561,7 @@ class TestAssertionRewrite(object):
|
||||
assert getmsg(f) == "assert 42"
|
||||
|
||||
def my_reprcompare(op, left, right):
|
||||
return "%s %s %s" % (left, op, right)
|
||||
return "{} {} {}".format(left, op, right)
|
||||
|
||||
monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
|
||||
|
||||
@@ -645,12 +655,10 @@ class TestRewriteOnImport(object):
|
||||
def test_readonly(self, testdir):
|
||||
sub = testdir.mkdir("testing")
|
||||
sub.join("test_readonly.py").write(
|
||||
py.builtin._totext(
|
||||
"""
|
||||
b"""
|
||||
def test_rewritten():
|
||||
assert "@py_builtins" in globals()
|
||||
"""
|
||||
).encode("utf-8"),
|
||||
""",
|
||||
"wb",
|
||||
)
|
||||
old_mode = sub.stat().mode
|
||||
@@ -1031,14 +1039,14 @@ class TestAssertionRewriteHookDetails(object):
|
||||
"""
|
||||
path = testdir.mkpydir("foo")
|
||||
path.join("test_foo.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
class Test(object):
|
||||
def test_foo(self):
|
||||
import pkgutil
|
||||
data = pkgutil.get_data('foo.test_foo', 'data.txt')
|
||||
assert data == b'Hey'
|
||||
"""
|
||||
class Test(object):
|
||||
def test_foo(self):
|
||||
import pkgutil
|
||||
data = pkgutil.get_data('foo.test_foo', 'data.txt')
|
||||
assert data == b'Hey'
|
||||
"""
|
||||
)
|
||||
)
|
||||
path.join("data.txt").write("Hey")
|
||||
@@ -1116,3 +1124,32 @@ def test_simple_failure():
|
||||
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines("*E*assert (1 + 1) == 3")
|
||||
|
||||
|
||||
def test_rewrite_infinite_recursion(testdir, pytestconfig, monkeypatch):
|
||||
"""Fix infinite recursion when writing pyc files: if an import happens to be triggered when writing the pyc
|
||||
file, this would cause another call to the hook, which would trigger another pyc writing, which could
|
||||
trigger another import, and so on. (#3506)"""
|
||||
from _pytest.assertion import rewrite
|
||||
|
||||
testdir.syspathinsert()
|
||||
testdir.makepyfile(test_foo="def test_foo(): pass")
|
||||
testdir.makepyfile(test_bar="def test_bar(): pass")
|
||||
|
||||
original_write_pyc = rewrite._write_pyc
|
||||
|
||||
write_pyc_called = []
|
||||
|
||||
def spy_write_pyc(*args, **kwargs):
|
||||
# make a note that we have called _write_pyc
|
||||
write_pyc_called.append(True)
|
||||
# try to import a module at this point: we should not try to rewrite this module
|
||||
assert hook.find_module("test_bar") is None
|
||||
return original_write_pyc(*args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(rewrite, "_write_pyc", spy_write_pyc)
|
||||
monkeypatch.setattr(sys, "dont_write_bytecode", False)
|
||||
|
||||
hook = AssertionRewritingHook(pytestconfig)
|
||||
assert hook.find_module("test_foo") is not None
|
||||
assert len(write_pyc_called) == 1
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import py
|
||||
import _pytest
|
||||
import pytest
|
||||
import os
|
||||
import shutil
|
||||
@@ -150,6 +150,32 @@ def test_cache_reportheader(testdir):
|
||||
result.stdout.fnmatch_lines(["cachedir: .pytest_cache"])
|
||||
|
||||
|
||||
def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
|
||||
external_cache = tmpdir_factory.mktemp(
|
||||
"test_cache_reportheader_external_abspath_abs"
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
def test_hello():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
testdir.makeini(
|
||||
"""
|
||||
[pytest]
|
||||
cache_dir = {abscache}
|
||||
""".format(
|
||||
abscache=external_cache
|
||||
)
|
||||
)
|
||||
|
||||
result = testdir.runpytest("-v")
|
||||
result.stdout.fnmatch_lines(
|
||||
["cachedir: {abscache}".format(abscache=external_cache)]
|
||||
)
|
||||
|
||||
|
||||
def test_cache_show(testdir):
|
||||
result = testdir.runpytest("--cache-show")
|
||||
assert result.ret == 0
|
||||
@@ -198,17 +224,17 @@ class TestLastFailed(object):
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(["*2 failed*"])
|
||||
p.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_1():
|
||||
assert 1
|
||||
|
||||
def test_2():
|
||||
assert 1
|
||||
|
||||
def test_3():
|
||||
assert 0
|
||||
"""
|
||||
def test_1():
|
||||
assert 1
|
||||
|
||||
def test_2():
|
||||
assert 1
|
||||
|
||||
def test_3():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--lf")
|
||||
@@ -226,19 +252,19 @@ class TestLastFailed(object):
|
||||
|
||||
def test_failedfirst_order(self, testdir):
|
||||
testdir.tmpdir.join("test_a.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
"""
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.tmpdir.join("test_b.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
"""
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -251,14 +277,14 @@ class TestLastFailed(object):
|
||||
def test_lastfailed_failedfirst_order(self, testdir):
|
||||
testdir.makepyfile(
|
||||
**{
|
||||
"test_a.py": """
|
||||
"test_a.py": """\
|
||||
def test_always_passes():
|
||||
assert 1
|
||||
""",
|
||||
"test_b.py": """
|
||||
""",
|
||||
"test_b.py": """\
|
||||
def test_always_fails():
|
||||
assert 0
|
||||
""",
|
||||
""",
|
||||
}
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -272,16 +298,16 @@ class TestLastFailed(object):
|
||||
def test_lastfailed_difference_invocations(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||
testdir.makepyfile(
|
||||
test_a="""
|
||||
test_a="""\
|
||||
def test_a1():
|
||||
assert 0
|
||||
def test_a2():
|
||||
assert 1
|
||||
""",
|
||||
test_b="""
|
||||
""",
|
||||
test_b="""\
|
||||
def test_b1():
|
||||
assert 0
|
||||
""",
|
||||
""",
|
||||
)
|
||||
p = testdir.tmpdir.join("test_a.py")
|
||||
p2 = testdir.tmpdir.join("test_b.py")
|
||||
@@ -291,11 +317,11 @@ class TestLastFailed(object):
|
||||
result = testdir.runpytest("--lf", p2)
|
||||
result.stdout.fnmatch_lines(["*1 failed*"])
|
||||
p2.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_b1():
|
||||
assert 1
|
||||
"""
|
||||
def test_b1():
|
||||
assert 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--lf", p2)
|
||||
@@ -306,18 +332,18 @@ class TestLastFailed(object):
|
||||
def test_lastfailed_usecase_splice(self, testdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_1():
|
||||
assert 0
|
||||
"""
|
||||
"""
|
||||
)
|
||||
p2 = testdir.tmpdir.join("test_something.py")
|
||||
p2.write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_2():
|
||||
assert 0
|
||||
"""
|
||||
def test_2():
|
||||
assert 0
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
@@ -533,6 +559,19 @@ class TestLastFailed(object):
|
||||
testdir.runpytest("-q", "--lf")
|
||||
assert os.path.exists(".pytest_cache/v/cache/lastfailed")
|
||||
|
||||
@pytest.mark.parametrize("quiet", [True, False])
|
||||
@pytest.mark.parametrize("opt", ["--ff", "--lf"])
|
||||
def test_lf_and_ff_obey_verbosity(self, quiet, opt, testdir):
|
||||
testdir.makepyfile("def test(): pass")
|
||||
args = [opt]
|
||||
if quiet:
|
||||
args.append("-q")
|
||||
result = testdir.runpytest(*args)
|
||||
if quiet:
|
||||
assert "run all" not in result.stdout.str()
|
||||
else:
|
||||
assert "run all" in result.stdout.str()
|
||||
|
||||
def test_xfail_not_considered_failure(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
# note: py.io capture tests where copied from
|
||||
@@ -5,13 +6,13 @@ from __future__ import absolute_import, division, print_function
|
||||
import pickle
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
from io import UnsupportedOperation
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
import contextlib
|
||||
from six import binary_type, text_type
|
||||
from six import text_type
|
||||
from _pytest import capture
|
||||
from _pytest.capture import CaptureManager
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||
@@ -23,12 +24,12 @@ needsosdup = pytest.mark.xfail("not hasattr(os, 'dup')")
|
||||
def tobytes(obj):
|
||||
if isinstance(obj, text_type):
|
||||
obj = obj.encode("UTF-8")
|
||||
assert isinstance(obj, binary_type)
|
||||
assert isinstance(obj, bytes)
|
||||
return obj
|
||||
|
||||
|
||||
def totext(obj):
|
||||
if isinstance(obj, binary_type):
|
||||
if isinstance(obj, bytes):
|
||||
obj = text_type(obj, "UTF-8")
|
||||
assert isinstance(obj, text_type)
|
||||
return obj
|
||||
@@ -70,19 +71,23 @@ class TestCaptureManager(object):
|
||||
try:
|
||||
capman = CaptureManager(method)
|
||||
capman.start_global_capturing()
|
||||
outerr = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
outerr = capman.read_global_capture()
|
||||
assert outerr == ("", "")
|
||||
outerr = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
outerr = capman.read_global_capture()
|
||||
assert outerr == ("", "")
|
||||
print("hello")
|
||||
out, err = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
out, err = capman.read_global_capture()
|
||||
if method == "no":
|
||||
assert old == (sys.stdout, sys.stderr, sys.stdin)
|
||||
else:
|
||||
assert not out
|
||||
capman.resume_global_capture()
|
||||
print("hello")
|
||||
out, err = capman.suspend_global_capture()
|
||||
capman.suspend_global_capture()
|
||||
out, err = capman.read_global_capture()
|
||||
if method != "no":
|
||||
assert out == "hello\n"
|
||||
capman.stop_global_capturing()
|
||||
@@ -264,7 +269,7 @@ class TestPerTestCapturing(object):
|
||||
|
||||
def test_capturing_outerr(self, testdir):
|
||||
p1 = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
import sys
|
||||
def test_capturing():
|
||||
print (42)
|
||||
@@ -273,7 +278,7 @@ class TestPerTestCapturing(object):
|
||||
print (1)
|
||||
sys.stderr.write(str(2))
|
||||
raise ValueError
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p1)
|
||||
result.stdout.fnmatch_lines(
|
||||
@@ -293,21 +298,21 @@ class TestPerTestCapturing(object):
|
||||
class TestLoggingInteraction(object):
|
||||
def test_logging_stream_ownership(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_logging():
|
||||
import logging
|
||||
import pytest
|
||||
stream = capture.CaptureIO()
|
||||
logging.basicConfig(stream=stream)
|
||||
stream.close() # to free memory/release resources
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess(p)
|
||||
assert result.stderr.str().find("atexit") == -1
|
||||
|
||||
def test_logging_and_immediate_setupteardown(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
import logging
|
||||
def setup_function(function):
|
||||
logging.warn("hello1")
|
||||
@@ -319,7 +324,7 @@ class TestLoggingInteraction(object):
|
||||
def teardown_function(function):
|
||||
logging.warn("hello3")
|
||||
assert 0
|
||||
"""
|
||||
"""
|
||||
)
|
||||
for optargs in (("--capture=sys",), ("--capture=fd",)):
|
||||
print(optargs)
|
||||
@@ -333,7 +338,7 @@ class TestLoggingInteraction(object):
|
||||
|
||||
def test_logging_and_crossscope_fixtures(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
import logging
|
||||
def setup_module(function):
|
||||
logging.warn("hello1")
|
||||
@@ -345,7 +350,7 @@ class TestLoggingInteraction(object):
|
||||
def teardown_module(function):
|
||||
logging.warn("hello3")
|
||||
assert 0
|
||||
"""
|
||||
"""
|
||||
)
|
||||
for optargs in (("--capture=sys",), ("--capture=fd",)):
|
||||
print(optargs)
|
||||
@@ -359,11 +364,11 @@ class TestLoggingInteraction(object):
|
||||
|
||||
def test_conftestlogging_is_shown(self, testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
"""\
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
logging.warn("hello435")
|
||||
"""
|
||||
"""
|
||||
)
|
||||
# make sure that logging is still captured in tests
|
||||
result = testdir.runpytest_subprocess("-s", "-p", "no:capturelog")
|
||||
@@ -373,19 +378,19 @@ class TestLoggingInteraction(object):
|
||||
|
||||
def test_conftestlogging_and_test_logging(self, testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
"""\
|
||||
import logging
|
||||
logging.basicConfig()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
# make sure that logging is still captured in tests
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_hello():
|
||||
import logging
|
||||
logging.warn("hello433")
|
||||
assert 0
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess(p, "-p", "no:capturelog")
|
||||
assert result.ret != 0
|
||||
@@ -398,24 +403,24 @@ class TestCaptureFixture(object):
|
||||
@pytest.mark.parametrize("opt", [[], ["-s"]])
|
||||
def test_std_functional(self, testdir, opt):
|
||||
reprec = testdir.inline_runsource(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capsys):
|
||||
print (42)
|
||||
out, err = capsys.readouterr()
|
||||
assert out.startswith("42")
|
||||
""",
|
||||
""",
|
||||
*opt
|
||||
)
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
def test_capsyscapfd(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_one(capsys, capfd):
|
||||
pass
|
||||
def test_two(capfd, capsys):
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines(
|
||||
@@ -433,12 +438,12 @@ class TestCaptureFixture(object):
|
||||
in the same test is an error.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_one(capsys, request):
|
||||
request.getfixturevalue("capfd")
|
||||
def test_two(capfd, request):
|
||||
request.getfixturevalue("capsys")
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
@@ -453,10 +458,10 @@ class TestCaptureFixture(object):
|
||||
|
||||
def test_capsyscapfdbinary(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_one(capsys, capfdbinary):
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines(
|
||||
@@ -466,12 +471,13 @@ class TestCaptureFixture(object):
|
||||
@pytest.mark.parametrize("method", ["sys", "fd"])
|
||||
def test_capture_is_represented_on_failure_issue128(self, testdir, method):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
def test_hello(cap%s):
|
||||
"""\
|
||||
def test_hello(cap{}):
|
||||
print ("xxx42xxx")
|
||||
assert 0
|
||||
"""
|
||||
% method
|
||||
""".format(
|
||||
method
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines(["xxx42xxx"])
|
||||
@@ -479,21 +485,21 @@ class TestCaptureFixture(object):
|
||||
@needsosdup
|
||||
def test_stdfd_functional(self, testdir):
|
||||
reprec = testdir.inline_runsource(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capfd):
|
||||
import os
|
||||
os.write(1, "42".encode('ascii'))
|
||||
out, err = capfd.readouterr()
|
||||
assert out.startswith("42")
|
||||
capfd.close()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
@needsosdup
|
||||
def test_capfdbinary(self, testdir):
|
||||
reprec = testdir.inline_runsource(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capfdbinary):
|
||||
import os
|
||||
# some likely un-decodable bytes
|
||||
@@ -501,7 +507,7 @@ class TestCaptureFixture(object):
|
||||
out, err = capfdbinary.readouterr()
|
||||
assert out == b'\\xfe\\x98\\x20'
|
||||
assert err == b''
|
||||
"""
|
||||
"""
|
||||
)
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
@@ -510,7 +516,7 @@ class TestCaptureFixture(object):
|
||||
)
|
||||
def test_capsysbinary(self, testdir):
|
||||
reprec = testdir.inline_runsource(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capsysbinary):
|
||||
import sys
|
||||
# some likely un-decodable bytes
|
||||
@@ -518,7 +524,7 @@ class TestCaptureFixture(object):
|
||||
out, err = capsysbinary.readouterr()
|
||||
assert out == b'\\xfe\\x98\\x20'
|
||||
assert err == b''
|
||||
"""
|
||||
"""
|
||||
)
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
@@ -527,10 +533,10 @@ class TestCaptureFixture(object):
|
||||
)
|
||||
def test_capsysbinary_forbidden_in_python2(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capsysbinary):
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
@@ -543,10 +549,10 @@ class TestCaptureFixture(object):
|
||||
|
||||
def test_partial_setup_failure(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capsys, missingarg):
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines(["*test_partial_setup_failure*", "*1 error*"])
|
||||
@@ -554,12 +560,12 @@ class TestCaptureFixture(object):
|
||||
@needsosdup
|
||||
def test_keyboardinterrupt_disables_capturing(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_hello(capfd):
|
||||
import os
|
||||
os.write(1, str(42).encode('ascii'))
|
||||
raise KeyboardInterrupt()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess(p)
|
||||
result.stdout.fnmatch_lines(["*KeyboardInterrupt*"])
|
||||
@@ -568,7 +574,7 @@ class TestCaptureFixture(object):
|
||||
@pytest.mark.issue14
|
||||
def test_capture_and_logging(self, testdir):
|
||||
p = testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
import logging
|
||||
def test_log(capsys):
|
||||
logging.error('x')
|
||||
@@ -581,7 +587,7 @@ class TestCaptureFixture(object):
|
||||
@pytest.mark.parametrize("no_capture", [True, False])
|
||||
def test_disabled_capture_fixture(self, testdir, fixture, no_capture):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_disabled({fixture}):
|
||||
print('captured before')
|
||||
with {fixture}.disabled():
|
||||
@@ -615,7 +621,7 @@ class TestCaptureFixture(object):
|
||||
Ensure that capsys and capfd can be used by other fixtures during setup and teardown.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import pytest
|
||||
@@ -647,15 +653,43 @@ class TestCaptureFixture(object):
|
||||
assert "stdout contents begin" not in result.stdout.str()
|
||||
assert "stderr contents begin" not in result.stdout.str()
|
||||
|
||||
@pytest.mark.parametrize("cap", ["capsys", "capfd"])
|
||||
def test_fixture_use_by_other_fixtures_teardown(self, testdir, cap):
|
||||
"""Ensure we can access setup and teardown buffers from teardown when using capsys/capfd (##3033)"""
|
||||
testdir.makepyfile(
|
||||
"""\
|
||||
import sys
|
||||
import pytest
|
||||
import os
|
||||
|
||||
@pytest.fixture()
|
||||
def fix({cap}):
|
||||
print("setup out")
|
||||
sys.stderr.write("setup err\\n")
|
||||
yield
|
||||
out, err = {cap}.readouterr()
|
||||
assert out == 'setup out\\ncall out\\n'
|
||||
assert err == 'setup err\\ncall err\\n'
|
||||
|
||||
def test_a(fix):
|
||||
print("call out")
|
||||
sys.stderr.write("call err\\n")
|
||||
""".format(
|
||||
cap=cap
|
||||
)
|
||||
)
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(passed=1)
|
||||
|
||||
|
||||
def test_setup_failure_does_not_kill_capturing(testdir):
|
||||
sub1 = testdir.mkpydir("sub1")
|
||||
sub1.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_runtest_setup(item):
|
||||
raise ValueError(42)
|
||||
"""
|
||||
def pytest_runtest_setup(item):
|
||||
raise ValueError(42)
|
||||
"""
|
||||
)
|
||||
)
|
||||
sub1.join("test_mod.py").write("def test_func1(): pass")
|
||||
@@ -1051,9 +1085,9 @@ class TestStdCapture(object):
|
||||
|
||||
def test_capturing_readouterr_unicode(self):
|
||||
with self.getcapture() as cap:
|
||||
print("hx\xc4\x85\xc4\x87")
|
||||
print("hxąć")
|
||||
out, err = cap.readouterr()
|
||||
assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8")
|
||||
assert out == u"hxąć\n"
|
||||
|
||||
@pytest.mark.skipif(
|
||||
"sys.version_info >= (3,)", reason="text output different for bytes on python3"
|
||||
@@ -1063,7 +1097,7 @@ class TestStdCapture(object):
|
||||
# triggered an internal error in pytest
|
||||
print("\xa6")
|
||||
out, err = cap.readouterr()
|
||||
assert out == py.builtin._totext("\ufffd\n", "unicode-escape")
|
||||
assert out == u"\ufffd\n"
|
||||
|
||||
def test_reset_twice_error(self):
|
||||
with self.getcapture() as cap:
|
||||
@@ -1385,3 +1419,95 @@ def test_pickling_and_unpickling_encoded_file():
|
||||
ef = capture.EncodedFile(None, None)
|
||||
ef_as_str = pickle.dumps(ef)
|
||||
pickle.loads(ef_as_str)
|
||||
|
||||
|
||||
def test_global_capture_with_live_logging(testdir):
|
||||
# Issue 3819
|
||||
# capture should work with live cli logging
|
||||
|
||||
# Teardown report seems to have the capture for the whole process (setup, capture, teardown)
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
def pytest_runtest_logreport(report):
|
||||
if "test_global" in report.nodeid:
|
||||
if report.when == "teardown":
|
||||
with open("caplog", "w") as f:
|
||||
f.write(report.caplog)
|
||||
with open("capstdout", "w") as f:
|
||||
f.write(report.capstdout)
|
||||
"""
|
||||
)
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@pytest.fixture
|
||||
def fix1():
|
||||
print("fix setup")
|
||||
logging.info("fix setup")
|
||||
yield
|
||||
logging.info("fix teardown")
|
||||
print("fix teardown")
|
||||
|
||||
def test_global(fix1):
|
||||
print("begin test")
|
||||
logging.info("something in test")
|
||||
print("end test")
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest_subprocess("--log-cli-level=INFO")
|
||||
assert result.ret == 0
|
||||
|
||||
with open("caplog", "r") as f:
|
||||
caplog = f.read()
|
||||
|
||||
assert "fix setup" in caplog
|
||||
assert "something in test" in caplog
|
||||
assert "fix teardown" in caplog
|
||||
|
||||
with open("capstdout", "r") as f:
|
||||
capstdout = f.read()
|
||||
|
||||
assert "fix setup" in capstdout
|
||||
assert "begin test" in capstdout
|
||||
assert "end test" in capstdout
|
||||
assert "fix teardown" in capstdout
|
||||
|
||||
|
||||
@pytest.mark.parametrize("capture_fixture", ["capsys", "capfd"])
|
||||
def test_capture_with_live_logging(testdir, capture_fixture):
|
||||
# Issue 3819
|
||||
# capture should work with live cli logging
|
||||
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
def test_capture({0}):
|
||||
print("hello")
|
||||
sys.stderr.write("world\\n")
|
||||
captured = {0}.readouterr()
|
||||
assert captured.out == "hello\\n"
|
||||
assert captured.err == "world\\n"
|
||||
|
||||
logging.info("something")
|
||||
print("next")
|
||||
logging.info("something")
|
||||
|
||||
captured = {0}.readouterr()
|
||||
assert captured.out == "next\\n"
|
||||
""".format(
|
||||
capture_fixture
|
||||
)
|
||||
)
|
||||
|
||||
result = testdir.runpytest_subprocess("--log-cli-level=INFO")
|
||||
assert result.ret == 0
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import pprint
|
||||
import sys
|
||||
import textwrap
|
||||
import pytest
|
||||
|
||||
import _pytest._code
|
||||
from _pytest.main import Session, EXIT_NOTESTSCOLLECTED, _in_venv
|
||||
|
||||
|
||||
@@ -638,6 +638,10 @@ class Test_getinitialnodes(object):
|
||||
assert col.config is config
|
||||
|
||||
def test_pkgfile(self, testdir):
|
||||
"""Verify nesting when a module is within a package.
|
||||
The parent chain should match: Module<x.py> -> Package<subdir> -> Session.
|
||||
Session's parent should always be None.
|
||||
"""
|
||||
tmpdir = testdir.tmpdir
|
||||
subdir = tmpdir.join("subdir")
|
||||
x = subdir.ensure("x.py")
|
||||
@@ -645,8 +649,11 @@ class Test_getinitialnodes(object):
|
||||
with subdir.as_cwd():
|
||||
config = testdir.parseconfigure(x)
|
||||
col = testdir.getnode(config, x)
|
||||
assert isinstance(col, pytest.Module)
|
||||
assert col.name == "x.py"
|
||||
assert isinstance(col, pytest.Module)
|
||||
assert isinstance(col.parent, pytest.Package)
|
||||
assert isinstance(col.parent.parent, pytest.Session)
|
||||
# session is batman (has no parents)
|
||||
assert col.parent.parent.parent is None
|
||||
for col in col.listchain():
|
||||
assert col.config is config
|
||||
@@ -906,13 +913,13 @@ def test_fixture_scope_sibling_conftests(testdir):
|
||||
"""Regression test case for https://github.com/pytest-dev/pytest/issues/2836"""
|
||||
foo_path = testdir.mkdir("foo")
|
||||
foo_path.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fix():
|
||||
return 1
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fix():
|
||||
return 1
|
||||
"""
|
||||
)
|
||||
)
|
||||
foo_path.join("test_foo.py").write("def test_foo(fix): assert fix == 1")
|
||||
@@ -931,3 +938,17 @@ def test_fixture_scope_sibling_conftests(testdir):
|
||||
"*1 passed, 1 error*",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
def test_collect_init_tests(testdir):
|
||||
"""Check that we collect files from __init__.py files when they patch the 'python_files' (#3773)"""
|
||||
p = testdir.copy_example("collect/collect_init_tests")
|
||||
result = testdir.runpytest(p, "--collect-only")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"*<Module '__init__.py'>",
|
||||
"*<Function 'test_init'>",
|
||||
"*<Module 'test_foo.py'>",
|
||||
"*<Function 'test_foo'>",
|
||||
]
|
||||
)
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import sys
|
||||
from functools import wraps
|
||||
|
||||
import six
|
||||
|
||||
import pytest
|
||||
from _pytest.compat import is_generator, get_real_func, safe_getattr
|
||||
from _pytest.compat import is_generator, get_real_func, safe_getattr, _PytestWrapper
|
||||
from _pytest.outcomes import OutcomeException
|
||||
|
||||
|
||||
@@ -38,6 +41,33 @@ def test_real_func_loop_limit():
|
||||
print(res)
|
||||
|
||||
|
||||
def test_get_real_func():
|
||||
"""Check that get_real_func correctly unwraps decorators until reaching the real function"""
|
||||
|
||||
def decorator(f):
|
||||
@wraps(f)
|
||||
def inner():
|
||||
pass
|
||||
|
||||
if six.PY2:
|
||||
inner.__wrapped__ = f
|
||||
return inner
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
wrapped_func = decorator(decorator(func))
|
||||
assert get_real_func(wrapped_func) is func
|
||||
|
||||
wrapped_func2 = decorator(decorator(wrapped_func))
|
||||
assert get_real_func(wrapped_func2) is func
|
||||
|
||||
# special case for __pytest_wrapped__ attribute: used to obtain the function up until the point
|
||||
# a function was wrapped by pytest itself
|
||||
wrapped_func2.__pytest_wrapped__ = _PytestWrapper(wrapped_func)
|
||||
assert get_real_func(wrapped_func2) is wrapped_func
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
sys.version_info < (3, 4), reason="asyncio available in Python 3.4+"
|
||||
)
|
||||
|
||||
@@ -17,11 +17,11 @@ class TestParseIni(object):
|
||||
sub = tmpdir.mkdir("sub")
|
||||
sub.chdir()
|
||||
tmpdir.join(filename).write(
|
||||
_pytest._code.Source(
|
||||
"""
|
||||
[{section}]
|
||||
name = value
|
||||
""".format(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
[{section}]
|
||||
name = value
|
||||
""".format(
|
||||
section=section
|
||||
)
|
||||
)
|
||||
@@ -38,11 +38,11 @@ class TestParseIni(object):
|
||||
def test_append_parse_args(self, testdir, tmpdir, monkeypatch):
|
||||
monkeypatch.setenv("PYTEST_ADDOPTS", '--color no -rs --tb="short"')
|
||||
tmpdir.join("pytest.ini").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
[pytest]
|
||||
addopts = --verbose
|
||||
"""
|
||||
[pytest]
|
||||
addopts = --verbose
|
||||
"""
|
||||
)
|
||||
)
|
||||
config = testdir.parseconfig(tmpdir)
|
||||
@@ -438,11 +438,11 @@ class TestConfigFromdictargs(object):
|
||||
|
||||
def test_inifilename(self, tmpdir):
|
||||
tmpdir.join("foo/bar.ini").ensure().write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
[pytest]
|
||||
name = value
|
||||
"""
|
||||
[pytest]
|
||||
name = value
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -453,12 +453,12 @@ class TestConfigFromdictargs(object):
|
||||
|
||||
cwd = tmpdir.join("a/b")
|
||||
cwd.join("pytest.ini").ensure().write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
[pytest]
|
||||
name = wrong-value
|
||||
should_not_be_set = true
|
||||
"""
|
||||
[pytest]
|
||||
name = wrong-value
|
||||
should_not_be_set = true
|
||||
"""
|
||||
)
|
||||
)
|
||||
with cwd.ensure(dir=True).as_cwd():
|
||||
@@ -745,6 +745,24 @@ def test_get_plugin_specs_as_list():
|
||||
assert _get_plugin_specs_as_list(("foo", "bar")) == ["foo", "bar"]
|
||||
|
||||
|
||||
def test_collect_pytest_prefix_bug_integration(testdir):
|
||||
"""Integration test for issue #3775"""
|
||||
p = testdir.copy_example("config/collect_pytest_prefix")
|
||||
result = testdir.runpytest(p)
|
||||
result.stdout.fnmatch_lines("* 1 passed *")
|
||||
|
||||
|
||||
def test_collect_pytest_prefix_bug(pytestconfig):
|
||||
"""Ensure we collect only actual functions from conftest files (#3775)"""
|
||||
|
||||
class Dummy(object):
|
||||
class pytest_something(object):
|
||||
pass
|
||||
|
||||
pm = pytestconfig.pluginmanager
|
||||
assert pm.parse_hookimpl_opts(Dummy(), "pytest_something") is None
|
||||
|
||||
|
||||
class TestWarning(object):
|
||||
def test_warn_config(self, testdir):
|
||||
testdir.makeconftest(
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
from __future__ import absolute_import, division, print_function
|
||||
from textwrap import dedent
|
||||
import textwrap
|
||||
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
from _pytest.config import PytestPluginManager
|
||||
@@ -174,11 +173,11 @@ def test_conftest_confcutdir(testdir):
|
||||
testdir.makeconftest("assert 0")
|
||||
x = testdir.mkdir("x")
|
||||
x.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("-h", "--confcutdir=%s" % x, x)
|
||||
@@ -198,11 +197,11 @@ def test_no_conftest(testdir):
|
||||
def test_conftest_existing_resultlog(testdir):
|
||||
x = testdir.mkdir("tests")
|
||||
x.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.makefile(ext=".log", result="") # Writes result.log
|
||||
@@ -213,11 +212,11 @@ def test_conftest_existing_resultlog(testdir):
|
||||
def test_conftest_existing_junitxml(testdir):
|
||||
x = testdir.mkdir("tests")
|
||||
x.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true")
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.makefile(ext=".xml", junit="") # Writes junit.xml
|
||||
@@ -247,38 +246,38 @@ def test_fixture_dependency(testdir, monkeypatch):
|
||||
sub = testdir.mkdir("sub")
|
||||
sub.join("__init__.py").write("")
|
||||
sub.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def not_needed():
|
||||
assert False, "Should not be called!"
|
||||
|
||||
@pytest.fixture
|
||||
def foo():
|
||||
assert False, "Should not be called!"
|
||||
|
||||
@pytest.fixture
|
||||
def bar(foo):
|
||||
return 'bar'
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def not_needed():
|
||||
assert False, "Should not be called!"
|
||||
|
||||
@pytest.fixture
|
||||
def foo():
|
||||
assert False, "Should not be called!"
|
||||
|
||||
@pytest.fixture
|
||||
def bar(foo):
|
||||
return 'bar'
|
||||
"""
|
||||
)
|
||||
)
|
||||
subsub = sub.mkdir("subsub")
|
||||
subsub.join("__init__.py").write("")
|
||||
subsub.join("test_bar.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def bar():
|
||||
return 'sub bar'
|
||||
|
||||
def test_event_fixture(bar):
|
||||
assert bar == 'sub bar'
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def bar():
|
||||
return 'sub bar'
|
||||
|
||||
def test_event_fixture(bar):
|
||||
assert bar == 'sub bar'
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("sub")
|
||||
@@ -288,11 +287,11 @@ def test_fixture_dependency(testdir, monkeypatch):
|
||||
def test_conftest_found_with_double_dash(testdir):
|
||||
sub = testdir.mkdir("sub")
|
||||
sub.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--hello-world", action="store_true")
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--hello-world", action="store_true")
|
||||
"""
|
||||
)
|
||||
)
|
||||
p = sub.join("test_hello.py")
|
||||
@@ -313,56 +312,54 @@ class TestConftestVisibility(object):
|
||||
package = testdir.mkdir("package")
|
||||
|
||||
package.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fxtr():
|
||||
return "from-package"
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fxtr():
|
||||
return "from-package"
|
||||
"""
|
||||
)
|
||||
)
|
||||
package.join("test_pkgroot.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_pkgroot(fxtr):
|
||||
assert fxtr == "from-package"
|
||||
"""
|
||||
def test_pkgroot(fxtr):
|
||||
assert fxtr == "from-package"
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
swc = package.mkdir("swc")
|
||||
swc.join("__init__.py").ensure()
|
||||
swc.join("conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fxtr():
|
||||
return "from-swc"
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fxtr():
|
||||
return "from-swc"
|
||||
"""
|
||||
)
|
||||
)
|
||||
swc.join("test_with_conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_with_conftest(fxtr):
|
||||
assert fxtr == "from-swc"
|
||||
|
||||
"""
|
||||
def test_with_conftest(fxtr):
|
||||
assert fxtr == "from-swc"
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
snc = package.mkdir("snc")
|
||||
snc.join("__init__.py").ensure()
|
||||
snc.join("test_no_conftest.py").write(
|
||||
dedent(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_no_conftest(fxtr):
|
||||
assert fxtr == "from-package" # No local conftest.py, so should
|
||||
# use value from parent dir's
|
||||
|
||||
"""
|
||||
def test_no_conftest(fxtr):
|
||||
assert fxtr == "from-package" # No local conftest.py, so should
|
||||
# use value from parent dir's
|
||||
"""
|
||||
)
|
||||
)
|
||||
print("created directory structure:")
|
||||
@@ -422,31 +419,31 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
|
||||
src = root.join("src").ensure(dir=1)
|
||||
src.join("pytest.ini").write("[pytest]")
|
||||
src.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fix1(): pass
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def fix1(): pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
src.join("test_foo.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def test_1(fix1):
|
||||
pass
|
||||
def test_2(out_of_reach):
|
||||
pass
|
||||
"""
|
||||
def test_1(fix1):
|
||||
pass
|
||||
def test_2(out_of_reach):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
root.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def out_of_reach(): pass
|
||||
"""
|
||||
import pytest
|
||||
@pytest.fixture
|
||||
def out_of_reach(): pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
@@ -464,19 +461,19 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error):
|
||||
|
||||
def test_issue1073_conftest_special_objects(testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
"""\
|
||||
class DontTouchMe(object):
|
||||
def __getattr__(self, x):
|
||||
raise Exception('cant touch me')
|
||||
|
||||
x = DontTouchMe()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_some():
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
res = testdir.runpytest()
|
||||
assert res.ret == 0
|
||||
@@ -484,15 +481,15 @@ def test_issue1073_conftest_special_objects(testdir):
|
||||
|
||||
def test_conftest_exception_handling(testdir):
|
||||
testdir.makeconftest(
|
||||
"""
|
||||
"""\
|
||||
raise ValueError()
|
||||
"""
|
||||
"""
|
||||
)
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
"""\
|
||||
def test_some():
|
||||
pass
|
||||
"""
|
||||
"""
|
||||
)
|
||||
res = testdir.runpytest()
|
||||
assert res.ret == 4
|
||||
@@ -507,7 +504,7 @@ def test_hook_proxy(testdir):
|
||||
**{
|
||||
"root/demo-0/test_foo1.py": "def test1(): pass",
|
||||
"root/demo-a/test_foo2.py": "def test1(): pass",
|
||||
"root/demo-a/conftest.py": """
|
||||
"root/demo-a/conftest.py": """\
|
||||
def pytest_ignore_collect(path, config):
|
||||
return True
|
||||
""",
|
||||
@@ -525,11 +522,11 @@ def test_required_option_help(testdir):
|
||||
testdir.makeconftest("assert 0")
|
||||
x = testdir.mkdir("x")
|
||||
x.join("conftest.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true", required=True)
|
||||
"""
|
||||
def pytest_addoption(parser):
|
||||
parser.addoption("--xyz", action="store_true", required=True)
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("-h", x)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# encoding: utf-8
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import sys
|
||||
import _pytest._code
|
||||
import textwrap
|
||||
from _pytest.compat import MODULE_NOT_FOUND_ERROR
|
||||
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
|
||||
import pytest
|
||||
@@ -258,16 +258,16 @@ class TestDoctests(object):
|
||||
|
||||
def test_doctest_linedata_missing(self, testdir):
|
||||
testdir.tmpdir.join("hello.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
class Fun(object):
|
||||
@property
|
||||
def test(self):
|
||||
'''
|
||||
>>> a = 1
|
||||
>>> 1/0
|
||||
'''
|
||||
"""
|
||||
class Fun(object):
|
||||
@property
|
||||
def test(self):
|
||||
'''
|
||||
>>> a = 1
|
||||
>>> 1/0
|
||||
'''
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("--doctest-modules")
|
||||
@@ -300,10 +300,10 @@ class TestDoctests(object):
|
||||
|
||||
def test_doctest_unex_importerror_with_module(self, testdir):
|
||||
testdir.tmpdir.join("hello.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
import asdalsdkjaslkdjasd
|
||||
"""
|
||||
import asdalsdkjaslkdjasd
|
||||
"""
|
||||
)
|
||||
)
|
||||
testdir.maketxtfile(
|
||||
@@ -339,27 +339,27 @@ class TestDoctests(object):
|
||||
def test_doctestmodule_external_and_issue116(self, testdir):
|
||||
p = testdir.mkpydir("hello")
|
||||
p.join("__init__.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
def somefunc():
|
||||
'''
|
||||
>>> i = 0
|
||||
>>> i + 1
|
||||
2
|
||||
'''
|
||||
"""
|
||||
def somefunc():
|
||||
'''
|
||||
>>> i = 0
|
||||
>>> i + 1
|
||||
2
|
||||
'''
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest(p, "--doctest-modules")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"004 *>>> i = 0",
|
||||
"005 *>>> i + 1",
|
||||
"003 *>>> i = 0",
|
||||
"004 *>>> i + 1",
|
||||
"*Expected:",
|
||||
"* 2",
|
||||
"*Got:",
|
||||
"* 1",
|
||||
"*:5: DocTestFailure",
|
||||
"*:4: DocTestFailure",
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,9 @@ def test_version(testdir, pytestconfig):
|
||||
result = testdir.runpytest("--version")
|
||||
assert result.ret == 0
|
||||
# p = py.path.local(py.__file__).dirpath()
|
||||
result.stderr.fnmatch_lines(["*pytest*%s*imported from*" % (pytest.__version__,)])
|
||||
result.stderr.fnmatch_lines(
|
||||
["*pytest*{}*imported from*".format(pytest.__version__)]
|
||||
)
|
||||
if pytestconfig.pluginmanager.list_plugin_distinfo():
|
||||
result.stderr.fnmatch_lines(["*setuptools registered plugins:", "*at*"])
|
||||
|
||||
|
||||
@@ -941,7 +941,7 @@ def test_double_colon_split_method_issue469(testdir):
|
||||
def test_unicode_issue368(testdir):
|
||||
path = testdir.tmpdir.join("test.xml")
|
||||
log = LogXML(str(path), None)
|
||||
ustr = py.builtin._totext("ВНИ!", "utf-8")
|
||||
ustr = u"ВНИ!"
|
||||
|
||||
class Report(BaseReport):
|
||||
longrepr = ustr
|
||||
|
||||
@@ -228,11 +228,15 @@ def test_syspath_prepend(mp):
|
||||
|
||||
|
||||
def test_syspath_prepend_double_undo(mp):
|
||||
mp.syspath_prepend("hello world")
|
||||
mp.undo()
|
||||
sys.path.append("more hello world")
|
||||
mp.undo()
|
||||
assert sys.path[-1] == "more hello world"
|
||||
old_syspath = sys.path[:]
|
||||
try:
|
||||
mp.syspath_prepend("hello world")
|
||||
mp.undo()
|
||||
sys.path.append("more hello world")
|
||||
mp.undo()
|
||||
assert sys.path[-1] == "more hello world"
|
||||
finally:
|
||||
sys.path[:] = old_syspath
|
||||
|
||||
|
||||
def test_chdir_with_path_local(mp, tmpdir):
|
||||
|
||||
@@ -294,7 +294,7 @@ def test_argcomplete(testdir, monkeypatch):
|
||||
script = str(testdir.tmpdir.join("test_argcomplete"))
|
||||
pytest_bin = sys.argv[0]
|
||||
if "pytest" not in os.path.basename(pytest_bin):
|
||||
pytest.skip("need to be run with pytest executable, not %s" % (pytest_bin,))
|
||||
pytest.skip("need to be run with pytest executable, not {}".format(pytest_bin))
|
||||
|
||||
with open(str(script), "w") as fp:
|
||||
# redirect output from argcomplete to stdin and stderr is not trivial
|
||||
|
||||
@@ -260,7 +260,9 @@ class TestPDB(object):
|
||||
assert False
|
||||
"""
|
||||
)
|
||||
child = testdir.spawn_pytest("--show-capture=%s --pdb %s" % (showcapture, p1))
|
||||
child = testdir.spawn_pytest(
|
||||
"--show-capture={} --pdb {}".format(showcapture, p1)
|
||||
)
|
||||
if showcapture in ("all", "log"):
|
||||
child.expect("captured log")
|
||||
child.expect("get rekt")
|
||||
@@ -473,7 +475,7 @@ class TestPDB(object):
|
||||
x = 5
|
||||
"""
|
||||
)
|
||||
child = testdir.spawn("%s %s" % (sys.executable, p1))
|
||||
child = testdir.spawn("{} {}".format(sys.executable, p1))
|
||||
child.expect("x = 5")
|
||||
child.sendeof()
|
||||
self.flush(child)
|
||||
|
||||
@@ -25,7 +25,6 @@ class TestPytestPluginInteractions(object):
|
||||
)
|
||||
conf = testdir.makeconftest(
|
||||
"""
|
||||
import sys ; sys.path.insert(0, '.')
|
||||
import newhooks
|
||||
def pytest_addhooks(pluginmanager):
|
||||
pluginmanager.addhooks(newhooks)
|
||||
@@ -263,8 +262,7 @@ class TestPytestPluginManager(object):
|
||||
mod.pytest_plugins = "pytest_a"
|
||||
aplugin = testdir.makepyfile(pytest_a="#")
|
||||
reprec = testdir.make_hook_recorder(pytestpm)
|
||||
# syspath.prepend(aplugin.dirpath())
|
||||
sys.path.insert(0, str(aplugin.dirpath()))
|
||||
testdir.syspathinsert(aplugin.dirpath())
|
||||
pytestpm.consider_module(mod)
|
||||
call = reprec.getcall(pytestpm.hook.pytest_plugin_registered.name)
|
||||
assert call.plugin.__name__ == "pytest_a"
|
||||
|
||||
@@ -8,7 +8,7 @@ import _pytest.pytester as pytester
|
||||
from _pytest.pytester import HookRecorder
|
||||
from _pytest.pytester import CwdSnapshot, SysModulesSnapshot, SysPathsSnapshot
|
||||
from _pytest.config import PytestPluginManager
|
||||
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED
|
||||
from _pytest.main import EXIT_OK, EXIT_TESTSFAILED, EXIT_NOTESTSCOLLECTED
|
||||
|
||||
|
||||
def test_make_hook_recorder(testdir):
|
||||
@@ -215,57 +215,6 @@ class TestInlineRunModulesCleanup(object):
|
||||
assert imported.data == 42
|
||||
|
||||
|
||||
def test_inline_run_clean_sys_paths(testdir):
|
||||
def test_sys_path_change_cleanup(self, testdir):
|
||||
test_path1 = testdir.tmpdir.join("boink1").strpath
|
||||
test_path2 = testdir.tmpdir.join("boink2").strpath
|
||||
test_path3 = testdir.tmpdir.join("boink3").strpath
|
||||
sys.path.append(test_path1)
|
||||
sys.meta_path.append(test_path1)
|
||||
original_path = list(sys.path)
|
||||
original_meta_path = list(sys.meta_path)
|
||||
test_mod = testdir.makepyfile(
|
||||
"""
|
||||
import sys
|
||||
sys.path.append({:test_path2})
|
||||
sys.meta_path.append({:test_path2})
|
||||
def test_foo():
|
||||
sys.path.append({:test_path3})
|
||||
sys.meta_path.append({:test_path3})""".format(
|
||||
locals()
|
||||
)
|
||||
)
|
||||
testdir.inline_run(str(test_mod))
|
||||
assert sys.path == original_path
|
||||
assert sys.meta_path == original_meta_path
|
||||
|
||||
def spy_factory(self):
|
||||
class SysPathsSnapshotSpy(object):
|
||||
instances = []
|
||||
|
||||
def __init__(self):
|
||||
SysPathsSnapshotSpy.instances.append(self)
|
||||
self._spy_restore_count = 0
|
||||
self.__snapshot = SysPathsSnapshot()
|
||||
|
||||
def restore(self):
|
||||
self._spy_restore_count += 1
|
||||
return self.__snapshot.restore()
|
||||
|
||||
return SysPathsSnapshotSpy
|
||||
|
||||
def test_inline_run_taking_and_restoring_a_sys_paths_snapshot(
|
||||
self, testdir, monkeypatch
|
||||
):
|
||||
spy_factory = self.spy_factory()
|
||||
monkeypatch.setattr(pytester, "SysPathsSnapshot", spy_factory)
|
||||
test_mod = testdir.makepyfile("def test_foo(): pass")
|
||||
testdir.inline_run(str(test_mod))
|
||||
assert len(spy_factory.instances) == 1
|
||||
spy = spy_factory.instances[0]
|
||||
assert spy._spy_restore_count == 1
|
||||
|
||||
|
||||
def test_assert_outcomes_after_pytest_error(testdir):
|
||||
testdir.makepyfile("def test_foo(): assert True")
|
||||
|
||||
@@ -396,3 +345,8 @@ class TestSysPathsSnapshot(object):
|
||||
def test_testdir_subprocess(testdir):
|
||||
testfile = testdir.makepyfile("def test_one(): pass")
|
||||
assert testdir.runpytest_subprocess(testfile).ret == 0
|
||||
|
||||
|
||||
def test_unicode_args(testdir):
|
||||
result = testdir.runpytest("-k", u"💩")
|
||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
||||
|
||||
@@ -3,10 +3,11 @@ terminal reporting of the full testing process.
|
||||
"""
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import collections
|
||||
import os
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import pluggy
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||
@@ -161,12 +162,12 @@ class TestTerminal(object):
|
||||
def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir):
|
||||
a = testdir.mkpydir("a123")
|
||||
a.join("test_hello123.py").write(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
class TestClass(object):
|
||||
def test_method(self):
|
||||
pass
|
||||
"""
|
||||
class TestClass(object):
|
||||
def test_method(self):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
)
|
||||
result = testdir.runpytest("-v")
|
||||
@@ -312,13 +313,13 @@ class TestCollectonly(object):
|
||||
result = testdir.runpytest("--collect-only", p)
|
||||
assert result.ret == 2
|
||||
result.stdout.fnmatch_lines(
|
||||
_pytest._code.Source(
|
||||
textwrap.dedent(
|
||||
"""\
|
||||
*ERROR*
|
||||
*ImportError*
|
||||
*No module named *Errlk*
|
||||
*1 error*
|
||||
"""
|
||||
*ERROR*
|
||||
*ImportError*
|
||||
*No module named *Errlk*
|
||||
*1 error*
|
||||
"""
|
||||
).strip()
|
||||
)
|
||||
|
||||
@@ -472,7 +473,7 @@ class TestTerminalFunctional(object):
|
||||
|
||||
def test_show_deselected_items_using_markexpr_before_test_execution(self, testdir):
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
test_show_deselected="""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.foo
|
||||
@@ -491,7 +492,7 @@ class TestTerminalFunctional(object):
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"collected 3 items / 1 deselected",
|
||||
"*test_show_des*.py ..*",
|
||||
"*test_show_deselected.py ..*",
|
||||
"*= 2 passed, 1 deselected in * =*",
|
||||
]
|
||||
)
|
||||
@@ -948,6 +949,46 @@ def pytest_report_header(config, startdir):
|
||||
assert "!This is stderr!" not in stdout
|
||||
assert "!This is a warning log msg!" not in stdout
|
||||
|
||||
def test_show_capture_with_teardown_logs(self, testdir):
|
||||
"""Ensure that the capturing of teardown logs honor --show-capture setting"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import logging
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
@pytest.fixture(scope="function", autouse="True")
|
||||
def hook_each_test(request):
|
||||
yield
|
||||
sys.stdout.write("!stdout!")
|
||||
sys.stderr.write("!stderr!")
|
||||
logging.warning("!log!")
|
||||
|
||||
def test_func():
|
||||
assert False
|
||||
"""
|
||||
)
|
||||
|
||||
result = testdir.runpytest("--show-capture=stdout", "--tb=short").stdout.str()
|
||||
assert "!stdout!" in result
|
||||
assert "!stderr!" not in result
|
||||
assert "!log!" not in result
|
||||
|
||||
result = testdir.runpytest("--show-capture=stderr", "--tb=short").stdout.str()
|
||||
assert "!stdout!" not in result
|
||||
assert "!stderr!" in result
|
||||
assert "!log!" not in result
|
||||
|
||||
result = testdir.runpytest("--show-capture=log", "--tb=short").stdout.str()
|
||||
assert "!stdout!" not in result
|
||||
assert "!stderr!" not in result
|
||||
assert "!log!" in result
|
||||
|
||||
result = testdir.runpytest("--show-capture=no", "--tb=short").stdout.str()
|
||||
assert "!stdout!" not in result
|
||||
assert "!stderr!" not in result
|
||||
assert "!log!" not in result
|
||||
|
||||
|
||||
@pytest.mark.xfail("not hasattr(os, 'dup')")
|
||||
def test_fdopen_kept_alive_issue124(testdir):
|
||||
@@ -1078,9 +1119,9 @@ def test_terminal_summary_warnings_are_displayed(testdir):
|
||||
)
|
||||
def test_summary_stats(exp_line, exp_color, stats_arg):
|
||||
print("Based on stats: %s" % stats_arg)
|
||||
print('Expect summary: "%s"; with color "%s"' % (exp_line, exp_color))
|
||||
print('Expect summary: "{}"; with color "{}"'.format(exp_line, exp_color))
|
||||
(line, color) = build_summary_stats_line(stats_arg)
|
||||
print('Actually got: "%s"; with color "%s"' % (line, color))
|
||||
print('Actually got: "{}"; with color "{}"'.format(line, color))
|
||||
assert line == exp_line
|
||||
assert color == exp_color
|
||||
|
||||
@@ -1094,7 +1135,53 @@ def test_no_trailing_whitespace_after_inifile_word(testdir):
|
||||
assert "inifile: tox.ini\n" in result.stdout.str()
|
||||
|
||||
|
||||
class TestProgress(object):
|
||||
class TestClassicOutputStyle(object):
|
||||
"""Ensure classic output style works as expected (#3883)"""
|
||||
|
||||
@pytest.fixture
|
||||
def test_files(self, testdir):
|
||||
testdir.makepyfile(
|
||||
**{
|
||||
"test_one.py": "def test_one(): pass",
|
||||
"test_two.py": "def test_two(): assert 0",
|
||||
"sub/test_three.py": """
|
||||
def test_three_1(): pass
|
||||
def test_three_2(): assert 0
|
||||
def test_three_3(): pass
|
||||
""",
|
||||
}
|
||||
)
|
||||
|
||||
def test_normal_verbosity(self, testdir, test_files):
|
||||
result = testdir.runpytest("-o", "console_output_style=classic")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_one.py .",
|
||||
"test_two.py F",
|
||||
"sub{}test_three.py .F.".format(os.sep),
|
||||
"*2 failed, 3 passed in*",
|
||||
]
|
||||
)
|
||||
|
||||
def test_verbose(self, testdir, test_files):
|
||||
result = testdir.runpytest("-o", "console_output_style=classic", "-v")
|
||||
result.stdout.fnmatch_lines(
|
||||
[
|
||||
"test_one.py::test_one PASSED",
|
||||
"test_two.py::test_two FAILED",
|
||||
"sub{}test_three.py::test_three_1 PASSED".format(os.sep),
|
||||
"sub{}test_three.py::test_three_2 FAILED".format(os.sep),
|
||||
"sub{}test_three.py::test_three_3 PASSED".format(os.sep),
|
||||
"*2 failed, 3 passed in*",
|
||||
]
|
||||
)
|
||||
|
||||
def test_quiet(self, testdir, test_files):
|
||||
result = testdir.runpytest("-o", "console_output_style=classic", "-q")
|
||||
result.stdout.fnmatch_lines([".F.F.", "*2 failed, 3 passed in*"])
|
||||
|
||||
|
||||
class TestProgressOutputStyle(object):
|
||||
@pytest.fixture
|
||||
def many_tests_files(self, testdir):
|
||||
testdir.makepyfile(
|
||||
|
||||
@@ -989,3 +989,24 @@ def test_usefixtures_marker_on_unittest(base, testdir):
|
||||
|
||||
result = testdir.runpytest("-s")
|
||||
result.assert_outcomes(passed=2)
|
||||
|
||||
|
||||
def test_testcase_handles_init_exceptions(testdir):
|
||||
"""
|
||||
Regression test to make sure exceptions in the __init__ method are bubbled up correctly.
|
||||
See https://github.com/pytest-dev/pytest/issues/3788
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
from unittest import TestCase
|
||||
import pytest
|
||||
class MyTestCase(TestCase):
|
||||
def __init__(self, *args, **kwargs):
|
||||
raise Exception("should raise this exception")
|
||||
def test_hello(self):
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest()
|
||||
assert "should raise this exception" in result.stdout.str()
|
||||
assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str()
|
||||
|
||||
@@ -287,3 +287,18 @@ def test_non_string_warning_argument(testdir):
|
||||
)
|
||||
result = testdir.runpytest("-W", "always")
|
||||
result.stdout.fnmatch_lines(["*= 1 passed, 1 warnings in *"])
|
||||
|
||||
|
||||
def test_filterwarnings_mark_registration(testdir):
|
||||
"""Ensure filterwarnings mark is registered"""
|
||||
testdir.makepyfile(
|
||||
"""
|
||||
import pytest
|
||||
|
||||
@pytest.mark.filterwarnings('error')
|
||||
def test_func():
|
||||
pass
|
||||
"""
|
||||
)
|
||||
result = testdir.runpytest("--strict")
|
||||
assert result.ret == 0
|
||||
|
||||
9
tox.ini
9
tox.ini
@@ -36,8 +36,7 @@ commands =
|
||||
|
||||
|
||||
[testenv:linting]
|
||||
skipsdist = True
|
||||
usedevelop = True
|
||||
skip_install = True
|
||||
basepython = python3.6
|
||||
deps = pre-commit
|
||||
commands = pre-commit run --all-files --show-diff-on-failure
|
||||
@@ -115,8 +114,6 @@ skipsdist = True
|
||||
usedevelop = True
|
||||
changedir = doc/en
|
||||
deps =
|
||||
attrs
|
||||
more-itertools
|
||||
PyYAML
|
||||
sphinx
|
||||
sphinxcontrib-trio
|
||||
@@ -165,16 +162,18 @@ commands =
|
||||
|
||||
|
||||
[testenv:coveralls]
|
||||
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
|
||||
passenv = CI TRAVIS TRAVIS_* COVERALLS_REPO_TOKEN
|
||||
usedevelop = True
|
||||
changedir = .
|
||||
deps =
|
||||
{[testenv]deps}
|
||||
coveralls
|
||||
codecov
|
||||
commands =
|
||||
coverage run --source=_pytest -m pytest testing
|
||||
coverage report -m
|
||||
coveralls
|
||||
codecov
|
||||
|
||||
[testenv:release]
|
||||
decription = do a release, required posarg of the version number
|
||||
|
||||
Reference in New Issue
Block a user