Skip to content

Commit 8ba6770

Browse files
author
Vasileios Karakasis
authored
Merge branch 'master' into feat/progress-report
2 parents 6dee297 + d769d5d commit 8ba6770

File tree

12 files changed

+413
-19
lines changed

12 files changed

+413
-19
lines changed

ci-scripts/ci-runner.bash

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,15 @@ fi
137137
if [[ $(hostname) =~ tsa ]]; then
138138
# FIXME: Temporary workaround until we have a reframe module on Tsa
139139
module load python
140-
python3 -m venv venv.unittests
141-
source venv.unittests/bin/activate
142-
pip install -r requirements.txt
143140
else
144141
module load reframe
145142
fi
146143

144+
# Always install our requirements
145+
python3 -m venv venv.unittests
146+
source venv.unittests/bin/activate
147+
pip install -r requirements.txt
148+
147149
echo "=============="
148150
echo "Loaded Modules"
149151
echo "=============="

cscs-checks/microbenchmarks/osu/osu_tests.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,8 @@ def __init__(self, variant):
5656
class FlexAlltoallTest(rfm.RegressionTest):
5757
def __init__(self):
5858
self.valid_systems = ['daint:gpu', 'daint:mc',
59-
'dom:gpu', 'dom:mc', 'tiger:gpu',
60-
'kesch:cn', 'kesch:pn',
59+
'dom:gpu', 'dom:mc',
60+
'tiger:gpu', 'kesch:cn',
6161
'arolla:cn', 'arolla:pn',
6262
'tsa:cn', 'tsa:pn']
6363
self.valid_prog_environs = ['PrgEnv-cray']

reframe/core/config.py

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,20 @@
44
# SPDX-License-Identifier: BSD-3-Clause
55

66
import collections.abc
7+
import json
8+
import jsonschema
9+
import os
710
import re
11+
import tempfile
812

13+
import reframe
914
import reframe.core.debug as debug
1015
import reframe.core.fields as fields
1116
import reframe.utility as util
1217
import reframe.utility.os_ext as os_ext
1318
import reframe.utility.typecheck as types
14-
from reframe.core.exceptions import (ConfigError, ReframeFatalError)
19+
from reframe.core.exceptions import (ConfigError, ReframeError,
20+
ReframeFatalError)
1521

1622

1723
_settings = None
@@ -222,3 +228,147 @@ def create_env(system, partition, name):
222228
system.add_partition(part)
223229

