Merge pull request #1643 from RonnyPfannschmidt/merge-master

Merge master
This commit is contained in:
Ronny Pfannschmidt 2016-06-22 17:05:59 +02:00 committed by GitHub
commit 7d60fcc098
21 changed files with 418 additions and 135 deletions

25
AUTHORS
View File

@ -5,6 +5,7 @@ Contributors include::
Abdeali JK
Abhijeet Kasurde
Alexei Kozlenok
Anatoly Bubenkoff
Andreas Zeidler
Andy Freeland
@ -12,14 +13,17 @@ Anthon van der Neut
Armin Rigo
Aron Curzon
Aviv Palivoda
Ben Webb
Benjamin Peterson
Bob Ippolito
Brian Dorsey
Brian Okken
Brianna Laugher
Bruno Oliveira
Cal Leeming
Carl Friedrich Bolz
Charles Cloud
Charnjit SiNGH (CCSJ)
Chris Lamb
Christian Theunert
Christian Tismer
@ -28,20 +32,24 @@ Daniel Grana
Daniel Hahler
Daniel Nuri
Dave Hunt
David Díaz-Barquero
David Mohr
David Vierra
Edison Gustavo Muenz
Eduardo Schettino
Endre Galaczi
Elizaveta Shashkova
Endre Galaczi
Eric Hunsberger
Eric Hunsberger
Eric Siegerman
Erik M. Bray
Feng Ma
Florian Bruhin
Floris Bruynooghe
Gabriel Reis
Georgy Dyuldin
Graham Horler
Greg Price
Grig Gheorghiu
Guido Wesdorp
Harald Armin Massa
@ -65,6 +73,7 @@ Mark Abramowitz
Markus Unterwaditzer
Martijn Faassen
Martin Prusse
Martin K. Scherer
Matt Bachmann
Matt Williams
Michael Aquilina
@ -84,18 +93,16 @@ Raphael Pierzina
Roman Bolshakov
Ronny Pfannschmidt
Ross Lawley
Russel Winder
Ryan Wooden
Samuele Pedroni
Stephan Obermann
Tareq Alayan
Simon Gomizelj
Stefano Taschini
Stefan Farmbauer
Thomas Grainger
Tom Viner
Trevor Bekolay
Wouter van Ackooy
David Díaz-Barquero
Eric Hunsberger
Simon Gomizelj
Russel Winder
Ben Webb
Alexei Kozlenok
Cal Leeming
Feng Ma
Bernard Pratz

View File

@ -82,15 +82,48 @@
was only available for test modules. Thanks `@flub`_, `@sober7`_ and
`@nicoddemus`_ for the PR (`#1619`_).
*
* Text documents without any doctests no longer appear as "skipped".
Thanks `@graingert`_ for reporting and providing a full PR (`#1580`_).
* Fix internal error issue when ``method`` argument is missing for
``teardown_method()``. Fixes (`#1605`_).
* Fix exception visualization in case the current working directory (CWD) gets
deleted during testing. Fixes (`#1235`). Thanks `@bukzor`_ for reporting. PR by
`@marscher`. Thanks `@nicoddemus`_ for his help.
* Ensure that a module within a namespace package can be found when it
is specified on the command line together with the ``--pyargs``
option. Thanks to `@taschini`_ for the PR (`#1597`_).
* Raise helpful failure message, when requesting parametrized fixture at runtime,
e.g. with ``request.getfuncargvalue``. BACKWARD INCOMPAT: Previously these params
were simply never defined. So a fixture decorated like ``@pytest.fixture(params=[0, 1, 2])``
only ran once. Now a failure is raised. Fixes (`#460`_). Thanks to
`@nikratio`_ for bug report, `@RedBeardCode`_ and `@tomviner`_ for PR.
* Create correct diff for strings ending with newlines. Fixes (`#1553`_).
Thanks `@Vogtinator`_ for reporting. Thanks to `@RedBeardCode`_ and
`@tomviner`_ for PR.
*
.. _#1580: https://github.com/pytest-dev/pytest/pull/1580
.. _#1605: https://github.com/pytest-dev/pytest/issues/1605
.. _#1597: https://github.com/pytest-dev/pytest/pull/1597
.. _#460: https://github.com/pytest-dev/pytest/pull/460
.. _#1553: https://github.com/pytest-dev/pytest/issues/1553
.. _@graingert: https://github.com/graingert
.. _@taschini: https://github.com/taschini
.. _@nikratio: https://github.com/nikratio
.. _@RedBeardCode: https://github.com/RedBeardCode
.. _@Vogtinator: https://github.com/Vogtinator
* Fix `#1421`_: Exit tests if a collection error occurs and add
``--continue-on-collection-errors`` option to restore previous behaviour.
Thanks `@olegpidsadnyi`_ and `@omarkohl`_ for the complete PR (`#1628`_).
*
*
@ -294,7 +327,7 @@
Thanks `@biern`_ for the PR.
* Fix `traceback style docs`_ to describe all of the available options
(auto/long/short/line/native/no), with `auto` being the default since v2.6.
(auto/long/short/line/native/no), with ``auto`` being the default since v2.6.
Thanks `@hackebrot`_ for the PR.
* Fix (`#1422`_): junit record_xml_property doesn't allow multiple records

