Skip to content

Commit 2efa729

Browse files
authored
Merge branch 'master' into bugfix/sqlite-rw-locks
2 parents bcae0b9 + 267f8b3 commit 2efa729

File tree

10 files changed

+73
-26
lines changed

10 files changed

+73
-26
lines changed

.github/workflows/test-flux.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: Test Flux Scheduler
2-
on:
3-
pull_request: []
2+
3+
on: [push, pull_request]
44

55
jobs:
66
build:

.github/workflows/test-schedulers.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name: ReFrame CI / Scheduler backend tests
2-
on:
3-
pull_request: []
2+
3+
on: [push, pull_request]
44

55
jobs:
66
scheduler-test:

reframe/core/logging.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,19 @@ def adjust_verbosity(self, num_steps):
10201020

10211021
h.setLevel(new_level)
10221022

1023+
def set_handler_level(self, level, filter=None):
1024+
'''Set handler level for handlers attached to this logger.
1025+
1026+
:arg level: The new level.
1027+
:arg filter: A callable accepting a single argument, which is the
1028+
handler type as declared in ReFrame's configuration. If the filter
1029+
is :obj:`None` then the level applies to all handlers, otherwise
1030+
it will apply to all handlers that the filter return :obj:`True`.
1031+
'''
1032+
for h in self.logger.handlers:
1033+
if filter is None or filter(h._rfm_type):
1034+
h.setLevel(level)
1035+
10231036

10241037
# A logger that doesn't log anything
10251038
null_logger = LoggerAdapter()

