diff options
Diffstat (limited to 'crates/shirabe/tests')
8 files changed, 655 insertions, 289 deletions
diff --git a/crates/shirabe/tests/autoload/autoload_generator_test.rs b/crates/shirabe/tests/autoload/autoload_generator_test.rs index 5f0629dd..85e98d81 100644 --- a/crates/shirabe/tests/autoload/autoload_generator_test.rs +++ b/crates/shirabe/tests/autoload/autoload_generator_test.rs @@ -13,7 +13,7 @@ use shirabe::io::{BufferIO, IOInterface}; use shirabe::package::handle::{AliasPackageHandle, PackageHandle, RootPackageHandle}; use shirabe::package::{Link, PackageInterfaceHandle, RootPackageInterfaceHandle}; use shirabe::repository::{ - InstalledArrayRepository, InstalledRepositoryInterface, WritableRepositoryInterface, + InstalledArrayRepository, InstalledRepositoryInterfaceHandle, WritableRepositoryInterface, }; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; @@ -39,7 +39,7 @@ impl InstallerInterface for InstallPathStubInstaller { fn is_installed( &self, - _repo: &mut dyn InstalledRepositoryInterface, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<bool> { Ok(true) @@ -64,7 +64,7 @@ impl InstallerInterface for InstallPathStubInstaller { async fn install( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { Ok(None) @@ -72,7 +72,7 @@ impl InstallerInterface for InstallPathStubInstaller { async fn update( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _initial: PackageInterfaceHandle, _target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { @@ -81,7 +81,7 @@ impl InstallerInterface for InstallPathStubInstaller { async fn uninstall( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { Ok(None) diff --git a/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs b/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs index e9620158..9cd51a7b 100644 --- a/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs +++ b/crates/shirabe/tests/event_dispatcher/event_dispatcher_test.rs @@ -426,13 +426,13 @@ mockall::mock! { fn disable_plugins(&mut self); fn is_package_installed( &mut self, - repo: &mut dyn InstalledRepositoryInterface, + repo: &shirabe::repository::InstalledRepositoryInterfaceHandle, package: PackageInterfaceHandle, ) -> anyhow::Result<bool>; fn ensure_binaries_presence(&mut self, package: PackageInterfaceHandle); fn execute( &mut self, - repo: &mut dyn InstalledRepositoryInterface, + repo: &shirabe::repository::InstalledRepositoryInterfaceHandle, operations: Vec<AnyOperation>, dev_mode: bool, run_scripts: bool, diff --git a/crates/shirabe/tests/installer/installation_manager_test.rs b/crates/shirabe/tests/installer/installation_manager_test.rs index 6cc231f5..8f2d7d4f 100644 --- a/crates/shirabe/tests/installer/installation_manager_test.rs +++ b/crates/shirabe/tests/installer/installation_manager_test.rs @@ -10,7 +10,7 @@ use shirabe::io::IOInterface; use shirabe::io::null_io::NullIO; use shirabe::package::PackageInterfaceHandle; use shirabe::package::handle::CompletePackageHandle; -use shirabe::repository::{InstalledArrayRepository, InstalledRepositoryInterface}; +use shirabe::repository::{InstalledArrayRepository, InstalledRepositoryInterfaceHandle}; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe_php_shim::PhpMixed; @@ -39,11 +39,9 @@ fn set_up() -> SetUp { SetUp { loop_, io } } -// Equivalent to `getMockBuilder(InstallerInterface::class)->getMock()`. mockall cannot generate an -// `#[async_trait]` impl for the async methods that take `&mut dyn InstalledRepositoryInterface` -// (the object lifetime async_trait inserts clashes with mockall's generated lifetimes), so the -// expectations live on inherent methods and a thin hand-written InstallerInterface impl forwards to -// them, dropping the unused `repo` argument exactly as the PHPUnit mock ignores it. The methods not +// Equivalent to `getMockBuilder(InstallerInterface::class)->getMock()`. The expectations live on +// inherent methods and a thin hand-written InstallerInterface impl forwards to them, dropping the +// unused `repo` argument exactly as the PHPUnit mock ignores it. The methods not // configured by any test (is_installed/download/prepare/cleanup/get_install_path, and the defaulted // as_binary_presence_interface/as_plugin_installer_mut) return the same defaults as an unconfigured // PHPUnit mock. @@ -75,7 +73,7 @@ impl InstallerInterface for MockInstaller { fn is_installed( &self, - _repo: &mut dyn InstalledRepositoryInterface, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<bool> { Ok(false) @@ -100,7 +98,7 @@ impl InstallerInterface for MockInstaller { async fn install( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { MockInstaller::install(self, package) @@ -108,7 +106,7 @@ impl InstallerInterface for MockInstaller { async fn update( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, initial: PackageInterfaceHandle, target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { @@ -117,7 +115,7 @@ impl InstallerInterface for MockInstaller { async fn uninstall( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { MockInstaller::uninstall(self, package) @@ -175,7 +173,7 @@ impl InstallerInterface for BinaryInstaller { fn is_installed( &self, - _repo: &mut dyn InstalledRepositoryInterface, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<bool> { Ok(false) @@ -200,7 +198,7 @@ impl InstallerInterface for BinaryInstaller { async fn install( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { Ok(None) @@ -208,7 +206,7 @@ impl InstallerInterface for BinaryInstaller { async fn update( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _initial: PackageInterfaceHandle, _target: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { @@ -217,7 +215,7 @@ impl InstallerInterface for BinaryInstaller { async fn uninstall( &self, - _repo: &std::cell::RefCell<&mut dyn InstalledRepositoryInterface>, + _repo: &InstalledRepositoryInterfaceHandle, _package: PackageInterfaceHandle, ) -> anyhow::Result<Option<PhpMixed>> { Ok(None) @@ -360,11 +358,9 @@ fn test_install() { let operation = InstallOperation::new(package); - let mut repository = InstalledArrayRepository::new().unwrap(); - run(manager.install( - &std::cell::RefCell::new(&mut repository as &mut dyn InstalledRepositoryInterface), - &operation, - )); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + run(manager.install(&repository, &operation)); } #[test] @@ -395,11 +391,9 @@ fn test_update_with_equal_types() { let operation = UpdateOperation::new(initial, target); - let mut repository = InstalledArrayRepository::new().unwrap(); - run(manager.update( - &std::cell::RefCell::new(&mut repository as &mut dyn InstalledRepositoryInterface), - &operation, - )); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + run(manager.update(&repository, &operation)); } #[test] @@ -440,11 +434,9 @@ fn test_update_with_not_equal_types() { let operation = UpdateOperation::new(initial, target); - let mut repository = InstalledArrayRepository::new().unwrap(); - run(manager.update( - &std::cell::RefCell::new(&mut repository as &mut dyn InstalledRepositoryInterface), - &operation, - )); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + run(manager.update(&repository, &operation)); } #[test] @@ -471,11 +463,9 @@ fn test_uninstall() { let operation = UninstallOperation::new(package); - let mut repository = InstalledArrayRepository::new().unwrap(); - run(manager.uninstall( - &std::cell::RefCell::new(&mut repository as &mut dyn InstalledRepositoryInterface), - &operation, - )); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + run(manager.uninstall(&repository, &operation)); } #[test] diff --git a/crates/shirabe/tests/installer/library_installer_test.rs b/crates/shirabe/tests/installer/library_installer_test.rs index baa4f13a..6402d903 100644 --- a/crates/shirabe/tests/installer/library_installer_test.rs +++ b/crates/shirabe/tests/installer/library_installer_test.rs @@ -12,9 +12,7 @@ use shirabe::installer::{BinaryInstallerInterface, InstallerInterface, LibraryIn use shirabe::io::IOInterface; use shirabe::io::null_io::NullIO; use shirabe::package::PackageInterfaceHandle; -use shirabe::repository::InstalledArrayRepository; -use shirabe::repository::RepositoryInterface; -use shirabe::repository::WritableRepositoryInterface; +use shirabe::repository::{InstalledArrayRepository, InstalledRepositoryInterfaceHandle}; use shirabe::util::filesystem::Filesystem; use shirabe_php_shim::PhpMixed; use std::fs; @@ -199,32 +197,27 @@ fn test_is_installed() { let library = LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None); let package = get_package("test/pkg", "1.0.0"); - let mut repository = InstalledArrayRepository::new().unwrap(); - assert!( - !library - .is_installed(&mut repository, package.clone()) - .unwrap() - ); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + assert!(!library.is_installed(&repository, package.clone()).unwrap()); // package being in repo is not enough to be installed - repository.add_package(package.clone()).unwrap(); - assert!( - !library - .is_installed(&mut repository, package.clone()) - .unwrap() - ); + repository + .borrow_mut() + .add_package(package.clone()) + .unwrap(); + assert!(!library.is_installed(&repository, package.clone()).unwrap()); // package being in repo and vendor/pkg/foo dir present means it is seen as installed let pkg_dir = format!("{}/{}", setup.vendor_dir, package.get_pretty_name()); fs::create_dir_all(&pkg_dir).unwrap(); - assert!( - library - .is_installed(&mut repository, package.clone()) - .unwrap() - ); + assert!(library.is_installed(&repository, package.clone()).unwrap()); - repository.remove_package(package.clone()).unwrap(); - assert!(!library.is_installed(&mut repository, package).unwrap()); + repository + .borrow_mut() + .remove_package(package.clone()) + .unwrap(); + assert!(!library.is_installed(&repository, package).unwrap()); tear_down(&mut setup); } @@ -250,18 +243,13 @@ fn test_install() { let library = LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None); - let mut repository = InstalledArrayRepository::new().unwrap(); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); - run(library.install( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - package.clone(), - )) - .unwrap(); + run(library.install(&repository, package.clone())).unwrap(); // PHP asserts repository->addPackage was called once with $package. - assert!(repository.has_package(package).unwrap()); + assert!(repository.borrow_mut().has_package(package).unwrap()); assert!( std::path::Path::new(&setup.vendor_dir).exists(), @@ -308,20 +296,17 @@ fn test_update() { .returning(|_, _, _| Ok(None)); set_download_manager(&setup, dm); - let mut repository = InstalledArrayRepository::new().unwrap(); - repository.add_package(initial.clone()).unwrap(); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + repository + .borrow_mut() + .add_package(initial.clone()) + .unwrap(); // The default Filesystem is fine; the LibraryInstaller's own filesystem performs the rename. let library = LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None); - run(library.update( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - initial.clone(), - target.clone(), - )) - .unwrap(); + run(library.update(&repository, initial.clone(), target.clone())).unwrap(); assert!( std::path::Path::new(&new_target_dir).exists(), @@ -329,8 +314,13 @@ fn test_update() { ); assert!(!std::path::Path::new(&old_target_dir).exists()); - assert!(!repository.has_package(initial.clone()).unwrap()); - assert!(repository.has_package(target.clone()).unwrap()); + assert!( + !repository + .borrow_mut() + .has_package(initial.clone()) + .unwrap() + ); + assert!(repository.borrow_mut().has_package(target.clone()).unwrap()); assert!( std::path::Path::new(&setup.vendor_dir).exists(), @@ -342,16 +332,7 @@ fn test_update() { ); // Updating again, with the initial package no longer installed, fails. - assert!( - run(library.update( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface - ), - initial, - target - )) - .is_err() - ); + assert!(run(library.update(&repository, initial, target)).is_err()); tear_down(&mut setup); } @@ -380,30 +361,25 @@ fn test_uninstall() { // PHP mocks hasPackage to return (true, false) over two calls; a real repository // seeded with the package reproduces this naturally: present, then absent after // the first uninstall removes it. - let mut repository = InstalledArrayRepository::new().unwrap(); - repository.add_package(package.clone()).unwrap(); - - run(library.uninstall( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - package.clone(), - )) - .unwrap(); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + repository + .borrow_mut() + .add_package(package.clone()) + .unwrap(); - assert!(!repository.has_package(package.clone()).unwrap()); + run(library.uninstall(&repository, package.clone())).unwrap(); - // Uninstalling again, with the package no longer installed, fails. assert!( - run(library.uninstall( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface - ), - package - )) - .is_err() + !repository + .borrow_mut() + .has_package(package.clone()) + .unwrap() ); + // Uninstalling again, with the package no longer installed, fails. + assert!(run(library.uninstall(&repository, package)).is_err()); + tear_down(&mut setup); } diff --git a/crates/shirabe/tests/installer/metapackage_installer_test.rs b/crates/shirabe/tests/installer/metapackage_installer_test.rs index b87fdebd..8675c00e 100644 --- a/crates/shirabe/tests/installer/metapackage_installer_test.rs +++ b/crates/shirabe/tests/installer/metapackage_installer_test.rs @@ -8,7 +8,7 @@ use crate::test_case::get_package; use shirabe::installer::{InstallerInterface, MetapackageInstaller}; use shirabe::io::IOInterface; use shirabe::io::null_io::NullIO; -use shirabe::repository::{InstalledArrayRepository, RepositoryInterface}; +use shirabe::repository::{InstalledArrayRepository, InstalledRepositoryInterfaceHandle}; fn installer() -> MetapackageInstaller { let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = @@ -20,17 +20,13 @@ fn installer() -> MetapackageInstaller { fn test_install() { let package = get_package("test/pkg", "1.0.0"); let installer = installer(); - let mut repository = InstalledArrayRepository::new_with_packages(vec![]).unwrap(); + let repository = InstalledRepositoryInterfaceHandle::new( + InstalledArrayRepository::new_with_packages(vec![]).unwrap(), + ); - run(installer.install( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - package.clone(), - )) - .unwrap(); + run(installer.install(&repository, package.clone())).unwrap(); - assert!(repository.has_package(package).unwrap()); + assert!(repository.borrow_mut().has_package(package).unwrap()); } #[test] @@ -38,59 +34,41 @@ fn test_update() { let initial = get_package("test/initial", "1.0.0"); let target = get_package("test/target", "1.0.1"); let installer = installer(); - let mut repository = - InstalledArrayRepository::new_with_packages(vec![initial.clone()]).unwrap(); - - run(installer.update( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - initial.clone(), - target.clone(), - )) - .unwrap(); + let repository = InstalledRepositoryInterfaceHandle::new( + InstalledArrayRepository::new_with_packages(vec![initial.clone()]).unwrap(), + ); - assert!(!repository.has_package(initial.clone()).unwrap()); - assert!(repository.has_package(target.clone()).unwrap()); + run(installer.update(&repository, initial.clone(), target.clone())).unwrap(); - // Updating again, with the initial package no longer installed, fails. assert!( - run(installer.update( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface - ), - initial, - target - )) - .is_err() + !repository + .borrow_mut() + .has_package(initial.clone()) + .unwrap() ); + assert!(repository.borrow_mut().has_package(target.clone()).unwrap()); + + // Updating again, with the initial package no longer installed, fails. + assert!(run(installer.update(&repository, initial, target)).is_err()); } #[test] fn test_uninstall() { let package = get_package("test/pkg", "1.0.0"); let installer = installer(); - let mut repository = - InstalledArrayRepository::new_with_packages(vec![package.clone()]).unwrap(); - - run(installer.uninstall( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface, - ), - package.clone(), - )) - .unwrap(); + let repository = InstalledRepositoryInterfaceHandle::new( + InstalledArrayRepository::new_with_packages(vec![package.clone()]).unwrap(), + ); - assert!(!repository.has_package(package.clone()).unwrap()); + run(installer.uninstall(&repository, package.clone())).unwrap(); - // Uninstalling again, with the package no longer installed, fails. assert!( - run(installer.uninstall( - &std::cell::RefCell::new( - &mut repository as &mut dyn shirabe::repository::InstalledRepositoryInterface - ), - package - )) - .is_err() + !repository + .borrow_mut() + .has_package(package.clone()) + .unwrap() ); + + // Uninstalling again, with the package no longer installed, fails. + assert!(run(installer.uninstall(&repository, package)).is_err()); } diff --git a/crates/shirabe/tests/plugin/main.rs b/crates/shirabe/tests/plugin/main.rs index 5cb77de4..62c8cfa8 100644 --- a/crates/shirabe/tests/plugin/main.rs +++ b/crates/shirabe/tests/plugin/main.rs @@ -1,3 +1,5 @@ +#[path = "../common/async_runtime.rs"] +mod async_runtime; #[path = "../common/config_stub.rs"] mod config_stub; diff --git a/crates/shirabe/tests/plugin/plugin_installer_test.rs b/crates/shirabe/tests/plugin/plugin_installer_test.rs index ca2dd879..e3f89851 100644 --- a/crates/shirabe/tests/plugin/plugin_installer_test.rs +++ b/crates/shirabe/tests/plugin/plugin_installer_test.rs @@ -1,28 +1,201 @@ //! ref: composer/tests/Composer/Test/Plugin/PluginInstallerTest.php -use crate::config_stub::ConfigStubBuilder; +use crate::async_runtime::run; use indexmap::IndexMap; +use shirabe::autoload::AutoloadGenerator; use shirabe::composer::{Composer, ComposerHandle, PartialOrFullComposer}; use shirabe::config::Config; +use shirabe::dependency_resolver::operation::AnyOperation; +use shirabe::downloader::{DownloadManagerInterface, DownloaderInterface}; +use shirabe::event_dispatcher::EventDispatcher; use shirabe::factory::DisablePlugins; -use shirabe::installer::InstallationManager; +use shirabe::installer::{ + InstallationManager, InstallationManagerInterface, InstallerInterface, PluginInstaller, +}; use shirabe::io::IOInterface; use shirabe::io::buffer_io::BufferIO; use shirabe::json::JsonFile; -use shirabe::package::{Locker, LockerInterface}; +use shirabe::package::loader::{ArrayLoader, JsonLoader, JsonLoaderInput}; +use shirabe::package::{Locker, LockerInterface, PackageInterfaceHandle, RootPackageHandle}; use shirabe::plugin::plugin_interface::PluginInterface; use shirabe::plugin::{Capable, PluginManager}; +use shirabe::repository::{ + InstalledArrayRepository, InstalledRepositoryInterfaceHandle, RepositoryInterfaceHandle, + RepositoryManagerInterface, +}; use shirabe::util::Platform; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe::util::process_executor::ProcessExecutor; use shirabe_external_packages::symfony::console::output::output_interface::VERBOSITY_NORMAL; +use shirabe_external_packages::symfony::process::PhpExecutableFinder; use shirabe_php_shim::PhpMixed; +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 { + PhpExecutableFinder::new().find(false).is_some() +} + +/// All tests in this binary share the single PHP worker, whose loaded-class table persists +/// across tests just like PHPUnit's single-process runs (that sharing is what exercises the +/// `_composer_tmp` rename path). Interleaving two tests would let one test's class definitions +/// 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, ()> { + PHP_WORKER_TESTS + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +/// `__DIR__ . '/Fixtures'` of the upstream test class. +fn fixtures_dir() -> String { + let dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../composer/tests/Composer/Test/Plugin/Fixtures"); + dir.canonicalize() + .expect("the Composer checkout must provide the plugin fixtures") + .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! { + #[derive(Debug)] + pub DownloadManager {} + #[async_trait::async_trait(?Send)] + impl DownloadManagerInterface for DownloadManager { + fn set_prefer_source(&mut self, prefer_source: bool); + fn set_prefer_dist(&mut self, prefer_dist: bool); + fn get_downloader_for_package( + &self, + package: PackageInterfaceHandle, + ) -> anyhow::Result<Option<std::rc::Rc<std::cell::RefCell<dyn DownloaderInterface>>>>; + async fn download( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option<PackageInterfaceHandle>, + ) -> anyhow::Result<Option<PhpMixed>>; + async fn prepare( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option<PackageInterfaceHandle>, + ) -> anyhow::Result<Option<PhpMixed>>; + async fn install( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> anyhow::Result<Option<PhpMixed>>; + async fn update( + &self, + initial: PackageInterfaceHandle, + target: PackageInterfaceHandle, + target_dir: &str, + ) -> anyhow::Result<Option<PhpMixed>>; + async fn remove( + &self, + package: PackageInterfaceHandle, + target_dir: &str, + ) -> anyhow::Result<Option<PhpMixed>>; + async fn cleanup( + &self, + r#type: &str, + package: PackageInterfaceHandle, + target_dir: &str, + prev_package: Option<PackageInterfaceHandle>, + ) -> anyhow::Result<Option<PhpMixed>>; + } +} + +/// PHP mocks `Composer\Repository\RepositoryManager` so that getLocalRepository returns the +/// test repository; the other methods are never reached. +#[derive(Debug)] +struct MockRepositoryManager { + local: RepositoryInterfaceHandle, + repositories: Vec<RepositoryInterfaceHandle>, +} + +impl RepositoryManagerInterface for MockRepositoryManager { + fn get_local_repository(&self) -> RepositoryInterfaceHandle { + self.local.clone() + } + + fn get_repositories(&self) -> &Vec<RepositoryInterfaceHandle> { + &self.repositories + } + + fn create_repository( + &self, + _type: &str, + _config: IndexMap<String, PhpMixed>, + _name: Option<&str>, + ) -> anyhow::Result<RepositoryInterfaceHandle> { + unimplemented!("not exercised by PluginInstallerTest") + } + + fn add_repository(&mut self, _repository: RepositoryInterfaceHandle) { + unimplemented!("not exercised by PluginInstallerTest") + } + + fn set_local_repository(&mut self, repository: RepositoryInterfaceHandle) { + self.local = repository; + } +} + +/// PHP mocks `Composer\Installer\InstallationManager` so that getInstallPath maps a package to +/// `__DIR__.'/Fixtures/'.$package->getPrettyName()`; every other method keeps the PHPUnit mock +/// default (no-op / falsy). +#[derive(Debug)] +struct MockInstallationManager; + +impl InstallationManagerInterface for MockInstallationManager { + fn add_installer(&mut self, _installer: Box<dyn InstallerInterface>) {} + + fn remove_installer(&mut self, _installer: &dyn InstallerInterface) {} + + fn disable_plugins(&mut self) {} + + fn is_package_installed( + &mut self, + _repo: &InstalledRepositoryInterfaceHandle, + _package: PackageInterfaceHandle, + ) -> anyhow::Result<bool> { + Ok(false) + } + + fn ensure_binaries_presence(&mut self, _package: PackageInterfaceHandle) {} + + fn execute( + &mut self, + _repo: &InstalledRepositoryInterfaceHandle, + _operations: Vec<AnyOperation>, + _dev_mode: bool, + _run_scripts: bool, + _download_only: bool, + ) -> anyhow::Result<()> { + Ok(()) + } + + fn get_install_path(&self, package: PackageInterfaceHandle) -> Option<String> { + Some(format!("{}/{}", fixtures_dir(), package.get_pretty_name())) + } + + fn set_output_progress(&mut self, _output_progress: bool) {} + + fn notify_installs(&mut self, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>) {} +} /// Equivalent to PHP setUp()'s `new InstallationManager(...)`, used only to satisfy -/// `Locker::new`'s constructor argument; it is never exercised by the currently-portable -/// tests below. -fn installation_manager( +/// `Locker::new`'s concrete constructor argument (PHP hands the same InstallationManager mock to +/// the Locker, which never touches it in these tests). +fn locker_installation_manager( io: &std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, ) -> std::rc::Rc<std::cell::RefCell<InstallationManager>> { let config = std::rc::Rc::new(std::cell::RefCell::new(Config::new(false, None))); @@ -42,175 +215,411 @@ fn installation_manager( #[derive(Debug)] struct SetUp { - #[allow(dead_code)] - io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, + io: std::rc::Rc<std::cell::RefCell<BufferIO>>, + io_dyn: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, pm: std::rc::Rc<std::cell::RefCell<PluginManager>>, + autoload_generator: std::rc::Rc<std::cell::RefCell<AutoloadGenerator>>, + packages: Vec<PackageInterfaceHandle>, + repository: InstalledRepositoryInterfaceHandle, // Keeps the Composer alive; PluginManager only holds a weak back-reference to it. - #[allow(dead_code)] composer: ComposerHandle, + // PHP's tearDown() removes this directory; TempDir does the same on drop. + _directory: TempDir, } -/// Builds a `Composer` the way PHP's setUp() does (config with `allow-plugins => true` -/// and a `Locker` backed by /dev/null) and constructs a `PluginManager` from it. -/// -/// PHP's setUp() additionally mocks DownloadManager/RepositoryManager/InstallationManager/ -/// EventDispatcher, loads 8 plugin-vN fixture packages, and creates a temp fixtures -/// directory. None of that is reproduced here: every test that would exercise it depends on -/// `PluginManager::register_package` actually instantiating a plugin class, which is an -/// unported runtime concern (`TODO(plugin)` in `plugin/plugin_manager.rs`) — those tests stay -/// `#[ignore]` below. Only the tests that call `PluginManager::get_plugin_capability` directly -/// with a hand-built plugin object are portable, and they need nothing more than `pm` itself. fn set_up() -> SetUp { - let io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = std::rc::Rc::new( - std::cell::RefCell::new(BufferIO::new(String::new(), VERBOSITY_NORMAL, None).unwrap()), - ); + let loader = JsonLoader::new(Box::new(ArrayLoader::new(None, false))); + let mut packages = vec![]; + let directory = TempDir::new().unwrap(); + let directory_path = directory.path().to_str().unwrap().to_string(); + for i in 1..=8 { + std::fs::create_dir_all(format!("{}/Fixtures/plugin-v{}", directory_path, i)).unwrap(); + packages.push( + loader + .load(JsonLoaderInput::String(format!( + "{}/plugin-v{}/composer.json", + fixtures_dir(), + i + ))) + .unwrap(), + ); + } - let config = ConfigStubBuilder::new() - .with("allow-plugins", PhpMixed::Bool(true)) - .build_shared(); + let mut dm = MockDownloadManager::new(); + dm.expect_install().returning(|_, _| Ok(None)); + dm.expect_update().returning(|_, _, _| Ok(None)); + dm.expect_remove().returning(|_, _| Ok(None)); - let json_file = JsonFile::new(Platform::get_dev_null(), None, Some(io.clone())).unwrap(); - let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( - io.clone(), - )))); - let locker: std::rc::Rc<std::cell::RefCell<dyn LockerInterface>> = - std::rc::Rc::new(std::cell::RefCell::new(Locker::new( - io.clone(), - json_file, - installation_manager(&io), - "{}", - process, - ))); + let repository = + InstalledRepositoryInterfaceHandle::new(InstalledArrayRepository::new().unwrap()); + + let repository_manager = MockRepositoryManager { + local: repository.as_repository_handle(), + repositories: vec![], + }; + + let installation_manager = MockInstallationManager; + + let io = std::rc::Rc::new(std::cell::RefCell::new( + BufferIO::new(String::new(), VERBOSITY_NORMAL, None).unwrap(), + )); + let io_dyn: std::rc::Rc<std::cell::RefCell<dyn IOInterface>> = io.clone(); + + let composer_rc = std::rc::Rc::new(std::cell::RefCell::new(PartialOrFullComposer::Full( + Composer::new(), + ))); + let composer = ComposerHandle::from_rc_unchecked(composer_rc.clone()); - let mut composer = Composer::new(); - composer.set_config(config); - composer.set_locker(locker); + // PHP hands the AutoloadGenerator a mocked EventDispatcher (disabled constructor); the Rust + // AutoloadGenerator requires a concrete EventDispatcher, and none of these tests reach it. + let dispatcher = std::rc::Rc::new(std::cell::RefCell::new(EventDispatcher::new( + composer.upcast().downgrade(), + io_dyn.clone(), + None, + ))); + let autoload_generator = std::rc::Rc::new(std::cell::RefCell::new(AutoloadGenerator::new( + dispatcher, + Some(io_dyn.clone()), + ))); - let composer = ComposerHandle::from_rc_unchecked(std::rc::Rc::new(std::cell::RefCell::new( - PartialOrFullComposer::Full(composer), + let mut config = Config::new(false, None); + let mut config_section: IndexMap<String, PhpMixed> = IndexMap::new(); + config_section.insert( + "vendor-dir".to_string(), + PhpMixed::String(format!("{}/Fixtures/", directory_path)), + ); + config_section.insert( + "home".to_string(), + PhpMixed::String(format!("{}/Fixtures", directory_path)), + ); + config_section.insert( + "bin-dir".to_string(), + PhpMixed::String(format!("{}/Fixtures/bin", directory_path)), + ); + config_section.insert("allow-plugins".to_string(), PhpMixed::Bool(true)); + let mut merged: IndexMap<String, PhpMixed> = IndexMap::new(); + merged.insert("config".to_string(), PhpMixed::Array(config_section)); + config.merge(&merged, Config::SOURCE_UNKNOWN); + + { + let mut c = composer.borrow_mut(); + c.set_config(std::rc::Rc::new(std::cell::RefCell::new(config))); + c.set_download_manager(std::rc::Rc::new(std::cell::RefCell::new(dm))); + c.set_repository_manager(std::rc::Rc::new(std::cell::RefCell::new( + repository_manager, + ))); + c.set_installation_manager(std::rc::Rc::new(std::cell::RefCell::new( + installation_manager, + ))); + c.set_autoload_generator(autoload_generator.clone()); + } + let real_dispatcher = std::rc::Rc::new(std::cell::RefCell::new(EventDispatcher::new( + composer.upcast().downgrade(), + io_dyn.clone(), + None, ))); + composer.borrow_mut().set_event_dispatcher(real_dispatcher); + composer.borrow_mut().set_package( + RootPackageHandle::new( + "dummy/root".to_string(), + "1.0.0.0".to_string(), + "1.0.0".to_string(), + ) + .into(), + ); + { + let json_file = + JsonFile::new(Platform::get_dev_null(), None, Some(io_dyn.clone())).unwrap(); + let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( + io_dyn.clone(), + )))); + let locker: std::rc::Rc<std::cell::RefCell<dyn LockerInterface>> = + std::rc::Rc::new(std::cell::RefCell::new(Locker::new( + io_dyn.clone(), + json_file, + locker_installation_manager(&io_dyn), + "{}", + process, + ))); + composer.borrow_mut().set_locker(locker); + } - let pm = PluginManager::new(io.clone(), composer.downgrade(), None, DisablePlugins::None); + let pm = std::rc::Rc::new(std::cell::RefCell::new(PluginManager::new( + io_dyn.clone(), + composer.downgrade(), + None, + DisablePlugins::None, + ))); + composer.borrow_mut().set_plugin_manager(pm.clone()); SetUp { io, - pm: std::rc::Rc::new(std::cell::RefCell::new(pm)), + io_dyn, + pm, + autoload_generator, + packages, + repository, composer, + _directory: directory, } } -/// PHP's tearDown() removes the temp fixtures directory created by setUp(); `set_up` above -/// creates no such directory, so there is nothing to clean up. -fn tear_down() {} - -struct TearDown; - -impl Drop for TearDown { - fn drop(&mut self) { - tear_down(); +/// PHPUnit asserts `$plugins[$i]->version` etc.; the plugin entity lives in the PHP child, so +/// the property is read over RPC through the proxy's test helper. +fn plugin_property( + plugin: &std::rc::Rc<std::cell::RefCell<dyn PluginInterface>>, + name: &str, +) -> String { + let plugin = plugin.borrow(); + let proxy = plugin + .as_php_plugin_proxy() + .expect("registered plugins are PHP-backed proxies"); + match proxy.__get_property(name).unwrap() { + PhpMixed::String(s) => s, + other => panic!("property `{name}` is not a string: {other:?}"), } } -// The plugin system requires the PHP runtime to load and instantiate plugin classes. -// `PluginInstaller::install`/`update` never call `PluginManager::register_package` (the calls -// are commented out in installer/plugin_installer.rs, TODO(plugin)), and `register_package` -// itself never instantiates a plugin class or calls `add_plugin` (TODO(plugin) in -// plugin/plugin_manager.rs). So `PluginManager::get_plugins()` can never contain the plugin -// instances these tests assert on. -#[ignore = "PluginInstaller and runtime loading of fixture plugin PHP classes (plugin-v1) are not implemented (TODO(plugin))"] +fn new_installer(set_up: &SetUp) -> PluginInstaller { + PluginInstaller::new( + set_up.io_dyn.clone(), + set_up.composer.upcast().downgrade(), + None, + None, + ) +} + #[test] fn test_install_new_plugin() { - // TODO(phase-d): PluginInstaller and runtime loading of fixture plugin PHP classes (plugin-v1) - // are not implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: $this->repository->getPackages() returns []. + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.install(&set_up.repository, set_up.packages[0].clone())).unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!("installer-v1", plugin_property(&plugins[0], "version")); + assert_eq!("activate v1\n", set_up.io.borrow().get_output()); } -#[ignore = "PluginInstaller and runtime loading of fixture plugin PHP classes are not implemented (TODO(plugin))"] #[test] fn test_install_plugin_with_root_package_having_files_autoload() { - // TODO(phase-d): PluginInstaller and runtime loading of fixture plugin PHP classes are not - // implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: $this->repository->getPackages() returns []. + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + set_up.autoload_generator.borrow_mut().set_dev_mode(true); + let files_autoload = format!("{}/files_autoload_which_should_not_run.php", fixtures_dir()); + let mut autoload: IndexMap<String, PhpMixed> = IndexMap::new(); + autoload.insert( + "files".to_string(), + PhpMixed::List(vec![PhpMixed::String(files_autoload)]), + ); + let root = set_up.composer.borrow().get_package().clone(); + root.set_autoload(autoload.clone()); + root.set_dev_autoload(autoload); + + run(installer.install(&set_up.repository, set_up.packages[0].clone())).unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!("activate v1\n", set_up.io.borrow().get_output()); + assert_eq!("installer-v1", plugin_property(&plugins[0], "version")); } -#[ignore = "PluginInstaller and runtime loading of fixture plugin PHP classes (plugin-v4) are not implemented (TODO(plugin))"] #[test] fn test_install_multiple_plugins() { - // TODO(phase-d): PluginInstaller and runtime loading of fixture plugin PHP classes (plugin-v4) - // are not implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: $this->repository->getPackages() returns [$this->packages[3]]. + set_up + .repository + .borrow_mut() + .add_package(set_up.packages[3].clone()) + .unwrap(); + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.install(&set_up.repository, set_up.packages[3].clone())).unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!("plugin1", plugin_property(&plugins[0], "name")); + assert_eq!("installer-v4", plugin_property(&plugins[0], "version")); + assert_eq!("plugin2", plugin_property(&plugins[1], "name")); + assert_eq!("installer-v4", plugin_property(&plugins[1], "version")); + assert_eq!( + "activate v4-plugin1\nactivate v4-plugin2\n", + set_up.io.borrow().get_output() + ); } -#[ignore = "PluginInstaller.update and runtime plugin class loading/deactivation are not implemented (TODO(plugin))"] #[test] fn test_upgrade_with_new_class_name() { - // TODO(phase-d): PluginInstaller.update and runtime plugin class loading/deactivation are not - // implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: getPackages returns [$this->packages[0]]; hasPackage answers (true, false), which a + // real repository seeded with the initial package reproduces naturally. + set_up + .repository + .borrow_mut() + .add_package(set_up.packages[0].clone()) + .unwrap(); + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.update( + &set_up.repository, + set_up.packages[0].clone(), + set_up.packages[1].clone(), + )) + .unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + // PHP: assertCount(1, $plugins); $plugins[1]->version — unset() keeps array keys, so the + // remaining plugin sits at key 1. The Vec port reindexes; the remaining plugin is [0]. + assert_eq!(1, plugins.len()); + assert_eq!("installer-v2", plugin_property(&plugins[0], "version")); + assert_eq!( + "activate v1\ndeactivate v1\nactivate v2\n", + set_up.io.borrow().get_output() + ); } -#[ignore = "PluginInstaller.uninstall and runtime plugin class loading/uninstall hook are not implemented (TODO(plugin))"] #[test] fn test_uninstall() { - // TODO(phase-d): PluginInstaller.uninstall and runtime plugin class loading/uninstall hook are - // not implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + set_up + .repository + .borrow_mut() + .add_package(set_up.packages[0].clone()) + .unwrap(); + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.uninstall(&set_up.repository, set_up.packages[0].clone())).unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!(0, plugins.len()); + assert_eq!( + "activate v1\ndeactivate v1\nuninstall v1\n", + set_up.io.borrow().get_output() + ); } -#[ignore = "PluginInstaller.update and runtime plugin class loading/deactivation are not implemented (TODO(plugin))"] #[test] fn test_upgrade_with_same_class_name() { - // TODO(phase-d): PluginInstaller.update and runtime plugin class loading/deactivation are not - // implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: getPackages returns [$this->packages[1]]; hasPackage answers (true, false). + set_up + .repository + .borrow_mut() + .add_package(set_up.packages[1].clone()) + .unwrap(); + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.update( + &set_up.repository, + set_up.packages[1].clone(), + set_up.packages[2].clone(), + )) + .unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!("installer-v3", plugin_property(&plugins[0], "version")); + assert_eq!( + "activate v2\ndeactivate v2\nactivate v3\n", + set_up.io.borrow().get_output() + ); } -#[ignore = "PluginInstaller and runtime loading of fixture plugin PHP classes are not implemented (TODO(plugin))"] #[test] fn test_register_plugin_only_one_time() { - // TODO(phase-d): PluginInstaller and runtime loading of fixture plugin PHP classes are not - // implemented (TODO(plugin)). - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP: $this->repository->getPackages() returns []. + let installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + run(installer.install(&set_up.repository, set_up.packages[0].clone())).unwrap(); + run(installer.install( + &set_up.repository, + PackageInterfaceHandle::dup(&set_up.packages[0]), + )) + .unwrap(); + + let pm = set_up.pm.borrow(); + let plugins = pm.get_plugins(); + assert_eq!(1, plugins.len()); + assert_eq!("installer-v1", plugin_property(&plugins[0], "version")); + assert_eq!("activate v1\n", set_up.io.borrow().get_output()); } -// PluginManager::register_package's version-constraint check against composer-plugin-api is -// fully ported and does gate loading correctly, but `getPluginApiVersion()` returns a hardcoded -// constant (plugin_interface::PLUGIN_API_VERSION) with no seam to override it per-test the way -// PHP's `getMockBuilder(PluginManager::class)->onlyMethods(['getPluginApiVersion'])` does, and -// even a matching version can never produce a registered plugin (register_package's -// instantiate-and-add_plugin step is an unported TODO(plugin) stub). Both blockers must be -// resolved together; a partial port (e.g. only the count==0 branches) would drop assertions the -// test relies on, which is disallowed. -#[ignore = "Requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP classes; not implemented (TODO(plugin))"] +// PluginManager::get_plugin_api_version returns a hardcoded constant +// (plugin_interface::PLUGIN_API_VERSION) with no seam to override it per-test the way PHP's +// `getMockBuilder(PluginManager::class)->onlyMethods(['getPluginApiVersion'])` does. +#[ignore = "Requires mocking getPluginApiVersion; PluginManager has no such seam (TODO(plugin))"] #[test] fn test_star_plugin_version_works_with_any_api_version() { - // TODO(phase-d): requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP - // classes; not implemented (TODO(plugin)). + // TODO(phase-d): requires mocking getPluginApiVersion; PluginManager has no such seam + // (TODO(plugin)). todo!() } -#[ignore = "Requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP classes; not implemented (TODO(plugin))"] +#[ignore = "Requires mocking getPluginApiVersion; PluginManager has no such seam (TODO(plugin))"] #[test] fn test_plugin_constraint_works_only_with_certain_api_version() { - // TODO(phase-d): requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP - // classes; not implemented (TODO(plugin)). + // TODO(phase-d): requires mocking getPluginApiVersion; PluginManager has no such seam + // (TODO(plugin)). todo!() } -#[ignore = "Requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP classes; not implemented (TODO(plugin))"] +#[ignore = "Requires mocking getPluginApiVersion; PluginManager has no such seam (TODO(plugin))"] #[test] fn test_plugin_range_constraints_work_only_with_certain_api_version() { - // TODO(phase-d): requires mocking getPluginApiVersion and runtime loading of fixture plugin PHP - // classes; not implemented (TODO(plugin)). + // TODO(phase-d): requires mocking getPluginApiVersion; PluginManager has no such seam + // (TODO(plugin)). todo!() } -#[ignore = "get_plugin_capabilities requires a registered plugin, which register_package never produces (TODO(plugin) in plugin/plugin_manager.rs); Capability::CommandProvider/BaseCommand runtime instantiation is also unported"] +#[ignore = "get_plugin_capability never instantiates a capability class (TODO(plugin) in plugin/plugin_manager.rs); Capability::CommandProvider/BaseCommand runtime instantiation is unported"] #[test] fn test_command_provider_capability() { - // TODO(phase-d): get_plugin_capabilities requires a registered plugin, which register_package - // never produces (TODO(plugin) in plugin/plugin_manager.rs); Capability::CommandProvider/ - // BaseCommand runtime instantiation is also unported. + // TODO(phase-d): get_plugin_capability never instantiates a capability class (TODO(plugin) + // in plugin/plugin_manager.rs); Capability::CommandProvider/BaseCommand runtime + // instantiation is also unported. todo!() } @@ -224,30 +633,36 @@ struct NoopPlugin; impl PluginInterface for NoopPlugin { fn activate( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) } fn deactivate( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) } fn uninstall( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) + } + + fn get_class_name(&self) -> String { + "NoopPlugin".to_string() } } #[test] fn test_incapable_plugin_is_correctly_detected() { let set_up = set_up(); - let _tear_down = TearDown; let plugin = NoopPlugin; let result = set_up @@ -293,23 +708,30 @@ struct CapablePlugin { impl PluginInterface for CapablePlugin { fn activate( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) } fn deactivate( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) } fn uninstall( &mut self, - _composer: &ComposerHandle, + _composer: ComposerHandle, _io: std::rc::Rc<std::cell::RefCell<dyn IOInterface>>, - ) { + ) -> anyhow::Result<()> { + Ok(()) + } + + fn get_class_name(&self) -> String { + "CapablePlugin".to_string() } fn as_capable(&self) -> Option<&dyn Capable> { @@ -327,7 +749,6 @@ impl Capable for CapablePlugin { #[test] fn test_querying_non_provided_capability_returns_null_safely() { let set_up = set_up(); - let _tear_down = TearDown; let plugin = CapablePlugin { get_capabilities_calls: std::cell::RefCell::new(0), diff --git a/crates/shirabe/tests/repository/filesystem_repository_test.rs b/crates/shirabe/tests/repository/filesystem_repository_test.rs index c7b8f9a6..323aff9f 100644 --- a/crates/shirabe/tests/repository/filesystem_repository_test.rs +++ b/crates/shirabe/tests/repository/filesystem_repository_test.rs @@ -10,7 +10,6 @@ use shirabe::io::IOInterface; use shirabe::json::json_file::JsonFile; use shirabe::package::loader::ArrayLoader; use shirabe::package::{Link, PackageInterfaceHandle, RootAliasPackageHandle, RootPackageHandle}; -use shirabe::repository::InstalledRepositoryInterface; use shirabe::repository::RepositoryInterface; use shirabe::repository::filesystem_repository::FilesystemRepository; use shirabe::util::filesystem::Filesystem; @@ -99,13 +98,13 @@ mockall::mock! { fn disable_plugins(&mut self); fn is_package_installed( &mut self, - repo: &mut dyn InstalledRepositoryInterface, + repo: &shirabe::repository::InstalledRepositoryInterfaceHandle, package: PackageInterfaceHandle, ) -> anyhow::Result<bool>; fn ensure_binaries_presence(&mut self, package: PackageInterfaceHandle); fn execute( &mut self, - repo: &mut dyn InstalledRepositoryInterface, + repo: &shirabe::repository::InstalledRepositoryInterfaceHandle, operations: Vec<AnyOperation>, dev_mode: bool, run_scripts: bool, |
