This commit is contained in:
2025-10-13 16:56:38 +02:00
parent bf636cbc66
commit 57b55b10a2
21 changed files with 624 additions and 202 deletions

View File

@@ -149,7 +149,7 @@ class TestGenerateTextureImage:
assert result is not None
assert normal_map is None # Normal maps not enabled
mock_pil_image_module.new.assert_called_once()
mock_pil_imagedraw_module.Draw.assert_called_once()
assert mock_pil_imagedraw_module.Draw.call_count >= 1
mock_draw.text.assert_called()
mock_bpy.data.images.new.assert_called_once()
mock_pil_image.transpose.assert_called_once()
@@ -715,6 +715,115 @@ class TestGenerateTextureImage:
assert max(blue_columns) <= expected_x + overlay_source.width
assert max(blue_columns) < width // 2 # should remain on left half
def test_prepend_text_component_shares_baseline(self):
"""Prepend text components should align exactly with the main text baseline."""
Image = pytest.importorskip("PIL.Image")
with patch('src.core.generation_engine.bpy') as mock_bpy:
width = height = 256
mock_blender_image = MagicMock()
mock_blender_image.pixels = [0.0] * (width * height * 4)
mock_bpy.data.images.new.return_value = mock_blender_image
mock_bpy.data.images.__contains__.return_value = False
props = create_mock_props(
text="World",
background_transparent=True,
background_color=(0.0, 0.0, 0.0, 0.0),
text_color=(1.0, 1.0, 1.0, 1.0),
font_size=72,
)
component = MagicMock()
component.enabled = True
component.component_type = 'TEXT'
component.text_content = "Test"
component.placement_mode = 'PREPEND'
component.scale = 1.0
component.rotation = 0.0
component.z_index = 0
component.text_spacing = 5.0
component.image_spacing = 0.0
props.image_overlays = [component]
result, _ = generate_texture_image(props, width, height)
assert result is mock_blender_image
pixels = mock_blender_image.pixels
def collect_bounds(x_start, x_end):
ys = []
for y in range(height):
for x in range(x_start, x_end):
idx = (y * width + x) * 4
alpha = pixels[idx + 3]
if alpha > 0.5:
ys.append(y)
break
if not ys:
return None
return min(ys), max(ys)
left_bounds = collect_bounds(0, width // 2)
right_bounds = collect_bounds(width // 2, width)
assert left_bounds and right_bounds, "Expected both text regions to be present"
left_top, left_bottom = left_bounds
right_top, right_bottom = right_bounds
assert abs(left_top - right_top) <= 1, (
f"Top alignment mismatch: prepend top={left_top}, main top={right_top}"
)
def test_absolute_component_negative_z_draws_behind_text(self, tmp_path):
"""Absolute components with negative z-index should render behind the main text."""
Image = pytest.importorskip("PIL.Image")
with patch('src.core.generation_engine.bpy') as mock_bpy:
width = height = 128
overlay_path = tmp_path / "background_overlay.png"
overlay_source = Image.new("RGBA", (128, 128), (255, 0, 0, 255))
overlay_source.save(overlay_path)
mock_blender_image = MagicMock()
mock_blender_image.pixels = [0.0] * (width * height * 4)
mock_bpy.data.images.new.return_value = mock_blender_image
mock_bpy.data.images.__contains__.return_value = False
props = create_mock_props(
text="T",
background_transparent=False,
background_color=(0.0, 0.0, 0.0, 1.0),
)
overlay = MagicMock()
overlay.enabled = True
overlay.component_type = 'IMAGE'
overlay.image_path = str(overlay_path)
overlay.placement_mode = 'ABSOLUTE'
overlay.x_position = 0.5
overlay.y_position = 0.5
overlay.scale = 1.0
overlay.rotation = 0.0
overlay.z_index = -2
overlay.text_spacing = 0.0
overlay.image_spacing = 0.0
props.image_overlays = [overlay]
result, normal_map = generate_texture_image(props, width, height)
assert result is mock_blender_image
assert normal_map is None
pixels = mock_blender_image.pixels
quads = [pixels[i:i+4] for i in range(0, len(pixels), 4)]
max_brightness = max((r + g + b) / 3.0 for r, g, b, _ in quads)
red_pixels = sum(1 for r, g, b, _ in quads if r >= 0.9 and g <= 0.3 and b <= 0.3)
assert max_brightness > 0.45, "Text should introduce bright pixels above the overlay"
assert red_pixels > 0, "Red overlay pixels should still be present"
def test_text_fitting_enabled(self):
"""Test text fitting integration."""
with patch('PIL.Image') as mock_pil_image_module, \

View File

@@ -72,45 +72,41 @@ class TestVersionDetection:
assert isinstance(limits, dict), "get_version_limits() should return a dictionary"
# Check required keys
required_keys = ["max_texture_size", "max_concurrent_operations"]
required_keys = ["max_concurrent_operations"]
for key in required_keys:
assert key in limits, f"Missing required key '{key}' in version limits"
assert "max_texture_size" not in limits, "Texture size limit should not be enforced"
# Check value types
assert isinstance(limits["max_texture_size"], int), "max_texture_size should be an integer"
assert isinstance(limits["max_concurrent_operations"], int), "max_concurrent_operations should be an integer"
# Check version-specific limits
if VERSION_TYPE == "free":
assert limits["max_texture_size"] == 1024, f"Free version should have 1024px limit, got {limits['max_texture_size']}"
assert limits["max_concurrent_operations"] == 1, f"Free version should have 1 concurrent operation, got {limits['max_concurrent_operations']}"
else:
assert limits["max_texture_size"] == 4096, f"Full version should have 4096px limit, got {limits['max_texture_size']}"
assert limits["max_concurrent_operations"] == 4, f"Full version should have 4 concurrent operations, got {limits['max_concurrent_operations']}"
def test_get_version_limits_full_returns_pro_limits(self):
"""Verify full version returns correct limits for texture size and concurrent operations."""
"""Verify full version returns correct limits for concurrent operations."""
from src.utils.constants import get_version_limits
from unittest.mock import patch
with patch('src.utils.constants.VERSION_TYPE', 'full'):
limits = get_version_limits()
assert limits["max_texture_size"] == 4096, \
f"Expected full version max_texture_size=4096, got {limits['max_texture_size']}"
assert "max_texture_size" not in limits, "Full version should not enforce a texture size limit"
assert limits["max_concurrent_operations"] == 4, \
f"Expected full version max_concurrent_operations=4, got {limits['max_concurrent_operations']}"
def test_get_version_limits_free_returns_restricted_limits(self):
"""Verify free version returns restricted limits for texture size and concurrent operations."""
"""Verify free version returns restricted limits for concurrent operations only."""
from src.utils.constants import get_version_limits
from unittest.mock import patch
with patch('src.utils.constants.VERSION_TYPE', 'free'):
limits = get_version_limits()
assert limits["max_texture_size"] == 1024, \
f"Expected free version max_texture_size=1024, got {limits['max_texture_size']}"
assert "max_texture_size" not in limits, "Free version should not enforce a texture size limit"
assert limits["max_concurrent_operations"] == 1, \
f"Expected free version max_concurrent_operations=1, got {limits['max_concurrent_operations']}"
@@ -278,7 +274,7 @@ class TestVersionDifferentiationScenarios:
# Technical limitations
limits = get_version_limits()
assert limits["max_texture_size"] == 1024, "Should have 1024px texture limit"
assert "max_texture_size" not in limits, "Free version should not enforce a texture size cap"
assert limits["max_concurrent_operations"] == 1, "Should have 1 concurrent operation limit"
def test_full_version_complete_scenario(self):
@@ -296,5 +292,5 @@ class TestVersionDifferentiationScenarios:
# Generous technical limits
limits = get_version_limits()
assert limits["max_texture_size"] == 4096, "Should have 4096px texture limit"
assert "max_texture_size" not in limits, "Full version should not enforce a texture size cap"
assert limits["max_concurrent_operations"] == 4, "Should have 4 concurrent operations limit"