fix #59: provide better Jenkins stdout and stderr sections

This commit is contained in:
holger krekel 2011-07-12 23:09:03 +02:00
parent aafe6a8e34
commit f3bc197afb
9 changed files with 52 additions and 18 deletions

View File

@ -1,6 +1,7 @@
Changes between 2.1.0 and 2.1.1.DEV Changes between 2.1.0 and 2.1.1.DEV
---------------------------------------------- ----------------------------------------------
- fix issue59: provide system-out/err tags for junitxml output
- fix assertion rewriting on boolean operations with 3 or more operands - fix assertion rewriting on boolean operations with 3 or more operands
Changes between 2.0.3 and 2.1.0.DEV Changes between 2.0.3 and 2.1.0.DEV

View File

@ -1,2 +1,2 @@
# #
__version__ = '2.1.1.dev1' __version__ = '2.1.1.dev2'

View File

@ -12,12 +12,9 @@ def pytest_addoption(parser):
help="shortcut for --capture=no.") help="shortcut for --capture=no.")
def addouterr(rep, outerr): def addouterr(rep, outerr):
repr = getattr(rep, 'longrepr', None)
if not hasattr(repr, 'addsection'):
return
for secname, content in zip(["out", "err"], outerr): for secname, content in zip(["out", "err"], outerr):
if content: if content:
repr.addsection("Captured std%s" % secname, content.rstrip()) rep.sections.append(("Captured std%s" % secname, content))
def pytest_unconfigure(config): def pytest_unconfigure(config):
# registered in config.py during early conftest.py loading # registered in config.py during early conftest.py loading

View File

@ -114,8 +114,16 @@ class LogXML(object):
'<skipped message="xfail-marked test passes unexpectedly"/>') '<skipped message="xfail-marked test passes unexpectedly"/>')
self.skipped += 1 self.skipped += 1
else: else:
self.appendlog('<failure message="test failure">%s</failure>', sec = dict(report.sections)
report.longrepr) fmt = '<failure message="test failure">%s'
args = [report.longrepr]
for name in ('out', 'err'):
content = sec.get("Captured std%s" % name)
if content:
fmt += "<system-%s>%%s</system-%s>" % (name, name)
args.append(content)
fmt += "</failure>"
self.appendlog(fmt, *args)
self.failed += 1 self.failed += 1
self._closetestcase() self._closetestcase()

View File

@ -167,7 +167,7 @@ class TestReport(BaseReport):
they fail). they fail).
""" """
def __init__(self, nodeid, location, def __init__(self, nodeid, location,
keywords, outcome, longrepr, when): keywords, outcome, longrepr, when, sections=()):
#: normalized collection node id #: normalized collection node id
self.nodeid = nodeid self.nodeid = nodeid
@ -189,6 +189,10 @@ class TestReport(BaseReport):
#: one of 'setup', 'call', 'teardown' to indicate runtest phase. #: one of 'setup', 'call', 'teardown' to indicate runtest phase.
self.when = when self.when = when
#: list of (secname, data) extra information which needs to
#: marshallable
self.sections = list(sections)
def __repr__(self): def __repr__(self):
return "<TestReport %r when=%r outcome=%r>" % ( return "<TestReport %r when=%r outcome=%r>" % (
self.nodeid, self.when, self.outcome) self.nodeid, self.when, self.outcome)
@ -198,6 +202,7 @@ class TeardownErrorReport(BaseReport):
when = "teardown" when = "teardown"
def __init__(self, longrepr): def __init__(self, longrepr):
self.longrepr = longrepr self.longrepr = longrepr
self.sections = []
def pytest_make_collect_report(collector): def pytest_make_collect_report(collector):
call = CallInfo(collector._memocollect, "memocollect") call = CallInfo(collector._memocollect, "memocollect")
@ -219,11 +224,12 @@ def pytest_make_collect_report(collector):
getattr(call, 'result', None)) getattr(call, 'result', None))
class CollectReport(BaseReport): class CollectReport(BaseReport):
def __init__(self, nodeid, outcome, longrepr, result): def __init__(self, nodeid, outcome, longrepr, result, sections=()):
self.nodeid = nodeid self.nodeid = nodeid
self.outcome = outcome self.outcome = outcome
self.longrepr = longrepr self.longrepr = longrepr
self.result = result or [] self.result = result or []
self.sections = list(sections)
@property @property
def location(self): def location(self):

