From f27ecaf29bcab96f6b4a3f8b3efff77fc86c61ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 28 Sep 2025 09:15:56 +0200 Subject: [PATCH 01/11] Add create-diff-cases --- Cargo.lock | 1 + gix-diff/tests/Cargo.toml | 1 + gix-diff/tests/diff/blob/mod.rs | 1 + gix-diff/tests/diff/blob/slider.rs | 148 +++++++++++++++++++++ tests/it/src/args.rs | 18 +++ tests/it/src/commands/create_diff_cases.rs | 129 ++++++++++++++++++ tests/it/src/commands/mod.rs | 3 + tests/it/src/main.rs | 7 + 8 files changed, 308 insertions(+) create mode 100644 gix-diff/tests/diff/blob/slider.rs create mode 100644 tests/it/src/commands/create_diff_cases.rs diff --git a/Cargo.lock b/Cargo.lock index 072b45faf98..3c2536c1bd6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1698,6 +1698,7 @@ dependencies = [ "gix-object", "gix-odb", "gix-pathspec", + "gix-ref", "gix-testtools", "gix-traverse", "gix-worktree", diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index 2485ee5e60d..59bfda7b4e2 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -26,6 +26,7 @@ gix-worktree = { path = "../../gix-worktree" } gix-object = { path = "../../gix-object" } gix-odb = { path = "../../gix-odb" } gix-filter = { path = "../../gix-filter" } +gix-ref = { path = "../../gix-ref" } gix-traverse = { path = "../../gix-traverse" } gix-testtools = { path = "../../tests/tools" } diff --git a/gix-diff/tests/diff/blob/mod.rs b/gix-diff/tests/diff/blob/mod.rs index 1959c4e6fdb..5742dfd4350 100644 --- a/gix-diff/tests/diff/blob/mod.rs +++ b/gix-diff/tests/diff/blob/mod.rs @@ -1,3 +1,4 @@ pub(crate) mod pipeline; mod platform; +mod slider; mod unified_diff; diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs new file mode 100644 index 00000000000..b9241fbee28 --- /dev/null +++ b/gix-diff/tests/diff/blob/slider.rs @@ -0,0 +1,148 @@ +use std::path::PathBuf; + +use gix_diff::blob::{ + intern::TokenSource, + unified_diff::{ConsumeBinaryHunk, ContextSize}, + Algorithm, UnifiedDiff, +}; +use gix_object::FindExt; +use gix_ref::{bstr::BString, file::ReferenceExt}; + +struct Baseline { + hunks: (), +} + +mod baseline {} + +#[test] +fn sliders() -> gix_testtools::Result { + let worktree_path = fixture_path()?; + let git_dir = worktree_path.join(".git"); + let odb = gix_odb::at(git_dir.join("objects"))?; + let refs = gix_ref::file::Store::at(git_dir.clone(), gix_ref::store::init::Options::default()); + // In `../tree.rs`, there is `head_of` which would obviate the need for using `gix_ref`. + let mut head = refs.find("HEAD")?; + let head_id = head.peel_to_id(&refs, &odb)?; + + let commits = gix_traverse::commit::Simple::new(Some(head_id), &odb) + .map(Result::unwrap) + .map(|commit| commit.id) + .collect::>(); + + let index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?; + let stack = gix_worktree::Stack::from_state_and_ignore_case( + worktree_path.clone(), + false, + gix_worktree::stack::State::AttributesAndIgnoreStack { + attributes: Default::default(), + ignore: Default::default(), + }, + &index, + index.path_backing(), + ); + let capabilities = gix_fs::Capabilities::probe(&git_dir); + let mut resource_cache = gix_diff::blob::Platform::new( + Default::default(), + gix_diff::blob::Pipeline::new( + gix_diff::blob::pipeline::WorktreeRoots { + old_root: None, + new_root: None, + }, + gix_filter::Pipeline::new(Default::default(), Default::default()), + vec![], + gix_diff::blob::pipeline::Options { + large_file_threshold_bytes: 0, + fs: capabilities, + }, + ), + gix_diff::blob::pipeline::Mode::ToGit, + stack, + ); + + assert!(commits.len() > 0); + assert!(commits.len().is_multiple_of(2)); + + let mut buffer = Vec::new(); + let mut buffer2 = Vec::new(); + + let mut iter = commits.chunks(2); + + while let Some(&[new, old]) = iter.next() { + // TODO: We can extract the duplicate code. + let old_commit = odb.find_commit(&old, &mut buffer)?.to_owned(); + let new_commit = odb.find_commit(&new, &mut buffer)?.to_owned(); + let file_path = BString::from(old_commit.message.trim_ascii_end()); + + eprintln!("diffing {old} and {new}, file path {file_path}"); + + let old_tree = old_commit.tree; + let old_blob_id = odb + .find_tree(&old_tree, &mut buffer2)? + .entries + .iter() + .find(|entry| file_path.eq(entry.filename)) + .unwrap() + .oid + .to_owned(); + + let new_tree = new_commit.tree; + let new_blob_id = odb + .find_tree(&new_tree, &mut buffer2)? + .entries + .iter() + .find(|entry| entry.filename == file_path) + .unwrap() + .oid + .to_owned(); + + resource_cache.set_resource( + old_blob_id, + gix_object::tree::EntryKind::Blob, + file_path.as_ref(), + gix_diff::blob::ResourceKind::OldOrSource, + &odb, + )?; + resource_cache.set_resource( + new_blob_id, + gix_object::tree::EntryKind::Blob, + file_path.as_ref(), + gix_diff::blob::ResourceKind::NewOrDestination, + &odb, + )?; + + let outcome = resource_cache.prepare_diff()?; + + //let old_string: gix_ref::bstr::BString = outcome.old.data.as_slice().unwrap().into(); + //let new_string: gix_ref::bstr::BString = outcome.new.data.as_slice().unwrap().into(); + //eprintln!("{old_string}"); + //eprintln!("{new_string}"); + + let interner = gix_diff::blob::intern::InternedInput::new( + tokens_for_diffing(outcome.old.data.as_slice().unwrap_or_default()), + tokens_for_diffing(outcome.new.data.as_slice().unwrap_or_default()), + ); + + let actual = gix_diff::blob::diff( + Algorithm::Myers, + &interner, + UnifiedDiff::new( + &interner, + // TODO: use `ConsumeHunk` instead. + ConsumeBinaryHunk::new(String::new(), "\n"), + ContextSize::symmetrical(3), + ), + )?; + + eprintln!("{actual}"); + } + + Ok(()) +} + +fn tokens_for_diffing(data: &[u8]) -> impl TokenSource { + gix_diff::blob::sources::byte_lines(data) +} + +fn fixture_path() -> gix_testtools::Result { + gix_testtools::scripted_fixture_read_only_standalone("make_diff_for_sliders_repo.sh") +} diff --git a/tests/it/src/args.rs b/tests/it/src/args.rs index 78f1873d64b..ea173d679ec 100644 --- a/tests/it/src/args.rs +++ b/tests/it/src/args.rs @@ -123,6 +123,24 @@ pub enum Subcommands { #[clap(value_parser = AsPathSpec)] patterns: Vec, }, + /// TODO: add description. + CreateDiffCases { + /// TODO: add description. + #[clap(long)] + sliders_file: PathBuf, + /// The git root to extract the diff-related parts from. + #[clap(long)] + worktree_dir: PathBuf, + /// The directory into which to copy the files. + #[clap(long)] + destination_dir: PathBuf, + /// TODO: add description. + #[clap(long, default_value_t = 10)] + count: usize, + /// The directory to place assets in. + #[clap(long)] + asset_dir: Option, + }, /// Check for executable bits that disagree with shebangs. /// /// This checks committed and staged files, but not anything unstaged, to find shell scripts diff --git a/tests/it/src/commands/create_diff_cases.rs b/tests/it/src/commands/create_diff_cases.rs new file mode 100644 index 00000000000..8d89a73c740 --- /dev/null +++ b/tests/it/src/commands/create_diff_cases.rs @@ -0,0 +1,129 @@ +pub(super) mod function { + use anyhow::Context; + use std::{ + collections::HashSet, + path::{Path, PathBuf}, + }; + + use gix::{ + bstr::{BString, ByteSlice}, + objs::FindExt, + }; + + pub fn create_diff_cases( + sliders_file: PathBuf, + worktree_dir: &Path, + destination_dir: PathBuf, + count: usize, + asset_dir: Option, + ) -> anyhow::Result<()> { + // TODO: turn into parameter. + let dry_run = false; + + let prefix = if dry_run { "WOULD" } else { "Will" }; + let sliders = std::fs::read_to_string(&sliders_file)?; + + eprintln!( + "read {} which has {} lines", + sliders_file.display(), + sliders.lines().count() + ); + + let sliders: HashSet<_> = sliders + .lines() + .take(count) + .map(|line| { + let parts: Vec<_> = line.split_ascii_whitespace().collect(); + + match parts[..] { + [before, after, ..] => (before, after), + _ => todo!(), + } + }) + .collect(); + + eprintln!("{sliders:?}"); + + let repo = gix::open(worktree_dir)?; + + let asset_dir = asset_dir.unwrap_or("assets".into()); + let assets = destination_dir.join(asset_dir.to_os_str()?); + + eprintln!("{prefix} create directory '{assets}'", assets = assets.display()); + if !dry_run { + std::fs::create_dir_all(&assets)?; + } + + let mut buf = Vec::new(); + + let script_name = "make_diff_for_sliders_repo.sh"; + + let mut blocks: Vec = vec![format!( + r#"#!/usr/bin/env bash +set -eu -o pipefail + +ROOT="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" + +git init +echo .gitignore >> .gitignore +echo {asset_dir}/ >> .gitignore +echo {script_name} >> .gitignore +"# + )]; + + for (before, after) in sliders.iter() { + let revspec = repo.rev_parse(*before)?; + let old_blob_id = revspec + .single() + .context(format!("rev-spec '{before}' must resolve to a single object"))?; + let (old_path, _) = revspec + .path_and_mode() + .context(format!("rev-spec '{before}' must contain a path"))?; + + let revspec = repo.rev_parse(*after)?; + let new_blob_id = revspec + .single() + .context(format!("rev-spec '{after}' must resolve to a single object"))?; + let (new_path, _) = revspec + .path_and_mode() + .context(format!("rev-spec '{after}' must contain a path"))?; + + eprintln!("{old_blob_id:?} {old_path:?} {new_blob_id:?} {new_path:?}"); + + let dst_old_blob = assets.join(format!("{}.commit", old_blob_id)); + let dst_new_blob = assets.join(format!("{}.commit", new_blob_id)); + if !dry_run { + let old_blob = repo.objects.find_blob(&old_blob_id, &mut buf)?.data; + std::fs::write(dst_old_blob, old_blob)?; + + let new_blob = repo.objects.find_blob(&new_blob_id, &mut buf)?.data; + std::fs::write(dst_new_blob, new_blob)?; + } + + blocks.push(format!( + r#"cp "$ROOT/{asset_dir}/{old_blob_id}.commit" ./{old_blob_id} +git add {old_blob_id} +git commit -m {old_blob_id} +cp "$ROOT/{asset_dir}/{new_blob_id}.commit" ./{old_blob_id} +git add {old_blob_id} +git commit -m "{old_blob_id} -> {new_blob_id}" + +git diff HEAD^ HEAD > .git/$(git rev-parse HEAD^)-$(git rev-parse HEAD).baseline +"# + )); + } + + let script_file = destination_dir.join(script_name); + eprintln!( + "{prefix} write script file at '{script_file}'", + script_file = script_file.display() + ); + + if !dry_run { + let script = blocks.join("\n"); + std::fs::write(script_file, script)?; + } + + Ok(()) + } +} diff --git a/tests/it/src/commands/mod.rs b/tests/it/src/commands/mod.rs index 6bec01671df..ed3abfd0cfc 100644 --- a/tests/it/src/commands/mod.rs +++ b/tests/it/src/commands/mod.rs @@ -7,6 +7,9 @@ pub use copy_royal::function::copy_royal; pub mod git_to_sh; pub use git_to_sh::function::git_to_sh; +pub mod create_diff_cases; +pub use create_diff_cases::function::create_diff_cases; + pub mod check_mode; pub use check_mode::function::check_mode; diff --git a/tests/it/src/main.rs b/tests/it/src/main.rs index df22a7e8c6f..57dad9b41cd 100644 --- a/tests/it/src/main.rs +++ b/tests/it/src/main.rs @@ -46,6 +46,13 @@ fn main() -> anyhow::Result<()> { destination_dir, patterns, } => commands::copy_royal(dry_run, &worktree_root, destination_dir, patterns), + Subcommands::CreateDiffCases { + sliders_file, + worktree_dir, + destination_dir, + count, + asset_dir, + } => commands::create_diff_cases(sliders_file, &worktree_dir, destination_dir, count, asset_dir), Subcommands::CheckMode {} => commands::check_mode(), Subcommands::Env {} => commands::env(), } From bc6bef24b53486b6ddc272a685019787240a4c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 28 Sep 2025 09:44:02 +0200 Subject: [PATCH 02/11] Add DiffHunkRecorder --- gix-diff/tests/diff/blob/slider.rs | 52 +++++++++++++++++++++++++----- 1 file changed, 44 insertions(+), 8 deletions(-) diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index b9241fbee28..97d7439bd58 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -2,7 +2,7 @@ use std::path::PathBuf; use gix_diff::blob::{ intern::TokenSource, - unified_diff::{ConsumeBinaryHunk, ContextSize}, + unified_diff::{ConsumeHunk, ContextSize, DiffLineKind, HunkHeader}, Algorithm, UnifiedDiff, }; use gix_object::FindExt; @@ -14,6 +14,47 @@ struct Baseline { mod baseline {} +#[derive(Debug)] +struct DiffHunk { + header: HunkHeader, + lines: Vec<(DiffLineKind, BString)>, +} + +struct DiffHunkRecorder { + inner: Vec, +} + +impl DiffHunkRecorder { + fn new() -> Self { + Self { inner: Vec::new() } + } +} + +impl ConsumeHunk for DiffHunkRecorder { + type Out = Vec; + + fn consume_hunk( + &mut self, + header: HunkHeader, + lines: &[(gix_diff::blob::unified_diff::DiffLineKind, &[u8])], + ) -> std::io::Result<()> { + let lines: Vec<_> = lines + .iter() + .map(|(kind, line)| (*kind, BString::new(line.to_vec()))) + .collect(); + + let diff_hunk = DiffHunk { header, lines }; + + self.inner.push(diff_hunk); + + Ok(()) + } + + fn finish(self) -> Self::Out { + self.inner + } +} + #[test] fn sliders() -> gix_testtools::Result { let worktree_path = fixture_path()?; @@ -125,15 +166,10 @@ fn sliders() -> gix_testtools::Result { let actual = gix_diff::blob::diff( Algorithm::Myers, &interner, - UnifiedDiff::new( - &interner, - // TODO: use `ConsumeHunk` instead. - ConsumeBinaryHunk::new(String::new(), "\n"), - ContextSize::symmetrical(3), - ), + UnifiedDiff::new(&interner, DiffHunkRecorder::new(), ContextSize::symmetrical(3)), )?; - eprintln!("{actual}"); + eprintln!("{actual:#?}"); } Ok(()) From 3b8937430ef64630d91e21919fb9481af89fd380 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 28 Sep 2025 19:57:19 +0200 Subject: [PATCH 03/11] Add Baseline for parsing git diff baseline --- Cargo.lock | 1 + gix-diff/tests/Cargo.toml | 1 + gix-diff/tests/diff/blob/slider.rs | 153 +++++++++++++++++++++++++---- 3 files changed, 135 insertions(+), 20 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3c2536c1bd6..e27a4e13780 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1704,6 +1704,7 @@ dependencies = [ "gix-worktree", "insta", "pretty_assertions", + "regex", "shell-words", ] diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index 59bfda7b4e2..10c6d17f58e 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -33,3 +33,4 @@ gix-testtools = { path = "../../tests/tools" } insta = "1.43.1" shell-words = "1" pretty_assertions = "1.4.0" +regex = "1.6.0" diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index 97d7439bd58..744a45edd1f 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{iter::Peekable, path::PathBuf}; use gix_diff::blob::{ intern::TokenSource, @@ -6,15 +6,12 @@ use gix_diff::blob::{ Algorithm, UnifiedDiff, }; use gix_object::FindExt; -use gix_ref::{bstr::BString, file::ReferenceExt}; - -struct Baseline { - hunks: (), -} - -mod baseline {} +use gix_ref::{ + bstr::{self, BString}, + file::ReferenceExt, +}; -#[derive(Debug)] +#[derive(Debug, PartialEq)] struct DiffHunk { header: HunkHeader, lines: Vec<(DiffLineKind, BString)>, @@ -55,6 +52,126 @@ impl ConsumeHunk for DiffHunkRecorder { } } +struct Baseline<'a> { + lines: Peekable>, +} + +mod baseline { + use std::path::Path; + + use gix_diff::blob::unified_diff::{DiffLineKind, HunkHeader}; + use gix_ref::bstr::{BString, ByteSlice}; + use gix_testtools::once_cell::sync::Lazy; + use regex::bytes::Regex; + + use super::{Baseline, DiffHunk}; + + impl Baseline<'_> { + pub fn collect(baseline_path: impl AsRef) -> std::io::Result> { + let content = std::fs::read(baseline_path)?; + + let mut baseline = Baseline { + lines: content.lines().peekable(), + }; + + baseline.skip_header(); + + Ok(baseline.collect()) + } + + fn skip_header(&mut self) -> () { + // diff --git a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + // index ccccccc..ddddddd 100644 + // --- a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + // +++ b/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb + + let line = self.lines.next().expect("line to be present"); + assert!(line.starts_with(b"diff --git ")); + + let line = self.lines.next().expect("line to be present"); + assert!(line.starts_with(b"index ")); + + let line = self.lines.next().expect("line to be present"); + assert!(line.starts_with(b"--- ")); + + let line = self.lines.next().expect("line to be present"); + assert!(line.starts_with(b"+++ ")); + } + } + + impl Iterator for Baseline<'_> { + type Item = DiffHunk; + + fn next(&mut self) -> Option { + static HEADER_REGEX: Lazy = Lazy::new(|| { + Regex::new(r"@@ -([[:digit:]]+),([[:digit:]]+) \+([[:digit:]]+),([[:digit:]]+) @@") + .expect("regex to be valid") + }); + + let mut hunk_header = None; + let mut hunk_lines: Vec<(DiffLineKind, BString)> = Vec::new(); + + while let Some(line) = self.lines.next() { + // @@ -18,6 +18,7 @@ abc def ghi + // TODO: + // make sure that the following is correct + // @@ -{before_hunk_start},{before_hunk_len} +{after_hunk_start},{after_hunk_len} + + static START_OF_HEADER: &[u8; 4] = b"@@ -"; + + if line.starts_with(START_OF_HEADER) { + let matches = HEADER_REGEX.captures(line).expect("line to be a hunk header"); + + let (_, [before_hunk_start, before_hunk_len, after_hunk_start, after_hunk_len]) = matches.extract(); + + hunk_header = Some(HunkHeader { + before_hunk_start: before_hunk_start + .to_str() + .expect("to be a valid UTF-8 string") + .parse::() + .expect("to be a number"), + before_hunk_len: before_hunk_len + .to_str() + .expect("to be a valid UTF-8 string") + .parse::() + .expect("to be a number"), + after_hunk_start: after_hunk_start + .to_str() + .expect("to be a valid UTF-8 string") + .parse::() + .expect("to be a number"), + after_hunk_len: after_hunk_len + .to_str() + .expect("to be a valid UTF-8 string") + .parse::() + .expect("to be a number"), + }); + + continue; + } + + match line[0] { + b' ' => hunk_lines.push((DiffLineKind::Context, line[1..].into())), + b'+' => hunk_lines.push((DiffLineKind::Add, line[1..].into())), + b'-' => hunk_lines.push((DiffLineKind::Remove, line[1..].into())), + _ => todo!(), + }; + + match self.lines.peek() { + Some(next_line) if next_line.starts_with(START_OF_HEADER) => break, + None => break, + _ => {} + } + } + + hunk_header.map(|hunk_header| DiffHunk { + header: hunk_header, + lines: hunk_lines, + }) + } + } +} + #[test] fn sliders() -> gix_testtools::Result { let worktree_path = fixture_path()?; @@ -108,14 +225,12 @@ fn sliders() -> gix_testtools::Result { let mut iter = commits.chunks(2); - while let Some(&[new, old]) = iter.next() { + while let Some(&[new_commit_id, old_commit_id]) = iter.next() { // TODO: We can extract the duplicate code. - let old_commit = odb.find_commit(&old, &mut buffer)?.to_owned(); - let new_commit = odb.find_commit(&new, &mut buffer)?.to_owned(); + let old_commit = odb.find_commit(&old_commit_id, &mut buffer)?.to_owned(); + let new_commit = odb.find_commit(&new_commit_id, &mut buffer)?.to_owned(); let file_path = BString::from(old_commit.message.trim_ascii_end()); - eprintln!("diffing {old} and {new}, file path {file_path}"); - let old_tree = old_commit.tree; let old_blob_id = odb .find_tree(&old_tree, &mut buffer2)? @@ -153,11 +268,6 @@ fn sliders() -> gix_testtools::Result { let outcome = resource_cache.prepare_diff()?; - //let old_string: gix_ref::bstr::BString = outcome.old.data.as_slice().unwrap().into(); - //let new_string: gix_ref::bstr::BString = outcome.new.data.as_slice().unwrap().into(); - //eprintln!("{old_string}"); - //eprintln!("{new_string}"); - let interner = gix_diff::blob::intern::InternedInput::new( tokens_for_diffing(outcome.old.data.as_slice().unwrap_or_default()), tokens_for_diffing(outcome.new.data.as_slice().unwrap_or_default()), @@ -169,7 +279,10 @@ fn sliders() -> gix_testtools::Result { UnifiedDiff::new(&interner, DiffHunkRecorder::new(), ContextSize::symmetrical(3)), )?; - eprintln!("{actual:#?}"); + let baseline_path = git_dir.join(format!("{old_commit_id}-{new_commit_id}.baseline")); + let baseline = Baseline::collect(baseline_path).unwrap(); + + pretty_assertions::assert_eq!(actual, baseline); } Ok(()) From 99a6bb65d4d1fa730730af5f938a1dc641037023 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 28 Sep 2025 21:07:33 +0200 Subject: [PATCH 04/11] Add assertion --- gix-diff/tests/diff/blob/slider.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index 744a45edd1f..c3030187ec8 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -124,6 +124,7 @@ mod baseline { let (_, [before_hunk_start, before_hunk_len, after_hunk_start, after_hunk_len]) = matches.extract(); + assert!(hunk_header.is_none(), "should not overwrite existing hunk_header"); hunk_header = Some(HunkHeader { before_hunk_start: before_hunk_start .to_str() From f0ceafa62ff519484e016789c50fa49a79819a80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Sun, 28 Sep 2025 21:34:52 +0200 Subject: [PATCH 05/11] Thanks clippy --- gix-diff/tests/diff/blob/slider.rs | 6 +++--- tests/it/src/commands/create_diff_cases.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index c3030187ec8..28a78b426a7 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -79,7 +79,7 @@ mod baseline { Ok(baseline.collect()) } - fn skip_header(&mut self) -> () { + fn skip_header(&mut self) { // diff --git a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa b/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb // index ccccccc..ddddddd 100644 // --- a/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa @@ -156,7 +156,7 @@ mod baseline { b'+' => hunk_lines.push((DiffLineKind::Add, line[1..].into())), b'-' => hunk_lines.push((DiffLineKind::Remove, line[1..].into())), _ => todo!(), - }; + } match self.lines.peek() { Some(next_line) if next_line.starts_with(START_OF_HEADER) => break, @@ -218,7 +218,7 @@ fn sliders() -> gix_testtools::Result { stack, ); - assert!(commits.len() > 0); + assert!(!commits.is_empty()); assert!(commits.len().is_multiple_of(2)); let mut buffer = Vec::new(); diff --git a/tests/it/src/commands/create_diff_cases.rs b/tests/it/src/commands/create_diff_cases.rs index 8d89a73c740..51fcc5f8152 100644 --- a/tests/it/src/commands/create_diff_cases.rs +++ b/tests/it/src/commands/create_diff_cases.rs @@ -90,8 +90,8 @@ echo {script_name} >> .gitignore eprintln!("{old_blob_id:?} {old_path:?} {new_blob_id:?} {new_path:?}"); - let dst_old_blob = assets.join(format!("{}.commit", old_blob_id)); - let dst_new_blob = assets.join(format!("{}.commit", new_blob_id)); + let dst_old_blob = assets.join(format!("{old_blob_id}.commit")); + let dst_new_blob = assets.join(format!("{new_blob_id}.commit")); if !dry_run { let old_blob = repo.objects.find_blob(&old_blob_id, &mut buf)?.data; std::fs::write(dst_old_blob, old_blob)?; From 6eaafda0685f52a519cef1c1e929008b11d93b43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Tue, 7 Oct 2025 15:35:00 +0200 Subject: [PATCH 06/11] Use `git diff --no-index` to create baseline This obviates the need to create a git history which considerably simplifies the test as it can just direclty read files instead. --- Cargo.lock | 1 - gix-diff/tests/Cargo.toml | 1 - gix-diff/tests/diff/blob/slider.rs | 125 +++++---------------- tests/it/src/commands/create_diff_cases.rs | 18 +-- 4 files changed, 35 insertions(+), 110 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e27a4e13780..3743d563d06 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1698,7 +1698,6 @@ dependencies = [ "gix-object", "gix-odb", "gix-pathspec", - "gix-ref", "gix-testtools", "gix-traverse", "gix-worktree", diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index 10c6d17f58e..6ce9b137f1a 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -26,7 +26,6 @@ gix-worktree = { path = "../../gix-worktree" } gix-object = { path = "../../gix-object" } gix-odb = { path = "../../gix-odb" } gix-filter = { path = "../../gix-filter" } -gix-ref = { path = "../../gix-ref" } gix-traverse = { path = "../../gix-traverse" } gix-testtools = { path = "../../tests/tools" } diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index 28a78b426a7..ee3394da103 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -5,11 +5,7 @@ use gix_diff::blob::{ unified_diff::{ConsumeHunk, ContextSize, DiffLineKind, HunkHeader}, Algorithm, UnifiedDiff, }; -use gix_object::FindExt; -use gix_ref::{ - bstr::{self, BString}, - file::ReferenceExt, -}; +use gix_object::bstr::{self, BString}; #[derive(Debug, PartialEq)] struct DiffHunk { @@ -60,7 +56,7 @@ mod baseline { use std::path::Path; use gix_diff::blob::unified_diff::{DiffLineKind, HunkHeader}; - use gix_ref::bstr::{BString, ByteSlice}; + use gix_object::bstr::{BString, ByteSlice}; use gix_testtools::once_cell::sync::Lazy; use regex::bytes::Regex; @@ -176,102 +172,33 @@ mod baseline { #[test] fn sliders() -> gix_testtools::Result { let worktree_path = fixture_path()?; + let asset_dir = worktree_path.join("assets"); let git_dir = worktree_path.join(".git"); - let odb = gix_odb::at(git_dir.join("objects"))?; - let refs = gix_ref::file::Store::at(git_dir.clone(), gix_ref::store::init::Options::default()); - // In `../tree.rs`, there is `head_of` which would obviate the need for using `gix_ref`. - let mut head = refs.find("HEAD")?; - let head_id = head.peel_to_id(&refs, &odb)?; - - let commits = gix_traverse::commit::Simple::new(Some(head_id), &odb) - .map(Result::unwrap) - .map(|commit| commit.id) - .collect::>(); - - let index = gix_index::File::at(git_dir.join("index"), gix_hash::Kind::Sha1, false, Default::default())?; - let stack = gix_worktree::Stack::from_state_and_ignore_case( - worktree_path.clone(), - false, - gix_worktree::stack::State::AttributesAndIgnoreStack { - attributes: Default::default(), - ignore: Default::default(), - }, - &index, - index.path_backing(), - ); - let capabilities = gix_fs::Capabilities::probe(&git_dir); - let mut resource_cache = gix_diff::blob::Platform::new( - Default::default(), - gix_diff::blob::Pipeline::new( - gix_diff::blob::pipeline::WorktreeRoots { - old_root: None, - new_root: None, - }, - gix_filter::Pipeline::new(Default::default(), Default::default()), - vec![], - gix_diff::blob::pipeline::Options { - large_file_threshold_bytes: 0, - fs: capabilities, - }, - ), - gix_diff::blob::pipeline::Mode::ToGit, - stack, - ); - - assert!(!commits.is_empty()); - assert!(commits.len().is_multiple_of(2)); - - let mut buffer = Vec::new(); - let mut buffer2 = Vec::new(); - - let mut iter = commits.chunks(2); - - while let Some(&[new_commit_id, old_commit_id]) = iter.next() { - // TODO: We can extract the duplicate code. - let old_commit = odb.find_commit(&old_commit_id, &mut buffer)?.to_owned(); - let new_commit = odb.find_commit(&new_commit_id, &mut buffer)?.to_owned(); - let file_path = BString::from(old_commit.message.trim_ascii_end()); - - let old_tree = old_commit.tree; - let old_blob_id = odb - .find_tree(&old_tree, &mut buffer2)? - .entries - .iter() - .find(|entry| file_path.eq(entry.filename)) - .unwrap() - .oid - .to_owned(); - - let new_tree = new_commit.tree; - let new_blob_id = odb - .find_tree(&new_tree, &mut buffer2)? - .entries - .iter() - .find(|entry| entry.filename == file_path) - .unwrap() - .oid - .to_owned(); - - resource_cache.set_resource( - old_blob_id, - gix_object::tree::EntryKind::Blob, - file_path.as_ref(), - gix_diff::blob::ResourceKind::OldOrSource, - &odb, - )?; - resource_cache.set_resource( - new_blob_id, - gix_object::tree::EntryKind::Blob, - file_path.as_ref(), - gix_diff::blob::ResourceKind::NewOrDestination, - &odb, - )?; - let outcome = resource_cache.prepare_diff()?; + let dir = std::fs::read_dir(&git_dir)?; + + for entry in dir { + let entry = entry?; + let file_name = entry.file_name().into_string().expect("to be string"); + + if !file_name.ends_with(".baseline") { + continue; + } + + let parts: Vec<_> = file_name.split(".").collect(); + let name = parts[0]; + + let parts: Vec<_> = name.split("-").collect(); + let [old_blob_id, new_blob_id] = parts[..] else { + unimplemented!(); + }; + + let old_data = std::fs::read(asset_dir.join(format!("{old_blob_id}.commit")))?; + let new_data = std::fs::read(asset_dir.join(format!("{new_blob_id}.commit")))?; let interner = gix_diff::blob::intern::InternedInput::new( - tokens_for_diffing(outcome.old.data.as_slice().unwrap_or_default()), - tokens_for_diffing(outcome.new.data.as_slice().unwrap_or_default()), + tokens_for_diffing(old_data.as_slice()), + tokens_for_diffing(new_data.as_slice()), ); let actual = gix_diff::blob::diff( @@ -280,7 +207,7 @@ fn sliders() -> gix_testtools::Result { UnifiedDiff::new(&interner, DiffHunkRecorder::new(), ContextSize::symmetrical(3)), )?; - let baseline_path = git_dir.join(format!("{old_commit_id}-{new_commit_id}.baseline")); + let baseline_path = git_dir.join(file_name); let baseline = Baseline::collect(baseline_path).unwrap(); pretty_assertions::assert_eq!(actual, baseline); diff --git a/tests/it/src/commands/create_diff_cases.rs b/tests/it/src/commands/create_diff_cases.rs index 51fcc5f8152..67973c539c9 100644 --- a/tests/it/src/commands/create_diff_cases.rs +++ b/tests/it/src/commands/create_diff_cases.rs @@ -60,7 +60,10 @@ pub(super) mod function { let mut blocks: Vec = vec![format!( r#"#!/usr/bin/env bash -set -eu -o pipefail +# TODO: +# `git diff --no-index` returns 1 when there's differences, but 1 is treated as an error by the +# shell. +# set -eu -o pipefail ROOT="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" @@ -68,6 +71,8 @@ git init echo .gitignore >> .gitignore echo {asset_dir}/ >> .gitignore echo {script_name} >> .gitignore + +mkdir -p {asset_dir} "# )]; @@ -101,14 +106,9 @@ echo {script_name} >> .gitignore } blocks.push(format!( - r#"cp "$ROOT/{asset_dir}/{old_blob_id}.commit" ./{old_blob_id} -git add {old_blob_id} -git commit -m {old_blob_id} -cp "$ROOT/{asset_dir}/{new_blob_id}.commit" ./{old_blob_id} -git add {old_blob_id} -git commit -m "{old_blob_id} -> {new_blob_id}" - -git diff HEAD^ HEAD > .git/$(git rev-parse HEAD^)-$(git rev-parse HEAD).baseline + r#"git diff --no-index "$ROOT/{asset_dir}/{old_blob_id}.commit" "$ROOT/{asset_dir}/{new_blob_id}.commit" > .git/{old_blob_id}-{new_blob_id}.baseline +cp "$ROOT/{asset_dir}/{old_blob_id}.commit" assets/ +cp "$ROOT/{asset_dir}/{new_blob_id}.commit" assets/ "# )); } From 653a353ea83dda294cb01b58b15c7312a6bbe25a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Tue, 7 Oct 2025 15:59:36 +0200 Subject: [PATCH 07/11] Use textual diff in test This makes visually interpreting differences between the two versions significantly easier. --- gix-diff/src/blob/unified_diff/impls.rs | 3 ++- gix-diff/tests/diff/blob/slider.rs | 35 +++++++++++++++---------- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/gix-diff/src/blob/unified_diff/impls.rs b/gix-diff/src/blob/unified_diff/impls.rs index 959560fba7f..a2afdd450ba 100644 --- a/gix-diff/src/blob/unified_diff/impls.rs +++ b/gix-diff/src/blob/unified_diff/impls.rs @@ -260,7 +260,8 @@ where } impl DiffLineKind { - const fn to_prefix(self) -> char { + /// TODO: Document. + pub const fn to_prefix(self) -> char { match self { DiffLineKind::Context => ' ', DiffLineKind::Add => '+', diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index ee3394da103..808a377c332 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -2,7 +2,7 @@ use std::{iter::Peekable, path::PathBuf}; use gix_diff::blob::{ intern::TokenSource, - unified_diff::{ConsumeHunk, ContextSize, DiffLineKind, HunkHeader}, + unified_diff::{ConsumeHunk, ContextSize, HunkHeader}, Algorithm, UnifiedDiff, }; use gix_object::bstr::{self, BString}; @@ -10,7 +10,7 @@ use gix_object::bstr::{self, BString}; #[derive(Debug, PartialEq)] struct DiffHunk { header: HunkHeader, - lines: Vec<(DiffLineKind, BString)>, + lines: BString, } struct DiffHunkRecorder { @@ -31,12 +31,18 @@ impl ConsumeHunk for DiffHunkRecorder { header: HunkHeader, lines: &[(gix_diff::blob::unified_diff::DiffLineKind, &[u8])], ) -> std::io::Result<()> { - let lines: Vec<_> = lines - .iter() - .map(|(kind, line)| (*kind, BString::new(line.to_vec()))) - .collect(); + let mut buf = Vec::new(); - let diff_hunk = DiffHunk { header, lines }; + for &(kind, line) in lines { + buf.push(kind.to_prefix() as u8); + buf.extend_from_slice(line); + buf.push(b'\n'); + } + + let diff_hunk = DiffHunk { + header, + lines: buf.into(), + }; self.inner.push(diff_hunk); @@ -55,8 +61,8 @@ struct Baseline<'a> { mod baseline { use std::path::Path; - use gix_diff::blob::unified_diff::{DiffLineKind, HunkHeader}; - use gix_object::bstr::{BString, ByteSlice}; + use gix_diff::blob::unified_diff::HunkHeader; + use gix_object::bstr::ByteSlice; use gix_testtools::once_cell::sync::Lazy; use regex::bytes::Regex; @@ -105,7 +111,7 @@ mod baseline { }); let mut hunk_header = None; - let mut hunk_lines: Vec<(DiffLineKind, BString)> = Vec::new(); + let mut hunk_lines = Vec::new(); while let Some(line) = self.lines.next() { // @@ -18,6 +18,7 @@ abc def ghi @@ -148,9 +154,10 @@ mod baseline { } match line[0] { - b' ' => hunk_lines.push((DiffLineKind::Context, line[1..].into())), - b'+' => hunk_lines.push((DiffLineKind::Add, line[1..].into())), - b'-' => hunk_lines.push((DiffLineKind::Remove, line[1..].into())), + b' ' | b'+' | b'-' => { + hunk_lines.extend_from_slice(line); + hunk_lines.push(b'\n'); + } _ => todo!(), } @@ -163,7 +170,7 @@ mod baseline { hunk_header.map(|hunk_header| DiffHunk { header: hunk_header, - lines: hunk_lines, + lines: hunk_lines.into(), }) } } From 41cfdba30c5f526ca32e6350c244f5a26f887648 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Tue, 7 Oct 2025 16:44:44 +0200 Subject: [PATCH 08/11] Parse hunk header without regex --- Cargo.lock | 1 - gix-diff/tests/Cargo.toml | 1 - gix-diff/tests/diff/blob/slider.rs | 76 ++++++++++++++---------------- 3 files changed, 36 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3743d563d06..072b45faf98 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1703,7 +1703,6 @@ dependencies = [ "gix-worktree", "insta", "pretty_assertions", - "regex", "shell-words", ] diff --git a/gix-diff/tests/Cargo.toml b/gix-diff/tests/Cargo.toml index 6ce9b137f1a..2485ee5e60d 100644 --- a/gix-diff/tests/Cargo.toml +++ b/gix-diff/tests/Cargo.toml @@ -32,4 +32,3 @@ gix-testtools = { path = "../../tests/tools" } insta = "1.43.1" shell-words = "1" pretty_assertions = "1.4.0" -regex = "1.6.0" diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index 808a377c332..e8feb4f5863 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -63,11 +63,11 @@ mod baseline { use gix_diff::blob::unified_diff::HunkHeader; use gix_object::bstr::ByteSlice; - use gix_testtools::once_cell::sync::Lazy; - use regex::bytes::Regex; use super::{Baseline, DiffHunk}; + static START_OF_HEADER: &[u8; 4] = b"@@ -"; + impl Baseline<'_> { pub fn collect(baseline_path: impl AsRef) -> std::io::Result> { let content = std::fs::read(baseline_path)?; @@ -99,56 +99,52 @@ mod baseline { let line = self.lines.next().expect("line to be present"); assert!(line.starts_with(b"+++ ")); } + + /// Parse diff hunk headers that conform to the unified diff hunk header format. + /// + /// The parser is very primitive and relies on the fact that `+18` is parsed as `18`. This + /// allows us to split the input on ` ` and `,` only. + /// + /// @@ -18,6 +18,7 @@ abc def ghi + /// @@ -{before_hunk_start},{before_hunk_len} +{after_hunk_start},{after_hunk_len} @@ + fn parse_hunk_header(&self, line: &[u8]) -> gix_testtools::Result { + let Some(line) = line.strip_prefix(START_OF_HEADER) else { + todo!() + }; + + let parts: Vec<_> = line.split(|b| *b == b' ' || *b == b',').collect(); + let [before_hunk_start, before_hunk_len, after_hunk_start, after_hunk_len, ..] = parts[..] else { + todo!() + }; + + Ok(HunkHeader { + before_hunk_start: self.parse_number(before_hunk_start), + before_hunk_len: self.parse_number(before_hunk_len), + after_hunk_start: self.parse_number(after_hunk_start), + after_hunk_len: self.parse_number(after_hunk_len), + }) + } + + fn parse_number(&self, bytes: &[u8]) -> u32 { + bytes + .to_str() + .expect("to be a valid UTF-8 string") + .parse::() + .expect("to be a number") + } } impl Iterator for Baseline<'_> { type Item = DiffHunk; fn next(&mut self) -> Option { - static HEADER_REGEX: Lazy = Lazy::new(|| { - Regex::new(r"@@ -([[:digit:]]+),([[:digit:]]+) \+([[:digit:]]+),([[:digit:]]+) @@") - .expect("regex to be valid") - }); - let mut hunk_header = None; let mut hunk_lines = Vec::new(); while let Some(line) = self.lines.next() { - // @@ -18,6 +18,7 @@ abc def ghi - // TODO: - // make sure that the following is correct - // @@ -{before_hunk_start},{before_hunk_len} +{after_hunk_start},{after_hunk_len} - - static START_OF_HEADER: &[u8; 4] = b"@@ -"; - if line.starts_with(START_OF_HEADER) { - let matches = HEADER_REGEX.captures(line).expect("line to be a hunk header"); - - let (_, [before_hunk_start, before_hunk_len, after_hunk_start, after_hunk_len]) = matches.extract(); - assert!(hunk_header.is_none(), "should not overwrite existing hunk_header"); - hunk_header = Some(HunkHeader { - before_hunk_start: before_hunk_start - .to_str() - .expect("to be a valid UTF-8 string") - .parse::() - .expect("to be a number"), - before_hunk_len: before_hunk_len - .to_str() - .expect("to be a valid UTF-8 string") - .parse::() - .expect("to be a number"), - after_hunk_start: after_hunk_start - .to_str() - .expect("to be a valid UTF-8 string") - .parse::() - .expect("to be a number"), - after_hunk_len: after_hunk_len - .to_str() - .expect("to be a valid UTF-8 string") - .parse::() - .expect("to be a number"), - }); + hunk_header = self.parse_hunk_header(line).ok(); continue; } From 20c728830ad249e165cd3922cfa372fb0897239b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Wed, 8 Oct 2025 09:27:30 +0200 Subject: [PATCH 09/11] Enable `set -eu -o pipefail` --- tests/it/src/commands/create_diff_cases.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/it/src/commands/create_diff_cases.rs b/tests/it/src/commands/create_diff_cases.rs index 67973c539c9..5675b483861 100644 --- a/tests/it/src/commands/create_diff_cases.rs +++ b/tests/it/src/commands/create_diff_cases.rs @@ -60,10 +60,7 @@ pub(super) mod function { let mut blocks: Vec = vec![format!( r#"#!/usr/bin/env bash -# TODO: -# `git diff --no-index` returns 1 when there's differences, but 1 is treated as an error by the -# shell. -# set -eu -o pipefail +set -eu -o pipefail ROOT="$(cd "$(dirname "${{BASH_SOURCE[0]}}")" && pwd)" @@ -106,7 +103,7 @@ mkdir -p {asset_dir} } blocks.push(format!( - r#"git diff --no-index "$ROOT/{asset_dir}/{old_blob_id}.commit" "$ROOT/{asset_dir}/{new_blob_id}.commit" > .git/{old_blob_id}-{new_blob_id}.baseline + r#"git diff --no-index "$ROOT/{asset_dir}/{old_blob_id}.commit" "$ROOT/{asset_dir}/{new_blob_id}.commit" > .git/{old_blob_id}-{new_blob_id}.baseline || true cp "$ROOT/{asset_dir}/{old_blob_id}.commit" assets/ cp "$ROOT/{asset_dir}/{new_blob_id}.commit" assets/ "# From 28f9fe8b683971819d4d1db45c8051b912505e5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Wed, 8 Oct 2025 09:29:31 +0200 Subject: [PATCH 10/11] Replace .commit by .blob --- gix-diff/tests/diff/blob/slider.rs | 4 ++-- tests/it/src/commands/create_diff_cases.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index e8feb4f5863..ae75fcb518a 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -196,8 +196,8 @@ fn sliders() -> gix_testtools::Result { unimplemented!(); }; - let old_data = std::fs::read(asset_dir.join(format!("{old_blob_id}.commit")))?; - let new_data = std::fs::read(asset_dir.join(format!("{new_blob_id}.commit")))?; + let old_data = std::fs::read(asset_dir.join(format!("{old_blob_id}.blob")))?; + let new_data = std::fs::read(asset_dir.join(format!("{new_blob_id}.blob")))?; let interner = gix_diff::blob::intern::InternedInput::new( tokens_for_diffing(old_data.as_slice()), diff --git a/tests/it/src/commands/create_diff_cases.rs b/tests/it/src/commands/create_diff_cases.rs index 5675b483861..438503f243d 100644 --- a/tests/it/src/commands/create_diff_cases.rs +++ b/tests/it/src/commands/create_diff_cases.rs @@ -92,8 +92,8 @@ mkdir -p {asset_dir} eprintln!("{old_blob_id:?} {old_path:?} {new_blob_id:?} {new_path:?}"); - let dst_old_blob = assets.join(format!("{old_blob_id}.commit")); - let dst_new_blob = assets.join(format!("{new_blob_id}.commit")); + let dst_old_blob = assets.join(format!("{old_blob_id}.blob")); + let dst_new_blob = assets.join(format!("{new_blob_id}.blob")); if !dry_run { let old_blob = repo.objects.find_blob(&old_blob_id, &mut buf)?.data; std::fs::write(dst_old_blob, old_blob)?; @@ -103,9 +103,9 @@ mkdir -p {asset_dir} } blocks.push(format!( - r#"git diff --no-index "$ROOT/{asset_dir}/{old_blob_id}.commit" "$ROOT/{asset_dir}/{new_blob_id}.commit" > .git/{old_blob_id}-{new_blob_id}.baseline || true -cp "$ROOT/{asset_dir}/{old_blob_id}.commit" assets/ -cp "$ROOT/{asset_dir}/{new_blob_id}.commit" assets/ + r#"git diff --no-index "$ROOT/{asset_dir}/{old_blob_id}.blob" "$ROOT/{asset_dir}/{new_blob_id}.blob" > .git/{old_blob_id}-{new_blob_id}.baseline || true +cp "$ROOT/{asset_dir}/{old_blob_id}.blob" assets/ +cp "$ROOT/{asset_dir}/{new_blob_id}.blob" assets/ "# )); } From 1060c4cd57dca604f223a1866d81dad6ecbc9612 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20R=C3=BC=C3=9Fler?= Date: Wed, 8 Oct 2025 09:51:26 +0200 Subject: [PATCH 11/11] Use unified diff in test --- gix-diff/tests/diff/blob/slider.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/gix-diff/tests/diff/blob/slider.rs b/gix-diff/tests/diff/blob/slider.rs index ae75fcb518a..16315229cb3 100644 --- a/gix-diff/tests/diff/blob/slider.rs +++ b/gix-diff/tests/diff/blob/slider.rs @@ -5,7 +5,7 @@ use gix_diff::blob::{ unified_diff::{ConsumeHunk, ContextSize, HunkHeader}, Algorithm, UnifiedDiff, }; -use gix_object::bstr::{self, BString}; +use gix_object::bstr::{self, BString, ByteVec}; #[derive(Debug, PartialEq)] struct DiffHunk { @@ -213,6 +213,30 @@ fn sliders() -> gix_testtools::Result { let baseline_path = git_dir.join(file_name); let baseline = Baseline::collect(baseline_path).unwrap(); + let actual = actual + .iter() + .fold(BString::default(), |mut acc, diff_hunk| { + acc.push_str(diff_hunk.header.to_string().as_str()); + acc.push(b'\n'); + + acc.extend_from_slice(&diff_hunk.lines); + + acc + }) + .to_string(); + + let baseline = baseline + .iter() + .fold(BString::default(), |mut acc, diff_hunk| { + acc.push_str(diff_hunk.header.to_string().as_str()); + acc.push(b'\n'); + + acc.extend_from_slice(&diff_hunk.lines); + + acc + }) + .to_string(); + pretty_assertions::assert_eq!(actual, baseline); }