Merge pull request #1336 from nicoddemus/merge-master-in-features
Merge master in features
This commit is contained in:
commit
ed3c96ee58
2
AUTHORS
2
AUTHORS
|
@ -37,6 +37,7 @@ Erik M. Bray
|
|||
Florian Bruhin
|
||||
Floris Bruynooghe
|
||||
Gabriel Reis
|
||||
Georgy Dyuldin
|
||||
Graham Horler
|
||||
Grig Gheorghiu
|
||||
Guido Wesdorp
|
||||
|
@ -77,3 +78,4 @@ Simon Gomizelj
|
|||
Russel Winder
|
||||
Ben Webb
|
||||
Alexei Kozlenok
|
||||
Cal Leeming
|
||||
|
|
|
@ -51,8 +51,12 @@
|
|||
.. _@The-Compiler: https://github.com/The-Compiler
|
||||
|
||||
|
||||
2.8.6.dev1
|
||||
==========
|
||||
2.8.7.dev1
|
||||
----------
|
||||
|
||||
|
||||
2.8.6
|
||||
-----
|
||||
|
||||
- fix #1259: allow for double nodeids in junitxml,
|
||||
this was a regression failing plugins combinations
|
||||
|
@ -68,10 +72,20 @@
|
|||
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
|
||||
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #1223: captured stdout and stderr are now properly displayed before
|
||||
entering pdb when ``--pdb`` is used instead of being thrown away.
|
||||
Thanks Cal Leeming for the PR.
|
||||
|
||||
- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
|
||||
properly displayed.
|
||||
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
|
||||
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #1334: Add captured stdout to jUnit XML report on setup error.
|
||||
Thanks Georgy Dyuldin for the PR.
|
||||
|
||||
|
||||
2.8.5
|
||||
=====
|
||||
|
|
|
@ -27,10 +27,6 @@ Note: this assumes you have already registered on pypi.
|
|||
devpi list pytest
|
||||
|
||||
or look at failures with "devpi list -f pytest".
|
||||
There will be some failed environments like e.g. the py33-trial
|
||||
or py27-pexpect tox environments on Win32 platforms
|
||||
which is ok (tox does not support skipping on
|
||||
per-platform basis yet).
|
||||
|
||||
7. Regenerate the docs examples using tox, and check for regressions::
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
.. image:: doc/en/img/pytest1.png
|
||||
.. image:: http://pytest.org/latest/_static/pytest1.png
|
||||
:target: http://pytest.org
|
||||
:align: center
|
||||
:alt: pytest
|
||||
|
@ -60,7 +60,7 @@ Features
|
|||
- Detailed info on failing `assert statements <http://pytest.org/latest/assert.html>`_ (no need to remember ``self.assert*`` names);
|
||||
|
||||
- `Auto-discovery
|
||||
<http://pytest.org/latest/goodpractises.html#python-test-discovery>`_
|
||||
<http://pytest.org/latest/goodpractices.html#python-test-discovery>`_
|
||||
of test modules and functions;
|
||||
|
||||
- `Modular fixtures <http://pytest.org/latest/fixture.html>`_ for
|
||||
|
|
|
@ -120,9 +120,9 @@ def pytest_runtest_setup(item):
|
|||
config=item.config, op=op, left=left, right=right)
|
||||
for new_expl in hook_result:
|
||||
if new_expl:
|
||||
if (sum(len(p) for p in new_expl[1:]) > 80*8
|
||||
and item.config.option.verbose < 2
|
||||
and not _running_on_ci()):
|
||||
if (sum(len(p) for p in new_expl[1:]) > 80*8 and
|
||||
item.config.option.verbose < 2 and
|
||||
not _running_on_ci()):
|
||||
show_max = 10
|
||||
truncated_lines = len(new_expl) - show_max
|
||||
new_expl[show_max:] = [py.builtin._totext(
|
||||
|
|
|
@ -140,8 +140,8 @@ def assertrepr_compare(config, op, left, right):
|
|||
|
||||
summary = u('%s %s %s') % (ecu(left_repr), op, ecu(right_repr))
|
||||
|
||||
issequence = lambda x: (isinstance(x, (list, tuple, Sequence))
|
||||
and not isinstance(x, basestring))
|
||||
issequence = lambda x: (isinstance(x, (list, tuple, Sequence)) and
|
||||
not isinstance(x, basestring))
|
||||
istext = lambda x: isinstance(x, basestring)
|
||||
isdict = lambda x: isinstance(x, dict)
|
||||
isset = lambda x: isinstance(x, (set, frozenset))
|
||||
|
@ -263,8 +263,7 @@ def _compare_eq_sequence(left, right, verbose=False):
|
|||
explanation += [
|
||||
u('Right contains more items, first extra item: %s') %
|
||||
py.io.saferepr(right[len(left)],)]
|
||||
return explanation # + _diff_text(pprint.pformat(left),
|
||||
# pprint.pformat(right))
|
||||
return explanation
|
||||
|
||||
|
||||
def _compare_eq_set(left, right, verbose=False):
|
||||
|
|
|
@ -90,15 +90,15 @@ class DoctestItem(pytest.Item):
|
|||
reprlocation = ReprFileLocation(filename, lineno, message)
|
||||
checker = _get_checker()
|
||||
REPORT_UDIFF = doctest.REPORT_UDIFF
|
||||
filelines = py.path.local(filename).readlines(cr=0)
|
||||
lines = []
|
||||
if lineno is not None:
|
||||
i = max(test.lineno, max(0, lineno - 10)) # XXX?
|
||||
for line in filelines[i:lineno]:
|
||||
lines.append("%03d %s" % (i+1, line))
|
||||
i += 1
|
||||
lines = doctestfailure.test.docstring.splitlines(False)
|
||||
# add line numbers to the left of the error message
|
||||
lines = ["%03d %s" % (i + test.lineno + 1, x)
|
||||
for (i, x) in enumerate(lines)]
|
||||
# trim docstring error lines to 10
|
||||
lines = lines[example.lineno - 9:example.lineno + 1]
|
||||
else:
|
||||
lines.append('EXAMPLE LOCATION UNKNOWN, not showing all tests of that example')
|
||||
lines = ['EXAMPLE LOCATION UNKNOWN, not showing all tests of that example']
|
||||
indent = '>>>'
|
||||
for line in example.source.splitlines():
|
||||
lines.append('??? %s %s' % (indent, line))
|
||||
|
|
|
@ -163,6 +163,7 @@ class _NodeReporter(object):
|
|||
def append_error(self, report):
|
||||
self._add_simple(
|
||||
Junit.error, "test setup failure", report.longrepr)
|
||||
self._write_captured_output(report)
|
||||
|
||||
def append_skipped(self, report):
|
||||
if hasattr(report, "wasxfail"):
|
||||
|
|
|
@ -52,7 +52,9 @@ class PdbInvoke:
|
|||
def pytest_exception_interact(self, node, call, report):
|
||||
capman = node.config.pluginmanager.getplugin("capturemanager")
|
||||
if capman:
|
||||
capman.suspendcapture(in_=True)
|
||||
out, err = capman.suspendcapture(in_=True)
|
||||
sys.stdout.write(out)
|
||||
sys.stdout.write(err)
|
||||
_enter_pdb(node, call.excinfo, report)
|
||||
|
||||
def pytest_internalerror(self, excrepr, excinfo):
|
||||
|
|
|
@ -416,8 +416,8 @@ class PyCollector(PyobjMixin, pytest.Collector):
|
|||
|
||||
def istestfunction(self, obj, name):
|
||||
return (
|
||||
(self.funcnamefilter(name) or self.isnosetest(obj))
|
||||
and safe_getattr(obj, "__call__", False) and getfixturemarker(obj) is None
|
||||
(self.funcnamefilter(name) or self.isnosetest(obj)) and
|
||||
safe_getattr(obj, "__call__", False) and getfixturemarker(obj) is None
|
||||
)
|
||||
|
||||
def istestclass(self, obj, name):
|
||||
|
@ -1765,8 +1765,10 @@ class FixtureLookupError(LookupError):
|
|||
stack.extend(map(lambda x: x.func, self.fixturestack))
|
||||
msg = self.msg
|
||||
if msg is not None:
|
||||
stack = stack[:-1] # the last fixture raise an error, let's present
|
||||
# it at the requesting side
|
||||
# the last fixture raise an error, let's present
|
||||
# it at the requesting side
|
||||
stack = stack[:-1]
|
||||
|
||||
for function in stack:
|
||||
fspath, lineno = getfslineno(function)
|
||||
try:
|
||||
|
|
|
@ -24,9 +24,10 @@ def pytest_pycollect_makeitem(collector, name, obj):
|
|||
|
||||
|
||||
class UnitTestCase(pytest.Class):
|
||||
nofuncargs = True # marker for fixturemanger.getfixtureinfo()
|
||||
# to declare that our children do not support funcargs
|
||||
#
|
||||
# marker for fixturemanger.getfixtureinfo()
|
||||
# to declare that our children do not support funcargs
|
||||
nofuncargs = True
|
||||
|
||||
def setup(self):
|
||||
cls = self.obj
|
||||
if getattr(cls, '__unittest_skip__', False):
|
||||
|
|
|
@ -6,6 +6,7 @@ Release announcements
|
|||
:maxdepth: 2
|
||||
|
||||
|
||||
release-2.8.6
|
||||
release-2.8.5
|
||||
release-2.8.4
|
||||
release-2.8.3
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
pytest-2.8.6
|
||||
============
|
||||
|
||||
pytest is a mature Python testing tool with more than a 1100 tests
|
||||
against itself, passing on many different interpreters and platforms.
|
||||
This release is supposed to be drop-in compatible to 2.8.5.
|
||||
|
||||
See below for the changes and see docs at:
|
||||
|
||||
http://pytest.org
|
||||
|
||||
As usual, you can upgrade from pypi via::
|
||||
|
||||
pip install -U pytest
|
||||
|
||||
Thanks to all who contributed to this release, among them:
|
||||
|
||||
AMiT Kumar
|
||||
Bruno Oliveira
|
||||
Erik M. Bray
|
||||
Florian Bruhin
|
||||
Georgy Dyuldin
|
||||
Jeff Widman
|
||||
Kartik Singhal
|
||||
Loïc Estève
|
||||
Manu Phatak
|
||||
Peter Demin
|
||||
Rick van Hattem
|
||||
Ronny Pfannschmidt
|
||||
Ulrich Petri
|
||||
foxx
|
||||
|
||||
|
||||
Happy testing,
|
||||
The py.test Development Team
|
||||
|
||||
|
||||
2.8.6 (compared to 2.8.5)
|
||||
-------------------------
|
||||
|
||||
- fix #1259: allow for double nodeids in junitxml,
|
||||
this was a regression failing plugins combinations
|
||||
like pytest-pep8 + pytest-flakes
|
||||
|
||||
- Workaround for exception that occurs in pyreadline when using
|
||||
``--pdb`` with standard I/O capture enabled.
|
||||
Thanks Erik M. Bray for the PR.
|
||||
|
||||
- fix #900: Better error message in case the target of a ``monkeypatch`` call
|
||||
raises an ``ImportError``.
|
||||
|
||||
- fix #1292: monkeypatch calls (setattr, setenv, etc.) are now O(1).
|
||||
Thanks David R. MacIver for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #1223: captured stdout and stderr are now properly displayed before
|
||||
entering pdb when ``--pdb`` is used instead of being thrown away.
|
||||
Thanks Cal Leeming for the PR.
|
||||
|
||||
- fix #1305: pytest warnings emitted during ``pytest_terminal_summary`` are now
|
||||
properly displayed.
|
||||
Thanks Ionel Maries Cristian for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #628: fixed internal UnicodeDecodeError when doctests contain unicode.
|
||||
Thanks Jason R. Coombs for the report and Bruno Oliveira for the PR.
|
||||
|
||||
- fix #1334: Add captured stdout to jUnit XML report on setup error.
|
||||
Thanks Georgy Dyuldin for the PR.
|
|
@ -193,7 +193,7 @@ Where to go next
|
|||
Here are a few suggestions where to go next:
|
||||
|
||||
* :ref:`cmdline` for command line invocation examples
|
||||
* :ref:`good practises <goodpractises>` for virtualenv, test layout, genscript support
|
||||
* :ref:`good practices <goodpractices>` for virtualenv, test layout, genscript support
|
||||
* :ref:`fixtures` for providing a functional baseline to your tests
|
||||
* :ref:`apiref` for documentation and examples on using ``pytest``
|
||||
* :ref:`plugins` managing and writing plugins
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
.. highlightlang:: python
|
||||
.. _`goodpractises`:
|
||||
.. _`goodpractices`:
|
||||
|
||||
Good Integration Practices
|
||||
=================================================
|
|
@ -40,7 +40,7 @@ pytest: helps you write better programs
|
|||
- multi-paradigm: pytest can run ``nose``, ``unittest`` and
|
||||
``doctest`` style test suites, including running testcases made for
|
||||
Django and trial
|
||||
- supports :ref:`good integration practises <goodpractises>`
|
||||
- supports :ref:`good integration practices <goodpractices>`
|
||||
- supports extended :ref:`xUnit style setup <xunitsetup>`
|
||||
- supports domain-specific :ref:`non-python tests`
|
||||
- supports generating `test coverage reports
|
||||
|
|
|
@ -7,7 +7,7 @@ Getting started basics
|
|||
|
||||
getting-started
|
||||
usage
|
||||
goodpractises
|
||||
goodpractices
|
||||
projects
|
||||
faq
|
||||
|
||||
|
|
|
@ -41,15 +41,15 @@ to an expected output::
|
|||
|
||||
# content of test_expectation.py
|
||||
import pytest
|
||||
@pytest.mark.parametrize("input,expected", [
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
("6*9", 42),
|
||||
])
|
||||
def test_eval(input, expected):
|
||||
assert eval(input) == expected
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
Here, the ``@parametrize`` decorator defines three different ``(input,expected)``
|
||||
Here, the ``@parametrize`` decorator defines three different ``(test_input,expected)``
|
||||
tuples so that the ``test_eval`` function will run three times using
|
||||
them in turn::
|
||||
|
||||
|
@ -64,15 +64,15 @@ them in turn::
|
|||
======= FAILURES ========
|
||||
_______ test_eval[6*9-42] ________
|
||||
|
||||
input = '6*9', expected = 42
|
||||
test_input = '6*9', expected = 42
|
||||
|
||||
@pytest.mark.parametrize("input,expected", [
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
("6*9", 42),
|
||||
])
|
||||
def test_eval(input, expected):
|
||||
> assert eval(input) == expected
|
||||
def test_eval(test_input, expected):
|
||||
> assert eval(test_input) == expected
|
||||
E assert 54 == 42
|
||||
E + where 54 = eval('6*9')
|
||||
|
||||
|
@ -91,13 +91,13 @@ for example with the builtin ``mark.xfail``::
|
|||
|
||||
# content of test_expectation.py
|
||||
import pytest
|
||||
@pytest.mark.parametrize("input,expected", [
|
||||
@pytest.mark.parametrize("test_input,expected", [
|
||||
("3+5", 8),
|
||||
("2+4", 6),
|
||||
pytest.mark.xfail(("6*9", 42)),
|
||||
])
|
||||
def test_eval(input, expected):
|
||||
assert eval(input) == expected
|
||||
def test_eval(test_input, expected):
|
||||
assert eval(test_input) == expected
|
||||
|
||||
Let's run this::
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ Here is a little annotated list for some popular plugins:
|
|||
check source code with pyflakes.
|
||||
|
||||
* `oejskit <http://pypi.python.org/pypi/oejskit>`_:
|
||||
a plugin to run javascript unittests in life browsers.
|
||||
a plugin to run javascript unittests in live browsers.
|
||||
|
||||
To see a complete list of all plugins with their latest testing
|
||||
status against different py.test and Python versions, please visit
|
||||
|
|
|
@ -95,7 +95,7 @@ Here is how you might run it::
|
|||
python package directory (i.e. one containing an ``__init__.py``) then
|
||||
"import conftest" can be ambiguous because there might be other
|
||||
``conftest.py`` files as well on your PYTHONPATH or ``sys.path``.
|
||||
It is thus good practise for projects to either put ``conftest.py``
|
||||
It is thus good practice for projects to either put ``conftest.py``
|
||||
under a package scope or to never import anything from a
|
||||
conftest.py file.
|
||||
|
||||
|
@ -485,12 +485,20 @@ Session related reporting hooks:
|
|||
.. autofunction:: pytest_itemcollected
|
||||
.. autofunction:: pytest_collectreport
|
||||
.. autofunction:: pytest_deselected
|
||||
.. autofunction:: pytest_report_header
|
||||
.. autofunction:: pytest_report_teststatus
|
||||
.. autofunction:: pytest_terminal_summary
|
||||
|
||||
And here is the central hook for reporting about
|
||||
test execution:
|
||||
|
||||
.. autofunction:: pytest_runtest_logreport
|
||||
|
||||
You can also use this hook to customize assertion representation for some
|
||||
types:
|
||||
|
||||
.. autofunction:: pytest_assertrepr_compare
|
||||
|
||||
|
||||
Debugging/Interaction hooks
|
||||
---------------------------
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
# encoding: utf-8
|
||||
import sys
|
||||
from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile
|
||||
import py
|
||||
|
@ -139,6 +140,46 @@ class TestDoctests:
|
|||
"*UNEXPECTED*ZeroDivision*",
|
||||
])
|
||||
|
||||
def test_docstring_context_around_error(self, testdir):
|
||||
"""Test that we show some context before the actual line of a failing
|
||||
doctest.
|
||||
"""
|
||||
testdir.makepyfile('''
|
||||
def foo():
|
||||
"""
|
||||
text-line-1
|
||||
text-line-2
|
||||
text-line-3
|
||||
text-line-4
|
||||
text-line-5
|
||||
text-line-6
|
||||
text-line-7
|
||||
text-line-8
|
||||
text-line-9
|
||||
text-line-10
|
||||
text-line-11
|
||||
>>> 1 + 1
|
||||
3
|
||||
|
||||
text-line-after
|
||||
"""
|
||||
''')
|
||||
result = testdir.runpytest('--doctest-modules')
|
||||
result.stdout.fnmatch_lines([
|
||||
'*docstring_context_around_error*',
|
||||
'005*text-line-3',
|
||||
'006*text-line-4',
|
||||
'013*text-line-11',
|
||||
'014*>>> 1 + 1',
|
||||
'Expected:',
|
||||
' 3',
|
||||
'Got:',
|
||||
' 2',
|
||||
])
|
||||
# lines below should be trimmed out
|
||||
assert 'text-line-2' not in result.stdout.str()
|
||||
assert 'text-line-after' not in result.stdout.str()
|
||||
|
||||
def test_doctest_linedata_missing(self, testdir):
|
||||
testdir.tmpdir.join('hello.py').write(py.code.Source("""
|
||||
class Fun(object):
|
||||
|
@ -369,6 +410,23 @@ class TestDoctests:
|
|||
reprec = testdir.inline_run(p, "--doctest-glob=x*.txt")
|
||||
reprec.assertoutcome(failed=1, passed=0)
|
||||
|
||||
def test_contains_unicode(self, testdir):
|
||||
"""Fix internal error with docstrings containing non-ascii characters.
|
||||
"""
|
||||
testdir.makepyfile(u'''
|
||||
# encoding: utf-8
|
||||
def foo():
|
||||
"""
|
||||
>>> name = 'с' # not letter 'c' but instead Cyrillic 's'.
|
||||
'anything'
|
||||
"""
|
||||
''')
|
||||
result = testdir.runpytest('--doctest-modules')
|
||||
result.stdout.fnmatch_lines([
|
||||
'Got nothing',
|
||||
'* 1 failed in*',
|
||||
])
|
||||
|
||||
def test_ignore_import_errors_on_doctest(self, testdir):
|
||||
p = testdir.makepyfile("""
|
||||
import asdf
|
||||
|
|
|
@ -419,6 +419,35 @@ class TestPython:
|
|||
systemout = pnode.find_first_by_tag("system-err")
|
||||
assert "hello-stderr" in systemout.toxml()
|
||||
|
||||
def test_setup_error_captures_stdout(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
def pytest_funcarg__arg(request):
|
||||
print('hello-stdout')
|
||||
raise ValueError()
|
||||
def test_function(arg):
|
||||
pass
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
pnode = node.find_first_by_tag("testcase")
|
||||
systemout = pnode.find_first_by_tag("system-out")
|
||||
assert "hello-stdout" in systemout.toxml()
|
||||
|
||||
def test_setup_error_captures_stderr(self, testdir):
|
||||
testdir.makepyfile("""
|
||||
import sys
|
||||
def pytest_funcarg__arg(request):
|
||||
sys.stderr.write('hello-stderr')
|
||||
raise ValueError()
|
||||
def test_function(arg):
|
||||
pass
|
||||
""")
|
||||
result, dom = runandparse(testdir)
|
||||
node = dom.find_first_by_tag("testsuite")
|
||||
pnode = node.find_first_by_tag("testcase")
|
||||
systemout = pnode.find_first_by_tag("system-err")
|
||||
assert "hello-stderr" in systemout.toxml()
|
||||
|
||||
|
||||
def test_mangle_testnames():
|
||||
from _pytest.junitxml import mangle_testnames
|
||||
|
|
|
@ -75,6 +75,22 @@ class TestPDB:
|
|||
if child.isalive():
|
||||
child.wait()
|
||||
|
||||
def test_pdb_interaction_capture(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def test_1():
|
||||
print("getrekt")
|
||||
assert False
|
||||
""")
|
||||
child = testdir.spawn_pytest("--pdb %s" % p1)
|
||||
child.expect("getrekt")
|
||||
child.expect("(Pdb)")
|
||||
child.sendeof()
|
||||
rest = child.read().decode("utf8")
|
||||
assert "1 failed" in rest
|
||||
assert "getrekt" not in rest
|
||||
if child.isalive():
|
||||
child.wait()
|
||||
|
||||
def test_pdb_interaction_exception(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
import pytest
|
||||
|
|
2
tox.ini
2
tox.ini
|
@ -157,4 +157,4 @@ norecursedirs = .tox ja .hg cx_freeze_source
|
|||
|
||||
|
||||
[flake8]
|
||||
ignore =E401,E225,E261,E128,E124,E301,E302,E121,E303,W391,E501,E231,E126,E701,E265,E241,E251,E226,E101,W191,E131,E203,E122,E123,E271,E712,E222,E127,E125,E221,W292,E111,E113,E293,E262,W293,E129,E702,E201,E272,E202
|
||||
ignore =E401,E225,E261,E128,E124,E301,E302,E121,E303,W391,E501,E231,E126,E701,E265,E241,E251,E226,E101,W191,E131,E203,E122,E123,E271,E712,E222,E127,E125,E221,W292,E111,E113,E293,E262,W293,E129,E702,E201,E272,E202,E704,E731,E402
|
||||
|
|
Loading…
Reference in New Issue