aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/installer
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-28 14:30:41 +0900
committernsfisis <nsfisis@gmail.com>2026-06-28 14:30:41 +0900
commite97dc6e64c1be4bf78c420b11cec2a18dfd506d4 (patch)
tree6e397f696631ca23e931a1873803e0b1f85af915 /crates/shirabe/tests/installer
parentf408ff1ff95ce6e512810ccb10695f773fe5a8b9 (diff)
downloadphp-shirabe-e97dc6e64c1be4bf78c420b11cec2a18dfd506d4.tar.gz
php-shirabe-e97dc6e64c1be4bf78c420b11cec2a18dfd506d4.tar.zst
php-shirabe-e97dc6e64c1be4bf78c420b11cec2a18dfd506d4.zip
test(tests): use mockall for hand-written interface mocks
Replace hand-written mock/stub structs that re-implemented PHPUnit mock-builder behavior (record-and-verify, manual call counters, unreachable!() guards) with mockall::mock! locals across: - package/loader: MockLoader, VersionGuesserMock - command: ArchiveManager/RepositoryManager/EventDispatcher mocks - util: ConfigSource/AuthJson mocks (auth_helper, bitbucket, github, forgejo, gitlab) - repository/vcs: github_driver NullConfigSource - installer: CountingInstaller, RecordingBinaryInstaller, and the DownloadManager mock (formerly common/downloader_stub.rs, now deleted) - downloader: download_manager create_downloader_mock Verification (counts/args) now lives in mockall expectations checked on drop. installation_manager BinaryInstaller is left hand-written because its as_binary_presence_interface seam returns Some(&mut self), which mockall cannot express; io_stub and io_mock are left as-is. 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/installation_manager_test.rs195
-rw-r--r--crates/shirabe/tests/installer/library_installer_test.rs265
-rw-r--r--crates/shirabe/tests/installer/main.rs2
3 files changed, 272 insertions, 190 deletions
diff --git a/crates/shirabe/tests/installer/installation_manager_test.rs b/crates/shirabe/tests/installer/installation_manager_test.rs
index 01a7513..7480a02 100644
--- a/crates/shirabe/tests/installer/installation_manager_test.rs
+++ b/crates/shirabe/tests/installer/installation_manager_test.rs
@@ -43,48 +43,38 @@ fn set_up() -> SetUp {
SetUp { loop_, io }
}
-/// Records of the calls a `CountingInstaller` received, shared so the test can inspect them
-/// after the installer has been moved into the InstallationManager. This reproduces PHPUnit's
-/// `expects($this->exactly(n))->method(...)->with(...)` assertions with explicit counters.
-#[derive(Debug, Default)]
-struct InstallerCalls {
- supports_args: Vec<String>,
- install: Vec<PackageInterfaceHandle>,
- uninstall: Vec<PackageInterfaceHandle>,
- update: Vec<(PackageInterfaceHandle, PackageInterfaceHandle)>,
-}
-
-/// Configurable `InstallerInterface` stub, equivalent to
-/// `getMockBuilder(InstallerInterface::class)->getMock()`. `supports` returns true only when the
-/// requested type equals `supported_type` (PHP uses a returnCallback comparing `$arg === 'vendor'`),
-/// and every method records its arguments into the shared `calls`.
-#[derive(Debug)]
-struct CountingInstaller {
- supported_type: String,
- calls: Rc<RefCell<InstallerCalls>>,
-}
-
-impl CountingInstaller {
- fn new(supported_type: &str) -> (Self, Rc<RefCell<InstallerCalls>>) {
- let calls = Rc::new(RefCell::new(InstallerCalls::default()));
- (
- Self {
- supported_type: supported_type.to_string(),
- calls: calls.clone(),
- },
- calls,
- )
+// Equivalent to `getMockBuilder(InstallerInterface::class)->getMock()`. mockall cannot generate an
+// `#[async_trait]` impl for the async methods that take `&mut dyn InstalledRepositoryInterface`
+// (the object lifetime async_trait inserts clashes with mockall's generated lifetimes), so the
+// expectations live on inherent methods and a thin hand-written InstallerInterface impl forwards to
+// them, dropping the unused `repo` argument exactly as the PHPUnit mock ignores it. The methods not
+// configured by any test (is_installed/download/prepare/cleanup/get_install_path, and the defaulted
+// as_binary_presence_interface/as_plugin_installer_mut) return the same defaults as an unconfigured
+// PHPUnit mock.
+mockall::mock! {
+ #[derive(Debug)]
+ pub Installer {
+ fn supports(&self, package_type: &str) -> bool;
+ fn install(
+ &mut self,
+ package: PackageInterfaceHandle,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ fn update(
+ &mut self,
+ initial: PackageInterfaceHandle,
+ target: PackageInterfaceHandle,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ fn uninstall(
+ &mut self,
+ package: PackageInterfaceHandle,
+ ) -> anyhow::Result<Option<PhpMixed>>;
}
}
#[async_trait::async_trait(?Send)]
-impl InstallerInterface for CountingInstaller {
+impl InstallerInterface for MockInstaller {
fn supports(&self, package_type: &str) -> bool {
- self.calls
- .borrow_mut()
- .supports_args
- .push(package_type.to_string());
- package_type == self.supported_type
+ MockInstaller::supports(self, package_type)
}
fn is_installed(
@@ -117,8 +107,7 @@ impl InstallerInterface for CountingInstaller {
_repo: &mut dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- self.calls.borrow_mut().install.push(package);
- Ok(None)
+ MockInstaller::install(self, package)
}
async fn update(
@@ -127,8 +116,7 @@ impl InstallerInterface for CountingInstaller {
initial: PackageInterfaceHandle,
target: PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- self.calls.borrow_mut().update.push((initial, target));
- Ok(None)
+ MockInstaller::update(self, initial, target)
}
async fn uninstall(
@@ -136,8 +124,7 @@ impl InstallerInterface for CountingInstaller {
_repo: &mut dyn InstalledRepositoryInterface,
package: PackageInterfaceHandle,
) -> anyhow::Result<Option<PhpMixed>> {
- self.calls.borrow_mut().uninstall.push(package);
- Ok(None)
+ MockInstaller::uninstall(self, package)
}
async fn cleanup(
@@ -283,7 +270,13 @@ fn same_handle(a: &PackageInterfaceHandle, b: &PackageInterfaceHandle) -> bool {
#[test]
fn test_add_get_installer() {
let set_up = set_up();
- let (installer, calls) = CountingInstaller::new("vendor");
+ let mut installer = MockInstaller::new();
+ // PHP expects supports() to be called exactly twice (once for the cached 'vendor' lookup,
+ // once for the failing 'unregistered' lookup), returning true only for 'vendor'.
+ installer
+ .expect_supports()
+ .times(2)
+ .returning(|arg| arg == "vendor");
let mut manager =
shirabe::installer::InstallationManager::new(set_up.loop_.clone(), set_up.io.clone(), None);
@@ -292,10 +285,6 @@ fn test_add_get_installer() {
assert!(manager.get_installer("vendor").is_ok());
assert!(manager.get_installer("unregistered").is_err());
-
- // PHP expects supports() to be called exactly twice (once for the cached 'vendor' lookup,
- // once for the failing 'unregistered' lookup).
- assert_eq!(calls.borrow().supports_args.len(), 2);
}
#[ignore = "removeInstaller compares installers by object identity, but add_installer moves the Box<dyn InstallerInterface> into the manager, leaving no &dyn reference to pass back to remove_installer; faithful reproduction needs a shared-ownership installer registry"]
@@ -313,87 +302,131 @@ fn test_execute() {
#[test]
fn test_install() {
let set_up = set_up();
- let (installer, calls) = CountingInstaller::new("library");
+ let package = get_package("test/pkg", "1.0.0");
+
+ let mut installer = MockInstaller::new();
+ installer
+ .expect_supports()
+ .times(1)
+ .withf(|package_type| package_type == "library")
+ .returning(|_| true);
+ let expected = package.clone();
+ installer
+ .expect_install()
+ .times(1)
+ .withf_st(move |package| same_handle(package, &expected))
+ .returning(|_| Ok(None));
+
let mut manager =
shirabe::installer::InstallationManager::new(set_up.loop_.clone(), set_up.io.clone(), None);
manager.add_installer(Box::new(installer));
- let package = get_package("test/pkg", "1.0.0");
let operation = InstallOperation::new(package.clone());
let mut repository = InstalledArrayRepository::new().unwrap();
- // install() returns the (empty) installer promise; the call is observed via the recorded args.
run(manager.install(&mut repository, &operation));
-
- assert_eq!(calls.borrow().supports_args, vec!["library".to_string()]);
- assert_eq!(calls.borrow().install.len(), 1);
- assert!(same_handle(&calls.borrow().install[0], &package));
}
#[test]
fn test_update_with_equal_types() {
let set_up = set_up();
- let (installer, calls) = CountingInstaller::new("library");
+ let initial = get_package("test/initial", "1.0.0");
+ let target = get_package("test/target", "1.0.1");
+
+ let mut installer = MockInstaller::new();
+ installer
+ .expect_supports()
+ .times(1)
+ .withf(|package_type| package_type == "library")
+ .returning(|_| true);
+ let expected_initial = initial.clone();
+ let expected_target = target.clone();
+ installer
+ .expect_update()
+ .times(1)
+ .withf_st(move |initial, target| {
+ same_handle(initial, &expected_initial) && same_handle(target, &expected_target)
+ })
+ .returning(|_, _| Ok(None));
+
let mut manager =
shirabe::installer::InstallationManager::new(set_up.loop_.clone(), set_up.io.clone(), None);
manager.add_installer(Box::new(installer));
- let initial = get_package("test/initial", "1.0.0");
- let target = get_package("test/target", "1.0.1");
let operation = UpdateOperation::new(initial.clone(), target.clone());
let mut repository = InstalledArrayRepository::new().unwrap();
run(manager.update(&mut repository, &operation));
-
- assert_eq!(calls.borrow().supports_args, vec!["library".to_string()]);
- assert_eq!(calls.borrow().update.len(), 1);
- assert!(same_handle(&calls.borrow().update[0].0, &initial));
- assert!(same_handle(&calls.borrow().update[0].1, &target));
}
#[test]
fn test_update_with_not_equal_types() {
let set_up = set_up();
- let (lib_installer, lib_calls) = CountingInstaller::new("library");
- let (bundle_installer, bundle_calls) = CountingInstaller::new("bundles");
+ let initial = typed_package("test/initial", "1.0.0", "library");
+ let target = typed_package("test/target", "1.0.1", "bundles");
+
+ let mut lib_installer = MockInstaller::new();
+ lib_installer
+ .expect_supports()
+ .times(1)
+ .withf(|package_type| package_type == "library")
+ .returning(|_| true);
+ let expected_initial = initial.clone();
+ lib_installer
+ .expect_uninstall()
+ .times(1)
+ .withf_st(move |package| same_handle(package, &expected_initial))
+ .returning(|_| Ok(None));
+
+ let mut bundle_installer = MockInstaller::new();
+ bundle_installer
+ .expect_supports()
+ .times(2)
+ .returning(|arg| arg == "bundles");
+ let expected_target = target.clone();
+ bundle_installer
+ .expect_install()
+ .times(1)
+ .withf_st(move |package| same_handle(package, &expected_target))
+ .returning(|_| Ok(None));
+
let mut manager =
shirabe::installer::InstallationManager::new(set_up.loop_.clone(), set_up.io.clone(), None);
manager.add_installer(Box::new(lib_installer));
manager.add_installer(Box::new(bundle_installer));
- let initial = typed_package("test/initial", "1.0.0", "library");
- let target = typed_package("test/target", "1.0.1", "bundles");
let operation = UpdateOperation::new(initial.clone(), target.clone());
let mut repository = InstalledArrayRepository::new().unwrap();
run(manager.update(&mut repository, &operation));
-
- // The lib installer uninstalls the initial package once.
- assert_eq!(lib_calls.borrow().uninstall.len(), 1);
- assert!(same_handle(&lib_calls.borrow().uninstall[0], &initial));
-
- // The bundle installer installs the target package once.
- assert_eq!(bundle_calls.borrow().install.len(), 1);
- assert!(same_handle(&bundle_calls.borrow().install[0], &target));
}
#[test]
fn test_uninstall() {
let set_up = set_up();
- let (installer, calls) = CountingInstaller::new("library");
+ let package = get_package("test/pkg", "1.0.0");
+
+ let mut installer = MockInstaller::new();
+ installer
+ .expect_supports()
+ .times(1)
+ .withf(|package_type| package_type == "library")
+ .returning(|_| true);
+ let expected = package.clone();
+ installer
+ .expect_uninstall()
+ .times(1)
+ .withf_st(move |package| same_handle(package, &expected))
+ .returning(|_| Ok(None));
+
let mut manager =
shirabe::installer::InstallationManager::new(set_up.loop_.clone(), set_up.io.clone(), None);
manager.add_installer(Box::new(installer));
- let package = get_package("test/pkg", "1.0.0");
let operation = UninstallOperation::new(package.clone());
let mut repository = InstalledArrayRepository::new().unwrap();
run(manager.uninstall(&mut repository, &operation));
-
- assert_eq!(calls.borrow().supports_args, vec!["library".to_string()]);
- assert_eq!(calls.borrow().uninstall.len(), 1);
- assert!(same_handle(&calls.borrow().uninstall[0], &package));
}
#[test]
diff --git a/crates/shirabe/tests/installer/library_installer_test.rs b/crates/shirabe/tests/installer/library_installer_test.rs
index f519764..b91ec51 100644
--- a/crates/shirabe/tests/installer/library_installer_test.rs
+++ b/crates/shirabe/tests/installer/library_installer_test.rs
@@ -11,19 +11,87 @@ use shirabe::composer::{
ComposerHandle, PartialComposerHandle, PartialComposerWeakHandle, PartialOrFullComposer,
};
use shirabe::config::Config;
-use shirabe::downloader::DownloadManager;
-use shirabe::installer::{InstallerInterface, LibraryInstaller};
+use shirabe::downloader::{DownloadManagerInterface, DownloaderInterface};
+use shirabe::installer::{BinaryInstallerInterface, InstallerInterface, LibraryInstaller};
use shirabe::io::IOInterface;
use shirabe::io::null_io::NullIO;
+use shirabe::package::PackageInterfaceHandle;
use shirabe::repository::InstalledArrayRepository;
use shirabe::repository::RepositoryInterface;
use shirabe::repository::WritableRepositoryInterface;
use shirabe::util::filesystem::Filesystem;
use shirabe_php_shim::PhpMixed;
-use crate::downloader_stub::{DownloaderCall, DownloaderStub};
use crate::test_case::get_package;
+// PHP mocks `Composer\Downloader\DownloadManager` with getMockBuilder and asserts its
+// install/update/remove calls; here the equivalent mock is injected into the Composer via
+// setDownloadManager.
+mockall::mock! {
+ #[derive(Debug)]
+ pub DownloadManager {}
+ #[async_trait::async_trait(?Send)]
+ impl DownloadManagerInterface for DownloadManager {
+ fn set_prefer_source(&mut self, prefer_source: bool);
+ fn set_prefer_dist(&mut self, prefer_dist: bool);
+ fn get_downloader_for_package(
+ &self,
+ package: PackageInterfaceHandle,
+ ) -> anyhow::Result<Option<Rc<RefCell<dyn DownloaderInterface>>>>;
+ async fn download(
+ &self,
+ package: PackageInterfaceHandle,
+ target_dir: &str,
+ prev_package: Option<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ async fn prepare(
+ &self,
+ r#type: &str,
+ package: PackageInterfaceHandle,
+ target_dir: &str,
+ prev_package: Option<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ async fn install(
+ &self,
+ package: PackageInterfaceHandle,
+ target_dir: &str,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ async fn update(
+ &self,
+ initial: PackageInterfaceHandle,
+ target: PackageInterfaceHandle,
+ target_dir: &str,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ async fn remove(
+ &self,
+ package: PackageInterfaceHandle,
+ target_dir: &str,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ async fn cleanup(
+ &self,
+ r#type: &str,
+ package: PackageInterfaceHandle,
+ target_dir: &str,
+ prev_package: Option<PackageInterfaceHandle>,
+ ) -> anyhow::Result<Option<PhpMixed>>;
+ }
+}
+
+// PHP mocks `Composer\Installer\BinaryInstaller`; the Rust seam is `BinaryInstallerInterface`.
+mockall::mock! {
+ #[derive(Debug)]
+ pub BinaryInstaller {}
+ impl BinaryInstallerInterface for BinaryInstaller {
+ fn install_binaries(
+ &mut self,
+ package: PackageInterfaceHandle,
+ install_path: &str,
+ warn_on_overwrite: bool,
+ );
+ fn remove_binaries(&mut self, package: PackageInterfaceHandle);
+ }
+}
+
fn run<F: std::future::Future>(future: F) -> F::Output {
tokio::runtime::Builder::new_current_thread()
.build()
@@ -31,10 +99,10 @@ fn run<F: std::future::Future>(future: F) -> F::Output {
.block_on(future)
}
-/// 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.
+/// Mirror of setUp(): builds the Composer/Config over temp root/vendor/bin dirs plus a NullIO and a
+/// DownloadManager mock. `composer_full` keeps the inner Rc alive for the duration of the test since
+/// LibraryInstaller only holds a weak handle, and lets tests swap in a configured DownloadManager
+/// mock before constructing the installer.
struct SetUp {
root: TempDir,
vendor_dir: String,
@@ -42,10 +110,16 @@ struct SetUp {
io: Rc<RefCell<dyn IOInterface>>,
composer: PartialComposerWeakHandle,
fs: Filesystem,
- /// Recorded downloader operations forwarded by the LibraryInstaller, mirroring the
- /// PHP DownloadManager mock's expectations on install/update/remove.
- downloader_calls: Rc<RefCell<Vec<DownloaderCall>>>,
- _composer_rc: ComposerHandle,
+ composer_full: ComposerHandle,
+}
+
+/// Replaces the Composer's DownloadManager with the given mock. Must be called before
+/// `LibraryInstaller::new`, which captures the manager at construction time.
+fn set_download_manager(setup: &SetUp, dm: MockDownloadManager) {
+ setup
+ .composer_full
+ .borrow_mut()
+ .set_download_manager(Rc::new(RefCell::new(dm)));
}
fn set_up() -> SetUp {
@@ -77,16 +151,15 @@ fn set_up() -> SetUp {
let io: Rc<RefCell<dyn IOInterface>> = Rc::new(RefCell::new(NullIO::new()));
- let mut dm = DownloadManager::new(io.clone(), false, None);
- let downloader = DownloaderStub::new();
- let downloader_calls = downloader.calls();
- dm.set_downloader("fake", Rc::new(RefCell::new(downloader)));
- 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);
+ // Default unconfigured mock so LibraryInstaller::new can resolve a DownloadManager even in
+ // tests that exercise no downloader call; tests that assert calls install their own via
+ // set_download_manager.
+ composer
+ .borrow_mut()
+ .set_download_manager(Rc::new(RefCell::new(MockDownloadManager::new())));
let weak = PartialComposerHandle::from_rc(composer_rc).downgrade();
@@ -97,21 +170,10 @@ fn set_up() -> SetUp {
io,
composer: weak,
fs,
- downloader_calls,
- _composer_rc: composer,
+ composer_full: composer,
}
}
-/// Builds a `dist`-installable test package backed by the `fake` downloader stub, so the
-/// real DownloadManager dispatches install/update/remove to the recording stub instead of
-/// erroring on a package with no installation source.
-fn get_installable_package(name: &str, version: &str) -> shirabe::package::PackageInterfaceHandle {
- let package = get_package(name, version);
- package.set_installation_source(Some("dist".to_string()));
- package.set_dist_type(Some("fake".to_string()));
- package
-}
-
fn tear_down(setup: &mut SetUp) {
let root = setup.root.path().to_path_buf();
setup.fs.remove_directory(&root).ok();
@@ -167,24 +229,29 @@ fn test_is_installed() {
#[test]
fn test_install() {
let mut setup = set_up();
+ let package = get_package("some/package", "1.0.0");
+
+ // PHP asserts the DownloadManager mock's install() is called once with
+ // ($package, vendorDir/some/package).
+ let mut dm = MockDownloadManager::new();
+ let expected_package = package.clone();
+ let expected_path = format!("{}/some/package", setup.vendor_dir);
+ dm.expect_install()
+ .times(1)
+ .withf_st(move |package, target_dir| {
+ Rc::ptr_eq(package.as_rc(), expected_package.as_rc())
+ && target_dir == expected_path.as_str()
+ })
+ .returning(|_, _| Ok(None));
+ set_download_manager(&setup, dm);
+
let mut library =
LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
- let package = get_installable_package("some/package", "1.0.0");
- // PHP asserts the DownloadManager mock's install() is called once with
- // ($package, vendorDir/some/package); here the recording downloader stub
- // captures the forwarded call instead.
let mut repository = InstalledArrayRepository::new().unwrap();
run(library.install(&mut repository, package.clone())).unwrap();
- assert_eq!(
- vec![DownloaderCall::Install {
- package: "some/package".to_string(),
- path: format!("{}/some/package", setup.vendor_dir),
- }],
- *setup.downloader_calls.borrow()
- );
// PHP asserts repository->addPackage was called once with $package.
assert!(repository.has_package(package));
@@ -204,8 +271,8 @@ fn test_install() {
fn test_update() {
let mut setup = set_up();
- let initial = get_installable_package("vendor/package1", "1.0.0");
- let target = get_installable_package("vendor/package1", "2.0.0");
+ let initial = get_package("vendor/package1", "1.0.0");
+ let target = get_package("vendor/package1", "2.0.0");
initial.__set_target_dir(Some("oldtarget".to_string()));
target.__set_target_dir(Some("newtarget".to_string()));
@@ -217,6 +284,22 @@ fn test_update() {
let new_target_dir = format!("{}/vendor/package1/newtarget", setup.vendor_dir);
fs::create_dir_all(&old_target_dir).unwrap();
+ // PHP asserts the DownloadManager mock's update() is called once with
+ // ($initial, $target, vendorDir/vendor/package1/newtarget).
+ let mut dm = MockDownloadManager::new();
+ let expected_initial = initial.clone();
+ let expected_target = target.clone();
+ let expected_path = new_target_dir.clone();
+ dm.expect_update()
+ .times(1)
+ .withf_st(move |initial, target, target_dir| {
+ Rc::ptr_eq(initial.as_rc(), expected_initial.as_rc())
+ && Rc::ptr_eq(target.as_rc(), expected_target.as_rc())
+ && target_dir == expected_path.as_str()
+ })
+ .returning(|_, _, _| Ok(None));
+ set_download_manager(&setup, dm);
+
let mut repository = InstalledArrayRepository::new().unwrap();
repository.add_package(initial.clone()).unwrap();
@@ -232,14 +315,6 @@ fn test_update() {
);
assert!(!std::path::Path::new(&old_target_dir).exists());
- assert_eq!(
- vec![DownloaderCall::Update {
- initial: "vendor/package1".to_string(),
- target: "vendor/package1".to_string(),
- path: new_target_dir.clone(),
- }],
- *setup.downloader_calls.borrow()
- );
assert!(!repository.has_package(initial.clone()));
assert!(repository.has_package(target.clone()));
@@ -261,9 +336,24 @@ fn test_update() {
#[test]
fn test_uninstall() {
let mut setup = set_up();
+ let package = get_package("vendor/pkg", "1.0.0");
+
+ // PHP asserts the DownloadManager mock's remove() is called once with
+ // ($package, vendorDir/vendor/pkg).
+ let mut dm = MockDownloadManager::new();
+ let expected_package = package.clone();
+ let expected_path = format!("{}/vendor/pkg", setup.vendor_dir);
+ dm.expect_remove()
+ .times(1)
+ .withf_st(move |package, target_dir| {
+ Rc::ptr_eq(package.as_rc(), expected_package.as_rc())
+ && target_dir == expected_path.as_str()
+ })
+ .returning(|_, _| Ok(None));
+ set_download_manager(&setup, dm);
+
let mut library =
LibraryInstaller::new(setup.io.clone(), setup.composer.clone(), None, None, None);
- let package = get_installable_package("vendor/pkg", "1.0.0");
// PHP mocks hasPackage to return (true, false) over two calls; a real repository
// seeded with the package reproduces this naturally: present, then absent after
@@ -273,13 +363,6 @@ fn test_uninstall() {
run(library.uninstall(&mut repository, package.clone())).unwrap();
- assert_eq!(
- vec![DownloaderCall::Remove {
- package: "vendor/pkg".to_string(),
- path: format!("{}/vendor/pkg", setup.vendor_dir),
- }],
- *setup.downloader_calls.borrow()
- );
assert!(!repository.has_package(package.clone()));
// Uninstalling again, with the package no longer installed, fails.
@@ -323,42 +406,9 @@ fn test_get_install_path_with_target_dir() {
tear_down(&mut setup);
}
-/// Records the calls a `BinaryInstaller` double receives, standing in for the PHPUnit mock that
-/// asserts `removeBinaries` is never called and `installBinaries` is called once.
-#[derive(Debug, Default)]
-struct BinaryInstallerCalls {
- install_binaries: Vec<(shirabe::package::PackageInterfaceHandle, String, bool)>,
- remove_binaries: Vec<shirabe::package::PackageInterfaceHandle>,
-}
-
-#[derive(Debug)]
-struct RecordingBinaryInstaller {
- calls: Rc<RefCell<BinaryInstallerCalls>>,
-}
-
-impl shirabe::installer::BinaryInstallerInterface for RecordingBinaryInstaller {
- fn install_binaries(
- &mut self,
- package: shirabe::package::PackageInterfaceHandle,
- install_path: &str,
- warn_on_overwrite: bool,
- ) {
- self.calls.borrow_mut().install_binaries.push((
- package,
- install_path.to_string(),
- warn_on_overwrite,
- ));
- }
-
- fn remove_binaries(&mut self, package: shirabe::package::PackageInterfaceHandle) {
- self.calls.borrow_mut().remove_binaries.push(package);
- }
-}
-
#[test]
fn test_ensure_binaries_installed() {
let mut setup = set_up();
- let calls = Rc::new(RefCell::new(BinaryInstallerCalls::default()));
let mut library = LibraryInstaller::new(
setup.io.clone(),
setup.composer.clone(),
@@ -366,26 +416,27 @@ fn test_ensure_binaries_installed() {
None,
None,
);
- library.__set_binary_installer(std::rc::Rc::new(std::cell::RefCell::new(
- RecordingBinaryInstaller {
- calls: calls.clone(),
- },
- )));
let package = get_package("foo/bar", "1.0.0");
let expected_path = library.get_install_path(package.clone()).unwrap();
- library.ensure_binaries_presence(package.clone());
-
- let recorded = calls.borrow();
+ let mut binary_installer = MockBinaryInstaller::new();
// PHP asserts removeBinaries is never called.
- assert!(recorded.remove_binaries.is_empty());
+ binary_installer.expect_remove_binaries().times(0);
// PHP asserts installBinaries is called once with ($package, getInstallPath, false).
- assert_eq!(recorded.install_binaries.len(), 1);
- let (recorded_package, recorded_path, warn_on_overwrite) = &recorded.install_binaries[0];
- assert!(Rc::ptr_eq(recorded_package.as_rc(), package.as_rc()));
- assert_eq!(recorded_path, &expected_path);
- assert!(!warn_on_overwrite);
- drop(recorded);
+ let expected_package = package.clone();
+ let expected_install_path = expected_path.clone();
+ binary_installer
+ .expect_install_binaries()
+ .times(1)
+ .withf_st(move |package, install_path, warn_on_overwrite| {
+ Rc::ptr_eq(package.as_rc(), expected_package.as_rc())
+ && install_path == expected_install_path.as_str()
+ && !*warn_on_overwrite
+ })
+ .returning(|_, _, _| ());
+ library.__set_binary_installer(Rc::new(RefCell::new(binary_installer)));
+
+ library.ensure_binaries_presence(package.clone());
tear_down(&mut setup);
}
diff --git a/crates/shirabe/tests/installer/main.rs b/crates/shirabe/tests/installer/main.rs
index a5ca671..4b42278 100644
--- a/crates/shirabe/tests/installer/main.rs
+++ b/crates/shirabe/tests/installer/main.rs
@@ -1,5 +1,3 @@
-#[path = "../common/downloader_stub.rs"]
-mod downloader_stub;
#[path = "../common/io_mock.rs"]
mod io_mock;
#[path = "../common/test_case.rs"]