Skip to content

Commit 463e657

Browse files
committed
Merge branch 'master' into merge-master-into-features
Preparing for 3.0
2 parents 789e467 + 3e685d6 commit 463e657

File tree

14 files changed

+209
-57
lines changed

14 files changed

+209
-57
lines changed

AUTHORS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ Carl Friedrich Bolz
2828
Charles Cloud
2929
Charnjit SiNGH (CCSJ)
3030
Chris Lamb
31+
Christian Boelsen
3132
Christian Theunert
3233
Christian Tismer
3334
Christopher Gilling

CHANGELOG.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,15 @@
33

44
**Incompatible changes**
55

6+
<<<<<<< HEAD
67
A number of incompatible changes were made in this release, with the intent of removing features deprecated for a long
78
time or change existing behaviors in order to make them less surprising/more useful.
9+
=======
10+
* Improve error message with fixture lookup errors: add an 'E' to the first
11+
line and '>' to the rest. Fixes `#717`_. Thanks `@blueyed`_ for reporting and
12+
a PR, `@eolo999`_ for the initial PR and `@tomviner`_ for his guidance during
13+
EuroPython2016 sprint.
14+
>>>>>>> master
815

916
* Reinterpretation mode has now been removed. Only plain and rewrite
1017
mode are available, consequently the ``--assert=reinterp`` option is
@@ -159,6 +166,14 @@ time or change existing behaviors in order to make them less surprising/more use
159166
* Plugins now benefit from assertion rewriting. Thanks
160167
`@sober7`_, `@nicoddemus`_ and `@flub`_ for the PR.
161168

169+
* Change ``report.outcome`` for ``xpassed`` tests to ``"passed"`` in non-strict
170+
mode and ``"failed"`` in strict mode. Thanks to `@hackebrot`_ for the PR
171+
(`#1795`_) and `@gprasad84`_ for report (`#1546`_).
172+
173+
* Tests marked with ``xfail(strict=False)`` (the default) now appear in
174+
JUnitXML reports as passing tests instead of skipped.
175+
Thanks to `@hackebrot`_ for the PR (`#1795`_).
176+
162177
* Highlight path of the file location in the error report to make it easier to copy/paste.
163178
Thanks `@suzaku`_ for the PR (`#1778`_).
164179

@@ -322,12 +337,17 @@ time or change existing behaviors in order to make them less surprising/more use
322337
* Fixed scope overriding inside metafunc.parametrize (`#634`_).
323338
Thanks to `@Stranger6667`_ for the PR.
324339

325-
*
340+
* Fixed the total tests tally in junit xml output (`#1798`_).
341+
Thanks to `@cryporchild`_ for the PR.
326342

327-
*
343+
* ``pytest_terminal_summary`` hook now receives the ``exitstatus``
344+
of the test session as argument. Thanks `@blueyed`_ for the PR (`#1809`_).
328345

329346
*
330347

348+
* Fixed off-by-one error with lines from ``request.node.warn``.
349+
Thanks to `@blueyed`_ for the PR.
350+
331351
*
332352

333353
.. _#1210: https://github.com/pytest-dev/pytest/issues/1210
@@ -354,6 +374,7 @@ time or change existing behaviors in order to make them less surprising/more use
354374
.. _#1526: https://github.com/pytest-dev/pytest/pull/1526
355375
.. _#1539: https://github.com/pytest-dev/pytest/issues/1539
356376
.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
377+
.. _#1546: https://github.com/pytest-dev/pytest/issues/1546
357378
.. _#1553: https://github.com/pytest-dev/pytest/issues/1553
358379
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
359380
.. _#1579: https://github.com/pytest-dev/pytest/issues/1579
@@ -377,6 +398,9 @@ time or change existing behaviors in order to make them less surprising/more use
377398
.. _#1740: https://github.com/pytest-dev/pytest/issues/1740
378399
.. _#1749: https://github.com/pytest-dev/pytest/issues/1749
379400
.. _#1778: https://github.com/pytest-dev/pytest/pull/1778
401+
.. _#1795: https://github.com/pytest-dev/pytest/pull/1795
402+
.. _#1798: https://github.com/pytest-dev/pytest/pull/1798
403+
.. _#1809: https://github.com/pytest-dev/pytest/pull/1809
380404
.. _#372: https://github.com/pytest-dev/pytest/issues/372
381405
.. _#457: https://github.com/pytest-dev/pytest/issues/457
382406
.. _#460: https://github.com/pytest-dev/pytest/pull/460
@@ -393,13 +417,15 @@ time or change existing behaviors in order to make them less surprising/more use
393417
.. _@BeyondEvil: https://github.com/BeyondEvil
394418
.. _@blueyed: https://github.com/blueyed
395419
.. _@ceridwen: https://github.com/ceridwen
420+
.. _@cryporchild: https://github.com/cryporchild
396421
.. _@csaftoiu: https://github.com/csaftoiu
397422
.. _@d6e: https://github.com/d6e
398423
.. _@davehunt: https://github.com/davehunt
399424
.. _@DRMacIver: https://github.com/DRMacIver
400425
.. _@eolo999: https://github.com/eolo999
401426
.. _@fengxx: https://github.com/fengxx
402427
.. _@flub: https://github.com/flub
428+
.. _@gprasad84: https://github.com/gprasad84
403429
.. _@graingert: https://github.com/graingert
404430
.. _@hartym: https://github.com/hartym
405431
.. _@JonathonSonesen: https://github.com/JonathonSonesen

