Skip to content

Commit 2b0e88c

Browse files
committed
TST: Add a fixture to create random PET data for tests
Add a fixture to create random PET data for tests. Adopt the fixture across PET tests.
1 parent 5e21f83 commit 2b0e88c

File tree

5 files changed

+124
-77
lines changed

5 files changed

+124
-77
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ env = "PYTHONHASHSEED=0"
223223
markers = [
224224
"random_gtab_data: Custom marker for random gtab data tests",
225225
"random_dwi_data: Custom marker for random dwi data tests",
226+
"random_pet_data: Custom marker for random pet data tests",
226227
"random_uniform_ndim_data: Custom marker for random multi-dimensional data tests",
227228
"random_uniform_spatial_data: Custom marker for random spatial data tests",
228229
]

test/conftest.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,3 +297,31 @@ def setup_random_dwi_data(request, setup_random_gtab_data):
297297
gradients,
298298
b0_thres,
299299
)
300+
301+
302+
@pytest.fixture(autouse=True)
303+
def setup_random_pet_data(request):
304+
"""Automatically generate random PET data for tests."""
305+
marker = request.node.get_closest_marker("random_pet_data")
306+
307+
n_frames = 5
308+
vol_size = (4, 4, 4)
309+
midframe = np.arange(n_frames, dtype=np.float32) + 1
310+
total_duration = float(n_frames + 1)
311+
if marker:
312+
n_frames, vol_size, midframe, total_duration = marker.args
313+
314+
rng = request.node.rng
315+
316+
pet_dataobj, affine = _generate_random_uniform_spatial_data(
317+
request, (*vol_size, n_frames), 0.0, 1.0
318+
)
319+
brainmask_dataobj = rng.choice([True, False], size=vol_size).astype(np.uint8)
320+
321+
return (
322+
pet_dataobj,
323+
affine,
324+
brainmask_dataobj,
325+
midframe,
326+
total_duration,
327+
)

test/test_data_pet.py

Lines changed: 32 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,27 @@
3131
from nifreeze.data.pet import PET, from_nii
3232

3333

