diff options
author | Nicolas "Pixel" Noble <pixel@nobis-crew.org> | 2015-06-26 02:04:12 +0200 |
---|---|---|
committer | Nicolas "Pixel" Noble <pixel@nobis-crew.org> | 2015-06-26 03:32:16 +0200 |
commit | 5937b5bc5a6ff73b6e64cdda1c2f76fe1d2aa621 (patch) | |
tree | aeccd4ec035e0efc71d62e92a940de55a913b519 /tools/run_tests | |
parent | 252d25100229b167535d34f4999b59e6651bb29a (diff) |
Exporting XML reports, JUnit-compatible.
Diffstat (limited to 'tools/run_tests')
-rwxr-xr-x | tools/run_tests/jobset.py | 31 | ||||
-rwxr-xr-x | tools/run_tests/run_tests.py | 18 |
2 files changed, 39 insertions, 10 deletions
diff --git a/tools/run_tests/jobset.py b/tools/run_tests/jobset.py index 058a30d1ce..8694b8f6bd 100755 --- a/tools/run_tests/jobset.py +++ b/tools/run_tests/jobset.py @@ -34,10 +34,12 @@ import multiprocessing import os import platform import signal +import string import subprocess import sys import tempfile import time +import xml.etree.cElementTree as ET _DEFAULT_MAX_JOBS = 16 * multiprocessing.cpu_count() @@ -159,7 +161,7 @@ class JobSpec(object): class Job(object): """Manages one job.""" - def __init__(self, spec, bin_hash, newline_on_success, travis): + def __init__(self, spec, bin_hash, newline_on_success, travis, xml_report): self._spec = spec self._bin_hash = bin_hash self._tempfile = tempfile.TemporaryFile() @@ -176,19 +178,27 @@ class Job(object): self._state = _RUNNING self._newline_on_success = newline_on_success self._travis = travis + self._xml_test = ET.SubElement(xml_report, 'testcase', + name=self._spec.shortname) if xml_report is not None else None message('START', spec.shortname, do_newline=self._travis) def state(self, update_cache): """Poll current state of the job. Prints messages at completion.""" if self._state == _RUNNING and self._process.poll() is not None: elapsed = time.time() - self._start + self._tempfile.seek(0) + stdout = self._tempfile.read() + filtered_stdout = filter(lambda x: x in string.printable, stdout.decode(errors='ignore')) + if self._xml_test is not None: + self._xml_test.set('time', str(elapsed)) + ET.SubElement(self._xml_test, 'system-out').text = filtered_stdout if self._process.returncode != 0: self._state = _FAILURE - self._tempfile.seek(0) - stdout = self._tempfile.read() message('FAILED', '%s [ret=%d, pid=%d]' % ( self._spec.shortname, self._process.returncode, self._process.pid), stdout, do_newline=True) + if self._xml_test is not None: + ET.SubElement(self._xml_test, 'failure', message='Failure').text else: self._state = _SUCCESS message('PASSED', '%s [time=%.1fsec]' % (self._spec.shortname, elapsed), @@ -200,6 +210,9 @@ class Job(object): stdout = self._tempfile.read() message('TIMEOUT', self._spec.shortname, stdout, do_newline=True) self.kill() + if self._xml_test is not None: + ET.SubElement(self._xml_test, 'system-out').text = stdout + ET.SubElement(self._xml_test, 'error', message='Timeout') return self._state def kill(self): @@ -212,7 +225,7 @@ class Jobset(object): """Manages one run of jobs.""" def __init__(self, check_cancelled, maxjobs, newline_on_success, travis, - stop_on_failure, cache): + stop_on_failure, cache, xml_report): self._running = set() self._check_cancelled = check_cancelled self._cancelled = False @@ -224,6 +237,7 @@ class Jobset(object): self._cache = cache self._stop_on_failure = stop_on_failure self._hashes = {} + self._xml_report = xml_report def start(self, spec): """Start a job. Return True on success, False on failure.""" @@ -250,7 +264,8 @@ class Jobset(object): self._running.add(Job(spec, bin_hash, self._newline_on_success, - self._travis)) + self._travis, + self._xml_report)) except: message('FAILED', spec.shortname) self._cancelled = True @@ -324,11 +339,13 @@ def run(cmdlines, travis=False, infinite_runs=False, stop_on_failure=False, - cache=None): + cache=None, + xml_report=None): js = Jobset(check_cancelled, maxjobs if maxjobs is not None else _DEFAULT_MAX_JOBS, newline_on_success, travis, stop_on_failure, - cache if cache is not None else NoCache()) + cache if cache is not None else NoCache(), + xml_report) for cmdline in cmdlines: if not js.start(cmdline): break diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py index 5ed70f0b7e..9edef4603c 100755 --- a/tools/run_tests/run_tests.py +++ b/tools/run_tests/run_tests.py @@ -42,6 +42,7 @@ import re import subprocess import sys import time +import xml.etree.cElementTree as ET import jobset import watch_dirs @@ -396,6 +397,8 @@ argp.add_argument('-S', '--stop_on_failure', action='store_const', const=True) argp.add_argument('-a', '--antagonists', default=0, type=int) +argp.add_argument('-x', '--xml_report', default=None, type=str, + help='Generates a JUnit-compatible XML report') args = argp.parse_args() # grab config @@ -492,7 +495,7 @@ class TestCache(object): self.parse(json.loads(f.read())) -def _build_and_run(check_cancelled, newline_on_success, travis, cache): +def _build_and_run(check_cancelled, newline_on_success, travis, cache, xml_report): """Do one pass of building & running tests.""" # build latest sequentially if not jobset.run(build_steps, maxjobs=1, @@ -518,16 +521,24 @@ def _build_and_run(check_cancelled, newline_on_success, travis, cache): runs_sequence = (itertools.repeat(massaged_one_run) if infinite_runs else itertools.repeat(massaged_one_run, runs_per_test)) all_runs = itertools.chain.from_iterable(runs_sequence) + + root = ET.Element('testsuites') if xml_report else None + testsuite = ET.SubElement(root, 'testsuite', id='1', package='grpc', name='tests') if xml_report else None + if not jobset.run(all_runs, check_cancelled, newline_on_success=newline_on_success, travis=travis, infinite_runs=infinite_runs, maxjobs=args.jobs, stop_on_failure=args.stop_on_failure, - cache=cache): + cache=cache if not xml_report else None, + xml_report=testsuite): return 2 finally: for antagonist in antagonists: antagonist.kill() + if xml_report: + tree = ET.ElementTree(root) + tree.write(xml_report, encoding='UTF-8') if cache: cache.save() @@ -559,7 +570,8 @@ else: result = _build_and_run(check_cancelled=lambda: False, newline_on_success=args.newline_on_success, travis=args.travis, - cache=test_cache) + cache=test_cache, + xml_report=args.xml_report) if result == 0: jobset.message('SUCCESS', 'All tests passed', do_newline=True) else: |