# vim: set fileencoding=utf-8 ft=python sw=4 ts=4 :
"""Format UCS Test results as JUnit report."""
from __future__ import print_function
import errno
import os
import sys
from codecs import encode as _encode
from datetime import datetime
from typing import IO, Any # noqa: F401
from xml.sax.saxutils import XMLGenerator
import six
from univention.testing.codes import TestCodes
from univention.testing.data import TestCase, TestFormatInterface, TestResult # noqa: F401
from univention.testing.format.text import Raw
__all__ = ['Junit']
def encode(string, *args): # type: (str, *Any) -> str
if six.PY2:
return _encode(string, *args)
return string
[docs]class Junit(TestFormatInterface):
"""
Create Junit report.
<http://windyroad.org/dl/Open%20Source/JUnit.xsd>
"""
def __init__(self, stream=sys.stdout): # type: (IO[str]) -> None
super(Junit, self).__init__(stream)
self.outdir = "test-reports"
self.now = datetime.today()
self.raw = Raw(stream)
[docs] def begin_test(self, case, prefix=''): # type: (TestCase, str) -> None
"""Called before each test."""
super(Junit, self).begin_test(case, prefix)
self.now = datetime.today().replace(microsecond=0)
print('\r', end='', file=self.stream)
self.raw.begin_test(case, prefix)
self.stream.flush()
[docs] def end_run(self):
print('') # clear \r
self.stream.flush()
[docs] def end_test(self, result): # type: (TestResult) -> None
"""Called after each test."""
failures = errors = skipped = disabled = 0
if result.eofs == 'O':
pass
elif result.eofs == 'S':
skipped = 1
elif result.eofs == 'F':
failures = 1
elif result.eofs == 'E':
errors = 1
else:
errors = 1
classname = encode(result.case.uid.replace("/", "."))
if classname.endswith('.py'):
classname = classname[:-3]
filename = os.path.join(self.outdir, '%s.xml' % (result.case.uid,))
if result.case.is_pytest and os.path.exists(filename):
return # pytest itself already writes the junit file! create one if pytest did not
dirname = os.path.dirname(filename)
try:
os.makedirs(dirname)
except OSError as ex:
if ex.errno != errno.EEXIST:
raise
with open(filename, 'w') as f_report:
xml = XMLGenerator(f_report, encoding='utf-8')
xml.startDocument()
xml.startElement('testsuite', {
'name': classname,
'tests': '%d' % (1,),
'failures': '%d' % (failures,),
'errors': '%d' % (errors,),
'time': '%0.3f' % (result.duration / 1000.0,),
'disabled': '%d' % (disabled,),
'skipped': '%d' % (skipped,),
'timestamp': self.now.isoformat(),
'hostname': os.uname()[1],
})
xml.startElement('properties', {})
xml.startElement('property', {
'name': 'hostname',
'value': result.environment.hostname,
})
xml.endElement('property')
xml.startElement('property', {
'name': 'architecture',
'value': result.environment.architecture,
})
xml.endElement('property')
xml.startElement('property', {
'name': 'role',
'value': result.environment.role,
})
xml.endElement('property')
xml.startElement('property', {
'name': 'version',
'value': '%s' % (result.environment.ucs_version,),
})
xml.endElement('property')
if result.case.description:
xml.startElement('property', {
'name': 'description',
'value': encode(result.case.description or result.case.uid),
})
xml.endElement('property')
xml.endElement('properties')
xml.startElement('testcase', {
'name': result.environment.hostname,
# 'assertions': '%d' % (0,),
'time': '%0.3f' % (result.duration / 1000.0,),
'classname': classname,
# 'status': '???',
})
if skipped:
try:
mime, content = result.artifacts['check']
except KeyError:
msg = ''
else:
msg = '\n'.join(['%s' % (c,) for c in content])
xml.startElement('skipped', {
'message': msg,
})
xml.endElement('skipped')
elif errors:
xml.startElement('error', {
'type': 'TestError',
'message': '%s' % (result.result,),
})
xml.endElement('error')
elif failures:
msg = TestCodes.MESSAGE.get(result.reason, '')
xml.startElement('failure', {
'type': 'TestFailure',
'message': '{} ({})'.format(msg, result.case.description or result.case.uid),
})
xml.endElement('failure')
try:
mime, content = result.artifacts['stdout']
except KeyError:
pass
else:
xml.startElement('system-out', {})
xml.characters(self.utf8(content))
xml.endElement('system-out')
try:
mime, content = result.artifacts['stderr']
except KeyError:
pass
else:
xml.startElement('system-err', {})
xml.characters(self.utf8(content))
xml.endElement('system-err')
xml.endElement('testcase')
xml.endElement('testsuite')
xml.endDocument()
super(Junit, self).end_test(result)
[docs] def utf8(self, data): # type: (Any) -> str
if isinstance(data, six.text_type):
data = data.encode('utf-8', 'replace').decode('utf-8')
elif isinstance(data, bytes):
data = data.decode('utf-8', 'replace').encode('utf-8')
return data
if __name__ == '__main__':
import doctest
doctest.testmod()