diff --git a/.gitignore b/.gitignore index b27f4fa93..2fa437b3e 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,6 @@ target/ # VS Code .vscode/ + +# Virtualenv +venv/ \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 963b0eef0..a3678ab0b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -30,6 +30,7 @@ coloredlogs==10.0.* nilearn==0.6.* pytest==5.3.* pytest_console_scripts==0.2.* +pytest-mock==3.10.* requests==2.23.* bctpy==0.5.* statsmodels==0.11.* diff --git a/scilpy/io/fetcher.py b/scilpy/io/fetcher.py index 50e33562f..77c95bf3f 100644 --- a/scilpy/io/fetcher.py +++ b/scilpy/io/fetcher.py @@ -68,7 +68,10 @@ def get_testing_files_dict(): 'bcc21835cf0bf7210bdc99ba5d8df44b'], 'anatomical_filtering.zip': ['1Li8DdySnMnO9Gich4pilhXisjkjz1-Dy', - '6f0eff5154ff0973a3dc26db00e383ea']} + '6f0eff5154ff0973a3dc26db00e383ea'], + 'fodf_filtering.zip': + ['1iyoX2ltLOoLer-v-49LHOzopHCFZ_Tv6', + 'e79c4291af584fdb25814aa7b403a6ce']} def fetch_data(files_dict, keys=None): diff --git a/scripts/__init__.py b/scripts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/scripts/tests/test_execute_angle_aware_bilateral_filtering.py b/scripts/tests/test_execute_angle_aware_bilateral_filtering.py index ea68da1d3..35b16beca 100644 --- a/scripts/tests/test_execute_angle_aware_bilateral_filtering.py +++ b/scripts/tests/test_execute_angle_aware_bilateral_filtering.py @@ -1,54 +1,112 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +import nibabel as nib +import numpy as np import os +import pytest import tempfile from scilpy.io.fetcher import get_testing_files_dict, fetch_data, get_home # If they already exist, this only takes 5 seconds (check md5sum) -fetch_data(get_testing_files_dict(), keys=['processing.zip']) +fetch_data(get_testing_files_dict(), keys=['fodf_filtering.zip']) +data_path = os.path.join(get_home(), 'fodf_filtering') tmp_dir = tempfile.TemporaryDirectory() +@pytest.fixture +def mock_filtering(mocker, out_fodf): + def _mock(*args, **kwargs): + img = nib.load(out_fodf) + return img.get_fdata().astype(np.float32) + + script = 'scil_execute_angle_aware_bilateral_filtering' + filtering_fn = "angle_aware_bilateral_filtering" + return mocker.patch("scripts.{}.{}".format(script, filtering_fn), + side_effect=_mock, create=True) + + def test_help_option(script_runner): ret = script_runner.run('scil_execute_angle_aware_bilateral_filtering.py', '--help') assert ret.success -def test_asym_basis_output(script_runner): +@pytest.mark.parametrize("in_fodf,out_fodf", + [[os.path.join(data_path, 'fodf_descoteaux07_sub.nii.gz'), + os.path.join(data_path, 'fodf_descoteaux07_sub_full.nii.gz')]]) +def test_asym_basis_output(script_runner, mock_filtering, in_fodf, out_fodf): os.chdir(os.path.expanduser(tmp_dir.name)) - in_fodf = os.path.join(get_home(), 'processing', - 'fodf_descoteaux07_sub.nii.gz') - # We use a low resolution sphere to reduce execution time ret = script_runner.run('scil_execute_angle_aware_bilateral_filtering.py', - in_fodf, 'out_0.nii.gz', - '--sphere', 'repulsion100') + in_fodf, + 'out_fodf1.nii.gz', + '--sphere', 'repulsion100', + '--sigma_angular', '1.0', + '--sigma_spatial', '1.0', + '--sigma_range', '1.0', + '--sh_basis', 'descoteaux07', + '--processes', '1', '-f', + print_result=True, shell=True) + assert ret.success + mock_filtering.assert_called_once() + ret_fodf = nib.load("out_fodf1.nii.gz") + test_fodf = nib.load(out_fodf) + assert np.allclose(ret_fodf.get_fdata(), test_fodf.get_fdata()) -def test_sym_basis_output(script_runner): + +@pytest.mark.parametrize("in_fodf,out_fodf,sym_fodf", + [[os.path.join(data_path, "fodf_descoteaux07_sub.nii.gz"), + os.path.join(data_path, "fodf_descoteaux07_sub_full.nii.gz"), + os.path.join(data_path, "fodf_descoteaux07_sub_sym.nii.gz")]]) +def test_sym_basis_output( + script_runner, mock_filtering, in_fodf, out_fodf, sym_fodf): os.chdir(os.path.expanduser(tmp_dir.name)) - in_fodf = os.path.join(get_home(), 'processing', - 'fodf_descoteaux07_sub.nii.gz') - # We use a low resolution sphere to reduce execution time ret = script_runner.run('scil_execute_angle_aware_bilateral_filtering.py', - in_fodf, 'out_1.nii.gz', '--out_sym', - 'out_sym.nii.gz', '--sphere', 'repulsion100') + in_fodf, + 'out_fodf2.nii.gz', + '--out_sym', 'out_sym.nii.gz', + '--sphere', 'repulsion100', + '--sigma_angular', '1.0', + '--sigma_spatial', '1.0', + '--sigma_range', '1.0', + '--sh_basis', 'descoteaux07', + '--processes', '1', '-f', + print_result=True, shell=True) + assert ret.success + mock_filtering.assert_called_once() + ret_sym_fodf = nib.load("out_sym.nii.gz") + test_sym_fodf = nib.load(sym_fodf) + assert np.allclose(ret_sym_fodf.get_fdata(), test_sym_fodf.get_fdata()) -def test_asym_input(script_runner): + +@pytest.mark.parametrize("in_fodf,out_fodf", + [[os.path.join(data_path, "fodf_descoteaux07_sub_full.nii.gz"), + os.path.join(data_path, "fodf_descoteaux07_sub_twice.nii.gz")]]) +def test_asym_input(script_runner, mock_filtering, in_fodf, out_fodf): os.chdir(os.path.expanduser(tmp_dir.name)) - in_fodf = os.path.join(get_home(), 'processing', - 'fodf_descoteaux07_sub_full.nii.gz') - # We use a low resolution sphere to reduce execution time ret = script_runner.run('scil_execute_angle_aware_bilateral_filtering.py', - in_fodf, 'out_2.nii.gz', - '--sphere', 'repulsion100', '-f') + in_fodf, + 'out_fodf3.nii.gz', + '--sphere', 'repulsion100', + '--sigma_angular', '1.0', + '--sigma_spatial', '1.0', + '--sigma_range', '1.0', + '--sh_basis', 'descoteaux07', + '--processes', '1', '-f', + print_result=True, shell=True) + assert ret.success + mock_filtering.assert_called_once() + + ret_fodf = nib.load("out_fodf3.nii.gz") + test_fodf = nib.load(out_fodf) + assert np.allclose(ret_fodf.get_fdata(), test_fodf.get_fdata()) diff --git a/setup.py b/setup.py index 04d6bd240..bcaba2b36 100644 --- a/setup.py +++ b/setup.py @@ -68,7 +68,11 @@ def run(self): ext_modules=get_extensions(), setup_requires=['cython', 'numpy'], install_requires=external_dependencies, - scripts=SCRIPTS, + entry_points={ + 'console_scripts': ["{}=scripts.{}:main".format( + os.path.basename(s), + os.path.basename(s).split(".")[0]) for s in SCRIPTS] + }, data_files=[('data/LUT', ["data/LUT/freesurfer_desikan_killiany.json", "data/LUT/freesurfer_subcortical.json",