Newer
Older
#!/usr/bin/env python
#
# test_create_remove_wrapper.py - Test the createFSLWrapper.sh and
# removeFSLWrapper.sh scripts. Requires Python>=3.7.
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#
import os.path as op
import subprocess as sp
import textwrap as tw
import itertools as it
import os
import shlex
import shutil
import tempfile
import contextlib
from unittest import mock
# Paths to create/remove wrapper scripts, when
# running from an in-source version. These may
# be overwritten within the __main__ clause at
# the bottom, where BASE_DIR may be provided
# as a command-line argument.
BASE_DIR = op.abspath(op.join(op.dirname(__file__), '..'))
CREATE_WRAPPER = op.join(BASE_DIR, 'share', 'fsl', 'sbin', 'createFSLWrapper')
REMOVE_WRAPPER = op.join(BASE_DIR, 'share', 'fsl', 'sbin', 'removeFSLWrapper')
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
def run(cmd, **kwargs):
return sp.run(shlex.split(cmd), check=True, **kwargs)
@contextlib.contextmanager
def temp_fsldir():
testdir = tempfile.mkdtemp()
prevdir = os.getcwd()
fsldir = op.join(testdir, 'fsl')
wrapperdir = op.join(testdir, 'fsl', 'share', 'fsl', 'bin')
try:
os.chdir(testdir)
os.mkdir(fsldir)
with mock.patch.dict(os.environ, {
'FSLDIR' : fsldir,
'PREFIX' : fsldir,
'FSL_CREATE_WRAPPER_SCRIPTS' : '1'}):
yield fsldir, wrapperdir
finally:
os.chdir(prevdir)
shutil.rmtree(testdir)
def touch(path):
dirname = op.dirname(path)
if not op.exists(dirname):
os.makedirs(dirname)
with open(path, 'wt') as f:
f.write('.')
def get_called_command(filename):
"""Returns the command that is being called by the given wrapper script.
"""
with open(filename, 'rt') as f:
line = f.readlines()[1]
tokens = line.split()
cmd = op.basename(tokens[0])
if cmd in ('python', 'pythonw'):
cmd = tokens[2]
return cmd
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
def test_env_vars_not_set():
"""Test that wrapper scripts are not created if the
FSL_CREATE_WRAPPER_SCRIPTS, FSLDIR, or PREFIX environment variables
are not set.
"""
with temp_fsldir() as (fsldir, wrapperdir):
touch(op.join(fsldir, 'bin', 'test_script'))
env = os.environ.copy()
env.pop('FSL_CREATE_WRAPPER_SCRIPTS')
run(f'{CREATE_WRAPPER} test_script', env=env)
assert not op.exists(op.join(wrapperdir, 'test_script1'))
env = os.environ.copy()
env.pop('FSLDIR')
run(f'{CREATE_WRAPPER} test_script', env=env)
assert not op.exists(op.join(wrapperdir, 'test_script1'))
env = os.environ.copy()
env.pop('PREFIX')
run(f'{CREATE_WRAPPER} test_script', env=env)
assert not op.exists(op.join(wrapperdir, 'test_script1'))
# FSLDIR invalid
env = os.environ.copy()
env['FSLDIR'] = '/some/non-existent/path'
run(f'{CREATE_WRAPPER} test_script', env=env)
assert not op.exists(op.join(wrapperdir, 'test_script1'))
# FSLDIR != PREFIX
env = os.environ.copy()
env['FSLDIR'] = op.join(env['PREFIX'], 'other_fsl')
run(f'{CREATE_WRAPPER} test_script', env=env)
assert not op.exists(op.join(wrapperdir, 'test_script1'))
def test_create_python_wrapper():
"""Test creation of a wrapper script for a python executable"""
with temp_fsldir() as (fsldir, wrapperdir):
script_path = op.join(fsldir, 'bin', 'test_script')
wrapper_path = op.join(wrapperdir, 'test_script')
touch(script_path)
with open(script_path, 'wt') as f:
f.write('#!/usr/bin/env python\n')
f.write('print("hello")\n')
expect = tw.dedent(f"""
#!/usr/bin/env bash
{fsldir}/bin/python -I {script_path} "$@"
""").strip()
run(f'{CREATE_WRAPPER} test_script')
assert op.exists(wrapper_path)
with open(wrapper_path, 'rt') as f:
got = f.read().strip()
assert got == expect
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
def test_create_pythonw_wrapper():
"""Test creation of a wrapper script for a python script which
uses pythonw as the interpreter (for GUI apps on macOS).
"""
hashbangs = [
'#!/bin/bash /usr/bin/pythonw',
'#!/usr/bin/pythonw',
'#!/usr/bin/env pythonw']
with temp_fsldir() as (fsldir, wrapperdir):
script_path = op.join(fsldir, 'bin', 'test_script')
wrapper_path = op.join(wrapperdir, 'test_script')
touch(script_path)
for hb in hashbangs:
with open(script_path, 'wt') as f:
f.write(hb + '\n')
f.write('print("hello")\n')
expect = tw.dedent(f"""
#!/usr/bin/env bash
{fsldir}/bin/pythonw -I {script_path} "$@"
""").strip()
run(f'{CREATE_WRAPPER} test_script')
assert op.exists(wrapper_path)
with open(wrapper_path, 'rt') as f:
got = f.read().strip()
assert got == expect
def test_create_other_wrapper():
"""Test creation of a wrapper script for a non-python executable."""
with temp_fsldir() as (fsldir, wrapperdir):
script_path = op.join(fsldir, 'bin', 'test_script')
wrapper_path = op.join(wrapperdir, 'test_script')
touch(script_path)
with open(op.join(fsldir, 'bin', 'test_script'), 'wt') as f:
f.write('#!/usr/bin/env bash\n')
f.write('echo "hello"\n')
expect = tw.dedent(f"""
#!/usr/bin/env bash
{script_path} "$@"
""").strip()
run(f'{CREATE_WRAPPER} test_script')
assert op.exists(wrapper_path)
with open(wrapper_path, 'rt') as f:
got = f.read().strip()
assert got == expect
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
def test_permissions_preserved():
"""Test that wrapper script has same permissions as wrapped script."""
with temp_fsldir() as (fsldir, wrapperdir):
perms = [0o777, 0o755, 0o644, 0o600, 0o755, 0o700]
script_path = op.join(fsldir, 'bin', 'test_script')
wrapper_path = op.join(wrapperdir, 'test_script')
touch(script_path)
for perm in perms:
os.chmod(script_path, perm)
run(f'{CREATE_WRAPPER} test_script')
stat = os.stat(wrapper_path)
assert (stat.st_mode & 0o777) == perm
def test_create_remove_wrappers():
"""Tests normal usage. """
with temp_fsldir() as (fsldir, wrapperdir):
touch(op.join(fsldir, 'bin', 'test_script1'))
touch(op.join(fsldir, 'bin', 'test_script2'))
run(f'{CREATE_WRAPPER} test_script1 test_script2')
assert op.exists(op.join(wrapperdir, 'test_script1'))
assert op.exists(op.join(wrapperdir, 'test_script2'))
run(f'{REMOVE_WRAPPER} test_script1 test_script2')
assert not op.exists(op.join(wrapperdir, 'test_script1'))
assert not op.exists(op.join(wrapperdir, 'test_script2'))
def test_create_gui_wrappers():
"""Tests creation of wrappers for FSL GUI commands, which are called
"<Command>_gui" on macOS, and "<Command>" on linux, where "<command>"
(note the case) may also exist. Post-link scripts should only pass the
"<Command>_gui" variant.
"""
# Test outcome differs for different platforms.
# Keys are passed to createFSLWrapper, values are
# wrappers that should be created
if sys.platform == 'darwin':
scripts = {'script' : 'script',
'Script_gui' : 'Script_gui'}
# linux
else:
scripts = {'script' : 'script',
'Script_gui' : 'Script'}
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
with temp_fsldir() as (fsldir, wrapperdir):
for target in scripts.values():
touch(op.join(fsldir, 'bin', target))
for wrappers in it.permutations(scripts.keys()):
args = ' '.join(wrappers)
run(f'{CREATE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert op.exists(wrapper)
assert get_called_command(wrapper) == scripts[arg]
run(f'{REMOVE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert not op.exists(wrapper)
def test_create_wrappers_no_handle_gui_wrappers():
"""Tests creation of wrappers for FSL commands which might end with "_gui",
and for which no special handling should take place (see
test_create_gui_wrappers above).
"""
scripts = {'fsl' : 'fsl',
'fsl_gui' : 'fsl_gui'}
with temp_fsldir() as (fsldir, wrapperdir):
for target in scripts.values():
touch(op.join(fsldir, 'bin', target))
for wrappers in it.permutations(scripts.keys()):
args = ' '.join(wrappers)
run(f'{CREATE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert op.exists(wrapper)
assert get_called_command(wrapper) == scripts[arg]
run(f'{REMOVE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert not op.exists(wrapper)
def test_create_wrappers_rename():
"""Tests the renaming functionality in createFSLWrapper. If
$FSLDIR/bin/script exists, a wrapper with a different name
(e.g. $FSLDIR/share/fsl/bin/renamed_script) can be created by passing
"script=renamed_script".
"""
# Keys are passed to createFSLWrapper, values
# are wrappers that should be created
scripts = {
'script1=renamed_script1' : 'renamed_script1',
'script2=renamed_script2' : 'renamed_script2',
'script3_gui=renamed_script3_gui' : 'renamed_script3_gui',
'script4_gui=renamed_script4' : 'renamed_script4'
}
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
with temp_fsldir() as (fsldir, wrapperdir):
for script in scripts.keys():
target = script.split('=')[0]
with open(target, 'wt') as f:
touch(op.join(fsldir, 'bin', target))
for wrappers in it.permutations(scripts.keys()):
args = ' '.join(wrappers)
run(f'{CREATE_WRAPPER} {args}')
for arg in wrappers:
target = arg.split('=')[0]
wrapper = op.join(wrapperdir, scripts[arg])
assert op.exists(wrapper)
assert get_called_command(wrapper) == target
run(f'{REMOVE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert not op.exists(wrapper)
def test_create_wrappers_multiple_same():
"""Tests creating multiple wrapper scripts which call the same
target command.
"""
# Keys are passed to createFSLWrapper, values
# are wrappers that should be created
scripts = {
'scripta' : 'scripta',
'scripta=script1' : 'script1',
'scripta=script2' : 'script2',
'scriptb' : 'scriptb',
'scriptc=script3' : 'script3',
'scriptc=script4' : 'script4',
}
with temp_fsldir() as (fsldir, wrapperdir):
for script in scripts.keys():
target = script.split('=')[0]
with open(target, 'wt') as f:
touch(op.join(fsldir, 'bin', target))
for wrappers in it.permutations(scripts.keys()):
args = ' '.join(wrappers)
run(f'{CREATE_WRAPPER} {args}')
for arg in wrappers:
target = arg.split('=')[0]
wrapper = op.join(wrapperdir, scripts[arg])
assert op.exists(wrapper)
assert get_called_command(wrapper) == target
run(f'{REMOVE_WRAPPER} {args}')
for arg in wrappers:
target = scripts[arg]
wrapper = op.join(wrapperdir, target)
assert not op.exists(wrapper)
if __name__ == '__main__':
# base dir can be speecified on command line
if len(sys.argv) > 1:
BASE_DIR = sys.argv[1]
SBIN_DIR = op.join(BASE_DIR, 'share', 'fsl', 'sbin')
CREATE_WRAPPER = op.join(SBIN_DIR, 'createFSLWrapper')
REMOVE_WRAPPER = op.join(SBIN_DIR, 'removeFSLWrapper')
thismod = sys.modules[__name__]
for testname in dir(thismod):
if testname.startswith('test_'):
test = getattr(thismod, testname)