pyPhotoAlbum/tests/test_size_ops_mixin.py
Duncan Tourolle 0d698a83b4
Some checks failed
Python CI / test (push) Successful in 55s
Lint / lint (push) Successful in 1m31s
Tests / test (3.10) (push) Failing after 44s
Tests / test (3.11) (push) Failing after 42s
Tests / test (3.9) (push) Failing after 42s
large change to allow project merging
2025-11-23 00:33:42 +01:00

350 lines
11 KiB
Python
Executable File

"""
Tests for SizeOperationsMixin
"""
import pytest
from unittest.mock import Mock, patch
from PyQt6.QtWidgets import QMainWindow
from pyPhotoAlbum.mixins.operations.size_ops import SizeOperationsMixin
from pyPhotoAlbum.models import ImageData
from pyPhotoAlbum.commands import CommandHistory
from pyPhotoAlbum.page_layout import PageLayout
class TestSizeWindow(SizeOperationsMixin, QMainWindow):
"""Test window with size operations mixin"""
def __init__(self):
super().__init__()
self.gl_widget = Mock()
self.gl_widget.selected_elements = set()
self.project = Mock()
self.project.history = CommandHistory()
self._update_view_called = False
self._status_message = None
self._warning_message = None
self._require_selection_count = None
def require_selection(self, min_count=1):
self._require_selection_count = min_count
return len(self.gl_widget.selected_elements) >= min_count
def get_current_page(self):
if hasattr(self, '_current_page'):
return self._current_page
return None
def update_view(self):
self._update_view_called = True
def show_status(self, message, timeout=0):
self._status_message = message
def show_warning(self, title, message):
self._warning_message = message
class TestMakeSameSize:
"""Test make_same_size method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_make_same_size_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
element2 = ImageData(image_path="/test2.jpg", x=150, y=0, width=200, height=150)
window.gl_widget.selected_elements = {element1, element2}
mock_manager.make_same_size.return_value = [
(element1, (0, 0), (100, 100)),
(element2, (150, 0), (200, 150))
]
window.make_same_size()
assert mock_manager.make_same_size.called
assert window._update_view_called
assert "same size" in window._status_message.lower()
def test_make_same_size_insufficient_selection(self, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
window.gl_widget.selected_elements = {element1}
window.make_same_size()
assert window._require_selection_count == 2
assert not window._update_view_called
class TestMakeSameWidth:
"""Test make_same_width method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_make_same_width_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
element2 = ImageData(image_path="/test2.jpg", x=150, y=0, width=200, height=150)
window.gl_widget.selected_elements = {element1, element2}
mock_manager.make_same_width.return_value = [
(element1, (0, 0), (100, 100)),
(element2, (150, 0), (200, 150))
]
window.make_same_width()
assert mock_manager.make_same_width.called
assert window._update_view_called
assert "same width" in window._status_message.lower()
class TestMakeSameHeight:
"""Test make_same_height method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_make_same_height_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
element2 = ImageData(image_path="/test2.jpg", x=150, y=0, width=200, height=150)
window.gl_widget.selected_elements = {element1, element2}
mock_manager.make_same_height.return_value = [
(element1, (0, 0), (100, 100)),
(element2, (150, 0), (200, 150))
]
window.make_same_height()
assert mock_manager.make_same_height.called
assert window._update_view_called
assert "same height" in window._status_message.lower()
class TestFitToWidth:
"""Test fit_to_width method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_fit_to_width_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
# Setup page
page = Mock()
page.layout = Mock()
page.layout.size = (210, 297) # A4
window._current_page = page
mock_manager.fit_to_page_width.return_value = (element, (50, 50), (100, 100))
window.fit_to_width()
assert mock_manager.fit_to_page_width.called
assert window._update_view_called
assert "width" in window._status_message.lower()
def test_fit_to_width_no_page(self, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
window._current_page = None
window.fit_to_width()
assert "page" in window._warning_message.lower()
assert not window._update_view_called
class TestFitToHeight:
"""Test fit_to_height method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_fit_to_height_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
page = Mock()
page.layout = Mock()
page.layout.size = (210, 297)
window._current_page = page
mock_manager.fit_to_page_height.return_value = (element, (50, 50), (100, 100))
window.fit_to_height()
assert mock_manager.fit_to_page_height.called
assert window._update_view_called
assert "height" in window._status_message.lower()
class TestFitToPage:
"""Test fit_to_page method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_fit_to_page_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
page = Mock()
page.layout = Mock()
page.layout.size = (210, 297)
window._current_page = page
mock_manager.fit_to_page.return_value = (element, (50, 50), (100, 100))
window.fit_to_page()
assert mock_manager.fit_to_page.called
assert window._update_view_called
assert "fitted element to page" in window._status_message.lower()
def test_fit_to_page_no_page(self, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
window._current_page = None
window.fit_to_page()
assert "page" in window._warning_message.lower()
assert not window._update_view_called
class TestSizeCommandPattern:
"""Test size operations with command pattern"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_size_operation_creates_command(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element1 = ImageData(image_path="/test1.jpg", x=0, y=0, width=100, height=100)
element2 = ImageData(image_path="/test2.jpg", x=150, y=0, width=200, height=150)
window.gl_widget.selected_elements = {element1, element2}
mock_manager.make_same_size.return_value = [
(element1, (0, 0), (100, 100)),
(element2, (150, 0), (200, 150))
]
assert not window.project.history.can_undo()
window.make_same_size()
assert window.project.history.can_undo()
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_fit_operation_creates_command(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=100, height=100)
window.gl_widget.selected_elements = {element}
page = Mock()
page.layout = Mock()
page.layout.size = (210, 297)
window._current_page = page
mock_manager.fit_to_page.return_value = (element, (50, 50), (100, 100))
assert not window.project.history.can_undo()
window.fit_to_page()
assert window.project.history.can_undo()
class TestExpandImage:
"""Test expand_image method"""
@patch('pyPhotoAlbum.mixins.operations.size_ops.AlignmentManager')
def test_expand_image_success(self, mock_manager, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
# Create an element to expand
element = ImageData(image_path="/test.jpg", x=50, y=50, width=50, height=50)
window.gl_widget.selected_elements = {element}
# Create a mock page with other elements
page = Mock()
page.layout = Mock()
page.layout.size = (210, 297)
page.layout.snapping_system = Mock()
page.layout.snapping_system.grid_spacing = 10.0
# Mock other elements on the page
other_element = ImageData(image_path="/other.jpg", x=150, y=50, width=50, height=50)
page.layout.elements = [element, other_element]
window._current_page = page
# Mock the expand_to_bounds return value
mock_manager.expand_to_bounds.return_value = (element, (50, 50), (50, 50))
window.expand_image()
# Verify that expand_to_bounds was called with correct parameters
assert mock_manager.expand_to_bounds.called
call_args = mock_manager.expand_to_bounds.call_args
assert call_args[0][0] == element # First arg is the element
assert call_args[0][1] == (210, 297) # Second arg is page size
assert other_element in call_args[0][2] # Third arg includes other elements
assert element not in call_args[0][2] # But not the selected element itself
assert call_args[0][3] == 10.0 # Fourth arg is min_gap
assert window._update_view_called
assert "expanded image" in window._status_message.lower()
assert "10" in window._status_message # Gap size mentioned
def test_expand_image_no_page(self, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
element = ImageData(image_path="/test.jpg", x=50, y=50, width=50, height=50)
window.gl_widget.selected_elements = {element}
window._current_page = None
window.expand_image()
assert "page" in window._warning_message.lower()
assert not window._update_view_called
def test_expand_image_insufficient_selection(self, qtbot):
window = TestSizeWindow()
qtbot.addWidget(window)
window.gl_widget.selected_elements = set()
window.expand_image()
assert window._require_selection_count == 1
assert not window._update_view_called