mirror of
https://github.com/gitbutlerapp/gitbutler.git
synced 2024-11-23 20:54:50 +03:00
Merge pull request #3344 from gitbutlerapp/separate-integration-tests
separate integration tests for 'changeset' crate
This commit is contained in:
commit
b9c07265c2
@ -2,6 +2,10 @@
|
||||
name = "gitbutler-changeset"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
thiserror.workspace = true
|
||||
|
@ -101,137 +101,3 @@ pub trait FormatHunk: RawHunk {
|
||||
}
|
||||
|
||||
impl<T> FormatHunk for T where T: RawHunk {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct TestHunk {
|
||||
removal_start: usize,
|
||||
addition_start: usize,
|
||||
changes: Vec<super::Change>,
|
||||
}
|
||||
|
||||
impl super::RawHunk for TestHunk {
|
||||
type ChangeIterator = std::vec::IntoIter<super::Change>;
|
||||
|
||||
fn get_removal_start(&self) -> usize {
|
||||
self.removal_start
|
||||
}
|
||||
|
||||
fn get_addition_start(&self) -> usize {
|
||||
self.addition_start
|
||||
}
|
||||
|
||||
fn changes(&self) -> Self::ChangeIterator {
|
||||
self.changes.clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TestHunk {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.fmt_unified(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_hunk() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 1,
|
||||
addition_start: 1,
|
||||
changes: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_removal() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![super::Change::Removal("Hello, world!".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30 +38,0 @@\n-Hello, world!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_removal_trailing_nl() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![super::Change::Removal("Hello, world!\n".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "@@ -30 +38,0 @@\n-Hello, world!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_addition() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![super::Change::Addition("Hello, world!".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30,0 +38 @@\n+Hello, world!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_addition_trailing_nl() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![super::Change::Addition("Hello, world!\n".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "@@ -30,0 +38 @@\n+Hello, world!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_modified_line() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![
|
||||
super::Change::Removal("Hello, world!".to_string()),
|
||||
super::Change::Addition("Hello, GitButler!\n".to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30 +38 @@\n-Hello, world!\n\\ No newline at end of file\n+Hello, GitButler!\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserve_change_order() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 20,
|
||||
changes: vec![
|
||||
super::Change::Addition("Hello, GitButler!\n".to_string()),
|
||||
super::Change::Removal("Hello, world!\n".to_string()),
|
||||
super::Change::Removal("Hello, world 2!\n".to_string()),
|
||||
super::Change::Addition("Hello, GitButler 2!\n".to_string()),
|
||||
super::Change::Removal("Hello, world 3!".to_string()),
|
||||
super::Change::Addition("Hello, GitButler 3!\n".to_string()),
|
||||
super::Change::Addition("Hello, GitButler 4!".to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30,3 +20,4 @@\n+Hello, GitButler!\n-Hello, world!\n-Hello, world 2!\n+Hello, GitButler 2!\n-Hello, world 3!\n\\ No newline at end of file\n+Hello, GitButler 3!\n+Hello, GitButler 4!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -175,131 +175,3 @@ impl<S: AsRef<str>> From<S> for Signature {
|
||||
fn bigrams(s: &[u8]) -> impl Iterator<Item = (u8, u8)> + '_ {
|
||||
s.iter().copied().zip(s.iter().skip(1).copied())
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
macro_rules! assert_score {
|
||||
($sig:ident, $s:expr, $e:expr) => {
|
||||
let score = $sig.score_str($s);
|
||||
if (score - $e).abs() >= 0.1 {
|
||||
panic!(
|
||||
"expected score of {} for string {:?}, got {}",
|
||||
$e, $s, score
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn score_signature() {
|
||||
let sig = Signature::from("hello world");
|
||||
|
||||
// NOTE: The scores here are not exact, but are close enough
|
||||
// to be useful for testing purposes, hence why some have the same
|
||||
// "score" but different strings.
|
||||
assert_score!(sig, "hello world", 1.0);
|
||||
assert_score!(sig, "hello world!", 0.95);
|
||||
assert_score!(sig, "hello world!!", 0.9);
|
||||
assert_score!(sig, "hello world!!!", 0.85);
|
||||
assert_score!(sig, "hello world!!!!", 0.8);
|
||||
assert_score!(sig, "hello world!!!!!", 0.75);
|
||||
assert_score!(sig, "hello world!!!!!!", 0.7);
|
||||
assert_score!(sig, "hello world!!!!!!!", 0.65);
|
||||
assert_score!(sig, "hello world!!!!!!!!", 0.62);
|
||||
assert_score!(sig, "hello world!!!!!!!!!", 0.6);
|
||||
assert_score!(sig, "hello world!!!!!!!!!!", 0.55);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn score_ignores_whitespace() {
|
||||
let sig = Signature::from("hello world");
|
||||
|
||||
assert_score!(sig, "hello world", 1.0);
|
||||
assert_score!(sig, "hello world ", 1.0);
|
||||
assert_score!(sig, "hello\nworld ", 1.0);
|
||||
assert_score!(sig, "hello\n\tworld ", 1.0);
|
||||
assert_score!(sig, "\t\t hel lo\n\two rld \t\t", 1.0);
|
||||
}
|
||||
|
||||
const TEXT1: &str = include_str!("../fixture/text1.txt");
|
||||
const TEXT2: &str = include_str!("../fixture/text2.txt");
|
||||
const TEXT3: &str = include_str!("../fixture/text3.txt");
|
||||
const CODE1: &str = include_str!("../fixture/code1.txt");
|
||||
const CODE2: &str = include_str!("../fixture/code2.txt");
|
||||
const CODE3: &str = include_str!("../fixture/code3.txt");
|
||||
const CODE4: &str = include_str!("../fixture/code4.txt");
|
||||
const LARGE1: &str = include_str!("../fixture/large1.txt");
|
||||
const LARGE2: &str = include_str!("../fixture/large2.txt");
|
||||
|
||||
macro_rules! real_test {
|
||||
($a: ident, $b: ident, are_similar) => {
|
||||
paste::paste! {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn [<test_ $a _ $b _are_similar>]() {
|
||||
let a = Signature::from($a);
|
||||
let b = Signature::from($b);
|
||||
assert!(a.score_str($b) >= 0.95);
|
||||
assert!(b.score_str($a) >= 0.95);
|
||||
}
|
||||
}
|
||||
};
|
||||
($a: ident, $b: ident, are_not_similar) => {
|
||||
paste::paste! {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn [<test_ $a _ $b _are_not_similar>]() {
|
||||
let a = Signature::from($a);
|
||||
let b = Signature::from($b);
|
||||
assert!(a.score_str($b) < 0.95);
|
||||
assert!(b.score_str($a) < 0.95);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Only similar pairs:
|
||||
// - TEXT1, TEXT2
|
||||
// - CODE1, CODE2
|
||||
// - LARGE1, LARGE2
|
||||
real_test!(TEXT1, TEXT2, are_similar);
|
||||
real_test!(CODE1, CODE2, are_similar);
|
||||
real_test!(LARGE1, LARGE2, are_similar);
|
||||
|
||||
// Check all other combos
|
||||
real_test!(TEXT1, TEXT3, are_not_similar);
|
||||
real_test!(TEXT1, CODE1, are_not_similar);
|
||||
real_test!(TEXT1, CODE2, are_not_similar);
|
||||
real_test!(TEXT1, CODE3, are_not_similar);
|
||||
real_test!(TEXT1, CODE4, are_not_similar);
|
||||
real_test!(TEXT1, LARGE1, are_not_similar);
|
||||
real_test!(TEXT1, LARGE2, are_not_similar);
|
||||
real_test!(TEXT2, TEXT3, are_not_similar);
|
||||
real_test!(TEXT2, CODE1, are_not_similar);
|
||||
real_test!(TEXT2, CODE2, are_not_similar);
|
||||
real_test!(TEXT2, CODE3, are_not_similar);
|
||||
real_test!(TEXT2, CODE4, are_not_similar);
|
||||
real_test!(TEXT2, LARGE1, are_not_similar);
|
||||
real_test!(TEXT2, LARGE2, are_not_similar);
|
||||
real_test!(TEXT3, CODE1, are_not_similar);
|
||||
real_test!(TEXT3, CODE2, are_not_similar);
|
||||
real_test!(TEXT3, CODE3, are_not_similar);
|
||||
real_test!(TEXT3, CODE4, are_not_similar);
|
||||
real_test!(TEXT3, LARGE1, are_not_similar);
|
||||
real_test!(TEXT3, LARGE2, are_not_similar);
|
||||
real_test!(CODE1, CODE3, are_not_similar);
|
||||
real_test!(CODE1, CODE4, are_not_similar);
|
||||
real_test!(CODE1, LARGE1, are_not_similar);
|
||||
real_test!(CODE1, LARGE2, are_not_similar);
|
||||
real_test!(CODE2, CODE3, are_not_similar);
|
||||
real_test!(CODE2, CODE4, are_not_similar);
|
||||
real_test!(CODE2, LARGE1, are_not_similar);
|
||||
real_test!(CODE2, LARGE2, are_not_similar);
|
||||
real_test!(CODE3, CODE4, are_not_similar);
|
||||
real_test!(CODE3, LARGE1, are_not_similar);
|
||||
real_test!(CODE3, LARGE2, are_not_similar);
|
||||
real_test!(CODE4, LARGE1, are_not_similar);
|
||||
real_test!(CODE4, LARGE2, are_not_similar);
|
||||
}
|
||||
|
@ -105,101 +105,3 @@ impl LineSpan {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn span_new() {
|
||||
for s in 0..20 {
|
||||
for e in s + 1..=20 {
|
||||
let span = LineSpan::new(s, e);
|
||||
assert_eq!(span.start(), s);
|
||||
assert_eq!(span.end(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_extract() {
|
||||
let lines = [
|
||||
"Hello, world!",
|
||||
"This is a test.",
|
||||
"This is another test.\r",
|
||||
"This is a third test.\r",
|
||||
"This is a fourth test.",
|
||||
"This is a fifth test.\r",
|
||||
"This is a sixth test.",
|
||||
"This is a seventh test.\r",
|
||||
"This is an eighth test.",
|
||||
"This is a ninth test.\r",
|
||||
"This is a tenth test.", // note no newline at end
|
||||
];
|
||||
|
||||
let full_text = lines.join("\n");
|
||||
|
||||
// calculate the known character offsets of each line
|
||||
let mut offsets = vec![];
|
||||
let mut start = 0;
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
// If it's not the last line, add 1 for the newline character.
|
||||
let end = start + line.len() + (i != (lines.len() - 1)) as usize;
|
||||
offsets.push((start, end));
|
||||
start = end;
|
||||
}
|
||||
|
||||
// Test single-line extraction
|
||||
for i in 0..lines.len() - 1 {
|
||||
let span = LineSpan::new(i, i + 1);
|
||||
let expected = &full_text[offsets[i].0..offsets[i].1];
|
||||
let (extracted, start_offset, end_offset) = span.extract(&full_text).unwrap();
|
||||
|
||||
assert_eq!(extracted, expected);
|
||||
assert_eq!((start_offset, end_offset), (offsets[i].0, offsets[i].1));
|
||||
}
|
||||
|
||||
// Test multi-line extraction
|
||||
for i in 0..lines.len() {
|
||||
for j in i..=lines.len() {
|
||||
let span = LineSpan::new(i, j);
|
||||
|
||||
assert!(span.line_count() == (j - i));
|
||||
|
||||
if i == j {
|
||||
assert!(span.is_empty());
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_start = offsets[i].0;
|
||||
let expected_end = offsets[j - 1].1;
|
||||
let expected_text = &full_text[expected_start..expected_end];
|
||||
|
||||
let (extracted, start_offset, end_offset) = span.extract(&full_text).unwrap();
|
||||
assert_eq!(extracted, expected_text);
|
||||
assert_eq!((start_offset, end_offset), (expected_start, expected_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_intersects() {
|
||||
let span = LineSpan::new(5, 11); // Exclusive end
|
||||
|
||||
assert!(span.intersects(&LineSpan::new(10, 11))); // Intersect at start
|
||||
assert!(span.intersects(&LineSpan::new(0, 11))); // Fully contained
|
||||
assert!(span.intersects(&LineSpan::new(10, 15))); // Partial overlap
|
||||
assert!(span.intersects(&LineSpan::new(4, 6))); // Intersect at end
|
||||
assert!(span.intersects(&LineSpan::new(5, 6))); // Exact match start
|
||||
assert!(span.intersects(&LineSpan::new(0, 6))); // Overlap at end
|
||||
assert!(span.intersects(&LineSpan::new(0, 8))); // Overlap middle
|
||||
assert!(span.intersects(&LineSpan::new(0, 10))); // Overlap up to end
|
||||
assert!(span.intersects(&LineSpan::new(9, 10))); // Overlap at single point
|
||||
assert!(span.intersects(&LineSpan::new(7, 9))); // Overlap inside
|
||||
|
||||
// Test cases where there should be no intersection due to exclusive end
|
||||
assert!(!span.intersects(&LineSpan::new(0, 5))); // Before start
|
||||
assert!(!span.intersects(&LineSpan::new(11, 20))); // After end
|
||||
assert!(!span.intersects(&LineSpan::new(11, 12))); // Just after end
|
||||
}
|
||||
}
|
||||
|
3
crates/gitbutler-changeset/tests/changeset.rs
Normal file
3
crates/gitbutler-changeset/tests/changeset.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod diff;
|
||||
mod signature;
|
||||
mod span;
|
133
crates/gitbutler-changeset/tests/diff/mod.rs
Normal file
133
crates/gitbutler-changeset/tests/diff/mod.rs
Normal file
@ -0,0 +1,133 @@
|
||||
mod hunk {
|
||||
use gitbutler_changeset::{Change, FormatHunk, RawHunk};
|
||||
use std::fmt;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct TestHunk {
|
||||
removal_start: usize,
|
||||
addition_start: usize,
|
||||
changes: Vec<Change>,
|
||||
}
|
||||
|
||||
impl RawHunk for TestHunk {
|
||||
type ChangeIterator = std::vec::IntoIter<Change>;
|
||||
|
||||
fn get_removal_start(&self) -> usize {
|
||||
self.removal_start
|
||||
}
|
||||
|
||||
fn get_addition_start(&self) -> usize {
|
||||
self.addition_start
|
||||
}
|
||||
|
||||
fn changes(&self) -> Self::ChangeIterator {
|
||||
self.changes.clone().into_iter()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for TestHunk {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.fmt_unified(f)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn empty_hunk() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 1,
|
||||
addition_start: 1,
|
||||
changes: vec![],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_removal() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![Change::Removal("Hello, world!".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30 +38,0 @@\n-Hello, world!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_removal_trailing_nl() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![Change::Removal("Hello, world!\n".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "@@ -30 +38,0 @@\n-Hello, world!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_addition() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![Change::Addition("Hello, world!".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30,0 +38 @@\n+Hello, world!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_addition_trailing_nl() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![Change::Addition("Hello, world!\n".to_string())],
|
||||
};
|
||||
|
||||
assert_eq!(format!("{hunk}"), "@@ -30,0 +38 @@\n+Hello, world!\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn single_modified_line() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 38,
|
||||
changes: vec![
|
||||
Change::Removal("Hello, world!".to_string()),
|
||||
Change::Addition("Hello, GitButler!\n".to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30 +38 @@\n-Hello, world!\n\\ No newline at end of file\n+Hello, GitButler!\n"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preserve_change_order() {
|
||||
let hunk = TestHunk {
|
||||
removal_start: 30,
|
||||
addition_start: 20,
|
||||
changes: vec![
|
||||
Change::Addition("Hello, GitButler!\n".to_string()),
|
||||
Change::Removal("Hello, world!\n".to_string()),
|
||||
Change::Removal("Hello, world 2!\n".to_string()),
|
||||
Change::Addition("Hello, GitButler 2!\n".to_string()),
|
||||
Change::Removal("Hello, world 3!".to_string()),
|
||||
Change::Addition("Hello, GitButler 3!\n".to_string()),
|
||||
Change::Addition("Hello, GitButler 4!".to_string()),
|
||||
],
|
||||
};
|
||||
|
||||
assert_eq!(
|
||||
format!("{hunk}"),
|
||||
"@@ -30,3 +20,4 @@\n+Hello, GitButler!\n-Hello, world!\n-Hello, world 2!\n+Hello, GitButler 2!\n-Hello, world 3!\n\\ No newline at end of file\n+Hello, GitButler 3!\n+Hello, GitButler 4!\n\\ No newline at end of file\n"
|
||||
);
|
||||
}
|
||||
}
|
124
crates/gitbutler-changeset/tests/signature/mod.rs
Normal file
124
crates/gitbutler-changeset/tests/signature/mod.rs
Normal file
@ -0,0 +1,124 @@
|
||||
use gitbutler_changeset::Signature;
|
||||
|
||||
macro_rules! assert_score {
|
||||
($sig:ident, $s:expr, $e:expr) => {
|
||||
let score = $sig.score_str($s);
|
||||
if (score - $e).abs() >= 0.1 {
|
||||
panic!(
|
||||
"expected score of {} for string {:?}, got {}",
|
||||
$e, $s, score
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn score_signature() {
|
||||
let sig = Signature::from("hello world");
|
||||
|
||||
// NOTE: The scores here are not exact, but are close enough
|
||||
// to be useful for testing purposes, hence why some have the same
|
||||
// "score" but different strings.
|
||||
assert_score!(sig, "hello world", 1.0);
|
||||
assert_score!(sig, "hello world!", 0.95);
|
||||
assert_score!(sig, "hello world!!", 0.9);
|
||||
assert_score!(sig, "hello world!!!", 0.85);
|
||||
assert_score!(sig, "hello world!!!!", 0.8);
|
||||
assert_score!(sig, "hello world!!!!!", 0.75);
|
||||
assert_score!(sig, "hello world!!!!!!", 0.7);
|
||||
assert_score!(sig, "hello world!!!!!!!", 0.65);
|
||||
assert_score!(sig, "hello world!!!!!!!!", 0.62);
|
||||
assert_score!(sig, "hello world!!!!!!!!!", 0.6);
|
||||
assert_score!(sig, "hello world!!!!!!!!!!", 0.55);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn score_ignores_whitespace() {
|
||||
let sig = Signature::from("hello world");
|
||||
|
||||
assert_score!(sig, "hello world", 1.0);
|
||||
assert_score!(sig, "hello world ", 1.0);
|
||||
assert_score!(sig, "hello\nworld ", 1.0);
|
||||
assert_score!(sig, "hello\n\tworld ", 1.0);
|
||||
assert_score!(sig, "\t\t hel lo\n\two rld \t\t", 1.0);
|
||||
}
|
||||
|
||||
const TEXT1: &str = include_str!("../fixtures/text1.txt");
|
||||
const TEXT2: &str = include_str!("../fixtures/text2.txt");
|
||||
const TEXT3: &str = include_str!("../fixtures/text3.txt");
|
||||
const CODE1: &str = include_str!("../fixtures/code1.txt");
|
||||
const CODE2: &str = include_str!("../fixtures/code2.txt");
|
||||
const CODE3: &str = include_str!("../fixtures/code3.txt");
|
||||
const CODE4: &str = include_str!("../fixtures/code4.txt");
|
||||
const LARGE1: &str = include_str!("../fixtures/large1.txt");
|
||||
const LARGE2: &str = include_str!("../fixtures/large2.txt");
|
||||
|
||||
macro_rules! real_test {
|
||||
($a: ident, $b: ident, are_similar) => {
|
||||
paste::paste! {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn [<$a _ $b _are_similar>]() {
|
||||
let a = Signature::from($a);
|
||||
let b = Signature::from($b);
|
||||
assert!(a.score_str($b) >= 0.95);
|
||||
assert!(b.score_str($a) >= 0.95);
|
||||
}
|
||||
}
|
||||
};
|
||||
($a: ident, $b: ident, are_not_similar) => {
|
||||
paste::paste! {
|
||||
#[test]
|
||||
#[allow(non_snake_case)]
|
||||
fn [<$a _ $b _are_not_similar>]() {
|
||||
let a = Signature::from($a);
|
||||
let b = Signature::from($b);
|
||||
assert!(a.score_str($b) < 0.95);
|
||||
assert!(b.score_str($a) < 0.95);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Only similar pairs:
|
||||
// - TEXT1, TEXT2
|
||||
// - CODE1, CODE2
|
||||
// - LARGE1, LARGE2
|
||||
real_test!(TEXT1, TEXT2, are_similar);
|
||||
real_test!(CODE1, CODE2, are_similar);
|
||||
real_test!(LARGE1, LARGE2, are_similar);
|
||||
|
||||
// Check all other combos
|
||||
real_test!(TEXT1, TEXT3, are_not_similar);
|
||||
real_test!(TEXT1, CODE1, are_not_similar);
|
||||
real_test!(TEXT1, CODE2, are_not_similar);
|
||||
real_test!(TEXT1, CODE3, are_not_similar);
|
||||
real_test!(TEXT1, CODE4, are_not_similar);
|
||||
real_test!(TEXT1, LARGE1, are_not_similar);
|
||||
real_test!(TEXT1, LARGE2, are_not_similar);
|
||||
real_test!(TEXT2, TEXT3, are_not_similar);
|
||||
real_test!(TEXT2, CODE1, are_not_similar);
|
||||
real_test!(TEXT2, CODE2, are_not_similar);
|
||||
real_test!(TEXT2, CODE3, are_not_similar);
|
||||
real_test!(TEXT2, CODE4, are_not_similar);
|
||||
real_test!(TEXT2, LARGE1, are_not_similar);
|
||||
real_test!(TEXT2, LARGE2, are_not_similar);
|
||||
real_test!(TEXT3, CODE1, are_not_similar);
|
||||
real_test!(TEXT3, CODE2, are_not_similar);
|
||||
real_test!(TEXT3, CODE3, are_not_similar);
|
||||
real_test!(TEXT3, CODE4, are_not_similar);
|
||||
real_test!(TEXT3, LARGE1, are_not_similar);
|
||||
real_test!(TEXT3, LARGE2, are_not_similar);
|
||||
real_test!(CODE1, CODE3, are_not_similar);
|
||||
real_test!(CODE1, CODE4, are_not_similar);
|
||||
real_test!(CODE1, LARGE1, are_not_similar);
|
||||
real_test!(CODE1, LARGE2, are_not_similar);
|
||||
real_test!(CODE2, CODE3, are_not_similar);
|
||||
real_test!(CODE2, CODE4, are_not_similar);
|
||||
real_test!(CODE2, LARGE1, are_not_similar);
|
||||
real_test!(CODE2, LARGE2, are_not_similar);
|
||||
real_test!(CODE3, CODE4, are_not_similar);
|
||||
real_test!(CODE3, LARGE1, are_not_similar);
|
||||
real_test!(CODE3, LARGE2, are_not_similar);
|
||||
real_test!(CODE4, LARGE1, are_not_similar);
|
||||
real_test!(CODE4, LARGE2, are_not_similar);
|
94
crates/gitbutler-changeset/tests/span/mod.rs
Normal file
94
crates/gitbutler-changeset/tests/span/mod.rs
Normal file
@ -0,0 +1,94 @@
|
||||
use gitbutler_changeset::LineSpan;
|
||||
|
||||
#[test]
|
||||
fn new() {
|
||||
for s in 0..20 {
|
||||
for e in s + 1..=20 {
|
||||
let span = LineSpan::new(s, e);
|
||||
assert_eq!(span.start(), s);
|
||||
assert_eq!(span.end(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extract() {
|
||||
let lines = [
|
||||
"Hello, world!",
|
||||
"This is a test.",
|
||||
"This is another test.\r",
|
||||
"This is a third test.\r",
|
||||
"This is a fourth test.",
|
||||
"This is a fifth test.\r",
|
||||
"This is a sixth test.",
|
||||
"This is a seventh test.\r",
|
||||
"This is an eighth test.",
|
||||
"This is a ninth test.\r",
|
||||
"This is a tenth test.", // note no newline at end
|
||||
];
|
||||
|
||||
let full_text = lines.join("\n");
|
||||
|
||||
// calculate the known character offsets of each line
|
||||
let mut offsets = vec![];
|
||||
let mut start = 0;
|
||||
for (i, line) in lines.iter().enumerate() {
|
||||
// If it's not the last line, add 1 for the newline character.
|
||||
let end = start + line.len() + (i != (lines.len() - 1)) as usize;
|
||||
offsets.push((start, end));
|
||||
start = end;
|
||||
}
|
||||
|
||||
// Test single-line extraction
|
||||
for i in 0..lines.len() - 1 {
|
||||
let span = LineSpan::new(i, i + 1);
|
||||
let expected = &full_text[offsets[i].0..offsets[i].1];
|
||||
let (extracted, start_offset, end_offset) = span.extract(&full_text).unwrap();
|
||||
|
||||
assert_eq!(extracted, expected);
|
||||
assert_eq!((start_offset, end_offset), (offsets[i].0, offsets[i].1));
|
||||
}
|
||||
|
||||
// Test multi-line extraction
|
||||
for i in 0..lines.len() {
|
||||
for j in i..=lines.len() {
|
||||
let span = LineSpan::new(i, j);
|
||||
|
||||
assert!(span.line_count() == (j - i));
|
||||
|
||||
if i == j {
|
||||
assert!(span.is_empty());
|
||||
continue;
|
||||
}
|
||||
|
||||
let expected_start = offsets[i].0;
|
||||
let expected_end = offsets[j - 1].1;
|
||||
let expected_text = &full_text[expected_start..expected_end];
|
||||
|
||||
let (extracted, start_offset, end_offset) = span.extract(&full_text).unwrap();
|
||||
assert_eq!(extracted, expected_text);
|
||||
assert_eq!((start_offset, end_offset), (expected_start, expected_end));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn intersects() {
|
||||
let span = LineSpan::new(5, 11); // Exclusive end
|
||||
|
||||
assert!(span.intersects(&LineSpan::new(10, 11))); // Intersect at start
|
||||
assert!(span.intersects(&LineSpan::new(0, 11))); // Fully contained
|
||||
assert!(span.intersects(&LineSpan::new(10, 15))); // Partial overlap
|
||||
assert!(span.intersects(&LineSpan::new(4, 6))); // Intersect at end
|
||||
assert!(span.intersects(&LineSpan::new(5, 6))); // Exact match start
|
||||
assert!(span.intersects(&LineSpan::new(0, 6))); // Overlap at end
|
||||
assert!(span.intersects(&LineSpan::new(0, 8))); // Overlap middle
|
||||
assert!(span.intersects(&LineSpan::new(0, 10))); // Overlap up to end
|
||||
assert!(span.intersects(&LineSpan::new(9, 10))); // Overlap at single point
|
||||
assert!(span.intersects(&LineSpan::new(7, 9))); // Overlap inside
|
||||
|
||||
// Test cases where there should be no intersection due to exclusive end
|
||||
assert!(!span.intersects(&LineSpan::new(0, 5))); // Before start
|
||||
assert!(!span.intersects(&LineSpan::new(11, 20))); // After end
|
||||
assert!(!span.intersects(&LineSpan::new(11, 12))); // Just after end
|
||||
}
|
Loading…
Reference in New Issue
Block a user