-
Paul McCarthy authoredPaul McCarthy authored
test_create_remove_wrapper.py 9.85 KiB
#!/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 sys
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')
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
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
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
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'}
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)
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)
if callable(test):
print(f'Running test {testname}')
test()