Skip to content

Commit 8c165af

Browse files
committed
WIP
1 parent be71b0d commit 8c165af

3 files changed

Lines changed: 56 additions & 5 deletions

File tree

naturtag/utils/thumbnails.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,10 @@ def _open_raw_image(path: Path) -> Image.Image:
7272
import rawpy
7373

7474
with rawpy.imread(str(path)) as raw:
75-
thumb = raw.extract_thumb()
75+
try:
76+
thumb = raw.extract_thumb()
77+
except rawpy.LibRawNoThumbnailError as e:
78+
raise ValueError('No embedded thumbnail found in RAW file') from e
7679

7780
if thumb.format == rawpy.ThumbFormat.JPEG:
7881
return Image.open(BytesIO(thumb.data))

test/controllers/test_image_gallery.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
from naturtag.controllers.image_gallery import (
1010
ImageGallery,
11+
MetaThumbnail,
1112
ThumbnailCard,
1213
)
1314

@@ -422,6 +423,35 @@ def test_thumbnail_card__update_metadata_resets_pending_icons(thumbnail_card, mo
422423
assert icons.taxon_icon.pixmap().toImage() == before # reset to secondary
423424

424425

426+
# --- MetaThumbnail ---
427+
428+
429+
def test_meta_thumbnail__get_pixmap_meta__returns_placeholder_on_error(qtbot):
430+
"""get_pixmap_meta returns (None, metadata, error_str) when thumbnail generation fails,
431+
so the caller can show a placeholder image instead of crashing.
432+
"""
433+
from PySide6.QtWidgets import QWidget
434+
435+
parent = QWidget()
436+
qtbot.addWidget(parent)
437+
thumbnail = MetaThumbnail(parent)
438+
qtbot.addWidget(thumbnail)
439+
440+
with (
441+
patch(
442+
'naturtag.controllers.image_gallery.generate_thumbnail',
443+
side_effect=ValueError('No embedded thumbnail found in RAW file'),
444+
),
445+
patch('naturtag.controllers.image_gallery.DerivedMetadata') as mock_meta_cls,
446+
):
447+
mock_meta_cls.return_value = MagicMock()
448+
image, metadata, error = thumbnail.get_pixmap_meta(Path('/test/photo.ORF'))
449+
450+
assert image is None
451+
assert 'No embedded thumbnail found in RAW file' in error
452+
assert metadata is mock_meta_cls.return_value
453+
454+
425455
# --- Error handling ---
426456

427457

test/utils/test_thumbnails.py

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ class MockThumbFormat(Enum):
4646
mock.ThumbFormat = MockThumbFormat
4747
sys.modules['rawpy'] = mock
4848
yield mock
49-
# Clean up
50-
if 'rawpy' in sys.modules:
51-
del sys.modules['rawpy']
49+
# Remove all rawpy submodules so real rawpy can re-import cleanly in later tests
50+
for key in [k for k in sys.modules if k == 'rawpy' or k.startswith('rawpy.')]:
51+
del sys.modules[key]
5252

5353

5454
def _setup_rawpy_mock(mock_rawpy, thumb_data, thumb_format_enum):
@@ -122,6 +122,25 @@ def test_open_raw_image_imread_error(mock_rawpy):
122122
_open_raw_image(Path('test.ORF'))
123123

124124

125+
def test_open_raw_image_no_thumbnail(mock_rawpy):
126+
"""Test that LibRawNoThumbnailError is converted to a friendly ValueError"""
127+
128+
# Define a real exception class so it can be raised as a side_effect
129+
class FakeLibRawNoThumbnailError(Exception):
130+
pass
131+
132+
mock_rawpy.LibRawNoThumbnailError = FakeLibRawNoThumbnailError
133+
134+
mock_raw = MagicMock()
135+
mock_raw.__enter__ = MagicMock(return_value=mock_raw)
136+
mock_raw.__exit__ = MagicMock(return_value=False)
137+
mock_raw.extract_thumb.side_effect = FakeLibRawNoThumbnailError
138+
mock_rawpy.imread.return_value = mock_raw
139+
140+
with pytest.raises(ValueError, match='No embedded thumbnail found in RAW file'):
141+
_open_raw_image(Path('test.ORF'))
142+
143+
125144
def test_open_raw_image_unsupported_format(mock_rawpy):
126145
"""Test that an unsupported ThumbFormat enum value raises ValueError"""
127146
_setup_rawpy_mock(mock_rawpy, b'', mock_rawpy.ThumbFormat.UNKNOWN)
@@ -158,7 +177,6 @@ def test_generate_thumbnail__mock_raw(sample_image, mock_rawpy):
158177
mock_rawpy.imread.assert_called_once_with(str(raw_path))
159178

160179

161-
@pytest.mark.integration
162180
@pytest.mark.parametrize('raw_path', SAMPLE_RAW_FILES)
163181
def test_generate_thumbnail__real_raw(raw_path):
164182
"""RAW file -> rawpy extraction -> PIL -> Qt QImage"""

0 commit comments

Comments
 (0)