View File

@ -393,7 +393,7 @@ class TerminalReporter:
else: else:
msg = self._getfailureheadline(rep) msg = self._getfailureheadline(rep)
self.write_sep("_", msg) self.write_sep("_", msg)
rep.toterminal(self._tw) self._outrep_summary(rep)
def summary_errors(self): def summary_errors(self):
if self.config.option.tbstyle != "no": if self.config.option.tbstyle != "no":
@ -411,7 +411,15 @@ class TerminalReporter:
elif rep.when == "teardown": elif rep.when == "teardown":
msg = "ERROR at teardown of " + msg msg = "ERROR at teardown of " + msg
self.write_sep("_", msg) self.write_sep("_", msg)
rep.toterminal(self._tw) self._outrep_summary(rep)
def _outrep_summary(self, rep):
rep.toterminal(self._tw)
for secname, content in rep.sections:
self._tw.sep("-", secname)
if content[-1:] == "\n":
content = content[:-1]
self._tw.line(content)
def summary_stats(self): def summary_stats(self):
session_duration = py.std.time.time() - self._sessionstarttime session_duration = py.std.time.time() - self._sessionstarttime

View File

@ -24,7 +24,7 @@ def main():
name='pytest', name='pytest',
description='py.test: simple powerful testing with Python', description='py.test: simple powerful testing with Python',
long_description = long_description, long_description = long_description,
version='2.1.1.dev1', version='2.1.1.dev2',
url='http://pytest.org', url='http://pytest.org',
license='MIT license', license='MIT license',
platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'],
@ -32,7 +32,7 @@ def main():
author_email='holger at merlinux.eu', author_email='holger at merlinux.eu',
entry_points= make_entry_points(), entry_points= make_entry_points(),
# the following should be enabled for release # the following should be enabled for release
install_requires=['py>=1.4.4'], install_requires=['py>=1.4.5.dev1'],
classifiers=['Development Status :: 5 - Production/Stable', classifiers=['Development Status :: 5 - Production/Stable',
'Intended Audience :: Developers', 'Intended Audience :: Developers',
'License :: OSI Approved :: MIT License', 'License :: OSI Approved :: MIT License',

View File

@ -131,7 +131,14 @@ class TestPython:
assert "Division" in fnode.toxml() assert "Division" in fnode.toxml()
def test_failure_function(self, testdir): def test_failure_function(self, testdir):
testdir.makepyfile("def test_fail(): raise ValueError(42)") testdir.makepyfile("""
import sys
def test_fail():
print ("hello-stdout")
sys.stderr.write("hello-stderr\\n")
raise ValueError(42)
""")
result, dom = runandparse(testdir) result, dom = runandparse(testdir)
assert result.ret assert result.ret
node = dom.getElementsByTagName("testsuite")[0] node = dom.getElementsByTagName("testsuite")[0]
@ -143,6 +150,10 @@ class TestPython:
fnode = tnode.getElementsByTagName("failure")[0] fnode = tnode.getElementsByTagName("failure")[0]
assert_attr(fnode, message="test failure") assert_attr(fnode, message="test failure")
assert "ValueError" in fnode.toxml() assert "ValueError" in fnode.toxml()
systemout = fnode.getElementsByTagName("system-out")[0]
assert "hello-stdout" in systemout.toxml()
systemerr = fnode.getElementsByTagName("system-err")[0]
assert "hello-stderr" in systemerr.toxml()
def test_failure_escape(self, testdir): def test_failure_escape(self, testdir):
testdir.makepyfile(""" testdir.makepyfile("""

View File

@ -258,10 +258,13 @@ class TestCollectonly:
def test_repr_python_version(monkeypatch): def test_repr_python_version(monkeypatch):
monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0)) try:
assert repr_pythonversion() == "2.5.1-final-0" monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0))
py.std.sys.version_info = x = (2,3) assert repr_pythonversion() == "2.5.1-final-0"
assert repr_pythonversion() == str(x) py.std.sys.version_info = x = (2,3)
assert repr_pythonversion() == str(x)
finally:
monkeypatch.undo() # do this early as pytest can get confused
class TestFixtureReporting: class TestFixtureReporting:
def test_setup_fixture_error(self, testdir): def test_setup_fixture_error(self, testdir):