aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/installer
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-22 02:09:59 +0900
committernsfisis <nsfisis@gmail.com>2026-06-22 02:09:59 +0900
commit822d9a872807a92a5337ee8b7bab96dc9845cdbb (patch)
tree4931365df5978caffade69cad2154718a9076f66 /crates/shirabe/tests/installer
parentf691b864b687f251c3b266e8cff2774d730567ba (diff)
downloadphp-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.tar.gz
php-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.tar.zst
php-shirabe-822d9a872807a92a5337ee8b7bab96dc9845cdbb.zip
test: port more test cases
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/installer')
-rw-r--r--crates/shirabe/tests/installer/binary_installer_test.rs2
-rw-r--r--crates/shirabe/tests/installer/installation_manager_test.rs16
-rw-r--r--crates/shirabe/tests/installer/library_installer_test.rs167
-rw-r--r--crates/shirabe/tests/installer/suggested_packages_reporter_test.rs76
4 files changed, 210 insertions, 51 deletions
diff --git a/crates/shirabe/tests/installer/binary_installer_test.rs b/crates/shirabe/tests/installer/binary_installer_test.rs
index 0a22243..7bd5eea 100644
--- a/crates/shirabe/tests/installer/binary_installer_test.rs
+++ b/crates/shirabe/tests/installer/binary_installer_test.rs
@@ -24,7 +24,7 @@ impl Drop for TearDown {
// program's output. It needs a real PHP runtime, the binary-proxy generation, and a
// mocked Package's getBinaries(), none of which are available here.
#[test]
-#[ignore = "installs and executes a PHP binary via ProcessExecutor (needs a real PHP runtime and binary proxies) and mocks a Package"]
+#[ignore = "needs PHPUnit getMockBuilder mocks of IOInterface and Package::getBinaries() plus a real PHP runtime to execute the installed binary and assert its output"]
fn test_install_and_exec_binary_with_full_compat() {
todo!()
}
diff --git a/crates/shirabe/tests/installer/installation_manager_test.rs b/crates/shirabe/tests/installer/installation_manager_test.rs
index b44a814..a12381b 100644
--- a/crates/shirabe/tests/installer/installation_manager_test.rs
+++ b/crates/shirabe/tests/installer/installation_manager_test.rs
@@ -8,50 +8,50 @@ fn set_up() {
// These mock individual installers, the repository and IO to drive InstallationManager's
// add/execute/install/update/uninstall logic; mocking is not available here.
+#[ignore = "requires PHPUnit mocks of InstallerInterface/IOInterface/Loop with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_add_get_installer() {
todo!()
}
+#[ignore = "requires PHPUnit mocks of InstallerInterface/IOInterface/Loop with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_add_remove_installer() {
todo!()
}
+#[ignore = "requires partial PHPUnit mock of InstallationManager (onlyMethods install/update/uninstall) and PackageInterface mock"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_execute() {
todo!()
}
+#[ignore = "requires PHPUnit mocks of InstallerInterface/PackageInterface with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_install() {
todo!()
}
+#[ignore = "requires PHPUnit mocks of InstallerInterface/PackageInterface with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_update_with_equal_types() {
todo!()
}
+#[ignore = "requires PHPUnit mocks of InstallerInterface/PackageInterface with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_update_with_not_equal_types() {
todo!()
}
+#[ignore = "requires PHPUnit mocks of InstallerInterface/PackageInterface with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_uninstall() {
todo!()
}
+#[ignore = "requires PHPUnit mock of LibraryInstaller and PackageInterface with expects() call-count assertions"]
#[test]
-#[ignore = "mocks installers/repository/IO to drive InstallationManager; mocking is not available"]
fn test_install_binary() {
todo!()
}
diff --git a/crates/shirabe/tests/installer/library_installer_test.rs b/crates/shirabe/tests/installer/library_installer_test.rs
index 2182962..62a2c0c 100644
--- a/crates/shirabe/tests/installer/library_installer_test.rs
+++ b/crates/shirabe/tests/installer/library_installer_test.rs
@@ -1,77 +1,188 @@
//! ref: composer/tests/Composer/Test/Installer/LibraryInstallerTest.php
-/// Sets up a Composer/Config over root/vendor/bin temp dirs plus mocked
-/// DownloadManager/repository/IO. The temp-dir helpers and the mocks are not
-/// available here, so this remains a stub.
-fn set_up() {
- todo!()
-}
+use std::cell::RefCell;
+use std::fs;
+use std::rc::Rc;
-/// Removes the root dir created by `set_up`, which is itself a stub.
-fn tear_down() {
- todo!()
+use indexmap::IndexMap;
+use tempfile::TempDir;
+
+use shirabe::composer::{
+ ComposerHandle, PartialComposerHandle, PartialComposerWeakHandle, PartialOrFullComposer,
+};
+use shirabe::config::Config;
+use shirabe::downloader::DownloadManager;
+use shirabe::installer::{InstallerInterface, LibraryInstaller};
+use shirabe::io::IOInterface;
+use shirabe::io::null_io::NullIO;
+use shirabe::repository::InstalledArrayRepository;
+use shirabe::repository::WritableRepositoryInterface;
+use shirabe::util::filesystem::Filesystem;
+use shirabe_php_shim::PhpMixed;
+
+use crate::test_case::get_package;
+
+/// Mirror of setUp(): builds the Composer/Config over temp root/vendor/bin dirs
+/// plus a real DownloadManager and a NullIO. The `_composer_rc` keeps the inner
+/// Rc alive for the duration of the test since LibraryInstaller only holds a weak
+/// handle.
+struct SetUp {
+ root: TempDir,
+ vendor_dir: String,
+ bin_dir: String,
+ io: Rc<RefCell<dyn IOInterface>>,
+ composer: PartialComposerWeakHandle,
+ fs: Filesystem,
+ _composer_rc: ComposerHandle,
}
-struct TearDown;
+fn set_up() -> SetUp {
+ let mut fs = Filesystem::new(None);
+
+ let root = TempDir::new().unwrap();
+ let root_dir = fs::canonicalize(root.path())
+ .unwrap()
+ .to_string_lossy()
+ .into_owned();
+
+ let vendor_dir = format!("{}/vendor", root_dir);
+ fs::create_dir_all(&vendor_dir).unwrap();
-impl Drop for TearDown {
- fn drop(&mut self) {
- tear_down();
+ let bin_dir = format!("{}/bin", root_dir);
+ fs::create_dir_all(&bin_dir).unwrap();
+
+ let mut config = Config::new(false, None);
+ let mut config_section: IndexMap<String, PhpMixed> = IndexMap::new();
+ config_section.insert(
+ "vendor-dir".to_string(),
+ PhpMixed::String(vendor_dir.clone()),
+ );
+ config_section.insert("bin-dir".to_string(), PhpMixed::String(bin_dir.clone()));
+ let mut merged: IndexMap<String, PhpMixed> = IndexMap::new();
+ merged.insert("config".to_string(), PhpMixed::Array(config_section));
+ config.merge(&merged, Config::SOURCE_UNKNOWN);
+ let config_rc = Rc::new(RefCell::new(config));
+
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
+
+ let dm = DownloadManager::new(io.clone(), false, None);
+ let dm_rc = Rc::new(RefCell::new(dm));
+
+ let composer_rc = Rc::new(RefCell::new(PartialOrFullComposer::new_full()));
+ let composer = ComposerHandle::from_rc_unchecked(composer_rc.clone());
+ composer.borrow_mut().set_config(config_rc);
+ composer.borrow_mut().set_download_manager(dm_rc);
+
+ let weak = PartialComposerHandle::from_rc(composer_rc).downgrade();
+
+ SetUp {
+ root,
+ vendor_dir,
+ bin_dir,
+ io,
+ composer: weak,
+ fs,
+ _composer_rc: composer,
}
}
-// These construct a LibraryInstaller over a temp dir with a mocked IO/Filesystem/repository
-// and mocked packages to drive install/update/uninstall and path resolution.
+fn tear_down(setup: &mut SetUp) {
+ let root = setup.root.path().to_path_buf();
+ setup.fs.remove_directory(&root).ok();
+}
+
+#[ignore]
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
fn test_installer_creation_should_not_create_vendor_directory() {
- todo!()
+ let mut setup = set_up();
+ setup.fs.remove_directory(&setup.vendor_dir).unwrap();
+
+ LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
+ assert!(!std::path::Path::new(&setup.vendor_dir).exists());
+
+ tear_down(&mut setup);
}
+#[ignore]
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
fn test_installer_creation_should_not_create_bin_directory() {
- todo!()
+ let mut setup = set_up();
+ setup.fs.remove_directory(&setup.bin_dir).unwrap();
+
+ LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
+ assert!(!std::path::Path::new(&setup.bin_dir).exists());
+
+ tear_down(&mut setup);
}
+#[ignore]
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
fn test_is_installed() {
- todo!()
+ let mut setup = set_up();
+ let mut library =
+ LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
+ let package = get_package("test/pkg", "1.0.0");
+
+ let mut repository = InstalledArrayRepository::new().unwrap();
+ assert!(!library.is_installed(&repository, package.clone()));
+
+ // package being in repo is not enough to be installed
+ repository.add_package(package.clone()).unwrap();
+ assert!(!library.is_installed(&repository, package.clone()));
+
+ // package being in repo and vendor/pkg/foo dir present means it is seen as installed
+ let pkg_dir = format!("{}/{}", setup.vendor_dir, package.get_pretty_name());
+ fs::create_dir_all(&pkg_dir).unwrap();
+ assert!(library.is_installed(&repository, package.clone()));
+
+ repository.remove_package(package.clone()).unwrap();
+ assert!(!library.is_installed(&repository, package));
+
+ tear_down(&mut setup);
}
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
+#[ignore = "requires PHPUnit mock of DownloadManager (expects(once)->method('install')->will(resolve(null))) and InstalledRepositoryInterface (expects(once)->addPackage); a real DownloadManager would attempt a download"]
fn test_install() {
todo!()
}
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
+#[ignore = "requires PHPUnit mocks (Filesystem::rename, DownloadManager::update, repository hasPackage/add/remove with onConsecutiveCalls) and Package::setTargetDir, which is not exposed on PackageInterfaceHandle"]
fn test_update() {
todo!()
}
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
+#[ignore = "requires PHPUnit mock of DownloadManager (expects(once)->method('remove')->will(resolve(null))) and InstalledRepositoryInterface (hasPackage onConsecutiveCalls, removePackage)"]
fn test_uninstall() {
todo!()
}
+#[ignore]
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
fn test_get_install_path_without_target_dir() {
- todo!()
+ let mut setup = set_up();
+ let mut library =
+ LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
+ let package = get_package("Vendor/Pkg", "1.0.0");
+
+ assert_eq!(
+ format!("{}/{}", setup.vendor_dir, package.get_pretty_name()),
+ library.get_install_path(package).unwrap()
+ );
+
+ tear_down(&mut setup);
}
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
+#[ignore = "Package::setTargetDir is not exposed on PackageInterfaceHandle, so the target-dir cannot be set on the test package"]
fn test_get_install_path_with_target_dir() {
todo!()
}
#[test]
-#[ignore = "mocks IO/Filesystem/repository and packages to drive LibraryInstaller; mocking is not available"]
+#[ignore = "requires PHPUnit mock of BinaryInstaller (expects(never)->removeBinaries, expects(once)->installBinaries) injected via LibraryInstaller's binaryInstaller argument"]
fn test_ensure_binaries_installed() {
todo!()
}
diff --git a/crates/shirabe/tests/installer/suggested_packages_reporter_test.rs b/crates/shirabe/tests/installer/suggested_packages_reporter_test.rs
index fbdb16f..bd7672e 100644
--- a/crates/shirabe/tests/installer/suggested_packages_reporter_test.rs
+++ b/crates/shirabe/tests/installer/suggested_packages_reporter_test.rs
@@ -1,75 +1,123 @@
//! ref: composer/tests/Composer/Test/Installer/SuggestedPackagesReporterTest.php
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use indexmap::IndexMap;
+use shirabe::installer::SuggestedPackagesReporter;
+use shirabe::io::IOInterface;
+use shirabe::io::null_io::NullIO;
+
/// Builds an IO mock and a SuggestedPackagesReporter over it. The IO mock
/// (`getIOMock`) is not available here, so this remains a stub.
fn set_up() {
todo!()
}
+/// ref: SuggestedPackagesReporterTest::getSuggestedPackageArray
+fn get_suggested_package_array() -> IndexMap<String, String> {
+ let mut entry = IndexMap::new();
+ entry.insert("source".to_string(), "a".to_string());
+ entry.insert("target".to_string(), "b".to_string());
+ entry.insert("reason".to_string(), "c".to_string());
+ entry
+}
+
+fn reporter() -> SuggestedPackagesReporter {
+ let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
+ SuggestedPackagesReporter::new(io)
+}
+
// These construct a SuggestedPackagesReporter with a mocked IO and assert its accumulated
// suggestions and formatted output; mocking is not available here.
+#[ignore = "asserts IO mock output via getIOMock()->expects(); no IO mocking infrastructure exists and BufferIO::new/get_output are todo!()"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_constructor() {
todo!()
}
+#[ignore]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_get_packages_empty_by_default() {
- todo!()
+ let reporter = reporter();
+ assert!(reporter.get_packages().is_empty());
}
+#[ignore]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_get_packages() {
- todo!()
+ let suggested_package = get_suggested_package_array();
+ let mut reporter = reporter();
+ reporter.add_package(
+ suggested_package["source"].clone(),
+ suggested_package["target"].clone(),
+ suggested_package["reason"].clone(),
+ );
+ assert_eq!(&vec![suggested_package], reporter.get_packages());
}
+#[ignore]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_add_package_appends() {
- todo!()
+ let suggested_package_a = get_suggested_package_array();
+ let mut suggested_package_b = get_suggested_package_array();
+ suggested_package_b.insert("source".to_string(), "different source".to_string());
+ suggested_package_b.insert("reason".to_string(), "different reason".to_string());
+ let mut reporter = reporter();
+ reporter.add_package(
+ suggested_package_a["source"].clone(),
+ suggested_package_a["target"].clone(),
+ suggested_package_a["reason"].clone(),
+ );
+ reporter.add_package(
+ suggested_package_b["source"].clone(),
+ suggested_package_b["target"].clone(),
+ suggested_package_b["reason"].clone(),
+ );
+ assert_eq!(
+ &vec![suggested_package_a, suggested_package_b],
+ reporter.get_packages()
+ );
}
+#[ignore = "addSuggestionsFromPackage test mocks Package::getSuggests; set_suggests only exists on RootPackageHandle, so the non-root Package fixture with suggests cannot be expressed"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_add_suggestions_from_package() {
todo!()
}
+#[ignore = "asserts IO mock output via getIOMock()->expects(); no IO mocking infrastructure exists and BufferIO::new/get_output are todo!()"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output() {
todo!()
}
+#[ignore = "asserts IO mock output via getIOMock()->expects(); no IO mocking infrastructure exists and BufferIO::new/get_output are todo!()"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output_with_no_suggestion_reason() {
todo!()
}
+#[ignore = "asserts IO mock output via getIOMock()->expects(); no IO mocking infrastructure exists and BufferIO::new/get_output are todo!()"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output_ignores_formatting() {
todo!()
}
+#[ignore = "asserts IO mock output via getIOMock()->expects(); no IO mocking infrastructure exists and BufferIO::new/get_output are todo!()"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output_multiple_packages() {
todo!()
}
+#[ignore = "asserts IO mock output via getIOMock()->expects() and uses getMockBuilder mocks of InstalledRepository/PackageInterface; no mocking infrastructure exists"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output_skip_installed_packages() {
todo!()
}
+#[ignore = "uses getMockBuilder mock of InstalledRepository with ->expects($this->exactly(0)) call-count assertion; no mocking infrastructure exists"]
#[test]
-#[ignore = "mocks IO to drive SuggestedPackagesReporter output; mocking is not available"]
fn test_output_not_getting_installed_packages_when_no_suggestions() {
todo!()
}