Skip to content

Commit 29e7c43

Browse files
authored
Merge pull request #470 from nipreps/masks/refactor-circle
MAINT: Fix test_masks
2 parents 4687dd6 + 1b4bf7a commit 29e7c43

File tree

7 files changed

+111
-72
lines changed

7 files changed

+111
-72
lines changed

.circleci/config.yml

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ version: 2
3232
jobs:
3333
build:
3434
machine:
35-
image: circleci/classic:201711-01
35+
image: circleci/classic:201808-01
3636
working_directory: /tmp/src/niworkflows
3737
environment:
3838
TZ: "/usr/share/zoneinfo/America/Los_Angeles"
@@ -98,7 +98,7 @@ jobs:
9898
get_data:
9999
machine:
100100
# Ubuntu 14.04 with Docker 17.10.0-ce
101-
image: circleci/classic:201711-01
101+
image: circleci/classic:201808-01
102102
working_directory: /home/circleci/data
103103
steps:
104104
- restore_cache:
@@ -146,7 +146,7 @@ jobs:
146146
get_regression_data:
147147
machine:
148148
# Ubuntu 14.04 with Docker 17.10.0-ce
149-
image: circleci/classic:201711-01
149+
image: circleci/classic:201808-01
150150
working_directory: /home/circleci/data
151151
steps:
152152
- restore_cache:
@@ -181,7 +181,7 @@ jobs:
181181

182182
test_pytest:
183183
machine:
184-
image: circleci/classic:201711-01
184+
image: circleci/classic:201808-01
185185
working_directory: /tmp/tests
186186
steps:
187187
- attach_workspace:
@@ -267,7 +267,7 @@ jobs:
267267

268268
test_masks:
269269
machine:
270-
image: circleci/classic:201711-01
270+
image: circleci/classic:201808-01
271271
working_directory: /tmp/masks
272272
steps:
273273
- attach_workspace:
@@ -289,58 +289,73 @@ jobs:
289289
- restore_cache:
290290
keys:
291291
- regression-v2-{{ .Revision }}
292-
293-
- checkout:
294-
path: /tmp/src/niworkflows
295-
- run:
296-
name: Set PR number
297-
command: |
298-
echo 'export CIRCLE_PR_NUMBER="${CIRCLE_PR_NUMBER:-${CIRCLE_PULL_REQUEST##*/}}"' >> $BASH_ENV
299-
source $BASH_ENV
300-
echo $CIRCLE_PR_NUMBER
301-
- run:
302-
name: Get codecov
303-
command: python -m pip install codecov
304-
292+
- restore_cache:
293+
keys:
294+
- masks-workdir-v1-{{ .Branch }}-{{epoch}}
295+
- masks-workdir-v1-{{ .Branch }}-
296+
- masks-workdir-v1-master-
297+
- masks-workdir-v1-
305298
- run:
306299
name: Run regression tests on EPI masks
307300
no_output_timeout: 2h
308301
command: |
309-
mkdir -p /tmp/masks/reports && \
310-
docker run -ti --rm=false -w /src/niworkflows \
311-
-e COVERAGE_FILE=/tmp/masks/.coverage \
312-
-v /tmp/data:/tmp/data -v /tmp/masks:/tmp/masks \
302+
mkdir -p /tmp/masks/{reports,workdir} && \
303+
docker run -ti -u $(id -u) -w /src/niworkflows \
304+
-e COVERAGE_FILE=/tmp/masks/reports/.coverage \
305+
-v /tmp/data:/tmp/data -v /tmp/masks/reports:/tmp/masks/reports \
313306
-e FMRIPREP_REGRESSION_SOURCE=/tmp/data/fmriprep_bold_truncated \
314307
-e FMRIPREP_REGRESSION_TARGETS=/tmp/data/fmriprep_bold_mask \
315308
-e FMRIPREP_REGRESSION_REPORTS=/tmp/masks/reports \
309+
-e CACHED_WORK_DIRECTORY=/tmp/work -v /tmp/masks/workdir:/tmp/work \
316310
niworkflows:latest \
317-
pytest --junit-xml=/tmp/masks/regression.xml \
318-
--cov niworkflows --cov-report xml:/tmp/masks/coverage.xml \
311+
coverage run -p --rcfile=setup.cfg \
312+
-m pytest --junit-xml=/tmp/masks/reports/regression.xml \
319313
niworkflows/func/tests/
314+
- save_cache:
315+
key: masks-workdir-v1-{{ .Branch }}-{{ epoch }}
316+
paths:
317+
- /tmp/masks/workdir
318+
- store_artifacts:
319+
path: /tmp/masks/reports
320+
- store_test_results:
321+
path: /tmp/masks/reports
320322