reframe/frontend/cli.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import reframe.utility.jsonext as jsonext
2929
import reframe.utility.osext as osext
3030
import reframe.utility.typecheck as typ
31+
from reframe.core.warnings import suppress_deprecations
3132
from reframe.frontend.testgenerators import (distribute_tests,
3233
getallnodes, repeat_tests,
3334
parameterize_tests)
@@ -149,13 +150,18 @@ def describe_checks(testcases, printer):
149150
#
150151
# 1. Add other fields that are relevant for users
151152
# 2. Remove all private fields
152-
rec['name'] = tc.check.name
153-
rec['unique_name'] = tc.check.unique_name
154-
rec['display_name'] = tc.check.display_name
153+
cls = type(tc.check)
154+
if hasattr(cls, 'loggable_attrs'):
155+
for name, alt_name in cls.loggable_attrs():
156+
key = alt_name if alt_name else name
157+
try:
158+
with suppress_deprecations():
159+
rec.setdefault(key, getattr(tc.check, name))
160+
except AttributeError:
161+
rec.setdefault(key, '<undefined>')
162+
155163
rec['pipeline_hooks'] = {}
156164
rec['perf_variables'] = list(rec['perf_variables'].keys())
157-
rec['prefix'] = tc.check.prefix
158-
rec['variant_num'] = tc.check.variant_num
159165
for stage, hooks in tc.check.pipeline_hooks().items():
160166
for hk in hooks:
161167
if hk.__name__ not in tc.check.disabled_hooks:
@@ -1041,6 +1047,8 @@ def restrict_logging():
10411047
if options.describe_stored_sessions:
10421048
# Restore logging level
10431049
printer.setLevel(logging.INFO)
1050+
printer.set_handler_level(logging.WARNING,
1051+
lambda htype: htype != 'stream')
10441052
with exit_gracefully_on_error('failed to retrieve session data',
10451053
printer):
10461054
printer.info(jsonext.dumps(reporting.session_info(
@@ -1051,6 +1059,8 @@ def restrict_logging():
10511059
if options.describe_stored_testcases:
10521060
# Restore logging level
10531061
printer.setLevel(logging.INFO)
1062+
printer.set_handler_level(logging.WARNING,
1063+
lambda htype: htype != 'stream')
10541064
namepatt = '|'.join(n.replace('%', ' %') for n in options.names)
10551065
with exit_gracefully_on_error('failed to retrieve test case data',
10561066
printer):
@@ -1366,7 +1376,7 @@ def _case_failed(t):
13661376
params = {}
13671377
for param_spec in options.parameterize:
13681378
try:
1369-
var, values_spec = param_spec.split('=')
1379+
var, values_spec = param_spec.split('=', maxsplit=1)
13701380
except ValueError:
13711381
raise errors.CommandLineError(
13721382
f'invalid parameter spec: {param_spec}'

reframe/frontend/executors/__init__.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -591,8 +591,13 @@ def on_task_success(self, task):
591591
'''Called when a regression test has succeeded.'''
592592

593593

594-
def _handle_sigterm(signum, frame):
595-
raise ForceExitError('received TERM signal')
594+
def _force_exit(signum, frame):
595+
# ReFrame will exit, ignore all other signals that cause a graceful
596+
# termination
597+
signal.signal(signal.SIGINT, signal.SIG_IGN)
598+
signal.signal(signal.SIGHUP, signal.SIG_IGN)
599+
signal.signal(signal.SIGTERM, signal.SIG_IGN)
600+
raise ForceExitError(f'received signal {signum}')
596601

597602

598603
class Runner:
@@ -620,7 +625,8 @@ def __init__(self, policy, printer=None, max_retries=0,
620625
self._policy.printer = self._printer
621626
self._policy.max_failures = max_failures
622627

623-
signal.signal(signal.SIGTERM, _handle_sigterm)
628+
signal.signal(signal.SIGHUP, _force_exit)
629+
signal.signal(signal.SIGTERM, _force_exit)
624630

625631
@property
626632
def max_failures(self):

reframe/frontend/reporting/storage.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,7 @@ def _db_store_report(self, conn, report, report_file_path):
226226

227227
return session_uuid
228228

229+
@time_function
229230
def store(self, report, report_file=None):
230231
with self._db_write(self._db_file()) as conn:
231232
return self._db_store_report(conn, report, report_file)

unittests/conftest.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import contextlib
1111
import copy
1212
import pytest
13+
import signal
1314
import tempfile
1415

1516
import reframe.core.settings as settings
@@ -105,9 +106,17 @@ def _make_loader(check_search_path, *args, **kwargs):
105106
return _make_loader
106107

107108

109+
@pytest.fixture
110+
def restore_signals():
111+
yield
112+
signal.signal(signal.SIGTERM, signal.SIG_DFL)
113+
signal.signal(signal.SIGINT, signal.SIG_DFL)
114+
signal.signal(signal.SIGHUP, signal.SIG_DFL)
115+
116+
108117
@pytest.fixture(params=[policies.SerialExecutionPolicy,
109118
policies.AsynchronousExecutionPolicy])
110-
def make_runner(request):
119+
def make_runner(request, restore_signals):
111120
'''Test runner with all the execution policies'''
112121

113122
def _make_runner(*args, **kwargs):
@@ -120,7 +129,7 @@ def _make_runner(*args, **kwargs):
120129

121130

122131
@pytest.fixture
123-
def make_async_runner():
132+
def make_async_runner(restore_signals):
124133
def _make_runner(*args, **kwargs):
125134
policy = policies.AsynchronousExecutionPolicy()
126135
policy._pollctl.SLEEP_MIN = 0.001

unittests/resources/checks/frontend_checks.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
#
99

1010
import os
11-
import signal
1211
import sys
1312
import time
1413

@@ -190,10 +189,13 @@ class SelfKillCheck(rfm.RunOnlyRegressionTest, special=True):
190189
executable = 'echo'
191190
sanity_patterns = sn.assert_true(1)
192191

192+
def __init__(self, signum):
193+
self.signum = signum
194+
193195
def run(self):
194196
super().run()
195197
time.sleep(0.5)
196-
os.kill(os.getpid(), signal.SIGTERM)
198+
os.kill(os.getpid(), self.signum)
197199

198200

199201
class CompileFailureCheck(rfm.RegressionTest):

unittests/test_cli.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1061,16 +1061,16 @@ def test_repeat_negative(run_reframe):
10611061

10621062
def test_parameterize_tests(run_reframe):
10631063
returncode, stdout, _ = run_reframe(
1064-
more_options=['-P', 'num_tasks=2,4,8', '-n', '^HelloTest'],
1064+
more_options=['-P', 'descr=msg=hello1,msg=hello2',
1065+
'-n', '^HelloTest'],
10651066
checkpath=['unittests/resources/checks/hellocheck.py'],
10661067
action='describe'
10671068
)
10681069
assert returncode == 0
10691070

1070-
test_descr = json.loads(stdout)
1071-
print(json.dumps(test_descr, indent=2))
1072-
num_tasks = {t['num_tasks'] for t in test_descr}
1073-
assert num_tasks == {2, 4, 8}
1071+
test_json = json.loads(stdout)
1072+
descr = [t['descr'] for t in test_json]
1073+
assert descr == ['msg=hello1', 'msg=hello2']
10741074

10751075

10761076
def test_parameterize_tests_invalid_params(run_reframe):

unittests/test_policies.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import contextlib
77
import os
88
import pytest
9+
import signal
910

1011
import reframe as rfm
1112
import reframe.core.runtime as rt
@@ -310,11 +311,16 @@ def test_pass_in_retries(make_runner, make_cases, tmp_path, common_exec_ctx):
310311
assert 0 == len(runner.stats.failed())
311312

312313

313-
def test_sigterm_handling(make_runner, make_cases, common_exec_ctx):
314+
@pytest.fixture(params=[signal.SIGTERM, signal.SIGHUP])
315+
def signum(request):
316+
return request.param
317+
318+
319+
def test_signal_handling(signum, make_runner, make_cases, common_exec_ctx):
314320
runner = make_runner()
315321
with pytest.raises(ForceExitError,
316-
match='received TERM signal'):
317-
runner.runall(make_cases([SelfKillCheck()]))
322+
match=f'received signal {signum}'):
323+
runner.runall(make_cases([SelfKillCheck(signum)]))
318324

319325
assert_all_dead(runner)
320326
assert runner.stats.num_cases() == 1
@@ -442,7 +448,7 @@ def max_jobs_opts(n):
442448

443449

444450
@pytest.fixture
445-
def make_async_runner():
451+
def make_async_runner(restore_signals):
446452
# We need to have control in the unit tests where the policy is created,
447453
# because in some cases we need it to be initialized after the execution
448454
# context. For this reason, we use a constructor fixture here.

0 commit comments

Comments
 (0)