aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates
diff options
context:
space:
mode:
Diffstat (limited to 'crates')
-rw-r--r--crates/shirabe-php-shim/src/runtime.rs25
-rw-r--r--crates/shirabe/src/installed_versions.rs458
-rw-r--r--crates/shirabe/src/lib.rs5
-rw-r--r--crates/shirabe/src/repository/filesystem_repository.rs23
-rw-r--r--crates/shirabe/tests/installed_versions_test.rs442
-rw-r--r--crates/shirabe/tests/repository/filesystem_repository_test.rs69
6 files changed, 59 insertions, 963 deletions
diff --git a/crates/shirabe-php-shim/src/runtime.rs b/crates/shirabe-php-shim/src/runtime.rs
index e24d6d69..2f8b79f1 100644
--- a/crates/shirabe-php-shim/src/runtime.rs
+++ b/crates/shirabe-php-shim/src/runtime.rs
@@ -66,18 +66,6 @@ pub fn defined(name: &str) -> bool {
)
}
-// Models methods available on Composer's own runtime classes when given a class name string.
-// Shirabe is a native binary that does not run under a Composer-dumped autoloader, so the dumped
-// `Composer\Autoload\ClassLoader` (and its `getRegisteredLoaders`) is absent from the running
-// process; the class-name form therefore reports no such method. The object form needs runtime
-// reflection that PhpMixed::Object does not carry.
-pub fn method_exists(object_or_class: &PhpMixed, _method_name: &str) -> bool {
- match object_or_class {
- PhpMixed::String(_) => false,
- _ => todo!(),
- }
-}
-
// Models the classes available in a standard PHP CLI environment running Composer:
// the common bundled extensions (zip, Phar) plus Composer's own runtime classes.
pub fn class_exists(name: &str) -> bool {
@@ -351,23 +339,12 @@ pub fn usleep(_microseconds: u64) {
}
/// Equivalent to PHP's __DIR__ magic constant
-pub fn php_dir() -> String {
+pub fn dir() -> String {
// TODO(php-runtime): __DIR__ is the directory of the source file at compile time; it must be supplied
// per call site (e.g. via a macro), not from a runtime shim function.
todo!()
}
-pub fn dir() -> String {
- // TODO(php-runtime): see php_dir; __DIR__ is a per-source-file compile-time value.
- todo!()
-}
-
-/// Equivalent to PHP's `require <file>` returning the file's return value
-pub fn require_php_file(_filename: &str) -> PhpMixed {
- // TODO(php-runtime): `require` evaluates a PHP source file at runtime; there is no PHP interpreter.
- todo!()
-}
-
pub fn memory_get_usage() -> i64 {
// TODO(phase-c): return PHP's actual emalloc-tracked memory usage instead of a stub 0.
0
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!()
}