test_run.py 12.1 KB
Newer Older
Paul McCarthy's avatar
Paul McCarthy committed
1
2
3
4
5
6
7
8
9
10
#!/usr/bin/env python
#
# test_run.py -
#
# Author: Paul McCarthy <pauldmccarthy@gmail.com>
#


import os.path as op
import            os
11
import            shutil
Paul McCarthy's avatar
Paul McCarthy committed
12
13
import            textwrap

14
from unittest import mock
Paul McCarthy's avatar
Paul McCarthy committed
15
16
17
18
19
20

import pytest

import fsl.utils.tempdir                  as tempdir
from   fsl.utils.platform import platform as fslplatform
import fsl.utils.run                      as run
21
import fsl.utils.fslsub                   as fslsub
Paul McCarthy's avatar
Paul McCarthy committed
22

Paul McCarthy's avatar
Paul McCarthy committed
23
from . import make_random_image, mockFSLDIR, CaptureStdout
Paul McCarthy's avatar
Paul McCarthy committed
24
25


Paul McCarthy's avatar
Paul McCarthy committed
26
27
28
pytestmark = pytest.mark.unixtest


29
30
31
32
33
34
def mkexec(path, contents):
    with open(path, 'wt') as f:
        f.write(contents)
    os.chmod(path, 0o755)


35
36
37
38
39
40
41
42
43
44
45
def test_prepareArgs():
    tests = [
        ('a b c',              ['a', 'b', 'c']),
        (['a', 'b', 'c'],      ['a', 'b', 'c']),
        ('abc "woop woop"',    ['abc', 'woop woop']),
        (['abc', 'woop woop'], ['abc', 'woop woop']),
    ]

    for args, expected in tests:
        assert run.prepareArgs((args, )) == expected

Paul McCarthy's avatar
Paul McCarthy committed
46
47
48
49
50
51
52
53
54
55
56
57
58
def test_run():

    test_script = textwrap.dedent("""
    #!/bin/bash

    echo "standard output - arguments: $@"
    echo "standard error" >&2
    exit {}
    """).strip()

    with tempdir.tempdir():

        # return code == 0
59
        mkexec('script.sh', test_script.format(0))
Paul McCarthy's avatar
Paul McCarthy committed
60

Paul McCarthy's avatar
Paul McCarthy committed
61
62
        expstdout = "standard output - arguments: 1 2 3\n"
        expstderr = "standard error\n"
Paul McCarthy's avatar
Paul McCarthy committed
63
64
65
66
67

        # test:
        #   - single string
        #   - packed sequence
        #   - unpacked sequence
Paul McCarthy's avatar
Paul McCarthy committed
68
69
        assert run.run('./script.sh 1 2 3')             == expstdout
        assert run.run(('./script.sh', '1', '2', '3'))  == expstdout
Paul McCarthy's avatar
Paul McCarthy committed
70
71
72
        assert run.run(*('./script.sh', '1', '2', '3')) == expstdout

        # test stdout/stderr
73
        stdout, stderr = run.run('./script.sh 1 2 3', stderr=True)
Paul McCarthy's avatar
Paul McCarthy committed
74
75
        assert stdout == expstdout
        assert stderr == expstderr
Paul McCarthy's avatar
Paul McCarthy committed
76
77

        # test return code
78
        res = run.run('./script.sh 1 2 3', exitcode=True)
79
        stdout, ret = res
Paul McCarthy's avatar
Paul McCarthy committed
80
        assert stdout == expstdout
Paul McCarthy's avatar
Paul McCarthy committed
81
        assert ret == 0
82
83
        stdout, stderr, ret = run.run('./script.sh 1 2 3', stderr=True,
                                      exitcode=True)
Paul McCarthy's avatar
Paul McCarthy committed
84
85
        assert stdout == expstdout
        assert stderr == expstderr
Paul McCarthy's avatar
Paul McCarthy committed
86
87
        assert ret == 0

88
89
90
91
92
93
        # stdout=False
        res = run.run('./script.sh 1 2 3', stdout=False)
        assert res == ()
        stderr = run.run('./script.sh 1 2 3', stdout=False, stderr=True)
        assert stderr == expstderr

Paul McCarthy's avatar
Paul McCarthy committed
94
        # return code != 0