224230
self._systems[sys_name] = system
231+
232+
233+
def convert_old_config(filename):
234+
old_config = load_settings_from_file(filename)
235+
converted = {
236+
'systems': [],
237+
'environments': [],
238+
'logging': [],
239+
'perf_logging': [],
240+
}
241+
old_systems = old_config.site_configuration['systems'].items()
242+
for sys_name, sys_specs in old_systems:
243+
sys_dict = {'name': sys_name}
244+
sys_dict.update(sys_specs)
245+
246+
# Make variables dictionary into a list of lists
247+
if 'variables' in sys_specs:
248+
sys_dict['variables'] = [
249+
[vname, v] for vname, v in sys_dict['variables'].items()
250+
]
251+
252+
# Make partitions dictionary into a list
253+
if 'partitions' in sys_specs:
254+
sys_dict['partitions'] = []
255+
for pname, p in sys_specs['partitions'].items():
256+
new_p = {'name': pname}
257+
new_p.update(p)
258+
if p['scheduler'] == 'nativeslurm':
259+
new_p['scheduler'] = 'slurm'
260+
new_p['launcher'] = 'srun'
261+
elif p['scheduler'] == 'local':
262+
new_p['scheduler'] = 'local'
263+
new_p['launcher'] = 'local'
264+
else:
265+
sched, launch, *_ = p['scheduler'].split('+')
266+
new_p['scheduler'] = sched
267+
new_p['launcher'] = launch
268+
269+
# Make resources dictionary into a list
270+
if 'resources' in p:
271+
new_p['resources'] = [
272+
{'name': rname, 'options': r}
273+
for rname, r in p['resources'].items()
274+
]
275+
276+
# Make variables dictionary into a list of lists
277+
if 'variables' in p:
278+
new_p['variables'] = [
279+
[vname, v] for vname, v in p['variables'].items()
280+
]
281+
282+
if 'container_platforms' in p:
283+
new_p['container_platforms'] = []
284+
for cname, c in p['container_platforms'].items():
285+
new_c = {'name': cname}
286+
new_c.update(c)
287+
if 'variables' in c:
288+
new_c['variables'] = [
289+
[vn, v] for vn, v in c['variables'].items()
290+
]
291+
292+
new_p['container_platforms'].append(new_c)
293+
294+
sys_dict['partitions'].append(new_p)
295+
296+
converted['systems'].append(sys_dict)
297+
298+
old_environs = old_config.site_configuration['environments'].items()
299+
for env_target, env_entries in old_environs:
300+
for ename, e in env_entries.items():
301+
new_env = {'name': ename}
302+
if env_target != '*':
303+
new_env['target_systems'] = [env_target]
304+
305+
new_env.update(e)
306+
307+
# Convert variables dictionary to a list of lists
308+
if 'variables' in e:
309+
new_env['variables'] = [
310+
[vname, v] for vname, v in e['variables'].items()
311+
]
312+
313+
# Type attribute is not used anymore
314+
if 'type' in new_env:
315+
del new_env['type']
316+
317+
converted['environments'].append(new_env)
318+
319+
if 'modes' in old_config.site_configuration:
320+
converted['modes'] = []
321+
old_modes = old_config.site_configuration['modes'].items()
322+
for target_mode, mode_entries in old_modes:
323+
for mname, m in mode_entries.items():
324+
new_mode = {'name': mname, 'options': m}
325+
if target_mode != '*':
326+
new_mode['target_systems'] = [target_mode]
327+
328+
converted['modes'].append(new_mode)
329+
330+
def update_logging_config(log_name, original_log):
331+
new_handlers = []
332+
for h in original_log['handlers']:
333+
new_h = h
334+
new_h['level'] = h['level'].lower()
335+
new_handlers.append(new_h)
336+
337+
converted[log_name].append(
338+
{
339+
'level': original_log['level'].lower(),
340+
'handlers': new_handlers
341+
}
342+
)
343+
344+
update_logging_config('logging', old_config.logging_config)
345+
update_logging_config('perf_logging', old_config.perf_logging_config)
346+
converted['general'] = [{}]
347+
if hasattr(old_config, 'checks_path'):
348+
converted['general'][0][
349+
'check_search_path'
350+
] = old_config.checks_path
351+
352+
if hasattr(old_config, 'checks_path_recurse'):
353+
converted['general'][0][
354+
'check_search_recursive'
355+
] = old_config.checks_path_recurse
356+
357+
if converted['general'] == [{}]:
358+
del converted['general']
359+
360+
# Validate the converted file
361+
schema_filename = os.path.join(reframe.INSTALL_PREFIX,
362+
'schemas', 'config.json')
363+
364+
# We let the following statements raise, because if they do, that's a BUG
365+
with open(schema_filename) as fp:
366+
schema = json.loads(fp.read())
367+
368+
jsonschema.validate(converted, schema)
369+
with tempfile.NamedTemporaryFile(mode='w', delete=False) as fp:
370+
fp.write(f"#\n# This file was automatically generated "
371+
f"by ReFrame based on '{filename}'.\n#\n\n")
372+
fp.write(f'site_configuration = {util.ppretty(converted)}\n')
373+
374+
return fp.name

reframe/frontend/cli.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@ def format_check(check, detailed):
3939

4040
if detailed:
4141
lines += [
42-
' - description: %s' % check.descr,
43-
' - systems: %s' % ', '.join(check.valid_systems),
44-
' - environments: %s' % ', '.join(check.valid_prog_environs),
45-
' - modules: %s' % ', '.join(check.modules),
46-
' - task allocation: %s' % flex,
47-
' - tags: %s' % ', '.join(check.tags),
48-
' - maintainers: %s' % ', '.join(check.maintainers)
42+
f" - description: {check.descr}",
43+
f" - systems: {', '.join(check.valid_systems)}",
44+
f" - environments: {', '.join(check.valid_prog_environs)}",
45+
f" - modules: {', '.join(check.modules)}",
46+
f" - task allocation: {flex}",
47+
f" - dependencies: "
48+
f"{', '.join([d[0] for d in check.user_deps()])}",
49+
f" - tags: {', '.join(check.tags)}",
50+
f" - maintainers: {', '.join(check.maintainers)}"
4951
]
5052

