Merge pull request #3720 from gitbutlerapp/remove-old-changesets

remove old changeset code
This commit is contained in:
Josh Junon 2024-05-07 07:17:09 +02:00 committed by GitHub
commit f1e9e95a1a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 1 additions and 980 deletions

View File

@ -16,7 +16,6 @@ jobs:
rust: ${{ steps.filter.outputs.rust }}
gitbutler-core: ${{ steps.filter.outputs.gitbutler-core }}
gitbutler-tauri: ${{ steps.filter.outputs.gitbutler-tauri }}
gitbutler-changeset: ${{ steps.filter.outputs.gitbutler-changeset }}
gitbutler-git: ${{ steps.filter.outputs.gitbutler-git }}
gitbutler-cli: ${{ steps.filter.outputs.gitbutler-cli }}
gitbutler-watcher: ${{ steps.filter.outputs.gitbutler-watcher }}
@ -47,9 +46,6 @@ jobs:
gitbutler-core:
- *rust
- 'crates/gitbutler-core/**'
gitbutler-changeset:
- *rust
- 'crates/gitbutler-changeset/**'
gitbutler-git:
- *rust
- 'crates/gitbutler-git/**'
@ -109,7 +105,7 @@ jobs:
- uses: ./.github/actions/init-env-rust
# TODO(qix-): we have to exclude the app here for now because for some
# TODO(qix-): reason it doesn't build with the docs feature enabled.
- run: cargo doc --no-deps --all-features --document-private-items -p gitbutler-changeset -p gitbutler-git
- run: cargo doc --no-deps --all-features --document-private-items -p gitbutler-git
env:
RUSTDOCFLAGS: -Dwarnings
@ -137,31 +133,6 @@ jobs:
features: ${{ toJson(matrix.features) }}
action: ${{ matrix.action }}
check-gitbutler-changeset:
needs: changes
if: ${{ needs.changes.outputs.gitbutler-changeset == 'true' }}
runs-on: ubuntu-latest
container:
image: ghcr.io/gitbutlerapp/ci-base-image:latest
strategy:
matrix:
action:
- test
- check
- check-tests
features:
- ''
- '*'
- []
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/init-env-rust
- uses: ./.github/actions/check-crate
with:
crate: gitbutler-changeset
features: ${{ toJson(matrix.features) }}
action: ${{ matrix.action }}
check-gitbutler-git:
needs: changes
if: ${{ needs.changes.outputs.gitbutler-git == 'true' }}
@ -267,7 +238,6 @@ jobs:
- changes
- check-gitbutler-tauri
- check-gitbutler-core
- check-gitbutler-changeset
- check-gitbutler-git
- check-gitbutler-cli
- check-gitbutler-watcher

14
Cargo.lock generated
View File

@ -2115,14 +2115,6 @@ dependencies = [
"thiserror",
]
[[package]]
name = "gitbutler-changeset"
version = "0.0.0"
dependencies = [
"paste",
"thiserror",
]
[[package]]
name = "gitbutler-cli"
version = "0.0.0"
@ -4602,12 +4594,6 @@ dependencies = [
"subtle",
]
[[package]]
name = "paste"
version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c"
[[package]]
name = "pathdiff"
version = "0.2.1"

View File

@ -2,7 +2,6 @@
members = [
"crates/gitbutler-core",
"crates/gitbutler-tauri",
"crates/gitbutler-changeset",
"crates/gitbutler-git",
"crates/gitbutler-watcher",
"crates/gitbutler-testsupport",

View File

@ -1,19 +0,0 @@
[package]
name = "gitbutler-changeset"
version = "0.0.0"
edition = "2021"
publish = false
[lib]
doctest = false
[dependencies]
thiserror.workspace = true
[dev-dependencies]
paste = "1.0.14"
[lints.clippy]
all = "deny"
perf = "deny"
correctness = "deny"

View File

@ -1,103 +0,0 @@
use core::fmt;
/// A single line change in a diff. Note that
/// hunks MUST NOT have context lines.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Change {
/// The line was added.
///
/// **Do not include the leading '+' character**,
/// and make sure that **the final (CR)LF _is_ included**, if any.
/// The only case where a newline can be omitted is at the end of the file.
Addition(String),
/// The line was removed.
///
/// **Do not include the leading '-' character**,
/// and make sure that **the final (CR)LF _is_ included**, if any.
/// The only case where a newline can be omitted is at the end of the file.
Removal(String),
}
/// Represents a "raw" hunk of a file diff.
/// "Raw" hunks are hunks that are reported directly
/// from Git or Git-like sources, whereby the hunk
/// represents a single, contiguous set of changes
/// that have not been split up into adjacent hunks
/// or the like.
pub trait RawHunk {
/// The type of iterator that produces [`Change`]s.
type ChangeIterator: Iterator<Item = Change> + Clone + 'static;
/// Returns the line at which removals begin.
/// Note that lines start at 1.
fn get_removal_start(&self) -> usize;
/// Returns the line at which additions begin.
/// Note that lines start at 1.
fn get_addition_start(&self) -> usize;
/// Returns an iterator over the additions and removals
/// in the hunk.
fn changes(&self) -> Self::ChangeIterator;
}
/// Formats the hunk as a unified diff.
pub trait FormatHunk: RawHunk {
/// Formats the hunk as a unified diff.
fn fmt_unified(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let removal_start = self.get_removal_start();
let addition_start = self.get_addition_start();
let mut removal_count = 0;
let mut addition_count = 0;
let mut last_addition_index = 0;
let mut last_removal_index = 0;
for (i, change) in self.changes().enumerate() {
match change {
Change::Addition(_) => {
addition_count += 1;
last_addition_index = i;
}
Change::Removal(_) => {
removal_count += 1;
last_removal_index = i;
}
}
}
if removal_count == 0 && addition_count == 0 {
return Ok(());
}
write!(f, "@@ -{removal_start}")?;
if removal_count != 1 {
write!(f, ",{removal_count}")?;
}
write!(f, " +{addition_start}")?;
if addition_count != 1 {
write!(f, ",{addition_count}")?;
}
writeln!(f, " @@")?;
for (i, change) in self.changes().enumerate() {
let line = match change {
Change::Addition(line) => {
write!(f, "+{}", line)?;
line
}
Change::Removal(line) => {
write!(f, "-{}", line)?;
line
}
};
if (i == last_removal_index || i == last_addition_index) && !line.ends_with('\n') {
write!(f, "\n\\ No newline at end of file\n")?;
}
}
Ok(())
}
}
impl<T> FormatHunk for T where T: RawHunk {}

