Compare commits

...

36 Commits
3.7.0 ... 3.7.1

Author SHA1 Message Date
Bruno Oliveira
f7a3f45a18 Preparing release version 3.7.1 2018-08-02 12:04:39 +00:00
Anthony Sottile
4705fd2bbe Merge pull request #3765 from asottile/require_changelog_rst
Enforce that changelog files are .rst
2018-08-01 23:47:09 -07:00
Ronny Pfannschmidt
ca0476953e Merge pull request #3751 from nicoddemus/collect-file-bug
Workaround for #3742
2018-08-02 07:35:46 +02:00
Ronny Pfannschmidt
7e92930fa9 Merge pull request #3764 from asottile/fix_3763
Fix `TypeError` when the assertion message is `bytes` in python 3.
2018-08-02 07:19:51 +02:00
Ronny Pfannschmidt
33769d0328 Merge pull request #3754 from nicoddemus/fix-function-call-warning
Refactor direct fixture call warning to avoid incompatibility with plugins
2018-08-02 07:17:15 +02:00
Bruno Oliveira
5db2e6c7a1 Merge pull request #3761 from nicoddemus/numpy-recursion-error
Fix recursion in pytest.approx() with arrays in numpy<1.13
2018-08-01 23:40:30 -03:00
Bruno Oliveira
804fc4063a Merge pull request #3741 from kalekundert/approx_misc_tweaks
Miscellaneous improvements to approx()
2018-08-01 23:40:21 -03:00
Bruno Oliveira
82a2174867 Fix typo in CHANGELOG 2018-08-01 20:11:16 -03:00
Anthony Sottile
a80e031c62 Enforce that changelog files are .rst 2018-08-01 15:22:43 -07:00
Anthony Sottile
452e5c1cf0 Fix TypeError when the assertion message is bytes in python 3. 2018-08-01 15:09:25 -07:00
Bruno Oliveira
c6b11b9f62 Refactor direct fixture call warning to avoid incompatibility with plugins
This refactors the code so we have the real function object right during
collection. This avoids having to unwrap it later and lose attached information
such as "async" functions.

Fix #3747
2018-08-01 16:38:43 -03:00
Kale Kundert
b8255308d6 Make the infinite-recusrion fix more explicit.
So we remember what happened and don't accidentally regress in the
future.
2018-08-01 12:11:03 -07:00
Bruno Oliveira
a5c0fb7f6b Rename recursive_map -> _recursive_list_map as requested in review 2018-08-01 15:17:58 -03:00
Bruno Oliveira
f25683354e Merge pull request #3760 from RonnyPfannschmidt/fix-3757-pin-pathlib
fix #3757 by pinning to pathlib2 that supports __fspath__
2018-08-01 09:44:30 -03:00
Bruno Oliveira
7d13599ba1 Fix recursion in pytest.approx() with arrays in numpy<1.13 2018-08-01 08:04:09 -03:00
Bruno Oliveira
43664d7841 Use ids for parametrized values in test_expected_value_type_error 2018-08-01 07:34:08 -03:00
Bruno Oliveira
2a2f888909 Move recursive_map from local to free function 2018-08-01 07:30:40 -03:00
Bruno Oliveira
ad5ddaf55a Simplify is_numpy_array as suggested in review 2018-08-01 07:28:39 -03:00
Bruno Oliveira
4588130c1e Merge pull request #3756 from RonnyPfannschmidt/fix-3745
fix #3745 - display absolute cache_dir if necessary
2018-08-01 07:09:19 -03:00
Bruno Oliveira
5003bae0de Fix 'at' string for non-numeric messages in approx() 2018-08-01 07:07:37 -03:00
Bruno Oliveira
6e32a1f73d Use parametrize in repr test for nd arrays 2018-08-01 07:04:25 -03:00
Bruno Oliveira
611d254ed5 Improve error checking messages: add position and use pprint 2018-08-01 07:01:00 -03:00
Ronny Pfannschmidt
57a8f208bc fix #3757 by pinning to pathlib2 that supports __fspath__ 2018-08-01 11:45:39 +02:00
Ronny Pfannschmidt
fcdc1d867e fix #3745 - display absolute cache_dir if necessary 2018-08-01 08:25:37 +02:00
Bruno Oliveira
098dca3a9f Use {!r} for a few other messages as well 2018-07-31 21:14:51 -03:00
Bruno Oliveira
8e2ed76227 Create appropriate CHANGELOG entries 2018-07-31 21:11:26 -03:00
Bruno Oliveira
bf7c188cc0 Improve error message for invalid types passed to pytest.approx()
* Hide the internal traceback
* Use !r representation instead of !s (the default for {} formatting)
2018-07-31 21:08:24 -03:00
Bruno Oliveira
8c9efd8608 Only call _collectfile on package instances
As discussed in #3751, this feels like a hack, pushing it only so we can
see how it fares on CI and if there are better solutions out there
2018-07-31 19:06:30 -03:00
Bruno Oliveira
e1ad1a14af Add example script and failure for #3742 2018-07-31 17:50:55 -03:00
Kale Kundert
327fe4cfcc Update the changelog. 2018-07-31 11:40:02 -07:00
Kale Kundert
d02491931a Fix the unused import. 2018-07-31 11:33:46 -07:00
Kale Kundert
032db159c9 Let black reformat the code... 2018-07-31 11:23:23 -07:00
Kale Kundert
cd2085ee71 approx(): Detect type errors earlier. 2018-07-31 00:26:35 -07:00
Kale Kundert
ad305e71d7 Improve docstrings for Approx classes. 2018-07-30 23:26:57 -07:00
Kale Kundert
7d8688d54b Reflect dimension in approx repr for numpy arrays. 2018-07-30 23:23:17 -07:00
Bruno Oliveira
253419316c Merge pull request #3738 from nicoddemus/release-3.7.0
Release 3.7.0
2018-07-30 20:38:22 -03:00
24 changed files with 298 additions and 100 deletions

