Compare commits
181 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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)$'
|
||||
|
||||
@@ -62,12 +62,7 @@ jobs:
|
||||
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
|
||||
env: TOXENV=linting
|
||||
|
||||
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
|
||||
|
||||
127
CHANGELOG.rst
127
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,123 @@
|
||||
|
||||
.. towncrier release notes start
|
||||
|
||||
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 ``cafd`` 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)
|
||||
=========================
|
||||
|
||||
|
||||
@@ -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,9 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
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
|
||||
@@ -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,6 +200,8 @@ 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 http://pytest.org/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
|
||||
@@ -374,6 +376,8 @@ 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 http://pytest.org/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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
|
||||
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)
|
||||
6
setup.py
6
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,7 +85,7 @@ 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",
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -223,7 +223,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 +402,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 +424,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 +445,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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -201,33 +201,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 +546,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 +584,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 +761,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 +898,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()
|
||||
|
||||
@@ -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 http://pytest.org/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
|
||||
|
||||
@@ -68,12 +68,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,7 +79,7 @@ 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)
|
||||
@@ -96,7 +92,7 @@ def test_unicode_handling():
|
||||
|
||||
@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))
|
||||
|
||||
@@ -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"])
|
||||
|
||||
@@ -6,8 +6,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
|
||||
|
||||
@@ -323,7 +323,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 +392,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"
|
||||
|
||||
|
||||
@@ -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()
|
||||
@@ -1588,37 +1587,42 @@ class TestFixtureManagerParseFactories(object):
|
||||
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 +1804,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 +2715,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 +3217,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 +3666,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 +3697,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 +3978,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")
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -4,9 +4,9 @@ terminal reporting of the full testing process.
|
||||
from __future__ import absolute_import, division, print_function
|
||||
import collections
|
||||
import sys
|
||||
import textwrap
|
||||
|
||||
import pluggy
|
||||
import _pytest._code
|
||||
import py
|
||||
import pytest
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||
@@ -161,12 +161,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 +312,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()
|
||||
)
|
||||
|
||||
@@ -948,6 +948,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 +1118,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
|
||||
|
||||
|
||||
@@ -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