View File

@ -1 +0,0 @@
pub(crate) mod hunk;

View File

@ -1,57 +0,0 @@
//! # GitButler diff algorithms and datastructures library
//!
//! This crate implements the GitButler algorithms and data structures
//! for working with diffs/changesets. Diffs are, at their core,
//! sets of hunks within one or more files. Canonically, Git
//! uses its own system for managing changesets called the staging
//! area, whereas GitButler uses multiple changesets within
//! a single Git staging area to add a level of granularity and
//! organization of multiple distinct groups of changes, called
//! "virtual branches". This library implements algorithms for
//! managing, moving, and otherwise working with diffs
//! across virtual branches.
//!
//! Note that all text is assumed UTF-8 due to how Git is designed.
//! For more information about how Git handles encoding, see
//! <https://git-scm.com/docs/gitattributes/2.19.2#_working_tree_encoding>.
//!
//! ---
//!
//! ## Hunk Theory
//!
//! GitButler's hunk theory is as follows:
//!
//! A hunk is a contiguous span of lines in a file that are being
//! changed. Boiling away the quintessential 'diff' representation
//! (with `-` indicating a line being removed and `+` indicating
//! a line being added, as one nuclear operation), the algorithms
//! herein treat a hunk as two discrete operations - a removal
//! of the source range and an insertion of the replacement text.
//!
//! Thus, conflicts are determined based on whether or not two hunks
//! remove all or part of the same source range. As a corollary,
//! two source ranges that intersect are thought of as conflicts.
//!
//! This allows us to trivially detect and help to reason about
//! conflicts, and to easily re-calculate and reflow content
//! within a file given a list of hunks (including sorting and
//! efficiently calculating new line number ranges).
//!
//! ---
//!
//! For more information, see the
//! [GitButler organization](https://github.com/gitbutlerapp)
//! or the [GitButler website](https://gitbutler.com).
#![deny(missing_docs)]
#![allow(clippy::doc_markdown, clippy::missing_errors_doc)]
#![feature(slice_as_chunks)]
pub(crate) mod diff;
pub(crate) mod signature;
pub(crate) mod span;
pub use self::{
diff::hunk::{Change, FormatHunk, RawHunk},
signature::Signature,
span::LineSpan,
};

View File

