Compare commits
131 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f592c7746a | ||
|
|
31f114e51f | ||
|
|
833acb9d3c | ||
|
|
0febd855e1 | ||
|
|
3c81f83602 | ||
|
|
57c4489916 | ||
|
|
5365f7c9ca | ||
|
|
1f0401ab62 | ||
|
|
7480342710 | ||
|
|
db62f160e1 | ||
|
|
81528ea81f | ||
|
|
64193add91 | ||
|
|
bc0f7e6243 | ||
|
|
9ed3d76b51 | ||
|
|
c856537e71 | ||
|
|
e612619aea | ||
|
|
30f0152ae6 | ||
|
|
f8d195253e | ||
|
|
8208a77a3e | ||
|
|
8b4da9d955 | ||
|
|
454d288138 | ||
|
|
40cffacadc | ||
|
|
6473c3d87e | ||
|
|
4e1609b12e | ||
|
|
0735d4549d | ||
|
|
f25ba4dd0b | ||
|
|
788e394c93 | ||
|
|
2d7197926a | ||
|
|
0a30f072e6 | ||
|
|
0e6ad8e59f | ||
|
|
b38fad4b82 | ||
|
|
483754216f | ||
|
|
1e97ea60f7 | ||
|
|
58f28bf049 | ||
|
|
5566b3ccb6 | ||
|
|
8138d88da2 | ||
|
|
0aa891543d | ||
|
|
6c5475660a | ||
|
|
1aa5bfea11 | ||
|
|
a6084ed797 | ||
|
|
d05d19da78 | ||
|
|
fa4d5da4ca | ||
|
|
6120570198 | ||
|
|
2e6a58ab69 | ||
|
|
c1b83cdeea | ||
|
|
33796c8a13 | ||
|
|
8763590eef | ||
|
|
b3efd9aa59 | ||
|
|
33c0b06fdf | ||
|
|
38f7562c7c | ||
|
|
629d8e9fd6 | ||
|
|
a5b5090c72 | ||
|
|
a3319ffe80 | ||
|
|
1e2b2af296 | ||
|
|
632c4d5daf | ||
|
|
984d4ce5ec | ||
|
|
1eb5a690d4 | ||
|
|
06bb61bbe3 | ||
|
|
cbf261c74e | ||
|
|
0ba930a11d | ||
|
|
73d481552d | ||
|
|
50328f47db | ||
|
|
f0e0250cd5 | ||
|
|
ec69514eb2 | ||
|
|
c169c883d3 | ||
|
|
5185f2a6ae | ||
|
|
351395b7ea | ||
|
|
71b68334e2 | ||
|
|
98caeedd9e | ||
|
|
1519b38af0 | ||
|
|
3e01e83390 | ||
|
|
ad4ef4f583 | ||
|
|
5717c71179 | ||
|
|
6c8c1da428 | ||
|
|
b8c6f13b37 | ||
|
|
8e0f7d3793 | ||
|
|
aaa547e763 | ||
|
|
26b1519534 | ||
|
|
84d7068723 | ||
|
|
ab274299fe | ||
|
|
ff72db2f1a | ||
|
|
fc304b8b44 | ||
|
|
1130b9f742 | ||
|
|
552c7d4286 | ||
|
|
1e5b21cd61 | ||
|
|
0b94c43bac | ||
|
|
e46e653794 | ||
|
|
07af307e4a | ||
|
|
a190ad27f2 | ||
|
|
f331e8f576 | ||
|
|
006a901b86 | ||
|
|
45b21fa9b0 | ||
|
|
e2bb4f893b | ||
|
|
e3544553b7 | ||
|
|
1e6ed2a25a | ||
|
|
382fa231a1 | ||
|
|
6f93ffb5d4 | ||
|
|
35d154f580 | ||
|
|
4e9c633185 | ||
|
|
7f95ea31d5 | ||
|
|
f2c01c5407 | ||
|
|
60a347aeb5 | ||
|
|
11ec96a927 | ||
|
|
37dcdfbc58 | ||
|
|
2a2b8cee09 | ||
|
|
82fb63ca2d | ||
|
|
620b384b69 | ||
|
|
5cbfefbba0 | ||
|
|
95007ddeca | ||
|
|
995e60efbf | ||
|
|
918af99a2a | ||
|
|
c0719a5b4c | ||
|
|
afc1e2b0e1 | ||
|
|
de1614923f | ||
|
|
bc1f8666aa | ||
|
|
78eec0d7f8 | ||
|
|
3301a1c173 | ||
|
|
65ebc75ee8 | ||
|
|
cf13355d3f | ||
|
|
1289cbb9a5 | ||
|
|
10433db225 | ||
|
|
50b960c1f0 | ||
|
|
d47ae799a7 | ||
|
|
c93a9e3361 | ||
|
|
a1d446b8e8 | ||
|
|
7d66e4eae1 | ||
|
|
fc02003220 | ||
|
|
336d7900c5 | ||
|
|
57bb3c6922 | ||
|
|
a87b1c79c1 | ||
|
|
30f3d95aeb |
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
3
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -3,6 +3,9 @@ Thanks for submitting a PR, your contribution is really appreciated!
|
||||
Here's a quick checklist that should be present in PRs:
|
||||
|
||||
- [ ] Target: for bug or doc fixes, target `master`; for new features, target `features`;
|
||||
|
||||
Unless your change is trivial documentation fix (e.g., a typo or reword of a small section) please:
|
||||
|
||||
- [ ] Make sure to include one or more tests for your change;
|
||||
- [ ] Add yourself to `AUTHORS`;
|
||||
- [ ] Add a new entry to `CHANGELOG.rst`
|
||||
|
||||
@@ -28,6 +28,11 @@ env:
|
||||
- TESTENV=freeze
|
||||
- TESTENV=docs
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
# py35-trial failing on Linux: #1989
|
||||
- env: TESTENV=py35-trial
|
||||
|
||||
script: tox --recreate -e $TESTENV
|
||||
|
||||
notifications:
|
||||
|
||||
8
AUTHORS
8
AUTHORS
@@ -36,6 +36,7 @@ Christopher Gilling
|
||||
Daniel Grana
|
||||
Daniel Hahler
|
||||
Daniel Nuri
|
||||
Daniel Wandschneider
|
||||
Danielle Jenkins
|
||||
Dave Hunt
|
||||
David Díaz-Barquero
|
||||
@@ -43,6 +44,7 @@ David Mohr
|
||||
David Vierra
|
||||
Diego Russo
|
||||
Dmitry Dygalo
|
||||
Duncan Betts
|
||||
Edison Gustavo Muenz
|
||||
Edoardo Batini
|
||||
Eduardo Schettino
|
||||
@@ -59,6 +61,7 @@ Georgy Dyuldin
|
||||
Graham Horler
|
||||
Greg Price
|
||||
Grig Gheorghiu
|
||||
Grigorii Eremeev (budulianin)
|
||||
Guido Wesdorp
|
||||
Harald Armin Massa
|
||||
Ian Bicking
|
||||
@@ -80,6 +83,7 @@ Kevin Cox
|
||||
Lee Kamentsky
|
||||
Lev Maximov
|
||||
Lukas Bednar
|
||||
Luke Murphy
|
||||
Maciek Fijalkowski
|
||||
Maho
|
||||
Marc Schlaich
|
||||
@@ -89,6 +93,7 @@ Markus Unterwaditzer
|
||||
Martijn Faassen
|
||||
Martin K. Scherer
|
||||
Martin Prusse
|
||||
Mathieu Clabaut
|
||||
Matt Bachmann
|
||||
Matt Williams
|
||||
Matthias Hafner
|
||||
@@ -96,7 +101,10 @@ mbyt
|
||||
Michael Aquilina
|
||||
Michael Birtwell
|
||||
Michael Droettboom
|
||||
Michael Seifert
|
||||
Mike Lundy
|
||||
Ned Batchelder
|
||||
Neven Mundar
|
||||
Nicolas Delaby
|
||||
Oleg Pidsadnyi
|
||||
Oliver Bestwalter
|
||||
|
||||
129
CHANGELOG.rst
129
CHANGELOG.rst
@@ -1,3 +1,125 @@
|
||||
3.0.5
|
||||
=====
|
||||
|
||||
* Add warning when not passing ``option=value`` correctly to ``-o/--override-ini`` (`#2105`_).
|
||||
Also improved the help documentation. Thanks to `@mbukatov`_ for the report and
|
||||
`@lwm`_ for the PR.
|
||||
|
||||
* Now ``--confcutdir`` and ``--junit-xml`` are properly validated if they are directories
|
||||
and filenames, respectively (`#2089`_ and `#2078`_). Thanks to `@lwm`_ for the PR.
|
||||
|
||||
* Add hint to error message hinting possible missing ``__init__.py`` (`#478`_). Thanks `@DuncanBetts`_.
|
||||
|
||||
* More accurately describe when fixture finalization occurs in documentation (`#687`_). Thanks `@DuncanBetts`_.
|
||||
|
||||
* Provide ``:ref:`` targets for ``recwarn.rst`` so we can use intersphinx referencing.
|
||||
Thanks to `@dupuy`_ for the report and `@lwm`_ for the PR.
|
||||
|
||||
* In Python 2, use a simple ``+-`` ASCII string in the string representation of ``pytest.approx`` (for example ``"4 +- 4.0e-06"``)
|
||||
because it is brittle to handle that in different contexts and representations internally in pytest
|
||||
which can result in bugs such as `#2111`_. In Python 3, the representation still uses ``±`` (for example ``4 ± 4.0e-06``).
|
||||
Thanks `@kerrick-lyft`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Using ``item.Function``, ``item.Module``, etc., is now issuing deprecation warnings, prefer
|
||||
``pytest.Function``, ``pytest.Module``, etc., instead (`#2034`_).
|
||||
Thanks `@nmundar`_ for the PR.
|
||||
|
||||
* Fix error message using ``approx`` with complex numbers (`#2082`_).
|
||||
Thanks `@adler-j`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fixed false-positives warnings from assertion rewrite hook for modules imported more than
|
||||
once by the ``pytest_plugins`` mechanism.
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Remove an internal cache which could cause hooks from ``conftest.py`` files in
|
||||
sub-directories to be called in other directories incorrectly (`#2016`_).
|
||||
Thanks `@d-b-w`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Remove internal code meant to support earlier Python 3 versions that produced the side effect
|
||||
of leaving ``None`` in ``sys.modules`` when expressions were evaluated by pytest (for example passing a condition
|
||||
as a string to ``pytest.mark.skipif``)(`#2103`_).
|
||||
Thanks `@jaraco`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Cope gracefully with a .pyc file with no matching .py file (`#2038`_). Thanks
|
||||
`@nedbat`_.
|
||||
|
||||
.. _@adler-j: https://github.com/adler-j
|
||||
.. _@d-b-w: https://bitbucket.org/d-b-w/
|
||||
.. _@DuncanBetts: https://github.com/DuncanBetts
|
||||
.. _@dupuy: https://bitbucket.org/dupuy/
|
||||
.. _@kerrick-lyft: https://github.com/kerrick-lyft
|
||||
.. _@lwm: https://github.com/lwm
|
||||
.. _@mbukatov: https://github.com/mbukatov
|
||||
.. _@nedbat: https://github.com/nedbat
|
||||
.. _@nmundar: https://github.com/nmundar
|
||||
|
||||
.. _#2016: https://github.com/pytest-dev/pytest/issues/2016
|
||||
.. _#2034: https://github.com/pytest-dev/pytest/issues/2034
|
||||
.. _#2038: https://github.com/pytest-dev/pytest/issues/2038
|
||||
.. _#2078: https://github.com/pytest-dev/pytest/issues/2078
|
||||
.. _#2082: https://github.com/pytest-dev/pytest/issues/2082
|
||||
.. _#2089: https://github.com/pytest-dev/pytest/issues/2089
|
||||
.. _#2103: https://github.com/pytest-dev/pytest/issues/2103
|
||||
.. _#2105: https://github.com/pytest-dev/pytest/issues/2105
|
||||
.. _#2111: https://github.com/pytest-dev/pytest/issues/2111
|
||||
.. _#478: https://github.com/pytest-dev/pytest/issues/478
|
||||
.. _#687: https://github.com/pytest-dev/pytest/issues/687
|
||||
|
||||
|
||||
3.0.4
|
||||
=====
|
||||
|
||||
* Import errors when collecting test modules now display the full traceback (`#1976`_).
|
||||
Thanks `@cwitty`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix confusing command-line help message for custom options with two or more ``metavar`` properties (`#2004`_).
|
||||
Thanks `@okulynyak`_ and `@davehunt`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* When loading plugins, import errors which contain non-ascii messages are now properly handled in Python 2 (`#1998`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fixed cyclic reference when ``pytest.raises`` is used in context-manager form (`#1965`_). Also as a
|
||||
result of this fix, ``sys.exc_info()`` is left empty in both context-manager and function call usages.
|
||||
Previously, ``sys.exc_info`` would contain the exception caught by the context manager,
|
||||
even when the expected exception occurred.
|
||||
Thanks `@MSeifert04`_ for the report and the PR.
|
||||
|
||||
* Fixed false-positives warnings from assertion rewrite hook for modules that were rewritten but
|
||||
were later marked explicitly by ``pytest.register_assert_rewrite``
|
||||
or implicitly as a plugin (`#2005`_).
|
||||
Thanks `@RonnyPfannschmidt`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Report teardown output on test failure (`#442`_).
|
||||
Thanks `@matclab`_ for the PR.
|
||||
|
||||
* Fix teardown error message in generated xUnit XML.
|
||||
Thanks `@gdyuldin`_ for the PR.
|
||||
|
||||
* Properly handle exceptions in ``multiprocessing`` tasks (`#1984`_).
|
||||
Thanks `@adborden`_ for the report and `@nicoddemus`_ for the PR.
|
||||
|
||||
* Clean up unittest TestCase objects after tests are complete (`#1649`_).
|
||||
Thanks `@d_b_w`_ for the report and PR.
|
||||
|
||||
|
||||
.. _@adborden: https://github.com/adborden
|
||||
.. _@cwitty: https://github.com/cwitty
|
||||
.. _@d_b_w: https://github.com/d_b_w
|
||||
.. _@gdyuldin: https://github.com/gdyuldin
|
||||
.. _@matclab: https://github.com/matclab
|
||||
.. _@MSeifert04: https://github.com/MSeifert04
|
||||
.. _@okulynyak: https://github.com/okulynyak
|
||||
|
||||
.. _#442: https://github.com/pytest-dev/pytest/issues/442
|
||||
.. _#1965: https://github.com/pytest-dev/pytest/issues/1965
|
||||
.. _#1976: https://github.com/pytest-dev/pytest/issues/1976
|
||||
.. _#1984: https://github.com/pytest-dev/pytest/issues/1984
|
||||
.. _#1998: https://github.com/pytest-dev/pytest/issues/1998
|
||||
.. _#2004: https://github.com/pytest-dev/pytest/issues/2004
|
||||
.. _#2005: https://github.com/pytest-dev/pytest/issues/2005
|
||||
.. _#1649: https://github.com/pytest-dev/pytest/issues/1649
|
||||
|
||||
|
||||
3.0.3
|
||||
=====
|
||||
|
||||
@@ -9,7 +131,7 @@
|
||||
(``pip install -e``) (`#1934`_).
|
||||
Thanks `@nicoddemus`_ for the PR.
|
||||
|
||||
* Fix pkg_resources import error in Jython projects (`#1853`).
|
||||
* Fix pkg_resources import error in Jython projects (`#1853`_).
|
||||
Thanks `@raquel-ucl`_ for the PR.
|
||||
|
||||
* Got rid of ``AttributeError: 'Module' object has no attribute '_obj'`` exception
|
||||
@@ -29,6 +151,7 @@
|
||||
.. _@axil: https://github.com/axil
|
||||
.. _@tgoodlet: https://github.com/tgoodlet
|
||||
|
||||
.. _#1853: https://github.com/pytest-dev/pytest/issues/1853
|
||||
.. _#1905: https://github.com/pytest-dev/pytest/issues/1905
|
||||
.. _#1934: https://github.com/pytest-dev/pytest/issues/1934
|
||||
.. _#1944: https://github.com/pytest-dev/pytest/issues/1944
|
||||
@@ -59,7 +182,7 @@
|
||||
enabled. This allows proper post mortem debugging for all applications
|
||||
which have significant logic in their tearDown machinery (`#1890`_). Thanks
|
||||
`@mbyt`_ for the PR.
|
||||
|
||||
|
||||
* Fix use of deprecated ``getfuncargvalue`` method in the internal doctest plugin.
|
||||
Thanks `@ViviCoder`_ for the report (`#1898`_).
|
||||
|
||||
@@ -356,7 +479,7 @@ time or change existing behaviors in order to make them less surprising/more use
|
||||
|
||||
* Refined logic for determining the ``rootdir``, considering only valid
|
||||
paths which fixes a number of issues: `#1594`_, `#1435`_ and `#1471`_.
|
||||
Updated the documentation according to current behavior. Thanks to
|
||||
Updated the documentation according to current behavior. Thanks to
|
||||
`@blueyed`_, `@davehunt`_ and `@matthiasha`_ for the PR.
|
||||
|
||||
* Always include full assertion explanation. The previous behaviour was hiding
|
||||
|
||||
@@ -79,6 +79,16 @@ Pytest could always use more documentation. What exactly is needed?
|
||||
You can also edit documentation files directly in the GitHub web interface,
|
||||
without using a local copy. This can be convenient for small fixes.
|
||||
|
||||
.. note::
|
||||
Build the documentation locally with the following command:
|
||||
|
||||
.. code:: bash
|
||||
|
||||
$ tox -e docs
|
||||
|
||||
The built documentation should be available in the ``doc/en/_build/``.
|
||||
|
||||
Where 'en' refers to the documentation language.
|
||||
|
||||
.. _submitplugin:
|
||||
|
||||
@@ -199,13 +209,10 @@ but here is a simple overview:
|
||||
You need to have Python 2.7 and 3.5 available in your system. Now
|
||||
running tests is as simple as issuing this command::
|
||||
|
||||
$ python3 runtox.py -e linting,py27,py35
|
||||
$ tox -e linting,py27,py35
|
||||
|
||||
This command will run tests via the "tox" tool against Python 2.7 and 3.5
|
||||
and also perform "lint" coding-style checks. ``runtox.py`` is
|
||||
a thin wrapper around ``tox`` which installs from a development package
|
||||
index where newer (not yet released to PyPI) versions of dependencies
|
||||
(especially ``py``) might be present.
|
||||
and also perform "lint" coding-style checks.
|
||||
|
||||
#. You can now edit your local working copy.
|
||||
|
||||
@@ -214,11 +221,11 @@ but here is a simple overview:
|
||||
To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on
|
||||
failure) to pytest you can do::
|
||||
|
||||
$ python3 runtox.py -e py27 -- --pdb
|
||||
$ tox -e py27 -- --pdb
|
||||
|
||||
Or to only run tests in a particular test module on Python 3.5::
|
||||
|
||||
$ python3 runtox.py -e py35 -- testing/test_config.py
|
||||
$ tox -e py35 -- testing/test_config.py
|
||||
|
||||
#. Commit and push once your tests pass and you are happy with your change(s)::
|
||||
|
||||
|
||||
16
MANIFEST.in
16
MANIFEST.in
@@ -9,24 +9,28 @@ include HOWTORELEASE.rst
|
||||
include tox.ini
|
||||
include setup.py
|
||||
|
||||
include .coveragerc
|
||||
recursive-include scripts *.py
|
||||
recursive-include scripts *.bat
|
||||
|
||||
include plugin-test.sh
|
||||
include requirements-docs.txt
|
||||
include runtox.py
|
||||
include .coveragerc
|
||||
|
||||
recursive-include bench *.py
|
||||
recursive-include extra *.py
|
||||
|
||||
graft testing
|
||||
graft doc
|
||||
prune doc/en/_build
|
||||
|
||||
exclude _pytest/impl
|
||||
|
||||
graft _pytest/vendored_packages
|
||||
|
||||
recursive-exclude * *.pyc *.pyo
|
||||
recursive-exclude testing/.hypothesis *
|
||||
recursive-exclude testing/freeze/~ *
|
||||
recursive-exclude testing/freeze/build *
|
||||
recursive-exclude testing/freeze/dist *
|
||||
|
||||
exclude appveyor/install.ps1
|
||||
exclude appveyor.yml
|
||||
exclude appveyor
|
||||
exclude .travis.yml
|
||||
prune .github
|
||||
|
||||
18
README.rst
18
README.rst
@@ -24,31 +24,31 @@ An example of a simple test:
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
def inc(x):
|
||||
return x + 1
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
assert inc(3) == 5
|
||||
|
||||
|
||||
To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
============================= test session starts =============================
|
||||
collected 1 items
|
||||
|
||||
test_sample.py F
|
||||
|
||||
======= FAILURES ========
|
||||
_______ test_answer ________
|
||||
================================== FAILURES ===================================
|
||||
_________________________________ test_answer _________________________________
|
||||
|
||||
def test_answer():
|
||||
> assert func(3) == 5
|
||||
> assert inc(3) == 5
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
========================== 1 failed in 0.04 seconds ===========================
|
||||
|
||||
|
||||
Due to ``pytest``'s detailed assertion introspection, only plain ``assert`` statements are used. See `getting-started <http://docs.pytest.org/en/latest/getting-started.html#our-first-test-run>`_ for more examples.
|
||||
@@ -89,7 +89,7 @@ Please use the `GitHub issue tracker <https://github.com/pytest-dev/pytest/issue
|
||||
Changelog
|
||||
---------
|
||||
|
||||
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`_ page for fixes and enhancements of each version.
|
||||
Consult the `Changelog <http://docs.pytest.org/en/latest/changelog.html>`__ page for fixes and enhancements of each version.
|
||||
|
||||
|
||||
License
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#
|
||||
__version__ = '3.0.3'
|
||||
__version__ = '3.0.5'
|
||||
|
||||
@@ -87,6 +87,7 @@ class FastFilesCompleter:
|
||||
completion.append(x[prefix_dir:])
|
||||
return completion
|
||||
|
||||
|
||||
if os.environ.get('_ARGCOMPLETE'):
|
||||
try:
|
||||
import argcomplete.completers
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import sys
|
||||
from inspect import CO_VARARGS, CO_VARKEYWORDS
|
||||
import re
|
||||
from weakref import ref
|
||||
|
||||
import py
|
||||
builtin_repr = repr
|
||||
@@ -230,7 +231,7 @@ class TracebackEntry(object):
|
||||
return False
|
||||
|
||||
if py.builtin.callable(tbh):
|
||||
return tbh(self._excinfo)
|
||||
return tbh(None if self._excinfo is None else self._excinfo())
|
||||
else:
|
||||
return tbh
|
||||
|
||||
@@ -342,6 +343,7 @@ class Traceback(list):
|
||||
l.append(entry.frame.f_locals)
|
||||
return None
|
||||
|
||||
|
||||
co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2',
|
||||
'?', 'eval')
|
||||
|
||||
@@ -370,7 +372,7 @@ class ExceptionInfo(object):
|
||||
#: the exception type name
|
||||
self.typename = self.type.__name__
|
||||
#: the exception traceback (_pytest._code.Traceback instance)
|
||||
self.traceback = _pytest._code.Traceback(self.tb, excinfo=self)
|
||||
self.traceback = _pytest._code.Traceback(self.tb, excinfo=ref(self))
|
||||
|
||||
def __repr__(self):
|
||||
return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback))
|
||||
@@ -623,16 +625,23 @@ class FormattedExcinfo(object):
|
||||
e = excinfo.value
|
||||
descr = None
|
||||
while e is not None:
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
if excinfo:
|
||||
reprtraceback = self.repr_traceback(excinfo)
|
||||
reprcrash = excinfo._getreprcrash()
|
||||
else:
|
||||
# fallback to native repr if the exception doesn't have a traceback:
|
||||
# ExceptionInfo objects require a full traceback to work
|
||||
reprtraceback = ReprTracebackNative(py.std.traceback.format_exception(type(e), e, None))
|
||||
reprcrash = None
|
||||
|
||||
repr_chain += [(reprtraceback, reprcrash, descr)]
|
||||
if e.__cause__ is not None:
|
||||
e = e.__cause__
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
|
||||
descr = 'The above exception was the direct cause of the following exception:'
|
||||
elif e.__context__ is not None:
|
||||
e = e.__context__
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__))
|
||||
excinfo = ExceptionInfo((type(e), e, e.__traceback__)) if e.__traceback__ else None
|
||||
descr = 'During handling of the above exception, another exception occurred:'
|
||||
else:
|
||||
e = None
|
||||
@@ -838,6 +847,7 @@ def getrawcode(obj, trycall=True):
|
||||
return x
|
||||
return obj
|
||||
|
||||
|
||||
if sys.version_info[:2] >= (3, 5): # RecursionError introduced in 3.5
|
||||
def is_recursion_error(excinfo):
|
||||
return excinfo.errisinstance(RecursionError) # noqa
|
||||
|
||||
@@ -4,7 +4,6 @@ from bisect import bisect_right
|
||||
import sys
|
||||
import inspect, tokenize
|
||||
import py
|
||||
from types import ModuleType
|
||||
cpy_compile = compile
|
||||
|
||||
try:
|
||||
@@ -192,14 +191,6 @@ class Source(object):
|
||||
if flag & _AST_FLAG:
|
||||
return co
|
||||
lines = [(x + "\n") for x in self.lines]
|
||||
if sys.version_info[0] >= 3:
|
||||
# XXX py3's inspect.getsourcefile() checks for a module
|
||||
# and a pep302 __loader__ ... we don't have a module
|
||||
# at code compile-time so we need to fake it here
|
||||
m = ModuleType("_pycodecompile_pseudo_module")
|
||||
py.std.inspect.modulesbyfile[filename] = None
|
||||
py.std.sys.modules[None] = m
|
||||
m.__loader__ = 1
|
||||
py.std.linecache.cache[filename] = (1, None, lines, filename)
|
||||
return co
|
||||
|
||||
@@ -265,6 +256,7 @@ def findsource(obj):
|
||||
source.lines = [line.rstrip() for line in sourcelines]
|
||||
return source, lineno
|
||||
|
||||
|
||||
def getsource(obj, **kwargs):
|
||||
import _pytest._code
|
||||
obj = _pytest._code.getrawcode(obj)
|
||||
@@ -275,6 +267,7 @@ def getsource(obj, **kwargs):
|
||||
assert isinstance(strsrc, str)
|
||||
return Source(strsrc, **kwargs)
|
||||
|
||||
|
||||
def deindent(lines, offset=None):
|
||||
if offset is None:
|
||||
for line in lines:
|
||||
@@ -288,6 +281,7 @@ def deindent(lines, offset=None):
|
||||
if offset == 0:
|
||||
return list(lines)
|
||||
newlines = []
|
||||
|
||||
def readline_generator(lines):
|
||||
for line in lines:
|
||||
yield line + '\n'
|
||||
|
||||
@@ -29,7 +29,7 @@ def pytest_namespace():
|
||||
|
||||
|
||||
def register_assert_rewrite(*names):
|
||||
"""Register a module name to be rewritten on import.
|
||||
"""Register one or more module names to be rewritten on import.
|
||||
|
||||
This function will make sure that this module or all modules inside
|
||||
the package will get their assert statements rewritten.
|
||||
@@ -80,10 +80,12 @@ def install_importhook(config):
|
||||
config._assertstate.hook = hook = rewrite.AssertionRewritingHook(config)
|
||||
sys.meta_path.insert(0, hook)
|
||||
config._assertstate.trace('installed rewrite import hook')
|
||||
|
||||
def undo():
|
||||
hook = config._assertstate.hook
|
||||
if hook is not None and hook in sys.meta_path:
|
||||
sys.meta_path.remove(hook)
|
||||
|
||||
config.add_cleanup(undo)
|
||||
return hook
|
||||
|
||||
|
||||
@@ -51,6 +51,7 @@ class AssertionRewritingHook(object):
|
||||
self.fnpats = config.getini("python_files")
|
||||
self.session = None
|
||||
self.modules = {}
|
||||
self._rewritten_names = set()
|
||||
self._register_with_pkg_resources()
|
||||
self._must_rewrite = set()
|
||||
|
||||
@@ -79,7 +80,12 @@ class AssertionRewritingHook(object):
|
||||
tp = desc[2]
|
||||
if tp == imp.PY_COMPILED:
|
||||
if hasattr(imp, "source_from_cache"):
|
||||
fn = imp.source_from_cache(fn)
|
||||
try:
|
||||
fn = imp.source_from_cache(fn)
|
||||
except ValueError:
|
||||
# Python 3 doesn't like orphaned but still-importable
|
||||
# .pyc files.
|
||||
fn = fn[:-1]
|
||||
else:
|
||||
fn = fn[:-1]
|
||||
elif tp != imp.PY_SOURCE:
|
||||
@@ -92,6 +98,8 @@ class AssertionRewritingHook(object):
|
||||
if not self._should_rewrite(name, fn_pypath, state):
|
||||
return None
|
||||
|
||||
self._rewritten_names.add(name)
|
||||
|
||||
# The requested module looks like a test file, so rewrite it. This is
|
||||
# the most magical part of the process: load the source, rewrite the
|
||||
# asserts, and load the rewritten source. We also cache the rewritten
|
||||
@@ -178,14 +186,15 @@ class AssertionRewritingHook(object):
|
||||
"""
|
||||
already_imported = set(names).intersection(set(sys.modules))
|
||||
if already_imported:
|
||||
self._warn_already_imported(already_imported)
|
||||
for name in already_imported:
|
||||
if name not in self._rewritten_names:
|
||||
self._warn_already_imported(name)
|
||||
self._must_rewrite.update(names)
|
||||
|
||||
def _warn_already_imported(self, names):
|
||||
def _warn_already_imported(self, name):
|
||||
self.config.warn(
|
||||
'P1',
|
||||
'Modules are already imported so can not be re-written: %s' %
|
||||
','.join(names))
|
||||
'Module already imported so can not be re-written: %s' % name)
|
||||
|
||||
def load_module(self, name):
|
||||
# If there is an existing module object named 'fullname' in
|
||||
@@ -271,6 +280,7 @@ def _write_pyc(state, co, source_stat, pyc):
|
||||
fp.close()
|
||||
return True
|
||||
|
||||
|
||||
RN = "\r\n".encode("utf-8")
|
||||
N = "\n".encode("utf-8")
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ class CaptureManager:
|
||||
item.add_report_section(when, "stdout", out)
|
||||
item.add_report_section(when, "stderr", err)
|
||||
|
||||
|
||||
error_capsysfderror = "cannot use capsys and capfd at the same time"
|
||||
|
||||
|
||||
|
||||
@@ -213,4 +213,18 @@ def _is_unittest_unexpected_success_a_failure():
|
||||
Changed in version 3.4: Returns False if there were any
|
||||
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
|
||||
"""
|
||||
return sys.version_info >= (3, 4)
|
||||
return sys.version_info >= (3, 4)
|
||||
|
||||
|
||||
if _PY3:
|
||||
def safe_str(v):
|
||||
"""returns v as string"""
|
||||
return str(v)
|
||||
else:
|
||||
def safe_str(v):
|
||||
"""returns v as string, converting to ascii if necessary"""
|
||||
try:
|
||||
return str(v)
|
||||
except UnicodeError:
|
||||
errors = 'replace'
|
||||
return v.encode('ascii', errors)
|
||||
|
||||
@@ -12,6 +12,7 @@ import _pytest._code
|
||||
import _pytest.hookspec # the extension point definitions
|
||||
import _pytest.assertion
|
||||
from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker
|
||||
from _pytest.compat import safe_str
|
||||
|
||||
hookimpl = HookimplMarker("pytest")
|
||||
hookspec = HookspecMarker("pytest")
|
||||
@@ -64,9 +65,33 @@ def main(args=None, plugins=None):
|
||||
class cmdline: # compatibility namespace
|
||||
main = staticmethod(main)
|
||||
|
||||
|
||||
class UsageError(Exception):
|
||||
""" error in pytest usage or invocation"""
|
||||
|
||||
|
||||
def filename_arg(path, optname):
|
||||
""" Argparse type validator for filename arguments.
|
||||
|
||||
:path: path of filename
|
||||
:optname: name of the option
|
||||
"""
|
||||
if os.path.isdir(path):
|
||||
raise UsageError("{0} must be a filename, given: {1}".format(optname, path))
|
||||
return path
|
||||
|
||||
|
||||
def directory_arg(path, optname):
|
||||
"""Argparse type validator for directory arguments.
|
||||
|
||||
:path: path of directory
|
||||
:optname: name of the option
|
||||
"""
|
||||
if not os.path.isdir(path):
|
||||
raise UsageError("{0} must be a directory, given: {1}".format(optname, path))
|
||||
return path
|
||||
|
||||
|
||||
_preinit = []
|
||||
|
||||
default_plugins = (
|
||||
@@ -405,7 +430,7 @@ class PytestPluginManager(PluginManager):
|
||||
try:
|
||||
__import__(importspec)
|
||||
except ImportError as e:
|
||||
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, e))
|
||||
new_exc = ImportError('Error importing plugin "%s": %s' % (modname, safe_str(e.args[0])))
|
||||
# copy over name and path attributes
|
||||
for attr in ('name', 'path'):
|
||||
if hasattr(e, attr):
|
||||
@@ -593,7 +618,7 @@ class Argument:
|
||||
if typ == 'choice':
|
||||
warnings.warn(
|
||||
'type argument to addoption() is a string %r.'
|
||||
' For parsearg this is optional and when supplied '
|
||||
' For parsearg this is optional and when supplied'
|
||||
' should be a type.'
|
||||
' (options: %s)' % (typ, names),
|
||||
DeprecationWarning,
|
||||
@@ -792,7 +817,7 @@ class DropShorterLongHelpFormatter(argparse.HelpFormatter):
|
||||
if len(option) == 2 or option[2] == ' ':
|
||||
return_list.append(option)
|
||||
if option[2:] == short_long.get(option.replace('-', '')):
|
||||
return_list.append(option.replace(' ', '='))
|
||||
return_list.append(option.replace(' ', '=', 1))
|
||||
action._formatted_action_invocation = ', '.join(return_list)
|
||||
return action._formatted_action_invocation
|
||||
|
||||
@@ -817,9 +842,11 @@ class Notset:
|
||||
def __repr__(self):
|
||||
return "<NOTSET>"
|
||||
|
||||
|
||||
notset = Notset()
|
||||
FILE_OR_DIR = 'file_or_dir'
|
||||
|
||||
|
||||
class Config(object):
|
||||
""" access to configuration values, pluginmanager and plugin hooks. """
|
||||
|
||||
@@ -842,9 +869,11 @@ class Config(object):
|
||||
self._warn = self.pluginmanager._warn
|
||||
self.pluginmanager.register(self, "pytestconfig")
|
||||
self._configured = False
|
||||
|
||||
def do_setns(dic):
|
||||
import pytest
|
||||
setns(pytest, dic)
|
||||
|
||||
self.hook.pytest_namespace.call_historic(do_setns, {})
|
||||
self.hook.pytest_addoption.call_historic(kwargs=dict(parser=self._parser))
|
||||
|
||||
@@ -1000,6 +1029,7 @@ class Config(object):
|
||||
self.pluginmanager.load_setuptools_entrypoints(entrypoint_name)
|
||||
self.pluginmanager.consider_env()
|
||||
self.known_args_namespace = ns = self._parser.parse_known_args(args, namespace=self.option.copy())
|
||||
confcutdir = self.known_args_namespace.confcutdir
|
||||
if self.known_args_namespace.confcutdir is None and self.inifile:
|
||||
confcutdir = py.path.local(self.inifile).dirname
|
||||
self.known_args_namespace.confcutdir = confcutdir
|
||||
@@ -1119,7 +1149,10 @@ class Config(object):
|
||||
if self.getoption("override_ini", None):
|
||||
for ini_config_list in self.option.override_ini:
|
||||
for ini_config in ini_config_list:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
try:
|
||||
(key, user_ini_value) = ini_config.split("=", 1)
|
||||
except ValueError:
|
||||
raise UsageError("-o/--override-ini expects option=value style.")
|
||||
if key == name:
|
||||
value = user_ini_value
|
||||
return value
|
||||
|
||||
@@ -31,10 +31,12 @@ def pytest_configure(config):
|
||||
pytestPDB._pdb_cls = pdb_cls
|
||||
|
||||
old = (pdb.set_trace, pytestPDB._pluginmanager)
|
||||
|
||||
def fin():
|
||||
pdb.set_trace, pytestPDB._pluginmanager = old
|
||||
pytestPDB._config = None
|
||||
pytestPDB._pdb_cls = pdb.Pdb
|
||||
|
||||
pdb.set_trace = pytest.set_trace
|
||||
pytestPDB._pluginmanager = config.pluginmanager
|
||||
pytestPDB._config = config
|
||||
|
||||
@@ -32,11 +32,13 @@ scope2props["function"] = scope2props["instance"] + ("function", "keywords")
|
||||
def scopeproperty(name=None, doc=None):
|
||||
def decoratescope(func):
|
||||
scopename = name or func.__name__
|
||||
|
||||
def provide(self):
|
||||
if func.__name__ in scope2props[self.scope]:
|
||||
return func(self)
|
||||
raise AttributeError("%s not available in %s-scoped context" % (
|
||||
scopename, self.scope))
|
||||
|
||||
return property(provide, None, None, func.__doc__)
|
||||
return decoratescope
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ def pytest_addoption(parser):
|
||||
group._addoption(
|
||||
'-o', '--override-ini', nargs='*', dest="override_ini",
|
||||
action="append",
|
||||
help="override config option, e.g. `-o xfail_strict=True`.")
|
||||
help="override config option with option=value style, e.g. `-o xfail_strict=True`.")
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
@@ -41,12 +41,14 @@ def pytest_cmdline_parse():
|
||||
config.trace.root.setwriter(debugfile.write)
|
||||
undo_tracing = config.pluginmanager.enable_tracing()
|
||||
sys.stderr.write("writing pytestdebug information to %s\n" % path)
|
||||
|
||||
def unset_tracing():
|
||||
debugfile.close()
|
||||
sys.stderr.write("wrote pytestdebug information to %s\n" %
|
||||
debugfile.name)
|
||||
config.trace.root.setwriter(None)
|
||||
undo_tracing()
|
||||
|
||||
config.add_cleanup(unset_tracing)
|
||||
|
||||
def pytest_cmdline_main(config):
|
||||
@@ -71,8 +73,8 @@ def showhelp(config):
|
||||
tw.write(config._parser.optparser.format_help())
|
||||
tw.line()
|
||||
tw.line()
|
||||
tw.line("[pytest] ini-options in the next "
|
||||
"pytest.ini|tox.ini|setup.cfg file:")
|
||||
tw.line("[pytest] ini-options in the first "
|
||||
"pytest.ini|tox.ini|setup.cfg file found:")
|
||||
tw.line()
|
||||
|
||||
for name in config._parser._ininames:
|
||||
|
||||
@@ -8,12 +8,14 @@ Based on initial code from Ross Lawley.
|
||||
# Output conforms to https://github.com/jenkinsci/xunit-plugin/blob/master/
|
||||
# src/main/resources/org/jenkinsci/plugins/xunit/types/model/xsd/junit-10.xsd
|
||||
|
||||
import functools
|
||||
import py
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import pytest
|
||||
from _pytest.config import filename_arg
|
||||
|
||||
# Python 2.X and 3.X compatibility
|
||||
if sys.version_info[0] < 3:
|
||||
@@ -27,6 +29,7 @@ else:
|
||||
class Junit(py.xml.Namespace):
|
||||
pass
|
||||
|
||||
|
||||
# We need to get the subset of the invalid unicode ranges according to
|
||||
# XML 1.0 which are valid in this python build. Hence we calculate
|
||||
# this dynamically instead of hardcoding it. The spec range of valid
|
||||
@@ -156,8 +159,12 @@ class _NodeReporter(object):
|
||||
Junit.skipped, "collection skipped", report.longrepr)
|
||||
|
||||
def append_error(self, report):
|
||||
if getattr(report, 'when', None) == 'teardown':
|
||||
msg = "test teardown failure"
|
||||
else:
|
||||
msg = "test setup failure"
|
||||
self._add_simple(
|
||||
Junit.error, "test setup failure", report.longrepr)
|
||||
Junit.error, msg, report.longrepr)
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_skipped(self, report):
|
||||
@@ -209,6 +216,7 @@ def pytest_addoption(parser):
|
||||
action="store",
|
||||
dest="xmlpath",
|
||||
metavar="path",
|
||||
type=functools.partial(filename_arg, optname="--junitxml"),
|
||||
default=None,
|
||||
help="create junit-xml style report file at given path.")
|
||||
group.addoption(
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
""" core implementation of testing process: init, session, runtest loop. """
|
||||
import functools
|
||||
import os
|
||||
import sys
|
||||
|
||||
@@ -11,6 +12,7 @@ try:
|
||||
except ImportError:
|
||||
from UserDict import DictMixin as MappingMixin
|
||||
|
||||
from _pytest.config import directory_arg
|
||||
from _pytest.runner import collect_one_node
|
||||
|
||||
tracebackcutdir = py.path.local(_pytest.__file__).dirpath()
|
||||
@@ -58,7 +60,7 @@ def pytest_addoption(parser):
|
||||
# when changing this to --conf-cut-dir, config.py Conftest.setinitial
|
||||
# needs upgrading as well
|
||||
group.addoption('--confcutdir', dest="confcutdir", default=None,
|
||||
metavar="dir",
|
||||
metavar="dir", type=functools.partial(directory_arg, optname="--confcutdir"),
|
||||
help="only load conftest.py's relative to specified dir.")
|
||||
group.addoption('--noconftest', action="store_true",
|
||||
dest="noconftest", default=False,
|
||||
@@ -190,7 +192,9 @@ class FSHookProxy:
|
||||
|
||||
def compatproperty(name):
|
||||
def fget(self):
|
||||
# deprecated - use pytest.name
|
||||
import warnings
|
||||
warnings.warn("This usage is deprecated, please use pytest.{0} instead".format(name),
|
||||
PendingDeprecationWarning, stacklevel=2)
|
||||
return getattr(pytest, name)
|
||||
|
||||
return property(fget)
|
||||
@@ -535,7 +539,6 @@ class Session(FSCollector):
|
||||
def __init__(self, config):
|
||||
FSCollector.__init__(self, config.rootdir, parent=None,
|
||||
config=config, session=self)
|
||||
self._fs2hookproxy = {}
|
||||
self.testsfailed = 0
|
||||
self.testscollected = 0
|
||||
self.shouldstop = False
|
||||
@@ -566,23 +569,18 @@ class Session(FSCollector):
|
||||
return path in self._initialpaths
|
||||
|
||||
def gethookproxy(self, fspath):
|
||||
try:
|
||||
return self._fs2hookproxy[fspath]
|
||||
except KeyError:
|
||||
# check if we have the common case of running
|
||||
# hooks with all conftest.py filesall conftest.py
|
||||
pm = self.config.pluginmanager
|
||||
my_conftestmodules = pm._getconftestmodules(fspath)
|
||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||
if remove_mods:
|
||||
# one or more conftests are not in use at this fspath
|
||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||
else:
|
||||
# all plugis are active for this fspath
|
||||
proxy = self.config.hook
|
||||
|
||||
self._fs2hookproxy[fspath] = proxy
|
||||
return proxy
|
||||
# check if we have the common case of running
|
||||
# hooks with all conftest.py filesall conftest.py
|
||||
pm = self.config.pluginmanager
|
||||
my_conftestmodules = pm._getconftestmodules(fspath)
|
||||
remove_mods = pm._conftest_plugins.difference(my_conftestmodules)
|
||||
if remove_mods:
|
||||
# one or more conftests are not in use at this fspath
|
||||
proxy = FSHookProxy(fspath, pm, remove_mods)
|
||||
else:
|
||||
# all plugis are active for this fspath
|
||||
proxy = self.config.hook
|
||||
return proxy
|
||||
|
||||
def perform_collect(self, args=None, genitems=True):
|
||||
hook = self.config.hook
|
||||
@@ -704,10 +702,9 @@ class Session(FSCollector):
|
||||
path = self.config.invocation_dir.join(relpath, abs=True)
|
||||
if not path.check():
|
||||
if self.config.option.pyargs:
|
||||
msg = "file or package not found: "
|
||||
raise pytest.UsageError("file or package not found: " + arg + " (missing __init__.py?)")
|
||||
else:
|
||||
msg = "file not found: "
|
||||
raise pytest.UsageError(msg + arg)
|
||||
raise pytest.UsageError("file not found: " + arg)
|
||||
parts[0] = path
|
||||
return parts
|
||||
|
||||
|
||||
@@ -54,6 +54,8 @@ def pytest_cmdline_main(config):
|
||||
tw.line()
|
||||
config._ensure_unconfigure()
|
||||
return 0
|
||||
|
||||
|
||||
pytest_cmdline_main.tryfirst = True
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ def pytest_addoption(parser):
|
||||
choices=['failed', 'all'],
|
||||
help="send failed|all info to bpaste.net pastebin service.")
|
||||
|
||||
|
||||
@pytest.hookimpl(trylast=True)
|
||||
def pytest_configure(config):
|
||||
import py
|
||||
@@ -23,13 +24,16 @@ def pytest_configure(config):
|
||||
# pastebin file will be utf-8 encoded binary file
|
||||
config._pastebinfile = tempfile.TemporaryFile('w+b')
|
||||
oldwrite = tr._tw.write
|
||||
|
||||
def tee_write(s, **kwargs):
|
||||
oldwrite(s, **kwargs)
|
||||
if py.builtin._istext(s):
|
||||
s = s.encode('utf-8')
|
||||
config._pastebinfile.write(s)
|
||||
|
||||
tr._tw.write = tee_write
|
||||
|
||||
|
||||
def pytest_unconfigure(config):
|
||||
if hasattr(config, '_pastebinfile'):
|
||||
# get terminal contents and delete file
|
||||
@@ -45,6 +49,7 @@ def pytest_unconfigure(config):
|
||||
pastebinurl = create_new_paste(sessionlog)
|
||||
tr.write_line("pastebin session-log: %s\n" % pastebinurl)
|
||||
|
||||
|
||||
def create_new_paste(contents):
|
||||
"""
|
||||
Creates a new paste using bpaste.net service.
|
||||
@@ -72,6 +77,7 @@ def create_new_paste(contents):
|
||||
else:
|
||||
return 'bad response: ' + response
|
||||
|
||||
|
||||
def pytest_terminal_summary(terminalreporter):
|
||||
import _pytest.config
|
||||
if terminalreporter.config.option.pastebin != "failed":
|
||||
|
||||
@@ -478,11 +478,14 @@ class Testdir:
|
||||
ret = None
|
||||
for name, value in items:
|
||||
p = self.tmpdir.join(name).new(ext=ext)
|
||||
p.dirpath().ensure_dir()
|
||||
source = Source(value)
|
||||
|
||||
def my_totext(s, encoding="utf-8"):
|
||||
if py.builtin._isbytes(s):
|
||||
s = py.builtin._totext(s, encoding=encoding)
|
||||
return s
|
||||
|
||||
source_unicode = "\n".join([my_totext(line) for line in source.lines])
|
||||
source = py.builtin._totext(source_unicode)
|
||||
content = source.strip().encode("utf-8") # + "\n"
|
||||
@@ -692,12 +695,15 @@ class Testdir:
|
||||
# warning which will trigger to say they can no longer be
|
||||
# re-written, which is fine as they are already re-written.
|
||||
orig_warn = AssertionRewritingHook._warn_already_imported
|
||||
|
||||
def revert():
|
||||
AssertionRewritingHook._warn_already_imported = orig_warn
|
||||
|
||||
self.request.addfinalizer(revert)
|
||||
AssertionRewritingHook._warn_already_imported = lambda *a: None
|
||||
|
||||
rec = []
|
||||
|
||||
class Collect:
|
||||
def pytest_configure(x, config):
|
||||
rec.append(self.make_hook_recorder(config.pluginmanager))
|
||||
@@ -732,10 +738,13 @@ class Testdir:
|
||||
try:
|
||||
reprec = self.inline_run(*args, **kwargs)
|
||||
except SystemExit as e:
|
||||
|
||||
class reprec:
|
||||
ret = e.args[0]
|
||||
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
|
||||
class reprec:
|
||||
ret = 3
|
||||
finally:
|
||||
@@ -1002,8 +1011,6 @@ class Testdir:
|
||||
pexpect = pytest.importorskip("pexpect", "3.0")
|
||||
if hasattr(sys, 'pypy_version_info') and '64' in platform.machine():
|
||||
pytest.skip("pypy-64 bit not supported")
|
||||
if sys.platform == "darwin":
|
||||
pytest.xfail("pexpect does not work reliably on darwin?!")
|
||||
if sys.platform.startswith("freebsd"):
|
||||
pytest.xfail("pexpect does not work reliably on freebsd")
|
||||
logfile = self.tmpdir.join("spawn.out").open("wb")
|
||||
|
||||
@@ -22,11 +22,16 @@ from _pytest.compat import (
|
||||
getlocation, enum,
|
||||
)
|
||||
|
||||
cutdir2 = py.path.local(_pytest.__file__).dirpath()
|
||||
cutdir1 = py.path.local(pluggy.__file__.rstrip("oc"))
|
||||
cutdir2 = py.path.local(_pytest.__file__).dirpath()
|
||||
cutdir3 = py.path.local(py.__file__).dirpath()
|
||||
|
||||
|
||||
def filter_traceback(entry):
|
||||
"""Return True if a TracebackEntry instance should be removed from tracebacks:
|
||||
* dynamically generated code (no code to show up for it);
|
||||
* internal traceback from pytest or its internal libraries, py and pluggy.
|
||||
"""
|
||||
# entry.path might sometimes return a str object when the entry
|
||||
# points to dynamically generated code
|
||||
# see https://bitbucket.org/pytest-dev/py/issues/71
|
||||
@@ -37,7 +42,7 @@ def filter_traceback(entry):
|
||||
# entry.path might point to an inexisting file, in which case it will
|
||||
# alsso return a str object. see #1133
|
||||
p = py.path.local(entry.path)
|
||||
return p != cutdir1 and not p.relto(cutdir2)
|
||||
return p != cutdir1 and not p.relto(cutdir2) and not p.relto(cutdir3)
|
||||
|
||||
|
||||
|
||||
@@ -209,9 +214,12 @@ class PyobjMixin(PyobjContext):
|
||||
if obj is None:
|
||||
self._obj = obj = self._getobj()
|
||||
return obj
|
||||
|
||||
def fset(self, value):
|
||||
self._obj = value
|
||||
|
||||
return property(fget, fset, None, "underlying python object")
|
||||
|
||||
obj = obj()
|
||||
|
||||
def _getobj(self):
|
||||
@@ -424,20 +432,25 @@ class Module(pytest.File, PyCollector):
|
||||
% e.args
|
||||
)
|
||||
except ImportError:
|
||||
exc_class, exc, _ = sys.exc_info()
|
||||
from _pytest._code.code import ExceptionInfo
|
||||
exc_info = ExceptionInfo()
|
||||
if self.config.getoption('verbose') < 2:
|
||||
exc_info.traceback = exc_info.traceback.filter(filter_traceback)
|
||||
exc_repr = exc_info.getrepr(style='short') if exc_info.traceback else exc_info.exconly()
|
||||
formatted_tb = py._builtin._totext(exc_repr)
|
||||
raise self.CollectError(
|
||||
"ImportError while importing test module '%s'.\n"
|
||||
"Original error message:\n'%s'\n"
|
||||
"Make sure your test modules/packages have valid Python names."
|
||||
% (self.fspath, exc or exc_class)
|
||||
"ImportError while importing test module '{fspath}'.\n"
|
||||
"Hint: make sure your test modules/packages have valid Python names.\n"
|
||||
"Traceback:\n"
|
||||
"{traceback}".format(fspath=self.fspath, traceback=formatted_tb)
|
||||
)
|
||||
except _pytest.runner.Skipped as e:
|
||||
if e.allow_module_level:
|
||||
raise
|
||||
raise self.CollectError(
|
||||
"Using @pytest.skip outside of a test (e.g. as a test "
|
||||
"function decorator) is not allowed. Use @pytest.mark.skip or "
|
||||
"@pytest.mark.skipif instead."
|
||||
"Using pytest.skip outside of a test is not allowed. If you are "
|
||||
"trying to decorate a test function, use the @pytest.mark.skip "
|
||||
"or @pytest.mark.skipif decorators instead."
|
||||
)
|
||||
self.config.pluginmanager.consider_module(mod)
|
||||
return mod
|
||||
@@ -1095,7 +1108,9 @@ def raises(expected_exception, *args, **kwargs):
|
||||
|
||||
>>> with raises(ZeroDivisionError, message="Expecting ZeroDivisionError"):
|
||||
... pass
|
||||
... Failed: Expecting ZeroDivisionError
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
Failed: Expecting ZeroDivisionError
|
||||
|
||||
|
||||
.. note::
|
||||
@@ -1106,19 +1121,21 @@ def raises(expected_exception, *args, **kwargs):
|
||||
Lines of code after that, within the scope of the context manager will
|
||||
not be executed. For example::
|
||||
|
||||
>>> with raises(OSError) as exc_info:
|
||||
assert 1 == 1 # this will execute as expected
|
||||
raise OSError(errno.EEXISTS, 'directory exists')
|
||||
assert exc_info.value.errno == errno.EEXISTS # this will not execute
|
||||
>>> value = 15
|
||||
>>> with raises(ValueError) as exc_info:
|
||||
... if value > 10:
|
||||
... raise ValueError("value must be <= 10")
|
||||
... assert str(exc_info.value) == "value must be <= 10" # this will not execute
|
||||
|
||||
Instead, the following approach must be taken (note the difference in
|
||||
scope)::
|
||||
|
||||
>>> with raises(OSError) as exc_info:
|
||||
assert 1 == 1 # this will execute as expected
|
||||
raise OSError(errno.EEXISTS, 'directory exists')
|
||||
>>> with raises(ValueError) as exc_info:
|
||||
... if value > 10:
|
||||
... raise ValueError("value must be <= 10")
|
||||
...
|
||||
>>> assert str(exc_info.value) == "value must be <= 10"
|
||||
|
||||
assert exc_info.value.errno == errno.EEXISTS # this will now execute
|
||||
|
||||
Or you can specify a callable by passing a to-be-called lambda::
|
||||
|
||||
@@ -1223,7 +1240,11 @@ class RaisesContext(object):
|
||||
exc_type, value, traceback = tp
|
||||
tp = exc_type, exc_type(value), traceback
|
||||
self.excinfo.__init__(tp)
|
||||
return issubclass(self.excinfo.type, self.expected_exception)
|
||||
suppress_exception = issubclass(self.excinfo.type, self.expected_exception)
|
||||
if sys.version_info[0] == 2 and suppress_exception:
|
||||
sys.exc_clear()
|
||||
return suppress_exception
|
||||
|
||||
|
||||
# builtin pytest.approx helper
|
||||
|
||||
@@ -1398,6 +1419,9 @@ class ApproxNonIterable(object):
|
||||
self.rel = rel
|
||||
|
||||
def __repr__(self):
|
||||
if isinstance(self.expected, complex):
|
||||
return str(self.expected)
|
||||
|
||||
# Infinities aren't compared using tolerances, so don't show a
|
||||
# tolerance.
|
||||
if math.isinf(self.expected):
|
||||
@@ -1410,16 +1434,10 @@ class ApproxNonIterable(object):
|
||||
except ValueError:
|
||||
vetted_tolerance = '???'
|
||||
|
||||
plus_minus = u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
|
||||
|
||||
# In python2, __repr__() must return a string (i.e. not a unicode
|
||||
# object). In python3, __repr__() must return a unicode object
|
||||
# (although now strings are unicode objects and bytes are what
|
||||
# strings were).
|
||||
if sys.version_info[0] == 2:
|
||||
return plus_minus.encode('utf-8')
|
||||
return '{0} +- {1}'.format(self.expected, vetted_tolerance)
|
||||
else:
|
||||
return plus_minus
|
||||
return u'{0} \u00b1 {1}'.format(self.expected, vetted_tolerance)
|
||||
|
||||
def __eq__(self, actual):
|
||||
# Short-circuit exact equality.
|
||||
|
||||
@@ -36,8 +36,13 @@ def deprecated_call(func=None, *args, **kwargs):
|
||||
|
||||
This function can be used as a context manager::
|
||||
|
||||
>>> import warnings
|
||||
>>> def api_call_v2():
|
||||
... warnings.warn('use v3 of this api', DeprecationWarning)
|
||||
... return 200
|
||||
|
||||
>>> with deprecated_call():
|
||||
... myobject.deprecated_method()
|
||||
... assert api_call_v2() == 200
|
||||
|
||||
Note: we cannot use WarningsRecorder here because it is still subject
|
||||
to the mechanism that prevents warnings of the same type from being
|
||||
|
||||
@@ -515,8 +515,10 @@ def exit(msg):
|
||||
__tracebackhide__ = True
|
||||
raise Exit(msg)
|
||||
|
||||
|
||||
exit.Exception = Exit
|
||||
|
||||
|
||||
def skip(msg=""):
|
||||
""" skip an executing test with the given message. Note: it's usually
|
||||
better to use the pytest.mark.skipif marker to declare a test to be
|
||||
@@ -525,8 +527,11 @@ def skip(msg=""):
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
raise Skipped(msg=msg)
|
||||
|
||||
|
||||
skip.Exception = Skipped
|
||||
|
||||
|
||||
def fail(msg="", pytrace=True):
|
||||
""" explicitly fail an currently-executing test with the given Message.
|
||||
|
||||
@@ -535,6 +540,8 @@ def fail(msg="", pytrace=True):
|
||||
"""
|
||||
__tracebackhide__ = True
|
||||
raise Failed(msg=msg, pytrace=pytrace)
|
||||
|
||||
|
||||
fail.Exception = Failed
|
||||
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ import sys
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("debugconfig")
|
||||
group.addoption('--setuponly', '--setup-only', action="store_true",
|
||||
help="only setup fixtures, don't execute the tests.")
|
||||
help="only setup fixtures, do not execute tests.")
|
||||
group.addoption('--setupshow', '--setup-show', action="store_true",
|
||||
help="show setup fixtures while executing the tests.")
|
||||
help="show setup of fixtures while executing tests.")
|
||||
|
||||
|
||||
@pytest.hookimpl(hookwrapper=True)
|
||||
|
||||
@@ -25,8 +25,10 @@ def pytest_configure(config):
|
||||
if config.option.runxfail:
|
||||
old = pytest.xfail
|
||||
config._cleanup.append(lambda: setattr(pytest, "xfail", old))
|
||||
|
||||
def nop(*args, **kwargs):
|
||||
pass
|
||||
|
||||
nop.Exception = XFailed
|
||||
setattr(pytest, "xfail", nop)
|
||||
|
||||
@@ -65,6 +67,8 @@ def xfail(reason=""):
|
||||
""" xfail an executing test or setup functions with the given reason."""
|
||||
__tracebackhide__ = True
|
||||
raise XFailed(reason)
|
||||
|
||||
|
||||
xfail.Exception = XFailed
|
||||
|
||||
|
||||
|
||||
@@ -458,6 +458,15 @@ class TerminalReporter:
|
||||
self.write_sep("_", msg)
|
||||
self._outrep_summary(rep)
|
||||
|
||||
def print_teardown_sections(self, rep):
|
||||
for secname, content in rep.sections:
|
||||
if 'teardown' in secname:
|
||||
self._tw.sep('-', secname)
|
||||
if content[-1:] == "\n":
|
||||
content = content[:-1]
|
||||
self._tw.line(content)
|
||||
|
||||
|
||||
def summary_failures(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
reports = self.getreports('failed')
|
||||
@@ -473,6 +482,9 @@ class TerminalReporter:
|
||||
markup = {'red': True, 'bold': True}
|
||||
self.write_sep("_", msg, **markup)
|
||||
self._outrep_summary(rep)
|
||||
for report in self.getreports(''):
|
||||
if report.nodeid == rep.nodeid and report.when == 'teardown':
|
||||
self.print_teardown_sections(report)
|
||||
|
||||
def summary_errors(self):
|
||||
if self.config.option.tbstyle != "no":
|
||||
|
||||
@@ -81,6 +81,7 @@ def get_user():
|
||||
except (ImportError, KeyError):
|
||||
return None
|
||||
|
||||
|
||||
# backward compatibility
|
||||
TempdirHandler = TempdirFactory
|
||||
|
||||
|
||||
@@ -94,6 +94,9 @@ class TestCaseFunction(pytest.Function):
|
||||
def teardown(self):
|
||||
if hasattr(self._testcase, 'teardown_method'):
|
||||
self._testcase.teardown_method(self._obj)
|
||||
# Allow garbage collection on TestCase instance attributes.
|
||||
self._testcase = None
|
||||
self._obj = None
|
||||
|
||||
def startTest(self, testcase):
|
||||
pass
|
||||
@@ -183,6 +186,7 @@ def pytest_runtest_protocol(item):
|
||||
ut = sys.modules['twisted.python.failure']
|
||||
Failure__init__ = ut.Failure.__init__
|
||||
check_testcase_implements_trial_reporter()
|
||||
|
||||
def excstore(self, exc_value=None, exc_type=None, exc_tb=None,
|
||||
captureVars=None):
|
||||
if exc_value is None:
|
||||
@@ -196,6 +200,7 @@ def pytest_runtest_protocol(item):
|
||||
captureVars=captureVars)
|
||||
except TypeError:
|
||||
Failure__init__(self, exc_value, exc_type, exc_tb)
|
||||
|
||||
ut.Failure.__init__ = excstore
|
||||
yield
|
||||
ut.Failure.__init__ = Failure__init__
|
||||
|
||||
39
appveyor.yml
39
appveyor.yml
@@ -6,31 +6,36 @@ environment:
|
||||
# https://www.appveyor.com/docs/build-configuration#secure-variables
|
||||
|
||||
matrix:
|
||||
# create multiple jobs to execute a set of tox runs on each; this is to workaround having
|
||||
# builds timing out in AppVeyor
|
||||
# pypy is disabled until #1963 gets fixed
|
||||
- TOXENV: "linting,py26,py27,py33,py34,py35"
|
||||
- TOXENV: "py27-pexpect,py27-xdist,py27-trial,py35-pexpect,py35-xdist,py35-trial"
|
||||
- TOXENV: "py27-nobyte,doctesting,freeze,docs"
|
||||
# coveralls is not in the default env list
|
||||
- TOXENV: "coveralls"
|
||||
# note: please use "tox --listenvs" to populate the build matrix below
|
||||
- TOXENV: "linting"
|
||||
- TOXENV: "py26"
|
||||
- TOXENV: "py27"
|
||||
- TOXENV: "py33"
|
||||
- TOXENV: "py34"
|
||||
- TOXENV: "py35"
|
||||
- TOXENV: "pypy"
|
||||
- TOXENV: "py27-pexpect"
|
||||
- TOXENV: "py27-xdist"
|
||||
- TOXENV: "py27-trial"
|
||||
- TOXENV: "py35-pexpect"
|
||||
- TOXENV: "py35-xdist"
|
||||
- TOXENV: "py35-trial"
|
||||
- TOXENV: "py27-nobyte"
|
||||
- TOXENV: "doctesting"
|
||||
- TOXENV: "freeze"
|
||||
- TOXENV: "docs"
|
||||
|
||||
install:
|
||||
- echo Installed Pythons
|
||||
- dir c:\Python*
|
||||
|
||||
# install pypy using choco (redirect to a file and write to console in case
|
||||
# choco install returns non-zero, because choco install python.pypy is too
|
||||
# noisy)
|
||||
- choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
|
||||
- set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
|
||||
- echo PyPy installed
|
||||
- pypy --version
|
||||
- if "%TOXENV%" == "pypy" call scripts\install-pypy.bat
|
||||
|
||||
- C:\Python35\python -m pip install tox
|
||||
|
||||
build: false # Not a C# project, build stuff at the test step instead.
|
||||
|
||||
test_script:
|
||||
- C:\Python35\python -m tox
|
||||
# coveralls is not in tox's envlist, plus for PRs the secure variable
|
||||
# is not defined so we have to check for it
|
||||
- if defined COVERALLS_REPO_TOKEN C:\Python35\python -m tox -e coveralls
|
||||
- call scripts\call-tox.bat
|
||||
|
||||
@@ -6,6 +6,8 @@ Release announcements
|
||||
:maxdepth: 2
|
||||
|
||||
|
||||
release-3.0.5
|
||||
release-3.0.4
|
||||
release-3.0.3
|
||||
release-3.0.2
|
||||
release-3.0.1
|
||||
|
||||
29
doc/en/announce/release-3.0.4.rst
Normal file
29
doc/en/announce/release-3.0.4.rst
Normal file
@@ -0,0 +1,29 @@
|
||||
pytest-3.0.4
|
||||
============
|
||||
|
||||
pytest 3.0.4 has just been released to PyPI.
|
||||
|
||||
This release fixes some regressions and bugs reported in the last version,
|
||||
being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Bruno Oliveira
|
||||
* Dan Wandschneider
|
||||
* Florian Bruhin
|
||||
* Georgy Dyuldin
|
||||
* Grigorii Eremeev
|
||||
* Jason R. Coombs
|
||||
* Manuel Jacob
|
||||
* Mathieu Clabaut
|
||||
* Michael Seifert
|
||||
* Nikolaus Rath
|
||||
* Ronny Pfannschmidt
|
||||
* Tom V
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
27
doc/en/announce/release-3.0.5.rst
Normal file
27
doc/en/announce/release-3.0.5.rst
Normal file
@@ -0,0 +1,27 @@
|
||||
pytest-3.0.5
|
||||
============
|
||||
|
||||
pytest 3.0.5 has just been released to PyPI.
|
||||
|
||||
This is a bug-fix release, being a drop-in replacement. To upgrade::
|
||||
|
||||
pip install --upgrade pytest
|
||||
|
||||
The changelog is available at http://doc.pytest.org/en/latest/changelog.html.
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
* Ana Vojnovic
|
||||
* Bruno Oliveira
|
||||
* Daniel Hahler
|
||||
* Duncan Betts
|
||||
* Igor Starikov
|
||||
* Ismail
|
||||
* Luke Murphy
|
||||
* Ned Batchelder
|
||||
* Ronny Pfannschmidt
|
||||
* Sebastian Ramacher
|
||||
* nmundar
|
||||
|
||||
Happy testing,
|
||||
The pytest Development Team
|
||||
@@ -26,7 +26,7 @@ you will see the return value of the function call::
|
||||
|
||||
$ pytest test_assert1.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -170,7 +170,7 @@ if you run this module::
|
||||
|
||||
$ pytest test_assert2.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ If you then run it with ``--lf``::
|
||||
|
||||
$ pytest --lf
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
run-last-failure: rerun last 2 failures
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 50 items
|
||||
@@ -122,7 +122,7 @@ of ``FF`` and dots)::
|
||||
|
||||
$ pytest --ff
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
run-last-failure: rerun last 2 failures first
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 50 items
|
||||
@@ -227,7 +227,7 @@ You can always peek at the content of the cache using the
|
||||
|
||||
$ py.test --cache-show
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
cachedir: $REGENDOC_TMPDIR/.cache
|
||||
------------------------------- cache values -------------------------------
|
||||
|
||||
@@ -64,7 +64,7 @@ of the failing function and hide the other one::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ then you can just invoke ``pytest`` without command line options::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ You can then restrict a test run to only run tests marked with ``webtest``::
|
||||
|
||||
$ pytest -v -m webtest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -45,7 +45,7 @@ Or the inverse, running all tests except the webtest ones::
|
||||
|
||||
$ pytest -v -m "not webtest"
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -66,7 +66,7 @@ tests based on their module, class, method, or function name::
|
||||
|
||||
$ pytest -v test_server.py::TestClass::test_method
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 5 items
|
||||
@@ -79,7 +79,7 @@ You can also select on the class::
|
||||
|
||||
$ pytest -v test_server.py::TestClass
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -92,7 +92,7 @@ Or select multiple nodes::
|
||||
|
||||
$ pytest -v test_server.py::TestClass test_server.py::test_send_http
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 8 items
|
||||
@@ -130,7 +130,7 @@ select tests based on their names::
|
||||
|
||||
$ pytest -v -k http # running with the above defined example module
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -144,7 +144,7 @@ And you can also run all tests except the ones that match the keyword::
|
||||
|
||||
$ pytest -k "not send_http" -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -160,7 +160,7 @@ Or to select "http" and "quick" tests::
|
||||
|
||||
$ pytest -k "http or quick" -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 4 items
|
||||
@@ -352,7 +352,7 @@ the test needs::
|
||||
|
||||
$ pytest -E stage2
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -364,7 +364,7 @@ and here is one that specifies exactly the environment needed::
|
||||
|
||||
$ pytest -E stage1
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -485,7 +485,7 @@ then you will see two test skipped and two executed tests as expected::
|
||||
|
||||
$ pytest -rs # this option reports skip reasons
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -499,7 +499,7 @@ Note that if you specify a platform via the marker-command line option like this
|
||||
|
||||
$ pytest -m linux2
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -551,7 +551,7 @@ We can now use the ``-m option`` to select one set::
|
||||
|
||||
$ pytest -m interface --tb=short
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -573,7 +573,7 @@ or to select both "event" and "interface" tests::
|
||||
|
||||
$ pytest -m "interface or event" --tb=short
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ now execute the test specification::
|
||||
|
||||
nonpython $ pytest test_simple.yml
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -59,7 +59,7 @@ consulted when reporting in ``verbose`` mode::
|
||||
|
||||
nonpython $ pytest -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -81,7 +81,7 @@ interesting to just look at the collection tree::
|
||||
|
||||
nonpython $ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
|
||||
collected 2 items
|
||||
<YamlFile 'test_simple.yml'>
|
||||
|
||||
@@ -130,7 +130,7 @@ objects, they are still using the default pytest representation::
|
||||
|
||||
$ pytest test_time.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 6 items
|
||||
<Module 'test_time.py'>
|
||||
@@ -181,7 +181,7 @@ this is a fully self-contained example which you can run with::
|
||||
|
||||
$ pytest test_scenarios.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -194,7 +194,7 @@ If you just collect tests you'll also nicely see 'advanced' and 'basic' as varia
|
||||
|
||||
$ pytest --collect-only test_scenarios.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
<Module 'test_scenarios.py'>
|
||||
@@ -259,7 +259,7 @@ Let's first see how it looks like at collection time::
|
||||
|
||||
$ pytest test_backends.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
<Module 'test_backends.py'>
|
||||
@@ -320,7 +320,7 @@ The result of this test will be successful::
|
||||
|
||||
$ pytest test_indirect_list.py --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
<Module 'test_indirect_list.py'>
|
||||
@@ -447,7 +447,7 @@ If you run this with reporting for skips enabled::
|
||||
|
||||
$ pytest -rs test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ then the test collection looks like this::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 2 items
|
||||
<Module 'check_myapp.py'>
|
||||
@@ -163,7 +163,7 @@ You can always peek at the collection tree without running tests like this::
|
||||
|
||||
. $ pytest --collect-only pythoncollection.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 3 items
|
||||
<Module 'CWD/pythoncollection.py'>
|
||||
@@ -230,7 +230,7 @@ will be left out::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||
collected 0 items
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ get on the terminal - we are working on that)::
|
||||
|
||||
assertion $ pytest failure_demo.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/assertion, inifile:
|
||||
collected 42 items
|
||||
|
||||
@@ -359,7 +359,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.5/site-packages/_pytest/python.py:1190>:1: ValueError
|
||||
<0-codegen $PYTHON_PREFIX/lib/python3.5/site-packages/_pytest/python.py:1207>:1: ValueError
|
||||
_______ TestRaises.test_raises_doesnt ________
|
||||
|
||||
self = <failure_demo.TestRaises object at 0xdeadbeef>
|
||||
|
||||
@@ -113,7 +113,7 @@ directory with the above conftest.py::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
|
||||
@@ -164,7 +164,7 @@ and when running it will see a skipped "slow" test::
|
||||
|
||||
$ pytest -rs # "-rs" means report details on the little 's'
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -178,7 +178,7 @@ Or run it including the ``slow`` marked test::
|
||||
|
||||
$ pytest --runslow
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -302,7 +302,7 @@ which will add the string to the test header accordingly::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
project deps: mylib-1.1
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
@@ -327,7 +327,7 @@ which will add info only when run with "--v"::
|
||||
|
||||
$ pytest -v
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
info1: did you know that ...
|
||||
did you?
|
||||
@@ -340,7 +340,7 @@ and nothing when run plainly::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 0 items
|
||||
|
||||
@@ -374,7 +374,7 @@ Now we can profile which test functions execute the slowest::
|
||||
|
||||
$ pytest --durations=3
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
@@ -440,7 +440,7 @@ If we run this::
|
||||
|
||||
$ pytest -rx
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 4 items
|
||||
|
||||
@@ -519,7 +519,7 @@ We can run this::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 7 items
|
||||
|
||||
@@ -627,7 +627,7 @@ and run them::
|
||||
|
||||
$ pytest test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -721,7 +721,7 @@ and run it::
|
||||
|
||||
$ pytest -s test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
|
||||
@@ -70,7 +70,7 @@ marked ``smtp`` fixture function. Running the test looks like this::
|
||||
|
||||
$ pytest test_smtpsimple.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -188,7 +188,7 @@ inspect what is going on and can now run the tests::
|
||||
|
||||
$ pytest test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
@@ -318,8 +318,7 @@ the ``with`` statement ends.
|
||||
request.addfinalizer(fin)
|
||||
return smtp # provide the fixture value
|
||||
|
||||
The ``fin`` function will execute when the last test using
|
||||
the fixture in the module has finished execution.
|
||||
The ``fin`` function will execute when the last test in the module has finished execution.
|
||||
|
||||
This method is still fully supported, but ``yield`` is recommended from 2.10 onward because
|
||||
it is considered simpler and better describes the natural code flow.
|
||||
@@ -375,6 +374,8 @@ Running it::
|
||||
assert 0, smtp.helo()
|
||||
E AssertionError: (250, b'mail.python.org')
|
||||
E assert 0
|
||||
------------------------- Captured stdout teardown -------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef> (mail.python.org)
|
||||
|
||||
voila! The ``smtp`` fixture function picked up our mail server name
|
||||
from the module namespace.
|
||||
@@ -464,6 +465,8 @@ So let's just do another run::
|
||||
E assert 0
|
||||
|
||||
test_module.py:11: AssertionError
|
||||
------------------------- Captured stdout teardown -------------------------
|
||||
finalizing <smtplib.SMTP object at 0xdeadbeef>
|
||||
4 failed in 0.12 seconds
|
||||
|
||||
We see that our two test functions each ran twice, against the different
|
||||
@@ -516,7 +519,7 @@ Running the above tests results in the following test IDs being used::
|
||||
|
||||
$ pytest --collect-only
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 11 items
|
||||
<Module 'test_anothersmtp.py'>
|
||||
@@ -569,7 +572,7 @@ Here we declare an ``app`` fixture which receives the previously defined
|
||||
|
||||
$ pytest -v test_appsetup.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 2 items
|
||||
@@ -638,7 +641,7 @@ Let's run the tests in verbose mode and with looking at the print-output::
|
||||
|
||||
$ pytest -v -s test_module.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0 -- $PYTHON_PREFIX/bin/python3.5
|
||||
cachedir: .cache
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collecting ... collected 8 items
|
||||
@@ -701,7 +704,7 @@ Using fixtures from classes, modules or projects
|
||||
Sometimes test functions do not directly need access to a fixture object.
|
||||
For example, tests may require to operate with an empty directory as the
|
||||
current working directory but otherwise do not care for the concrete
|
||||
directory. Here is how you can can use the standard `tempfile
|
||||
directory. Here is how you can use the standard `tempfile
|
||||
<http://docs.python.org/library/tempfile.html>`_ and pytest fixtures to
|
||||
achieve it. We separate the creation of the fixture into a conftest.py
|
||||
file::
|
||||
|
||||
@@ -26,7 +26,7 @@ Installation::
|
||||
To check your installation has installed the correct version::
|
||||
|
||||
$ pytest --version
|
||||
This is pytest version 3.0.3, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
|
||||
This is pytest version 3.0.5, imported from $PYTHON_PREFIX/lib/python3.5/site-packages/pytest.py
|
||||
|
||||
.. _`simpletest`:
|
||||
|
||||
@@ -46,7 +46,7 @@ That's it. You can execute the test function now::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -16,10 +16,12 @@ Conventions for Python test discovery
|
||||
* If no arguments are specified then collection starts from :confval:`testpaths`
|
||||
(if configured) or the current directory. Alternatively, command line arguments
|
||||
can be used in any combination of directories, file names or node ids.
|
||||
* recurse into directories, unless they match :confval:`norecursedirs`
|
||||
* ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
|
||||
* ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
* ``test_`` prefixed test functions or methods are test items
|
||||
* Recurse into directories, unless they match :confval:`norecursedirs`.
|
||||
* In those directories, search for ``test_*.py`` or ``*_test.py`` files, imported by their `test package name`_.
|
||||
* From those files, collect test items:
|
||||
|
||||
* ``test_`` prefixed test functions or methods outside of class
|
||||
* ``test_`` prefixed test functions or methods inside ``Test`` prefixed test classes (without an ``__init__`` method)
|
||||
|
||||
For examples of how to customize your test discovery :doc:`example/pythoncollection`.
|
||||
|
||||
@@ -125,7 +127,7 @@ required configurations.
|
||||
The reason for this somewhat evolved importing technique is
|
||||
that in larger projects multiple test modules might import
|
||||
from each other and thus deriving a canonical import name helps
|
||||
to avoid surprises such as a test modules getting imported twice.
|
||||
to avoid surprises such as a test module getting imported twice.
|
||||
|
||||
|
||||
.. _`virtualenv`: http://pypi.python.org/pypi/virtualenv
|
||||
@@ -236,9 +238,10 @@ your own setuptools Test command for invoking pytest.
|
||||
self.pytest_args = []
|
||||
|
||||
def run_tests(self):
|
||||
import shlex
|
||||
#import here, cause outside the eggs aren't loaded
|
||||
import pytest
|
||||
errno = pytest.main(self.pytest_args)
|
||||
errno = pytest.main(shlex.split(self.pytest_args))
|
||||
sys.exit(errno)
|
||||
|
||||
|
||||
|
||||
@@ -14,18 +14,18 @@ An example of a simple test:
|
||||
.. code-block:: python
|
||||
|
||||
# content of test_sample.py
|
||||
def func(x):
|
||||
def inc(x):
|
||||
return x + 1
|
||||
|
||||
def test_answer():
|
||||
assert func(3) == 5
|
||||
assert inc(3) == 5
|
||||
|
||||
|
||||
To execute it::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
@@ -35,9 +35,9 @@ To execute it::
|
||||
_______ test_answer ________
|
||||
|
||||
def test_answer():
|
||||
> assert func(3) == 5
|
||||
> assert inc(3) == 5
|
||||
E assert 4 == 5
|
||||
E + where 4 = func(3)
|
||||
E + where 4 = inc(3)
|
||||
|
||||
test_sample.py:5: AssertionError
|
||||
======= 1 failed in 0.12 seconds ========
|
||||
|
||||
@@ -55,6 +55,14 @@ will delete the method ``request.session.Session.request``
|
||||
so that any attempts within tests to create http requests will fail.
|
||||
|
||||
|
||||
.. note::
|
||||
|
||||
Be advised that it is not recommended to patch builtin functions such as ``open``,
|
||||
``compile``, etc., because it might break pytest's internals. If that's
|
||||
unavoidable, passing ``--tb=native``, ``--assert=plain`` and ``--capture=no`` might
|
||||
help althought there's no guarantee.
|
||||
|
||||
|
||||
Method reference of the monkeypatch fixture
|
||||
-------------------------------------------
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ them in turn::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
@@ -103,7 +103,7 @@ Let's run this::
|
||||
|
||||
$ pytest
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 3 items
|
||||
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
.. _`asserting warnings`:
|
||||
|
||||
.. _assertwarnings:
|
||||
|
||||
Asserting Warnings
|
||||
=====================================================
|
||||
|
||||
.. _`asserting warnings with the warns function`:
|
||||
|
||||
.. _warns:
|
||||
|
||||
Asserting warnings with the warns function
|
||||
@@ -46,6 +50,8 @@ Alternatively, you can examine raised warnings in detail using the
|
||||
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
|
||||
differently; see :ref:`ensuring_function_triggers`.
|
||||
|
||||
.. _`recording warnings`:
|
||||
|
||||
.. _recwarn:
|
||||
|
||||
Recording warnings
|
||||
@@ -96,6 +102,8 @@ class of the warning. The ``message`` is the warning itself; calling
|
||||
``DeprecationWarning`` and ``PendingDeprecationWarning`` are treated
|
||||
differently; see :ref:`ensuring_function_triggers`.
|
||||
|
||||
.. _`ensuring a function triggers a deprecation warning`:
|
||||
|
||||
.. _ensuring_function_triggers:
|
||||
|
||||
Ensuring a function triggers a deprecation warning
|
||||
|
||||
@@ -224,7 +224,7 @@ Running it with the report-on-xfail option gives this output::
|
||||
|
||||
example $ pytest -rx xfail_demo.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR/example, inifile:
|
||||
collected 7 items
|
||||
|
||||
|
||||
@@ -4,13 +4,16 @@ Talks and Tutorials
|
||||
|
||||
.. sidebar:: Next Open Trainings
|
||||
|
||||
`professional testing with pytest and tox <http://www.python-academy.com/courses/specialtopics/python_course_testing.html>`_, 27-29th June 2016, Freiburg, Germany
|
||||
`pytest workshop <http://www.meetup.com/Python-Django-User-Group-Bern/events/235151115/>`_, 8th December 2016, Bern, Switzerland
|
||||
|
||||
.. _`funcargs`: funcargs.html
|
||||
|
||||
Talks and blog postings
|
||||
---------------------------------------------
|
||||
|
||||
- `Pythonic testing, Igor Starikov (Russian, PyNsk, November 2016)
|
||||
<https://www.youtube.com/watch?v=_92nfdd5nK8>`_.
|
||||
|
||||
- `pytest - Rapid Simple Testing, Florian Bruhin, Swiss Python Summit 2016
|
||||
<https://www.youtube.com/watch?v=rCBHkQ_LVIs>`_.
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ Running this would result in a passed test except for the last
|
||||
|
||||
$ pytest test_tmpdir.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 1 items
|
||||
|
||||
|
||||
@@ -100,7 +100,7 @@ the ``self.db`` values in the traceback::
|
||||
|
||||
$ pytest test_unittest_db.py
|
||||
======= test session starts ========
|
||||
platform linux -- Python 3.5.2, pytest-3.0.3, py-1.4.31, pluggy-0.4.0
|
||||
platform linux -- Python 3.5.2, pytest-3.0.5, py-1.4.31, pluggy-0.4.0
|
||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||
collected 2 items
|
||||
|
||||
|
||||
@@ -310,10 +310,6 @@ You can pass in options and arguments::
|
||||
|
||||
pytest.main(['-x', 'mytestdir'])
|
||||
|
||||
or pass in a string::
|
||||
|
||||
pytest.main("-x mytestdir")
|
||||
|
||||
You can specify additional plugins to ``pytest.main``::
|
||||
|
||||
# content of myinvoke.py
|
||||
|
||||
@@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# this assumes plugins are installed as sister directories
|
||||
|
||||
set -e
|
||||
cd ../pytest-pep8
|
||||
pytest
|
||||
cd ../pytest-instafail
|
||||
pytest
|
||||
cd ../pytest-cache
|
||||
pytest
|
||||
cd ../pytest-xprocess
|
||||
pytest
|
||||
#cd ../pytest-cov
|
||||
#pytest
|
||||
cd ../pytest-capturelog
|
||||
pytest
|
||||
cd ../pytest-xdist
|
||||
pytest
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
sphinx==1.2.3
|
||||
regendoc
|
||||
pyyaml
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
if __name__ == "__main__":
|
||||
import subprocess
|
||||
import sys
|
||||
subprocess.call([sys.executable, "-m", "tox",
|
||||
"-i", "ALL=https://devpi.net/hpk/dev/",
|
||||
"--develop"] + sys.argv[1:])
|
||||
8
scripts/call-tox.bat
Normal file
8
scripts/call-tox.bat
Normal file
@@ -0,0 +1,8 @@
|
||||
REM skip "coveralls" run in PRs or forks
|
||||
if "%TOXENV%" == "coveralls" (
|
||||
if not defined COVERALLS_REPO_TOKEN (
|
||||
echo skipping coveralls run because COVERALLS_REPO_TOKEN is not defined
|
||||
exit /b 0
|
||||
)
|
||||
)
|
||||
C:\Python35\python -m tox
|
||||
21
scripts/check-manifest.py
Normal file
21
scripts/check-manifest.py
Normal file
@@ -0,0 +1,21 @@
|
||||
"""
|
||||
Script used by tox.ini to check the manifest file if we are under version control, or skip the
|
||||
check altogether if not.
|
||||
|
||||
"check-manifest" will needs a vcs to work, which is not available when testing the package
|
||||
instead of the source code (with ``devpi test`` for example).
|
||||
"""
|
||||
|
||||
from __future__ import print_function
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
if os.path.isdir('.git'):
|
||||
sys.exit(subprocess.call('check-manifest', shell=True))
|
||||
else:
|
||||
print('No .git directory found, skipping checking the manifest file')
|
||||
sys.exit(0)
|
||||
|
||||
6
scripts/install-pypy.bat
Normal file
6
scripts/install-pypy.bat
Normal file
@@ -0,0 +1,6 @@
|
||||
REM install pypy using choco
|
||||
REM redirect to a file because choco install python.pypy is too noisy. If the command fails, write output to console
|
||||
choco install python.pypy > pypy-inst.log 2>&1 || (type pypy-inst.log & exit /b 1)
|
||||
set PATH=C:\tools\pypy\pypy;%PATH% # so tox can find pypy
|
||||
echo PyPy installed
|
||||
pypy --version
|
||||
1
setup.py
1
setup.py
@@ -72,6 +72,7 @@ def main():
|
||||
entry_points={'console_scripts':
|
||||
['pytest=pytest:main', 'py.test=pytest:main']},
|
||||
classifiers=classifiers,
|
||||
keywords="test unittest",
|
||||
cmdclass={'test': PyTest},
|
||||
# the following should be enabled for release
|
||||
install_requires=install_requires,
|
||||
|
||||
@@ -120,7 +120,7 @@ class TestGeneralUsage:
|
||||
result.stdout.fnmatch_lines([
|
||||
#XXX on jython this fails: "> import import_fails",
|
||||
"ImportError while importing test module*",
|
||||
"'No module named *does_not_work*",
|
||||
"*No module named *does_not_work*",
|
||||
])
|
||||
assert result.ret == 2
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ def test_code_with_class():
|
||||
pass
|
||||
pytest.raises(TypeError, "_pytest._code.Code(A)")
|
||||
|
||||
|
||||
if True:
|
||||
def x():
|
||||
pass
|
||||
@@ -68,8 +69,10 @@ def test_code_from_func():
|
||||
|
||||
def test_unicode_handling():
|
||||
value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8')
|
||||
|
||||
def f():
|
||||
raise Exception(value)
|
||||
|
||||
excinfo = pytest.raises(Exception, f)
|
||||
str(excinfo)
|
||||
if sys.version_info[0] < 3:
|
||||
@@ -79,8 +82,10 @@ 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')
|
||||
|
||||
def f():
|
||||
raise SyntaxError('invalid syntax', (None, 1, 3, value))
|
||||
|
||||
excinfo = pytest.raises(Exception, f)
|
||||
str(excinfo)
|
||||
if sys.version_info[0] < 3:
|
||||
|
||||
@@ -56,13 +56,15 @@ def test_excinfo_simple():
|
||||
def test_excinfo_getstatement():
|
||||
def g():
|
||||
raise ValueError
|
||||
|
||||
def f():
|
||||
g()
|
||||
|
||||
try:
|
||||
f()
|
||||
except ValueError:
|
||||
excinfo = _pytest._code.ExceptionInfo()
|
||||
linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 3,
|
||||
linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 4,
|
||||
_pytest._code.getrawcode(f).co_firstlineno - 1 + 1,
|
||||
_pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ]
|
||||
l = list(excinfo.traceback)
|
||||
@@ -168,11 +170,13 @@ class TestTraceback_f_g_h:
|
||||
#
|
||||
raise ValueError
|
||||
#
|
||||
|
||||
def g():
|
||||
#
|
||||
__tracebackhide__ = tracebackhide
|
||||
f()
|
||||
#
|
||||
|
||||
def h():
|
||||
#
|
||||
g()
|
||||
@@ -214,15 +218,18 @@ class TestTraceback_f_g_h:
|
||||
def test_traceback_no_recursion_index(self):
|
||||
def do_stuff():
|
||||
raise RuntimeError
|
||||
|
||||
def reraise_me():
|
||||
import sys
|
||||
exc, val, tb = sys.exc_info()
|
||||
py.builtin._reraise(exc, val, tb)
|
||||
|
||||
def f(n):
|
||||
try:
|
||||
do_stuff()
|
||||
except:
|
||||
reraise_me()
|
||||
|
||||
excinfo = pytest.raises(RuntimeError, f, 8)
|
||||
traceback = excinfo.traceback
|
||||
recindex = traceback.recursionindex()
|
||||
@@ -245,17 +252,18 @@ class TestTraceback_f_g_h:
|
||||
excinfo = pytest.raises(ValueError, fail)
|
||||
assert excinfo.traceback.recursionindex() is None
|
||||
|
||||
|
||||
|
||||
def test_traceback_getcrashentry(self):
|
||||
def i():
|
||||
__tracebackhide__ = True
|
||||
raise ValueError
|
||||
|
||||
def h():
|
||||
i()
|
||||
|
||||
def g():
|
||||
__tracebackhide__ = True
|
||||
h()
|
||||
|
||||
def f():
|
||||
g()
|
||||
|
||||
@@ -271,6 +279,7 @@ class TestTraceback_f_g_h:
|
||||
def g():
|
||||
__tracebackhide__ = True
|
||||
raise ValueError
|
||||
|
||||
def f():
|
||||
__tracebackhide__ = True
|
||||
g()
|
||||
@@ -465,11 +474,13 @@ raise ValueError()
|
||||
class FakeCode(object):
|
||||
class raw:
|
||||
co_filename = '?'
|
||||
|
||||
path = '?'
|
||||
firstlineno = 5
|
||||
|
||||
def fullsource(self):
|
||||
return None
|
||||
|
||||
fullsource = property(fullsource)
|
||||
|
||||
class FakeFrame(object):
|
||||
@@ -491,17 +502,21 @@ raise ValueError()
|
||||
class FakeExcinfo(_pytest._code.ExceptionInfo):
|
||||
typename = "Foo"
|
||||
value = Exception()
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def exconly(self, tryshort):
|
||||
return "EXC"
|
||||
|
||||
def errisinstance(self, cls):
|
||||
return False
|
||||
|
||||
excinfo = FakeExcinfo()
|
||||
|
||||
class FakeRawTB(object):
|
||||
tb_next = None
|
||||
|
||||
tb = FakeRawTB()
|
||||
excinfo.traceback = Traceback(tb)
|
||||
|
||||
@@ -719,8 +734,10 @@ raise ValueError()
|
||||
excinfo = pytest.raises(ValueError, mod.entry)
|
||||
|
||||
p = FormattedExcinfo()
|
||||
|
||||
def raiseos():
|
||||
raise OSError(2)
|
||||
|
||||
monkeypatch.setattr(py.std.os, 'getcwd', raiseos)
|
||||
assert p._makepath(__file__) == __file__
|
||||
p.repr_traceback(excinfo)
|
||||
@@ -789,9 +806,11 @@ raise ValueError()
|
||||
|
||||
def test_reprexcinfo_unicode(self):
|
||||
from _pytest._code.code import TerminalRepr
|
||||
|
||||
class MyRepr(TerminalRepr):
|
||||
def toterminal(self, tw):
|
||||
tw.line(py.builtin._totext("я", "utf-8"))
|
||||
|
||||
x = py.builtin._totext(MyRepr())
|
||||
assert x == py.builtin._totext("я", "utf-8")
|
||||
|
||||
@@ -1050,6 +1069,50 @@ raise ValueError()
|
||||
assert line.endswith('mod.py')
|
||||
assert tw.lines[47] == ":15: AttributeError"
|
||||
|
||||
@pytest.mark.skipif("sys.version_info[0] < 3")
|
||||
@pytest.mark.parametrize('reason, description', [
|
||||
('cause', 'The above exception was the direct cause of the following exception:'),
|
||||
('context', 'During handling of the above exception, another exception occurred:'),
|
||||
])
|
||||
def test_exc_chain_repr_without_traceback(self, importasmod, reason, description):
|
||||
"""
|
||||
Handle representation of exception chains where one of the exceptions doesn't have a
|
||||
real traceback, such as those raised in a subprocess submitted by the multiprocessing
|
||||
module (#1984).
|
||||
"""
|
||||
from _pytest.pytester import LineMatcher
|
||||
exc_handling_code = ' from e' if reason == 'cause' else ''
|
||||
mod = importasmod("""
|
||||
def f():
|
||||
try:
|
||||
g()
|
||||
except Exception as e:
|
||||
raise RuntimeError('runtime problem'){exc_handling_code}
|
||||
def g():
|
||||
raise ValueError('invalid value')
|
||||
""".format(exc_handling_code=exc_handling_code))
|
||||
|
||||
with pytest.raises(RuntimeError) as excinfo:
|
||||
mod.f()
|
||||
|
||||
# emulate the issue described in #1984
|
||||
attr = '__%s__' % reason
|
||||
getattr(excinfo.value, attr).__traceback__ = None
|
||||
|
||||
r = excinfo.getrepr()
|
||||
tw = py.io.TerminalWriter(stringio=True)
|
||||
tw.hasmarkup = False
|
||||
r.toterminal(tw)
|
||||
|
||||
matcher = LineMatcher(tw.stringio.getvalue().splitlines())
|
||||
matcher.fnmatch_lines([
|
||||
"ValueError: invalid value",
|
||||
description,
|
||||
"* except Exception as e:",
|
||||
"> * raise RuntimeError('runtime problem')" + exc_handling_code,
|
||||
"E *RuntimeError: runtime problem",
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.parametrize("style", ["short", "long"])
|
||||
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# encoding: utf-8
|
||||
|
||||
import sys
|
||||
import pytest
|
||||
import doctest
|
||||
|
||||
@@ -9,6 +9,7 @@ from decimal import Decimal
|
||||
from fractions import Fraction
|
||||
inf, nan = float('inf'), float('nan')
|
||||
|
||||
|
||||
class MyDocTestRunner(doctest.DocTestRunner):
|
||||
|
||||
def __init__(self):
|
||||
@@ -22,12 +23,17 @@ class MyDocTestRunner(doctest.DocTestRunner):
|
||||
class TestApprox:
|
||||
|
||||
def test_repr_string(self):
|
||||
# Just make sure the Unicode handling doesn't raise any exceptions.
|
||||
print(approx(1.0))
|
||||
print(approx([1.0, 2.0, 3.0]))
|
||||
print(approx(inf))
|
||||
print(approx(1.0, rel=nan))
|
||||
print(approx(1.0, rel=inf))
|
||||
# for some reason in Python 2.6 it is not displaying the tolerance representation correctly
|
||||
plus_minus = u'\u00b1' if sys.version_info[0] > 2 else u'+-'
|
||||
tol1, tol2, infr = '1.0e-06', '2.0e-06', 'inf'
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
tol1, tol2, infr = '???', '???', '???'
|
||||
assert repr(approx(1.0)) == '1.0 {pm} {tol1}'.format(pm=plus_minus, tol1=tol1)
|
||||
assert repr(approx([1.0, 2.0])) == '1.0 {pm} {tol1}, 2.0 {pm} {tol2}'.format(pm=plus_minus, tol1=tol1, tol2=tol2)
|
||||
assert repr(approx(inf)) == 'inf'
|
||||
assert repr(approx(1.0, rel=nan)) == '1.0 {pm} ???'.format(pm=plus_minus)
|
||||
assert repr(approx(1.0, rel=inf)) == '1.0 {pm} {infr}'.format(pm=plus_minus, infr=infr)
|
||||
assert repr(approx(1.0j, rel=inf)) == '1j'
|
||||
|
||||
def test_operator_overloading(self):
|
||||
assert 1 == approx(1, rel=1e-6, abs=1e-12)
|
||||
@@ -284,3 +290,23 @@ class TestApprox:
|
||||
runner = MyDocTestRunner()
|
||||
runner.run(test)
|
||||
|
||||
def test_unicode_plus_minus(self, testdir):
|
||||
"""
|
||||
Comparing approx instances inside lists should not produce an error in the detailed diff.
|
||||
Integration test for issue #2111.
|
||||
"""
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
def test_foo():
|
||||
assert [3] == [pytest.approx(4)]
|
||||
""")
|
||||
expected = '4.0e-06'
|
||||
# for some reason in Python 2.6 it is not displaying the tolerance representation correctly
|
||||
if sys.version_info[:2] == (2, 6):
|
||||
expected = '???'
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([
|
||||
'*At index 0 diff: 3 != 4 * {0}'.format(expected),
|
||||
'=* 1 failed in *=',
|
||||
])
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import os
|
||||
import sys
|
||||
from textwrap import dedent
|
||||
|
||||
@@ -68,9 +69,41 @@ class TestModule:
|
||||
result = testdir.runpytest("-rw")
|
||||
result.stdout.fnmatch_lines([
|
||||
"ImportError while importing test module*test_one.part1*",
|
||||
"Make sure your test modules/packages have valid Python names.",
|
||||
"Hint: make sure your test modules/packages have valid Python names.",
|
||||
])
|
||||
|
||||
@pytest.mark.parametrize('verbose', [0, 1, 2])
|
||||
def test_show_traceback_import_error(self, testdir, verbose):
|
||||
"""Import errors when collecting modules should display the traceback (#1976).
|
||||
|
||||
With low verbosity we omit pytest and internal modules, otherwise show all traceback entries.
|
||||
"""
|
||||
testdir.makepyfile(
|
||||
foo_traceback_import_error="""
|
||||
from bar_traceback_import_error import NOT_AVAILABLE
|
||||
""",
|
||||
bar_traceback_import_error="",
|
||||
)
|
||||
testdir.makepyfile("""
|
||||
import foo_traceback_import_error
|
||||
""")
|
||||
args = ('-v',) * verbose
|
||||
result = testdir.runpytest(*args)
|
||||
result.stdout.fnmatch_lines([
|
||||
"ImportError while importing test module*",
|
||||
"Traceback:",
|
||||
"*from bar_traceback_import_error import NOT_AVAILABLE",
|
||||
"*cannot import name *NOT_AVAILABLE*",
|
||||
])
|
||||
assert result.ret == 2
|
||||
|
||||
stdout = result.stdout.str()
|
||||
for name in ('_pytest', os.path.join('py', '_path')):
|
||||
if verbose == 2:
|
||||
assert name in stdout
|
||||
else:
|
||||
assert name not in stdout
|
||||
|
||||
|
||||
class TestClass:
|
||||
def test_class_with_init_warning(self, testdir):
|
||||
@@ -350,10 +383,13 @@ class TestFunction:
|
||||
config = testdir.parseconfigure()
|
||||
session = testdir.Session(config)
|
||||
session._fixturemanager = FixtureManager(session)
|
||||
|
||||
def func1():
|
||||
pass
|
||||
|
||||
def func2():
|
||||
pass
|
||||
|
||||
f1 = pytest.Function(name="name", parent=session, config=config,
|
||||
args=(1,), callobj=func1)
|
||||
assert f1 == f1
|
||||
@@ -514,12 +550,15 @@ class TestFunction:
|
||||
def test_pyfunc_call(self, testdir):
|
||||
item = testdir.getitem("def test_func(): raise ValueError")
|
||||
config = item.config
|
||||
|
||||
class MyPlugin1:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
raise ValueError
|
||||
|
||||
class MyPlugin2:
|
||||
def pytest_pyfunc_call(self, pyfuncitem):
|
||||
return True
|
||||
|
||||
config.pluginmanager.register(MyPlugin1())
|
||||
config.pluginmanager.register(MyPlugin2())
|
||||
config.hook.pytest_runtest_setup(item=item)
|
||||
|
||||
@@ -10,15 +10,20 @@ from _pytest import fixtures
|
||||
def test_getfuncargnames():
|
||||
def f(): pass
|
||||
assert not fixtures.getfuncargnames(f)
|
||||
|
||||
def g(arg): pass
|
||||
assert fixtures.getfuncargnames(g) == ('arg',)
|
||||
|
||||
def h(arg1, arg2="hello"): pass
|
||||
assert fixtures.getfuncargnames(h) == ('arg1',)
|
||||
|
||||
def h(arg1, arg2, arg3="hello"): pass
|
||||
assert fixtures.getfuncargnames(h) == ('arg1', 'arg2')
|
||||
|
||||
class A:
|
||||
def f(self, arg1, arg2="hello"):
|
||||
pass
|
||||
|
||||
assert fixtures.getfuncargnames(A().f) == ('arg1',)
|
||||
if sys.version_info < (3,0):
|
||||
assert fixtures.getfuncargnames(A.f) == ('arg1',)
|
||||
@@ -869,8 +874,10 @@ class TestRequestCachedSetup:
|
||||
item1 = testdir.getitem("def test_func(): pass")
|
||||
req1 = fixtures.FixtureRequest(item1)
|
||||
l = ["hello", "world"]
|
||||
|
||||
def setup():
|
||||
return l.pop()
|
||||
|
||||
ret1 = req1.cached_setup(setup, extrakey=1)
|
||||
ret2 = req1.cached_setup(setup, extrakey=2)
|
||||
assert ret2 == "hello"
|
||||
@@ -884,10 +891,13 @@ class TestRequestCachedSetup:
|
||||
item1 = testdir.getitem("def test_func(): pass")
|
||||
req1 = fixtures.FixtureRequest(item1)
|
||||
l = []
|
||||
|
||||
def setup():
|
||||
l.append("setup")
|
||||
|
||||
def teardown(val):
|
||||
l.append("teardown")
|
||||
|
||||
req1.cached_setup(setup, teardown, scope="function")
|
||||
assert l == ['setup']
|
||||
# artificial call of finalizer
|
||||
|
||||
@@ -63,10 +63,12 @@ class TestOEJSKITSpecials:
|
||||
def test_wrapped_getfslineno():
|
||||
def func():
|
||||
pass
|
||||
|
||||
def wrap(f):
|
||||
func.__wrapped__ = f
|
||||
func.patchings = ["qwe"]
|
||||
return func
|
||||
|
||||
@wrap
|
||||
def wrapped_func(x, y, z):
|
||||
pass
|
||||
@@ -77,28 +79,36 @@ def test_wrapped_getfslineno():
|
||||
class TestMockDecoration:
|
||||
def test_wrapped_getfuncargnames(self):
|
||||
from _pytest.compat import getfuncargnames
|
||||
|
||||
def wrap(f):
|
||||
|
||||
def func():
|
||||
pass
|
||||
|
||||
func.__wrapped__ = f
|
||||
return func
|
||||
|
||||
@wrap
|
||||
def f(x):
|
||||
pass
|
||||
|
||||
l = getfuncargnames(f)
|
||||
assert l == ("x",)
|
||||
|
||||
def test_wrapped_getfuncargnames_patching(self):
|
||||
from _pytest.compat import getfuncargnames
|
||||
|
||||
def wrap(f):
|
||||
def func():
|
||||
pass
|
||||
func.__wrapped__ = f
|
||||
func.patchings = ["qwe"]
|
||||
return func
|
||||
|
||||
@wrap
|
||||
def f(x, y, z):
|
||||
pass
|
||||
|
||||
l = getfuncargnames(f)
|
||||
assert l == ("y", "z")
|
||||
|
||||
|
||||
@@ -20,8 +20,10 @@ class TestMetafunc:
|
||||
# initiliazation
|
||||
class FixtureInfo:
|
||||
name2fixturedefs = None
|
||||
|
||||
def __init__(self, names):
|
||||
self.names_closure = names
|
||||
|
||||
names = fixtures.getfuncargnames(func)
|
||||
fixtureinfo = FixtureInfo(names)
|
||||
return python.Metafunc(func, fixtureinfo, None)
|
||||
@@ -65,7 +67,9 @@ class TestMetafunc:
|
||||
def test_addcall_param(self):
|
||||
def func(arg1): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
|
||||
class obj: pass
|
||||
|
||||
metafunc.addcall(param=obj)
|
||||
metafunc.addcall(param=obj)
|
||||
metafunc.addcall(param=1)
|
||||
@@ -76,8 +80,11 @@ class TestMetafunc:
|
||||
|
||||
def test_addcall_funcargs(self):
|
||||
def func(x): pass
|
||||
|
||||
metafunc = self.Metafunc(func)
|
||||
|
||||
class obj: pass
|
||||
|
||||
metafunc.addcall(funcargs={"x": 2})
|
||||
metafunc.addcall(funcargs={"x": 3})
|
||||
pytest.raises(pytest.fail.Exception, "metafunc.addcall({'xyz': 0})")
|
||||
@@ -142,8 +149,10 @@ class TestMetafunc:
|
||||
def test_parametrize_with_userobjects(self):
|
||||
def func(x, y): pass
|
||||
metafunc = self.Metafunc(func)
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
metafunc.parametrize("x", [A(), A()])
|
||||
metafunc.parametrize("y", list("ab"))
|
||||
assert metafunc._calls[0].id == "x0-a"
|
||||
@@ -254,6 +263,7 @@ class TestMetafunc:
|
||||
@pytest.mark.issue351
|
||||
def test_idmaker_idfn(self):
|
||||
from _pytest.python import idmaker
|
||||
|
||||
def ids(val):
|
||||
if isinstance(val, Exception):
|
||||
return repr(val)
|
||||
@@ -270,6 +280,7 @@ class TestMetafunc:
|
||||
@pytest.mark.issue351
|
||||
def test_idmaker_idfn_unique_names(self):
|
||||
from _pytest.python import idmaker
|
||||
|
||||
def ids(val):
|
||||
return 'a'
|
||||
|
||||
@@ -285,6 +296,7 @@ class TestMetafunc:
|
||||
@pytest.mark.issue351
|
||||
def test_idmaker_idfn_exception(self):
|
||||
from _pytest.python import idmaker
|
||||
|
||||
def ids(val):
|
||||
raise Exception("bad code")
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import pytest
|
||||
import sys
|
||||
|
||||
|
||||
class TestRaises:
|
||||
def test_raises(self):
|
||||
@@ -96,3 +98,31 @@ class TestRaises:
|
||||
assert e.msg == message
|
||||
else:
|
||||
assert False, "Expected pytest.raises.Exception"
|
||||
|
||||
@pytest.mark.parametrize('method', ['function', 'with'])
|
||||
def test_raises_cyclic_reference(self, method):
|
||||
"""
|
||||
Ensure pytest.raises does not leave a reference cycle (#1965).
|
||||
"""
|
||||
import gc
|
||||
|
||||
class T(object):
|
||||
def __call__(self):
|
||||
raise ValueError
|
||||
|
||||
t = T()
|
||||
if method == 'function':
|
||||
pytest.raises(ValueError, t)
|
||||
else:
|
||||
with pytest.raises(ValueError):
|
||||
t()
|
||||
|
||||
# ensure both forms of pytest.raises don't leave exceptions in sys.exc_info()
|
||||
assert sys.exc_info() == (None, None, None)
|
||||
|
||||
del t
|
||||
|
||||
# ensure the t instance is not stuck in a cyclic reference
|
||||
for o in gc.get_objects():
|
||||
assert type(o) is not T
|
||||
|
||||
|
||||
@@ -12,12 +12,15 @@ PY3 = sys.version_info >= (3, 0)
|
||||
|
||||
@pytest.fixture
|
||||
def mock_config():
|
||||
|
||||
class Config(object):
|
||||
verbose = False
|
||||
|
||||
def getoption(self, name):
|
||||
if name == 'verbose':
|
||||
return self.verbose
|
||||
raise KeyError('Not mocked out: %s' % name)
|
||||
|
||||
return Config()
|
||||
|
||||
|
||||
@@ -749,6 +752,37 @@ def test_traceback_failure(testdir):
|
||||
"*test_traceback_failure.py:4: AssertionError"
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info[:2] <= (3, 3), reason='Python 3.4+ shows chained exceptions on multiprocess')
|
||||
def test_exception_handling_no_traceback(testdir):
|
||||
"""
|
||||
Handle chain exceptions in tasks submitted by the multiprocess module (#1984).
|
||||
"""
|
||||
p1 = testdir.makepyfile("""
|
||||
from multiprocessing import Pool
|
||||
|
||||
def process_task(n):
|
||||
assert n == 10
|
||||
|
||||
def multitask_job():
|
||||
tasks = [1]
|
||||
with Pool(processes=1) as pool:
|
||||
pool.map(process_task, tasks)
|
||||
|
||||
def test_multitask_job():
|
||||
multitask_job()
|
||||
""")
|
||||
result = testdir.runpytest(p1, "--tb=long")
|
||||
result.stdout.fnmatch_lines([
|
||||
"====* FAILURES *====",
|
||||
"*multiprocessing.pool.RemoteTraceback:*",
|
||||
"Traceback (most recent call last):",
|
||||
"*assert n == 10",
|
||||
"The above exception was the direct cause of the following exception:",
|
||||
"> * multitask_job()",
|
||||
])
|
||||
|
||||
|
||||
@pytest.mark.skipif("'__pypy__' in sys.builtin_module_names or sys.platform.startswith('java')" )
|
||||
def test_warn_missing(testdir):
|
||||
testdir.makepyfile("")
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
import glob
|
||||
import os
|
||||
import py_compile
|
||||
import stat
|
||||
import sys
|
||||
import zipfile
|
||||
|
||||
import py
|
||||
import pytest
|
||||
|
||||
@@ -104,20 +107,29 @@ class TestAssertionRewrite:
|
||||
def f():
|
||||
assert False
|
||||
assert getmsg(f) == "assert False"
|
||||
|
||||
def f():
|
||||
f = False
|
||||
assert f
|
||||
|
||||
assert getmsg(f) == "assert False"
|
||||
|
||||
def f():
|
||||
assert a_global # noqa
|
||||
|
||||
assert getmsg(f, {"a_global" : False}) == "assert False"
|
||||
|
||||
def f():
|
||||
assert sys == 42
|
||||
|
||||
assert getmsg(f, {"sys" : sys}) == "assert sys == 42"
|
||||
|
||||
def f():
|
||||
assert cls == 42 # noqa
|
||||
|
||||
class X(object):
|
||||
pass
|
||||
|
||||
assert getmsg(f, {"cls" : X}) == "assert cls == 42"
|
||||
|
||||
def test_assert_already_has_message(self):
|
||||
@@ -190,78 +202,110 @@ class TestAssertionRewrite:
|
||||
def f():
|
||||
f = g = False
|
||||
assert f and g
|
||||
|
||||
assert getmsg(f) == "assert (False)"
|
||||
|
||||
def f():
|
||||
f = True
|
||||
g = False
|
||||
assert f and g
|
||||
|
||||
assert getmsg(f) == "assert (True and False)"
|
||||
|
||||
def f():
|
||||
f = False
|
||||
g = True
|
||||
assert f and g
|
||||
|
||||
assert getmsg(f) == "assert (False)"
|
||||
|
||||
def f():
|
||||
f = g = False
|
||||
assert f or g
|
||||
|
||||
assert getmsg(f) == "assert (False or False)"
|
||||
|
||||
def f():
|
||||
f = g = False
|
||||
assert not f and not g
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def x():
|
||||
return False
|
||||
|
||||
def f():
|
||||
assert x() and x()
|
||||
|
||||
assert getmsg(f, {"x" : x}) == """assert (False)
|
||||
+ where False = x()"""
|
||||
|
||||
def f():
|
||||
assert False or x()
|
||||
|
||||
assert getmsg(f, {"x" : x}) == """assert (False or False)
|
||||
+ where False = x()"""
|
||||
|
||||
def f():
|
||||
assert 1 in {} and 2 in {}
|
||||
|
||||
assert getmsg(f) == "assert (1 in {})"
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
y = 2
|
||||
assert x in {1 : None} and y in {}
|
||||
|
||||
assert getmsg(f) == "assert (1 in {1: None} and 2 in {})"
|
||||
|
||||
def f():
|
||||
f = True
|
||||
g = False
|
||||
assert f or g
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def f():
|
||||
f = g = h = lambda: True
|
||||
assert f() and g() and h()
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def test_short_circut_evaluation(self):
|
||||
def f():
|
||||
assert True or explode # noqa
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def f():
|
||||
x = 1
|
||||
assert x == 1 or x == 2
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def test_unary_op(self):
|
||||
def f():
|
||||
x = True
|
||||
assert not x
|
||||
|
||||
assert getmsg(f) == "assert not True"
|
||||
|
||||
def f():
|
||||
x = 0
|
||||
assert ~x + 1
|
||||
|
||||
assert getmsg(f) == "assert (~0 + 1)"
|
||||
|
||||
def f():
|
||||
x = 3
|
||||
assert -x + x
|
||||
|
||||
assert getmsg(f) == "assert (-3 + 3)"
|
||||
|
||||
def f():
|
||||
x = 0
|
||||
assert +x + x
|
||||
|
||||
assert getmsg(f) == "assert (+0 + 0)"
|
||||
|
||||
def test_binary_op(self):
|
||||
@@ -269,7 +313,9 @@ class TestAssertionRewrite:
|
||||
x = 1
|
||||
y = -1
|
||||
assert x + y
|
||||
|
||||
assert getmsg(f) == "assert (1 + -1)"
|
||||
|
||||
def f():
|
||||
assert not 5 % 4
|
||||
assert getmsg(f) == "assert not (5 % 4)"
|
||||
@@ -277,7 +323,9 @@ class TestAssertionRewrite:
|
||||
def test_boolop_percent(self):
|
||||
def f():
|
||||
assert 3 % 2 and False
|
||||
|
||||
assert getmsg(f) == "assert ((3 % 2) and False)"
|
||||
|
||||
def f():
|
||||
assert False or 4 % 2
|
||||
assert getmsg(f) == "assert (False or (4 % 2))"
|
||||
@@ -298,113 +346,159 @@ class TestAssertionRewrite:
|
||||
def test_call(self):
|
||||
def g(a=42, *args, **kwargs):
|
||||
return False
|
||||
|
||||
ns = {"g" : g}
|
||||
|
||||
def f():
|
||||
assert g()
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g()"""
|
||||
|
||||
def f():
|
||||
assert g(1)
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(1)"""
|
||||
|
||||
def f():
|
||||
assert g(1, 2)
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(1, 2)"""
|
||||
|
||||
def f():
|
||||
assert g(1, g=42)
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(1, g=42)"""
|
||||
|
||||
def f():
|
||||
assert g(1, 3, g=23)
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(1, 3, g=23)"""
|
||||
|
||||
def f():
|
||||
seq = [1, 2, 3]
|
||||
assert g(*seq)
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(*[1, 2, 3])"""
|
||||
|
||||
def f():
|
||||
x = "a"
|
||||
assert g(**{x : 2})
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = g(**{'a': 2})"""
|
||||
|
||||
def test_attribute(self):
|
||||
class X(object):
|
||||
g = 3
|
||||
|
||||
ns = {"x" : X}
|
||||
|
||||
def f():
|
||||
assert not x.g # noqa
|
||||
|
||||
assert getmsg(f, ns) == """assert not 3
|
||||
+ where 3 = x.g"""
|
||||
|
||||
def f():
|
||||
x.a = False # noqa
|
||||
assert x.a # noqa
|
||||
|
||||
assert getmsg(f, ns) == """assert False
|
||||
+ where False = x.a"""
|
||||
|
||||
def test_comparisons(self):
|
||||
|
||||
def f():
|
||||
a, b = range(2)
|
||||
assert b < a
|
||||
|
||||
assert getmsg(f) == """assert 1 < 0"""
|
||||
|
||||
def f():
|
||||
a, b, c = range(3)
|
||||
assert a > b > c
|
||||
|
||||
assert getmsg(f) == """assert 0 > 1"""
|
||||
|
||||
def f():
|
||||
a, b, c = range(3)
|
||||
assert a < b > c
|
||||
|
||||
assert getmsg(f) == """assert 1 > 2"""
|
||||
|
||||
def f():
|
||||
a, b, c = range(3)
|
||||
assert a < b <= c
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def f():
|
||||
a, b, c = range(3)
|
||||
assert a < b
|
||||
assert b < c
|
||||
|
||||
getmsg(f, must_pass=True)
|
||||
|
||||
def test_len(self):
|
||||
|
||||
def f():
|
||||
l = list(range(10))
|
||||
assert len(l) == 11
|
||||
|
||||
assert getmsg(f).startswith("""assert 10 == 11
|
||||
+ where 10 = len([""")
|
||||
|
||||
def test_custom_reprcompare(self, monkeypatch):
|
||||
def my_reprcompare(op, left, right):
|
||||
return "42"
|
||||
|
||||
monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
|
||||
|
||||
def f():
|
||||
assert 42 < 3
|
||||
|
||||
assert getmsg(f) == "assert 42"
|
||||
|
||||
def my_reprcompare(op, left, right):
|
||||
return "%s %s %s" % (left, op, right)
|
||||
|
||||
monkeypatch.setattr(util, "_reprcompare", my_reprcompare)
|
||||
|
||||
def f():
|
||||
assert 1 < 3 < 5 <= 4 < 7
|
||||
|
||||
assert getmsg(f) == "assert 5 <= 4"
|
||||
|
||||
def test_assert_raising_nonzero_in_comparison(self):
|
||||
def f():
|
||||
class A(object):
|
||||
|
||||
def __nonzero__(self):
|
||||
raise ValueError(42)
|
||||
|
||||
def __lt__(self, other):
|
||||
return A()
|
||||
|
||||
def __repr__(self):
|
||||
return "<MY42 object>"
|
||||
|
||||
def myany(x):
|
||||
return False
|
||||
|
||||
assert myany(A() < 0)
|
||||
|
||||
assert "<MY42 object> < 0" in getmsg(f)
|
||||
|
||||
def test_formatchar(self):
|
||||
def f():
|
||||
assert "%test" == "test"
|
||||
|
||||
assert getmsg(f).startswith("assert '%test' == 'test'")
|
||||
|
||||
def test_custom_repr(self):
|
||||
@@ -414,8 +508,10 @@ class TestAssertionRewrite:
|
||||
|
||||
def __repr__(self):
|
||||
return "\n{ \n~ \n}"
|
||||
|
||||
f = Foo()
|
||||
assert 0 == f.a
|
||||
|
||||
assert r"where 1 = \n{ \n~ \n}.a" in util._format_lines([getmsg(f)])[0]
|
||||
|
||||
|
||||
@@ -480,6 +576,31 @@ def test_rewritten():
|
||||
monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1")
|
||||
assert testdir.runpytest_subprocess().ret == 0
|
||||
|
||||
def test_orphaned_pyc_file(self, testdir):
|
||||
if sys.version_info < (3, 0) and hasattr(sys, 'pypy_version_info'):
|
||||
pytest.skip("pypy2 doesn't run orphaned pyc files")
|
||||
|
||||
testdir.makepyfile("""
|
||||
import orphan
|
||||
def test_it():
|
||||
assert orphan.value == 17
|
||||
""")
|
||||
testdir.makepyfile(orphan="""
|
||||
value = 17
|
||||
""")
|
||||
py_compile.compile("orphan.py")
|
||||
os.remove("orphan.py")
|
||||
|
||||
# Python 3 puts the .pyc files in a __pycache__ directory, and will
|
||||
# not import from there without source. It will import a .pyc from
|
||||
# the source location though.
|
||||
if not os.path.exists("orphan.pyc"):
|
||||
pycs = glob.glob("__pycache__/orphan.*.pyc")
|
||||
assert len(pycs) == 1
|
||||
os.rename(pycs[0], "orphan.pyc")
|
||||
|
||||
assert testdir.runpytest().ret == 0
|
||||
|
||||
@pytest.mark.skipif('"__pypy__" in sys.modules')
|
||||
def test_pyc_vs_pyo(self, testdir, monkeypatch):
|
||||
testdir.makepyfile("""
|
||||
@@ -527,8 +648,10 @@ def test_rewritten():
|
||||
def test_rewrite_warning(self, pytestconfig, monkeypatch):
|
||||
hook = AssertionRewritingHook(pytestconfig)
|
||||
warnings = []
|
||||
|
||||
def mywarn(code, msg):
|
||||
warnings.append((code, msg))
|
||||
|
||||
monkeypatch.setattr(hook.config, 'warn', mywarn)
|
||||
hook.mark_rewrite('_pytest')
|
||||
assert '_pytest' in warnings[0][1]
|
||||
@@ -543,6 +666,35 @@ def test_rewritten():
|
||||
''')
|
||||
assert testdir.runpytest_subprocess().ret == 0
|
||||
|
||||
def test_remember_rewritten_modules(self, pytestconfig, testdir, monkeypatch):
|
||||
"""
|
||||
AssertionRewriteHook should remember rewritten modules so it
|
||||
doesn't give false positives (#2005).
|
||||
"""
|
||||
monkeypatch.syspath_prepend(testdir.tmpdir)
|
||||
testdir.makepyfile(test_remember_rewritten_modules='')
|
||||
warnings = []
|
||||
hook = AssertionRewritingHook(pytestconfig)
|
||||
monkeypatch.setattr(hook.config, 'warn', lambda code, msg: warnings.append(msg))
|
||||
hook.find_module('test_remember_rewritten_modules')
|
||||
hook.load_module('test_remember_rewritten_modules')
|
||||
hook.mark_rewrite('test_remember_rewritten_modules')
|
||||
hook.mark_rewrite('test_remember_rewritten_modules')
|
||||
assert warnings == []
|
||||
|
||||
def test_rewrite_warning_using_pytest_plugins(self, testdir, monkeypatch):
|
||||
testdir.makepyfile(**{
|
||||
'conftest.py': "pytest_plugins = ['core', 'gui', 'sci']",
|
||||
'core.py': "",
|
||||
'gui.py': "pytest_plugins = ['core', 'sci']",
|
||||
'sci.py': "pytest_plugins = ['core']",
|
||||
'test_rewrite_warning_pytest_plugins.py': "def test(): pass",
|
||||
})
|
||||
testdir.chdir()
|
||||
result = testdir.runpytest_subprocess()
|
||||
result.stdout.fnmatch_lines(['*= 1 passed in *=*'])
|
||||
assert 'pytest-warning summary' not in result.stdout.str()
|
||||
|
||||
|
||||
class TestAssertionRewriteHookDetails(object):
|
||||
def test_loader_is_package_false_for_module(self, testdir):
|
||||
@@ -626,10 +778,12 @@ class TestAssertionRewriteHookDetails(object):
|
||||
source_path = tmpdir.ensure("source.py")
|
||||
pycpath = tmpdir.join("pyc").strpath
|
||||
assert _write_pyc(state, [1], source_path.stat(), pycpath)
|
||||
|
||||
def open(*args):
|
||||
e = IOError()
|
||||
e.errno = 10
|
||||
raise e
|
||||
|
||||
monkeypatch.setattr(b, "open", open)
|
||||
assert not _write_pyc(state, [1], source_path.stat(), pycpath)
|
||||
|
||||
|
||||
@@ -150,11 +150,13 @@ class TestCollectFS:
|
||||
class TestCollectPluginHookRelay:
|
||||
def test_pytest_collect_file(self, testdir):
|
||||
wascalled = []
|
||||
|
||||
class Plugin:
|
||||
def pytest_collect_file(self, path, parent):
|
||||
if not path.basename.startswith("."):
|
||||
# Ignore hidden files, e.g. .testmondata.
|
||||
wascalled.append(path)
|
||||
|
||||
testdir.makefile(".abc", "xyz")
|
||||
pytest.main([testdir.tmpdir], plugins=[Plugin()])
|
||||
assert len(wascalled) == 1
|
||||
@@ -162,27 +164,19 @@ class TestCollectPluginHookRelay:
|
||||
|
||||
def test_pytest_collect_directory(self, testdir):
|
||||
wascalled = []
|
||||
|
||||
class Plugin:
|
||||
def pytest_collect_directory(self, path, parent):
|
||||
wascalled.append(path.basename)
|
||||
|
||||
testdir.mkdir("hello")
|
||||
testdir.mkdir("world")
|
||||
pytest.main(testdir.tmpdir, plugins=[Plugin()])
|
||||
assert "hello" in wascalled
|
||||
assert "world" in wascalled
|
||||
|
||||
|
||||
class TestPrunetraceback:
|
||||
def test_collection_error(self, testdir):
|
||||
p = testdir.makepyfile("""
|
||||
import not_exists
|
||||
""")
|
||||
result = testdir.runpytest(p)
|
||||
assert "__import__" not in result.stdout.str(), "too long traceback"
|
||||
result.stdout.fnmatch_lines([
|
||||
"*ERROR collecting*",
|
||||
"ImportError while importing test module*",
|
||||
"'No module named *not_exists*",
|
||||
])
|
||||
|
||||
def test_custom_repr_failure(self, testdir):
|
||||
p = testdir.makepyfile("""
|
||||
|
||||
@@ -83,7 +83,7 @@ class TestParseIni:
|
||||
""")
|
||||
result = testdir.inline_run("--confcutdir=.")
|
||||
assert result.ret == 0
|
||||
|
||||
|
||||
class TestConfigCmdlineParsing:
|
||||
def test_parsing_again_fails(self, testdir):
|
||||
config = testdir.parseconfig()
|
||||
@@ -294,6 +294,15 @@ class TestConfigAPI:
|
||||
assert len(l) == 2
|
||||
assert l == ["456", "123"]
|
||||
|
||||
def test_confcutdir_check_isdir(self, testdir):
|
||||
"""Give an error if --confcutdir is not a valid directory (#2078)"""
|
||||
with pytest.raises(pytest.UsageError):
|
||||
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('file').ensure(file=1))
|
||||
with pytest.raises(pytest.UsageError):
|
||||
testdir.parseconfig('--confcutdir', testdir.tmpdir.join('inexistant'))
|
||||
config = testdir.parseconfig('--confcutdir', testdir.tmpdir.join('dir').ensure(dir=1))
|
||||
assert config.getoption('confcutdir') == str(testdir.tmpdir.join('dir'))
|
||||
|
||||
|
||||
class TestConfigFromdictargs:
|
||||
def test_basic_behavior(self):
|
||||
@@ -373,23 +382,31 @@ def test_options_on_small_file_do_not_blow_up(testdir):
|
||||
['--traceconfig'], ['-v'], ['-v', '-v']):
|
||||
runfiletest(opts + [path])
|
||||
|
||||
|
||||
def test_preparse_ordering_with_setuptools(testdir, monkeypatch):
|
||||
pkg_resources = pytest.importorskip("pkg_resources")
|
||||
|
||||
def my_iter(name):
|
||||
assert name == "pytest11"
|
||||
|
||||
class Dist:
|
||||
project_name = 'spam'
|
||||
version = '1.0'
|
||||
|
||||
def _get_metadata(self, name):
|
||||
return ['foo.txt,sha256=abc,123']
|
||||
|
||||
class EntryPoint:
|
||||
name = "mytestplugin"
|
||||
dist = Dist()
|
||||
|
||||
def load(self):
|
||||
class PseudoPlugin:
|
||||
x = 42
|
||||
return PseudoPlugin()
|
||||
|
||||
return iter([EntryPoint()])
|
||||
|
||||
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
||||
testdir.makeconftest("""
|
||||
pytest_plugins = "mytestplugin",
|
||||
@@ -402,18 +419,24 @@ def test_preparse_ordering_with_setuptools(testdir, monkeypatch):
|
||||
|
||||
def test_setuptools_importerror_issue1479(testdir, monkeypatch):
|
||||
pkg_resources = pytest.importorskip("pkg_resources")
|
||||
|
||||
def my_iter(name):
|
||||
assert name == "pytest11"
|
||||
|
||||
class Dist:
|
||||
project_name = 'spam'
|
||||
version = '1.0'
|
||||
|
||||
def _get_metadata(self, name):
|
||||
return ['foo.txt,sha256=abc,123']
|
||||
|
||||
class EntryPoint:
|
||||
name = "mytestplugin"
|
||||
dist = Dist()
|
||||
|
||||
def load(self):
|
||||
raise ImportError("Don't hide me!")
|
||||
|
||||
return iter([EntryPoint()])
|
||||
|
||||
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
||||
@@ -423,19 +446,26 @@ def test_setuptools_importerror_issue1479(testdir, monkeypatch):
|
||||
|
||||
def test_plugin_preparse_prevents_setuptools_loading(testdir, monkeypatch):
|
||||
pkg_resources = pytest.importorskip("pkg_resources")
|
||||
|
||||
def my_iter(name):
|
||||
assert name == "pytest11"
|
||||
|
||||
class Dist:
|
||||
project_name = 'spam'
|
||||
version = '1.0'
|
||||
|
||||
def _get_metadata(self, name):
|
||||
return ['foo.txt,sha256=abc,123']
|
||||
|
||||
class EntryPoint:
|
||||
name = "mytestplugin"
|
||||
dist = Dist()
|
||||
|
||||
def load(self):
|
||||
assert 0, "should not arrive here"
|
||||
|
||||
return iter([EntryPoint()])
|
||||
|
||||
monkeypatch.setattr(pkg_resources, 'iter_entry_points', my_iter)
|
||||
config = testdir.parseconfig("-p", "no:mytestplugin")
|
||||
plugin = config.pluginmanager.getplugin("mytestplugin")
|
||||
@@ -503,9 +533,11 @@ def test_notify_exception(testdir, capfd):
|
||||
config.notify_exception(excinfo)
|
||||
out, err = capfd.readouterr()
|
||||
assert "ValueError" in err
|
||||
|
||||
class A:
|
||||
def pytest_internalerror(self, excrepr):
|
||||
return True
|
||||
|
||||
config.pluginmanager.register(A())
|
||||
config.notify_exception(excinfo)
|
||||
out, err = capfd.readouterr()
|
||||
@@ -515,9 +547,11 @@ def test_notify_exception(testdir, capfd):
|
||||
def test_load_initial_conftest_last_ordering(testdir):
|
||||
from _pytest.config import get_config
|
||||
pm = get_config().pluginmanager
|
||||
|
||||
class My:
|
||||
def pytest_load_initial_conftests(self):
|
||||
pass
|
||||
|
||||
m = My()
|
||||
pm.register(m)
|
||||
hc = pm.hook.pytest_load_initial_conftests
|
||||
@@ -698,6 +732,14 @@ class TestOverrideIniArgs:
|
||||
"ini3:True",
|
||||
"ini4:False"])
|
||||
|
||||
def test_override_ini_usage_error_bad_style(self, testdir):
|
||||
testdir.makeini("""
|
||||
[pytest]
|
||||
xdist_strict=False
|
||||
""")
|
||||
result = testdir.runpytest("--override-ini", 'xdist_strict True', "-s")
|
||||
result.stderr.fnmatch_lines(["*ERROR* *expects option=value*"])
|
||||
|
||||
def test_with_arg_outside_cwd_without_inifile(self, tmpdir, monkeypatch):
|
||||
monkeypatch.chdir(str(tmpdir))
|
||||
a = tmpdir.mkdir("a")
|
||||
|
||||
@@ -200,8 +200,10 @@ def test_conftest_import_order(testdir, monkeypatch):
|
||||
sub = testdir.mkdir("sub")
|
||||
ct2 = sub.join("conftest.py")
|
||||
ct2.write("")
|
||||
|
||||
def impct(p):
|
||||
return p
|
||||
|
||||
conftest = PytestPluginManager()
|
||||
conftest._confcutdir = testdir.tmpdir
|
||||
monkeypatch.setattr(conftest, '_importconftest', impct)
|
||||
@@ -421,3 +423,28 @@ def test_conftest_exception_handling(testdir):
|
||||
res = testdir.runpytest()
|
||||
assert res.ret == 4
|
||||
assert 'raise ValueError()' in [line.strip() for line in res.errlines]
|
||||
|
||||
|
||||
def test_hook_proxy(testdir):
|
||||
"""Session's gethookproxy() would cache conftests incorrectly (#2016).
|
||||
It was decided to remove the cache altogether.
|
||||
"""
|
||||
testdir.makepyfile(**{
|
||||
'root/demo-0/test_foo1.py': "def test1(): pass",
|
||||
|
||||
'root/demo-a/test_foo2.py': "def test1(): pass",
|
||||
'root/demo-a/conftest.py': """
|
||||
def pytest_ignore_collect(path, config):
|
||||
return True
|
||||
""",
|
||||
|
||||
'root/demo-b/test_foo3.py': "def test1(): pass",
|
||||
'root/demo-c/test_foo4.py': "def test1(): pass",
|
||||
})
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([
|
||||
'*test_foo1.py*',
|
||||
'*test_foo3.py*',
|
||||
'*test_foo4.py*',
|
||||
'*3 passed*',
|
||||
])
|
||||
|
||||
@@ -165,6 +165,30 @@ class TestPython:
|
||||
fnode.assert_attr(message="test setup failure")
|
||||
assert "ValueError" in fnode.toxml()
|
||||
|
||||
def test_teardown_error(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
|
||||
@pytest.fixture
|
||||
def arg():
|
||||
yield
|
||||
raise ValueError()
|
||||
def test_function(arg):
|
||||
pass
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
assert result.ret
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
tnode = node.find_first_by_tag("testcase")
|
||||
tnode.assert_attr(
|
||||
file="test_teardown_error.py",
|
||||
line="6",
|
||||
classname="test_teardown_error",
|
||||
name="test_function")
|
||||
fnode = tnode.find_first_by_tag("error")
|
||||
fnode.assert_attr(message="test teardown failure")
|
||||
assert "ValueError" in fnode.toxml()
|
||||
|
||||
def test_skip_contains_name_reason(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@@ -225,6 +249,18 @@ class TestPython:
|
||||
snode = tnode.find_first_by_tag("skipped")
|
||||
snode.assert_attr(type="pytest.skip", message="hello25", )
|
||||
|
||||
def test_mark_skip_doesnt_capture_output(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import pytest
|
||||
@pytest.mark.skip(reason="foo")
|
||||
def test_skip():
|
||||
print("bar!")
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
assert result.ret == 0
|
||||
node_xml = dom.find_first_by_tag("testsuite").toxml()
|
||||
assert "bar!" not in node_xml
|
||||
|
||||
def test_classname_instance(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
class TestClass:
|
||||
@@ -679,6 +715,10 @@ def test_logxml_makedir(testdir):
|
||||
assert result.ret == 0
|
||||
assert testdir.tmpdir.join("path/to/results.xml").check()
|
||||
|
||||
def test_logxml_check_isdir(testdir):
|
||||
"""Give an error if --junit-xml is a directory (#2089)"""
|
||||
result = testdir.runpytest("--junit-xml=.")
|
||||
result.stderr.fnmatch_lines(["*--junitxml must be a filename*"])
|
||||
|
||||
def test_escaped_parametrized_names_xml(testdir):
|
||||
testdir.makepyfile("""
|
||||
|
||||
@@ -23,15 +23,19 @@ class TestMark:
|
||||
|
||||
def test_pytest_mark_bare(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
mark.hello(f)
|
||||
assert f.hello
|
||||
|
||||
def test_pytest_mark_keywords(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
mark.world(x=3, y=4)(f)
|
||||
assert f.world
|
||||
assert f.world.kwargs['x'] == 3
|
||||
@@ -39,8 +43,10 @@ class TestMark:
|
||||
|
||||
def test_apply_multiple_and_merge(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
mark.world
|
||||
mark.world(x=3)(f)
|
||||
assert f.world.kwargs['x'] == 3
|
||||
@@ -53,33 +59,43 @@ class TestMark:
|
||||
|
||||
def test_pytest_mark_positional(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
mark.world("hello")(f)
|
||||
assert f.world.args[0] == "hello"
|
||||
mark.world("world")(f)
|
||||
|
||||
def test_pytest_mark_positional_func_and_keyword(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
raise Exception
|
||||
|
||||
m = mark.world(f, omega="hello")
|
||||
|
||||
def g():
|
||||
pass
|
||||
|
||||
assert m(g) == g
|
||||
assert g.world.args[0] is f
|
||||
assert g.world.kwargs["omega"] == "hello"
|
||||
|
||||
def test_pytest_mark_reuse(self):
|
||||
mark = Mark()
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
w = mark.some
|
||||
w("hello", reason="123")(f)
|
||||
assert f.some.args[0] == "hello"
|
||||
assert f.some.kwargs['reason'] == "123"
|
||||
|
||||
def g():
|
||||
pass
|
||||
|
||||
w("world", reason2="456")(g)
|
||||
assert g.some.args[0] == "world"
|
||||
assert 'reason' not in g.some.kwargs
|
||||
@@ -610,11 +626,12 @@ class TestFunctional:
|
||||
def test_1(parameter):
|
||||
assert True
|
||||
""")
|
||||
|
||||
reprec = testdir.inline_run()
|
||||
reprec.assertoutcome(skipped=1)
|
||||
|
||||
|
||||
class TestKeywordSelection:
|
||||
|
||||
def test_select_simple(self, testdir):
|
||||
file_test = testdir.makepyfile("""
|
||||
def test_one():
|
||||
@@ -623,6 +640,7 @@ class TestKeywordSelection:
|
||||
def test_method_one(self):
|
||||
assert 42 == 43
|
||||
""")
|
||||
|
||||
def check(keyword, name):
|
||||
reprec = testdir.inline_run("-s", "-k", keyword, file_test)
|
||||
passed, skipped, failed = reprec.listoutcomes()
|
||||
@@ -709,6 +727,7 @@ class TestKeywordSelection:
|
||||
p = testdir.makepyfile("""
|
||||
def test_one(): assert 1
|
||||
""")
|
||||
|
||||
def assert_test_is_not_selected(keyword):
|
||||
reprec = testdir.inline_run("-k", keyword, p)
|
||||
passed, skipped, failed = reprec.countoutcomes()
|
||||
|
||||
@@ -25,18 +25,22 @@ def test_nose_setup(testdir):
|
||||
def test_setup_func_with_setup_decorator():
|
||||
from _pytest.nose import call_optional
|
||||
l = []
|
||||
|
||||
class A:
|
||||
@pytest.fixture(autouse=True)
|
||||
def f(self):
|
||||
l.append(1)
|
||||
|
||||
call_optional(A(), "f")
|
||||
assert not l
|
||||
|
||||
|
||||
def test_setup_func_not_callable():
|
||||
from _pytest.nose import call_optional
|
||||
|
||||
class A:
|
||||
f = 1
|
||||
|
||||
call_optional(A(), "f")
|
||||
|
||||
def test_nose_setup_func(testdir):
|
||||
|
||||
@@ -138,7 +138,10 @@ class TestParser:
|
||||
def test_parse_setoption(self, parser):
|
||||
parser.addoption("--hello", dest="hello", action="store")
|
||||
parser.addoption("--world", dest="world", default=42)
|
||||
class A: pass
|
||||
|
||||
class A:
|
||||
pass
|
||||
|
||||
option = A()
|
||||
args = parser.parse_setoption(['--hello', 'world'], option)
|
||||
assert option.hello == "world"
|
||||
@@ -248,7 +251,19 @@ class TestParser:
|
||||
help="show help message and configuration info")
|
||||
parser.parse(['-h'])
|
||||
help = parser.optparser.format_help()
|
||||
assert '-doit, --func-args foo' in help
|
||||
assert '-doit, --func-args foo' in help
|
||||
|
||||
def test_multiple_metavar_help(self, parser):
|
||||
"""
|
||||
Help text for options with a metavar tuple should display help
|
||||
in the form "--preferences=value1 value2 value3" (#2004).
|
||||
"""
|
||||
group = parser.getgroup("general")
|
||||
group.addoption('--preferences', metavar=('value1', 'value2', 'value3'), nargs=3)
|
||||
group._addoption("-h", "--help", action="store_true", dest="help")
|
||||
parser.parse(['-h'])
|
||||
help = parser.optparser.format_help()
|
||||
assert '--preferences=value1 value2 value3' in help
|
||||
|
||||
|
||||
def test_argcomplete(testdir, monkeypatch):
|
||||
|
||||
@@ -84,8 +84,10 @@ class TestPaste:
|
||||
function that connects to bpaste service.
|
||||
"""
|
||||
calls = []
|
||||
|
||||
def mocked(url, data):
|
||||
calls.append((url, data))
|
||||
|
||||
class DummyFile:
|
||||
def read(self):
|
||||
# part of html of a normal response
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import sys
|
||||
import platform
|
||||
|
||||
import _pytest._code
|
||||
import pytest
|
||||
@@ -18,8 +19,10 @@ class TestPDB:
|
||||
def pdblist(self, request):
|
||||
monkeypatch = request.getfixturevalue("monkeypatch")
|
||||
pdblist = []
|
||||
|
||||
def mypdb(*args):
|
||||
pdblist.append(args)
|
||||
|
||||
plugin = request.config.pluginmanager.getplugin('debugging')
|
||||
monkeypatch.setattr(plugin, 'post_mortem', mypdb)
|
||||
return pdblist
|
||||
@@ -76,6 +79,12 @@ class TestPDB:
|
||||
rest = child.read().decode("utf8")
|
||||
assert "1 failed" in rest
|
||||
assert "def test_1" not in rest
|
||||
self.flush(child)
|
||||
|
||||
@staticmethod
|
||||
def flush(child):
|
||||
if platform.system() == 'Darwin':
|
||||
return
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
|
||||
@@ -95,8 +104,7 @@ class TestPDB:
|
||||
child.sendeof()
|
||||
rest = child.read().decode("utf8")
|
||||
assert 'debug.me' in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_capture(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -111,8 +119,7 @@ class TestPDB:
|
||||
rest = child.read().decode("utf8")
|
||||
assert "1 failed" in rest
|
||||
assert "getrekt" not in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_exception(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -130,8 +137,7 @@ class TestPDB:
|
||||
child.expect(".*function")
|
||||
child.sendeof()
|
||||
child.expect("1 failed")
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_on_collection_issue181(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -143,8 +149,7 @@ class TestPDB:
|
||||
child.expect("(Pdb)")
|
||||
child.sendeof()
|
||||
child.expect("1 error")
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_on_internal_error(self, testdir):
|
||||
testdir.makeconftest("""
|
||||
@@ -156,8 +161,7 @@ class TestPDB:
|
||||
#child.expect(".*import pytest.*")
|
||||
child.expect("(Pdb)")
|
||||
child.sendeof()
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_capturing_simple(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -177,8 +181,7 @@ class TestPDB:
|
||||
assert "1 failed" in rest
|
||||
assert "def test_1" in rest
|
||||
assert "hello17" in rest # out is captured
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_set_trace_interception(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -193,8 +196,7 @@ class TestPDB:
|
||||
rest = child.read().decode("utf8")
|
||||
assert "1 failed" in rest
|
||||
assert "reading from stdin while output" not in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_and_capsys(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -209,8 +211,7 @@ class TestPDB:
|
||||
child.expect("hello1")
|
||||
child.sendeof()
|
||||
child.read()
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_set_trace_capturing_afterwards(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -229,8 +230,7 @@ class TestPDB:
|
||||
child.expect("hello")
|
||||
child.sendeof()
|
||||
child.read()
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_doctest(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -249,8 +249,7 @@ class TestPDB:
|
||||
child.sendeof()
|
||||
rest = child.read().decode("utf8")
|
||||
assert "1 failed" in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_interaction_capturing_twice(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -276,8 +275,7 @@ class TestPDB:
|
||||
assert "def test_1" in rest
|
||||
assert "hello17" in rest # out is captured
|
||||
assert "hello18" in rest # out is captured
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_used_outside_test(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -288,7 +286,7 @@ class TestPDB:
|
||||
child = testdir.spawn("%s %s" %(sys.executable, p1))
|
||||
child.expect("x = 5")
|
||||
child.sendeof()
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_used_in_generate_tests(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
@@ -302,7 +300,7 @@ class TestPDB:
|
||||
child = testdir.spawn_pytest(str(p1))
|
||||
child.expect("x = 5")
|
||||
child.sendeof()
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_collection_failure_is_shown(self, testdir):
|
||||
p1 = testdir.makepyfile("""xxx """)
|
||||
@@ -331,8 +329,7 @@ class TestPDB:
|
||||
child.expect("enter_pdb_hook")
|
||||
child.send('c\n')
|
||||
child.sendeof()
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
self.flush(child)
|
||||
|
||||
def test_pdb_custom_cls(self, testdir):
|
||||
called = []
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
# encoding: UTF-8
|
||||
import pytest
|
||||
import py
|
||||
import os
|
||||
|
||||
from _pytest.config import get_config, PytestPluginManager
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED, Session
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def pytestpm():
|
||||
@@ -82,6 +84,7 @@ class TestPytestPluginInteractions:
|
||||
def test_configure(self, testdir):
|
||||
config = testdir.parseconfig()
|
||||
l = []
|
||||
|
||||
class A:
|
||||
def pytest_configure(self, config):
|
||||
l.append(self)
|
||||
@@ -101,13 +104,16 @@ class TestPytestPluginInteractions:
|
||||
def test_hook_tracing(self):
|
||||
pytestpm = get_config().pluginmanager # fully initialized with plugins
|
||||
saveindent = []
|
||||
|
||||
class api1:
|
||||
def pytest_plugin_registered(self):
|
||||
saveindent.append(pytestpm.trace.root.indent)
|
||||
|
||||
class api2:
|
||||
def pytest_plugin_registered(self):
|
||||
saveindent.append(pytestpm.trace.root.indent)
|
||||
raise ValueError()
|
||||
|
||||
l = []
|
||||
pytestpm.trace.root.setwriter(l.append)
|
||||
undo = pytestpm.enable_tracing()
|
||||
@@ -128,6 +134,25 @@ class TestPytestPluginInteractions:
|
||||
finally:
|
||||
undo()
|
||||
|
||||
def test_hook_proxy(self, testdir):
|
||||
"""Test the gethookproxy function(#2016)"""
|
||||
config = testdir.parseconfig()
|
||||
session = Session(config)
|
||||
testdir.makepyfile(**{
|
||||
'tests/conftest.py': '',
|
||||
'tests/subdir/conftest.py': '',
|
||||
})
|
||||
|
||||
conftest1 = testdir.tmpdir.join('tests/conftest.py')
|
||||
conftest2 = testdir.tmpdir.join('tests/subdir/conftest.py')
|
||||
|
||||
config.pluginmanager._importconftest(conftest1)
|
||||
ihook_a = session.gethookproxy(testdir.tmpdir.join('tests'))
|
||||
assert ihook_a is not None
|
||||
config.pluginmanager._importconftest(conftest2)
|
||||
ihook_b = session.gethookproxy(testdir.tmpdir.join('tests'))
|
||||
assert ihook_a is not ihook_b
|
||||
|
||||
def test_warn_on_deprecated_multicall(self, pytestpm):
|
||||
warnings = []
|
||||
|
||||
@@ -179,15 +204,20 @@ def test_default_markers(testdir):
|
||||
])
|
||||
|
||||
|
||||
def test_importplugin_issue375(testdir, pytestpm):
|
||||
def test_importplugin_error_message(testdir, pytestpm):
|
||||
"""Don't hide import errors when importing plugins and provide
|
||||
an easy to debug message.
|
||||
|
||||
See #375 and #1998.
|
||||
"""
|
||||
testdir.syspathinsert(testdir.tmpdir)
|
||||
testdir.makepyfile(qwe="import aaaa")
|
||||
testdir.makepyfile(qwe="""
|
||||
# encoding: UTF-8
|
||||
raise ImportError(u'Not possible to import: ☺')
|
||||
""")
|
||||
with pytest.raises(ImportError) as excinfo:
|
||||
pytestpm.import_plugin("qwe")
|
||||
expected = '.*Error importing plugin "qwe": No module named \'?aaaa\'?'
|
||||
expected = '.*Error importing plugin "qwe": Not possible to import: .'
|
||||
assert py.std.re.match(expected, str(excinfo.value))
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ def test_make_hook_recorder(testdir):
|
||||
assert not recorder.getfailures()
|
||||
|
||||
pytest.xfail("internal reportrecorder tests need refactoring")
|
||||
|
||||
class rep:
|
||||
excinfo = None
|
||||
passed = False
|
||||
@@ -80,10 +81,13 @@ def make_holder():
|
||||
"x"
|
||||
|
||||
apimod = type(os)('api')
|
||||
|
||||
def pytest_xyz(arg):
|
||||
"x"
|
||||
|
||||
def pytest_xyz_noarg():
|
||||
"x"
|
||||
|
||||
apimod.pytest_xyz = pytest_xyz
|
||||
apimod.pytest_xyz_noarg = pytest_xyz_noarg
|
||||
return apiclass, apimod
|
||||
|
||||
@@ -38,9 +38,13 @@ class TestSetupState:
|
||||
|
||||
def test_teardown_multiple_one_fails(self, testdir):
|
||||
r = []
|
||||
|
||||
def fin1(): r.append('fin1')
|
||||
|
||||
def fin2(): raise Exception('oops')
|
||||
|
||||
def fin3(): r.append('fin3')
|
||||
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
ss = runner.SetupState()
|
||||
ss.addfinalizer(fin1, item)
|
||||
@@ -55,7 +59,9 @@ class TestSetupState:
|
||||
# Ensure the first exception is the one which is re-raised.
|
||||
# Ideally both would be reported however.
|
||||
def fin1(): raise Exception('oops1')
|
||||
|
||||
def fin2(): raise Exception('oops2')
|
||||
|
||||
item = testdir.getitem("def test_func(): pass")
|
||||
ss = runner.SetupState()
|
||||
ss.addfinalizer(fin1, item)
|
||||
@@ -527,8 +533,10 @@ def test_exception_printing_skip():
|
||||
|
||||
def test_importorskip(monkeypatch):
|
||||
importorskip = pytest.importorskip
|
||||
|
||||
def f():
|
||||
importorskip("asdlkj")
|
||||
|
||||
try:
|
||||
sys = importorskip("sys") # noqa
|
||||
assert sys == py.std.sys
|
||||
@@ -643,11 +651,13 @@ def test_makereport_getsource_dynamic_code(testdir, monkeypatch):
|
||||
"""Test that exception in dynamically generated code doesn't break getting the source line."""
|
||||
import inspect
|
||||
original_findsource = inspect.findsource
|
||||
|
||||
def findsource(obj, *args, **kwargs):
|
||||
# Can be triggered by dynamically created functions
|
||||
if obj.__name__ == 'foo':
|
||||
raise IndexError()
|
||||
return original_findsource(obj, *args, **kwargs)
|
||||
|
||||
monkeypatch.setattr(inspect, 'findsource', findsource)
|
||||
|
||||
testdir.makepyfile("""
|
||||
|
||||
@@ -967,5 +967,5 @@ def test_module_level_skip_error(testdir):
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines(
|
||||
"*Using @pytest.skip outside of a test * is not allowed*"
|
||||
"*Using pytest.skip outside of a test is not allowed*"
|
||||
)
|
||||
|
||||
@@ -370,6 +370,31 @@ class TestFixtureReporting:
|
||||
"*1 failed*1 error*",
|
||||
])
|
||||
|
||||
def test_setup_teardown_output_and_test_failure(self, testdir):
|
||||
""" Test for issue #442 """
|
||||
testdir.makepyfile("""
|
||||
def setup_function(function):
|
||||
print ("setup func")
|
||||
|
||||
def test_fail():
|
||||
assert 0, "failingfunc"
|
||||
|
||||
def teardown_function(function):
|
||||
print ("teardown func")
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
result.stdout.fnmatch_lines([
|
||||
"*test_fail*",
|
||||
"*def test_fail():",
|
||||
"*failingfunc*",
|
||||
"*Captured stdout setup*",
|
||||
"*setup func*",
|
||||
"*Captured stdout teardown*",
|
||||
"*teardown func*",
|
||||
|
||||
"*1 failed*",
|
||||
])
|
||||
|
||||
class TestTerminalFunctional:
|
||||
def test_deselected(self, testdir):
|
||||
testpath = testdir.makepyfile("""
|
||||
@@ -667,7 +692,7 @@ class TestGenericReporting:
|
||||
result = testdir.runpytest(*option.args)
|
||||
result.stdout.fnmatch_lines([
|
||||
"ImportError while importing*",
|
||||
"'No module named *xyz*",
|
||||
"*No module named *xyz*",
|
||||
"*1 error*",
|
||||
])
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||
import pytest
|
||||
import gc
|
||||
|
||||
def test_simple_unittest(testdir):
|
||||
testpath = testdir.makepyfile("""
|
||||
@@ -134,6 +135,28 @@ def test_teardown(testdir):
|
||||
assert passed == 2
|
||||
assert passed + skipped + failed == 2
|
||||
|
||||
def test_teardown_issue1649(testdir):
|
||||
"""
|
||||
Are TestCase objects cleaned up? Often unittest TestCase objects set
|
||||
attributes that are large and expensive during setUp.
|
||||
|
||||
The TestCase will not be cleaned up if the test fails, because it
|
||||
would then exist in the stackframe.
|
||||
"""
|
||||
testpath = testdir.makepyfile("""
|
||||
import unittest
|
||||
class TestCaseObjectsShouldBeCleanedUp(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.an_expensive_object = 1
|
||||
def test_demo(self):
|
||||
pass
|
||||
|
||||
""")
|
||||
testdir.inline_run("-s", testpath)
|
||||
gc.collect()
|
||||
for obj in gc.get_objects():
|
||||
assert type(obj).__name__ != 'TestCaseObjectsShouldBeCleanedUp'
|
||||
|
||||
@pytest.mark.skipif("sys.version_info < (2,7)")
|
||||
def test_unittest_skip_issue148(testdir):
|
||||
testpath = testdir.makepyfile("""
|
||||
|
||||
46
tox.ini
46
tox.ini
@@ -3,9 +3,18 @@ minversion=2.0
|
||||
distshare={homedir}/.tox/distshare
|
||||
# make sure to update enviroment list on appveyor.yml
|
||||
envlist=
|
||||
linting,py26,py27,py33,py34,py35,pypy,
|
||||
{py27,py35}-{pexpect,xdist,trial},
|
||||
py27-nobyte,doctesting,freeze,docs
|
||||
linting
|
||||
py26
|
||||
py27
|
||||
py33
|
||||
py34
|
||||
py35
|
||||
pypy
|
||||
{py27,py35}-{pexpect,xdist,trial}
|
||||
py27-nobyte
|
||||
doctesting
|
||||
freeze
|
||||
docs
|
||||
|
||||
[testenv]
|
||||
commands= pytest --lsof -rfsxX {posargs:testing}
|
||||
@@ -33,15 +42,19 @@ deps=pytest-xdist>=1.13
|
||||
commands=
|
||||
pytest -n3 -rfsxX --runpytest=subprocess {posargs:testing}
|
||||
|
||||
[testenv:genscript]
|
||||
commands= pytest --genscript=pytest1
|
||||
|
||||
[testenv:linting]
|
||||
basepython = python2.7
|
||||
deps = flake8
|
||||
deps =
|
||||
flake8
|
||||
# pygments required by rst-lint
|
||||
pygments
|
||||
restructuredtext_lint
|
||||
commands = flake8 pytest.py _pytest testing
|
||||
rst-lint CHANGELOG.rst HOWTORELEASE.rst
|
||||
check-manifest
|
||||
commands =
|
||||
{envpython} scripts/check-manifest.py
|
||||
flake8 pytest.py _pytest testing
|
||||
rst-lint CHANGELOG.rst HOWTORELEASE.rst README.rst
|
||||
|
||||
[testenv:py27-xdist]
|
||||
deps=pytest-xdist>=1.13
|
||||
@@ -90,10 +103,6 @@ deps={[testenv:py27-trial]deps}
|
||||
commands=
|
||||
pytest -ra {posargs:testing/test_unittest.py}
|
||||
|
||||
[testenv:doctest]
|
||||
commands=pytest --doctest-modules _pytest
|
||||
deps=
|
||||
|
||||
[testenv:docs]
|
||||
basepython=python
|
||||
changedir=doc/en
|
||||
@@ -106,9 +115,13 @@ commands=
|
||||
|
||||
[testenv:doctesting]
|
||||
basepython = python
|
||||
changedir=doc/en
|
||||
deps=PyYAML
|
||||
commands= pytest -rfsxX {posargs}
|
||||
usedevelop=True
|
||||
skipsdist=True
|
||||
deps=
|
||||
PyYAML
|
||||
commands=
|
||||
pytest -rfsxX doc/en
|
||||
pytest --doctest-modules {toxinidir}/_pytest
|
||||
|
||||
[testenv:regen]
|
||||
changedir=doc/en
|
||||
@@ -139,7 +152,7 @@ commands=
|
||||
[testenv:coveralls]
|
||||
passenv = TRAVIS TRAVIS_JOB_ID TRAVIS_BRANCH COVERALLS_REPO_TOKEN
|
||||
usedevelop=True
|
||||
basepython=python3.4
|
||||
basepython=python3.5
|
||||
changedir=.
|
||||
deps =
|
||||
{[testenv]deps}
|
||||
@@ -163,3 +176,4 @@ norecursedirs = .tox ja .hg cx_freeze_source
|
||||
|
||||
[flake8]
|
||||
ignore =E401,E225,E261,E128,E124,E301,E302,E121,E303,W391,E501,E231,E126,E701,E265,E241,E251,E226,E101,W191,E131,E203,E122,E123,E271,E712,E222,E127,E125,E221,W292,E111,E113,E293,E262,W293,E129,E702,E201,E272,E202,E704,E731,E402
|
||||
exclude = _pytest/vendored_packages/pluggy.py
|
||||
|
||||
Reference in New Issue
Block a user