Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
5663628
xml
jgphpc Apr 8, 2021
898ffcb
rpt
jgphpc Apr 9, 2021
1b96f72
Merge branch 'master' into jg/xml
jgphpc Apr 9, 2021
6020d54
typo
jgphpc Apr 9, 2021
543a4e1
Merge branch 'master' of https://github.com/eth-cscs/reframe into jg/xml
jgphpc Apr 9, 2021
6af94ee
fix for review
jgphpc Apr 9, 2021
52bd60b
Merge branch 'master' of https://github.com/eth-cscs/reframe into jg/xml
jgphpc Apr 12, 2021
75f1bb4
fix for https://github.com/eth-cscs/reframe/pull/1925#discussion_r611…
jgphpc Apr 12, 2021
286cc91
fix for review
jgphpc Apr 13, 2021
4285849
Merge branch 'master' of https://github.com/eth-cscs/reframe into jg/xml
jgphpc Apr 16, 2021
6ecb6ed
Fix for review
jgphpc Apr 16, 2021
aad88e6
fix for review
jgphpc Apr 16, 2021
13e73f9
Merge branch 'master' of https://github.com/eth-cscs/reframe into jg/xml
jgphpc Apr 16, 2021
b6eaa50
fix for review
jgphpc Apr 16, 2021
fea4f48
typo
jgphpc Apr 18, 2021
57c0926
unittest
jgphpc Apr 18, 2021
faef8b1
typo
jgphpc Apr 18, 2021
9671db7
Merge branch 'master' of https://github.com/eth-cscs/reframe into jg/xml
jgphpc Apr 18, 2021
5d9bfa5
fix for review
jgphpc Apr 22, 2021
0cfdb0f
fix for review
jgphpc Apr 22, 2021
54a971e
Fine tune implementation
Apr 27, 2021
c504ded
Style change
Apr 27, 2021
7462e30
Merge branch 'master' into jg/xml
jgphpc Apr 28, 2021
d22ea80
fix for review
jgphpc Apr 28, 2021
1ce7705
import
jgphpc Apr 28, 2021
0242811
fix for review
jgphpc Apr 28, 2021
be61e07
fix for review
jgphpc Apr 29, 2021
808e6ba
Merge branch 'master' into jg/xml
jgphpc Apr 29, 2021
d8cfd71
fix for UnicodeDecodeError
jgphpc Apr 29, 2021
acc49a0
Merge branch 'jg/xml' of https://github.com/jgphpc/reframe into jg/xml
jgphpc Apr 29, 2021
631d08b
fix for error type 'xs:decimal'
jgphpc Apr 29, 2021
42285eb
fix for review
jgphpc May 1, 2021
6d31bb7
Final fine tuning
May 2, 2021
a30f9c2
Fix wheel package creation
May 2, 2021
ff53160
Merge branch 'master' into jg/xml
jgphpc May 3, 2021
da5a067
Final corrections
May 4, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/config_reference.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1224,6 +1224,17 @@ General Configuration
Default value has changed to avoid generating a report file per session.


.. js:attribute:: .general[].report_junit

:required: No
:default: ``null``

The file where ReFrame will store its report in JUnit format.
The report adheres to the XSD schema `here <https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd>`__.

.. versionadded:: 3.6.0


.. js:attribute:: .general[].resolve_module_conflicts

:required: No
Expand Down
25 changes: 25 additions & 0 deletions docs/manpage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,16 @@ Options controlling ReFrame output
.. versionadded:: 3.1


.. option:: --report-junit=FILE

Instruct ReFrame to generate a JUnit XML report in ``FILE``.
The generated report adheres to the XSD schema `here <https://github.com/windyroad/JUnit-Schema/blob/master/JUnit.xsd>`__ and it takes into account only the first run, ignoring retries of failed tests.

This option can also be set using the :envvar:`RFM_REPORT_JUNIT` environment variable or the :js:attr:`report_junit` general configuration parameter.

.. versionadded:: 3.6.0


-------------------------------------
Options controlling ReFrame execution
-------------------------------------
Expand Down Expand Up @@ -859,6 +869,21 @@ Here is an alphabetical list of the environment variables recognized by ReFrame:
================================== ==================


.. envvar:: RFM_REPORT_JUNIT

The file where ReFrame will generate a JUnit XML report.

.. versionadded:: 3.6.0

.. table::
:align: left

================================== ==================
Associated command line option :option:`--report-junit`
Associated configuration parameter :js:attr:`report_junit` general configuration parameter
================================== ==================


.. envvar:: RFM_RESOLVE_MODULE_CONFLICTS

Resolve module conflicts automatically.
Expand Down
21 changes: 21 additions & 0 deletions reframe/frontend/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,12 @@ def main():
envvar='RFM_REPORT_FILE',
configvar='general/report_file'
)
output_options.add_argument(
'--report-junit', action='store', metavar='FILE',
help="Store a JUnit report in FILE",
envvar='RFM_REPORT_JUNIT',
configvar='general/report_junit'
)

