Skip to content

Commit 3748112

Browse files
authored
Merge pull request #1816 from nicoddemus/merge-master-into-features
Merge master into features
2 parents 789e467 + 0334e75 commit 3748112

File tree

15 files changed

+209
-60
lines changed

15 files changed

+209
-60
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: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
**Incompatible changes**
55

6+
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.
89

@@ -159,6 +160,14 @@ time or change existing behaviors in order to make them less surprising/more use
159160
* Plugins now benefit from assertion rewriting. Thanks
160161
`@sober7`_, `@nicoddemus`_ and `@flub`_ for the PR.
161162

163+
* Change ``report.outcome`` for ``xpassed`` tests to ``"passed"`` in non-strict
164+
mode and ``"failed"`` in strict mode. Thanks to `@hackebrot`_ for the PR
165+
(`#1795`_) and `@gprasad84`_ for report (`#1546`_).
166+
167+
* Tests marked with ``xfail(strict=False)`` (the default) now appear in
168+
JUnitXML reports as passing tests instead of skipped.
169+
Thanks to `@hackebrot`_ for the PR (`#1795`_).
170+
162171
* Highlight path of the file location in the error report to make it easier to copy/paste.
163172
Thanks `@suzaku`_ for the PR (`#1778`_).
164173

@@ -175,6 +184,9 @@ time or change existing behaviors in order to make them less surprising/more use
175184
fixture is declared in a test function.
176185
Thanks `@nicoddemus`_ for the PR.
177186

187+
* ``pytest_terminal_summary`` hook now receives the ``exitstatus``
188+
of the test session as argument. Thanks `@blueyed`_ for the PR (`#1809`_).
189+
178190
* Parametrize ids can accept ``None`` as specific test id, in which case the
179191
automatically generated id for that argument will be used.
180192
Thanks `@palaviv`_ for the complete PR (`#1468`_).
@@ -279,7 +291,10 @@ time or change existing behaviors in order to make them less surprising/more use
279291
and there are more modern and better alternatives (see `#830`_).
280292
Thanks `@nicoddemus`_ for the PR.
281293

282-
*
294+
* Improve error message with fixture lookup errors: add an 'E' to the first
295+
line and '>' to the rest. Fixes `#717`_. Thanks `@blueyed`_ for reporting and
296+
a PR, `@eolo999`_ for the initial PR and `@tomviner`_ for his guidance during
297+
EuroPython2016 sprint.
283298

284299
*
285300

@@ -322,11 +337,11 @@ 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-
*
326-
327-
*
340+
* Fixed the total tests tally in junit xml output (`#1798`_).
341+
Thanks to `@cryporchild`_ for the PR.
328342

329-
*
343+
* Fixed off-by-one error with lines from ``request.node.warn``.
344+
Thanks to `@blueyed`_ for the PR.
330345

331346
*
332347

@@ -354,6 +369,7 @@ time or change existing behaviors in order to make them less surprising/more use
354369
.. _#1526: https://github.com/pytest-dev/pytest/pull/1526
355370
.. _#1539: https://github.com/pytest-dev/pytest/issues/1539
356371
.. _#1544: https://github.com/pytest-dev/pytest/issues/1544
372+
.. _#1546: https://github.com/pytest-dev/pytest/issues/1546
357373
.. _#1553: https://github.com/pytest-dev/pytest/issues/1553
358374
.. _#1562: https://github.com/pytest-dev/pytest/issues/1562
359375
.. _#1579: https://github.com/pytest-dev/pytest/issues/1579
@@ -377,6 +393,9 @@ time or change existing behaviors in order to make them less surprising/more use
377393
.. _#1740: https://github.com/pytest-dev/pytest/issues/1740
378394
.. _#1749: https://github.com/pytest-dev/pytest/issues/1749
379395
.. _#1778: https://github.com/pytest-dev/pytest/pull/1778
396+
.. _#1795: https://github.com/pytest-dev/pytest/pull/1795
397+
.. _#1798: https://github.com/pytest-dev/pytest/pull/1798
398+
.. _#1809: https://github.com/pytest-dev/pytest/pull/1809
380399
.. _#372: https://github.com/pytest-dev/pytest/issues/372
381400
.. _#457: https://github.com/pytest-dev/pytest/issues/457
382401
.. _#460: https://github.com/pytest-dev/pytest/pull/460
@@ -393,13 +412,15 @@ time or change existing behaviors in order to make them less surprising/more use
393412
.. _@BeyondEvil: https://github.com/BeyondEvil
394413
.. _@blueyed: https://github.com/blueyed
395414
.. _@ceridwen: https://github.com/ceridwen
415+
.. _@cryporchild: https://github.com/cryporchild
396416
.. _@csaftoiu: https://github.com/csaftoiu
397417
.. _@d6e: https://github.com/d6e
398418
.. _@davehunt: https://github.com/davehunt
399419
.. _@DRMacIver: https://github.com/DRMacIver
400420
.. _@eolo999: https://github.com/eolo999
401421
.. _@fengxx: https://github.com/fengxx
402422
.. _@flub: https://github.com/flub
423+
.. _@gprasad84: https://github.com/gprasad84
403424
.. _@graingert: https://github.com/graingert
404425
.. _@hartym: https://github.com/hartym
405426
.. _@JonathonSonesen: https://github.com/JonathonSonesen