95
        mkexec('./script.sh', test_script.format(255))
Paul McCarthy's avatar
Paul McCarthy committed
96
97
98
99

        with pytest.raises(RuntimeError):
            run.run('./script.sh 1 2 3')

100
        stdout, ret = run.run('./script.sh 1 2 3', exitcode=True)
Paul McCarthy's avatar
Paul McCarthy committed
101
        assert stdout == expstdout
Paul McCarthy's avatar
Paul McCarthy committed
102
103
104
        assert ret == 255


Paul McCarthy's avatar
Paul McCarthy committed
105
106
107
108
109
110
111
112
113
114
def test_run_tee():
    test_script = textwrap.dedent("""
    #!/bin/bash

    echo "standard output - arguments: $@"
    echo "standard error" >&2
    exit 0
    """).strip()

    with tempdir.tempdir():
115
        mkexec('script.sh', test_script)
Paul McCarthy's avatar
Paul McCarthy committed
116
117
118
119
120
121

        expstdout = "standard output - arguments: 1 2 3\n"
        expstderr = "standard error\n"

        capture = CaptureStdout()

122
        # default behaviour is for tee=True
Paul McCarthy's avatar
Paul McCarthy committed
123
        with capture:
124
125
126
            stdout = run.run('./script.sh 1 2 3')
        assert stdout         == expstdout
        assert capture.stdout == expstdout
Paul McCarthy's avatar
Paul McCarthy committed
127

128
129
        with capture.reset():
            stdout = run.run('./script.sh 1 2 3', log={'tee' : True})
Paul McCarthy's avatar
Paul McCarthy committed
130
131
132
        assert stdout         == expstdout
        assert capture.stdout == expstdout

133
134
135
136
137
138
        # disable forwarding
        with capture.reset():
            stdout = run.run('./script.sh 1 2 3', log={'tee' : False})
        assert stdout         == expstdout
        assert capture.stdout == ''

Paul McCarthy's avatar
Paul McCarthy committed
139
        with capture.reset():
140
141
            stdout, stderr = run.run('./script.sh 1 2 3', stderr=True,
                                     log={'tee' : True})
Paul McCarthy's avatar
Paul McCarthy committed
142
143
144
145
146
147
148
149

        assert stdout         == expstdout
        assert stderr         == expstderr
        assert capture.stdout == expstdout
        assert capture.stderr == expstderr

        with capture.reset():
            stdout, stderr, ret = run.run('./script.sh 1 2 3',
150
151
152
                                          stderr=True,
                                          exitcode=True,
                                          log={'tee' : True})
Paul McCarthy's avatar
Paul McCarthy committed
153
154
155
156
157
158
159
160

        assert ret            == 0
        assert stdout         == expstdout
        assert stderr         == expstderr
        assert capture.stdout == expstdout
        assert capture.stderr == expstderr

        with capture.reset():
161
162
163
            stdout, ret = run.run('./script.sh 1 2 3',
                                  exitcode=True,
                                  log={'tee' : True})
Paul McCarthy's avatar
Paul McCarthy committed
164
165
166
167
168
169

        assert ret            == 0
        assert stdout         == expstdout
        assert capture.stdout == expstdout


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def test_run_passthrough():

    test_script = textwrap.dedent("""
    #!/bin/bash

    echo "env: $RUN_TEST_ENV_VAR"
    """).strip()

    with tempdir.tempdir():

        # return code == 0
        mkexec('script.sh', test_script.format(0))

        env       = {'RUN_TEST_ENV_VAR' : 'howdy ho'}
        expstdout = "env: howdy ho\n"

        assert run.run('./script.sh', env=env) == expstdout


Paul McCarthy's avatar
Paul McCarthy committed
189
190
191
192
193
194
195
def test_cmdonly():
    assert run.run('script.sh',        cmdonly=True) == ['script.sh']
    assert run.run('script.sh 1 2 3',  cmdonly=True) == ['script.sh', '1', '2', '3']
    assert run.run(['script.sh'],      cmdonly=True) == ['script.sh']
    assert run.run(['script.sh', '1'], cmdonly=True) == ['script.sh', '1']


Paul McCarthy's avatar
Paul McCarthy committed
196
197
198
199
200
201
202
203
def test_dryrun():

    test_script = textwrap.dedent("""
    #!/bin/bash
    touch foo
    """).strip()

    with tempdir.tempdir():
