(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:
parent
f6a04b92d2
commit
c8d78177b9
|
@ -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
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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("""
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue