Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrating Quicktest to PyTest and adding dockerized self hosted runner #549

Merged
merged 4 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 65 additions & 46 deletions .github/workflows/quicktest.yaml
Original file line number Diff line number Diff line change
@@ -1,90 +1,109 @@
name: FastSurfer Singularity
name: quicktest

taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
# File: quicktest.yaml
# Author: Taha Abdullah
# Created on: 2023-03-04
# Functionality: This workflow runs some quick integration tests on FastSurfer commits. It checks out the new
# FastSurfer repo, sets up Python, builds a Singularity image, runs FastSurfer on sample MRI data, and
# runs pytest to check if the results are acceptable
# Usage: This workflow is exclusively triggered manually with workflow-dispatch in DeepMI/FastSurfer.


on:
# pull_request:
workflow_dispatch:

jobs:
# Checkout repo
checkout:
runs-on: ci-gpu
runs-on: self-hosted
steps:
- uses: actions/checkout@v2

# Prepare job: Set up Python, Go, Singularity
# Prepare job: Set up Python, Go, Apptainer
prepare-job:
runs-on: ci-gpu
runs-on: self-hosted
needs: checkout
steps:
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '^1.13.1' # The Go version to download (if necessary) and use.
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools wheel
python -m pip install --progress-bar off .[test]
# - name: Set up Go
# uses: actions/setup-go@v5
# with:
# go-version: '^1.13.1' # The Go version to download (if necessary) and use.
- name: Set up Singularity
uses: eWaterCycle/setup-singularity@v7
with:
singularity-version: 3.8.3
# Build Docker Image and convert it to Singularity
build-singularity-image:
runs-on: ci-gpu
singularity-version: 3.8.7

# Build Docker Image and convert it to Apptainer
build-apptainer-image:
runs-on: self-hosted
needs: prepare-job
steps:
- name: Build Docker Image and convert to Singularity
- name: Build Docker Image and convert to Apptainer
run: |
cd $RUNNER_SINGULARITY_IMGS
cd $RUNNER_FASTSURFER_IMGS
FILE="fastsurfer-gpu.sif"
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
if [ ! -f "$FILE" ]; then
# If the file does not exist, build the file
echo "SIF File does not exist. Building file."
PYTHONPATH=$PYTHONPATH
cd $PYTHONPATH
cd $FASTSURFER_HOME
python3 Docker/build.py --device cuda --tag fastsurfer_gpu:cuda
cd $RUNNER_SINGULARITY_IMGS
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
singularity build --force fastsurfer-gpu.sif docker-daemon://fastsurfer_gpu:cuda
apptainer build --force fastsurfer-gpu.sif docker-daemon://fastsurfer_gpu:cuda
else
echo "File already exists"
cd $PYTHONPATH
cd $FASTSURFER_HOME
fi
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved

# Run FastSurfer on MRI data
run-fastsurfer:
runs-on: ci-gpu
needs: build-singularity-image
runs-on: self-hosted
needs: build-apptainer-image
steps:
- name: Run FastSurfer
run: |
singularity exec --nv \
cd $RUNNER_FS_OUTPUT
# DIRECTORY="subjectX"
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
echo "pwd: $(pwd)"
# if [ -d "$DIRECTORY" ]; then
# # if output already exists, delete it and run again
# echo "Output already exists. Deleting output directory and running FastSurfer again."
# rm -rf $DIRECTORY
# fi
apptainer exec --nv \
--no-home \
--bind $GITHUB_WORKSPACE:/fastsurfer-dev \
--env FASTSURFER_HOME=/fastsurfer-dev \
-B $RUNNER_FS_MRI_DATA:/data \
-B $RUNNER_FS_OUTPUT:/output \
-B $RUNNER_FS_LICENSE:/fs_license \
$RUNNER_SINGULARITY_IMGS/fastsurfer-gpu.sif \
/fastsurfer/run_fastsurfer.sh \
-B $RUNNER_FS_LICENSE:/fs_license/.license \
$RUNNER_FASTSURFER_IMGS/fastsurfer-gpu.sif \
/fastsurfer/brun_fastsurfer.sh \
--fs_license /fs_license/.license \
--t1 /data/subjectx/orig.mgz \
--sid subjectX --sd /output \
--parallel --3T
--subject_list /data/subject_list.txt \
--sd /output \
--parallel --3T \
--parallel_subjects surf

