pyPhotoAlbum/test_multiselect.py
Duncan Tourolle 3805b6b913
All checks were successful
Python CI / test (push) Successful in 1m17s
Lint / lint (push) Successful in 1m13s
Tests / test (3.10) (push) Successful in 46s
Tests / test (3.11) (push) Successful in 45s
Tests / test (3.9) (push) Successful in 44s
new alginment options and fix for multiselect
2025-11-10 22:16:43 +01:00

188 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""
Test script to verify multiselect visual feedback functionality
"""
import sys
from unittest.mock import Mock, patch, MagicMock
from PyQt6.QtWidgets import QApplication
from pyPhotoAlbum.gl_widget import GLWidget
from pyPhotoAlbum.models import ImageData
from pyPhotoAlbum.project import Project, Page
from pyPhotoAlbum.page_layout import PageLayout
def test_multiselect_visual_feedback():
"""Test that all selected elements get selection handles drawn"""
print("Testing multiselect visual feedback...")
# Create a project with a page
project = Project("Test Project")
page_layout = PageLayout(width=200, height=200)
page = Page(layout=page_layout, page_number=1)
project.add_page(page)
# Create GL widget
widget = GLWidget()
# Mock the main window to return our project
mock_window = Mock()
mock_window.project = project
widget.window = Mock(return_value=mock_window)
# Create test elements
element1 = ImageData(image_path="test1.jpg", x=10, y=10, width=50, height=50)
element2 = ImageData(image_path="test2.jpg", x=70, y=70, width=50, height=50)
element3 = ImageData(image_path="test3.jpg", x=130, y=130, width=50, height=50)
# Set up page renderer mock for each element
mock_renderer = Mock()
mock_renderer.page_to_screen = Mock(side_effect=lambda x, y: (x, y))
mock_renderer.zoom = 1.0
element1._parent_page = page
element2._parent_page = page
element3._parent_page = page
element1._page_renderer = mock_renderer
element2._page_renderer = mock_renderer
element3._page_renderer = mock_renderer
# Add elements to page
page.layout.add_element(element1)
page.layout.add_element(element2)
page.layout.add_element(element3)
print(f"Created 3 test elements")
# Test 1: Single selection
print("\nTest 1: Single selection")
widget.selected_elements = {element1}
with patch.object(widget, '_draw_selection_handles') as mock_draw:
# Simulate paintGL call (only the relevant part)
for selected_elem in widget.selected_elements:
widget._draw_selection_handles(selected_elem)
assert mock_draw.call_count == 1, f"Expected 1 call, got {mock_draw.call_count}"
assert mock_draw.call_args[0][0] == element1, "Wrong element passed"
print(f"✓ Single selection: _draw_selection_handles called 1 time with element1")
# Test 2: Multiple selection (2 elements)
print("\nTest 2: Multiple selection (2 elements)")
widget.selected_elements = {element1, element2}
with patch.object(widget, '_draw_selection_handles') as mock_draw:
for selected_elem in widget.selected_elements:
widget._draw_selection_handles(selected_elem)
assert mock_draw.call_count == 2, f"Expected 2 calls, got {mock_draw.call_count}"
called_elements = {call[0][0] for call in mock_draw.call_args_list}
assert called_elements == {element1, element2}, f"Wrong elements passed: {called_elements}"
print(f"✓ Multiple selection (2): _draw_selection_handles called 2 times with correct elements")
# Test 3: Multiple selection (3 elements)
print("\nTest 3: Multiple selection (3 elements)")
widget.selected_elements = {element1, element2, element3}
with patch.object(widget, '_draw_selection_handles') as mock_draw:
for selected_elem in widget.selected_elements:
widget._draw_selection_handles(selected_elem)
assert mock_draw.call_count == 3, f"Expected 3 calls, got {mock_draw.call_count}"
called_elements = {call[0][0] for call in mock_draw.call_args_list}
assert called_elements == {element1, element2, element3}, f"Wrong elements passed: {called_elements}"
print(f"✓ Multiple selection (3): _draw_selection_handles called 3 times with correct elements")
# Test 4: No selection
print("\nTest 4: No selection")
widget.selected_elements = set()
with patch.object(widget, '_draw_selection_handles') as mock_draw:
for selected_elem in widget.selected_elements:
widget._draw_selection_handles(selected_elem)
assert mock_draw.call_count == 0, f"Expected 0 calls, got {mock_draw.call_count}"
print(f"✓ No selection: _draw_selection_handles not called")
# Test 5: Verify _draw_selection_handles receives correct element parameter
print("\nTest 5: Verify _draw_selection_handles uses passed element")
widget.selected_elements = {element2}
# Mock OpenGL functions
with patch('pyPhotoAlbum.gl_widget.glColor3f'), \
patch('pyPhotoAlbum.gl_widget.glLineWidth'), \
patch('pyPhotoAlbum.gl_widget.glBegin'), \
patch('pyPhotoAlbum.gl_widget.glEnd'), \
patch('pyPhotoAlbum.gl_widget.glVertex2f'), \
patch('pyPhotoAlbum.gl_widget.glPushMatrix'), \
patch('pyPhotoAlbum.gl_widget.glPopMatrix'), \
patch('pyPhotoAlbum.gl_widget.glTranslatef'), \
patch('pyPhotoAlbum.gl_widget.glRotatef'):
# Call the actual method
widget._draw_selection_handles(element2)
# Verify it used element2's properties
assert element2._page_renderer.page_to_screen.called, "page_to_screen should be called"
print(f"✓ _draw_selection_handles correctly uses the passed element parameter")
print("\n✓ All multiselect visual feedback tests passed!")
def test_regression_old_code_bug():
"""
Regression test: Verify the old bug (only first element gets handles)
would have been caught by this test
"""
print("\nRegression test: Simulating old buggy behavior...")
widget = GLWidget()
# Create mock elements
element1 = Mock()
element2 = Mock()
element3 = Mock()
# Select multiple elements
widget.selected_elements = {element1, element2, element3}
# OLD BUGGY CODE (what we fixed):
# if self.selected_element: # This only returns first element!
# self._draw_selection_handles()
# Simulate old behavior
call_count_old = 0
if widget.selected_element: # This property returns only first element
call_count_old = 1
# NEW CORRECT CODE:
# for selected_elem in self.selected_elements:
# self._draw_selection_handles(selected_elem)
# Simulate new behavior
call_count_new = 0
for selected_elem in widget.selected_elements:
call_count_new += 1
print(f"Old buggy code: would call _draw_selection_handles {call_count_old} time(s)")
print(f"New fixed code: calls _draw_selection_handles {call_count_new} time(s)")
assert call_count_old == 1, "Old code should only handle 1 element"
assert call_count_new == 3, "New code should handle all 3 elements"
print("✓ Regression test confirms the bug would have been caught!")
if __name__ == "__main__":
# Initialize Qt application (needed for PyQt6 widgets)
app = QApplication(sys.argv)
test_multiselect_visual_feedback()
test_regression_old_code_bug()
print("\n" + "="*60)
print("All tests completed successfully!")
print("="*60)