use py.xml for generating the junitxml files
This commit is contained in:
		
							parent
							
								
									0c8e71faa5
								
							
						
					
					
						commit
						96cb1208d3
					
				|  | @ -30,6 +30,7 @@ Changes between 2.1.3 and 2.2.0 | ||||||
| - fix issue74: pyarg module names are now checked against imp.find_module false positives | - fix issue74: pyarg module names are now checked against imp.find_module false positives | ||||||
| - fix compatibility with twisted/trial-11.1.0 use cases | - fix compatibility with twisted/trial-11.1.0 use cases | ||||||
| - simplify Node.listchain | - simplify Node.listchain | ||||||
|  | - simplify junitxml output code by relying on py.xml | ||||||
| 
 | 
 | ||||||
| Changes between 2.1.2 and 2.1.3 | Changes between 2.1.2 and 2.1.3 | ||||||
| ---------------------------------------- | ---------------------------------------- | ||||||
|  |  | ||||||
|  | @ -25,6 +25,10 @@ except NameError: | ||||||
|     long = int |     long = int | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | class Junit(py.xml.Namespace): | ||||||
|  |     pass | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| # We need to get the subset of the invalid unicode ranges according to | # We need to get the subset of the invalid unicode ranges according to | ||||||
| # XML 1.0 which are valid in this python build.  Hence we calculate | # XML 1.0 which are valid in this python build.  Hence we calculate | ||||||
| # this dynamically instead of hardcoding it.  The spec range of valid | # this dynamically instead of hardcoding it.  The spec range of valid | ||||||
|  | @ -40,6 +44,14 @@ illegal_xml_re = re.compile(unicode('[%s]') % | ||||||
| del _illegal_unichrs | del _illegal_unichrs | ||||||
| del _illegal_ranges | del _illegal_ranges | ||||||
| 
 | 
 | ||||||
|  | def bin_xml_escape(arg): | ||||||
|  |     def repl(matchobj): | ||||||
|  |         i = ord(matchobj.group()) | ||||||
|  |         if i <= 0xFF: | ||||||
|  |             return unicode('#x%02X') % i | ||||||
|  |         else: | ||||||
|  |             return unicode('#x%04X') % i | ||||||
|  |     return illegal_xml_re.sub(repl, py.xml.escape(arg)) | ||||||
| 
 | 
 | ||||||
| def pytest_addoption(parser): | def pytest_addoption(parser): | ||||||
|     group = parser.getgroup("terminal reporting") |     group = parser.getgroup("terminal reporting") | ||||||
|  | @ -68,116 +80,97 @@ class LogXML(object): | ||||||
|         logfile = os.path.expanduser(os.path.expandvars(logfile)) |         logfile = os.path.expanduser(os.path.expandvars(logfile)) | ||||||
|         self.logfile = os.path.normpath(logfile) |         self.logfile = os.path.normpath(logfile) | ||||||
|         self.prefix = prefix |         self.prefix = prefix | ||||||
|         self.test_logs = [] |         self.tests = [] | ||||||
|         self.passed = self.skipped = 0 |         self.passed = self.skipped = 0 | ||||||
|         self.failed = self.errors = 0 |         self.failed = self.errors = 0 | ||||||
| 
 | 
 | ||||||
|     def _opentestcase(self, report): |     def _opentestcase(self, report): | ||||||
|         names = report.nodeid.split("::") |         names = report.nodeid.split("::") | ||||||
|         names[0] = names[0].replace("/", '.') |         names[0] = names[0].replace("/", '.') | ||||||
|         names = tuple(names) |  | ||||||
|         d = {'time': getattr(report, 'duration', 0)} |  | ||||||
|         names = [x.replace(".py", "") for x in names if x != "()"] |         names = [x.replace(".py", "") for x in names if x != "()"] | ||||||
|         classnames = names[:-1] |         classnames = names[:-1] | ||||||
|         if self.prefix: |         if self.prefix: | ||||||
|             classnames.insert(0, self.prefix) |             classnames.insert(0, self.prefix) | ||||||
|         d['classname'] = ".".join(classnames) |         self.tests.append(Junit.testcase( | ||||||
|         d['name'] = py.xml.escape(names[-1]) |             classname=".".join(classnames), | ||||||
|         attrs = ['%s="%s"' % item for item in sorted(d.items())] |             name=names[-1], | ||||||
|         self.test_logs.append("\n<testcase %s>" % " ".join(attrs)) |             time=getattr(report, 'duration', 0) | ||||||
|  |         )) | ||||||
| 
 | 
 | ||||||
|     def _closetestcase(self): |     def append(self, obj): | ||||||
|         self.test_logs.append("</testcase>") |         self.tests[-1].append(obj) | ||||||
| 
 |  | ||||||
|     def appendlog(self, fmt, *args): |  | ||||||
|         def repl(matchobj): |  | ||||||
|             i = ord(matchobj.group()) |  | ||||||
|             if i <= 0xFF: |  | ||||||
|                 return unicode('#x%02X') % i |  | ||||||
|             else: |  | ||||||
|                 return unicode('#x%04X') % i |  | ||||||
|         args = tuple([illegal_xml_re.sub(repl, 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._closetestcase() |  | ||||||
| 
 | 
 | ||||||
|     def append_failure(self, report): |     def append_failure(self, report): | ||||||
|         self._opentestcase(report) |  | ||||||
|         #msg = str(report.longrepr.reprtraceback.extraline) |         #msg = str(report.longrepr.reprtraceback.extraline) | ||||||
|         if "xfail" in report.keywords: |         if "xfail" in report.keywords: | ||||||
|             self.appendlog( |             self.append( | ||||||
|                 '<skipped message="xfail-marked test passes unexpectedly"/>') |                 Junit.skipped(message="xfail-marked test passes unexpectedly")) | ||||||
|             self.skipped += 1 |             self.skipped += 1 | ||||||
|         else: |         else: | ||||||
|             sec = dict(report.sections) |             sec = dict(report.sections) | ||||||
|             self.appendlog('<failure message="test failure">%s</failure>', |             fail = Junit.failure(message="test failure") | ||||||
|                 report.longrepr) |             fail.append(str(report.longrepr)) | ||||||
|  |             self.append(fail) | ||||||
|             for name in ('out', 'err'): |             for name in ('out', 'err'): | ||||||
|                 content = sec.get("Captured std%s" % name) |                 content = sec.get("Captured std%s" % name) | ||||||
|                 if content: |                 if content: | ||||||
|                     self.appendlog( |                     tag = getattr(Junit, 'system-'+name) | ||||||
|                         "<system-%s>%%s</system-%s>" % (name, name), content) |                     self.append(tag(bin_xml_escape(content))) | ||||||
|             self.failed += 1 |             self.failed += 1 | ||||||
|         self._closetestcase() |  | ||||||
| 
 | 
 | ||||||
|     def append_collect_failure(self, report): |     def append_collect_failure(self, report): | ||||||
|         self._opentestcase(report) |  | ||||||
|         #msg = str(report.longrepr.reprtraceback.extraline) |         #msg = str(report.longrepr.reprtraceback.extraline) | ||||||
|         self.appendlog('<failure message="collection failure">%s</failure>', |         self.append(Junit.failure(str(report.longrepr), | ||||||
|             report.longrepr) |                                   message="collection failure")) | ||||||
|         self._closetestcase() |  | ||||||
|         self.errors += 1 |         self.errors += 1 | ||||||
| 
 | 
 | ||||||
|     def append_collect_skipped(self, report): |     def append_collect_skipped(self, report): | ||||||
|         self._opentestcase(report) |  | ||||||
|         #msg = str(report.longrepr.reprtraceback.extraline) |         #msg = str(report.longrepr.reprtraceback.extraline) | ||||||
|         self.appendlog('<skipped message="collection skipped">%s</skipped>', |         self.append(Junit.skipped(str(report.longrepr), | ||||||
|             report.longrepr) |                                   message="collection skipped")) | ||||||
|         self._closetestcase() |  | ||||||
|         self.skipped += 1 |         self.skipped += 1 | ||||||
| 
 | 
 | ||||||
|     def append_error(self, report): |     def append_error(self, report): | ||||||
|         self._opentestcase(report) |         self.append(Junit.error(str(report.longrepr), | ||||||
|         self.appendlog('<error message="test setup failure">%s</error>', |                                 message="test setup failure")) | ||||||
|             report.longrepr) |  | ||||||
|         self._closetestcase() |  | ||||||
|         self.errors += 1 |         self.errors += 1 | ||||||
| 
 | 
 | ||||||
|     def append_skipped(self, report): |     def append_skipped(self, report): | ||||||
|         self._opentestcase(report) |  | ||||||
|         if "xfail" in report.keywords: |         if "xfail" in report.keywords: | ||||||
|             self.appendlog( |             self.append(Junit.skipped(str(report.keywords['xfail']), | ||||||
|                 '<skipped message="expected test failure">%s</skipped>', |                                       message="expected test failure")) | ||||||
|                 report.keywords['xfail']) |  | ||||||
|         else: |         else: | ||||||
|             filename, lineno, skipreason = report.longrepr |             filename, lineno, skipreason = report.longrepr | ||||||
|             if skipreason.startswith("Skipped: "): |             if skipreason.startswith("Skipped: "): | ||||||
|                 skipreason = skipreason[9:] |                 skipreason = skipreason[9:] | ||||||
|             self.appendlog('<skipped type="pytest.skip" ' |             self.append( | ||||||
|                            'message="%s">%s</skipped>', |                 Junit.skipped("%s:%s: %s" % report.longrepr, | ||||||
|                 skipreason, "%s:%s: %s" % report.longrepr, |                               type="pytest.skip", | ||||||
|                 ) |                               message=skipreason | ||||||
|         self._closetestcase() |                 )) | ||||||
|         self.skipped += 1 |         self.skipped += 1 | ||||||
| 
 | 
 | ||||||
|     def pytest_runtest_logreport(self, report): |     def pytest_runtest_logreport(self, report): | ||||||
|         if report.passed: |         if report.passed: | ||||||
|             if report.when == "call": # ignore setup/teardown |             if report.when == "call": # ignore setup/teardown | ||||||
|  |                 self._opentestcase(report) | ||||||
|                 self.append_pass(report) |                 self.append_pass(report) | ||||||
|         elif report.failed: |         elif report.failed: | ||||||
|  |             self._opentestcase(report) | ||||||
|             if report.when != "call": |             if report.when != "call": | ||||||
|                 self.append_error(report) |                 self.append_error(report) | ||||||
|             else: |             else: | ||||||
|                 self.append_failure(report) |                 self.append_failure(report) | ||||||
|         elif report.skipped: |         elif report.skipped: | ||||||
|  |             self._opentestcase(report) | ||||||
|             self.append_skipped(report) |             self.append_skipped(report) | ||||||
| 
 | 
 | ||||||
|     def pytest_collectreport(self, report): |     def pytest_collectreport(self, report): | ||||||
|         if not report.passed: |         if not report.passed: | ||||||
|  |             self._opentestcase(report) | ||||||
|             if report.failed: |             if report.failed: | ||||||
|                 self.append_collect_failure(report) |                 self.append_collect_failure(report) | ||||||
|             else: |             else: | ||||||
|  | @ -186,10 +179,11 @@ class LogXML(object): | ||||||
|     def pytest_internalerror(self, excrepr): |     def pytest_internalerror(self, excrepr): | ||||||
|         self.errors += 1 |         self.errors += 1 | ||||||
|         data = py.xml.escape(excrepr) |         data = py.xml.escape(excrepr) | ||||||
|         self.test_logs.append( |         self.tests.append( | ||||||
|             '\n<testcase classname="pytest" name="internal">' |             Junit.testcase( | ||||||
|             '    <error message="internal error">' |                     Junit.error(data, message="internal error"), | ||||||
|             '%s</error></testcase>' % data) |                     classname="pytest", | ||||||
|  |                     name="internal")) | ||||||
| 
 | 
 | ||||||
|     def pytest_sessionstart(self, session): |     def pytest_sessionstart(self, session): | ||||||
|         self.suite_start_time = time.time() |         self.suite_start_time = time.time() | ||||||
|  | @ -203,17 +197,17 @@ class LogXML(object): | ||||||
|         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 | ||||||
|  | 
 | ||||||
|         logfile.write('<?xml version="1.0" encoding="utf-8"?>') |         logfile.write('<?xml version="1.0" encoding="utf-8"?>') | ||||||
|         logfile.write('<testsuite ') |         logfile.write(Junit.testsuite( | ||||||
|         logfile.write('name="" ') |             self.tests, | ||||||
|         logfile.write('errors="%i" ' % self.errors) |             name="", | ||||||
|         logfile.write('failures="%i" ' % self.failed) |             errors=self.errors, | ||||||
|         logfile.write('skips="%i" ' % self.skipped) |             failures=self.failed, | ||||||
|         logfile.write('tests="%i" ' % numtests) |             skips=self.skipped, | ||||||
|         logfile.write('time="%.3f"' % suite_time_delta) |             tests=numtests, | ||||||
|         logfile.write(' >') |             time="%.3f" % suite_time_delta, | ||||||
|         logfile.writelines(self.test_logs) |         ).unicode(indent=0)) | ||||||
|         logfile.write('</testsuite>') |  | ||||||
|         logfile.close() |         logfile.close() | ||||||
| 
 | 
 | ||||||
|     def pytest_terminal_summary(self, terminalreporter): |     def pytest_terminal_summary(self, terminalreporter): | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue