import sys import tempfile import textwrap import types import unittest from pathlib import Path from unittest import mock sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) input_event_manager_stub = types.ModuleType("cthulhu.input_event_manager") input_event_manager_stub.get_manager = mock.Mock(return_value=mock.Mock()) sys.modules["cthulhu.input_event_manager"] = input_event_manager_stub from cthulhu.plugin_system_manager import PluginInfo, PluginSystemManager PLUGIN_TEMPLATE = """ from cthulhu.plugin import Plugin, cthulhu_hookimpl class {class_name}(Plugin): def __init__(self): super().__init__() self.activation_count = 0 self.deactivation_count = 0 @cthulhu_hookimpl def activate(self, plugin=None): self.activation_count += 1 @cthulhu_hookimpl def deactivate(self, plugin=None): self.deactivation_count += 1 """ PLUGIN_TEMPLATE_PLAIN_SIGNATURE = """ from cthulhu.plugin import Plugin, cthulhu_hookimpl class {class_name}(Plugin): def __init__(self): super().__init__() self.activation_count = 0 self.deactivation_count = 0 @cthulhu_hookimpl def activate(self): self.activation_count += 1 @cthulhu_hookimpl def deactivate(self): self.deactivation_count += 1 """ class PluginSystemManagerRegressionTests(unittest.TestCase): def _create_plugin_info( self, root_dir: str, module_name: str, template: str = PLUGIN_TEMPLATE, ) -> PluginInfo: plugin_dir = Path(root_dir) / module_name plugin_dir.mkdir() (plugin_dir / "plugin.py").write_text( textwrap.dedent(template.format(class_name=module_name)), encoding="utf-8", ) return PluginInfo( module_name, module_name, str(plugin_dir), canonical_name=module_name, source_id="test", origin="test", ) def _create_manager(self) -> PluginSystemManager: app = mock.Mock() app.getSignalManager.return_value = mock.Mock() return PluginSystemManager(app) @mock.patch("cthulhu.plugin_system_manager.dbus_service.get_remote_controller") def test_loading_second_plugin_does_not_reactivate_first_plugin(self, remote_controller): remote_controller.return_value = mock.Mock() with tempfile.TemporaryDirectory() as temp_dir: manager = self._create_manager() first_plugin = self._create_plugin_info(temp_dir, "FirstPlugin") second_plugin = self._create_plugin_info(temp_dir, "SecondPlugin") self.assertTrue(manager.loadPlugin(first_plugin)) self.assertEqual(first_plugin.instance.activation_count, 1) self.assertTrue(manager.loadPlugin(second_plugin)) self.assertEqual(first_plugin.instance.activation_count, 1) self.assertEqual(second_plugin.instance.activation_count, 1) @mock.patch("cthulhu.plugin_system_manager.dbus_service.get_remote_controller") def test_unloading_plugin_does_not_deactivate_other_loaded_plugins(self, remote_controller): remote_controller.return_value = mock.Mock() with tempfile.TemporaryDirectory() as temp_dir: manager = self._create_manager() first_plugin = self._create_plugin_info(temp_dir, "FirstPlugin") second_plugin = self._create_plugin_info(temp_dir, "SecondPlugin") self.assertTrue(manager.loadPlugin(first_plugin)) self.assertTrue(manager.loadPlugin(second_plugin)) second_instance = second_plugin.instance self.assertTrue(manager.unloadPlugin(second_plugin)) self.assertEqual(first_plugin.instance.deactivation_count, 0) self.assertEqual(second_instance.deactivation_count, 1) @mock.patch("cthulhu.plugin_system_manager.dbus_service.get_remote_controller") def test_loading_plugin_with_plain_activate_signature_succeeds(self, remote_controller): remote_controller.return_value = mock.Mock() with tempfile.TemporaryDirectory() as temp_dir: manager = self._create_manager() plugin_info = self._create_plugin_info( temp_dir, "PlainSignaturePlugin", template=PLUGIN_TEMPLATE_PLAIN_SIGNATURE, ) self.assertTrue(manager.loadPlugin(plugin_info)) instance = plugin_info.instance self.assertEqual(instance.activation_count, 1) self.assertTrue(manager.unloadPlugin(plugin_info)) self.assertEqual(instance.deactivation_count, 1) self.assertEqual(plugin_info.instance, None) if __name__ == "__main__": unittest.main()