Added loading funcs for images
This commit is contained in:
@@ -5,6 +5,12 @@ Tests the core abstract block classes that form the foundation of the document m
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import os
|
||||
import tempfile
|
||||
import shutil
|
||||
import threading
|
||||
import time
|
||||
from PIL import Image as PILImage
|
||||
from pyWebLayout.abstract.block import (
|
||||
Block, BlockType, Paragraph, Heading, HeadingLevel, Quote, CodeBlock,
|
||||
HList, ListStyle, ListItem, Table, TableRow, TableCell,
|
||||
@@ -13,6 +19,13 @@ from pyWebLayout.abstract.block import (
|
||||
from pyWebLayout.abstract.inline import Word, LineBreak
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
# Flask server for testing URL functionality
|
||||
try:
|
||||
from flask import Flask, send_file
|
||||
FLASK_AVAILABLE = True
|
||||
except ImportError:
|
||||
FLASK_AVAILABLE = False
|
||||
|
||||
|
||||
class TestBlockElements(unittest.TestCase):
|
||||
"""Test cases for basic block elements."""
|
||||
@@ -271,5 +284,289 @@ class TestBlockElements(unittest.TestCase):
|
||||
self.assertIsNone(br.parent)
|
||||
|
||||
|
||||
class TestImagePIL(unittest.TestCase):
|
||||
"""Test cases for Image class with PIL functionality."""
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
"""Set up temporary directory and test images."""
|
||||
cls.temp_dir = tempfile.mkdtemp()
|
||||
cls.sample_image_path = "tests/data/sample_image.jpg"
|
||||
|
||||
# Create test images in different formats
|
||||
cls._create_test_images()
|
||||
|
||||
# Start Flask server for URL testing if Flask is available
|
||||
if FLASK_AVAILABLE:
|
||||
cls._start_flask_server()
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
"""Clean up temporary directory and stop Flask server."""
|
||||
shutil.rmtree(cls.temp_dir, ignore_errors=True)
|
||||
|
||||
if FLASK_AVAILABLE and hasattr(cls, 'flask_thread'):
|
||||
cls.flask_server_running = False
|
||||
cls.flask_thread.join(timeout=2)
|
||||
|
||||
@classmethod
|
||||
def _create_test_images(cls):
|
||||
"""Create test images in different formats."""
|
||||
# Load the sample image
|
||||
if os.path.exists(cls.sample_image_path):
|
||||
with PILImage.open(cls.sample_image_path) as img:
|
||||
cls.original_size = img.size
|
||||
|
||||
# Save in different formats
|
||||
cls.jpg_path = os.path.join(cls.temp_dir, "test.jpg")
|
||||
cls.png_path = os.path.join(cls.temp_dir, "test.png")
|
||||
cls.bmp_path = os.path.join(cls.temp_dir, "test.bmp")
|
||||
cls.gif_path = os.path.join(cls.temp_dir, "test.gif")
|
||||
|
||||
img.save(cls.jpg_path, "JPEG")
|
||||
img.save(cls.png_path, "PNG")
|
||||
img.save(cls.bmp_path, "BMP")
|
||||
|
||||
# Convert to RGB for GIF (GIF doesn't support transparency from RGBA)
|
||||
rgb_img = img.convert("RGB")
|
||||
rgb_img.save(cls.gif_path, "GIF")
|
||||
else:
|
||||
# Create a simple test image if sample doesn't exist
|
||||
cls.original_size = (100, 100)
|
||||
test_img = PILImage.new("RGB", cls.original_size, (255, 0, 0))
|
||||
|
||||
cls.jpg_path = os.path.join(cls.temp_dir, "test.jpg")
|
||||
cls.png_path = os.path.join(cls.temp_dir, "test.png")
|
||||
cls.bmp_path = os.path.join(cls.temp_dir, "test.bmp")
|
||||
cls.gif_path = os.path.join(cls.temp_dir, "test.gif")
|
||||
|
||||
test_img.save(cls.jpg_path, "JPEG")
|
||||
test_img.save(cls.png_path, "PNG")
|
||||
test_img.save(cls.bmp_path, "BMP")
|
||||
test_img.save(cls.gif_path, "GIF")
|
||||
|
||||
@classmethod
|
||||
def _start_flask_server(cls):
|
||||
"""Start a Flask server for URL testing."""
|
||||
cls.flask_app = Flask(__name__)
|
||||
cls.flask_port = 5555 # Use a specific port for testing
|
||||
cls.flask_server_running = True
|
||||
|
||||
@cls.flask_app.route('/test.jpg')
|
||||
def serve_test_image():
|
||||
return send_file(cls.jpg_path, mimetype='image/jpeg')
|
||||
|
||||
def run_flask():
|
||||
cls.flask_app.run(host='127.0.0.1', port=cls.flask_port, debug=False,
|
||||
use_reloader=False, threaded=True)
|
||||
|
||||
cls.flask_thread = threading.Thread(target=run_flask, daemon=True)
|
||||
cls.flask_thread.start()
|
||||
|
||||
# Wait for server to start
|
||||
time.sleep(1)
|
||||
|
||||
def test_image_url_detection(self):
|
||||
"""Test URL detection functionality."""
|
||||
img = Image()
|
||||
|
||||
# Test URL detection
|
||||
self.assertTrue(img._is_url("http://example.com/image.jpg"))
|
||||
self.assertTrue(img._is_url("https://example.com/image.png"))
|
||||
self.assertTrue(img._is_url("ftp://example.com/image.gif"))
|
||||
|
||||
# Test non-URL detection
|
||||
self.assertFalse(img._is_url("image.jpg"))
|
||||
self.assertFalse(img._is_url("/path/to/image.png"))
|
||||
self.assertFalse(img._is_url("../relative/path.gif"))
|
||||
self.assertFalse(img._is_url(""))
|
||||
|
||||
def test_load_local_image_jpg(self):
|
||||
"""Test loading local JPG image."""
|
||||
img = Image(self.jpg_path)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertEqual(file_path, self.jpg_path)
|
||||
self.assertEqual(pil_img.size, self.original_size)
|
||||
self.assertEqual(img.width, self.original_size[0])
|
||||
self.assertEqual(img.height, self.original_size[1])
|
||||
|
||||
def test_load_local_image_png(self):
|
||||
"""Test loading local PNG image."""
|
||||
img = Image(self.png_path)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertEqual(file_path, self.png_path)
|
||||
self.assertEqual(pil_img.size, self.original_size)
|
||||
|
||||
def test_load_local_image_bmp(self):
|
||||
"""Test loading local BMP image."""
|
||||
img = Image(self.bmp_path)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertEqual(file_path, self.bmp_path)
|
||||
self.assertEqual(pil_img.size, self.original_size)
|
||||
|
||||
def test_load_local_image_gif(self):
|
||||
"""Test loading local GIF image."""
|
||||
img = Image(self.gif_path)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertEqual(file_path, self.gif_path)
|
||||
self.assertEqual(pil_img.size, self.original_size)
|
||||
|
||||
def test_load_nonexistent_image(self):
|
||||
"""Test loading non-existent image."""
|
||||
img = Image("nonexistent.jpg")
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNone(pil_img)
|
||||
self.assertIsNone(file_path)
|
||||
|
||||
def test_load_empty_source(self):
|
||||
"""Test loading with empty source."""
|
||||
img = Image("")
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNone(pil_img)
|
||||
self.assertIsNone(file_path)
|
||||
|
||||
def test_auto_update_dimensions(self):
|
||||
"""Test automatic dimension updating."""
|
||||
img = Image(self.jpg_path, width=50, height=50) # Wrong initial dimensions
|
||||
|
||||
# Test with auto-update enabled (default)
|
||||
file_path, pil_img = img.load_image_data(auto_update_dimensions=True)
|
||||
|
||||
self.assertEqual(img.width, self.original_size[0])
|
||||
self.assertEqual(img.height, self.original_size[1])
|
||||
|
||||
def test_no_auto_update_dimensions(self):
|
||||
"""Test loading without automatic dimension updating."""
|
||||
original_width, original_height = 50, 50
|
||||
img = Image(self.jpg_path, width=original_width, height=original_height)
|
||||
|
||||
# Test with auto-update disabled
|
||||
file_path, pil_img = img.load_image_data(auto_update_dimensions=False)
|
||||
|
||||
self.assertEqual(img.width, original_width) # Should remain unchanged
|
||||
self.assertEqual(img.height, original_height) # Should remain unchanged
|
||||
|
||||
def test_get_image_info(self):
|
||||
"""Test getting detailed image information."""
|
||||
img = Image(self.jpg_path)
|
||||
|
||||
info = img.get_image_info()
|
||||
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertIn('format', info)
|
||||
self.assertIn('mode', info)
|
||||
self.assertIn('size', info)
|
||||
self.assertIn('width', info)
|
||||
self.assertIn('height', info)
|
||||
|
||||
self.assertEqual(info['size'], self.original_size)
|
||||
self.assertEqual(info['width'], self.original_size[0])
|
||||
self.assertEqual(info['height'], self.original_size[1])
|
||||
|
||||
def test_get_image_info_different_formats(self):
|
||||
"""Test getting image info for different formats."""
|
||||
formats_and_paths = [
|
||||
('JPEG', self.jpg_path),
|
||||
('PNG', self.png_path),
|
||||
('BMP', self.bmp_path),
|
||||
('GIF', self.gif_path),
|
||||
]
|
||||
|
||||
for expected_format, path in formats_and_paths:
|
||||
with self.subTest(format=expected_format):
|
||||
img = Image(path)
|
||||
info = img.get_image_info()
|
||||
|
||||
self.assertEqual(info['format'], expected_format)
|
||||
self.assertEqual(info['size'], self.original_size)
|
||||
|
||||
def test_get_image_info_nonexistent(self):
|
||||
"""Test getting image info for non-existent image."""
|
||||
img = Image("nonexistent.jpg")
|
||||
|
||||
info = img.get_image_info()
|
||||
|
||||
self.assertEqual(info, {})
|
||||
|
||||
@unittest.skipUnless(FLASK_AVAILABLE, "Flask not available for URL testing")
|
||||
def test_load_image_from_url(self):
|
||||
"""Test loading image from URL."""
|
||||
url = f"http://127.0.0.1:{self.flask_port}/test.jpg"
|
||||
img = Image(url)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertIsNotNone(file_path)
|
||||
self.assertTrue(file_path.endswith('.tmp')) # Should be a temp file
|
||||
self.assertEqual(pil_img.size, self.original_size)
|
||||
|
||||
# Check that dimensions were updated
|
||||
self.assertEqual(img.width, self.original_size[0])
|
||||
self.assertEqual(img.height, self.original_size[1])
|
||||
|
||||
@unittest.skipUnless(FLASK_AVAILABLE, "Flask not available for URL testing")
|
||||
def test_get_image_info_from_url(self):
|
||||
"""Test getting image info from URL."""
|
||||
url = f"http://127.0.0.1:{self.flask_port}/test.jpg"
|
||||
img = Image(url)
|
||||
|
||||
info = img.get_image_info()
|
||||
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertEqual(info['format'], 'JPEG')
|
||||
self.assertEqual(info['size'], self.original_size)
|
||||
|
||||
def test_load_invalid_url(self):
|
||||
"""Test loading from invalid URL."""
|
||||
img = Image("http://nonexistent.domain/image.jpg")
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNone(pil_img)
|
||||
self.assertIsNone(file_path)
|
||||
|
||||
def test_multiple_loads_cleanup(self):
|
||||
"""Test that multiple loads don't leave temp files."""
|
||||
img = Image(self.jpg_path)
|
||||
|
||||
# Load multiple times
|
||||
for _ in range(3):
|
||||
file_path, pil_img = img.load_image_data()
|
||||
self.assertIsNotNone(pil_img)
|
||||
|
||||
def test_original_sample_image(self):
|
||||
"""Test loading the original sample image if it exists."""
|
||||
if os.path.exists(self.sample_image_path):
|
||||
img = Image(self.sample_image_path)
|
||||
|
||||
file_path, pil_img = img.load_image_data()
|
||||
|
||||
self.assertIsNotNone(pil_img)
|
||||
self.assertEqual(file_path, self.sample_image_path)
|
||||
|
||||
# Test that we can get image info
|
||||
info = img.get_image_info()
|
||||
self.assertIsInstance(info, dict)
|
||||
self.assertIn('format', info)
|
||||
self.assertIn('size', info)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user