diff options
Diffstat (limited to 'crates/shirabe/tests')
| -rw-r--r-- | crates/shirabe/tests/plugin/plugin_installer_test.rs | 122 |
1 files changed, 97 insertions, 25 deletions
diff --git a/crates/shirabe/tests/plugin/plugin_installer_test.rs b/crates/shirabe/tests/plugin/plugin_installer_test.rs index 70548ba6..ec5f4fe3 100644 --- a/crates/shirabe/tests/plugin/plugin_installer_test.rs +++ b/crates/shirabe/tests/plugin/plugin_installer_test.rs @@ -18,7 +18,7 @@ use shirabe::json::JsonFile; 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::plugin::{Capable, PluginManager, composer_handle_value, io_handle_value}; use shirabe::repository::{ InstalledArrayRepository, InstalledRepositoryInterfaceHandle, RepositoryInterfaceHandle, RepositoryManagerInterface, @@ -633,13 +633,44 @@ fn test_plugin_range_constraints_work_only_with_certain_api_version() { todo!() } -#[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_capability never instantiates a capability class (TODO(plugin) - // in plugin/plugin_manager.rs); Capability::CommandProvider/BaseCommand runtime - // instantiation is also unported. - todo!() + if !php_runtime_available() { + return; + } + let _worker = lock_php_worker(); + let set_up = set_up(); + // PHP mocks the repository so getPackages() returns [$this->packages[7]] (plugin-v8); + // the real InstalledArrayRepository reaches the same state by adding the package. + set_up + .repository + .borrow_mut() + .add_package(set_up.packages[7].clone()) + .unwrap(); + let _installer = new_installer(&set_up); + set_up.pm.borrow_mut().load_installed_plugins().unwrap(); + + let mut ctor_args = IndexMap::new(); + ctor_args.insert( + "composer".to_string(), + composer_handle_value(&set_up.composer), + ); + ctor_args.insert("io".to_string(), io_handle_value(&set_up.io_dyn).unwrap()); + let caps = set_up + .pm + .borrow() + .get_plugin_capabilities("Composer\\Plugin\\Capability\\CommandProvider", ctor_args) + .unwrap(); + assert_eq!(1, caps.len()); + // PHP: assertInstanceOf('Composer\Plugin\Capability\CommandProvider', $caps[0]). + let provider = caps[0] + .as_command_provider() + .expect("the capability implements CommandProvider"); + + let commands = provider.get_commands().unwrap(); + assert_eq!(1, commands.len()); + // PHP: assertInstanceOf('Composer\Command\BaseCommand', $commands[0]) is witnessed by the + // element type of Vec<Box<dyn BaseCommand>>. } // A hand-written stub is used in place of PHPUnit's @@ -692,27 +723,63 @@ fn test_incapable_plugin_is_correctly_detected() { assert!(result.is_none()); } -#[ignore = "Requires runtime instantiation of Mock\\Capability via get_plugin_capability; not implemented (TODO(plugin))"] +#[ignore = "the mocked Capable plugin is Rust-native and cannot cross the RPC boundary: getPluginCapability passes the plugin itself as $ctorArgs['plugin'] to Mock\\Capability, and the test reads $capability->args back; PluginInterface has no rust-proxy stub (TODO(plugin))"] #[test] fn test_capability_implements_composer_plugin_api_class_and_is_constructed_with_args() { - // TODO(phase-d): requires runtime instantiation of Mock\Capability via - // get_plugin_capability; not implemented (TODO(plugin)). + // TODO(phase-d): the mocked Capable plugin is Rust-native and cannot cross the RPC + // boundary — getPluginCapability must pass the plugin itself as $ctorArgs['plugin'] to + // Mock\Capability's constructor, and the test reads $capability->args back over RPC; + // PluginInterface has no rust-proxy stub (TODO(plugin)). todo!() } -// PluginManager::get_capability_implementation_class_name (via Capable::get_capabilities) -// resolves capability class names through an IndexMap<String, String>, so most of PHP's -// invalidImplementationClassNames data provider (null, 0, 1000, [1], [], stdClass) cannot be -// represented at all in the ported type — only the string entries ("", " ") could be -// constructed. Per the phase-d rule against porting a subset of a data provider, this whole -// test must stay unported rather than dropping the non-string cases. -#[ignore = "Capable::get_capabilities is typed IndexMap<String, String>; most of the invalidImplementationClassNames data provider (null, 0, 1000, [1], [], stdClass) is not representable, and porting only the string cases would drop data-provider entries (TODO(phase-d))"] +/// PHP data provider `invalidImplementationClassNames`, one PhpMixed per entry. +fn invalid_implementation_class_names() -> Vec<PhpMixed> { + vec![ + PhpMixed::Null, + PhpMixed::String(String::new()), + PhpMixed::Int(0), + PhpMixed::Int(1000), + PhpMixed::String(" ".to_string()), + PhpMixed::List(vec![PhpMixed::Int(1)]), + PhpMixed::List(vec![]), + // PHP: new \stdClass() + PhpMixed::Object(IndexMap::new()), + ] +} + #[test] fn test_querying_with_invalid_capability_class_name_throws() { - // TODO(phase-d): Capable::get_capabilities is typed IndexMap<String, String>; most of the - // invalidImplementationClassNames data provider (null, 0, 1000, [1], [], stdClass) is not - // representable, and porting only the string cases would drop data-provider entries. - todo!() + let capability_api = "Composer\\Plugin\\Capability\\Capability"; + for invalid_implementation_class_name in invalid_implementation_class_names() { + let set_up = set_up(); + let plugin = CapablePlugin { + capabilities: IndexMap::from([( + capability_api.to_string(), + invalid_implementation_class_name.clone(), + )]), + get_capabilities_calls: std::cell::RefCell::new(0), + }; + + let err = + match set_up + .pm + .borrow() + .get_plugin_capability(&plugin, capability_api, IndexMap::new()) + { + Err(err) => err, + Ok(_) => panic!( + "expected UnexpectedValueException for {invalid_implementation_class_name:?}" + ), + }; + assert!( + err.downcast_ref::<shirabe_php_shim::UnexpectedValueException>() + .is_some(), + "expected UnexpectedValueException for {invalid_implementation_class_name:?}, got: {err}" + ); + // PHP: ->expects($this->once())->method('getCapabilities'). + assert_eq!(1, *plugin.get_capabilities_calls.borrow()); + } } // A hand-written stub plays the role of PHPUnit's @@ -721,6 +788,7 @@ fn test_querying_with_invalid_capability_class_name_throws() { // is reproduced with a call counter asserted after the call. #[derive(Debug)] struct CapablePlugin { + capabilities: IndexMap<String, PhpMixed>, get_capabilities_calls: std::cell::RefCell<i64>, } @@ -759,9 +827,9 @@ impl PluginInterface for CapablePlugin { } impl Capable for CapablePlugin { - fn get_capabilities(&self) -> anyhow::Result<IndexMap<String, String>> { + fn get_capabilities(&self) -> anyhow::Result<IndexMap<String, PhpMixed>> { *self.get_capabilities_calls.borrow_mut() += 1; - Ok(IndexMap::new()) + Ok(self.capabilities.clone()) } } @@ -770,6 +838,7 @@ fn test_querying_non_provided_capability_returns_null_safely() { let set_up = set_up(); let plugin = CapablePlugin { + capabilities: IndexMap::new(), get_capabilities_calls: std::cell::RefCell::new(0), }; @@ -786,10 +855,13 @@ fn test_querying_non_provided_capability_returns_null_safely() { assert_eq!(1, *plugin.get_capabilities_calls.borrow()); } -#[ignore = "Requires runtime get_plugin_capability with PHP-class-name capability lookup (class_exists/instanceof checks are unported TODO(plugin)); not implemented"] +#[ignore = "the '\\stdClass' data-provider case reaches new \\stdClass($ctorArgs), which needs the Rust-native mocked plugin passed as $ctorArgs['plugin'] over RPC (no PluginInterface rust-proxy stub exists, TODO(plugin)); porting only the NonExistentClassLikeMiddleClass case would drop a data-provider entry (TODO(phase-d))"] #[test] fn test_querying_with_non_existing_or_wrong_capability_class_types_throws() { - // TODO(phase-d): requires runtime get_plugin_capability with PHP-class-name capability lookup - // (class_exists/instanceof checks are unported TODO(plugin)); not implemented. + // TODO(phase-d): the '\stdClass' data-provider case reaches new \stdClass($ctorArgs), + // which needs the Rust-native mocked plugin passed as $ctorArgs['plugin'] over RPC — no + // PluginInterface rust-proxy stub exists (TODO(plugin)). Porting only the + // NonExistentClassLikeMiddleClass case (which fails at class_exists, before the plugin is + // passed) would drop a data-provider entry. todo!() } |
