"""
测试 engine_registry 模块
"""
import pytest
from unittest.mock import Mock
from core.engine_registry import (
EngineMetadata,
EngineRegistry,
register_engine
)
from core.config_manager import EngineConfig, RenpyConfig, NaninovelConfig
from core.exceptions import EngineNotRegisteredError
[文档]
class TestEngineRegistry:
"""测试 EngineRegistry 类"""
[文档]
@pytest.fixture(autouse=True)
def reset_registry(self):
"""每个测试前重置注册表"""
EngineRegistry.reset()
yield
EngineRegistry.reset()
[文档]
def test_singleton_pattern(self):
"""测试单例模式"""
registry1 = EngineRegistry()
registry2 = EngineRegistry()
assert registry1 is registry2
[文档]
def test_register_engine(self):
"""测试注册引擎"""
processor_factory = Mock()
metadata = EngineMetadata(
name="renpy",
display_name="Ren'Py",
file_extension=".rpy",
config_class=RenpyConfig,
processor_factory=processor_factory
)
EngineRegistry.register(metadata)
assert EngineRegistry.is_registered("renpy")
[文档]
def test_register_duplicate_engine(self):
"""测试注册重复引擎(应该覆盖)"""
processor_factory1 = Mock()
processor_factory2 = Mock()
metadata1 = EngineMetadata(
name="test",
display_name="Test 1",
file_extension=".test",
config_class=EngineConfig,
processor_factory=processor_factory1
)
metadata2 = EngineMetadata(
name="test",
display_name="Test 2",
file_extension=".test2",
config_class=EngineConfig,
processor_factory=processor_factory2
)
EngineRegistry.register(metadata1)
EngineRegistry.register(metadata2)
# 应该被覆盖为第二个
retrieved = EngineRegistry.get("test")
assert retrieved.display_name == "Test 2"
assert retrieved.file_extension == ".test2"
[文档]
def test_get_registered_engine(self):
"""测试获取已注册引擎"""
processor_factory = Mock()
metadata = EngineMetadata(
name="naninovel",
display_name="Naninovel",
file_extension=".nani",
config_class=NaninovelConfig,
processor_factory=processor_factory,
description="Naninovel engine"
)
EngineRegistry.register(metadata)
retrieved = EngineRegistry.get("naninovel")
assert retrieved.name == "naninovel"
assert retrieved.display_name == "Naninovel"
assert retrieved.file_extension == ".nani"
assert retrieved.config_class == NaninovelConfig
assert retrieved.description == "Naninovel engine"
[文档]
def test_get_unregistered_engine(self):
"""测试获取未注册引擎(应该抛出异常)"""
with pytest.raises(EngineNotRegisteredError, match="引擎 'nonexistent' 未注册"):
EngineRegistry.get("nonexistent")
[文档]
def test_is_registered_true(self):
"""测试检查已注册引擎"""
processor_factory = Mock()
metadata = EngineMetadata(
name="test",
display_name="Test",
file_extension=".test",
config_class=EngineConfig,
processor_factory=processor_factory
)
EngineRegistry.register(metadata)
assert EngineRegistry.is_registered("test") is True
[文档]
def test_is_registered_false(self):
"""测试检查未注册引擎"""
assert EngineRegistry.is_registered("nonexistent") is False
[文档]
def test_list_engines_empty(self):
"""测试列出引擎(空注册表)"""
engines = EngineRegistry.list_engines()
assert engines == {}
assert isinstance(engines, dict)
[文档]
def test_list_engines_with_multiple_engines(self):
"""测试列出多个引擎"""
processor1 = Mock()
processor2 = Mock()
metadata1 = EngineMetadata(
name="renpy",
display_name="Ren'Py",
file_extension=".rpy",
config_class=RenpyConfig,
processor_factory=processor1
)
metadata2 = EngineMetadata(
name="naninovel",
display_name="Naninovel",
file_extension=".nani",
config_class=NaninovelConfig,
processor_factory=processor2
)
EngineRegistry.register(metadata1)
EngineRegistry.register(metadata2)
engines = EngineRegistry.list_engines()
assert len(engines) == 2
assert "renpy" in engines
assert "naninovel" in engines
assert engines["renpy"].display_name == "Ren'Py"
assert engines["naninovel"].display_name == "Naninovel"
[文档]
def test_list_engines_returns_copy(self):
"""测试 list_engines 返回副本(不影响原注册表)"""
processor = Mock()
metadata = EngineMetadata(
name="test",
display_name="Test",
file_extension=".test",
config_class=EngineConfig,
processor_factory=processor
)
EngineRegistry.register(metadata)
engines = EngineRegistry.list_engines()
# 修改返回的字典
engines.clear()
# 原注册表不应该被影响
assert EngineRegistry.is_registered("test") is True
[文档]
def test_reset_registry(self):
"""测试重置注册表"""
processor = Mock()
metadata = EngineMetadata(
name="test",
display_name="Test",
file_extension=".test",
config_class=EngineConfig,
processor_factory=processor
)
EngineRegistry.register(metadata)
assert EngineRegistry.is_registered("test") is True
EngineRegistry.reset()
assert EngineRegistry.is_registered("test") is False
assert EngineRegistry.list_engines() == {}
[文档]
class TestRegisterEngineDecorator:
"""测试 register_engine 装饰器"""
[文档]
@pytest.fixture(autouse=True)
def reset_registry(self):
"""每个测试前重置注册表"""
EngineRegistry.reset()
yield
EngineRegistry.reset()
[文档]
def test_register_engine_decorator_basic(self):
"""测试基础装饰器功能"""
@register_engine(
name="test_engine",
display_name="Test Engine",
file_extension=".test",
config_class=EngineConfig
)
def create_processor():
return Mock()
# 验证引擎已注册
assert EngineRegistry.is_registered("test_engine")
# 验证元数据
metadata = EngineRegistry.get("test_engine")
assert metadata.name == "test_engine"
assert metadata.display_name == "Test Engine"
assert metadata.file_extension == ".test"
assert metadata.config_class == EngineConfig
# 验证装饰器返回原函数
assert callable(create_processor)
[文档]
def test_register_engine_decorator_with_all_params(self):
"""测试装饰器包含所有参数"""
validator = Mock()
@register_engine(
name="full_engine",
display_name="Full Engine",
file_extension=".full",
config_class=RenpyConfig,
validator_factory=validator,
description="A fully configured engine"
)
def create_processor():
return Mock()
metadata = EngineRegistry.get("full_engine")
assert metadata.name == "full_engine"
assert metadata.display_name == "Full Engine"
assert metadata.file_extension == ".full"
assert metadata.config_class == RenpyConfig
assert metadata.validator_factory == validator
assert metadata.description == "A fully configured engine"
[文档]
def test_register_engine_decorator_preserves_function(self):
"""测试装饰器保留原函数功能"""
@register_engine(
name="func_test",
display_name="Function Test",
file_extension=".func",
config_class=EngineConfig
)
def create_processor(config):
return f"Processor with {config}"
# 验证函数仍然可以正常调用
result = create_processor("test_config")
assert result == "Processor with test_config"
[文档]
def test_register_multiple_engines_with_decorator(self):
"""测试使用装饰器注册多个引擎"""
@register_engine(
name="engine1",
display_name="Engine 1",
file_extension=".e1",
config_class=EngineConfig
)
def create_processor1():
return "processor1"
@register_engine(
name="engine2",
display_name="Engine 2",
file_extension=".e2",
config_class=EngineConfig
)
def create_processor2():
return "processor2"
# 验证两个引擎都已注册
assert EngineRegistry.is_registered("engine1")
assert EngineRegistry.is_registered("engine2")
engines = EngineRegistry.list_engines()
assert len(engines) == 2
[文档]
class TestEngineRegistryIntegration:
"""集成测试:测试完整的引擎注册和使用流程"""
[文档]
@pytest.fixture(autouse=True)
def reset_registry(self):
"""每个测试前重置注册表"""
EngineRegistry.reset()
yield
EngineRegistry.reset()
[文档]
def test_full_workflow(self):
"""测试完整工作流程"""
# 1. 使用装饰器注册引擎
@register_engine(
name="renpy",
display_name="Ren'Py",
file_extension=".rpy",
config_class=RenpyConfig,
description="Ren'Py visual novel engine"
)
def create_renpy_processor(config):
return f"RenpyProcessor({config})"
# 2. 检查引擎是否已注册
assert EngineRegistry.is_registered("renpy")
# 3. 获取引擎元数据
metadata = EngineRegistry.get("renpy")
assert metadata.display_name == "Ren'Py"
# 4. 使用 processor_factory 创建处理器
processor = metadata.processor_factory("test_config")
assert processor == "RenpyProcessor(test_config)"
# 5. 列出所有引擎
engines = EngineRegistry.list_engines()
assert "renpy" in engines
[文档]
def test_multiple_engines_workflow(self):
"""测试多引擎工作流程"""
# 注册多个引擎
@register_engine(
name="renpy",
display_name="Ren'Py",
file_extension=".rpy",
config_class=RenpyConfig
)
def create_renpy():
return "renpy_processor"
@register_engine(
name="naninovel",
display_name="Naninovel",
file_extension=".nani",
config_class=NaninovelConfig
)
def create_naninovel():
return "naninovel_processor"
# 验证所有引擎
engines = EngineRegistry.list_engines()
assert len(engines) == 2
# 分别获取并使用
renpy_meta = EngineRegistry.get("renpy")
nani_meta = EngineRegistry.get("naninovel")
assert renpy_meta.processor_factory() == "renpy_processor"
assert nani_meta.processor_factory() == "naninovel_processor"