Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions gix-command/tests/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,7 @@ mod prepare {
);
assert_eq!(
format!("{cmd:?}"),
quoted(&[*SH, "-c", r#"\'ls\' \"$@\""#, "--", "--foo=a b"]),
quoted(&[*SH, "-c", r#"'ls' \"$@\""#, "--", "--foo=a b"]),
"looks strange thanks to debug printing, but is the right amount of quotes actually"
);
}
Expand All @@ -380,9 +380,9 @@ mod prepare {
quoted(&[
*SH,
"-c",
r#"\'C:\\Users\\O\'\\\'\'Shaughnessy\\with space.exe\' \"$@\""#,
r#"'C:\\Users\\O'\\''Shaughnessy\\with space.exe' \"$@\""#,
"--",
r"--foo=\'a b\'"
r"--foo='a b'"
Comment on lines -383 to +385
Copy link
Member

@EliahKagan EliahKagan Jan 22, 2026

Choose a reason for hiding this comment

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

I'm glad the Debug trait for OsString no longer prints ' characters as \'--it makes things a lot easier to read! Thanks for updating this to adapt to that!

I wonder if we should condition this on the Rust version for a while so the tests can run and pass on previous Rust versions. I might look into doing that--whether it makes sense might depend, in part, on how much else there is in the test suite that relies on very recent Rust.

(It looks like the adapted-to change in the standard library was done in rust-lang/rust#148798 for rust-lang/rust#114583. If I divide this into cases, I can mention that somewhere so it's clear why.)

Copy link
Member Author

Choose a reason for hiding this comment

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

Right now the test-suite is susceptible to CI image changes to Rust as well as Git (to a lesser extend), and I think it's fine for now: the failure rate due to that is quite low, with Clippy the number one offender here, but somehow I am not bothered by it.

I am just saying that in case becoming more resilient against it would mean slower CI, rust-toolchain.toml or more complexity for some tests.

]),
"again, a lot of extra backslashes, but it's correct outside of the debug formatting"
);
Expand Down
31 changes: 23 additions & 8 deletions gix-status/src/index_as_worktree_with_renames/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -411,19 +411,18 @@ pub(super) mod function {
}

mod rewrite {
use std::{io::Read, path::Path};

use crate::{
index_as_worktree::{Change, EntryStatus},
index_as_worktree_with_renames::{Entry, Error},
};
use bstr::BStr;
use gix_diff::{rewrites::tracker::ChangeKind, tree::visit::Relation};
use gix_dir::entry::Kind;
use gix_filter::pipeline::convert::ToGitOutcome;
use gix_hash::oid;
use gix_object::tree::EntryMode;

use crate::{
index_as_worktree::{Change, EntryStatus},
index_as_worktree_with_renames::{Entry, Error},
};
use std::io::ErrorKind;
use std::{io::Read, path::Path};

#[derive(Clone)]
pub enum ModificationOrDirwalkEntry<'index, T, U>
Expand Down Expand Up @@ -527,7 +526,23 @@ pub(super) mod function {
.map_err(Error::SetAttributeContext)?;
let rela_path = gix_path::from_bstr(rela_path);
let file_path = worktree_root.join(rela_path.as_ref());
let file = std::fs::File::open(&file_path).map_err(Error::OpenWorktreeFile)?;
let file = match std::fs::File::open(&file_path) {
Ok(f) => f,
Err(err)
if matches!(
err.kind(),
ErrorKind::NotFound | ErrorKind::PermissionDenied | ErrorKind::Interrupted
) =>
{
gix_features::trace::debug!(
?file_path,
?err,
"ignoring worktree file as it can't be read for hashing"
);
return Ok(object_hash.null());
}
Err(err) => return Err(Error::OpenWorktreeFile(err)),
};
let out = filter.convert_to_git(
file,
rela_path.as_ref(),
Expand Down
1 change: 1 addition & 0 deletions gix-status/tests/fixtures/generated-archives/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ status_changed.tar
symlink_stack.tar
status_nonfile.tar
status_unchanged_filter.tar
unreadable_untracked.tar
11 changes: 11 additions & 0 deletions gix-status/tests/fixtures/unreadable_untracked.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env bash
set -eu -o pipefail

git init
>tracked
git add tracked && git commit -m "init"

>unreadable
chmod 000 unreadable

git status
56 changes: 54 additions & 2 deletions gix-status/tests/status/index_as_worktree_with_renames.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ use gix_status::{
};
use pretty_assertions::assert_eq;

use crate::fixture_path;
use crate::{fixture_path, fixture_path_rw_slow};

#[test]
fn changed_and_untracked_and_renamed() {
Expand Down Expand Up @@ -89,6 +89,7 @@ fn changed_and_untracked_and_renamed() {
&expectations_with_dirwalk,
Some(rewrites),
Some(Default::default()),
Fixture::ReadOnly,
);
assert_eq!(
out.rewrites,
Expand All @@ -111,6 +112,7 @@ fn nonfile_untracked_are_not_visible() {
&[],
None,
Some(Default::default()),
Fixture::ReadOnly,
);
}

Expand All @@ -130,8 +132,10 @@ fn tracked_changed_to_non_file() {
}],
None,
Some(Default::default()),
Fixture::ReadOnly,
);
}

#[test]
fn changed_and_untracked() {
let out = fixture_filtered_detailed(
Expand All @@ -148,6 +152,7 @@ fn changed_and_untracked() {
}],
None,
None,
Fixture::ReadOnly,
);
assert_eq!(out.tracked_file_modification.entries_processed, 4);
assert_eq!(
Expand Down Expand Up @@ -183,6 +188,7 @@ fn changed_and_untracked() {
&expectations_with_dirwalk,
None,
Some(gix_dir::walk::Options::default()),
Fixture::ReadOnly,
);

let dirwalk = out.dirwalk.expect("configured thus has output");
Expand All @@ -203,6 +209,7 @@ fn changed_and_untracked() {
&expectations_with_dirwalk,
Some(Default::default()),
Some(gix_dir::walk::Options::default()),
Fixture::ReadOnly,
);

let rewrites = out.rewrites.expect("configured thus has output");
Expand All @@ -213,13 +220,48 @@ fn changed_and_untracked() {
);
}

#[cfg(unix)]
#[test]
fn unreadable_untracked() {
let expectations_with_dirwalk = [Expectation::DirwalkEntry {
rela_path: "unreadable",
status: gix_dir::entry::Status::Untracked,
disk_kind: Some(gix_dir::entry::Kind::File),
}];
let out = fixture_filtered_detailed(
"unreadable_untracked.sh",
"",
&[],
&expectations_with_dirwalk,
Some(Default::default()),
Some(gix_dir::walk::Options::default()),
Fixture::WritableExecuted,
);

let dirwalk = out.dirwalk.expect("configured thus has output");
assert_eq!(
dirwalk,
gix_dir::walk::Outcome {
read_dir_calls: 1,
returned_entries: 1,
seen_entries: 3,
}
);
}

enum Fixture {
ReadOnly,
WritableExecuted,
}

fn fixture_filtered_detailed(
script: &str,
subdir: &str,
pathspecs: &[&str],
expected: &[Expectation<'_>],
rewrites: Option<gix_diff::Rewrites>,
dirwalk: Option<gix_dir::walk::Options>,
fixture: Fixture,
) -> Outcome {
fn cleanup(mut out: Outcome) -> Outcome {
out.tracked_file_modification.worktree_bytes = 0;
Expand All @@ -229,7 +271,17 @@ fn fixture_filtered_detailed(
out
}

let worktree = fixture_path(script).join(subdir);
let (worktree, _tmp) = match fixture {
Fixture::ReadOnly => {
let dir = fixture_path(script).join(subdir);
(dir, None)
}
Fixture::WritableExecuted => {
let tmp = fixture_path_rw_slow(script);
let dir = tmp.path().join(subdir);
(dir, Some(tmp))
}
};
let git_dir = worktree.join(".git");
let index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default()).unwrap();
let search = gix_pathspec::Search::from_specs(
Expand Down
11 changes: 11 additions & 0 deletions gix-status/tests/status/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use gix_testtools::Creation;
pub use gix_testtools::Result;

mod index_as_worktree;
Expand All @@ -11,6 +12,16 @@ pub fn fixture_path(name: &str) -> std::path::PathBuf {
dir
}

pub fn fixture_path_rw_slow(name: &str) -> gix_testtools::tempfile::TempDir {
let tmp = gix_testtools::scripted_fixture_writable_with_args_standalone_single_archive(
std::path::Path::new(name).with_extension("sh"),
None::<String>,
Creation::ExecuteScript,
)
.expect("script works");
tmp
}

fn hex_to_id(hex: &str) -> gix_hash::ObjectId {
gix_hash::ObjectId::from_hex(hex.as_bytes()).expect("40 bytes hex")
}
6 changes: 3 additions & 3 deletions tests/tools/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -330,7 +330,7 @@ pub fn scripted_fixture_writable(script_name: impl AsRef<Path>) -> Result<tempfi
}

/// Like [`scripted_fixture_writable`], but does not prefix the fixture directory with `tests`
pub fn scripted_fixture_writable_standalone(script_name: &str) -> Result<tempfile::TempDir> {
pub fn scripted_fixture_writable_standalone(script_name: impl AsRef<Path>) -> Result<tempfile::TempDir> {
scripted_fixture_writable_with_args_standalone(script_name, None::<String>, Creation::CopyFromReadOnly)
}

Expand Down Expand Up @@ -358,7 +358,7 @@ pub fn scripted_fixture_writable_with_args_single_archive(

/// Like [`scripted_fixture_writable_with_args`], but does not prefix the fixture directory with `tests`
pub fn scripted_fixture_writable_with_args_standalone(
script_name: &str,
script_name: impl AsRef<Path>,
args: impl IntoIterator<Item = impl Into<String>>,
mode: Creation,
) -> Result<tempfile::TempDir> {
Expand All @@ -369,7 +369,7 @@ pub fn scripted_fixture_writable_with_args_standalone(
///
/// See [`scripted_fixture_read_only_with_args_single_archive()`] for important details on what `single_archive` means.
pub fn scripted_fixture_writable_with_args_standalone_single_archive(
script_name: &str,
script_name: impl AsRef<Path>,
args: impl IntoIterator<Item = impl Into<String>>,
mode: Creation,
) -> Result<tempfile::TempDir> {
Expand Down
Loading