Skip to content
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
5 changes: 3 additions & 2 deletions pixi.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

168 changes: 33 additions & 135 deletions tests/test_bioio_plugin_utils.py
Original file line number Diff line number Diff line change
@@ -1,169 +1,67 @@
"""Tests for _bioio_plugin_utils module."""
"""Tests for _bioio_plugin_utils module.

import logging
This module tests:
- suggest_plugins_for_path: maps file extensions to bioio plugin names
- format_plugin_installation_message: formats installation instructions
- BIOIO_PLUGINS: the plugin metadata registry
"""

import pytest


class TestSuggestPluginsForPath:
"""Test suggest_plugins_for_path function."""

def test_czi_file(self):
"""Test that CZI file suggests bioio-czi."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path

plugins = suggest_plugins_for_path('test.czi')

assert len(plugins) == 1
assert plugins[0] == 'bioio-czi'

def test_lif_file(self):
"""Test that LIF file suggests bioio-lif."""
@pytest.mark.parametrize(
('filename', 'expected_plugins'),
[
('test.czi', ['bioio-czi']),
('test.lif', ['bioio-lif']),
('test.nd2', ['bioio-nd2']),
('test.dv', ['bioio-dv']),
('test.xyz', []), # Unsupported returns empty
],
)
def test_extension_to_plugin_mapping(self, filename, expected_plugins):
"""Test that file extensions map to correct plugin suggestions."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path

plugins = suggest_plugins_for_path('test.lif')
plugins = suggest_plugins_for_path(filename)
assert plugins == expected_plugins

assert len(plugins) == 1
assert plugins[0] == 'bioio-lif'

def test_tiff_file_suggests_all(self):
def test_tiff_suggests_multiple_plugins(self):
"""Test that TIFF files suggest all TIFF-compatible plugins."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path

plugins = suggest_plugins_for_path('test.tiff')

# Should get bioio-ome-tiff, bioio-tifffile, bioio-tiff-glob
# TIFF has multiple compatible readers
assert 'bioio-ome-tiff' in plugins
assert 'bioio-tifffile' in plugins
assert 'bioio-tiff-glob' in plugins

def test_unsupported_extension(self):
"""Test that unsupported extensions return empty list."""
from ndevio._bioio_plugin_utils import suggest_plugins_for_path

plugins = suggest_plugins_for_path('test.xyz')

assert len(plugins) == 0


class TestReaderPluginManager:
"""Test ReaderPluginManager class."""

def test_manager_filters_installed_plugins(self):
"""Test that manager correctly identifies installable plugins."""
from unittest.mock import Mock, patch

from ndevio._plugin_manager import ReaderPluginManager

# Mock feasibility report showing bioio-czi as installed
with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
'bioio-czi': Mock(supported=False),
'ArrayLike': Mock(supported=False),
}

manager = ReaderPluginManager('test.czi')

# bioio-czi should be in installed_plugins
assert 'bioio-czi' in manager.installed_plugins

# bioio-czi should NOT be in installable_plugins (already installed)
assert 'bioio-czi' not in manager.installable_plugins

def test_manager_suggests_uninstalled_plugins(self):
"""Test that manager suggests uninstalled plugins."""
from unittest.mock import Mock, patch

from ndevio._plugin_manager import ReaderPluginManager

# Mock feasibility report with no bioio-lif installed
with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
'bioio-ome-tiff': Mock(supported=False),
'ArrayLike': Mock(supported=False),
}

manager = ReaderPluginManager('test.lif')

# bioio-lif should be in suggested_plugins
assert 'bioio-lif' in manager.suggested_plugins

# bioio-lif should also be in installable_plugins
assert 'bioio-lif' in manager.installable_plugins

def test_manager_excludes_core_plugins_from_installable(self):
"""Test that core plugins are excluded from installable list."""
from unittest.mock import Mock, patch

from ndevio._plugin_manager import ReaderPluginManager

# Mock report showing no plugins installed
with patch('bioio.plugin_feasibility_report') as mock_report:
mock_report.return_value = {
'ArrayLike': Mock(supported=False),
}

manager = ReaderPluginManager('test.tiff')

# Core plugins should not be in installable
installable_plugins = manager.installable_plugins

# These are core plugins, shouldn't need installation
core_plugins = [
'bioio-ome-tiff',
'bioio-imageio',
'bioio-ome-zarr',
'bioio-tifffile',
]
for core in core_plugins:
assert core not in installable_plugins

# bioio-tiff-glob is not core, should be installable
assert 'bioio-tiff-glob' in installable_plugins

def test_get_working_reader_no_path_(self, caplog):
from ndevio._plugin_manager import ReaderPluginManager

manager = ReaderPluginManager() # No path

with caplog.at_level(logging.WARNING):
result = manager.get_working_reader()

assert result is None
assert 'Cannot get working reader without a path' in caplog.text

def test_get_installation_message_no_path_returns_empty(self):
"""Test that get_installation_message returns empty string without path."""
from ndevio._plugin_manager import ReaderPluginManager

manager = ReaderPluginManager() # No path

install_msg = manager.get_installation_message()

assert install_msg == ''


class TestFormatPluginInstallationMessage:
"""Test format_plugin_installation_message function."""

def test_czi_message_basic(self):
"""Test message generation for CZI file."""
def test_message_with_installable_plugins(self):
"""Test message includes plugin name and install command."""
from ndevio._bioio_plugin_utils import (
format_plugin_installation_message,
suggest_plugins_for_path,
)

suggested = suggest_plugins_for_path('test.czi')
message = format_plugin_installation_message(
filename='test.czi',
suggested_plugins=suggested,
installed_plugins=set(),
installable_plugins=suggested,
filename='test.nd2',
suggested_plugins=['bioio-nd2'],
installed_plugins=set(), # Not installed
installable_plugins=['bioio-nd2'],
)

assert 'bioio-czi' in message
assert 'bioio-nd2' in message
assert 'pip install' in message or 'conda install' in message

def test_unsupported_extension_message(self):
"""Test message for completely unsupported extension."""
def test_message_for_unsupported_extension(self):
"""Test message for extension with no known plugins."""
from ndevio._bioio_plugin_utils import (
format_plugin_installation_message,
)
Expand Down
10 changes: 0 additions & 10 deletions tests/test_napari_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,8 @@ def test_reader_supported_formats(
expected_dtype,
expected_has_scale: bool,
expected_num_layers: int,
make_napari_viewer,
) -> None:
"""Test reader with formats that should work with core dependencies."""
make_napari_viewer()

# Resolve filename to filepath
if isinstance(filename, str):
Expand Down Expand Up @@ -233,14 +231,6 @@ def test_napari_get_reader_general_exception(caplog):
assert 'Test exception' in caplog.text


def test_napari_get_reader_png(resources_dir: Path) -> None:
reader = napari_get_reader(
str(resources_dir / PNG_FILE),
)

assert callable(reader)


def test_napari_get_reader_supported_formats_work(resources_dir: Path):
"""Test that supported formats return valid readers."""
# PNG should work (bioio-imageio is core)
Expand Down
62 changes: 0 additions & 62 deletions tests/test_nimage.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
from bioio_base.exceptions import UnsupportedFileFormatError

from ndevio import nImage
from ndevio.nimage import determine_reader_plugin

RGB_TIFF = (
'RGB_bad_metadata.tiff' # has two scenes, with really difficult metadata
Expand Down Expand Up @@ -204,10 +203,6 @@ def test_get_layer_data_tuples_ome_validation_error_logged(
assert caplog.records[0].levelname == 'WARNING'
assert 'Could not parse OME metadata' in caplog.records[0].message
assert 'LatticeLightsheet' in caplog.records[0].message
assert len(caplog.records) == 1
assert caplog.records[0].levelname == 'WARNING'
assert 'Could not parse OME metadata' in caplog.records[0].message
assert 'LatticeLightsheet' in caplog.records[0].message


def test_get_layer_data_tuples_ome_not_implemented_silent(
Expand Down Expand Up @@ -278,63 +273,6 @@ def test_get_layer_data_mosaic_tile_not_in_memory(
assert img.napari_layer_data.shape == (3,)


@pytest.mark.parametrize(
('filename', 'should_work', 'expected_plugin_suggestion'),
[
(LOGO_PNG, True, None), # PNG works with bioio-imageio (core)
(
CELLS3D2CH_OME_TIFF,
True,
None,
), # OME-TIFF works with bioio-ome-tiff (core)
(CZI_FILE, True, None),
(ND2_FILE, False, 'bioio-nd2'), # ND2 needs bioio-nd2
(RGB_TIFF, True, None),
],
)
def test_determine_reader_plugin_behavior(
resources_dir: Path,
filename: str,
should_work: bool | str,
expected_plugin_suggestion: str | None,
):
"""Test determine_reader_plugin with various file formats.

Parameters
----------
filename : str
Test file name
should_work : bool | "maybe"
True = must succeed, False = must fail, "maybe" = can succeed or fail
expected_plugin_suggestion : str | None
If failure expected, the plugin name that should be suggested
"""
if should_work is True:
# Must successfully determine a reader
reader = determine_reader_plugin(resources_dir / filename)
assert reader is not None
elif should_work is False:
# Must fail with helpful error message
with pytest.raises(UnsupportedFileFormatError) as exc_info:
determine_reader_plugin(resources_dir / filename)

error_msg = str(exc_info.value)
assert filename in error_msg
if expected_plugin_suggestion:
assert expected_plugin_suggestion in error_msg
assert 'pip install' in error_msg
else: # "maybe"
# Can succeed or fail; if fails, check for helpful message
try:
reader = determine_reader_plugin(resources_dir / filename)
assert reader is not None
except UnsupportedFileFormatError as e:
error_msg = str(e)
if expected_plugin_suggestion:
assert expected_plugin_suggestion in error_msg
assert 'pip install' in error_msg


@pytest.mark.parametrize(
('filename', 'should_work', 'expected_error_contains'),
[
Expand Down
Loading