1
1
from copy import deepcopy
2
+ from pathlib import Path
3
+ from unittest .mock import patch
2
4
3
5
import bids
6
+ import nibabel as nb
7
+ import numpy as np
8
+ import pytest
9
+ from nipype .pipeline .engine .utils import generate_expanded_graph
4
10
from niworkflows .utils .testing import generate_bids_skeleton
5
11
from sdcflows .fieldmaps import clear_registry
6
12
from sdcflows .utils .wrangler import find_estimators
7
13
8
- from ..base import get_estimator
14
+ from ... import config
15
+ from ..base import get_estimator , init_fmriprep_wf
16
+ from ..tests import mock_config
9
17
10
18
BASE_LAYOUT = {
11
19
"01" : {
19
27
{
20
28
"task" : "rest" ,
21
29
"run" : i ,
22
- "suffix" : "bold" ,
30
+ "suffix" : suffix ,
23
31
"metadata" : {
24
32
"RepetitionTime" : 2.0 ,
25
33
"PhaseEncodingDirection" : "j" ,
28
36
"SliceTiming" : [0.0 , 0.2 , 0.4 , 0.6 , 0.8 , 1.0 , 1.2 , 1.4 , 1.6 , 1.8 ],
29
37
},
30
38
}
39
+ for suffix in ("bold" , "sbref" )
31
40
for i in range (1 , 3 )
32
41
),
33
42
* (
64
73
}
65
74
66
75
76
+ @pytest .fixture (scope = "module" , autouse = True )
77
+ def _quiet_logger ():
78
+ import logging
79
+
80
+ logger = logging .getLogger ("nipype.workflow" )
81
+ old_level = logger .getEffectiveLevel ()
82
+ logger .setLevel (logging .ERROR )
83
+ yield
84
+ logger .setLevel (old_level )
85
+
86
+
87
+ @pytest .fixture (autouse = True )
88
+ def _reset_sdcflows_registry ():
89
+ yield
90
+ clear_registry ()
91
+
92
+
93
+ @pytest .fixture (scope = "module" )
94
+ def bids_root (tmp_path_factory ):
95
+ base = tmp_path_factory .mktemp ("base" )
96
+ bids_dir = base / "bids"
97
+ generate_bids_skeleton (bids_dir , BASE_LAYOUT )
98
+
99
+ img = nb .Nifti1Image (np .zeros ((10 , 10 , 10 , 10 )), np .eye (4 ))
100
+
101
+ for bold_path in bids_dir .glob ('sub-01/*/*.nii.gz' ):
102
+ img .to_filename (bold_path )
103
+
104
+ yield bids_dir
105
+
106
+
107
+ def _make_params (
108
+ bold2t1w_init : str = "register" ,
109
+ use_bbr : bool | None = None ,
110
+ dummy_scans : int | None = None ,
111
+ me_output_echos : bool = False ,
112
+ medial_surface_nan : bool = False ,
113
+ project_goodvoxels : bool = False ,
114
+ cifti_output : bool | str = False ,
115
+ run_msmsulc : bool = True ,
116
+ skull_strip_t1w : str = "auto" ,
117
+ use_syn_sdc : str | bool = False ,
118
+ force_syn : bool = False ,
119
+ freesurfer : bool = True ,
120
+ ignore : list [str ] = None ,
121
+ bids_filters : dict = None ,
122
+ ):
123
+ if ignore is None :
124
+ ignore = []
125
+ if bids_filters is None :
126
+ bids_filters = {}
127
+ return (
128
+ bold2t1w_init ,
129
+ use_bbr ,
130
+ dummy_scans ,
131
+ me_output_echos ,
132
+ medial_surface_nan ,
133
+ project_goodvoxels ,
134
+ cifti_output ,
135
+ run_msmsulc ,
136
+ skull_strip_t1w ,
137
+ use_syn_sdc ,
138
+ force_syn ,
139
+ freesurfer ,
140
+ ignore ,
141
+ bids_filters ,
142
+ )
143
+
144
+
145
+ @pytest .mark .parametrize ("level" , ["minimal" , "resampling" , "full" ])
146
+ @pytest .mark .parametrize ("anat_only" , [False , True ])
147
+ @pytest .mark .parametrize (
148
+ (
149
+ "bold2t1w_init" ,
150
+ "use_bbr" ,
151
+ "dummy_scans" ,
152
+ "me_output_echos" ,
153
+ "medial_surface_nan" ,
154
+ "project_goodvoxels" ,
155
+ "cifti_output" ,
156
+ "run_msmsulc" ,
157
+ "skull_strip_t1w" ,
158
+ "use_syn_sdc" ,
159
+ "force_syn" ,
160
+ "freesurfer" ,
161
+ "ignore" ,
162
+ "bids_filters" ,
163
+ ),
164
+ [
165
+ _make_params (),
166
+ _make_params (bold2t1w_init = "header" ),
167
+ _make_params (use_bbr = True ),
168
+ _make_params (use_bbr = False ),
169
+ _make_params (bold2t1w_init = "header" , use_bbr = True ),
170
+ # Currently disabled
171
+ # _make_params(bold2t1w_init="header", use_bbr=False),
172
+ _make_params (dummy_scans = 2 ),
173
+ _make_params (me_output_echos = True ),
174
+ _make_params (medial_surface_nan = True ),
175
+ _make_params (cifti_output = '91k' ),
176
+ _make_params (cifti_output = '91k' , project_goodvoxels = True ),
177
+ _make_params (cifti_output = '91k' , project_goodvoxels = True , run_msmsulc = False ),
178
+ _make_params (cifti_output = '91k' , run_msmsulc = False ),
179
+ _make_params (skull_strip_t1w = 'force' ),
180
+ _make_params (skull_strip_t1w = 'skip' ),
181
+ _make_params (use_syn_sdc = 'warn' , force_syn = True , ignore = ['fieldmaps' ]),
182
+ _make_params (freesurfer = False ),
183
+ _make_params (freesurfer = False , use_bbr = True ),
184
+ _make_params (freesurfer = False , use_bbr = False ),
185
+ # Currently unsupported:
186
+ # _make_params(freesurfer=False, bold2t1w_init="header"),
187
+ # _make_params(freesurfer=False, bold2t1w_init="header", use_bbr=True),
188
+ # _make_params(freesurfer=False, bold2t1w_init="header", use_bbr=False),
189
+ # Regression test for gh-3154:
190
+ _make_params (bids_filters = {'sbref' : {'suffix' : 'sbref' }}),
191
+ ],
192
+ )
193
+ def test_init_fmriprep_wf (
194
+ bids_root : Path ,
195
+ tmp_path : Path ,
196
+ level : str ,
197
+ anat_only : bool ,
198
+ bold2t1w_init : str ,
199
+ use_bbr : bool | None ,
200
+ dummy_scans : int | None ,
201
+ me_output_echos : bool ,
202
+ medial_surface_nan : bool ,
203
+ project_goodvoxels : bool ,
204
+ cifti_output : bool | str ,
205
+ run_msmsulc : bool ,
206
+ skull_strip_t1w : str ,
207
+ use_syn_sdc : str | bool ,
208
+ force_syn : bool ,
209
+ freesurfer : bool ,
210
+ ignore : list [str ],
211
+ bids_filters : dict ,
212
+ ):
213
+ with mock_config (bids_dir = bids_root ):
214
+ config .workflow .level = level
215
+ config .workflow .anat_only = anat_only
216
+ config .workflow .bold2t1w_init = bold2t1w_init
217
+ config .workflow .use_bbr = use_bbr
218
+ config .workflow .dummy_scans = dummy_scans
219
+ config .execution .me_output_echos = me_output_echos
220
+ config .workflow .medial_surface_nan = medial_surface_nan
221
+ config .workflow .project_goodvoxels = project_goodvoxels
222
+ config .workflow .run_msmsulc = run_msmsulc
223
+ config .workflow .skull_strip_t1w = skull_strip_t1w
224
+ config .workflow .cifti_output = cifti_output
225
+ config .workflow .run_reconall = freesurfer
226
+ config .workflow .ignore = ignore
227
+ with patch .dict ('fmriprep.config.execution.bids_filters' , bids_filters ):
228
+ wf = init_fmriprep_wf ()
229
+
230
+ generate_expanded_graph (wf ._create_flat_graph ())
231
+
232
+
67
233
def test_get_estimator_none (tmp_path ):
68
234
bids_dir = tmp_path / "bids"
69
235
@@ -100,7 +266,6 @@ def test_get_estimator_b0field_and_intendedfor(tmp_path):
100
266
101
267
assert get_estimator (layout , bold_files [0 ]) == ('epi' ,)
102
268
assert get_estimator (layout , bold_files [1 ]) == ('auto_00000' ,)
103
- clear_registry ()
104
269
105
270
106
271
def test_get_estimator_overlapping_specs (tmp_path ):
@@ -130,7 +295,6 @@ def test_get_estimator_overlapping_specs(tmp_path):
130
295
# B0Fields take precedence
131
296
assert get_estimator (layout , bold_files [0 ]) == ('epi' ,)
132
297
assert get_estimator (layout , bold_files [1 ]) == ('epi' ,)
133
- clear_registry ()
134
298
135
299
136
300
def test_get_estimator_multiple_b0fields (tmp_path ):
@@ -156,4 +320,3 @@ def test_get_estimator_multiple_b0fields(tmp_path):
156
320
# Always get an iterable; don't care if it's a list or tuple
157
321
assert get_estimator (layout , bold_files [0 ]) == ['epi' , 'phasediff' ]
158
322
assert get_estimator (layout , bold_files [1 ]) == ('epi' ,)
159
- clear_registry ()
0 commit comments