working
This commit is contained in:
BIN
Archive.zip
BIN
Archive.zip
Binary file not shown.
1520
__init__.py
1520
__init__.py
File diff suppressed because it is too large
Load Diff
@@ -1,558 +0,0 @@
|
||||
#!/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.")
|
||||
@@ -1,150 +0,0 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test script to verify that preset names with Unicode characters like 'ö' work correctly.
|
||||
This script tests the validation logic outside of Blender.
|
||||
"""
|
||||
|
||||
import re
|
||||
import json
|
||||
import os
|
||||
import tempfile
|
||||
|
||||
def test_preset_name_validation():
|
||||
"""Test the new preset name validation regex"""
|
||||
|
||||
# Test cases with expected results
|
||||
test_cases = [
|
||||
# Valid names (should pass)
|
||||
("Test Preset", True),
|
||||
("Mein schönes Preset", True), # German with ö
|
||||
("Préférence française", True), # French with accents
|
||||
("Configuración español", True), # Spanish with ñ
|
||||
("Test_123", True),
|
||||
("Hello-World", True),
|
||||
("Тест", True), # Cyrillic
|
||||
("テスト", True), # Japanese
|
||||
("测试", True), # Chinese
|
||||
|
||||
# Invalid names (should fail)
|
||||
("Test<Preset", False), # Contains <
|
||||
("Test>Preset", False), # Contains >
|
||||
("Test:Preset", False), # Contains :
|
||||
("Test\"Preset", False), # Contains "
|
||||
("Test|Preset", False), # Contains |
|
||||
("Test?Preset", False), # Contains ?
|
||||
("Test*Preset", False), # Contains *
|
||||
("Test/Preset", False), # Contains /
|
||||
("Test\\Preset", False), # Contains \
|
||||
("", False), # Empty string
|
||||
]
|
||||
|
||||
print("Testing preset name validation...")
|
||||
|
||||
for preset_name, should_pass in test_cases:
|
||||
# Test the Unicode regex pattern
|
||||
unicode_pattern_match = bool(re.match(r'^[\w\s\-]+$', preset_name, re.UNICODE))
|
||||
|
||||
# Test filesystem-unsafe characters
|
||||
invalid_chars = ['<', '>', ':', '"', '|', '?', '*', '/', '\\']
|
||||
has_invalid_chars = any(char in preset_name for char in invalid_chars)
|
||||
|
||||
# Combined validation (same as in the actual code)
|
||||
validation_passes = unicode_pattern_match and not has_invalid_chars and preset_name.strip()
|
||||
|
||||
status = "✅ PASS" if validation_passes == should_pass else "❌ FAIL"
|
||||
print(f"{status}: '{preset_name}' -> Expected: {should_pass}, Got: {validation_passes}")
|
||||
|
||||
if validation_passes != should_pass:
|
||||
print(f" Unicode match: {unicode_pattern_match}, Has invalid chars: {has_invalid_chars}")
|
||||
|
||||
def test_json_serialization():
|
||||
"""Test that Unicode characters work correctly with JSON serialization/deserialization"""
|
||||
|
||||
print("\nTesting JSON serialization with Unicode characters...")
|
||||
|
||||
test_data = {
|
||||
'name': 'Schönes Preset',
|
||||
'text': 'Höllö Wörld',
|
||||
'texture_width': 1024,
|
||||
'texture_height': 1024,
|
||||
'font_size': 96
|
||||
}
|
||||
|
||||
try:
|
||||
# Test JSON encoding/decoding
|
||||
json_str = json.dumps(test_data, indent=2, ensure_ascii=False)
|
||||
decoded_data = json.loads(json_str)
|
||||
|
||||
print("✅ JSON serialization successful")
|
||||
print(f"Original name: {test_data['name']}")
|
||||
print(f"Decoded name: {decoded_data['name']}")
|
||||
print(f"Names match: {test_data['name'] == decoded_data['name']}")
|
||||
|
||||
# Test file I/O
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
||||
json.dump(test_data, f, indent=2, ensure_ascii=False)
|
||||
temp_file = f.name
|
||||
|
||||
with open(temp_file, 'r', encoding='utf-8') as f:
|
||||
file_data = json.load(f)
|
||||
|
||||
os.unlink(temp_file)
|
||||
|
||||
print("✅ File I/O successful")
|
||||
print(f"File data name: {file_data['name']}")
|
||||
print(f"File names match: {test_data['name'] == file_data['name']}")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ JSON serialization failed: {e}")
|
||||
|
||||
def test_filesystem_compatibility():
|
||||
"""Test creating files with Unicode names"""
|
||||
|
||||
print("\nTesting filesystem compatibility...")
|
||||
|
||||
test_names = [
|
||||
"schönes_preset",
|
||||
"café_preset",
|
||||
"naïve_preset",
|
||||
"test_ñ_preset"
|
||||
]
|
||||
|
||||
temp_dir = tempfile.mkdtemp()
|
||||
print(f"Testing in directory: {temp_dir}")
|
||||
|
||||
for name in test_names:
|
||||
try:
|
||||
filename = f"{name}.json"
|
||||
filepath = os.path.join(temp_dir, filename)
|
||||
|
||||
# Create file
|
||||
with open(filepath, 'w', encoding='utf-8') as f:
|
||||
json.dump({'name': name}, f, ensure_ascii=False)
|
||||
|
||||
# Read file back
|
||||
with open(filepath, 'r', encoding='utf-8') as f:
|
||||
data = json.load(f)
|
||||
|
||||
# Clean up
|
||||
os.unlink(filepath)
|
||||
|
||||
print(f"✅ {name}: File created and read successfully")
|
||||
|
||||
except Exception as e:
|
||||
print(f"❌ {name}: Failed - {e}")
|
||||
|
||||
# Clean up temp directory
|
||||
os.rmdir(temp_dir)
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("=" * 60)
|
||||
print("Testing Text Texture Generator Preset Unicode Support")
|
||||
print("=" * 60)
|
||||
|
||||
test_preset_name_validation()
|
||||
test_json_serialization()
|
||||
test_filesystem_compatibility()
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("Test completed! Check results above.")
|
||||
print("=" * 60)
|
||||
@@ -1,231 +0,0 @@
|
||||
#!/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