Skip to content

Commit 82d0fba

Browse files
committed
new runreport format
1 parent 6cf2dde commit 82d0fba

File tree

5 files changed

+168
-79
lines changed

5 files changed

+168
-79
lines changed

reframe/frontend/cli.py

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
#
44
# SPDX-License-Identifier: BSD-3-Clause
55

6+
import datetime
67
import inspect
78
import json
89
import os
910
import re
1011
import socket
1112
import sys
1213
import traceback
14+
from pathlib import Path
1315

1416
import reframe
1517
import reframe.core.config as config
@@ -138,7 +140,7 @@ def main():
138140
envvar='RFM_SAVE_LOG_FILES', configvar='general/save_log_files'
139141
)
140142
output_options.add_argument(
141-
'--keep-runreport', action='store_true', default=False,
143+
'--runreport-name', action='store', metavar='NAME',
142144
help="Do not overwrite runreport.json",
143145
)
144146

@@ -502,19 +504,32 @@ def print_infoline(param, value):
502504
param = param + ':'
503505
printer.info(f" {param.ljust(18)} {value}")
504506

507+
reframe_info = {
508+
'version': os_ext.reframe_version(),
509+
'command': repr(' '.join(sys.argv)),
510+
'user': f"{os_ext.osuser() or '<unknown>'}",
511+
'host': socket.gethostname(),
512+
'working_directory': repr(os.getcwd()),
513+
'check_search_path': f"{':'.join(loader.load_path)!r}",
514+
'recursive_search_path': loader.recurse,
515+
'settings_file': site_config.filename,
516+
'stage_prefix': repr(rt.stage_prefix),
517+
'output_prefix': repr(rt.output_prefix),
518+
}
519+
505520
# Print command line
506521
printer.info(f"[ReFrame Setup]")
507-
print_infoline('version', os_ext.reframe_version())
508-
print_infoline('command', repr(' '.join(sys.argv)))
522+
print_infoline('version', reframe_info['version'])
523+
print_infoline('command', reframe_info['command'])
509524
print_infoline('launched by',
510-
f"{os_ext.osuser() or '<unknown>'}@{socket.gethostname()}")
511-
print_infoline('working directory', repr(os.getcwd()))
512-
print_infoline('settings file', f'{site_config.filename!r}')
525+
f"{reframe_info['user']}@{reframe_info['host']}")
526+
print_infoline('working directory', reframe_info['working_directory'])
527+
print_infoline('settings file', f"{reframe_info['settings_file']!r}")
513528
print_infoline('check search path',
514-
f"{'(R) ' if loader.recurse else ''}"
515-
f"{':'.join(loader.load_path)!r}")
516-
print_infoline('stage directory', repr(rt.stage_prefix))
517-
print_infoline('output directory', repr(rt.output_prefix))
529+
f"{'(R) ' if reframe_info['recursive_search_path'] else ''}"
530+
f"{reframe_info['check_search_path']!r}")
531+
print_infoline('stage directory', reframe_info['stage_prefix'])
532+
print_infoline('output directory', reframe_info['output_prefix'])
518533
printer.info('')
519534
try:
520535
# Locate and load checks
@@ -689,9 +704,13 @@ def print_infoline(param, value):
689704
max_retries) from None
690705
runner = Runner(exec_policy, printer, max_retries)
691706
try:
707+
reframe_info['start_time'] = (
708+
datetime.datetime.today().strftime('%c %Z'))
692709
runner.runall(testcases)
693710
finally:
694711
# Print a retry report if we did any retries
712+
reframe_info['end_time'] = (
713+
datetime.datetime.today().strftime('%c %Z'))
695714
if runner.stats.failures(run=0):
696715
printer.info(runner.stats.retry_report())
697716

@@ -705,14 +724,23 @@ def print_infoline(param, value):
705724
if options.performance_report:
706725
printer.info(runner.stats.performance_report())
707726

708-
if options.keep_runreport:
709-
runreport_id = get_next_runreport_index()
710-
runreport_name = f'runreport-{runreport_id}.json'
727+
if options.runreport_name:
728+
runreport_file = options.runreport_name
711729
else:
712-
runreport_name = 'runreport.json'
713-
714-
with open(runreport_name, 'w') as fp:
715-
json.dump(runner.stats.json(), fp, indent=4)
730+
runreport_dir = os.path.join(Path.home(), '.local/reframe')
731+
runreport_id = get_next_runreport_index(runreport_dir)
732+
runreport_name = f'runreport-{runreport_id}.json'
733+
Path(runreport_dir).mkdir(parents=True, exist_ok=True)
734+
runreport_file = os.path.join(runreport_dir,
735+
runreport_name)
736+
737+
try:
738+
with open(runreport_file, 'w') as fp:
739+
json.dump(runner.stats.json(reframe_info, force=True),
740+
fp, indent=4)
741+
except OSError:
742+
printer.error(f'invalid path: {runreport_file}')
743+
sys.exit(1)
716744