View File

@ -48,7 +48,7 @@ to fix the bug yet.
Fix bugs
--------
Look through the GitHub issues for bugs. Here is sample filter you can use:
Look through the GitHub issues for bugs. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/bug
:ref:`Talk <contact>` to developers to find out how you can fix specific bugs.
@ -60,8 +60,7 @@ Don't forget to check the issue trackers of your favourite plugins, too!
Implement features
------------------
Look through the GitHub issues for enhancements. Here is sample filter you
can use:
Look through the GitHub issues for enhancements. Here is a filter you can use:
https://github.com/pytest-dev/pytest/labels/enhancement
:ref:`Talk <contact>` to developers to find out how you can implement specific
@ -70,16 +69,15 @@ features.
Write documentation
-------------------
pytest could always use more documentation. What exactly is needed?
Pytest could always use more documentation. What exactly is needed?
* More complementary documentation. Have you perhaps found something unclear?
* Documentation translations. We currently have only English.
* Docstrings. There can never be too many of them.
* Blog posts, articles and such -- they're all very appreciated.
You can also edit documentation files directly in the Github web interface
without needing to make a fork and local copy. This can be convenient for
small fixes.
You can also edit documentation files directly in the GitHub web interface,
without using a local copy. This can be convenient for small fixes.
.. _submitplugin:
@ -95,13 +93,14 @@ in repositories living under the ``pytest-dev`` organisations:
- `pytest-dev on Bitbucket <https://bitbucket.org/pytest-dev>`_
All pytest-dev Contributors team members have write access to all contained
repositories. pytest core and plugins are generally developed
repositories. Pytest core and plugins are generally developed
using `pull requests`_ to respective repositories.
The objectives of the ``pytest-dev`` organisation are:
* Having a central location for popular pytest plugins
* Sharing some of the maintenance responsibility (in case a maintainer no longer whishes to maintain a plugin)
* Sharing some of the maintenance responsibility (in case a maintainer no
longer wishes to maintain a plugin)
You can submit your plugin by subscribing to the `pytest-dev mail list
<https://mail.python.org/mailman/listinfo/pytest-dev>`_ and writing a
@ -127,27 +126,18 @@ transferred to the ``pytest-dev`` organisation.
Here's a rundown of how a repository transfer usually proceeds
(using a repository named ``joedoe/pytest-xyz`` as example):
* One of the ``pytest-dev`` administrators creates:
- ``pytest-xyz-admin`` team, with full administration rights to
``pytest-dev/pytest-xyz``.
- ``pytest-xyz-developers`` team, with write access to
``pytest-dev/pytest-xyz``.
* ``joedoe`` is invited to the ``pytest-xyz-admin`` team;
* After accepting the invitation, ``joedoe`` transfers the repository from its
original location to ``pytest-dev/pytest-xyz`` (A nice feature is that GitHub handles URL redirection from
the old to the new location automatically).
* ``joedoe`` is free to add any other collaborators to the
``pytest-xyz-admin`` or ``pytest-xyz-developers`` team as desired.
* ``joedoe`` transfers repository ownership to ``pytest-dev`` administrator ``calvin``.
* ``calvin`` creates ``pytest-xyz-admin`` and ``pytest-xyz-developers`` teams, inviting ``joedoe`` to both as **maintainer**.
* ``calvin`` transfers repository to ``pytest-dev`` and configures team access:
- ``pytest-xyz-admin`` **admin** access;
- ``pytest-xyz-developers`` **write** access;
The ``pytest-dev/Contributors`` team has write access to all projects, and
every project administrator is in it. We recommend that each plugin has at least three
people who have the right to release to PyPI.
Repository owners can be assured that no ``pytest-dev`` administrator will ever make
Repository owners can rest assured that no ``pytest-dev`` administrator will ever make
releases of your repository or take ownership in any way, except in rare cases
where someone becomes unresponsive after months of contact attempts.
As stated, the objective is to share maintenance and avoid "plugin-abandon".
@ -159,15 +149,11 @@ As stated, the objective is to share maintenance and avoid "plugin-abandon".
Preparing Pull Requests on GitHub
---------------------------------
There's an excellent tutorial on how Pull Requests work in the
`GitHub Help Center <https://help.github.com/articles/using-pull-requests/>`_
.. note::
What is a "pull request"? It informs project's core developers about the
changes you want to review and merge. Pull requests are stored on
`GitHub servers <https://github.com/pytest-dev/pytest/pulls>`_.
Once you send pull request, we can discuss it's potential modifications and
Once you send a pull request, we can discuss its potential modifications and
even add more commits to it later on.
There's an excellent tutorial on how Pull Requests work in the
@ -216,19 +202,19 @@ but here is a simple overview:
This command will run tests via the "tox" tool against Python 2.7 and 3.5
and also perform "lint" coding-style checks. ``runtox.py`` is
a thin wrapper around ``tox`` which installs from a development package
index where newer (not yet released to pypi) versions of dependencies
index where newer (not yet released to PyPI) versions of dependencies
(especially ``py``) might be present.
#. You can now edit your local working copy.
You can now make the changes you want and run the tests again as necessary.
To run tests on py27 and pass options to pytest (e.g. enter pdb on failure)
to pytest you can do::
To run tests on Python 2.7 and pass options to pytest (e.g. enter pdb on
failure) to pytest you can do::
$ python3 runtox.py -e py27 -- --pdb
or to only run tests in a particular test module on py35::
Or to only run tests in a particular test module on Python 3.5::
$ python3 runtox.py -e py35 -- testing/test_config.py
@ -237,9 +223,9 @@ but here is a simple overview:
$ git commit -a -m "<commit message>"
$ git push -u
Make sure you add a CHANGELOG message, and add yourself to AUTHORS. If you
are unsure about either of these steps, submit your pull request and we'll
help you fix it up.
Make sure you add a message to ``CHANGELOG.rst`` and add yourself to
``AUTHORS``. If you are unsure about either of these steps, submit your
pull request and we'll help you fix it up.
#. Finally, submit a pull request through the GitHub website using this data::
@ -248,6 +234,6 @@ but here is a simple overview:
base-fork: pytest-dev/pytest
base: master # if it's a bugfix
base: feature # if it's a feature
base: features # if it's a feature