View File

@@ -38,3 +38,8 @@ repos:
language: python
additional_dependencies: [pygments, restructuredtext_lint]
python_version: python3.6
- id: changelogs-rst
name: changelog files must end in .rst
entry: ./scripts/fail
language: script
files: 'changelog/.*(?<!\.rst)$'

View File

@@ -8,6 +8,33 @@
.. towncrier release notes start
pytest 3.7.1 (2018-08-02)
=========================
Bug Fixes
---------
- `#3473 <https://github.com/pytest-dev/pytest/issues/3473>`_: Raise immediately if ``approx()`` is given an expected value of a type it doesn't understand (e.g. strings, nested dicts, etc.).
- `#3712 <https://github.com/pytest-dev/pytest/issues/3712>`_: Correctly represent the dimensions of an numpy array when calling ``repr()`` on ``approx()``.
- `#3745 <https://github.com/pytest-dev/pytest/issues/3745>`_: Display the absolute path if ``cache_dir`` is not relative to the ``rootdir`` instead of failing.
- `#3747 <https://github.com/pytest-dev/pytest/issues/3747>`_: Fix compatibility problem with plugins and the warning code issued by fixture functions when they are called directly.
- `#3748 <https://github.com/pytest-dev/pytest/issues/3748>`_: Fix infinite recursion in ``pytest.approx`` with arrays in ``numpy<1.13``.
- `#3757 <https://github.com/pytest-dev/pytest/issues/3757>`_: Pin pathlib2 to ``>=2.2.0`` as we require ``__fspath__`` support.
- `#3763 <https://github.com/pytest-dev/pytest/issues/3763>`_: Fix ``TypeError`` when the assertion message is ``bytes`` in python 3.
pytest 3.7.0 (2018-07-30)
=========================

View File

@@ -6,6 +6,7 @@ Release announcements
:maxdepth: 2
release-3.7.1
release-3.7.0
release-3.6.4
release-3.6.3

View File

@@ -0,0 +1,21 @@
pytest-3.7.1
=======================================
pytest 3.7.1 has just been released to PyPI.
This is a bug-fix release, being a drop-in replacement. To upgrade::
pip install --upgrade pytest
The full changelog is available at http://doc.pytest.org/en/latest/changelog.html.
Thanks to all who contributed to this release, among them:
* Anthony Sottile
* Bruno Oliveira
* Kale Kundert
* Ronny Pfannschmidt
Happy testing,
The pytest Development Team

View File