323+
- run:
324+
name: Coverage preparation
325+
command: |
326+
docker run -ti -u $(id -u) -w /tmp/masks/reports \
327+
-e COVERAGE_FILE=/tmp/masks/reports/.coverage \
328+
-v /tmp/masks/reports:/tmp/masks/reports \
329+
niworkflows:latest coverage combine
330+
docker run -ti -u $(id -u) -w /tmp/masks/reports \
331+
-e COVERAGE_FILE=/tmp/masks/reports/.coverage \
332+
-v /tmp/masks/reports:/tmp/masks/reports \
333+
niworkflows:latest coverage xml -o coverage.xml
334+
- checkout:
335+
path: /tmp/src/niworkflows
336+
- run:
337+
name: Get codecov
338+
command: python -m pip install codecov
321339
- run:
322340
name: Submit masks test coverage
341+
working_directory: /tmp/src/niworkflows
323342
command: |
324-
python -m codecov --file /tmp/masks/coverage.xml --root /tmp/src/niworkflows \
325-
--flags masks -e CIRCLE_JOB
326-
343+
cp /tmp/masks/reports/coverage.xml .
344+
sed -i "s+/src/niworkflows+/tmp/src/niworkflows+g" coverage.xml
345+
python -m codecov --file coverage.xml --flags masks -e CIRCLE_JOB
327346
- run:
328347
name: Package new masks
329348
when: always
330349
no_output_timeout: 10m
331350
working_directory: /tmp/data
332351
command: |
333352
tar cfz /tmp/masks/fmriprep_bold_mask.tar.gz fmriprep_bold_mask/*/*.nii.gz
334-
335353
- store_artifacts:
336-
path: /tmp/masks
337-
338-
- store_test_results:
339-
path: /tmp/masks
354+
path: /tmp/masks/fmriprep_bold_mask.tar.gz
340355

341356
test_package:
342357
machine:
343-
image: circleci/classic:201711-01
358+
image: circleci/classic:201808-01
344359
working_directory: /tmp/src/niworkflows
345360
steps:
346361
- checkout
@@ -365,7 +380,7 @@ jobs:
365380
366381
deploy:
367382
machine:
368-
image: circleci/classic:201711-01
383+
image: circleci/classic:201808-01
369384
working_directory: /tmp/src/niworkflows
370385
steps:
371386
- checkout

Dockerfile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,7 @@ RUN pip install --no-cache-dir -r requirements.txt && \
156156
RUN python -c "from templateflow import api as tfapi; \
157157
tfapi.get(['MNI152Lin', 'MNI152NLin2009cAsym', 'OASIS30ANTs'], suffix='T1w'); \
158158
tfapi.get(['MNI152Lin', 'MNI152NLin2009cAsym', 'OASIS30ANTs'], desc='brain', suffix='mask'); \
159+
tfapi.get(['MNI152NLin2009cAsym'], desc='fMRIPrep', suffix='boldref'); \
159160
tfapi.get('OASIS30ANTs', resolution=1, desc='4', suffix='dseg'); \
160161
tfapi.get(['OASIS30ANTs', 'NKI'], resolution=1, label='brain', suffix='probseg'); \
161162
tfapi.get(['OASIS30ANTs', 'NKI'], resolution=1, desc='BrainCerebellumRegistration', suffix='mask'); "
@@ -165,6 +166,8 @@ WORKDIR /src/niworkflows/
165166
RUN pip install --no-cache-dir -e .[all] && \
166167
rm -rf $HOME/.cache/pip
167168

169+
COPY docker/files/nipype.cfg /home/niworkflows/.nipype/nipype.cfg
170+
168171
# Cleanup and ensure perms.
169172
RUN rm -rf $HOME/.npm $HOME/.conda $HOME/.empty && \
170173
find $HOME -type d -exec chmod go=u {} + && \

docker/files/nipype.cfg

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[execution]
2+
hash_method = content
3+
poll_sleep_duration = 0.01
4+
remove_unnecessary_outputs = true
5+
crashfile_format = txt
6+
profile_runtime = false
7+
use_relative_paths = false

niworkflows/func/tests/test_util.py

Lines changed: 14 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010

1111
from niworkflows.interfaces.masks import ROIsPlot
1212

13-
from ..util import init_bold_reference_wf, _pass_dummy_scans
13+
from ..util import init_bold_reference_wf
1414

1515

1616
def symmetric_overlap(img1, img2):
@@ -58,20 +58,29 @@ def symmetric_overlap(img1, img2):
5858
)
5959
])
6060
def test_masking(input_fname, expected_fname):
61-
bold_reference_wf = init_bold_reference_wf(omp_nthreads=1)
62-
bold_reference_wf.inputs.inputnode.bold_file = input_fname
61+
basename = Path(input_fname).name
62+
dsname = Path(expected_fname).parent.name
6363

6464
# Reconstruct base_fname from above
65-
dirname, basename = os.path.split(input_fname)
66-
dsname = os.path.basename(dirname)
6765
reports_dir = Path(os.getenv('FMRIPREP_REGRESSION_REPORTS', ''))
6866
newpath = reports_dir / dsname
67+
68+
name = basename.rstrip('_bold.nii.gz').replace('-', '_')
69+
bold_reference_wf = init_bold_reference_wf(omp_nthreads=1, name=name)
70+
bold_reference_wf.inputs.inputnode.bold_file = input_fname
71+
base_dir = os.getenv('CACHED_WORK_DIRECTORY')
72+
if base_dir:
73+
base_dir = Path(base_dir) / dsname
74+
base_dir.mkdir(parents=True, exist_ok=True)
75+
bold_reference_wf.base_dir = str(base_dir)
76+
6977
out_fname = fname_presuffix(basename, suffix='_masks.svg', use_ext=False,
7078
newpath=str(newpath))
7179
newpath.mkdir(parents=True, exist_ok=True)
7280

7381
mask_diff_plot = pe.Node(ROIsPlot(colors=['limegreen'], levels=[0.5]),
7482
name='mask_diff_plot')
83+
mask_diff_plot.always_run = True
7584
mask_diff_plot.inputs.in_mask = expected_fname
7685
mask_diff_plot.inputs.out_report = out_fname
7786

@@ -94,14 +103,3 @@ def test_masking(input_fname, expected_fname):
94103
copy=True)
95104

96105
assert overlap > 0.95, input_fname
97-
98-
99-
@pytest.mark.parametrize('algo_dummy_scans,dummy_scans,expected_out', [
100-
(2, 1, 1),
101-
(2, None, 2),
102-
(2, 0, 0),
103-
])
104-
def test_pass_dummy_scans(algo_dummy_scans, dummy_scans, expected_out):
105-
skip_vols = _pass_dummy_scans(algo_dummy_scans, dummy_scans)
106-
107-
assert skip_vols == expected_out

niworkflows/func/util.py

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
from ..interfaces.masks import SimpleShowMaskRPT
2121
from ..interfaces.registration import EstimateReferenceImage
2222
from ..interfaces.utils import CopyXForm
23+
from ..utils.misc import pass_dummy_scans as _pass_dummy_scans
2324

2425

2526
DEFAULT_MEMORY_MIN_GB = 0.01
@@ -425,25 +426,3 @@ def init_skullstrip_bold_wf(name='skullstrip_bold_wf'):
425426
])
426427

427428
return workflow
428-
429-
430-
def _pass_dummy_scans(algo_dummy_scans, dummy_scans=None):
431-
"""
432-
Graft manually provided number of dummy scans, if necessary.
433-
434-
Parameters
435-
----------
436-
algo_dummy_scans : int
437-
number of volumes to skip determined by an algorithm
438-
dummy_scans : int or None
439-
number of volumes to skip determined by the user
440-
441-
Returns
442-
-------
443-
skip_vols_num : int
444-
number of volumes to skip
445-
446-
"""
447-
if dummy_scans is None:
448-
return algo_dummy_scans
449-
return dummy_scans

niworkflows/utils/misc.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,5 +241,27 @@ def clean_directory(path):
241241
return True
242242

243243

244+
def pass_dummy_scans(algo_dummy_scans, dummy_scans=None):
245+
"""
246+
Graft manually provided number of dummy scans, if necessary.
247+
248+
Parameters
249+
----------
250+
algo_dummy_scans : int
251+
number of volumes to skip determined by an algorithm
252+
dummy_scans : int or None
253+
number of volumes to skip determined by the user
254+
255+
Returns
256+
-------
257+
skip_vols_num : int
258+
number of volumes to skip
259+
260+
"""
261+
if dummy_scans is None:
262+
return algo_dummy_scans
263+
return dummy_scans
264+
265+
244266
if __name__ == '__main__':
245267
pass
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
"""Test misc module."""
2+
import pytest
3+
from ..misc import pass_dummy_scans
4+
5+
6+
@pytest.mark.parametrize('algo_dummy_scans,dummy_scans,expected_out', [
7+
(2, 1, 1),
8+
(2, None, 2),
9+
(2, 0, 0),
10+
])
11+
def test_pass_dummy_scans(algo_dummy_scans, dummy_scans, expected_out):
12+
"""Check dummy scans passing."""
13+
skip_vols = pass_dummy_scans(algo_dummy_scans, dummy_scans)
14+
15+
assert skip_vols == expected_out

0 commit comments

Comments
 (0)