View File

@ -1,3 +1,2 @@
#
__version__ = '2.10.0.dev1'

View File

@ -3,7 +3,6 @@ from inspect import CO_VARARGS, CO_VARKEYWORDS
import re
import py
builtin_repr = repr
reprlib = py.builtin._tryimport('repr', 'reprlib')
@ -36,12 +35,16 @@ class Code(object):
def path(self):
""" return a path object pointing to source code (note that it
might not point to an actually existing file). """
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
try:
p = py.path.local(self.raw.co_filename)
# maybe don't try this checking
if not p.check():
raise OSError("py.path check failed.")
except OSError:
# XXX maybe try harder like the weird logic
# in the standard lib [linecache.updatecache] does?
p = self.raw.co_filename
return p
@property

View File

@ -225,9 +225,10 @@ def _diff_text(left, right, verbose=False):
'characters in diff, use -v to show') % i]
left = left[:-i]
right = right[:-i]
keepends = True
explanation += [line.strip('\n')
for line in ndiff(left.splitlines(),
right.splitlines())]
for line in ndiff(left.splitlines(keepends),
right.splitlines(keepends))]
return explanation

View File

@ -146,23 +146,19 @@ def get_optionflags(parent):
return flag_acc
class DoctestTextfile(DoctestItem, pytest.Module):
class DoctestTextfile(pytest.Module):
obj = None
def runtest(self):
def collect(self):
import doctest
fixture_request = _setup_fixtures(self)
# inspired by doctest.testfile; ideally we would use it directly,
# but it doesn't support passing a custom checker
text = self.fspath.read()
filename = str(self.fspath)
name = self.fspath.basename
globs = dict(getfixture=fixture_request.getfuncargvalue)
if '__name__' not in globs:
globs['__name__'] = '__main__'
globs = {'__name__': '__main__'}
for name, value in fixture_request.getfuncargvalue('doctest_namespace').items():
globs[name] = value
optionflags = get_optionflags(self)
runner = doctest.DebugRunner(verbose=0, optionflags=optionflags,
@ -170,8 +166,8 @@ class DoctestTextfile(DoctestItem, pytest.Module):
parser = doctest.DocTestParser()
test = parser.get_doctest(text, globs, name, filename, 0)
_check_all_skipped(test)
runner.run(test)
if test.examples:
yield DoctestItem(test.name, self, runner, test)
def _check_all_skipped(test):

View File

@ -1,7 +1,5 @@
""" core implementation of testing process: init, session, runtest loop. """
import imp
import os
import re
import sys
import _pytest
@ -25,8 +23,6 @@ EXIT_INTERNALERROR = 3
EXIT_USAGEERROR = 4
EXIT_NOTESTSCOLLECTED = 5
name_re = re.compile("^[a-zA-Z_]\w*$")
def pytest_addoption(parser):
parser.addini("norecursedirs", "directory patterns to avoid for recursion",
type="args", default=['.*', 'build', 'dist', 'CVS', '_darcs', '{arch}', '*.egg'])
@ -144,17 +140,8 @@ def pytest_runtestloop(session):
if session.config.option.collectonly:
return True
def getnextitem(i):
# this is a function to avoid python2
# keeping sys.exc_info set when calling into a test
# python2 keeps sys.exc_info till the frame is left
try:
return session.items[i+1]
except IndexError:
return None
for i, item in enumerate(session.items):
nextitem = getnextitem(i)
nextitem = session.items[i+1] if i+1 < len(session.items) else None
item.config.hook.pytest_runtest_protocol(item=item, nextitem=nextitem)
if session.shouldstop:
raise session.Interrupted(session.shouldstop)
@ -400,7 +387,10 @@ class Node(object):
if self.config.option.fulltrace:
style="long"
else:
tb = _pytest._code.Traceback([excinfo.traceback[-1]])
self._prunetraceback(excinfo)
if len(excinfo.traceback) == 0:
excinfo.traceback = tb
tbfilter = False # prunetraceback already does it
if style == "auto":
style = "long"
@ -411,7 +401,13 @@ class Node(object):
else:
style = "long"
return excinfo.getrepr(funcargs=True,
try:
os.getcwd()
abspath = False
except OSError:
abspath = True
return excinfo.getrepr(funcargs=True, abspath=abspath,
showlocals=self.config.option.showlocals,
style=style, tbfilter=tbfilter)
@ -657,36 +653,32 @@ class Session(FSCollector):
return True
def _tryconvertpyarg(self, x):
mod = None
path = [os.path.abspath('.')] + sys.path
for name in x.split('.'):
# ignore anything that's not a proper name here
# else something like --pyargs will mess up '.'
# since imp.find_module will actually sometimes work for it
# but it's supposed to be considered a filesystem path
# not a package
if name_re.match(name) is None:
return x
try:
fd, mod, type_ = imp.find_module(name, path)
except ImportError:
return x
else:
if fd is not None:
fd.close()
"""Convert a dotted module name to path.
if type_[2] != imp.PKG_DIRECTORY:
path = [os.path.dirname(mod)]
else:
path = [mod]
return mod
"""
import pkgutil
try:
loader = pkgutil.find_loader(x)
except ImportError:
return x
if loader is None:
return x
# This method is sometimes invoked when AssertionRewritingHook, which
# does not define a get_filename method, is already in place:
try:
path = loader.get_filename()
except AttributeError:
# Retrieve path from AssertionRewritingHook:
path = loader.modules[x][0].co_filename
if loader.is_package(x):
path = os.path.dirname(path)
return path
def _parsearg(self, arg):
""" return (fspath, names) tuple after checking the file exists. """
arg = str(arg)
if self.config.option.pyargs:
arg = self._tryconvertpyarg(arg)
parts = str(arg).split("::")
if self.config.option.pyargs:
parts[0] = self._tryconvertpyarg(parts[0])
relpath = parts[0].replace("/", os.sep)
path = self.config.invocation_dir.join(relpath, abs=True)
if not path.check():

View File

@ -123,15 +123,18 @@ def getexecutable(name, cache={}):
except KeyError:
executable = py.path.local.sysfind(name)
if executable:
import subprocess
popen = subprocess.Popen([str(executable), "--version"],
universal_newlines=True, stderr=subprocess.PIPE)
out, err = popen.communicate()
if name == "jython":
import subprocess
popen = subprocess.Popen([str(executable), "--version"],
universal_newlines=True, stderr=subprocess.PIPE)
out, err = popen.communicate()
if not err or "2.5" not in err:
executable = None
if "2.5.2" in err:
executable = None # http://bugs.jython.org/issue1790
elif popen.returncode != 0:
# Handle pyenv's 127.
executable = None
cache[name] = executable
return executable

View File

@ -2031,6 +2031,25 @@ class FixtureRequest(FuncargnamesCompatAttr):
except (AttributeError, ValueError):
param = NOTSET
param_index = 0
if fixturedef.params is not None:
frame = inspect.stack()[3]
frameinfo = inspect.getframeinfo(frame[0])
source_path = frameinfo.filename
source_lineno = frameinfo.lineno
source_path = py.path.local(source_path)
if source_path.relto(funcitem.config.rootdir):
source_path = source_path.relto(funcitem.config.rootdir)
msg = (
"The requested fixture has no parameter defined for the "
"current test.\n\nRequested fixture '{0}' defined in:\n{1}"
"\n\nRequested here:\n{2}:{3}".format(
fixturedef.argname,
getlocation(fixturedef.func, funcitem.config.rootdir),
source_path,
source_lineno,
)
)
pytest.fail(msg)
else:
# indices might not be set if old-style metafunc.addcall() was used
param_index = funcitem.callspec.indices.get(argname, 0)
@ -2369,7 +2388,7 @@ class FixtureManager:
else:
if marker.name:
name = marker.name
assert not name.startswith(self._argprefix)
assert not name.startswith(self._argprefix), name
fixturedef = FixtureDef(self, nodeid, name, obj,
marker.scope, marker.params,
unittest=unittest, ids=marker.ids)

View File

@ -7,7 +7,7 @@ Release announcements
sprint2016
release-2.9.1
release-2.9.2
release-2.9.1
release-2.9.0
release-2.8.7

View File

@ -222,9 +222,9 @@ Inspecting Cache content
-------------------------------
You can always peek at the content of the cache using the
``--cache-clear`` command line option::
``--cache-show`` command line option::
$ pytest --cache-clear
$ py.test --cache-show
======= test session starts ========
platform linux -- Python 3.5.1, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: $REGENDOC_TMPDIR, inifile:

View File

@ -295,13 +295,13 @@ which will add the string to the test header accordingly::
You can also return a list of strings which will be considered as several
lines of information. You can of course also make the amount of reporting
information on e.g. the value of ``config.option.verbose`` so that
information on e.g. the value of ``config.getoption('verbose')`` so that
you present more information appropriately::
# content of conftest.py
def pytest_report_header(config):
if config.option.verbose > 0:
if config.getoption('verbose') > 0:
return ["info1: did you know that ...", "did you?"]
which will add info only when run with "--v"::

View File

@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
import os
import sys
import _pytest._code
@ -513,12 +515,11 @@ class TestInvocationVariants:
path = testdir.mkpydir("tpkg")
path.join("test_hello.py").write('raise ImportError')
result = testdir.runpytest("--pyargs", "tpkg.test_hello")
result = testdir.runpytest_subprocess("--pyargs", "tpkg.test_hello")
assert result.ret != 0
# FIXME: It would be more natural to match NOT
# "ERROR*file*or*package*not*found*".
result.stdout.fnmatch_lines([
"*collected 0 items*"
"collected*0*items*/*1*errors"
])
def test_cmdline_python_package(self, testdir, monkeypatch):
@ -540,7 +541,7 @@ class TestInvocationVariants:
def join_pythonpath(what):
cur = py.std.os.environ.get('PYTHONPATH')
if cur:
return str(what) + ':' + cur
return str(what) + os.pathsep + cur
return what
empty_package = testdir.mkpydir("empty_package")
monkeypatch.setenv('PYTHONPATH', join_pythonpath(empty_package))
@ -551,11 +552,72 @@ class TestInvocationVariants:
])
monkeypatch.setenv('PYTHONPATH', join_pythonpath(testdir))
path.join('test_hello.py').remove()
result = testdir.runpytest("--pyargs", "tpkg.test_hello")
result = testdir.runpytest("--pyargs", "tpkg.test_missing")
assert result.ret != 0
result.stderr.fnmatch_lines([
"*not*found*test_hello*",
"*not*found*test_missing*",
])
def test_cmdline_python_namespace_package(self, testdir, monkeypatch):
"""
test --pyargs option with namespace packages (#1567)
"""
monkeypatch.delenv('PYTHONDONTWRITEBYTECODE', raising=False)
search_path = []
for dirname in "hello", "world":
d = testdir.mkdir(dirname)
search_path.append(d)
ns = d.mkdir("ns_pkg")
ns.join("__init__.py").write(
"__import__('pkg_resources').declare_namespace(__name__)")
lib = ns.mkdir(dirname)
lib.ensure("__init__.py")
lib.join("test_{0}.py".format(dirname)). \
write("def test_{0}(): pass\n"
"def test_other():pass".format(dirname))
# The structure of the test directory is now:
# .
# ├── hello
# │   └── ns_pkg
# │   ├── __init__.py
# │   └── hello
# │   ├── __init__.py
# │   └── test_hello.py
# └── world
# └── ns_pkg
# ├── __init__.py
# └── world
# ├── __init__.py
# └── test_world.py
def join_pythonpath(*dirs):
cur = py.std.os.environ.get('PYTHONPATH')
if cur:
dirs += (cur,)
return os.pathsep.join(str(p) for p in dirs)
monkeypatch.setenv('PYTHONPATH', join_pythonpath(*search_path))
for p in search_path:
monkeypatch.syspath_prepend(p)
# mixed module and filenames:
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.hello", "world/ns_pkg")
assert result.ret == 0
result.stdout.fnmatch_lines([
"*test_hello.py::test_hello*PASSED",
"*test_hello.py::test_other*PASSED",
"*test_world.py::test_world*PASSED",
"*test_world.py::test_other*PASSED",
"*4 passed*"
])
# specify tests within a module
result = testdir.runpytest("--pyargs", "-v", "ns_pkg.world.test_world::test_other")
assert result.ret == 0
result.stdout.fnmatch_lines([
"*test_world.py::test_other*PASSED",
"*1 passed*"
])
def test_cmdline_python_package_not_exists(self, testdir):
@ -700,4 +762,3 @@ class TestDurationWithFixture:
* setup *test_1*
* call *test_1*
""")

View File

@ -1066,3 +1066,15 @@ def test_repr_traceback_with_unicode(style, encoding):
formatter = FormattedExcinfo(style=style)
repr_traceback = formatter.repr_traceback(e_info)
assert repr_traceback is not None
def test_cwd_deleted(testdir):
testdir.makepyfile("""
def test(tmpdir):
tmpdir.chdir()
tmpdir.remove()
assert False
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines(['* 1 failed in *'])
assert 'INTERNALERROR' not in result.stdout.str() + result.stderr.str()

