• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2
3import argparse
4import json
5import sys
6import unicodedata
7import xml.etree.ElementTree as ET
8from datetime import datetime
9
10
11aparser = argparse.ArgumentParser(
12    description='Convert Meson test log into JUnit report')
13aparser.add_argument('--project-name', metavar='NAME',
14                     help='The project name',
15                     default='unknown')
16aparser.add_argument('--job-id', metavar='ID',
17                     help='The job ID for the report',
18                     default='Unknown')
19aparser.add_argument('--branch', metavar='NAME',
20                     help='Branch of the project being tested',
21                     default='master')
22aparser.add_argument('--output', metavar='FILE',
23                     help='The output file, stdout by default',
24                     type=argparse.FileType('w', encoding='UTF-8'),
25                     default=sys.stdout)
26aparser.add_argument('infile', metavar='FILE',
27                     help='The input testlog.json, stdin by default',
28                     type=argparse.FileType('r', encoding='UTF-8'),
29                     default=sys.stdin)
30args = aparser.parse_args()
31
32outfile = args.output
33
34testsuites = ET.Element('testsuites')
35testsuites.set('id', '{}/{}'.format(args.job_id, args.branch))
36testsuites.set('package', args.project_name)
37testsuites.set('timestamp', datetime.utcnow().isoformat(timespec='minutes'))
38
39testsuite = ET.SubElement(testsuites, 'testsuite')
40testsuite.set('name', args.project_name)
41
42successes = 0
43failures = 0
44skips = 0
45
46
47def escape_control_chars(text):
48    return "".join(c if unicodedata.category(c)[0] != "C" else
49                   "<{:02x}>".format(ord(c)) for c in text)
50
51
52for line in args.infile:
53    unit = json.loads(line)
54
55    testcase = ET.SubElement(testsuite, 'testcase')
56    testcase.set('classname', '{}/{}'.format(args.project_name, unit['name']))
57    testcase.set('name', unit['name'])
58    testcase.set('time', str(unit['duration']))
59
60    stdout = escape_control_chars(unit.get('stdout', ''))
61    stderr = escape_control_chars(unit.get('stderr', ''))
62    if stdout:
63        ET.SubElement(testcase, 'system-out').text = stdout
64    if stderr:
65        ET.SubElement(testcase, 'system-out').text = stderr
66
67    result = unit['result'].lower()
68    if result == 'skip':
69        skips += 1
70        ET.SubElement(testcase, 'skipped')
71    elif result == 'fail':
72        failures += 1
73        failure = ET.SubElement(testcase, 'failure')
74        failure.set('message', "{} failed".format(unit['name']))
75        failure.text = "### stdout\n{}\n### stderr\n{}\n".format(stdout,
76                                                                 stderr)
77    else:
78        successes += 1
79        assert unit['returncode'] == 0
80
81testsuite.set('tests', str(successes + failures + skips))
82testsuite.set('skipped', str(skips))
83testsuite.set('errors', str(failures))
84testsuite.set('failures', str(failures))
85
86print('{}: {} pass, {} fail, {} skip'.format(args.project_name,
87                                             successes,
88                                             failures,
89                                             skips))
90
91output = ET.tostring(testsuites, encoding='unicode')
92outfile.write(output)
93