|
| 1 | +"""Tests for ASL/M0 renaming behavior. |
| 2 | +
|
| 3 | +Ensures that when ASL scans are renamed with variant acquisition labels: |
| 4 | +- aslcontext files are renamed to match the ASL scan |
| 5 | +- M0 files (nii/json) are NOT renamed |
| 6 | +- M0 JSON IntendedFor entries are updated to point to the new ASL path |
| 7 | +""" |
| 8 | + |
| 9 | +import json |
| 10 | +from pathlib import Path |
| 11 | + |
| 12 | +from cubids.cubids import CuBIDS |
| 13 | + |
| 14 | + |
| 15 | +def _write(path: Path, content: str = ""): |
| 16 | + path.parent.mkdir(parents=True, exist_ok=True) |
| 17 | + path.write_text(content) |
| 18 | + |
| 19 | + |
| 20 | +def test_m0_not_renamed_but_aslcontext_is_and_intendedfor_updated(tmp_path): |
| 21 | + bids_root = tmp_path / "bids" |
| 22 | + sub = "sub-01" |
| 23 | + ses = "ses-01" |
| 24 | + |
| 25 | + # Create minimal perf files |
| 26 | + perf_dir = bids_root / sub / ses / "perf" |
| 27 | + perf_dir.mkdir(parents=True, exist_ok=True) |
| 28 | + |
| 29 | + asl_base = perf_dir / f"{sub}_{ses}_asl.nii.gz" |
| 30 | + asl_json = perf_dir / f"{sub}_{ses}_asl.json" |
| 31 | + m0_base = perf_dir / f"{sub}_{ses}_m0scan.nii.gz" |
| 32 | + m0_json = perf_dir / f"{sub}_{ses}_m0scan.json" |
| 33 | + aslcontext = perf_dir / f"{sub}_{ses}_aslcontext.tsv" |
| 34 | + |
| 35 | + # Touch NIfTIs (empty is fine for this test) and sidecars |
| 36 | + asl_base.write_bytes(b"") |
| 37 | + m0_base.write_bytes(b"") |
| 38 | + |
| 39 | + _write(asl_json, json.dumps({})) |
| 40 | + |
| 41 | + # M0 IntendedFor should reference the ASL time series (participant-relative path) |
| 42 | + intended_for_rel = f"{ses}/perf/{sub}_{ses}_asl.nii.gz" |
| 43 | + _write(m0_json, json.dumps({"IntendedFor": [intended_for_rel]})) |
| 44 | + |
| 45 | + _write(aslcontext, "label\ncontrol\nlabel\ncontrol\n") |
| 46 | + |
| 47 | + c = CuBIDS(str(bids_root)) |
| 48 | + |
| 49 | + # Rename the ASL scan by adding a variant acquisition |
| 50 | + entities = {"suffix": "asl", "acquisition": "VARIANTTest"} |
| 51 | + c.change_filename(str(asl_base), entities) |
| 52 | + |
| 53 | + # Old/new filenames prepared for ASL and aslcontext, but NOT for M0 |
| 54 | + assert str(asl_base) in c.old_filenames |
| 55 | + assert any(fn.endswith("_asl.json") for fn in c.old_filenames) |
| 56 | + assert any(fn.endswith("_aslcontext.tsv") for fn in c.old_filenames) |
| 57 | + |
| 58 | + assert not any(fn.endswith("_m0scan.nii.gz") for fn in c.old_filenames) |
| 59 | + assert not any(fn.endswith("_m0scan.json") for fn in c.old_filenames) |
| 60 | + |
| 61 | + # Compute expected new ASL path and aslcontext path |
| 62 | + expected_new_asl = perf_dir / f"{sub}_{ses}_acq-VARIANTTest_asl.nii.gz" |
| 63 | + expected_new_aslcontext = perf_dir / f"{sub}_{ses}_acq-VARIANTTest_aslcontext.tsv" |
| 64 | + |
| 65 | + assert str(expected_new_asl) in c.new_filenames |
| 66 | + assert str(expected_new_aslcontext) in c.new_filenames |
| 67 | + |
| 68 | + # M0 files remain with original names |
| 69 | + assert m0_base.exists() |
| 70 | + assert m0_json.exists() |
| 71 | + |
| 72 | + # But M0 IntendedFor should now point to the new ASL relative path |
| 73 | + with open(m0_json, "r") as f: |
| 74 | + m0_meta = json.load(f) |
| 75 | + |
| 76 | + new_rel = f"{ses}/perf/{sub}_{ses}_acq-VARIANTTest_asl.nii.gz" |
| 77 | + assert "IntendedFor" in m0_meta |
| 78 | + assert new_rel in m0_meta["IntendedFor"] |
| 79 | + # Ensure old reference removed |
| 80 | + assert intended_for_rel not in m0_meta["IntendedFor"] |
| 81 | + |
0 commit comments