View File

@ -490,6 +490,20 @@ class TestRequestBasic:
print(ss.stack)
assert teardownlist == [1]
def test_mark_as_fixture_with_prefix_and_decorator_fails(self, testdir):
testdir.makeconftest("""
import pytest
@pytest.fixture
def pytest_funcarg__marked_with_prefix_and_decorator():
pass
""")
result = testdir.runpytest_subprocess()
assert result.ret != 0
result.stdout.fnmatch_lines([
"*AssertionError:*pytest_funcarg__marked_with_prefix_and_decorator*"
])
def test_request_addfinalizer_failing_setup(self, testdir):
testdir.makepyfile("""
import pytest
@ -2704,3 +2718,108 @@ class TestContextManagerFixtureFuncs:
""".format(flavor=flavor))
result = testdir.runpytest("-s")
result.stdout.fnmatch_lines("*mew*")
class TestParameterizedSubRequest:
def test_call_from_fixture(self, testdir):
testfile = testdir.makepyfile("""
import pytest
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
@pytest.fixture
def get_named_fixture(request):
return request.getfuncargvalue('fix_with_param')
def test_foo(request, get_named_fixture):
pass
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
E*Failed: The requested fixture has no parameter defined for the current test.
E*
E*Requested fixture 'fix_with_param' defined in:
E*{0}:4
E*Requested here:
E*{1}:9
*1 error*
""".format(testfile.basename, testfile.basename))
def test_call_from_test(self, testdir):
testfile = testdir.makepyfile("""
import pytest
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
def test_foo(request):
request.getfuncargvalue('fix_with_param')
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
E*Failed: The requested fixture has no parameter defined for the current test.
E*
E*Requested fixture 'fix_with_param' defined in:
E*{0}:4
E*Requested here:
E*{1}:8
*1 failed*
""".format(testfile.basename, testfile.basename))
def test_external_fixture(self, testdir):
conffile = testdir.makeconftest("""
import pytest
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
""")
testfile = testdir.makepyfile("""
def test_foo(request):
request.getfuncargvalue('fix_with_param')
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
E*Failed: The requested fixture has no parameter defined for the current test.
E*
E*Requested fixture 'fix_with_param' defined in:
E*{0}:4
E*Requested here:
E*{1}:2
*1 failed*
""".format(conffile.basename, testfile.basename))
def test_non_relative_path(self, testdir):
tests_dir = testdir.mkdir('tests')
fixdir = testdir.mkdir('fixtures')
fixfile = fixdir.join("fix.py")
fixfile.write(_pytest._code.Source("""
import pytest
@pytest.fixture(params=[0, 1, 2])
def fix_with_param(request):
return request.param
"""))
testfile = tests_dir.join("test_foos.py")
testfile.write(_pytest._code.Source("""
from fix import fix_with_param
def test_foo(request):
request.getfuncargvalue('fix_with_param')
"""))
tests_dir.chdir()
testdir.syspathinsert(fixdir)
result = testdir.runpytest()
result.stdout.fnmatch_lines("""
E*Failed: The requested fixture has no parameter defined for the current test.
E*
E*Requested fixture 'fix_with_param' defined in:
E*{0}:5
E*Requested here:
E*{1}:5
*1 failed*
""".format(fixfile.strpath, testfile.basename))