@@ -84,9 +84,8 @@ interesting to just look at the collection tree::
platform linux -- Python 3.x.y, pytest-3.x.y, py-1.x.y, pluggy-0.x.y
rootdir: $REGENDOC_TMPDIR/nonpython, inifile:
collected 2 items
<Package '$REGENDOC_TMPDIR/nonpython'>
<YamlFile 'test_simple.yml'>
<YamlItem 'hello'>
<YamlItem 'ok'>
<YamlFile 'test_simple.yml'>
<YamlItem 'hello'>
<YamlItem 'ok'>
======================= no tests ran in 0.12 seconds =======================

View File

@@ -411,10 +411,11 @@ is to be run with different sets of arguments for its three arguments:
Running it results in some skips if we don't have all the python interpreters installed and otherwise runs all combinations (5 interpreters times 5 interpreters times 3 objects to serialize/deserialize)::
. $ pytest -rs -q multipython.py
...sss...sssssssss...sss... [100%]
...ssssssssssssssssssssssss [100%]
========================= short test summary info ==========================
SKIP [15] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
12 passed, 15 skipped in 0.12 seconds
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.4' not found
SKIP [12] $REGENDOC_TMPDIR/CWD/multipython.py:28: 'python3.5' not found
3 passed, 24 skipped in 0.12 seconds
Indirect parametrization of optional implementations/imports
--------------------------------------------------------------------

View File

@@ -363,7 +363,7 @@ get on the terminal - we are working on that)::
> int(s)
E ValueError: invalid literal for int() with base 10: 'qwe'
<0-codegen $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/python_api.py:635>:1: ValueError
<0-codegen $PYTHON_PREFIX/lib/python3.6/site-packages/_pytest/python_api.py:682>:1: ValueError
______________________ TestRaises.test_raises_doesnt _______________________
self = <failure_demo.TestRaises object at 0xdeadbeef>

7
scripts/fail Executable file
View File

@@ -0,0 +1,7 @@
#!/usr/bin/env python
"""Used by .pre-commit-config.yaml"""
import sys
if __name__ == "__main__":
print(" ".join(sys.argv[1:]))
sys.exit(1)

View File

