Skip to main content
Glama
test_gimp_bitmap_enhanced.py9.22 kB
#!/usr/bin/env python3 """ Unit tests for GIMP MCP enhanced bitmap functionality Tests edge cases and parameter validation for get_image_bitmap() """ import unittest from unittest.mock import Mock, patch, MagicMock import sys import os import importlib.util # Add the project directory to the path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) # Import test fixtures from test_fixtures import setup_gimp_mocks, cleanup_gimp_mocks, create_mock_plugin, setup_region_mock, setup_scaled_mock class TestGimpBitmapEnhanced(unittest.TestCase): """Test cases for enhanced GIMP bitmap functionality.""" def setUp(self): """Set up test fixtures.""" # Set up GIMP mocks setup_gimp_mocks() # Create plugin instance with mocked dependencies self.plugin = create_mock_plugin() def tearDown(self): """Clean up test fixtures.""" cleanup_gimp_mocks() def test_region_validation_valid_params(self): """Test region parameter validation with valid parameters.""" # Valid region parameters valid_region = { "origin_x": 100, "origin_y": 100, "width": 400, "height": 300, "max_width": 200, "max_height": 150 } # Test with valid parameters - should not raise validation exception try: result = self.plugin._get_current_image_bitmap({"region": valid_region}) # In test environment, this will likely fail due to no real GIMP connection # But it should not fail due to parameter validation except Exception as e: # Should not contain parameter validation errors error_msg = str(e).lower() self.assertNotIn("parameter", error_msg) self.assertNotIn("must be", error_msg) def test_region_validation_invalid_types(self): """Test region parameter validation with invalid types.""" # Test invalid type for origin_x invalid_region = { "origin_x": "100", # String instead of int "origin_y": 100, "width": 400, "height": 300 } result = self.plugin._get_current_image_bitmap({"region": invalid_region}) self.assertEqual(result["status"], "error") self.assertIn("must be of type int", result["error"]) def test_region_validation_negative_values(self): """Test region parameter validation with negative values.""" # Test negative origin_x invalid_region = { "origin_x": -10, "origin_y": 100, "width": 400, "height": 300 } result = self.plugin._get_current_image_bitmap({"region": invalid_region}) self.assertEqual(result["status"], "error") self.assertIn("must be non-negative", result["error"]) def test_region_bounds_validation(self): """Test region bounds validation against image dimensions.""" # Test region that exceeds image bounds (mock image is 1920x1080) invalid_region = { "origin_x": 1800, "origin_y": 100, "width": 400, # 1800 + 400 = 2200 > 1920 (image width) "height": 300 } result = self.plugin._get_current_image_bitmap({"region": invalid_region}) self.assertEqual(result["status"], "error") self.assertIn("Region bounds invalid", result["error"]) def test_scaling_ratio_warning(self): """Test warning for large scaling operations.""" # Test large scaling operation (should trigger warning) large_scaling_params = { "max_width": 8000, # 4x larger than 1920 "max_height": 6000 # ~5.5x larger than 1080 } # This should trigger a scaling ratio warning with patch('builtins.print') as mock_print: result = self.plugin._get_current_image_bitmap(large_scaling_params) # Check that warning was printed about large scaling warning_calls = [call for call in mock_print.call_args_list if 'scaling operation detected' in str(call)] self.assertTrue(len(warning_calls) > 0) def test_center_inside_scaling_math(self): """Test center-inside scaling mathematical correctness.""" # Test landscape image (wider than tall) fitting in square bounds image_width, image_height = 1920, 1080 # 16:9 aspect ratio max_width, max_height = 800, 800 # Square bounds aspect_ratio = image_width / image_height # 1.777... max_aspect_ratio = max_width / max_height # 1.0 # Width should be limiting factor (aspect_ratio > max_aspect_ratio) expected_width = max_width # 800 expected_height = int(max_width / aspect_ratio) # 450 self.assertEqual(expected_width, 800) self.assertEqual(expected_height, 450) # Test portrait image fitting in landscape bounds image_width, image_height = 600, 800 # 3:4 aspect ratio max_width, max_height = 1000, 600 # Landscape bounds aspect_ratio = image_width / image_height # 0.75 max_aspect_ratio = max_width / max_height # 1.667 # Height should be limiting factor (aspect_ratio < max_aspect_ratio) expected_height = max_height # 600 expected_width = int(max_height * aspect_ratio) # 450 self.assertEqual(expected_width, 450) self.assertEqual(expected_height, 600) def test_no_images_open_error(self): """Test error handling when no images are open in GIMP.""" # Mock Gimp.get_images() to return empty list with patch('gi.repository.Gimp.get_images', return_value=[]): result = self.plugin._get_current_image_bitmap({}) self.assertEqual(result["status"], "error") self.assertIn("No images", result["error"]) def test_cleanup_on_scaling_failure(self): """Test proper cleanup when scaling fails.""" # Mock scaling to fail - the cleanup happens on the duplicate image, not the original mock_image = MagicMock() mock_image.get_width.return_value = 1920 mock_image.get_height.return_value = 1080 # Mock the duplicate image to fail on scaling mock_duplicate = MagicMock() mock_duplicate.scale.side_effect = RuntimeError("Scaling failed") mock_duplicate.get_width.return_value = 1920 mock_duplicate.get_height.return_value = 1080 mock_image.duplicate.return_value = mock_duplicate with patch('gi.repository.Gimp.get_images', return_value=[mock_image]): result = self.plugin._get_current_image_bitmap({"max_width": 100, "max_height": 100}) self.assertEqual(result["status"], "error") # Should have attempted cleanup on the duplicate image self.assertIn("Failed to scale image", result["error"]) # Verify cleanup was called on the duplicate image mock_duplicate.delete.assert_called_once() def test_mixed_region_and_global_scaling_params(self): """Test behavior with both region and global scaling parameters.""" # Both global and region scaling specified mixed_region = { "origin_x": 100, "origin_y": 100, "width": 400, "height": 300, "max_width": 200, # Region scaling (should take precedence) "max_height": 150 } # Mock the necessary GIMP operations with patch('tempfile.mkstemp') as mock_mkstemp: with patch('os.close'): with patch('os.path.exists', return_value=True): with patch('os.unlink'): with patch('base64.b64encode', return_value=b'mock_data'): mock_mkstemp.return_value = (1, '/tmp/test.png') # Use helper functions to reduce duplication mock_region_image = setup_region_mock(400, 300) mock_scaled_image = setup_scaled_mock(200, 150) mock_region_image.duplicate.return_value = mock_scaled_image with patch('builtins.open', create=True) as mock_open: mock_open.return_value.__enter__.return_value.read.return_value = b'mock_png_data' result = self.plugin._get_current_image_bitmap({"region": mixed_region}) # Should succeed and use region scaling # Note: In test environment, this may fail gracefully # The important thing is no parameter validation errors if __name__ == '__main__': # Run the tests unittest.main(verbosity=2)

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/maorcc/gimp-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server