#[cfg(test)]
mod tests {
extern crate alloc;
use alloc::{vec, vec::Vec};
use crate::{
SCORE_MAX,
SCORE_MIN,
config::*,
has_match,
score,
score_positions,
};
const EPS: f64 = 1e-6;
fn approx_eq(a: f64, b: f64) -> bool {
(a - b).abs() < EPS
}
#[test]
fn exact_match_should_return_true() {
assert!(has_match(b"a", b"a"));
}
#[test]
fn partial_match_should_return_true() {
assert!(has_match(b"a", b"ab"));
assert!(has_match(b"a", b"ba"));
}
#[test]
fn match_with_delimiters_in_between() {
assert!(has_match(b"abc", b"a|b|c"));
}
#[test]
fn non_match_should_return_false() {
assert!(!has_match(b"a", b""));
assert!(!has_match(b"a", b"b"));
assert!(!has_match(b"ass", b"tags"));
}
#[test]
fn empty_query_should_always_match() {
assert!(has_match(b"", b""));
assert!(has_match(b"", b"a"));
}
#[test]
fn should_prefer_starts_of_words() {
assert!(
score(b"amor", b"app/models/order") > score(b"amor", b"app/models/zrder")
);
}
#[test]
fn should_prefer_consecutive_letters() {
assert!(score(b"amo", b"app/m/foo") < score(b"amo", b"app/models/foo"));
}
#[test]
fn should_prefer_contiguous_over_letter_following_period() {
assert!(score(b"gemfil", b"Gemfile.lock") < score(b"gemfil", b"Gemfile"));
}
#[test]
fn should_prefer_shorter_matches() {
assert!(score(b"abce", b"abcdef") > score(b"abce", b"abc de"));
assert!(score(b"abc", b" a b c ") > score(b"abc", b" a b c "));
assert!(score(b"abc", b" a b c ") > score(b"abc", b" a b c "));
}
#[test]
fn should_prefer_shorter_candidates() {
assert!(score(b"test", b"tests") > score(b"test", b"testing"));
}
#[test]
fn should_prefer_start_of_candidate() {
assert!(score(b"test", b"testing") > score(b"test", b"/testing"));
}
#[test]
fn score_exact_match() {
assert_eq!(SCORE_MAX, score(b"abc", b"abc"));
assert_eq!(SCORE_MAX, score(b"aBc", b"abC"));
}
#[test]
fn score_equal_length_mismatch_is_not_perfect() {
assert_eq!(SCORE_MIN, score(b"abc", b"xyz"));
assert_eq!(SCORE_MIN, score(b"a", b"b"));
}
#[test]
fn score_empty_query() {
assert_eq!(SCORE_MIN, score(b"", b""));
assert_eq!(SCORE_MIN, score(b"", b"a"));
assert_eq!(SCORE_MIN, score(b"", b"bb"));
}
#[test]
fn score_gaps() {
assert!(approx_eq(SCORE_GAP_LEADING, score(b"a", b"*a")));
assert!(approx_eq(SCORE_GAP_LEADING * 2.0, score(b"a", b"*ba")));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_GAP_TRAILING,
score(b"a", b"**a*")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_GAP_TRAILING * 2.0,
score(b"a", b"**a**")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0
+ SCORE_MATCH_CONSECUTIVE
+ SCORE_GAP_TRAILING * 2.0,
score(b"aa", b"**aa**")
));
assert!(approx_eq(
SCORE_GAP_LEADING
+ SCORE_GAP_LEADING
+ SCORE_GAP_INNER
+ SCORE_GAP_TRAILING
+ SCORE_GAP_TRAILING,
score(b"aa", b"**a*a**")
));
}
#[test]
fn score_consecutive() {
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_MATCH_CONSECUTIVE,
score(b"aa", b"*aa")
));
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_MATCH_CONSECUTIVE * 2.0,
score(b"aaa", b"*aaa")
));
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_GAP_INNER + SCORE_MATCH_CONSECUTIVE,
score(b"aaa", b"*a*aa")
));
}
#[test]
fn score_slash() {
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_MATCH_SLASH,
score(b"a", b"/a")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_MATCH_SLASH,
score(b"a", b"*/a")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_MATCH_SLASH + SCORE_MATCH_CONSECUTIVE,
score(b"aa", b"a/aa")
));
}
#[test]
fn score_capital() {
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_MATCH_CAPITAL,
score(b"a", b"bA")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_MATCH_CAPITAL,
score(b"a", b"baA")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 2.0 + SCORE_MATCH_CAPITAL + SCORE_MATCH_CONSECUTIVE,
score(b"aa", b"baAa")
));
}
#[test]
fn score_dot() {
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_MATCH_DOT,
score(b"a", b".a")
));
assert!(approx_eq(
SCORE_GAP_LEADING * 3.0 + SCORE_MATCH_DOT,
score(b"a", b"*a.a")
));
assert!(approx_eq(
SCORE_GAP_LEADING + SCORE_GAP_INNER + SCORE_MATCH_DOT,
score(b"a", b"*a.a")
));
}
#[test]
fn score_long_string() {
let s: Vec<u8> = vec![b'a'; 4096];
assert_eq!(SCORE_MIN, score(b"aa", &s));
assert_eq!(SCORE_MIN, score(&s, b"aa"));
assert_eq!(SCORE_MIN, score(&s, &s));
}
#[test]
fn positions_consecutive() {
let mut pos = [0usize; 3];
score_positions(b"amo", b"app/models/foo", &mut pos);
assert_eq!(0, pos[0]);
assert_eq!(4, pos[1]);
assert_eq!(5, pos[2]);
}
#[test]
fn positions_start_of_word() {
let mut pos = [0usize; 4];
score_positions(b"amor", b"app/models/order", &mut pos);
assert_eq!(0, pos[0]);
assert_eq!(4, pos[1]);
assert_eq!(11, pos[2]);
assert_eq!(12, pos[3]);
}
#[test]
fn positions_no_bonuses() {
let mut pos = [0usize; 2];
score_positions(b"as", b"tags", &mut pos);
assert_eq!(1, pos[0]);
assert_eq!(3, pos[1]);
score_positions(b"as", b"examples.txt", &mut pos);
assert_eq!(2, pos[0]);
assert_eq!(7, pos[1]);
}
#[test]
fn positions_multiple_candidates_start_of_words() {
let mut pos = [0usize; 3];
score_positions(b"abc", b"a/a/b/c/c", &mut pos);
assert_eq!(2, pos[0]);
assert_eq!(4, pos[1]);
assert_eq!(6, pos[2]);
}
#[test]
fn positions_exact_match() {
let mut pos = [0usize; 3];
score_positions(b"foo", b"foo", &mut pos);
assert_eq!(0, pos[0]);
assert_eq!(1, pos[1]);
assert_eq!(2, pos[2]);
}
#[test]
fn positions_equal_length_mismatch_is_not_perfect() {
let mut pos = [usize::MAX; 3];
assert_eq!(SCORE_MIN, score_positions(b"abc", b"xyz", &mut pos));
assert_eq!([usize::MAX; 3], pos);
}
}