@ -1,177 +0,0 @@
//! A note about serialization:
//!
//! You may serialize the signature however you'd like; it's just a fixed-length byte array.
//! We _would_ support `serde` but currently fixed-length arrays have terrible, quasi-non-existent
//! support.
//!
//! Related issues:
//!
//! - <https://github.com/serde-rs/serde/issues/2120>
//! - <https://github.com/serde-rs/serde/issues/1937>
//! - <https://github.com/serde-rs/serde/issues/1272>
//!
//! If/when those are fixed, we should be able to (trivially) add `serde` support.
//! Otherwise, neither the length prefix imposed by `(de)serialize_bytes()` nor the
//! terrible compaction and optimization of `(de)serialize_tuple()` are acceptable.
const BITS: usize = 3;
const SHIFT: usize = 8 - BITS;
const FINGERPRINT_ENTRIES: usize = (1 << BITS) * (1 << BITS);
const FINGERPRINT_BYTES: usize = FINGERPRINT_ENTRIES * ::core::mem::size_of::<SigBucket>();
const TOTAL_BYTES: usize = 1 + 4 + FINGERPRINT_BYTES; // we encode a version byte and a 4-byte length at the beginning
// NOTE: This is not efficient if `SigBucket` is 1 byte (u8).
// NOTE: If `SigBucket` is changed to a u8, then the implementation
// NOTE: *should* be updated to eschew the byte conversion and use
// NOTE: slices directly.
type SigBucket = u16;
/// Similarity signatures are fixed-width bigram histograms from the
/// [Sørensen-Dice coefficient algorithm](https://en.wikipedia.org/wiki/S%C3%B8rensen%E2%80%93Dice_coefficient).
/// They act as fixed-length fingerprints for a file's contents, usable
/// to check similarity between two hunks, using the fingerprint of the
/// old hunk and the string contents of a new hunk.
///
/// This implementation is based on the crate [`strsim`](https://crates.io/crates/strsim),
/// but has been modified to be two-step (first, create a signature from
/// a string, then compare the signature to another string), as well as
/// to use bytes instead of `char`s, quantize the input bytes, and then
/// to use saturating arithmetic to avoid overflows and reduce total
/// memory usage.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct Signature([u8; TOTAL_BYTES]);
impl Signature {
/// Creates a new signature from a byte array.
#[inline]
#[must_use]
pub fn new(bytes: [u8; TOTAL_BYTES]) -> Self {
Self(bytes)
}
/// Returns the similarity signature as a byte array.
///
/// **NOTE:** Do not inspect the contents of this array,
/// or assume anything about its contents. It is an
/// implementation detail and may change at any time.
#[inline]
#[must_use]
pub fn as_bytes(&self) -> &[u8; TOTAL_BYTES] {
&self.0
}
/// Scores this signature against a string.
/// Values are between 0.0 and 1.0, with 1.0 being
/// a perfect match.
///
/// Typically, values below 0.95 are pretty indicative
/// of unrelated hunks.
///
/// # Panics
///
/// Will panic if the signature has an unsupported version
/// byte.
///
/// # Security
///
/// This function is not fixed-time, and may leak information
/// about the signature or the original file contents.
///
/// Do not use for any security-related purposes.
#[must_use]
pub fn score_str<S: AsRef<str>>(&self, other: S) -> f64 {
assert!(self.0[0] == 0, "unsupported signature version");
let original_length = u32::from_le_bytes(self.0[1..5].try_into().expect("invalid length"));
if original_length < 2 {
return 0.0;
}
let other = other.as_ref();
let other_string: String = other.chars().filter(|&x| !char::is_whitespace(x)).collect();
let other = other_string.as_bytes();
if other.len() < 2 {
return 0.0;
}
let mut matching_bigrams: usize = 0;
let mut self_buckets = self.bucket_iter().collect::<Vec<_>>();
for (left, right) in bigrams(other) {
let left = left >> SHIFT;
let right = right >> SHIFT;
let index = ((left as usize) << BITS) | (right as usize);
if self_buckets[index] > 0 {
self_buckets[index] -= 1;
matching_bigrams += 1;
}
}
#[allow(clippy::cast_precision_loss)]
{
(2 * matching_bigrams) as f64 / (original_length as usize + other.len() - 2) as f64
}
}
fn bucket_iter(&self) -> impl Iterator<Item = SigBucket> + '_ {
unsafe {
self.0[(TOTAL_BYTES - FINGERPRINT_BYTES)..]
.as_chunks_unchecked::<{ ::core::mem::size_of::<SigBucket>() }>()
.iter()
.map(|ch: &[u8; ::core::mem::size_of::<SigBucket>()]| SigBucket::from_le_bytes(*ch))
}
}
}
impl<S: AsRef<str>> From<S> for Signature {
#[inline]
fn from(source: S) -> Self {
let source = source.as_ref();
let source_string: String = source
.chars()
.filter(|&x| !char::is_whitespace(x))
.collect();
let source = source_string.as_bytes();
let source_len: u32 = source
.len()
.try_into()
.expect("strings with a byte-length above u32::MAX are not supported");
let mut bytes = [0; TOTAL_BYTES];
bytes[0] = 0; // version byte (0)
bytes[1..5].copy_from_slice(&source_len.to_le_bytes()); // next 4 bytes are the length
if source_len >= 2 {
let mut buckets = [0 as SigBucket; FINGERPRINT_ENTRIES];
for (left, right) in bigrams(source) {
let left = left >> SHIFT;
let right = right >> SHIFT;
let index = ((left as usize) << BITS) | (right as usize);
buckets[index] = buckets[index].saturating_add(1);
}
// NOTE: This is not efficient if `SigBucket` is 1 byte (u8).
let mut offset = TOTAL_BYTES - FINGERPRINT_BYTES;
for bucket in buckets {
let start = offset;
let end = start + ::core::mem::size_of::<SigBucket>();
bytes[start..end].copy_from_slice(&bucket.to_le_bytes());
offset = end;
}
}
Self(bytes)
}
}
/// Copies the passed bytes twice and zips them together with a one-byte offset.
/// This produces an iterator of the bigrams (pairs of consecutive bytes) in the input.
/// For example, the bigrams of 1, 2, 3, 4, 5 would be (1, 2), (2, 3), (3, 4), (4, 5).
#[inline]
fn bigrams(s: &[u8]) -> impl Iterator<Item = (u8, u8)> + '_ {
s.iter().copied().zip(s.iter().skip(1).copied())
}

View File

@ -1,107 +0,0 @@
/// A line-based span of text.
///
/// All line spans are at least one line long.
/// Line numbers are 0-based.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct LineSpan {
start: usize,
end: usize, // Exclusive
}
impl LineSpan {
/// Creates a new line span from the given start and end lines.
/// Note that line numbers are zero-based, and the ending
/// line number is exclusive.
///
/// # Panics
///
/// Panics if the start line is greater than or equal to the end line.
#[must_use]
pub fn new(start: usize, end: usize) -> Self {
assert!(
start <= end,
"start line must be less than or equal to the end line"
);
Self { start, end }
}
/// The starting line of the span. Zero-based.
#[inline]
#[must_use]
pub fn start(&self) -> usize {
self.start
}
/// The ending line of the span. Zero-based, exclusive.
#[inline]
#[must_use]
pub fn end(&self) -> usize {
self.end
}
/// Gets the line count from the span
#[inline]
#[must_use]
pub fn line_count(&self) -> usize {
self.end - self.start
}
/// Checks if the span is empty.
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.start == self.end
}
/// Returns true if the given span intersects with this span.
#[inline]
#[must_use]
pub fn intersects(&self, other: &Self) -> bool {
other.start < self.end && other.end > self.start
}
/// Extracts the lines from the span from the given text.
/// The final line ending (if any) is included.
///
/// Also returns the character offsets (exclusive).
///
/// If the span is empty (i.e. start == end), or if the start
/// line starts after the last line, this will return `None`.
///
/// If the end line is after the last line, it will be clamped
/// to the last line of the input text.
///
/// # Panics
/// Panics if the span's start > end.
#[must_use]
pub fn extract<'a>(&self, text: &'a str) -> Option<(&'a str, usize, usize)> {
debug_assert!(self.end >= self.start);
if text.is_empty() || self.start == self.end {
return None;
}
let mut start_offset = if self.start == 0 { Some(0) } else { None };
let mut current_line = 0;
let mut end_offset = None;
for (i, _) in text.char_indices().filter(|(_, c)| *c == '\n') {
current_line += 1;
if current_line == self.start {
start_offset = Some(i + 1);
} else if current_line == self.end {
debug_assert!(start_offset.is_some());
end_offset = Some(i + 1);
break;
}
}
start_offset.map(|start_offset| {
let end_offset = end_offset
.map(|i| if i > text.len() { i - 1 } else { i })
.unwrap_or_else(|| text.len());
(&text[start_offset..end_offset], start_offset, end_offset)
})
}
}

