Merge pull request #1554 from RonnyPfannschmidt/merge-master
Merge master into features
This commit is contained in:
commit
8c1be624a6
4
AUTHORS
4
AUTHORS
|
@ -50,6 +50,7 @@ Jaap Broekhuizen
|
||||||
Jan Balster
|
Jan Balster
|
||||||
Janne Vanhala
|
Janne Vanhala
|
||||||
Jason R. Coombs
|
Jason R. Coombs
|
||||||
|
John Towler
|
||||||
Joshua Bronson
|
Joshua Bronson
|
||||||
Jurko Gospodnetić
|
Jurko Gospodnetić
|
||||||
Katarzyna Jachim
|
Katarzyna Jachim
|
||||||
|
@ -63,6 +64,7 @@ Marc Schlaich
|
||||||
Mark Abramowitz
|
Mark Abramowitz
|
||||||
Markus Unterwaditzer
|
Markus Unterwaditzer
|
||||||
Martijn Faassen
|
Martijn Faassen
|
||||||
|
Martin Prusse
|
||||||
Matt Bachmann
|
Matt Bachmann
|
||||||
Matt Williams
|
Matt Williams
|
||||||
Michael Aquilina
|
Michael Aquilina
|
||||||
|
@ -74,6 +76,7 @@ Omar Kohl
|
||||||
Pieter Mulder
|
Pieter Mulder
|
||||||
Piotr Banaszkiewicz
|
Piotr Banaszkiewicz
|
||||||
Punyashloka Biswal
|
Punyashloka Biswal
|
||||||
|
Quentin Pradet
|
||||||
Ralf Schmitt
|
Ralf Schmitt
|
||||||
Raphael Pierzina
|
Raphael Pierzina
|
||||||
Roman Bolshakov
|
Roman Bolshakov
|
||||||
|
@ -92,3 +95,4 @@ Russel Winder
|
||||||
Ben Webb
|
Ben Webb
|
||||||
Alexei Kozlenok
|
Alexei Kozlenok
|
||||||
Cal Leeming
|
Cal Leeming
|
||||||
|
Feng Ma
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
for a fixture (to solve the funcarg-shadowing-fixture problem).
|
for a fixture (to solve the funcarg-shadowing-fixture problem).
|
||||||
Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
|
Thanks `@novas0x2a`_ for the complete PR (`#1444`_).
|
||||||
|
|
||||||
* New ``approx()`` function for easily comparing floating-point numbers in
|
* New ``approx()`` function for easily comparing floating-point numbers in
|
||||||
tests.
|
tests.
|
||||||
Thanks `@kalekundert`_ for the complete PR (`#1441`_).
|
Thanks `@kalekundert`_ for the complete PR (`#1441`_).
|
||||||
|
|
||||||
|
@ -86,11 +86,24 @@
|
||||||
|
|
||||||
* When receiving identical test ids in parametrize we generate unique test ids.
|
* When receiving identical test ids in parametrize we generate unique test ids.
|
||||||
|
|
||||||
*
|
* Fix win32 path issue when puttinging custom config file with absolute path
|
||||||
|
in ``pytest.main("-c your_absolute_path")``.
|
||||||
|
|
||||||
*
|
* Fix maximum recursion depth detection when raised error class is not aware
|
||||||
|
of unicode/encoded bytes.
|
||||||
|
Thanks `@prusse-martin`_ for the PR (`#1506`_).
|
||||||
|
|
||||||
*
|
* Fix ``pytest.mark.skip`` mark when used in strict mode.
|
||||||
|
Thanks `@pquentin`_ for the PR and `@RonnyPfannschmidt`_ for
|
||||||
|
showing how to fix the bug.
|
||||||
|
|
||||||
|
* Minor improvements and fixes to the documentation.
|
||||||
|
Thanks `@omarkohl`_ for the PR.
|
||||||
|
|
||||||
|
|
||||||
|
.. _#1506: https://github.com/pytest-dev/pytest/pull/1506
|
||||||
|
|
||||||
|
.. _@prusse-martin: https://github.com/prusse-martin
|
||||||
|
|
||||||
|
|
||||||
2.9.1
|
2.9.1
|
||||||
|
@ -148,7 +161,7 @@
|
||||||
``xfail_strict`` ini option that can be used to configure it project-wise.
|
``xfail_strict`` ini option that can be used to configure it project-wise.
|
||||||
Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
|
Thanks `@rabbbit`_ for the request and `@nicoddemus`_ for the PR (`#1355`_).
|
||||||
|
|
||||||
* ``Parser.addini`` now supports options of type ``bool``.
|
* ``Parser.addini`` now supports options of type ``bool``.
|
||||||
Thanks `@nicoddemus`_ for the PR.
|
Thanks `@nicoddemus`_ for the PR.
|
||||||
|
|
||||||
* New ``ALLOW_BYTES`` doctest option. This strips ``b`` prefixes from byte strings
|
* New ``ALLOW_BYTES`` doctest option. This strips ``b`` prefixes from byte strings
|
||||||
|
@ -159,26 +172,26 @@
|
||||||
Fixes `#1366`_.
|
Fixes `#1366`_.
|
||||||
Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
|
Thanks to `@hpk42`_ for the report and `@RonnyPfannschmidt`_ for the PR.
|
||||||
|
|
||||||
* Catch ``IndexError`` exceptions when getting exception source location.
|
* Catch ``IndexError`` exceptions when getting exception source location.
|
||||||
Fixes a pytest internal error for dynamically generated code (fixtures and tests)
|
Fixes a pytest internal error for dynamically generated code (fixtures and tests)
|
||||||
where source lines are fake by intention.
|
where source lines are fake by intention.
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
||||||
* **Important**: `py.code <http://pylib.readthedocs.org/en/latest/code.html>`_ has been
|
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
|
||||||
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
||||||
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
||||||
fact that it was in a different repository made it difficult to fix bugs on
|
fact that it was in a different repository made it difficult to fix bugs on
|
||||||
its code in a timely manner. The team hopes with this to be able to better
|
its code in a timely manner. The team hopes with this to be able to better
|
||||||
refactor out and improve that code.
|
refactor out and improve that code.
|
||||||
This change shouldn't affect users, but it is useful to let users aware
|
This change shouldn't affect users, but it is useful to let users aware
|
||||||
if they encounter any strange behavior.
|
if they encounter any strange behavior.
|
||||||
|
|
||||||
Keep in mind that the code for ``pytest._code`` is **private** and
|
Keep in mind that the code for ``pytest._code`` is **private** and
|
||||||
**experimental**, so you definitely should not import it explicitly!
|
**experimental**, so you definitely should not import it explicitly!
|
||||||
|
|
||||||
Please note that the original ``py.code`` is still available in
|
Please note that the original ``py.code`` is still available in
|
||||||
`pylib <http://pylib.readthedocs.org>`_.
|
`pylib <https://pylib.readthedocs.io>`_.
|
||||||
|
|
||||||
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
||||||
Thanks `@nicoddemus`_ for the PR.
|
Thanks `@nicoddemus`_ for the PR.
|
||||||
|
@ -246,6 +259,7 @@
|
||||||
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
|
.. _@RonnyPfannschmidt: https://github.com/RonnyPfannschmidt
|
||||||
.. _@rabbbit: https://github.com/rabbbit
|
.. _@rabbbit: https://github.com/rabbbit
|
||||||
.. _@hackebrot: https://github.com/hackebrot
|
.. _@hackebrot: https://github.com/hackebrot
|
||||||
|
.. _@pquentin: https://github.com/pquentin
|
||||||
|
|
||||||
2.8.7
|
2.8.7
|
||||||
=====
|
=====
|
||||||
|
@ -269,7 +283,7 @@
|
||||||
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
|
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
|
||||||
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
|
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
|
||||||
|
|
||||||
- fix #1223: captured stdout and stderr are now properly displayed before
|
- fix #1223: captured stdout and stderr are now properly displayed before
|
||||||
entering pdb when ``--pdb`` is used instead of being thrown away.
|
entering pdb when ``--pdb`` is used instead of being thrown away.
|
||||||
Thanks Cal Leeming for the PR.
|
Thanks Cal Leeming for the PR.
|
||||||
|
|
||||||
|
@ -344,8 +358,8 @@
|
||||||
Thanks Gabriel Reis for the PR.
|
Thanks Gabriel Reis for the PR.
|
||||||
|
|
||||||
- add more talks to the documentation
|
- add more talks to the documentation
|
||||||
- extend documentation on the --ignore cli option
|
- extend documentation on the --ignore cli option
|
||||||
- use pytest-runner for setuptools integration
|
- use pytest-runner for setuptools integration
|
||||||
- minor fixes for interaction with OS X El Capitan
|
- minor fixes for interaction with OS X El Capitan
|
||||||
system integrity protection (thanks Florian)
|
system integrity protection (thanks Florian)
|
||||||
|
|
||||||
|
|
|
@ -305,11 +305,11 @@ class Traceback(list):
|
||||||
def filter(self, fn=lambda x: not x.ishidden()):
|
def filter(self, fn=lambda x: not x.ishidden()):
|
||||||
""" return a Traceback instance with certain items removed
|
""" return a Traceback instance with certain items removed
|
||||||
|
|
||||||
fn is a function that gets a single argument, a TracebackItem
|
fn is a function that gets a single argument, a TracebackEntry
|
||||||
instance, and should return True when the item should be added
|
instance, and should return True when the item should be added
|
||||||
to the Traceback, False when not
|
to the Traceback, False when not
|
||||||
|
|
||||||
by default this removes all the TracebackItems which are hidden
|
by default this removes all the TracebackEntries which are hidden
|
||||||
(see ishidden() above)
|
(see ishidden() above)
|
||||||
"""
|
"""
|
||||||
return Traceback(filter(fn, self), self._excinfo)
|
return Traceback(filter(fn, self), self._excinfo)
|
||||||
|
@ -325,7 +325,7 @@ class Traceback(list):
|
||||||
return self[-1]
|
return self[-1]
|
||||||
|
|
||||||
def recursionindex(self):
|
def recursionindex(self):
|
||||||
""" return the index of the frame/TracebackItem where recursion
|
""" return the index of the frame/TracebackEntry where recursion
|
||||||
originates if appropriate, None if no recursion occurred
|
originates if appropriate, None if no recursion occurred
|
||||||
"""
|
"""
|
||||||
cache = {}
|
cache = {}
|
||||||
|
@ -603,9 +603,8 @@ class FormattedExcinfo(object):
|
||||||
if self.tbfilter:
|
if self.tbfilter:
|
||||||
traceback = traceback.filter()
|
traceback = traceback.filter()
|
||||||
recursionindex = None
|
recursionindex = None
|
||||||
if excinfo.errisinstance(RuntimeError):
|
if is_recursion_error(excinfo):
|
||||||
if "maximum recursion depth exceeded" in str(excinfo.value):
|
recursionindex = traceback.recursionindex()
|
||||||
recursionindex = traceback.recursionindex()
|
|
||||||
last = traceback[-1]
|
last = traceback[-1]
|
||||||
entries = []
|
entries = []
|
||||||
extraline = None
|
extraline = None
|
||||||
|
@ -867,3 +866,14 @@ def getrawcode(obj, trycall=True):
|
||||||
return x
|
return x
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
if sys.version_info[:2] >= (3, 5): # RecursionError introduced in 3.5
|
||||||
|
def is_recursion_error(excinfo):
|
||||||
|
return excinfo.errisinstance(RecursionError) # noqa
|
||||||
|
else:
|
||||||
|
def is_recursion_error(excinfo):
|
||||||
|
if not excinfo.errisinstance(RuntimeError):
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
return "maximum recursion depth exceeded" in str(excinfo.value)
|
||||||
|
except UnicodeError:
|
||||||
|
return False
|
||||||
|
|
|
@ -104,7 +104,7 @@ def _prepareconfig(args=None, plugins=None):
|
||||||
elif not isinstance(args, (tuple, list)):
|
elif not isinstance(args, (tuple, list)):
|
||||||
if not isinstance(args, str):
|
if not isinstance(args, str):
|
||||||
raise ValueError("not a string or argument list: %r" % (args,))
|
raise ValueError("not a string or argument list: %r" % (args,))
|
||||||
args = shlex.split(args)
|
args = shlex.split(args, posix=sys.platform != "win32")
|
||||||
config = get_config()
|
config = get_config()
|
||||||
pluginmanager = config.pluginmanager
|
pluginmanager = config.pluginmanager
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -373,7 +373,7 @@ class LogXML(object):
|
||||||
suite_stop_time = time.time()
|
suite_stop_time = time.time()
|
||||||
suite_time_delta = suite_stop_time - self.suite_start_time
|
suite_time_delta = suite_stop_time - self.suite_start_time
|
||||||
|
|
||||||
numtests = self.stats['passed'] + self.stats['failure']
|
numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped']
|
||||||
|
|
||||||
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
|
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
|
||||||
|
|
||||||
|
|
|
@ -58,7 +58,7 @@ pytest_cmdline_main.tryfirst = True
|
||||||
|
|
||||||
|
|
||||||
def pytest_collection_modifyitems(items, config):
|
def pytest_collection_modifyitems(items, config):
|
||||||
keywordexpr = config.option.keyword
|
keywordexpr = config.option.keyword.lstrip()
|
||||||
matchexpr = config.option.markexpr
|
matchexpr = config.option.markexpr
|
||||||
if not keywordexpr and not matchexpr:
|
if not keywordexpr and not matchexpr:
|
||||||
return
|
return
|
||||||
|
|
|
@ -1746,7 +1746,7 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
self._pyfuncitem = pyfuncitem
|
self._pyfuncitem = pyfuncitem
|
||||||
#: fixture for which this request is being performed
|
#: fixture for which this request is being performed
|
||||||
self.fixturename = None
|
self.fixturename = None
|
||||||
#: Scope string, one of "function", "cls", "module", "session"
|
#: Scope string, one of "function", "class", "module", "session"
|
||||||
self.scope = "function"
|
self.scope = "function"
|
||||||
self._funcargs = {}
|
self._funcargs = {}
|
||||||
self._fixturedefs = {}
|
self._fixturedefs = {}
|
||||||
|
|
|
@ -30,6 +30,11 @@ def pytest_configure(config):
|
||||||
nop.Exception = XFailed
|
nop.Exception = XFailed
|
||||||
setattr(pytest, "xfail", nop)
|
setattr(pytest, "xfail", nop)
|
||||||
|
|
||||||
|
config.addinivalue_line("markers",
|
||||||
|
"skip(reason=None): skip the given test function with an optional reason. "
|
||||||
|
"Example: skip(reason=\"no way of currently testing this\") skips the "
|
||||||
|
"test."
|
||||||
|
)
|
||||||
config.addinivalue_line("markers",
|
config.addinivalue_line("markers",
|
||||||
"skipif(condition): skip the given test function if eval(condition) "
|
"skipif(condition): skip the given test function if eval(condition) "
|
||||||
"results in a True value. Evaluation happens within the "
|
"results in a True value. Evaluation happens within the "
|
||||||
|
@ -38,13 +43,13 @@ def pytest_configure(config):
|
||||||
"http://pytest.org/latest/skipping.html"
|
"http://pytest.org/latest/skipping.html"
|
||||||
)
|
)
|
||||||
config.addinivalue_line("markers",
|
config.addinivalue_line("markers",
|
||||||
"xfail(condition, reason=None, run=True, raises=None): mark the the test function "
|
"xfail(condition, reason=None, run=True, raises=None, strict=False): "
|
||||||
"as an expected failure if eval(condition) has a True value. "
|
"mark the the test function as an expected failure if eval(condition) "
|
||||||
"Optionally specify a reason for better reporting and run=False if "
|
"has a True value. Optionally specify a reason for better reporting "
|
||||||
"you don't even want to execute the test function. If only specific "
|
"and run=False if you don't even want to execute the test function. "
|
||||||
"exception(s) are expected, you can list them in raises, and if the test fails "
|
"If only specific exception(s) are expected, you can list them in "
|
||||||
"in other ways, it will be reported as a true failure. "
|
"raises, and if the test fails in other ways, it will be reported as "
|
||||||
"See http://pytest.org/latest/skipping.html"
|
"a true failure. See http://pytest.org/latest/skipping.html"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -75,7 +75,7 @@ The py.test Development Team
|
||||||
|
|
||||||
**Changes**
|
**Changes**
|
||||||
|
|
||||||
* **Important**: `py.code <http://pylib.readthedocs.org/en/latest/code.html>`_ has been
|
* **Important**: `py.code <https://pylib.readthedocs.io/en/latest/code.html>`_ has been
|
||||||
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
merged into the ``pytest`` repository as ``pytest._code``. This decision
|
||||||
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
was made because ``py.code`` had very few uses outside ``pytest`` and the
|
||||||
fact that it was in a different repository made it difficult to fix bugs on
|
fact that it was in a different repository made it difficult to fix bugs on
|
||||||
|
@ -88,7 +88,7 @@ The py.test Development Team
|
||||||
**experimental**, so you definitely should not import it explicitly!
|
**experimental**, so you definitely should not import it explicitly!
|
||||||
|
|
||||||
Please note that the original ``py.code`` is still available in
|
Please note that the original ``py.code`` is still available in
|
||||||
`pylib <http://pylib.readthedocs.org>`_.
|
`pylib <https://pylib.readthedocs.io>`_.
|
||||||
|
|
||||||
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
* ``pytest_enter_pdb`` now optionally receives the pytest config object.
|
||||||
Thanks `@nicoddemus`_ for the PR.
|
Thanks `@nicoddemus`_ for the PR.
|
||||||
|
|
|
@ -5,7 +5,7 @@ Setting up bash completion
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
When using bash as your shell, ``pytest`` can use argcomplete
|
When using bash as your shell, ``pytest`` can use argcomplete
|
||||||
(https://argcomplete.readthedocs.org/) for auto-completion.
|
(https://argcomplete.readthedocs.io/) for auto-completion.
|
||||||
For this ``argcomplete`` needs to be installed **and** enabled.
|
For this ``argcomplete`` needs to be installed **and** enabled.
|
||||||
|
|
||||||
Install argcomplete using::
|
Install argcomplete using::
|
||||||
|
|
|
@ -9,19 +9,19 @@ by passing the ``--ignore=path`` option on the cli. ``pytest`` allows multiple
|
||||||
``--ignore`` options. Example::
|
``--ignore`` options. Example::
|
||||||
|
|
||||||
tests/
|
tests/
|
||||||
├── example
|
|-- example
|
||||||
│ ├── test_example_01.py
|
| |-- test_example_01.py
|
||||||
│ ├── test_example_02.py
|
| |-- test_example_02.py
|
||||||
│ └── test_example_03.py
|
| '-- test_example_03.py
|
||||||
├── foobar
|
|-- foobar
|
||||||
│ ├── test_foobar_01.py
|
| |-- test_foobar_01.py
|
||||||
│ ├── test_foobar_02.py
|
| |-- test_foobar_02.py
|
||||||
│ └── test_foobar_03.py
|
| '-- test_foobar_03.py
|
||||||
└── hello
|
'-- hello
|
||||||
└── world
|
'-- world
|
||||||
├── test_world_01.py
|
|-- test_world_01.py
|
||||||
├── test_world_02.py
|
|-- test_world_02.py
|
||||||
└── test_world_03.py
|
'-- test_world_03.py
|
||||||
|
|
||||||
Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
|
Now if you invoke ``pytest`` with ``--ignore=tests/foobar/test_foobar_03.py --ignore=tests/hello/``,
|
||||||
you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
|
you will see that ``pytest`` only collects test-modules, which do not match the patterns specified::
|
||||||
|
@ -177,16 +177,27 @@ and a setup.py dummy file like this::
|
||||||
# content of setup.py
|
# content of setup.py
|
||||||
0/0 # will raise exception if imported
|
0/0 # will raise exception if imported
|
||||||
|
|
||||||
then a pytest run on python2 will find the one test when run with a python2
|
then a pytest run on Python2 will find the one test and will leave out the
|
||||||
interpreters and will leave out the setup.py file::
|
setup.py file::
|
||||||
|
|
||||||
$ py.test --collect-only
|
$ py.test --collect-only
|
||||||
======= test session starts ========
|
====== test session starts ======
|
||||||
platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
|
platform linux2 -- Python 2.7.10, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
|
||||||
|
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||||
|
collected 1 items
|
||||||
|
<Module 'pkg/module_py2.py'>
|
||||||
|
<Function 'test_only_on_python2'>
|
||||||
|
|
||||||
|
====== no tests ran in 0.04 seconds ======
|
||||||
|
|
||||||
|
If you run with a Python3 interpreter both the one test and the setup.py file
|
||||||
|
will be left out::
|
||||||
|
|
||||||
|
$ py.test --collect-only
|
||||||
|
====== test session starts ======
|
||||||
|
platform linux -- Python 3.4.3+, pytest-2.9.1, py-1.4.31, pluggy-0.3.1
|
||||||
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
rootdir: $REGENDOC_TMPDIR, inifile: pytest.ini
|
||||||
collected 0 items
|
collected 0 items
|
||||||
|
|
||||||
======= no tests ran in 0.12 seconds ========
|
|
||||||
|
|
||||||
If you run with a Python3 interpreter the moduled added through the conftest.py file will not be considered for test collection.
|
====== no tests ran in 0.03 seconds ======
|
||||||
|
|
||||||
|
|
|
@ -201,7 +201,7 @@ Example::
|
||||||
|
|
||||||
The ``__tracebackhide__`` setting influences ``pytest`` showing
|
The ``__tracebackhide__`` setting influences ``pytest`` showing
|
||||||
of tracebacks: the ``checkconfig`` function will not be shown
|
of tracebacks: the ``checkconfig`` function will not be shown
|
||||||
unless the ``--fulltrace`` command line option is specified.
|
unless the ``--full-trace`` command line option is specified.
|
||||||
Let's run our little function::
|
Let's run our little function::
|
||||||
|
|
||||||
$ py.test -q test_checkconfig.py
|
$ py.test -q test_checkconfig.py
|
||||||
|
@ -725,7 +725,7 @@ Integrating pytest runner and cx_freeze
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
If you freeze your application using a tool like
|
If you freeze your application using a tool like
|
||||||
`cx_freeze <http://cx-freeze.readthedocs.org>`_ in order to distribute it
|
`cx_freeze <https://cx-freeze.readthedocs.io>`_ in order to distribute it
|
||||||
to your end-users, it is a good idea to also package your test runner and run
|
to your end-users, it is a good idea to also package your test runner and run
|
||||||
your tests using the frozen application.
|
your tests using the frozen application.
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,9 @@ style <unittest.TestCase>` or :ref:`nose based <nosestyle>` projects.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
pytest-2.4 introduced an additional experimental
|
pytest-2.4 introduced an additional :ref:`yield fixture mechanism
|
||||||
:ref:`yield fixture mechanism <yieldfixture>` for easier context manager
|
<yieldfixture>` for easier context manager integration and more linear
|
||||||
integration and more linear writing of teardown code.
|
writing of teardown code.
|
||||||
|
|
||||||
.. _`funcargs`:
|
.. _`funcargs`:
|
||||||
.. _`funcarg mechanism`:
|
.. _`funcarg mechanism`:
|
||||||
|
@ -283,6 +283,14 @@ module itself does not need to change or know about these details
|
||||||
of fixture setup.
|
of fixture setup.
|
||||||
|
|
||||||
|
|
||||||
|
Finalization/teardown with yield fixtures
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Another alternative to the *request.addfinalizer()* method is to use *yield
|
||||||
|
fixtures*. All the code after the *yield* statement serves as the teardown
|
||||||
|
code. See the :ref:`yield fixture documentation <yieldfixture>`.
|
||||||
|
|
||||||
|
|
||||||
.. _`request-context`:
|
.. _`request-context`:
|
||||||
|
|
||||||
Fixtures can introspect the requesting test context
|
Fixtures can introspect the requesting test context
|
||||||
|
@ -577,55 +585,85 @@ to show the setup/teardown flow::
|
||||||
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
@pytest.fixture(scope="module", params=["mod1", "mod2"])
|
||||||
def modarg(request):
|
def modarg(request):
|
||||||
param = request.param
|
param = request.param
|
||||||
print ("create", param)
|
print (" SETUP modarg %s" % param)
|
||||||
def fin():
|
def fin():
|
||||||
print ("fin %s" % param)
|
print (" TEARDOWN modarg %s" % param)
|
||||||
|
request.addfinalizer(fin)
|
||||||
return param
|
return param
|
||||||
|
|
||||||
@pytest.fixture(scope="function", params=[1,2])
|
@pytest.fixture(scope="function", params=[1,2])
|
||||||
def otherarg(request):
|
def otherarg(request):
|
||||||
return request.param
|
param = request.param
|
||||||
|
print (" SETUP otherarg %s" % param)
|
||||||
|
def fin():
|
||||||
|
print (" TEARDOWN otherarg %s" % param)
|
||||||
|
request.addfinalizer(fin)
|
||||||
|
return param
|
||||||
|
|
||||||
def test_0(otherarg):
|
def test_0(otherarg):
|
||||||
print (" test0", otherarg)
|
print (" RUN test0 with otherarg %s" % otherarg)
|
||||||
def test_1(modarg):
|
def test_1(modarg):
|
||||||
print (" test1", modarg)
|
print (" RUN test1 with modarg %s" % modarg)
|
||||||
def test_2(otherarg, modarg):
|
def test_2(otherarg, modarg):
|
||||||
print (" test2", otherarg, modarg)
|
print (" RUN test2 with otherarg %s and modarg %s" % (otherarg, modarg))
|
||||||
|
|
||||||
|
|
||||||
Let's run the tests in verbose mode and with looking at the print-output::
|
Let's run the tests in verbose mode and with looking at the print-output::
|
||||||
|
|
||||||
$ py.test -v -s test_module.py
|
$ py.test -v -s test_module.py
|
||||||
======= test session starts ========
|
====== test session starts ======
|
||||||
platform linux -- Python 3.4.0, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3.4
|
platform linux -- Python 3.4.3+, pytest-2.9.1, py-1.4.31, pluggy-0.3.1 -- $PYTHON_PREFIX/bin/python3
|
||||||
cachedir: .cache
|
cachedir: .cache
|
||||||
rootdir: $REGENDOC_TMPDIR, inifile:
|
rootdir: $REGENDOC_TMPDIR, inifile:
|
||||||
collecting ... collected 8 items
|
collected 8 items
|
||||||
|
|
||||||
test_module.py::test_0[1] test0 1
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_0[2] test0 2
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_1[mod1] create mod1
|
|
||||||
test1 mod1
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_2[1-mod1] test2 1 mod1
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_2[2-mod1] test2 2 mod1
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_1[mod2] create mod2
|
|
||||||
test1 mod2
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_2[1-mod2] test2 1 mod2
|
|
||||||
PASSED
|
|
||||||
test_module.py::test_2[2-mod2] test2 2 mod2
|
|
||||||
PASSED
|
|
||||||
|
|
||||||
======= 8 passed in 0.12 seconds ========
|
|
||||||
|
|
||||||
You can see that the parametrized module-scoped ``modarg`` resource caused
|
test_module.py::test_0[1] SETUP otherarg 1
|
||||||
an ordering of test execution that lead to the fewest possible "active" resources. The finalizer for the ``mod1`` parametrized resource was executed
|
RUN test0 with otherarg 1
|
||||||
before the ``mod2`` resource was setup.
|
PASSED TEARDOWN otherarg 1
|
||||||
|
|
||||||
|
test_module.py::test_0[2] SETUP otherarg 2
|
||||||
|
RUN test0 with otherarg 2
|
||||||
|
PASSED TEARDOWN otherarg 2
|
||||||
|
|
||||||
|
test_module.py::test_1[mod1] SETUP modarg mod1
|
||||||
|
RUN test1 with modarg mod1
|
||||||
|
PASSED
|
||||||
|
test_module.py::test_2[1-mod1] SETUP otherarg 1
|
||||||
|
RUN test2 with otherarg 1 and modarg mod1
|
||||||
|
PASSED TEARDOWN otherarg 1
|
||||||
|
|
||||||
|
test_module.py::test_2[2-mod1] SETUP otherarg 2
|
||||||
|
RUN test2 with otherarg 2 and modarg mod1
|
||||||
|
PASSED TEARDOWN otherarg 2
|
||||||
|
|
||||||
|
test_module.py::test_1[mod2] TEARDOWN modarg mod1
|
||||||
|
SETUP modarg mod2
|
||||||
|
RUN test1 with modarg mod2
|
||||||
|
PASSED
|
||||||
|
test_module.py::test_2[1-mod2] SETUP otherarg 1
|
||||||
|
RUN test2 with otherarg 1 and modarg mod2
|
||||||
|
PASSED TEARDOWN otherarg 1
|
||||||
|
|
||||||
|
test_module.py::test_2[2-mod2] SETUP otherarg 2
|
||||||
|
RUN test2 with otherarg 2 and modarg mod2
|
||||||
|
PASSED TEARDOWN otherarg 2
|
||||||
|
TEARDOWN modarg mod2
|
||||||
|
|
||||||
|
|
||||||
|
====== 8 passed in 0.01 seconds ======
|
||||||
|
|
||||||
|
|
||||||
|
You can see that the parametrized module-scoped ``modarg`` resource caused an
|
||||||
|
ordering of test execution that lead to the fewest possible "active" resources.
|
||||||
|
The finalizer for the ``mod1`` parametrized resource was executed before the
|
||||||
|
``mod2`` resource was setup.
|
||||||
|
|
||||||
|
In particular notice that test_0 is completely independent and finishes first.
|
||||||
|
Then test_1 is executed with ``mod1``, then test_2 with ``mod1``, then test_1
|
||||||
|
with ``mod2`` and finally test_2 with ``mod2``.
|
||||||
|
|
||||||
|
The ``otherarg`` parametrized resource (having function scope) was set up before
|
||||||
|
and teared down after every test that used it.
|
||||||
|
|
||||||
|
|
||||||
.. _`usefixtures`:
|
.. _`usefixtures`:
|
||||||
|
|
|
@ -153,7 +153,9 @@ against your source code checkout, helping to detect packaging
|
||||||
glitches.
|
glitches.
|
||||||
|
|
||||||
Continuous integration services such as Jenkins_ can make use of the
|
Continuous integration services such as Jenkins_ can make use of the
|
||||||
``--junitxml=PATH`` option to create a JUnitXML file and generate reports.
|
``--junitxml=PATH`` option to create a JUnitXML file and generate reports (e.g.
|
||||||
|
by publishing the results in a nice format with the `Jenkins xUnit Plugin
|
||||||
|
<https://wiki.jenkins-ci.org/display/JENKINS/xUnit+Plugin>`_).
|
||||||
|
|
||||||
|
|
||||||
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
|
Integrating with setuptools / ``python setup.py test`` / ``pytest-runner``
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
.. _`pytest_nose`: plugin/nose.html
|
.. _`pytest_nose`: plugin/nose.html
|
||||||
.. _`reStructured Text`: http://docutils.sourceforge.net
|
.. _`reStructured Text`: http://docutils.sourceforge.net
|
||||||
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
|
.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html
|
||||||
.. _nose: https://nose.readthedocs.org/en/latest/
|
.. _nose: https://nose.readthedocs.io/en/latest/
|
||||||
.. _pytest: http://pypi.python.org/pypi/pytest
|
.. _pytest: http://pypi.python.org/pypi/pytest
|
||||||
.. _mercurial: http://mercurial.selenic.com/wiki/
|
.. _mercurial: http://mercurial.selenic.com/wiki/
|
||||||
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
.. _`setuptools`: http://pypi.python.org/pypi/setuptools
|
||||||
|
@ -18,4 +18,4 @@
|
||||||
.. _hudson: http://hudson-ci.org/
|
.. _hudson: http://hudson-ci.org/
|
||||||
.. _jenkins: http://jenkins-ci.org/
|
.. _jenkins: http://jenkins-ci.org/
|
||||||
.. _tox: http://testrun.org/tox
|
.. _tox: http://testrun.org/tox
|
||||||
.. _pylib: http://py.readthedocs.org/en/latest/
|
.. _pylib: https://py.readthedocs.io/en/latest/
|
||||||
|
|
|
@ -46,7 +46,7 @@ Unsupported idioms / known issues
|
||||||
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
(e.g. ``tests/test_mode.py`` and ``other/tests/test_mode.py``)
|
||||||
by extending sys.path/import semantics. pytest does not do that
|
by extending sys.path/import semantics. pytest does not do that
|
||||||
but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
|
but there is discussion in `issue268 <https://github.com/pytest-dev/pytest/issues/268>`_ for adding some support. Note that
|
||||||
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.org/en/latest/differences.html#test-discovery-and-loading>`_.
|
`nose2 choose to avoid this sys.path/import hackery <https://nose2.readthedocs.io/en/latest/differences.html#test-discovery-and-loading>`_.
|
||||||
|
|
||||||
- nose-style doctests are not collected and executed correctly,
|
- nose-style doctests are not collected and executed correctly,
|
||||||
also doctest fixtures don't work.
|
also doctest fixtures don't work.
|
||||||
|
|
|
@ -90,7 +90,7 @@ Finding out which plugins are active
|
||||||
If you want to find out which plugins are active in your
|
If you want to find out which plugins are active in your
|
||||||
environment you can type::
|
environment you can type::
|
||||||
|
|
||||||
py.test --traceconfig
|
py.test --trace-config
|
||||||
|
|
||||||
and will get an extended test header which shows activated plugins
|
and will get an extended test header which shows activated plugins
|
||||||
and their names. It will also print local plugins aka
|
and their names. It will also print local plugins aka
|
||||||
|
|
|
@ -57,7 +57,7 @@ Here are some examples of projects using ``pytest`` (please send notes via :ref:
|
||||||
* `bu <http://packages.python.org/bu/>`_ a microscopic build system
|
* `bu <http://packages.python.org/bu/>`_ a microscopic build system
|
||||||
* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
|
* `katcp <https://bitbucket.org/hodgestar/katcp>`_ Telescope communication protocol over Twisted
|
||||||
* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
|
* `kss plugin timer <http://pypi.python.org/pypi/kss.plugin.timer>`_
|
||||||
* `pyudev <http://pyudev.readthedocs.org/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
|
* `pyudev <https://pyudev.readthedocs.io/en/latest/tests/plugins.html>`_ a pure Python binding to the Linux library libudev
|
||||||
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
|
* `pytest-localserver <https://bitbucket.org/basti/pytest-localserver/>`_ a plugin for pytest that provides a httpserver and smtpserver
|
||||||
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
|
* `pytest-monkeyplus <http://pypi.python.org/pypi/pytest-monkeyplus/>`_ a plugin that extends monkeypatch
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ but note that project specific settings will be considered
|
||||||
first. There is a flag that helps you debugging your
|
first. There is a flag that helps you debugging your
|
||||||
conftest.py configurations::
|
conftest.py configurations::
|
||||||
|
|
||||||
py.test --traceconfig
|
py.test --trace-config
|
||||||
|
|
||||||
|
|
||||||
customizing the collecting and running process
|
customizing the collecting and running process
|
||||||
|
|
|
@ -16,7 +16,7 @@ command line options
|
||||||
display py lib version and import information.
|
display py lib version and import information.
|
||||||
``-p name``
|
``-p name``
|
||||||
early-load given plugin (multi-allowed).
|
early-load given plugin (multi-allowed).
|
||||||
``--traceconfig``
|
``--trace-config``
|
||||||
trace considerations of conftest.py files.
|
trace considerations of conftest.py files.
|
||||||
``--nomagic``
|
``--nomagic``
|
||||||
don't reinterpret asserts, no traceback cutting.
|
don't reinterpret asserts, no traceback cutting.
|
||||||
|
|
|
@ -22,7 +22,7 @@ command line options
|
||||||
(deprecated, use -r)
|
(deprecated, use -r)
|
||||||
``--tb=style``
|
``--tb=style``
|
||||||
traceback print mode (long/short/line/no).
|
traceback print mode (long/short/line/no).
|
||||||
``--fulltrace``
|
``--full-trace``
|
||||||
don't cut any tracebacks (default is to cut).
|
don't cut any tracebacks (default is to cut).
|
||||||
``--fixtures``
|
``--fixtures``
|
||||||
show available function arguments, sorted by plugin
|
show available function arguments, sorted by plugin
|
||||||
|
|
|
@ -56,7 +56,7 @@ Several test run options::
|
||||||
|
|
||||||
Import 'pkg' and use its filesystem location to find and run tests::
|
Import 'pkg' and use its filesystem location to find and run tests::
|
||||||
|
|
||||||
py.test --pyargs pkg # run all tests found below directory of pypkg
|
py.test --pyargs pkg # run all tests found below directory of pkg
|
||||||
|
|
||||||
Modifying Python traceback printing
|
Modifying Python traceback printing
|
||||||
----------------------------------------------
|
----------------------------------------------
|
||||||
|
@ -74,6 +74,14 @@ Examples for modifying traceback printing::
|
||||||
py.test --tb=native # Python standard library formatting
|
py.test --tb=native # Python standard library formatting
|
||||||
py.test --tb=no # no traceback at all
|
py.test --tb=no # no traceback at all
|
||||||
|
|
||||||
|
The ``--full-trace`` causes very long traces to be printed on error (longer
|
||||||
|
than ``--tb=long``). It also ensures that a stack trace is printed on
|
||||||
|
**KeyboardInterrrupt** (Ctrl+C).
|
||||||
|
This is very useful if the tests are taking too long and you interrupt them
|
||||||
|
with Ctrl+C to find out where the tests are *hanging*. By default no output
|
||||||
|
will be shown (because KeyboardInterrupt is catched by pytest). By using this
|
||||||
|
option you make sure a trace is shown.
|
||||||
|
|
||||||
Dropping to PDB_ (Python Debugger) on failures
|
Dropping to PDB_ (Python Debugger) on failures
|
||||||
-----------------------------------------------
|
-----------------------------------------------
|
||||||
|
|
||||||
|
@ -150,7 +158,7 @@ To get a list of the slowest 10 test durations::
|
||||||
Creating JUnitXML format files
|
Creating JUnitXML format files
|
||||||
----------------------------------------------------
|
----------------------------------------------------
|
||||||
|
|
||||||
To create result files which can be read by Hudson_ or other Continuous
|
To create result files which can be read by Jenkins_ or other Continuous
|
||||||
integration servers, use this invocation::
|
integration servers, use this invocation::
|
||||||
|
|
||||||
py.test --junitxml=path
|
py.test --junitxml=path
|
||||||
|
|
|
@ -23,7 +23,7 @@ reporting by calling `well specified hooks`_ of the following plugins:
|
||||||
|
|
||||||
In principle, each hook call is a ``1:N`` Python function call where ``N`` is the
|
In principle, each hook call is a ``1:N`` Python function call where ``N`` is the
|
||||||
number of registered implementation functions for a given specification.
|
number of registered implementation functions for a given specification.
|
||||||
All specifications and implementations following the ``pytest_`` prefix
|
All specifications and implementations follow the ``pytest_`` prefix
|
||||||
naming convention, making them easy to distinguish and find.
|
naming convention, making them easy to distinguish and find.
|
||||||
|
|
||||||
.. _`pluginorder`:
|
.. _`pluginorder`:
|
||||||
|
@ -158,13 +158,22 @@ it in your setuptools-invocation:
|
||||||
'name_of_plugin = myproject.pluginmodule',
|
'name_of_plugin = myproject.pluginmodule',
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
|
# custom PyPI classifier for pytest plugins
|
||||||
|
classifiers=[
|
||||||
|
"Framework :: Pytest",
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
If a package is installed this way, ``pytest`` will load
|
If a package is installed this way, ``pytest`` will load
|
||||||
``myproject.pluginmodule`` as a plugin which can define
|
``myproject.pluginmodule`` as a plugin which can define
|
||||||
`well specified hooks`_.
|
`well specified hooks`_.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Make sure to include ``Framework :: Pytest`` in your list of
|
||||||
|
`PyPI classifiers <https://python-packaging-user-guide.readthedocs.io/en/latest/distributing/#classifiers>`_
|
||||||
|
to make it easy for users to find your plugin.
|
||||||
|
|
||||||
|
|
||||||
Requiring/Loading plugins in a test module or conftest file
|
Requiring/Loading plugins in a test module or conftest file
|
||||||
|
@ -194,7 +203,7 @@ the plugin manager like this:
|
||||||
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
||||||
|
|
||||||
If you want to look at the names of existing plugins, use
|
If you want to look at the names of existing plugins, use
|
||||||
the ``--traceconfig`` option.
|
the ``--trace-config`` option.
|
||||||
|
|
||||||
Testing plugins
|
Testing plugins
|
||||||
---------------
|
---------------
|
||||||
|
|
|
@ -2,30 +2,34 @@ import json
|
||||||
import py
|
import py
|
||||||
import textwrap
|
import textwrap
|
||||||
|
|
||||||
issues_url = "http://bitbucket.org/api/1.0/repositories/pytest-dev/pytest/issues"
|
issues_url = "https://api.github.com/repos/pytest-dev/pytest/issues"
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
|
||||||
def get_issues():
|
def get_issues():
|
||||||
chunksize = 50
|
|
||||||
start = 0
|
|
||||||
issues = []
|
issues = []
|
||||||
|
url = issues_url
|
||||||
while 1:
|
while 1:
|
||||||
post_data = {"accountname": "pytest-dev",
|
get_data = {"state": "all"}
|
||||||
"repo_slug": "pytest",
|
r = requests.get(url, params=get_data)
|
||||||
"start": start,
|
|
||||||
"limit": chunksize}
|
|
||||||
print ("getting from", start)
|
|
||||||
r = requests.get(issues_url, params=post_data)
|
|
||||||
data = r.json()
|
data = r.json()
|
||||||
issues.extend(data["issues"])
|
if r.status_code == 403:
|
||||||
if start + chunksize >= data["count"]:
|
# API request limit exceeded
|
||||||
|
print(data['message'])
|
||||||
|
exit(1)
|
||||||
|
issues.extend(data)
|
||||||
|
|
||||||
|
# Look for next page
|
||||||
|
links = requests.utils.parse_header_links(r.headers['Link'])
|
||||||
|
another_page = False
|
||||||
|
for link in links:
|
||||||
|
if link['rel'] == 'next':
|
||||||
|
url = link['url']
|
||||||
|
another_page = True
|
||||||
|
if not another_page:
|
||||||
return issues
|
return issues
|
||||||
start += chunksize
|
|
||||||
|
|
||||||
kind2num = "bug enhancement task proposal".split()
|
|
||||||
|
|
||||||
status2num = "new open resolved duplicate invalid wontfix".split()
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
cachefile = py.path.local(args.cache)
|
cachefile = py.path.local(args.cache)
|
||||||
|
@ -35,33 +39,38 @@ def main(args):
|
||||||
else:
|
else:
|
||||||
issues = json.loads(cachefile.read())
|
issues = json.loads(cachefile.read())
|
||||||
|
|
||||||
open_issues = [x for x in issues
|
open_issues = [x for x in issues if x["state"] == "open"]
|
||||||
if x["status"] in ("new", "open")]
|
|
||||||
|
|
||||||
def kind_and_id(x):
|
open_issues.sort(key=lambda x: x["number"])
|
||||||
kind = x["metadata"]["kind"]
|
|
||||||
return kind2num.index(kind), len(issues)-int(x["local_id"])
|
|
||||||
open_issues.sort(key=kind_and_id)
|
|
||||||
report(open_issues)
|
report(open_issues)
|
||||||
|
|
||||||
|
|
||||||
|
def _get_kind(issue):
|
||||||
|
labels = [l['name'] for l in issue['labels']]
|
||||||
|
for key in ('bug', 'enhancement', 'proposal'):
|
||||||
|
if key in labels:
|
||||||
|
return key
|
||||||
|
return 'issue'
|
||||||
|
|
||||||
|
|
||||||
def report(issues):
|
def report(issues):
|
||||||
for issue in issues:
|
for issue in issues:
|
||||||
metadata = issue["metadata"]
|
|
||||||
priority = issue["priority"]
|
|
||||||
title = issue["title"]
|
title = issue["title"]
|
||||||
content = issue["content"]
|
body = issue["body"]
|
||||||
kind = metadata["kind"]
|
kind = _get_kind(issue)
|
||||||
status = issue["status"]
|
status = issue["state"]
|
||||||
id = issue["local_id"]
|
number = issue["number"]
|
||||||
link = "https://bitbucket.org/pytest-dev/pytest/issue/%s/" % id
|
link = "https://github.com/pytest-dev/pytest/issues/%s/" % number
|
||||||
print("----")
|
print("----")
|
||||||
print(status, kind, link)
|
print(status, kind, link)
|
||||||
print(title)
|
print(title)
|
||||||
#print()
|
#print()
|
||||||
#lines = content.split("\n")
|
#lines = body.split("\n")
|
||||||
#print ("\n".join(lines[:3]))
|
#print ("\n".join(lines[:3]))
|
||||||
#if len(lines) > 3 or len(content) > 240:
|
#if len(lines) > 3 or len(body) > 240:
|
||||||
# print ("...")
|
# print ("...")
|
||||||
|
print("\n\nFound %s open issues" % len(issues))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import argparse
|
import argparse
|
||||||
|
@ -72,3 +81,4 @@ if __name__ == "__main__":
|
||||||
help="cache file")
|
help="cache file")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
main(args)
|
main(args)
|
||||||
|
|
||||||
|
|
2
setup.py
2
setup.py
|
@ -37,7 +37,7 @@ def has_environment_marker_support():
|
||||||
|
|
||||||
References:
|
References:
|
||||||
|
|
||||||
* https://wheel.readthedocs.org/en/latest/index.html#defining-conditional-dependencies
|
* https://wheel.readthedocs.io/en/latest/index.html#defining-conditional-dependencies
|
||||||
* https://www.python.org/dev/peps/pep-0426/#environment-markers
|
* https://www.python.org/dev/peps/pep-0426/#environment-markers
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -671,6 +671,11 @@ class TestDurations:
|
||||||
"*call*test_1*",
|
"*call*test_1*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_with_not(self, testdir):
|
||||||
|
testdir.makepyfile(self.source)
|
||||||
|
result = testdir.runpytest("-k not 1")
|
||||||
|
assert result.ret == 0
|
||||||
|
|
||||||
|
|
||||||
class TestDurationWithFixture:
|
class TestDurationWithFixture:
|
||||||
source = """
|
source = """
|
||||||
|
|
|
@ -4,8 +4,11 @@ import operator
|
||||||
import _pytest
|
import _pytest
|
||||||
import py
|
import py
|
||||||
import pytest
|
import pytest
|
||||||
from _pytest._code.code import (FormattedExcinfo, ReprExceptionInfo,
|
from _pytest._code.code import (
|
||||||
ExceptionChainRepr)
|
ExceptionInfo,
|
||||||
|
FormattedExcinfo,
|
||||||
|
ReprExceptionInfo,
|
||||||
|
ExceptionChainRepr)
|
||||||
|
|
||||||
queue = py.builtin._tryimport('queue', 'Queue')
|
queue = py.builtin._tryimport('queue', 'Queue')
|
||||||
|
|
||||||
|
@ -1048,3 +1051,18 @@ raise ValueError()
|
||||||
assert tw.lines[40] == "E AttributeError"
|
assert tw.lines[40] == "E AttributeError"
|
||||||
assert tw.lines[41] == ""
|
assert tw.lines[41] == ""
|
||||||
assert tw.lines[42].endswith("mod.py:15: AttributeError")
|
assert tw.lines[42].endswith("mod.py:15: AttributeError")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize("style", ["short", "long"])
|
||||||
|
@pytest.mark.parametrize("encoding", [None, "utf8", "utf16"])
|
||||||
|
def test_repr_traceback_with_unicode(style, encoding):
|
||||||
|
msg = u'☹'
|
||||||
|
if encoding is not None:
|
||||||
|
msg = msg.encode(encoding)
|
||||||
|
try:
|
||||||
|
raise RuntimeError(msg)
|
||||||
|
except RuntimeError:
|
||||||
|
e_info = ExceptionInfo()
|
||||||
|
formatter = FormattedExcinfo(style=style)
|
||||||
|
repr_traceback = formatter.repr_traceback(e_info)
|
||||||
|
assert repr_traceback is not None
|
||||||
|
|
|
@ -79,7 +79,7 @@ class TestParseIni:
|
||||||
""")
|
""")
|
||||||
result = testdir.inline_run("--confcutdir=.")
|
result = testdir.inline_run("--confcutdir=.")
|
||||||
assert result.ret == 0
|
assert result.ret == 0
|
||||||
|
|
||||||
class TestConfigCmdlineParsing:
|
class TestConfigCmdlineParsing:
|
||||||
def test_parsing_again_fails(self, testdir):
|
def test_parsing_again_fails(self, testdir):
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
|
@ -101,6 +101,16 @@ class TestConfigCmdlineParsing:
|
||||||
config = testdir.parseconfig("-c", "custom.cfg")
|
config = testdir.parseconfig("-c", "custom.cfg")
|
||||||
assert config.getini("custom") == "1"
|
assert config.getini("custom") == "1"
|
||||||
|
|
||||||
|
def test_absolute_win32_path(self, testdir):
|
||||||
|
temp_cfg_file = testdir.makefile(".cfg", custom="""
|
||||||
|
[pytest]
|
||||||
|
addopts = --version
|
||||||
|
""")
|
||||||
|
from os.path import normpath
|
||||||
|
temp_cfg_file = normpath(str(temp_cfg_file))
|
||||||
|
ret = pytest.main("-c " + temp_cfg_file)
|
||||||
|
assert ret == _pytest.main.EXIT_OK
|
||||||
|
|
||||||
class TestConfigAPI:
|
class TestConfigAPI:
|
||||||
def test_config_trace(self, testdir):
|
def test_config_trace(self, testdir):
|
||||||
config = testdir.parseconfig()
|
config = testdir.parseconfig()
|
||||||
|
|
|
@ -100,7 +100,7 @@ class TestPython:
|
||||||
result, dom = runandparse(testdir)
|
result, dom = runandparse(testdir)
|
||||||
assert result.ret
|
assert result.ret
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
node.assert_attr(name="pytest", errors=0, failures=1, skips=3, tests=2)
|
node.assert_attr(name="pytest", errors=0, failures=1, skips=3, tests=5)
|
||||||
|
|
||||||
def test_timing_function(self, testdir):
|
def test_timing_function(self, testdir):
|
||||||
testdir.makepyfile("""
|
testdir.makepyfile("""
|
||||||
|
@ -304,7 +304,7 @@ class TestPython:
|
||||||
result, dom = runandparse(testdir)
|
result, dom = runandparse(testdir)
|
||||||
assert not result.ret
|
assert not result.ret
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
node.assert_attr(skips=1, tests=0)
|
node.assert_attr(skips=1, tests=1)
|
||||||
tnode = node.find_first_by_tag("testcase")
|
tnode = node.find_first_by_tag("testcase")
|
||||||
tnode.assert_attr(
|
tnode.assert_attr(
|
||||||
file="test_xfailure_function.py",
|
file="test_xfailure_function.py",
|
||||||
|
@ -325,7 +325,7 @@ class TestPython:
|
||||||
result, dom = runandparse(testdir)
|
result, dom = runandparse(testdir)
|
||||||
# assert result.ret
|
# assert result.ret
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
node.assert_attr(skips=1, tests=0)
|
node.assert_attr(skips=1, tests=1)
|
||||||
tnode = node.find_first_by_tag("testcase")
|
tnode = node.find_first_by_tag("testcase")
|
||||||
tnode.assert_attr(
|
tnode.assert_attr(
|
||||||
file="test_xfailure_xpass.py",
|
file="test_xfailure_xpass.py",
|
||||||
|
@ -356,7 +356,7 @@ class TestPython:
|
||||||
result, dom = runandparse(testdir)
|
result, dom = runandparse(testdir)
|
||||||
assert result.ret == EXIT_NOTESTSCOLLECTED
|
assert result.ret == EXIT_NOTESTSCOLLECTED
|
||||||
node = dom.find_first_by_tag("testsuite")
|
node = dom.find_first_by_tag("testsuite")
|
||||||
node.assert_attr(skips=1, tests=0)
|
node.assert_attr(skips=1, tests=1)
|
||||||
tnode = node.find_first_by_tag("testcase")
|
tnode = node.find_first_by_tag("testcase")
|
||||||
tnode.assert_attr(
|
tnode.assert_attr(
|
||||||
file="test_collect_skipped.py",
|
file="test_collect_skipped.py",
|
||||||
|
|
|
@ -539,6 +539,19 @@ class TestSkip:
|
||||||
"*1 passed*2 skipped*",
|
"*1 passed*2 skipped*",
|
||||||
])
|
])
|
||||||
|
|
||||||
|
def test_strict_and_skip(self, testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import pytest
|
||||||
|
@pytest.mark.skip
|
||||||
|
def test_hello():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
result = testdir.runpytest("-rs --strict")
|
||||||
|
result.stdout.fnmatch_lines([
|
||||||
|
"*unconditional skip*",
|
||||||
|
"*1 skipped*",
|
||||||
|
])
|
||||||
|
|
||||||
class TestSkipif:
|
class TestSkipif:
|
||||||
def test_skipif_conditional(self, testdir):
|
def test_skipif_conditional(self, testdir):
|
||||||
item = testdir.getitem("""
|
item = testdir.getitem("""
|
||||||
|
@ -812,7 +825,7 @@ def test_default_markers(testdir):
|
||||||
result = testdir.runpytest("--markers")
|
result = testdir.runpytest("--markers")
|
||||||
result.stdout.fnmatch_lines([
|
result.stdout.fnmatch_lines([
|
||||||
"*skipif(*condition)*skip*",
|
"*skipif(*condition)*skip*",
|
||||||
"*xfail(*condition, reason=None, run=True, raises=None)*expected failure*",
|
"*xfail(*condition, reason=None, run=True, raises=None, strict=False)*expected failure*",
|
||||||
])
|
])
|
||||||
|
|
||||||
def test_xfail_test_setup_exception(testdir):
|
def test_xfail_test_setup_exception(testdir):
|
||||||
|
|
3
tox.ini
3
tox.ini
|
@ -17,10 +17,11 @@ deps=
|
||||||
|
|
||||||
[testenv:py26]
|
[testenv:py26]
|
||||||
commands= py.test --lsof -rfsxX {posargs:testing}
|
commands= py.test --lsof -rfsxX {posargs:testing}
|
||||||
|
# pinning mock to last supported version for python 2.6
|
||||||
deps=
|
deps=
|
||||||
hypothesis<3.0
|
hypothesis<3.0
|
||||||
nose
|
nose
|
||||||
mock<1.1 # last supported version for py26
|
mock<1.1
|
||||||
|
|
||||||
[testenv:py27-subprocess]
|
[testenv:py27-subprocess]
|
||||||
changedir=.
|
changedir=.
|
||||||
|
|
Loading…
Reference in New Issue