#!/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)