View File

@ -1,3 +0,0 @@
mod diff;
mod signature;
mod span;

View File

@ -1,133 +0,0 @@
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"
);
}
}

View File

@ -1,8 +0,0 @@
for i in 0..hashes.len() {
for j in i+1..hashes.len() {
- let (file, hash) = &hashes[i];
+ let (file1, hash1) = &hashes[i];
let (file2, hash2) = &hashes[j];
println!("Hamming distance between {} and {}: {}", file1, file2, hash1.distance(hash2));
}
}

View File

@ -1,8 +0,0 @@
for i in 0..hashes.len() {
for j in i+1..hashes.len() {
- let (file, hash) = &hashes[i];
+ let (first, first_hash) = &hashes[i];
let (file2, hash2) = &hashes[j];
println!("Hamming distance between {} and {}: {}", file1, file2, hash1.distance(hash2));
}
}

View File

@ -1,8 +0,0 @@
for file in &files {
println!("File: {} ({} bytes)", file.0, file.1.len());
}
let hasher: hamming_lsh::HammingHasher<B, H> = hamming_lsh::HammingHasher::new();
println!("Created hasher");

View File

@ -1,12 +0,0 @@
fn chunk_bytes<'a, const N: usize>(bytes: &'a [u8]) -> impl Iterator<Item = BitArray<N>> + 'a {
bytes
.chunks(N)
.map(|chunk| {
// Create a new byte array of size N and copy the chunk into it,
// padding with zeros if the chunk is shorter than N.
let mut padded_chunk = [0; N];
padded_chunk[..chunk.len()].copy_from_slice(chunk);
BitArray::new(padded_chunk)
})
}

View File

