From 759b2980e70dfb8960238f75d68bb6dddce25414 Mon Sep 17 00:00:00 2001 From: nsfisis Date: Fri, 7 Aug 2026 07:01:50 +0900 Subject: test: port the tests left as todo!() stubs Replace the todo!() bodies with real ports. Four autoload-generator tests now run for real; the rest stay #[ignore]d, but each ignore reason now names the concrete missing symbol instead of a vague subsystem. Production additions the ports need: the deprecated AuthHelper::addAuthenticationHeader wrapper, EventDispatcher::__set_dispatch_script_override as the seam for PHPUnit onlyMethods(['dispatchScript']), and a define() stub in the shim. Co-Authored-By: Claude Opus 5 (1M context) --- .../tests/autoload/autoload_generator_test.rs | 329 +++++++++++++++++++-- crates/shirabe/tests/autoload/class_loader_test.rs | 39 ++- 2 files changed, 341 insertions(+), 27 deletions(-) (limited to 'crates/shirabe/tests/autoload') diff --git a/crates/shirabe/tests/autoload/autoload_generator_test.rs b/crates/shirabe/tests/autoload/autoload_generator_test.rs index 81f508cc..92baadcd 100644 --- a/crates/shirabe/tests/autoload/autoload_generator_test.rs +++ b/crates/shirabe/tests/autoload/autoload_generator_test.rs @@ -10,15 +10,18 @@ use shirabe::event_dispatcher::EventDispatcher; use shirabe::filter::platform_requirement_filter::PlatformRequirementFilterFactory; use shirabe::installer::{InstallationManager, InstallerInterface}; use shirabe::io::{BufferIO, IOInterface}; -use shirabe::package::handle::{AliasPackageHandle, PackageHandle, RootPackageHandle}; +use shirabe::package::handle::{ + AliasPackageHandle, CompletePackageHandle, PackageHandle, RootPackageHandle, +}; use shirabe::package::{Link, PackageInterfaceHandle, RootPackageInterfaceHandle}; use shirabe::repository::{ InstalledArrayRepository, InstalledRepositoryInterfaceHandle, WritableRepositoryInterface, }; +use shirabe::script::ScriptEvents; use shirabe::util::http_downloader::HttpDownloader; use shirabe::util::r#loop::Loop; use shirabe_external_packages::symfony::console::output::output_interface; -use shirabe_php_shim::PhpMixed; +use shirabe_php_shim::{PhpMixed, dirname, preg_quote, realpath, strtr}; use shirabe_semver::VersionParser; use shirabe_semver::constraint::{AnyConstraint, MatchAllConstraint, SimpleConstraint}; use tempfile::TempDir; @@ -125,6 +128,8 @@ struct SetUp { working_dir: String, vendor_dir: String, repository: InstalledArrayRepository, + /// ref: `$this->configValueMap['use-include-path']`, which testUseGlobalIncludePath mutates. + use_include_path: bool, im: InstallationManager, io: std::rc::Rc>, generator: AutoloadGenerator, @@ -199,6 +204,7 @@ fn set_up() -> SetUp { working_dir, vendor_dir, repository, + use_include_path: false, im, io, generator, @@ -213,7 +219,7 @@ impl SetUp { ConfigStubBuilder::new() .with("vendor-dir", PhpMixed::String(self.vendor_dir.clone())) .with("platform-check", PhpMixed::Bool(true)) - .with("use-include-path", PhpMixed::Bool(false)) + .with("use-include-path", PhpMixed::Bool(self.use_include_path)) .build() } @@ -1740,11 +1746,164 @@ fn test_files_autoload_order_by_dependencies() { } #[test] -#[ignore = "needs getCanonicalPackages consecutive-call return values (different package set per dump)"] +#[serial] fn test_files_autoload_generation_remove_extra_entities_from_autoload_files() { - // TODO(phase-d): needs a repository mock returning a different package set on each of several - // consecutive dump() calls (PHPUnit consecutive-call mock); not modeled by InstalledArrayRepository. - todo!() + let mut s = set_up(); + let autoload_package = new_root_pkg("root/a"); + autoload_package.set_autoload(autoload(vec![("files", str_list(&["root.php"]))])); + autoload_package.__set_include_paths(vec!["/lib".to_string(), "/src".to_string()]); + + let not_autoload_package = new_root_pkg("root/a"); + + let requires_ = || { + requires(vec![ + ("a/a", link("a", "a/a", match_all(), None)), + ("b/b", link("a", "b/b", match_all(), None)), + ("c/c", link("a", "c/c", match_all(), None)), + ]) + }; + autoload_package.set_requires(requires_()); + not_autoload_package.set_requires(requires_()); + + let a = new_pkg("a/a"); + let b = new_pkg("b/b"); + let c = new_pkg("c/c"); + a.__set_autoload(autoload(vec![("files", str_list(&["test.php"]))])); + a.__set_include_paths(vec!["lib1".to_string(), "src1".to_string()]); + b.__set_autoload(autoload(vec![("files", str_list(&["test2.php"]))])); + b.__set_include_paths(vec!["lib2".to_string()]); + c.__set_autoload(autoload(vec![( + "files", + str_list(&["test3.php", "foo/bar/test4.php"]), + )])); + c.__set_include_paths(vec!["lib3".to_string()]); + c.__set_target_dir(Some("foo/bar".to_string())); + let autoload_packages: Vec = vec![a.into(), b.into(), c.into()]; + + // PHP re-creates the three packages without autoload/include-path settings for the second and + // third `getCanonicalPackages` calls; the repository contents are swapped between dumps here. + let not_autoload_packages = || -> Vec { + vec![ + new_pkg("a/a").into(), + new_pkg("b/b").into(), + new_pkg("c/c").into(), + ] + }; + + s.ensure_dir(&format!("{}/a/a", s.vendor_dir)); + s.ensure_dir(&format!("{}/b/b", s.vendor_dir)); + s.ensure_dir(&format!("{}/c/c/foo/bar", s.vendor_dir)); + s.put( + &format!("{}/a/a/test.php", s.vendor_dir), + " array($vendorDir . '/dep/a/src'),\n);\n"; + assert_str_equals_file( + expected_namespace, + &format!("{}/composer/autoload_namespaces.php", s.vendor_dir), + ); + + dep.__set_autoload(autoload(vec![( + "psr-4", + str_map(&[("Acme\\Foo\\", pstr("./src-psr4"))]), + )])); + dump(&mut s, package.clone().into(), true, "_19").unwrap(); + + let expected_psr4 = " array($vendorDir . '/dep/a/src-psr4'),\n);\n"; + assert_str_equals_file( + expected_psr4, + &format!("{}/composer/autoload_psr4.php", s.vendor_dir), + ); + + dep.__set_autoload(autoload(vec![("classmap", str_list(&["classmap"]))])); + let expected_message = format!( + "Could not scan for classes inside \"{}/dep/a/classmap\" which does not appear to be a file nor a folder", + s.vendor_dir + ); + if let Err(e) = dump(&mut s, package.clone().into(), true, "_19") { + assert_eq!(expected_message, e.to_string()); + } + + dep.__set_autoload(autoload(vec![("files", str_list(&["./test.php"]))])); + dump(&mut s, package.clone().into(), true, "_19").unwrap(); + let files = + std::fs::read_to_string(format!("{}/composer/autoload_files.php", s.vendor_dir)).unwrap(); + assert!(files.contains("$vendorDir . '/dep/a/test.php',\n")); + + package.set_autoload(autoload(vec![( + "exclude-from-classmap", + str_list(&["../excludedroot", "root/excl"]), + )])); + dep.__set_autoload(autoload(vec![( + "exclude-from-classmap", + str_list(&["../../excluded", "foo/bar"]), + )])); + let map = s + .generator + .build_package_map(&mut s.im, package.clone().into(), vec![dep.clone().into()]) + .unwrap(); + let parsed = s + .generator + .parse_autoloads(map, package.into(), PhpMixed::Bool(false)); + let excluded_root = format!( + "{}/excludedroot($|/)", + preg_quote( + &strtr( + &realpath(dirname(&s.working_dir)).unwrap_or_default(), + "\\", + "/" + ), + None + ) + ); + let root_excl = format!( + "{}/root/excl($|/)", + preg_quote( + &strtr(&realpath(&s.working_dir).unwrap_or_default(), "\\", "/"), + None + ) + ); + assert_eq!( + str_map(&[("0", pstr(&excluded_root)), ("1", pstr(&root_excl))]), + parsed["exclude-from-classmap"] + ); } /// ref: AutoloadGeneratorTest::platformCheckProvider — builds the link map for a requires/provides/ diff --git a/crates/shirabe/tests/autoload/class_loader_test.rs b/crates/shirabe/tests/autoload/class_loader_test.rs index 582e24fa..998683c9 100644 --- a/crates/shirabe/tests/autoload/class_loader_test.rs +++ b/crates/shirabe/tests/autoload/class_loader_test.rs @@ -1,13 +1,37 @@ //! ref: composer/tests/Composer/Test/Autoload/ClassLoaderTest.php use shirabe::autoload::class_loader::ClassLoader; +use shirabe_php_shim::class_exists; + +/// ref: ClassLoaderTest::getLoadClassTests +fn get_load_class_tests() -> Vec<&'static str> { + vec![ + "Namespaced\\Foo", + "Pearlike_Foo", + "ShinyVendor\\ShinyPackage\\SubNamespace\\Foo", + ] +} #[test] -#[ignore = "depends on PHP runtime class_exists() to verify loadClass defined a class; no Rust equivalent"] +#[ignore = "shirabe_php_shim::class_exists models a fixed set of classes available in a PHP CLI environment; loadClass cannot add to it because including a PHP file does not define a class on the Rust side"] fn test_load_class() { - // TODO(phase-d): loadClass() include()s a fixture and PHPUnit asserts via class_exists(); - // Rust has no equivalent of runtime class definition/loading. - todo!() + let fixtures = std::path::Path::new(env!("CARGO_MANIFEST_DIR")) + .join("../../composer/tests/Composer/Test/Autoload/Fixtures") + .canonicalize() + .unwrap() + .display() + .to_string(); + + for class in get_load_class_tests() { + let mut loader = ClassLoader::new(None); + loader.add("Namespaced\\", vec![fixtures.clone()], false); + loader.add("Pearlike_", vec![fixtures.clone()], false); + loader + .add_psr4("ShinyVendor\\ShinyPackage\\", vec![fixtures.clone()], false) + .unwrap(); + loader.load_class(class); + assert!(class_exists(class), "->loadClass() loads '{}'", class); + } } #[test] @@ -17,9 +41,10 @@ fn test_get_prefixes_with_no_psr0_configuration() { } #[test] -#[ignore = "depends on PHP serialize()/unserialize() round-trip of ClassLoader; no Rust equivalent"] +#[ignore = "the round trip is `unserialize(serialize($loader))`: shirabe_php_shim::serialize takes a PhpMixed (a ClassLoader cannot be turned into one) and there is no unserialize at all, so the ClassLoader under test cannot be round-tripped"] fn test_serializability() { - // TODO(phase-d): serializes/unserializes the ClassLoader and compares every getter; PHP - // serialize()/unserialize() has no Rust equivalent here. + // TODO(phase-d): the round trip is `unserialize(serialize($loader))`. serialize() in the shim + // takes a PhpMixed, which a ClassLoader cannot be converted into, and there is no unserialize + // symbol to produce the second ClassLoader the assertions compare against. todo!() } -- cgit v1.3.1