(fixes issue85) correctly write non-ascii test output to junitxml files, refine some internal methods for it

--HG--
branch : trunk
This commit is contained in:
holger krekel 2010-04-27 15:15:43 +02:00
parent f6a04b92d2
commit c8d78177b9
7 changed files with 68 additions and 17 deletions

View File

@ -2,6 +2,7 @@ Changes between 1.2.1 and 1.2.2 (release pending)
================================================== ==================================================
- new mechanism to allow plugins to register new hooks - new mechanism to allow plugins to register new hooks
- (issue85) fix junitxml plugin to handle tests with non-ascii output
- fixes for handling of unicode exception values - fixes for handling of unicode exception values
- added links to the new capturelog and coverage plugins - added links to the new capturelog and coverage plugins
- (issue87) fix unboundlocal error in assertionold code - (issue87) fix unboundlocal error in assertionold code

View File

@ -417,6 +417,12 @@ class ExceptionInfo(object):
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return str(loc) return str(loc)
def __unicode__(self):
entry = self.traceback[-1]
loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly())
return unicode(loc)
class FormattedExcinfo(object): class FormattedExcinfo(object):
""" presenting information about failing Functions and Generators. """ """ presenting information about failing Functions and Generators. """
# for traceback entries # for traceback entries
@ -579,11 +585,15 @@ class FormattedExcinfo(object):
class TerminalRepr: class TerminalRepr:
def __str__(self): def __str__(self):
s = self.__unicode__()
if sys.version_info[0] < 3:
s = s.encode('utf-8')
return s
def __unicode__(self):
tw = py.io.TerminalWriter(stringio=True) tw = py.io.TerminalWriter(stringio=True)
self.toterminal(tw) self.toterminal(tw)
s = tw.stringio.getvalue().strip() s = tw.stringio.getvalue().strip()
if sys.version_info[0] < 3:
s = s.encode('utf-8')
return s return s
def __repr__(self): def __repr__(self):

View File

