Merge pull request #3269 from nicoddemus/merge-master-into-features
Merge master into features
This commit is contained in:
commit
0a5a6c19be
1
AUTHORS
1
AUTHORS
|
@ -202,3 +202,4 @@ Xuan Luong
|
||||||
Xuecong Liao
|
Xuecong Liao
|
||||||
Zoltán Máté
|
Zoltán Máté
|
||||||
Roland Puntaier
|
Roland Puntaier
|
||||||
|
Allan Feldman
|
||||||
|
|
|
@ -23,6 +23,9 @@
|
||||||
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
|
.. image:: https://ci.appveyor.com/api/projects/status/mrgbjaua7t33pg6b?svg=true
|
||||||
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
:target: https://ci.appveyor.com/project/pytestbot/pytest
|
||||||
|
|
||||||
|
.. image:: https://www.codetriage.com/pytest-dev/pytest/badges/users.svg
|
||||||
|
:target: https://www.codetriage.com/pytest-dev/pytest
|
||||||
|
|
||||||
The ``pytest`` framework makes it easy to write small tests, yet
|
The ``pytest`` framework makes it easy to write small tests, yet
|
||||||
scales to support complex functional testing for applications and libraries.
|
scales to support complex functional testing for applications and libraries.
|
||||||
|
|
||||||
|
|
|
@ -1328,10 +1328,14 @@ def determine_setup(inifile, args, warnfunc=None, rootdir_cmd_arg=None):
|
||||||
dirs = get_dirs_from_args(args)
|
dirs = get_dirs_from_args(args)
|
||||||
if inifile:
|
if inifile:
|
||||||
iniconfig = py.iniconfig.IniConfig(inifile)
|
iniconfig = py.iniconfig.IniConfig(inifile)
|
||||||
try:
|
is_cfg_file = str(inifile).endswith('.cfg')
|
||||||
inicfg = iniconfig["pytest"]
|
sections = ['tool:pytest', 'pytest'] if is_cfg_file else ['pytest']
|
||||||
except KeyError:
|
for section in sections:
|
||||||
inicfg = None
|
try:
|
||||||
|
inicfg = iniconfig[section]
|
||||||
|
break
|
||||||
|
except KeyError:
|
||||||
|
inicfg = None
|
||||||
rootdir = get_common_ancestor(dirs)
|
rootdir = get_common_ancestor(dirs)
|
||||||
else:
|
else:
|
||||||
ancestor = get_common_ancestor(dirs)
|
ancestor = get_common_ancestor(dirs)
|
||||||
|
|
|
@ -24,6 +24,12 @@ from _pytest.compat import (
|
||||||
from _pytest.outcomes import fail, TEST_OUTCOME
|
from _pytest.outcomes import fail, TEST_OUTCOME
|
||||||
|
|
||||||
|
|
||||||
|
@attr.s(frozen=True)
|
||||||
|
class PseudoFixtureDef(object):
|
||||||
|
cached_result = attr.ib()
|
||||||
|
scope = attr.ib()
|
||||||
|
|
||||||
|
|
||||||
def pytest_sessionstart(session):
|
def pytest_sessionstart(session):
|
||||||
import _pytest.python
|
import _pytest.python
|
||||||
import _pytest.nodes
|
import _pytest.nodes
|
||||||
|
@ -440,10 +446,9 @@ class FixtureRequest(FuncargnamesCompatAttr):
|
||||||
fixturedef = self._getnextfixturedef(argname)
|
fixturedef = self._getnextfixturedef(argname)
|
||||||
except FixtureLookupError:
|
except FixtureLookupError:
|
||||||
if argname == "request":
|
if argname == "request":
|
||||||
class PseudoFixtureDef(object):
|
cached_result = (self, [0], None)
|
||||||
cached_result = (self, [0], None)
|
scope = "function"
|
||||||
scope = "function"
|
return PseudoFixtureDef(cached_result, scope)
|
||||||
return PseudoFixtureDef
|
|
||||||
raise
|
raise
|
||||||
# remove indent to prevent the python3 exception
|
# remove indent to prevent the python3 exception
|
||||||
# from leaking into the call
|
# from leaking into the call
|
||||||
|
|
|
@ -153,6 +153,8 @@ class ApproxScalar(ApproxBase):
|
||||||
"""
|
"""
|
||||||
Perform approximate comparisons for single numbers only.
|
Perform approximate comparisons for single numbers only.
|
||||||
"""
|
"""
|
||||||
|
DEFAULT_ABSOLUTE_TOLERANCE = 1e-12
|
||||||
|
DEFAULT_RELATIVE_TOLERANCE = 1e-6
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
"""
|
"""
|
||||||
|
@ -223,7 +225,7 @@ class ApproxScalar(ApproxBase):
|
||||||
|
|
||||||
# Figure out what the absolute tolerance should be. ``self.abs`` is
|
# Figure out what the absolute tolerance should be. ``self.abs`` is
|
||||||
# either None or a value specified by the user.
|
# either None or a value specified by the user.
|
||||||
absolute_tolerance = set_default(self.abs, 1e-12)
|
absolute_tolerance = set_default(self.abs, self.DEFAULT_ABSOLUTE_TOLERANCE)
|
||||||
|
|
||||||
if absolute_tolerance < 0:
|
if absolute_tolerance < 0:
|
||||||
raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance))
|
raise ValueError("absolute tolerance can't be negative: {}".format(absolute_tolerance))
|
||||||
|
@ -241,7 +243,7 @@ class ApproxScalar(ApproxBase):
|
||||||
# we've made sure the user didn't ask for an absolute tolerance only,
|
# we've made sure the user didn't ask for an absolute tolerance only,
|
||||||
# because we don't want to raise errors about the relative tolerance if
|
# because we don't want to raise errors about the relative tolerance if
|
||||||
# we aren't even going to use it.
|
# we aren't even going to use it.
|
||||||
relative_tolerance = set_default(self.rel, 1e-6) * abs(self.expected)
|
relative_tolerance = set_default(self.rel, self.DEFAULT_RELATIVE_TOLERANCE) * abs(self.expected)
|
||||||
|
|
||||||
if relative_tolerance < 0:
|
if relative_tolerance < 0:
|
||||||
raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance))
|
raise ValueError("relative tolerance can't be negative: {}".format(absolute_tolerance))
|
||||||
|
@ -252,6 +254,13 @@ class ApproxScalar(ApproxBase):
|
||||||
return max(relative_tolerance, absolute_tolerance)
|
return max(relative_tolerance, absolute_tolerance)
|
||||||
|
|
||||||
|
|
||||||
|
class ApproxDecimal(ApproxScalar):
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
|
DEFAULT_ABSOLUTE_TOLERANCE = Decimal('1e-12')
|
||||||
|
DEFAULT_RELATIVE_TOLERANCE = Decimal('1e-6')
|
||||||
|
|
||||||
|
|
||||||
def approx(expected, rel=None, abs=None, nan_ok=False):
|
def approx(expected, rel=None, abs=None, nan_ok=False):
|
||||||
"""
|
"""
|
||||||
Assert that two numbers (or two sets of numbers) are equal to each other
|
Assert that two numbers (or two sets of numbers) are equal to each other
|
||||||
|
@ -401,6 +410,7 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
|
||||||
|
|
||||||
from collections import Mapping, Sequence
|
from collections import Mapping, Sequence
|
||||||
from _pytest.compat import STRING_TYPES as String
|
from _pytest.compat import STRING_TYPES as String
|
||||||
|
from decimal import Decimal
|
||||||
|
|
||||||
# Delegate the comparison to a class that knows how to deal with the type
|
# 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).
|
# of the expected value (e.g. int, float, list, dict, numpy.array, etc).
|
||||||
|
@ -422,6 +432,8 @@ def approx(expected, rel=None, abs=None, nan_ok=False):
|
||||||
cls = ApproxMapping
|
cls = ApproxMapping
|
||||||
elif isinstance(expected, Sequence) and not isinstance(expected, String):
|
elif isinstance(expected, Sequence) and not isinstance(expected, String):
|
||||||
cls = ApproxSequence
|
cls = ApproxSequence
|
||||||
|
elif isinstance(expected, Decimal):
|
||||||
|
cls = ApproxDecimal
|
||||||
else:
|
else:
|
||||||
cls = ApproxScalar
|
cls = ApproxScalar
|
||||||
|
|
||||||
|
|
|
@ -329,6 +329,8 @@ class TerminalReporter(object):
|
||||||
_PROGRESS_LENGTH = len(' [100%]')
|
_PROGRESS_LENGTH = len(' [100%]')
|
||||||
|
|
||||||
def _get_progress_information_message(self):
|
def _get_progress_information_message(self):
|
||||||
|
if self.config.getoption('capture') == 'no':
|
||||||
|
return ''
|
||||||
collected = self._session.testscollected
|
collected = self._session.testscollected
|
||||||
if collected:
|
if collected:
|
||||||
progress = len(self._progress_nodeids_reported) * 100 // collected
|
progress = len(self._progress_nodeids_reported) * 100 // collected
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Removed progress information when capture option is ``no``.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix ``TypeError`` issue when using ``approx`` with a ``Decimal`` value.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix reference cycle generated when using the ``request`` fixture.
|
|
@ -0,0 +1 @@
|
||||||
|
Fix minor typo in fixture.rst
|
|
@ -0,0 +1 @@
|
||||||
|
``[tool:pytest]`` sections in ``*.cfg`` files passed by the ``-c`` option are now properly recognized.
|
|
@ -369,7 +369,7 @@ ends, but ``addfinalizer`` has two key differences over ``yield``:
|
||||||
Fixtures can introspect the requesting test context
|
Fixtures can introspect the requesting test context
|
||||||
-------------------------------------------------------------
|
-------------------------------------------------------------
|
||||||
|
|
||||||
Fixture function can accept the :py:class:`request <FixtureRequest>` object
|
Fixture functions can accept the :py:class:`request <FixtureRequest>` object
|
||||||
to introspect the "requesting" test function, class or module context.
|
to introspect the "requesting" test function, class or module context.
|
||||||
Further extending the previous ``smtp`` fixture example, let's
|
Further extending the previous ``smtp`` fixture example, let's
|
||||||
read an optional server URL from the test module which uses our fixture::
|
read an optional server URL from the test module which uses our fixture::
|
||||||
|
|
|
@ -160,9 +160,9 @@ List the name ``tmpdir`` in the test function signature and ``pytest`` will look
|
||||||
PYTEST_TMPDIR/test_needsfiles0
|
PYTEST_TMPDIR/test_needsfiles0
|
||||||
1 failed in 0.12 seconds
|
1 failed in 0.12 seconds
|
||||||
|
|
||||||
More info on tmpdir handling is available at `Temporary directories and files <tmpdir handling>`_.
|
More info on tmpdir handling is available at :ref:`Temporary directories and files <tmpdir handling>`.
|
||||||
|
|
||||||
Find out what kind of builtin ```pytest`` fixtures <fixtures>`_ exist with the command::
|
Find out what kind of builtin :ref:`pytest fixtures <fixtures>` exist with the command::
|
||||||
|
|
||||||
pytest --fixtures # shows builtin and custom fixtures
|
pytest --fixtures # shows builtin and custom fixtures
|
||||||
|
|
||||||
|
|
|
@ -278,7 +278,7 @@ the plugin manager like this:
|
||||||
|
|
||||||
.. sourcecode:: python
|
.. sourcecode:: python
|
||||||
|
|
||||||
plugin = config.pluginmanager.getplugin("name_of_plugin")
|
plugin = config.pluginmanager.get_plugin("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 ``--trace-config`` option.
|
the ``--trace-config`` option.
|
||||||
|
|
|
@ -249,6 +249,7 @@ class TestApprox(object):
|
||||||
(Decimal('-1.000001'), Decimal('-1.0')),
|
(Decimal('-1.000001'), Decimal('-1.0')),
|
||||||
]
|
]
|
||||||
for a, x in within_1e6:
|
for a, x in within_1e6:
|
||||||
|
assert a == approx(x)
|
||||||
assert a == approx(x, rel=Decimal('5e-6'), abs=0)
|
assert a == approx(x, rel=Decimal('5e-6'), abs=0)
|
||||||
assert a != approx(x, rel=Decimal('5e-7'), abs=0)
|
assert a != approx(x, rel=Decimal('5e-7'), abs=0)
|
||||||
assert approx(x, rel=Decimal('5e-6'), abs=0) == a
|
assert approx(x, rel=Decimal('5e-6'), abs=0) == a
|
||||||
|
|
|
@ -519,6 +519,41 @@ class TestRequestBasic(object):
|
||||||
assert len(arg2fixturedefs) == 1
|
assert len(arg2fixturedefs) == 1
|
||||||
assert arg2fixturedefs['something'][0].argname == "something"
|
assert arg2fixturedefs['something'][0].argname == "something"
|
||||||
|
|
||||||
|
def test_request_garbage(self, testdir):
|
||||||
|
testdir.makepyfile("""
|
||||||
|
import sys
|
||||||
|
import pytest
|
||||||
|
import gc
|
||||||
|
|
||||||
|
@pytest.fixture(autouse=True)
|
||||||
|
def something(request):
|
||||||
|
# this method of test doesn't work on pypy
|
||||||
|
if hasattr(sys, "pypy_version_info"):
|
||||||
|
yield
|
||||||
|
else:
|
||||||
|
original = gc.get_debug()
|
||||||
|
gc.set_debug(gc.DEBUG_SAVEALL)
|
||||||
|
gc.collect()
|
||||||
|
|
||||||
|
yield
|
||||||
|
|
||||||
|
gc.collect()
|
||||||
|
leaked_types = sum(1 for _ in gc.garbage
|
||||||
|
if 'PseudoFixtureDef' in str(_))
|
||||||
|
|
||||||
|
gc.garbage[:] = []
|
||||||
|
|
||||||
|
try:
|
||||||
|
assert leaked_types == 0
|
||||||
|
finally:
|
||||||
|
gc.set_debug(original)
|
||||||
|
|
||||||
|
def test_func():
|
||||||
|
pass
|
||||||
|
""")
|
||||||
|
reprec = testdir.inline_run()
|
||||||
|
reprec.assertoutcome(passed=1)
|
||||||
|
|
||||||
def test_getfixturevalue_recursive(self, testdir):
|
def test_getfixturevalue_recursive(self, testdir):
|
||||||
testdir.makeconftest("""
|
testdir.makeconftest("""
|
||||||
import pytest
|
import pytest
|
||||||
|
|
|
@ -110,6 +110,13 @@ class TestConfigCmdlineParsing(object):
|
||||||
config = testdir.parseconfig("-c", "custom.cfg")
|
config = testdir.parseconfig("-c", "custom.cfg")
|
||||||
assert config.getini("custom") == "1"
|
assert config.getini("custom") == "1"
|
||||||
|
|
||||||
|
testdir.makefile(".cfg", custom_tool_pytest_section="""
|
||||||
|
[tool:pytest]
|
||||||
|
custom = 1
|
||||||
|
""")
|
||||||
|
config = testdir.parseconfig("-c", "custom_tool_pytest_section.cfg")
|
||||||
|
assert config.getini("custom") == "1"
|
||||||
|
|
||||||
def test_absolute_win32_path(self, testdir):
|
def test_absolute_win32_path(self, testdir):
|
||||||
temp_cfg_file = testdir.makefile(".cfg", custom="""
|
temp_cfg_file = testdir.makefile(".cfg", custom="""
|
||||||
[pytest]
|
[pytest]
|
||||||
|
|
|
@ -1121,6 +1121,9 @@ class TestProgress(object):
|
||||||
r'test_foobar.py \.{5}',
|
r'test_foobar.py \.{5}',
|
||||||
])
|
])
|
||||||
|
|
||||||
|
output = testdir.runpytest('--capture=no')
|
||||||
|
assert "%]" not in output.stdout.str()
|
||||||
|
|
||||||
|
|
||||||
class TestProgressWithTeardown(object):
|
class TestProgressWithTeardown(object):
|
||||||
"""Ensure we show the correct percentages for tests that fail during teardown (#3088)"""
|
"""Ensure we show the correct percentages for tests that fail during teardown (#3088)"""
|
||||||
|
|
Loading…
Reference in New Issue