diff --git a/.github/actions/install-slicer/action.yml b/.github/actions/install-slicer/action.yml new file mode 100644 index 0000000..4515ebd --- /dev/null +++ b/.github/actions/install-slicer/action.yml @@ -0,0 +1,78 @@ +name: install-slicer +author: Sebastien Goll (Kitware SAS) +description: Install 3D Slicer +inputs: + version: + description: Version of Slicer available at https://community.chocolatey.org/packages/3dslicer#versionhistory + default: '' +outputs: + slicer_executable: + description: Slicer executable + value: ${{ steps.slicer_exe.outputs.executable }} + +runs: + using: composite + steps: + - name: Setting the permission + run: chmod +x "${{ github.action_path }}/downloader.sh" + shell: bash + - name: Download installer + run: | + "${{ github.action_path }}/downloader.sh" ${{ runner.os }} + shell: bash + - name: Get installer file + id: installer + run: | + installer=$(find "./installer" -maxdepth 1 -name "Slicer*" | head -n1 | xargs realpath) + echo "slicer_installer=$installer" >> $GITHUB_OUTPUT + shell: bash + - name: Windows Slicer install + id: win_install + if: ${{runner.os == 'Windows'}} + run: | + ${{ steps.installer.outputs.slicer_installer }} //S + folder=$(find "C:/ProgramData/slicer.org" -maxdepth 1 -type d -name Slicer* -exec stat --format "%Y %n" {} + |sort -nr | head -n1 | cut -d' ' -f2-) + echo "executable=$folder/Slicer.exe" >> $GITHUB_OUTPUT + shell: bash + - name: Linux env setup + if: ${{ runner.os == 'Linux' }} + run: | + sudo apt-get install libglu1-mesa libpulse-mainloop-glib0 libnss3 libasound2t64 qt5dxcb-plugin libxcb-util1 xvfb + mkdir ./Slicer + export DISPLAY=:99 + Xvfb :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & + echo "DISPLAY=:99" >> $GITHUB_ENV + shell: bash + - name: Linux Slicer install + id: linux_install + if: ${{ runner.os == 'Linux' }} + run: | + tar xzf "${{ steps.installer.outputs.slicer_installer }}" -C "." + folder=$(find "." -maxdepth 1 -name "Slicer*" -type d | head -n1 | xargs realpath) + echo "executable=$folder/Slicer" >> $GITHUB_OUTPUT + shell: bash + - name: Max env setup + if: ${{ runner.os == 'macOS' }} + run: | + brew install expect + chmod +x "${{ github.action_path }}/macos_installer.exp" + shell: bash + - name: Mac Slicer install + if: ${{ runner.os == 'macOS' }} + id: mac_install + run: | + MOUNT_POINT=$("${{ github.action_path }}/macos_installer.exp" ${{ steps.installer.outputs.slicer_installer }} | grep -o '/Volumes/[^ ]*'| tr -d '\r\n') + cp -R "$MOUNT_POINT/Slicer.app" /Applications/ + hdiutil detach "$MOUNT_POINT" + echo "executable=/Applications/Slicer.app/Contents/MacOS/Slicer" >> $GITHUB_OUTPUT + shell: bash + - id: slicer_exe + run: | + if [ ${{ runner.os }} == "Windows" ]; then + echo "executable=${{ steps.win_install.outputs.executable }}" >> $GITHUB_OUTPUT + elif [ ${{ runner.os }} == "Linux" ]; then + echo "executable=${{ steps.linux_install.outputs.executable }}" >> $GITHUB_OUTPUT + elif [ ${{ runner.os }} == "macOS" ]; then + echo "executable=${{ steps.mac_install.outputs.executable }}" >> $GITHUB_OUTPUT + fi + shell: bash \ No newline at end of file diff --git a/.github/actions/install-slicer/downloader.sh b/.github/actions/install-slicer/downloader.sh new file mode 100644 index 0000000..05a1d87 --- /dev/null +++ b/.github/actions/install-slicer/downloader.sh @@ -0,0 +1,30 @@ +if [ $1 == "Windows" ]; then + PACK_OS="win" +elif [ $1 == "Linux" ]; then + PACK_OS="linux" +elif [ $1 == "macOS" ]; then + PACK_OS="macosx" +fi + +PACK_ARCH="amd64" +BASE_URL="https://slicer-packages.kitware.com/api/v1" + +APP_ID=$(curl -s "$BASE_URL/app?name=Slicer&limit=1" | jq -r '.[0]._id') +echo "Application id found for Slicer: $APP_ID" + +RELEASE_NAME=$(curl -s "$BASE_URL/app/$APP_ID/release?sort=meta.revision&sortdir=-1" | jq -r '.[0].lowerName') +echo "Realease name found: $RELEASE_NAME" + +PACK=$(curl -s "$BASE_URL/app/$APP_ID/package?release_id_or_name=$RELEASE_NAME&os=$PACK_OS&arch=$PACK_ARCH&limit=1" | jq '.[0]') +PACK_ID=$(jq -r '._id' <<< "$PACK") +PACK_NAME=$(jq -r '.name' <<< "$PACK") +echo "Name of the package found: $PACK_NAME ($PACK_ID)" + +mkdir -p installer + +HEADER_TMP=$(mktemp) +curl -# -D "$HEADER_TMP" "$BASE_URL/item/$PACK_ID/download" -o file +CONTENT_DISPOSITION=$(grep -i '^Content-Disposition:' "$HEADER_TMP") +FILENAME=$(echo "$CONTENT_DISPOSITION" | sed -n 's/.*filename="\(.*\)".*/\1/p') +mv file "installer/$FILENAME" + diff --git a/.github/actions/install-slicer/macos_installer.exp b/.github/actions/install-slicer/macos_installer.exp new file mode 100644 index 0000000..3e09c1c --- /dev/null +++ b/.github/actions/install-slicer/macos_installer.exp @@ -0,0 +1,17 @@ +#!/usr/bin/expect -f + +set timeout 10 +set dmg_file [lindex $argv 0] + +spawn hdiutil attach "$dmg_file" + +sleep 2 +send "q\r" + +expect { + "Agree Y/N?" { + send "y\r" + } +} + +expect eof \ No newline at end of file diff --git a/.github/actions/slicer-install-extensions/action.yml b/.github/actions/slicer-install-extensions/action.yml new file mode 100644 index 0000000..aebdc3d --- /dev/null +++ b/.github/actions/slicer-install-extensions/action.yml @@ -0,0 +1,23 @@ +name: slicer-install-extensions +author: Sebastien Goll (Kitware SAS) +description: Install extensions for Slicer + +inputs: + slicer_exe: + description: Slicer executable + required: true + additional_arguments: + description: Additional arguments for Slicer executable + default: '--no-main-window --no-splash' + extensions_name: + description: Extensions names to install + required: true + +runs: + using: composite + steps: + - uses: ./.github/actions/slicer-run-python-script + with: + script: ${{ github.action_path }}/install_extensions.py + slicer_exe: ${{ inputs.slicer_exe }} + additional_arguments: ${{ inputs.additional_arguments }} ${{ inputs.extensions_name }} diff --git a/.github/actions/slicer-install-extensions/install_extensions.py b/.github/actions/slicer-install-extensions/install_extensions.py new file mode 100644 index 0000000..ff954b3 --- /dev/null +++ b/.github/actions/slicer-install-extensions/install_extensions.py @@ -0,0 +1,15 @@ +import sys +import slicer.util + +if __name__ == '__main__': + + extension_names = sys.argv[1:] + print("Found extensions: ", extension_names) + + em = slicer.app.extensionsManagerModel() + em.setInteractive(False) + + for name in extension_names: + em.installExtensionFromServer(name, False) + + slicer.util.quit() diff --git a/.github/actions/slicer-install-python-packages/action.yml b/.github/actions/slicer-install-python-packages/action.yml new file mode 100644 index 0000000..d3d1245 --- /dev/null +++ b/.github/actions/slicer-install-python-packages/action.yml @@ -0,0 +1,22 @@ +name: slicer-install-python-packages +author: Sebastien Goll (Kitware SAS) +description: Install python packages in Slicer's python environment + +inputs: + slicer_exe: + description: Slicer directory + required: true + additional_arguments: + description: Additional arguments for Slicer executable + default: '--no-main-window --no-splash' + package_names: + description: Packages to install + required: true +runs: + using: composite + steps: + - uses: ./.github/actions/slicer-run-python-script + with: + script: ${{ github.action_path }}/install_py_packages.py + slicer_exe: ${{ inputs.slicer_exe }} + additional_arguments: ${{ inputs.additional_arguments }} ${{ inputs.package_names }} diff --git a/.github/actions/slicer-install-python-packages/install_py_packages.py b/.github/actions/slicer-install-python-packages/install_py_packages.py new file mode 100644 index 0000000..f6914b5 --- /dev/null +++ b/.github/actions/slicer-install-python-packages/install_py_packages.py @@ -0,0 +1,11 @@ +import sys + +import slicer.util + +if __name__ == "__main__": + + print("Packages found:",sys.argv[1:]) + + for package in sys.argv[1:]: + slicer.util.pip_install(package) + slicer.util.quit() \ No newline at end of file diff --git a/.github/actions/slicer-run-python-script/action.yml b/.github/actions/slicer-run-python-script/action.yml new file mode 100644 index 0000000..5a26b71 --- /dev/null +++ b/.github/actions/slicer-run-python-script/action.yml @@ -0,0 +1,19 @@ +name: slicer-run-python-script +author: Sebastien Goll (Kitware SAS) +description: Run a python script through Slicer +inputs: + slicer_exe: + description: Slicer executable + required: true + script: + description: Path to python to run + required: true + additional_arguments: + description: Additional arguments for Slicer executable + default: '' +runs: + using: composite + steps: + - run: | + "${{inputs.slicer_exe}}" --python-script "${{ inputs.script }}" ${{ inputs.additional_arguments }} + shell: bash \ No newline at end of file diff --git a/.github/actions/slicer-run-python-tests/action.yml b/.github/actions/slicer-run-python-tests/action.yml new file mode 100644 index 0000000..bf42d5b --- /dev/null +++ b/.github/actions/slicer-run-python-tests/action.yml @@ -0,0 +1,29 @@ +name: slicer-run-python-tests +author: Sebastien Goll (Kitware SAS) +description: Run python tests in Slicer + +inputs: + slicer_exe: + description: Slicer directory + required: true + additional_arguments: + description: Additional arguments for Slicer executable + default: '' + tests_root_path: + description: Root path to search for tests + required: true + tests_name_pattern: + description: Pattern used to find test files + required: true + module_paths: + description: Paths to the tested modules + required: true + +runs: + using: composite + steps: + - uses: ./.github/actions/slicer-run-python-script + with: + script: ${{ github.action_path }}/run_tests.py + slicer_exe: ${{ inputs.slicer_exe }} + additional_arguments: --additional-module-paths ${{ inputs.module_paths }} ${{ inputs.additional_arguments }} ${{ inputs.tests_root_path }} ${{ inputs.tests_name_pattern }} diff --git a/.github/actions/slicer-run-python-tests/run_tests.py b/.github/actions/slicer-run-python-tests/run_tests.py new file mode 100644 index 0000000..0d84d88 --- /dev/null +++ b/.github/actions/slicer-run-python-tests/run_tests.py @@ -0,0 +1,17 @@ +import sys +from pathlib import Path + +import slicer.testing + +if __name__ == "__main__": + + if len(sys.argv) < 3: + raise AttributeError(f"run_tests.py requires 2 arguments, found {sys.argv[1:]}") + + root = Path(sys.argv[1]) + files = list(root.glob(sys.argv[2])) + + print(f"Found {len(files)} test file(s).") + + for file in files: + slicer.testing.runUnitTest(file.parent.as_posix(), file.stem) diff --git a/.github/python_scripts/install_python_dependencies.py b/.github/python_scripts/install_python_dependencies.py new file mode 100644 index 0000000..2bb121f --- /dev/null +++ b/.github/python_scripts/install_python_dependencies.py @@ -0,0 +1,16 @@ +from pathlib import Path + +import slicer.util +import sys + +if __name__ == '__main__': + sys.path.append(Path(__file__).parent.joinpath("../..").resolve().as_posix()) + + from MultiverSeg.SegmentEditorMultiverSegLib import InstallLogic, DependenciesLogic + DependenciesLogic.INTERACTIVE_MODE = False + InstallLogic.INTERACTIVE_MODE = False + + DependenciesLogic.installTorchIfNeeded() + DependenciesLogic.installMultiverSegIfNeeded() + InstallLogic.downloadCheckpointsIfNeeded() + slicer.util.quit() diff --git a/.github/python_scripts/install_pytorch_module.py b/.github/python_scripts/install_pytorch_module.py new file mode 100644 index 0000000..63c6263 --- /dev/null +++ b/.github/python_scripts/install_pytorch_module.py @@ -0,0 +1,14 @@ +import sys +from pathlib import Path + +import slicer.util + + + +if __name__ == '__main__': + sys.path.append(Path(__file__).parent.joinpath("../..").resolve().as_posix()) + + from MultiverSeg.SegmentEditorMultiverSegLib import DependenciesLogic + DependenciesLogic.INTERACTIVE_MODE = False + DependenciesLogic.installPyTorchExtensionIfNeeded() + slicer.util.quit() diff --git a/.github/workflows/windows-testing.yml b/.github/workflows/windows-testing.yml index 7f145a5..ce4df93 100644 --- a/.github/workflows/windows-testing.yml +++ b/.github/workflows/windows-testing.yml @@ -7,94 +7,42 @@ on: pull_request: branches: - main + workflow_dispatch: + jobs: - Tests-Windows: - runs-on: windows-latest - outputs: - install_path: ${{ steps.slicerLocation.outputs.install_path }} - python_path: ${{ steps.pythonLocation.outputs.python_path }} + Running-Tests: + timeout-minutes: 30 + continue-on-error: true + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} steps: - - name: Install Slicer - run: choco install 3dslicer -y - - name: Get slicer executable - id: slicerLocation - run: | - $folder = Get-ChildItem -Path "C:\ProgramData\slicer.org" -Filter "Slicer.exe" -Recurse -ErrorAction SilentlyContinue -Force | - Select-Object -First 1 - - if ($folder) { - $installPath = $folder.FullName - Write-Host "Found Slicer at $installPath" - # Set the output - "install_path=$installPath" >> $env:GITHUB_OUTPUT - } else { - Write-Host "Slicer not found." - exit 1 - } - - name: Get python env - id: pythonLocation - run: | - $pythonPath = Resolve-Path "${{ steps.slicerLocation.outputs.install_path }}/../bin/PythonSlicer.exe" - echo "$pythonPath" - "python_path=$pythonPath" >> $env:GITHUB_OUTPUT - - name: Install SlicerPytorch module - id: slicerpytorch - run: | - $path = "${{ steps.slicerLocation.outputs.install_path }}" - $process = Start-Process -FilePath "$path" -ArgumentList "--testing", "--no-splash", "--no-main-window", "--python-code `"slicer.app.extensionsManagerModel().installExtensionFromServer('PyTorch')`"" -Wait -RedirectStandardOutput "log_std.txt" -RedirectStandardError "log_err.txt" -PassThru - echo "Standard output:" - Get-Content log_std.txt - echo "Error output:" - Get-Content log_err.txt - $exitCode = $process.ExitCode - echo "Exit code: $exitCode" - $folder = Get-ChildItem -Path "${{ steps.slicerLocation.outputs.install_path }}/../slicer.org" -Include qt-*modules -Recurse | Select-Object -First 1 - $folder = $folder.FullName - echo "PyTorch module installed at $folder" - "slicerPyTorch=$folder" >> $env:GITHUB_OUTPUT - exit $exitCode - - name: Install pytorch - run: | - $path = "${{ steps.slicerLocation.outputs.install_path }}" - $pytorchmodule = "${{ steps.slicerpytorch.outputs.slicerPyTorch }}" - $process = Start-Process -FilePath "$path" -ArgumentList "--testing", "--no-splash", "--no-main-window","--additional-module-paths `"$pytorchmodule`"" ,"--python-code `"import PyTorchUtils;PyTorchUtils.PyTorchUtilsLogic().installTorch(askConfirmation=False,torchvisionVersionRequirement='>=0.20')`"" -Wait -RedirectStandardOutput "log_std.txt" -RedirectStandardError "log_err.txt" -PassThru - echo "Standard output:" - Get-Content log_std.txt - echo "Error output:" - Get-Content log_err.txt - $exitCode = $process.ExitCode - echo "Exit code: $exitCode" - exit $exitCode - - name: Install multiverseg - run: | - $path = "${{ steps.pythonLocation.outputs.python_path }}" - & $path -m pip install "git+https://github.com/halleewong/MultiverSeg.git" - - name: Check out repository code - uses: actions/checkout@v4 - - name: Download models - run: | - $path = "${{ steps.slicerLocation.outputs.install_path }}" - $modulepath= "${{ github.workspace }}/MultiverSeg" - $process = Start-Process -FilePath "$path" -ArgumentList "--testing", "--no-splash", "--no-main-window","--additional-module-paths `"$modulepath`"" ,"--python-code `"from SegmentEditorMultiverSegLib import InstallLogic as i;i._downloadModel(i.MULTIVERSEG_FILE_NAME, i.MULTIVERSEG_DOWNLOAD_URL);i._downloadModel(i.SCRIBBLEPROMPT_FILE_NAME, i.SCRIBBLEPROMPT_DOWNLOAD_URL)`"" -Wait -RedirectStandardOutput "log_std.txt" -RedirectStandardError "log_err.txt" -PassThru - - name: Test - run: | - $path = "${{ steps.slicerLocation.outputs.install_path }}" - $modulepath= "${{ github.workspace }}/MultiverSeg" - $pytorchmodule = "${{ steps.slicerpytorch.outputs.slicerPyTorch }}" - $testPath = "$modulepath/Testing/Python" - $testNames = Get-ChildItem ${{ github.workspace }} -Filter *TestCase.py -Recurse | ForEach-Object {$_.BaseName} - $exitCode = 0 - echo "Found tests:" - echo $testNames - foreach($testCase in $testNames){ - $process = Start-Process -FilePath "$path" -ArgumentList "--testing", "--no-splash", "--additional-module-paths `"$modulepath`" `"$pytorchmodule`"" ,"--python-code `"import slicer.testing; slicer.testing.runUnitTest(r'$testPath', '$testCase')`"" -Wait -RedirectStandardOutput "log_std.txt" -RedirectStandardError "log_err.txt" -PassThru - echo "Standard output:" - Get-Content log_std.txt - echo "Error output:" - Get-Content log_err.txt - $currentCode = $process.ExitCode - echo "Exit code: $currentCode" - $exitCode = ($currentCode , $exitCode | Measure-Object -Max).Maximum - } - exit $exitCode \ No newline at end of file + - uses: actions/checkout@v4 + name: Checkout + - uses: ./.github/actions/install-slicer + id: slicer_install + name: Install Slicer + - name: Install PyTorch module + uses: ./.github/actions/slicer-run-python-script + with: + script: './.github/python_scripts/install_pytorch_module.py' + slicer_exe: ${{ steps.slicer_install.outputs.slicer_executable }} + additional_arguments: --no-main-window --no-splash + - name: Install Python dependencies + uses: ./.github/actions/slicer-run-python-script + with: + script: './.github/python_scripts/install_python_dependencies.py' + slicer_exe: ${{ steps.slicer_install.outputs.slicer_executable }} + additional_arguments: --no-main-window --no-splash + - name: Run tests + uses: ./.github/actions/slicer-run-python-tests + with: + slicer_exe: ${{ steps.slicer_install.outputs.slicer_executable }} + additional_arguments: --testing --no-splash + tests_root_path: './MultiverSeg/Testing/Python' + tests_name_pattern: '*TestCase.py' + module_paths: './MultiverSeg' + + diff --git a/MultiverSeg/SegmentEditorMultiverSegLib/InstallLogic.py b/MultiverSeg/SegmentEditorMultiverSegLib/InstallLogic.py index dfc71c6..1fb7715 100644 --- a/MultiverSeg/SegmentEditorMultiverSegLib/InstallLogic.py +++ b/MultiverSeg/SegmentEditorMultiverSegLib/InstallLogic.py @@ -14,6 +14,8 @@ class InstallLogic: SCRIBBLEPROMPT_FILE_NAME = "ScribblePrompt_unet_v1_nf192_res128.pt" SCRIBBLEPROMPT_DOWNLOAD_URL = "https://www.dropbox.com/scl/fi/pnw88n05irnv5z1snlklr/ScribblePrompt_unet_v1_nf192_res128.pt?rlkey=dr8xvkf0wj2r082h1zzpcmz5o&dl=1" + INTERACTIVE_MODE = True # Can the user interact with the window + @classmethod def downloadCheckpointsIfNeeded(cls): try: @@ -32,7 +34,8 @@ def downloadMultiverSegCheckpointIfNeeded(cls): modelPath = cls.CKPT_DIR.joinpath("MultiverSeg_v0_nf256_res128.pt") if not modelPath.is_file(): - if slicer.util.confirmOkCancelDisplay("The MultiverSeg model is required. Confirm to download (74MB)."): + if (not cls.INTERACTIVE_MODE) or slicer.util.confirmOkCancelDisplay( + "The MultiverSeg model is required. Confirm to download (74MB)."): cls._downloadModel(cls.MULTIVERSEG_FILE_NAME, cls.MULTIVERSEG_DOWNLOAD_URL) return True # Model downloaded correctly @@ -45,7 +48,7 @@ def downloadScribblePromptCheckpointIfNeeded(cls): modelPath = cls.CKPT_DIR.joinpath(cls.SCRIBBLEPROMPT_FILE_NAME) if not modelPath.is_file(): - if slicer.util.confirmOkCancelDisplay("The ScribblePrompt model is required. Confirm to download (16MB)."): + if (not cls.INTERACTIVE_MODE) or slicer.util.confirmOkCancelDisplay("The ScribblePrompt model is required. Confirm to download (16MB)."): cls._downloadModel(cls.SCRIBBLEPROMPT_FILE_NAME, cls.SCRIBBLEPROMPT_DOWNLOAD_URL) @@ -89,6 +92,7 @@ def reportProgress(logic, msg, level=None): class DependenciesLogic: + INTERACTIVE_MODE = True # Can the user interact with the window # Install dependencies to torch and multiverseg if not already installed @classmethod @@ -114,7 +118,7 @@ def installPyTorchExtensionIfNeeded(cls): import PyTorchUtils # noqa return True except ModuleNotFoundError: - ret = slicer.util.confirmOkCancelDisplay("""This module requires PyTorch extension. Would you like to install it? + ret = (not cls.INTERACTIVE_MODE) or slicer.util.confirmOkCancelDisplay("""This module requires PyTorch extension. Would you like to install it? Slicer will need to be restarted before continuing the install.""", "PyTorch extension not found.") if ret: @@ -122,9 +126,10 @@ def installPyTorchExtensionIfNeeded(cls): return False # Need restart or not installed # Install the PyTorch Utils extension - @staticmethod - def installPyTorchExtension(): + @classmethod + def installPyTorchExtension(cls): extensionManager = slicer.app.extensionsManagerModel() + extensionManager.setInteractive(cls.INTERACTIVE_MODE) extName = "PyTorch" if extensionManager.isExtensionInstalled(extName): return @@ -140,7 +145,7 @@ def installMultiverSegIfNeeded(cls): import multiverseg return True except ModuleNotFoundError: - ret = slicer.util.confirmOkCancelDisplay( + ret = (not cls.INTERACTIVE_MODE) or slicer.util.confirmOkCancelDisplay( "This module requires the MultiverSeg python package. Would you like to install it?", "MultiverSeg package not found.") if ret: @@ -169,7 +174,7 @@ def installTorchIfNeeded(cls): def installTorch(cls): import PyTorchUtils torchLogic = PyTorchUtils.PyTorchUtilsLogic() - res = torchLogic.installTorch(askConfirmation=True) + res = torchLogic.installTorch(askConfirmation=cls.INTERACTIVE_MODE, torchvisionVersionRequirement=">=0.10") if res is None: raise RuntimeError("Failed to install torch and torchvision. " diff --git a/MultiverSeg/Testing/Python/ContextLogicTestCase.py b/MultiverSeg/Testing/Python/ContextLogicTestCase.py index 49fd8ab..b3c5601 100644 --- a/MultiverSeg/Testing/Python/ContextLogicTestCase.py +++ b/MultiverSeg/Testing/Python/ContextLogicTestCase.py @@ -1,5 +1,6 @@ import unittest from pathlib import Path +from shutil import rmtree from unittest import SkipTest import SampleData @@ -15,6 +16,11 @@ def setUpClass(cls): emptyContextPath = Path(__file__).parent.joinpath("../TestData/Context/empty_context").resolve() emptyContextPath.mkdir(exist_ok=True) + @classmethod + def tearDownClass(cls): + emptyContextPath = Path(__file__).parent.joinpath("../TestData/Context/empty_context").resolve() + rmtree(emptyContextPath) + def test_instantiation(self): logic = ContextLogic(None) @@ -102,11 +108,11 @@ def test_saveNewExample(self): iTruth = logic.loadImage(ressourcePath.joinpath("image_0.png")) mTruth = logic.loadImage(ressourcePath.joinpath("mask_0.png")) - diff = torch.abs(i - iTruth) - self.assertEqual(torch.max(diff).item(), 0) + diff = torch.abs(i.to(torch.int16) - iTruth.to(torch.int16)).to(torch.uint8) + self.assertLessEqual(torch.max(diff).item(), 5) # Need tolerance to different compression methods diff = torch.abs(m - mTruth) - self.assertEqual(torch.max(diff).item(), 0) + self.assertLessEqual(torch.max(diff).item(), 5) import shutil shutil.rmtree(contextPath) diff --git a/MultiverSeg/Testing/Python/SegmentationLogicTestCase.py b/MultiverSeg/Testing/Python/SegmentationLogicTestCase.py index c54b358..79576db 100644 --- a/MultiverSeg/Testing/Python/SegmentationLogicTestCase.py +++ b/MultiverSeg/Testing/Python/SegmentationLogicTestCase.py @@ -9,6 +9,11 @@ class SegmentationLogicTestCase(unittest.TestCase): + + @classmethod + def setUpClass(cls): + torch.backends.mps.is_available = lambda : False + def test_instantiation(self): logic = SegmentationLogic(None)