aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/tests/downloader/hg_downloader_test.rs
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-25 17:49:35 +0900
committernsfisis <nsfisis@gmail.com>2026-06-26 00:20:05 +0900
commitd4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad (patch)
tree0a85bd085961fcdfd5570cafc3b44ba5eb8cb152 /crates/shirabe/tests/downloader/hg_downloader_test.rs
parent22d0b327368e5f39de6f381046c081d08efdba15 (diff)
downloadphp-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.gz
php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.tar.zst
php-shirabe-d4ec1dcb6fc04ff029ded35cc2fcc14f3bbc02ad.zip
test: port 32 command/repository/downloader tests
Add create_installed_json/create_composer_lock test helpers. Port command (8), repository path/forgejo/perforce/vcs (11), and fossil/hg/download_manager (13) tests. Fix production porting bugs: root_package_loader/forgejo_url/version_bumper regex delimiters, repository_manager create_repository_by_class, array_loader isset, licenses_command RefCell borrow; implement disk_free_space and touch2/touch3 via libc. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Diffstat (limited to 'crates/shirabe/tests/downloader/hg_downloader_test.rs')
-rw-r--r--crates/shirabe/tests/downloader/hg_downloader_test.rs172
1 files changed, 156 insertions, 16 deletions
diff --git a/crates/shirabe/tests/downloader/hg_downloader_test.rs b/crates/shirabe/tests/downloader/hg_downloader_test.rs
index 67a13cb..c331533 100644
--- a/crates/shirabe/tests/downloader/hg_downloader_test.rs
+++ b/crates/shirabe/tests/downloader/hg_downloader_test.rs
@@ -1,8 +1,28 @@
//! ref: composer/tests/Composer/Test/Downloader/HgDownloaderTest.php
+use std::cell::RefCell;
+use std::rc::Rc;
+
+use shirabe::config::Config;
+use shirabe::downloader::VcsDownloader;
+use shirabe::downloader::hg_downloader::HgDownloader;
+use shirabe::io::IOInterface;
+use shirabe::package::handle::{CompletePackageHandle, PackageInterfaceHandle};
+use shirabe::util::ProcessExecutor;
use shirabe::util::filesystem::Filesystem;
+use shirabe_semver::VersionParser;
use tempfile::TempDir;
+use crate::io_stub::IOStub;
+use crate::process_executor_mock::{cmd, get_process_executor_mock};
+
+fn run<F: std::future::Future>(future: F) -> F::Output {
+ tokio::runtime::Builder::new_current_thread()
+ .build()
+ .unwrap()
+ .block_on(future)
+}
+
fn set_up() -> TempDir {
TempDir::new().unwrap()
}
@@ -30,48 +50,165 @@ impl Drop for TearDown {
}
}
-// Every case constructs an HgDownloader with a mocked IO/Config and a mocked
-// ProcessExecutor to feed hg command output; a real HttpDownloader reaches
-// curl_multi_init (todo!()), and ProcessExecutor mocking is not available.
+/// ref: TestCase::getMockBuilder('Composer\Package\PackageInterface')->getMock()
+///
+/// A real CompletePackage seeded with the stubbed values is a faithful stand-in
+/// for a PackageInterface mock as long as `getSourceUrls()` equals
+/// `[getSourceUrl()]`, which holds for every non-ignored case here.
+fn get_package(source_reference: Option<&str>, source_url: Option<&str>) -> PackageInterfaceHandle {
+ let norm_version = VersionParser.normalize("1.0.0", None).unwrap();
+ let package =
+ CompletePackageHandle::new("dummy/pkg".to_string(), norm_version, "1.0.0".to_string());
+ package.__set_source_type(Some("hg".to_string()));
+ package.set_source_reference(source_reference.map(|s| s.to_string()));
+ package.set_source_url(source_url.map(|s| s.to_string()));
+ package.into()
+}
+
+/// ref: HgDownloaderTest::getDownloaderMock
+///
+/// PHP uses a `getMockBuilder('Composer\Config')->getMock()`, whose `get()`
+/// returns null for everything; a default real `Config` resolves the only
+/// relevant key (`secure-http`) compatibly for the https URLs used here.
+fn get_downloader_mock(
+ io: Option<Rc<RefCell<dyn IOInterface>>>,
+ config: Option<Config>,
+ process: Rc<RefCell<ProcessExecutor>>,
+) -> HgDownloader {
+ let io =
+ io.unwrap_or_else(|| Rc::new(RefCell::new(IOStub::new())) as Rc<RefCell<dyn IOInterface>>);
+ let config = Rc::new(RefCell::new(
+ config.unwrap_or_else(|| Config::new(false, None)),
+ ));
+ let fs = Rc::new(RefCell::new(Filesystem::new(None)));
+ HgDownloader::new(io, config, process, fs)
+}
#[test]
-#[ignore = "needs PHPUnit mocks of IOInterface/Config/Filesystem/PackageInterface for getDownloaderMock; not available"]
fn test_download_for_package_without_source_reference() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
let _ = &working_dir;
- todo!()
+
+ let package = get_package(None, None);
+
+ let (process, _guard) = get_process_executor_mock(vec![], false, Default::default());
+ let mut downloader = get_downloader_mock(None, None, process);
+
+ let result = run(downloader.install(package, "/path"));
+ let e = result.expect_err("missing source reference should throw");
+ assert!(e.to_string().contains("missing reference information"));
}
#[test]
-#[ignore = "needs ProcessExecutorMock with expects() command-sequence assertions and PHPUnit PackageInterface mock; not available"]
fn test_download() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
- let _ = &working_dir;
- todo!()
+
+ let working_dir_str = working_dir.path().to_string_lossy().into_owned();
+ let package = get_package(Some("ref"), Some("https://mercurial.dev/l3l0/composer"));
+
+ let (process, _guard) = get_process_executor_mock(
+ vec![
+ cmd(vec![
+ "hg",
+ "clone",
+ "--",
+ "https://mercurial.dev/l3l0/composer",
+ &working_dir_str,
+ ]),
+ cmd(vec!["hg", "up", "--", "ref"]),
+ ],
+ true,
+ Default::default(),
+ );
+
+ let mut downloader = get_downloader_mock(None, None, process);
+ run(downloader.install(package, &working_dir_str)).unwrap();
}
#[test]
-#[ignore = "needs PHPUnit mocks of IOInterface/Config/Filesystem/PackageInterface for getDownloaderMock; not available"]
fn test_updatefor_package_without_source_reference() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
let _ = &working_dir;
- todo!()
+
+ let initial_package = get_package(Some("ref"), None);
+ let source_package = get_package(None, None);
+
+ let (process, _guard) = get_process_executor_mock(vec![], false, Default::default());
+ let mut downloader = get_downloader_mock(None, None, process);
+
+ let result = run(async {
+ downloader
+ .prepare(
+ "update",
+ source_package.clone(),
+ "/path",
+ Some(initial_package.clone()),
+ )
+ .await?;
+ downloader
+ .update(initial_package.clone(), source_package.clone(), "/path")
+ .await?;
+ downloader
+ .cleanup("update", source_package, "/path", Some(initial_package))
+ .await
+ });
+
+ let e = result.expect_err("missing source reference should throw");
+ assert!(e.to_string().contains("missing reference information"));
}
#[test]
-#[ignore = "needs ProcessExecutorMock with expects() command-sequence assertions and PHPUnit PackageInterface mock; not available"]
fn test_update() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
- let _ = &working_dir;
- todo!()
+
+ let working_dir_str = working_dir.path().to_string_lossy().into_owned();
+
+ let mut fs = Filesystem::new(None);
+ fs.ensure_directory_exists(&format!("{}/.hg", working_dir_str))
+ .unwrap();
+
+ let package = get_package(Some("ref"), Some("https://github.com/l3l0/composer"));
+
+ let (process, _guard) = get_process_executor_mock(
+ vec![
+ cmd(vec!["hg", "st"]),
+ cmd(vec!["hg", "pull", "--", "https://github.com/l3l0/composer"]),
+ cmd(vec!["hg", "up", "--", "ref"]),
+ ],
+ true,
+ Default::default(),
+ );
+
+ let mut downloader = get_downloader_mock(None, None, process);
+ run(async {
+ downloader
+ .prepare(
+ "update",
+ package.clone(),
+ &working_dir_str,
+ Some(package.clone()),
+ )
+ .await
+ .unwrap();
+ downloader
+ .update(package.clone(), package.clone(), &working_dir_str)
+ .await
+ .unwrap();
+ downloader
+ .cleanup("update", package.clone(), &working_dir_str, Some(package))
+ .await
+ .unwrap();
+ });
}
+#[ignore = "PHP mocks Filesystem::removeDirectoryAsync; with a real Filesystem the actual \
+ removeDirectoryAsync drives its own ProcessExecutor for `rm -rf` through the \
+ unported execute_async mock seam"]
#[test]
-#[ignore = "needs ProcessExecutorMock and PHPUnit Filesystem mock with removeDirectoryAsync expectation; not available"]
fn test_remove() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
@@ -80,10 +217,13 @@ fn test_remove() {
}
#[test]
-#[ignore = "needs PHPUnit mocks of IOInterface/Config/Filesystem for getDownloaderMock; not available"]
fn test_get_installation_source() {
let working_dir = set_up();
let _tear_down = TearDown::new(working_dir.path().to_path_buf());
let _ = &working_dir;
- todo!()
+
+ let (process, _guard) = get_process_executor_mock(vec![], false, Default::default());
+ let downloader = get_downloader_mock(None, None, process);
+
+ assert_eq!("source", downloader.get_installation_source());
}