update
This commit is contained in:
BIN
Archive.zip
BIN
Archive.zip
Binary file not shown.
1707
__init__.py
1707
__init__.py
File diff suppressed because it is too large
Load Diff
558
test_positioning_system.py
Normal file
558
test_positioning_system.py
Normal file
@@ -0,0 +1,558 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Comprehensive test suite for the Text Texture Generator's enhanced positioning system.
|
||||||
|
This script validates the positioning calculations and identifies potential issues
|
||||||
|
without requiring Blender to be installed.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import traceback
|
||||||
|
from typing import Dict, Tuple, List, Any
|
||||||
|
|
||||||
|
# Mock Blender properties for testing
|
||||||
|
class MockProps:
|
||||||
|
"""Mock properties class to simulate Blender addon properties"""
|
||||||
|
def __init__(self):
|
||||||
|
# Enhanced positioning properties
|
||||||
|
self.anchor_point = 'MIDDLE_CENTER'
|
||||||
|
self.position_mode = 'PRESET'
|
||||||
|
self.margin_x = 0.0
|
||||||
|
self.margin_y = 0.0
|
||||||
|
self.margins_linked = False
|
||||||
|
self.offset_x = 0
|
||||||
|
self.offset_y = 0
|
||||||
|
self.manual_position_x = 50.0
|
||||||
|
self.manual_position_y = 50.0
|
||||||
|
self.manual_position_unit = 'PERCENTAGE'
|
||||||
|
self.constrain_to_canvas = True
|
||||||
|
|
||||||
|
# Legacy properties for backward compatibility testing
|
||||||
|
self.text_align = 'center'
|
||||||
|
self.padding = 20
|
||||||
|
|
||||||
|
# Copy of the enhanced positioning function from the addon
|
||||||
|
def calculate_text_position(props, canvas_width, canvas_height, text_width, text_height):
|
||||||
|
"""Calculate text position based on enhanced positioning properties"""
|
||||||
|
|
||||||
|
if props.position_mode == 'MANUAL':
|
||||||
|
# Manual positioning mode
|
||||||
|
if props.manual_position_unit == 'PERCENTAGE':
|
||||||
|
x = int((props.manual_position_x / 100.0) * canvas_width)
|
||||||
|
y = int((props.manual_position_y / 100.0) * canvas_height)
|
||||||
|
else: # PIXELS
|
||||||
|
x = int(props.manual_position_x)
|
||||||
|
y = int(props.manual_position_y)
|
||||||
|
else:
|
||||||
|
# Preset anchor-based positioning
|
||||||
|
margin_x_px = int((props.margin_x / 100.0) * canvas_width)
|
||||||
|
margin_y_px = int((props.margin_y / 100.0) * canvas_height)
|
||||||
|
|
||||||
|
# Calculate base position based on anchor point
|
||||||
|
if 'LEFT' in props.anchor_point:
|
||||||
|
base_x = margin_x_px
|
||||||
|
elif 'RIGHT' in props.anchor_point:
|
||||||
|
base_x = canvas_width - text_width - margin_x_px
|
||||||
|
else: # CENTER
|
||||||
|
base_x = (canvas_width - text_width) // 2
|
||||||
|
|
||||||
|
if 'TOP' in props.anchor_point:
|
||||||
|
base_y = margin_y_px
|
||||||
|
elif 'BOTTOM' in props.anchor_point:
|
||||||
|
base_y = canvas_height - text_height - margin_y_px
|
||||||
|
else: # MIDDLE
|
||||||
|
base_y = (canvas_height - text_height) // 2
|
||||||
|
|
||||||
|
# Apply fine-tuning offsets
|
||||||
|
x = base_x + props.offset_x
|
||||||
|
y = base_y + props.offset_y
|
||||||
|
|
||||||
|
# Apply canvas constraints if enabled
|
||||||
|
if props.constrain_to_canvas:
|
||||||
|
x = max(0, min(x, canvas_width - text_width))
|
||||||
|
y = max(0, min(y, canvas_height - text_height))
|
||||||
|
|
||||||
|
return x, y
|
||||||
|
|
||||||
|
def sync_margin_values(props, changed_margin):
|
||||||
|
"""Synchronize margin values when linked"""
|
||||||
|
if props.margins_linked:
|
||||||
|
if changed_margin == 'x':
|
||||||
|
props.margin_y = props.margin_x
|
||||||
|
elif changed_margin == 'y':
|
||||||
|
props.margin_x = props.margin_y
|
||||||
|
|
||||||
|
def get_anchor_point_matrix():
|
||||||
|
"""Return 3x3 matrix of anchor point identifiers for UI layout"""
|
||||||
|
return [
|
||||||
|
['TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT'],
|
||||||
|
['MIDDLE_LEFT', 'MIDDLE_CENTER', 'MIDDLE_RIGHT'],
|
||||||
|
['BOTTOM_LEFT', 'BOTTOM_CENTER', 'BOTTOM_RIGHT']
|
||||||
|
]
|
||||||
|
|
||||||
|
class PositioningTestSuite:
|
||||||
|
"""Comprehensive test suite for positioning system"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.test_results = []
|
||||||
|
self.issues_found = []
|
||||||
|
self.canvas_width = 1024
|
||||||
|
self.canvas_height = 1024
|
||||||
|
self.text_width = 200
|
||||||
|
self.text_height = 50
|
||||||
|
|
||||||
|
def log_test(self, test_name: str, passed: bool, details: str = "", expected=None, actual=None):
|
||||||
|
"""Log test result"""
|
||||||
|
result = {
|
||||||
|
'test': test_name,
|
||||||
|
'passed': passed,
|
||||||
|
'details': details,
|
||||||
|
'expected': expected,
|
||||||
|
'actual': actual
|
||||||
|
}
|
||||||
|
self.test_results.append(result)
|
||||||
|
|
||||||
|
if not passed:
|
||||||
|
self.issues_found.append(f"❌ {test_name}: {details}")
|
||||||
|
if expected is not None and actual is not None:
|
||||||
|
self.issues_found.append(f" Expected: {expected}, Got: {actual}")
|
||||||
|
else:
|
||||||
|
print(f"✅ {test_name}")
|
||||||
|
|
||||||
|
def test_anchor_point_calculations(self):
|
||||||
|
"""Test all 9 anchor point calculations"""
|
||||||
|
print("\n=== Testing Anchor Point Calculations ===")
|
||||||
|
|
||||||
|
anchor_matrix = get_anchor_point_matrix()
|
||||||
|
expected_positions = {
|
||||||
|
'TOP_LEFT': (0, 0),
|
||||||
|
'TOP_CENTER': (412, 0), # (1024-200)/2 = 412
|
||||||
|
'TOP_RIGHT': (824, 0), # 1024-200 = 824
|
||||||
|
'MIDDLE_LEFT': (0, 487), # (1024-50)/2 = 487
|
||||||
|
'MIDDLE_CENTER': (412, 487),
|
||||||
|
'MIDDLE_RIGHT': (824, 487),
|
||||||
|
'BOTTOM_LEFT': (0, 974), # 1024-50 = 974
|
||||||
|
'BOTTOM_CENTER': (412, 974),
|
||||||
|
'BOTTOM_RIGHT': (824, 974)
|
||||||
|
}
|
||||||
|
|
||||||
|
for row in anchor_matrix:
|
||||||
|
for anchor in row:
|
||||||
|
props = MockProps()
|
||||||
|
props.anchor_point = anchor
|
||||||
|
props.position_mode = 'PRESET'
|
||||||
|
props.margin_x = 0.0
|
||||||
|
props.margin_y = 0.0
|
||||||
|
props.offset_x = 0
|
||||||
|
props.offset_y = 0
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
expected = expected_positions[anchor]
|
||||||
|
|
||||||
|
if (x, y) == expected:
|
||||||
|
self.log_test(f"Anchor {anchor}", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test(f"Anchor {anchor}", False,
|
||||||
|
f"Incorrect position calculation", expected, (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test(f"Anchor {anchor}", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def test_margin_calculations(self):
|
||||||
|
"""Test margin calculations with various percentages"""
|
||||||
|
print("\n=== Testing Margin Calculations ===")
|
||||||
|
|
||||||
|
test_cases = [
|
||||||
|
(10.0, 10.0), # 10% margins
|
||||||
|
(25.0, 25.0), # 25% margins
|
||||||
|
(50.0, 50.0), # 50% margins (maximum)
|
||||||
|
(0.0, 0.0), # No margins
|
||||||
|
]
|
||||||
|
|
||||||
|
for margin_x, margin_y in test_cases:
|
||||||
|
props = MockProps()
|
||||||
|
props.anchor_point = 'TOP_LEFT'
|
||||||
|
props.position_mode = 'PRESET'
|
||||||
|
props.margin_x = margin_x
|
||||||
|
props.margin_y = margin_y
|
||||||
|
props.offset_x = 0
|
||||||
|
props.offset_y = 0
|
||||||
|
|
||||||
|
expected_x = int((margin_x / 100.0) * self.canvas_width)
|
||||||
|
expected_y = int((margin_y / 100.0) * self.canvas_height)
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test(f"Margins {margin_x}%/{margin_y}%", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test(f"Margins {margin_x}%/{margin_y}%", False,
|
||||||
|
f"Incorrect margin calculation", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test(f"Margins {margin_x}%/{margin_y}%", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def test_manual_positioning(self):
|
||||||
|
"""Test manual positioning mode with percentage and pixel units"""
|
||||||
|
print("\n=== Testing Manual Positioning ===")
|
||||||
|
|
||||||
|
# Test percentage mode
|
||||||
|
props = MockProps()
|
||||||
|
props.position_mode = 'MANUAL'
|
||||||
|
props.manual_position_unit = 'PERCENTAGE'
|
||||||
|
props.manual_position_x = 25.0 # 25%
|
||||||
|
props.manual_position_y = 75.0 # 75%
|
||||||
|
|
||||||
|
expected_x = int((25.0 / 100.0) * self.canvas_width) # 256
|
||||||
|
expected_y = int((75.0 / 100.0) * self.canvas_height) # 768
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Manual Percentage Mode", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Manual Percentage Mode", False,
|
||||||
|
f"Incorrect calculation", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Manual Percentage Mode", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
# Test pixel mode
|
||||||
|
props.manual_position_unit = 'PIXELS'
|
||||||
|
props.manual_position_x = 100.0
|
||||||
|
props.manual_position_y = 200.0
|
||||||
|
|
||||||
|
expected_x = 100
|
||||||
|
expected_y = 200
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Manual Pixel Mode", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Manual Pixel Mode", False,
|
||||||
|
f"Incorrect calculation", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Manual Pixel Mode", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def test_canvas_constraints(self):
|
||||||
|
"""Test canvas constraint functionality"""
|
||||||
|
print("\n=== Testing Canvas Constraints ===")
|
||||||
|
|
||||||
|
# Test position that would go out of bounds
|
||||||
|
props = MockProps()
|
||||||
|
props.position_mode = 'MANUAL'
|
||||||
|
props.manual_position_unit = 'PIXELS'
|
||||||
|
props.manual_position_x = 1000.0 # Would put text partially outside (text_width=200)
|
||||||
|
props.manual_position_y = 1000.0 # Would put text partially outside (text_height=50)
|
||||||
|
props.constrain_to_canvas = True
|
||||||
|
|
||||||
|
expected_x = self.canvas_width - self.text_width # 824
|
||||||
|
expected_y = self.canvas_height - self.text_height # 974
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Canvas Constraints (constrained)", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Canvas Constraints (constrained)", False,
|
||||||
|
f"Constraint failed", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Canvas Constraints (constrained)", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
# Test with constraints disabled
|
||||||
|
props.constrain_to_canvas = False
|
||||||
|
expected_x = 1000 # Should not be constrained
|
||||||
|
expected_y = 1000
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Canvas Constraints (unconstrained)", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Canvas Constraints (unconstrained)", False,
|
||||||
|
f"Should not be constrained", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Canvas Constraints (unconstrained)", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def test_offset_application(self):
|
||||||
|
"""Test pixel offset fine-tuning"""
|
||||||
|
print("\n=== Testing Pixel Offset Application ===")
|
||||||
|
|
||||||
|
props = MockProps()
|
||||||
|
props.anchor_point = 'MIDDLE_CENTER'
|
||||||
|
props.position_mode = 'PRESET'
|
||||||
|
props.margin_x = 0.0
|
||||||
|
props.margin_y = 0.0
|
||||||
|
props.offset_x = 50 # 50px right
|
||||||
|
props.offset_y = -25 # 25px up
|
||||||
|
|
||||||
|
base_x = (self.canvas_width - self.text_width) // 2 # 412
|
||||||
|
base_y = (self.canvas_height - self.text_height) // 2 # 487
|
||||||
|
expected_x = base_x + props.offset_x # 462
|
||||||
|
expected_y = base_y + props.offset_y # 462
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Pixel Offset Application", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Pixel Offset Application", False,
|
||||||
|
f"Offset not applied correctly", (expected_x, expected_y), (x, y))
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Pixel Offset Application", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def test_margin_synchronization(self):
|
||||||
|
"""Test margin link functionality"""
|
||||||
|
print("\n=== Testing Margin Synchronization ===")
|
||||||
|
|
||||||
|
props = MockProps()
|
||||||
|
props.margins_linked = True
|
||||||
|
props.margin_x = 15.0
|
||||||
|
props.margin_y = 10.0
|
||||||
|
|
||||||
|
# Test X margin sync
|
||||||
|
sync_margin_values(props, 'x')
|
||||||
|
if props.margin_y == props.margin_x:
|
||||||
|
self.log_test("Margin Sync (X to Y)", True, f"Y margin updated to {props.margin_y}")
|
||||||
|
else:
|
||||||
|
self.log_test("Margin Sync (X to Y)", False,
|
||||||
|
f"Y margin not synced", props.margin_x, props.margin_y)
|
||||||
|
|
||||||
|
# Reset and test Y margin sync
|
||||||
|
props.margin_x = 20.0
|
||||||
|
props.margin_y = 25.0
|
||||||
|
sync_margin_values(props, 'y')
|
||||||
|
if props.margin_x == props.margin_y:
|
||||||
|
self.log_test("Margin Sync (Y to X)", True, f"X margin updated to {props.margin_x}")
|
||||||
|
else:
|
||||||
|
self.log_test("Margin Sync (Y to X)", False,
|
||||||
|
f"X margin not synced", props.margin_y, props.margin_x)
|
||||||
|
|
||||||
|
# Test unlinked margins
|
||||||
|
props.margins_linked = False
|
||||||
|
original_x = props.margin_x
|
||||||
|
original_y = props.margin_y
|
||||||
|
sync_margin_values(props, 'x')
|
||||||
|
|
||||||
|
if props.margin_x == original_x and props.margin_y == original_y:
|
||||||
|
self.log_test("Margin Sync (Unlinked)", True, "Margins unchanged when unlinked")
|
||||||
|
else:
|
||||||
|
self.log_test("Margin Sync (Unlinked)", False,
|
||||||
|
"Margins changed when they shouldn't")
|
||||||
|
|
||||||
|
def test_edge_cases(self):
|
||||||
|
"""Test edge cases and boundary conditions"""
|
||||||
|
print("\n=== Testing Edge Cases ===")
|
||||||
|
|
||||||
|
# Test zero canvas dimensions
|
||||||
|
props = MockProps()
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, 0, 0, self.text_width, self.text_height)
|
||||||
|
self.log_test("Zero Canvas Dimensions", True, f"Handled gracefully: ({x}, {y})")
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Zero Canvas Dimensions", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
# Test text larger than canvas
|
||||||
|
props = MockProps()
|
||||||
|
props.anchor_point = 'TOP_LEFT'
|
||||||
|
props.constrain_to_canvas = True
|
||||||
|
large_text_width = 2000
|
||||||
|
large_text_height = 2000
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
large_text_width, large_text_height)
|
||||||
|
|
||||||
|
if x <= 0 and y <= 0:
|
||||||
|
self.log_test("Text Larger Than Canvas", True, f"Constrained to: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Text Larger Than Canvas", False,
|
||||||
|
f"Not properly constrained: ({x}, {y})")
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Text Larger Than Canvas", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
# Test maximum margin values (50%)
|
||||||
|
props = MockProps()
|
||||||
|
props.anchor_point = 'TOP_LEFT'
|
||||||
|
props.margin_x = 50.0
|
||||||
|
props.margin_y = 50.0
|
||||||
|
|
||||||
|
try:
|
||||||
|
x, y = calculate_text_position(props, self.canvas_width, self.canvas_height,
|
||||||
|
self.text_width, self.text_height)
|
||||||
|
|
||||||
|
expected_x = int(0.5 * self.canvas_width) # 512
|
||||||
|
expected_y = int(0.5 * self.canvas_height) # 512
|
||||||
|
|
||||||
|
if x == expected_x and y == expected_y:
|
||||||
|
self.log_test("Maximum Margins (50%)", True, f"Position: ({x}, {y})")
|
||||||
|
else:
|
||||||
|
self.log_test("Maximum Margins (50%)", False,
|
||||||
|
f"Incorrect calculation", (expected_x, expected_y), (x, y))
|
||||||
|
except Exception as e:
|
||||||
|
self.log_test("Maximum Margins (50%)", False, f"Exception: {str(e)}")
|
||||||
|
|
||||||
|
def analyze_ui_integration(self):
|
||||||
|
"""Analyze UI integration potential issues"""
|
||||||
|
print("\n=== Analyzing UI Integration ===")
|
||||||
|
|
||||||
|
issues = []
|
||||||
|
|
||||||
|
# Check anchor point matrix structure
|
||||||
|
matrix = get_anchor_point_matrix()
|
||||||
|
if len(matrix) != 3 or any(len(row) != 3 for row in matrix):
|
||||||
|
issues.append("❌ Anchor point matrix is not 3x3")
|
||||||
|
else:
|
||||||
|
print("✅ Anchor point matrix structure is correct")
|
||||||
|
|
||||||
|
# Check all anchor points are defined
|
||||||
|
all_anchors = [anchor for row in matrix for anchor in row]
|
||||||
|
expected_anchors = [
|
||||||
|
'TOP_LEFT', 'TOP_CENTER', 'TOP_RIGHT',
|
||||||
|
'MIDDLE_LEFT', 'MIDDLE_CENTER', 'MIDDLE_RIGHT',
|
||||||
|
'BOTTOM_LEFT', 'BOTTOM_CENTER', 'BOTTOM_RIGHT'
|
||||||
|
]
|
||||||
|
|
||||||
|
missing_anchors = set(expected_anchors) - set(all_anchors)
|
||||||
|
if missing_anchors:
|
||||||
|
issues.append(f"❌ Missing anchor points: {missing_anchors}")
|
||||||
|
else:
|
||||||
|
print("✅ All required anchor points are defined")
|
||||||
|
|
||||||
|
# Check for property type consistency
|
||||||
|
props = MockProps()
|
||||||
|
|
||||||
|
# Test property ranges
|
||||||
|
if hasattr(props, 'margin_x') and hasattr(props, 'margin_y'):
|
||||||
|
print("✅ Margin properties exist")
|
||||||
|
else:
|
||||||
|
issues.append("❌ Margin properties missing")
|
||||||
|
|
||||||
|
if hasattr(props, 'offset_x') and hasattr(props, 'offset_y'):
|
||||||
|
print("✅ Offset properties exist")
|
||||||
|
else:
|
||||||
|
issues.append("❌ Offset properties missing")
|
||||||
|
|
||||||
|
self.issues_found.extend(issues)
|
||||||
|
|
||||||
|
def run_all_tests(self):
|
||||||
|
"""Run all tests and generate comprehensive report"""
|
||||||
|
print("🔍 ENHANCED POSITIONING SYSTEM TEST SUITE")
|
||||||
|
print("=" * 50)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.test_anchor_point_calculations()
|
||||||
|
self.test_margin_calculations()
|
||||||
|
self.test_manual_positioning()
|
||||||
|
self.test_canvas_constraints()
|
||||||
|
self.test_offset_application()
|
||||||
|
self.test_margin_synchronization()
|
||||||
|
self.test_edge_cases()
|
||||||
|
self.analyze_ui_integration()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Critical error during testing: {e}")
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
self.generate_report()
|
||||||
|
|
||||||
|
def generate_report(self):
|
||||||
|
"""Generate comprehensive test report"""
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("📊 COMPREHENSIVE TEST REPORT")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
total_tests = len(self.test_results)
|
||||||
|
passed_tests = sum(1 for result in self.test_results if result['passed'])
|
||||||
|
failed_tests = total_tests - passed_tests
|
||||||
|
|
||||||
|
print(f"📈 SUMMARY:")
|
||||||
|
print(f" Total Tests: {total_tests}")
|
||||||
|
print(f" ✅ Passed: {passed_tests}")
|
||||||
|
print(f" ❌ Failed: {failed_tests}")
|
||||||
|
print(f" Success Rate: {(passed_tests/total_tests*100):.1f}%" if total_tests > 0 else "No tests run")
|
||||||
|
|
||||||
|
if self.issues_found:
|
||||||
|
print(f"\n🐛 ISSUES IDENTIFIED ({len(self.issues_found)}):")
|
||||||
|
for issue in self.issues_found:
|
||||||
|
print(f" {issue}")
|
||||||
|
|
||||||
|
# Categorize issues by severity
|
||||||
|
critical_issues = []
|
||||||
|
major_issues = []
|
||||||
|
minor_issues = []
|
||||||
|
|
||||||
|
for result in self.test_results:
|
||||||
|
if not result['passed']:
|
||||||
|
if 'exception' in result['details'].lower():
|
||||||
|
critical_issues.append(result)
|
||||||
|
elif 'constraint' in result['test'].lower() or 'calculation' in result['details'].lower():
|
||||||
|
major_issues.append(result)
|
||||||
|
else:
|
||||||
|
minor_issues.append(result)
|
||||||
|
|
||||||
|
if critical_issues:
|
||||||
|
print(f"\n🚨 CRITICAL ISSUES ({len(critical_issues)}):")
|
||||||
|
for issue in critical_issues:
|
||||||
|
print(f" • {issue['test']}: {issue['details']}")
|
||||||
|
|
||||||
|
if major_issues:
|
||||||
|
print(f"\n⚠️ MAJOR ISSUES ({len(major_issues)}):")
|
||||||
|
for issue in major_issues:
|
||||||
|
print(f" • {issue['test']}: {issue['details']}")
|
||||||
|
|
||||||
|
if minor_issues:
|
||||||
|
print(f"\n ℹ️ MINOR ISSUES ({len(minor_issues)}):")
|
||||||
|
for issue in minor_issues:
|
||||||
|
print(f" • {issue['test']}: {issue['details']}")
|
||||||
|
|
||||||
|
# Recommendations
|
||||||
|
print(f"\n🎯 RECOMMENDATIONS:")
|
||||||
|
|
||||||
|
if critical_issues:
|
||||||
|
print(" 1. Fix critical exceptions before deployment")
|
||||||
|
print(" 2. Add proper error handling for edge cases")
|
||||||
|
|
||||||
|
if major_issues:
|
||||||
|
print(" 3. Review positioning calculation logic")
|
||||||
|
print(" 4. Test constraint functionality thoroughly")
|
||||||
|
|
||||||
|
print(" 5. Add comprehensive unit tests to the addon")
|
||||||
|
print(" 6. Implement validation for property ranges")
|
||||||
|
print(" 7. Add user feedback for invalid input combinations")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'total_tests': total_tests,
|
||||||
|
'passed': passed_tests,
|
||||||
|
'failed': failed_tests,
|
||||||
|
'issues': self.issues_found,
|
||||||
|
'critical_issues': len(critical_issues),
|
||||||
|
'major_issues': len(major_issues),
|
||||||
|
'minor_issues': len(minor_issues)
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("🚀 Starting Enhanced Positioning System Test Suite...")
|
||||||
|
test_suite = PositioningTestSuite()
|
||||||
|
results = test_suite.run_all_tests()
|
||||||
|
|
||||||
|
print(f"\n✨ Testing completed!")
|
||||||
|
print(f"Check the detailed report above for analysis and recommendations.")
|
||||||
231
test_ui_and_compatibility.py
Normal file
231
test_ui_and_compatibility.py
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
"""
|
||||||
|
Advanced UI Integration and Backward Compatibility Test Suite
|
||||||
|
for the Text Texture Generator's enhanced positioning system.
|
||||||
|
|
||||||
|
This focuses on potential issues that the basic positioning tests might miss.
|
||||||
|
"""
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
class UICompatibilityTestSuite:
|
||||||
|
"""Test suite focused on UI integration and compatibility issues"""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.issues_found = []
|
||||||
|
self.warnings = []
|
||||||
|
|
||||||
|
def analyze_ui_layout_structure(self):
|
||||||
|
"""Analyze the UI layout structure from the addon code"""
|
||||||
|
print("\n=== Analyzing UI Layout Structure ===")
|
||||||
|
|
||||||
|
# Read the addon code to analyze UI structure
|
||||||
|
try:
|
||||||
|
with open('__init__.py', 'r') as f:
|
||||||
|
addon_code = f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
self.issues_found.append("❌ Cannot read __init__.py for UI analysis")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for Positioning & Alignment section
|
||||||
|
if "Positioning & Alignment" in addon_code:
|
||||||
|
print("✅ 'Positioning & Alignment' section found in UI")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ 'Positioning & Alignment' section not found in UI")
|
||||||
|
|
||||||
|
# Check for position mode toggle
|
||||||
|
if 'position_mode' in addon_code and 'PRESET' in addon_code and 'MANUAL' in addon_code:
|
||||||
|
print("✅ Position mode toggle implementation found")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ Position mode toggle not properly implemented")
|
||||||
|
|
||||||
|
# Check for 3x3 anchor grid implementation
|
||||||
|
anchor_grid_patterns = [
|
||||||
|
r'get_anchor_point_matrix\(\)',
|
||||||
|
r'for.*anchor.*in.*matrix',
|
||||||
|
r'grid_row.*=.*position_col\.row'
|
||||||
|
]
|
||||||
|
|
||||||
|
grid_found = all(re.search(pattern, addon_code, re.DOTALL) for pattern in anchor_grid_patterns)
|
||||||
|
if grid_found:
|
||||||
|
print("✅ 3x3 anchor grid UI implementation found")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ 3x3 anchor grid UI implementation incomplete")
|
||||||
|
|
||||||
|
# Check for margin controls with link functionality
|
||||||
|
if 'margins_linked' in addon_code and 'LINKED' in addon_code and 'UNLINKED' in addon_code:
|
||||||
|
print("✅ Margin link/unlink controls found")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ Margin link/unlink controls not properly implemented")
|
||||||
|
|
||||||
|
# Check for conditional UI visibility
|
||||||
|
if 'position_mode == ' in addon_code:
|
||||||
|
print("✅ Conditional UI visibility based on position mode found")
|
||||||
|
else:
|
||||||
|
self.warnings.append("⚠️ No conditional UI visibility found - controls might always be visible")
|
||||||
|
|
||||||
|
def test_backward_compatibility_integration(self):
|
||||||
|
"""Test how the new system integrates with existing text_align property"""
|
||||||
|
print("\n=== Testing Backward Compatibility Integration ===")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open('__init__.py', 'r') as f:
|
||||||
|
addon_code = f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
self.issues_found.append("❌ Cannot read addon code for compatibility analysis")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find the backward compatibility section in generate_texture_image
|
||||||
|
compat_section = re.search(
|
||||||
|
r'# Apply legacy text_align.*?props\.anchor_point == \'MIDDLE_CENTER\'.*?# Apply offsets even in legacy mode',
|
||||||
|
addon_code, re.DOTALL
|
||||||
|
)
|
||||||
|
|
||||||
|
if compat_section:
|
||||||
|
print("✅ Backward compatibility section found in texture generation")
|
||||||
|
|
||||||
|
# Check if all text_align values are handled
|
||||||
|
compat_code = compat_section.group(0)
|
||||||
|
align_values = ['left', 'center', 'right']
|
||||||
|
missing_aligns = []
|
||||||
|
|
||||||
|
for align in align_values:
|
||||||
|
if f"text_align == '{align}'" not in compat_code:
|
||||||
|
missing_aligns.append(align)
|
||||||
|
|
||||||
|
if missing_aligns:
|
||||||
|
self.issues_found.append(f"❌ Missing text_align compatibility for: {missing_aligns}")
|
||||||
|
else:
|
||||||
|
print("✅ All text_align values have compatibility handling")
|
||||||
|
|
||||||
|
# Check if legacy mode only applies to MIDDLE_CENTER
|
||||||
|
if "anchor_point == 'MIDDLE_CENTER'" in compat_code:
|
||||||
|
print("✅ Legacy compatibility properly scoped to MIDDLE_CENTER anchor")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ Legacy compatibility scope issue - should only apply to MIDDLE_CENTER")
|
||||||
|
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ Backward compatibility section not found in texture generation")
|
||||||
|
|
||||||
|
# Check if text_align property is still defined
|
||||||
|
if re.search(r'text_align.*EnumProperty', addon_code):
|
||||||
|
print("✅ Legacy text_align property still defined")
|
||||||
|
else:
|
||||||
|
self.issues_found.append("❌ Legacy text_align property not found - breaking change!")
|
||||||
|
|
||||||
|
def analyze_property_update_chains(self):
|
||||||
|
"""Analyze property update chains for consistency"""
|
||||||
|
print("\n=== Analyzing Property Update Chains ===")
|
||||||
|
|
||||||
|
try:
|
||||||
|
with open('__init__.py', 'r') as f:
|
||||||
|
addon_code = f.read()
|
||||||
|
except FileNotFoundError:
|
||||||
|
self.issues_found.append("❌ Cannot read addon code for update chain analysis")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find all properties with update=update_live_preview
|
||||||
|
update_properties = re.findall(r'(\w+):\s*\w+Property.*?update=update_live_preview', addon_code, re.DOTALL)
|
||||||
|
|
||||||
|
expected_positioning_props = [
|
||||||
|
'anchor_point', 'position_mode', 'margin_x', 'margin_y',
|
||||||
|
'margins_linked', 'offset_x', 'offset_y', 'manual_position_x',
|
||||||
|
'manual_position_y', 'manual_position_unit', 'constrain_to_canvas'
|
||||||
|
]
|
||||||
|
|
||||||
|
missing_updates = []
|
||||||
|
for prop in expected_positioning_props:
|
||||||
|
if prop not in update_properties:
|
||||||
|
missing_updates.append(prop)
|
||||||
|
|
||||||
|
if missing_updates:
|
||||||
|
self.issues_found.append(f"❌ Properties missing live preview updates: {missing_updates}")
|
||||||
|
else:
|
||||||
|
print("✅ All positioning properties have live preview updates")
|
||||||
|
|
||||||
|
# Check for visual feedback properties
|
||||||
|
visual_props = ['show_position_guides', 'show_text_bounds', 'show_canvas_grid']
|
||||||
|
missing_visual_updates = []
|
||||||
|
for prop in visual_props:
|
||||||
|
if prop not in update_properties:
|
||||||
|
missing_visual_updates.append(prop)
|
||||||
|
|
||||||
|
if missing_visual_updates:
|
||||||
|
self.issues_found.append(f"❌ Visual feedback properties missing updates: {missing_visual_updates}")
|
||||||
|
else:
|
||||||
|
print("✅ All visual feedback properties have live preview updates")
|
||||||
|
|
||||||
|
def run_compatibility_tests(self):
|
||||||
|
"""Run all compatibility and UI integration tests"""
|
||||||
|
print("🔍 UI INTEGRATION & COMPATIBILITY TEST SUITE")
|
||||||
|
print("=" * 55)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.analyze_ui_layout_structure()
|
||||||
|
self.test_backward_compatibility_integration()
|
||||||
|
self.analyze_property_update_chains()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"❌ Critical error during compatibility testing: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
self.generate_compatibility_report()
|
||||||
|
|
||||||
|
def generate_compatibility_report(self):
|
||||||
|
"""Generate compatibility and UI integration report"""
|
||||||
|
print("\n" + "=" * 60)
|
||||||
|
print("📊 UI INTEGRATION & COMPATIBILITY REPORT")
|
||||||
|
print("=" * 60)
|
||||||
|
|
||||||
|
total_issues = len(self.issues_found)
|
||||||
|
total_warnings = len(self.warnings)
|
||||||
|
|
||||||
|
print(f"📈 SUMMARY:")
|
||||||
|
print(f" 🚨 Critical Issues: {total_issues}")
|
||||||
|
print(f" ⚠️ Warnings: {total_warnings}")
|
||||||
|
|
||||||
|
if self.issues_found:
|
||||||
|
print(f"\n🚨 CRITICAL ISSUES FOUND ({total_issues}):")
|
||||||
|
for issue in self.issues_found:
|
||||||
|
print(f" {issue}")
|
||||||
|
|
||||||
|
if self.warnings:
|
||||||
|
print(f"\n⚠️ WARNINGS ({total_warnings}):")
|
||||||
|
for warning in self.warnings:
|
||||||
|
print(f" {warning}")
|
||||||
|
|
||||||
|
if not self.issues_found and not self.warnings:
|
||||||
|
print("\n🎉 NO CRITICAL ISSUES OR WARNINGS FOUND!")
|
||||||
|
print(" The UI integration and compatibility appear to be well implemented.")
|
||||||
|
|
||||||
|
# Recommendations based on findings
|
||||||
|
print(f"\n🎯 RECOMMENDATIONS:")
|
||||||
|
|
||||||
|
if total_issues > 0:
|
||||||
|
print(" 1. 🚨 Address all critical issues before deployment")
|
||||||
|
print(" 2. Test UI elements thoroughly in actual Blender environment")
|
||||||
|
|
||||||
|
if total_warnings > 0:
|
||||||
|
print(" 3. ⚠️ Review and address warnings for robustness")
|
||||||
|
print(" 4. Add explicit error handling for edge cases")
|
||||||
|
|
||||||
|
print(" 5. ✅ Add automated UI tests to prevent regressions")
|
||||||
|
print(" 6. ✅ Test backward compatibility with existing presets")
|
||||||
|
print(" 7. ✅ Validate all property ranges in actual use cases")
|
||||||
|
|
||||||
|
return {
|
||||||
|
'critical_issues': total_issues,
|
||||||
|
'warnings': total_warnings,
|
||||||
|
'issues_list': self.issues_found,
|
||||||
|
'warnings_list': self.warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("🚀 Starting UI Integration & Compatibility Test Suite...")
|
||||||
|
test_suite = UICompatibilityTestSuite()
|
||||||
|
results = test_suite.run_compatibility_tests()
|
||||||
|
|
||||||
|
print(f"\n✨ UI & Compatibility testing completed!")
|
||||||
Reference in New Issue
Block a user