# Test file existence
test-file-existence:
runs-on: ci-gpu
needs: run-fastsurfer
steps:
- name: Test File Existence
run: |
python3 test/quick_test/test_file_existence.py $RUNNER_FS_OUTPUT_FILES

# Test for errors in log files
test-error-messages:
runs-on: ci-gpu
needs: [run-fastsurfer, test-file-existence]
steps:
- name: Test Log Files For Error Messages
run: |
python3 test/quick_test/test_errors.py $RUNNER_FS_OUTPUT_LOGS
# Run pytest
run-pytest:
runs-on: self-hosted
needs: run-fastsurfer
steps:
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install package
run: |
python -m pip install --progress-bar off --upgrade pip setuptools wheel
python -m pip install --progress-bar off .[test]
- name : Run pytest
run: pytest test/quick_test
87 changes: 87 additions & 0 deletions .github/workflows/quicktest_runner.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: quicktest-runner

# File: quicktest_runner.yaml
# Author: Taha Abdullah
# Created on: 2023-07-10
# Functionality: This workflow runs FastSurfer on MRI data and runs pytest to check if the results are acceptable. It
# also checks if the FastSurfer environment and output already exist, and if not, it creates them.
# Usage: This workflow is triggered on a pull request to the dev and main branch. It can also be triggered manually
# with workflow-dispatch.
# Expected/Used Environment Variables:
# - MAMBAPATH: Path to the micromamba binary.
# - MAMBAROOT: Root path for micromamba.
# - RUNNER_FS_OUTPUT: Path to the directory where FastSurfer output is stored.
# - RUNNER_FS_MRI_DATA: Path to the directory where MRI data is stored.
# - FREESURFER_HOME: Path to the freesurfer directory.
# - FS_LICENSE: Path to the FreeSurfer license file.

on:
pull_request:
branches:
- dev
- stable
workflow_dispatch:

jobs:
# Checkout repo
checkout:
runs-on: self-hosted
steps:
- uses: actions/checkout@v2

# Create conda environment, install packages, and run Fastsurfer
run-fastsurfer:
runs-on: self-hosted
needs: checkout
steps:
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
# Check if the Environment Variables used in further steps are present
- name: Check Environment Variables
run: |
REQUIRED_ENV_VARS=(
"MAMBAPATH"
"MAMBAROOT"
"RUNNER_FS_OUTPUT"
"RUNNER_FS_MRI_DATA"
"FREESURFER_HOME"
"FS_LICENSE"
)

for VAR_NAME in "${REQUIRED_ENV_VARS[@]}"; do
if [ -z "${!VAR_NAME}" ]; then
echo "Error: Required environment variable $VAR_NAME is not set"
exit 1
fi
done
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved

if [ ! -f "$FS_LICENSE" ]; then
echo "Error: FreeSurfer license file does not exist at $FS_LICENSE"
exit 1
fi

if [ ! -d "$FREESURFER_HOME" ]; then
echo "Error: FreeSurfer installation directory does not exist at $FREESURFER_HOME"
exit 1
fi
# Run FastSurfer on test subjects
- name: Run FastSurfer
run: |
echo "Running FastSurfer..."
echo "Output will be saved in data/${GITHUB_SHA:0:7}"
export FASTSURFER_HOME=$(pwd)
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
export THIS_RUN_OUTDIR=${GITHUB_SHA:0:7}
mkdir -p $SUBJECTS_DIR/$THIS_RUN_OUTDIR
export TEST_DIR=$THIS_RUN_OUTDIR
./brun_fastsurfer.sh --subject_list $RUNNER_FS_MRI_DATA/subjects_list.txt \
--sd $SUBJECTS_DIR/$THIS_RUN_OUTDIR \
--parallel --threads 4 --3T --parallel_subjects surf

