""" 定向输入事件在源插件本体与其分身之间的投递归属测试。 """ import sys from types import ModuleType from app.runtime.event.dispatch import EventDispatcher from app.runtime.event.registry import EventRegistry def _install_plugin_handler( monkeypatch, *, module_name: str, declared_class_name: str, runtime_class_name: str, ): """ 在指定模块命名空间下造出一个插件事件处理器,并返回其注册标识。 复刻装载器造出的运行事实:分身与本体共享源码,因此处理器的 ``__qualname__`` 恒为源类名;区别只在分身被放进自己的模块命名空间、并被改写了类的 ``__name__``。 :param monkeypatch: 用于把假模块登记进 ``sys.modules`` 并在用例结束后还原 :param module_name: 该实例的模块名 :param declared_class_name: 源码里声明的类名,即处理器限定名的前缀 :param runtime_class_name: 运行身份的类名,分身为其实例 ID :return: 处理器函数与其注册标识 """ module = ModuleType(module_name) def handle_message_action(self, event): """ 占位的定向输入事件处理器。 """ return self, event handle_message_action.__module__ = module_name handle_message_action.__qualname__ = f"{declared_class_name}.handle_message_action" plugin_class = type( declared_class_name, (), {"handle_message_action": handle_message_action}, ) plugin_class.__module__ = module_name plugin_class.__name__ = runtime_class_name setattr(module, declared_class_name, plugin_class) monkeypatch.setitem(sys.modules, module_name, module) handler = plugin_class.handle_message_action return handler, EventRegistry.handler_identifier(handler) def test_targeted_event_reaches_the_clone_that_opened_the_input_session(monkeypatch): """ 定向到分身的输入事件必须投递给该分身自己的处理器。 分身共享源码,处理器限定名里的类名始终是源类名,只比类名会让「目标是分身」 这一判断恒不成立,用户在分身里发起的输入会话随后收不到任何回复。 """ handler, handler_id = _install_plugin_handler( monkeypatch, module_name="app.plugins.demopluginwork", declared_class_name="DemoPlugin", runtime_class_name="DemoPluginWork", ) assert handler_id == "app.plugins.demopluginwork.DemoPlugin.handle_message_action" assert EventDispatcher.should_dispatch_to_target_plugin( handler, handler_id, "DemoPluginWork", ) is True def test_targeted_event_for_the_host_is_not_broadcast_to_its_clones(monkeypatch): """ 定向到本体的输入事件不得同时落进它的分身。 分身与本体的处理器限定名完全一致,只比类名会让本体的定向事件被全部分身一起 收到——定向投递本来就是为了不让自由文本被别的实例看到。 """ host_handler, host_id = _install_plugin_handler( monkeypatch, module_name="app.plugins.demoplugin", declared_class_name="DemoPlugin", runtime_class_name="DemoPlugin", ) clone_handler, clone_id = _install_plugin_handler( monkeypatch, module_name="app.plugins.demopluginwork", declared_class_name="DemoPlugin", runtime_class_name="DemoPluginWork", ) assert EventDispatcher.should_dispatch_to_target_plugin( host_handler, host_id, "DemoPlugin", ) is True assert EventDispatcher.should_dispatch_to_target_plugin( clone_handler, clone_id, "DemoPlugin", ) is False assert EventDispatcher.should_dispatch_to_target_plugin( host_handler, host_id, "DemoPluginWork", ) is False def test_non_plugin_handlers_keep_matching_by_declared_class_name(monkeypatch): """ 宿主侧处理器不在插件命名空间里,仍按声明类名匹配。 """ handler, handler_id = _install_plugin_handler( monkeypatch, module_name="tests.hosted.demo_plugin", declared_class_name="DemoPlugin", runtime_class_name="DemoPlugin", ) assert EventDispatcher.should_dispatch_to_target_plugin( handler, handler_id, "DemoPlugin", ) is True assert EventDispatcher.should_dispatch_to_target_plugin( handler, handler_id, "OtherPlugin", ) is False