_pytest/fixtures.py

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
import sys
2+
3+
from py._code.code import FormattedExcinfo
4+
25
import py
36
import pytest
47
import warnings
@@ -649,6 +652,7 @@ def formatrepr(self):
649652

650653
return FixtureLookupErrorRepr(fspath, lineno, tblines, msg, self.argname)
651654

655+
652656
class FixtureLookupErrorRepr(TerminalRepr):
653657
def __init__(self, filename, firstlineno, tblines, errorstring, argname):
654658
self.tblines = tblines
@@ -658,16 +662,16 @@ def __init__(self, filename, firstlineno, tblines, errorstring, argname):
658662
self.argname = argname
659663

660664
def toterminal(self, tw):
661-
#tw.line("FixtureLookupError: %s" %(self.argname), red=True)
665+
# tw.line("FixtureLookupError: %s" %(self.argname), red=True)
662666
for tbline in self.tblines:
663667
tw.line(tbline.rstrip())
664668
lines = self.errorstring.split("\n")
665-
for line in lines:
666-
if line == lines[0]:
667-
prefix = 'E '
668-
else:
669-
prefix = ' '
670-
tw.line(prefix + line.strip(), red=True)
669+
if lines:
670+
tw.line('{0} {1}'.format(FormattedExcinfo.fail_marker,
671+
lines[0].strip()), red=True)
672+
for line in lines[1:]:
673+
tw.line('{0} {1}'.format(FormattedExcinfo.flow_marker,
674+
line.strip()), red=True)
671675
tw.line()
672676
tw.line("%s:%d" % (self.filename, self.firstlineno+1))
673677

_pytest/junitxml.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -370,7 +370,7 @@ def pytest_sessionfinish(self):
370370
suite_stop_time = time.time()
371371
suite_time_delta = suite_stop_time - self.suite_start_time
372372

373-
numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped']
373+
numtests = self.stats['passed'] + self.stats['failure'] + self.stats['skipped'] + self.stats['error']
374374

375375
logfile.write('<?xml version="1.0" encoding="utf-8"?>')
376376

_pytest/main.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ def warn(self, code, message):
292292
if fslocation is None:
293293
fslocation = getattr(self, "fspath", None)
294294
else:
295-
fslocation = "%s:%s" % fslocation[:2]
295+
fslocation = "%s:%s" % (fslocation[0], fslocation[1] + 1)
296296