# Test fastsurfer output
run-pytest:
runs-on: self-hosted
if: always()
needs: run-fastsurfer
steps:
- name: Run pytest
run: |
source /venv-pytest/bin/activate
python -m pytest test/quick_test
dkuegler marked this conversation as resolved.
Show resolved Hide resolved
4 changes: 4 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,13 @@ style = [
'pydocstyle[toml]',
'ruff',
]
quicktest = [
'pytest>=8.2.2',
]
all = [
'fastsurfer[doc]',
'fastsurfer[style]',
'fastsurfer[quicktest]',
taha-abdullah marked this conversation as resolved.
Show resolved Hide resolved
]
full = [
'fastsurfer[all]',
Expand Down
13 changes: 5 additions & 8 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@



__all__ = [ # This is a list of modules that should be imported when using the import * syntax
'test_file_existence',
'test_error_messages',
'test_errors'
]
__all__ = [ # This is a list of modules that should be imported when using the import * syntax
"test_file_existence",
"test_error_messages",
"test_errors",
]
31 changes: 31 additions & 0 deletions test/quick_test/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import os
from logging import getLogger

logger = getLogger(__name__)


__all__ = ["load_test_subjects"]


def load_test_subjects():
"""
Load the test files from the given file path.

Returns:
test_subjects (list): List of subjects to test subjects.
"""

subjects_dir = os.environ["SUBJECTS_DIR"]
subjects_list = os.environ["SUBJECTS_LIST"]

test_subjects = []

# Load the reference and test files
with open(os.path.join(subjects_dir, subjects_list)) as file:
for line in file:
filename = line.strip()
logger.debug(filename)
# test_file = os.path.join(subjects_dir, filename)
test_subjects.append(filename)

return test_subjects
26 changes: 26 additions & 0 deletions test/quick_test/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from pathlib import Path

import pytest

__all__ = ["subjects_dir", "test_dir", "reference_dir", "subjects_list"]


@pytest.fixture
def subjects_dir():
return Path(os.environ["SUBJECTS_DIR"])


@pytest.fixture
def test_dir():
return Path(os.environ["TEST_DIR"])


@pytest.fixture
def reference_dir():
return Path(os.environ["REFERENCE_DIR"])


@pytest.fixture
def subjects_list():
return Path(os.environ["SUBJECTS_LIST"])
11 changes: 0 additions & 11 deletions test/quick_test/data/errors.yaml

This file was deleted.

14 changes: 14 additions & 0 deletions test/quick_test/data/logfile.errors.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
errors:
- "error"
- "error:"
- "exception"
- "traceback"

whitelist:
- "without error"
- "not included"
- "distance"
- "correcting"
- "error="
- "rms error"
- "mcsrch error"
23 changes: 23 additions & 0 deletions test/quick_test/data/thresholds/aparc+DKT.stats.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
default_threshold: 0.01

thresholds:
BrainSegVol: 0.1
BrainSegVolNotVent: 0.1
VentricleChoroidVol: 0.1
lhCortexVol: 0.1
rhCortexVol: 0.1
CortexVol: 0.1
lhCerebralWhiteMatterVol: 0.1
rhCerebralWhiteMatterVol: 0.1
CerebralWhiteMatterVol: 0.1
SubCortGrayVol: 0.1
TotalGrayVol: 0.1
SupraTentorialVol: 0.1
SupraTentorialVolNotVent: 0.1
MaskVol: 0.1
BrainSegVol-to-eTIV: 0.1
MaskVol-to-eTIV: 0.1
lhSurfaceHoles: 0.1
rhSurfaceHoles: 0.1
SurfaceHoles: 0.1
eTIV: 0.1
Loading
Loading