@ -1,37 +0,0 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut orci libero, tincidunt sed elit vel, gravida vehicula velit. Cras lacinia aliquam purus, eu vestibulum massa faucibus a. Curabitur ultrices molestie sapien, quis laoreet lacus facilisis ut. Donec sollicitudin vestibulum leo, at imperdiet leo porta venenatis. Proin suscipit, ipsum quis eleifend eleifend, ante eros egestas nisi, et pharetra nulla lacus eget nulla. Pellentesque tempus erat nec velit semper, id eleifend dui vulputate. Nullam enim sem, sollicitudin eget aliquet et, scelerisque ullamcorper magna. Donec tempus velit ut neque ullamcorper, ut facilisis neque laoreet. Nulla facilisi. Nam lorem purus, porttitor id efficitur in, consectetur at dui. Praesent orci eros, auctor id tellus vel, maximus pharetra nulla. Sed sit amet ullamcorper risus.
Ut non tellus luctus, faucibus risus a, faucibus leo. Ut sem tortor, placerat eget gravida ornare, convallis sit amet velit. Proin eu enim id nisl egestas rhoncus. Suspendisse porta vulputate tortor, in ultricies tellus sollicitudin in. Nunc sit amet mi in ante tincidunt hendrerit sit amet vitae augue. Phasellus dignissim mauris nec justo sodales consectetur sed eu lorem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam lectus sapien, hendrerit sed rhoncus in, congue et eros. Integer ac mi eu dolor hendrerit dapibus in sit amet metus. Cras bibendum vestibulum dui at ultricies. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In aliquam velit a lobortis rhoncus. Nullam convallis gravida dui sed consequat. In a feugiat libero, vitae placerat justo. Suspendisse a euismod leo.
Morbi et porttitor lectus. Proin sed vestibulum lorem. Quisque id sem efficitur, maximus arcu et, gravida purus. Nullam ut ante eu arcu ultrices sollicitudin quis nec nisl. Nulla venenatis urna laoreet arcu tincidunt, sed maximus tellus vehicula. Quisque condimentum lectus vel ante hendrerit fringilla a sed turpis. Nam ut enim enim.
Duis dapibus blandit porttitor. Aliquam ac arcu interdum nibh eleifend dignissim sed ac elit. Phasellus sit amet bibendum purus, quis fermentum turpis. Nam eu ante metus. In consequat nisl ligula, eu pharetra ligula tempor eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate metus lectus, eu sodales nulla maximus ac. Praesent luctus dictum mi. Etiam malesuada rhoncus vehicula. Phasellus aliquam feugiat sagittis. Donec sed auctor erat. Proin volutpat metus felis, id iaculis libero euismod ut. Suspendisse potenti. Aenean scelerisque nunc non rhoncus imperdiet. Quisque sit amet massa eu purus tristique interdum nec quis felis.
Aliquam erat volutpat. Maecenas accumsan massa id nisl facilisis vulputate. Nulla non sollicitudin neque. Aliquam sit amet ipsum nulla. Integer nec condimentum quam. Quisque sagittis dapibus turpis ac consequat. Nam magna tortor, aliquam vel rhoncus laoreet, dictum sed turpis. Suspendisse potenti. Aenean sit amet lectus felis. Sed laoreet tincidunt velit et bibendum. Fusce nec risus lacinia, fringilla nunc in, porta libero. Phasellus condimentum fermentum hendrerit. Fusce id lacus neque. Phasellus turpis quam, porta nec blandit et, posuere vel nibh.
Integer tempor leo sed mi elementum vehicula. Donec eu nisl semper nisi auctor tempor. Morbi purus lorem, rhoncus ac sem ultricies, sagittis accumsan elit. Fusce ligula sapien, gravida ut diam iaculis, rhoncus blandit tellus. Vivamus molestie congue ex ac vestibulum. Vestibulum varius dolor leo, nec auctor nunc mollis ut. Quisque dignissim urna nunc, at accumsan nunc efficitur id. Nullam convallis enim nisi, sed pharetra velit rutrum ac. Etiam aliquam diam eu ante congue rhoncus. Duis fermentum nibh vitae ante scelerisque, ut pretium libero pulvinar. Nulla erat felis, tincidunt eget imperdiet quis, malesuada ac lorem. Sed pellentesque imperdiet sem non condimentum. Integer iaculis erat et ipsum ultrices porttitor.
Aliquam euismod iaculis hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin consequat neque quis malesuada pretium. Vivamus gravida ex nec nunc lobortis, quis convallis nisi placerat. Nullam non tincidunt erat. Sed sed hendrerit magna. Curabitur porta nulla tortor, at pharetra est vulputate eu. Donec facilisis sem vitae vestibulum vulputate. Maecenas ornare venenatis magna, sit amet sodales felis posuere ac. Vestibulum ac massa dictum, finibus est a, congue velit. Ut ligula enim, ullamcorper id dolor non, congue dignissim dolor. Nulla ut lacus eu velit congue lacinia sit amet a velit.
Integer quis mauris vitae massa porttitor faucibus. Phasellus et congue turpis. In sit amet leo convallis enim tempus ullamcorper a venenatis velit. Ut sodales facilisis nisi nec dictum. Donec in nisi gravida, aliquet lacus vel, dictum risus. Curabitur sapien lectus, vestibulum nec commodo vitae, volutpat a neque. In maximus mi ac purus luctus mattis. Pellentesque rutrum sem vitae euismod venenatis. Nunc in augue risus. Phasellus finibus ipsum non nibh feugiat molestie. Curabitur nisl metus, tincidunt eget blandit sit amet, viverra at ante. Mauris sed sapien non purus feugiat efficitur. Pellentesque volutpat elementum diam, ut aliquet purus convallis sed.
Aenean libero velit, viverra in quam a, ullamcorper finibus quam. Morbi vitae massa eget mi faucibus blandit vitae at felis. Sed tempor ante vel augue blandit vulputate. Aliquam condimentum augue purus, non iaculis libero consectetur at. Pellentesque in ultricies enim. In hac habitasse platea dictumst. Suspendisse potenti. Suspendisse rhoncus elementum nibh, a luctus leo facilisis sit amet. Sed ac tellus pellentesque, porta urna eu, tempus enim. Proin tempor nisl dictum justo egestas faucibus.
Aliquam vulputate purus sed lacus faucibus, in blandit augue semper. In vehicula laoreet rutrum. Duis commodo mattis arcu ut vulputate. Maecenas dignissim sed enim auctor rhoncus. Sed porta, odio vitae rutrum rhoncus, leo lectus maximus lorem, a tincidunt erat mi quis urna. Maecenas vitae lacus tempus, luctus nibh in, fermentum risus. Maecenas et risus nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed aliquet ex mi, non malesuada libero sagittis nec. Morbi molestie nibh nec facilisis pharetra. Integer et accumsan ligula, vel ullamcorper metus. Donec id neque erat. Mauris ut ligula nec diam pharetra tristique. Fusce eget placerat dui. Nam suscipit, nulla non blandit eleifend, lorem lectus consequat eros, sit amet condimentum diam elit vel odio. Nam rhoncus aliquet dolor in fermentum. Sed faucibus magna consectetur condimentum malesuada. Morbi vel sem non justo interdum tincidunt. Quisque tellus dolor, pulvinar ac tellus eget, volutpat dictum justo.
Vivamus tempor vel tellus ac dapibus. Nullam sagittis orci vel luctus tempus. Cras lorem tellus, scelerisque non mollis sit amet, fringilla vitae risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius pretium posuere. Aenean commodo porta ullamcorper. Cras rutrum ac nisl vel mattis. Integer vitae sem volutpat, porttitor lacus ultrices, laoreet tellus. Etiam vulputate odio quis finibus pulvinar. Mauris id nunc porta, consequat ante ac, varius dui. Proin fermentum a eros at fermentum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean sem turpis, volutpat sit amet vestibulum non, cursus vel justo. Nam congue sapien nisl, sed posuere diam vestibulum ac. In mauris erat, convallis ac bibendum sed, consectetur a sapien.
Aenean varius, tortor ac luctus posuere, quam diam elementum lorem, eu vulputate velit metus vitae est. In imperdiet augue et venenatis dictum. Phasellus nec dapibus ex. Praesent porta ultricies sem quis molestie. Ut interdum enim et mi tristique, sed vehicula augue volutpat. Cras magna felis, maximus quis rutrum ac, varius at turpis. Donec tempor, velit at consectetur porttitor, elit est suscipit mi, in porttitor urna metus quis lorem. Mauris auctor vitae elit eu volutpat. Vestibulum at vulputate velit. Curabitur lorem odio, convallis et tellus at, dapibus consectetur ante. Sed vitae felis in odio dapibus tempor. Duis hendrerit dignissim lacus eget blandit. Sed eu volutpat eros, in feugiat mauris.
Proin elit ante, imperdiet vel elit et, iaculis facilisis purus. Ut eros neque, pharetra nec efficitur a, eleifend ut massa. In facilisis quam at neque aliquam, et pharetra purus finibus. Morbi et maximus tortor. Suspendisse nec enim sed ipsum semper lobortis. Nulla facilisi. Donec tempus dolor et feugiat volutpat. Nunc eget laoreet nisi. Aliquam dapibus ultricies ipsum, ut congue sem pellentesque vitae. In venenatis mauris risus, a elementum justo lobortis posuere. Sed facilisis vulputate lacus, sed laoreet mi. Praesent pretium enim ut efficitur tempor.
Etiam tempus finibus libero, at fermentum ipsum iaculis a. Sed consequat lacus dui, ac pulvinar enim placerat id. Nullam eu commodo felis. Cras vel nisi non lorem dapibus varius. Aliquam erat volutpat. Pellentesque a est nec turpis volutpat viverra. Phasellus tempus, diam ullamcorper varius viverra, nunc nibh fermentum justo, in pulvinar dolor nulla id mi. Nullam eget elementum sapien. Nam bibendum ac nisl eget ultrices. Etiam a accumsan nulla. Proin in tristique nulla, et feugiat tortor. Ut a justo ac tortor aliquam facilisis non eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam non viverra purus. Cras diam enim, venenatis vel risus nec, commodo imperdiet ligula.
Maecenas mattis quam non sapien venenatis, at congue massa sodales. Nullam sit amet arcu non quam tincidunt convallis id at ante. Aliquam commodo massa quis orci maximus, ut feugiat est congue. Nam vitae commodo nisi, quis feugiat massa. Proin vitae nibh nisl. Maecenas id enim nisi. Vivamus vel iaculis leo, non pretium dui. Mauris porttitor imperdiet lacus.
Quisque gravida sapien at ante gravida tempor. Proin consequat eros eu nibh suscipit, eu iaculis ipsum rhoncus. Sed at ipsum sed elit placerat egestas. Praesent dapibus tortor sed diam malesuada vehicula. Sed maximus orci semper, semper leo eget, suscipit elit. Phasellus odio lacus, maximus ac aliquam at, finibus ac velit. Quisque orci lacus, luctus ut nibh in, suscipit dignissim ipsum.
Vivamus et ex diam. Aliquam laoreet aliquet nibh quis faucibus. Curabitur congue molestie faucibus. Fusce a cursus risus. Mauris tincidunt dolor turpis, quis aliquet tellus commodo nec. Proin eu pharetra purus. Proin sed metus vitae purus tincidunt auctor nec vitae purus. Vivamus venenatis ut mi id viverra. Donec eleifend neque a diam posuere imperdiet. Praesent pellentesque risus at convallis feugiat. Aenean feugiat erat vel tellus finibus commodo. Donec blandit nec ante in ornare.
Aliquam dui enim, accumsan id magna vitae, dignissim luctus libero. Pellentesque porttitor ipsum ac est porttitor vehicula sed ac justo. Fusce gravida ultricies commodo. Donec a vulputate ex. Proin tempus purus et pretium maximus. Proin nec aliquam odio. Maecenas venenatis, sem eu mollis consectetur, justo dui viverra risus, fermentum pulvinar leo orci non est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac rutrum orci.