@@ -73,11 +73,11 @@ def main():
environment_marker_support_level = get_environment_marker_support_level()
if environment_marker_support_level >= 2:
install_requires.append('funcsigs;python_version<"3.0"')
install_requires.append('pathlib2;python_version<"3.6"')
install_requires.append('pathlib2>=2.2.0;python_version<"3.6"')
install_requires.append('colorama;sys_platform=="win32"')
elif environment_marker_support_level == 1:
extras_require[':python_version<"3.0"'] = ["funcsigs"]
extras_require[':python_version<"3.6"'] = ["pathlib2"]
extras_require[':python_version<"3.6"'] = ["pathlib2>=2.2.0"]
extras_require[':sys_platform=="win32"'] = ["colorama"]
else:
if sys.platform == "win32":
@@ -85,7 +85,7 @@ def main():
if sys.version_info < (3, 0):
install_requires.append("funcsigs")
if sys.version_info < (3, 6):
install_requires.append("pathlib2")
install_requires.append("pathlib2>=2.2.0")
setup(
name="pytest",

View File

@@ -425,20 +425,18 @@ def _format_assertmsg(obj):
# contains a newline it gets escaped, however if an object has a
# .__repr__() which contains newlines it does not get escaped.
# However in either case we want to preserve the newline.
if isinstance(obj, six.text_type) or isinstance(obj, six.binary_type):
s = obj
is_repr = False
else:
s = py.io.saferepr(obj)
is_repr = True
if isinstance(s, six.text_type):
t = six.text_type
else:
t = six.binary_type
s = s.replace(t("\n"), t("\n~")).replace(t("%"), t("%%"))
if is_repr:
s = s.replace(t("\\n"), t("\n~"))
return s
replaces = [(u"\n", u"\n~"), (u"%", u"%%")]
if not isinstance(obj, six.string_types):
obj = py.io.saferepr(obj)
replaces.append((u"\\n", u"\n~"))
if isinstance(obj, bytes):
replaces = [(r1.encode(), r2.encode()) for r1, r2 in replaces]
for r1, r2 in replaces:
obj = obj.replace(r1, r2)
return obj
def _should_repr_global_name(obj):

View File

@@ -312,8 +312,15 @@ def cache(request):
def pytest_report_header(config):
if config.option.verbose:
relpath = config.cache._cachedir.relative_to(config.rootdir)
return "cachedir: {}".format(relpath)
cachedir = config.cache._cachedir
# TODO: evaluate generating upward relative paths
# starting with .., ../.. if sensible
try:
displaypath = cachedir.relative_to(config.rootdir)
except ValueError:
displaypath = cachedir
return "cachedir: {}".format(displaypath)
def cacheshow(config, session):

View File

@@ -249,6 +249,21 @@ def get_real_func(obj):
return obj
def get_real_method(obj, holder):
"""
Attempts to obtain the real function object that might be wrapping ``obj``, while at the same time
returning a bound method to ``holder`` if the original object was a bound method.
"""
try:
is_method = hasattr(obj, "__func__")
obj = get_real_func(obj)
except Exception:
return obj
if is_method and hasattr(obj, "__get__") and callable(obj.__get__):
obj = obj.__get__(holder)
return obj
def getfslineno(obj):
# xxx let decorators etc specify a sane ordering
obj = get_real_func(obj)

View File

@@ -30,6 +30,7 @@ from _pytest.compat import (
getfuncargnames,
safe_getattr,
FuncargnamesCompatAttr,
get_real_method,
)
from _pytest.deprecated import FIXTURE_FUNCTION_CALL, RemovedInPytest4Warning
from _pytest.outcomes import fail, TEST_OUTCOME
@@ -931,13 +932,6 @@ def pytest_fixture_setup(fixturedef, request):
request._check_scope(argname, request.scope, fixdef.scope)
kwargs[argname] = result
# if function has been defined with @pytest.fixture, we want to
# pass the special __being_called_by_pytest parameter so we don't raise a warning
# this is an ugly hack, see #3720 for an opportunity to improve this
defined_using_fixture_decorator = hasattr(fixturedef.func, "_pytestfixturefunction")
if defined_using_fixture_decorator:
kwargs["__being_called_by_pytest"] = True
fixturefunc = resolve_fixture_function(fixturedef, request)
my_cache_key = request.param_index
try:
@@ -973,9 +967,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
if not __being_called_by_pytest:
warnings.warn(warning, stacklevel=3)
warnings.warn(warning, stacklevel=3)
for x in function(*args, **kwargs):
yield x
@@ -984,9 +976,7 @@ def wrap_function_to_warning_if_called_directly(function, fixture_marker):
@functools.wraps(function)
def result(*args, **kwargs):
__tracebackhide__ = True
__being_called_by_pytest = kwargs.pop("__being_called_by_pytest", False)
if not __being_called_by_pytest:
warnings.warn(warning, stacklevel=3)
warnings.warn(warning, stacklevel=3)
return function(*args, **kwargs)
if six.PY2:
@@ -1279,9 +1269,9 @@ class FixtureManager(object):
# The attribute can be an arbitrary descriptor, so the attribute
# access below can raise. safe_getatt() ignores such exceptions.
obj = safe_getattr(holderobj, name, None)
marker = getfixturemarker(obj)
# fixture functions have a pytest_funcarg__ prefix (pre-2.3 style)
# or are "@pytest.fixture" marked
marker = getfixturemarker(obj)
if marker is None:
if not name.startswith(self._argprefix):
continue
@@ -1303,6 +1293,15 @@ class FixtureManager(object):
name = marker.name
assert not name.startswith(self._argprefix), FIXTURE_MSG.format(name)
# during fixture definition we wrap the original fixture function
# to issue a warning if called directly, so here we unwrap it in order to not emit the warning
# when pytest itself calls the fixture function
if six.PY2 and unittest:
# hack on Python 2 because of the unbound methods
obj = get_real_func(obj)
else:
obj = get_real_method(obj, holderobj)
fixture_def = FixtureDef(
self,
nodeid,

View File

@@ -482,6 +482,8 @@ class Session(nodes.FSCollector):
self.trace.root.indent -= 1
def _collect(self, arg):
from _pytest.python import Package
names = self._parsearg(arg)
argpath = names.pop(0)
paths = []
@@ -503,7 +505,7 @@ class Session(nodes.FSCollector):
root = self._node_cache[pkginit]
else:
col = root._collectfile(pkginit)
if col:
if col and isinstance(col, Package):
root = col[0]
self._node_cache[root.fspath] = root

View File

@@ -1,5 +1,8 @@
import math
import pprint
import sys
from numbers import Number
from decimal import Decimal
import py
from six.moves import zip, filterfalse
@@ -30,6 +33,15 @@ def _cmp_raises_type_error(self, other):
)
def _non_numeric_type_error(value, at):
at_str = " at {}".format(at) if at else ""
return TypeError(
"cannot make approximate comparisons to non-numeric values: {!r} {}".format(
value, at_str
)
)
# builtin pytest.approx helper
@@ -39,15 +51,17 @@ class ApproxBase(object):
or sequences of numbers.
"""
# Tell numpy to use our `__eq__` operator instead of its
# Tell numpy to use our `__eq__` operator instead of its.
__array_ufunc__ = None
__array_priority__ = 100
def __init__(self, expected, rel=None, abs=None, nan_ok=False):
__tracebackhide__ = True
self.expected = expected
self.abs = abs
self.rel = rel
self.nan_ok = nan_ok
self._check_type()
def __repr__(self):
raise NotImplementedError
@@ -75,21 +89,32 @@ class ApproxBase(object):
"""
raise NotImplementedError
def _check_type(self):
"""
Raise a TypeError if the expected value is not a valid type.
"""
# This is only a concern if the expected value is a sequence. In every
# other case, the approx() function ensures that the expected value has
# a numeric type. For this reason, the default is to do nothing. The
# classes that deal with sequences should reimplement this method to
# raise if there are any non-numeric elements in the sequence.
pass
def _recursive_list_map(f, x):
if isinstance(x, list):
return list(_recursive_list_map(f, xi) for xi in x)
else:
return f(x)
class ApproxNumpy(ApproxBase):
"""
Perform approximate comparisons for numpy arrays.
Perform approximate comparisons where the expected value is numpy array.
"""
def __repr__(self):
# It might be nice to rewrite this function to account for the
# shape of the array...
import numpy as np
list_scalars = []
for x in np.ndindex(self.expected.shape):
list_scalars.append(self._approx_scalar(np.asscalar(self.expected[x])))
list_scalars = _recursive_list_map(self._approx_scalar, self.expected.tolist())
return "approx({!r})".format(list_scalars)
if sys.version_info[0] == 2:
@@ -128,8 +153,8 @@ class ApproxNumpy(ApproxBase):
class ApproxMapping(ApproxBase):
"""
Perform approximate comparisons for mappings where the values are numbers
(the keys can be anything).
Perform approximate comparisons where the expected value is a mapping with
numeric values (the keys can be anything).
"""
def __repr__(self):
@@ -147,10 +172,20 @@ class ApproxMapping(ApproxBase):
for k in self.expected.keys():
yield actual[k], self.expected[k]
def _check_type(self):
__tracebackhide__ = True
for key, value in self.expected.items():
if isinstance(value, type(self.expected)):
msg = "pytest.approx() does not support nested dictionaries: key={!r} value={!r}\n full mapping={}"
raise TypeError(msg.format(key, value, pprint.pformat(self.expected)))
elif not isinstance(value, Number):
raise _non_numeric_type_error(self.expected, at="key={!r}".format(key))
class ApproxSequence(ApproxBase):
"""
Perform approximate comparisons for sequences of numbers.
Perform approximate comparisons where the expected value is a sequence of
numbers.
"""
def __repr__(self):
@@ -169,10 +204,21 @@ class ApproxSequence(ApproxBase):
def _yield_comparisons(self, actual):
return zip(actual, self.expected)
def _check_type(self):
__tracebackhide__ = True
for index, x in enumerate(self.expected):
if isinstance(x, type(self.expected)):
msg = "pytest.approx() does not support nested data structures: {!r} at index {}\n full sequence: {}"
raise TypeError(msg.format(x, index, pprint.pformat(self.expected)))
elif not isinstance(x, Number):
raise _non_numeric_type_error(
self.expected, at="index {}".format(index)
)
class ApproxScalar(ApproxBase):
"""
Perform approximate comparisons for single numbers only.
Perform approximate comparisons where the expected value is a single number.
"""
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
@@ -211,7 +257,9 @@ class ApproxScalar(ApproxBase):
the pre-specified tolerance.
"""
if _is_numpy_array(actual):
return all(a == self for a in actual.flat)
# Call ``__eq__()`` manually to prevent infinite-recursion with
# numpy<1.13. See #3748.
return all(self.__eq__(a) for a in actual.flat)
# Short-circuit exact equality.
if actual == self.expected:
@@ -286,7 +334,9 @@ class ApproxScalar(ApproxBase):
class ApproxDecimal(ApproxScalar):
from decimal import Decimal
"""
Perform approximate comparisons where the expected value is a decimal.
"""
DEFAULT_ABSOLUTE_TOLERANCE = Decimal("1e-12")
DEFAULT_RELATIVE_TOLERANCE = Decimal("1e-6")
@@ -445,32 +495,35 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
__ https://docs.python.org/3/reference/datamodel.html#object.__ge__
"""
from decimal import Decimal
# Delegate the comparison to a class that knows how to deal with the type
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
#
# This architecture is really driven by the need to support numpy arrays.
# The only way to override `==` for arrays without requiring that approx be
# the left operand is to inherit the approx object from `numpy.ndarray`.
# But that can't be a general solution, because it requires (1) numpy to be
# installed and (2) the expected value to be a numpy array. So the general
# solution is to delegate each type of expected value to a different class.
# The primary responsibility of these classes is to implement ``__eq__()``
# and ``__repr__()``. The former is used to actually check if some
# "actual" value is equivalent to the given expected value within the
# allowed tolerance. The latter is used to show the user the expected
# value and tolerance, in the case that a test failed.
#
# This has the advantage that it made it easy to support mapping types
# (i.e. dict). The old code accepted mapping types, but would only compare
# their keys, which is probably not what most people would expect.
# The actual logic for making approximate comparisons can be found in
# ApproxScalar, which is used to compare individual numbers. All of the
# other Approx classes eventually delegate to this class. The ApproxBase
# class provides some convenient methods and overloads, but isn't really
# essential.
if _is_numpy_array(expected):
cls = ApproxNumpy
__tracebackhide__ = True
if isinstance(expected, Decimal):
cls = ApproxDecimal
elif isinstance(expected, Number):
cls = ApproxScalar
elif isinstance(expected, Mapping):
cls = ApproxMapping
elif isinstance(expected, Sequence) and not isinstance(expected, STRING_TYPES):
cls = ApproxSequence
elif isinstance(expected, Decimal):
cls = ApproxDecimal
elif _is_numpy_array(expected):
cls = ApproxNumpy
else:
cls = ApproxScalar
raise _non_numeric_type_error(expected, at=None)
return cls(expected, rel, abs, nan_ok)
@@ -480,17 +533,11 @@ def _is_numpy_array(obj):
Return true if the given object is a numpy array. Make a special effort to
avoid importing numpy unless it's really necessary.
"""
import inspect
for cls in inspect.getmro(type(obj)):
if cls.__module__ == "numpy":
try:
import numpy as np
return isinstance(obj, np.ndarray)
except ImportError:
pass
import sys
np = sys.modules.get("numpy")
if np is not None:
return isinstance(obj, np.ndarray)
return False

View File

@@ -267,7 +267,6 @@ def test_pytest_plugins_in_non_top_level_conftest_deprecated_no_false_positives(
)
# @pytest.mark.skipif(six.PY2, reason="We issue the warning in Python 3 only")
def test_call_fixture_function_deprecated():
"""Check if a warning is raised if a fixture function is called directly (#3661)"""

View File

@@ -0,0 +1,10 @@
import pytest
class CustomItem(pytest.Item, pytest.File):
def runtest(self):
pass
def pytest_collect_file(path, parent):
return CustomItem(path, parent)

View File

@@ -0,0 +1,2 @@
def test():
pass

View File

@@ -59,17 +59,21 @@ class TestApprox(object):
),
)
def test_repr_0d_array(self, plus_minus):
@pytest.mark.parametrize(
"value, repr_string",
[
(5., "approx(5.0 {pm} 5.0e-06)"),
([5.], "approx([5.0 {pm} 5.0e-06])"),
([[5.]], "approx([[5.0 {pm} 5.0e-06]])"),
([[5., 6.]], "approx([[5.0 {pm} 5.0e-06, 6.0 {pm} 6.0e-06]])"),
([[5.], [6.]], "approx([[5.0 {pm} 5.0e-06], [6.0 {pm} 6.0e-06]])"),
],
)
def test_repr_nd_array(self, plus_minus, value, repr_string):
"""Make sure that arrays of all different dimensions are repr'd correctly."""
np = pytest.importorskip("numpy")
np_array = np.array(5.)
assert approx(np_array) == 5.0
string_expected = "approx([5.0 {} 5.0e-06])".format(plus_minus)
assert repr(approx(np_array)) == string_expected
np_array = np.array([5.])
assert approx(np_array) == 5.0
assert repr(approx(np_array)) == string_expected
np_array = np.array(value)
assert repr(approx(np_array)) == repr_string.format(pm=plus_minus)
def test_operator_overloading(self):
assert 1 == approx(1, rel=1e-6, abs=1e-12)
@@ -439,6 +443,21 @@ class TestApprox(object):
["*At index 0 diff: 3 != 4 * {}".format(expected), "=* 1 failed in *="]
)
@pytest.mark.parametrize(
"x",
[
pytest.param(None),
pytest.param("string"),
pytest.param(["string"], id="nested-str"),
pytest.param([[1]], id="nested-list"),
pytest.param({"key": "string"}, id="dict-with-string"),
pytest.param({"key": {"key": 1}}, id="nested-dict"),
],
)
def test_expected_value_type_error(self, x):
with pytest.raises(TypeError):
approx(x)
@pytest.mark.parametrize(
"op",
[

View File

@@ -1470,10 +1470,9 @@ class TestFixtureManagerParseFactories(object):
print (faclist)
assert len(faclist) == 3
kwargs = {'__being_called_by_pytest': True}
assert faclist[0].func(item._request, **kwargs) == "conftest"
assert faclist[1].func(item._request, **kwargs) == "module"
assert faclist[2].func(item._request, **kwargs) == "class"
assert faclist[0].func(item._request) == "conftest"
assert faclist[1].func(item._request) == "module"
assert faclist[2].func(item._request) == "class"
"""
)
reprec = testdir.inline_run("-s")
@@ -1619,6 +1618,11 @@ class TestFixtureManagerParseFactories(object):
reprec = testdir.inline_run()
reprec.assertoutcome(passed=2)
def test_collect_custom_items(self, testdir):
testdir.copy_example("fixtures/custom_item")
result = testdir.runpytest("foo")
result.stdout.fnmatch_lines("*passed*")
class TestAutouseDiscovery(object):
@pytest.fixture

View File

@@ -246,6 +246,15 @@ class TestAssertionRewrite(object):
["*AssertionError: To be escaped: %", "*assert 1 == 2"]
)
@pytest.mark.skipif(
sys.version_info < (3,), reason="bytes is a string type in python 2"
)
def test_assertion_messages_bytes(self, testdir):
testdir.makepyfile("def test_bytes_assertion():\n assert False, b'ohai!'\n")
result = testdir.runpytest()
assert result.ret == 1
result.stdout.fnmatch_lines(["*AssertionError: b'ohai!'", "*assert False"])
def test_boolop(self):
def f():
f = g = False

View File

@@ -150,6 +150,32 @@ def test_cache_reportheader(testdir):
result.stdout.fnmatch_lines(["cachedir: .pytest_cache"])
def test_cache_reportheader_external_abspath(testdir, tmpdir_factory):
external_cache = tmpdir_factory.mktemp(
"test_cache_reportheader_external_abspath_abs"
)
testdir.makepyfile(
"""
def test_hello():
pass
"""
)
testdir.makeini(
"""
[pytest]
cache_dir = {abscache}
""".format(
abscache=external_cache
)
)
result = testdir.runpytest("-v")
result.stdout.fnmatch_lines(
["cachedir: {abscache}".format(abscache=external_cache)]
)
def test_cache_show(testdir):
result = testdir.runpytest("--cache-show")
assert result.ret == 0

View File

@@ -647,7 +647,7 @@ class Test_getinitialnodes(object):
col = testdir.getnode(config, x)
assert isinstance(col, pytest.Module)
assert col.name == "x.py"
assert col.parent.parent.parent is None
assert col.parent.parent is None
for col in col.listchain():
assert col.config is config