| Age | Commit message (Collapse) | Author |
|
Add the __add_installer test seam that registers an installer as a
pre-built Rc handle, so the test can reproduce PHP's object-identity
semantics (assertSame / removeInstaller) via Rc::ptr_eq; add_installer
cannot serve because Rc::from(Box) reallocates, losing the caller's
pointer identity.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Re-verified the reason: besides the missing as_any seam on
EventInterface (a cross-cutting trait change over every event type),
the mocked dispatchScript call expectation is also inexpressible since
dispatch_script is a concrete method with no call-recording seam.
Record both blockers in the ignore string and TODO(phase-d) comment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The ignore reason had rotted: ConsoleOutputInterface::set_error_output
is already ported, so a BufferedOutput error sink can be injected into
a real ConsoleOutput and the error-routed write read back, matching the
file's convention of replacing PHPUnit mock expectations with
BufferedOutput assertions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The only missing seam was the PHP test's
ReflectionMethod(getPhpExecCommand) access; add the test-only
__get_php_exec_command wrapper and port the test on the existing
get_listeners override and process-executor mock infrastructure.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
PHP's BufferIO extends ConsoleIO, so $io instanceof ConsoleIO matches
it; the port models that inheritance as composition, making the plain
ConsoleIO downcast reject BufferIO and throw where PHP renders tables.
Also try a BufferIO downcast and unwrap its inner ConsoleIO, which
unblocks the two FORMAT_TABLE cases and un-ignores test_audit.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Spell out why the existing __new_mock seam cannot serve: it replaces
execute() wholesale (skipping the download step, ref
InstallationManagerMock), while the PHP test runs the real execute() and
spies only on the three per-operation methods it dispatches to. A
per-method spy on the real path would be a production design change.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The old reason blamed missing mock infrastructure, but a spy against
dyn AutoloadGeneratorInterface is perfectly writable. The actual blocker
is that make_autoloader (PHP makeAutoloader, called from doDispatch's
script branches, where setDevMode is invoked) is an intentional no-op in
the port, so set_dev_mode is never reached and a spy would observe
nothing.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Beyond the missing CommandCompletionTester harness, the PHP test's
expected suggestions depend on the Composer dev checkout environment
(its own composer.json/lock, installed vendor packages) and live
Packagist queries, which the port cannot reproduce without a fixture
environment.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The ignore reason went stale: the getListeners override seam and a real
ProcessExecutor wired to the IO already cover the PHP setup, and IOStub
is the PHPUnit IOInterface-mock equivalent. IOStub now records
writeError calls (writeRaw was already recorded) so the
expects(once)->with(...) spies on writeError/writeRaw can be reproduced
as call-list equality assertions.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The ignore reason went stale: the FilesystemMock seam added for
FileDownloaderTest already intercepts removeDirectoryAsync, and the
sibling fossil/hg testRemove ports use it. The seam now records the
removed directories instead of a bare call count so the PHP
->with($this->equalTo($this->workingDir)) argument assertion can be
reproduced faithfully.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The tests fail before any network access: DiagnoseCommand::check_platform
captures phpinfo() via ob_start()/ob_get_clean(), which are todo!() in
shirabe-php-shim, so the command panics before producing output. Record
that as the primary blocker alongside the live-network requirement.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
PHP's getRemoteContents calls file_get_contents unconditionally: the
same stream wrapper reads file:// URLs, plain local paths and network
schemes. The Rust port only handled the explicit "file" scheme, so a
scheme-less local packages.json repository failed with "file could not
be downloaded". Extend the local branch to empty schemes; the http(s)
stub is unchanged.
This clears the first blocker of the create-project functional test;
its ignore reason now documents the remaining ones (live network and
Glob::to_regex emitting PCRE lookaheads the regex crate cannot
compile), which need a user decision.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The ignore reason went stale: HttpDownloader is reqwest-based now, so
building it for HgDriver no longer reaches curl_multi_init() (todo!() in
shirabe-php-shim::curl). Test content unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
The file:// branch of RemoteFilesystem::get_remote_contents is now
implemented, so the dist download reaches the ZipArchive "is not a zip
archive" path the test asserts. Test content unchanged.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
validate_array judged emptiness via as_array(), which only matches
PhpMixed::Array, so a non-empty PhpMixed::List (e.g. a funding array) was
misjudged as empty and dropped, diverging from PHP's !count() check.
Match both Array and List.
The stale ignore reason on test_fund_command no longer applies:
init_temp_composer injects packagist:false so no network is reached, and
the downloader stack is reqwest-based (no curl shim todo!()).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
support
ProcessExecutor::execute_async's mock branch was an unimplemented
todo!(), blocking every test whose code path calls it (feature-branch
git diffing, system-unzip/7z fallback extraction). Implement it by:
- Adding Process::__mock (mirroring the existing ZipArchive::__mock
pattern) so execute_async can resolve with a fabricated, already-
terminated Process instead of spawning a real subprocess.
- Extracting the sync mock's expectation-matching logic into a shared
ProcessExecutor::mock_match, wrapping the mock state in a RefCell so
it works from execute_async's &self/&mut self receivers.
- Setting error_output/capture_output from the mock branch, matching
PHP's ProcessExecutorMock::executeAsync sharing doExecute with the
sync path; execute_async now takes &mut self for this (safe, since
the borrow only needs to live through the synchronous setup, not
across the .await).
- Turning a strict-mode expectation mismatch from a panic!() into a
shirabe_php_shim::RuntimeException Err, mirroring PHPUnit's
AssertionFailedError extending \RuntimeException: PHP call sites
that catch (\RuntimeException $e) around a mocked git/hg/svn call
(e.g. Git::get_mirror_default_branch, GitDriver::supports) treat a
mismatch as an ordinary recoverable failure, and now so does the
port. The RefMut is dropped before firing an expectation's optional
callback so a re-entrant callback doesn't panic on double-borrow.
Also fixes two real bugs found while porting test_private_repository_
no_interaction: GitHub::authorize_oauth and GitLab::authorize_oauth
checked their domains config via PhpMixed::as_array(), which only
matches the Array (map) variant, but github-domains/gitlab-domains
default to PhpMixed::List, so the check always returned false and OAuth
token lookup was silently skipped. Use the in_array shim instead,
matching PHP's in_array() semantics. Also fix Git::run_command's
"capture credentials from git remote -v" call, which used the panic-
swallowing execute_args wrapper instead of a fallible execute(), so a
mock mismatch there couldn't reach get_mirror_default_branch's catch.
Un-ignores:
- zip_downloader_test::test_system_unzip_only_{good,failed}
- zip_downloader_test::test_non_windows_fallback_{good,failed}
- event_dispatcher_test::test_dispatcher_outputs_error_on_failed_command
- root_package_loader_test::test_feature_branch_pretty_version
- version_guesser_test::test_guess_version_reads_and_respects_non_feature_branches_configuration_for_arbitrary_naming{,_regex}
- version_guesser_test::test_remote_branches_are_selected
- github_driver_test::test_private_repository_no_interaction (also
adds the missing #[serial], since it seeds the shared Git::VERSION
static that vcs_repository_test::test_load_versions depends on for
real)
- init_command_test::test_get_git_config, made deterministic by
pointing HOME at a throwaway dir with its own .gitconfig instead of
depending on the host's global git config
Deduplicates the GitVersionGuard/RestoreEnv test-drop-guard idioms into
tests/common/test_case.rs instead of reimplementing them per file.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
port
extract_stability_flags ported PHP's `isset($stabilityFlags[$name]) &&
$stabilityFlags[$name] > $stability` as `unwrap_or(i64::MAX) > stability`,
so the check always short-circuited to "already more unstable" and no flag
(e.g. from an explicit `*@dev` requirement) was ever recorded. Fixed using
Option::is_some_and, a direct translation of PHP's isset() && ... check.
Also fixes tests/common/test_case.rs's shared installation_manager()
helper, which built a real InstallationManager::new instead of the
__new_mock constructor (mirroring PHP's FactoryMock::createInstallationManager()),
so it always had zero installers registered and wrote install-path: null
into fixture installed.json files.
Un-ignores test_reinstall_command, test_locally_modified_packages_from_source/
_from_dist, and test_package_still_present_error_when_no_install_flag_used —
the first three were already passing (their #[ignore] reasons were stale),
the last is fixed by the test_case.rs change above.
Updates the #[ignore] reasons on installer_test.rs's three fixture-driven
integration tests to reflect their current state: the install pipeline now
runs end-to-end, but the ~189-fixture installer/ set still hits several
independent, unrelated bugs/gaps that need case-by-case triage rather than
a single fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
repo bugs
Replace the PhpMixed-based `$showWarnings` hack in VersionSelector::
findBestCandidate with a typed ShowWarnings enum (Always / Predicate),
letting ShowCommand::findLatestPackage pass its real closure instead of
hardcoding `true`. Fix the --no-dev branch in ShowCommand::execute,
which built `repos` from an empty package list instead of sharing the
same InstalledRepository as `installed_repo`. Pass repository handles
instead of pre-borrowed `&dyn RepositoryInterface` refs into get_package/
generate_package_tree/add_tree to stop a RefCell double-borrow panic on
--all/--locked. Add the missing CompletePackage/RootPackage
set_release_date setter so the outdated sorting-by-age test can set
fixture dates. Resolve OutputFormatterStyleStack::pop's empty-style
todo!() via clone_box(), and fix FileDownloader's cache-GC log call to
pass the VERY_VERBOSE verbosity PHP uses.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
abandoned propagation
The json format branch ignored search results entirely and always wrote
null. Encode results into the same name/description/abandoned/url shape
Composer's array-backed repositories produce. Also fix
ComposerRepository's RepositoryInterface::search adapter, which dropped
the abandoned field from raw API results even when present.
|
|
AliasPackage wrongly delegated several methods (id, names, unique
name, pretty string, full pretty version, repository, __toString)
to aliasOf, but PHP's AliasPackage inherits these from BasePackage
unmodified, so they must use the alias's own state instead. This
collapsed alias and aliasOf into the same SAT literal id, breaking
the solver's alias-resolution rules; un-ignore the two solver tests
that exposed it.
|
|
The Preg delimiter bug the ignore reason described was already fixed.
|
|
PHP matches the SPDX license-expression grammar with a single recursive
PCRE pattern ((?(DEFINE) subpatterns plus (?&name) recursion), which the
regex crate cannot express, so port it as a hand-written recursive-descent
parser instead. licenseid/licenseexceptionid dictionary lookups use
longest-match-first, guarded against accepting a short entry that is only
a coincidental prefix of a longer identifier run (e.g. "DOC" prefixing
"DocumentRef-..."), which a length-sorted greedy match alone would
misparse.
Un-ignores the two validating_array_loader tests that were blocked on
this todo!().
|
|
test_update was skipped for a stale reason; running it uncovered three
distinct bugs it was actually catching:
- ApplicationTester::run never restored SHELL_VERBOSITY after
Application::configureIO mutates it, so one dataset's -vv verbosity
leaked into later runs sharing the process (Symfony's tester restores
it in a finally block; the port dropped that).
- Installer::do_install built its RepositorySet with a hardcoded empty
temporary_constraints map instead of self.temporary_constraints, so
--with never actually constrained the resolver.
- BumpCommand was missing a <warning> tag pair around one of its output
lines.
|
|
actually run
Without this, Application::do_run force-disabled interactivity whenever
stdin wasn't a tty (as under cargo test), so ApplicationTester runs with
set_inputs silently produced non-interactive default output instead of
consuming the answers, masking real behavior as several stale #[ignore]s
blaming already-implemented ProcessExecutor/Process todo!()s.
Port composer/tests/bootstrap.php's env setup into a bootstrap() helper
called from get_application_tester(), un-ignore the now-passing
init/update command tests, and update init_command_test's expected
schema-validation wording to match the jsonschema crate (already the
accepted wording per 541a8b4f, not an unported gap).
|
|
PHP's printTable builds a 5-column row for text output, with the raw
Link object (cast via __toString) as its own column separate from the
formatted description string. The port had collapsed both into one
column, dropping an empty column for successful checks and throwing
off the rendered column widths/spacing versus real Composer output.
|
|
|
|
executeBatch now builds one future per operation — the PHP promise
chain prepare -> install/update/uninstall -> cleanup -> repo->write,
including the '<Op> of <pkg> failed' rejection handler covering the
chain up to cleanup — and drives the whole batch through
waitOnPromises()/Loop::wait, so archive extraction (the unzip
subprocesses gated by ProcessExecutor's semaphore) finally overlaps
across packages. Alias operations stay synchronous in the collection
loop like PHP.
The shared repository is threaded through the chains as
RefCell<&mut dyn InstalledRepositoryInterface>: execute() wraps the
incoming &mut once, and InstallerInterface::install/update/uninstall
take the cell so implementations borrow it only in their synchronous
head/tail, never across an await. InstallationManager's own
install/update/uninstall/download/get_installer/get_install_path/
mark_for_notification move to &self (cache and notifiable_packages
behind RefCell) so every chain can capture &self.
WritableRepositoryInterface::write and the InstallationManagerInterface
get_install_path it relies on lose their &mut manager requirement —
the per-op repo->write inside the chains only reads install paths.
Warm-cache create-project laravel/laravel: the package-operations
phase (109 installs) drops from ~3.0-3.8s serial to ~1.9s, on par
with real Composer (~2.1s) measured back-to-back; the resulting
vendor tree, installed.json included, stays byte-identical to
Composer's (diff -rq clean).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
InstallationManager::downloadAndExecuteBatch now matches PHP: every
update/install operation's installer->download() promise is collected
and driven concurrently through waitOnPromises()/Loop::wait instead of
being awaited one package at a time. Concurrency caps stay where PHP
puts them (HttpDownloader 12, ProcessExecutor 10 via their semaphores).
Error semantics follow PHP too: all downloads settle before the first
rejection is rethrown, rather than aborting on the first failure.
To let the collected futures and the cleanup closures own their
installer beyond the loop iteration that created them, the installer
registry becomes Vec<Rc<dyn InstallerInterface>> and get_installer
hands out clones (PHP closures capture $installer the same way), with
InstallerInterface methods taking &self across the six implementors —
the only genuinely mutable state was LibraryInstaller.vendor_dir
(canonicalized in place), now behind a RefCell.
as_plugin_installer_mut/as_binary_presence_interface lose their &mut.
The cleanup_promises entries are now the real thing: the PHP closure
including the getInstallationSource() guard and the
installer->cleanup($opType, $package, $initialPackage) call, replacing
the no-op futures (drops one TODO(phase-b) and two TODO(phase-c)).
Verified against the real network: create-project laravel/laravel
produces a vendor tree byte-identical to real Composer's (diff -rq
clean across all 109 packages including vendor/composer).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
Concurrent package operations call into the same downloader instances
through Rc<RefCell<dyn DownloaderInterface>>; with &mut self methods
every call holds a RefMut across its awaits, which panics with
'already mutably borrowed' the moment two operations overlap. This is
groundwork for fanning out InstallationManager's download/install
loops (same rework HttpDownloader/CurlDownloader already got).
- DownloaderInterface/ChangeReportInterface/ArchiveDownloader/
VcsDownloader methods now take &self; as_change_report_interface
returns &dyn instead of &mut dyn.
- Implementors move their genuinely mutable state behind cells:
FileDownloader.additional_cleanup_paths, the archive downloaders'
cleanup_executed, ZipDownloader.zip_archive_object,
VcsDownloaderBase.has_cleaned_changes, GitDownloader's stash/discard/
cache maps and GitUtil, SvnDownloader.cache_credentials,
PerforceDownloader.perforce. FileDownloader.io gains a RefCell layer
so get_local_changes can keep PHP's NullIO swap under &self.
- ProcessExecutor::execute_async now returns a future that captures
everything up front instead of borrowing the executor, and call
sites build the future before awaiting, so no borrow on the shared
executor is held while a subprocess runs.
- Filesystem::remove_directory_async becomes remove_directory_async_via
taking the Rc handle: the Filesystem is only borrowed for the sync
head/tail, never across the rm subprocess await (sync borrow_mut
users like rename/ensure_directory_exists would otherwise collide).
- DownloadManager async call sites hold shared borrows only.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
|
regex::Regex::clone() does not share the underlying meta engine's
search-cache pool, so every fresh clone pays a ~10us warmup cost on
its first use. Two changes together eliminate this across nearly all
preg_* call sites:
- A php_regex! macro resolves PHP-style patterns to a per-call-site
&'static regex::Regex (via regex-macro's LazyLock), applied at the
majority of call sites throughout the codebase.
- Call sites still passing dynamic pattern strings go through
PATTERN_CACHE, which now stores Arc<(Regex, bool)> and hands out
Arc::clone()s instead of cloning the Regex itself.
PregPattern::resolve() returns a ResolvedPattern enum (Arc or
'static reference) rather than an owned Regex, so neither path ever
clones the Regex proper.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
The `regex` crate does not support negative lookahead, so the ported
`^dev-(?!main$|master$|trunk$|latest$)` pattern panicked at runtime on
any `require` invocation that reached version-selection. Replace it
with equivalent hand-written string logic per docs/dev/regex-porting.md.
|
|
11 downloader/installer integration-test files each redefined an
identical current_thread `run()` helper to block on async code. Extract
one shared multi_thread Runtime into tests/common/async_runtime.rs so
concurrent #[test] threads can all block_on it, matching the direction
item 7 (top-level Runtime) will take in production code.
|
|
Replaces Job/Request/JobHandle/id_gen/running_jobs/max_jobs with a
tokio::sync::Semaphore permit held for the duration of each request.
get/add/copy/add_copy are now &self (add/add_copy are also genuinely
async); a shared execute()/dispatch() core replaces
add_job/run_rfs_job/start_job/settle_job, returning the Response
directly instead of deferring to wait()/count_active_jobs()/
get_response() (all removed — confirmed zero callers, same for the
now-unused STATUS_* constants).
get()/copy() stay synchronous rather than becoming async wrappers
around add()/add_copy(), bridging via the existing sync_executor
instead of the curl_runtime() introduced for CurlDownloader: their
callers (~35 files reaching HttpDownloader) are mostly plain sync fns
with no async boundary anywhere in the call chain, and forcing that
propagation now would pull forward the dedicated async-propagation
task. curl-eligible requests still route through curl_runtime()
inside dispatch(), same as before — nesting sync_executor::block_on
(no real reactor) around curl_runtime().block_on() (a real, separate
Runtime) is safe; it's only nesting curl_runtime() inside itself that
would panic.
CurlDownloader no longer needs Rc<RefCell<>> wrapping despite the
original design sketch: since item 2 made all of its methods &self,
a plain Option<CurlDownloader> field works fine under HttpDownloader's
own &self methods.
get/add/copy/add_copy becoming &self (rather than &mut self) requires
no changes at any of their ~35 calling files: RefMut/Ref both deref to
a type that can call &self methods just fine.
Verified manually against real network I/O (sandbox disabled): `shirabe
show -a` (get()'s sync_executor-bridged path) and `shirabe
create-project` (add_copy()'s genuinely async path via file_downloader.rs)
both complete correctly with no hang.
|
|
Wrap RemoteFilesystem in Rc<RefCell<>> inside HttpDownloader, in prep
for the upcoming &self conversion of add()/get(). copy()/get_contents()
now bundle the response headers into their return value instead of
requiring a follow-up get_last_headers() call, since two separate calls
through a shared RefCell could otherwise race: nothing would guarantee
the reader observes the headers from its own request rather than one
clobbered by a concurrently borrowed call. get_last_headers() itself is
left in place, mirroring RemoteFilesystem::getLastHeaders() in PHP.
|
|
reset() eagerly rebuilt the ProxyManager singleton immediately,
capturing env vars before a caller could set them for the next
request. PHP's reset() just nulls the static instance; getInstance()
lazily constructs on next use. Match that so proxy env vars set after
reset() are observed.
get_instance() also ensured the singleton was constructed under its
own lock, dropped that lock, and returned the bare Mutex; every caller
then took a second, independent lock. A reset() landing in that gap
would leave the caller observing None and panicking on
.as_ref().unwrap(), a state the old eager-reconstructing reset() could
not produce. Return the already-locked MutexGuard from get_instance()
instead, so construction and use happen under one lock, and update all
call sites accordingly.
Holding that guard across a loop body then deadlocked in
diagnose_command, since check_http_proxy transitively re-enters
get_instance() via HttpDownloader -> CurlDownloader, and
std::sync::Mutex is not reentrant. Re-acquire the lock fresh each
iteration with a short-lived guard instead.
Finally, Mutex::new is a const fn, so the OnceLock wrapper around it
was unnecessary indirection; a bare static Mutex<Option<ProxyManager>>
initializes to the same state without the get_or_init/get dance.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
locker_test.rs defined its own installation_manager, identical to
test_case.rs's (both build a bare InstallationManager over a mock
HttpDownloader). Expose the shared one via pub(crate) and drop the
duplicate.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
reasons
Implement test_incapable_plugin_is_correctly_detected and
test_querying_non_provided_capability_returns_null_safely against a
real PluginManager, using hand-written PluginInterface/Capable stubs
in place of PHPUnit's ad-hoc mocks. Wire the shared config_stub test
helper into the plugin test binary, and derive Debug on the SetUp
struct per this project's convention.
The remaining plugin_installer_test.rs cases stay #[ignore]d:
install/update/uninstall wiring in PluginInstaller and class
instantiation in PluginManager::register_package are still
TODO(plugin) stubs, so no plugin ever actually gets registered. Each
now records its blocking TODO(plugin) site via // TODO(phase-d).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
No test logic changes.
|
|
zip/git/file downloader tests that were already #[ignore]d for a
documented reason lacked the required in-body // TODO(phase-d)
comment; record it for each.
Two git_downloader_test.rs cases,
test_download_uses_various_protocols_and_sets_push_url_for_github and
test_update_doesnt_throws_runtime_exception_if_git_command_fails_at_first_but_is_able_to_recover,
had been left as todo!() with a TODO(phase-d) claiming
ComposerMirror::process_git_url's github regex lacks PCRE delimiters
and panics. That regex already has delimiters and does not panic
(verified directly), and other tests in this same file already use
Package::set_source_mirrors to give a real package a mirror-prefixed
getSourceUrls() list, so the stated blocker no longer applies. Port
both tests using that existing technique instead of recording a
TODO(phase-d) for them.
No other test logic changes.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Implement previously-todo!() tests in auth_helper_test.rs,
process_executor_test.rs, remote_filesystem_test.rs, and
stream_context_factory_test.rs by porting the corresponding PHPUnit
test methods. Extend IOStub with writeRaw/setAuthentication call
tracking and askAndValidate/getAuthentication overrides to model the
PHPUnit mocks these tests rely on, deduping the resulting
call-recording fields into a small generic CallRecorder<T> helper
instead of repeating the same RefCell<Vec<T>> push/borrow().clone()
boilerplate five times.
testStoreAuthWithPromptInvalidAnswer and
testPromptAuthIfNeededMultipleBitbucketDownloads had initially lost
the ported PHPUnit mock's argument/call-count assertions
(askAndValidate's exact prompt string, and
hasAuthentication/getAuthentication's exactly(2) call counts),
silently narrowing what the tests verify; IOStub now records these
calls and the tests assert on them, matching upstream.
Tests left unportable (PHP set_error_handler machinery, closures in
data providers, network/subclass-mock dependencies, etc.) keep
#[ignore] with a single // TODO(phase-d) reason recorded in the
function body.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
|
|
Application::find/reset_composer and the shared ApplicationHandle are
now available, so GlobalCommand can reset the composer instance before
building the sub-command input and proxy execution through the full
Application::run dispatch, matching PHP's behavior. Un-ignores the
tests that only depended on this wiring, and re-points the remaining
ignores at their real (unrelated) blockers.
|
|
Convert errors/warnings/config to RefCell so load() can satisfy the
trait's &self signature, matching upstream's `instanceof
ValidatingArrayLoader` check in VcsRepository. This makes the
InvalidPackageException downcast path in VcsRepository reachable for
the first time instead of being permanently dead code.
|
|
get_remote_contents was a full stub always returning None, so any
file:// download raised a TransportException. Read local files
directly for the file scheme, mirroring PHP's file_get_contents
transparently handling the file:// stream wrapper. Also fixes
file_get_contents5 to strip the file:// prefix like the 0-arg
variant already did.
|
|
Two related "already borrowed" panics reachable from
AutoloadGenerator::dump() (which holds the local-repository,
installation-manager, and config RefCells for the duration of its own
statement, per the temporary-lifetime-extension pattern fixed
separately in create_project_command.rs):
- ensure_bin_dir_is_in_path called config.borrow_mut() to read
"bin-dir", but Config::get only needs &self; use borrow() so it can
coexist with an outer borrow instead of conflicting with it.
- make_autoloader's real body needed composer_handle.borrow_mut() plus
the same local-repository/installation-manager RefCells the caller
already holds mutably, which cannot be made reentrant-safe without a
larger restructuring. Since all 3 call sites already discard its
return value, and its only effect (registering a Composer-generated
ClassLoader for autoloading during event-listener PHP execution) is
unobservable in this port — there's no embedded PHP interpreter to
register it into, and class_exists for user-defined classes is a
hardcoded-false shim so the caller's very next check always treats
the class as unavailable regardless — make it a genuine no-op.
This unblocks the post-autoload-dump event for any script listener
naming a PHP class (e.g. Illuminate\Foundation\ComposerScripts), which
every create-project/install run reaches once real packages get
installed.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
|
Several Preg::*() call sites lost their PHP delimiter (and in one case
the `i` modifier) during porting, since preg_*() expects the delimiter
to be preserved in the caller's pattern literal and stripped internally.
This made compile_php_pattern panic or silently misparse the pattern.
Un-ignore the Version tests that were blocked by this bug.
|
|
|
|
Extends no_banned_use to cover std::any::Any, std::io::Read/Write, and
std::process::Command, and teaches the linter to allow `as _` imports
so trait methods can still be brought into scope without binding the
banned name. Fully qualifies all existing usages across the codebase.
|
|
PathRepository already implements RepositoryInterface and create_repository
already handles the "path" class, so the prior ignore reason no longer
applies; verify the FilterRepository/PathRepository wrapping via
RepositoryInterfaceHandle::is/downcast_rc.
|
|
Port testNoPluginsDisablesPluginsWhenScriptCommandsExist and
testScriptCommandTakesPriorityOverAbbreviatedBuiltinCommand. Both stay
#[ignore]d because do_run panics at the script-registration todo!()
(application.rs:2461) when composer.json has scripts. Add a test-only
ApplicationHandle::__get_composer accessor so the first test's
getPluginManager assertions can be expressed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|