717745
else:
718746
printer.error("No action specified. Please specify `-l'/`-L' for "

reframe/frontend/statistics.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -71,13 +71,14 @@ def retry_report(self):
7171

7272
return '\n'.join(report)
7373

74-
def json(self, force=False):
74+
def json(self, reframe_info=None, force=False):
7575
if not force and self._records:
7676
return self._records
7777

78-
self._records = []
78+
self._records = {'run_info': []}
7979
current_run = rt.runtime().current_run
8080
for run_no, run in enumerate(self._alltasks):
81+
tests = []
8182
for t in run:
8283
check = t.check
8384
partition = check.current_partition
@@ -138,9 +139,12 @@ def json(self, force=False):
138139
entry['result'] = 'success'
139140
entry['outputdir'] = check.outputdir
140141

141-
entry['runid'] = run_no
142+
tests.append(entry)
143+
144+
self._records['run_info'].append({'runid': run_no, 'tests': tests})
142145

143-
self._records.append(entry)
146+
if reframe_info:
147+
self._records['reframe_info'] = reframe_info
144148

145149
return self._records
146150

@@ -149,8 +153,8 @@ def failure_report(self):
149153
report = [line_width * '=']
150154
report.append('SUMMARY OF FAILURES')
151155
last_run = rt.runtime().current_run
152-
for r in self.json():
153-
if r['result'] == 'success' or r['runid'] != last_run:
156+
for r in self.json()['run_info'][last_run]['tests']:
157+
if r['result'] == 'success':
154158
continue
155159

156160
retry_info = (f'(for the last of {last_run} retries)'

reframe/schemas/runreport.json

Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,95 @@
22
"$schema": "http://json-schema.org/draft-07/schema#",
33
"$id": "https://raw.githubusercontent.com/eth-cscs/reframe/master/schemas/runreport.json",
44
"title": "Validation schema for ReFrame's run report",
5-
"type": "array",
6-
"items": {
7-
"type": "object",
8-
"properties": {
9-
"testname": {"type": "string"},
10-
"description": {"type": ["string"]},
11-
"system": {"type": "string"},
12-
"environment": {"type": ["string", "null"]},
13-
"stagedir": {"type": ["string", "null"]},
14-
"outputdir": {"type": ["string", "null"]},
15-
"nodelist": {
16-
"type": "array",
17-
"items": {"type": "string"}
5+
"type": "object",
6+
"properties": {
7+
"reframe_info": {
8+
"type": "object",
9+
"properties": {
10+
"version": {"type": "string"},
11+
"command": {"type": "string"},
12+
"user": {"type": "string"},
13+
"host": {"type": "string"},
14+
"check_search_path": {"type": "string"},
15+
"working_directory": {"type": "string"},
16+
"recursive_search_path": {"type": "boolean"},
17+
"settings_file": {"type": "string"},
18+
"stage_prefix": {"type": "string"},
19+
"output_prefix": {"type": "string"},
20+
"start_time": {"type": "string"},
21+
"end_time": {"type": "string"}
1822
},
19-
"scheduler": {"type": ["string", "null"]},
20-
"jobid": {"type": ["number", "null"]},
21-
"result": {
22-
"type": "string",
23-
"enum": ["success", "fail"]
24-
},
25-
"failing_phase": {"type": ["string", "null"]},
26-
"failing_reason": {"type": ["string", "null"]},
27-
"build_stdout": {"type": ["string", "null"]},
28-
"build_stderr": {"type": ["string", "null"]},
29-
"job_stdout": {"type": ["string", "null"]},
30-
"job_stderr": {"type": ["string", "null"]},
31-
"maintainers": {
32-
"type": "array",
33-
"items": {"type": ["string"]}
34-
},
35-
"tags": {
36-
"type": "array",
37-
"items": {"type": ["string"]}
38-
},
39-
"runid": {"type": "number"},
40-
"time_setup": {"type": ["number", "null"]},
41-
"time_compile": {"type": ["number", "null"]},
42-
"time_run": {"type": ["number", "null"]},
43-
"time_sanity": {"type": ["number", "null"]},
44-
"time_performance": {"type": ["number", "null"]},
45-
"time_total": {"type": ["number", "null"]}
23+
"additionalProperties": false,
24+
"required": ["check_search_path", "command", "end_time", "host",
25+
"output_prefix", "recursive_search_path",
26+
"stage_prefix", "start_time", "user", "version",
27+
"working_directory"]
4628
},
47-
"additionalProperties": false,
48-
"required": ["build_stderr", "build_stdout", "description",
49-
"environment", "failing_phase", "failing_reason",
50-
"job_stderr", "job_stdout", "jobid", "maintainers",
51-
"nodelist", "outputdir", "result", "runid",
52-
"scheduler", "stagedir", "system", "tags", "testname",
53-
"time_compile", "time_performance", "time_run",
54-
"time_sanity", "time_setup", "time_total"]
55-
}
29+
"run_info": {
30+
"type": "array",
31+
"items": {
32+
"type": "object",
33+
"properties": {
34+
"runid": {"type": "number"},
35+
"tests": {
36+
"type": "array",
37+
"items": {
38+
"type": "object",
39+
"properties": {
40+
"testname": {"type": "string"},
41+
"description": {"type": "string"},
42+
"system": {"type": "string"},
43+
"environment": {"type": ["string", "null"]},
44+
"stagedir": {"type": ["string", "null"]},
45+
"outputdir": {"type": ["string", "null"]},
46+
"nodelist": {
47+
"type": "array",
48+
"items": {"type": "string"}
49+
},
50+
"scheduler": {"type": ["string", "null"]},
51+
"jobid": {"type": ["number", "null"]},
52+
"result": {
53+
"type": "string",
54+
"enum": ["success", "fail"]
55+
},
56+
"failing_phase": {"type": ["string", "null"]},
57+
"failing_reason": {"type": ["string", "null"]},
58+
"build_stdout": {"type": ["string", "null"]},
59+
"build_stderr": {"type": ["string", "null"]},
60+
"job_stdout": {"type": ["string", "null"]},
61+
"job_stderr": {"type": ["string", "null"]},
62+
"maintainers": {
63+
"type": "array",
64+
"items": {"type": ["string"]}
65+
},
66+
"tags": {
67+
"type": "array",
68+
"items": {"type": "string"}
69+
},
70+
"runid": {"type": "number"},
71+
"time_setup": {"type": ["number", "null"]},
72+
"time_compile": {"type": ["number", "null"]},
73+
"time_run": {"type": ["number", "null"]},
74+
"time_sanity": {"type": ["number", "null"]},
75+
"time_performance": {"type": ["number", "null"]},
76+
"time_total": {"type": ["number", "null"]}
77+
},
78+
"additionalProperties": false,
79+
"required": ["build_stderr", "build_stdout", "description",
80+
"environment", "failing_phase", "failing_reason",
81+
"job_stderr", "job_stdout", "jobid", "maintainers",
82+
"nodelist", "outputdir", "result", "scheduler",
83+
"stagedir", "system", "tags", "testname",
84+
"time_compile", "time_performance", "time_run",
85+
"time_sanity", "time_setup", "time_total"]
86+
},
87+
"additionalProperties": false,
88+
"required": ["runid", "tests"]
89+
}
90+
}
91+
}
92+
}
93+
},
94+
"additionalProperties": false,
95+
"required": ["reframe_info", "run_info"]
5696
}

reframe/utility/__init__.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,17 @@
1616
from collections import UserDict
1717

1818

19-
def get_next_runreport_index():
20-
runreports = os.listdir('.')
19+
def get_next_runreport_index(runreport_dir):
20+
runreports = os.listdir(runreport_dir)
2121
pattern = r'runreport-\d+.json'
2222
filenames = filter(re.compile(pattern).match, runreports)
23-
filenames = sorted(filenames)
2423

2524
if not filenames:
2625
return 1
2726

28-
for i, f in enumerate(filenames, 1):
29-
idx = f.split('.')[0].split('-')[1]
30-
idx = int(idx)
27+
idx_list = sorted([int(f.split('.')[0].split('-')[1])
28+
for f in filenames])
29+
for i, idx in enumerate(idx_list):
3130
if idx != i:
3231
return i
3332

unittests/test_policies.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
#
44
# SPDX-License-Identifier: BSD-3-Clause
55

6+
import datetime
67
import json
78
import jsonschema
89
import os
910
import pytest
11+
import socket
12+
import sys
1013

1114
import reframe
1215
import reframe.core.runtime as rt
@@ -116,10 +119,25 @@ def validate_report(runreport):
116119

117120

118121
def test_runall(make_runner, make_cases, common_exec_ctx):
122+
reframe_info = {
123+
'version': os_ext.reframe_version(),
124+
'command': repr(' '.join(sys.argv)),
125+
'user': f"{os_ext.osuser() or '<unknown>'}",
126+
'host': socket.gethostname(),
127+
'working_directory': repr(os.getcwd()),
128+
'check_search_path': 'unittests/resources/checks',
129+
'recursive_search_path': True,
130+
'settings_file': fixtures.TEST_CONFIG_FILE,
131+
'stage_prefix': repr(rt.runtime().stage_prefix),
132+
'output_prefix': repr(rt.runtime().output_prefix),
133+
}
119134
runner = make_runner()
135+
reframe_info['start_time'] = datetime.datetime.today().strftime('%c %Z')
120136
runner.runall(make_cases())
137+
reframe_info['end_time'] = datetime.datetime.today().strftime('%c %Z')
121138
stats = runner.stats
122-
validate_report(runner.stats.json())
139+
runreport = runner.stats.json(reframe_info, force=True)
140+
validate_report(runreport)
123141
assert 8 == stats.num_cases()
124142
assert_runall(runner)
125143
assert 5 == len(stats.failures())

0 commit comments

Comments
 (0)