diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-06-22 02:09:59 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-06-22 02:09:59 +0900 |
| commit | 822d9a872807a92a5337ee8b7bab96dc9845cdbb (patch) | |
| tree | 4931365df5978caffade69cad2154718a9076f66 /crates/shirabe/tests/package | |
| parent | f691b864b687f251c3b266e8cff2774d730567ba (diff) | |
| download | php-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.tar.gz php-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.tar.zst php-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.zip | |
test: port more test cases
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/package')
12 files changed, 1773 insertions, 142 deletions
diff --git a/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs b/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs index 5454074..48f0fc5 100644 --- a/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs +++ b/crates/shirabe/tests/package/archiver/archivable_files_finder_test.rs @@ -26,20 +26,20 @@ impl Drop for TearDown { // These set up a temp directory tree (including a git repo) and assert the files the finder // selects with manual/git/skip excludes; the git-backed fixture setup is not ported. +#[ignore = "setUp needs TestCase::getUniqueTmpDirectory to build the on-disk fixture tree; not ported"] #[test] -#[ignore = "needs a temp directory tree and git-backed fixtures to drive ArchivableFilesFinder; not ported"] fn test_manual_excludes() { todo!() } +#[ignore = "setUp needs TestCase::getUniqueTmpDirectory plus skipIfNotExecutable/Process::fromShellCommandline/PharData/RecursiveIteratorIterator; none ported"] #[test] -#[ignore = "needs a temp directory tree and git-backed fixtures to drive ArchivableFilesFinder; not ported"] fn test_git_excludes() { todo!() } +#[ignore = "setUp needs TestCase::getUniqueTmpDirectory to build the on-disk fixture tree; not ported"] #[test] -#[ignore = "needs a temp directory tree and git-backed fixtures to drive ArchivableFilesFinder; not ported"] fn test_skip_excludes() { todo!() } diff --git a/crates/shirabe/tests/package/archiver/archive_manager_test.rs b/crates/shirabe/tests/package/archiver/archive_manager_test.rs index b3b61f8..2712da6 100644 --- a/crates/shirabe/tests/package/archiver/archive_manager_test.rs +++ b/crates/shirabe/tests/package/archiver/archive_manager_test.rs @@ -29,31 +29,31 @@ impl Drop for TearDown { // the filename-derivation helpers over packages; the archiving and fixture setup are not // ported. #[test] -#[ignore = "ArchiveManager builds archives via PharData (todo!()) over fixtures; not ported"] +#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"] fn test_unknown_format() { todo!() } #[test] -#[ignore = "ArchiveManager builds archives via PharData (todo!()) over fixtures; not ported"] +#[ignore = "needs setupGitRepo + PharData tar archiving and Factory-built ArchiveManager (set_up), and setupPackage's setSourceType which is not exposed on the package handle API"] fn test_archive_tar() { todo!() } #[test] -#[ignore = "ArchiveManager builds archives via PharData (todo!()) over fixtures; not ported"] +#[ignore = "needs setupGitRepo + PharData tar archiving and Factory-built ArchiveManager (set_up), and setupPackage's setSourceType which is not exposed on the package handle API"] fn test_archive_custom_file_name() { todo!() } #[test] -#[ignore = "ArchiveManager builds archives via PharData (todo!()) over fixtures; not ported"] +#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"] fn test_get_package_filename_parts() { todo!() } #[test] -#[ignore = "ArchiveManager builds archives via PharData (todo!()) over fixtures; not ported"] +#[ignore = "setupPackage requires CompletePackage::setSourceType, not exposed on the package handle API; also needs Factory-built ArchiveManager (set_up) which is unported"] fn test_get_package_filename() { todo!() } diff --git a/crates/shirabe/tests/package/archiver/phar_archiver_test.rs b/crates/shirabe/tests/package/archiver/phar_archiver_test.rs index 414aa36..385177b 100644 --- a/crates/shirabe/tests/package/archiver/phar_archiver_test.rs +++ b/crates/shirabe/tests/package/archiver/phar_archiver_test.rs @@ -1,13 +1,13 @@ //! ref: composer/tests/Composer/Test/Package/Archiver/PharArchiverTest.php +#[ignore = "setUp/setupDummyRepo need TestCase::getUniqueTmpDirectory plus Filesystem to build the on-disk fixture tree; not ported"] #[test] -#[ignore = "PharArchiver::archive builds a tar via PharData, which is todo!() in the php-shim"] fn test_tar_archive() { todo!() } +#[ignore = "setUp/setupDummyRepo need TestCase::getUniqueTmpDirectory plus Filesystem to build the on-disk fixture tree; not ported"] #[test] -#[ignore = "PharArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim"] fn test_zip_archive() { todo!() } diff --git a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs index 5f41c78..5b51ce9 100644 --- a/crates/shirabe/tests/package/archiver/zip_archiver_test.rs +++ b/crates/shirabe/tests/package/archiver/zip_archiver_test.rs @@ -28,19 +28,19 @@ impl Drop for TearDown { // ZipArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim. #[test] -#[ignore = "ZipArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim"] +#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"] fn test_simple_files() { todo!() } #[test] -#[ignore = "ZipArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim"] +#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"] fn test_gitignore_exclude_negation() { todo!() } #[test] -#[ignore = "ZipArchiver::archive builds a zip via ZipArchive, which is todo!() in the php-shim"] +#[ignore = "ArchiverTestCase infrastructure (setup_package/setup_dummy_repo/test_dir) not ported; ZipArchive shim lacks get_from_name"] fn test_folder_with_backslashes() { todo!() } diff --git a/crates/shirabe/tests/package/base_package_test.rs b/crates/shirabe/tests/package/base_package_test.rs index 0d77f49..1056de1 100644 --- a/crates/shirabe/tests/package/base_package_test.rs +++ b/crates/shirabe/tests/package/base_package_test.rs @@ -35,8 +35,8 @@ fn test_set_another_repository() { // on an abstract BasePackage to drive getFullPrettyVersion(). Reproducing those exact // getter values requires mocking; a real package cannot carry the pretty version // "PrettyVersion" together with isDev() == true. +#[ignore = "requires mocking isDev/getSourceType/getPrettyVersion/getSourceReference on an abstract BasePackage; no mock infrastructure exists and a real package cannot carry prettyVersion \"PrettyVersion\" with isDev() == true"] #[test] -#[ignore = "mocks isDev/getSourceType/getPrettyVersion/getSourceReference on an abstract BasePackage; not reproducible with a real package"] fn test_format_version_for_dev_package() { todo!() } diff --git a/crates/shirabe/tests/package/dumper/array_dumper_test.rs b/crates/shirabe/tests/package/dumper/array_dumper_test.rs index 6ed12fe..64880f1 100644 --- a/crates/shirabe/tests/package/dumper/array_dumper_test.rs +++ b/crates/shirabe/tests/package/dumper/array_dumper_test.rs @@ -83,7 +83,7 @@ fn test_dump_abandoned_replacement() { // arrays, a DateTime and Link maps, then checks the corresponding dumped key. Reproducing // that dynamic dispatch faithfully would require per-case wiring for every property type. #[test] -#[ignore = "exercises ~25 dynamic set<Property> calls over heterogeneous value types; not reproduced here"] +#[ignore = "RootPackageHandle lacks set_type/set_release_date/set_binaries/set_php_ext setters required by the data provider's type/time/bin/php-ext cases"] fn test_keys() { todo!() } diff --git a/crates/shirabe/tests/package/loader/array_loader_test.rs b/crates/shirabe/tests/package/loader/array_loader_test.rs index 2dc4f3e..d35f671 100644 --- a/crates/shirabe/tests/package/loader/array_loader_test.rs +++ b/crates/shirabe/tests/package/loader/array_loader_test.rs @@ -1,170 +1,647 @@ //! ref: composer/tests/Composer/Test/Package/Loader/ArrayLoaderTest.php -use shirabe::package::loader::ArrayLoader; +use indexmap::IndexMap; +use shirabe::package::ArrayDumper; +use shirabe::package::Link; +use shirabe::package::loader::{ArrayLoader, LoaderInterface}; +use shirabe::package::version::VersionParser; +use shirabe_php_shim::PhpMixed; fn set_up() -> ArrayLoader { ArrayLoader::new(None, false) } -// ArrayLoader::load parses version/link constraints through a look-around regex the regex -// crate cannot compile. +fn s(value: &str) -> PhpMixed { + PhpMixed::String(value.to_string()) +} + +fn map(entries: Vec<(&str, PhpMixed)>) -> PhpMixed { + let mut m: IndexMap<String, PhpMixed> = IndexMap::new(); + for (k, v) in entries { + m.insert(k.to_string(), v); + } + PhpMixed::Array(m) +} + +fn make_config(entries: Vec<(&str, PhpMixed)>) -> IndexMap<String, PhpMixed> { + let mut m: IndexMap<String, PhpMixed> = IndexMap::new(); + for (k, v) in entries { + m.insert(k.to_string(), v); + } + m +} + +fn list(items: Vec<PhpMixed>) -> PhpMixed { + PhpMixed::List(items) +} + +/// ref: ArrayLoaderTest::parseDumpProvider valid config +fn valid_config() -> IndexMap<String, PhpMixed> { + make_config(vec![ + ("name", s("A/B")), + ("version", s("1.2.3")), + ("version_normalized", s("1.2.3.0")), + ("description", s("Foo bar")), + ("type", s("library")), + ("keywords", list(vec![s("a"), s("b"), s("c")])), + ("homepage", s("http://example.com")), + ("license", list(vec![s("MIT"), s("GPLv3")])), + ( + "authors", + list(vec![map(vec![ + ("name", s("Bob")), + ("email", s("bob@example.org")), + ("homepage", s("example.org")), + ("role", s("Developer")), + ])]), + ), + ( + "funding", + list(vec![map(vec![ + ("type", s("example")), + ("url", s("https://example.org/fund")), + ])]), + ), + ("require", map(vec![("foo/bar", s("1.0"))])), + ("require-dev", map(vec![("foo/baz", s("1.0"))])), + ("replace", map(vec![("foo/qux", s("1.0"))])), + ("conflict", map(vec![("foo/quux", s("1.0"))])), + ("provide", map(vec![("foo/quuux", s("1.0"))])), + ( + "autoload", + map(vec![ + ("psr-0", map(vec![("Ns\\Prefix", s("path"))])), + ("classmap", list(vec![s("path"), s("path2")])), + ]), + ), + ("include-path", list(vec![s("path3"), s("path4")])), + ("target-dir", s("some/prefix")), + ( + "extra", + map(vec![( + "random", + map(vec![("things", s("of")), ("any", s("shape"))]), + )]), + ), + ("bin", list(vec![s("bin1"), s("bin/foo")])), + ( + "archive", + map(vec![( + "exclude", + list(vec![s("/foo/bar"), s("baz"), s("!/foo/bar/baz")]), + )]), + ), + ( + "transport-options", + map(vec![( + "ssl", + map(vec![("local_cert", s("/opt/certs/test.pem"))]), + )]), + ), + ("abandoned", s("foo/bar")), + ]) +} + +/// ref: ArrayLoaderTest::fixConfigWhenLoadConfigIsFalse +fn fix_config_when_load_config_is_false( + config: &IndexMap<String, PhpMixed>, +) -> IndexMap<String, PhpMixed> { + let mut expected_config = config.clone(); + expected_config.shift_remove("transport-options"); + expected_config +} + +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_self_version() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("A")), + ("version", s("1.2.3.4")), + ("replace", map(vec![("foo", s("self.version"))])), + ]); + + let package = loader.load(config, None).unwrap(); + let replaces = package.get_replaces(); + assert_eq!( + "== 1.2.3.4", + replaces.get("foo").unwrap().get_constraint().to_string() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_type_default() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![("name", s("A")), ("version", s("1.0"))]); + + let package = loader.load(config, None).unwrap(); + assert_eq!("library", package.get_type()); + + let config = make_config(vec![ + ("name", s("A")), + ("version", s("1.0")), + ("type", s("foo")), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!("foo", package.get_type()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_normalized_version_optimization() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![("name", s("A")), ("version", s("1.2.3"))]); + + let package = loader.load(config, None).unwrap(); + assert_eq!("1.2.3.0", package.get_version()); + + let config = make_config(vec![ + ("name", s("A")), + ("version", s("1.2.3")), + ("version_normalized", s("1.2.3.4")), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!("1.2.3.4", package.get_version()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_parse_dump_default_load_config() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = valid_config(); + let package = loader.load(config.clone(), None).unwrap(); + let dumper = ArrayDumper::new(); + let expected_config = fix_config_when_load_config_is_false(&config); + assert_eq!(expected_config, dumper.dump(package)); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_parse_dump_true_load_config() { - let _loader = set_up(); - todo!() + let config = valid_config(); + let loader = ArrayLoader::new(None, true); + let package = loader.load(config.clone(), None).unwrap(); + let dumper = ArrayDumper::new(); + let expected_config = config; + assert_eq!(expected_config, dumper.dump(package)); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_parse_dump_false_load_config() { - let _loader = set_up(); - todo!() + let config = valid_config(); + let loader = ArrayLoader::new(None, false); + let package = loader.load(config.clone(), None).unwrap(); + let dumper = ArrayDumper::new(); + let expected_config = fix_config_when_load_config_is_false(&config); + assert_eq!(expected_config, dumper.dump(package)); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_with_branch_alias() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("A")), + ("version", s("dev-master")), + ( + "extra", + map(vec![( + "branch-alias", + map(vec![("dev-master", s("1.0.x-dev"))]), + )]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_alias_package().is_some()); + assert_eq!("1.0.x-dev", package.get_pretty_version()); + + let config = make_config(vec![ + ("name", s("A")), + ("version", s("dev-master")), + ( + "extra", + map(vec![( + "branch-alias", + map(vec![("dev-master", s("1.0-dev"))]), + )]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_alias_package().is_some()); + assert_eq!("1.0.x-dev", package.get_pretty_version()); + + let config = make_config(vec![ + ("name", s("B")), + ("version", s("4.x-dev")), + ( + "extra", + map(vec![( + "branch-alias", + map(vec![("4.x-dev", s("4.0.x-dev"))]), + )]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_alias_package().is_some()); + assert_eq!("4.0.x-dev", package.get_pretty_version()); + + let config = make_config(vec![ + ("name", s("B")), + ("version", s("4.x-dev")), + ( + "extra", + map(vec![("branch-alias", map(vec![("4.x-dev", s("4.0-dev"))]))]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_alias_package().is_some()); + assert_eq!("4.0.x-dev", package.get_pretty_version()); + + let config = make_config(vec![ + ("name", s("C")), + ("version", s("4.x-dev")), + ( + "extra", + map(vec![( + "branch-alias", + map(vec![("4.x-dev", s("3.4.x-dev"))]), + )]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_package().is_some()); + assert_eq!("4.x-dev", package.get_pretty_version()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_aliasing_without_branch_alias() { - let _loader = set_up(); - todo!() + let loader = set_up(); + // non-numeric gets a default alias + let config = make_config(vec![ + ("name", s("A")), + ("version", s("dev-main")), + ("default-branch", PhpMixed::Bool(true)), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_alias_package().is_some()); + assert_eq!( + VersionParser::DEFAULT_BRANCH_ALIAS, + package.get_pretty_version() + ); + + // non-default branch gets no alias even if non-numeric + let config = make_config(vec![ + ("name", s("A")), + ("version", s("dev-main")), + ("default-branch", PhpMixed::Bool(false)), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_package().is_some()); + assert_eq!("dev-main", package.get_pretty_version()); + + // default branch gets no alias if already numeric + let config = make_config(vec![ + ("name", s("A")), + ("version", s("2.x-dev")), + ("default-branch", PhpMixed::Bool(true)), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_package().is_some()); + assert_eq!("2.9999999.9999999.9999999-dev", package.get_version()); + + // default branch gets no alias if already numeric, with v prefix + let config = make_config(vec![ + ("name", s("A")), + ("version", s("v2.x-dev")), + ("default-branch", PhpMixed::Bool(true)), + ]); + + let package = loader.load(config, None).unwrap(); + + assert!(package.as_complete_package().is_some()); + assert_eq!("2.9999999.9999999.9999999-dev", package.get_version()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_abandoned() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("A")), + ("version", s("1.2.3.4")), + ("abandoned", s("foo/bar")), + ]); + + let package = loader.load(config, None).unwrap(); + let package = package.as_complete().unwrap(); + assert!(package.is_abandoned()); + assert_eq!( + Some("foo/bar".to_string()), + package.get_replacement_package() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_not_abandoned() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![("name", s("A")), ("version", s("1.2.3.4"))]); + + let package = loader.load(config, None).unwrap(); + let package = package.as_complete().unwrap(); + assert!(!package.is_abandoned()); +} + +/// ref: ArrayLoaderTest::providePluginApiVersions +fn provide_plugin_api_versions() -> Vec<&'static str> { + vec![ + "1.0", + "1.0.0", + "1.0.0.0", + "1", + "=1.0.0", + "==1.0", + "~1.0.0", + "*", + "3.0.*", + "@stable", + "1.0.0@stable", + "^5.1", + ">=1.0.0 <2.5", + "x", + "1.0.0-dev", + ] } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_plugin_api_version_are_kept_as_declared() { - let _loader = set_up(); - todo!() + let loader = set_up(); + for api_version in provide_plugin_api_versions() { + let links = loader + .parse_links( + "Plugin", + "9.9.9", + Link::TYPE_REQUIRE, + make_config(vec![("composer-plugin-api", s(api_version))]), + ) + .unwrap(); + + assert!(links.contains_key("composer-plugin-api")); + assert_eq!( + api_version, + links + .get("composer-plugin-api") + .unwrap() + .get_constraint() + .get_pretty_string() + ); + } } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_plugin_api_version_does_support_self_version() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let links = loader + .parse_links( + "Plugin", + "6.6.6", + Link::TYPE_REQUIRE, + make_config(vec![("composer-plugin-api", s("self.version"))]), + ) + .unwrap(); + + assert!(links.contains_key("composer-plugin-api")); + assert_eq!( + "6.6.6", + links + .get("composer-plugin-api") + .unwrap() + .get_constraint() + .get_pretty_string() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_parse_links_integer_target() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let links = loader + .parse_links( + "Plugin", + "9.9.9", + Link::TYPE_REQUIRE, + make_config(vec![("1", s("dev-main"))]), + ) + .unwrap(); + + assert!(links.contains_key("1")); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_parse_links_invalid_version() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let err = loader + .parse_links( + "Plugin", + "9.9.9", + Link::TYPE_REQUIRE, + make_config(vec![("composer-plugin-api", s("^^^"))]), + ) + .unwrap_err(); + assert_eq!( + "Link constraint in Plugin requires > composer-plugin-api should be a valid version constraint, got \"^^^\"", + err.to_string() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_none_string_version() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", PhpMixed::Int(1)), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!("1", package.get_pretty_version()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_none_string_source_dist_reference() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-main")), + ( + "source", + map(vec![ + ("type", s("svn")), + ("url", s("https://example.org/")), + ("reference", PhpMixed::Int(2019)), + ]), + ), + ( + "dist", + map(vec![ + ("type", s("zip")), + ("url", s("https://example.org/")), + ("reference", PhpMixed::Int(2019)), + ]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!(Some("2019".to_string()), package.get_source_reference()); + assert_eq!(Some("2019".to_string()), package.get_dist_reference()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_branch_alias_integer_index() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ( + "extra", + map(vec![("branch-alias", map(vec![("1", s("1.3-dev"))]))]), + ), + ( + "dist", + map(vec![("type", s("zip")), ("url", s("https://example.org/"))]), + ), + ]); + + assert_eq!(None, loader.get_branch_alias(&config).unwrap()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_links_require() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ("require", map(vec![("foo/bar", s("1.0"))])), + ]); + + let package = loader.load(config, None).unwrap(); + assert!(package.get_requires().contains_key("foo/bar")); + assert_eq!( + "1.0", + package + .get_requires() + .get("foo/bar") + .unwrap() + .get_constraint() + .get_pretty_string() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_links_require_invalid() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ( + "require", + map(vec![("foo/bar", map(vec![("random-string", s("1.0"))]))]), + ), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!(0, package.get_requires().len()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_links_replace() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ("replace", map(vec![("coyote/package", s("self.version"))])), + ]); + + let package = loader.load(config, None).unwrap(); + assert!(package.get_replaces().contains_key("coyote/package")); + assert_eq!( + "dev-1", + package + .get_replaces() + .get("coyote/package") + .unwrap() + .get_constraint() + .get_pretty_string() + ); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_package_links_replace_invalid() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ("replace", s("coyote/package")), + ]); + + let package = loader.load(config, None).unwrap(); + assert_eq!(0, package.get_replaces().len()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_support_string_value() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![ + ("name", s("acme/package")), + ("version", s("dev-1")), + ("support", s("https://example.org")), + ]); + + let package = loader.load(config, None).unwrap(); + let package = package.as_complete().unwrap(); + assert_eq!(0, package.get_support().len()); } +#[ignore] #[test] -#[ignore = "ArrayLoader::load parses constraints via a look-around regex the regex crate cannot compile"] fn test_invalid_version() { - let _loader = set_up(); - todo!() + let loader = set_up(); + let config = make_config(vec![("name", s("acme/package")), ("version", s("AA"))]); + + let err = loader.load(config, None).unwrap_err(); + assert_eq!( + "Failed to normalize version for package \"acme/package\": Invalid version string \"AA\"", + err.to_string() + ); } diff --git a/crates/shirabe/tests/package/loader/root_package_loader_test.rs b/crates/shirabe/tests/package/loader/root_package_loader_test.rs index 82d60ba..e78967e 100644 --- a/crates/shirabe/tests/package/loader/root_package_loader_test.rs +++ b/crates/shirabe/tests/package/loader/root_package_loader_test.rs @@ -4,32 +4,157 @@ // ProcessExecutor / VersionGuesser or require constraints whose parsing goes through a // look-around regex the regex crate cannot compile. +use std::cell::RefCell; +use std::rc::Rc; + +use indexmap::IndexMap; +use shirabe::config::Config; +use shirabe::io::IOInterface; +use shirabe::io::null_io::NullIO; +use shirabe::package::loader::RootPackageLoader; +use shirabe::package::version::{VersionGuesser, VersionParser}; +use shirabe::package::{STABILITY_ALPHA, STABILITY_DEV, STABILITY_RC}; +use shirabe::repository::RepositoryManager; +use shirabe::util::http_downloader::HttpDownloader; +use shirabe::util::process_executor::ProcessExecutor; +use shirabe_php_shim::PhpMixed; + +fn null_io() -> Rc<RefCell<dyn IOInterface>> { + Rc::new(RefCell::new(NullIO::new())) +} + +fn http_downloader( + io: &Rc<RefCell<dyn IOInterface>>, + config: &Rc<RefCell<Config>>, +) -> Rc<RefCell<HttpDownloader>> { + Rc::new(RefCell::new(HttpDownloader::new( + io.clone(), + config.clone(), + IndexMap::new(), + true, + ))) +} + #[test] -#[ignore = "RootPackageLoader::load parses require constraints via a look-around regex; mocks RepositoryManager"] +#[ignore] fn test_stability_flags_parsing() { - todo!() + let io = null_io(); + let config = Rc::new(RefCell::new(Config::new(true, None))); + { + let mut cfg = IndexMap::new(); + cfg.insert( + "repositories".to_string(), + PhpMixed::Array({ + let mut m = IndexMap::new(); + m.insert("packagist".to_string(), PhpMixed::Bool(false)); + m + }), + ); + config.borrow_mut().merge(&cfg, "test"); + } + + let manager = Rc::new(RefCell::new(RepositoryManager::new( + io.clone(), + config.clone(), + http_downloader(&io, &config), + None, + None, + ))); + + let mut process_executor = ProcessExecutor::new(Some(io.clone())); + process_executor.enable_async(); + let guesser = VersionGuesser::new( + config.clone(), + Rc::new(RefCell::new(process_executor)), + VersionParser::new(), + Some(io.clone()), + ); + + let mut loader = RootPackageLoader::new(manager, config.clone(), None, Some(guesser), None); + + let mut data = IndexMap::new(); + data.insert( + "require".to_string(), + PhpMixed::Array({ + let mut m = IndexMap::new(); + m.insert( + "foo/bar".to_string(), + PhpMixed::String("~2.1.0-beta2".to_string()), + ); + m.insert( + "bar/baz".to_string(), + PhpMixed::String("1.0.x-dev as 1.2.0".to_string()), + ); + m.insert( + "qux/quux".to_string(), + PhpMixed::String("1.0.*@rc".to_string()), + ); + m.insert( + "zux/complex".to_string(), + PhpMixed::String("~1.0,>=1.0.2@dev".to_string()), + ); + m.insert( + "or/op".to_string(), + PhpMixed::String("^2.0@dev || ^2.0@dev".to_string()), + ); + m.insert( + "multi/lowest-wins".to_string(), + PhpMixed::String("^2.0@rc || >=3.0@dev , ~3.5@alpha".to_string()), + ); + m.insert( + "or/op-without-flags".to_string(), + PhpMixed::String("dev-master || 2.0 , ~3.5-alpha".to_string()), + ); + m.insert( + "or/op-without-flags2".to_string(), + PhpMixed::String("3.0-beta || 2.0 , ~3.5-alpha".to_string()), + ); + m + }), + ); + data.insert( + "minimum-stability".to_string(), + PhpMixed::String("alpha".to_string()), + ); + + let package = loader + .load(data, "Composer\\Package\\RootPackage", None) + .unwrap(); + let package = package.as_root().unwrap(); + + assert_eq!("alpha", package.get_minimum_stability()); + + let mut expected = IndexMap::new(); + expected.insert("bar/baz".to_string(), STABILITY_DEV); + expected.insert("qux/quux".to_string(), STABILITY_RC); + expected.insert("zux/complex".to_string(), STABILITY_DEV); + expected.insert("or/op".to_string(), STABILITY_DEV); + expected.insert("multi/lowest-wins".to_string(), STABILITY_DEV); + expected.insert("or/op-without-flags".to_string(), STABILITY_DEV); + expected.insert("or/op-without-flags2".to_string(), STABILITY_ALPHA); + assert_eq!(expected, package.get_stability_flags()); } #[test] -#[ignore = "mocks RepositoryManager and a ProcessExecutor returning a non-zero git result"] +#[ignore = "requires getProcessExecutorMock with expects(['return' => 1]); no ProcessExecutorMock mocking infrastructure exists"] fn test_no_version_is_visible_in_pretty_version() { todo!() } #[test] -#[ignore = "mocks RepositoryManager and a VersionGuesser returning a fixed guessed version"] +#[ignore = "requires getMockBuilder VersionGuesser mock with guessVersion expectation; no VersionGuesser mocking infrastructure exists"] fn test_pretty_version_for_root_package_in_version_branch() { todo!() } #[test] -#[ignore = "mocks RepositoryManager and a ProcessExecutor feeding git branch output"] +#[ignore = "requires getProcessExecutorMock with expects() git command expectations; no ProcessExecutorMock mocking infrastructure exists"] fn test_feature_branch_pretty_version() { todo!() } #[test] -#[ignore = "mocks RepositoryManager and a ProcessExecutor feeding git branch output"] +#[ignore = "requires getProcessExecutorMock with expects() git command expectations; no ProcessExecutorMock mocking infrastructure exists"] fn test_non_feature_branch_pretty_version() { todo!() } diff --git a/crates/shirabe/tests/package/loader/validating_array_loader_test.rs b/crates/shirabe/tests/package/loader/validating_array_loader_test.rs index 9858df1..db5ff07 100644 --- a/crates/shirabe/tests/package/loader/validating_array_loader_test.rs +++ b/crates/shirabe/tests/package/loader/validating_array_loader_test.rs @@ -1,27 +1,1029 @@ //! ref: composer/tests/Composer/Test/Package/Loader/ValidatingArrayLoaderTest.php -// ValidatingArrayLoader wraps ArrayLoader, whose constraint parsing uses a look-around -// regex the regex crate cannot compile; the success/warning data sets are large. +use std::cell::RefCell; +use std::rc::Rc; + +use indexmap::IndexMap; +use shirabe::package::handle::PackageInterfaceHandle; +use shirabe::package::loader::{InvalidPackageException, LoaderInterface, ValidatingArrayLoader}; +use shirabe_php_shim::PhpMixed; + +#[path = "../../common/test_case.rs"] +mod test_case; + +fn s(v: &str) -> PhpMixed { + PhpMixed::String(v.to_string()) +} + +fn i(v: i64) -> PhpMixed { + PhpMixed::Int(v) +} + +fn b(v: bool) -> PhpMixed { + PhpMixed::Bool(v) +} + +/// Build a PHP assoc array (string-keyed). The impl reads every PHP `array`, +/// list or assoc, via `PhpMixed::as_array`, so both shapes are `PhpMixed::Array`. +fn arr(entries: Vec<(&str, PhpMixed)>) -> PhpMixed { + let mut m = IndexMap::new(); + for (k, v) in entries { + m.insert(k.to_string(), v); + } + PhpMixed::Array(m) +} + +/// Build a PHP list array as a `PhpMixed::Array` with sequential string keys. +fn list(items: Vec<PhpMixed>) -> PhpMixed { + let mut m = IndexMap::new(); + for (idx, v) in items.into_iter().enumerate() { + m.insert(idx.to_string(), v); + } + PhpMixed::Array(m) +} + +fn config(entries: Vec<(&str, PhpMixed)>) -> IndexMap<String, PhpMixed> { + let mut m = IndexMap::new(); + for (k, v) in entries { + m.insert(k.to_string(), v); + } + m +} + +type Calls = Rc<RefCell<Vec<IndexMap<String, PhpMixed>>>>; + +/// Mock LoaderInterface recording every `load` invocation, mirroring PHPUnit's +/// `expects($this->once())->method('load')->with(...)`. +#[derive(Debug)] +struct MockLoader { + calls: Calls, +} + +impl MockLoader { + fn new() -> (Self, Calls) { + let calls: Calls = Rc::new(RefCell::new(Vec::new())); + ( + MockLoader { + calls: calls.clone(), + }, + calls, + ) + } +} + +impl LoaderInterface for MockLoader { + fn load( + &self, + config: IndexMap<String, PhpMixed>, + _class: Option<String>, + ) -> anyhow::Result<PackageInterfaceHandle> { + self.calls.borrow_mut().push(config); + Ok(test_case::get_package("mock/mock", "1.0.0")) + } + + fn as_any(&self) -> &dyn std::any::Any { + self + } +} + +fn invalid_naming_error(name: &str) -> Vec<String> { + vec*/[a-z0-9](([_.]?|-{{0,2}})[a-z0-9]+)*$\".", + name + )] +} + +/// ref: ValidatingArrayLoaderTest::successProvider +fn success_provider() -> Vec<IndexMap<String, PhpMixed>> { + vec![ + // minimal + config(vec![("name", s("foo/bar"))]), + // complete + config(vec![ + ("name", s("foo/bar")), + ("description", s("Foo bar")), + ("version", s("1.0.0")), + ("type", s("library")), + ( + "keywords", + list(vec![s("a"), s("b_c"), s("D E"), s("éîüø"), s("微信")]), + ), + ("homepage", s("https://foo.com")), + ("time", s("2010-10-10T10:10:10+00:00")), + ("license", list(vec![s("MIT"), s("WTFPL")])), + ( + "authors", + list(vec![ + arr(vec![ + ("name", s("Alice")), + ("email", s("alice@example.org")), + ("role", s("Lead")), + ("homepage", s("http://example.org")), + ]), + arr(vec![("name", s("Bob")), ("homepage", s(""))]), + ]), + ), + ( + "support", + arr(vec![ + ("email", s("mail@example.org")), + ("issues", s("http://example.org/")), + ("forum", s("http://example.org/")), + ("wiki", s("http://example.org/")), + ("source", s("http://example.org/")), + ("irc", s("irc://example.org/example")), + ("rss", s("http://example.org/rss")), + ("chat", s("http://example.org/chat")), + ("security", s("https://example.org/security")), + ]), + ), + ( + "funding", + list(vec![ + arr(vec![ + ("type", s("example")), + ("url", s("https://example.org/fund")), + ]), + arr(vec![("url", s("https://example.org/fund"))]), + ]), + ), + ( + "require", + arr(vec![ + ("a/b", s("1.*")), + ("b/c", s("~2")), + ("example/pkg", s(">2.0-dev,<2.4-dev")), + ("composer-runtime-api", s("*")), + ]), + ), + ( + "require-dev", + arr(vec![ + ("a/b", s("1.*")), + ("b/c", s("*")), + ("example/pkg", s(">2.0-dev,<2.4-dev")), + ]), + ), + ( + "conflict", + arr(vec![ + ("a/bx", s("1.*")), + ("b/cx", s(">2.7")), + ("example/pkgx", s(">2.0-dev,<2.4-dev")), + ]), + ), + ( + "replace", + arr(vec![ + ("a/b", s("1.*")), + ("example/pkg", s(">2.0-dev,<2.4-dev")), + ]), + ), + ( + "provide", + arr(vec![ + ("a/b", s("1.*")), + ("example/pkg", s(">2.0-dev,<2.4-dev")), + ]), + ), + ( + "suggest", + arr(vec![("foo/bar", s("Foo bar is very useful"))]), + ), + ( + "autoload", + arr(vec![ + ( + "psr-0", + arr(vec![("Foo\\Bar", s("src/")), ("", s("fallback/libs/"))]), + ), + ("classmap", list(vec![s("dir/"), s("dir2/file.php")])), + ("files", list(vec![s("functions.php")])), + ]), + ), + ("include-path", list(vec![s("lib/")])), + ("target-dir", s("Foo/Bar")), + ("minimum-stability", s("dev")), + ( + "repositories", + list(vec![arr(vec![ + ("type", s("composer")), + ("url", s("https://repo.packagist.org/")), + ])]), + ), + ( + "config", + arr(vec![ + ("bin-dir", s("bin")), + ("vendor-dir", s("vendor")), + ("process-timeout", i(10000)), + ]), + ), + ( + "archive", + arr(vec![( + "exclude", + list(vec![s("/foo/bar"), s("baz"), s("!/foo/bar/baz")]), + )]), + ), + ( + "scripts", + arr(vec![ + ("post-update-cmd", s("Foo\\Bar\\Baz::doSomething")), + ( + "post-install-cmd", + list(vec![s("Foo\\Bar\\Baz::doSomething")]), + ), + ]), + ), + ( + "extra", + arr(vec![ + ( + "random", + arr(vec![("stuff", arr(vec![("deeply", s("nested"))]))]), + ), + ( + "branch-alias", + arr(vec![ + ("dev-master", s("2.0-dev")), + ("dev-old", s("1.0.x-dev")), + ("3.x-dev", s("3.1.x-dev")), + ]), + ), + ]), + ), + ("bin", list(vec![s("bin/foo"), s("bin/bar")])), + ( + "transport-options", + arr(vec![( + "ssl", + arr(vec![("local_cert", s("/opt/certs/test.pem"))]), + )]), + ), + ]), + // test bin as string + config(vec![("name", s("foo/bar")), ("bin", s("bin1"))]), + // package name with dashes + config(vec![("name", s("foo/bar-baz"))]), + config(vec![("name", s("foo/bar--baz"))]), + config(vec![("name", s("foo/b-ar--ba-z"))]), + config(vec![("name", s("npm-asset/angular--core"))]), + // refs as int or string + config(vec![ + ("name", s("foo/bar")), + ( + "source", + arr(vec![ + ("url", s("https://example.org")), + ("reference", i(1234)), + ("type", s("baz")), + ]), + ), + ( + "dist", + arr(vec![ + ("url", s("https://example.org")), + ("reference", s("foobar")), + ("type", s("baz")), + ]), + ), + ]), + // valid php-ext configuration + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![ + ("extension-name", s("ext-xdebug")), + ("priority", i(80)), + ("support-zts", b(true)), + ("support-nts", b(false)), + ("build-path", s("my-extension-source")), + ("download-url-method", s("composer-default")), + ("os-families", list(vec![s("linux"), s("darwin")])), + ( + "configure-options", + list(vec![ + arr(vec![ + ("name", s("enable-xdebug")), + ("needs-value", b(false)), + ("description", s("Enable xdebug support")), + ]), + arr(vec![ + ("name", s("with-xdebug-path")), + ("needs-value", b(true)), + ]), + ]), + ), + ]), + ), + ]), + // valid php-ext with os-families-exclude + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext-zend")), + ( + "php-ext", + arr(vec![("os-families-exclude", list(vec![s("windows")]))]), + ), + ]), + // valid php-ext with null build-path + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("build-path", PhpMixed::Null)])), + ]), + // valid php-ext with one download-url-method in a list + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "download-url-method", + list(vec![s("pre-packaged-binary")]), + )]), + ), + ]), + // valid php-ext with multiple download-url-methods + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "download-url-method", + list(vec![ + s("pre-packaged-binary"), + s("pre-packaged-source"), + s("composer-default"), + ]), + )]), + ), + ]), + ] +} + +/// ref: ValidatingArrayLoaderTest::testLoadSuccess +#[ignore] #[test] -#[ignore = "ValidatingArrayLoader -> ArrayLoader parses constraints via a look-around regex the regex crate cannot compile"] fn test_load_success() { - todo!() + for cfg in success_provider() { + let (internal_loader, _calls) = MockLoader::new(); + let mut loader = ValidatingArrayLoader::new( + Box::new(internal_loader), + true, + None, + ValidatingArrayLoader::CHECK_ALL, + ); + loader + .load(cfg, "Composer\\Package\\CompletePackage") + .unwrap(); + } } +/// ref: ValidatingArrayLoaderTest::errorProvider +fn error_provider() -> Vec<(IndexMap<String, PhpMixed>, Vec<String>)> { + let mut data: Vec<(IndexMap<String, PhpMixed>, Vec<String>)> = Vec::new(); + + for invalid_name in ["foo", "foo/-bar-", "foo/-bar"] { + data.push(( + config(vec![("name", s(invalid_name))]), + invalid_naming_error(invalid_name), + )); + } + for invalid_name in [ + "fo--oo/bar", + "fo-oo/bar__baz", + "fo-oo/bar_.baz", + "foo/bar---baz", + ] { + data.push(( + config(vec![("name", s(invalid_name))]), + invalid_naming_error(invalid_name), + )); + } + + data.push(( + config(vec![("name", s("foo/bar")), ("homepage", i(43))]), + vec!["homepage : should be a string, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("support", arr(vec![("source", list(vec![]))])), + ]), + vec!["support.source : invalid value, must be a string".to_string()], + )); + data.push(( + config(vec![("name", s("foo/bar.json"))]), + vec!["name : foo/bar.json is invalid, package names can not end in .json, consider renaming it or perhaps using a -json suffix instead.".to_string()], + )); + data.push(( + config(vec![("name", s("com1/foo"))]), + vec!["name : com1/foo is reserved, package and vendor names can not match any of: nul, con, prn, aux, com1, com2, com3, com4, com5, com6, com7, com8, com9, lpt1, lpt2, lpt3, lpt4, lpt5, lpt6, lpt7, lpt8, lpt9.".to_string()], + )); + data.push(( + config(vec![("name", s("Foo/Bar"))]), + vec!["name : Foo/Bar is invalid, it should not contain uppercase characters. We suggest using foo/bar instead.".to_string()], + )); + data.push(( + config(vec![("name", s("foo/bar")), ("autoload", s("strings"))]), + vec!["autoload : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("autoload", arr(vec![("psr0", arr(vec![("foo", s("src"))]))])), + ]), + vec!["autoload : invalid value (psr0), must be one of psr-0, psr-4, classmap, files, exclude-from-classmap".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("transport-options", s("test")), + ]), + vec!["transport-options : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ( + "source", + arr(vec![ + ("url", s("--foo")), + ("reference", s(" --bar")), + ("type", s("baz")), + ]), + ), + ( + "dist", + arr(vec![ + ("url", s(" --foox")), + ("reference", s("--barx")), + ("type", s("baz")), + ]), + ), + ]), + vec![ + "dist.reference : must not start with a \"-\", \"--barx\" given".to_string(), + "dist.url : must not start with a \"-\", \" --foox\" given".to_string(), + "source.reference : must not start with a \"-\", \" --bar\" given".to_string(), + "source.url : must not start with a \"-\", \"--foo\" given".to_string(), + ], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("require", arr(vec![("foo/Bar", s("1.*"))])), + ]), + vec!["require.foo/Bar : a package cannot set a require on itself".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("source", arr(vec![("url", i(1))])), + ("dist", arr(vec![("url", PhpMixed::Null)])), + ]), + vec![ + "source.type : must be present".to_string(), + "source.url : should be a string, int given".to_string(), + "source.reference : must be present".to_string(), + "dist.type : must be present".to_string(), + "dist.url : must be present".to_string(), + ], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("replace", list(vec![s("acme/bar")])), + ]), + vec!["replace.0 : invalid version constraint (Could not parse version constraint acme/bar: Invalid version string \"acme/bar\")".to_string()], + )); + data.push(( + config(vec![("require", arr(vec![("acme/bar", s("^1.0"))]))]), + vec!["name : must be present".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("library")), + ("php-ext", arr(vec![("extension-name", s("ext-foobar"))])), + ]), + vec!["php-ext can only be set by packages of type \"php-ext\" or \"php-ext-zend\" which must be C extensions".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("extension-name", i(123))])), + ]), + vec!["php-ext.extension-name : should be a string, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("priority", s("invalid"))])), + ]), + vec!["php-ext.priority : should be an integer, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("support-zts", s("yes"))])), + ]), + vec!["php-ext.support-zts : should be a boolean, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("support-nts", i(1))])), + ]), + vec!["php-ext.support-nts : should be a boolean, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("build-path", i(123))])), + ]), + vec!["php-ext.build-path : should be a string or null, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("download-url-method", i(123))])), + ]), + vec!["php-ext.download-url-method : should be an array or a string, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![("download-url-method", s("invalid-method"))]), + ), + ]), + vec!["php-ext.download-url-method.0 : invalid value (invalid-method), must be one of composer-default, pre-packaged-source, pre-packaged-binary".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("download-url-method", list(vec![]))])), + ]), + vec!["php-ext.download-url-method : must contain at least one element".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "download-url-method", + list(vec![i(1), b(true), list(vec![])]), + )]), + ), + ]), + vec![ + "php-ext.download-url-method.0 : should be a string, int given".to_string(), + "php-ext.download-url-method.1 : should be a string, bool given".to_string(), + "php-ext.download-url-method.2 : should be a string, array given".to_string(), + ], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "download-url-method", + list(vec![s("invalid-method"), s("composer-default")]), + )]), + ), + ]), + vec!["php-ext.download-url-method.0 : invalid value (invalid-method), must be one of composer-default, pre-packaged-source, pre-packaged-binary".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "download-url-method", + list(vec![s("invalid-method"), s("another-invalid-method")]), + )]), + ), + ]), + vec![ + "php-ext.download-url-method.0 : invalid value (invalid-method), must be one of composer-default, pre-packaged-source, pre-packaged-binary".to_string(), + "php-ext.download-url-method.1 : invalid value (another-invalid-method), must be one of composer-default, pre-packaged-source, pre-packaged-binary".to_string(), + ], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![ + ("os-families", list(vec![s("linux")])), + ("os-families-exclude", list(vec![s("windows")])), + ]), + ), + ]), + vec!["php-ext : os-families and os-families-exclude cannot both be specified".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("os-families", s("linux"))])), + ]), + vec!["php-ext.os-families : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("os-families", list(vec![]))])), + ]), + vec!["php-ext.os-families : must contain at least one element".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![("os-families", list(vec![s("invalid-os"), s("linux")]))]), + ), + ]), + vec!["php-ext.os-families.0 : invalid value (invalid-os), must be one of windows, bsd, darwin, solaris, linux, unknown".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("os-families", list(vec![i(123)]))])), + ]), + vec!["php-ext.os-families.0 : should be a string, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("os-families-exclude", s("windows"))])), + ]), + vec!["php-ext.os-families-exclude : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("os-families-exclude", list(vec![]))])), + ]), + vec!["php-ext.os-families-exclude : must contain at least one element".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![("os-families-exclude", list(vec![s("invalid")]))]), + ), + ]), + vec!["php-ext.os-families-exclude.0 : invalid value (invalid), must be one of windows, bsd, darwin, solaris, linux, unknown".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ("php-ext", arr(vec![("configure-options", s("invalid"))])), + ]), + vec!["php-ext.configure-options : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![("configure-options", list(vec![s("invalid")]))]), + ), + ]), + vec!["php-ext.configure-options.0 : should be an array, string given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "configure-options", + list(vec![arr(vec![("description", s("test"))])]), + )]), + ), + ]), + vec!["php-ext.configure-options.0.name : must be present".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "configure-options", + list(vec![arr(vec![("name", i(123))])]), + )]), + ), + ]), + vec!["php-ext.configure-options.0.name : should be a string, int given".to_string()], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "configure-options", + list(vec![arr(vec![ + ("name", s("valid-name")), + ("needs-value", s("yes")), + ])]), + )]), + ), + ]), + vec![ + "php-ext.configure-options.0.needs-value : should be a boolean, string given" + .to_string(), + ], + )); + data.push(( + config(vec![ + ("name", s("foo/bar")), + ("type", s("php-ext")), + ( + "php-ext", + arr(vec![( + "configure-options", + list(vec![arr(vec![ + ("name", s("valid-name")), + ("description", i(123)), + ])]), + )]), + ), + ]), + vec!["php-ext.configure-options.0.description : should be a string, int given".to_string()], + )); + + data +} + +/// ref: ValidatingArrayLoaderTest::testLoadFailureThrowsException +#[ignore] #[test] -#[ignore = "ValidatingArrayLoader -> ArrayLoader parses constraints via a look-around regex the regex crate cannot compile"] fn test_load_failure_throws_exception() { - todo!() + for (cfg, mut expected_errors) in error_provider() { + let (internal_loader, _calls) = MockLoader::new(); + let mut loader = ValidatingArrayLoader::new( + Box::new(internal_loader), + true, + None, + ValidatingArrayLoader::CHECK_ALL, + ); + match loader.load(cfg, "Composer\\Package\\CompletePackage") { + Ok(_) => panic!("Expected exception to be thrown"), + Err(e) => { + let exception = e + .downcast_ref::<InvalidPackageException>() + .expect("Expected InvalidPackageException"); + let mut errors: Vec<String> = exception.get_errors().to_vec(); + expected_errors.sort(); + errors.sort(); + assert_eq!(expected_errors, errors); + } + } + } } +/// ref: ValidatingArrayLoaderTest::warningProvider +/// Returns (config, expected_warnings, must_check, expected_array). +fn warning_provider() -> Vec<( + IndexMap<String, PhpMixed>, + Vec<String>, + bool, + Option<IndexMap<String, PhpMixed>>, +)> { + vec![ + ( + config(vec![("name", s("foo/bar")), ("homepage", s("foo:bar"))]), + vec!["homepage : invalid value (foo:bar), must be an http/https URL".to_string()], + true, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ( + "support", + arr(vec![ + ("source", s("foo:bar")), + ("forum", s("foo:bar")), + ("issues", s("foo:bar")), + ("wiki", s("foo:bar")), + ("chat", s("foo:bar")), + ("security", s("foo:bar")), + ]), + ), + ]), + vec![ + "support.source : invalid value (foo:bar), must be an http/https URL".to_string(), + "support.forum : invalid value (foo:bar), must be an http/https URL".to_string(), + "support.issues : invalid value (foo:bar), must be an http/https URL".to_string(), + "support.wiki : invalid value (foo:bar), must be an http/https URL".to_string(), + "support.chat : invalid value (foo:bar), must be an http/https URL".to_string(), + "support.security : invalid value (foo:bar), must be an http/https URL".to_string(), + ], + true, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ( + "require", + arr(vec![ + ("foo/baz", s("*")), + ("bar/baz", s(">=1.0")), + ("bar/hacked", s("@stable")), + ("bar/woo", s("1.0.0")), + ]), + ), + ]), + vec![ + "require.foo/baz : unbound version constraints (*) should be avoided".to_string(), + "require.bar/baz : unbound version constraints (>=1.0) should be avoided" + .to_string(), + "require.bar/hacked : unbound version constraints (@stable) should be avoided" + .to_string(), + "require.bar/woo : exact version constraints (1.0.0) should be avoided if the package follows semantic versioning".to_string(), + ], + false, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ( + "require", + arr(vec![ + ("foo/baz", s(">1, <0.5")), + ("bar/baz", s("dev-main, >0.5")), + ]), + ), + ]), + vec![ + "require.foo/baz : this version constraint cannot possibly match anything (>1, <0.5)".to_string(), + "require.bar/baz : this version constraint cannot possibly match anything (dev-main, >0.5)".to_string(), + ], + false, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ("require", arr(vec![("bar/unstable", s("0.3.0"))])), + ]), + vec![], + false, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ( + "extra", + arr(vec![( + "branch-alias", + arr(vec![("5.x-dev", s("3.1.x-dev"))]), + )]), + ), + ]), + vec!["extra.branch-alias.5.x-dev : the target branch (3.1.x-dev) is not a valid numeric alias for this version".to_string()], + false, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ( + "extra", + arr(vec![("branch-alias", arr(vec![("5.x-dev", s("3.1-dev"))]))]), + ), + ]), + vec!["extra.branch-alias.5.x-dev : the target branch (3.1-dev) is not a valid numeric alias for this version".to_string()], + false, + None, + ), + ( + config(vec![ + ("name", s("foo/bar")), + ("require", arr(vec![("Foo/Baz", s("^1.0"))])), + ]), + vec!["require.Foo/Baz is invalid, it should not contain uppercase characters. Please use foo/baz instead.".to_string()], + false, + None, + ), + ( + config(vec![("name", s("a/b")), ("license", s("XXXXX"))]), + vec![format!( + "License \"XXXXX\" is not a valid SPDX license identifier, see https://spdx.org/licenses/ if you use an open license.{}If the software is closed-source, you may use \"proprietary\" as license.", + shirabe_php_shim::PHP_EOL + )], + true, + Some(config(vec![ + ("name", s("a/b")), + ("license", list(vec![s("XXXXX")])), + ])), + ), + ( + config(vec![ + ("name", s("a/b")), + ("license", list(vec![arr(vec![("author", s("bar"))]), s("MIT")])), + ]), + vec!["License {\"author\":\"bar\"} should be a string.".to_string()], + true, + Some(config(vec![ + ("name", s("a/b")), + ("license", list(vec![s("MIT")])), + ])), + ), + ] +} + +/// ref: ValidatingArrayLoaderTest::testLoadWarnings +#[ignore] #[test] -#[ignore = "ValidatingArrayLoader -> ArrayLoader parses constraints via a look-around regex the regex crate cannot compile"] fn test_load_warnings() { - todo!() + for (cfg, mut expected_warnings, _must_check, _expected_array) in warning_provider() { + let (internal_loader, _calls) = MockLoader::new(); + let mut loader = ValidatingArrayLoader::new( + Box::new(internal_loader), + true, + None, + ValidatingArrayLoader::CHECK_ALL, + ); + loader + .load(cfg, "Composer\\Package\\CompletePackage") + .unwrap(); + let mut warnings: Vec<String> = loader.get_warnings().to_vec(); + expected_warnings.sort(); + warnings.sort(); + assert_eq!(expected_warnings, warnings); + } } +/// ref: ValidatingArrayLoaderTest::testLoadSkipsWarningDataWhenIgnoringErrors +#[ignore] #[test] -#[ignore = "ValidatingArrayLoader -> ArrayLoader parses constraints via a look-around regex the regex crate cannot compile"] fn test_load_skips_warning_data_when_ignoring_errors() { - todo!() + for (mut cfg, _expected_warnings, must_check, expected_array) in warning_provider() { + if !must_check { + assert!(true); + continue; + } + let (internal_loader, calls) = MockLoader::new(); + let expected = expected_array.unwrap_or_else(|| config(vec![("name", s("a/b"))])); + + let mut loader = ValidatingArrayLoader::new( + Box::new(internal_loader), + true, + None, + ValidatingArrayLoader::CHECK_ALL, + ); + cfg.insert("name".to_string(), s("a/b")); + loader + .load(cfg, "Composer\\Package\\CompletePackage") + .unwrap(); + + // The mock recorded exactly the (post-validation) config passed to the inner loader. + let recorded = calls.borrow(); + assert_eq!(recorded.len(), 1); + assert_eq!(recorded[0], expected); + } } diff --git a/crates/shirabe/tests/package/locker_test.rs b/crates/shirabe/tests/package/locker_test.rs index 7b5d39f..c793a8a 100644 --- a/crates/shirabe/tests/package/locker_test.rs +++ b/crates/shirabe/tests/package/locker_test.rs @@ -4,61 +4,61 @@ // mocked ProcessExecutor to drive lock read/write and freshness checks; mocking is not // available here. #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (exists/read) which is not available"] fn test_is_locked() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (exists) which is not available"] fn test_get_not_locked_packages() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (exists/read) which is not available"] fn test_get_locked_packages() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (write) which is not available"] fn test_set_lock_data() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile and PackageInterface (createPackageMock) which is not available"] fn test_lock_bad_packages() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (read) which is not available"] fn test_is_fresh() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (read) which is not available"] fn test_is_fresh_false() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (read) which is not available"] fn test_is_fresh_with_content_hash() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (read) which is not available"] fn test_is_fresh_with_content_hash_and_no_hash() { todo!() } #[test] -#[ignore = "mocks JsonFile/InstallationManager/repository/ProcessExecutor to drive Locker; mocking is not available"] +#[ignore = "requires PHPUnit mock of JsonFile (read) which is not available"] fn test_is_fresh_false_with_content_hash() { todo!() } diff --git a/crates/shirabe/tests/package/version/version_guesser_test.rs b/crates/shirabe/tests/package/version/version_guesser_test.rs index 75d4ae0..76cd672 100644 --- a/crates/shirabe/tests/package/version/version_guesser_test.rs +++ b/crates/shirabe/tests/package/version/version_guesser_test.rs @@ -1,5 +1,13 @@ //! ref: composer/tests/Composer/Test/Package/Version/VersionGuesserTest.php +use std::cell::RefCell; +use std::rc::Rc; + +use shirabe::config::Config; +use shirabe::package::version::{VersionGuesser, VersionParser}; +use shirabe::util::platform::Platform; +use shirabe::util::process_executor::ProcessExecutor; + #[allow(dead_code)] fn set_up() { // Resets GitUtil's cached `version` static via ReflectionProperty; the static is not @@ -23,94 +31,113 @@ impl Drop for TearDown { // These drive VersionGuesser with a mocked ProcessExecutor feeding git/hg command output; // mocking is not available here. +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_hg_guess_version_returns_data() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_guess_version_returns_data() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_guess_version_does_not_see_custom_default_branch_as_non_feature_branch() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_guess_version_reads_and_respects_non_feature_branches_configuration_for_arbitrary_naming() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_guess_version_reads_and_respects_non_feature_branches_configuration_for_arbitrary_naming_regex() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_guess_version_reads_and_respects_non_feature_branches_configuration_for_arbitrary_naming_when_on_non_feature_branch() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_detached_head_becomes_dev_hash() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_detached_fetch_head_becomes_dev_hash_git2() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_detached_commit_head_becomes_dev_hash_git2() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_tag_becomes_version() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_tag_becomes_pretty_version() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_invalid_tag_becomes_version() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_numeric_branches_show_nicely() { todo!() } +#[ignore = "requires getProcessExecutorMock with expects() command expectations; no ProcessExecutorMock mocking infrastructure exists"] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_remote_branches_are_selected() { todo!() } +#[ignore] #[test] -#[ignore = "mocks a ProcessExecutor feeding git/hg output to drive VersionGuesser; mocking is not available"] fn test_get_root_version_from_env() { - todo!() + // @dataProvider rootEnvVersionsProvider + let root_env_versions: Vec<(&str, &str)> = vec![ + ("1.0-dev", "1.0.x-dev"), + ("1.0.x-dev", "1.0.x-dev"), + ("1-dev", "1.x-dev"), + ("1.x-dev", "1.x-dev"), + ("1.0.0", "1.0.0"), + ]; + + for (env, expected_version) in root_env_versions { + Platform::put_env("COMPOSER_ROOT_VERSION", env); + let config = Rc::new(RefCell::new(Config::new(true, None))); + let process = Rc::new(RefCell::new(ProcessExecutor::new(None))); + let guesser = VersionGuesser::new(config, process, VersionParser::new(), None); + assert_eq!( + expected_version, + guesser.get_root_version_from_env().unwrap() + ); + Platform::clear_env("COMPOSER_ROOT_VERSION"); + } } diff --git a/crates/shirabe/tests/package/version/version_selector_test.rs b/crates/shirabe/tests/package/version/version_selector_test.rs index fd426ce..fa288cd 100644 --- a/crates/shirabe/tests/package/version/version_selector_test.rs +++ b/crates/shirabe/tests/package/version/version_selector_test.rs @@ -3,79 +3,79 @@ // VersionSelector ranks candidate packages whose versions/constraints are parsed through a // look-around regex the regex crate cannot compile; the setup also mocks a repository. #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_latest_version_is_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_latest_version_is_returned_that_matches_php_requirements() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_latest_version_is_returned_that_matches_ext_requirements() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_latest_version_is_returned_that_matches_platform_ext() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_latest_version_is_returned_that_matches_composer_requirements() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_most_stable_version_is_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages with willReturnOnConsecutiveCalls; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_most_stable_version_is_returned_regardless_of_order() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_highest_version_is_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_highest_version_matching_stability_is_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_most_stable_unstable_version_is_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return fixed package objects; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_default_branch_alias_is_never_returned() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "requires mocking RepositorySet::find_packages to return an empty list; RepositorySet is a concrete struct with no injectable/overridable find_packages"] fn test_false_returned_on_no_packages() { todo!() } #[test] -#[ignore = "not yet ported (VersionSelector over a mocked repository; constraint parsing uses a look-around regex)"] +#[ignore = "branch-alias cases need Package::set_extra, exposed only on RootPackageHandle, not on the PackageHandle/PackageInterfaceHandle passed to find_recommended_require_version"] fn test_find_recommended_require_version() { todo!() } |