# Check discovery options
locate_options.add_argument(
Expand Down Expand Up @@ -1045,6 +1051,21 @@ def module_unuse(*paths):
f'failed to generate report in {report_file!r}: {e}'
)

# Generate the junit xml report for this session
junit_report_file = rt.get_option('general/0/report_junit')
if junit_report_file:
# Expand variables in filename
junit_report_file = osext.expandvars(junit_report_file)
junit_xml = runreport.junit_xml_report(json_report)
try:
with open(junit_report_file, 'w') as fp:
runreport.junit_dump(junit_xml, fp)
except OSError as e:
printer.warning(
f'failed to generate report in {junit_report_file!r}: '
f'{e}'
)

if not success:
sys.exit(1)

Expand Down
62 changes: 61 additions & 1 deletion reframe/frontend/runreport.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#
# SPDX-License-Identifier: BSD-3-Clause

import decimal
import json
import jsonschema
import lxml.etree as etree
import os
import re

Expand All @@ -13,7 +15,6 @@
import reframe.utility.jsonext as jsonext
import reframe.utility.versioning as versioning


DATA_VERSION = '1.3.0'
_SCHEMA = os.path.join(rfm.INSTALL_PREFIX, 'reframe/schemas/runreport.json')

Expand Down Expand Up @@ -152,3 +153,62 @@ def load_report(filename):
)

return _RunReport(report)


def junit_xml_report(json_report):
'''Generate a JUnit report from a standard ReFrame JSON report.'''

xml_testsuites = etree.Element('testsuites')
xml_testsuite = etree.SubElement(
xml_testsuites, 'testsuite',
attrib={
'errors': '0',
'failures': str(json_report['session_info']['num_failures']),
'hostname': json_report['session_info']['hostname'],
'id': '0',
'name': 'reframe',
'package': 'reframe',
'tests': str(json_report['session_info']['num_cases']),
'time': str(json_report['session_info']['time_elapsed']),

# XSD schema does not like the timezone format, so we remove it
'timestamp': json_report['session_info']['time_start'][:-5],
}
)
testsuite_properties = etree.SubElement(xml_testsuite, 'properties')
for testid in range(len(json_report['runs'][0]['testcases'])):
tid = json_report['runs'][0]['testcases'][testid]
casename = (
f"{tid['name']}[{tid['system']}, {tid['environment']}]"
)
testcase = etree.SubElement(
xml_testsuite, 'testcase',
attrib={
'classname': tid['filename'],
'name': casename,

# XSD schema does not like the exponential format and since we
# do not want to impose a fixed width, we pass it to `Decimal`
# to format it automatically.
'time': str(decimal.Decimal(tid['time_total'])),
}
)
if tid['result'] == 'failure':
testcase_msg = etree.SubElement(
testcase, 'failure', attrib={'type': 'failure',
'message': tid['fail_phase']}
)
testcase_msg.text = f"{tid['fail_phase']}: {tid['fail_reason']}"

testsuite_stdout = etree.SubElement(xml_testsuite, 'system-out')
testsuite_stdout.text = ''
testsuite_stderr = etree.SubElement(xml_testsuite, 'system-err')
testsuite_stderr.text = ''
return xml_testsuites


def junit_dump(xml, fp):
fp.write(
etree.tostring(xml, encoding='utf8', pretty_print=True,
method='xml', xml_declaration=True).decode()
)
5 changes: 3 additions & 2 deletions reframe/frontend/statistics.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import inspect
import traceback

import reframe.core.runtime as rt
import reframe.core.exceptions as errors
import reframe.utility as util
Expand Down Expand Up @@ -266,8 +267,8 @@ def print_failure_stats(self, printer):
stats_header = row_format.format('Phase', '#', 'Failing test cases')
num_tests = len(self.tasks(current_run))
num_failures = 0
for l in failures.values():
num_failures += len(l)
for fl in failures.values():
num_failures += len(fl)

stats_body = ['']
stats_body.append(f'Total number of test cases: {num_tests}')
Expand Down
2 changes: 2 additions & 0 deletions reframe/schemas/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,7 @@
"non_default_craype": {"type": "boolean"},
"purge_environment": {"type": "boolean"},
"report_file": {"type": "string"},
"report_junit": {"type": ["string", "null"]},
"resolve_module_conflicts": {"type": "boolean"},
"save_log_files": {"type": "boolean"},
"target_systems": {"$ref": "#/defs/system_ref"},
Expand Down Expand Up @@ -487,6 +488,7 @@
"general/non_default_craype": false,
"general/purge_environment": false,
"general/report_file": "${HOME}/.reframe/reports/run-report.json",
"general/report_junit": null,
"general/resolve_module_conflicts": true,
"general/save_log_files": false,
"general/target_systems": ["*"],
Expand Down
Loading