683 lines
24 KiB
Python
683 lines
24 KiB
Python
"""Unit tests for generation_ops.py - Blender operator tests"""
|
|
|
|
from unittest.mock import Mock, patch, MagicMock, call
|
|
import pytest
|
|
|
|
from types import SimpleNamespace
|
|
|
|
from src.operators.generation_ops import (
|
|
TEXT_TEXTURE_OT_generate,
|
|
TEXT_TEXTURE_OT_preview,
|
|
TEXT_TEXTURE_OT_generate_shader,
|
|
TEXT_TEXTURE_OT_generate_plane,
|
|
TEXT_TEXTURE_OT_reset_settings,
|
|
_sanitize_text_for_name,
|
|
_resolve_material_name,
|
|
)
|
|
|
|
|
|
# Helper functions for common mock setups
|
|
|
|
def create_mock_props(text="test", width=512, height=512, **kwargs):
|
|
"""Create mock texture properties with sensible defaults.
|
|
|
|
Args:
|
|
text: Text content for texture generation
|
|
width: Texture width in pixels
|
|
height: Texture height in pixels
|
|
**kwargs: Additional property overrides
|
|
|
|
Returns:
|
|
Mock object with standard texture properties
|
|
"""
|
|
props = Mock()
|
|
props.text = text
|
|
props.texture_width = width
|
|
props.texture_height = height
|
|
props.generate_normal_map = kwargs.get('generate_normal_map', False)
|
|
props.preview_size = kwargs.get('preview_size', 512)
|
|
props.shader_type = kwargs.get('shader_type', 'PRINCIPLED')
|
|
props.shader_connection = kwargs.get('shader_connection', 'BASE_COLOR')
|
|
props.use_alpha = kwargs.get('use_alpha', False)
|
|
props.disable_shadows = kwargs.get('disable_shadows', False)
|
|
props.disable_reflections = kwargs.get('disable_reflections', False)
|
|
props.disable_backfacing = kwargs.get('disable_backfacing', False)
|
|
props.custom_material_name = kwargs.get('custom_material_name', "")
|
|
props.auto_assign_material = kwargs.get('auto_assign_material', False)
|
|
return props
|
|
|
|
|
|
class DummyModifiers:
|
|
"""Simple stand-in for Blender's modifier collection."""
|
|
|
|
def __init__(self):
|
|
self.created = []
|
|
|
|
def new(self, name, mod_type=None, **kwargs):
|
|
mod_type = mod_type or kwargs.get('type')
|
|
modifier = SimpleNamespace(
|
|
name=name,
|
|
type=mod_type,
|
|
subdivision_type='CATMULL_CLARK',
|
|
levels=0,
|
|
render_levels=0,
|
|
)
|
|
self.created.append(modifier)
|
|
return modifier
|
|
|
|
|
|
class DummyCollection:
|
|
"""Simple collection helper with clear tracking."""
|
|
|
|
def __init__(self):
|
|
self.cleared = False
|
|
|
|
def clear(self):
|
|
self.cleared = True
|
|
|
|
|
|
def create_mock_context(props=None, **kwargs):
|
|
"""Create mock Blender context with scene properties.
|
|
|
|
Args:
|
|
props: Texture properties (creates default if None)
|
|
**kwargs: Context attribute overrides
|
|
|
|
Returns:
|
|
Mock context object with scene.text_texture_props
|
|
"""
|
|
context = Mock()
|
|
context.scene.text_texture_props = props if props else create_mock_props()
|
|
context.space_data = kwargs.get('space_data', None)
|
|
context.active_object = kwargs.get('active_object', None)
|
|
context.screen.areas = kwargs.get('screen_areas', [])
|
|
return context
|
|
|
|
|
|
def create_mock_image(width=512, height=512):
|
|
"""Create mock PIL Image with pixel data.
|
|
|
|
Args:
|
|
width: Image width in pixels
|
|
height: Image height in pixels
|
|
|
|
Returns:
|
|
Mock image with pixels attribute
|
|
"""
|
|
img = Mock()
|
|
img.pixels = [0.5] * (width * height * 4)
|
|
return img
|
|
|
|
|
|
def create_mock_blender_image(width=512, height=512):
|
|
"""Create mock Blender image with pack method.
|
|
|
|
Args:
|
|
width: Image width in pixels
|
|
height: Image height in pixels
|
|
|
|
Returns:
|
|
Mock Blender image ready for bpy.data.images
|
|
"""
|
|
img = Mock()
|
|
img.pixels = [0.0] * (width * height * 4)
|
|
img.pack = Mock()
|
|
return img
|
|
|
|
|
|
def create_mock_shader_nodes():
|
|
"""Create mock shader node tree components.
|
|
|
|
Returns:
|
|
Tuple of (output_node, shader_node, texture_node) mocks
|
|
"""
|
|
output_node = MagicMock()
|
|
output_node.inputs = {'Surface': Mock()}
|
|
|
|
shader_node = MagicMock()
|
|
shader_node.inputs = {
|
|
'Base Color': Mock(),
|
|
'Emission Color': Mock(),
|
|
'Emission Strength': Mock(),
|
|
'Alpha': Mock(),
|
|
'Normal': Mock()
|
|
}
|
|
shader_node.outputs = {'BSDF': Mock()}
|
|
|
|
tex_node = MagicMock()
|
|
tex_node.outputs = {'Color': Mock(), 'Alpha': Mock()}
|
|
tex_node.image = None
|
|
tex_node.location = (0, 0)
|
|
|
|
return output_node, shader_node, tex_node
|
|
|
|
|
|
class TestGenerateOperator:
|
|
"""Test suite for TEXT_TEXTURE_OT_generate operator"""
|
|
|
|
def test_execute_success(self):
|
|
"""Successful generation returns {'FINISHED'}"""
|
|
op = TEXT_TEXTURE_OT_generate()
|
|
op.report = Mock()
|
|
|
|
props = create_mock_props(text="test texture")
|
|
context = create_mock_context(props)
|
|
mock_img = create_mock_image()
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen, \
|
|
patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
|
|
mock_gen.return_value = (mock_img, None)
|
|
mock_blender_img = create_mock_blender_image()
|
|
mock_bpy.data.images.new.return_value = mock_blender_img
|
|
mock_bpy.data.images.__contains__.return_value = False
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
mock_gen.assert_called_once_with(props, 512, 512)
|
|
assert op.report.call_count >= 1
|
|
|
|
def test_execute_creates_blender_image(self):
|
|
"""Image added to bpy.data.images"""
|
|
op = TEXT_TEXTURE_OT_generate()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.texture_width = 256
|
|
mock_props.texture_height = 256
|
|
mock_props.generate_normal_map = False
|
|
context.scene.text_texture_props = mock_props
|
|
context.space_data = None
|
|
|
|
mock_img = Mock()
|
|
mock_img.pixels = [0.5] * (256 * 256 * 4)
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen, \
|
|
patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
|
|
mock_gen.return_value = (mock_img, None)
|
|
|
|
mock_blender_img = Mock()
|
|
mock_blender_img.pixels = [0.0] * (256 * 256 * 4)
|
|
mock_blender_img.pack = Mock()
|
|
mock_bpy.data.images.new.return_value = mock_blender_img
|
|
mock_bpy.data.images.__contains__.return_value = False
|
|
|
|
result = op.execute(context)
|
|
|
|
mock_bpy.data.images.new.assert_called_once()
|
|
call_args = mock_bpy.data.images.new.call_args
|
|
assert call_args[0][1] == 256
|
|
assert call_args[0][2] == 256
|
|
assert call_args[1]['alpha'] is True
|
|
|
|
mock_blender_img.pack.assert_called_once()
|
|
assert result == {'FINISHED'}
|
|
|
|
def test_execute_replaces_existing_image(self):
|
|
"""Old image removed before new creation"""
|
|
op = TEXT_TEXTURE_OT_generate()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "existing"
|
|
mock_props.texture_width = 512
|
|
mock_props.texture_height = 512
|
|
mock_props.generate_normal_map = False
|
|
context.scene.text_texture_props = mock_props
|
|
context.space_data = None
|
|
|
|
mock_img = Mock()
|
|
mock_img.pixels = [0.5] * (512 * 512 * 4)
|
|
|
|
mock_old_image = Mock()
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen, \
|
|
patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
|
|
mock_gen.return_value = (mock_img, None)
|
|
|
|
mock_bpy.data.images.__contains__.return_value = True
|
|
mock_bpy.data.images.__getitem__.return_value = mock_old_image
|
|
mock_bpy.data.images.remove = Mock()
|
|
|
|
mock_blender_img = Mock()
|
|
mock_blender_img.pixels = [0.0] * (512 * 512 * 4)
|
|
mock_blender_img.pack = Mock()
|
|
mock_bpy.data.images.new.return_value = mock_blender_img
|
|
|
|
result = op.execute(context)
|
|
|
|
mock_bpy.data.images.remove.assert_called_once_with(mock_old_image)
|
|
assert result == {'FINISHED'}
|
|
|
|
def test_execute_error_handling(self):
|
|
"""Generation failure returns {'CANCELLED'} with error report"""
|
|
op = TEXT_TEXTURE_OT_generate()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.texture_width = 512
|
|
mock_props.texture_height = 512
|
|
context.scene.text_texture_props = mock_props
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen:
|
|
mock_gen.return_value = (None, None)
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'CANCELLED'}
|
|
op.report.assert_called_once()
|
|
assert 'ERROR' in op.report.call_args[0][0]
|
|
|
|
def test_invoke_opens_file_browser(self):
|
|
"""Invoke context opens file browser"""
|
|
# TEXT_TEXTURE_OT_generate doesn't implement a custom invoke method
|
|
# It uses the default Blender operator behavior (direct execute)
|
|
# This test verifies that behavior is as expected
|
|
pytest.skip("Operator uses default Blender invoke (no file browser)")
|
|
|
|
def test_poll_requires_context(self):
|
|
"""Poll method validates context availability"""
|
|
if hasattr(TEXT_TEXTURE_OT_generate, 'poll'):
|
|
valid_context = Mock()
|
|
valid_context.scene = Mock()
|
|
assert TEXT_TEXTURE_OT_generate.poll(valid_context) is True
|
|
|
|
invalid_context = Mock()
|
|
invalid_context.scene = None
|
|
assert TEXT_TEXTURE_OT_generate.poll(invalid_context) is False
|
|
else:
|
|
pytest.skip("Operator does not implement poll method")
|
|
|
|
|
|
class TestPreviewOperator:
|
|
"""Test suite for TEXT_TEXTURE_OT_preview operator"""
|
|
|
|
def test_execute_generates_preview(self):
|
|
"""Preview created at preview resolution"""
|
|
op = TEXT_TEXTURE_OT_preview()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "preview test"
|
|
mock_props.preview_size = 512
|
|
context.scene.text_texture_props = mock_props
|
|
context.screen.areas = []
|
|
|
|
mock_preview_img = Mock()
|
|
mock_preview_img.name = "Preview_Image"
|
|
|
|
# Patch at the core module level since that's where generate_preview lives
|
|
with patch('src.core.generation_engine.generate_preview') as mock_preview:
|
|
mock_preview.return_value = mock_preview_img
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
mock_preview.assert_called_once()
|
|
call_args = mock_preview.call_args
|
|
assert call_args[0][0] == mock_props
|
|
assert call_args[0][1] == 512
|
|
assert call_args[0][2] == 512
|
|
|
|
def test_execute_preview_error_handling(self):
|
|
"""Preview failure returns {'CANCELLED'}"""
|
|
op = TEXT_TEXTURE_OT_preview()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.preview_size = 512
|
|
context.scene.text_texture_props = mock_props
|
|
|
|
with patch('src.operators.generation_ops.generate_preview') as mock_preview:
|
|
mock_preview.return_value = None
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'CANCELLED'}
|
|
op.report.assert_called()
|
|
assert 'ERROR' in op.report.call_args[0][0]
|
|
|
|
def test_poll_requires_context(self):
|
|
"""Poll validates context"""
|
|
if hasattr(TEXT_TEXTURE_OT_preview, 'poll'):
|
|
valid_context = Mock()
|
|
valid_context.scene = Mock()
|
|
assert TEXT_TEXTURE_OT_preview.poll(valid_context) is True
|
|
|
|
invalid_context = Mock()
|
|
invalid_context.scene = None
|
|
assert TEXT_TEXTURE_OT_preview.poll(invalid_context) is False
|
|
else:
|
|
pytest.skip("Operator does not implement poll method")
|
|
|
|
def test_preview_uses_correct_resolution(self):
|
|
"""Uses preview_width and preview_height"""
|
|
op = TEXT_TEXTURE_OT_preview()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.preview_size = 256
|
|
mock_props.texture_width = 2048
|
|
mock_props.texture_height = 2048
|
|
context.scene.text_texture_props = mock_props
|
|
context.screen.areas = []
|
|
|
|
mock_result = Mock()
|
|
mock_result.name = "Preview"
|
|
|
|
with patch('src.core.generation_engine.generate_preview') as mock_preview:
|
|
mock_preview.return_value = mock_result
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
call_args = mock_preview.call_args
|
|
assert call_args[0][1] == 256
|
|
assert call_args[0][2] == 256
|
|
|
|
|
|
class TestGenerateShaderOperator:
|
|
"""Test suite for TEXT_TEXTURE_OT_generate_shader operator"""
|
|
|
|
def test_execute_creates_shader_nodes(self):
|
|
"""Shader nodes created and linked"""
|
|
op = TEXT_TEXTURE_OT_generate_shader()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "shader test"
|
|
mock_props.texture_width = 512
|
|
mock_props.texture_height = 512
|
|
mock_props.generate_normal_map = False
|
|
mock_props.shader_type = 'PRINCIPLED'
|
|
mock_props.shader_connection = 'BASE_COLOR'
|
|
mock_props.use_alpha = False
|
|
mock_props.disable_shadows = False
|
|
mock_props.disable_reflections = False
|
|
mock_props.disable_backfacing = False
|
|
mock_props.custom_material_name = ""
|
|
mock_props.auto_assign_material = False
|
|
context.scene.text_texture_props = mock_props
|
|
context.active_object = None
|
|
context.screen.areas = []
|
|
|
|
mock_img = Mock()
|
|
mock_img.pixels = [0.5] * (512 * 512 * 4)
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen, \
|
|
patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
|
|
mock_gen.return_value = (mock_img, None)
|
|
|
|
# Create mock nodes with subscriptable outputs/inputs
|
|
mock_output_node = MagicMock()
|
|
mock_output_node.inputs = {'Surface': Mock()}
|
|
|
|
mock_shader_node = MagicMock()
|
|
mock_shader_node.inputs = {
|
|
'Base Color': Mock(),
|
|
'Emission Color': Mock(),
|
|
'Emission Strength': Mock(),
|
|
'Alpha': Mock(),
|
|
'Normal': Mock()
|
|
}
|
|
mock_shader_node.outputs = {'BSDF': Mock()}
|
|
|
|
mock_tex_node = MagicMock()
|
|
mock_tex_node.outputs = {'Color': Mock(), 'Alpha': Mock()}
|
|
mock_tex_node.image = None
|
|
mock_tex_node.location = (0, 0)
|
|
|
|
# Mock material and node tree
|
|
mock_material = Mock()
|
|
mock_nodes = Mock()
|
|
mock_links = Mock()
|
|
mock_material.node_tree.nodes = mock_nodes
|
|
mock_material.node_tree.links = mock_links
|
|
mock_material.node_tree.nodes.clear = Mock()
|
|
mock_material.use_nodes = True
|
|
|
|
# Return different nodes on each call
|
|
mock_nodes.new.side_effect = [mock_output_node, mock_shader_node, mock_tex_node]
|
|
|
|
mock_bpy.data.materials.new.return_value = mock_material
|
|
mock_bpy.data.materials.__contains__.return_value = False
|
|
|
|
mock_blender_img = Mock()
|
|
mock_blender_img.pixels = [0.0] * (512 * 512 * 4)
|
|
mock_blender_img.pack = Mock()
|
|
mock_bpy.data.images.new.return_value = mock_blender_img
|
|
mock_bpy.data.images.__contains__.return_value = False
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
assert mock_nodes.new.call_count >= 3
|
|
assert mock_links.new.call_count >= 1
|
|
|
|
def test_execute_requires_material(self):
|
|
"""Material must exist in context"""
|
|
op = TEXT_TEXTURE_OT_generate_shader()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.texture_width = 512
|
|
mock_props.texture_height = 512
|
|
mock_props.generate_normal_map = False
|
|
mock_props.shader_type = 'PRINCIPLED'
|
|
mock_props.shader_connection = 'BASE_COLOR'
|
|
mock_props.use_alpha = False
|
|
mock_props.disable_shadows = False
|
|
mock_props.disable_reflections = False
|
|
mock_props.disable_backfacing = False
|
|
mock_props.custom_material_name = ""
|
|
mock_props.auto_assign_material = False
|
|
context.scene.text_texture_props = mock_props
|
|
context.active_object = None
|
|
context.screen.areas = []
|
|
|
|
mock_img = Mock()
|
|
mock_img.pixels = [0.5] * (512 * 512 * 4)
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen, \
|
|
patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
|
|
mock_gen.return_value = (mock_img, None)
|
|
|
|
# Create mock nodes with subscriptable outputs/inputs
|
|
mock_output_node = MagicMock()
|
|
mock_output_node.inputs = {'Surface': Mock()}
|
|
|
|
mock_shader_node = MagicMock()
|
|
mock_shader_node.inputs = {
|
|
'Base Color': Mock(),
|
|
'Alpha': Mock(),
|
|
'Normal': Mock()
|
|
}
|
|
mock_shader_node.outputs = {'BSDF': Mock()}
|
|
|
|
mock_tex_node = MagicMock()
|
|
mock_tex_node.outputs = {'Color': Mock(), 'Alpha': Mock()}
|
|
|
|
mock_material = Mock()
|
|
mock_material.node_tree.nodes = Mock()
|
|
mock_material.node_tree.links = Mock()
|
|
mock_material.node_tree.nodes.clear = Mock()
|
|
mock_material.node_tree.nodes.new.side_effect = [mock_output_node, mock_shader_node, mock_tex_node]
|
|
mock_material.use_nodes = True
|
|
|
|
mock_bpy.data.materials.__contains__.return_value = False
|
|
mock_bpy.data.materials.new.return_value = mock_material
|
|
|
|
mock_blender_img = Mock()
|
|
mock_blender_img.pixels = [0.0] * (512 * 512 * 4)
|
|
mock_blender_img.pack = Mock()
|
|
mock_bpy.data.images.new.return_value = mock_blender_img
|
|
mock_bpy.data.images.__contains__.return_value = False
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
mock_bpy.data.materials.new.assert_called_once()
|
|
|
|
def test_execute_error_handling(self):
|
|
"""Missing material returns {'CANCELLED'}"""
|
|
op = TEXT_TEXTURE_OT_generate_shader()
|
|
op.report = Mock()
|
|
|
|
context = Mock()
|
|
mock_props = Mock()
|
|
mock_props.text = "test"
|
|
mock_props.texture_width = 512
|
|
mock_props.texture_height = 512
|
|
context.scene.text_texture_props = mock_props
|
|
|
|
with patch('src.operators.generation_ops.generate_texture_image') as mock_gen:
|
|
mock_gen.return_value = (None, None)
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'CANCELLED'}
|
|
op.report.assert_called()
|
|
assert 'ERROR' in op.report.call_args[0][0]
|
|
|
|
def test_poll_validates_material_context(self):
|
|
"""Poll checks material availability"""
|
|
if hasattr(TEXT_TEXTURE_OT_generate_shader, 'poll'):
|
|
valid_context = Mock()
|
|
valid_context.scene = Mock()
|
|
assert TEXT_TEXTURE_OT_generate_shader.poll(valid_context) is True
|
|
|
|
invalid_context = Mock()
|
|
invalid_context.scene = None
|
|
assert TEXT_TEXTURE_OT_generate_shader.poll(invalid_context) is False
|
|
else:
|
|
pytest.skip("Operator does not implement poll method")
|
|
|
|
|
|
class TestGenerationHelpers:
|
|
"""Unit tests for helper utilities in generation_ops."""
|
|
|
|
def test_sanitize_text_removes_invalid_characters(self):
|
|
assert _sanitize_text_for_name("Hello @World!!!") == "Hello_World"
|
|
|
|
def test_sanitize_text_truncates_and_defaults(self):
|
|
assert _sanitize_text_for_name(" " * 5 + "--") == "Texture"
|
|
assert len(_sanitize_text_for_name("A" * 50)) == 20
|
|
|
|
def test_resolve_material_uses_custom_name(self):
|
|
props = create_mock_props()
|
|
props.custom_material_name = " Custom Mat "
|
|
assert _resolve_material_name(props, "Ignored") == "Custom Mat"
|
|
|
|
def test_resolve_material_defaults_to_sanitized(self):
|
|
props = create_mock_props()
|
|
props.custom_material_name = ""
|
|
assert _resolve_material_name(props, "Sample") == "TextTexture_Mat_Sample"
|
|
|
|
|
|
class TestGeneratePlaneOperator:
|
|
"""Tests for the Create Plane operator."""
|
|
|
|
def test_generate_plane_success(self):
|
|
op = TEXT_TEXTURE_OT_generate_plane()
|
|
op.report = Mock()
|
|
|
|
props = create_mock_props(text="Plane Test", width=800, height=400)
|
|
context = create_mock_context(props)
|
|
|
|
plane = SimpleNamespace(
|
|
type='MESH',
|
|
name='Plane',
|
|
scale=SimpleNamespace(x=1.0, y=1.0, z=1.0),
|
|
modifiers=DummyModifiers(),
|
|
data=SimpleNamespace(materials=[]),
|
|
)
|
|
context.active_object = plane
|
|
|
|
mat = SimpleNamespace(name="TextTexture_Mat_Plane_Test")
|
|
|
|
with patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
mock_bpy.ops.text_texture.generate_shader.return_value = {'FINISHED'}
|
|
mock_bpy.ops.mesh.primitive_plane_add.return_value = None
|
|
mock_bpy.data.materials.get.return_value = mat
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
assert pytest.approx(plane.scale.x, rel=1e-6) == 2.0
|
|
assert pytest.approx(plane.scale.y, rel=1e-6) == 1.0
|
|
assert plane.modifiers.created[0].subdivision_type == 'SIMPLE'
|
|
assert plane.modifiers.created[0].levels == 1
|
|
assert plane.data.materials[0] is mat
|
|
|
|
def test_generate_plane_handles_shader_failure(self):
|
|
op = TEXT_TEXTURE_OT_generate_plane()
|
|
op.report = Mock()
|
|
|
|
props = create_mock_props()
|
|
context = create_mock_context(props)
|
|
|
|
with patch('src.operators.generation_ops.bpy') as mock_bpy:
|
|
mock_bpy.ops.text_texture.generate_shader.return_value = {'CANCELLED'}
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'CANCELLED'}
|
|
op.report.assert_called()
|
|
|
|
|
|
class TestResetSettingsOperator:
|
|
"""Tests for the reset settings operator."""
|
|
|
|
class DummyProps:
|
|
__annotations__ = {
|
|
'text': None,
|
|
'image_overlays': None,
|
|
'preset_search_query': None,
|
|
}
|
|
|
|
def __init__(self):
|
|
self.text = "Custom"
|
|
self.image_overlays = DummyCollection()
|
|
self.preset_search_query = "search"
|
|
self.unset_called = []
|
|
self.bl_rna = SimpleNamespace(properties=[
|
|
SimpleNamespace(identifier='text'),
|
|
SimpleNamespace(identifier='image_overlays'),
|
|
SimpleNamespace(identifier='preset_search_query'),
|
|
])
|
|
|
|
def property_unset(self, name):
|
|
self.unset_called.append(name)
|
|
|
|
def test_reset_settings_unsets_all_known_properties(self):
|
|
op = TEXT_TEXTURE_OT_reset_settings()
|
|
op.report = Mock()
|
|
|
|
props = self.DummyProps()
|
|
context = create_mock_context(props)
|
|
|
|
result = op.execute(context)
|
|
|
|
assert result == {'FINISHED'}
|
|
assert set(props.unset_called) >= {'text', 'image_overlays', 'preset_search_query'}
|
|
assert props.image_overlays.cleared is True
|
|
op.report.assert_called()
|