//! 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 = Factory::__create_mock( io.clone(), Some(LocalConfigInput::Data(composer_data)), DisablePlugins::None, false, ) .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): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; } 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=1"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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", ignore = "TODO(phase-d): known-failing fixture under COMPOSER_POOL_OPTIMIZER=0"; 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"; }