Merge pull request #4100 from nicoddemus/merge-master-into-features
Merge master into features
This commit is contained in:
commit
943bbdd8ce
|
@ -7,13 +7,13 @@ repos:
|
||||||
args: [--safe, --quiet]
|
args: [--safe, --quiet]
|
||||||
language_version: python3
|
language_version: python3
|
||||||
- repo: https://github.com/asottile/blacken-docs
|
- repo: https://github.com/asottile/blacken-docs
|
||||||
rev: v0.2.0
|
rev: v0.3.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: blacken-docs
|
- id: blacken-docs
|
||||||
additional_dependencies: [black==18.6b4]
|
additional_dependencies: [black==18.9b0]
|
||||||
language_version: python3
|
language_version: python3
|
||||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
rev: v1.3.0
|
rev: v1.4.0-1
|
||||||
hooks:
|
hooks:
|
||||||
- id: trailing-whitespace
|
- id: trailing-whitespace
|
||||||
- id: end-of-file-fixer
|
- id: end-of-file-fixer
|
||||||
|
@ -22,11 +22,12 @@ repos:
|
||||||
exclude: _pytest/debugging.py
|
exclude: _pytest/debugging.py
|
||||||
- id: flake8
|
- id: flake8
|
||||||
- repo: https://github.com/asottile/pyupgrade
|
- repo: https://github.com/asottile/pyupgrade
|
||||||
rev: v1.2.0
|
rev: v1.8.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: pyupgrade
|
- id: pyupgrade
|
||||||
|
args: [--keep-percent-format]
|
||||||
- repo: https://github.com/pre-commit/pygrep-hooks
|
- repo: https://github.com/pre-commit/pygrep-hooks
|
||||||
rev: v1.0.0
|
rev: v1.1.0
|
||||||
hooks:
|
hooks:
|
||||||
- id: rst-backticks
|
- id: rst-backticks
|
||||||
- repo: local
|
- repo: local
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
|
|
||||||
|
|
||||||
# 10000 iterations, just for relative comparison
|
# 10000 iterations, just for relative comparison
|
||||||
# 2.7.5 3.3.2
|
# 2.7.5 3.3.2
|
||||||
# FilesCompleter 75.1109 69.2116
|
# FilesCompleter 75.1109 69.2116
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Improve error message when test functions of ``unittest.TestCase`` subclasses use a parametrized fixture.
|
|
@ -0,0 +1 @@
|
||||||
|
``request.fixturenames`` now correctly returns the name of fixtures created by ``request.getfixturevalue()``.
|
|
@ -0,0 +1 @@
|
||||||
|
Update fixture documentation to specify that a fixture can be invoked twice in the scope it's defined for.
|
|
@ -0,0 +1 @@
|
||||||
|
According to unittest.rst, setUpModule and tearDownModule were not implemented, but it turns out they are. So updated the documentation for unittest.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix source reindenting by using ``textwrap.dedent`` directly.
|
|
@ -20,8 +20,7 @@ Thanks to all who contributed to this release, among them:
|
||||||
* Ondřej Súkup
|
* Ondřej Súkup
|
||||||
* Ronny Pfannschmidt
|
* Ronny Pfannschmidt
|
||||||
* T.E.A de Souza
|
* T.E.A de Souza
|
||||||
* Victor
|
* Victor Maryama
|
||||||
* victor
|
|
||||||
|
|
||||||
|
|
||||||
Happy testing,
|
Happy testing,
|
||||||
|
|
|
@ -22,10 +22,9 @@ Thanks to all who contributed to this release, among them:
|
||||||
* Ronny Pfannschmidt
|
* Ronny Pfannschmidt
|
||||||
* Sankt Petersbug
|
* Sankt Petersbug
|
||||||
* Tyler Richard
|
* Tyler Richard
|
||||||
* Victor
|
* Victor Maryama
|
||||||
* Vlad Shcherbina
|
* Vlad Shcherbina
|
||||||
* turturica
|
* turturica
|
||||||
* victor
|
|
||||||
* wim glenn
|
* wim glenn
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -245,9 +245,9 @@ class TestCustomAssertMsg(object):
|
||||||
a = 1
|
a = 1
|
||||||
|
|
||||||
b = 2
|
b = 2
|
||||||
assert (
|
assert A.a == b, (
|
||||||
A.a == b
|
"A.a appears not to be b\n" "or does not appear to be b\none of those"
|
||||||
), "A.a appears not to be b\n" "or does not appear to be b\none of those"
|
)
|
||||||
|
|
||||||
def test_custom_repr(self):
|
def test_custom_repr(self):
|
||||||
class JSON(object):
|
class JSON(object):
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
hello = "world"
|
hello = "world"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import py
|
import py
|
||||||
|
|
||||||
failure_demo = py.path.local(__file__).dirpath("failure_demo.py")
|
failure_demo = py.path.local(__file__).dirpath("failure_demo.py")
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -852,6 +852,8 @@ In that order.
|
||||||
can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting
|
can be changed between releases (even bug fixes) so it shouldn't be relied on for scripting
|
||||||
or automation.
|
or automation.
|
||||||
|
|
||||||
|
.. _freezing-pytest:
|
||||||
|
|
||||||
Freezing pytest
|
Freezing pytest
|
||||||
---------------
|
---------------
|
||||||
|
|
||||||
|
|
|
@ -259,6 +259,11 @@ instance, you can simply declare it:
|
||||||
|
|
||||||
Finally, the ``class`` scope will invoke the fixture once per test *class*.
|
Finally, the ``class`` scope will invoke the fixture once per test *class*.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
Pytest will only cache one instance of a fixture at a time.
|
||||||
|
This means that when using a parametrized fixture, pytest may invoke a fixture more than once in the given scope.
|
||||||
|
|
||||||
|
|
||||||
``package`` scope (experimental)
|
``package`` scope (experimental)
|
||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
@ -69,17 +69,15 @@ You may also discover more plugins through a `pytest- pypi.python.org search`_.
|
||||||
Requiring/Loading plugins in a test module or conftest file
|
Requiring/Loading plugins in a test module or conftest file
|
||||||
-----------------------------------------------------------
|
-----------------------------------------------------------
|
||||||
|
|
||||||
You can require plugins in a test module or a conftest file like this::
|
You can require plugins in a test module or a conftest file like this:
|
||||||
|
|
||||||
pytest_plugins = "myapp.testsupport.myplugin",
|
.. code-block:: python
|
||||||
|
|
||||||
|
pytest_plugins = ("myapp.testsupport.myplugin",)
|
||||||
|
|
||||||
When the test module or conftest plugin is loaded the specified plugins
|
When the test module or conftest plugin is loaded the specified plugins
|
||||||
will be loaded as well.
|
will be loaded as well.
|
||||||
|
|
||||||
pytest_plugins = "myapp.testsupport.myplugin"
|
|
||||||
|
|
||||||
which will import the specified module as a ``pytest`` plugin.
|
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
Requiring plugins using a ``pytest_plugins`` variable in non-root
|
Requiring plugins using a ``pytest_plugins`` variable in non-root
|
||||||
``conftest.py`` files is deprecated. See
|
``conftest.py`` files is deprecated. See
|
||||||
|
|
|
@ -84,6 +84,12 @@ pytest.warns
|
||||||
.. autofunction:: pytest.warns(expected_warning: Exception, [match])
|
.. autofunction:: pytest.warns(expected_warning: Exception, [match])
|
||||||
:with:
|
:with:
|
||||||
|
|
||||||
|
pytest.freeze_includes
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
**Tutorial**: :ref:`freezing-pytest`.
|
||||||
|
|
||||||
|
.. autofunction:: pytest.freeze_includes
|
||||||
|
|
||||||
.. _`marks ref`:
|
.. _`marks ref`:
|
||||||
|
|
||||||
|
|
|
@ -22,16 +22,15 @@ Almost all ``unittest`` features are supported:
|
||||||
|
|
||||||
* ``@unittest.skip`` style decorators;
|
* ``@unittest.skip`` style decorators;
|
||||||
* ``setUp/tearDown``;
|
* ``setUp/tearDown``;
|
||||||
* ``setUpClass/tearDownClass()``;
|
* ``setUpClass/tearDownClass``;
|
||||||
|
* ``setUpModule/tearDownModule``;
|
||||||
|
|
||||||
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
|
.. _`load_tests protocol`: https://docs.python.org/3/library/unittest.html#load-tests-protocol
|
||||||
.. _`setUpModule/tearDownModule`: https://docs.python.org/3/library/unittest.html#setupmodule-and-teardownmodule
|
|
||||||
.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
|
.. _`subtests`: https://docs.python.org/3/library/unittest.html#distinguishing-test-iterations-using-subtests
|
||||||
|
|
||||||
Up to this point pytest does not have support for the following features:
|
Up to this point pytest does not have support for the following features:
|
||||||
|
|
||||||
* `load_tests protocol`_;
|
* `load_tests protocol`_;
|
||||||
* `setUpModule/tearDownModule`_;
|
|
||||||
* `subtests`_;
|
* `subtests`_;
|
||||||
|
|
||||||
Benefits out of the box
|
Benefits out of the box
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
"""allow bash-completion for argparse with argcomplete if installed
|
"""allow bash-completion for argparse with argcomplete if installed
|
||||||
needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
|
needs argcomplete>=0.5.6 for python 3.2/3.3 (older versions fail
|
||||||
to find the magic string, so _ARGCOMPLETE env. var is never set, and
|
to find the magic string, so _ARGCOMPLETE env. var is never set, and
|
||||||
|
|
|
@ -7,6 +7,7 @@ import linecache
|
||||||
import sys
|
import sys
|
||||||
import six
|
import six
|
||||||
import inspect
|
import inspect
|
||||||
|
import textwrap
|
||||||
import tokenize
|
import tokenize
|
||||||
import py
|
import py
|
||||||
|
|
||||||
|
@ -23,7 +24,6 @@ class Source(object):
|
||||||
def __init__(self, *parts, **kwargs):
|
def __init__(self, *parts, **kwargs):
|
||||||
self.lines = lines = []
|
self.lines = lines = []
|
||||||
de = kwargs.get("deindent", True)
|
de = kwargs.get("deindent", True)
|
||||||
rstrip = kwargs.get("rstrip", True)
|
|
||||||
for part in parts:
|
for part in parts:
|
||||||
if not part:
|
if not part:
|
||||||
partlines = []
|
partlines = []
|
||||||
|
@ -33,11 +33,6 @@ class Source(object):
|
||||||
partlines = [x.rstrip("\n") for x in part]
|
partlines = [x.rstrip("\n") for x in part]
|
||||||
elif isinstance(part, six.string_types):
|
elif isinstance(part, six.string_types):
|
||||||
partlines = part.split("\n")
|
partlines = part.split("\n")
|
||||||
if rstrip:
|
|
||||||
while partlines:
|
|
||||||
if partlines[-1].strip():
|
|
||||||
break
|
|
||||||
partlines.pop()
|
|
||||||
else:
|
else:
|
||||||
partlines = getsource(part, deindent=de).lines
|
partlines = getsource(part, deindent=de).lines
|
||||||
if de:
|
if de:
|
||||||
|
@ -115,17 +110,10 @@ class Source(object):
|
||||||
ast, start, end = getstatementrange_ast(lineno, self)
|
ast, start, end = getstatementrange_ast(lineno, self)
|
||||||
return start, end
|
return start, end
|
||||||
|
|
||||||
def deindent(self, offset=None):
|
def deindent(self):
|
||||||
""" return a new source object deindented by offset.
|
"""return a new source object deindented."""
|
||||||
If offset is None then guess an indentation offset from
|
|
||||||
the first non-blank line. Subsequent lines which have a
|
|
||||||
lower indentation offset will be copied verbatim as
|
|
||||||
they are assumed to be part of multilines.
|
|
||||||
"""
|
|
||||||
# XXX maybe use the tokenizer to properly handle multiline
|
|
||||||
# strings etc.pp?
|
|
||||||
newsource = Source()
|
newsource = Source()
|
||||||
newsource.lines[:] = deindent(self.lines, offset)
|
newsource.lines[:] = deindent(self.lines)
|
||||||
return newsource
|
return newsource
|
||||||
|
|
||||||
def isparseable(self, deindent=True):
|
def isparseable(self, deindent=True):
|
||||||
|
@ -268,47 +256,8 @@ def getsource(obj, **kwargs):
|
||||||
return Source(strsrc, **kwargs)
|
return Source(strsrc, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def deindent(lines, offset=None):
|
def deindent(lines):
|
||||||
if offset is None:
|
return textwrap.dedent("\n".join(lines)).splitlines()
|
||||||
for line in lines:
|
|
||||||
line = line.expandtabs()
|
|
||||||
s = line.lstrip()
|
|
||||||
if s:
|
|
||||||
offset = len(line) - len(s)
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
offset = 0
|
|
||||||
if offset == 0:
|
|
||||||
return list(lines)
|
|
||||||
newlines = []
|
|
||||||
|
|
||||||
def readline_generator(lines):
|
|
||||||
for line in lines:
|
|
||||||
yield line + "\n"
|
|
||||||
|
|
||||||
it = readline_generator(lines)
|
|
||||||
|
|
||||||
try:
|
|
||||||
for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(
|
|
||||||
lambda: next(it)
|
|
||||||
):
|
|
||||||
if sline > len(lines):
|
|
||||||
break # End of input reached
|
|
||||||
if sline > len(newlines):
|
|
||||||
line = lines[sline - 1].expandtabs()
|
|
||||||
if line.lstrip() and line[:offset].isspace():
|
|
||||||
line = line[offset:] # Deindent
|
|
||||||
newlines.append(line)
|
|
||||||
|
|
||||||
for i in range(sline, eline):
|
|
||||||
# Don't deindent continuing lines of
|
|
||||||
# multiline tokens (i.e. multiline strings)
|
|
||||||
newlines.append(lines[i])
|
|
||||||
except (IndentationError, tokenize.TokenError):
|
|
||||||
pass
|
|
||||||
# Add any lines we didn't see. E.g. if an exception was raised.
|
|
||||||
newlines.extend(lines[len(newlines) :])
|
|
||||||
return newlines
|
|
||||||
|
|
||||||
|
|
||||||
def get_statement_startend2(lineno, node):
|
def get_statement_startend2(lineno, node):
|
||||||
|
|
|
@ -359,8 +359,10 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fixturenames(self):
|
def fixturenames(self):
|
||||||
# backward incompatible note: now a readonly property
|
"""names of all active fixtures in this request"""
|
||||||
return list(self._pyfuncitem._fixtureinfo.names_closure)
|
result = list(self._pyfuncitem._fixtureinfo.names_closure)
|
||||||
|
result.extend(set(self._fixture_defs).difference(result))
|
||||||
|
return result
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def node(self):
|
def node(self):
|
||||||
|
@ -565,7 +567,20 @@ 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:
|
has_params = fixturedef.params is not None
|
||||||
|
fixtures_not_supported = getattr(funcitem, "nofuncargs", False)
|
||||||
|
if has_params and fixtures_not_supported:
|
||||||
|
msg = (
|
||||||
|
"{name} does not support fixtures, maybe unittest.TestCase subclass?\n"
|
||||||
|
"Node id: {nodeid}\n"
|
||||||
|
"Function type: {typename}"
|
||||||
|
).format(
|
||||||
|
name=funcitem.name,
|
||||||
|
nodeid=funcitem.nodeid,
|
||||||
|
typename=type(funcitem).__name__,
|
||||||
|
)
|
||||||
|
fail(msg)
|
||||||
|
if has_params:
|
||||||
frame = inspect.stack()[3]
|
frame = inspect.stack()[3]
|
||||||
frameinfo = inspect.getframeinfo(frame[0])
|
frameinfo = inspect.getframeinfo(frame[0])
|
||||||
source_path = frameinfo.filename
|
source_path = frameinfo.filename
|
||||||
|
@ -574,9 +589,11 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
if source_path.relto(funcitem.config.rootdir):
|
if source_path.relto(funcitem.config.rootdir):
|
||||||
source_path = source_path.relto(funcitem.config.rootdir)
|
source_path = source_path.relto(funcitem.config.rootdir)
|
||||||
msg = (
|
msg = (
|
||||||
"The requested fixture has no parameter defined for the "
|
"The requested fixture has no parameter defined for test:\n"
|
||||||
"current test.\n\nRequested fixture '{}' defined in:\n{}"
|
" {}\n\n"
|
||||||
|
"Requested fixture '{}' defined in:\n{}"
|
||||||
"\n\nRequested here:\n{}:{}".format(
|
"\n\nRequested here:\n{}:{}".format(
|
||||||
|
funcitem.nodeid,
|
||||||
fixturedef.argname,
|
fixturedef.argname,
|
||||||
getlocation(fixturedef.func, funcitem.config.rootdir),
|
getlocation(fixturedef.func, funcitem.config.rootdir),
|
||||||
source_path,
|
source_path,
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Junit(py.xml.Namespace):
|
||||||
# this dynamically instead of hardcoding it. The spec range of valid
|
# this dynamically instead of hardcoding it. The spec range of valid
|
||||||
# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
|
# chars is: Char ::= #x9 | #xA | #xD | [#x20-#xD7FF] | [#xE000-#xFFFD]
|
||||||
# | [#x10000-#x10FFFF]
|
# | [#x10000-#x10FFFF]
|
||||||
_legal_chars = (0x09, 0x0A, 0x0d)
|
_legal_chars = (0x09, 0x0A, 0x0D)
|
||||||
_legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF))
|
_legal_ranges = ((0x20, 0x7E), (0x80, 0xD7FF), (0xE000, 0xFFFD), (0x10000, 0x10FFFF))
|
||||||
_legal_xml_re = [
|
_legal_xml_re = [
|
||||||
unicode("%s-%s") % (unichr(low), unichr(high))
|
unicode("%s-%s") % (unichr(low), unichr(high))
|
||||||
|
|
|
@ -213,7 +213,8 @@ class LogCaptureFixture(object):
|
||||||
def __init__(self, item):
|
def __init__(self, item):
|
||||||
"""Creates a new funcarg."""
|
"""Creates a new funcarg."""
|
||||||
self._item = item
|
self._item = item
|
||||||
self._initial_log_levels = {} # type: Dict[str, int] # dict of log name -> log level
|
# dict of log name -> log level
|
||||||
|
self._initial_log_levels = {} # type: Dict[str, int]
|
||||||
|
|
||||||
def _finalize(self):
|
def _finalize(self):
|
||||||
"""Finalizes the fixture.
|
"""Finalizes the fixture.
|
||||||
|
|
|
@ -90,7 +90,10 @@ class MarkEvaluator(object):
|
||||||
else:
|
else:
|
||||||
if "reason" not in mark.kwargs:
|
if "reason" not in mark.kwargs:
|
||||||
# XXX better be checked at collection time
|
# XXX better be checked at collection time
|
||||||
msg = "you need to specify reason=STRING " "when using booleans as conditions."
|
msg = (
|
||||||
|
"you need to specify reason=STRING "
|
||||||
|
"when using booleans as conditions."
|
||||||
|
)
|
||||||
fail(msg)
|
fail(msg)
|
||||||
result = bool(expr)
|
result = bool(expr)
|
||||||
if result:
|
if result:
|
||||||
|
|
|
@ -31,6 +31,14 @@ failsonjython = pytest.mark.xfail("sys.platform.startswith('java')")
|
||||||
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
|
pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3]))
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def limited_recursion_depth():
|
||||||
|
before = sys.getrecursionlimit()
|
||||||
|
sys.setrecursionlimit(150)
|
||||||
|
yield
|
||||||
|
sys.setrecursionlimit(before)
|
||||||
|
|
||||||
|
|
||||||
class TWMock(object):
|
class TWMock(object):
|
||||||
WRITE = object()
|
WRITE = object()
|
||||||
|
|
||||||
|
@ -239,7 +247,7 @@ class TestTraceback_f_g_h(object):
|
||||||
raise RuntimeError("hello")
|
raise RuntimeError("hello")
|
||||||
f(n - 1)
|
f(n - 1)
|
||||||
|
|
||||||
excinfo = pytest.raises(RuntimeError, f, 100)
|
excinfo = pytest.raises(RuntimeError, f, 25)
|
||||||
monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
|
monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex")
|
||||||
repr = excinfo.getrepr()
|
repr = excinfo.getrepr()
|
||||||
assert "RuntimeError: hello" in str(repr.reprcrash)
|
assert "RuntimeError: hello" in str(repr.reprcrash)
|
||||||
|
@ -1341,11 +1349,13 @@ def test_cwd_deleted(testdir):
|
||||||
assert "INTERNALERROR" not in result.stdout.str() + result.stderr.str()
|
assert "INTERNALERROR" not in result.stdout.str() + result.stderr.str()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("limited_recursion_depth")
|
||||||
def test_exception_repr_extraction_error_on_recursion():
|
def test_exception_repr_extraction_error_on_recursion():
|
||||||
"""
|
"""
|
||||||
Ensure we can properly detect a recursion error even
|
Ensure we can properly detect a recursion error even
|
||||||
if some locals raise error on comparison (#2459).
|
if some locals raise error on comparison (#2459).
|
||||||
"""
|
"""
|
||||||
|
from _pytest.pytester import LineMatcher
|
||||||
|
|
||||||
class numpy_like(object):
|
class numpy_like(object):
|
||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
|
@ -1361,40 +1371,30 @@ def test_exception_repr_extraction_error_on_recursion():
|
||||||
def b(x):
|
def b(x):
|
||||||
return a(numpy_like())
|
return a(numpy_like())
|
||||||
|
|
||||||
try:
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
a(numpy_like())
|
a(numpy_like())
|
||||||
except: # noqa
|
|
||||||
from _pytest._code.code import ExceptionInfo
|
|
||||||
from _pytest.pytester import LineMatcher
|
|
||||||
|
|
||||||
exc_info = ExceptionInfo()
|
matcher = LineMatcher(str(excinfo.getrepr()).splitlines())
|
||||||
|
matcher.fnmatch_lines(
|
||||||
matcher = LineMatcher(str(exc_info.getrepr()).splitlines())
|
[
|
||||||
matcher.fnmatch_lines(
|
"!!! Recursion error detected, but an error occurred locating the origin of recursion.",
|
||||||
[
|
"*The following exception happened*",
|
||||||
"!!! Recursion error detected, but an error occurred locating the origin of recursion.",
|
"*ValueError: The truth value of an array*",
|
||||||
"*The following exception happened*",
|
]
|
||||||
"*ValueError: The truth value of an array*",
|
)
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("limited_recursion_depth")
|
||||||
def test_no_recursion_index_on_recursion_error():
|
def test_no_recursion_index_on_recursion_error():
|
||||||
"""
|
"""
|
||||||
Ensure that we don't break in case we can't find the recursion index
|
Ensure that we don't break in case we can't find the recursion index
|
||||||
during a recursion error (#2486).
|
during a recursion error (#2486).
|
||||||
"""
|
"""
|
||||||
try:
|
|
||||||
|
|
||||||
class RecursionDepthError(object):
|
class RecursionDepthError(object):
|
||||||
def __getattr__(self, attr):
|
def __getattr__(self, attr):
|
||||||
return getattr(self, "_" + attr)
|
return getattr(self, "_" + attr)
|
||||||
|
|
||||||
|
with pytest.raises(RuntimeError) as excinfo:
|
||||||
RecursionDepthError().trigger
|
RecursionDepthError().trigger
|
||||||
except: # noqa
|
assert "maximum recursion" in str(excinfo.getrepr())
|
||||||
from _pytest._code.code import ExceptionInfo
|
|
||||||
|
|
||||||
exc_info = ExceptionInfo()
|
|
||||||
assert "maximum recursion" in str(exc_info.getrepr())
|
|
||||||
else:
|
|
||||||
assert 0
|
|
||||||
|
|
|
@ -27,16 +27,7 @@ def test_source_str_function():
|
||||||
x = Source(
|
x = Source(
|
||||||
"""
|
"""
|
||||||
3
|
3
|
||||||
""",
|
|
||||||
rstrip=False,
|
|
||||||
)
|
|
||||||
assert str(x) == "\n3\n "
|
|
||||||
|
|
||||||
x = Source(
|
|
||||||
"""
|
"""
|
||||||
3
|
|
||||||
""",
|
|
||||||
rstrip=True,
|
|
||||||
)
|
)
|
||||||
assert str(x) == "\n3"
|
assert str(x) == "\n3"
|
||||||
|
|
||||||
|
@ -400,10 +391,13 @@ def test_getfuncsource_with_multine_string():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert (
|
expected = '''\
|
||||||
str(_pytest._code.Source(f)).strip()
|
def f():
|
||||||
== 'def f():\n c = """while True:\n pass\n"""'
|
c = """while True:
|
||||||
)
|
pass
|
||||||
|
"""
|
||||||
|
'''
|
||||||
|
assert str(_pytest._code.Source(f)) == expected.rstrip()
|
||||||
|
|
||||||
|
|
||||||
def test_deindent():
|
def test_deindent():
|
||||||
|
@ -411,21 +405,13 @@ def test_deindent():
|
||||||
|
|
||||||
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
|
assert deindent(["\tfoo", "\tbar"]) == ["foo", "bar"]
|
||||||
|
|
||||||
def f():
|
source = """\
|
||||||
c = """while True:
|
|
||||||
pass
|
|
||||||
"""
|
|
||||||
|
|
||||||
lines = deindent(inspect.getsource(f).splitlines())
|
|
||||||
assert lines == ["def f():", ' c = """while True:', " pass", '"""']
|
|
||||||
|
|
||||||
source = """
|
|
||||||
def f():
|
def f():
|
||||||
def g():
|
def g():
|
||||||
pass
|
pass
|
||||||
"""
|
"""
|
||||||
lines = deindent(source.splitlines())
|
lines = deindent(source.splitlines())
|
||||||
assert lines == ["", "def f():", " def g():", " pass", " "]
|
assert lines == ["def f():", " def g():", " pass"]
|
||||||
|
|
||||||
|
|
||||||
def test_source_of_class_at_eof_without_newline(tmpdir):
|
def test_source_of_class_at_eof_without_newline(tmpdir):
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dynamic():
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def a(request):
|
||||||
|
request.getfixturevalue("dynamic")
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def b(a):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def test(b, request):
|
||||||
|
assert request.fixturenames == ["b", "request", "a", "dynamic"]
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import pprint
|
import pprint
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import pytest
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(params=[1, 2])
|
||||||
|
def two(request):
|
||||||
|
return request.param
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.usefixtures("two")
|
||||||
|
class TestSomethingElse(unittest.TestCase):
|
||||||
|
def test_two(self):
|
||||||
|
pass
|
|
@ -45,7 +45,7 @@ def test_change_level_undo(testdir):
|
||||||
assert 0
|
assert 0
|
||||||
"""
|
"""
|
||||||
)
|
)
|
||||||
result = testdir.runpytest_subprocess()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"])
|
result.stdout.fnmatch_lines(["*log from test1*", "*2 failed in *"])
|
||||||
assert "log from test2" not in result.stdout.str()
|
assert "log from test2" not in result.stdout.str()
|
||||||
|
|
||||||
|
|
|
@ -62,11 +62,11 @@ class TestApprox(object):
|
||||||
@pytest.mark.parametrize(
|
@pytest.mark.parametrize(
|
||||||
"value, repr_string",
|
"value, repr_string",
|
||||||
[
|
[
|
||||||
(5., "approx(5.0 {pm} 5.0e-06)"),
|
(5.0, "approx(5.0 {pm} 5.0e-06)"),
|
||||||
([5.], "approx([5.0 {pm} 5.0e-06])"),
|
([5.0], "approx([5.0 {pm} 5.0e-06])"),
|
||||||
([[5.]], "approx([[5.0 {pm} 5.0e-06]])"),
|
([[5.0]], "approx([[5.0 {pm} 5.0e-06]])"),
|
||||||
([[5., 6.]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
|
([[5.0, 6.0]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
|
||||||
([[5.], [6.]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
|
([[5.0], [6.0]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
def test_repr_nd_array(self, plus_minus, value, repr_string):
|
def test_repr_nd_array(self, plus_minus, value, repr_string):
|
||||||
|
@ -354,16 +354,16 @@ class TestApprox(object):
|
||||||
Test all permutations of where the approx and np.array() can show up
|
Test all permutations of where the approx and np.array() can show up
|
||||||
"""
|
"""
|
||||||
np = pytest.importorskip("numpy")
|
np = pytest.importorskip("numpy")
|
||||||
expected = 100.
|
expected = 100.0
|
||||||
actual = 99.
|
actual = 99.0
|
||||||
abs_diff = expected - actual
|
abs_diff = expected - actual
|
||||||
rel_diff = (expected - actual) / expected
|
rel_diff = (expected - actual) / expected
|
||||||
|
|
||||||
tests = [
|
tests = [
|
||||||
(eq, abs_diff, 0),
|
(eq, abs_diff, 0),
|
||||||
(eq, 0, rel_diff),
|
(eq, 0, rel_diff),
|
||||||
(ne, 0, rel_diff / 2.), # rel diff fail
|
(ne, 0, rel_diff / 2.0), # rel diff fail
|
||||||
(ne, abs_diff / 2., 0), # abs diff fail
|
(ne, abs_diff / 2.0, 0), # abs diff fail
|
||||||
]
|
]
|
||||||
|
|
||||||
for op, _abs, _rel in tests:
|
for op, _abs, _rel in tests:
|
||||||
|
|
|
@ -756,6 +756,12 @@ class TestRequestBasic(object):
|
||||||
reprec = testdir.inline_run()
|
reprec = testdir.inline_run()
|
||||||
reprec.assertoutcome(passed=1)
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
|
def test_request_fixturenames_dynamic_fixture(self, testdir):
|
||||||
|
"""Regression test for #3057"""
|
||||||
|
testdir.copy_example("fixtures/test_getfixturevalue_dynamic.py")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines("*1 passed*")
|
||||||
|
|
||||||
def test_funcargnames_compatattr(self, testdir):
|
def test_funcargnames_compatattr(self, testdir):
|
||||||
testdir.makepyfile(
|
testdir.makepyfile(
|
||||||
"""
|
"""
|
||||||
|
@ -3602,7 +3608,8 @@ class TestParameterizedSubRequest(object):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
E*Failed: The requested fixture has no parameter defined for the current test.
|
E*Failed: The requested fixture has no parameter defined for test:
|
||||||
|
E* test_call_from_fixture.py::test_foo
|
||||||
E*
|
E*
|
||||||
E*Requested fixture 'fix_with_param' defined in:
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
E*test_call_from_fixture.py:4
|
E*test_call_from_fixture.py:4
|
||||||
|
@ -3628,7 +3635,8 @@ class TestParameterizedSubRequest(object):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
E*Failed: The requested fixture has no parameter defined for the current test.
|
E*Failed: The requested fixture has no parameter defined for test:
|
||||||
|
E* test_call_from_test.py::test_foo
|
||||||
E*
|
E*
|
||||||
E*Requested fixture 'fix_with_param' defined in:
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
E*test_call_from_test.py:4
|
E*test_call_from_test.py:4
|
||||||
|
@ -3658,7 +3666,8 @@ class TestParameterizedSubRequest(object):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
E*Failed: The requested fixture has no parameter defined for the current test.
|
E*Failed: The requested fixture has no parameter defined for test:
|
||||||
|
E* test_external_fixture.py::test_foo
|
||||||
E*
|
E*
|
||||||
E*Requested fixture 'fix_with_param' defined in:
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
E*conftest.py:4
|
E*conftest.py:4
|
||||||
|
@ -3701,7 +3710,8 @@ class TestParameterizedSubRequest(object):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
result.stdout.fnmatch_lines(
|
result.stdout.fnmatch_lines(
|
||||||
"""
|
"""
|
||||||
E*Failed: The requested fixture has no parameter defined for the current test.
|
E*Failed: The requested fixture has no parameter defined for test:
|
||||||
|
E* test_foos.py::test_foo
|
||||||
E*
|
E*
|
||||||
E*Requested fixture 'fix_with_param' defined in:
|
E*Requested fixture 'fix_with_param' defined in:
|
||||||
E*fix.py:4
|
E*fix.py:4
|
||||||
|
|
|
@ -177,7 +177,7 @@ def test_makepyfile_unicode(testdir):
|
||||||
unichr(65)
|
unichr(65)
|
||||||
except NameError:
|
except NameError:
|
||||||
unichr = chr
|
unichr = chr
|
||||||
testdir.makepyfile(unichr(0xfffd))
|
testdir.makepyfile(unichr(0xFFFD))
|
||||||
|
|
||||||
|
|
||||||
def test_makepyfile_utf8(testdir):
|
def test_makepyfile_utf8(testdir):
|
||||||
|
|
|
@ -1010,3 +1010,15 @@ def test_testcase_handles_init_exceptions(testdir):
|
||||||
result = testdir.runpytest()
|
result = testdir.runpytest()
|
||||||
assert "should raise this exception" in result.stdout.str()
|
assert "should raise this exception" in result.stdout.str()
|
||||||
assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str()
|
assert "ERROR at teardown of MyTestCase.test_hello" not in result.stdout.str()
|
||||||
|
|
||||||
|
|
||||||
|
def test_error_message_with_parametrized_fixtures(testdir):
|
||||||
|
testdir.copy_example("unittest/test_parametrized_fixture_error_message.py")
|
||||||
|
result = testdir.runpytest()
|
||||||
|
result.stdout.fnmatch_lines(
|
||||||
|
[
|
||||||
|
"*test_two does not support fixtures*",
|
||||||
|
"*TestSomethingElse::test_two",
|
||||||
|
"*Function type: TestCaseFunction",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue