From c8d78177b9ec2c4eb449c387e8941244d8b5c7f9 Mon Sep 17 00:00:00 2001 From: holger krekel Date: Tue, 27 Apr 2010 15:15:43 +0200 Subject: [PATCH] (fixes issue85) correctly write non-ascii test output to junitxml files, refine some internal methods for it --HG-- branch : trunk --- CHANGELOG | 1 + py/_code/code.py | 14 +++++++++-- py/_plugin/pytest_junitxml.py | 34 ++++++++++++++------------ py/_xmlgen.py | 1 + testing/code/test_code.py | 2 ++ testing/plugin/test_pytest_junitxml.py | 14 +++++++++++ testing/root/test_xmlgen.py | 19 ++++++++++++++ 7 files changed, 68 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 7f1232366..778a8aa55 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,7 @@ Changes between 1.2.1 and 1.2.2 (release pending) ================================================== - 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 diff --git a/py/_code/code.py b/py/_code/code.py index 7cfec853b..55ff38981 100644 --- 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): diff --git a/py/_plugin/pytest_junitxml.py b/py/_plugin/pytest_junitxml.py index cdad69881..674c80637 100644 --- 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("") + + 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( - '%s' % (s)) + self.appendlog('%s', + 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( - '%s' % (s)) + self.appendlog('%s', + 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( - '%s' % (s)) + self.appendlog('%s', + 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( - '%s' % s) + self.appendlog('%s', + report.longrepr) self._closetestcase() self.errors += 1 def append_skipped(self, report): self._opentestcase(report) - self.test_logs.append("") + self.appendlog("") 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' ' ' @@ -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 diff --git a/py/_xmlgen.py b/py/_xmlgen.py index 2d1f63350..854fbccae 100644 --- 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() diff --git a/testing/code/test_code.py b/testing/code/test_code.py index 583fc7852..add389762 100644 --- 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) diff --git a/testing/plugin/test_pytest_junitxml.py b/testing/plugin/test_pytest_junitxml.py index 65597c535..8e5e8a57f 100644 --- 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(""" diff --git a/testing/root/test_xmlgen.py b/testing/root/test_xmlgen.py index dc076332b..e566e07e4 100644 --- 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)