presets
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -21,6 +21,7 @@ try:
|
||||
from .preset_ops import (
|
||||
TEXT_TEXTURE_OT_save_preset,
|
||||
TEXT_TEXTURE_OT_save_preset_enter,
|
||||
TEXT_TEXTURE_OT_clear_preset_search,
|
||||
TEXT_TEXTURE_OT_load_preset,
|
||||
TEXT_TEXTURE_OT_delete_preset,
|
||||
TEXT_TEXTURE_OT_refresh_presets,
|
||||
@@ -31,6 +32,7 @@ try:
|
||||
PRESET_OPERATORS = [
|
||||
'TEXT_TEXTURE_OT_save_preset',
|
||||
'TEXT_TEXTURE_OT_save_preset_enter',
|
||||
'TEXT_TEXTURE_OT_clear_preset_search',
|
||||
'TEXT_TEXTURE_OT_load_preset',
|
||||
'TEXT_TEXTURE_OT_delete_preset',
|
||||
'TEXT_TEXTURE_OT_refresh_presets',
|
||||
@@ -108,4 +110,4 @@ __all__.extend(PRESET_OPERATORS)
|
||||
|
||||
# Add text editor operators if available
|
||||
__all__.extend(TEXT_EDITOR_OPERATORS)
|
||||
__all__.extend(UTILITY_OPERATORS)
|
||||
__all__.extend(UTILITY_OPERATORS)
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -168,15 +168,24 @@ class TEXT_TEXTURE_OT_save_preset(Operator):
|
||||
existing_preset = preset
|
||||
break
|
||||
|
||||
target_index = None
|
||||
|
||||
if not existing_preset:
|
||||
pass
|
||||
preset = props.presets.add()
|
||||
preset.name = preset_name
|
||||
preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE'
|
||||
target_index = len(props.presets) - 1
|
||||
else:
|
||||
pass
|
||||
# Update source based on where it was saved
|
||||
existing_preset.source = 'BLEND_FILE' if props.save_with_blend_file else 'PERSISTENT_FILE'
|
||||
for idx, collection_preset in enumerate(props.presets):
|
||||
pass
|
||||
if collection_preset.name == preset_name:
|
||||
pass
|
||||
target_index = idx
|
||||
break
|
||||
|
||||
# Provide clear feedback
|
||||
location = "with .blend file" if props.save_with_blend_file else "to persistent storage"
|
||||
@@ -188,6 +197,13 @@ class TEXT_TEXTURE_OT_save_preset(Operator):
|
||||
pass
|
||||
self.report({'INFO'}, f"✅ Preset '{preset_name}' saved {location}")
|
||||
|
||||
if target_index is not None and hasattr(props, "active_preset_index"):
|
||||
pass
|
||||
props.active_preset_index = target_index
|
||||
if hasattr(props, "last_loaded_preset"):
|
||||
pass
|
||||
props.last_loaded_preset = preset_name
|
||||
|
||||
except (OSError, IOError, json.JSONEncodeError) as e:
|
||||
pass
|
||||
self.report({'ERROR'}, f"Failed to save preset: {str(e)}")
|
||||
@@ -234,6 +250,22 @@ class TEXT_TEXTURE_OT_save_preset_enter(Operator):
|
||||
# Delegate to the main save preset operator
|
||||
return bpy.ops.text_texture.save_preset('INVOKE_DEFAULT')
|
||||
|
||||
class TEXT_TEXTURE_OT_clear_preset_search(Operator):
|
||||
pass
|
||||
"""Clear the preset search filter"""
|
||||
bl_idname = "text_texture.clear_preset_search"
|
||||
bl_label = "Clear Preset Search"
|
||||
bl_description = "Reset the preset search field"
|
||||
bl_options = {'INTERNAL'}
|
||||
|
||||
def execute(self, context):
|
||||
pass
|
||||
props = context.scene.text_texture_props
|
||||
if hasattr(props, "preset_search_query"):
|
||||
pass
|
||||
props.preset_search_query = ""
|
||||
return {'FINISHED'}
|
||||
|
||||
class TEXT_TEXTURE_OT_load_preset(Operator):
|
||||
pass
|
||||
"""Load preset"""
|
||||
@@ -382,6 +414,18 @@ class TEXT_TEXTURE_OT_load_preset(Operator):
|
||||
from ..core.generation_engine import generate_preview
|
||||
generate_preview(props, props.preview_size if hasattr(props, 'preview_size') else 512, props.preview_size if hasattr(props, 'preview_size') else 512)
|
||||
|
||||
if hasattr(props, "last_loaded_preset"):
|
||||
pass
|
||||
props.last_loaded_preset = self.preset_name
|
||||
if hasattr(props, "active_preset_index"):
|
||||
pass
|
||||
for idx, preset in enumerate(props.presets):
|
||||
pass
|
||||
if preset.name == self.preset_name:
|
||||
pass
|
||||
props.active_preset_index = idx
|
||||
break
|
||||
|
||||
source_info = {
|
||||
'BLEND_FILE': "from blend file",
|
||||
'PERSISTENT_FILE': "from persistent storage",
|
||||
@@ -438,13 +482,27 @@ class TEXT_TEXTURE_OT_delete_preset(Operator):
|
||||
self.report({'WARNING'}, f"Preset '{self.preset_name}' not found in blend file or local storage")
|
||||
|
||||
# Remove from UI list
|
||||
removed_index = None
|
||||
for i, preset in enumerate(props.presets):
|
||||
pass
|
||||
if preset.name == self.preset_name:
|
||||
pass
|
||||
props.presets.remove(i)
|
||||
removed_index = i
|
||||
break
|
||||
|
||||
if hasattr(props, "last_loaded_preset") and props.last_loaded_preset == self.preset_name:
|
||||
pass
|
||||
props.last_loaded_preset = ""
|
||||
if removed_index is not None and hasattr(props, "active_preset_index"):
|
||||
pass
|
||||
if len(props.presets) == 0:
|
||||
pass
|
||||
props.active_preset_index = 0
|
||||
else:
|
||||
pass
|
||||
props.active_preset_index = min(removed_index, len(props.presets) - 1)
|
||||
|
||||
delete_info = []
|
||||
if blend_deleted:
|
||||
pass
|
||||
@@ -809,6 +867,7 @@ class TEXT_TEXTURE_OT_import_presets(Operator):
|
||||
__all__ = [
|
||||
'TEXT_TEXTURE_OT_save_preset',
|
||||
'TEXT_TEXTURE_OT_save_preset_enter',
|
||||
'TEXT_TEXTURE_OT_clear_preset_search',
|
||||
'TEXT_TEXTURE_OT_load_preset',
|
||||
'TEXT_TEXTURE_OT_delete_preset',
|
||||
'TEXT_TEXTURE_OT_refresh_presets',
|
||||
|
||||
Binary file not shown.
@@ -606,6 +606,18 @@ class TEXT_TEXTURE_Properties(PropertyGroup):
|
||||
description="Name for new preset (Press Enter to save)",
|
||||
default="New Preset"
|
||||
)
|
||||
|
||||
preset_search_query: StringProperty(
|
||||
name="Preset Search",
|
||||
description="Filter presets by name",
|
||||
default=""
|
||||
)
|
||||
|
||||
last_loaded_preset: StringProperty(
|
||||
name="Last Loaded Preset",
|
||||
description="Tracks the most recently loaded preset name for highlighting",
|
||||
default=""
|
||||
)
|
||||
|
||||
# Property to track when user wants to save with Enter
|
||||
preset_save_requested: BoolProperty(
|
||||
|
||||
Binary file not shown.
118
src/ui/panels.py
118
src/ui/panels.py
@@ -498,78 +498,104 @@ def draw_presets_section(layout, props):
|
||||
draw_feature_lock_indicator(layout, 'presets')
|
||||
return
|
||||
|
||||
# Save preset section - use safe operator calls
|
||||
save_row = layout.row(align=True)
|
||||
col = layout.column(align=True)
|
||||
|
||||
# Save preset controls
|
||||
save_row = col.row(align=True)
|
||||
save_row.prop(props, "new_preset_name", text="", placeholder="Preset Name")
|
||||
|
||||
premium_lock = create_premium_lock_function()
|
||||
safe_operator_call(save_row, "text_texture.save_preset", premium_lock,
|
||||
text="Save", icon='FILE_NEW')
|
||||
|
||||
# Save with blend file checkbox
|
||||
layout.prop(props, "save_with_blend_file", text="Project-specific (only available in this .blend file)")
|
||||
save_location_row = col.row(align=True)
|
||||
save_location_row.prop(props, "save_with_blend_file", text="Project-specific (saved inside this .blend)")
|
||||
|
||||
layout.separator()
|
||||
|
||||
# Import/Export and refresh buttons - use safe operator calls
|
||||
backup_row = layout.row(align=True)
|
||||
col.separator()
|
||||
|
||||
# Import/export/refresh controls
|
||||
backup_row = col.row(align=True)
|
||||
export_lock = create_premium_lock_function()
|
||||
import_lock = create_premium_lock_function()
|
||||
|
||||
safe_operator_call(backup_row, "text_texture.export_presets", export_lock,
|
||||
text="Export Backup", icon='EXPORT')
|
||||
safe_operator_call(backup_row, "text_texture.import_presets", import_lock,
|
||||
text="Import Backup", icon='IMPORT')
|
||||
|
||||
utility_row = layout.row(align=True)
|
||||
|
||||
utility_row = col.row(align=True)
|
||||
refresh_lock = create_premium_lock_function()
|
||||
|
||||
safe_operator_call(utility_row, "text_texture.refresh_presets", refresh_lock,
|
||||
text="Refresh", icon='FILE_REFRESH')
|
||||
text="Refresh List", icon='FILE_REFRESH')
|
||||
|
||||
layout.separator()
|
||||
col.separator()
|
||||
|
||||
# Load presets section - unified list
|
||||
if props.presets:
|
||||
total_presets = len(props.presets)
|
||||
|
||||
if total_presets:
|
||||
pass
|
||||
layout.label(text="Presets", icon='PRESET')
|
||||
blend_count = len([preset for preset in props.presets if preset.source == 'BLEND_FILE'])
|
||||
persistent_count = total_presets - blend_count
|
||||
|
||||
# Sort presets alphabetically for consistent display
|
||||
summary_row = col.row(align=True)
|
||||
summary_row.label(text=f"{total_presets} presets", icon='PRESET_HLT')
|
||||
summary_row.label(text=f"{blend_count} project", icon='FILE_BLEND')
|
||||
summary_row.label(text=f"{persistent_count} global", icon='FILE_CACHE')
|
||||
|
||||
search_row = col.row(align=True)
|
||||
search_row.prop(props, "preset_search_query", text="", icon='VIEWZOOM')
|
||||
if getattr(props, "preset_search_query", "").strip():
|
||||
search_row.operator("text_texture.clear_preset_search", text="", icon='PANEL_CLOSE')
|
||||
|
||||
# Prepare sorted and filtered presets for display
|
||||
search_query = getattr(props, "preset_search_query", "").strip().lower()
|
||||
sorted_presets = sorted(props.presets, key=lambda p: p.name.lower())
|
||||
if search_query:
|
||||
visible_presets = [preset for preset in sorted_presets if search_query in preset.name.lower()]
|
||||
else:
|
||||
visible_presets = sorted_presets
|
||||
|
||||
for preset in sorted_presets:
|
||||
if visible_presets:
|
||||
pass
|
||||
row = layout.row(align=True)
|
||||
row.scale_y = 0.9
|
||||
|
||||
# Load button - use safe operator call
|
||||
load_lock = create_premium_lock_function()
|
||||
load_btn = safe_operator_call(row, "text_texture.load_preset", load_lock,
|
||||
text=preset.name)
|
||||
if load_btn:
|
||||
load_btn.preset_name = preset.name
|
||||
|
||||
# Quick overwrite button - use safe operator call
|
||||
overwrite_lock = create_premium_lock_function()
|
||||
overwrite_btn = safe_operator_call(row, "text_texture.save_preset", overwrite_lock,
|
||||
text="", icon='FILE_REFRESH')
|
||||
if overwrite_btn:
|
||||
overwrite_btn.overwrite = True
|
||||
overwrite_btn.preset_name = preset.name
|
||||
|
||||
# Delete button - use safe operator call
|
||||
delete_lock = create_premium_lock_function()
|
||||
delete_btn = safe_operator_call(row, "text_texture.delete_preset", delete_lock,
|
||||
text="", icon='TRASH')
|
||||
if delete_btn:
|
||||
delete_btn.preset_name = preset.name
|
||||
|
||||
for preset in visible_presets:
|
||||
pass
|
||||
row = col.row(align=True)
|
||||
row.scale_y = 0.95
|
||||
|
||||
is_active = getattr(props, "last_loaded_preset", "") == preset.name
|
||||
load_icon = 'CHECKMARK' if is_active else 'PRESET'
|
||||
|
||||
load_lock = create_premium_lock_function()
|
||||
load_btn = safe_operator_call(row, "text_texture.load_preset", load_lock,
|
||||
text=preset.name, icon=load_icon)
|
||||
if load_btn:
|
||||
load_btn.preset_name = preset.name
|
||||
|
||||
source_icon = 'FILE_BLEND' if preset.source == 'BLEND_FILE' else 'FILE_CACHE'
|
||||
source_label = "Project" if preset.source == 'BLEND_FILE' else "Global"
|
||||
row.label(text=source_label, icon=source_icon)
|
||||
|
||||
actions = row.row(align=True)
|
||||
actions.alignment = 'RIGHT'
|
||||
|
||||
overwrite_lock = create_premium_lock_function()
|
||||
overwrite_btn = safe_operator_call(actions, "text_texture.save_preset", overwrite_lock,
|
||||
text="", icon='FILE_REFRESH')
|
||||
if overwrite_btn:
|
||||
overwrite_btn.overwrite = True
|
||||
overwrite_btn.preset_name = preset.name
|
||||
|
||||
delete_lock = create_premium_lock_function()
|
||||
delete_btn = safe_operator_call(actions, "text_texture.delete_preset", delete_lock,
|
||||
text="", icon='TRASH')
|
||||
if delete_btn:
|
||||
delete_btn.preset_name = preset.name
|
||||
else:
|
||||
pass
|
||||
col.label(text="No presets match your search", icon='INFO')
|
||||
else:
|
||||
pass
|
||||
layout.label(text="No presets yet")
|
||||
layout.label(text="Save your settings above to create your first preset.")
|
||||
col.label(text="No presets yet", icon='INFO')
|
||||
col.label(text="Save your current settings to start your preset library.")
|
||||
|
||||
def draw_shader_section(layout, props):
|
||||
pass
|
||||
|
||||
Binary file not shown.
17
tests/unit/operators/test_preset_ui_controls.py
Normal file
17
tests/unit/operators/test_preset_ui_controls.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from unittest.mock import Mock
|
||||
|
||||
from src.operators.preset_ops import TEXT_TEXTURE_OT_clear_preset_search
|
||||
|
||||
|
||||
def test_clear_preset_search_resets_query():
|
||||
props = Mock()
|
||||
props.preset_search_query = "Glow"
|
||||
|
||||
context = Mock()
|
||||
context.scene.text_texture_props = props
|
||||
|
||||
op = TEXT_TEXTURE_OT_clear_preset_search()
|
||||
result = op.execute(context)
|
||||
|
||||
assert result == {'FINISHED'}
|
||||
assert props.preset_search_query == ""
|
||||
1
tests/unit/presets/__init__.py
Normal file
1
tests/unit/presets/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
263
tests/unit/presets/test_storage_system.py
Normal file
263
tests/unit/presets/test_storage_system.py
Normal file
@@ -0,0 +1,263 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock
|
||||
|
||||
import pytest
|
||||
|
||||
import src.presets.storage_system as storage_system
|
||||
|
||||
|
||||
class MockPresetItem:
|
||||
"""Simple container for preset collection entries."""
|
||||
|
||||
def __init__(self):
|
||||
self.name = ""
|
||||
self.source = ""
|
||||
|
||||
|
||||
class MockPresetCollection:
|
||||
"""Minimal stand-in for Blender's CollectionProperty."""
|
||||
|
||||
def __init__(self):
|
||||
self._items = []
|
||||
self.cleared = False
|
||||
|
||||
def clear(self):
|
||||
self.cleared = True
|
||||
self._items.clear()
|
||||
|
||||
def add(self):
|
||||
item = MockPresetItem()
|
||||
self._items.append(item)
|
||||
return item
|
||||
|
||||
def remove(self, index):
|
||||
self._items.pop(index)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._items)
|
||||
|
||||
def __len__(self):
|
||||
return len(self._items)
|
||||
|
||||
|
||||
class MockProps:
|
||||
"""Mock text texture properties containing preset collection."""
|
||||
|
||||
def __init__(self):
|
||||
self.presets = MockPresetCollection()
|
||||
self.save_with_blend_file = False
|
||||
self.new_preset_name = ""
|
||||
self.active_preset_index = 0
|
||||
self.preset_search_query = ""
|
||||
self.last_loaded_preset = ""
|
||||
|
||||
|
||||
class MockScene(dict):
|
||||
"""Dictionary-like scene object supporting custom properties."""
|
||||
|
||||
def __init__(self, props=None):
|
||||
super().__init__()
|
||||
self.text_texture_props = props
|
||||
|
||||
def __bool__(self):
|
||||
# Scene should be truthy even when no custom properties are set
|
||||
return True
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def make_scene(mock_bpy_fixture):
|
||||
"""Factory for assigning a mock scene to bpy.context."""
|
||||
|
||||
def _make_scene(props=None):
|
||||
scene = MockScene(props)
|
||||
mock_bpy_fixture.context.scene = scene
|
||||
return scene
|
||||
|
||||
return _make_scene
|
||||
|
||||
|
||||
def test_get_persistent_preset_path_creates_directory(tmp_path, mock_bpy_fixture):
|
||||
config_dir = tmp_path / "config"
|
||||
config_dir.mkdir()
|
||||
mock_bpy_fixture.utils.user_resource.return_value = str(config_dir)
|
||||
|
||||
path = storage_system.get_persistent_preset_path()
|
||||
expected = config_dir / "text_texture_generator" / "presets"
|
||||
|
||||
assert Path(path) == expected
|
||||
assert expected.is_dir()
|
||||
|
||||
|
||||
def test_save_preset_unified_persistent_writes_file(tmp_path, monkeypatch):
|
||||
persistent_dir = tmp_path / "persistent"
|
||||
persistent_dir.mkdir()
|
||||
monkeypatch.setattr(storage_system, "get_persistent_preset_path", lambda: str(persistent_dir))
|
||||
|
||||
preset_data = {"text": "hello", "font_size": 24}
|
||||
result = storage_system.save_preset_unified(preset_data, "SamplePreset", save_to_blend_file=False)
|
||||
|
||||
saved_path = persistent_dir / "SamplePreset.json"
|
||||
assert result is True
|
||||
assert saved_path.exists()
|
||||
with saved_path.open("r") as handle:
|
||||
assert json.load(handle) == preset_data
|
||||
|
||||
|
||||
def test_save_preset_unified_blend_delegates(monkeypatch):
|
||||
mock_save = Mock(return_value=True)
|
||||
monkeypatch.setattr(storage_system, "save_blend_file_preset", mock_save)
|
||||
|
||||
payload = {"text": "blend storage"}
|
||||
result = storage_system.save_preset_unified(payload, "BlendPreset", save_to_blend_file=True)
|
||||
|
||||
assert result is True
|
||||
mock_save.assert_called_once_with("BlendPreset", payload)
|
||||
|
||||
|
||||
def test_get_blend_file_presets_initializes_empty_collection(make_scene):
|
||||
scene = make_scene()
|
||||
|
||||
presets = storage_system.get_blend_file_presets()
|
||||
|
||||
assert presets == {}
|
||||
assert scene["text_texture_presets"] == "{}"
|
||||
|
||||
|
||||
def test_save_blend_file_preset_stores_json_in_scene(make_scene):
|
||||
scene = make_scene()
|
||||
preset_content = {"text": "stored", "font_size": 18}
|
||||
|
||||
result = storage_system.save_blend_file_preset("StoredPreset", preset_content)
|
||||
|
||||
assert result is True
|
||||
stored = json.loads(scene["text_texture_presets"])
|
||||
assert stored["StoredPreset"] == preset_content
|
||||
roundtrip = storage_system.get_blend_file_presets()
|
||||
assert roundtrip["StoredPreset"] == preset_content
|
||||
|
||||
|
||||
def test_delete_blend_file_preset_removes_entry(make_scene):
|
||||
scene = make_scene()
|
||||
storage_system.save_blend_file_preset("TempPreset", {"value": 1})
|
||||
|
||||
removed = storage_system.delete_blend_file_preset("TempPreset")
|
||||
assert removed is True
|
||||
assert json.loads(scene["text_texture_presets"]) == {}
|
||||
|
||||
removed_again = storage_system.delete_blend_file_preset("TempPreset")
|
||||
assert removed_again is False
|
||||
|
||||
|
||||
def test_get_unified_preset_list_prioritizes_blend_presets(tmp_path, monkeypatch):
|
||||
persistent_dir = tmp_path / "persistent"
|
||||
persistent_dir.mkdir()
|
||||
monkeypatch.setattr(storage_system, "get_persistent_preset_path", lambda: str(persistent_dir))
|
||||
|
||||
with (persistent_dir / "alpha.json").open("w") as handle:
|
||||
json.dump({"source": "persistent"}, handle)
|
||||
with (persistent_dir / "shared.json").open("w") as handle:
|
||||
json.dump({"source": "persistent-shared"}, handle)
|
||||
|
||||
monkeypatch.setattr(
|
||||
storage_system,
|
||||
"get_blend_file_presets",
|
||||
lambda: {
|
||||
"shared": {"source": "blend"},
|
||||
"blend_only": {"source": "blend-only"},
|
||||
},
|
||||
)
|
||||
|
||||
presets = storage_system.get_unified_preset_list()
|
||||
|
||||
assert presets["alpha"][1] == "PERSISTENT_FILE"
|
||||
assert presets["alpha"][0]["source"] == "persistent"
|
||||
assert presets["shared"][1] == "BLEND_FILE"
|
||||
assert presets["shared"][0]["source"] == "blend"
|
||||
assert presets["blend_only"][1] == "BLEND_FILE"
|
||||
|
||||
|
||||
def test_migrate_from_legacy_system_migrates_files_and_creates_backup(tmp_path, monkeypatch):
|
||||
legacy_dir = tmp_path / "legacy"
|
||||
persistent_dir = tmp_path / "persistent"
|
||||
legacy_dir.mkdir()
|
||||
persistent_dir.mkdir()
|
||||
|
||||
legacy_payload = {"example": True}
|
||||
with (legacy_dir / "legacy_preset.json").open("w") as handle:
|
||||
json.dump(legacy_payload, handle)
|
||||
with (persistent_dir / "existing.json").open("w") as handle:
|
||||
json.dump({"existing": True}, handle)
|
||||
|
||||
monkeypatch.setattr(storage_system, "get_preset_path", lambda: str(legacy_dir))
|
||||
monkeypatch.setattr(storage_system, "get_persistent_preset_path", lambda: str(persistent_dir))
|
||||
|
||||
result = storage_system.migrate_from_legacy_system()
|
||||
|
||||
migrated_file = persistent_dir / "legacy_preset.json"
|
||||
backup_file = persistent_dir / "legacy_backup" / "legacy_preset.json"
|
||||
marker_file = persistent_dir / ".migration_complete"
|
||||
|
||||
assert result is True
|
||||
assert migrated_file.exists()
|
||||
assert json.loads(migrated_file.read_text()) == legacy_payload
|
||||
assert backup_file.exists()
|
||||
assert marker_file.exists()
|
||||
|
||||
|
||||
def test_initialize_presets_populates_props_collection(make_scene, monkeypatch):
|
||||
props = MockProps()
|
||||
# Seed with existing entry to ensure clear() is invoked
|
||||
props.presets.add().name = "stale"
|
||||
migrate_mock = Mock(return_value=True)
|
||||
monkeypatch.setattr(storage_system, "migrate_from_legacy_system", migrate_mock)
|
||||
monkeypatch.setattr(
|
||||
storage_system,
|
||||
"get_unified_preset_list",
|
||||
lambda: {
|
||||
"BlendPreset": ({"value": "blend"}, "BLEND_FILE"),
|
||||
"PersistentPreset": ({"value": "persistent"}, "PERSISTENT_FILE"),
|
||||
},
|
||||
)
|
||||
|
||||
scene = make_scene(props)
|
||||
storage_system.initialize_presets()
|
||||
|
||||
assert migrate_mock.called
|
||||
assert props.presets.cleared is True
|
||||
assert len(props.presets) == 2
|
||||
entries = {(item.name, item.source) for item in props.presets}
|
||||
assert ("BlendPreset", "BLEND_FILE") in entries
|
||||
assert ("PersistentPreset", "PERSISTENT_FILE") in entries
|
||||
assert "text_texture_presets" not in scene # ensure scene custom data untouched
|
||||
|
||||
|
||||
def test_ensure_presets_available_refreshes_when_empty(make_scene, monkeypatch):
|
||||
props = MockProps()
|
||||
make_scene(props)
|
||||
refresh_mock = Mock(return_value=True)
|
||||
monkeypatch.setattr(storage_system, "refresh_presets_sync", refresh_mock)
|
||||
|
||||
result = storage_system.ensure_presets_available()
|
||||
|
||||
assert result is True
|
||||
refresh_mock.assert_called_once()
|
||||
|
||||
|
||||
def test_ensure_presets_available_returns_true_when_presets_loaded(make_scene, monkeypatch):
|
||||
props = MockProps()
|
||||
props.presets.add().name = "cached"
|
||||
make_scene(props)
|
||||
refresh_mock = Mock(return_value=True)
|
||||
monkeypatch.setattr(storage_system, "refresh_presets_sync", refresh_mock)
|
||||
|
||||
result = storage_system.ensure_presets_available()
|
||||
|
||||
assert result is True
|
||||
refresh_mock.assert_not_called()
|
||||
|
||||
|
||||
def test_refresh_presets_sync_returns_false_without_scene(mock_bpy_fixture):
|
||||
mock_bpy_fixture.context.scene = None
|
||||
assert storage_system.refresh_presets_sync() is False
|
||||
Reference in New Issue
Block a user