use gix_diff::{ Rewrites, blob::DiffLineStats, rewrites, rewrites::{ Copies, CopySource, tracker::{ ChangeKind, visit::{Source, SourceKind}, }, }, tree::visit::Relation,};use gix_object::tree::EntryKind;use pretty_assertions::assert_eq; use crate::{ hex_to_id, rewrites::{Change, NULL_ID},}; #[test]fn rename_by_id() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: None, percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); assert!( track.try_push_change(Change::modification(), "a".into()).is_some(), "modifications play no role in rename tracking" ); assert!( track.try_push_change(Change::deletion(), "b".into()).is_none(), "recorded for later matching" ); assert!( track.try_push_change(Change::addition(), "c".into()).is_none(), "recorded for later matching" ); let mut called = false; let out = util::assert_emit(&mut track, |dst, src| { assert!(!called, "only one rename pair is expected"); called = true; assert_eq!( src.unwrap(), Source { entry_mode: EntryKind::Blob.into(), id: *NULL_ID, kind: SourceKind::Rename, location: "b".into(), change: &Change::deletion(), diff: None, } ); assert_eq!(dst.location, "c"); std::ops::ControlFlow::Continue(()) }); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(())} #[test]fn copy_by_similarity_reports_limit_if_encountered() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: Some(0.5), }), percentage: None, limit: 1, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a\n"), (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), (Change::modification(), "d", "ab"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { assert!(src.is_none()); match calls { 0 => assert_eq!(dst.location, "a"), 1 => assert_eq!(dst.location, "a-cpy-1"), 2 => assert_eq!(dst.location, "a-cpy-2"), 3 => assert_eq!(dst.location, "d"), _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks_skipped_for_copy_tracking_due_to_limit: 4, ..Default::default() }, "no similarity check was performed at all - all or nothing" ); Ok(())} #[test]fn copy_by_id() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: None, }), percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a"), (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), (Change::modification(), "d", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { let id = hex_to_id( "2e65efe2a145dda7ee51d1741299f848e5bf752e", "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ); let source_a = Source { entry_mode: EntryKind::Blob.into(), id, kind: SourceKind::Copy, location: "a".into(), change: &Change { id, ..Change::modification() }, diff: None, }; match calls { 0 => { assert_eq!(src.unwrap(), source_a); assert_eq!( dst.location, "a-cpy-1", "it just finds the first possible match in order, ignoring other candidates" ); } 1 => { assert_eq!(src.unwrap(), source_a, "copy-sources can be used multiple times"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(())} #[test]fn copy_by_id_search_in_all_sources() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFilesAndAllSources, percentage: None, }), percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), ], )?; let content_id = hex_to_id( "2e65efe2a145dda7ee51d1741299f848e5bf752e", "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ); let mut calls = 0; let out = util::assert_emit_with_objects_and_sources( &mut track, |dst, src| { let source_a = Source { entry_mode: EntryKind::Blob.into(), id: content_id, kind: SourceKind::Copy, location: "a-src".into(), change: &Change { id: content_id, ..Change::modification() }, diff: None, }; match calls { 0 => { assert_eq!(src.unwrap(), source_a); assert_eq!( dst.location, "a-cpy-1", "it just finds the first possible match in order, ignoring other candidates" ); } 1 => { assert_eq!(src.unwrap(), source_a, "copy-sources can be used multiple times"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, [( { let mut c = Change::modification(); c.id = content_id; c }, "a-src", )], ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(())} #[test]fn copy_by_50_percent_similarity() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: Some(0.5), }), percentage: None, limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a\n"), (Change::addition(), "a-cpy-1", "a\nb"), (Change::addition(), "a-cpy-2", "a\nc"), (Change::modification(), "d", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { let id = hex_to_id( "78981922613b2afb6025042ff6bd878ac1994e85", "f8625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b", ); let source_a = Source { entry_mode: EntryKind::Blob.into(), id, kind: SourceKind::Copy, location: "a".into(), change: &Change { id, ..Change::modification() }, diff: Some(DiffLineStats { removals: 0, insertions: 1, before: 1, after: 2, similarity: 0.6666667, }), }; match calls { 0 => { assert_eq!( src.unwrap(), source_a, "it finds the first possible source, no candidates" ); assert_eq!(dst.location, "a-cpy-1"); } 1 => { assert_eq!(src.unwrap(), source_a, "the same source can be reused as well"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks: 4, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); Ok(())} #[test]fn copy_by_id_in_additions_only() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: None, }), percentage: None, limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a"), (Change::modification(), "a-cpy-1", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { match calls { 0 => { assert!(src.is_none()); assert_eq!(dst.location, "a"); } 1 => { assert!(src.is_none()); assert_eq!( dst.location, "a-cpy-1", "copy detection is only done for additions, not within modifications" ); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); Ok(())} #[test]fn rename_by_similarity_reports_limit_if_encountered() -> crate::Result { let rewrites = Rewrites { copies: None, percentage: Some(0.5), limit: 1, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::deletion(), "a", "first\nsecond\n"), (Change::addition(), "b", "firt\nsecond\n"), (Change::addition(), "c", "second\nunrelated\n"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { assert!(src.is_none()); match calls { 0 => assert_eq!(dst.location, "a"), 1 => assert_eq!(dst.location, "b"), 2 => assert_eq!(dst.location, "c"), _ => panic!("too many elements emitted"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks_skipped_for_rename_tracking_due_to_limit: 2, ..Default::default() }, "no similarity check was performed at all - all or nothing" ); Ok(())} #[test]fn rename_by_50_percent_similarity() -> crate::Result { let rewrites = Rewrites { copies: None, percentage: Some(0.5), limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::deletion(), "a", "first\nsecond\n"), (Change::addition(), "b", "firt\nsecond\n"), (Change::addition(), "c", "second\nunrelated\n"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { match calls { 0 => { let id = hex_to_id( "66a52ee7a1d803dc57859c3e95ac9dcdc87c0164", "fdf2fd32de1d33ee5744979b40b07e0930a12c59dfbc5b3b6d34b807f944ba6e", ); assert_eq!(Lines 1–500 of 1206. Load more
use gix_diff::{ Rewrites, blob::DiffLineStats, rewrites, rewrites::{ Copies, CopySource, tracker::{ ChangeKind, visit::{Source, SourceKind}, }, }, tree::visit::Relation, }; use gix_object::tree::EntryKind; use pretty_assertions::assert_eq; use crate::{ hex_to_id, rewrites::{Change, NULL_ID}, }; #[test] fn rename_by_id() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: None, percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); assert!( track.try_push_change(Change::modification(), "a".into()).is_some(), "modifications play no role in rename tracking" ); assert!( track.try_push_change(Change::deletion(), "b".into()).is_none(), "recorded for later matching" ); assert!( track.try_push_change(Change::addition(), "c".into()).is_none(), "recorded for later matching" ); let mut called = false; let out = util::assert_emit(&mut track, |dst, src| { assert!(!called, "only one rename pair is expected"); called = true; assert_eq!( src.unwrap(), Source { entry_mode: EntryKind::Blob.into(), id: *NULL_ID, kind: SourceKind::Rename, location: "b".into(), change: &Change::deletion(), diff: None, } ); assert_eq!(dst.location, "c"); std::ops::ControlFlow::Continue(()) }); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(()) } #[test] fn copy_by_similarity_reports_limit_if_encountered() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: Some(0.5), }), percentage: None, limit: 1, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a\n"), (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), (Change::modification(), "d", "ab"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { assert!(src.is_none()); match calls { 0 => assert_eq!(dst.location, "a"), 1 => assert_eq!(dst.location, "a-cpy-1"), 2 => assert_eq!(dst.location, "a-cpy-2"), 3 => assert_eq!(dst.location, "d"), _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks_skipped_for_copy_tracking_due_to_limit: 4, ..Default::default() }, "no similarity check was performed at all - all or nothing" ); Ok(()) } #[test] fn copy_by_id() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: None, }), percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a"), (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), (Change::modification(), "d", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { let id = hex_to_id( "2e65efe2a145dda7ee51d1741299f848e5bf752e", "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ); let source_a = Source { entry_mode: EntryKind::Blob.into(), id, kind: SourceKind::Copy, location: "a".into(), change: &Change { id, ..Change::modification() }, diff: None, }; match calls { 0 => { assert_eq!(src.unwrap(), source_a); assert_eq!( dst.location, "a-cpy-1", "it just finds the first possible match in order, ignoring other candidates" ); } 1 => { assert_eq!(src.unwrap(), source_a, "copy-sources can be used multiple times"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(()) } #[test] fn copy_by_id_search_in_all_sources() -> crate::Result { // Limits are only applied when doing rewrite-checks for limit in [0, 1] { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFilesAndAllSources, percentage: None, }), percentage: None, limit, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::addition(), "a-cpy-1", "a"), (Change::addition(), "a-cpy-2", "a"), ], )?; let content_id = hex_to_id( "2e65efe2a145dda7ee51d1741299f848e5bf752e", "eb337bcee2061c5313c9a1392116b6c76039e9e30d71467ae359b36277e17dc7", ); let mut calls = 0; let out = util::assert_emit_with_objects_and_sources( &mut track, |dst, src| { let source_a = Source { entry_mode: EntryKind::Blob.into(), id: content_id, kind: SourceKind::Copy, location: "a-src".into(), change: &Change { id: content_id, ..Change::modification() }, diff: None, }; match calls { 0 => { assert_eq!(src.unwrap(), source_a); assert_eq!( dst.location, "a-cpy-1", "it just finds the first possible match in order, ignoring other candidates" ); } 1 => { assert_eq!(src.unwrap(), source_a, "copy-sources can be used multiple times"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, [( { let mut c = Change::modification(); c.id = content_id; c }, "a-src", )], ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); } Ok(()) } #[test] fn copy_by_50_percent_similarity() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: Some(0.5), }), percentage: None, limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a\n"), (Change::addition(), "a-cpy-1", "a\nb"), (Change::addition(), "a-cpy-2", "a\nc"), (Change::modification(), "d", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { let id = hex_to_id( "78981922613b2afb6025042ff6bd878ac1994e85", "f8625e43f9e04f24291f77cdbe4c71b3c2a3b0003f60419b3ed06a058d766c8b", ); let source_a = Source { entry_mode: EntryKind::Blob.into(), id, kind: SourceKind::Copy, location: "a".into(), change: &Change { id, ..Change::modification() }, diff: Some(DiffLineStats { removals: 0, insertions: 1, before: 1, after: 2, similarity: 0.6666667, }), }; match calls { 0 => { assert_eq!( src.unwrap(), source_a, "it finds the first possible source, no candidates" ); assert_eq!(dst.location, "a-cpy-1"); } 1 => { assert_eq!(src.unwrap(), source_a, "the same source can be reused as well"); assert_eq!(dst.location, "a-cpy-2"); } 2 => { assert!(src.is_none()); assert_eq!(dst.location, "d"); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks: 4, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); Ok(()) } #[test] fn copy_by_id_in_additions_only() -> crate::Result { let rewrites = Rewrites { copies: Some(Copies { source: CopySource::FromSetOfModifiedFiles, percentage: None, }), percentage: None, limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::modification(), "a", "a"), (Change::modification(), "a-cpy-1", "a"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { match calls { 0 => { assert!(src.is_none()); assert_eq!(dst.location, "a"); } 1 => { assert!(src.is_none()); assert_eq!( dst.location, "a-cpy-1", "copy detection is only done for additions, not within modifications" ); } _ => panic!("too many emissions"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, ..Default::default() }, "no similarity check was performed, it was all matched by id" ); Ok(()) } #[test] fn rename_by_similarity_reports_limit_if_encountered() -> crate::Result { let rewrites = Rewrites { copies: None, percentage: Some(0.5), limit: 1, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::deletion(), "a", "first\nsecond\n"), (Change::addition(), "b", "firt\nsecond\n"), (Change::addition(), "c", "second\nunrelated\n"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { assert!(src.is_none()); match calls { 0 => assert_eq!(dst.location, "a"), 1 => assert_eq!(dst.location, "b"), 2 => assert_eq!(dst.location, "c"), _ => panic!("too many elements emitted"), } calls += 1; std::ops::ControlFlow::Continue(()) }, odb, ); assert_eq!( out, rewrites::Outcome { options: rewrites, num_similarity_checks_skipped_for_rename_tracking_due_to_limit: 2, ..Default::default() }, "no similarity check was performed at all - all or nothing" ); Ok(()) } #[test] fn rename_by_50_percent_similarity() -> crate::Result { let rewrites = Rewrites { copies: None, percentage: Some(0.5), limit: 0, track_empty: false, }; let mut track = util::new_tracker(rewrites); let odb = util::add_retained_blobs( &mut track, [ (Change::deletion(), "a", "first\nsecond\n"), (Change::addition(), "b", "firt\nsecond\n"), (Change::addition(), "c", "second\nunrelated\n"), ], )?; let mut calls = 0; let out = util::assert_emit_with_objects( &mut track, |dst, src| { match calls { 0 => { let id = hex_to_id( "66a52ee7a1d803dc57859c3e95ac9dcdc87c0164", "fdf2fd32de1d33ee5744979b40b07e0930a12c59dfbc5b3b6d34b807f944ba6e", ); assert_eq!(