aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/shirabe/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/shirabe/src')
-rw-r--r--crates/shirabe/src/event_dispatcher/event_dispatcher.rs30
-rw-r--r--crates/shirabe/src/io/buffer_io.rs64
-rw-r--r--crates/shirabe/src/package/handle.rs10
-rw-r--r--crates/shirabe/src/repository/composer_repository.rs66
4 files changed, 137 insertions, 33 deletions
diff --git a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
index 3ef27f8..2441be6 100644
--- a/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
+++ b/crates/shirabe/src/event_dispatcher/event_dispatcher.rs
@@ -70,6 +70,19 @@ pub struct EventDispatcher {
skip_scripts: Vec<String>,
previous_hash: Option<String>,
previous_listeners: IndexMap<String, bool>,
+ /// For testing only. Mirrors PHPUnit's `getMockBuilder(EventDispatcher)->onlyMethods(['getListeners'])`:
+ /// when set, `get_listeners` returns this closure's result verbatim instead of resolving
+ /// registered listeners and package scripts.
+ get_listeners_override: Option<GetListenersOverride>,
+}
+
+/// For testing only. Holds a closure standing in for an overridden `getListeners` method.
+pub struct GetListenersOverride(pub Box<dyn Fn(&dyn EventInterface) -> Vec<Callable>>);
+
+impl std::fmt::Debug for GetListenersOverride {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.write_str("GetListenersOverride(..)")
+ }
}
impl EventDispatcher {
@@ -101,9 +114,20 @@ impl EventDispatcher {
skip_scripts,
previous_hash: None,
previous_listeners: IndexMap::new(),
+ get_listeners_override: None,
}
}
+ /// For testing only. Installs a closure that overrides `get_listeners`, mirroring
+ /// PHPUnit's `onlyMethods(['getListeners'])->will($this->returnValue(...))` /
+ /// `->will($this->returnCallback(...))`.
+ pub fn __set_get_listeners_override(
+ &mut self,
+ callback: Box<dyn Fn(&dyn EventInterface) -> Vec<Callable>>,
+ ) {
+ self.get_listeners_override = Some(GetListenersOverride(callback));
+ }
+
/// Set whether script handlers are active or not
pub fn set_run_scripts(&mut self, run_scripts: bool) -> &mut Self {
self.run_scripts = run_scripts;
@@ -923,6 +947,12 @@ impl EventDispatcher {
/// Retrieves all listeners for a given event
fn get_listeners(&mut self, event: &dyn EventInterface) -> Vec<Callable> {
+ // For testing only: a test may override this method, mirroring PHPUnit's
+ // `onlyMethods(['getListeners'])`.
+ if let Some(override_cb) = &self.get_listeners_override {
+ return override_cb.0(event);
+ }
+
let script_listeners: Vec<Callable> = if self.run_scripts {
self.get_script_listeners(event)
} else {
diff --git a/crates/shirabe/src/io/buffer_io.rs b/crates/shirabe/src/io/buffer_io.rs
index 16b97e4..75de4c7 100644
--- a/crates/shirabe/src/io/buffer_io.rs
+++ b/crates/shirabe/src/io/buffer_io.rs
@@ -67,31 +67,49 @@ impl BufferIO {
let output = stream_get_contents(stream).unwrap_or_default();
- Preg::replace_callback(
- r"{(?<=^|\n|\x08)(.+?)(\x08+)}",
- |matches: &indexmap::IndexMap<
- shirabe_external_packages::composer::pcre::CaptureKey,
- String,
- >|
- -> String {
- let empty = String::new();
- let g1 = matches
- .get(&shirabe_external_packages::composer::pcre::CaptureKey::ByIndex(1))
- .unwrap_or(&empty);
- let g2 = matches
- .get(&shirabe_external_packages::composer::pcre::CaptureKey::ByIndex(2))
- .unwrap_or(&empty);
- let pre = strip_tags(g1);
+ // Regex pattern compatibility:
+ // PHP uses `{(?<=^|\n|\x08)(.+?)(\x08+)}` to collapse backspace-overwritten spans (e.g.
+ // progress bars). The `regex` crate has no look-behind, so the `(?<=^|\n|\x08)` anchor is
+ // turned into a consuming optional leading group `(^|\n|\x08)` that is re-emitted in the
+ // replacement. Because PCRE's look-behind is zero-width, a `\x08` ending one match can also
+ // anchor the following match; consuming-and-restoring would break that chaining in a single
+ // pass, so the replacement is applied to a fixpoint (each pass strictly shrinks the string).
+ let mut output = output;
+ loop {
+ let next = Preg::replace_callback(
+ r"{(^|\n|\x08)(.+?)(\x08+)}",
+ |matches: &indexmap::IndexMap<
+ shirabe_external_packages::composer::pcre::CaptureKey,
+ String,
+ >|
+ -> String {
+ let empty = String::new();
+ let g1 = matches
+ .get(&shirabe_external_packages::composer::pcre::CaptureKey::ByIndex(1))
+ .unwrap_or(&empty);
+ let g2 = matches
+ .get(&shirabe_external_packages::composer::pcre::CaptureKey::ByIndex(2))
+ .unwrap_or(&empty);
+ let g3 = matches
+ .get(&shirabe_external_packages::composer::pcre::CaptureKey::ByIndex(3))
+ .unwrap_or(&empty);
+ let pre = strip_tags(g2);
- if pre.len() == g2.len() {
- return String::new();
- }
+ if pre.len() == g3.len() {
+ return g1.clone();
+ }
- // TODO reverse parse the string, skipping span tags and \033\[([0-9;]+)m(.*?)\033\[0m style blobs
- format!("{}\n", g1.trim_end())
- },
- &output,
- )
+ // TODO reverse parse the string, skipping span tags and \033\[([0-9;]+)m(.*?)\033\[0m style blobs
+ format!("{}{}\n", g1, g2.trim_end())
+ },
+ &output,
+ );
+ if next == output {
+ break;
+ }
+ output = next;
+ }
+ output
}
pub fn set_user_inputs(&mut self, inputs: Vec<String>) -> Result<()> {
diff --git a/crates/shirabe/src/package/handle.rs b/crates/shirabe/src/package/handle.rs
index 665efb6..8a6ed83 100644
--- a/crates/shirabe/src/package/handle.rs
+++ b/crates/shirabe/src/package/handle.rs
@@ -1222,10 +1222,20 @@ macro_rules! impl_real_package_test_setters {
.expect("real package handle invariant")
.set_source_type(r#type);
}
+
+ /// For testing only: mirrors PHP `Package::setDistSha1Checksum`.
+ pub fn __set_dist_sha1_checksum(&self, sha1checksum: Option<String>) {
+ self.0
+ .borrow_mut()
+ .as_package_mut()
+ .expect("real package handle invariant")
+ .set_dist_sha1_checksum(sha1checksum);
+ }
}
};
}
+impl_real_package_test_setters!(PackageInterfaceHandle);
impl_real_package_test_setters!(PackageHandle);
impl_real_package_test_setters!(CompletePackageHandle);
impl_real_package_test_setters!(RootPackageHandle);
diff --git a/crates/shirabe/src/repository/composer_repository.rs b/crates/shirabe/src/repository/composer_repository.rs
index 4428518..3314ebd 100644
--- a/crates/shirabe/src/repository/composer_repository.rs
+++ b/crates/shirabe/src/repository/composer_repository.rs
@@ -414,6 +414,13 @@ impl ComposerRepository {
}.into());
}
+ // PHP relies on ArrayRepository::getPackages() invoking the virtual initialize(),
+ // which ComposerRepository overrides. Embedded inheritance does not dispatch back to
+ // the wrapper, so initialize the wrapper explicitly before delegating.
+ if !self.inner.is_initialized() {
+ self.initialize()?;
+ }
+
self.inner.get_packages()
}
@@ -1461,12 +1468,13 @@ impl ComposerRepository {
let mut result: IndexMap<String, BasePackageHandle> = IndexMap::new();
let mut versions_to_load: IndexMap<String, IndexMap<String, PhpMixed>> = IndexMap::new();
- let packages_inner = packages
- .get("packages")
- .and_then(|v| v.as_array())
- .cloned()
- .unwrap_or_default();
- for (_pkg_key, versions_mixed) in packages_inner.iter() {
+ // $packages['packages'] can be either array<string, mixed> or list<mixed>
+ let packages_inner: Vec<PhpMixed> = match packages.get("packages") {
+ Some(PhpMixed::Array(a)) => a.values().cloned().collect(),
+ Some(PhpMixed::List(l)) => l.clone(),
+ _ => Vec::new(),
+ };
+ for versions_mixed in packages_inner.iter() {
// $versions can be either array<string, array> or list<array>
let iter_versions: Vec<PhpMixed> = match versions_mixed {
PhpMixed::Array(a) => a.values().map(|v| v.clone()).collect(),
@@ -1633,6 +1641,42 @@ impl ComposerRepository {
.map(RepositoryInterfaceHandle::from_rc)
}
+ /// For testing only. Exposes the private `whatProvides` method, mirroring the
+ /// `ReflectionMethod` invocation in `ComposerRepositoryTest::testWhatProvides`.
+ pub fn __what_provides(
+ &mut self,
+ name: &str,
+ ) -> anyhow::Result<IndexMap<String, BasePackageHandle>> {
+ self.what_provides(name, None, None, IndexMap::new())
+ }
+
+ /// For testing only. Exposes the private `canonicalizeUrl` method, mirroring the
+ /// `ReflectionMethod` invocation in `ComposerRepositoryTest::testCanonicalizeUrl`.
+ pub fn __canonicalize_url(&self, url: &str) -> anyhow::Result<String> {
+ self.canonicalize_url(url)
+ }
+
+ /// For testing only. Sets the private `url` property, mirroring the
+ /// `ReflectionProperty` write in `ComposerRepositoryTest::testCanonicalizeUrl`.
+ pub fn __set_url(&mut self, url: impl Into<String>) {
+ self.url = url.into();
+ }
+
+ /// For testing only. Sets the private `providerListing` property, mirroring the
+ /// `ReflectionProperty` write in `ComposerRepositoryTest::testWhatProvides`.
+ pub fn __set_provider_listing(
+ &mut self,
+ provider_listing: IndexMap<String, ProviderListingEntry>,
+ ) {
+ self.provider_listing = Some(provider_listing);
+ }
+
+ /// For testing only. Sets the private `providersUrl` property, mirroring the
+ /// `ReflectionProperty` write in `ComposerRepositoryTest::testWhatProvides`.
+ pub fn __set_providers_url(&mut self, providers_url: impl Into<String>) {
+ self.providers_url = Some(providers_url.into());
+ }
+
/// @param packageNames array of package name => ConstraintInterface|null - if a constraint is provided, only packages matching it will be loaded
fn load_async_packages(
&mut self,
@@ -2474,11 +2518,13 @@ impl ComposerRepository {
if let Some(pkgs) = data.get("packages").and_then(|v| v.as_array()).cloned() {
for (package, versions_mixed) in pkgs.iter() {
let package_name = strtolower(package);
- let versions = match versions_mixed.as_array() {
- Some(a) => a.clone(),
- None => continue,
+ // $versions can be either array<string, array> or list<array>
+ let versions: Vec<PhpMixed> = match versions_mixed {
+ PhpMixed::Array(a) => a.values().cloned().collect(),
+ PhpMixed::List(l) => l.clone(),
+ _ => continue,
};
- for (_version, metadata_mixed) in versions.iter() {
+ for metadata_mixed in versions.iter() {
let metadata = match metadata_mixed.as_array() {
Some(a) => a.clone(),
None => continue,