297297
self.ihook.pytest_logwarning.call_historic(kwargs=dict(
298298
code=code, message=message,

_pytest/python.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1538,4 +1538,3 @@ def runtest(self):
15381538
def setup(self):
15391539
super(Function, self).setup()
15401540
fixtures.fillfixtures(self)
1541-

_pytest/skipping.py

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,18 @@ def check_strict_xfail(pyfuncitem):
216216
pytest.fail('[XPASS(strict)] ' + explanation, pytrace=False)
217217

218218

219+
def _is_unittest_unexpected_success_a_failure():
220+
"""Return if the test suite should fail if a @expectedFailure unittest test PASSES.
221+
222+
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
223+
Changed in version 3.4: Returns False if there were any
224+
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
225+
226+
TODO: this should be moved to the "compat" module.
227+
"""
228+
return sys.version_info >= (3, 4)
229+
230+
219231
@pytest.hookimpl(hookwrapper=True)
220232
def pytest_runtest_makereport(item, call):
221233
outcome = yield
@@ -224,9 +236,15 @@ def pytest_runtest_makereport(item, call):
224236
evalskip = getattr(item, '_evalskip', None)
225237
# unitttest special case, see setting of _unexpectedsuccess
226238
if hasattr(item, '_unexpectedsuccess') and rep.when == "call":
227-
# we need to translate into how pytest encodes xpass
228-
rep.wasxfail = "reason: " + repr(item._unexpectedsuccess)
229-
rep.outcome = "failed"
239+
if item._unexpectedsuccess:
240+
rep.longrepr = "Unexpected success: {0}".format(item._unexpectedsuccess)
241+
else:
242+
rep.longrepr = "Unexpected success"
243+
if _is_unittest_unexpected_success_a_failure():
244+
rep.outcome = "failed"
245+
else:
246+
rep.outcome = "passed"
247+
rep.wasxfail = rep.longrepr
230248
elif item.config.option.runxfail:
231249
pass # don't interefere
232250
elif call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
@@ -241,8 +259,15 @@ def pytest_runtest_makereport(item, call):
241259
rep.outcome = "skipped"
242260
rep.wasxfail = evalxfail.getexplanation()
243261
elif call.when == "call":
244-
rep.outcome = "failed" # xpass outcome
245-
rep.wasxfail = evalxfail.getexplanation()
262+
strict_default = item.config.getini('xfail_strict')
263+
is_strict_xfail = evalxfail.get('strict', strict_default)
264+
explanation = evalxfail.getexplanation()
265+
if is_strict_xfail:
266+
rep.outcome = "failed"
267+
rep.longrepr = "[XPASS(strict)] {0}".format(explanation)
268+
else:
269+
rep.outcome = "passed"
270+
rep.wasxfail = explanation
246271
elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple:
247272
# skipped by mark.skipif; change the location of the failure
248273
# to point to the item definition, otherwise it will display
@@ -256,7 +281,7 @@ def pytest_report_teststatus(report):
256281
if hasattr(report, "wasxfail"):
257282
if report.skipped:
258283
return "xfailed", "x", "xfail"
259-
elif report.failed:
284+
elif report.passed:
260285
return "xpassed", "X", ("XPASS", {'yellow': True})
261286

262287
# called by the terminalreporter instance/plugin

testing/python/fixture.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,11 @@ def test_lookup_error(unknown):
425425
""")
426426
result = testdir.runpytest()
427427
result.stdout.fnmatch_lines([
428-
"*ERROR*test_lookup_error*",
429-
"*def test_lookup_error(unknown):*",
430-
"*fixture*unknown*not found*",
431-
# check if fixtures appear sorted
432-
"*available fixtures:*a_fixture,*b_fixture,*c_fixture,*d_fixture*monkeypatch,*",
428+
"*ERROR at setup of test_lookup_error*",
429+
" def test_lookup_error(unknown):*",
430+
"E fixture 'unknown' not found",
431+
"> available fixtures:*a_fixture,*b_fixture,*c_fixture,*d_fixture*monkeypatch,*", # sorted
432+
"> use 'py*test --fixtures *' for help on them.",
433433
"*1 error*",
434434
])
435435
assert "INTERNAL" not in result.stdout.str()

testing/python/metafunc.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,22 +1191,23 @@ def test_increment(n, expected):
11911191
reprec = testdir.inline_run()
11921192
reprec.assertoutcome(passed=2, skipped=1)
11931193

1194-
def test_xfail_passing_is_xpass(self, testdir):
1194+
@pytest.mark.parametrize('strict', [True, False])
1195+
def test_xfail_passing_is_xpass(self, testdir, strict):
11951196
s = """
11961197
import pytest
11971198
11981199
@pytest.mark.parametrize(("n", "expected"), [
11991200
(1, 2),
1200-
pytest.mark.xfail("sys.version > 0", reason="some bug")((2, 3)),
1201+
pytest.mark.xfail("sys.version_info > (0, 0, 0)", reason="some bug", strict={strict})((2, 3)),
12011202
(3, 4),
12021203
])
12031204
def test_increment(n, expected):
12041205
assert n + 1 == expected
1205-
"""
1206+
""".format(strict=strict)
12061207
testdir.makepyfile(s)
12071208
reprec = testdir.inline_run()
1208-
# xpass is fail, obviously :)
1209-
reprec.assertoutcome(passed=2, failed=1)
1209+
passed, failed = (2, 1) if strict else (3, 0)
1210+
reprec.assertoutcome(passed=passed, failed=failed)
12101211

12111212
def test_parametrize_called_in_generate_tests(self, testdir):
12121213
s = """

testing/test_config.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,13 +548,14 @@ def test_proper(pytestconfig):
548548
reprec = testdir.inline_run()
549549
reprec.assertoutcome(passed=1)
550550

551-
def test_warn_on_test_item_from_request(self, testdir):
551+
def test_warn_on_test_item_from_request(self, testdir, request):
552552
testdir.makepyfile("""
553553
import pytest
554554
555555
@pytest.fixture
556556
def fix(request):
557557
request.node.warn("T1", "hello")
558+
558559
def test_hello(fix):
559560
pass
560561
""")
@@ -565,7 +566,7 @@ def test_hello(fix):
565566
result = testdir.runpytest()
566567
result.stdout.fnmatch_lines("""
567568
===*pytest-warning summary*===
568-
*WT1*test_warn_on_test_item*:5*hello*
569+
*WT1*test_warn_on_test_item*:7 hello*
569570
""")
570571

571572
class TestRootdir:
@@ -625,6 +626,7 @@ def test_with_specific_inifile(self, tmpdir):
625626
rootdir, inifile, inicfg = determine_setup(inifile, [tmpdir])
626627
assert rootdir == tmpdir
627628

629+
628630
class TestOverrideIniArgs:
629631
@pytest.mark.parametrize("name", "setup.cfg tox.ini pytest.ini".split())
630632
def test_override_ini_names(self, testdir, name):

0 commit comments

Comments
 (0)