@ -44,6 +44,10 @@ class LogXML(object):
def _closetestcase(self): def _closetestcase(self):
self.test_logs.append("</testcase>") self.test_logs.append("</testcase>")
def appendlog(self, fmt, *args):
args = tuple([py.xml.escape(arg) for arg in args])
self.test_logs.append(fmt % args)
def append_pass(self, report): def append_pass(self, report):
self.passed += 1 self.passed += 1
self._opentestcase(report) self._opentestcase(report)
@ -51,10 +55,9 @@ class LogXML(object):
def append_failure(self, report): def append_failure(self, report):
self._opentestcase(report) self._opentestcase(report)
s = py.xml.escape(str(report.longrepr))
#msg = str(report.longrepr.reprtraceback.extraline) #msg = str(report.longrepr.reprtraceback.extraline)
self.test_logs.append( self.appendlog('<failure message="test failure">%s</failure>',
'<failure message="test failure">%s</failure>' % (s)) report.longrepr)
self._closetestcase() self._closetestcase()
self.failed += 1 self.failed += 1
@ -69,33 +72,30 @@ class LogXML(object):
def append_collect_failure(self, report): def append_collect_failure(self, report):
self._opentestcase_collectfailure(report) self._opentestcase_collectfailure(report)
s = py.xml.escape(str(report.longrepr))
#msg = str(report.longrepr.reprtraceback.extraline) #msg = str(report.longrepr.reprtraceback.extraline)
self.test_logs.append( self.appendlog('<failure message="collection failure">%s</failure>',
'<failure message="collection failure">%s</failure>' % (s)) report.longrepr)
self._closetestcase() self._closetestcase()
self.errors += 1 self.errors += 1
def append_collect_skipped(self, report): def append_collect_skipped(self, report):
self._opentestcase_collectfailure(report) self._opentestcase_collectfailure(report)
s = py.xml.escape(str(report.longrepr))
#msg = str(report.longrepr.reprtraceback.extraline) #msg = str(report.longrepr.reprtraceback.extraline)
self.test_logs.append( self.appendlog('<skipped message="collection skipped">%s</skipped>',
'<skipped message="collection skipped">%s</skipped>' % (s)) report.longrepr)
self._closetestcase() self._closetestcase()
self.skipped += 1 self.skipped += 1
def append_error(self, report): def append_error(self, report):
self._opentestcase(report) self._opentestcase(report)
s = py.xml.escape(str(report.longrepr)) self.appendlog('<error message="test setup failure">%s</error>',
self.test_logs.append( report.longrepr)
'<error message="test setup failure">%s</error>' % s)
self._closetestcase() self._closetestcase()
self.errors += 1 self.errors += 1
def append_skipped(self, report): def append_skipped(self, report):
self._opentestcase(report) self._opentestcase(report)
self.test_logs.append("<skipped/>") self.appendlog("<skipped/>")
self._closetestcase() self._closetestcase()
self.skipped += 1 self.skipped += 1
@ -126,7 +126,7 @@ class LogXML(object):
def pytest_internalerror(self, excrepr): def pytest_internalerror(self, excrepr):
self.errors += 1 self.errors += 1
data = py.xml.escape(str(excrepr)) data = py.xml.escape(excrepr)
self.test_logs.append( self.test_logs.append(
'\n<testcase classname="pytest" name="internal">' '\n<testcase classname="pytest" name="internal">'
' <error message="internal error">' ' <error message="internal error">'
@ -136,7 +136,11 @@ class LogXML(object):
self.suite_start_time = time.time() self.suite_start_time = time.time()
def pytest_sessionfinish(self, session, exitstatus, __multicall__): def pytest_sessionfinish(self, session, exitstatus, __multicall__):
logfile = open(self.logfile, 'w', 1) # line buffered if py.std.sys.version_info[0] < 3:
logfile = py.std.codecs.open(self.logfile, 'w', encoding='utf-8')
else:
logfile = open(self.logfile, 'w', encoding='utf-8')
suite_stop_time = time.time() suite_stop_time = time.time()
suite_time_delta = suite_stop_time - self.suite_start_time suite_time_delta = suite_stop_time - self.suite_start_time
numtests = self.passed + self.failed numtests = self.passed + self.failed

View File

@ -238,6 +238,7 @@ class _escape:
def __call__(self, ustring): def __call__(self, ustring):
""" xml-escape the given unicode string. """ """ xml-escape the given unicode string. """
ustring = unicode(ustring)
return self.charef_rex.sub(self._replacer, ustring) return self.charef_rex.sub(self._replacer, ustring)
escape = _escape() escape = _escape()

View File

@ -199,3 +199,5 @@ def test_unicode_handling(testdir):
raise Exception(value) raise Exception(value)
excinfo = py.test.raises(Exception, f) excinfo = py.test.raises(Exception, f)
s = str(excinfo) s = str(excinfo)
if sys.version_info[0] < 3:
u = unicode(excinfo)

View File

@ -1,5 +1,6 @@
from xml.dom import minidom from xml.dom import minidom
import py
def runandparse(testdir, *args): def runandparse(testdir, *args):
resultpath = testdir.tmpdir.join("junit.xml") resultpath = testdir.tmpdir.join("junit.xml")
@ -118,6 +119,19 @@ class TestPython:
fnode = tnode.getElementsByTagName("skipped")[0] fnode = tnode.getElementsByTagName("skipped")[0]
assert_attr(fnode, message="collection skipped") assert_attr(fnode, message="collection skipped")
def test_unicode(self, testdir):
value = 'hx\xc4\x85\xc4\x87\n'
testdir.makepyfile("""
def test_hello():
print (%r)
assert 0
""" % value)
result, dom = runandparse(testdir)
assert result.ret == 1
tnode = dom.getElementsByTagName("testcase")[0]
fnode = tnode.getElementsByTagName("failure")[0]
assert "hx" in fnode.toxml()
class TestNonPython: class TestNonPython:
def test_summing_simple(self, testdir): def test_summing_simple(self, testdir):
testdir.makeconftest(""" testdir.makeconftest("""

View File

@ -5,6 +5,25 @@ from py._xmlgen import unicode, html
class ns(py.xml.Namespace): class ns(py.xml.Namespace):
pass pass
def test_escape():
uvalue = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8')
class A:
def __unicode__(self):
return uvalue
def __str__(self):
x = self.__unicode__()
if py.std.sys.version_info[0] < 3:
return x.encode('utf-8')
return x
y = py.xml.escape(uvalue)
assert y == uvalue
x = py.xml.escape(A())
assert x == uvalue
if py.std.sys.version_info[0] < 3:
assert isinstance(x, unicode)
assert isinstance(y, unicode)
def test_tag_with_text(): def test_tag_with_text():
x = ns.hello("world") x = ns.hello("world")
u = unicode(x) u = unicode(x)