34+
@pytest.fixture
35+
def random_dataset(setup_random_pet_data) -> PET:
36+
"""Create a PET dataset with random data for testing."""
37+
38+
(
39+
pet_dataobj,
40+
affine,
41+
brainmask_dataobj,
42+
midframe,
43+
total_duration,
44+
) = setup_random_pet_data
45+
46+
return PET(
47+
dataobj=pet_dataobj,
48+
affine=affine,
49+
brainmask=brainmask_dataobj,
50+
midframe=midframe,
51+
total_duration=total_duration,
52+
)
53+
54+
3455
@pytest.mark.random_uniform_spatial_data((2, 2, 2, 2), 0.0, 1.0)
3556
def test_from_nii_requires_frame_time(setup_random_uniform_spatial_data, tmp_path):
3657
data, affine = setup_random_uniform_spatial_data
@@ -42,37 +63,22 @@ def test_from_nii_requires_frame_time(setup_random_uniform_spatial_data, tmp_pat
4263
from_nii(fname)
4364

4465

45-
def _create_dataset():
46-
rng = np.random.default_rng(12345)
47-
data = rng.random((4, 4, 4, 5), dtype=np.float32)
48-
affine = np.eye(4, dtype=np.float32)
49-
mask = np.ones((4, 4, 4), dtype=bool)
50-
midframe = np.array([10, 20, 30, 40, 50], dtype=np.float32)
51-
return PET(
52-
dataobj=data,
53-
affine=affine,
54-
brainmask=mask,
55-
midframe=midframe,
56-
total_duration=60.0,
57-
)
58-
59-
60-
def test_pet_set_transform_updates_motion_affines():
61-
dataset = _create_dataset()
66+
@pytest.mark.random_pet_data(5, (4, 4, 4), np.asarray([10.0, 20.0, 30.0, 40.0, 50.0]), 60.0)
67+
def test_pet_set_transform_updates_motion_affines(random_dataset):
6268
idx = 2
63-
data_before = np.copy(dataset.dataobj[..., idx])
69+
data_before = np.copy(random_dataset.dataobj[..., idx])
6470

6571
affine = np.eye(4)
66-
dataset.set_transform(idx, affine)
72+
random_dataset.set_transform(idx, affine)
6773

68-
np.testing.assert_allclose(dataset.dataobj[..., idx], data_before)
69-
assert dataset.motion_affines is not None
70-
assert len(dataset.motion_affines) == len(dataset)
71-
assert isinstance(dataset.motion_affines[idx], Affine)
72-
np.testing.assert_array_equal(dataset.motion_affines[idx].matrix, affine)
74+
np.testing.assert_allclose(random_dataset.dataobj[..., idx], data_before)
75+
assert random_dataset.motion_affines is not None
76+
assert len(random_dataset.motion_affines) == len(random_dataset)
77+
assert isinstance(random_dataset.motion_affines[idx], Affine)
78+
np.testing.assert_array_equal(random_dataset.motion_affines[idx].matrix, affine)
7379

74-
vol, aff, time = dataset[idx]
75-
assert aff is dataset.motion_affines[idx]
80+
vol, aff, time = random_dataset[idx]
81+
assert aff is random_dataset.motion_affines[idx]
7682

7783

7884
@pytest.mark.random_uniform_spatial_data((2, 2, 2, 2), 0.0, 1.0)

test/test_integration_pet.py

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -24,51 +24,57 @@
2424
import types
2525

2626
import numpy as np
27+
import pytest
2728

2829
from nifreeze.data.pet import PET
2930
from nifreeze.estimator import PETMotionEstimator
3031

3132

32-
def _pet_dataset(n_frames=3):
33-
rng = np.random.default_rng(42)
34-
data = rng.random((2, 2, 2, n_frames), dtype=np.float32)
35-
affine = np.eye(4, dtype=np.float32)
36-
mask = np.ones((2, 2, 2), dtype=bool)
37-
midframe = np.arange(n_frames, dtype=np.float32) + 1
33+
@pytest.fixture
34+
def random_dataset(setup_random_pet_data) -> PET:
35+
"""Create a PET dataset with random data for testing."""
36+
37+
(
38+
pet_dataobj,
39+
affine,
40+
brainmask_dataobj,
41+
midframe,
42+
total_duration,
43+
) = setup_random_pet_data
44+
3845
return PET(
39-
dataobj=data,
46+
dataobj=pet_dataobj,
4047
affine=affine,
41-
brainmask=mask,
48+
brainmask=brainmask_dataobj,
4249
midframe=midframe,
43-
total_duration=float(n_frames + 1),
50+
total_duration=total_duration,
4451
)
4552

4653

47-
def test_lofo_split_shapes(tmp_path):
48-
ds = _pet_dataset(4)
54+
@pytest.mark.random_pet_data(4, (2, 2, 2), np.asarray([1.0, 2.0, 3.0, 4.0]), 5.0)
55+
def test_lofo_split_shapes(random_dataset, tmp_path):
4956
idx = 2
50-
(train_data, train_times), (test_data, test_time) = ds.lofo_split(idx)
51-
assert train_data.shape[-1] == ds.dataobj.shape[-1] - 1
52-
np.testing.assert_array_equal(test_data, ds.dataobj[..., idx])
53-
np.testing.assert_array_equal(train_times, np.delete(ds.midframe, idx))
54-
assert test_time == ds.midframe[idx]
57+
(train_data, train_times), (test_data, test_time) = random_dataset.lofo_split(idx)
58+
assert train_data.shape[-1] == random_dataset.dataobj.shape[-1] - 1
59+
np.testing.assert_array_equal(test_data, random_dataset.dataobj[..., idx])
60+
np.testing.assert_array_equal(train_times, np.delete(random_dataset.midframe, idx))
61+
assert test_time == random_dataset.midframe[idx]
5562

5663

57-
def test_to_from_filename_roundtrip(tmp_path):
58-
ds = _pet_dataset(3)
64+
@pytest.mark.random_pet_data(3, (2, 2, 2), np.asarray([1.0, 2.0, 3.0]), 4.0)
65+
def test_to_from_filename_roundtrip(random_dataset, tmp_path):
5966
out_file = tmp_path / "petdata"
60-
ds.to_filename(out_file)
67+
random_dataset.to_filename(out_file)
6168
assert (tmp_path / "petdata.h5").exists()
6269
loaded = PET.from_filename(tmp_path / "petdata.h5")
63-
np.testing.assert_allclose(loaded.dataobj, ds.dataobj)
64-
np.testing.assert_allclose(loaded.affine, ds.affine)
65-
np.testing.assert_allclose(loaded.midframe, ds.midframe)
66-
assert loaded.total_duration == ds.total_duration
67-
70+
np.testing.assert_allclose(loaded.dataobj, random_dataset.dataobj)
71+
np.testing.assert_allclose(loaded.affine, random_dataset.affine)
72+
np.testing.assert_allclose(loaded.midframe, random_dataset.midframe)
73+
assert loaded.total_duration == random_dataset.total_duration
6874

69-
def test_pet_motion_estimator_run(monkeypatch):
70-
ds = _pet_dataset(3)
7175

76+
@pytest.mark.random_pet_data(5, (4, 4, 4), np.asarray([10.0, 20.0, 30.0, 40.0, 50.0]), 60.0)
77+
def test_pet_motion_estimator_run(random_dataset, monkeypatch):
7278
class DummyModel:
7379
def __init__(self, dataset, timepoints, xlim):
7480
self.dataset = dataset
@@ -90,7 +96,7 @@ def run(self, cwd=None):
9096
monkeypatch.setattr("nifreeze.estimator.Registration", DummyRegistration)
9197

9298
estimator = PETMotionEstimator(None)
93-
affines = estimator.run(ds)
94-
assert len(affines) == len(ds)
99+
affines = estimator.run(random_dataset)
100+
assert len(affines) == len(random_dataset)
95101
for mat in affines:
96102
np.testing.assert_array_equal(mat, np.eye(4))

test/test_model_pet.py

Lines changed: 29 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -28,27 +28,33 @@
2828
from nifreeze.model.pet import PETModel
2929

3030

31-
def _create_dataset():
32-
rng = np.random.default_rng(12345)
33-
data = rng.random((4, 4, 4, 5), dtype=np.float32)
34-
affine = np.eye(4, dtype=np.float32)
35-
mask = np.ones((4, 4, 4), dtype=bool)
36-
midframe = np.array([10, 20, 30, 40, 50], dtype=np.float32)
31+
@pytest.fixture
32+
def random_dataset(setup_random_pet_data) -> PET:
33+
"""Create a PET dataset with random data for testing."""
34+
35+
(
36+
pet_dataobj,
37+
affine,
38+
brainmask_dataobj,
39+
midframe,
40+
total_duration,
41+
) = setup_random_pet_data
42+
3743
return PET(
38-
dataobj=data,
44+
dataobj=pet_dataobj,
3945
affine=affine,
40-
brainmask=mask,
46+
brainmask=brainmask_dataobj,
4147
midframe=midframe,
42-
total_duration=60.0,
48+
total_duration=total_duration,
4349
)
4450

4551

46-
def test_petmodel_fit_predict():
47-
dataset = _create_dataset()
52+
@pytest.mark.random_pet_data(5, (4, 4, 4), np.asarray([10.0, 20.0, 30.0, 40.0, 50.0]), 60.0)
53+
def test_petmodel_fit_predict(random_dataset):
4854
model = PETModel(
49-
dataset=dataset,
50-
timepoints=dataset.midframe,
51-
xlim=dataset.total_duration,
55+
dataset=random_dataset,
56+
timepoints=random_dataset.midframe,
57+
xlim=random_dataset.total_duration,
5258
smooth_fwhm=0,
5359
thresh_pct=0,
5460
)
@@ -58,19 +64,19 @@ def test_petmodel_fit_predict():
5864
assert model.is_fitted
5965

6066
# Predict at a specific timepoint
61-
vol = model.fit_predict(dataset.midframe[2])
62-
assert vol.shape == dataset.shape3d
63-
assert vol.dtype == dataset.dataobj.dtype
67+
vol = model.fit_predict(random_dataset.midframe[2])
68+
assert vol.shape == random_dataset.shape3d
69+
assert vol.dtype == random_dataset.dataobj.dtype
6470

6571

66-
def test_petmodel_invalid_init():
67-
dataset = _create_dataset()
72+
@pytest.mark.random_pet_data(5, (4, 4, 4), np.asarray([10.0, 20.0, 30.0, 40.0, 50.0]), 60.0)
73+
def test_petmodel_invalid_init(random_dataset):
6874
with pytest.raises(TypeError):
69-
PETModel(dataset=dataset)
75+
PETModel(dataset=random_dataset)
7076

7177

72-
def test_petmodel_time_check():
73-
dataset = _create_dataset()
78+
@pytest.mark.random_pet_data(5, (4, 4, 4), np.asarray([10.0, 20.0, 30.0, 40.0, 50.0]), 60.0)
79+
def test_petmodel_time_check(random_dataset):
7480
bad_times = np.array([0, 10, 20, 30, 50], dtype=np.float32)
7581
with pytest.raises(ValueError):
76-
PETModel(dataset=dataset, timepoints=bad_times, xlim=60.0)
82+
PETModel(dataset=random_dataset, timepoints=bad_times, xlim=60.0)

0 commit comments

Comments
 (0)