diff options
Diffstat (limited to 'crates/shirabe/tests/repository/platform_repository_test.rs')
| -rw-r--r-- | crates/shirabe/tests/repository/platform_repository_test.rs | 1844 |
1 files changed, 1830 insertions, 14 deletions
diff --git a/crates/shirabe/tests/repository/platform_repository_test.rs b/crates/shirabe/tests/repository/platform_repository_test.rs index c85af4b..7d2a328 100644 --- a/crates/shirabe/tests/repository/platform_repository_test.rs +++ b/crates/shirabe/tests/repository/platform_repository_test.rs @@ -1,38 +1,1854 @@ //! ref: composer/tests/Composer/Test/Repository/PlatformRepositoryTest.php -use shirabe::repository::PlatformRepository; +use indexmap::IndexMap; +use mockall::predicate::eq; +use shirabe::package::{BasePackageHandle, Link}; +use shirabe::platform::{HhvmDetectorInterface, RuntimeInterface}; +use shirabe::repository::{ + FindPackageConstraint, PlatformRepository, RepositoryInterface, SEARCH_NAME, +}; +use shirabe_php_shim::PhpMixed; +use shirabe_semver::constraint::SimpleConstraint; + +// The Runtime/HhvmDetector seams are concrete structs in PHP; the tests mock them +// directly. +mockall::mock! { + pub Runtime {} + impl RuntimeInterface for Runtime { + fn has_constant(&self, constant_name: &str, class: Option<String>) -> bool; + fn get_constant(&self, constant_name: &str, class: Option<String>) -> PhpMixed; + fn invoke(&self, callable: PhpMixed, arguments: Vec<PhpMixed>) -> PhpMixed; + fn has_class(&self, class: &str) -> bool; + fn construct(&self, class: &str, arguments: Vec<PhpMixed>) -> anyhow::Result<PhpMixed>; + fn get_extensions(&self) -> Vec<String>; + fn get_extension_version(&self, extension: &str) -> String; + fn get_extension_info(&self, extension: &str) -> anyhow::Result<String>; + } +} + +mockall::mock! { + pub HhvmDetector {} + impl HhvmDetectorInterface for HhvmDetector { + fn reset(&self); + fn get_version(&mut self) -> Option<String>; + } +} + +// The seam traits require `Debug` (so `PlatformRepository` can derive it); mockall does +// not generate it for mocks. +impl std::fmt::Debug for MockRuntime { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("MockRuntime") + } +} + +impl std::fmt::Debug for MockHhvmDetector { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("MockHhvmDetector") + } +} + +/// PHP: ltrim($class.'::'.$constant, ':') +fn constant_key(constant_name: &str, class: Option<&str>) -> String { + format!("{}::{}", class.unwrap_or(""), constant_name) + .trim_start_matches(':') + .to_string() +} + +// All five tests below are fully ported but kept `#[ignore]`: every code path through +// `PlatformRepository::initialize()` reaches `XdebugHandler::get_skipped_version()`, +// which is an unrelated `todo!()` stub in `shirabe-external-packages`, so the tests +// panic before any assertion (Phase D: visible failure is acceptable, and the category +// brief permits leaving confidently-panicking tests ignored). The seam/mocks are in +// place so they will execute once that stub lands. -// These probe runtime/extension/library versions and assert the synthesized platform -// packages; the detection mocks ProcessExecutor/Runtime/HhvmDetector and the package -// versions are parsed through a look-around regex. #[test] -#[ignore = "requires PHPUnit getMockBuilder mock of Composer\\Platform\\HhvmDetector (willReturn getVersion); Runtime/HhvmDetector are concrete structs with no mock/override mechanism"] +#[ignore = "seam + HhvmDetector mock ported; execution panics at the unrelated \ + XdebugHandler::get_skipped_version() todo!() during initialize(), and the \ + default (real) Runtime also relies on PHP-runtime shim todo!()s"] fn test_hhvm_package() { - todo!() + let mut hhvm_detector = MockHhvmDetector::new(); + hhvm_detector + .expect_get_version() + .times(..) + .returning(|| Some("2.1.0".to_string())); + + let mut platform_repository = + PlatformRepository::new4(vec![], IndexMap::new(), None, Some(Box::new(hhvm_detector))) + .unwrap(); + + let hhvm = platform_repository + .find_package("hhvm", FindPackageConstraint::String("*".to_string())) + .unwrap(); + assert!(hhvm.is_some(), "hhvm found"); + + assert_eq!("2.1.0", hhvm.unwrap().get_pretty_version()); +} + +fn php_flavor_test_cases() -> Vec<( + IndexMap<String, PhpMixed>, + Vec<(&'static str, &'static str)>, + Vec<(PhpMixed, Vec<PhpMixed>, PhpMixed)>, +)> { + let ubuntu = "7.2.31-1+ubuntu16.04.1+deb.sury.org+1"; + let s = |v: &str| PhpMixed::String(v.to_string()); + + vec![ + ( + IndexMap::from([("PHP_VERSION".to_string(), s("7.1.33"))]), + vec![("php", "7.1.33")], + vec![], + ), + ( + IndexMap::from([ + ("PHP_VERSION".to_string(), s(ubuntu)), + ("PHP_DEBUG".to_string(), PhpMixed::Bool(true)), + ]), + vec![("php", "7.2.31"), ("php-debug", "7.2.31")], + vec![], + ), + ( + IndexMap::from([ + ("PHP_VERSION".to_string(), s(ubuntu)), + ("PHP_ZTS".to_string(), PhpMixed::Bool(true)), + ]), + vec![("php", "7.2.31"), ("php-zts", "7.2.31")], + vec![], + ), + ( + IndexMap::from([ + ("PHP_VERSION".to_string(), s(ubuntu)), + ("PHP_INT_SIZE".to_string(), PhpMixed::Int(8)), + ]), + vec![("php", "7.2.31"), ("php-64bit", "7.2.31")], + vec![], + ), + ( + IndexMap::from([ + ("PHP_VERSION".to_string(), s(ubuntu)), + ("AF_INET6".to_string(), PhpMixed::Int(30)), + ]), + vec![("php", "7.2.31"), ("php-ipv6", "7.2.31")], + vec![], + ), + ( + IndexMap::from([("PHP_VERSION".to_string(), s(ubuntu))]), + vec![("php", "7.2.31"), ("php-ipv6", "7.2.31")], + vec![(s("inet_pton"), vec![s("::")], s(""))], + ), + ( + IndexMap::from([("PHP_VERSION".to_string(), s(ubuntu))]), + vec![("php", "7.2.31")], + vec![(s("inet_pton"), vec![s("::")], PhpMixed::Bool(false))], + ), + ] } #[test] -#[ignore = "requires PHPUnit getMockBuilder mock of Composer\\Platform\\Runtime (willReturnCallback/willReturnMap for hasConstant/getConstant/invoke/getExtensions); no mocking framework and Runtime is a concrete struct"] +#[ignore = "seam + Runtime mock ported (hasConstant/getConstant/invoke/getExtensions); \ + execution panics at the unrelated XdebugHandler::get_skipped_version() todo!()"] fn test_php_version() { - todo!() + for (constants, packages, functions) in php_flavor_test_cases() { + let constants_has = constants.clone(); + let constants_get = constants.clone(); + + let mut runtime = MockRuntime::new(); + runtime + .expect_get_extensions() + .times(..) + .returning(Vec::new); + runtime + .expect_has_constant() + .times(..) + .returning(move |constant, class| { + constants_has.contains_key(&constant_key(constant, class.as_deref())) + }); + runtime + .expect_get_constant() + .times(..) + .returning(move |constant, class| { + constants_get + .get(&constant_key(constant, class.as_deref())) + .cloned() + .unwrap_or(PhpMixed::Null) + }); + runtime + .expect_invoke() + .times(..) + .returning(move |callable, arguments| { + for (c, a, ret) in &functions { + if *c == callable && *a == arguments { + return ret.clone(); + } + } + PhpMixed::Null + }); + + let mut repository = + PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None) + .unwrap(); + + for (package_name, version) in packages { + let package = repository + .find_package(package_name, FindPackageConstraint::String("*".to_string())) + .unwrap(); + assert!( + package.is_some(), + "Expected to find package \"{}\"", + package_name + ); + assert_eq!( + version, + package.unwrap().get_pretty_version(), + "Expected package \"{}\" version to be {}", + package_name, + version + ); + } + } } #[test] -#[ignore = "requires PHPUnit getMockBuilder mock of Composer\\Platform\\Runtime (expects(once)/with/willReturn for invoke, willReturnCallback for getConstant); no mocking framework"] +#[ignore = "seam + Runtime mock ported (invoke once + getConstant callback); \ + execution panics at the unrelated XdebugHandler::get_skipped_version() todo!()"] fn test_inet_pton_regression() { - todo!() + let mut runtime = MockRuntime::new(); + // PHP: ->expects(self::once())->method('invoke')->with('inet_pton', ['::'])->willReturn(false). + runtime + .expect_invoke() + .with( + eq(PhpMixed::String("inet_pton".to_string())), + eq(vec![PhpMixed::String("::".to_string())]), + ) + .times(1) + .returning(|_callable, _arguments| PhpMixed::Bool(false)); + // suppressing PHP_ZTS & AF_INET6 + runtime + .expect_has_constant() + .times(..) + .returning(|_, _| false); + + let constants: IndexMap<String, PhpMixed> = IndexMap::from([ + ( + "PHP_VERSION".to_string(), + PhpMixed::String("7.0.0".to_string()), + ), + ("PHP_DEBUG".to_string(), PhpMixed::Bool(false)), + ]); + runtime + .expect_get_constant() + .times(..) + .returning(move |constant, class| { + constants + .get(&constant_key(constant, class.as_deref())) + .cloned() + .unwrap_or(PhpMixed::Null) + }); + runtime + .expect_get_extensions() + .times(..) + .returning(Vec::new); + + let mut repository = + PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None).unwrap(); + let package = repository + .find_package("php-ipv6", FindPackageConstraint::String("*".to_string())) + .unwrap(); + assert!(package.is_none()); +} + +enum Exp { + Version(&'static str), + Missing, + Full( + &'static str, + &'static [&'static str], + &'static [&'static str], + ), +} + +impl Exp { + fn parts( + &self, + ) -> ( + Option<&'static str>, + &'static [&'static str], + &'static [&'static str], + ) { + match self { + Exp::Version(v) => (Some(*v), &[], &[]), + Exp::Missing => (None, &[], &[]), + Exp::Full(v, replaces, provides) => (Some(*v), *replaces, *provides), + } + } +} + +#[derive(Clone)] +struct ClassDef { + class: &'static str, + construct: Option<(Vec<PhpMixed>, PhpMixed)>, +} + +struct LibCase { + extensions: Vec<&'static str>, + info: Option<&'static str>, + expectations: Vec<(&'static str, Exp)>, + functions: Vec<(PhpMixed, Vec<PhpMixed>, PhpMixed)>, + constants: Vec<(&'static str, Option<&'static str>, PhpMixed)>, + class_definitions: Vec<ClassDef>, +} + +// Port of PlatformRepositoryTest::provideLibraryTestCases (all 59 datasets). The intl and +// imagick cases reference the PHP ResourceBundleStub/ImagickStub objects (modelled as +// PhpMixed::Object holding the version they would expose); their runtime read paths are +// still `TODO(plugin)` stubs, but the data is ported faithfully. +fn library_test_cases() -> Vec<LibCase> { + let s = |v: &str| PhpMixed::String(v.to_string()); + let curl = |v: &str| -> (PhpMixed, Vec<PhpMixed>, PhpMixed) { + ( + s("curl_version"), + vec![], + PhpMixed::Array(IndexMap::from([("version".to_string(), s(v))])), + ) + }; + + vec![ + LibCase { + extensions: vec!["amqp"], + info: Some( + " + +amqp + +Version => 1.9.4 +Revision => release +Compiled => Nov 19 2019 @ 08:44:26 +AMQP protocol version => 0-9-1 +librabbitmq version => 0.9.0 +Default max channels per connection => 256 +Default max frame size => 131072 +Default heartbeats interval => 0", + ), + expectations: vec![ + ("lib-amqp-protocol", Exp::Version("0.9.1")), + ("lib-amqp-librabbitmq", Exp::Version("0.9.0")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["bz2"], + info: Some( + " +bz2 + +BZip2 Support => Enabled +Stream Wrapper support => compress.bzip2:// +Stream Filter support => bzip2.decompress, bzip2.compress +BZip2 Version => 1.0.5, 6-Sept-2010", + ), + expectations: vec![("lib-bz2", Exp::Version("1.0.5"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 7.38.0 +Age => 3 +Features +AsynchDNS => Yes +CharConv => No +Debug => No +GSS-Negotiate => No +IDN => Yes +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => Yes +SSL => Yes +SSPI => No +TLS-SRP => Yes +HTTP2 => No +GSSAPI => Yes +Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, scp, sftp, smtp, smtps, telnet, tftp +Host => x86_64-pc-linux-gnu +SSL Version => OpenSSL/1.0.1t +ZLib Version => 1.2.8 +libSSH Version => libssh2/1.4.3 + +Directive => Local Value => Master Value +curl.cainfo => no value => no value", + ), + expectations: vec![ + ("lib-curl", Exp::Version("2.0.0")), + ("lib-curl-openssl", Exp::Version("1.0.1.20")), + ("lib-curl-zlib", Exp::Version("1.2.8")), + ("lib-curl-libssh2", Exp::Version("1.4.3")), + ], + functions: vec![curl("2.0.0")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 7.38.0 +Age => 3 +Features +AsynchDNS => Yes +CharConv => No +Debug => No +GSS-Negotiate => No +IDN => Yes +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => Yes +SSL => Yes +SSPI => No +TLS-SRP => Yes +HTTP2 => No +GSSAPI => Yes +Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, scp, sftp, smtp, smtps, telnet, tftp +Host => x86_64-pc-linux-gnu +SSL Version => OpenSSL/1.0.1t-fips +ZLib Version => 1.2.8 +libSSH Version => libssh2/1.4.3 + +Directive => Local Value => Master Value +curl.cainfo => no value => no value", + ), + expectations: vec![ + ("lib-curl", Exp::Version("2.0.0")), + ( + "lib-curl-openssl-fips", + Exp::Full("1.0.1.20", &[], &["lib-curl-openssl"]), + ), + ("lib-curl-zlib", Exp::Version("1.2.8")), + ("lib-curl-libssh2", Exp::Version("1.4.3")), + ], + functions: vec![curl("2.0.0")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 7.22.0 +Age => 3 +Features +AsynchDNS => No +CharConv => No +Debug => No +GSS-Negotiate => Yes +IDN => Yes +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => No +SSL => Yes +SSPI => No +TLS-SRP => Yes +Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, pop3, pop3s, rtmp, rtsp, smtp, smtps, telnet, tftp +Host => x86_64-pc-linux-gnu +SSL Version => GnuTLS/2.12.14 +ZLib Version => 1.2.3.4", + ), + expectations: vec![ + ("lib-curl", Exp::Version("7.22.0")), + ("lib-curl-zlib", Exp::Version("1.2.3.4")), + ( + "lib-curl-gnutls", + Exp::Full("2.12.14", &["lib-curl-openssl"], &[]), + ), + ], + functions: vec![curl("7.22.0")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 7.24.0 +Age => 3 +Features +AsynchDNS => Yes +Debug => No +GSS-Negotiate => Yes +IDN => Yes +IPv6 => Yes +Largefile => Yes +NTLM => Yes +SPNEGO => No +SSL => Yes +SSPI => No +krb4 => No +libz => Yes +CharConv => No +Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtsp, scp, sftp, smtp, smtps, telnet, tftp +Host => x86_64-redhat-linux-gnu +SSL Version => NSS/3.13.3.0 +ZLib Version => 1.2.5 +libSSH Version => libssh2/1.4.1", + ), + expectations: vec![ + ("lib-curl", Exp::Version("7.24.0")), + ( + "lib-curl-nss", + Exp::Full("3.13.3.0", &["lib-curl-openssl"], &[]), + ), + ("lib-curl-zlib", Exp::Version("1.2.5")), + ("lib-curl-libssh2", Exp::Version("1.4.1")), + ], + functions: vec![curl("7.24.0")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 7.68.0 +Age => 5 +Features +AsynchDNS => Yes +CharConv => No +Debug => No +GSS-Negotiate => No +IDN => Yes +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => Yes +SSL => Yes +SSPI => No +TLS-SRP => Yes +HTTP2 => Yes +GSSAPI => Yes +KERBEROS5 => Yes +UNIX_SOCKETS => Yes +PSL => Yes +HTTPS_PROXY => Yes +MULTI_SSL => No +BROTLI => Yes +Protocols => dict, file, ftp, ftps, gopher, http, https, imap, imaps, ldap, ldaps, pop3, pop3s, rtmp, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp +Host => x86_64-pc-linux-gnu +SSL Version => OpenSSL/1.1.1g +ZLib Version => 1.2.11 +libSSH Version => libssh/0.9.3/openssl/zlib", + ), + expectations: vec![ + ("lib-curl", Exp::Version("7.68.0")), + ("lib-curl-openssl", Exp::Version("1.1.1.7")), + ("lib-curl-zlib", Exp::Version("1.2.11")), + ("lib-curl-libssh", Exp::Version("0.9.3")), + ], + functions: vec![curl("7.68.0")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 8.1.2 +Age => 10 +Features +AsynchDNS => Yes +CharConv => No +Debug => No +GSS-Negotiate => No +IDN => Yes +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => Yes +SSL => Yes +SSPI => No +TLS-SRP => Yes +HTTP2 => Yes +GSSAPI => Yes +KERBEROS5 => Yes +UNIX_SOCKETS => Yes +PSL => No +HTTPS_PROXY => Yes +MULTI_SSL => Yes +BROTLI => Yes +ALTSVC => Yes +HTTP3 => No +UNICODE => No +ZSTD => Yes +HSTS => Yes +GSASL => No +Protocols => dict, file, ftp, ftps, gopher, gophers, http, https, imap, imaps, ldap, ldaps, mqtt, pop3, pop3s, rtmp, rtmpe, rtmps, rtmpt, rtmpte, rtmpts, rtsp, scp, sftp, smb, smbs, smtp, smtps, telnet, tftp +Host => aarch64-apple-darwin22.4.0 +SSL Version => (SecureTransport) OpenSSL/3.1.1 +ZLib Version => 1.2.11 +libSSH Version => libssh2/1.11.0", + ), + expectations: vec![ + ("lib-curl", Exp::Version("8.1.2")), + ( + "lib-curl-securetransport", + Exp::Full("3.1.1", &["lib-curl-openssl"], &[]), + ), + ("lib-curl-zlib", Exp::Version("1.2.11")), + ("lib-curl-libssh2", Exp::Version("1.11.0")), + ], + functions: vec![curl("8.1.2")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["curl"], + info: Some( + " +curl + +cURL support => enabled +cURL Information => 8.1.2 +Age => 10 +Features +AsynchDNS => Yes +CharConv => No +Debug => No +GSS-Negotiate => No +IDN => No +IPv6 => Yes +krb4 => No +Largefile => Yes +libz => Yes +NTLM => Yes +NTLMWB => Yes +SPNEGO => Yes +SSL => Yes +SSPI => No +TLS-SRP => No +HTTP2 => Yes +GSSAPI => Yes +KERBEROS5 => Yes +UNIX_SOCKETS => Yes +PSL => No +HTTPS_PROXY => Yes +MULTI_SSL => Yes +BROTLI => No +ALTSVC => Yes +HTTP3 => No +UNICODE => No +ZSTD => No +HSTS => Yes +GSASL => No +Protocols => dict, file, ftp, ftps, gopher, gophers, http, https, imap, imaps, ldap, ldaps, mqtt, pop3, pop3s, rtsp, smb, smbs, smtp, smtps, telnet, tftp +Host => x86_64-apple-darwin20.0 +SSL Version => (SecureTransport) LibreSSL/2.8.3 +ZLib Version => 1.2.11", + ), + expectations: vec![ + ("lib-curl", Exp::Version("8.1.2")), + ( + "lib-curl-securetransport", + Exp::Full("2.8.3", &["lib-curl-libressl"], &[]), + ), + ("lib-curl-zlib", Exp::Version("1.2.11")), + ], + functions: vec![curl("8.1.2")], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["date"], + info: Some( + " +date + +date/time support => enabled +timelib version => 2018.03 +\"Olson\" Timezone Database Version => 2020.1 +Timezone Database => external +Default timezone => Europe/Berlin", + ), + expectations: vec![ + ("lib-date-timelib", Exp::Version("2018.03")), + ("lib-date-zoneinfo", Exp::Version("2020.1")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["date"], + info: Some( + " +date + +date/time support => enabled +\"Olson\" Timezone Database Version => 2013.2 +Timezone Database => internal +Default timezone => Europe/Amsterdam", + ), + expectations: vec![ + ("lib-date-zoneinfo", Exp::Version("2013.2")), + ("lib-date-timelib", Exp::Missing), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["date", "timezonedb"], + info: Some( + " +date + +date/time support => enabled +\"Olson\" Timezone Database Version => 2020.1 +Timezone Database => internal +Default timezone => UTC", + ), + expectations: vec![("lib-date-zoneinfo", Exp::Version("2020.1"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["date", "timezonedb"], + info: Some( + " +date + +date/time support => enabled +\"Olson\" Timezone Database Version => 2020.1 +Timezone Database => external +Default timezone => UTC", + ), + expectations: vec![( + "lib-timezonedb-zoneinfo", + Exp::Full("2020.1", &["lib-date-zoneinfo"], &[]), + )], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["date"], + info: Some( + " + + +date/time support => enabled +timelib version => 2018.03 +\"Olson\" Timezone Database Version => 0.system +Timezone Database => internal +Default timezone => Europe/Berlin + +Directive => Local Value => Master Value +date.timezone => no value => no value +date.default_latitude => 31.7667 => 31.7667 +date.default_longitude => 35.2333 => 35.2333 +date.sunset_zenith => 90.583333 => 90.583333 +date.sunrise_zenith => 90.583333 => 90.583333", + ), + expectations: vec![ + ("lib-date-zoneinfo", Exp::Version("0")), + ("lib-date-timelib", Exp::Version("2018.03")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["fileinfo"], + info: Some( + " +fileinfo + +fileinfo support => enabled +libmagic => 537", + ), + expectations: vec![("lib-fileinfo-libmagic", Exp::Version("537"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["gd"], + info: Some( + " +gd + +GD Support => enabled +GD Version => bundled (2.1.0 compatible) +FreeType Support => enabled +FreeType Linkage => with freetype +FreeType Version => 2.10.0 +GIF Read Support => enabled +GIF Create Support => enabled +JPEG Support => enabled +libJPEG Version => 9 compatible +PNG Support => enabled +libPNG Version => 1.6.34 +WBMP Support => enabled +XBM Support => enabled +WebP Support => enabled + +Directive => Local Value => Master Value +gd.jpeg_ignore_warning => 1 => 1", + ), + expectations: vec![ + ("lib-gd", Exp::Version("1.2.3")), + ("lib-gd-freetype", Exp::Version("2.10.0")), + ("lib-gd-libjpeg", Exp::Version("9.0")), + ("lib-gd-libpng", Exp::Version("1.6.34")), + ], + functions: vec![], + constants: vec![("GD_VERSION", None, s("1.2.3"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["gd"], + info: Some( + " +gd + +GD Support => enabled +GD Version => bundled (2.1.0 compatible) +FreeType Support => enabled +FreeType Linkage => with freetype +FreeType Version => 2.9.1 +GIF Read Support => enabled +GIF Create Support => enabled +JPEG Support => enabled +libJPEG Version => 6b +PNG Support => enabled +libPNG Version => 1.6.35 +WBMP Support => enabled +XBM Support => enabled +WebP Support => enabled + +Directive => Local Value => Master Value +gd.jpeg_ignore_warning => 1 => 1", + ), + expectations: vec![ + ("lib-gd", Exp::Version("1.2.3")), + ("lib-gd-freetype", Exp::Version("2.9.1")), + ("lib-gd-libjpeg", Exp::Version("6.2")), + ("lib-gd-libpng", Exp::Version("1.6.35")), + ], + functions: vec![], + constants: vec![("GD_VERSION", None, s("1.2.3"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["gd"], + info: Some( + " +gd + +GD Support => enabled +GD headers Version => 2.2.5 +GD library Version => 2.2.5 +FreeType Support => enabled +FreeType Linkage => with freetype +FreeType Version => 2.6.3 +GIF Read Support => enabled +GIF Create Support => enabled +JPEG Support => enabled +libJPEG Version => 6b +PNG Support => enabled +libPNG Version => 1.6.28 +WBMP Support => enabled +XPM Support => enabled +libXpm Version => 30411 +XBM Support => enabled +WebP Support => enabled + +Directive => Local Value => Master Value +gd.jpeg_ignore_warning => 1 => 1", + ), + expectations: vec![ + ("lib-gd", Exp::Version("2.2.5")), + ("lib-gd-freetype", Exp::Version("2.6.3")), + ("lib-gd-libjpeg", Exp::Version("6.2")), + ("lib-gd-libpng", Exp::Version("1.6.28")), + ("lib-gd-libxpm", Exp::Version("3.4.11")), + ], + functions: vec![], + constants: vec![("GD_VERSION", None, s("2.2.5"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["iconv"], + info: None, + expectations: vec![("lib-iconv", Exp::Version("1.2.4"))], + functions: vec![], + constants: vec![("ICONV_VERSION", None, s("1.2.4"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["gmp"], + info: None, + expectations: vec![("lib-gmp", Exp::Version("6.1.0"))], + functions: vec![], + constants: vec![("GMP_VERSION", None, s("6.1.0"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["intl"], + info: Some( + " +intl + +Internationalization support => enabled +ICU version => 57.1 +ICU Data version => 57.1 +ICU TZData version => 2016b +ICU Unicode version => 8.0 + +Directive => Local Value => Master Value +intl.default_locale => no value => no value +intl.error_level => 0 => 0 +intl.use_exceptions => 0 => 0", + ), + expectations: vec![ + ("lib-icu", Exp::Version("100")), + ("lib-icu-cldr", Exp::Version("32.0.1")), + ("lib-icu-unicode", Exp::Version("7.0.0")), + ("lib-icu-zoneinfo", Exp::Version("2016.2")), + ], + functions: vec![ + ( + PhpMixed::List(vec![s("ResourceBundle"), s("create")]), + vec![s("root"), s("ICUDATA"), PhpMixed::Bool(false)], + PhpMixed::Object(IndexMap::from([("Version".to_string(), s("32.0.1"))])), + ), + ( + PhpMixed::List(vec![s("IntlChar"), s("getUnicodeVersion")]), + vec![], + PhpMixed::List(vec![ + PhpMixed::Int(7), + PhpMixed::Int(0), + PhpMixed::Int(0), + PhpMixed::Int(0), + ]), + ), + ], + constants: vec![("INTL_ICU_VERSION", None, s("100"))], + class_definitions: vec![ + ClassDef { + class: "ResourceBundle", + construct: None, + }, + ClassDef { + class: "IntlChar", + construct: None, + }, + ], + }, + LibCase { + extensions: vec!["intl"], + info: Some( + " +intl + +Internationalization support => enabled +version => 1.1.0 +ICU version => 57.1 +ICU Data version => 57.1", + ), + expectations: vec![("lib-icu", Exp::Version("57.1"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["imagick"], + info: None, + expectations: vec![( + "lib-imagick-imagemagick", + Exp::Full("6.2.9", &["lib-imagick"], &[]), + )], + functions: vec![], + constants: vec![], + class_definitions: vec![ClassDef { + class: "Imagick", + construct: Some(( + vec![], + PhpMixed::Object(IndexMap::from([( + "versionString".to_string(), + s("ImageMagick 6.2.9 Q16 x86_64 2018-05-18 http://www.imagemagick.org"), + )])), + )), + }], + }, + LibCase { + extensions: vec!["imagick"], + info: None, + expectations: vec![( + "lib-imagick-imagemagick", + Exp::Full("7.0.8.34", &["lib-imagick"], &[]), + )], + functions: vec![], + constants: vec![], + class_definitions: vec![ClassDef { + class: "Imagick", + construct: Some(( + vec![], + PhpMixed::Object(IndexMap::from([( + "versionString".to_string(), + s("ImageMagick 7.0.8-34 Q16 x86_64 2019-03-23 https://imagemagick.org"), + )])), + )), + }], + }, + LibCase { + extensions: vec!["ldap"], + info: Some( + " +ldap + +LDAP Support => enabled +RCS Version => $Id: 5f1913de8e05a346da913956f81e0c0d8991c7cb $ +Total Links => 0/unlimited +API Version => 3001 +Vendor Name => OpenLDAP +Vendor Version => 20450 +SASL Support => Enabled + +Directive => Local Value => Master Value +ldap.max_links => Unlimited => Unlimited", + ), + expectations: vec![("lib-ldap-openldap", Exp::Version("2.4.50"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["libxml"], + info: None, + expectations: vec![("lib-libxml", Exp::Version("2.1.5"))], + functions: vec![], + constants: vec![("LIBXML_DOTTED_VERSION", None, s("2.1.5"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["libxml", "dom", "simplexml", "xml", "xmlreader", "xmlwriter"], + info: None, + expectations: vec![( + "lib-libxml", + Exp::Full( + "2.1.5", + &[], + &[ + "lib-dom-libxml", + "lib-simplexml-libxml", + "lib-xml-libxml", + "lib-xmlreader-libxml", + "lib-xmlwriter-libxml", + ], + ), + )], + functions: vec![], + constants: vec![("LIBXML_DOTTED_VERSION", None, s("2.1.5"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["mbstring"], + info: Some( + " +mbstring + +Multibyte Support => enabled +Multibyte string engine => libmbfl +HTTP input encoding translation => disabled +libmbfl version => 1.3.2 + +mbstring extension makes use of \"streamable kanji code filter and converter\", which is distributed under the GNU Lesser General Public License version 2.1. + +Multibyte (japanese) regex support => enabled +Multibyte regex (oniguruma) version => 6.1.3", + ), + expectations: vec![ + ("lib-mbstring-libmbfl", Exp::Version("1.3.2")), + ("lib-mbstring-oniguruma", Exp::Version("7.0.0")), + ], + functions: vec![], + constants: vec![("MB_ONIGURUMA_VERSION", None, s("7.0.0"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["mbstring"], + info: Some( + " +mbstring + +Multibyte Support => enabled +Multibyte string engine => libmbfl +HTTP input encoding translation => disabled +libmbfl version => 1.3.2 + +mbstring extension makes use of \"streamable kanji code filter and converter\", which is distributed under the GNU Lesser General Public License version 2.1. + +Multibyte (japanese) regex support => enabled +Multibyte regex (oniguruma) version => 6.1.3", + ), + expectations: vec![ + ("lib-mbstring-libmbfl", Exp::Version("1.3.2")), + ("lib-mbstring-oniguruma", Exp::Version("6.1.3")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["mbstring"], + info: Some( + " +mbstring + +Multibyte Support => enabled +Multibyte string engine => libmbfl +HTTP input encoding translation => disabled +libmbfl version => 1.3.2 +oniguruma version => 6.9.4 + +mbstring extension makes use of \"streamable kanji code filter and converter\", which is distributed under the GNU Lesser General Public License version 2.1. + +Multibyte (japanese) regex support => enabled +Multibyte regex (oniguruma) backtrack check => On", + ), + expectations: vec![ + ("lib-mbstring-libmbfl", Exp::Version("1.3.2")), + ("lib-mbstring-oniguruma", Exp::Version("6.9.4")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["memcached"], + info: Some( + " +memcached + +memcached support => enabled +Version => 3.1.5 +libmemcached version => 1.0.18 +SASL support => yes +Session support => yes +igbinary support => yes +json support => yes +msgpack support => yes", + ), + expectations: vec![("lib-memcached-libmemcached", Exp::Version("1.0.18"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7"))], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("OpenSSL 1.1.1g 21 Apr 2020"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7"))], + functions: vec![], + constants: vec![( + "OPENSSL_VERSION_TEXT", + None, + s("OpenSSL 1.1.1g-freebsd 21 Apr 2020"), + )], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("0.9.8.33"))], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("OpenSSL 0.9.8zg 21 Apr 2020"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7-alpha1"))], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("OpenSSL 1.1.1g-pre1 21 Apr 2020"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7-beta2"))], + functions: vec![], + constants: vec![( + "OPENSSL_VERSION_TEXT", + None, + s("OpenSSL 1.1.1g-beta2 21 Apr 2020"), + )], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7-alpha4"))], + functions: vec![], + constants: vec![( + "OPENSSL_VERSION_TEXT", + None, + s("OpenSSL 1.1.1g-alpha4 21 Apr 2020"), + )], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("1.1.1.7-rc2"))], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("OpenSSL 1.1.1g-rc2 21 Apr 2020"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![( + "lib-openssl-fips", + Exp::Full("1.1.1.7", &[], &["lib-openssl"]), + )], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("OpenSSL 1.1.1g-fips 21 Apr 2020"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["openssl"], + info: None, + expectations: vec![("lib-openssl", Exp::Version("2.0.1.0"))], + functions: vec![], + constants: vec![("OPENSSL_VERSION_TEXT", None, s("LibreSSL 2.0.1"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["mysqlnd"], + info: Some( + " + mysqlnd + +mysqlnd => enabled +Version => mysqlnd 5.0.11-dev - 20150407 - $Id: 38fea24f2847fa7519001be390c98ae0acafe387 $ +Compression => supported +core SSL => supported +extended SSL => supported +Command buffer size => 4096 +Read buffer size => 32768 +Read timeout => 31536000 +Collecting statistics => Yes +Collecting memory statistics => Yes +Tracing => n/a +Loaded plugins => mysqlnd,debug_trace,auth_plugin_mysql_native_password,auth_plugin_mysql_clear_password,auth_plugin_sha256_password +API Extensions => pdo_mysql,mysqli", + ), + expectations: vec![("lib-mysqlnd-mysqlnd", Exp::Version("5.0.11-dev"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pdo_mysql"], + info: Some( + " + pdo_mysql + +PDO Driver for MySQL => enabled +Client API version => mysqlnd 5.0.10-dev - 20150407 - $Id: 38fea24f2847fa7519001be390c98ae0acafe387 $ + +Directive => Local Value => Master Value +pdo_mysql.default_socket => /tmp/mysql.sock => /tmp/mysql.sock", + ), + expectations: vec![("lib-pdo_mysql-mysqlnd", Exp::Version("5.0.10-dev"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["mongodb"], + info: Some( + " + mongodb + +MongoDB support => enabled +MongoDB extension version => 1.6.1 +MongoDB extension stability => stable +libbson bundled version => 1.15.2 +libmongoc bundled version => 1.15.2 +libmongoc SSL => enabled +libmongoc SSL library => OpenSSL +libmongoc crypto => enabled +libmongoc crypto library => libcrypto +libmongoc crypto system profile => disabled +libmongoc SASL => disabled +libmongoc ICU => enabled +libmongoc compression => enabled +libmongoc compression snappy => disabled +libmongoc compression zlib => enabled + +Directive => Local Value => Master Value +mongodb.debug => no value => no value", + ), + expectations: vec![ + ("lib-mongodb-libmongoc", Exp::Version("1.15.2")), + ("lib-mongodb-libbson", Exp::Version("1.15.2")), + ], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pcre"], + info: Some( + " +pcre + +PCRE (Perl Compatible Regular Expressions) Support => enabled +PCRE Library Version => 10.33 2019-04-16 +PCRE Unicode Version => 11.0.0 +PCRE JIT Support => enabled +PCRE JIT Target => x86 64bit (little endian + unaligned)", + ), + expectations: vec![ + ("lib-pcre", Exp::Version("10.33")), + ("lib-pcre-unicode", Exp::Version("11.0.0")), + ], + functions: vec![], + constants: vec![("PCRE_VERSION", None, s("10.33 2019-04-16"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pcre"], + info: Some( + " +pcre + +PCRE (Perl Compatible Regular Expressions) Support => enabled +PCRE Library Version => 8.38 2015-11-23 + +Directive => Local Value => Master Value +pcre.backtrack_limit => 1000000 => 1000000 +pcre.recursion_limit => 100000 => 100000 + ", + ), + expectations: vec![("lib-pcre", Exp::Version("8.38"))], + functions: vec![], + constants: vec![("PCRE_VERSION", None, s("8.38 2015-11-23"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pgsql"], + info: Some( + " +pgsql + +PostgreSQL Support => enabled +PostgreSQL(libpq) Version => 12.2 +PostgreSQL(libpq) => PostgreSQL 12.3 on x86_64-apple-darwin18.7.0, compiled by Apple clang version 11.0.0 (clang-1100.0.33.17), 64-bit +Multibyte character support => enabled +SSL support => enabled +Active Persistent Links => 0 +Active Links => 0 + +Directive => Local Value => Master Value +pgsql.allow_persistent => On => On +pgsql.max_persistent => Unlimited => Unlimited +pgsql.max_links => Unlimited => Unlimited +pgsql.auto_reset_persistent => Off => Off +pgsql.ignore_notice => Off => Off +pgsql.log_notice => Off => Off", + ), + expectations: vec![("lib-pgsql-libpq", Exp::Version("12.2"))], + functions: vec![], + constants: vec![("PGSQL_LIBPQ_VERSION", None, s("12.2"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pdo_pgsql"], + info: Some( + " + pdo_pgsql + +PDO Driver for PostgreSQL => enabled +PostgreSQL(libpq) Version => 12.1 +Module version => 7.1.33 +Revision => $Id: 9c5f356c77143981d2e905e276e439501fe0f419 $", + ), + expectations: vec![("lib-pdo_pgsql-libpq", Exp::Version("12.1"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pq"], + info: Some( + "pq + +PQ Support => enabled +Extension Version => 2.2.0 + +Used Library => Compiled => Linked +libpq => 14.3 (Ubuntu 14.3-1.pgdg22.04+1) => 15.0.2 + ", + ), + expectations: vec![("lib-pq-libpq", Exp::Version("15.0.2"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["rdkafka"], + info: None, + expectations: vec![("lib-rdkafka-librdkafka", Exp::Version("1.9.2"))], + functions: vec![], + constants: vec![("RD_KAFKA_VERSION", None, PhpMixed::Int(17367807))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["libsodium"], + info: None, + expectations: vec![("lib-libsodium", Exp::Version("1.0.17"))], + functions: vec![], + constants: vec![("SODIUM_LIBRARY_VERSION", None, s("1.0.17"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["sodium"], + info: None, + expectations: vec![("lib-libsodium", Exp::Version("1.0.15"))], + functions: vec![], + constants: vec![("SODIUM_LIBRARY_VERSION", None, s("1.0.15"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["pdo_sqlite"], + info: Some( + " +pdo_sqlite + +PDO Driver for SQLite 3.x => enabled +SQLite Library => 3.32.3 + ", + ), + expectations: vec![("lib-pdo_sqlite-sqlite", Exp::Version("3.32.3"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["sqlite3"], + info: Some( + " +sqlite3 + +SQLite3 support => enabled +SQLite3 module version => 7.1.33 +SQLite Library => 3.31.0 + +Directive => Local Value => Master Value +sqlite3.extension_dir => no value => no value +sqlite3.defensive => 1 => 1", + ), + expectations: vec![("lib-sqlite3-sqlite", Exp::Version("3.31.0"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["ssh2"], + info: Some( + " +ssh2 + +SSH2 support => enabled +extension version => 1.2 +libssh2 version => 1.8.0 +banner => SSH-2.0-libssh2_1.8.0", + ), + expectations: vec![("lib-ssh2-libssh2", Exp::Version("1.8.0"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["yaml"], + info: Some( + " + yaml + +LibYAML Support => enabled +Module Version => 2.0.2 +LibYAML Version => 0.2.2 + +Directive => Local Value => Master Value +yaml.decode_binary => 0 => 0 +yaml.decode_timestamp => 0 => 0 +yaml.decode_php => 0 => 0 +yaml.output_canonical => 0 => 0 +yaml.output_indent => 2 => 2 +yaml.output_width => 80 => 80", + ), + expectations: vec![("lib-yaml-libyaml", Exp::Version("0.2.2"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["xsl"], + info: Some( + " +xsl + +XSL => enabled +libxslt Version => 1.1.33 +libxslt compiled against libxml Version => 2.9.8 +EXSLT => enabled +libexslt Version => 1.1.29", + ), + expectations: vec![ + ("lib-libxslt", Exp::Full("1.1.29", &["lib-xsl"], &[])), + ("lib-libxslt-libxml", Exp::Version("2.9.8")), + ], + functions: vec![], + constants: vec![("LIBXSLT_DOTTED_VERSION", None, s("1.1.29"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["zip"], + info: None, + expectations: vec![("lib-zip-libzip", Exp::Full("1.5.0", &["lib-zip"], &[]))], + functions: vec![], + constants: vec![("LIBZIP_VERSION", Some("ZipArchive"), s("1.5.0"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["zlib"], + info: None, + expectations: vec![("lib-zlib", Exp::Version("1.2.10"))], + functions: vec![], + constants: vec![("ZLIB_VERSION", None, s("1.2.10"))], + class_definitions: vec![], + }, + LibCase { + extensions: vec!["zlib"], + info: Some( + " +zlib + +ZLib Support => enabled +Stream Wrapper => compress.zlib:// +Stream Filter => zlib.inflate, zlib.deflate +Compiled Version => 1.2.8 +Linked Version => 1.2.11", + ), + expectations: vec![("lib-zlib", Exp::Version("1.2.11"))], + functions: vec![], + constants: vec![], + class_definitions: vec![], + }, + ] +} + +fn assert_package_links( + context: &str, + expected_links: &[&str], + source_package: &BasePackageHandle, + links: IndexMap<String, Link>, +) { + assert_eq!( + expected_links.len(), + links.len(), + "{}: expected package count to match", + context + ); + + for link in links.values() { + assert_eq!(source_package.get_name(), link.get_source()); + assert!( + expected_links.contains(&link.get_target()), + "{}: package {} not in {:?}", + context, + link.get_target(), + expected_links + ); + let version_constraint = + SimpleConstraint::new("=".to_string(), source_package.get_version(), None); + assert!(link.get_constraint().matches(&version_constraint.into())); + } } #[test] -#[ignore = "requires PHPUnit getMockBuilder mock of Composer\\Platform\\Runtime (willReturnMap/willReturnCallback for getExtensions/getExtensionVersion/getExtensionInfo/invoke/hasConstant/getConstant/hasClass/construct) plus ResourceBundleStub/ImagickStub; no mocking framework"] +#[ignore = "all 59 provideLibraryTestCases datasets ported faithfully; execution still \ + panics at the unrelated XdebugHandler::get_skipped_version() todo!() (and a few \ + cases additionally rely on the ResourceBundle/Imagick stub paths), so it stays \ + ignored"] fn test_library_information() { - todo!() + let extension_version = "100.200.300"; + + for case in library_test_cases() { + let extensions: Vec<String> = case.extensions.iter().map(|e| e.to_string()).collect(); + + let mut constants: Vec<(String, Option<String>, PhpMixed)> = case + .constants + .iter() + .map(|(n, c, v)| (n.to_string(), c.map(|s| s.to_string()), v.clone())) + .collect(); + constants.push(( + "PHP_VERSION".to_string(), + None, + PhpMixed::String("7.1.0".to_string()), + )); + + let functions = case.functions.clone(); + let info = case.info.map(|s| s.to_string()); + + let exts_for_get = extensions.clone(); + let constants_has = constants.clone(); + let constants_get = constants.clone(); + + let mut runtime = MockRuntime::new(); + runtime + .expect_get_extensions() + .times(..) + .returning(move || exts_for_get.clone()); + runtime + .expect_get_extension_version() + .times(..) + .returning(move |_extension| extension_version.to_string()); + runtime + .expect_get_extension_info() + .times(..) + .returning(move |_extension| Ok(info.clone().unwrap_or_default())); + runtime + .expect_invoke() + .times(..) + .returning(move |callable, arguments| { + for (c, a, ret) in &functions { + if *c == callable && *a == arguments { + return ret.clone(); + } + } + PhpMixed::Null + }); + runtime + .expect_has_constant() + .times(..) + .returning(move |constant, class| { + constants_has + .iter() + .any(|(n, c, _)| n == constant && c.as_deref() == class.as_deref()) + }); + runtime + .expect_get_constant() + .times(..) + .returning(move |constant, class| { + constants_get + .iter() + .find(|(n, c, _)| n == constant && c.as_deref() == class.as_deref()) + .map(|(_, _, v)| v.clone()) + .unwrap_or(PhpMixed::Null) + }); + let class_definitions_has = case.class_definitions.clone(); + let class_definitions_construct = case.class_definitions.clone(); + runtime + .expect_has_class() + .times(..) + .returning(move |class| class_definitions_has.iter().any(|d| d.class == class)); + runtime + .expect_construct() + .times(..) + .returning(move |class, arguments| { + for d in &class_definitions_construct { + if d.class == class + && let Some((args, ret)) = &d.construct + && *args == arguments + { + return Ok(ret.clone()); + } + } + Ok(PhpMixed::Null) + }); + + let mut platform_repository = + PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None) + .unwrap(); + + let libraries: Vec<String> = platform_repository + .search("lib".to_string(), SEARCH_NAME, None) + .unwrap() + .into_iter() + .map(|package| package.name) + .filter(|name| name.starts_with("lib-")) + .collect(); + let expected_libraries: Vec<&str> = case + .expectations + .iter() + .filter(|(_, exp)| !matches!(exp, Exp::Missing)) + .map(|(name, _)| *name) + .collect(); + assert_eq!( + expected_libraries.len(), + libraries.len(), + "Expected: {:?}, got {:?}", + expected_libraries, + libraries + ); + + let mut all_expectations: Vec<(String, (Option<&str>, &[&str], &[&str]))> = case + .expectations + .iter() + .map(|(name, exp)| (name.to_string(), exp.parts())) + .collect(); + for extension in &extensions { + all_expectations.push(( + format!("ext-{}", extension), + (Some(extension_version), &[], &[]), + )); + } + + for (package_name, (expected_version, expected_replaces, expected_provides)) in + all_expectations + { + let package = platform_repository + .find_package( + &package_name, + FindPackageConstraint::String("*".to_string()), + ) + .unwrap(); + match expected_version { + None => assert!( + package.is_none(), + "Expected to not find package \"{}\"", + package_name + ), + Some(expected_version) => { + assert!( + package.is_some(), + "Expected to find package \"{}\"", + package_name + ); + let package = package.unwrap(); + assert_eq!( + expected_version, + package.get_pretty_version(), + "Expected version {} for {}", + expected_version, + package_name + ); + assert_package_links( + "replaces", + expected_replaces, + &package, + package.get_replaces(), + ); + assert_package_links( + "provides", + expected_provides, + &package, + package.get_provides(), + ); + } + } + } + } } #[test] -#[ignore = "requires PHPUnit getMockBuilder mock of Composer\\Platform\\Runtime (willReturnMap for getConstant/getExtensions); no mocking framework and Runtime is a concrete struct"] +#[ignore = "seam + Runtime mock ported (getConstant/getExtensions map); execution panics \ + at the unrelated XdebugHandler::get_skipped_version() todo!()"] fn test_composer_platform_version() { - todo!() + let constants: IndexMap<String, PhpMixed> = IndexMap::from([ + ( + "PHP_VERSION".to_string(), + PhpMixed::String("7.0.0".to_string()), + ), + ("PHP_DEBUG".to_string(), PhpMixed::Bool(false)), + ]); + + let mut runtime = MockRuntime::new(); + runtime + .expect_get_extensions() + .times(..) + .returning(Vec::new); + runtime + .expect_get_constant() + .times(..) + .returning(move |constant, class| { + constants + .get(&constant_key(constant, class.as_deref())) + .cloned() + .unwrap_or(PhpMixed::Null) + }); + // PHP only stubs getExtensions/getConstant; PHPUnit auto-returns null/false for the + // other probed methods. Mirror that so initialize() does not hit unset expectations. + runtime + .expect_has_constant() + .times(..) + .returning(|_, _| false); + runtime + .expect_invoke() + .times(..) + .returning(|_, _| PhpMixed::Null); + + let mut platform_repository = + PlatformRepository::new4(vec![], IndexMap::new(), Some(Box::new(runtime)), None).unwrap(); + + let package = platform_repository + .find_package( + "composer", + FindPackageConstraint::String(format!("={}", shirabe::composer::get_version())), + ) + .unwrap(); + assert!(package.is_some(), "Composer package exists"); } #[test] |