5153
return '\n'.join(lines)
@@ -231,6 +233,9 @@ def main():
231233
misc_options.add_argument(
232234
'--nocolor', action='store_false', dest='colorize', default=True,
233235
help='Disable coloring of output')
236+
misc_options.add_argument(
237+
'--failure-stats', action='store_true',
238+
help='Print failure statistics')
234239
misc_options.add_argument('--performance-report', action='store_true',
235240
help='Print the performance report')
236241
misc_options.add_argument(
@@ -630,6 +635,8 @@ def main():
630635
if runner.stats.failures():
631636
printer.info(runner.stats.failure_report())
632637
success = False
638+
if options.failure_stats:
639+
printer.info(runner.stats.failure_stats())
633640

634641
if options.performance_report:
635642
printer.info(runner.stats.performance_report())

reframe/frontend/statistics.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ def failure_report(self):
9797
report.append(' * Job type: %s (id=%s)' % (job_type, jobid))
9898
report.append(' * Maintainers: %s' % check.maintainers)
9999
report.append(' * Failing phase: %s' % tf.failed_stage)
100+
report.append(" * Rerun with '-n %s -p %s --system %s'" %
101+
(check.name, environ_name, partname))
100102
reason = ' * Reason: '
101103
if tf.exc_info is not None:
102104
from reframe.core.exceptions import format_exception
@@ -115,6 +117,50 @@ def failure_report(self):
115117
report.append(line_width * '-')
116118
return '\n'.join(report)
117119

120+
def failure_stats(self):
121+
failures = {}
122+
current_run = rt.runtime().current_run
123+
for tf in (t for t in self.tasks(current_run) if t.failed):
124+
check = tf.check
125+
partition = check.current_partition
126+
partname = partition.fullname if partition else 'None'
127+
environ_name = (check.current_environ.name
128+
if check.current_environ else 'None')
129+
f = f'[{check.name}, {environ_name}, {partname}]'
130+
if tf.failed_stage not in failures:
131+
failures[tf.failed_stage] = []
132+
133+
failures[tf.failed_stage].append(f)
134+
135+
line_width = 78
136+
stats_start = line_width * '='
137+
stats_title = 'FAILURE STATISTICS'
138+
stats_end = line_width * '-'
139+
stats_body = []
140+
row_format = "{:<11} {:<5} {}"
141+
stats_hline = row_format.format(11*'-', 5*'-', 60*'-')
142+
stats_header = row_format.format('Phase', '#', 'Failing test cases')
143+
num_tests = len(self.tasks(current_run))
144+
num_failures = 0
145+
for l in failures.values():
146+
num_failures += len(l)
147+
148+
stats_body = ['']
149+
stats_body.append('Total number of test cases: %s' % num_tests)
150+
stats_body.append('Total number of failures: %s' % num_failures)
151+
stats_body.append('')
152+
stats_body.append(stats_header)
153+
stats_body.append(stats_hline)
154+
for p, l in failures.items():
155+
stats_body.append(row_format.format(p, len(l), l[0]))
156+
for f in l[1:]:
157+
stats_body.append(row_format.format('', '', str(f)))
158+
159+
if stats_body:
160+
return '\n'.join([stats_start, stats_title, *stats_body,
161+
stats_end])
162+
return ''
163+
118164
def performance_report(self):
119165
line_width = 78
120166
report_start = line_width * '='

reframe/utility/__init__.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,64 @@ def toalphanum(s):
126126
return re.sub(r'\W', '_', s)
127127

128128

129+
def ppretty(value, htchar=' ', lfchar='\n', indent=4, basic_offset=0):
130+
'''Format string of dictionaries, lists and tuples
131+
132+
:arg value: The value to be formatted.
133+
:arg htchar: Horizontal-tab character.
134+
:arg lfchar: Linefeed character.
135+
:arg indent: Number of htchar characters for every indentation level.
136+
:arg basic_offset: Basic offset for the representation, any additional
137+
indentation space is added to the ``basic_offset``.
138+
:returns: a formatted string of the ``value``.
139+
'''
140+
141+
nlch = lfchar + htchar * indent * (basic_offset + 1)
142+
if isinstance(value, tuple):
143+
if value == ():
144+
return '()'
145+
146+
items = [
147+
nlch + ppretty(item, htchar, lfchar, indent, basic_offset + 1)
148+
for item in value
149+
]
150+
return '(%s)' % (','.join(items) + lfchar +
151+
htchar * indent * basic_offset)
152+
elif isinstance(value, list):
153+
if value == []:
154+
return '[]'
155+
156+
items = [
157+
nlch + ppretty(item, htchar, lfchar, indent, basic_offset + 1)
158+
for item in value
159+
]
160+
return '[%s]' % (','.join(items) + lfchar +
161+
htchar * indent * basic_offset)
162+
elif isinstance(value, dict):
163+
if value == {}:
164+
return '{}'
165+
166+
items = [
167+
nlch + repr(key) + ': ' +
168+
ppretty(value[key], htchar, lfchar, indent, basic_offset + 1)
169+
for key in value
170+
]
171+
return '{%s}' % (','.join(items) + lfchar +
172+
htchar * indent * basic_offset)
173+
elif isinstance(value, set):
174+
if value == set():
175+
return 'set()'
176+
177+
items = [
178+
nlch + ppretty(item, htchar, lfchar, indent, basic_offset + 1)
179+
for item in value
180+
]
181+
return '{%s}' % (','.join(items) + lfchar +
182+
htchar * indent * basic_offset)
183+
else:
184+
return repr(value)
185+
186+
129187
class ScopedDict(UserDict):
130188
'''This is a special dict that imposes scopes on its keys.
131189

0 commit comments

Comments
 (0)