Merge pull request #3269 from nicoddemus/merge-master-into-features

Merge master into features
This commit is contained in:
Bruno Oliveira 2018-02-27 22:42:08 -03:00 committed by GitHub
commit 0a5a6c19be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 92 additions and 14 deletions

View File

@ -202,3 +202,4 @@ Xuan Luong
Xuecong Liao Xuecong Liao
Zoltán Máté Zoltán Máté
Roland Puntaier Roland Puntaier
Allan Feldman

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1 @@
Removed progress information when capture option is ``no``.

View File

@ -0,0 +1 @@
Fix ``TypeError`` issue when using ``approx`` with a ``Decimal`` value.

View File

@ -0,0 +1 @@
Fix reference cycle generated when using the ``request`` fixture.

1
changelog/3259.trivial Normal file
View File

@ -0,0 +1 @@
Fix minor typo in fixture.rst

View File

@ -0,0 +1 @@
``[tool:pytest]`` sections in ``*.cfg`` files passed by the ``-c`` option are now properly recognized.

View File

@ -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::

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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)"""