204
        mkexec('./script.sh', test_script)
Paul McCarthy's avatar
Paul McCarthy committed
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221

        run.run('./script.sh')
        assert op.exists('foo')

        os.remove('foo')

        with run.dryrun():
            run.run('./script.sh')

        assert not op.exists('foo')


# test runfsl with/without $FSLDIR
def test_runfsl():

    test_script = textwrap.dedent("""
    #!/bin/bash
222
    echo {}
Paul McCarthy's avatar
Paul McCarthy committed
223
224
225
    exit 0
    """).strip()

226
227
228
    old_fsldir    = fslplatform.fsldir
    old_fsldevdir = fslplatform.fsldevdir

Paul McCarthy's avatar
Paul McCarthy committed
229
230
231
232
233
234
    try:
        with tempdir.tempdir():

            make_random_image('image.nii.gz')

            # no FSLDIR - should error
235
236
            fslplatform.fsldir    = None
            fslplatform.fsldevdir = None
Paul McCarthy's avatar
Paul McCarthy committed
237
            with pytest.raises(run.FSLNotPresent):
238
                run.runfsl('fslhd')
Paul McCarthy's avatar
Paul McCarthy committed
239
240
241
242
243

            # FSLDIR/bin exists - should be good
            fsldir = op.abspath('./fsl')
            fslhd  = op.join(fsldir, 'bin', 'fslhd')
            os.makedirs(op.join(fsldir, 'bin'))
244
245

            mkexec(fslhd, test_script.format('fsldir'))
Paul McCarthy's avatar
Paul McCarthy committed
246
247

            fslplatform.fsldir = fsldir
248
249
            assert run.runfsl('fslhd').strip() == 'fsldir'

250
251
252
253
            # non-FSL command - should error
            with pytest.raises(FileNotFoundError):
                run.runfsl('ls')

254
255
256
257
            # FSLDEVDIR should take precedence
            fsldevdir = './fsldev'
            fslhd  = op.join(fsldevdir, 'bin', 'fslhd')
            shutil.copytree(fsldir, fsldevdir)
258
259

            mkexec(fslhd, test_script.format('fsldevdir'))
260
261
262
263
264
265
266
267
268

            fslplatform.fsldevdir = fsldevdir
            fslplatform.fsldir    = None
            assert run.runfsl('fslhd').strip() == 'fsldevdir'

            # FSL_PREFIX should override all
            override = './override'
            fslhd    = op.join(override, 'fslhd')
            os.makedirs(override)
269
            mkexec(fslhd, test_script.format('override'))
270
271
272
273
274
275

            fslplatform.fsldir    = None
            fslplatform.fsldevdir = None
            run.FSL_PREFIX = override
            assert run.runfsl('fslhd').strip() == 'override'

Paul McCarthy's avatar
Paul McCarthy committed
276
    finally:
277
278
279
        fslplatform.fsldir    = old_fsldir
        fslplatform.fsldevdir = old_fsldevdir
        run.FSL_PREFIX        = None
280
281


282
283
284
def mock_fsl_sub(*cmd, **kwargs):
    if len(cmd) == 1 and isinstance(cmd[0], str):
        name = cmd[0].split()[0]
285
286
287
288
289
290
291
292
293
294
295
296
    else:
        name = cmd[0]

    name = op.basename(name)

    jid = '12345'
    output = run.run(cmd)

    with open('{}.o{}'.format(name, jid), 'wt') as f:
        f.write(output)

    with open('{}.e{}'.format(name, jid), 'wt') as f:
Paul McCarthy's avatar
Paul McCarthy committed
297
298
        for k in sorted(kwargs.keys()):
            f.write('{}: {}\n'.format(k, kwargs[k]))
299

300
    return (jid, '')
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315


def test_run_submit():

    def mkexec(path, contents):
        with open(path, 'wt') as f:
            f.write(contents)
        os.chmod(path, 0o755)

    test_script = textwrap.dedent("""
    #!/usr/bin/env bash
    echo test_script running
    exit 0
    """).strip()

316
317
    with tempdir.tempdir(), \
         mockFSLDIR(), \
318
         mock.patch('fsl.wrappers.fsl_sub', mock_fsl_sub):
