Skip to content

Make sure rust-scripts are always executed in the final destination.#2414

Merged
Byron merged 1 commit intomainfrom
improvements
Feb 4, 2026
Merged

Make sure rust-scripts are always executed in the final destination.#2414
Byron merged 1 commit intomainfrom
improvements

Conversation

@Byron
Copy link
Member

@Byron Byron commented Feb 4, 2026

An oversight, and still no test for it.

Nonetheless, I think it's way more testable now and also overall better tested than before.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates gix-testtools fixture helpers so Rust-based fixture/post-processing closures can be invoked more flexibly, enabling results to be computed against the final writable destination rather than only against cached/read-only locations.

Changes:

  • Switches various fixture “post” closures from FnOnce to FnMut and threads them through as &mut where needed.
  • Adjusts rust_fixture_writable_* (CopyFromReadOnly path) to re-run the closure on the final tempdir to “prime” the returned value for the writable location.
  • Refactors scripted fixture internals to pass post-process closures by mutable reference.

)?;
copy_recursively_into_existing_dir(ro_dir, dst.path())?;
(dst, post_result)
(dst, _res_ignored)
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In Creation::CopyFromReadOnly, the returned Option<T> comes from scripted_fixture_read_only_with_args_inner(...), so any T computed by post_process is based on the read-only directory, not on the returned writable dst. With the new FnMut/as_mut() plumbing, consider re-running the post-process closure on dst after copy_recursively_into_existing_dir(...) (e.g. with FixtureState::Fresh(dst.path())) and returning that value instead; otherwise callers can receive paths that don’t exist in the writable tempdir.

Suggested change
(dst, _res_ignored)
// Re-run post_process on the writable destination to ensure `T` matches `dst`
let post_result = if let Some((_version, ref mut f)) = post_process {
Some(f(FixtureState::Fresh(dst.path()))?)
} else {
None
};
(dst, post_result)

Copilot uses AI. Check for mistakes.
Comment on lines 592 to 596
pub fn scripted_fixture_read_only_with_post<T>(
script_name: impl AsRef<Path>,
version: u32,
post_process: impl FnOnce(FixtureState<'_>) -> PostResult<T>,
post_process: impl FnMut(FixtureState<'_>) -> PostResult<T>,
) -> Result<(PathBuf, T)> {
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing the post_process bound from FnOnce to FnMut for the read-only *_with_post APIs is a breaking and more restrictive public API change for gix-testtools (it rejects closures that consume captures). If you only need multiple invocations for writable fixtures, consider keeping the read-only variants (and scripted_fixture_read_only_with_args_inner) accepting FnOnce and pass &mut closures from writable code as needed.

Copilot uses AI. Check for mistakes.
/// It may be called multiple times, and the returned `T` will be primed on the final, writable location.
///
/// `version` should be incremented when the closure's behavior changes to invalidate the cache.
/// `name` is used to identify this fixture for caching purposes and should be unique within the crate.
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust_fixture_writable still takes a mode: Creation parameter, but the docs no longer mention what mode controls (the earlier “mode controls how…” line was removed). Please re-add a brief explanation of mode here so callers understand the CopyFromReadOnly vs ExecuteScript tradeoff.

Suggested change
/// `name` is used to identify this fixture for caching purposes and should be unique within the crate.
/// `name` is used to identify this fixture for caching purposes and should be unique within the crate.
/// `mode` controls how the writable directory is prepared: [`Creation::CopyFromReadOnly`] copies a cached
/// read-only fixture into a fresh temporary directory, while [`Creation::ExecuteScript`] reruns the closure
/// to build the fixture from scratch each time.

Copilot uses AI. Check for mistakes.
Comment on lines +953 to 956
let (ro_dir, _res_ignored) = rust_fixture_read_only_inner(name, version, &mut make_fixture, None, root)?;
copy_recursively_into_existing_dir(ro_dir, dst.path())?;
let res = make_fixture(FixtureState::Fresh(dst.path()))?;
res
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test coverage: the new behavior in rust_fixture_writable_inner for Creation::CopyFromReadOnly intentionally calls make_fixture again on the final tempdir to “prime” the returned T for the writable location. Please add a regression test that returns something path-dependent (e.g. a PathBuf into the fixture) and asserts it points into tmp.path() rather than the cached read-only directory.

Copilot generated this review using guidance from repository custom instructions.
Comment on lines 715 to 719
pub fn scripted_fixture_writable_with_post<T>(
script_name: impl AsRef<Path>,
version: u32,
post_process: impl FnOnce(FixtureState<'_>) -> PostResult<T>,
post_process: impl FnMut(FixtureState<'_>) -> PostResult<T>,
) -> Result<(tempfile::TempDir, T)> {
Copy link

Copilot AI Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment immediately above scripted_fixture_writable_with_post mentions a FixtureState::Cached variant and says Fresh is “newly created”, but FixtureState only has Uninitialized and Fresh (where Fresh corresponds to already-created/cached). Please adjust the wording to match the actual enum variants/semantics so callers know when it’s safe to mutate the fixture directory.

Copilot uses AI. Check for mistakes.
@Byron Byron enabled auto-merge February 4, 2026 20:23
@Byron Byron merged commit a3e7b6e into main Feb 4, 2026
36 checks passed
@Byron Byron deleted the improvements branch February 4, 2026 20:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant