aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/repository
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/tests/repository')
-rw-r--r--crates/shirabe/tests/repository/filesystem_repository_test.rs117
-rw-r--r--crates/shirabe/tests/repository/platform_repository_test.rs1844
-rw-r--r--crates/shirabe/tests/repository/vcs/perforce_driver_test.rs229
3 files changed, 2065 insertions, 125 deletions
diff --git a/crates/shirabe/tests/repository/filesystem_repository_test.rs b/crates/shirabe/tests/repository/filesystem_repository_test.rs
index 1914927..0d0e621 100644
--- a/crates/shirabe/tests/repository/filesystem_repository_test.rs
+++ b/crates/shirabe/tests/repository/filesystem_repository_test.rs
@@ -1,12 +1,23 @@
//! ref: composer/tests/Composer/Test/Repository/FilesystemRepositoryTest.php
+use std::cell::RefCell;
+use std::rc::Rc;
+
use indexmap::IndexMap;
+use shirabe::dependency_resolver::operation::OperationInterface;
use shirabe::installed_versions::InstalledVersions;
+use shirabe::installer::{InstallationManagerInterface, InstallerInterface};
+use shirabe::io::IOInterface;
use shirabe::json::json_file::JsonFile;
+use shirabe::package::PackageInterfaceHandle;
+use shirabe::repository::InstalledRepositoryInterface;
use shirabe::repository::RepositoryInterface;
use shirabe::repository::filesystem_repository::FilesystemRepository;
+use shirabe::util::filesystem::Filesystem;
use shirabe_php_shim::PhpMixed;
+use crate::test_case::get_package;
+
/// PHP mocks JsonFile::read()/exists(); without a mocking framework the canned read value is
/// materialized as a real temp file whose decoded JSON reproduces the mock return value exactly.
fn create_temp_json_file(contents: &str) -> String {
@@ -78,14 +89,114 @@ fn test_unexistent_repository_file() {
assert_eq!(packages.len(), 0);
}
+/// Stub for `InstallationManagerInterface` whose `getInstallPath` returns a fixed path, mirroring
+/// the PHPUnit mock built with `disableOriginalConstructor()`. Only `get_install_path` is reached by
+/// `FilesystemRepository::write`; the call counter backs PHP's `expects($this->exactly(2))`.
+#[derive(Debug)]
+struct InstallPathStub {
+ calls: Rc<RefCell<i64>>,
+ fixed_path: String,
+}
+
+impl InstallationManagerInterface for InstallPathStub {
+ fn add_installer(&mut self, _installer: Box<dyn InstallerInterface>) {
+ unimplemented!()
+ }
+ fn remove_installer(&mut self, _installer: &dyn InstallerInterface) {
+ unimplemented!()
+ }
+ fn disable_plugins(&mut self) {
+ unimplemented!()
+ }
+ fn is_package_installed(
+ &mut self,
+ _repo: &dyn InstalledRepositoryInterface,
+ _package: PackageInterfaceHandle,
+ ) -> anyhow::Result<bool> {
+ unimplemented!()
+ }
+ fn ensure_binaries_presence(&mut self, _package: PackageInterfaceHandle) {
+ unimplemented!()
+ }
+ fn execute(
+ &mut self,
+ _repo: &mut dyn InstalledRepositoryInterface,
+ _operations: Vec<Rc<dyn OperationInterface>>,
+ _dev_mode: bool,
+ _run_scripts: bool,
+ _download_only: bool,
+ ) -> anyhow::Result<()> {
+ unimplemented!()
+ }
+ fn get_install_path(&mut self, _package: PackageInterfaceHandle) -> Option<String> {
+ *self.calls.borrow_mut() += 1;
+ Some(self.fixed_path.clone())
+ }
+ fn set_output_progress(&mut self, _output_progress: bool) {
+ unimplemented!()
+ }
+ fn notify_installs(&mut self, _io: Rc<RefCell<dyn IOInterface>>) {
+ unimplemented!()
+ }
+}
+
#[test]
-#[ignore = "requires mocking InstallationManager::get_install_path; write() takes a concrete InstallationManager with no trait/seam to stub the canned per-package paths the PHP test relies on"]
fn test_repository_write() {
- todo!()
+ // PHP mocks JsonFile::write/read/getPath; here a real JsonFile under a temp repo dir is written
+ // and read back. write() never reads the file (it dumps the in-memory packages), so the mocked
+ // read()/exists() return values are irrelevant to the result.
+ let base = std::fs::canonicalize(std::env::temp_dir()).unwrap();
+ let repo_dir = format!(
+ "{}/shirabe_repo_write_test_{}_{}",
+ base.display(),
+ std::process::id(),
+ std::time::SystemTime::now()
+ .duration_since(std::time::UNIX_EPOCH)
+ .unwrap()
+ .as_nanos()
+ );
+ let mut fs = Filesystem::new(None);
+ fs.remove_directory(&repo_dir).ok();
+
+ let json_path = format!("{}/vendor/composer/installed.json", repo_dir);
+ let json = JsonFile::new(json_path.clone(), None, None).unwrap();
+ let mut repository = FilesystemRepository::new(json, false, None, None).unwrap();
+
+ let calls = Rc::new(RefCell::new(0i64));
+ let mut im = InstallPathStub {
+ calls: calls.clone(),
+ fixed_path: format!("{}/vendor/woop/woop", repo_dir),
+ };
+
+ repository.set_dev_package_names(vec!["mypkg2".to_string()]);
+ repository
+ .add_package(get_package("mypkg2", "1.2.3"))
+ .unwrap();
+ repository
+ .add_package(get_package("mypkg", "0.1.10"))
+ .unwrap();
+ repository.write(true, &mut im).unwrap();
+
+ // PHP asserts getInstallPath is called exactly twice (once per installed package).
+ assert_eq!(*calls.borrow(), 2);
+
+ let written = std::fs::read_to_string(&json_path).unwrap();
+ let actual: serde_json::Value = serde_json::from_str(&written).unwrap();
+ let expected = serde_json::json!({
+ "packages": [
+ {"name": "mypkg", "type": "library", "version": "0.1.10", "version_normalized": "0.1.10.0", "install-path": "../woop/woop"},
+ {"name": "mypkg2", "type": "library", "version": "1.2.3", "version_normalized": "1.2.3.0", "install-path": "../woop/woop"},
+ ],
+ "dev": true,
+ "dev-package-names": ["mypkg2"],
+ });
+ assert_eq!(actual, expected);
+
+ fs.remove_directory(&repo_dir).ok();
}
#[test]
-#[ignore = "requires mocking InstallationManager::get_install_path (concrete method, no stub seam) plus missing test helpers get_root_package and configure_links"]
+#[ignore = "needs get_root_package + configure_links test helpers (not present in tests/common; project_test_port_link_setters describes them via ArrayLoader::load_packages but this branch lacks them) plus exact byte-match of the generated installed.php fixture under a chdir"]
fn test_repository_writes_installed_php() {
todo!()
}
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]
diff --git a/crates/shirabe/tests/repository/vcs/perforce_driver_test.rs b/crates/shirabe/tests/repository/vcs/perforce_driver_test.rs
index 5e9471f..ef55c75 100644
--- a/crates/shirabe/tests/repository/vcs/perforce_driver_test.rs
+++ b/crates/shirabe/tests/repository/vcs/perforce_driver_test.rs
@@ -8,25 +8,48 @@ use shirabe::config::Config;
use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
use shirabe::repository::vcs::PerforceDriver;
+use shirabe::util::HttpDownloader;
use shirabe::util::filesystem::Filesystem;
+use shirabe::util::process_executor::MockHandler;
use shirabe_php_shim::PhpMixed;
use tempfile::TempDir;
+use crate::process_executor_mock::{ProcessExecutorMockGuard, get_process_executor_mock};
+
const TEST_URL: &str = "TEST_PERFORCE_URL";
const TEST_DEPOT: &str = "TEST_DEPOT_CONFIG";
const TEST_BRANCH: &str = "TEST_BRANCH_CONFIG";
+// A getMockBuilder('Composer\Util\Perforce') stand-in: the seam trait extracted from the
+// concrete `Perforce` struct, injected via overrideDriverInternalPerforce's Rust equivalent.
+mockall::mock! {
+ #[derive(Debug)]
+ pub Perforce {}
+ impl shirabe::util::PerforceInterface for Perforce {
+ fn initialize_path(&mut self, path: &str);
+ fn set_stream(&mut self, stream: &str);
+ fn p4_login(&mut self) -> anyhow::Result<()>;
+ fn check_stream(&mut self) -> bool;
+ fn write_p4_client_spec(&mut self) -> anyhow::Result<()>;
+ fn connect_client(&mut self) -> anyhow::Result<()>;
+ fn sync_code_base(&mut self, source_reference: Option<String>) -> anyhow::Result<()>;
+ fn cleanup_client_spec(&mut self);
+ fn get_commit_logs(&mut self, from_reference: &str, to_reference: &str) -> Option<String>;
+ fn get_file_content(&mut self, file: &str, identifier: &str) -> Option<String>;
+ fn get_branches(&mut self) -> IndexMap<String, String>;
+ fn get_tags(&mut self) -> IndexMap<String, String>;
+ fn get_user(&self) -> Option<String>;
+ fn get_composer_information(
+ &mut self,
+ identifier: &str,
+ ) -> anyhow::Result<Option<IndexMap<String, PhpMixed>>>;
+ }
+}
+
struct SetUp {
test_path: TempDir,
config: Config,
repo_config: IndexMap<String, PhpMixed>,
- // The IOInterface, ProcessExecutor, HttpDownloader and Perforce mocks, the
- // driver instance and the reflection-based perforce override are not ported.
- io: (),
- process: (),
- http_downloader: (),
- perforce: (),
- driver: (),
}
fn get_test_config(test_path: &std::path::Path) -> Config {
@@ -57,25 +80,34 @@ fn set_up() -> SetUp {
PhpMixed::String(TEST_BRANCH.to_string()),
);
- let io = ();
- let process = ();
- let http_downloader = ();
- let perforce = ();
- // The driver construction and overrideDriverInternalPerforce (reflection) are not ported.
- let driver = ();
-
SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
}
}
+// ref: setUp builds the driver from a mock IO, an empty ProcessExecutorMock and a mock
+// HttpDownloader. The empty, non-strict process mock lets any p4 query Perforce issues resolve
+// with the default (exit 0) result.
+fn make_driver(
+ config: Config,
+ repo_config: &IndexMap<String, PhpMixed>,
+) -> (PerforceDriver, ProcessExecutorMockGuard) {
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
+ let config = Rc::new(RefCell::new(config));
+ let http_downloader = Rc::new(RefCell::new(HttpDownloader::new(
+ io.clone(),
+ config.clone(),
+ IndexMap::new(),
+ false,
+ )));
+ let (process, guard) = get_process_executor_mock(vec![], false, MockHandler::default());
+ let driver = PerforceDriver::new(repo_config.clone(), io, config, http_downloader, process);
+
+ (driver, guard)
+}
+
fn tear_down(test_path: &std::path::Path) {
// cleanup directory under test path
let mut fs = Filesystem::new(None);
@@ -106,146 +138,127 @@ fn test_supports_returns_false_no_deep_check() {
assert!(!PerforceDriver::supports(io, config, "existing.url", false).unwrap());
}
-// The remaining cases mock Perforce, the repository config and IO to drive initialization,
-// composer-file detection and cleanup; mocking is not available here.
#[test]
fn test_initialize_captures_variables_from_repo_config() {
let SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
} = set_up();
let _tear_down = TearDown::new(test_path.path().to_path_buf());
- let _ = (&io, &process, &http_downloader, &perforce, &driver);
- let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
- let config = Rc::new(RefCell::new(config));
- let http_downloader = Rc::new(RefCell::new(shirabe::util::HttpDownloader::new(
- io.clone(),
- config.clone(),
- IndexMap::new(),
- false,
- )));
- // ref: setUp uses getProcessExecutorMock(); the empty, non-strict mock lets Perforce's p4
- // queries resolve with the default (exit 0) result so initialize() never shells out to p4.
- let (process, _process_guard) = crate::process_executor_mock::get_process_executor_mock(
- vec![],
- false,
- shirabe::util::process_executor::MockHandler::default(),
- );
-
- let mut driver = PerforceDriver::new(repo_config, io, config, http_downloader, process);
+ let (mut driver, _process_guard) = make_driver(config, &repo_config);
driver.initialize().unwrap();
assert_eq!(TEST_URL, driver.get_url());
assert_eq!(TEST_DEPOT, driver.get_depot());
assert_eq!(TEST_BRANCH, driver.get_branch());
}
-#[ignore = "needs a Perforce mock injected via reflection (overrideDriverInternalPerforce) to assert p4Login/checkStream/writeP4ClientSpec/connectClient are each called once; the perforce field is pub(crate) and no mock infra exists"]
#[test]
fn test_initialize_logs_in_and_connects_client() {
let SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
} = set_up();
let _tear_down = TearDown::new(test_path.path().to_path_buf());
- let _ = (
- &config,
- &repo_config,
- &io,
- &process,
- &http_downloader,
- &perforce,
- &driver,
- );
- todo!()
+
+ let (mut driver, _process_guard) = make_driver(config, &repo_config);
+ let mut perforce = MockPerforce::new();
+ perforce.expect_p4_login().times(1).returning(|| Ok(()));
+ perforce.expect_check_stream().times(1).returning(|| false);
+ perforce
+ .expect_write_p4_client_spec()
+ .times(1)
+ .returning(|| Ok(()));
+ perforce
+ .expect_connect_client()
+ .times(1)
+ .returning(|| Ok(()));
+ driver.__override_perforce(Box::new(perforce));
+
+ driver.initialize().unwrap();
}
-#[ignore = "needs a Perforce mock injected via reflection (overrideDriverInternalPerforce) to stub getComposerInformation; the perforce field is pub(crate) and no mock infra exists"]
#[test]
fn test_has_composer_file_returns_false_on_no_composer_file() {
let SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
} = set_up();
let _tear_down = TearDown::new(test_path.path().to_path_buf());
- let _ = (
- &config,
- &repo_config,
- &io,
- &process,
- &http_downloader,
- &perforce,
- &driver,
- );
- todo!()
+
+ let identifier = "TEST_IDENTIFIER";
+ let formatted_depot_path = format!("//{}/{}", TEST_DEPOT, identifier);
+
+ let (mut driver, _process_guard) = make_driver(config, &repo_config);
+ let mut perforce = MockPerforce::new();
+ perforce.expect_p4_login().returning(|| Ok(()));
+ perforce.expect_check_stream().returning(|| false);
+ perforce.expect_write_p4_client_spec().returning(|| Ok(()));
+ perforce.expect_connect_client().returning(|| Ok(()));
+ perforce
+ .expect_get_composer_information()
+ .withf(move |id: &str| id == formatted_depot_path)
+ .returning(|_| Ok(Some(IndexMap::new())));
+ driver.__override_perforce(Box::new(perforce));
+
+ driver.initialize().unwrap();
+ let result = driver.has_composer_file(identifier);
+ assert!(!result);
}
-#[ignore = "needs a Perforce mock injected via reflection (overrideDriverInternalPerforce) to stub getComposerInformation; the perforce field is pub(crate) and no mock infra exists"]
#[test]
fn test_has_composer_file_returns_true_with_one_or_more_composer_files() {
let SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
} = set_up();
let _tear_down = TearDown::new(test_path.path().to_path_buf());
- let _ = (
- &config,
- &repo_config,
- &io,
- &process,
- &http_downloader,
- &perforce,
- &driver,
- );
- todo!()
+
+ let identifier = "TEST_IDENTIFIER";
+ let formatted_depot_path = format!("//{}/{}", TEST_DEPOT, identifier);
+
+ let (mut driver, _process_guard) = make_driver(config, &repo_config);
+ let mut perforce = MockPerforce::new();
+ perforce.expect_p4_login().returning(|| Ok(()));
+ perforce.expect_check_stream().returning(|| false);
+ perforce.expect_write_p4_client_spec().returning(|| Ok(()));
+ perforce.expect_connect_client().returning(|| Ok(()));
+ perforce
+ .expect_get_composer_information()
+ .withf(move |id: &str| id == formatted_depot_path)
+ .returning(|_| {
+ // ref: returnValue(['']) — a non-empty list with one empty-string element.
+ let mut info: IndexMap<String, PhpMixed> = IndexMap::new();
+ info.insert("0".to_string(), PhpMixed::String(String::new()));
+ Ok(Some(info))
+ });
+ driver.__override_perforce(Box::new(perforce));
+
+ driver.initialize().unwrap();
+ let result = driver.has_composer_file(identifier);
+ assert!(result);
}
-#[ignore = "needs a Perforce mock injected via reflection (overrideDriverInternalPerforce) to assert cleanupClientSpec is called once; the perforce field is pub(crate) and no mock infra exists"]
#[test]
fn test_cleanup() {
let SetUp {
test_path,
config,
repo_config,
- io,
- process,
- http_downloader,
- perforce,
- driver,
} = set_up();
let _tear_down = TearDown::new(test_path.path().to_path_buf());
- let _ = (
- &config,
- &repo_config,
- &io,
- &process,
- &http_downloader,
- &perforce,
- &driver,
- );
- todo!()
+
+ let (mut driver, _process_guard) = make_driver(config, &repo_config);
+ let mut perforce = MockPerforce::new();
+ perforce
+ .expect_cleanup_client_spec()
+ .times(1)
+ .returning(|| ());
+ driver.__override_perforce(Box::new(perforce));
+
+ driver.cleanup().unwrap();
}