diff --git a/harbomaster/__init__.py b/harbomaster/__init__.py index 31eec01..1602326 100644 --- a/harbomaster/__init__.py +++ b/harbomaster/__init__.py @@ -1,26 +1,26 @@ # for the module import sys as __hbm_sys def export(definition): """ Decorator to export definitions from sub-modules to the top-level package :param definition: definition to be exported :return: definition """ __module = __hbm_sys.modules[definition.__module__] __pkg = __hbm_sys.modules[__module.__package__] __pkg.__dict__[definition.__name__] = definition if '__all__' not in __pkg.__dict__: __pkg.__dict__['__all__'] = [] __pkg.__all__.append(definition.__name__) return definition -from . import ctestresults # noqa +from . import testresults # noqa from . import arclint # noqa from . import hbm # noqa diff --git a/harbomaster/ctestresults.py b/harbomaster/ctestresults.py deleted file mode 100644 index ecce89a..0000000 --- a/harbomaster/ctestresults.py +++ /dev/null @@ -1,51 +0,0 @@ -import xml.etree.ElementTree as xml_etree -from .results import Results -from . import export - - -@export -class CTestResults: - STATUS = {'passed': Results.PASS, - 'failed': Results.FAIL} - - def __init__(self, filename): - self._file = open(filename, "r") - self._etree = xml_etree.parse(self._file) - self._root = self._etree.getroot() - self.test_format = 'CTest' - - def __iter__(self): - self._tests = iter(self._root.findall('./Testing/Test')) - return self - - def __next__(self): - class Test: - def __init__(self, element): - self.name = element.find('Name').text - self.path = element.find('FullName').text - self.status = CTestResults.STATUS[element.attrib['Status']] - self.duration = float(element.find( - "./Results/NamedMeasurement[@name='Execution Time']/Value").text) - self.reason = None - if self.status == Results.FAIL: - self.reason = element.find( - "./Results/NamedMeasurement[@name='Exit Code']/Value").text - if self.reason == "Timeout": - self.status = Results.BROKEN - else: - self.reason = "{0} with exit code [{1}]\nSTDOUT:\n{2}".format( - self.reason, - element.find( - "./Results/NamedMeasurement[@name='Exit Value']/Value").text, - '\n'.join((el.text for el in element.findall("./Results/Measurement/Value"))), # noqa: E501 - ) - - test = next(self._tests) - - return Test(test) - - def __exit__(self, exc_type, exc_value, traceback): - self._file.close() - - def __enter__(self): - return self diff --git a/harbomaster/testresults.py b/harbomaster/testresults.py new file mode 100644 index 0000000..805ef0e --- /dev/null +++ b/harbomaster/testresults.py @@ -0,0 +1,92 @@ +import xml.etree.ElementTree as xml_etree +from collections import namedtuple + +from .results import Results +from . import export + + +class TestResults: + STATUS = {'passed': Results.PASS, + 'failed': Results.FAIL} + Test = namedtuple('Test', 'name path status duration reason'.split()) + + def __init__(self, filename): + self._file = open(filename, "r") + self._etree = xml_etree.parse(self._file) + self._root = self._etree.getroot() + + def __exit__(self, exc_type, exc_value, traceback): + self._file.close() + + def __enter__(self): + return self + + +@export +class CTestResults(TestResults): + def __init__(self, filename): + super().__init__(self, filename) + self.test_format = 'CTest' + + def __iter__(self): + self._tests = iter(self._root.findall('./Testing/Test')) + return self + + def __next__(self): + element = next(self._tests) + + name = element.find('Name').text + path = element.find('FullName').text + status = CTestResults.STATUS[element.attrib['Status']] + duration = float(element.find( + "./Results/NamedMeasurement[@name='Execution Time']/Value").text) + reason = None + + if status == Results.FAIL: + reason = element.find( + "./Results/NamedMeasurement[@name='Exit Code']/Value").text + if reason == "Timeout": + status = Results.BROKEN + else: + reason = "{0} with exit code [{1}]\nSTDOUT:\n{2}".format( + reason, + element.find( + "./Results/NamedMeasurement[@name='Exit Value']/Value").text, # noqa: E501 + '\n'.join((el.text for el in element.findall("./Results/Measurement/Value"))), # noqa: E501 + ) + + return self.Test(name, path, status, duration, reason) + + +@export +class JUnitTestResults(TestResults): + def __init__(self, filename): + super().__init__(self, filename) + self.test_format = 'JUnit' + + def __iter__(self): + self._tests = iter(self._root.findall('testcase')) + return self + + def __next__(self): + element = next(self._tests) + + name = element.attrib['name'] + path = element.attrib['file'] \ + + ':{}'.format(element.attrib['line']) + duration = element.attrib['time'] + + failure = element.find('failure') + error = element.find('error') + + if failure is not None and error is not None: + status = Results.PASS + + elif error: + status = Results.BROKEN + reason = error.attrib['message'] + '\n' + error.attrib['type'] + elif failure: + status = Results.FAIL + reason = failure.attrib['message'] + '\n' \ + + failure.attrib['type'] + return self.Test(name, path, status, duration, reason) diff --git a/hbm b/hbm index 0ebd246..120d8df 100755 --- a/hbm +++ b/hbm @@ -1,69 +1,80 @@ #!/usr/bin/env python3 import click import harbomaster @click.group() @click.option('-a', '--api-token', default=None, envvar='API_TOKEN') @click.option('-h', '--host', default=None, envvar='PHABRICATOR_HOST') @click.option('-b', '--build-target-phid', envvar='BUILD_TARGET_PHID') @click.pass_context def hbm(ctx, api_token, host, build_target_phid): ctx.obj['API_TOKEN'] = api_token ctx.obj['HOST'] = host ctx.obj['BUILD_TARGET_PHID'] = build_target_phid @hbm.command() @click.option('-f', '--filename') @click.pass_context def send_ctest_results(ctx, filename): try: _hbm = harbomaster.Harbormaster(ctx=ctx.obj) with harbomaster.CTestResults(filename) as tests: _hbm.send_unit_tests(tests) except: pass +@hbm.command() +@click.option('-f', '--filename') +@click.pass_context +def send_junit_results(ctx, filename): + try: + _hbm = harbomaster.Harbormaster(ctx=ctx.obj) + with harbomaster.JUnitTestResults(filename) as tests: + _hbm.send_unit_tests(tests) + except: + pass + @hbm.command() @click.option('-f', '--filename') @click.pass_context def send_arc_lint(ctx, filename): try: _hbm = harbomaster.Harbormaster(ctx=ctx.obj) with harbomaster.ARCLintJson(filename) as tests: _hbm.send_lint(tests) except: pass @hbm.command() @click.option('-k', '--key') @click.option('-u', '--uri') @click.option('-l', '--label') @click.pass_context def send_uri(ctx, key, uri, label): _hbm = harbomaster.Harbormaster(ctx=ctx.obj) _hbm.send_uri(key, uri, label) @hbm.command() @click.option('-f', '--filename') @click.option('-n', '--name') @click.option('-v', '--view_policy', default=None) @click.pass_context def upload_file(ctx, filename, name, view_policy): _hbm = harbomaster.Harbormaster(ctx=ctx.obj) _hbm.upload_file(filename, name, view_policy) @hbm.command() @click.pass_context def passed(ctx): _hbm = harbomaster.Harbormaster(ctx=ctx.obj) _hbm.passed() @hbm.command() @click.pass_context def failed(ctx): _hbm = harbomaster.Harbormaster(ctx=ctx.obj) _hbm.failed() if __name__ == '__main__': hbm(obj={})