View File

@ -1,37 +0,0 @@
Ut non tellus luctus, faucibus risus a, faucibus leo. Ut sem tortor, placerat eget gravida ornare, convallis sit amet velit. Proin eu enim id nisl egestas rhoncus. Suspendisse porta vulputate tortor, in ultricies tellus sollicitudin in. Nunc sit amet mi in ante tincidunt hendrerit sit amet vitae augue. Phasellus dignissim mauris nec justo sodales consectetur sed eu lorem. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam lectus sapien, hendrerit sed rhoncus in, congue et eros. Integer ac mi eu dolor hendrerit dapibus in sit amet metus. Cras bibendum vestibulum dui at ultricies. Lorem ipsum dolor sit amet, consectetur adipiscing elit. In aliquam velit a lobortis rhoncus. Nullam convallis gravida dui sed consequat. In a feugiat libero, vitae placerat justo. Suspendisse a euismod leo.
Morbi et porttitor lectus. Proin sed vestibulum lorem. Quisque id sem efficitur, maximus arcu et, gravida purus. Nullam ut ante eu arcu ultrices sollicitudin quis nec nisl. Nulla venenatis urna laoreet arcu tincidunt, sed maximus tellus vehicula. Quisque condimentum lectus vel ante hendrerit fringilla a sed turpis. Nam ut enim enim.
Duis dapibus blandit porttitor. Aliquam ac arcu interdum nibh eleifend dignissim sed ac elit. Phasellus sit amet bibendum purus, quis fermentum turpis. Nam eu ante metus. In consequat nisl ligula, eu pharetra ligula tempor eu. Interdum et malesuada fames ac ante ipsum primis in faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vulputate metus lectus, eu sodales nulla maximus ac. Praesent luctus dictum mi. Etiam malesuada rhoncus vehicula. Phasellus aliquam feugiat sagittis. Donec sed auctor erat. Proin volutpat metus felis, id iaculis libero euismod ut. Suspendisse potenti. Aenean scelerisque nunc non rhoncus imperdiet. Quisque sit amet massa eu purus tristique interdum nec quis felis.
Aliquam erat volutpat. Maecenas accumsan massa id nisl facilisis vulputate. Nulla non sollicitudin neque. Aliquam sit amet ipsum nulla. Integer nec condimentum quam. Quisque sagittis dapibus turpis ac consequat. Nam magna tortor, aliquam vel rhoncus laoreet, dictum sed turpis. Suspendisse potenti. Aenean sit amet lectus felis. Sed laoreet tincidunt velit et bibendum. Fusce nec risus lacinia, fringilla nunc in, porta libero. Phasellus condimentum fermentum hendrerit. Fusce id lacus neque. Phasellus turpis quam, porta nec blandit et, posuere vel nibh.
Integer tempor leo sed mi elementum vehicula. Donec eu nisl semper nisi auctor tempor. Morbi purus lorem, rhoncus ac sem ultricies, sagittis accumsan elit. Fusce ligula sapien, gravida ut diam iaculis, rhoncus blandit tellus. Vivamus molestie congue ex ac vestibulum. Vestibulum varius dolor leo, nec auctor nunc mollis ut. Quisque dignissim urna nunc, at accumsan nunc efficitur id. Nullam convallis enim nisi, sed pharetra velit rutrum ac. Etiam aliquam diam eu ante congue rhoncus. Duis fermentum nibh vitae ante scelerisque, ut pretium libero pulvinar. Nulla erat felis, tincidunt eget imperdiet quis, malesuada ac lorem. Sed pellentesque imperdiet sem non condimentum. Integer iaculis erat et ipsum ultrices porttitor.
Aliquam euismod iaculis hendrerit. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin consequat neque quis malesuada pretium. Vivamus gravida ex nec nunc lobortis, quis convallis nisi placerat. Nullam non tincidunt erat. Sed sed hendrerit magna. Curabitur porta nulla tortor, at pharetra est vulputate eu. Donec facilisis sem vitae vestibulum vulputate. Maecenas ornare venenatis magna, sit amet sodales felis posuere ac. Vestibulum ac massa dictum, finibus est a, congue velit. Ut ligula enim, ullamcorper id dolor non, congue dignissim dolor. Nulla ut lacus eu velit congue lacinia sit amet a velit.
Integer quis mauris vitae massa porttitor faucibus. Phasellus et congue turpis. In sit amet leo convallis enim tempus ullamcorper a venenatis velit. Ut sodales facilisis nisi nec dictum. Donec in nisi gravida, aliquet lacus vel, dictum risus. Curabitur sapien lectus, vestibulum nec commodo vitae, volutpat a neque. In maximus mi ac purus luctus mattis. Pellentesque rutrum sem vitae euismod venenatis. Nunc in augue risus. Phasellus finibus ipsum non nibh feugiat molestie. Curabitur nisl metus, tincidunt eget blandit sit amet, viverra at ante. Mauris sed sapien non purus feugiat efficitur. Pellentesque volutpat elementum diam, ut aliquet purus convallis sed.
Aenean libero velit, viverra in quam a, ullamcorper finibus quam. Morbi vitae massa eget mi faucibus blandit vitae at felis. Sed tempor ante vel augue blandit vulputate. Aliquam condimentum augue purus, non iaculis libero consectetur at. Pellentesque in ultricies enim. In hac habitasse platea dictumst. Suspendisse potenti. Suspendisse rhoncus elementum nibh, a luctus leo facilisis sit amet. Sed ac tellus pellentesque, porta urna eu, tempus enim. Proin tempor nisl dictum justo egestas faucibus.
Aliquam vulputate purus sed lacus faucibus, in blandit augue semper. In vehicula laoreet rutrum. Duis commodo mattis arcu ut vulputate. Maecenas dignissim sed enim auctor rhoncus. Sed porta, odio vitae rutrum rhoncus, leo lectus maximus lorem, a tincidunt erat mi quis urna. Maecenas vitae lacus tempus, luctus nibh in, fermentum risus. Maecenas et risus nibh. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
Sed aliquet ex mi, non malesuada libero sagittis nec. Morbi molestie nibh nec facilisis pharetra. Integer et accumsan ligula, vel ullamcorper metus. Donec id neque erat. Mauris ut ligula nec diam pharetra tristique. Fusce eget placerat dui. Nam suscipit, nulla non blandit eleifend, lorem lectus consequat eros, sit amet condimentum diam elit vel odio. Nam rhoncus aliquet dolor in fermentum. Sed faucibus magna consectetur condimentum malesuada. Morbi vel sem non justo interdum tincidunt. Quisque tellus dolor, pulvinar ac tellus eget, volutpat dictum justo.
Vivamus tempor vel tellus ac dapibus. Nullam sagittis orci vel luctus tempus. Cras lorem tellus, scelerisque non mollis sit amet, fringilla vitae risus. Interdum et malesuada fames ac ante ipsum primis in faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec varius pretium posuere. Aenean commodo porta ullamcorper. Cras rutrum ac nisl vel mattis. Integer vitae sem volutpat, porttitor lacus ultrices, laoreet tellus. Etiam vulputate odio quis finibus pulvinar. Mauris id nunc porta, consequat ante ac, varius dui. Proin fermentum a eros at fermentum. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Aenean sem turpis, volutpat sit amet vestibulum non, cursus vel justo. Nam congue sapien nisl, sed posuere diam vestibulum ac. In mauris erat, convallis ac bibendum sed, consectetur a sapien.
Aenean varius, tortor ac luctus posuere, quam diam elementum lorem, eu vulputate velit metus vitae est. In imperdiet augue et venenatis dictum. Phasellus nec dapibus ex. Praesent porta ultricies sem quis molestie. Ut interdum enim et mi tristique, sed vehicula augue volutpat. Cras magna felis, maximus quis rutrum ac, varius at turpis. Donec tempor, velit at consectetur porttitor, elit est suscipit mi, in porttitor urna metus quis lorem. Mauris auctor vitae elit eu volutpat. Vestibulum at vulputate velit. Curabitur lorem odio, convallis et tellus at, dapibus consectetur ante. Sed vitae felis in odio dapibus tempor. Duis hendrerit dignissim lacus eget blandit. Sed eu volutpat eros, in feugiat mauris.
Proin elit ante, imperdiet vel elit et, iaculis facilisis purus. Ut eros neque, pharetra nec efficitur a, eleifend ut massa. In facilisis quam at neque aliquam, et pharetra purus finibus. Morbi et maximus tortor. Suspendisse nec enim sed ipsum semper lobortis. Nulla facilisi. Donec tempus dolor et feugiat volutpat. Nunc eget laoreet nisi. Aliquam dapibus ultricies ipsum, ut congue sem pellentesque vitae. In venenatis mauris risus, a elementum justo lobortis posuere. Sed facilisis vulputate lacus, sed laoreet mi. Praesent pretium enim ut efficitur tempor.
Etiam tempus finibus libero, at fermentum ipsum iaculis a. Sed consequat lacus dui, ac pulvinar enim placerat id. Nullam eu commodo felis. Cras vel nisi non lorem dapibus varius. Aliquam erat volutpat. Pellentesque a est nec turpis volutpat viverra. Phasellus tempus, diam ullamcorper varius viverra, nunc nibh fermentum justo, in pulvinar dolor nulla id mi. Nullam eget elementum sapien. Nam bibendum ac nisl eget ultrices. Etiam a accumsan nulla. Proin in tristique nulla, et feugiat tortor. Ut a justo ac tortor aliquam facilisis non eu quam. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Nullam non viverra purus. Cras diam enim, venenatis vel risus nec, commodo imperdiet ligula.
Maecenas mattis quam non sapien venenatis, at congue massa sodales. Nullam sit amet arcu non quam tincidunt convallis id at ante. Aliquam commodo massa quis orci maximus, ut feugiat est congue. Nam vitae commodo nisi, quis feugiat massa. Proin vitae nibh nisl. Maecenas id enim nisi. Vivamus vel iaculis leo, non pretium dui. Mauris porttitor imperdiet lacus.
Quisque gravida sapien at ante gravida tempor. Proin consequat eros eu nibh suscipit, eu iaculis ipsum rhoncus. Sed at ipsum sed elit placerat egestas. Praesent dapibus tortor sed diam malesuada vehicula. Sed maximus orci semper, semper leo eget, suscipit elit. Phasellus odio lacus, maximus ac aliquam at, finibus ac velit. Quisque orci lacus, luctus ut nibh in, suscipit dignissim ipsum.
Vivamus et ex diam. Aliquam laoreet aliquet nibh quis faucibus. Curabitur congue molestie faucibus. Fusce a cursus risus. Mauris tincidunt dolor turpis, quis aliquet tellus commodo nec. Proin eu pharetra purus. Proin sed metus vitae purus tincidunt auctor nec vitae purus. Vivamus venenatis ut mi id viverra. Donec eleifend neque a diam posuere imperdiet. Praesent pellentesque risus at convallis feugiat. Aenean feugiat erat vel tellus finibus commodo. Donec blandit nec ante in ornare.
Aliquam dui enim, accumsan id magna vitae, dignissim luctus libero. Pellentesque porttitor ipsum ac est porttitor vehicula sed ac justo. Fusce gravida ultricies commodo. Donec a vulputate ex. Proin tempus purus et pretium maximus. Proin nec aliquam odio. Maecenas venenatis, sem eu mollis consectetur, justo dui viverra risus, fermentum pulvinar leo orci non est. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis ac rutrum orci.
Sed eu vehicula orci. Suspendisse convallis lectus justo, ac euismod justo sollicitudin non. Duis sodales eleifend neque ac sagittis. Donec non enim vulputate, auctor turpis eget, mattis sem. Aliquam pulvinar metus non posuere vestibulum. Mauris tristique, sem nec accumsan vulputate, ipsum urna imperdiet ante, quis hendrerit lorem sem sit amet enim. Nunc vel purus ut arcu lobortis imperdiet sed eu ipsum. Morbi aliquet purus vitae purus convallis vestibulum. Nullam et malesuada velit. Aenean tellus nisi, rutrum vitae augue ut, fermentum vestibulum quam. Morbi cursus neque a mi dictum pulvinar. Mauris a vulputate risus, sit amet lobortis ex. Cras sagittis ex sed nunc mattis, sit amet malesuada arcu dignissim.

View File

@ -1,2 +0,0 @@
This is a test
of your soundsystem

View File

@ -1,2 +0,0 @@
This is a test
of your pound system

View File

@ -1,2 +0,0 @@
This is entirely different text. There should be no similarities
between them.

View File

@ -1,124 +0,0 @@
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);

View File

@ -1,94 +0,0 @@
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
}