Merge pull request #1643 from RonnyPfannschmidt/merge-master
Merge master
This commit is contained in:
commit
7d60fcc098
25
AUTHORS
25
AUTHORS
|
@ -5,6 +5,7 @@ Contributors include::
|
||||||
|
|
||||||
Abdeali JK
|
Abdeali JK
|
||||||
Abhijeet Kasurde
|
Abhijeet Kasurde
|
||||||
|
Alexei Kozlenok
|
||||||
Anatoly Bubenkoff
|
Anatoly Bubenkoff
|
||||||
Andreas Zeidler
|
Andreas Zeidler
|
||||||
Andy Freeland
|
Andy Freeland
|
||||||
|
@ -12,14 +13,17 @@ Anthon van der Neut
|
||||||
Armin Rigo
|
Armin Rigo
|
||||||
Aron Curzon
|
Aron Curzon
|
||||||
Aviv Palivoda
|
Aviv Palivoda
|
||||||
|
Ben Webb
|
||||||
Benjamin Peterson
|
Benjamin Peterson
|
||||||
Bob Ippolito
|
Bob Ippolito
|
||||||
Brian Dorsey
|
Brian Dorsey
|
||||||
Brian Okken
|
Brian Okken
|
||||||
Brianna Laugher
|
Brianna Laugher
|
||||||
Bruno Oliveira
|
Bruno Oliveira
|
||||||
|
Cal Leeming
|
||||||
Carl Friedrich Bolz
|
Carl Friedrich Bolz
|
||||||
Charles Cloud
|
Charles Cloud
|
||||||
|
Charnjit SiNGH (CCSJ)
|
||||||
Chris Lamb
|
Chris Lamb
|
||||||
Christian Theunert
|
Christian Theunert
|
||||||
Christian Tismer
|
Christian Tismer
|
||||||
|
@ -28,20 +32,24 @@ Daniel Grana
|
||||||
Daniel Hahler
|
Daniel Hahler
|
||||||
Daniel Nuri
|
Daniel Nuri
|
||||||
Dave Hunt
|
Dave Hunt
|
||||||
|
David Díaz-Barquero
|
||||||
David Mohr
|
David Mohr
|
||||||
David Vierra
|
David Vierra
|
||||||
Edison Gustavo Muenz
|
Edison Gustavo Muenz
|
||||||
Eduardo Schettino
|
Eduardo Schettino
|
||||||
Endre Galaczi
|
|
||||||
Elizaveta Shashkova
|
Elizaveta Shashkova
|
||||||
|
Endre Galaczi
|
||||||
|
Eric Hunsberger
|
||||||
Eric Hunsberger
|
Eric Hunsberger
|
||||||
Eric Siegerman
|
Eric Siegerman
|
||||||
Erik M. Bray
|
Erik M. Bray
|
||||||
|
Feng Ma
|
||||||
Florian Bruhin
|
Florian Bruhin
|
||||||
Floris Bruynooghe
|
Floris Bruynooghe
|
||||||
Gabriel Reis
|
Gabriel Reis
|
||||||
Georgy Dyuldin
|
Georgy Dyuldin
|
||||||
Graham Horler
|
Graham Horler
|
||||||
|
Greg Price
|
||||||
Grig Gheorghiu
|
Grig Gheorghiu
|
||||||
Guido Wesdorp
|
Guido Wesdorp
|
||||||
Harald Armin Massa
|
Harald Armin Massa
|
||||||
|
@ -65,6 +73,7 @@ Mark Abramowitz
|
||||||
Markus Unterwaditzer
|
Markus Unterwaditzer
|
||||||
Martijn Faassen
|
Martijn Faassen
|
||||||
Martin Prusse
|
Martin Prusse
|
||||||
|
Martin K. Scherer
|
||||||
Matt Bachmann
|
Matt Bachmann
|
||||||
Matt Williams
|
Matt Williams
|
||||||
Michael Aquilina
|
Michael Aquilina
|
||||||
|
@ -84,18 +93,16 @@ Raphael Pierzina
|
||||||
Roman Bolshakov
|
Roman Bolshakov
|
||||||
Ronny Pfannschmidt
|
Ronny Pfannschmidt
|
||||||
Ross Lawley
|
Ross Lawley
|
||||||
|
Russel Winder
|
||||||
Ryan Wooden
|
Ryan Wooden
|
||||||
Samuele Pedroni
|
Samuele Pedroni
|
||||||
Stephan Obermann
|
Stephan Obermann
|
||||||
Tareq Alayan
|
Tareq Alayan
|
||||||
|
Simon Gomizelj
|
||||||
|
Stefano Taschini
|
||||||
|
Stefan Farmbauer
|
||||||
|
Thomas Grainger
|
||||||
Tom Viner
|
Tom Viner
|
||||||
Trevor Bekolay
|
Trevor Bekolay
|
||||||
Wouter van Ackooy
|
Wouter van Ackooy
|
||||||
David Díaz-Barquero
|
Bernard Pratz
|
||||||
Eric Hunsberger
|
|
||||||
Simon Gomizelj
|
|
||||||
Russel Winder
|
|
||||||
Ben Webb
|
|
||||||
Alexei Kozlenok
|
|
||||||
Cal Leeming
|
|
||||||
Feng Ma
|
|
||||||
|
|
|
@ -82,15 +82,48 @@
|
||||||
was only available for test modules. Thanks `@flub`_, `@sober7`_ and
|
was only available for test modules. Thanks `@flub`_, `@sober7`_ and
|
||||||
`@nicoddemus`_ for the PR (`#1619`_).
|
`@nicoddemus`_ for the PR (`#1619`_).
|
||||||
|
|
||||||
*
|
* Text documents without any doctests no longer appear as "skipped".
|
||||||
|
Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_).
|
||||||
|
|
||||||
|
* Fix internal error issue when ``method`` argument is missing for
|
||||||
|
``teardown_method()``. Fixes (`#1605`_).
|
||||||
|
|
||||||
|
* Fix exception visualization in case the current working directory (CWD) gets
|
||||||
|
deleted during testing. Fixes (`#1235`). Thanks `@bukzor`_ for reporting. PR by
|
||||||
|
`@marscher`. Thanks `@nicoddemus`_ for his help.
|
||||||
|
|
||||||
|
* Ensure that a module within a namespace package can be found when it
|
||||||
|
is specified on the command line together with the ``--pyargs``
|
||||||
|
option. Thanks to `@taschini`_ for the PR (`#1597`_).
|
||||||
|
|
||||||
|
* Raise helpful failure message, when requesting parametrized fixture at runtime,
|
||||||
|
e.g. with ``request.getfuncargvalue``. BACKWARD INCOMPAT: Previously these params
|
||||||
|
were simply never defined. So a fixture decorated like ``@pytest.fixture(params=[0, 1, 2])``
|
||||||
|
only ran once. Now a failure is raised. Fixes (`#460`_). Thanks to
|
||||||
|
`@nikratio`_ for bug report, `@RedBeardCode`_ and `@tomviner`_ for PR.
|
||||||
|
|
||||||
|
* Create correct diff for strings ending with newlines. Fixes (`#1553`_).
|
||||||
|
Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and
|
||||||
|
`@tomviner`_ for PR.
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
|
||||||
|
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
|
||||||
|
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
|
||||||
|
.. _#460: https://github.com/pytest-dev/pytest/pull/460
|
||||||
|
.. _#1553: https://github.com/pytest-dev/pytest/issues/1553
|
||||||
|
|
||||||
|
.. _@graingert: https://github.com/graingert
|
||||||
|
.. _@taschini: https://github.com/taschini
|
||||||
|
.. _@nikratio: https://github.com/nikratio
|
||||||
|
.. _@RedBeardCode: https://github.com/RedBeardCode
|
||||||
|
.. _@Vogtinator: https://github.com/Vogtinator
|
||||||
|
|
||||||
* Fix `#1421`_: Exit tests if a collection error occurs and add
|
* Fix `#1421`_: Exit tests if a collection error occurs and add
|
||||||
``--continue-on-collection-errors`` option to restore previous behaviour.
|
``--continue-on-collection-errors`` option to restore previous behaviour.
|
||||||
Thanks `@olegpidsadnyi`_ and `@omarkohl`_ for the complete PR (`#1628`_).
|
Thanks `@olegpidsadnyi`_ and `@omarkohl`_ for the complete PR (`#1628`_).
|
||||||
|
|
||||||
*
|
|
||||||
|
|
||||||
*
|
*
|
||||||
|
|
||||||
|
@ -294,7 +327,7 @@
|
||||||
Thanks `@biern`_ for the PR.
|
Thanks `@biern`_ for the PR.
|
||||||
|
|
||||||
* Fix `traceback style docs`_ to describe all of the available options
|
* Fix `traceback style docs`_ to describe all of the available options
|
||||||
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
|
(auto/long/short/line/native/no), with ``auto`` being the default since v2.6.
|
||||||
Thanks `@hackebrot`_ for the PR.
|
Thanks `@hackebrot`_ for the PR.
|
||||||
|
|
||||||
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
|
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records
|
||||||
|
|
|
@ -48,7 +48,7 @@ to fix the bug yet.
|
||||||
Fix bugs
|
Fix bugs
|
||||||
--------
|
--------
|
||||||
|
|
||||||
Look through the GitHub issues for bugs. Here is sample filter you can use:
|
Look through the GitHub issues for bugs. Here is a filter you can use:
|
||||||
https://github.com/pytest-dev/pytest/labels/bug
|
https://github.com/pytest-dev/pytest/labels/bug
|
||||||
|
|
||||||
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
|
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
|
||||||
|
@ -60,8 +60,7 @@ Don't forget to check the issue trackers of your favourite plugins, too!
|
||||||
Implement features
|
Implement features
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
Look through the GitHub issues for enhancements. Here is sample filter you
|
Look through the GitHub issues for enhancements. Here is a filter you can use:
|
||||||
can use:
|
|
||||||
https://github.com/pytest-dev/pytest/labels/enhancement
|
https://github.com/pytest-dev/pytest/labels/enhancement
|
||||||
|
|
||||||
:ref:`Talk <contact>` to developers to find out how you can implement specific
|
:ref:`Talk <contact>` to developers to find out how you can implement specific
|
||||||
|
@ -70,16 +69,15 @@ features.
|
||||||
Write documentation
|
Write documentation
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
pytest could always use more documentation. What exactly is needed?
|
Pytest could always use more documentation. What exactly is needed?
|
||||||
|
|
||||||
* More complementary documentation. Have you perhaps found something unclear?
|
* More complementary documentation. Have you perhaps found something unclear?
|
||||||
* Documentation translations. We currently have only English.
|
* Documentation translations. We currently have only English.
|
||||||
* Docstrings. There can never be too many of them.
|
* Docstrings. There can never be too many of them.
|
||||||
* Blog posts, articles and such -- they're all very appreciated.
|
* Blog posts, articles and such -- they're all very appreciated.
|
||||||
|
|
||||||
You can also edit documentation files directly in the Github web interface
|
You can also edit documentation files directly in the GitHub web interface,
|
||||||
without needing to make a fork and local copy. This can be convenient for
|
without using a local copy. This can be convenient for small fixes.
|
||||||
small fixes.
|
|
||||||
|
|
||||||
|
|
||||||
.. _submitplugin:
|
.. _submitplugin:
|
||||||
|
@ -95,13 +93,14 @@ in repositories living under the ``pytest-dev`` organisations:
|
||||||
- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
|
- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
|
||||||
|
|
||||||
All pytest-dev Contributors team members have write access to all contained
|
All pytest-dev Contributors team members have write access to all contained
|
||||||
repositories. pytest core and plugins are generally developed
|
repositories. Pytest core and plugins are generally developed
|
||||||
using `pull requests`_ to respective repositories.
|
using `pull requests`_ to respective repositories.
|
||||||
|
|
||||||
The objectives of the ``pytest-dev`` organisation are:
|
The objectives of the ``pytest-dev`` organisation are:
|
||||||
|
|
||||||
* Having a central location for popular pytest plugins
|
* Having a central location for popular pytest plugins
|
||||||
* Sharing some of the maintenance responsibility (in case a maintainer no longer whishes to maintain a plugin)
|
* Sharing some of the maintenance responsibility (in case a maintainer no
|
||||||
|
longer wishes to maintain a plugin)
|
||||||
|
|
||||||
You can submit your plugin by subscribing to the `pytest-dev mail list
|
You can submit your plugin by subscribing to the `pytest-dev mail list
|
||||||
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
|
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
|
||||||
|
@ -127,27 +126,18 @@ transferred to the ``pytest-dev`` organisation.
|
||||||
Here's a rundown of how a repository transfer usually proceeds
|
Here's a rundown of how a repository transfer usually proceeds
|
||||||
(using a repository named ``joedoe/pytest-xyz`` as example):
|
(using a repository named ``joedoe/pytest-xyz`` as example):
|
||||||
|
|
||||||
* One of the ``pytest-dev`` administrators creates:
|
* ``joedoe`` transfers repository ownership to ``pytest-dev`` administrator ``calvin``.
|
||||||
|
* ``calvin`` creates ``pytest-xyz-admin`` and ``pytest-xyz-developers`` teams, inviting ``joedoe`` to both as **maintainer**.
|
||||||
|
* ``calvin`` transfers repository to ``pytest-dev`` and configures team access:
|
||||||
|
|
||||||
- ``pytest-xyz-admin`` team, with full administration rights to
|
- ``pytest-xyz-admin`` **admin** access;
|
||||||
``pytest-dev/pytest-xyz``.
|
- ``pytest-xyz-developers`` **write** access;
|
||||||
- ``pytest-xyz-developers`` team, with write access to
|
|
||||||
``pytest-dev/pytest-xyz``.
|
|
||||||
|
|
||||||
* ``joedoe`` is invited to the ``pytest-xyz-admin`` team;
|
|
||||||
|
|
||||||
* After accepting the invitation, ``joedoe`` transfers the repository from its
|
|
||||||
original location to ``pytest-dev/pytest-xyz`` (A nice feature is that GitHub handles URL redirection from
|
|
||||||
the old to the new location automatically).
|
|
||||||
|
|
||||||
* ``joedoe`` is free to add any other collaborators to the
|
|
||||||
``pytest-xyz-admin`` or ``pytest-xyz-developers`` team as desired.
|
|
||||||
|
|
||||||
The ``pytest-dev/Contributors`` team has write access to all projects, and
|
The ``pytest-dev/Contributors`` team has write access to all projects, and
|
||||||
every project administrator is in it. We recommend that each plugin has at least three
|
every project administrator is in it. We recommend that each plugin has at least three
|
||||||
people who have the right to release to PyPI.
|
people who have the right to release to PyPI.
|
||||||
|
|
||||||
Repository owners can be assured that no ``pytest-dev`` administrator will ever make
|
Repository owners can rest assured that no ``pytest-dev`` administrator will ever make
|
||||||
releases of your repository or take ownership in any way, except in rare cases
|
releases of your repository or take ownership in any way, except in rare cases
|
||||||
where someone becomes unresponsive after months of contact attempts.
|
where someone becomes unresponsive after months of contact attempts.
|
||||||
As stated, the objective is to share maintenance and avoid "plugin-abandon".
|
As stated, the objective is to share maintenance and avoid "plugin-abandon".
|
||||||
|
@ -159,15 +149,11 @@ As stated, the objective is to share maintenance and avoid "plugin-abandon".
|
||||||
Preparing Pull Requests on GitHub
|
Preparing Pull Requests on GitHub
|
||||||
---------------------------------
|
---------------------------------
|
||||||
|
|
||||||
There's an excellent tutorial on how Pull Requests work in the
|
|
||||||
`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_
|
|
||||||
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
What is a "pull request"? It informs project's core developers about the
|
What is a "pull request"? It informs project's core developers about the
|
||||||
changes you want to review and merge. Pull requests are stored on
|
changes you want to review and merge. Pull requests are stored on
|
||||||
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
|
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
|
||||||
Once you send pull request, we can discuss it's potential modifications and
|
Once you send a pull request, we can discuss its potential modifications and
|
||||||
even add more commits to it later on.
|
even add more commits to it later on.
|
||||||
|
|
||||||
There's an excellent tutorial on how Pull Requests work in the
|
There's an excellent tutorial on how Pull Requests work in the
|
||||||
|
@ -216,19 +202,19 @@ but here is a simple overview:
|
||||||
This command will run tests via the "tox" tool against Python 2.7 and 3.5
|
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
|
and also perform "lint" coding-style checks. ``runtox.py`` is
|
||||||
a thin wrapper around ``tox`` which installs from a development package
|
a thin wrapper around ``tox`` which installs from a development package
|
||||||
index where newer (not yet released to pypi) versions of dependencies
|
index where newer (not yet released to PyPI) versions of dependencies
|
||||||
(especially ``py``) might be present.
|
(especially ``py``) might be present.
|
||||||
|
|
||||||
#. You can now edit your local working copy.
|
#. You can now edit your local working copy.
|
||||||
|
|
||||||
You can now make the changes you want and run the tests again as necessary.
|
You can now make the changes you want and run the tests again as necessary.
|
||||||
|
|
||||||
To run tests on py27 and pass options to pytest (e.g. enter pdb on failure)
|
To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on
|
||||||
to pytest you can do::
|
failure) to pytest you can do::
|
||||||
|
|
||||||
$ python3 runtox.py -e py27 -- --pdb
|
$ python3 runtox.py -e py27 -- --pdb
|
||||||
|
|
||||||
or to only run tests in a particular test module on py35::
|
Or to only run tests in a particular test module on Python 3.5::
|
||||||
|
|
||||||
$ python3 runtox.py -e py35 -- testing/test_config.py
|
$ python3 runtox.py -e py35 -- testing/test_config.py
|
||||||
|
|
||||||
|
@ -237,9 +223,9 @@ but here is a simple overview:
|
||||||
$ git commit -a -m "<commit message>"
|
$ git commit -a -m "<commit message>"
|
||||||
$ git push -u
|
$ git push -u
|
||||||
|
|
||||||
Make sure you add a CHANGELOG message, and add yourself to AUTHORS. If you
|
Make sure you add a message to ``CHANGELOG.rst`` and add yourself to
|
||||||
are unsure about either of these steps, submit your pull request and we'll
|
``AUTHORS``. If you are unsure about either of these steps, submit your
|
||||||
help you fix it up.
|
pull request and we'll help you fix it up.
|
||||||
|
|
||||||
#. Finally, submit a pull request through the GitHub website using this data::
|
#. Finally, submit a pull request through the GitHub website using this data::
|
||||||
|
|
||||||
|
@ -248,6 +234,6 @@ but here is a simple overview:
|
||||||
|
|
||||||
base-fork: pytest-dev/pytest
|
base-fork: pytest-dev/pytest
|
||||||
base: master # if it's a bugfix
|
base: master # if it's a bugfix
|
||||||
base: feature # if it's a feature
|
base: features # if it's a feature
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
__version__ = '2.10.0.dev1'
|
__version__ = '2.10.0.dev1'
|
||||||
|
|
|
@ -3,7 +3,6 @@ from inspect import CO_VARARGS, CO_VARKEYWORDS
|
||||||
import re
|
import re
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
builtin_repr = repr
|
builtin_repr = repr
|
||||||
|
|
||||||
reprlib = py.builtin._tryimport('repr', 'reprlib')
|
reprlib = py.builtin._tryimport('repr', 'reprlib')
|
||||||
|
@ -36,12 +35,16 @@ class Code(object):
|
||||||
def path(self):
|
def path(self):
|
||||||
""" return a path object pointing to source code (note that it
|
""" return a path object pointing to source code (note that it
|
||||||
might not point to an actually existing file). """
|
might not point to an actually existing file). """
|
||||||
|
try:
|
||||||
p = py.path.local(self.raw.co_filename)
|
p = py.path.local(self.raw.co_filename)
|
||||||
# maybe don't try this checking
|
# maybe don't try this checking
|
||||||
if not p.check():
|
if not p.check():
|
||||||
|
raise OSError("py.path check failed.")
|
||||||
|
except OSError:
|
||||||
# XXX maybe try harder like the weird logic
|
# XXX maybe try harder like the weird logic
|
||||||
# in the standard lib [linecache.updatecache] does?
|
# in the standard lib [linecache.updatecache] does?
|
||||||
p = self.raw.co_filename
|
p = self.raw.co_filename
|
||||||
|
|
||||||
return p
|
return p
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|
|
@ -225,9 +225,10 @@ def _diff_text(left, right, verbose=False):
|
||||||
'characters in diff, use -v to show') % i]
|
'characters in diff, use -v to show') % i]
|
||||||
left = left[:-i]
|
left = left[:-i]
|
||||||
right = right[:-i]
|
right = right[:-i]
|
||||||
|
keepends = True
|
||||||
explanation += [line.strip('\n')
|
explanation += [line.strip('\n')
|
||||||
for line in ndiff(left.splitlines(),
|
for line in ndiff(left.splitlines(keepends),
|
||||||
right.splitlines())]
|
right.splitlines(keepends))]
|
||||||
return explanation
|
return explanation
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -146,23 +146,19 @@ def get_optionflags(parent):
|
||||||
return flag_acc
|
return flag_acc
|
||||||
|
|
||||||
|
|
||||||
class DoctestTextfile(DoctestItem, pytest.Module):
|
class DoctestTextfile(pytest.Module):
|
||||||
|
obj = None
|
||||||
|
|
||||||
def runtest(self):
|
def collect(self):
|
||||||
import doctest
|
import doctest
|
||||||
fixture_request = _setup_fixtures(self)
|
|
||||||
|
|
||||||
# inspired by doctest.testfile; ideally we would use it directly,
|
# inspired by doctest.testfile; ideally we would use it directly,
|
||||||
# but it doesn't support passing a custom checker
|
# but it doesn't support passing a custom checker
|
||||||
text = self.fspath.read()
|
text = self.fspath.read()
|
||||||
filename = str(self.fspath)
|
filename = str(self.fspath)
|
||||||
name = self.fspath.basename
|
name = self.fspath.basename
|
||||||
globs = dict(getfixture=fixture_request.getfuncargvalue)
|
globs = {'__name__': '__main__'}
|
||||||
if '__name__' not in globs:
|
|
||||||
globs['__name__'] = '__main__'
|
|
||||||
|
|
||||||
for name, value in fixture_request.getfuncargvalue('doctest_namespace').items():
|
|
||||||
globs[name] = value
|
|
||||||
|
|
||||||
optionflags = get_optionflags(self)
|
optionflags = get_optionflags(self)
|
||||||
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
|
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
|
||||||
|
@ -170,8 +166,8 @@ class DoctestTextfile(DoctestItem, pytest.Module):
|
||||||
|
|
||||||
parser = doctest.DocTestParser()
|
parser = doctest.DocTestParser()
|
||||||
test = parser.get_doctest(text, globs, name, filename, 0)
|
test = parser.get_doctest(text, globs, name, filename, 0)
|
||||||
_check_all_skipped(test)
|
if test.examples:
|
||||||
runner.run(test)
|
yield DoctestItem(test.name, self, runner, test)
|
||||||
|
|
||||||
|
|
||||||
def _check_all_skipped(test):
|
def _check_all_skipped(test):
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
""" core implementation of testing process: init, session, runtest loop. """
|
""" core implementation of testing process: init, session, runtest loop. """
|
||||||
import imp
|
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import _pytest
|
import _pytest
|
||||||
|
@ -25,8 +23,6 @@ EXIT_INTERNALERROR = 3
|
||||||
EXIT_USAGEERROR = 4
|
EXIT_USAGEERROR = 4
|
||||||
EXIT_NOTESTSCOLLECTED = 5
|
EXIT_NOTESTSCOLLECTED = 5
|
||||||
|
|
||||||
name_re = re.compile("^[a-zA-Z_]\w*$")
|
|
||||||
|
|
||||||
def pytest_addoption(parser):
|
def pytest_addoption(parser):
|
||||||
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
|
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
|
||||||
type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'])
|
type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'])
|
||||||
|
@ -144,17 +140,8 @@ def pytest_runtestloop(session):
|
||||||
if session.config.option.collectonly:
|
if session.config.option.collectonly:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def getnextitem(i):
|
|
||||||
# this is a function to avoid python2
|
|
||||||
# keeping sys.exc_info set when calling into a test
|
|
||||||
# python2 keeps sys.exc_info till the frame is left
|
|
||||||
try:
|
|
||||||
return session.items[i+1]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
for i, item in enumerate(session.items):
|
for i, item in enumerate(session.items):
|
||||||
nextitem = getnextitem(i)
|
nextitem = session.items[i+1] if i+1 < len(session.items) else None
|
||||||
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
|
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
|
||||||
if session.shouldstop:
|
if session.shouldstop:
|
||||||
raise session.Interrupted(session.shouldstop)
|
raise session.Interrupted(session.shouldstop)
|
||||||
|
@ -400,7 +387,10 @@ class Node(object):
|
||||||
if self.config.option.fulltrace:
|
if self.config.option.fulltrace:
|
||||||
style="long"
|
style="long"
|
||||||
else:
|
else:
|
||||||
|
tb = _pytest._code.Traceback([excinfo.traceback[-1]])
|
||||||
self._prunetraceback(excinfo)
|
self._prunetraceback(excinfo)
|
||||||
|
if len(excinfo.traceback) == 0:
|
||||||
|
excinfo.traceback = tb
|
||||||
tbfilter = False # prunetraceback already does it
|
tbfilter = False # prunetraceback already does it
|
||||||
if style == "auto":
|
if style == "auto":
|
||||||
style = "long"
|
style = "long"
|
||||||
|
@ -411,7 +401,13 @@ class Node(object):
|
||||||
else:
|
else:
|
||||||
style = "long"
|
style = "long"
|
||||||
|
|
||||||
return excinfo.getrepr(funcargs=True,
|
try:
|
||||||
|
os.getcwd()
|
||||||
|
abspath = False
|
||||||
|
except OSError:
|
||||||
|
abspath = True
|
||||||
|
|
||||||
|
return excinfo.getrepr(funcargs=True, abspath=abspath,
|
||||||
showlocals=self.config.option.showlocals,
|
showlocals=self.config.option.showlocals,
|
||||||
style=style, tbfilter=tbfilter)
|
style=style, tbfilter=tbfilter)
|
||||||
|
|
||||||
|
@ -657,36 +653,32 @@ class Session(FSCollector):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def _tryconvertpyarg(self, x):
|
def _tryconvertpyarg(self, x):
|
||||||
mod = None
|
"""Convert a dotted module name to path.
|
||||||
path = [os.path.abspath('.')] + sys.path
|
|
||||||
for name in x.split('.'):
|
"""
|
||||||
# ignore anything that's not a proper name here
|
import pkgutil
|
||||||
# else something like --pyargs will mess up '.'
|
|
||||||
# since imp.find_module will actually sometimes work for it
|
|
||||||
# but it's supposed to be considered a filesystem path
|
|
||||||
# not a package
|
|
||||||
if name_re.match(name) is None:
|
|
||||||
return x
|
|
||||||
try:
|
try:
|
||||||
fd, mod, type_ = imp.find_module(name, path)
|
loader = pkgutil.find_loader(x)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
return x
|
return x
|
||||||
else:
|
if loader is None:
|
||||||
if fd is not None:
|
return x
|
||||||
fd.close()
|
# This method is sometimes invoked when AssertionRewritingHook, which
|
||||||
|
# does not define a get_filename method, is already in place:
|
||||||
if type_[2] != imp.PKG_DIRECTORY:
|
try:
|
||||||
path = [os.path.dirname(mod)]
|
path = loader.get_filename()
|
||||||
else:
|
except AttributeError:
|
||||||
path = [mod]
|
# Retrieve path from AssertionRewritingHook:
|
||||||
return mod
|
path = loader.modules[x][0].co_filename
|
||||||
|
if loader.is_package(x):
|
||||||
|
path = os.path.dirname(path)
|
||||||
|
return path
|
||||||
|
|
||||||
def _parsearg(self, arg):
|
def _parsearg(self, arg):
|
||||||
""" return (fspath, names) tuple after checking the file exists. """
|
""" return (fspath, names) tuple after checking the file exists. """
|
||||||
arg = str(arg)
|
|
||||||
if self.config.option.pyargs:
|
|
||||||
arg = self._tryconvertpyarg(arg)
|
|
||||||
parts = str(arg).split("::")
|
parts = str(arg).split("::")
|
||||||
|
if self.config.option.pyargs:
|
||||||
|
parts[0] = self._tryconvertpyarg(parts[0])
|
||||||
relpath = parts[0].replace("/", os.sep)
|
relpath = parts[0].replace("/", os.sep)
|
||||||
path = self.config.invocation_dir.join(relpath, abs=True)
|
path = self.config.invocation_dir.join(relpath, abs=True)
|
||||||
if not path.check():
|
if not path.check():
|
||||||
|
|
|
@ -123,15 +123,18 @@ def getexecutable(name, cache={}):
|
||||||
except KeyError:
|
except KeyError:
|
||||||
executable = py.path.local.sysfind(name)
|
executable = py.path.local.sysfind(name)
|
||||||
if executable:
|
if executable:
|
||||||
if name == "jython":
|
|
||||||
import subprocess
|
import subprocess
|
||||||
popen = subprocess.Popen([str(executable), "--version"],
|
popen = subprocess.Popen([str(executable), "--version"],
|
||||||
universal_newlines=True, stderr=subprocess.PIPE)
|
universal_newlines=True, stderr=subprocess.PIPE)
|
||||||
out, err = popen.communicate()
|
out, err = popen.communicate()
|
||||||
|
if name == "jython":
|
||||||
if not err or "2.5" not in err:
|
if not err or "2.5" not in err:
|
||||||
executable = None
|
executable = None
|
||||||
if "2.5.2" in err:
|
if "2.5.2" in err:
|
||||||
executable = None # http://bugs.jython.org/issue1790
|
executable = None # http://bugs.jython.org/issue1790
|
||||||
|
elif popen.returncode != 0:
|
||||||
|
# Handle pyenv's 127.
|
||||||
|
executable = None
|
||||||
cache[name] = executable
|
cache[name] = executable
|
||||||
return executable
|
return executable
|
||||||
|
|
||||||
|
|
|
@ -2031,6 +2031,25 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
except (AttributeError, ValueError):
|
except (AttributeError, ValueError):
|
||||||
param = NOTSET
|
param = NOTSET
|
||||||
param_index = 0
|
param_index = 0
|
||||||
|
if fixturedef.params is not None:
|
||||||
|
frame = inspect.stack()[3]
|
||||||
|
frameinfo = inspect.getframeinfo(frame[0])
|
||||||
|
source_path = frameinfo.filename
|
||||||
|
source_lineno = frameinfo.lineno
|
||||||
|
source_path = py.path.local(source_path)
|
||||||
|
if source_path.relto(funcitem.config.rootdir):
|
||||||
|
source_path = source_path.relto(funcitem.config.rootdir)
|
||||||
|
msg = (
|
||||||
|
"The requested fixture has no parameter defined for the "
|
||||||
|
"current test.\n\nRequested fixture '{0}' defined in:\n{1}"
|
||||||
|
"\n\nRequested here:\n{2}:{3}".format(
|
||||||
|
fixturedef.argname,
|
||||||
|
getlocation(fixturedef.func, funcitem.config.rootdir),
|
||||||
|
source_path,
|
||||||
|
source_lineno,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
pytest.fail(msg)
|
||||||
else:
|
else:
|
||||||
# indices might not be set if old-style metafunc.addcall() was used
|
# indices might not be set if old-style metafunc.addcall() was used
|
||||||
param_index = funcitem.callspec.indices.get(argname, 0)
|
param_index = funcitem.callspec.indices.get(argname, 0)
|
||||||
|
@ -2369,7 +2388,7 @@ class FixtureManager:
|
||||||
else:
|
else:
|
||||||
if marker.name:
|
if marker.name:
|
||||||
name = marker.name
|
name = marker.name
|
||||||
assert not name.startswith(self._argprefix)
|
assert not name.startswith(self._argprefix), name
|
||||||
fixturedef = FixtureDef(self, nodeid, name, obj,
|
fixturedef = FixtureDef(self, nodeid, name, obj,
|
||||||
marker.scope, marker.params,
|
marker.scope, marker.params,
|
||||||
unittest=unittest, ids=marker.ids)
|
unittest=unittest, ids=marker.ids)
|
||||||
|
|
|
@ -7,7 +7,7 @@ Release announcements
|
||||||
|
|
||||||
|
|
||||||
sprint2016
|
sprint2016
|
||||||
release-2.9.1
|
release-2.9.2
|
||||||
release-2.9.1
|
release-2.9.1
|
||||||
release-2.9.0
|
release-2.9.0
|
||||||
release-2.8.7
|
release-2.8.7
|
||||||
|
|
|
@ -222,9 +222,9 @@ Inspecting Cache content
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
You can always peek at the content of the cache using the
|
You can always peek at the content of the cache using the
|
||||||
``--cache-clear`` command line option::
|
``--cache-show`` command line option::
|
||||||
|
|
||||||
$ pytest --cache-clear
|
$ py.test --cache-show
|
||||||
======= test session starts ========
|
======= test session starts ========
|
||||||
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
|
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
|
||||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||||
|
|
|
@ -295,13 +295,13 @@ which will add the string to the test header accordingly::
|
||||||
|
|
||||||
You can also return a list of strings which will be considered as several
|
You can also return a list of strings which will be considered as several
|
||||||
lines of information. You can of course also make the amount of reporting
|
lines of information. You can of course also make the amount of reporting
|
||||||
information on e.g. the value of ``config.option.verbose`` so that
|
information on e.g. the value of ``config.getoption('verbose')`` so that
|
||||||
you present more information appropriately::
|
you present more information appropriately::
|
||||||
|
|
||||||
# content of conftest.py
|
# content of conftest.py
|
||||||
|
|
||||||
def pytest_report_header(config):
|
def pytest_report_header(config):
|
||||||
if config.option.verbose > 0:
|
if config.getoption('verbose') > 0:
|
||||||
return ["info1: did you know that ...", "did you?"]
|
return ["info1: did you know that ...", "did you?"]
|
||||||
|
|
||||||
which will add info only when run with "--v"::
|
which will add info only when run with "--v"::
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
|
@ -513,12 +515,11 @@ class TestInvocationVariants:
|
||||||
path = testdir.mkpydir("tpkg")
|
path = testdir.mkpydir("tpkg")
|
||||||
path.join("test_hello.py").write('raise ImportError')
|
path.join("test_hello.py").write('raise ImportError')
|
||||||
|
|
||||||
result = testdir.runpytest("--pyargs", "tpkg.test_hello")
|
result = testdir.runpytest_subprocess("--pyargs", "tpkg.test_hello")
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
# FIXME: It would be more natural to match NOT
|
|
||||||
# "ERROR*file*or*package*not*found*".
|
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*collected 0 items*"
|
"collected*0*items*/*1*errors"
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_cmdline_python_package(self, testdir, monkeypatch):
|
def test_cmdline_python_package(self, testdir, monkeypatch):
|
||||||
|
@ -540,7 +541,7 @@ class TestInvocationVariants:
|
||||||
def join_pythonpath(what):
|
def join_pythonpath(what):
|
||||||
cur = py.std.os.environ.get('PYTHONPATH')
|
cur = py.std.os.environ.get('PYTHONPATH')
|
||||||
if cur:
|
if cur:
|
||||||
return str(what) + ':' + cur
|
return str(what) + os.pathsep + cur
|
||||||
return what
|
return what
|
||||||
empty_package = testdir.mkpydir("empty_package")
|
empty_package = testdir.mkpydir("empty_package")
|
||||||
monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
|
monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
|
||||||
|
@ -551,11 +552,72 @@ class TestInvocationVariants:
|
||||||
])
|
])
|
||||||
|
|
||||||
monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
|
monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
|
||||||
path.join('test_hello.py').remove()
|
result = testdir.runpytest("--pyargs", "tpkg.test_missing")
|
||||||
result = testdir.runpytest("--pyargs", "tpkg.test_hello")
|
|
||||||
assert result.ret != 0
|
assert result.ret != 0
|
||||||
result.stderr.fnmatch_lines([
|
result.stderr.fnmatch_lines([
|
||||||
"*not*found*test_hello*",
|
"*not*found*test_missing*",
|
||||||
|
])
|
||||||
|
|
||||||
|
def test_cmdline_python_namespace_package(self, testdir, monkeypatch):
|
||||||
|
"""
|
||||||
|
test --pyargs option with namespace packages (#1567)
|
||||||
|
"""
|
||||||
|
monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)
|
||||||
|
|
||||||
|
search_path = []
|
||||||
|
for dirname in "hello", "world":
|
||||||
|
d = testdir.mkdir(dirname)
|
||||||
|
search_path.append(d)
|
||||||
|
ns = d.mkdir("ns_pkg")
|
||||||
|
ns.join("__init__.py").write(
|
||||||
|
"__import__('pkg_resources').declare_namespace(__name__)")
|
||||||
|
lib = ns.mkdir(dirname)
|
||||||
|
lib.ensure("__init__.py")
|
||||||
|
lib.join("test_{0}.py".format(dirname)). \
|
||||||
|
write("def test_{0}(): pass\n"
|
||||||
|
"def test_other():pass".format(dirname))
|
||||||
|
|
||||||
|
# The structure of the test directory is now:
|
||||||
|
# .
|
||||||
|
# ├── hello
|
||||||
|
# │ └── ns_pkg
|
||||||
|
# │ ├── __init__.py
|
||||||
|
# │ └── hello
|
||||||
|
# │ ├── __init__.py
|
||||||
|
# │ └── test_hello.py
|
||||||
|
# └── world
|
||||||
|
# └── ns_pkg
|
||||||
|
# ├── __init__.py
|
||||||
|
# └── world
|
||||||
|
# ├── __init__.py
|
||||||
|
# └── test_world.py
|
||||||
|
|
||||||
|
def join_pythonpath(*dirs):
|
||||||
|
cur = py.std.os.environ.get('PYTHONPATH')
|
||||||
|
if cur:
|
||||||
|
dirs += (cur,)
|
||||||
|
return os.pathsep.join(str(p) for p in dirs)
|
||||||
|
monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
|
||||||
|
for p in search_path:
|
||||||
|
monkeypatch.syspath_prepend(p)
|
||||||
|
|
||||||
|
# mixed module and filenames:
|
||||||
|
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "world/ns_pkg")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*test_hello.py::test_hello*PASSED",
|
||||||
|
"*test_hello.py::test_other*PASSED",
|
||||||
|
"*test_world.py::test_world*PASSED",
|
||||||
|
"*test_world.py::test_other*PASSED",
|
||||||
|
"*4 passed*"
|
||||||
|
])
|
||||||
|
|
||||||
|
# specify tests within a module
|
||||||
|
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.world.test_world::test_other")
|
||||||
|
assert result.ret == 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*test_world.py::test_other*PASSED",
|
||||||
|
"*1 passed*"
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_cmdline_python_package_not_exists(self, testdir):
|
def test_cmdline_python_package_not_exists(self, testdir):
|
||||||
|
@ -700,4 +762,3 @@ class TestDurationWithFixture:
|
||||||
* setup *test_1*
|
* setup *test_1*
|
||||||
* call *test_1*
|
* call *test_1*
|
||||||
""")
|
""")
|
||||||
|
|
||||||
|
|
|
@ -1066,3 +1066,15 @@ def test_repr_traceback_with_unicode(style, encoding):
|
||||||
formatter = FormattedExcinfo(style=style)
|
formatter = FormattedExcinfo(style=style)
|
||||||
repr_traceback = formatter.repr_traceback(e_info)
|
repr_traceback = formatter.repr_traceback(e_info)
|
||||||
assert repr_traceback is not None
|
assert repr_traceback is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_cwd_deleted(testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
def test(tmpdir):
|
||||||
|
tmpdir.chdir()
|
||||||
|
tmpdir.remove()
|
||||||
|
assert False
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(['* 1 failed in *'])
|
||||||
|
assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str()
|
|
@ -490,6 +490,20 @@ class TestRequestBasic:
|
||||||
print(ss.stack)
|
print(ss.stack)
|
||||||
assert teardownlist == [1]
|
assert teardownlist == [1]
|
||||||
|
|
||||||
|
def test_mark_as_fixture_with_prefix_and_decorator_fails(self, testdir):
|
||||||
|
testdir.makeconftest("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def pytest_funcarg__marked_with_prefix_and_decorator():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest_subprocess()
|
||||||
|
assert result.ret != 0
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*AssertionError:*pytest_funcarg__marked_with_prefix_and_decorator*"
|
||||||
|
])
|
||||||
|
|
||||||
def test_request_addfinalizer_failing_setup(self, testdir):
|
def test_request_addfinalizer_failing_setup(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
import pytest
|
import pytest
|
||||||
|
@ -2704,3 +2718,108 @@ class TestContextManagerFixtureFuncs:
|
||||||
""".format(flavor=flavor))
|
""".format(flavor=flavor))
|
||||||
result = testdir.runpytest("-s")
|
result = testdir.runpytest("-s")
|
||||||
result.stdout.fnmatch_lines("*mew*")
|
result.stdout.fnmatch_lines("*mew*")
|
||||||
|
class TestParameterizedSubRequest:
|
||||||
|
def test_call_from_fixture(self, testdir):
|
||||||
|
testfile = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(params=[0, 1, 2])
|
||||||
|
def fix_with_param(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def get_named_fixture(request):
|
||||||
|
return request.getfuncargvalue('fix_with_param')
|
||||||
|
|
||||||
|
def test_foo(request, get_named_fixture):
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
E*Failed: The requested fixture has no parameter defined for the current test.
|
||||||
|
E*
|
||||||
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
|
E*{0}:4
|
||||||
|
E*Requested here:
|
||||||
|
E*{1}:9
|
||||||
|
*1 error*
|
||||||
|
""".format(testfile.basename, testfile.basename))
|
||||||
|
|
||||||
|
def test_call_from_test(self, testdir):
|
||||||
|
testfile = testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(params=[0, 1, 2])
|
||||||
|
def fix_with_param(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
def test_foo(request):
|
||||||
|
request.getfuncargvalue('fix_with_param')
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
E*Failed: The requested fixture has no parameter defined for the current test.
|
||||||
|
E*
|
||||||
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
|
E*{0}:4
|
||||||
|
E*Requested here:
|
||||||
|
E*{1}:8
|
||||||
|
*1 failed*
|
||||||
|
""".format(testfile.basename, testfile.basename))
|
||||||
|
|
||||||
|
def test_external_fixture(self, testdir):
|
||||||
|
conffile = testdir.makeconftest("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(params=[0, 1, 2])
|
||||||
|
def fix_with_param(request):
|
||||||
|
return request.param
|
||||||
|
""")
|
||||||
|
|
||||||
|
testfile = testdir.makepyfile("""
|
||||||
|
def test_foo(request):
|
||||||
|
request.getfuncargvalue('fix_with_param')
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
E*Failed: The requested fixture has no parameter defined for the current test.
|
||||||
|
E*
|
||||||
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
|
E*{0}:4
|
||||||
|
E*Requested here:
|
||||||
|
E*{1}:2
|
||||||
|
*1 failed*
|
||||||
|
""".format(conffile.basename, testfile.basename))
|
||||||
|
|
||||||
|
def test_non_relative_path(self, testdir):
|
||||||
|
tests_dir = testdir.mkdir('tests')
|
||||||
|
fixdir = testdir.mkdir('fixtures')
|
||||||
|
fixfile = fixdir.join("fix.py")
|
||||||
|
fixfile.write(_pytest._code.Source("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
@pytest.fixture(params=[0, 1, 2])
|
||||||
|
def fix_with_param(request):
|
||||||
|
return request.param
|
||||||
|
"""))
|
||||||
|
|
||||||
|
testfile = tests_dir.join("test_foos.py")
|
||||||
|
testfile.write(_pytest._code.Source("""
|
||||||
|
from fix import fix_with_param
|
||||||
|
|
||||||
|
def test_foo(request):
|
||||||
|
request.getfuncargvalue('fix_with_param')
|
||||||
|
"""))
|
||||||
|
|
||||||
|
tests_dir.chdir()
|
||||||
|
testdir.syspathinsert(fixdir)
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines("""
|
||||||
|
E*Failed: The requested fixture has no parameter defined for the current test.
|
||||||
|
E*
|
||||||
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
|
E*{0}:5
|
||||||
|
E*Requested here:
|
||||||
|
E*{1}:5
|
||||||
|
*1 failed*
|
||||||
|
""".format(fixfile.strpath, testfile.basename))
|
||||||
|
|
|
@ -428,7 +428,7 @@ def test_assert_compare_truncate_longmessage(monkeypatch, testdir):
|
||||||
"*- 3",
|
"*- 3",
|
||||||
"*- 5",
|
"*- 5",
|
||||||
"*- 7",
|
"*- 7",
|
||||||
"*truncated (191 more lines)*use*-vv*",
|
"*truncated (193 more lines)*use*-vv*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
|
||||||
|
@ -626,3 +626,17 @@ def test_set_with_unsortable_elements():
|
||||||
+ repr(3)
|
+ repr(3)
|
||||||
""").strip()
|
""").strip()
|
||||||
assert '\n'.join(expl) == dedent
|
assert '\n'.join(expl) == dedent
|
||||||
|
|
||||||
|
def test_diff_newline_at_end(monkeypatch, testdir):
|
||||||
|
testdir.makepyfile(r"""
|
||||||
|
def test_diff():
|
||||||
|
assert 'asdf' == 'asdf\n'
|
||||||
|
""")
|
||||||
|
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(r"""
|
||||||
|
*assert 'asdf' == 'asdf\n'
|
||||||
|
* - asdf
|
||||||
|
* + asdf
|
||||||
|
* ? +
|
||||||
|
""")
|
||||||
|
|
|
@ -152,6 +152,8 @@ class TestCollectPluginHookRelay:
|
||||||
wascalled = []
|
wascalled = []
|
||||||
class Plugin:
|
class Plugin:
|
||||||
def pytest_collect_file(self, path, parent):
|
def pytest_collect_file(self, path, parent):
|
||||||
|
if not path.basename.startswith("."):
|
||||||
|
# Ignore hidden files, e.g. .testmondata.
|
||||||
wascalled.append(path)
|
wascalled.append(path)
|
||||||
testdir.makefile(".abc", "xyz")
|
testdir.makefile(".abc", "xyz")
|
||||||
pytest.main([testdir.tmpdir], plugins=[Plugin()])
|
pytest.main([testdir.tmpdir], plugins=[Plugin()])
|
||||||
|
|
|
@ -14,13 +14,16 @@ class TestDoctests:
|
||||||
>>> i-1
|
>>> i-1
|
||||||
4
|
4
|
||||||
""")
|
""")
|
||||||
|
|
||||||
for x in (testdir.tmpdir, checkfile):
|
for x in (testdir.tmpdir, checkfile):
|
||||||
#print "checking that %s returns custom items" % (x,)
|
#print "checking that %s returns custom items" % (x,)
|
||||||
items, reprec = testdir.inline_genitems(x)
|
items, reprec = testdir.inline_genitems(x)
|
||||||
assert len(items) == 1
|
assert len(items) == 1
|
||||||
assert isinstance(items[0], DoctestTextfile)
|
assert isinstance(items[0], DoctestItem)
|
||||||
|
assert isinstance(items[0].parent, DoctestTextfile)
|
||||||
|
# Empty file has no items.
|
||||||
items, reprec = testdir.inline_genitems(w)
|
items, reprec = testdir.inline_genitems(w)
|
||||||
assert len(items) == 1
|
assert len(items) == 0
|
||||||
|
|
||||||
def test_collect_module_empty(self, testdir):
|
def test_collect_module_empty(self, testdir):
|
||||||
path = testdir.makepyfile(whatever="#")
|
path = testdir.makepyfile(whatever="#")
|
||||||
|
@ -608,6 +611,11 @@ class TestDoctestSkips:
|
||||||
reprec = testdir.inline_run("--doctest-modules")
|
reprec = testdir.inline_run("--doctest-modules")
|
||||||
reprec.assertoutcome(skipped=1)
|
reprec.assertoutcome(skipped=1)
|
||||||
|
|
||||||
|
def test_vacuous_all_skipped(self, testdir, makedoctest):
|
||||||
|
makedoctest('')
|
||||||
|
reprec = testdir.inline_run("--doctest-modules")
|
||||||
|
reprec.assertoutcome(passed=0, skipped=0)
|
||||||
|
|
||||||
|
|
||||||
class TestDoctestAutoUseFixtures:
|
class TestDoctestAutoUseFixtures:
|
||||||
|
|
||||||
|
|
|
@ -228,6 +228,39 @@ class BaseFunctionalTests:
|
||||||
assert reps[5].nodeid.endswith("test_func")
|
assert reps[5].nodeid.endswith("test_func")
|
||||||
assert reps[5].failed
|
assert reps[5].failed
|
||||||
|
|
||||||
|
def test_exact_teardown_issue1206(self, testdir):
|
||||||
|
rec = testdir.inline_runsource("""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
class TestClass:
|
||||||
|
def teardown_method(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_method(self):
|
||||||
|
assert True
|
||||||
|
""")
|
||||||
|
reps = rec.getreports("pytest_runtest_logreport")
|
||||||
|
print (reps)
|
||||||
|
assert len(reps) == 3
|
||||||
|
#
|
||||||
|
assert reps[0].nodeid.endswith("test_method")
|
||||||
|
assert reps[0].passed
|
||||||
|
assert reps[0].when == 'setup'
|
||||||
|
#
|
||||||
|
assert reps[1].nodeid.endswith("test_method")
|
||||||
|
assert reps[1].passed
|
||||||
|
assert reps[1].when == 'call'
|
||||||
|
#
|
||||||
|
assert reps[2].nodeid.endswith("test_method")
|
||||||
|
assert reps[2].failed
|
||||||
|
assert reps[2].when == "teardown"
|
||||||
|
assert reps[2].longrepr.reprcrash.message in (
|
||||||
|
# python3 error
|
||||||
|
'TypeError: teardown_method() takes 1 positional argument but 2 were given',
|
||||||
|
# python2 error
|
||||||
|
'TypeError: teardown_method() takes exactly 1 argument (2 given)'
|
||||||
|
)
|
||||||
|
|
||||||
def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
|
def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
|
||||||
testdir.makepyfile(conftest="""
|
testdir.makepyfile(conftest="""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -8,16 +8,11 @@ import _pytest._pluggy as pluggy
|
||||||
import _pytest._code
|
import _pytest._code
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest import runner
|
|
||||||
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
from _pytest.main import EXIT_NOTESTSCOLLECTED
|
||||||
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
|
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
|
||||||
from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
|
from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
|
||||||
|
|
||||||
|
|
||||||
def basic_run_report(item):
|
|
||||||
runner.call_and_report(item, "setup", log=False)
|
|
||||||
return runner.call_and_report(item, "call", log=False)
|
|
||||||
|
|
||||||
DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version'])
|
DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version'])
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue