//! ref: composer/tests/Composer/Test/InstallerTest.php #[path = "common/bootstrap.rs"] mod bootstrap; #[path = "common/config_stub.rs"] mod config_stub; #[path = "common/test_case.rs"] mod test_case; use config_stub::ConfigStubBuilder; use serial_test::serial; use test_case::{get_package, get_version_constraint}; use indexmap::IndexMap; use shirabe::advisory::{AuditConfig, Auditor}; use shirabe::autoload::{AutoloadGeneratorInterface, ClassLoader}; use shirabe::config::Config; use shirabe::console::application::ApplicationHandle; use shirabe::dependency_resolver::{Transaction, UpdateAllowTransitiveDeps}; use shirabe::downloader::{DownloadManagerInterface, DownloaderInterface}; use shirabe::event_dispatcher::{Callable, EventDispatcherInterface, EventInterface}; use shirabe::factory::{DisablePlugins, Factory, LocalConfigInput}; use shirabe::filter::platform_requirement_filter::{ PlatformRequirementFilterFactory, PlatformRequirementFilterInterface, }; use shirabe::installer::{InstallationManager, Installer}; use shirabe::io::IOInterface; use shirabe::io::buffer_io::BufferIO; use shirabe::json::JsonFile; use shirabe::package::dumper::ArrayDumper; use shirabe::package::{ Link, Locker, LockerInterface, PackageInterfaceHandle, RootPackageHandle, RootPackageInterfaceHandle, }; use shirabe::repository::{ ArrayRepository, InstalledArrayRepository, InstalledRepositoryInterface, RepositoryInterfaceHandle, RepositoryManager, RepositoryManagerInterface, }; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe::util::platform::Platform; use shirabe::util::process_executor::ProcessExecutor; use shirabe_class_map_generator::class_map::ClassMap; use shirabe_external_packages::composer::pcre::preg::Preg; use shirabe_external_packages::symfony::console::command::command::Command as SymfonyCommand; use shirabe_external_packages::symfony::console::command::command::CommandData; use shirabe_external_packages::symfony::console::input::input_argument::InputArgument; use shirabe_external_packages::symfony::console::input::input_interface::InputInterface; use shirabe_external_packages::symfony::console::input::input_option::InputOption; use shirabe_external_packages::symfony::console::input::string_input::StringInput; use shirabe_external_packages::symfony::console::output::output_interface::{ OutputInterface, VERBOSITY_NORMAL, }; use shirabe_external_packages::symfony::console::output::stream_output::StreamOutput; use shirabe_php_shim::{PREG_SPLIT_DELIM_CAPTURE, PhpMixed, php_regex}; use shirabe_semver::VersionParser; use shirabe_semver::constraint::AnyConstraint; // removeDirectory of tempComposerHome (a path produced by the unported install pipeline) is not // ported; the cwd management (setUp's chdir(__DIR__) / tearDown's chdir($this->prevCwd)) and the // env clears are. fn tear_down() { Platform::clear_env("COMPOSER_POOL_OPTIMIZER"); Platform::clear_env("COMPOSER_FUND"); } /// `#[serial]` on every test constructing this makes the process-wide chdir safe: serial_test's /// unnamed lock is shared across the whole binary, so no two tests holding one of these can run /// concurrently. struct TearDown { prev_cwd: std::path::PathBuf, } impl TearDown { fn new() -> Self { let prev_cwd = std::env::current_dir().unwrap(); let test_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../composer/tests/Composer/Test") .canonicalize() .unwrap(); std::env::set_current_dir(&test_dir).unwrap(); Self { prev_cwd } } } impl Drop for TearDown { fn drop(&mut self) { tear_down(); std::env::set_current_dir(&self.prev_cwd).unwrap(); } } // PHP mocks `Composer\Downloader\DownloadManager` with getMockBuilder; PHPUnit mocks are permissive // (every method returns null), so the Rust equivalent is a no-op stub over the trait seam. #[derive(Debug)] struct StubDownloadManager; #[async_trait::async_trait(?Send)] impl DownloadManagerInterface for StubDownloadManager { 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>>> { Ok(None) } async fn download( &self, _package: PackageInterfaceHandle, _target_dir: &str, _prev_package: Option, ) -> anyhow::Result> { Ok(None) } async fn prepare( &self, _type: &str, _package: PackageInterfaceHandle, _target_dir: &str, _prev_package: Option, ) -> anyhow::Result> { Ok(None) } async fn install( &self, _package: PackageInterfaceHandle, _target_dir: &str, ) -> anyhow::Result> { Ok(None) } async fn update( &self, _initial: PackageInterfaceHandle, _target: PackageInterfaceHandle, _target_dir: &str, ) -> anyhow::Result> { Ok(None) } async fn remove( &self, _package: PackageInterfaceHandle, _target_dir: &str, ) -> anyhow::Result> { Ok(None) } async fn cleanup( &self, _type: &str, _package: PackageInterfaceHandle, _target_dir: &str, _prev_package: Option, ) -> anyhow::Result> { Ok(None) } } // PHP mocks `Composer\EventDispatcher\EventDispatcher` with disableOriginalConstructor()->getMock(); // a permissive no-op stub mirrors the PHPUnit mock. #[derive(Debug)] struct StubEventDispatcher; impl EventDispatcherInterface for StubEventDispatcher { fn dispatch( &mut self, _event_name: Option<&str>, _event: Option<&mut dyn EventInterface>, ) -> anyhow::Result { Ok(0) } fn dispatch_script( &mut self, _event_name: &str, _dev_mode: bool, _additional_args: Vec, _flags: IndexMap, ) -> anyhow::Result { Ok(0) } fn dispatch_installer_event( &mut self, _event_name: &str, _dev_mode: bool, _execute_operations: bool, _transaction: Transaction, ) -> anyhow::Result { Ok(0) } fn add_listener(&mut self, _event_name: &str, _listener: Callable, _priority: i64) {} fn has_event_listeners(&mut self, _event: &dyn EventInterface) -> bool { false } } // PHP mocks `Composer\Autoload\AutoloadGenerator` with disableOriginalConstructor()->getMock(); // a permissive no-op stub mirrors the PHPUnit mock. #[derive(Debug)] struct StubAutoloadGenerator; impl AutoloadGeneratorInterface for StubAutoloadGenerator { fn set_dev_mode(&mut self, _dev_mode: bool) {} fn set_class_map_authoritative(&mut self, _class_map_authoritative: bool) {} fn set_apcu(&mut self, _apcu: bool, _apcu_prefix: Option) {} fn set_run_scripts(&mut self, _run_scripts: bool) {} fn set_dry_run(&mut self, _dry_run: bool) {} fn set_platform_requirement_filter( &mut self, _platform_requirement_filter: std::rc::Rc, ) { } #[allow(clippy::too_many_arguments)] fn dump( &mut self, _config: &Config, _local_repo: &mut dyn InstalledRepositoryInterface, _root_package: RootPackageInterfaceHandle, _installation_manager: &mut dyn shirabe::installer::InstallationManagerInterface, _target_dir: &str, _scan_psr_packages: bool, _suffix: Option, _locker: Option<&mut dyn LockerInterface>, _strict_ambiguous: bool, ) -> anyhow::Result { Ok(ClassMap::new()) } fn build_package_map( &self, _installation_manager: &mut dyn shirabe::installer::InstallationManagerInterface, _root_package: RootPackageInterfaceHandle, _packages: Vec, ) -> anyhow::Result)>> { Ok(vec![]) } fn parse_autoloads( &self, _package_map: Vec<(PackageInterfaceHandle, Option)>, _root_package: RootPackageInterfaceHandle, _filtered_dev_packages: PhpMixed, ) -> IndexMap { IndexMap::new() } fn create_loader( &self, _autoloads: &IndexMap, _vendor_dir: Option, ) -> ClassLoader { unimplemented!("create_loader is not reached by the installer test path") } } /// ref: TestCase::getPackage with class `Composer\Package\RootPackage`. fn root_package(name: &str, version: &str) -> RootPackageHandle { let normalized = VersionParser.normalize(version, None).unwrap(); RootPackageHandle::new(name.to_string(), normalized, version.to_string()) } /// ref: `new Link($source, $target, $constraint, $type, $constraint->getPrettyString())`. fn link(source: &str, target: &str, constraint: AnyConstraint, r#type: &str) -> Link { let pretty = constraint.get_pretty_string(); Link::new( source.to_string(), target.to_string(), constraint, Some(r#type.to_string()), pretty, ) } /// One row of `provideInstaller`. struct InstallerCase { root_package: RootPackageHandle, repositories: Vec, expected_install: Vec, expected_update: Vec<(PackageInterfaceHandle, PackageInterfaceHandle)>, expected_uninstall: Vec, } /// ref: InstallerTest::provideInstaller fn provide_installer() -> Vec { let mut cases = vec![]; // when A requires B and B requires A, and A is a non-published root package // the install of B should succeed let a = root_package("A", "1.0.0"); a.set_requires(IndexMap::from([( "b".to_string(), link( "A", "B", get_version_constraint("=", "1.0.0"), Link::TYPE_REQUIRE, ), )])); let b = get_package("B", "1.0.0"); b.as_complete_package() .unwrap() .__set_requires(IndexMap::from([( "a".to_string(), link( "B", "A", get_version_constraint("=", "1.0.0"), Link::TYPE_REQUIRE, ), )])); cases.push(InstallerCase { root_package: a, repositories: vec![RepositoryInterfaceHandle::new( ArrayRepository::new(vec![b.clone()]).unwrap(), )], expected_install: vec![b], expected_update: vec![], expected_uninstall: vec![], }); // #480: when A requires B and B requires A, and A is a published root package // only B should be installed, as A is the root let a = root_package("A", "1.0.0"); a.set_requires(IndexMap::from([( "b".to_string(), link( "A", "B", get_version_constraint("=", "1.0.0"), Link::TYPE_REQUIRE, ), )])); let b = get_package("B", "1.0.0"); b.as_complete_package() .unwrap() .__set_requires(IndexMap::from([( "a".to_string(), link( "B", "A", get_version_constraint("=", "1.0.0"), Link::TYPE_REQUIRE, ), )])); cases.push(InstallerCase { root_package: a.clone(), repositories: vec![RepositoryInterfaceHandle::new( ArrayRepository::new(vec![a.into(), b.clone()]).unwrap(), )], expected_install: vec![b], expected_update: vec![], expected_uninstall: vec![], }); // TODO why are there not more cases with uninstall/update? cases } /// ref: InstallerTest::makePackagesComparable fn make_packages_comparable( packages: &[PackageInterfaceHandle], ) -> Vec> { let dumper = ArrayDumper::new(); packages.iter().map(|p| dumper.dump(p.clone())).collect() } #[test] #[serial] fn test_installer() { let _tear_down = TearDown::new(); for case in provide_installer() { let io_buffer = std::rc::Rc::new(std::cell::RefCell::new( BufferIO::new(String::new(), VERBOSITY_NORMAL, None).unwrap(), )); let io: std::rc::Rc> = io_buffer.clone(); let config = ConfigStubBuilder::new() .with("vendor-dir", PhpMixed::String("foo".to_string())) .with("lock", PhpMixed::Bool(true)) .with("notify-on-install", PhpMixed::Bool(true)) .build_shared(); let download_manager: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(StubDownloadManager)); let http_downloader = std::rc::Rc::new(std::cell::RefCell::new( HttpDownloader::__new_mock(io.clone(), config.clone()), )); let mut repository_manager = RepositoryManager::new( io.clone(), config.clone(), http_downloader.clone(), None, None, ); repository_manager.set_local_repository(RepositoryInterfaceHandle::new( InstalledArrayRepository::new().unwrap(), )); for repository in &case.repositories { repository_manager.add_repository(repository.clone()); } let repository_manager: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(repository_manager)); let r#loop = std::rc::Rc::new(std::cell::RefCell::new(Loop::new( http_downloader.clone(), None, ))); let installation_manager: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(InstallationManager::__new_mock( r#loop, io.clone(), None, ))); // emulate a writable lock file: a real JsonFile over a fresh temp path (initially absent, so // the installer falls back to an update; PHP uses an in-memory JsonFile mock instead). let lock_dir = tempfile::TempDir::new().unwrap(); let lock_path = lock_dir.path().join("composer.lock"); let lock_json = JsonFile::new(lock_path.to_string_lossy().into_owned(), None, None).unwrap(); let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( io.clone(), )))); let locker: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(Locker::new( io.clone(), lock_json, installation_manager.clone(), "{}", process, ))); let autoload_generator: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(StubAutoloadGenerator)); let root_package: RootPackageInterfaceHandle = RootPackageInterfaceHandle::dup(&case.root_package.clone().into()); let mut installer = Installer::new( io.clone(), config.clone(), root_package, download_manager, repository_manager, locker, installation_manager.clone(), std::rc::Rc::new(std::cell::RefCell::new(StubEventDispatcher)), autoload_generator, ); installer.set_audit_config( AuditConfig::from_config(&mut config.borrow_mut(), false, Auditor::FORMAT_SUMMARY) .unwrap(), ); let result = installer.run().unwrap(); let output = io_buffer.borrow().get_output().replace('\r', ""); assert_eq!(0, result, "{}", output); let installed = installation_manager.borrow().__get_installed_packages(); assert_eq!( make_packages_comparable(&case.expected_install), make_packages_comparable(&installed), "{}", output ); let updated = installation_manager.borrow().__get_updated_packages(); assert_eq!(case.expected_update, updated); let uninstalled = installation_manager.borrow().__get_uninstalled_packages(); assert_eq!(case.expected_uninstall, uninstalled); } } /// ref: PHPUnit assertStringMatchesFormat's StringMatchesFormatDescription::createPatternFromFormat. fn create_pattern_from_format(format: &str) -> String { let escaped = regex::escape(format); let bytes = escaped.as_bytes(); let mut out = String::from("(?s)^"); let mut i = 0; while i < bytes.len() { // regex::escape turns "%" into "%" (it is not special) so the format codes survive intact. if bytes[i] == b'%' && i + 1 < bytes.len() { let replacement: Option<&str> = match bytes[i + 1] { b'%' => Some("%"), b'e' => Some("\\/"), b's' => Some("[^\\r\\n]+"), b'S' => Some("[^\\r\\n]*"), b'a' => Some(".+"), b'A' => Some(".*"), b'w' => Some("\\s*"), b'i' => Some("[+-]?\\d+"), b'd' => Some("\\d+"), b'x' => Some("[0-9a-fA-F]+"), b'f' => Some("[+-]?\\.?\\d+\\.?\\d*(?:[Ee][+-]?\\d+)?"), b'c' => Some("."), _ => None, }; if let Some(replacement) = replacement { out.push_str(replacement); i += 2; continue; } } out.push(escaped[i..].chars().next().unwrap()); i += escaped[i..].chars().next().unwrap().len_utf8(); } out.push('$'); out } /// ref: PHPUnit self::assertStringMatchesFormat. fn assert_string_matches_format(format: &str, subject: &str, context: &str) { let pattern = create_pattern_from_format(format); let re = regex::Regex::new(&pattern) .unwrap_or_else(|e| panic!("invalid format pattern {}: {}", pattern, e)); assert!( re.is_match(subject), "output does not match format.\n--- format ---\n{}\n--- output ---\n{}\n--- context ---\n{}", format, subject, context ); } #[derive(Debug, Clone)] enum ExpectLock { /// No EXPECT-LOCK section (`[]` in PHP); the lock is not asserted. Unset, /// EXPECT-LOCK is the literal string "false"; the lock must never be written. Never, /// EXPECT-LOCK holds an expected lock JSON. Json(serde_json::Value), } #[derive(Debug, Clone)] enum ExpectResult { ExitCode(i64), /// EXPECT-EXCEPTION: the class-string of an expected exception. Exception(String), } #[derive(Debug, Clone)] struct IntegrationCase { file: String, message: String, condition: Option, composer: serde_json::Value, lock: Option, installed: Option, run: String, expect_lock: ExpectLock, expect_installed: Option, expect_output: Option, expect_output_optimized: Option, expect: String, expect_result: ExpectResult, } fn fixtures_dir(path: &str) -> std::path::PathBuf { std::path::Path::new(env!("CARGO_MANIFEST_DIR")) .join("../../composer/tests/Composer/Test/Fixtures") .join(path) .canonicalize() .unwrap() } /// ref: InstallerTest::readTestFile fn read_test_file( file: &std::path::Path, fixtures_dir: &std::path::Path, ) -> IndexMap { let contents = std::fs::read_to_string(file).unwrap(); let tokens = Preg::split4( php_regex!(r"#(?:^|\n*)--([A-Z-]+)--\n#"), &contents, -1, PREG_SPLIT_DELIM_CAPTURE, ); let section_info: [(&str, bool); 13] = [ ("TEST", true), ("CONDITION", false), ("COMPOSER", true), ("LOCK", false), ("INSTALLED", false), ("RUN", true), ("EXPECT-LOCK", false), ("EXPECT-INSTALLED", false), ("EXPECT-OUTPUT", false), ("EXPECT-OUTPUT-OPTIMIZED", false), ("EXPECT-EXIT-CODE", false), ("EXPECT-EXCEPTION", false), ("EXPECT", true), ]; let known: indexmap::IndexSet<&str> = section_info.iter().map(|(k, _)| *k).collect(); let mut section: Option = None; let mut data: IndexMap = IndexMap::new(); for token in tokens { if section.is_none() && token.is_empty() { continue; // skip leading blank } if section.is_none() { assert!( known.contains(token.as_str()), "The test file \"{}\" must not contain a section named \"{}\".", file.display(), token ); section = Some(token); continue; } let sec = section.take().unwrap(); data.insert(sec, token); } for (sec, required) in section_info { if required { assert!( data.contains_key(sec), "The test file \"{}\" must have a section named \"{}\".", file.display(), sec ); } } let _ = fixtures_dir; data } /// PHP truthiness of a `json_decode`d value, used to replicate `if ($expectLock)` checks. fn php_json_truthy(v: &serde_json::Value) -> bool { match v { serde_json::Value::Null => false, serde_json::Value::Bool(b) => *b, serde_json::Value::Number(n) => n.as_f64().is_some_and(|f| f != 0.0), serde_json::Value::String(s) => !s.is_empty() && s != "0", serde_json::Value::Array(a) => !a.is_empty(), serde_json::Value::Object(o) => !o.is_empty(), } } fn collect_test_files(dir: &std::path::Path, out: &mut Vec) { for entry in std::fs::read_dir(dir).unwrap() { let entry = entry.unwrap(); let path = entry.path(); if path.is_dir() { collect_test_files(&path, out); } else if path.extension().and_then(|e| e.to_str()) == Some("test") { out.push(path); } } } /// ref: InstallerTest::loadIntegrationTests fn load_integration_tests(path: &str) -> Vec { let dir = fixtures_dir(path); let mut files = Vec::new(); collect_test_files(&dir, &mut files); files.sort(); let mut tests = Vec::new(); for file in files { let test_data = read_test_file(&file, &dir); // skip 64bit related tests on 32bit (usize is 64-bit here, so this never triggers). if test_data .get("EXPECT-OUTPUT") .map(|s| s.contains("php-64bit")) .unwrap_or(false) && (usize::BITS == 32) { continue; } let message = test_data["TEST"].clone(); let condition = test_data .get("CONDITION") .filter(|s| !s.is_empty()) .cloned(); let mut composer: serde_json::Value = serde_json::from_str(&test_data["COMPOSER"]).unwrap(); if let Some(repositories) = composer.get_mut("repositories") { let fixtures_str = dir.to_string_lossy().replace('\\', "/"); let rewrite = |repo: &mut serde_json::Value| { if repo.get("type").and_then(|t| t.as_str()) != Some("composer") { return; } if let Some(url) = repo.get("url").and_then(|u| u.as_str()) && Preg::is_match(php_regex!(r"{^file://[^/]}"), url) { let new_url = format!("file://{}/{}", fixtures_str, &url[7..]); repo["url"] = serde_json::Value::String(new_url); } }; match repositories { serde_json::Value::Array(list) => list.iter_mut().for_each(rewrite), serde_json::Value::Object(map) => map.values_mut().for_each(rewrite), _ => {} } } let lock = test_data.get("LOCK").filter(|s| !s.is_empty()).map(|s| { let mut lock: serde_json::Value = serde_json::from_str(s).unwrap(); if lock.get("hash").is_none() { let encoded = JsonFile::encode_with_options( &composer, shirabe::json::JsonEncodeOptions::none(), ); let hash = format!("{:x}", md5::compute(encoded.as_bytes())); lock["hash"] = serde_json::Value::String(hash); } lock }); let installed = test_data .get("INSTALLED") .filter(|s| !s.is_empty()) .map(|s| serde_json::from_str(s).unwrap()); let run = test_data["RUN"].clone(); // PHP: `$expectLock === 'false' ? false : JsonFile::parseJson(...)`, then branches on // truthiness (`if ($expectLock)` / `elseif ($expectLock === false)`). A bare `false` // literal parses to the JSON boolean false, which is falsy either way, so both paths // collapse to the same outcome; mirror that by checking truthiness of the parsed value // rather than the raw string (the section's trailing newline defeats a literal "false" // string comparison when it is the file's last section). let expect_lock = match test_data.get("EXPECT-LOCK").filter(|s| !s.is_empty()) { None => ExpectLock::Unset, Some(s) => { let parsed: serde_json::Value = serde_json::from_str(s).unwrap(); if php_json_truthy(&parsed) { ExpectLock::Json(parsed) } else { ExpectLock::Never } } }; let expect_installed = test_data .get("EXPECT-INSTALLED") .filter(|s| !s.is_empty()) .map(|s| serde_json::from_str(s).unwrap()); let expect_output = test_data.get("EXPECT-OUTPUT").cloned(); let expect_output_optimized = test_data.get("EXPECT-OUTPUT-OPTIMIZED").cloned(); let expect = test_data["EXPECT"].clone(); let expect_result = if let Some(exc) = test_data.get("EXPECT-EXCEPTION").filter(|s| !s.is_empty()) { assert!( test_data .get("EXPECT-EXIT-CODE") .filter(|s| !s.is_empty()) .is_none(), "EXPECT-EXCEPTION and EXPECT-EXIT-CODE are mutually exclusive" ); ExpectResult::Exception(exc.clone()) } else if let Some(code) = test_data.get("EXPECT-EXIT-CODE").filter(|s| !s.is_empty()) { ExpectResult::ExitCode(code.trim().parse().unwrap()) } else { ExpectResult::ExitCode(0) }; tests.push(IntegrationCase { file: file .strip_prefix(&dir) .unwrap() .to_string_lossy() .into_owned(), message, condition, composer, lock, installed, run, expect_lock, expect_installed, expect_output, expect_output_optimized, expect, expect_result, }); } tests } /// ref: the inline `eval($condition)` in doTestIntegration, ported for the known fixture conditions. fn evaluate_condition(condition: &str) -> bool { match condition.trim() { // putenv() returns true on success, so these conditions always run the test (with the env set). "putenv('COMPOSER_FUND=1')" => { Platform::put_env("COMPOSER_FUND", "1"); true } "putenv('COMPOSER_FUND=0')" => { Platform::put_env("COMPOSER_FUND", "0"); true } // HHVM is never defined under the Rust port. "!defined('HHVM_VERSION')" => true, // TODO(phase-d): unported CONDITION expression (PHP eval has no Rust equivalent). other => panic!("// TODO(phase-d): unported CONDITION: {}", other), } } fn opt_bool(input: &dyn InputInterface, name: &str) -> bool { input .get_option(name) .ok() .and_then(|m| m.as_bool()) .unwrap_or(false) } /// ref: `$ignorePlatformReqs = true === getOption('ignore-platform-reqs') ?: (getOption('ignore-platform-req') ?: false)`. fn ignore_platform_reqs_value(input: &dyn InputInterface) -> PhpMixed { if opt_bool(input, "ignore-platform-reqs") { return PhpMixed::Bool(true); } let list = input .get_option("ignore-platform-req") .unwrap_or(PhpMixed::Bool(false)); match &list { PhpMixed::List(items) if !items.is_empty() => list, PhpMixed::Array(map) if !map.is_empty() => list, _ => PhpMixed::Bool(false), } } fn write_json(path: &std::path::Path, value: &serde_json::Value) { std::fs::write(path, serde_json::to_string_pretty(value).unwrap()).unwrap(); } /// ref: InstallerTest::doTestIntegration fn do_test_integration(case: &IntegrationCase, expect_output: Option<&str>) { if let Some(condition) = &case.condition && !evaluate_condition(condition) { return; // markTestSkipped } let io_buffer = std::rc::Rc::new(std::cell::RefCell::new( BufferIO::new(String::new(), VERBOSITY_NORMAL, None).unwrap(), )); let io: std::rc::Rc> = io_buffer.clone(); let is_exception = matches!(case.expect_result, ExpectResult::Exception(_)); // Create Composer mock object according to configuration (FactoryMock::create). let composer_str = serde_json::to_string(&case.composer).unwrap(); let composer_data = JsonFile::parse_json(Some(&composer_str), None) .unwrap() .as_array() .cloned() .unwrap_or_default(); let composer_result = Factory::__create_mock( io.clone(), Some(LocalConfigInput::Data(composer_data)), DisablePlugins::None, false, ); // PHP's PHPUnit::expectException wraps the whole rest of the test method, so an exception // expected from the fixture can come from composer construction itself, not just the later // install/update run; check for that here since Rust has no equivalent ambient wrapper. if is_exception && let Err(e) = &composer_result { let normalized = case.expect.replace('\n', shirabe_php_shim::PHP_EOL); let normalized = normalized.trim_end(); let err = format!("{}", e); assert!( err.contains(normalized), "expected exception message containing:\n{}\n--- got ---\n{}", normalized, err ); return; } let composer = composer_result.unwrap(); // installed.json mock: a real JsonFile over a temp file holding $installed, wrapped in the // no-op InstalledFilesystemRepositoryMock. let installed_dir = tempfile::TempDir::new().unwrap(); let installed_path = installed_dir.path().join("installed.json"); write_json( &installed_path, case.installed.as_ref().unwrap_or(&serde_json::json!([])), ); let installed_json = JsonFile::new(installed_path.to_string_lossy().into_owned(), None, None).unwrap(); let local_repo = shirabe::repository::InstalledFilesystemRepository::__new_mock( installed_json, false, None, None, ) .unwrap(); let repository_manager = composer.borrow().get_repository_manager(); repository_manager .borrow_mut() .set_local_repository(RepositoryInterfaceHandle::new(local_repo)); // emulate a writable lock file: a real composer.lock over a temp path. let lock_dir = tempfile::TempDir::new().unwrap(); let lock_path = lock_dir.path().join("composer.lock"); if let Some(lock) = &case.lock { write_json(&lock_path, lock); } let lock_before = std::fs::read_to_string(&lock_path).ok(); let lock_json = JsonFile::new(lock_path.to_string_lossy().into_owned(), None, None).unwrap(); // The Locker needs a concrete InstallationManager; build a fresh recording mock just for it. The // asserted trace comes from the composer's own installation manager (read via as_any below). let process = std::rc::Rc::new(std::cell::RefCell::new(ProcessExecutor::new(Some( io.clone(), )))); let locker_loop = composer.borrow().get_loop(); let locker_im = std::rc::Rc::new(std::cell::RefCell::new(InstallationManager::__new_mock( locker_loop, io.clone(), None, ))); // Must match the encoding used for the lock's auto-computed "hash" field in // load_integration_tests (PHP: `json_encode($composerConfig)`, i.e. options=0 with slashes // escaped), or the freshness check spuriously fails on any composer.json containing "/" // (e.g. any vendor/package name). let contents = JsonFile::encode_with_options(&case.composer, shirabe::json::JsonEncodeOptions::none()); let locker = Locker::new(io.clone(), lock_json, locker_im, &contents, process); composer .borrow_mut() .set_locker(std::rc::Rc::new(std::cell::RefCell::new(locker))); composer .borrow_mut() .set_autoload_generator(std::rc::Rc::new(std::cell::RefCell::new( StubAutoloadGenerator, ))); composer .borrow_mut() .set_event_dispatcher(std::rc::Rc::new(std::cell::RefCell::new( StubEventDispatcher, ))); let installer = std::rc::Rc::new(std::cell::RefCell::new(Installer::create( io.clone(), &composer.upcast(), ))); // Application with inline install/update commands (setCode closures). let application = ApplicationHandle::new("Composer".to_string(), "".to_string()).unwrap(); application.set_catch_exceptions(false); let run_result: std::rc::Rc>>> = std::rc::Rc::new(std::cell::RefCell::new(None)); let install = std::rc::Rc::new(std::cell::RefCell::new(CommandData::new(Some( "install".to_string(), )))); { let install_ref = install.borrow(); install_ref .add_option( "ignore-platform-reqs", PhpMixed::Null, Some(InputOption::VALUE_NONE), "", PhpMixed::Null, ) .unwrap(); install_ref .add_option( "ignore-platform-req", PhpMixed::Null, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "", PhpMixed::Null, ) .unwrap(); install_ref .add_option( "no-dev", PhpMixed::Null, Some(InputOption::VALUE_NONE), "", PhpMixed::Null, ) .unwrap(); install_ref .add_option( "dry-run", PhpMixed::Null, Some(InputOption::VALUE_NONE), "", PhpMixed::Null, ) .unwrap(); let installer_cl = installer.clone(); let composer_cl = composer.clone(); let run_result_cl = run_result.clone(); install_ref.set_code(Box::new(move |input, _output| { let ignore = ignore_platform_reqs_value(input); let mut inst = installer_cl.borrow_mut(); inst.set_dev_mode(!opt_bool(input, "no-dev")) .set_dry_run(opt_bool(input, "dry-run")) .set_platform_requirement_filter( PlatformRequirementFilterFactory::from_bool_or_list(ignore).unwrap(), ) .set_audit_config( AuditConfig::from_config( &mut composer_cl.borrow().get_config().borrow_mut(), false, Auditor::FORMAT_SUMMARY, ) .unwrap(), ); let r = inst.run(); let code = match &r { Ok(c) => *c, Err(_) => 1, }; *run_result_cl.borrow_mut() = Some(r); PhpMixed::Int(code) })); } application .add(install.clone() as std::rc::Rc>) .unwrap(); let update = std::rc::Rc::new(std::cell::RefCell::new(CommandData::new(Some( "update".to_string(), )))); { let update_ref = update.borrow(); for (name, mode) in [ ("ignore-platform-reqs", InputOption::VALUE_NONE), ("no-dev", InputOption::VALUE_NONE), ("no-install", InputOption::VALUE_NONE), ("dry-run", InputOption::VALUE_NONE), ("lock", InputOption::VALUE_NONE), ("with-all-dependencies", InputOption::VALUE_NONE), ("with-dependencies", InputOption::VALUE_NONE), ("minimal-changes", InputOption::VALUE_NONE), ("prefer-stable", InputOption::VALUE_NONE), ("prefer-lowest", InputOption::VALUE_NONE), ] { update_ref .add_option(name, PhpMixed::Null, Some(mode), "", PhpMixed::Null) .unwrap(); } update_ref .add_option( "ignore-platform-req", PhpMixed::Null, Some(InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY), "", PhpMixed::Null, ) .unwrap(); update_ref .add_argument( "packages", Some(InputArgument::IS_ARRAY | InputArgument::OPTIONAL), "", PhpMixed::Null, ) .unwrap(); let installer_cl = installer.clone(); let composer_cl = composer.clone(); let run_result_cl = run_result.clone(); update_ref.set_code(Box::new(move |input, _output| { let packages: Vec = match input.get_argument("packages").unwrap_or(PhpMixed::Null) { PhpMixed::List(items) => items .into_iter() .filter_map(|v| v.as_string().map(|s| s.to_string())) .collect(), _ => vec![], }; let filtered: Vec = packages .iter() .filter(|p| !["lock", "nothing", "mirrors"].contains(&p.as_str())) .cloned() .collect(); let update_mirrors = opt_bool(input, "lock") || filtered.len() != packages.len(); let update_allow_transitive = if opt_bool(input, "with-all-dependencies") { UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDeps } else if opt_bool(input, "with-dependencies") { UpdateAllowTransitiveDeps::UpdateListedWithTransitiveDepsNoRootRequire } else { UpdateAllowTransitiveDeps::UpdateOnlyListed }; let ignore = ignore_platform_reqs_value(input); let mut inst = installer_cl.borrow_mut(); inst.set_dev_mode(!opt_bool(input, "no-dev")) .set_update(true) .set_install(!opt_bool(input, "no-install")) .set_dry_run(opt_bool(input, "dry-run")) .set_update_mirrors(update_mirrors) .set_update_allow_list(filtered) .set_update_allow_transitive_dependencies(update_allow_transitive) .unwrap() .set_prefer_stable(opt_bool(input, "prefer-stable")) .set_prefer_lowest(opt_bool(input, "prefer-lowest")) .set_platform_requirement_filter( PlatformRequirementFilterFactory::from_bool_or_list(ignore).unwrap(), ) .set_audit_config( AuditConfig::from_config( &mut composer_cl.borrow().get_config().borrow_mut(), false, Auditor::FORMAT_SUMMARY, ) .unwrap(), ) .set_minimal_update(opt_bool(input, "minimal-changes")); let r = inst.run(); let code = match &r { Ok(c) => *c, Err(_) => 1, }; *run_result_cl.borrow_mut() = Some(r); PhpMixed::Int(code) })); } application .add(update.clone() as std::rc::Rc>) .unwrap(); assert!( Preg::is_match(r"{^(install|update)\b}", &case.run), "The run command only supports install and update" ); let app_output_stream = shirabe_php_shim::php_fopen_resource("php://memory", "w+"); let app_output = StreamOutput::new(app_output_stream.clone(), None, None, None) .unwrap() .expect("php://memory is a valid stream"); let mut string_input = StringInput::new(&format!("{} -vvv", case.run)).unwrap(); string_input.set_interactive(false); let input: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(string_input)); let output: std::rc::Rc> = std::rc::Rc::new(std::cell::RefCell::new(app_output)); let app_run = application.run(Some(input), Some(output)); let output_string = io_buffer.borrow().get_output().replace('\r', ""); // Shouldn't check output and results if an exception was expected by this point. if is_exception { let ExpectResult::Exception(_) = &case.expect_result else { unreachable!() }; let normalized = case.expect.replace('\n', shirabe_php_shim::PHP_EOL); let normalized = normalized.trim_end(); let err = match run_result.borrow().as_ref() { Some(Err(e)) => format!("{}", e), _ => app_run .as_ref() .err() .map(|e| format!("{}", e)) .unwrap_or_default(), }; assert!( err.contains(normalized), "expected exception message containing:\n{}\n--- got ---\n{}", normalized, err ); return; } let result = match run_result.borrow().as_ref() { Some(Ok(c)) => *c, Some(Err(e)) => panic!("installer run failed: {}\n{}", e, output_string), None => app_run.unwrap_or(-1) as i64, }; let ExpectResult::ExitCode(expect_result) = &case.expect_result else { unreachable!() }; shirabe_php_shim::rewind(&app_output_stream); let app_output_contents = shirabe_php_shim::stream_get_contents(&app_output_stream).unwrap_or_default(); assert_eq!( *expect_result, result, "{}{}", output_string, app_output_contents ); if let ExpectLock::Json(expect_lock) = &case.expect_lock { let actual = std::fs::read_to_string(&lock_path).unwrap(); let mut actual_lock: serde_json::Value = serde_json::from_str(&actual).unwrap(); if let Some(obj) = actual_lock.as_object_mut() { for k in ["hash", "content-hash", "_readme", "plugin-api-version"] { obj.remove(k); } } let mut expect_lock = expect_lock.clone(); // PHP turns the empty-array sentinel into stdClass; serde compares {} vs [] strictly, so // normalize the known object-valued keys to {} when they are empty. if let Some(obj) = expect_lock.as_object_mut() { for k in ["stability-flags", "platform", "platform-dev"] { if obj.get(k) == Some(&serde_json::json!([])) { obj.insert(k.to_string(), serde_json::json!({})); } } } assert_eq!(expect_lock, actual_lock); } else if let ExpectLock::Never = &case.expect_lock { let lock_after = std::fs::read_to_string(&lock_path).ok(); assert_eq!(lock_before, lock_after, "lock file must not be written"); } if let Some(expect_installed) = &case.expect_installed { let dumper = ArrayDumper::new(); let local_repo = repository_manager.borrow().get_local_repository(); let mut actual_installed: Vec> = local_repo .get_canonical_packages() .unwrap() .into_iter() .map(|package| { let mut dumped = dumper.dump(package); dumped.shift_remove("version_normalized"); dumped }) .collect(); actual_installed.sort_by( |a: &IndexMap, b: &IndexMap| { let an = a .get("name") .and_then(|m| m.as_string()) .map(|s| s.to_string()) .unwrap_or_default(); let bn = b .get("name") .and_then(|m| m.as_string()) .map(|s| s.to_string()) .unwrap_or_default(); an.cmp(&bn) }, ); // Faithful comparison would dump expect_installed through the same shape; we compare the // serialized forms so the assertion still fails loudly on divergence. let actual_json = serde_json::to_value( actual_installed .iter() .map(php_mixed_map_to_json) .collect::>(), ) .unwrap(); assert_eq!(expect_installed, &actual_json); } // trace from the composer's recording InstallationManager. let im_handle = composer.borrow().get_installation_manager(); let im_ref = im_handle.borrow(); let trace = im_ref .as_any() .downcast_ref::() .expect("composer installation manager is the recording mock") .__get_trace(); assert_eq!(case.expect.trim_end(), trace.join("\n")); if let Some(expect_output) = expect_output && !expect_output.is_empty() { let output = Preg::replace( php_regex!(r"{^ - .*?\.ini$}m"), "__inilist__", &output_string, ); let output = Preg::replace( php_regex!(r"{(__inilist__\r?\n)+}"), "__inilist__\n", &output, ); assert_string_matches_format(expect_output.trim_end(), output.trim_end(), &output_string); } } fn php_mixed_map_to_json(map: &IndexMap) -> serde_json::Value { serde_json::to_value(map).unwrap_or(serde_json::Value::Null) } /// One generated #[test] fn per fixture in installer-slow/, run with COMPOSER_POOL_OPTIMIZER=0. macro_rules! slow_test { ($($ident:ident => $file:literal $(, ignore = $reason:literal)? ;)*) => { $( #[test] #[serial] $(#[ignore = $reason])? fn $ident() { let _tear_down = TearDown::new(); Platform::clear_env("COMPOSER_FUND"); Platform::put_env("COMPOSER_POOL_OPTIMIZER", "0"); let cases = load_integration_tests("installer-slow/"); let case = cases .iter() .find(|c| c.file == $file) .unwrap_or_else(|| panic!("fixture not found: {}", $file)); let expect_output = case.expect_output.clone(); do_test_integration(case, expect_output.as_deref()); } )* }; } /// One generated #[test] fn per fixture in installer/, run with COMPOSER_POOL_OPTIMIZER=1. macro_rules! pool_optimizer_test { ($($ident:ident => $file:literal $(, ignore = $reason:literal)? ;)*) => { $( #[test] #[serial] $(#[ignore = $reason])? fn $ident() { let _tear_down = TearDown::new(); Platform::clear_env("COMPOSER_FUND"); Platform::put_env("COMPOSER_POOL_OPTIMIZER", "1"); let cases = load_integration_tests("installer/"); let case = cases .iter() .find(|c| c.file == $file) .unwrap_or_else(|| panic!("fixture not found: {}", $file)); let expect_output = case .expect_output_optimized .clone() .filter(|s| !s.is_empty()) .or_else(|| case.expect_output.clone()); do_test_integration(case, expect_output.as_deref()); } )* }; } /// One generated #[test] fn per fixture in installer/, run with COMPOSER_POOL_OPTIMIZER=0. macro_rules! raw_pool_test { ($($ident:ident => $file:literal $(, ignore = $reason:literal)? ;)*) => { $( #[test] #[serial] $(#[ignore = $reason])? fn $ident() { let _tear_down = TearDown::new(); Platform::clear_env("COMPOSER_FUND"); Platform::put_env("COMPOSER_POOL_OPTIMIZER", "0"); let cases = load_integration_tests("installer/"); let case = cases .iter() .find(|c| c.file == $file) .unwrap_or_else(|| panic!("fixture not found: {}", $file)); let expect_output = case.expect_output.clone(); do_test_integration(case, expect_output.as_deref()); } )* }; } slow_test! { slow_github_issues_7665 => "github-issues-7665.test", ignore = "TODO(phase-d): upstream Composer defect (composer/composer#12111), not a porting bug. Problem::getPrettyString breaks RULE_LEARNED sort ties with getSortableString() <=> getSortableString(), which compares numerically when both sides are numeric strings and by byte otherwise, so it is not transitive. Those sortable strings are SAT-solver literal ids assigned sequentially while rules are generated, so their values shift with the pool's total package count, including platform packages that Installer::createPlatformRepo() derives from the real ambient PHP runtime, which Composer's own test suite never mocks. This fixture's expected order is therefore brittle to whichever extensions happen to be loaded on the machine that generates or runs it. Nothing to fix on the Rust side."; } pool_optimizer_test! { pool_optimizer_abandoned_listed => "abandoned-listed.test"; pool_optimizer_alias_in_complex_constraints => "alias-in-complex-constraints.test"; pool_optimizer_alias_in_lock => "alias-in-lock.test"; pool_optimizer_alias_in_lock2 => "alias-in-lock2.test"; pool_optimizer_alias_on_unloadable_package => "alias-on-unloadable-package.test"; pool_optimizer_alias_solver_problems => "alias-solver-problems.test"; pool_optimizer_alias_solver_problems2 => "alias-solver-problems2.test"; pool_optimizer_alias_with_reference => "alias-with-reference.test"; pool_optimizer_aliased_priority_conflicting => "aliased-priority-conflicting.test"; pool_optimizer_aliased_priority => "aliased-priority.test"; pool_optimizer_aliases_with_require_dev => "aliases-with-require-dev.test"; pool_optimizer_broken_deps_do_not_replace => "broken-deps-do-not-replace.test"; pool_optimizer_circular_dependency_errors => "circular-dependency-errors.test"; pool_optimizer_circular_dependency => "circular-dependency.test"; pool_optimizer_circular_dependency2 => "circular-dependency2.test"; pool_optimizer_conflict_against_provided_by_dep_package_works => "conflict-against-provided-by-dep-package-works.test"; pool_optimizer_conflict_against_provided_package_works => "conflict-against-provided-package-works.test"; pool_optimizer_conflict_against_replaced_by_dep_package_problem => "conflict-against-replaced-by-dep-package-problem.test"; pool_optimizer_conflict_against_replaced_package_problem => "conflict-against-replaced-package-problem.test"; pool_optimizer_conflict_between_dependents => "conflict-between-dependents.test"; pool_optimizer_conflict_between_root_and_dependent => "conflict-between-root-and-dependent.test"; pool_optimizer_conflict_downgrade_nested => "conflict-downgrade-nested.test"; pool_optimizer_conflict_downgrade => "conflict-downgrade.test"; pool_optimizer_conflict_on_root_with_alias_prevents_update_if_not_required => "conflict-on-root-with-alias-prevents-update-if-not-required.test"; pool_optimizer_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test"; pool_optimizer_conflict_with_alias_prevents_update_if_not_required => "conflict-with-alias-prevents-update-if-not-required.test"; pool_optimizer_conflict_with_alias_prevents_update => "conflict-with-alias-prevents-update.test"; pool_optimizer_conflict_with_all_dependencies_option_dont_recommend_to_use_it => "conflict-with-all-dependencies-option-dont-recommend-to-use-it.test"; pool_optimizer_deduplicate_solver_problems => "deduplicate-solver-problems.test"; pool_optimizer_disjunctive_multi_constraints => "disjunctive-multi-constraints.test"; pool_optimizer_full_update_minimal_changes => "full-update-minimal-changes.test"; pool_optimizer_github_issues_4319 => "github-issues-4319.test"; pool_optimizer_github_issues_4795_2 => "github-issues-4795-2.test"; pool_optimizer_github_issues_4795 => "github-issues-4795.test"; pool_optimizer_github_issues_7051 => "github-issues-7051.test"; pool_optimizer_github_issues_8902 => "github-issues-8902.test"; pool_optimizer_github_issues_8903 => "github-issues-8903.test"; pool_optimizer_github_issues_9012 => "github-issues-9012.test"; pool_optimizer_github_issues_9290 => "github-issues-9290.test"; pool_optimizer_hint_main_rename => "hint-main-rename.test"; pool_optimizer_install_aliased_alias => "install-aliased-alias.test"; pool_optimizer_install_branch_alias_composer_repo => "install-branch-alias-composer-repo.test"; pool_optimizer_install_dev_using_dist => "install-dev-using-dist.test"; pool_optimizer_install_dev => "install-dev.test"; pool_optimizer_install_forces_reinstall_if_abandon_changes => "install-forces-reinstall-if-abandon-changes.test"; pool_optimizer_install_from_incomplete_lock_with_ignore => "install-from-incomplete-lock-with-ignore.test"; pool_optimizer_install_from_incomplete_lock => "install-from-incomplete-lock.test"; pool_optimizer_install_from_lock_removes_package => "install-from-lock-removes-package.test"; pool_optimizer_install_funding_notice_env => "install-funding-notice-env.test"; pool_optimizer_install_funding_notice_not_displayed_env => "install-funding-notice-not-displayed-env.test"; pool_optimizer_install_funding_notice => "install-funding-notice.test"; pool_optimizer_install_ignore_platform_package_requirement_list => "install-ignore-platform-package-requirement-list.test"; pool_optimizer_install_ignore_platform_package_requirement_wildcard => "install-ignore-platform-package-requirement-wildcard.test"; pool_optimizer_install_ignore_platform_package_requirements => "install-ignore-platform-package-requirements.test"; pool_optimizer_install_missing_alias_from_lock => "install-missing-alias-from-lock.test"; pool_optimizer_install_overridden_platform_packages => "install-overridden-platform-packages.test"; pool_optimizer_install_package_and_its_provider_skips_original => "install-package-and-its-provider-skips-original.test"; pool_optimizer_install_prefers_repos_over_package_versions => "install-prefers-repos-over-package-versions.test"; pool_optimizer_install_reference => "install-reference.test"; pool_optimizer_install_security_advisory_matching_dependency => "install-security-advisory-matching-dependency.test"; pool_optimizer_install_self_from_root => "install-self-from-root.test"; pool_optimizer_install_simple => "install-simple.test"; pool_optimizer_install_without_lock => "install-without-lock.test"; pool_optimizer_load_replaced_package_if_replacer_dropped => "load-replaced-package-if-replacer-dropped.test"; pool_optimizer_outdated_lock_file_fails_install => "outdated-lock-file-fails-install.test"; pool_optimizer_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test"; pool_optimizer_partial_update_always_updates_symlinked_path_repos => "partial-update-always-updates-symlinked-path-repos.test"; pool_optimizer_partial_update_downgrades_non_allow_listed_unstable => "partial-update-downgrades-non-allow-listed-unstable.test"; pool_optimizer_partial_update_forces_dev_reference_from_lock_for_non_updated_packages => "partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test"; pool_optimizer_partial_update_from_lock_with_root_alias => "partial-update-from-lock-with-root-alias.test"; pool_optimizer_partial_update_from_lock => "partial-update-from-lock.test"; pool_optimizer_partial_update_installs_from_lock_even_missing => "partial-update-installs-from-lock-even-missing.test"; pool_optimizer_partial_update_keeps_older_dep_if_still_required_with_provide => "partial-update-keeps-older-dep-if-still-required-with-provide.test"; pool_optimizer_partial_update_keeps_older_dep_if_still_required => "partial-update-keeps-older-dep-if-still-required.test"; pool_optimizer_partial_update_loads_root_aliases_for_path_repos => "partial-update-loads-root-aliases-for-path-repos.test"; pool_optimizer_partial_update_security_advisory_matching_locked_dep_with_dependencies => "partial-update-security-advisory-matching-locked-dep-with-dependencies.test"; pool_optimizer_partial_update_security_advisory_matching_locked_dep => "partial-update-security-advisory-matching-locked-dep.test"; pool_optimizer_partial_update_with_dependencies_provide => "partial-update-with-dependencies-provide.test"; pool_optimizer_partial_update_with_dependencies_replace => "partial-update-with-dependencies-replace.test"; pool_optimizer_partial_update_with_deps_warns_root => "partial-update-with-deps-warns-root.test"; pool_optimizer_partial_update_with_symlinked_path_repos => "partial-update-with-symlinked-path-repos.test"; pool_optimizer_partial_update_without_lock => "partial-update-without-lock.test"; pool_optimizer_platform_ext_solver_problems => "platform-ext-solver-problems.test"; pool_optimizer_plugins_are_installed_first => "plugins-are-installed-first.test"; pool_optimizer_prefer_lowest_branches => "prefer-lowest-branches.test"; pool_optimizer_problems_reduce_versions => "problems-reduce-versions.test"; pool_optimizer_provider_can_coexist_with_other_version_of_provided => "provider-can-coexist-with-other-version-of-provided.test"; pool_optimizer_provider_conflicts => "provider-conflicts.test"; pool_optimizer_provider_conflicts2 => "provider-conflicts2.test"; pool_optimizer_provider_conflicts3 => "provider-conflicts3.test"; pool_optimizer_provider_dev_require_can_satisfy_require => "provider-dev-require-can-satisfy-require.test"; pool_optimizer_provider_gets_picked_together_with_other_version_of_provided_conflict => "provider-gets-picked-together-with-other-version-of-provided-conflict.test"; pool_optimizer_provider_gets_picked_together_with_other_version_of_provided_indirect => "provider-gets-picked-together-with-other-version-of-provided-indirect.test"; pool_optimizer_provider_gets_picked_together_with_other_version_of_provided => "provider-gets-picked-together-with-other-version-of-provided.test"; pool_optimizer_provider_packages_can_be_installed_if_selected => "provider-packages-can-be-installed-if-selected.test"; pool_optimizer_provider_packages_can_be_installed_together_with_provided_if_both_installable => "provider-packages-can-be-installed-together-with-provided-if-both-installable.test"; pool_optimizer_provider_packages_can_not_be_installed_unless_selected => "provider-packages-can-not-be-installed-unless-selected.test"; pool_optimizer_provider_satisfies_its_own_requirement => "provider-satisfies-its-own-requirement.test"; pool_optimizer_remove_deletes_unused_deps => "remove-deletes-unused-deps.test"; pool_optimizer_remove_does_nothing_if_removal_requires_update_of_dep => "remove-does-nothing-if-removal-requires-update-of-dep.test"; pool_optimizer_replace_alias => "replace-alias.test"; pool_optimizer_replace_priorities => "replace-priorities.test"; pool_optimizer_replace_range_require_single_version => "replace-range-require-single-version.test"; pool_optimizer_replace_root_require => "replace-root-require.test"; pool_optimizer_replaced_packages_should_not_be_installed_when_installing_from_lock => "replaced-packages-should-not-be-installed-when-installing-from-lock.test"; pool_optimizer_replaced_packages_should_not_be_installed => "replaced-packages-should-not-be-installed.test"; pool_optimizer_replacer_satisfies_its_own_requirement => "replacer-satisfies-its-own-requirement.test"; pool_optimizer_repositories_priorities => "repositories-priorities.test"; pool_optimizer_repositories_priorities2 => "repositories-priorities2.test"; pool_optimizer_repositories_priorities3 => "repositories-priorities3.test"; pool_optimizer_repositories_priorities4 => "repositories-priorities4.test"; pool_optimizer_repositories_priorities5 => "repositories-priorities5.test"; pool_optimizer_root_alias_change_with_circular_dep => "root-alias-change-with-circular-dep.test"; pool_optimizer_root_alias_gets_loaded_for_locked_pkgs => "root-alias-gets-loaded-for-locked-pkgs.test"; pool_optimizer_root_requirements_do_not_affect_locked_versions => "root-requirements-do-not-affect-locked-versions.test"; pool_optimizer_solver_problem_with_hash_in_branch => "solver-problem-with-hash-in-branch.test"; pool_optimizer_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test"; pool_optimizer_solver_problems => "solver-problems.test"; pool_optimizer_suggest_installed => "suggest-installed.test"; pool_optimizer_suggest_prod_nolock => "suggest-prod-nolock.test"; pool_optimizer_suggest_prod => "suggest-prod.test"; pool_optimizer_suggest_replaced => "suggest-replaced.test"; pool_optimizer_suggest_uninstalled => "suggest-uninstalled.test"; pool_optimizer_unbounded_conflict_does_not_match_default_branch_with_branch_alias => "unbounded-conflict-does-not-match-default-branch-with-branch-alias.test"; pool_optimizer_unbounded_conflict_does_not_match_default_branch_with_numeric_branch => "unbounded-conflict-does-not-match-default-branch-with-numeric-branch.test"; pool_optimizer_unbounded_conflict_matches_default_branch => "unbounded-conflict-matches-default-branch.test"; pool_optimizer_update_abandoned_package_required_but_blocked_via_audit_config => "update-abandoned-package-required-but-blocked-via-audit-config.test"; pool_optimizer_update_alias_lock => "update-alias-lock.test"; pool_optimizer_update_alias_lock2 => "update-alias-lock2.test"; pool_optimizer_update_alias => "update-alias.test"; pool_optimizer_update_all_dry_run => "update-all-dry-run.test"; pool_optimizer_update_all => "update-all.test"; pool_optimizer_update_allow_list_locked_require => "update-allow-list-locked-require.test"; pool_optimizer_update_allow_list_minimal_changes => "update-allow-list-minimal-changes.test"; pool_optimizer_update_allow_list_patterns_with_all_dependencies => "update-allow-list-patterns-with-all-dependencies.test"; pool_optimizer_update_allow_list_patterns_with_dependencies => "update-allow-list-patterns-with-dependencies.test"; pool_optimizer_update_allow_list_patterns_with_root_dependencies => "update-allow-list-patterns-with-root-dependencies.test"; pool_optimizer_update_allow_list_patterns_without_dependencies => "update-allow-list-patterns-without-dependencies.test"; pool_optimizer_update_allow_list_patterns => "update-allow-list-patterns.test"; pool_optimizer_update_allow_list_reads_lock => "update-allow-list-reads-lock.test"; pool_optimizer_update_allow_list_removes_unused => "update-allow-list-removes-unused.test"; pool_optimizer_update_allow_list_require_new_replace => "update-allow-list-require-new-replace.test"; pool_optimizer_update_allow_list_warns_non_existing_patterns => "update-allow-list-warns-non-existing-patterns.test"; pool_optimizer_update_allow_list_with_dependencies_alias => "update-allow-list-with-dependencies-alias.test"; pool_optimizer_update_allow_list_with_dependencies_new_requirement => "update-allow-list-with-dependencies-new-requirement.test"; pool_optimizer_update_allow_list_with_dependencies_require_new_replace_mutual => "update-allow-list-with-dependencies-require-new-replace-mutual.test"; pool_optimizer_update_allow_list_with_dependencies_require_new_replace => "update-allow-list-with-dependencies-require-new-replace.test"; pool_optimizer_update_allow_list_with_dependencies_require_new => "update-allow-list-with-dependencies-require-new.test"; pool_optimizer_update_allow_list_with_dependencies => "update-allow-list-with-dependencies.test"; pool_optimizer_update_allow_list_with_dependency_conflict => "update-allow-list-with-dependency-conflict.test"; pool_optimizer_update_allow_list => "update-allow-list.test"; pool_optimizer_update_changes_url => "update-changes-url.test"; pool_optimizer_update_dev_ignores_providers => "update-dev-ignores-providers.test"; pool_optimizer_update_dev_packages_updates_repo_url => "update-dev-packages-updates-repo-url.test"; pool_optimizer_update_dev_to_new_ref_picks_up_changes => "update-dev-to-new-ref-picks-up-changes.test"; pool_optimizer_update_downgrades_unstable_packages => "update-downgrades-unstable-packages.test"; pool_optimizer_update_ignore_platform_package_requirement_list_upper_bounds => "update-ignore-platform-package-requirement-list-upper-bounds.test"; pool_optimizer_update_ignore_platform_package_requirement_list => "update-ignore-platform-package-requirement-list.test"; pool_optimizer_update_ignore_platform_package_requirement_wildcard => "update-ignore-platform-package-requirement-wildcard.test"; pool_optimizer_update_ignore_platform_package_requirements => "update-ignore-platform-package-requirements.test"; pool_optimizer_update_installed_alias_dry_run => "update-installed-alias-dry-run.test"; pool_optimizer_update_installed_alias => "update-installed-alias.test"; pool_optimizer_update_installed_reference_dry_run => "update-installed-reference-dry-run.test"; pool_optimizer_update_installed_reference => "update-installed-reference.test"; pool_optimizer_update_mirrors_changes_url => "update-mirrors-changes-url.test"; pool_optimizer_update_mirrors_fails_with_new_req => "update-mirrors-fails-with-new-req.test"; pool_optimizer_update_no_dev_still_resolves_dev => "update-no-dev-still-resolves-dev.test"; pool_optimizer_update_no_install => "update-no-install.test"; pool_optimizer_update_package_present_in_lock_but_not_at_all_in_remote => "update-package-present-in-lock-but-not-at-all-in-remote.test"; pool_optimizer_update_package_present_in_lock_but_not_in_remote_due_to_min_stability => "update-package-present-in-lock-but-not-in-remote-due-to-min-stability.test"; pool_optimizer_update_package_present_in_lock_but_not_in_remote => "update-package-present-in-lock-but-not-in-remote.test"; pool_optimizer_update_package_present_in_lower_repo_prio_but_not_main_due_to_min_stability => "update-package-present-in-lower-repo-prio-but-not-main-due-to-min-stability.test"; pool_optimizer_update_picks_up_change_of_vcs_type => "update-picks-up-change-of-vcs-type.test"; pool_optimizer_update_prefer_lowest_stable => "update-prefer-lowest-stable.test"; pool_optimizer_update_reference_picks_latest => "update-reference-picks-latest.test"; pool_optimizer_update_reference => "update-reference.test"; pool_optimizer_update_removes_unused_locked_dep => "update-removes-unused-locked-dep.test"; pool_optimizer_update_requiring_decision_reverts_and_learning_positive_literals => "update-requiring-decision-reverts-and-learning-positive-literals.test"; pool_optimizer_update_security_advisory_matching_direct_dependency => "update-security-advisory-matching-direct-dependency.test"; pool_optimizer_update_security_advisory_matching_indirect_dependency => "update-security-advisory-matching-indirect-dependency.test"; pool_optimizer_update_syncs_outdated => "update-syncs-outdated.test"; pool_optimizer_update_to_empty_from_blank => "update-to-empty-from-blank.test"; pool_optimizer_update_to_empty_from_locked => "update-to-empty-from-locked.test"; pool_optimizer_update_with_all_dependencies => "update-with-all-dependencies.test"; pool_optimizer_update_without_lock => "update-without-lock.test"; pool_optimizer_updating_dev_from_lock_removes_old_deps => "updating-dev-from-lock-removes-old-deps.test"; pool_optimizer_updating_dev_updates_url_and_reference => "updating-dev-updates-url-and-reference.test"; } raw_pool_test! { raw_pool_abandoned_listed => "abandoned-listed.test"; raw_pool_alias_in_complex_constraints => "alias-in-complex-constraints.test"; raw_pool_alias_in_lock => "alias-in-lock.test"; raw_pool_alias_in_lock2 => "alias-in-lock2.test"; raw_pool_alias_on_unloadable_package => "alias-on-unloadable-package.test"; raw_pool_alias_solver_problems => "alias-solver-problems.test"; raw_pool_alias_solver_problems2 => "alias-solver-problems2.test"; raw_pool_alias_with_reference => "alias-with-reference.test"; raw_pool_aliased_priority_conflicting => "aliased-priority-conflicting.test"; raw_pool_aliased_priority => "aliased-priority.test"; raw_pool_aliases_with_require_dev => "aliases-with-require-dev.test"; raw_pool_broken_deps_do_not_replace => "broken-deps-do-not-replace.test"; raw_pool_circular_dependency_errors => "circular-dependency-errors.test"; raw_pool_circular_dependency => "circular-dependency.test"; raw_pool_circular_dependency2 => "circular-dependency2.test"; raw_pool_conflict_against_provided_by_dep_package_works => "conflict-against-provided-by-dep-package-works.test"; raw_pool_conflict_against_provided_package_works => "conflict-against-provided-package-works.test"; raw_pool_conflict_against_replaced_by_dep_package_problem => "conflict-against-replaced-by-dep-package-problem.test"; raw_pool_conflict_against_replaced_package_problem => "conflict-against-replaced-package-problem.test"; raw_pool_conflict_between_dependents => "conflict-between-dependents.test"; raw_pool_conflict_between_root_and_dependent => "conflict-between-root-and-dependent.test"; raw_pool_conflict_downgrade_nested => "conflict-downgrade-nested.test"; raw_pool_conflict_downgrade => "conflict-downgrade.test"; raw_pool_conflict_on_root_with_alias_prevents_update_if_not_required => "conflict-on-root-with-alias-prevents-update-if-not-required.test"; raw_pool_conflict_with_alias_in_lock_does_prevents_install => "conflict-with-alias-in-lock-does-prevents-install.test"; raw_pool_conflict_with_alias_prevents_update_if_not_required => "conflict-with-alias-prevents-update-if-not-required.test"; raw_pool_conflict_with_alias_prevents_update => "conflict-with-alias-prevents-update.test"; raw_pool_conflict_with_all_dependencies_option_dont_recommend_to_use_it => "conflict-with-all-dependencies-option-dont-recommend-to-use-it.test"; raw_pool_deduplicate_solver_problems => "deduplicate-solver-problems.test"; raw_pool_disjunctive_multi_constraints => "disjunctive-multi-constraints.test"; raw_pool_full_update_minimal_changes => "full-update-minimal-changes.test"; raw_pool_github_issues_4319 => "github-issues-4319.test"; raw_pool_github_issues_4795_2 => "github-issues-4795-2.test"; raw_pool_github_issues_4795 => "github-issues-4795.test"; raw_pool_github_issues_7051 => "github-issues-7051.test"; raw_pool_github_issues_8902 => "github-issues-8902.test"; raw_pool_github_issues_8903 => "github-issues-8903.test"; raw_pool_github_issues_9012 => "github-issues-9012.test"; raw_pool_github_issues_9290 => "github-issues-9290.test"; raw_pool_hint_main_rename => "hint-main-rename.test"; raw_pool_install_aliased_alias => "install-aliased-alias.test"; raw_pool_install_branch_alias_composer_repo => "install-branch-alias-composer-repo.test"; raw_pool_install_dev_using_dist => "install-dev-using-dist.test"; raw_pool_install_dev => "install-dev.test"; raw_pool_install_forces_reinstall_if_abandon_changes => "install-forces-reinstall-if-abandon-changes.test"; raw_pool_install_from_incomplete_lock_with_ignore => "install-from-incomplete-lock-with-ignore.test"; raw_pool_install_from_incomplete_lock => "install-from-incomplete-lock.test"; raw_pool_install_from_lock_removes_package => "install-from-lock-removes-package.test"; raw_pool_install_funding_notice_env => "install-funding-notice-env.test"; raw_pool_install_funding_notice_not_displayed_env => "install-funding-notice-not-displayed-env.test"; raw_pool_install_funding_notice => "install-funding-notice.test"; raw_pool_install_ignore_platform_package_requirement_list => "install-ignore-platform-package-requirement-list.test"; raw_pool_install_ignore_platform_package_requirement_wildcard => "install-ignore-platform-package-requirement-wildcard.test"; raw_pool_install_ignore_platform_package_requirements => "install-ignore-platform-package-requirements.test"; raw_pool_install_missing_alias_from_lock => "install-missing-alias-from-lock.test"; raw_pool_install_overridden_platform_packages => "install-overridden-platform-packages.test"; raw_pool_install_package_and_its_provider_skips_original => "install-package-and-its-provider-skips-original.test"; raw_pool_install_prefers_repos_over_package_versions => "install-prefers-repos-over-package-versions.test"; raw_pool_install_reference => "install-reference.test"; raw_pool_install_security_advisory_matching_dependency => "install-security-advisory-matching-dependency.test"; raw_pool_install_self_from_root => "install-self-from-root.test"; raw_pool_install_simple => "install-simple.test"; raw_pool_install_without_lock => "install-without-lock.test"; raw_pool_load_replaced_package_if_replacer_dropped => "load-replaced-package-if-replacer-dropped.test"; raw_pool_outdated_lock_file_fails_install => "outdated-lock-file-fails-install.test"; raw_pool_outdated_lock_file_with_new_platform_reqs_fails => "outdated-lock-file-with-new-platform-reqs-fails.test"; raw_pool_partial_update_always_updates_symlinked_path_repos => "partial-update-always-updates-symlinked-path-repos.test"; raw_pool_partial_update_downgrades_non_allow_listed_unstable => "partial-update-downgrades-non-allow-listed-unstable.test"; raw_pool_partial_update_forces_dev_reference_from_lock_for_non_updated_packages => "partial-update-forces-dev-reference-from-lock-for-non-updated-packages.test"; raw_pool_partial_update_from_lock_with_root_alias => "partial-update-from-lock-with-root-alias.test"; raw_pool_partial_update_from_lock => "partial-update-from-lock.test"; raw_pool_partial_update_installs_from_lock_even_missing => "partial-update-installs-from-lock-even-missing.test"; raw_pool_partial_update_keeps_older_dep_if_still_required_with_provide => "partial-update-keeps-older-dep-if-still-required-with-provide.test"; raw_pool_partial_update_keeps_older_dep_if_still_required => "partial-update-keeps-older-dep-if-still-required.test"; raw_pool_partial_update_loads_root_aliases_for_path_repos => "partial-update-loads-root-aliases-for-path-repos.test"; raw_pool_partial_update_security_advisory_matching_locked_dep_with_dependencies => "partial-update-security-advisory-matching-locked-dep-with-dependencies.test"; raw_pool_partial_update_security_advisory_matching_locked_dep => "partial-update-security-advisory-matching-locked-dep.test"; raw_pool_partial_update_with_dependencies_provide => "partial-update-with-dependencies-provide.test"; raw_pool_partial_update_with_dependencies_replace => "partial-update-with-dependencies-replace.test"; raw_pool_partial_update_with_deps_warns_root => "partial-update-with-deps-warns-root.test"; raw_pool_partial_update_with_symlinked_path_repos => "partial-update-with-symlinked-path-repos.test"; raw_pool_partial_update_without_lock => "partial-update-without-lock.test"; raw_pool_platform_ext_solver_problems => "platform-ext-solver-problems.test"; raw_pool_plugins_are_installed_first => "plugins-are-installed-first.test"; raw_pool_prefer_lowest_branches => "prefer-lowest-branches.test"; raw_pool_problems_reduce_versions => "problems-reduce-versions.test"; raw_pool_provider_can_coexist_with_other_version_of_provided => "provider-can-coexist-with-other-version-of-provided.test"; raw_pool_provider_conflicts => "provider-conflicts.test"; raw_pool_provider_conflicts2 => "provider-conflicts2.test"; raw_pool_provider_conflicts3 => "provider-conflicts3.test"; raw_pool_provider_dev_require_can_satisfy_require => "provider-dev-require-can-satisfy-require.test"; raw_pool_provider_gets_picked_together_with_other_version_of_provided_conflict => "provider-gets-picked-together-with-other-version-of-provided-conflict.test"; raw_pool_provider_gets_picked_together_with_other_version_of_provided_indirect => "provider-gets-picked-together-with-other-version-of-provided-indirect.test"; raw_pool_provider_gets_picked_together_with_other_version_of_provided => "provider-gets-picked-together-with-other-version-of-provided.test"; raw_pool_provider_packages_can_be_installed_if_selected => "provider-packages-can-be-installed-if-selected.test"; raw_pool_provider_packages_can_be_installed_together_with_provided_if_both_installable => "provider-packages-can-be-installed-together-with-provided-if-both-installable.test"; raw_pool_provider_packages_can_not_be_installed_unless_selected => "provider-packages-can-not-be-installed-unless-selected.test"; raw_pool_provider_satisfies_its_own_requirement => "provider-satisfies-its-own-requirement.test"; raw_pool_remove_deletes_unused_deps => "remove-deletes-unused-deps.test"; raw_pool_remove_does_nothing_if_removal_requires_update_of_dep => "remove-does-nothing-if-removal-requires-update-of-dep.test"; raw_pool_replace_alias => "replace-alias.test"; raw_pool_replace_priorities => "replace-priorities.test"; raw_pool_replace_range_require_single_version => "replace-range-require-single-version.test"; raw_pool_replace_root_require => "replace-root-require.test"; raw_pool_replaced_packages_should_not_be_installed_when_installing_from_lock => "replaced-packages-should-not-be-installed-when-installing-from-lock.test"; raw_pool_replaced_packages_should_not_be_installed => "replaced-packages-should-not-be-installed.test"; raw_pool_replacer_satisfies_its_own_requirement => "replacer-satisfies-its-own-requirement.test"; raw_pool_repositories_priorities => "repositories-priorities.test"; raw_pool_repositories_priorities2 => "repositories-priorities2.test"; raw_pool_repositories_priorities3 => "repositories-priorities3.test"; raw_pool_repositories_priorities4 => "repositories-priorities4.test"; raw_pool_repositories_priorities5 => "repositories-priorities5.test"; raw_pool_root_alias_change_with_circular_dep => "root-alias-change-with-circular-dep.test"; raw_pool_root_alias_gets_loaded_for_locked_pkgs => "root-alias-gets-loaded-for-locked-pkgs.test"; raw_pool_root_requirements_do_not_affect_locked_versions => "root-requirements-do-not-affect-locked-versions.test"; raw_pool_solver_problem_with_hash_in_branch => "solver-problem-with-hash-in-branch.test"; raw_pool_solver_problems_with_disabled_platform => "solver-problems-with-disabled-platform.test"; raw_pool_solver_problems => "solver-problems.test"; raw_pool_suggest_installed => "suggest-installed.test"; raw_pool_suggest_prod_nolock => "suggest-prod-nolock.test"; raw_pool_suggest_prod => "suggest-prod.test"; raw_pool_suggest_replaced => "suggest-replaced.test"; raw_pool_suggest_uninstalled => "suggest-uninstalled.test"; raw_pool_unbounded_conflict_does_not_match_default_branch_with_branch_alias => "unbounded-conflict-does-not-match-default-branch-with-branch-alias.test"; raw_pool_unbounded_conflict_does_not_match_default_branch_with_numeric_branch => "unbounded-conflict-does-not-match-default-branch-with-numeric-branch.test"; raw_pool_unbounded_conflict_matches_default_branch => "unbounded-conflict-matches-default-branch.test"; raw_pool_update_abandoned_package_required_but_blocked_via_audit_config => "update-abandoned-package-required-but-blocked-via-audit-config.test"; raw_pool_update_alias_lock => "update-alias-lock.test"; raw_pool_update_alias_lock2 => "update-alias-lock2.test"; raw_pool_update_alias => "update-alias.test"; raw_pool_update_all_dry_run => "update-all-dry-run.test"; raw_pool_update_all => "update-all.test"; raw_pool_update_allow_list_locked_require => "update-allow-list-locked-require.test"; raw_pool_update_allow_list_minimal_changes => "update-allow-list-minimal-changes.test"; raw_pool_update_allow_list_patterns_with_all_dependencies => "update-allow-list-patterns-with-all-dependencies.test"; raw_pool_update_allow_list_patterns_with_dependencies => "update-allow-list-patterns-with-dependencies.test"; raw_pool_update_allow_list_patterns_with_root_dependencies => "update-allow-list-patterns-with-root-dependencies.test"; raw_pool_update_allow_list_patterns_without_dependencies => "update-allow-list-patterns-without-dependencies.test"; raw_pool_update_allow_list_patterns => "update-allow-list-patterns.test"; raw_pool_update_allow_list_reads_lock => "update-allow-list-reads-lock.test"; raw_pool_update_allow_list_removes_unused => "update-allow-list-removes-unused.test"; raw_pool_update_allow_list_require_new_replace => "update-allow-list-require-new-replace.test"; raw_pool_update_allow_list_warns_non_existing_patterns => "update-allow-list-warns-non-existing-patterns.test"; raw_pool_update_allow_list_with_dependencies_alias => "update-allow-list-with-dependencies-alias.test"; raw_pool_update_allow_list_with_dependencies_new_requirement => "update-allow-list-with-dependencies-new-requirement.test"; raw_pool_update_allow_list_with_dependencies_require_new_replace_mutual => "update-allow-list-with-dependencies-require-new-replace-mutual.test"; raw_pool_update_allow_list_with_dependencies_require_new_replace => "update-allow-list-with-dependencies-require-new-replace.test"; raw_pool_update_allow_list_with_dependencies_require_new => "update-allow-list-with-dependencies-require-new.test"; raw_pool_update_allow_list_with_dependencies => "update-allow-list-with-dependencies.test"; raw_pool_update_allow_list_with_dependency_conflict => "update-allow-list-with-dependency-conflict.test"; raw_pool_update_allow_list => "update-allow-list.test"; raw_pool_update_changes_url => "update-changes-url.test"; raw_pool_update_dev_ignores_providers => "update-dev-ignores-providers.test"; raw_pool_update_dev_packages_updates_repo_url => "update-dev-packages-updates-repo-url.test"; raw_pool_update_dev_to_new_ref_picks_up_changes => "update-dev-to-new-ref-picks-up-changes.test"; raw_pool_update_downgrades_unstable_packages => "update-downgrades-unstable-packages.test"; raw_pool_update_ignore_platform_package_requirement_list_upper_bounds => "update-ignore-platform-package-requirement-list-upper-bounds.test"; raw_pool_update_ignore_platform_package_requirement_list => "update-ignore-platform-package-requirement-list.test"; raw_pool_update_ignore_platform_package_requirement_wildcard => "update-ignore-platform-package-requirement-wildcard.test"; raw_pool_update_ignore_platform_package_requirements => "update-ignore-platform-package-requirements.test"; raw_pool_update_installed_alias_dry_run => "update-installed-alias-dry-run.test"; raw_pool_update_installed_alias => "update-installed-alias.test"; raw_pool_update_installed_reference_dry_run => "update-installed-reference-dry-run.test"; raw_pool_update_installed_reference => "update-installed-reference.test"; raw_pool_update_mirrors_changes_url => "update-mirrors-changes-url.test"; raw_pool_update_mirrors_fails_with_new_req => "update-mirrors-fails-with-new-req.test"; raw_pool_update_no_dev_still_resolves_dev => "update-no-dev-still-resolves-dev.test"; raw_pool_update_no_install => "update-no-install.test"; raw_pool_update_package_present_in_lock_but_not_at_all_in_remote => "update-package-present-in-lock-but-not-at-all-in-remote.test"; raw_pool_update_package_present_in_lock_but_not_in_remote_due_to_min_stability => "update-package-present-in-lock-but-not-in-remote-due-to-min-stability.test"; raw_pool_update_package_present_in_lock_but_not_in_remote => "update-package-present-in-lock-but-not-in-remote.test"; raw_pool_update_package_present_in_lower_repo_prio_but_not_main_due_to_min_stability => "update-package-present-in-lower-repo-prio-but-not-main-due-to-min-stability.test"; raw_pool_update_picks_up_change_of_vcs_type => "update-picks-up-change-of-vcs-type.test"; raw_pool_update_prefer_lowest_stable => "update-prefer-lowest-stable.test"; raw_pool_update_reference_picks_latest => "update-reference-picks-latest.test"; raw_pool_update_reference => "update-reference.test"; raw_pool_update_removes_unused_locked_dep => "update-removes-unused-locked-dep.test"; raw_pool_update_requiring_decision_reverts_and_learning_positive_literals => "update-requiring-decision-reverts-and-learning-positive-literals.test"; raw_pool_update_security_advisory_matching_direct_dependency => "update-security-advisory-matching-direct-dependency.test"; raw_pool_update_security_advisory_matching_indirect_dependency => "update-security-advisory-matching-indirect-dependency.test"; raw_pool_update_syncs_outdated => "update-syncs-outdated.test"; raw_pool_update_to_empty_from_blank => "update-to-empty-from-blank.test"; raw_pool_update_to_empty_from_locked => "update-to-empty-from-locked.test"; raw_pool_update_with_all_dependencies => "update-with-all-dependencies.test"; raw_pool_update_without_lock => "update-without-lock.test"; raw_pool_updating_dev_from_lock_removes_old_deps => "updating-dev-from-lock-removes-old-deps.test"; raw_pool_updating_dev_updates_url_and_reference => "updating-dev-updates-url-and-reference.test"; }