VibecoderMcSwaggins's picture
feat: Phase 1A + Phase 2 - Local data loader and DeepISLES Docker wrapper (#3)
aef1f5a unverified
"""Tests for the data adapter."""
from __future__ import annotations
from typing import TYPE_CHECKING
from stroke_deepisles_demo.data.adapter import (
LocalDataset,
build_local_dataset,
parse_subject_id,
)
if TYPE_CHECKING:
from pathlib import Path
def test_parse_subject_id_extracts_correctly() -> None:
"""Test extracting subject ID from BIDS filename."""
# Valid cases
assert parse_subject_id("sub-stroke0005_ses-02_dwi.nii.gz") == "sub-stroke0005"
assert parse_subject_id("sub-stroke0149_ses-02_adc.nii.gz") == "sub-stroke0149"
assert parse_subject_id("sub-stroke1234_ses-02_lesion-msk.nii.gz") == "sub-stroke1234"
# Invalid cases
assert parse_subject_id("random_file.nii.gz") is None
assert parse_subject_id("sub-strokeABC_ses-02_dwi.nii.gz") is None # Non-digit ID
def test_build_local_dataset_matches_files(synthetic_isles_dir: Path) -> None:
"""Test that files are correctly matched by subject ID."""
dataset = build_local_dataset(synthetic_isles_dir)
assert isinstance(dataset, LocalDataset)
assert len(dataset) == 2 # synthetic_isles_dir creates 2 subjects
assert dataset.list_case_ids() == ["sub-stroke0001", "sub-stroke0002"]
# Verify matching logic
case1 = dataset.get_case("sub-stroke0001")
assert case1["dwi"].name == "sub-stroke0001_ses-02_dwi.nii.gz"
assert case1["adc"].name == "sub-stroke0001_ses-02_adc.nii.gz"
assert case1["ground_truth"] is not None
assert case1["ground_truth"].name == "sub-stroke0001_ses-02_lesion-msk.nii.gz"
def test_get_case_returns_case_files(synthetic_isles_dir: Path) -> None:
"""Test retrieval of cases by ID and index."""
dataset = build_local_dataset(synthetic_isles_dir)
# By ID
case_by_id = dataset.get_case("sub-stroke0001")
assert isinstance(case_by_id, dict)
assert "dwi" in case_by_id
assert "adc" in case_by_id
# By Index
case_by_idx = dataset.get_case(0)
assert isinstance(case_by_idx, dict)
assert case_by_id == case_by_idx # Should be the same case
def test_build_local_dataset_skips_incomplete(
synthetic_isles_dir: Path,
) -> None:
"""Test that incomplete cases (missing ADC) are skipped."""
# Delete ADC for subject 2
adc_file = synthetic_isles_dir / "Images-ADC" / "sub-stroke0002_ses-02_adc.nii.gz"
adc_file.unlink()
dataset = build_local_dataset(synthetic_isles_dir)
# Subject 2 should be gone
assert len(dataset) == 1
assert dataset.list_case_ids() == ["sub-stroke0001"]
def test_build_local_dataset_handles_missing_mask(
synthetic_isles_dir: Path,
) -> None:
"""Test that missing mask results in ground_truth=None (if allowed)."""
# NOTE: Adapter currently allows missing mask?
# Spec says: "ground_truth=mask_file if mask_file.exists() else None"
# So yes, it should load but with None.
# Delete Mask for subject 2
mask_file = synthetic_isles_dir / "Masks" / "sub-stroke0002_ses-02_lesion-msk.nii.gz"
mask_file.unlink()
dataset = build_local_dataset(synthetic_isles_dir)
# Subject 2 should still exist
assert len(dataset) == 2
case2 = dataset.get_case("sub-stroke0002")
assert case2.get("ground_truth") is None