diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-08-09 09:14:11 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-08-09 09:14:11 +0900 |
| commit | 6643eb8b7d305818f80c910144b3b29b1af34ba7 (patch) | |
| tree | 3baee8b2291725add75e9963d7666890aae62760 /crates/shirabe | |
| parent | be514aeff17ab7d8debcc59acddd07f7c7b72bd7 (diff) | |
| download | php-shirabe-6643eb8b7d305818f80c910144b3b29b1af34ba7.tar.gz php-shirabe-6643eb8b7d305818f80c910144b3b29b1af34ba7.tar.zst php-shirabe-6643eb8b7d305818f80c910144b3b29b1af34ba7.zip | |
refactor(installed-versions): drop the Rust port, which has no readers
InstalledVersions is a runtime API for plugins and project code; Composer
itself never reads it. Its consumers run in the PHP worker against the copy
FilesystemRepository dumps to vendor/composer/InstalledVersions.php, whose
static state is already kept in sync by __shirabe_installed_versions_reload.
Nothing in Rust read the mirrored statics, so reload() and the reflection
setters were no-ops.
The tests covered only the Rust port, not the PHP class the worker loads, so
they assert nothing about compatibility; they are left as todo!() skeletons.
This also removes the shim functions method_exists, php_dir and
require_php_file, whose only caller was the deleted module.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe')
| -rw-r--r-- | crates/shirabe/src/installed_versions.rs | 458 | ||||
| -rw-r--r-- | crates/shirabe/src/lib.rs | 5 | ||||
| -rw-r--r-- | crates/shirabe/src/repository/filesystem_repository.rs | 23 | ||||
| -rw-r--r-- | crates/shirabe/tests/installed_versions_test.rs | 442 | ||||
| -rw-r--r-- | crates/shirabe/tests/repository/filesystem_repository_test.rs | 69 |
5 files changed, 58 insertions, 939 deletions
diff --git a/crates/shirabe/src/installed_versions.rs b/crates/shirabe/src/installed_versions.rs deleted file mode 100644 index aa934841..00000000 --- a/crates/shirabe/src/installed_versions.rs +++ /dev/null @@ -1,458 +0,0 @@ -//! ref: composer/src/Composer/InstalledVersions.php - -use crate::autoload::ClassLoader; -use indexmap::IndexMap; -use shirabe_php_shim::{ - OutOfBoundsException, PhpMixed, array_flip_strings, array_keys, array_merge, implode, is_file, - method_exists, php_dir, require_php_file, strtr_array, substr, -}; -use shirabe_semver::VersionParser; -use std::sync::Mutex; - -/// This class is copied in every Composer installed project and available to all -/// -/// See also https://getcomposer.org/doc/07-runtime.md#installed-versions -/// -/// To require its presence, you can require `composer-runtime-api ^2.0` -/// -/// @final -pub struct InstalledVersions; - -/// @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to -/// @internal -static SELF_DIR: Mutex<Option<String>> = Mutex::new(None); - -/// @var mixed[]|null -/// @psalm-var array{root: array{...}, versions: array<string, array{...}>}|array{}|null -static INSTALLED: Mutex<Option<IndexMap<String, PhpMixed>>> = Mutex::new(None); - -/// @var bool -static INSTALLED_IS_LOCAL_DIR: Mutex<bool> = Mutex::new(false); - -/// @var bool|null -static CAN_GET_VENDORS: Mutex<Option<bool>> = Mutex::new(None); - -/// @var array[] -/// @psalm-var array<string, array{...}> -static INSTALLED_BY_VENDOR: std::sync::LazyLock< - Mutex<IndexMap<String, IndexMap<String, PhpMixed>>>, -> = std::sync::LazyLock::new(|| Mutex::new(IndexMap::new())); - -impl InstalledVersions { - /// Returns a list of all package names which are present, either by being installed, replaced or provided - pub fn get_installed_packages() -> Vec<String> { - let mut packages: Vec<Vec<String>> = vec![]; - for installed in Self::get_installed() { - let versions = installed - .get("versions") - .and_then(|v| v.as_array()) - .cloned() - .unwrap_or_default(); - let keys: Vec<String> = array_keys(&versions); - packages.push(keys); - } - - if 1 == packages.len() { - return packages.into_iter().next().unwrap(); - } - - let merged: Vec<String> = packages.into_iter().flatten().collect(); - array_keys(&array_flip_strings(&merged)) - } - - /// Returns a list of all package names with a specific type e.g. 'library' - pub fn get_installed_packages_by_type(r#type: &str) -> Vec<String> { - let mut packages_by_type: Vec<String> = vec![]; - - for installed in Self::get_installed() { - let versions = installed - .get("versions") - .and_then(|v| v.as_array()) - .cloned() - .unwrap_or_default(); - for (name, package) in versions { - if let Some(pkg) = package.as_array() - && let Some(pkg_type) = pkg.get("type").and_then(|v| v.as_string()) - && pkg_type == r#type - { - packages_by_type.push(name); - } - } - } - - packages_by_type - } - - /// Checks whether the given package is installed - /// - /// This also returns true if the package name is provided or replaced by another package - pub fn is_installed(package_name: &str, include_dev_requirements: bool) -> bool { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - if let Some(package) = versions.get(package_name) { - let dev_requirement = package - .as_array() - .and_then(|a| a.get("dev_requirement")) - .cloned() - .unwrap_or(PhpMixed::Null); - return include_dev_requirements - || matches!(dev_requirement, PhpMixed::Null) - || matches!(dev_requirement, PhpMixed::Bool(false)); - } - } - - false - } - - /// Checks whether the given package satisfies a version constraint - /// - /// e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: - /// - /// Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') - /// - /// @param VersionParser $parser Install composer/semver to have access to this class and functionality - /// @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package - pub fn satisfies( - parser: &VersionParser, - package_name: &str, - constraint: Option<&str>, - ) -> anyhow::Result<bool> { - let constraint = parser.parse_constraints(constraint.unwrap_or(""))?; - let provided = parser.parse_constraints(&Self::get_version_ranges(package_name)?)?; - - Ok(provided.matches(&constraint)) - } - - /// Returns a version constraint representing all the range(s) which are installed for a given package - /// - /// It is easier to use this via isInstalled() with the $constraint argument if you need to check - /// whether a given version of a package is installed, and not just whether it exists - /// - /// @return string Version constraint usable with composer/semver - pub fn get_version_ranges(package_name: &str) -> anyhow::Result<String> { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - let Some(pkg) = versions - .get(package_name) - .and_then(|v| v.as_array()) - .cloned() - else { - continue; - }; - - let mut ranges: Vec<String> = vec![]; - if let Some(pretty_version) = pkg.get("pretty_version").and_then(|v| v.as_string()) { - ranges.push(pretty_version.to_string()); - } - if pkg.contains_key("aliases") { - ranges = array_merge( - PhpMixed::List(ranges.iter().map(|s| PhpMixed::String(s.clone())).collect()), - pkg.get("aliases").cloned().unwrap_or(PhpMixed::Null), - ) - .as_list() - .map(|l| { - l.iter() - .filter_map(|v| v.as_string().map(|s| s.to_string())) - .collect() - }) - .unwrap_or_default(); - } - if pkg.contains_key("replaced") { - ranges = array_merge( - PhpMixed::List(ranges.iter().map(|s| PhpMixed::String(s.clone())).collect()), - pkg.get("replaced").cloned().unwrap_or(PhpMixed::Null), - ) - .as_list() - .map(|l| { - l.iter() - .filter_map(|v| v.as_string().map(|s| s.to_string())) - .collect() - }) - .unwrap_or_default(); - } - if pkg.contains_key("provided") { - ranges = array_merge( - PhpMixed::List(ranges.iter().map(|s| PhpMixed::String(s.clone())).collect()), - pkg.get("provided").cloned().unwrap_or(PhpMixed::Null), - ) - .as_list() - .map(|l| { - l.iter() - .filter_map(|v| v.as_string().map(|s| s.to_string())) - .collect() - }) - .unwrap_or_default(); - } - - return Ok(implode(" || ", &ranges)); - } - - Err( - OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) - .into(), - ) - } - - /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - pub fn get_version(package_name: &str) -> anyhow::Result<Option<String>> { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - let Some(pkg) = versions.get(package_name).and_then(|v| v.as_array()) else { - continue; - }; - - if !pkg.contains_key("version") { - return Ok(None); - } - - return Ok(pkg - .get("version") - .and_then(|v| v.as_string()) - .map(|s| s.to_string())); - } - - Err( - OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) - .into(), - ) - } - - /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present - pub fn get_pretty_version(package_name: &str) -> anyhow::Result<Option<String>> { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - let Some(pkg) = versions.get(package_name).and_then(|v| v.as_array()) else { - continue; - }; - - if !pkg.contains_key("pretty_version") { - return Ok(None); - } - - return Ok(pkg - .get("pretty_version") - .and_then(|v| v.as_string()) - .map(|s| s.to_string())); - } - - Err( - OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) - .into(), - ) - } - - /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference - pub fn get_reference(package_name: &str) -> anyhow::Result<Option<String>> { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - let Some(pkg) = versions.get(package_name).and_then(|v| v.as_array()) else { - continue; - }; - - if !pkg.contains_key("reference") { - return Ok(None); - } - - return Ok(pkg - .get("reference") - .and_then(|v| v.as_string()) - .map(|s| s.to_string())); - } - - Err( - OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) - .into(), - ) - } - - /// @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. - pub fn get_install_path(package_name: &str) -> anyhow::Result<Option<String>> { - for installed in Self::get_installed() { - let Some(versions) = installed.get("versions").and_then(|v| v.as_array()) else { - continue; - }; - let Some(pkg) = versions.get(package_name).and_then(|v| v.as_array()) else { - continue; - }; - - return Ok(if pkg.contains_key("install_path") { - pkg.get("install_path") - .and_then(|v| v.as_string()) - .map(|s| s.to_string()) - } else { - None - }); - } - - Err( - OutOfBoundsException::new(format!("Package \"{}\" is not installed", package_name)) - .into(), - ) - } - - pub fn get_root_package() -> IndexMap<String, PhpMixed> { - let installed = Self::get_installed(); - - installed - .into_iter() - .next() - .and_then(|d| d.get("root").and_then(|v| v.as_array()).cloned()) - .unwrap_or_default() - } - - /// Returns the raw data of all installed.php which are currently loaded for custom implementations - /// - /// Returns the first dataset loaded, which may not be what you expect. Use get_all_raw_data - /// instead, which returns all datasets for all autoloaders present in the process. - pub fn get_raw_data() -> IndexMap<String, PhpMixed> { - // PHP emits an E_USER_DEPRECATED notice here; there is no Rust equivalent. - let mut installed = INSTALLED.lock().unwrap(); - if installed.is_none() { - // PHP only includes __DIR__/installed.php when loaded from its dumped location; the - // shim is always the source location (PHP's `else` branch), so the data is empty. - *installed = Some(IndexMap::new()); - } - - installed.clone().unwrap() - } - - pub fn get_all_raw_data() -> Vec<IndexMap<String, PhpMixed>> { - Self::get_installed() - } - - /// Lets you reload the static array from another file - /// - /// This is only useful for complex integrations in which a project needs to use - /// this class but then also needs to execute another project's autoloader in process, - /// and wants to ensure both projects have access to their version of installed.php. - /// - /// A typical case would be PHPUnit, where it would need to make sure it reads all - /// the data it needs from this class, then call reload() with - /// `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure - /// the project in which it runs can then also use this class safely, without - /// interference between PHPUnit's dependencies and the project's dependencies. - /// - /// @param array[] $data A vendor/composer/installed.php data set - pub fn reload(data: IndexMap<String, PhpMixed>) { - *INSTALLED.lock().unwrap() = Some(data); - *INSTALLED_BY_VENDOR.lock().unwrap() = IndexMap::new(); - - // when using reload, we disable the duplicate protection to ensure that self::$installed data is - // always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not, - // so we have to assume it does not, and that may result in duplicate data being returned when listing - // all installed packages for example - *INSTALLED_IS_LOCAL_DIR.lock().unwrap() = false; - } - - /// PHP mutates the private static `$selfDir` via Reflection. Rust exposes a mutating function. - pub fn set_self_dir(dir: String) { - *SELF_DIR.lock().unwrap() = Some(dir); - } - - /// PHP mutates the private static `$installedIsLocalDir` via Reflection. Rust exposes a mutating function. - pub fn set_installed_is_local_dir(value: bool) { - *INSTALLED_IS_LOCAL_DIR.lock().unwrap() = value; - } - - fn get_self_dir() -> String { - let mut self_dir = SELF_DIR.lock().unwrap(); - if self_dir.is_none() { - *self_dir = Some(strtr_array(&php_dir(), &{ - let mut m = IndexMap::new(); - m.insert("\\".to_string(), "/".to_string()); - m - })); - } - - self_dir.clone().unwrap() - } - - fn get_installed() -> Vec<IndexMap<String, PhpMixed>> { - { - let mut can_get_vendors = CAN_GET_VENDORS.lock().unwrap(); - if can_get_vendors.is_none() { - *can_get_vendors = Some(method_exists( - &PhpMixed::String("Composer\\Autoload\\ClassLoader".to_string()), - "getRegisteredLoaders", - )); - } - } - - let mut installed: Vec<IndexMap<String, PhpMixed>> = vec![]; - let mut copied_local_dir = false; - - if CAN_GET_VENDORS.lock().unwrap().unwrap_or(false) { - let self_dir = Self::get_self_dir(); - for (vendor_dir, _loader) in ClassLoader::get_registered_loaders() { - let vendor_dir = strtr_array(&vendor_dir, &{ - let mut m = IndexMap::new(); - m.insert("\\".to_string(), "/".to_string()); - m - }); - let cached = INSTALLED_BY_VENDOR - .lock() - .unwrap() - .get(&vendor_dir) - .cloned(); - if let Some(cached) = cached { - installed.push(cached); - } else if is_file(format!("{}/composer/installed.php", vendor_dir)) { - let required = - require_php_file(&format!("{}/composer/installed.php", vendor_dir)); - let required_map: IndexMap<String, PhpMixed> = - required.as_array().cloned().unwrap_or_default(); - INSTALLED_BY_VENDOR - .lock() - .unwrap() - .insert(vendor_dir.clone(), required_map.clone()); - installed.push(required_map.clone()); - let mut installed_static = INSTALLED.lock().unwrap(); - if installed_static.is_none() && format!("{}/composer", vendor_dir) == self_dir - { - *installed_static = Some(required_map); - *INSTALLED_IS_LOCAL_DIR.lock().unwrap() = true; - } - } - if *INSTALLED_IS_LOCAL_DIR.lock().unwrap() - && format!("{}/composer", vendor_dir) == self_dir - { - copied_local_dir = true; - } - } - } - - { - let mut installed_static = INSTALLED.lock().unwrap(); - if installed_static.is_none() { - // only require the installed.php file if this file is loaded from its dumped location, - // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 - if substr(&php_dir(), -8, Some(1)) != "C" { - let required = require_php_file(&format!("{}/installed.php", php_dir())); - *installed_static = required - .as_array() - .cloned() - .map(|m| m.into_iter().collect()); - } else { - *installed_static = Some(IndexMap::new()); - } - } - } - - let installed_static_data = INSTALLED.lock().unwrap().clone().unwrap_or_default(); - if !installed_static_data.is_empty() && !copied_local_dir { - installed.push(installed_static_data); - } - - installed - } -} diff --git a/crates/shirabe/src/lib.rs b/crates/shirabe/src/lib.rs index 4667e7c9..e5624988 100644 --- a/crates/shirabe/src/lib.rs +++ b/crates/shirabe/src/lib.rs @@ -11,7 +11,6 @@ pub mod event_dispatcher; pub mod exception; pub mod factory; pub mod filter; -pub mod installed_versions; pub mod installer; pub mod io; pub mod json; @@ -25,6 +24,10 @@ pub mod script; pub mod self_update; pub mod util; +// InstalledVersions is intentionally unported to Rust. It is a runtime API for plugins and project +// code, never read by Composer itself. Its real state is stored in PHP's InstalledVersions class. +// See also `__shirabe_installed_versions_reload` in crates/shirabe-php-rpc/php/worker.php. + /// ref: composer/bin/composer pub fn run(argv: Vec<String>) -> anyhow::Result<i32> { use crate::console::ApplicationHandle; diff --git a/crates/shirabe/src/repository/filesystem_repository.rs b/crates/shirabe/src/repository/filesystem_repository.rs index d9085bfd..b39fe829 100644 --- a/crates/shirabe/src/repository/filesystem_repository.rs +++ b/crates/shirabe/src/repository/filesystem_repository.rs @@ -1,7 +1,6 @@ //! ref: composer/src/Composer/Repository/FilesystemRepository.php use crate::config::is_php_integer_key; -use crate::installed_versions::InstalledVersions; use crate::installer::InstallationManagerInterface; use crate::json::JsonFile; use crate::package::BasePackageHandle; @@ -338,30 +337,20 @@ impl FilesystemRepository { ); // make sure the in memory state is up to date with on disk - // The upstream in-process reload/selfDir/installedIsLocalDir tail is mirrored - // twice: into the Rust-side statics below, and into the PHP worker where the real - // observers (plugins) live. The push is skipped when no worker is running — with no - // child there is nothing that could observe the state; the glue skips it only when - // the class is not even autoloadable there (no Composer PHP runtime = no observer - // code either). + // The upstream in-process reload/selfDir/installedIsLocalDir tail is pushed to the + // PHP worker, where the real observers (plugins) live. The push is skipped when no + // worker is running — with no child there is nothing that could observe the state; + // the glue skips it only when the class is not even autoloadable there (no Composer + // PHP runtime = no observer code either). if shirabe_php_rpc::worker_is_running() { crate::event_dispatcher::unwrap_php_result(shirabe_php_rpc::call_function( "__shirabe_installed_versions_reload", vec![ - shirabe_php_rpc::PluginValue::from_php_mixed(&PhpMixed::Array( - versions.clone(), - )), + shirabe_php_rpc::PluginValue::from_php_mixed(&PhpMixed::Array(versions)), shirabe_php_rpc::PluginValue::string(repo_dir.clone()), ], ))?; } - InstalledVersions::reload(versions); - - // make sure the selfDir matches the expected data at runtime if the class was loaded from the vendor dir, as it may have been - // loaded from the Composer sources, causing packages to appear twice in that case if the installed.php is loaded in addition to the - // in memory loaded data from above - InstalledVersions::set_self_dir(repo_dir.replace('\\', "/")); - InstalledVersions::set_installed_is_local_dir(true); } Ok(()) diff --git a/crates/shirabe/tests/installed_versions_test.rs b/crates/shirabe/tests/installed_versions_test.rs index 7257ff6a..724c844e 100644 --- a/crates/shirabe/tests/installed_versions_test.rs +++ b/crates/shirabe/tests/installed_versions_test.rs @@ -1,451 +1,97 @@ //! ref: composer/tests/Composer/Test/InstalledVersionsTest.php +//! +//! Every test here needs the lookup APIs of `InstalledVersions`, which the Rust port does not +//! have. The PHP setUp reflects into `ClassLoader::registeredLoaders` to make it seem like no +//! class loaders are registered, then loads the installed_relative.php fixture via `require`. -// setUpBeforeClass reflects into ClassLoader::registeredLoaders to make it seem like no class -// loaders are registered; this is not ported because InstalledVersions::reload already disables -// the multiple-ClassLoader-based checks by setting installedIsLocalDir to false. The PHP setUp -// loads the installed_relative.php fixture via `require`; here the fixture is built inline as an -// IndexMap<String, PhpMixed> with the tmp root substituted for `$dir`. - -use indexmap::IndexMap; -use serial_test::serial; -use shirabe::installed_versions::InstalledVersions; -use shirabe_php_shim::{PhpMixed, realpath}; -use shirabe_semver::VersionParser; -use tempfile::TempDir; - -fn arr(entries: Vec<(&str, PhpMixed)>) -> PhpMixed { - let mut m = IndexMap::new(); - for (k, v) in entries { - m.insert(k.to_string(), v); - } - PhpMixed::Array(m) -} - -fn list(items: Vec<PhpMixed>) -> PhpMixed { - PhpMixed::List(items) -} - -fn s(value: &str) -> PhpMixed { - PhpMixed::String(value.to_string()) -} - -/// Builds the installed_relative.php fixture with `$dir` substituted by `dir`. -fn fixture(dir: &str) -> IndexMap<String, PhpMixed> { - let root_install_path = format!("{}/./", dir); - let mut data = IndexMap::new(); - - data.insert( - "root".to_string(), - arr(vec![ - ("name", s("__root__")), - ("pretty_version", s("dev-master")), - ("version", s("dev-master")), - ("reference", s("sourceref-by-default")), - ("type", s("library")), - ("install_path", s(&root_install_path)), - ("aliases", list(vec![s("1.10.x-dev")])), - ("dev", PhpMixed::Bool(true)), - ]), - ); - - data.insert( - "versions".to_string(), - arr(vec![ - ( - "__root__", - arr(vec![ - ("pretty_version", s("dev-master")), - ("version", s("dev-master")), - ("reference", s("sourceref-by-default")), - ("type", s("library")), - ("install_path", s(&root_install_path)), - ("aliases", list(vec![s("1.10.x-dev")])), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - ), - ( - "a/provider", - arr(vec![ - ("pretty_version", s("1.1")), - ("version", s("1.1.0.0")), - ("reference", s("distref-as-no-source")), - ("type", s("library")), - ("install_path", s(&format!("{}/vendor/a/provider", dir))), - ("aliases", list(vec![])), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - ), - ( - "a/provider2", - arr(vec![ - ("pretty_version", s("1.2")), - ("version", s("1.2.0.0")), - ("reference", s("distref-as-installed-from-dist")), - ("type", s("library")), - ("install_path", s(&format!("{}/vendor/a/provider2", dir))), - ("aliases", list(vec![s("1.4")])), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - ), - ( - "b/replacer", - arr(vec![ - ("pretty_version", s("2.2")), - ("version", s("2.2.0.0")), - ("reference", PhpMixed::Null), - ("type", s("library")), - ("install_path", s(&format!("{}/vendor/b/replacer", dir))), - ("aliases", list(vec![])), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - ), - ( - "c/c", - arr(vec![ - ("pretty_version", s("3.0")), - ("version", s("3.0.0.0")), - ("reference", PhpMixed::Null), - ("type", s("library")), - ("install_path", s("/foo/bar/vendor/c/c")), - ("aliases", list(vec![])), - ("dev_requirement", PhpMixed::Bool(true)), - ]), - ), - ( - "foo/impl", - arr(vec![ - ("dev_requirement", PhpMixed::Bool(false)), - ( - "provided", - list(vec![s("^1.1"), s("1.2"), s("1.4"), s("2.0")]), - ), - ]), - ), - ( - "foo/impl2", - arr(vec![ - ("dev_requirement", PhpMixed::Bool(false)), - ("provided", list(vec![s("2.0")])), - ("replaced", list(vec![s("2.2")])), - ]), - ), - ( - "foo/replaced", - arr(vec![ - ("dev_requirement", PhpMixed::Bool(false)), - ("replaced", list(vec![s("^3.0")])), - ]), - ), - ( - "meta/package", - arr(vec![ - ("pretty_version", s("3.0")), - ("version", s("3.0.0.0")), - ("reference", PhpMixed::Null), - ("type", s("metapackage")), - ("install_path", PhpMixed::Null), - ("aliases", list(vec![])), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - ), - ]), - ); - - data -} - -/// Returns the tmp root (kept alive for the duration of the test) and its path string. -fn set_up() -> (TempDir, String) { - let root = TempDir::new().unwrap(); - let dir = root.path().to_str().unwrap().to_string(); - - InstalledVersions::reload(fixture(&dir)); - - (root, dir) -} - -#[serial] #[test] +#[ignore = "InstalledVersions::getInstalledPackages has no Rust counterpart"] fn test_get_installed_packages() { - let (_root, _dir) = set_up(); - - let names = vec![ - "__root__".to_string(), - "a/provider".to_string(), - "a/provider2".to_string(), - "b/replacer".to_string(), - "c/c".to_string(), - "foo/impl".to_string(), - "foo/impl2".to_string(), - "foo/replaced".to_string(), - "meta/package".to_string(), - ]; - assert_eq!(names, InstalledVersions::get_installed_packages()); + // TODO(phase-d): needs InstalledVersions::get_installed_packages. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::isInstalled has no Rust counterpart"] fn test_is_installed() { - let (_root, _dir) = set_up(); - - let cases: Vec<(bool, &str, bool)> = vec![ - (true, "foo/impl", true), - (true, "foo/replaced", true), - (true, "c/c", true), - (false, "c/c", false), - (true, "__root__", true), - (true, "b/replacer", true), - (false, "not/there", true), - (true, "meta/package", true), - ]; - - for (expected, name, include_dev_requirements) in cases { - assert_eq!( - expected, - InstalledVersions::is_installed(name, include_dev_requirements) - ); - } + // TODO(phase-d): needs InstalledVersions::is_installed. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::satisfies has no Rust counterpart"] fn test_satisfies() { - let (_root, _dir) = set_up(); - - let cases: Vec<(bool, &str, &str)> = vec![ - (true, "foo/impl", "1.5"), - (true, "foo/impl", "1.2"), - (true, "foo/impl", "^1.0"), - (true, "foo/impl", "^3 || ^2"), - (false, "foo/impl", "^3"), - (true, "foo/replaced", "3.5"), - (true, "foo/replaced", "^3.2"), - (false, "foo/replaced", "4.0"), - (true, "c/c", "3.0.0"), - (true, "c/c", "^3"), - (false, "c/c", "^3.1"), - (true, "__root__", "dev-master"), - (true, "__root__", "^1.10"), - (false, "__root__", "^2"), - (true, "b/replacer", "^2.1"), - (false, "b/replacer", "^2.3"), - (true, "a/provider2", "^1.2"), - (true, "a/provider2", "^1.4"), - (false, "a/provider2", "^1.5"), - ]; - - for (expected, name, constraint) in cases { - assert_eq!( - expected, - InstalledVersions::satisfies(&VersionParser, name, Some(constraint)).unwrap() - ); - } + // TODO(phase-d): needs InstalledVersions::satisfies. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getVersionRanges has no Rust counterpart"] fn test_get_version_ranges() { - let (_root, _dir) = set_up(); - - let cases: Vec<(&str, &str)> = vec![ - ("dev-master || 1.10.x-dev", "__root__"), - ("^1.1 || 1.2 || 1.4 || 2.0", "foo/impl"), - ("2.2 || 2.0", "foo/impl2"), - ("^3.0", "foo/replaced"), - ("1.1", "a/provider"), - ("1.2 || 1.4", "a/provider2"), - ("2.2", "b/replacer"), - ("3.0", "c/c"), - ]; - - for (expected, name) in cases { - assert_eq!( - expected, - InstalledVersions::get_version_ranges(name).unwrap() - ); - } + // TODO(phase-d): needs InstalledVersions::get_version_ranges. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getVersion has no Rust counterpart"] fn test_get_version() { - let (_root, _dir) = set_up(); - - let cases: Vec<(Option<&str>, &str)> = vec![ - (Some("dev-master"), "__root__"), - (None, "foo/impl"), - (None, "foo/impl2"), - (None, "foo/replaced"), - (Some("1.1.0.0"), "a/provider"), - (Some("1.2.0.0"), "a/provider2"), - (Some("2.2.0.0"), "b/replacer"), - (Some("3.0.0.0"), "c/c"), - ]; - - for (expected, name) in cases { - assert_eq!( - expected.map(|s| s.to_string()), - InstalledVersions::get_version(name).unwrap() - ); - } + // TODO(phase-d): needs InstalledVersions::get_version. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getPrettyVersion has no Rust counterpart"] fn test_get_pretty_version() { - let (_root, _dir) = set_up(); - - let cases: Vec<(Option<&str>, &str)> = vec![ - (Some("dev-master"), "__root__"), - (None, "foo/impl"), - (None, "foo/impl2"), - (None, "foo/replaced"), - (Some("1.1"), "a/provider"), - (Some("1.2"), "a/provider2"), - (Some("2.2"), "b/replacer"), - (Some("3.0"), "c/c"), - ]; - - for (expected, name) in cases { - assert_eq!( - expected.map(|s| s.to_string()), - InstalledVersions::get_pretty_version(name).unwrap() - ); - } + // TODO(phase-d): needs InstalledVersions::get_pretty_version. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getVersion has no Rust counterpart"] fn test_get_version_out_of_bounds() { - let (_root, _dir) = set_up(); - - assert!(InstalledVersions::get_version("not/installed").is_err()); + // TODO(phase-d): needs InstalledVersions::get_version. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getRootPackage has no Rust counterpart"] fn test_get_root_package() { - let (_root, dir) = set_up(); - - let expected = { - let mut m = IndexMap::new(); - m.insert("name".to_string(), s("__root__")); - m.insert("pretty_version".to_string(), s("dev-master")); - m.insert("version".to_string(), s("dev-master")); - m.insert("reference".to_string(), s("sourceref-by-default")); - m.insert("type".to_string(), s("library")); - m.insert("install_path".to_string(), s(&format!("{}/./", dir))); - m.insert("aliases".to_string(), list(vec![s("1.10.x-dev")])); - m.insert("dev".to_string(), PhpMixed::Bool(true)); - m - }; - - assert_eq!(expected, InstalledVersions::get_root_package()); + // TODO(phase-d): needs InstalledVersions::get_root_package. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getRawData has no Rust counterpart"] fn test_get_raw_data() { - let (_root, dir) = set_up(); - - assert_eq!(fixture(&dir), InstalledVersions::get_raw_data()); + // TODO(phase-d): needs InstalledVersions::get_raw_data. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getReference has no Rust counterpart"] fn test_get_reference() { - let (_root, _dir) = set_up(); - - let cases: Vec<(Option<&str>, &str)> = vec![ - (Some("sourceref-by-default"), "__root__"), - (None, "foo/impl"), - (None, "foo/impl2"), - (None, "foo/replaced"), - (Some("distref-as-no-source"), "a/provider"), - (Some("distref-as-installed-from-dist"), "a/provider2"), - (None, "b/replacer"), - (None, "c/c"), - ]; - - for (expected, name) in cases { - assert_eq!( - expected.map(|s| s.to_string()), - InstalledVersions::get_reference(name).unwrap() - ); - } + // TODO(phase-d): needs InstalledVersions::get_reference. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getInstalledPackagesByType has no Rust counterpart"] fn test_get_installed_packages_by_type() { - let (_root, _dir) = set_up(); - - let names = vec![ - "__root__".to_string(), - "a/provider".to_string(), - "a/provider2".to_string(), - "b/replacer".to_string(), - "c/c".to_string(), - ]; - - assert_eq!( - names, - InstalledVersions::get_installed_packages_by_type("library") - ); + // TODO(phase-d): needs InstalledVersions::get_installed_packages_by_type. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::getInstallPath has no Rust counterpart"] fn test_get_install_path() { - let (_root, dir) = set_up(); - - assert_eq!( - realpath(&dir), - realpath( - InstalledVersions::get_install_path("__root__") - .unwrap() - .unwrap() - ) - ); - assert_eq!( - Some("/foo/bar/vendor/c/c".to_string()), - InstalledVersions::get_install_path("c/c").unwrap() - ); - assert_eq!( - None, - InstalledVersions::get_install_path("foo/impl").unwrap() - ); + // TODO(phase-d): needs InstalledVersions::get_install_path. + todo!() } -#[serial] #[test] +#[ignore = "InstalledVersions::isInstalled and getRootPackage have no Rust counterpart"] fn test_with_class_loader_loaded() { - let (_root, _dir) = set_up(); - - // The reflection into ClassLoader::registeredLoaders is not ported; installedIsLocalDir is - // toggled directly via the exposed setter to mirror the PHP reflection on it. - InstalledVersions::set_installed_is_local_dir(true); - - assert!(!InstalledVersions::is_installed("foo/bar", true)); - - let reload_data = { - let mut m = IndexMap::new(); - m.insert( - "root".to_string(), - PhpMixed::Array(InstalledVersions::get_root_package()), - ); - m.insert( - "versions".to_string(), - arr(vec![( - "foo/bar", - arr(vec![ - ("version", s("1.0.0")), - ("dev_requirement", PhpMixed::Bool(false)), - ]), - )]), - ); - m - }; - InstalledVersions::reload(reload_data); - assert!(InstalledVersions::is_installed("foo/bar", true)); + // TODO(phase-d): needs InstalledVersions::is_installed and + // InstalledVersions::get_root_package. + todo!() } diff --git a/crates/shirabe/tests/repository/filesystem_repository_test.rs b/crates/shirabe/tests/repository/filesystem_repository_test.rs index a9ac91a0..b87e1502 100644 --- a/crates/shirabe/tests/repository/filesystem_repository_test.rs +++ b/crates/shirabe/tests/repository/filesystem_repository_test.rs @@ -4,7 +4,6 @@ use crate::test_case::{get_alias_package, get_package}; use indexmap::IndexMap; use serial_test::serial; use shirabe::dependency_resolver::operation::AnyOperation; -use shirabe::installed_versions::InstalledVersions; use shirabe::installer::{InstallationManagerInterface, InstallerInterface}; use shirabe::io::IOInterface; use shirabe::json::json_file::JsonFile; @@ -326,70 +325,10 @@ fn test_repository_writes_installed_php() { assert_eq!(expected, actual); } -#[ignore = "safely_load_installed_versions's pattern uses a PCRE (?(DEFINE)...) recursive grammar the regex crate cannot compile"] +#[ignore = "safely_load_installed_versions's pattern uses a PCRE (?(DEFINE)...) recursive grammar the regex crate cannot compile, and InstalledVersions::getAllRawData has no Rust counterpart"] #[test] fn test_safely_load_installed_versions() { - let fixtures_dir = format!( - "{}/../../composer/tests/Composer/Test/Repository/Fixtures", - env!("CARGO_MANIFEST_DIR") - ); - let path = format!("{}/installed_complex.php", fixtures_dir); - - let result = FilesystemRepository::safely_load_installed_versions(&path); - assert!(result, "The file should be considered valid"); - - let raw_data = InstalledVersions::get_all_raw_data(); - let raw_data = raw_data.last().cloned().unwrap(); - - let mut root: IndexMap<String, PhpMixed> = IndexMap::new(); - root.insert( - "install_path".to_string(), - PhpMixed::String(format!("{}/./", fixtures_dir)), - ); - root.insert( - "aliases".to_string(), - PhpMixed::List(vec![ - PhpMixed::String("1.10.x-dev".to_string()), - PhpMixed::String("2.10.x-dev".to_string()), - ]), - ); - root.insert("name".to_string(), PhpMixed::String("__root__".to_string())); - root.insert("true".to_string(), PhpMixed::Bool(true)); - root.insert("false".to_string(), PhpMixed::Bool(false)); - root.insert("null".to_string(), PhpMixed::Null); - - let mut a_provider: IndexMap<String, PhpMixed> = IndexMap::new(); - a_provider.insert( - "foo".to_string(), - PhpMixed::String("simple string/no backslash".to_string()), - ); - a_provider.insert( - "install_path".to_string(), - PhpMixed::String(format!( - "{}/vendor/{{${{passthru('bash -i')}}}}", - fixtures_dir - )), - ); - a_provider.insert("empty array".to_string(), PhpMixed::List(vec![])); - - let mut c_c: IndexMap<String, PhpMixed> = IndexMap::new(); - c_c.insert( - "install_path".to_string(), - PhpMixed::String("/foo/bar/ven/do{}r/c/c${}".to_string()), - ); - c_c.insert("aliases".to_string(), PhpMixed::List(vec![])); - c_c.insert( - "reference".to_string(), - PhpMixed::String("{${passthru('bash -i')}} Foo\\Bar\n\ttab\u{0b}verticaltab\0".to_string()), - ); - - let mut versions: IndexMap<String, PhpMixed> = IndexMap::new(); - versions.insert("a/provider".to_string(), PhpMixed::Array(a_provider)); - versions.insert("c/c".to_string(), PhpMixed::Array(c_c)); - - let mut expected: IndexMap<String, PhpMixed> = IndexMap::new(); - expected.insert("root".to_string(), PhpMixed::Array(root)); - expected.insert("versions".to_string(), PhpMixed::Array(versions)); - - assert_eq!(raw_data, expected); + // TODO(phase-d): needs a regex-crate expression equivalent to the PCRE recursive grammar, and + // InstalledVersions::get_all_raw_data. + todo!() } |
