Skip to content

Commit 66b3c30

Browse files
authored
Merge pull request #159 from oesteban/enh/increase-coverage
ENH: Increase unit tests coverage of ``sdcflows.fieldmaps``
2 parents 39728a8 + 9551112 commit 66b3c30

File tree

4 files changed

+111
-14
lines changed

4 files changed

+111
-14
lines changed

sdcflows/fieldmaps.py

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,8 +50,7 @@ def _type_setter(obj, attribute, value):
5050
"""Make sure the type of estimation is not changed."""
5151
if obj.method == value:
5252
return value
53-
54-
if obj.method != EstimatorType.UNKNOWN and obj.method != value:
53+
elif obj.method != EstimatorType.UNKNOWN:
5554
raise TypeError(f"Cannot change determined method {obj.method} to {value}.")
5655

5756
if value not in (
@@ -99,6 +98,13 @@ class FieldmapFile:
9998
>>> f.metadata['TotalReadoutTime']
10099
0.005
101100
101+
>>> f = FieldmapFile(
102+
... dsA_dir / "sub-01" / "fmap" / "sub-01_dir-LR_epi.nii.gz",
103+
... metadata={"TotalReadoutTime": None},
104+
... ) # doctest: +IGNORE_EXCEPTION_DETAIL
105+
Traceback (most recent call last):
106+
MetadataError:
107+
102108
>>> f = FieldmapFile(
103109
... dsA_dir / "sub-01" / "fmap" / "sub-01_dir-LR_epi.nii.gz",
104110
... metadata={'TotalReadoutTime': 0.006}
@@ -171,17 +177,12 @@ class FieldmapFile:
171177
@path.validator
172178
def check_path(self, attribute, value):
173179
"""Validate a fieldmap path."""
174-
if isinstance(value, BIDSFile):
175-
value = Path(value.path)
176-
elif isinstance(value, str):
177-
value = Path(value)
178-
179180
if not value.is_file():
180181
raise FileNotFoundError(
181182
f"File path <{value}> does not exist, is a broken link, or it is not a file"
182183
)
183184

184-
if not str(value).endswith((".nii", ".nii.gz")):
185+
if not value.name.endswith((".nii", ".nii.gz")):
185186
raise ValueError(f"File path <{value}> does not look like a NIfTI file.")
186187

187188
suffix = re.search(r"(?<=_)\w+(?=\.nii)", value.name).group()
@@ -208,8 +209,11 @@ def __attrs_post_init__(self):
208209

209210
# Attempt to infer a bids_root folder
210211
relative_path = relative_to_root(self.path)
211-
if str(relative_path) != str(self.path):
212-
self.bids_root = Path(str(self.path)[: -len(str(relative_path))])
212+
self.bids_root = (
213+
Path(str(self.path)[: -len(str(relative_path))])
214+
if str(relative_path) != str(self.path)
215+
else None
216+
)
213217

214218
# Check for REQUIRED metadata (depends on suffix.)
215219
if self.suffix in ("bold", "dwi", "epi", "sbref"):
@@ -222,10 +226,10 @@ def __attrs_post_init__(self):
222226

223227
try:
224228
get_trt(self.metadata, in_file=self.path)
225-
except ValueError:
229+
except ValueError as exc:
226230
raise MetadataError(
227231
f"Missing readout timing information for <{self.path}>."
228-
)
232+
) from exc
229233

230234
elif self.suffix == "fieldmap" and "Units" not in self.metadata:
231235
raise MetadataError(f"Missing 'Units' for <{self.path}>.")

sdcflows/tests/data/dsA/sub-01/anat/sub-01_FLAIR.nii.gz

Whitespace-only changes.

sdcflows/tests/test_fieldmaps.py

Lines changed: 90 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
"""test_fieldmaps."""
2+
from collections import namedtuple
3+
import shutil
24
import pytest
5+
import bids
36
from .. import fieldmaps as fm
47

58

69
def test_FieldmapFile(dsA_dir):
710
"""Test one existing file."""
8-
fm.FieldmapFile(dsA_dir / "sub-01" / "anat" / "sub-01_T1w.nii.gz")
11+
f1 = fm.FieldmapFile(dsA_dir / "sub-01" / "anat" / "sub-01_T1w.nii.gz")
12+
f2 = fm.FieldmapFile(str(dsA_dir / "sub-01" / "anat" / "sub-01_T1w.nii.gz"))
13+
f3 = fm.FieldmapFile(
14+
bids.layout.BIDSFile(str(dsA_dir / "sub-01" / "anat" / "sub-01_T1w.nii.gz"))
15+
)
16+
assert f1 == f2 == f3
17+
18+
with pytest.raises(ValueError):
19+
fm.FieldmapFile(dsA_dir / "sub-01" / "fmap" / "sub-01_dir-AP_epi.json")
20+
21+
with pytest.raises(ValueError):
22+
fm.FieldmapFile(dsA_dir / "sub-01" / "anat" / "sub-01_FLAIR.nii.gz")
923

1024

1125
@pytest.mark.parametrize(
@@ -203,3 +217,78 @@ def test_FieldmapEstimationIdentifier(monkeypatch, dsA_dir):
203217
fm.get_identifier("file", by="invalid")
204218

205219
fm.clear_registry()
220+
221+
222+
def test_type_setter():
223+
"""Cover the _type_setter routine."""
224+
obj = namedtuple("FieldmapEstimation", ("method",))(method=fm.EstimatorType.UNKNOWN)
225+
with pytest.raises(ValueError):
226+
fm._type_setter(obj, "method", 10)
227+
228+
obj = namedtuple("FieldmapEstimation", ("method",))(method=fm.EstimatorType.PEPOLAR)
229+
assert (
230+
fm._type_setter(obj, "method", fm.EstimatorType.PEPOLAR)
231+
== fm.EstimatorType.PEPOLAR
232+
)
233+
234+
235+
def test_FieldmapEstimation_missing_files(tmpdir, dsA_dir):
236+
"""Exercise some FieldmapEstimation checks."""
237+
tmpdir.chdir()
238+
tmpdir.mkdir("data")
239+
240+
# fieldmap - no magnitude
241+
path = dsA_dir / "sub-01" / "fmap" / "sub-01_fieldmap.nii.gz"
242+
shutil.copy(path, f"data/{path.name}")
243+
244+
with pytest.raises(
245+
ValueError,
246+
match=r"A fieldmap or phase-difference .* \(magnitude file\) is missing.*",
247+
):
248+
fm.FieldmapEstimation(
249+
[fm.FieldmapFile("data/sub-01_fieldmap.nii.gz", metadata={"Units": "Hz"})]
250+
)
251+
252+
# phase1/2 - no magnitude2
253+
path = dsA_dir / "sub-01" / "fmap" / "sub-01_phase1.nii.gz"
254+
shutil.copy(path, f"data/{path.name}")
255+
256+
path = dsA_dir / "sub-01" / "fmap" / "sub-01_magnitude1.nii.gz"
257+
shutil.copy(path, f"data/{path.name}")
258+
259+
path = dsA_dir / "sub-01" / "fmap" / "sub-01_phase2.nii.gz"
260+
shutil.copy(path, f"data/{path.name}")
261+
262+
with pytest.raises(
263+
ValueError, match=r".* \(phase1/2\) .* \(magnitude2 file\) is missing.*"
264+
):
265+
fm.FieldmapEstimation(
266+
[
267+
fm.FieldmapFile(
268+
"data/sub-01_phase1.nii.gz", metadata={"EchoTime": 0.004}
269+
),
270+
fm.FieldmapFile(
271+
"data/sub-01_phase2.nii.gz", metadata={"EchoTime": 0.007}
272+
),
273+
]
274+
)
275+
276+
# pepolar - only one PE
277+
path = dsA_dir / "sub-01" / "fmap" / "sub-01_dir-AP_epi.nii.gz"
278+
shutil.copy(path, f"data/{path.name}")
279+
280+
path = dsA_dir / "sub-01" / "dwi" / "sub-01_dir-AP_dwi.nii.gz"
281+
shutil.copy(path, f"data/{path.name}")
282+
with pytest.raises(ValueError, match="Only one phase-encoding direction <j>.*"):
283+
fm.FieldmapEstimation(
284+
[
285+
fm.FieldmapFile(
286+
"data/sub-01_dir-AP_epi.nii.gz",
287+
metadata={"PhaseEncodingDirection": "j", "TotalReadoutTime": 0.004},
288+
),
289+
fm.FieldmapFile(
290+
"data/sub-01_dir-AP_dwi.nii.gz",
291+
metadata={"PhaseEncodingDirection": "j", "TotalReadoutTime": 0.004},
292+
),
293+
]
294+
)

sdcflows/utils/epimanip.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,11 @@ def get_trt(in_meta, in_file=None):
161161

162162
# Use case 1: TRT is defined
163163
if "TotalReadoutTime" in in_meta:
164-
return in_meta.get("TotalReadoutTime")
164+
trt = in_meta.get("TotalReadoutTime")
165+
if not trt:
166+
raise ValueError(f"'{trt}'")
167+
168+
return trt
165169

166170
# npe = N voxels PE direction
167171
pe_index = "ijk".index(in_meta["PhaseEncodingDirection"][0])

0 commit comments

Comments
 (0)