Skip to content

Commit

Permalink
fix(tests): fix tests for napari widgets, fix #374 (#376)
Browse files Browse the repository at this point in the history
* Fix: Refactor test_widget_open_file to use qtbot for consistent GUI testing

* style(pre-commit.ci): auto fixes

* fix(tests): fix tests for napari widgets, fix #374

Napari widget tests hang before

---------

Co-authored-by: adambasha0 <readam@adbasha>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Qin Yu <[email protected]>
  • Loading branch information
4 people authored Jan 24, 2025
1 parent e4750f5 commit eaf05e4
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 43 deletions.
20 changes: 6 additions & 14 deletions tests/widgets/test_widget_nuc_fix.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,18 @@
import os

import napari
import pytest
from magicgui import magicgui
from napari.types import LayerDataTuple

from plantseg.core.image import PlantSegImage
from plantseg.viewer_napari.widgets.dataprocessing import widget_fix_over_under_segmentation_from_nuclei
from tests.tasks.dataprocessing.test_advanced_dataprocessing_tasks import complex_test_PlantSegImages

IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" # set to true in GitHub Actions by default to skip CUDA tests


@magicgui
def widget_add_image(image: PlantSegImage) -> LayerDataTuple:
"""Add a plantseg.core.image.PlantSegImage to napari viewer as a napari.layers.Layer."""
return image.to_napari_layer_tuple()


@pytest.mark.skip(reason="Test hangs even if scheduled task completes.")
@pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="GUI tests hang in GitHub Actions.")
def test_widget_fix_over_under_segmentation_from_nuclei(make_napari_viewer_proxy, complex_test_PlantSegImages):
def test_widget_fix_over_under_segmentation_from_nuclei(qtbot, make_napari_viewer_proxy, complex_test_PlantSegImages):
"""
Test the widget_fix_over_under_segmentation_from_nuclei function in a napari viewer environment.
Expand All @@ -47,16 +39,16 @@ def test_widget_fix_over_under_segmentation_from_nuclei(make_napari_viewer_proxy
widget_add_image(boundary_pmap)

# Run the widget for correcting segmentation
count_layers = len(viewer.layers)
widget_fix_over_under_segmentation_from_nuclei(
segmentation_cells=viewer.layers[cell_seg.name],
segmentation_nuclei=viewer.layers[nuclei_seg.name],
boundary_pmaps=viewer.layers[boundary_pmap.name] if boundary_pmap is not None else None,
threshold=(30, 60), # Threshold range as percentages (30% merge, 60% split)
quantile=(10, 90), # Quantile range as percentages (10%-90%)
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

# Validate the corrected segmentation layer properties
corrected_layer = viewer.layers[f"{cell_seg.name}_nuc_fix"]
assert corrected_layer.data.shape == cell_seg.data.shape, "Corrected layer shape is incorrect."
assert corrected_layer.data.dtype == cell_seg.data.dtype, "Corrected layer data type is incorrect."
corrected_layer = viewer.layers[f"{cell_seg.name}_nuc_fixed"]
assert corrected_layer.data.shape == cell_seg.shape, "Corrected layer shape is incorrect."
assert corrected_layer.data.dtype == cell_seg._data.dtype, "Corrected layer data type is incorrect."
12 changes: 3 additions & 9 deletions tests/widgets/test_widget_open_file.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,19 @@
import os

import napari
import numpy as np
import pytest

from plantseg.io.h5 import create_h5
from plantseg.io.voxelsize import VoxelSize
from plantseg.viewer_napari.widgets.io import PathMode, widget_open_file

IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" # set to true in GitHub Actions by default to skip CUDA tests


@pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="GUI tests hangs in GitHub Actions.")
def test_widget_open_file(make_napari_viewer_proxy, path_h5):
def test_widget_open_file(qtbot, make_napari_viewer_proxy, path_h5):
viewer = make_napari_viewer_proxy()
shape = (10, 10, 10)
data = np.random.rand(*shape).astype(np.float32)
voxel_size = VoxelSize(voxels_size=(0.235, 0.15, 0.15))
create_h5(path_h5, data, "raw", voxel_size=voxel_size)
create_h5(path_h5, data, "prob", voxel_size=voxel_size)

count_layers = len(viewer.layers)
widget_open_file(
path_mode=PathMode.FILE.value,
path=path_h5,
Expand All @@ -29,7 +23,7 @@ def test_widget_open_file(make_napari_viewer_proxy, path_h5):
stack_layout="ZYX",
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

assert viewer.layers[0].name == "test_raw"
assert viewer.layers[0].data.shape == shape
Expand Down
41 changes: 21 additions & 20 deletions tests/widgets/test_widget_preprocessing.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
import os

import napari
import numpy as np
import pytest
from magicgui import magicgui
Expand All @@ -10,8 +7,6 @@
from plantseg.io.voxelsize import VoxelSize
from plantseg.viewer_napari.widgets.dataprocessing import RescaleModes, widget_rescaling

IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" # set to true in GitHub Actions by default to skip CUDA tests


def create_layer_name(name: str, suffix: str):
return f"{name}_{suffix}"
Expand Down Expand Up @@ -51,56 +46,58 @@ def widget_add_image(image: PlantSegImage) -> LayerDataTuple:
return image.to_napari_layer_tuple()


@pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="GUI tests hangs in GitHub Actions.")
class TestWidgetRescaling:
def test_rescaling_from_factor(self, make_napari_viewer_proxy, sample_image):
def test_rescaling_from_factor(self, qtbot, make_napari_viewer_proxy, sample_image):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)

factor = 0.5
count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.FROM_FACTOR,
rescaling_factor=(factor, factor, factor),
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

old_layer = viewer.layers[sample_image.name]
new_layer = viewer.layers[sample_image.name + '_rescaled']
np.testing.assert_allclose(new_layer.data.shape, np.multiply(old_layer.data.shape, factor), rtol=1e-5)
np.testing.assert_allclose(np.multiply(new_layer.scale, factor), old_layer.scale, rtol=1e-5)

def test_rescaling_to_layer_voxel_size(self, make_napari_viewer_proxy, sample_image, sample_label):
def test_rescaling_to_layer_voxel_size(self, qtbot, make_napari_viewer_proxy, sample_image, sample_label):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)
widget_add_image(sample_label)

count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.TO_LAYER_VOXEL_SIZE,
reference_layer=viewer.layers[sample_label.name],
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

reference_layer = viewer.layers[sample_label.name]
new_layer = viewer.layers[sample_image.name + '_rescaled']

np.testing.assert_allclose(new_layer.data.shape, reference_layer.data.shape, rtol=1e-5)
np.testing.assert_allclose(new_layer.scale, reference_layer.scale, rtol=1e-5)

def test_rescaling_to_model_voxel_size(self, make_napari_viewer_proxy, sample_image):
def test_rescaling_to_model_voxel_size(self, qtbot, make_napari_viewer_proxy, sample_image):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)

count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.TO_MODEL_VOXEL_SIZE,
reference_model='PlantSeg_3Dnuc_platinum', # voxel size: (0.2837, 0.1268, 0.1268)
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

old_layer = viewer.layers[sample_image.name]
new_layer = viewer.layers[sample_image.name + '_rescaled']
Expand All @@ -116,18 +113,19 @@ def test_rescaling_to_model_voxel_size(self, make_napari_viewer_proxy, sample_im

np.testing.assert_allclose(new_shape * new_scale, old_shape * old_scale, atol=0.1)

def test_rescaling_to_voxel_size(self, make_napari_viewer_proxy, sample_image):
def test_rescaling_to_voxel_size(self, qtbot, make_napari_viewer_proxy, sample_image):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)

expected_scale = (0.5, 0.5, 0.5) # target voxel size
count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.TO_VOXEL_SIZE,
out_voxel_size=expected_scale,
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

old_layer = viewer.layers[sample_image.name]
new_layer = viewer.layers[sample_image.name + '_rescaled']
Expand All @@ -138,18 +136,19 @@ def test_rescaling_to_voxel_size(self, make_napari_viewer_proxy, sample_image):
np.testing.assert_allclose(new_layer.data.shape, expected_shape, rtol=1e-5)
np.testing.assert_allclose(new_layer.scale, expected_scale, rtol=1e-5)

def test_rescaling_to_layer_shape(self, make_napari_viewer_proxy, sample_image, sample_label):
def test_rescaling_to_layer_shape(self, qtbot, make_napari_viewer_proxy, sample_image, sample_label):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)
widget_add_image(sample_label)

count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.TO_LAYER_SHAPE,
reference_layer=viewer.layers[sample_label.name],
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

reference_layer = viewer.layers[sample_label.name]
new_layer = viewer.layers[sample_image.name + '_reshaped']
Expand All @@ -159,18 +158,19 @@ def test_rescaling_to_layer_shape(self, make_napari_viewer_proxy, sample_image,
# In our special case in this test, yes, the scale should be the same
np.testing.assert_allclose(new_layer.scale, reference_layer.scale, rtol=1e-5)

def test_rescaling_set_shape(self, make_napari_viewer_proxy, sample_image):
def test_rescaling_set_shape(self, qtbot, make_napari_viewer_proxy, sample_image):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)

target_shape = (20, 50, 50)
count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.TO_SHAPE,
reference_shape=target_shape,
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

old_layer = viewer.layers[sample_image.name]
new_layer = viewer.layers[sample_image.name + '_reshaped']
Expand All @@ -182,18 +182,19 @@ def test_rescaling_set_shape(self, make_napari_viewer_proxy, sample_image):

np.testing.assert_allclose(new_layer.scale, expected_scale, rtol=1e-5)

def test_rescaling_set_voxel_size(self, make_napari_viewer_proxy, sample_image):
def test_rescaling_set_voxel_size(self, qtbot, make_napari_viewer_proxy, sample_image):
viewer = make_napari_viewer_proxy()
widget_add_image(sample_image)

target_voxel_size = (2.0, 2.0, 2.0)
count_layers = len(viewer.layers)
widget_rescaling(
image=viewer.layers[sample_image.name],
mode=RescaleModes.SET_VOXEL_SIZE,
out_voxel_size=target_voxel_size,
update_other_widgets=False,
)
napari.run()
qtbot.waitUntil(lambda: count_layers < len(viewer.layers), timeout=20000)

new_layer = viewer.layers[sample_image.name + '_set_voxel_size']
np.testing.assert_allclose(new_layer.scale, target_voxel_size, rtol=1e-5)

0 comments on commit eaf05e4

Please sign in to comment.