Meson test log to HTML conversion script
#!/usr/bin/env python3 | |
# meson-junit-report.py: Turns a Meson test log into an HTML report | |
# | |
# Copyright 2019 GNOME Foundation | |
# | |
# SPDX-License-Identifier: LGPL-2.1-or-later | |
import argparse | |
import datetime | |
import json | |
import os | |
import sys | |
from jinja2 import Template | |
REPORT_TEMPLATE = ''' | |
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>{{ report.project_name }} Test Report</title> | |
<meta charset="utf-8" /> | |
</head> | |
<body> | |
<header> | |
<h1>{{ report.project_name }} :: Test Reports</h1> | |
<div class="report-meta"> | |
<p><strong>Branch:</strong> {{ report.branch_name }}</p> | |
<p><strong>Date:</strong> <time datetime="{{ report.date.isoformat() }}">{{ report.locale_date }}</time></p> | |
{% if report.job_id %}<p><strong>Job ID:</strong> {{ report.job_id }}</p>{% endif %} | |
</div> | |
</header> | |
<article> | |
<section> | |
<div class="summary"> | |
<h3>Summary</h3> | |
<ul> | |
<li><strong>Total units:</strong> {{ report.total_units }}</li> | |
<li><strong>Passed:</strong> {{ report.total_successes }}</li> | |
<li><strong>Failed:</strong> {{ report.total_failures }}</li> | |
</ul> | |
</div> | |
</section> | |
{% for suite_result in report.results_list %} | |
<section> | |
<div class="result"> | |
<h3>Suite: {{ suite_result.suite_name }}</h3> | |
<ul> | |
<li><strong>Units:</strong> {{ suite_result.n_units }}</li> | |
<li><strong>Passed:</strong> {{ suite_result.n_successes }}</li> | |
<li><strong>Failed:</strong> {{ suite_result.n_failures }}</li> | |
</ul> | |
{% for failure in suite_result.failures %} | |
{% if loop.first %} | |
<div> | |
<h4>Failures</h4> | |
<ul> | |
{% endif %} | |
<li>{{ failure.name }} - result: <span class="failure">{{ failure.result }}</span><br/> | |
<pre>{{ failure.stdout }}</pre> | |
</li> | |
{% if loop.last %} | |
</ul> | |
</div> | |
{% endif %} | |
{% endfor %} | |
</div> | |
</section> | |
{% endfor %} | |
</article> | |
</body> | |
</html> | |
''' | |
aparser = argparse.ArgumentParser(description='Turns a Meson test log into an HTML report') | |
aparser.add_argument('--project-name', metavar='NAME', | |
help='The project name', | |
default='Unknown') | |
aparser.add_argument('--job-id', metavar='ID', | |
help='The job ID for the report', | |
default=None) | |
aparser.add_argument('--branch', metavar='NAME', | |
help='Branch of the project being tested', | |
default='master') | |
aparser.add_argument('--output', metavar='FILE', | |
help='The output HTML file, stdout by default', | |
type=argparse.FileType('w', encoding='UTF-8'), | |
default=sys.stdout) | |
aparser.add_argument('infile', metavar='FILE', | |
help='The input testlog.json, stdin by default', | |
type=argparse.FileType('r', encoding='UTF-8'), | |
default=sys.stdin) | |
args = aparser.parse_args() | |
outfile = args.output | |
suites = {} | |
for line in args.infile: | |
data = json.loads(line) | |
(full_suite, unit_name) = data['name'].split(' / ') | |
(project_name, suite_name) = full_suite.split(':') | |
unit = { | |
'project-name': project_name, | |
'suite': suite_name, | |
'name': unit_name, | |
'duration': data['duration'], | |
'returncode': data['returncode'], | |
'result': data['result'], | |
'stdout': data['stdout'], | |
} | |
units = suites.setdefault(full_suite, []) | |
units.append(unit) | |
report = {} | |
report['date'] = datetime.datetime.utcnow() | |
report['locale_date'] = report['date'].strftime("%c") | |
report['project_name'] = args.project_name | |
report['job_id'] = args.job_id | |
report['branch_name'] = args.branch | |
report['total_successes'] = 0 | |
report['total_failures'] = 0 | |
report['total_units'] = 0 | |
report['results_list'] = [] | |
for name, units in suites.items(): | |
(project_name, suite_name) = name.split(':') | |
print('Processing {} suite {}:'.format(project_name, suite_name)) | |
def if_failed(unit): | |
if unit['result'] in ['FAIL', 'TIMEOUT']: | |
return True | |
return False | |
def if_succeded(unit): | |
if unit['result'] in ['OK', 'EXPECTEDFAIL', 'SKIP']: | |
return True | |
return False | |
successes = list(filter(if_succeded, units)) | |
failures = list(filter(if_failed, units)) | |
n_units = len(units) | |
n_successes = len(successes) | |
n_failures = len(failures) | |
report['total_units'] += n_units | |
report['total_successes'] += n_successes | |
report['total_failures'] += n_failures | |
print(' - {}: {} total, {} pass, {} fail'.format(suite_name, n_units, n_successes, n_failures)) | |
suite_report = { | |
'suite_name': suite_name, | |
'n_units': n_units, | |
'successes': successes, | |
'n_successes': n_successes, | |
'failures': failures, | |
'n_failures': n_failures, | |
} | |
report['results_list'].append(suite_report) | |
template = Template(REPORT_TEMPLATE) | |
outfile.write(template.render({'report': report})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment