Skip to content

Commit

Permalink
[INFRA] Run anat non regression tests with GitHub actions (#987)
Browse files Browse the repository at this point in the history
* refactor anat pipelines tests

* update jenkins files

* Add a ghaction workflow (runs on PR for now but will make it a cron)

* increase timeout and make cron
  • Loading branch information
NicolasGensollen authored Oct 9, 2023
1 parent 15511a9 commit 54876f1
Show file tree
Hide file tree
Showing 7 changed files with 640 additions and 646 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/test_pipelines_anat.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
name: Anat Pipelines Tests

on:
schedule:
- cron: 0 20 * * 4 # every thursday at 8pm

permissions:
contents: read

jobs:
test-pipelines-anat-MacOS:
runs-on:
- self-hosted
- macOS
timeout-minutes: 720
steps:
- uses: actions/checkout@v4
- uses: snok/install-poetry@v1
- name: Run tests for anat pipelines
run: |
make env.conda
source ~/miniconda3/etc/profile.d/conda.sh
conda activate "${{ github.workspace }}"/env
source "$(brew --prefix)/opt/modules/init/bash"
module load clinica.all
make install
cd test
poetry run pytest --verbose \
--working_directory=/Volumes/data/working_dir_mac \
--input_data_directory=/Volumes/data_ci \
--basetemp=/Volumes/data/tmp \
--junitxml=./test-reports/non_regression_anat_mac.xml \
--disable-warnings \
./nonregression/pipelines/anat
test-pipelines-anat-Linux:
runs-on:
- self-hosted
- Linux
timeout-minutes: 720
steps:
- uses: actions/checkout@v4
- uses: snok/install-poetry@v1
- name: Run tests for anat pipelines
run: |
make env.conda
source /builds/miniconda/etc/profile.d/conda.sh
conda activate "${{ github.workspace }}"/env
source /usr/local/Modules/init/profile.sh
module load clinica.all
make install
cd test
poetry run pytest --verbose \
--working_directory=/mnt/data/ci/working_dir_linux \
--input_data_directory=/mnt/data_ci \
--basetemp=/mnt/data/ci/tmp \
--junitxml=./test-reports/non_regression_anat_linux.xml \
--disable-warnings \
./nonregression/pipelines/anat
4 changes: 2 additions & 2 deletions .jenkins/nonregression_fast.Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ pipeline {
--disable-warnings \
--timeout=0 \
-m "not slow" \
./nonregression/pipelines/test_run_pipelines_anat.py
./nonregression/pipelines/anat
'''
}
post {
Expand Down Expand Up @@ -415,7 +415,7 @@ pipeline {
--timeout=0 \
-n 4 \
-m "not slow" \
./nonregression/pipelines/test_run_pipelines_anat.py
./nonregression/pipelines/anat
'''
}
post {
Expand Down
4 changes: 2 additions & 2 deletions .jenkins/nonregression_slow.Jenkinsfile
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pipeline {
--timeout=0 \
-n 4 \
-m "slow" \
./nonregression/pipelines/test_run_pipelines_anat.py
./nonregression/pipelines/anat
'''
}
}
Expand Down Expand Up @@ -424,7 +424,7 @@ pipeline {
--timeout=0 \
-n 4 \
-m "slow" \
./nonregression/pipelines/test_run_pipelines_anat.py
./nonregression/pipelines/anat
'''
}
post {
Expand Down
191 changes: 191 additions & 0 deletions test/nonregression/pipelines/anat/test_t1_freesurfer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import shutil
from os import fspath
from pathlib import Path
from test.nonregression.testing_tools import compare_folders, configure_paths

import pytest


@pytest.mark.slow
def test_t1_freesurfer_cross_sectional(cmdopt, tmp_path):
base_dir = Path(cmdopt["input"])
working_dir = Path(cmdopt["wd"])
input_dir, tmp_dir, ref_dir = configure_paths(base_dir, tmp_path, "T1FreeSurfer")
run_t1_freesurfer_cross_sectional(input_dir, tmp_dir, ref_dir, working_dir)


def run_t1_freesurfer_cross_sectional(
input_dir: Path, output_dir: Path, ref_dir: Path, working_dir: Path
) -> None:
"""Run the T1FreeSurfer pipeline on test data.
Notes
-----
Data for this functional test comes from https://openneuro.org/datasets/ds000204
We only check that folders are the same meaning that FreeSurfer finished without error.
surf/ folder is ignored because it contains symlinks that makes hard to check with ref data
(symlinks of ref data are ignored after rsync on CI machines).
"""
from clinica.pipelines.t1_freesurfer.t1_freesurfer_pipeline import T1FreeSurfer

parameters = {"recon_all_args": "-qcache", "skip_question": False}

pipeline = T1FreeSurfer(
bids_directory=fspath(input_dir / "bids"),
caps_directory=fspath(output_dir / "caps"),
tsv_file=fspath(input_dir / "subjects.tsv"),
parameters=parameters,
base_dir=fspath(working_dir),
)
pipeline.run(bypass_check=True)

folder = _get_path_to_caps_freesurfer_cross_sectional("sub-01", "ses-2011")
compare_folders(
output_dir / folder / "regional_measures",
ref_dir / folder / "regional_measures",
output_dir,
)
for sub_folder in ("label", "mri", "stats"):
compare_folders(
output_dir / folder / "sub-01_ses-2011" / sub_folder,
ref_dir / folder / "sub-01_ses-2011" / sub_folder,
output_dir,
)


def _get_path_to_caps_freesurfer_cross_sectional(part_id: str, session_id: str) -> Path:
return (
Path("caps")
/ "subjects"
/ part_id
/ session_id
/ "t1"
/ "freesurfer_cross_sectional"
)


@pytest.mark.slow
def test_t1_freesurfer_template(cmdopt, tmp_path):
base_dir = Path(cmdopt["input"])
working_dir = Path(cmdopt["wd"])
input_dir, tmp_dir, ref_dir = configure_paths(
base_dir, tmp_path, "T1FreeSurferTemplate"
)
run_t1_freesurfer_template(input_dir, tmp_dir, ref_dir, working_dir)


def run_t1_freesurfer_template(
input_dir: Path, output_dir: Path, ref_dir: Path, working_dir: Path
) -> None:
"""Run the T1FreeSurferTemplate pipeline on test data.
Notes
-----
Data for this functional test comes from https://openneuro.org/datasets/ds000204
sub-01 was duplicated into to sub-02 with one session in order to test the "one time point" case.
We only check that folders are the same meaning that FreeSurfer finished without error.
surf/ folder is ignored because it contains symlinks that makes hard to check with ref data
(symlinks of ref data are ignored after rsync on CI machines).
"""
from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_template_pipeline import (
T1FreeSurferTemplate,
)

shutil.copytree(input_dir / "caps", output_dir / "caps", copy_function=shutil.copy)

pipeline = T1FreeSurferTemplate(
caps_directory=fspath(output_dir / "caps"),
tsv_file=fspath(input_dir / "subjects.tsv"),
base_dir=fspath(working_dir),
)
pipeline.run(plugin="MultiProc", plugin_args={"n_procs": 2}, bypass_check=True)

for part_id, long_id in zip(["sub-01", "sub-02"], ["long-20112015", "long-2011"]):
folder = (
_get_path_to_caps_freesurfer_template(part_id, long_id)
/ f"{part_id}_{long_id}"
)
for sub_folder in ("label", "mri", "stats"):
compare_folders(
output_dir / folder / sub_folder,
ref_dir / folder / sub_folder,
output_dir,
)


def _get_path_to_caps_freesurfer_template(part_id: str, long_id: str) -> Path:
return (
Path("caps") / "subjects" / part_id / long_id / "freesurfer_unbiased_template"
)


@pytest.mark.slow
def test_t1_freesurfer_longitudinal_correction(cmdopt, tmp_path):
base_dir = Path(cmdopt["input"])
working_dir = Path(cmdopt["wd"])
input_dir, tmp_dir, ref_dir = configure_paths(
base_dir, tmp_path, "T1FreeSurferLongitudinalCorrection"
)
run_t1_freesurfer_longitudinal_correction(input_dir, tmp_dir, ref_dir, working_dir)


def run_t1_freesurfer_longitudinal_correction(
input_dir: Path, output_dir: Path, ref_dir: Path, working_dir: Path
) -> None:
"""Run the T1FreeSurferLongitudinalCorrection pipeline on test data.
Notes
-----
Data for this functional test comes from https://openneuro.org/datasets/ds000204
We only check that folders are the same meaning that FreeSurfer finished without error.
surf/ folder is ignored because it contains symlinks that makes hard to check with ref data
(symlinks of ref data are ignored after rsync on CI machines).
"""
from clinica.pipelines.t1_freesurfer_longitudinal.t1_freesurfer_longitudinal_correction_pipeline import (
T1FreeSurferLongitudinalCorrection,
)

shutil.copytree(input_dir / "caps", output_dir / "caps", copy_function=shutil.copy)

pipeline = T1FreeSurferLongitudinalCorrection(
caps_directory=fspath(output_dir / "caps"),
tsv_file=fspath(input_dir / "subjects.tsv"),
base_dir=fspath(working_dir),
)
pipeline.run(bypass_check=True)

folder = _get_path_to_caps_freesurfer_longitudinal(
"sub-01", "ses-2011", "long-20112015"
)
compare_folders(
output_dir / folder / "regional_measures",
ref_dir / folder / "regional_measures",
output_dir,
)
for sub_folder in ("label", "mri", "stats"):
compare_folders(
output_dir
/ folder
/ "sub-01_ses-2011.long.sub-01_long-20112015"
/ sub_folder,
ref_dir / folder / "sub-01_ses-2011.long.sub-01_long-20112015" / sub_folder,
output_dir,
)


def _get_path_to_caps_freesurfer_longitudinal(
part_id: str, session_id: str, long_id: str
) -> Path:
return (
Path("caps")
/ "subjects"
/ part_id
/ session_id
/ "t1"
/ long_id
/ "freesurfer_longitudinal"
)
56 changes: 56 additions & 0 deletions test/nonregression/pipelines/anat/test_t1_linear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
from os import fspath
from pathlib import Path
from test.nonregression.testing_tools import compare_folders, configure_paths

import pytest


@pytest.mark.fast
def test_t1_linear(cmdopt, tmp_path):
base_dir = Path(cmdopt["input"])
working_dir = Path(cmdopt["wd"])
input_dir, tmp_dir, ref_dir = configure_paths(base_dir, tmp_path, "T1Linear")
run_t1_linear(input_dir, tmp_dir, ref_dir, working_dir)


@pytest.mark.fast
def test_flair_linear(cmdopt, tmp_path):
base_dir = Path(cmdopt["input"])
working_dir = Path(cmdopt["wd"])
input_dir, tmp_dir, ref_dir = configure_paths(base_dir, tmp_path, "FlairLinear")
run_flair_linear(input_dir, tmp_dir, ref_dir, working_dir)


def run_t1_linear(
input_dir: Path, output_dir: Path, ref_dir: Path, working_dir: Path
) -> None:
from clinica.pipelines.t1_linear.anat_linear_pipeline import AnatLinear

pipeline = AnatLinear(
bids_directory=fspath(input_dir / "bids"),
caps_directory=fspath(output_dir / "caps"),
tsv_file=fspath(input_dir / "subjects.tsv"),
base_dir=fspath(working_dir),
parameters={"uncropped_image": False},
name="t1-linear",
)
pipeline.run(plugin="MultiProc", plugin_args={"n_procs": 4}, bypass_check=True)

compare_folders(output_dir / "caps", ref_dir / "caps", output_dir)


def run_flair_linear(
input_dir: Path, output_dir: Path, ref_dir: Path, working_dir: Path
) -> None:
from clinica.pipelines.t1_linear.anat_linear_pipeline import AnatLinear

pipeline = AnatLinear(
bids_directory=fspath(input_dir / "bids"),
caps_directory=fspath(output_dir / "caps"),
base_dir=fspath(working_dir),
parameters={"uncropped_image": False},
name="flair-linear",
)
pipeline.run(plugin="MultiProc", plugin_args={"n_procs": 4}, bypass_check=True)

compare_folders(output_dir / "caps", ref_dir / "caps", output_dir)
Loading

0 comments on commit 54876f1

Please sign in to comment.