Skip to content

Commit 509cb48

Browse files
committed
Merge branch 'master' into develop
2 parents 584c84b + 26ceee7 commit 509cb48

File tree

5 files changed

+53
-18
lines changed

5 files changed

+53
-18
lines changed

reframe/core/pipeline.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1786,10 +1786,11 @@ def _setup_paths(self):
17861786
self.current_system.name, self._current_partition.name,
17871787
self._current_environ.name, self.short_name
17881788
)
1789-
self._outputdir = runtime.make_outputdir(
1790-
self.current_system.name, self._current_partition.name,
1791-
self._current_environ.name, self.short_name
1792-
)
1789+
if not self.is_dry_run():
1790+
self._outputdir = runtime.make_outputdir(
1791+
self.current_system.name, self._current_partition.name,
1792+
self._current_environ.name, self.short_name
1793+
)
17931794
except OSError as e:
17941795
raise PipelineError('failed to set up paths') from e
17951796

reframe/utility/osext.py

Lines changed: 31 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ class _ProcFuture:
4848

4949
def __init__(self, check=False, *args, **kwargs):
5050
self._proc = None
51+
self._stdout = None
52+
self._stderr = None
5153
self._exitcode = None
5254
self._signal = None
5355
self._check = check
@@ -77,6 +79,17 @@ def start(self):
7779

7880
return self
7981

82+
def _shutdown(self):
83+
'''Shut down cleanly the underlying `Popen` object.'''
84+
if self._stdout is None and self._proc.stdout:
85+
self._stdout = self._proc.stdout.read()
86+
87+
if self._stderr is None and self._proc.stderr:
88+
self._stderr = self._proc.stderr.read()
89+
90+
self._proc.wait()
91+
self._proc.communicate()
92+
8093
@property
8194
def pid(self):
8295
'''The PID of the spawned process.'''
@@ -179,6 +192,7 @@ def _wait(self, *, nohang=False):
179192
except OSError as e:
180193
if e.errno == errno.ECHILD:
181194
self._completed = True
195+
self._shutdown()
182196
return self._completed
183197
else:
184198
raise e
@@ -191,16 +205,19 @@ def _wait(self, *, nohang=False):
191205
elif os.WIFSIGNALED(status):
192206
self._signal = os.WTERMSIG(status)
193207

208+
# Retrieve the stdout/stderr and clean up the underlying Popen object
194209
self._completed = True
210+
self._shutdown()
195211

196212
# Call any done callbacks
197213
for func in self._done_callbacks:
198214
func(self)
199215

200216
# Start the next futures in the chain
201-
for fut, cond in self._next:
202-
if cond(self):
203-
fut.start()
217+
if not self._cancelled:
218+
for fut, cond in self._next:
219+
if cond(self):
220+
fut.start()
204221

205222
return self._completed
206223

@@ -230,29 +247,28 @@ def exception(self):
230247
if not self._check:
231248
return
232249

233-
if self._proc.returncode == 0:
250+
if self._exitcode == 0:
234251
return
235252

236253
return SpawnedProcessError(self._proc.args,
237-
self._proc.stdout.read(),
238-
self._proc.stderr.read(),
239-
self._proc.returncode)
254+
self._stdout, self._stderr,
255+
self._exitcode)
240256

241257
def stdout(self):
242258
'''Retrieve the standard output of the spawned process.
243259
244260
This is a blocking call and will wait until the future finishes.
245261
'''
246262
self._wait()
247-
return self._proc.stdout
263+
return self._stdout
248264

249265
def stderr(self):
250266
'''Retrieve the standard error of the spawned process.
251267
252268
This is a blocking call and will wait until the future finishes.
253269
'''
254270
self._wait()
255-
return self._proc.stderr
271+
return self._stderr
256272

257273

258274
def run_command(cmd, check=False, timeout=None, **kwargs):
@@ -281,9 +297,12 @@ def run_command(cmd, check=False, timeout=None, **kwargs):
281297
proc_stdout, proc_stderr = proc.communicate(timeout=timeout)
282298
except subprocess.TimeoutExpired as e:
283299
os.killpg(proc.pid, signal.SIGKILL)
284-
raise SpawnedProcessTimeout(e.cmd,
285-
proc.stdout.read(),
286-
proc.stderr.read(), timeout) from None
300+
os.waitpid(proc.pid, 0)
301+
proc_stdout = proc.stdout.read()
302+
proc_stderr = proc.stderr.read()
303+
proc.communicate()
304+
raise SpawnedProcessTimeout(e.cmd, proc_stdout,
305+
proc_stderr, timeout) from None
287306

288307
completed = subprocess.CompletedProcess(cmd,
289308
returncode=proc.returncode,

unittests/test_cli.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ def test_check_success(run_reframe, tmp_path, run_action):
162162
'reports' / 'run-report-0.json')
163163

164164

165+
def test_outputdir_dry_run(run_reframe, tmp_path):
166+
# We need to make sure that outputdir is maintained if the same test is
167+
# rerun with `--dry-run`
168+
169+
run_reframe(action='run', more_options=['-n', '^HelloTest'])
170+
run_reframe(action='dry_run', more_options=['-n', '^HelloTest'])
171+
prefix = (tmp_path / 'output' / 'generic' / 'default' /
172+
'builtin' / 'HelloTest')
173+
for path in ('rfm_build.err', 'rfm_build.out', 'rfm_build.sh',
174+
'rfm_job.err', 'rfm_job.out', 'rfm_job.sh'):
175+
assert (prefix / path).exists()
176+
177+
165178
def test_check_restore_session_failed(run_reframe, tmp_path):
166179
run_reframe(
167180
checkpath=['unittests/resources/checks_unlisted/deps_complex.py']

unittests/test_shell.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ def test_trap_signal(script_file):
130130

131131
# We kill the whole spawned process group (we are launching a shell)
132132
os.killpg(proc.pid, 15)
133-
proc.wait()
133+
proc.communicate()
134134

135135
f_stdout.flush()
136136
f_stdout.seek(0)

unittests/test_utility.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ def test_command_async():
7878
t_launch = time.time() - t_launch
7979

8080
proc.wait()
81+
proc.communicate()
8182
t_sleep = time.time() - t_sleep
8283

8384
# Now check the timings
@@ -110,7 +111,7 @@ def test_command_futures():
110111
assert not proc.is_session()
111112

112113
# stdout must block
113-
assert proc.stdout().read() == 'hello\n'
114+
assert proc.stdout() == 'hello\n'
114115
assert proc.exitcode == 0
115116
assert proc.signal is None
116117

@@ -270,6 +271,7 @@ def test_command_futures_chain_cancel():
270271

271272
assert proc1.started()
272273
proc1.cancel()
274+
proc1.wait()
273275
assert proc1.cancelled()
274276
assert not proc2.started()
275277

0 commit comments

Comments
 (0)