From a2f433123ed58101f74308ff85643324615a6b25 Mon Sep 17 00:00:00 2001 From: luiz Date: Thu, 29 Aug 2024 11:34:57 +0200 Subject: [PATCH] tests --- .github/workflows/run_tests.yaml | 75 ++++++++++++++++++++++++ app/app.py | 16 +++--- tests/README.md | 7 +++ tests/requirements.txt | 2 + tests/test_voluseg.py | 97 ++++++++++++++++++++++++++++++++ voluseg/_tools/sample_data.py | 9 ++- 6 files changed, 195 insertions(+), 11 deletions(-) create mode 100644 .github/workflows/run_tests.yaml create mode 100644 tests/README.md create mode 100644 tests/requirements.txt create mode 100644 tests/test_voluseg.py diff --git a/.github/workflows/run_tests.yaml b/.github/workflows/run_tests.yaml new file mode 100644 index 0000000..788de2b --- /dev/null +++ b/.github/workflows/run_tests.yaml @@ -0,0 +1,75 @@ +name: Tests with Spark + +on: + push: + branches: + - tests + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + + strategy: + matrix: + python: + - '3.10' + - '3.11' + - '3.12' + spark: + - 3.4.0 + + env: + ANTS_PATH: $HOME/ants-2.5.3/bin + + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python }} + + - uses: actions/setup-java@v4 + with: + java-version: '21' + distribution: temurin + + # https://github.com/marketplace/actions/setup-apache-spark + - uses: vemonet/setup-spark@v1 + with: + spark-version: ${{ matrix.spark }} + hadoop-version: '3' + + - run: spark-submit --version + + # Download and install ANTs + - name: Install ANTs + run: | + wget https://github.com/ANTsX/ANTs/releases/download/v2.5.3/ants-2.5.3-ubuntu-20.04-X64-gcc.zip + unzip ants-2.5.3-ubuntu-20.04-X64-gcc.zip -d $HOME/ + rm ants-2.5.3-ubuntu-20.04-X64-gcc.zip + echo "ANTs installed successfully" + + - name: Install test dependencies + run: | + python -m pip install --upgrade pip + pip install -r tests/requirements.txt --no-cache-dir + + - name: Install voluseg + run: | + pip install . --no-cache-dir + + - name: Run tests with pytest + run: | + pytest --cov=voluseg --cov-report=term-missing + + - name: Upload coverage report + if: success() + uses: actions/upload-artifact@v3 + with: + name: coverage-report + path: htmlcov/ \ No newline at end of file diff --git a/app/app.py b/app/app.py index a3ff5a7..fa9fb54 100644 --- a/app/app.py +++ b/app/app.py @@ -5,16 +5,16 @@ # set and save parameters parameters0 = voluseg.parameter_dictionary() -parameters0['dir_ants'] = '/ants-2.5.3/bin/' -parameters0['dir_input'] = '/voluseg/data/' -parameters0['dir_output'] = '/voluseg/output/' -parameters0['registration'] = 'high' -parameters0['diam_cell'] = 5.0 -parameters0['f_volume'] = 2.0 +parameters0["dir_ants"] = "/ants-2.5.3/bin/" +parameters0["dir_input"] = "/voluseg/data/" +parameters0["dir_output"] = "/voluseg/output/" +parameters0["registration"] = "high" +parameters0["diam_cell"] = 5.0 +parameters0["f_volume"] = 2.0 voluseg.step0_process_parameters(parameters0) # load and print parameters -filename_parameters = os.path.join(parameters0['dir_output'], 'parameters.pickle') +filename_parameters = os.path.join(parameters0["dir_output"], "parameters.pickle") parameters = voluseg.load_parameters(filename_parameters) pprint.pprint(parameters) @@ -31,4 +31,4 @@ voluseg.step4_detect_cells(parameters) print("clean cells.") -voluseg.step5_clean_cells(parameters) \ No newline at end of file +voluseg.step5_clean_cells(parameters) diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 0000000..12c5ece --- /dev/null +++ b/tests/README.md @@ -0,0 +1,7 @@ +# Tests + +Run tests locally with `pytest`: + +```bash +pytest -s +``` \ No newline at end of file diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 0000000..efe3dd5 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,2 @@ +pytest==8.3.2 +pytest-cov==5.0.0 \ No newline at end of file diff --git a/tests/test_voluseg.py b/tests/test_voluseg.py new file mode 100644 index 0000000..b7dd877 --- /dev/null +++ b/tests/test_voluseg.py @@ -0,0 +1,97 @@ +import os +import pprint +import voluseg +import pytest +from pathlib import Path + +from voluseg._tools import download_sample_data + + +@pytest.fixture +def setup_parameters(tmp_path): + # Define parameters and paths + parameters = voluseg.parameter_dictionary() + + # Download sample data, only if running in GitHub Actions + if os.environ.get("GITHUB_ACTIONS") == "true": + data_path = download_sample_data() + else: + data_path = "/change_path_to/sample_data" + parameters["dir_input"] = data_path + parameters["dir_ants"] = os.environ.get("ANTS_PATH") + parameters["dir_output"] = str(tmp_path) # Use pytest's tmp_path fixture for output + parameters["registration"] = "high" + parameters["diam_cell"] = 5.0 + parameters["f_volume"] = 2.0 + + # Save parameters + voluseg.step0_process_parameters(parameters) + + # Return parameters for further use in tests + return parameters + + +def test_load_parameters(setup_parameters): + # Load parameters + file_path = str(Path(setup_parameters["dir_output"] / "parameters.pickle")) + file_parameters = voluseg.load_parameters(file_path) + + all_keys = set(setup_parameters.keys()).union(set(file_parameters.keys())) + + for key in all_keys: + assert key in setup_parameters, f"Key '{key}' is missing in setup_parameters" + assert key in file_parameters, f"Key '{key}' is missing in file_parameters" + if key in setup_parameters and key in file_parameters: + assert ( + setup_parameters[key] == file_parameters[key] + ), f"Value differs for key '{key}': setup_parameters has {setup_parameters[key]}, file_parameters has {file_parameters[key]}" + + +def test_voluseg_pipeline_h5_dir(setup_parameters): + + # Test step 1 - process volumes + print("Process volumes.") + voluseg.step1_process_volumes(setup_parameters) + n_files_input = len([p for p in Path(setup_parameters["dir_input"]).glob("*.h5")]) + n_files_output = len( + [ + p + for p in (Path(setup_parameters["dir_output"]) / "volumes/0/").glob( + "*.nii.gz" + ) + ] + ) + assert ( + n_files_input == n_files_output + ), f"Number of input files ({n_files_input}) does not match number of output files ({n_files_output})" + + # Test step 2 - align volumes + print("Align volumes.") + voluseg.step2_align_volumes(setup_parameters) + n_files_transform = len( + [ + p + for p in (Path(setup_parameters["dir_output"]) / "transforms/0/").glob( + "*.mat" + ) + ] + ) + assert ( + n_files_output == n_files_transform + ), f"Number of output files ({n_files_output}) does not match number of transform files ({n_files_transform})" + + # Test step 3 - mask volumes + print("Mask volumes.") + voluseg.step3_mask_volumes(setup_parameters) + assert ( + Path(setup_parameters["dir_output"]) / "masks_plots/0/histogram.png" + ).exists(), "Histogram plot does not exist" + assert ( + Path(setup_parameters["dir_output"]) / "masks_plots/0/mask_z000.png" + ).exists(), "Mask plot does not exist" + + # print("Detect cells.") + # voluseg.step4_detect_cells(setup_parameters) + + # print("Clean cells.") + # voluseg.step5_clean_cells(setup_parameters) diff --git a/voluseg/_tools/sample_data.py b/voluseg/_tools/sample_data.py index b5040aa..72cc878 100644 --- a/voluseg/_tools/sample_data.py +++ b/voluseg/_tools/sample_data.py @@ -2,9 +2,10 @@ import zipfile import os from pathlib import Path +from typing import Union -def download_sample_data(destination_folder: str = ".") -> None: +def download_sample_data(destination_folder: str = ".") -> Union[str, None]: """ Download sample data for Voluseg. @@ -16,7 +17,8 @@ def download_sample_data(destination_folder: str = ".") -> None: Returns ------- - None + str or None + Path to the folder where the data was extracted, or None if the download failed. """ # Download the ZIP file download_url = "https://drive.usercontent.google.com/download" @@ -47,6 +49,7 @@ def download_sample_data(destination_folder: str = ".") -> None: # Remove the ZIP file os.remove(filename) print(f"Removed the ZIP file: {filename}") - + return extract_folder else: print(f"Failed to download file. Status code: {response.status_code}") + return None