188 lines
7.0 KiB
Python
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)
|