This commit is contained in:
2025-10-12 11:30:53 +02:00
parent ffa046affd
commit e3eb1c8d48
6 changed files with 136 additions and 71 deletions

View File

@@ -5,6 +5,7 @@ Tests verify:
- Operator class exists and is properly structured
- PRO-only access control via poll()
- Text processing and limit enforcement
- Single-line validation (rejects newlines)
- Proper Blender operator contract
"""
@@ -53,13 +54,13 @@ class TestModalTextEditorOperator:
operator = TEXT_TEXTURE_OT_edit_multiline_text()
mock_context = Mock()
mock_context.scene.text_texture_props.text = "Line 1\nLine 2\nLine 3"
mock_context.scene.text_texture_props.text = "Single line text"
mock_context.window_manager.invoke_props_dialog.return_value = {'RUNNING_MODAL'}
mock_event = Mock()
result = operator.invoke(mock_context, mock_event)
assert operator.temp_text == "Line 1\nLine 2\nLine 3"
assert operator.temp_text == "Single line text"
assert result == {'RUNNING_MODAL'}
mock_context.window_manager.invoke_props_dialog.assert_called_once()
@@ -68,84 +69,83 @@ class TestModalTextEditorOperator:
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
operator.temp_text = "Edited Line 1\nEdited Line 2"
operator.temp_text = "Edited single line"
mock_context = Mock()
mock_context.scene.text_texture_props.text = "Old text"
with patch('src.operators.text_editor_ops.get_version_limits', return_value={
'max_text_lines': 50,
'max_text_length': 4096
}):
result = operator.execute(mock_context)
result = operator.execute(mock_context)
assert result == {'FINISHED'}
assert mock_context.scene.text_texture_props.text == "Edited Line 1\nEdited Line 2"
assert mock_context.scene.text_texture_props.text == "Edited single line"
def test_execute_enforces_character_limit(self):
"""execute() must truncate text exceeding max_text_length."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
# Create text exceeding 100 char limit
operator.temp_text = "x" * 150
# Create text exceeding 4096 char limit
operator.temp_text = "x" * 5000
mock_context = Mock()
mock_context.scene.text_texture_props.text = ""
with patch('src.operators.text_editor_ops.get_version_limits', return_value={
'max_text_lines': 50,
'max_text_length': 100
}):
operator.execute(mock_context)
operator.execute(mock_context)
saved_text = mock_context.scene.text_texture_props.text
assert len(saved_text) == 100, f"Expected 100 chars, got {len(saved_text)}"
assert len(saved_text) == 4096, f"Expected 4096 chars, got {len(saved_text)}"
def test_execute_enforces_line_limit(self):
"""execute() must truncate lines exceeding max_text_lines."""
def test_execute_rejects_text_with_newlines(self):
"""execute() must reject text containing newline characters."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
# Create 10 lines when limit is 5
operator.temp_text = "\n".join([f"Line {i}" for i in range(10)])
operator.temp_text = "Line 1\nLine 2"
mock_context = Mock()
mock_context.scene.text_texture_props.text = ""
mock_context.scene.text_texture_props.text = "Old text"
with patch('src.operators.text_editor_ops.get_version_limits', return_value={
'max_text_lines': 5,
'max_text_length': 4096
}):
operator.execute(mock_context)
result = operator.execute(mock_context)
saved_text = mock_context.scene.text_texture_props.text
line_count = saved_text.count('\n') + 1
assert line_count == 5, f"Expected 5 lines, got {line_count}"
assert saved_text == "Line 0\nLine 1\nLine 2\nLine 3\nLine 4"
# Should return CANCELLED
assert result == {'CANCELLED'}
# Should not modify the original text
assert mock_context.scene.text_texture_props.text == "Old text"
# Should report an error
operator.report.assert_called_once_with(
{'ERROR'},
"Multiline text not supported. Please use single line only."
)
def test_execute_handles_both_limits_simultaneously(self):
"""execute() must enforce both character and line limits correctly."""
def test_execute_rejects_multiple_newlines(self):
"""execute() must reject text with multiple newline characters."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
# Create 10 lines of 20 chars each (200 chars total)
operator.temp_text = "\n".join([f"Line {i:015d}" for i in range(10)])
operator.temp_text = "Line 1\nLine 2\nLine 3\nLine 4"
mock_context = Mock()
mock_context.scene.text_texture_props.text = ""
mock_context.scene.text_texture_props.text = "Old text"
with patch('src.operators.text_editor_ops.get_version_limits', return_value={
'max_text_lines': 5,
'max_text_length': 150
}):
operator.execute(mock_context)
result = operator.execute(mock_context)
saved_text = mock_context.scene.text_texture_props.text
# Should truncate to 150 chars first, then check lines
assert len(saved_text) <= 150
line_count = saved_text.count('\n') + 1
assert line_count <= 5
assert result == {'CANCELLED'}
assert mock_context.scene.text_texture_props.text == "Old text"
def test_execute_accepts_text_without_newlines(self):
"""execute() must accept and save text without newlines."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
operator.temp_text = "This is a single line with no newlines"
mock_context = Mock()
mock_context.scene.text_texture_props.text = "Old text"
result = operator.execute(mock_context)
assert result == {'FINISHED'}
assert mock_context.scene.text_texture_props.text == "This is a single line with no newlines"
def test_operator_has_temp_text_property(self):
"""Operator must have StringProperty named temp_text."""
@@ -178,4 +178,50 @@ class TestModalTextEditorOperator:
from src.operators import text_editor_ops
assert hasattr(text_editor_ops, 'unregister')
assert callable(text_editor_ops.unregister)
assert callable(text_editor_ops.unregister)
class TestSingleLineUIBehavior:
"""Test suite for single-line text UI display and statistics."""
def test_draw_calculates_character_count(self):
"""draw() must count characters correctly."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
mock_context = Mock()
operator.temp_text = "Single line text"
operator.draw(mock_context)
actual_chars = len(operator.temp_text)
expected_chars = 16
assert actual_chars == expected_chars, f"Expected {expected_chars} chars, got {actual_chars}"
def test_draw_handles_empty_text(self):
"""draw() must handle empty text without errors."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
mock_context = Mock()
operator.temp_text = ""
operator.draw(mock_context)
char_count = len(operator.temp_text)
assert char_count == 0, f"Empty text should have 0 characters, got {char_count}"
def test_draw_handles_long_single_line(self):
"""draw() must handle long single-line text correctly."""
from src.operators.text_editor_ops import TEXT_TEXTURE_OT_edit_multiline_text
operator = TEXT_TEXTURE_OT_edit_multiline_text()
mock_context = Mock()
# Create a very long single line
long_text = "x" * 5000
operator.temp_text = long_text
operator.draw(mock_context)
# Should track all characters
assert len(operator.temp_text) == 5000