319
320
321

        mkexec(op.expandvars('$FSLDIR/bin/fsltest'), test_script)

322
        jid = run.run('fsltest', submit=True)
323
        assert jid == '12345'
324
        stdout, stderr = fslsub.output(jid)
Paul McCarthy's avatar
Paul McCarthy committed
325
326
        assert stdout == 'test_script running\n'
        assert stderr == ''
327

Paul McCarthy's avatar
Paul McCarthy committed
328
        # or can pass submit opts as a dict
329
        kwargs = {'name' : 'abcde', 'ram' : '4GB'}
330
        jid = run.run('fsltest', submit=kwargs)
331
332
        assert jid == '12345'
        stdout, stderr = fslsub.output(jid)
Paul McCarthy's avatar
Paul McCarthy committed
333
        experr = '\n'.join(['{}: {}'.format(k, kwargs[k])
Paul McCarthy's avatar
Paul McCarthy committed
334
                            for k in sorted(kwargs.keys())]) + '\n'
Paul McCarthy's avatar
Paul McCarthy committed
335
336
        assert stdout == 'test_script running\n'
        assert stderr == experr
337

Paul McCarthy's avatar
Paul McCarthy committed
338
339
340
341
342
343
344
        # or can pass submit opts as kwargs
        kwargs = {'name' : 'abcde', 'ram' : '4GB'}
        jid = run.run('fsltest', submit=True, **kwargs)
        assert jid == '12345'
        stdout, stderr = fslsub.output(jid)
        experr = '\n'.join(['{}: {}'.format(k, kwargs[k])
                            for k in sorted(kwargs.keys())]) + '\n'
Paul McCarthy's avatar
Paul McCarthy committed
345
346
        assert stdout == 'test_script running\n'
        assert stderr == experr
347
348


Paul McCarthy's avatar
Paul McCarthy committed
349

350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
def test_run_streams():
    """
    """

    test_script = textwrap.dedent("""
    #!/usr/bin/env bash
    echo standard output
    echo standard error >&2
    exit 0
    """).strip()

    expstdout = 'standard output\n'
    expstderr = 'standard error\n'

    with tempdir.tempdir():
        mkexec('./script.sh', test_script)

        with open('my_stdout', 'wt') as stdout, \
             open('my_stderr', 'wt') as stderr:

            stdout, stderr = run.run('./script.sh',
                                     stderr=True,
                                     log={'stdout' : stdout,
                                          'stderr' : stderr})

        assert stdout                         == expstdout
        assert stderr                         == expstderr
        assert open('my_stdout', 'rt').read() == expstdout
        assert open('my_stderr', 'rt').read() == expstderr


        capture = CaptureStdout()

        with open('my_stdout', 'wt') as stdout, \
             open('my_stderr', 'wt') as stderr, \
             capture.reset():

            stdout, stderr = run.run('./script.sh',
                                     stderr=True,
                                     log={'tee'    : True,
                                          'stdout' : stdout,
                                          'stderr' : stderr})

        assert stdout                         == expstdout
        assert stderr                         == expstderr
        assert capture.stdout                 == expstdout
        assert capture.stderr                 == expstderr
        assert open('my_stdout', 'rt').read() == expstdout
        assert open('my_stderr', 'rt').read() == expstderr


def test_run_logcmd():
    test_script = textwrap.dedent("""
    #!/usr/bin/env bash
    echo output $@
    exit 0
    """).strip()

408
409
    expcmd = './script.sh 1 2 3\n'
    expstdout = 'output 1 2 3\n'
410
411
412
413

    with tempdir.tempdir():
        mkexec('script.sh', test_script)

414
415
416
417
418
419
        with open('my_stdout', 'wt') as stdoutf:
            stdout = run.run('./script.sh 1 2 3',
                             log={'cmd' : stdoutf})

        assert stdout                         == expstdout
        assert open('my_stdout', 'rt').read() == expcmd
420
421
422

        with open('my_stdout', 'wt') as stdoutf:
            stdout = run.run('./script.sh 1 2 3',
423
                             log={'cmd' : stdoutf, "stdout" : stdoutf})
424
425

        assert stdout                         == expstdout
426
        assert open('my_stdout', 'rt').read() == expcmd + expstdout