View File

@ -428,7 +428,7 @@ def test_assert_compare_truncate_longmessage(monkeypatch, testdir):
"*- 3",
"*- 5",
"*- 7",
"*truncated (191 more lines)*use*-vv*",
"*truncated (193 more lines)*use*-vv*",
])
@ -626,3 +626,17 @@ def test_set_with_unsortable_elements():
+ repr(3)
""").strip()
assert '\n'.join(expl) == dedent
def test_diff_newline_at_end(monkeypatch, testdir):
testdir.makepyfile(r"""
def test_diff():
assert 'asdf' == 'asdf\n'
""")
result = testdir.runpytest()
result.stdout.fnmatch_lines(r"""
*assert 'asdf' == 'asdf\n'
* - asdf
* + asdf
* ? +
""")

View File

@ -152,7 +152,9 @@ class TestCollectPluginHookRelay:
wascalled = []
class Plugin:
def pytest_collect_file(self, path, parent):
wascalled.append(path)
if not path.basename.startswith("."):
# Ignore hidden files, e.g. .testmondata.
wascalled.append(path)
testdir.makefile(".abc", "xyz")
pytest.main([testdir.tmpdir], plugins=[Plugin()])
assert len(wascalled) == 1

View File

@ -14,13 +14,16 @@ class TestDoctests:
>>> i-1
4
""")
for x in (testdir.tmpdir, checkfile):
#print "checking that %s returns custom items" % (x,)
items, reprec = testdir.inline_genitems(x)
assert len(items) == 1
assert isinstance(items[0], DoctestTextfile)
assert isinstance(items[0], DoctestItem)
assert isinstance(items[0].parent, DoctestTextfile)
# Empty file has no items.
items, reprec = testdir.inline_genitems(w)
assert len(items) == 1
assert len(items) == 0
def test_collect_module_empty(self, testdir):
path = testdir.makepyfile(whatever="#")
@ -608,6 +611,11 @@ class TestDoctestSkips:
reprec = testdir.inline_run("--doctest-modules")
reprec.assertoutcome(skipped=1)
def test_vacuous_all_skipped(self, testdir, makedoctest):
makedoctest('')
reprec = testdir.inline_run("--doctest-modules")
reprec.assertoutcome(passed=0, skipped=0)
class TestDoctestAutoUseFixtures:

View File

@ -228,6 +228,39 @@ class BaseFunctionalTests:
assert reps[5].nodeid.endswith("test_func")
assert reps[5].failed
def test_exact_teardown_issue1206(self, testdir):
rec = testdir.inline_runsource("""
import pytest
class TestClass:
def teardown_method(self):
pass
def test_method(self):
assert True
""")
reps = rec.getreports("pytest_runtest_logreport")
print (reps)
assert len(reps) == 3
#
assert reps[0].nodeid.endswith("test_method")
assert reps[0].passed
assert reps[0].when == 'setup'
#
assert reps[1].nodeid.endswith("test_method")
assert reps[1].passed
assert reps[1].when == 'call'
#
assert reps[2].nodeid.endswith("test_method")
assert reps[2].failed
assert reps[2].when == "teardown"
assert reps[2].longrepr.reprcrash.message in (
# python3 error
'TypeError: teardown_method() takes 1 positional argument but 2 were given',
# python2 error
'TypeError: teardown_method() takes exactly 1 argument (2 given)'
)
def test_failure_in_setup_function_ignores_custom_repr(self, testdir):
testdir.makepyfile(conftest="""
import pytest

View File

@ -8,16 +8,11 @@ import _pytest._pluggy as pluggy
import _pytest._code
import py
import pytest
from _pytest import runner
from _pytest.main import EXIT_NOTESTSCOLLECTED
from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt
from _pytest.terminal import build_summary_stats_line, _plugin_nameversions
def basic_run_report(item):
runner.call_and_report(item, "setup", log=False)
return runner.call_and_report(item, "call", log=False)
DistInfo = collections.namedtuple('DistInfo', ['project_name', 'version'])