_pytest/compat.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ def get_real_func(obj):
173173
obj = obj.func
174174
return obj
175175

176+
176177
def getfslineno(obj):
177178
# xxx let decorators etc specify a sane ordering
178179
obj = get_real_func(obj)
@@ -182,6 +183,7 @@ def getfslineno(obj):
182183
assert isinstance(fslineno[1], int), obj
183184
return fslineno
184185

186+
185187
def getimfunc(func):
186188
try:
187189
return func.__func__
@@ -191,6 +193,7 @@ def getimfunc(func):
191193
except AttributeError:
192194
return func
193195

196+
194197
def safe_getattr(object, name, default):
195198
""" Like getattr but return default upon any Exception.
196199
@@ -201,3 +204,13 @@ def safe_getattr(object, name, default):
201204
return getattr(object, name, default)
202205
except Exception:
203206
return default
207+
208+
209+
def _is_unittest_unexpected_success_a_failure():
210+
"""Return if the test suite should fail if a @expectedFailure unittest test PASSES.
211+
212+
From https://docs.python.org/3/library/unittest.html?highlight=unittest#unittest.TestResult.wasSuccessful:
213+
Changed in version 3.4: Returns False if there were any
214+
unexpectedSuccesses from tests marked with the expectedFailure() decorator.
215+
"""
216+
return sys.version_info >= (3, 4)

_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: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -224,9 +224,16 @@ def pytest_runtest_makereport(item, call):
224224
evalskip = getattr(item, '_evalskip', None)
225225
# unitttest special case, see setting of _unexpectedsuccess
226226
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"
227+
from _pytest.compat import _is_unittest_unexpected_success_a_failure
228+
if item._unexpectedsuccess:
229+
rep.longrepr = "Unexpected success: {0}".format(item._unexpectedsuccess)
230+
else:
231+
rep.longrepr = "Unexpected success"
232+
if _is_unittest_unexpected_success_a_failure():
233+
rep.outcome = "failed"
234+
else:
235+
rep.outcome = "passed"
236+
rep.wasxfail = rep.longrepr
230237
elif item.config.option.runxfail:
231238
pass # don't interefere
232239
elif call.excinfo and call.excinfo.errisinstance(pytest.xfail.Exception):
@@ -241,8 +248,15 @@ def pytest_runtest_makereport(item, call):
241248
rep.outcome = "skipped"
242249
rep.wasxfail = evalxfail.getexplanation()
243250
elif call.when == "call":
244-
rep.outcome = "failed" # xpass outcome
245-
rep.wasxfail = evalxfail.getexplanation()
251+
strict_default = item.config.getini('xfail_strict')
252+
is_strict_xfail = evalxfail.get('strict', strict_default)
253+
explanation = evalxfail.getexplanation()
254+
if is_strict_xfail:
255+
rep.outcome = "failed"
256+
rep.longrepr = "[XPASS(strict)] {0}".format(explanation)
257+
else:
258+
rep.outcome = "passed"
259+
rep.wasxfail = explanation
246260
elif evalskip is not None and rep.skipped and type(rep.longrepr) is tuple:
247261
# skipped by mark.skipif; change the location of the failure
248262
# to point to the item definition, otherwise it will display
@@ -256,7 +270,7 @@ def pytest_report_teststatus(report):
256270
if hasattr(report, "wasxfail"):
257271
if report.skipped:
258272
return "xfailed", "x", "xfail"
259-
elif report.failed:
273+
elif report.passed:
260274
return "xpassed", "X", ("XPASS", {'yellow': True})
261275

262276
# 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 = """

0 commit comments

Comments
 (0)