From 695365a0ad68e4534425c64b7a6a4b6598b68ef7 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Tue, 4 Aug 2026 04:07:41 +0900 Subject: feat(plugin): dispatch plugin event subscribers through the RPC worker Wires the addPlugin subscriber branch end to end: EventSubscriberInterface and Capable become fallible and dyn-compatible (their sole implementor is the PHP plugin proxy, which answers getSubscribedEvents over RPC), listeners register as Callable::PhpMethod and are invoked with a per-call event handle, and the R table now drops entries when a child-side stub destructs. The R table keeps its IndexMap with monotonically increasing handles, so released handles are never reused and no generation counter is needed. Upstream has no subscriber-plugin test, so the path is covered by a Shirabe-owned fixture exercising all three getSubscribedEvents shapes. Co-Authored-By: Claude Fable 5 --- .../shirabe/tests/plugin/plugin_installer_test.rs | 45 +++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) (limited to 'crates/shirabe/tests/plugin/plugin_installer_test.rs') diff --git a/crates/shirabe/tests/plugin/plugin_installer_test.rs b/crates/shirabe/tests/plugin/plugin_installer_test.rs index 5dfb6e06..70548ba6 100644 --- a/crates/shirabe/tests/plugin/plugin_installer_test.rs +++ b/crates/shirabe/tests/plugin/plugin_installer_test.rs @@ -35,7 +35,7 @@ use tempfile::TempDir; /// The register/activate flow runs the plugin in the real PHP worker; without a PHP binary the /// worker cannot start. Tests exercising it return early, following the convention of the /// non-mock tests in `shirabe-php-rpc`. -fn php_runtime_available() -> bool { +pub(crate) fn php_runtime_available() -> bool { PhpExecutableFinder::new().find(false).is_some() } @@ -45,7 +45,7 @@ fn php_runtime_available() -> bool { /// race the other's `class_exists` checks, so the worker-touching tests run serialized. static PHP_WORKER_TESTS: std::sync::Mutex<()> = std::sync::Mutex::new(()); -fn lock_php_worker() -> std::sync::MutexGuard<'static, ()> { +pub(crate) fn lock_php_worker() -> std::sync::MutexGuard<'static, ()> { PHP_WORKER_TESTS .lock() .unwrap_or_else(|poisoned| poisoned.into_inner()) @@ -62,6 +62,16 @@ fn fixtures_dir() -> String { .to_string() } +/// Shirabe-owned fixtures with no upstream counterpart (see `subscriber_test.rs`). +pub(crate) fn shirabe_fixtures_dir() -> String { + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/plugin/fixtures"); + dir.canonicalize() + .expect("the Shirabe plugin fixtures directory must exist") + .to_str() + .unwrap() + .to_string() +} + // PHP mocks `Composer\Downloader\DownloadManager`; install/update/remove resolve to null and the // other methods are never reached by these tests. mockall::mock! { @@ -184,7 +194,16 @@ impl InstallationManagerInterface for MockInstallationManager { } fn get_install_path(&self, package: PackageInterfaceHandle) -> Option { - Some(format!("{}/{}", fixtures_dir(), package.get_pretty_name())) + let upstream = format!("{}/{}", fixtures_dir(), package.get_pretty_name()); + if std::path::Path::new(&upstream).exists() { + return Some(upstream); + } + // Shirabe-specific fixtures (subscriber_test) live next to this test binary. + Some(format!( + "{}/{}", + shirabe_fixtures_dir(), + package.get_pretty_name() + )) } fn set_output_progress(&mut self, _output_progress: bool) {} @@ -214,20 +233,20 @@ fn locker_installation_manager( } #[derive(Debug)] -struct SetUp { - io: std::rc::Rc>, - io_dyn: std::rc::Rc>, - pm: std::rc::Rc>, +pub(crate) struct SetUp { + pub(crate) io: std::rc::Rc>, + pub(crate) io_dyn: std::rc::Rc>, + pub(crate) pm: std::rc::Rc>, autoload_generator: std::rc::Rc>, packages: Vec, - repository: InstalledRepositoryInterfaceHandle, + pub(crate) repository: InstalledRepositoryInterfaceHandle, // Keeps the Composer alive; PluginManager only holds a weak back-reference to it. - composer: ComposerHandle, + pub(crate) composer: ComposerHandle, // PHP's tearDown() removes this directory; TempDir does the same on drop. _directory: TempDir, } -fn set_up() -> SetUp { +pub(crate) fn set_up() -> SetUp { let loader = JsonLoader::new(Box::new(ArrayLoader::new(None, false))); let mut packages = vec![]; let directory = TempDir::new().unwrap(); @@ -380,7 +399,7 @@ fn plugin_property( } } -fn new_installer(set_up: &SetUp) -> PluginInstaller { +pub(crate) fn new_installer(set_up: &SetUp) -> PluginInstaller { PluginInstaller::new( set_up.io_dyn.clone(), set_up.composer.upcast().downgrade(), @@ -740,9 +759,9 @@ impl PluginInterface for CapablePlugin { } impl Capable for CapablePlugin { - fn get_capabilities(&self) -> IndexMap { + fn get_capabilities(&self) -> anyhow::Result> { *self.get_capabilities_calls.borrow_mut() += 1; - IndexMap::new() + Ok(IndexMap::new()) } } -- cgit v1.3.1