aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/repository/platform_repository_test.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests/repository/platform_repository_test.rs')
-rw-r--r--crates/shirabe/tests/repository/platform_repository_test.rs1844
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]