# HG changeset patch -- Bitbucket.org # Project py-trunk # URL http://bitbucket.org/hpk42/py-trunk/overview # User holger krekel <hol...@merlinux.eu> # Date 1272374143 -7200 # Node ID e368347f7d1342098d61d6abe8b54361e7859b4f # Parent e387b83b6267b3fd7e44b59b899299e528ce6733 (fixes issue85) correctly write non-ascii test output to junitxml files, refine some internal methods for it
--- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Changes between 1.2.1 and 1.2.2 (release ================================================== - 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 - added links to the new capturelog and coverage plugins - (issue87) fix unboundlocal error in assertionold code --- a/testing/plugin/test_pytest_junitxml.py +++ b/testing/plugin/test_pytest_junitxml.py @@ -1,5 +1,6 @@ from xml.dom import minidom +import py def runandparse(testdir, *args): resultpath = testdir.tmpdir.join("junit.xml") @@ -118,6 +119,19 @@ class TestPython: fnode = tnode.getElementsByTagName("skipped")[0] 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: def test_summing_simple(self, testdir): testdir.makeconftest(""" --- a/py/_plugin/pytest_junitxml.py +++ b/py/_plugin/pytest_junitxml.py @@ -43,6 +43,10 @@ class LogXML(object): def _closetestcase(self): 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): self.passed += 1 @@ -51,10 +55,9 @@ class LogXML(object): def append_failure(self, report): self._opentestcase(report) - s = py.xml.escape(str(report.longrepr)) #msg = str(report.longrepr.reprtraceback.extraline) - self.test_logs.append( - '<failure message="test failure">%s</failure>' % (s)) + self.appendlog('<failure message="test failure">%s</failure>', + report.longrepr) self._closetestcase() self.failed += 1 @@ -69,33 +72,30 @@ class LogXML(object): def append_collect_failure(self, report): self._opentestcase_collectfailure(report) - s = py.xml.escape(str(report.longrepr)) #msg = str(report.longrepr.reprtraceback.extraline) - self.test_logs.append( - '<failure message="collection failure">%s</failure>' % (s)) + self.appendlog('<failure message="collection failure">%s</failure>', + report.longrepr) self._closetestcase() self.errors += 1 def append_collect_skipped(self, report): self._opentestcase_collectfailure(report) - s = py.xml.escape(str(report.longrepr)) #msg = str(report.longrepr.reprtraceback.extraline) - self.test_logs.append( - '<skipped message="collection skipped">%s</skipped>' % (s)) + self.appendlog('<skipped message="collection skipped">%s</skipped>', + report.longrepr) self._closetestcase() self.skipped += 1 def append_error(self, report): self._opentestcase(report) - s = py.xml.escape(str(report.longrepr)) - self.test_logs.append( - '<error message="test setup failure">%s</error>' % s) + self.appendlog('<error message="test setup failure">%s</error>', + report.longrepr) self._closetestcase() self.errors += 1 def append_skipped(self, report): self._opentestcase(report) - self.test_logs.append("<skipped/>") + self.appendlog("<skipped/>") self._closetestcase() self.skipped += 1 @@ -126,7 +126,7 @@ class LogXML(object): def pytest_internalerror(self, excrepr): self.errors += 1 - data = py.xml.escape(str(excrepr)) + data = py.xml.escape(excrepr) self.test_logs.append( '\n<testcase classname="pytest" name="internal">' ' <error message="internal error">' @@ -136,7 +136,11 @@ class LogXML(object): self.suite_start_time = time.time() 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_time_delta = suite_stop_time - self.suite_start_time numtests = self.passed + self.failed --- a/py/_code/code.py +++ b/py/_code/code.py @@ -417,6 +417,12 @@ class ExceptionInfo(object): loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) 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): """ presenting information about failing Functions and Generators. """ # for traceback entries @@ -579,11 +585,15 @@ class FormattedExcinfo(object): class TerminalRepr: 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) self.toterminal(tw) s = tw.stringio.getvalue().strip() - if sys.version_info[0] < 3: - s = s.encode('utf-8') return s def __repr__(self): --- a/testing/root/test_xmlgen.py +++ b/testing/root/test_xmlgen.py @@ -5,6 +5,25 @@ from py._xmlgen import unicode, html class ns(py.xml.Namespace): 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(): x = ns.hello("world") u = unicode(x) --- a/testing/code/test_code.py +++ b/testing/code/test_code.py @@ -199,3 +199,5 @@ def test_unicode_handling(testdir): raise Exception(value) excinfo = py.test.raises(Exception, f) s = str(excinfo) + if sys.version_info[0] < 3: + u = unicode(excinfo) --- a/py/_xmlgen.py +++ b/py/_xmlgen.py @@ -238,6 +238,7 @@ class _escape: def __call__(self, ustring): """ xml-escape the given unicode string. """ + ustring = unicode(ustring) return self.charef_rex.sub(self._replacer, ustring) escape = _escape() _______________________________________________ py-svn mailing list py-svn@codespeak.net http://codespeak.net/mailman/listinfo/py-svn