use super::*;
fn item(text: &str) -> Arc<dyn SkimItem> {
Arc::new(text.to_string())
}
fn matched(text: &str, index: i32, score: i32) -> MatchedItem {
let rank = Rank {
score,
index,
..Default::default()
};
MatchedItem::new(item(text), rank, None, &RankBuilder::default())
}
#[test]
fn rank_builder_inserts_score_criterion() {
let rb = RankBuilder::new(vec![RankCriteria::Begin]);
assert_eq!(rb.criteria().first(), Some(&RankCriteria::Score));
let rb = RankBuilder::new(vec![RankCriteria::NegScore, RankCriteria::Begin]);
assert_eq!(rb.criteria().first(), Some(&RankCriteria::NegScore));
assert!(!rb.criteria().contains(&RankCriteria::Score));
}
#[test]
fn build_rank_records_offsets_and_pathname() {
let rb = RankBuilder::default();
let rank = rb.build_rank(50, 4, 7, "src/lib/foo.rs");
assert_eq!(rank.score, 50);
assert_eq!(rank.begin, 4);
assert_eq!(rank.end, 7);
assert_eq!(rank.length, i32::try_from("src/lib/foo.rs".len()).unwrap());
assert_eq!(rank.path_name_offset, i32::try_from("src/lib/".len()).unwrap());
}
#[test]
fn sort_key_flips_score_sign() {
let rank = Rank {
score: 10,
begin: 2,
end: 5,
..Default::default()
};
let key = rank.sort_key(&[RankCriteria::Score, RankCriteria::Begin, RankCriteria::End]);
assert_eq!(key[0], -10);
assert_eq!(key[1], 2);
assert_eq!(key[2], 5);
}
#[test]
fn matched_item_ordering_prefers_higher_score() {
let high = matched("a", 0, 100);
let low = matched("b", 1, 10);
assert!(high < low);
}
#[test]
fn sorted_merge_handles_empty_inputs() {
let a = vec![matched("a", 0, 10)];
assert_eq!(MatchedItem::sorted_merge(a.clone(), vec![]).len(), 1);
assert_eq!(MatchedItem::sorted_merge(vec![], a.clone()).len(), 1);
}
#[test]
fn sorted_merge_interleaves_in_order() {
let existing = vec![matched("a", 0, 100), matched("c", 2, 10)];
let incoming = vec![matched("b", 1, 50)];
let merged = MatchedItem::sorted_merge(existing, incoming);
let scores: Vec<i32> = merged.iter().map(|m| m.rank.score).collect();
assert_eq!(scores, vec![100, 50, 10]);
}
#[test]
fn sorted_merge_prepends_when_incoming_all_better() {
let existing = vec![matched("c", 2, 10)];
let incoming = vec![matched("a", 0, 100), matched("b", 1, 50)];
let merged = MatchedItem::sorted_merge(existing, incoming);
let scores: Vec<i32> = merged.iter().map(|m| m.rank.score).collect();
assert_eq!(scores, vec![100, 50, 10]);
}
#[test]
fn sorted_merge_appends_when_existing_all_better() {
let existing = vec![matched("a", 0, 100), matched("b", 1, 50)];
let incoming = vec![matched("c", 2, 10)];
let merged = MatchedItem::sorted_merge(existing, incoming);
let scores: Vec<i32> = merged.iter().map(|m| m.rank.score).collect();
assert_eq!(scores, vec![100, 50, 10]);
}
#[test]
fn sorted_merge_drains_incoming_tail() {
let existing = vec![matched("a", 0, 100), matched("c", 2, 40)];
let incoming = vec![matched("b", 1, 50), matched("d", 3, 10)];
let merged = MatchedItem::sorted_merge(existing, incoming);
let scores: Vec<i32> = merged.iter().map(|m| m.rank.score).collect();
assert_eq!(scores, vec![100, 50, 40, 10]);
}
#[test]
fn matched_item_debug_includes_text_and_rank() {
let s = format!("{:?}", matched("hello", 3, 42));
assert!(s.contains("MatchedItem"));
assert!(s.contains("hello"));
}
#[test]
fn merge_into_sorted_small_insert() {
let mut existing = vec![matched("a", 0, 100), matched("c", 2, 10)];
MatchedItem::merge_into_sorted(&mut existing, vec![matched("b", 1, 50)]);
let scores: Vec<i32> = existing.iter().map(|m| m.rank.score).collect();
assert_eq!(scores, vec![100, 50, 10]);
}
#[test]
fn merge_into_sorted_large_uses_backwards_merge() {
let mut existing: Vec<MatchedItem> = (0i32..300).map(|i| matched("x", i, 1000 - i * 2)).collect();
let incoming: Vec<MatchedItem> = (0i32..300).map(|i| matched("y", 1000 + i, 1000 - i * 2 - 1)).collect();
let total = existing.len() + incoming.len();
MatchedItem::merge_into_sorted(&mut existing, incoming);
assert_eq!(existing.len(), total);
assert!(existing.windows(2).all(|w| w[0] <= w[1]));
}
#[test]
fn merge_into_sorted_large_with_incoming_holding_best() {
let mut existing: Vec<MatchedItem> = (0i32..300).map(|i| matched("x", i, 500 - i * 2)).collect();
let incoming: Vec<MatchedItem> = (0i32..300).map(|i| matched("y", 1000 + i, 2000 - i * 2)).collect();
let total = existing.len() + incoming.len();
MatchedItem::merge_into_sorted(&mut existing, incoming);
assert_eq!(existing.len(), total);
assert!(existing.windows(2).all(|w| w[0] <= w[1]));
assert_eq!(existing[0].rank.score, 2000);
}
#[test]
fn downcast_item_recovers_concrete_type() {
let m = matched("hello", 0, 1);
let s: Option<&String> = m.downcast_item::<String>();
assert_eq!(s.map(String::as_str), Some("hello"));
}
#[test]
fn item_pool_append_take_and_counters() {
let pool = ItemPool::new();
assert!(pool.is_empty());
pool.append(vec![item("a"), item("b"), item("c")]);
assert_eq!(pool.len(), 3);
assert_eq!(pool.num_not_taken(), 3);
assert_eq!(pool.num_taken(), 0);
let taken = pool.take();
assert_eq!(taken.len(), 3);
assert_eq!(pool.num_taken(), 3);
assert_eq!(pool.num_not_taken(), 0);
assert!(pool.take().is_empty());
}
#[test]
fn item_pool_reset_replays_items() {
let pool = ItemPool::new();
pool.append(vec![item("a"), item("b")]);
let _ = pool.take();
assert_eq!(pool.num_not_taken(), 0);
pool.reset();
assert_eq!(pool.num_not_taken(), 2);
assert_eq!(pool.take().len(), 2);
}
#[test]
fn item_pool_clear_empties_everything() {
let pool = ItemPool::new();
pool.append(vec![item("a"), item("b")]);
pool.clear();
assert!(pool.is_empty());
assert_eq!(pool.num_taken(), 0);
}
#[test]
fn item_pool_reserves_header_lines() {
let mut options = crate::SkimOptions::default();
options.header_lines = 2;
let pool = ItemPool::from_options(&options);
pool.append(vec![item("h1"), item("h2"), item("body1"), item("body2")]);
let reserved = pool.reserved();
assert_eq!(reserved.len(), 2);
assert_eq!(reserved[0].text(), "h1");
assert_eq!(pool.len(), 2);
}
#[test]
fn item_pool_tac_reverses_take_order() {
let mut options = crate::SkimOptions::default();
options.tac = true;
let pool = ItemPool::from_options(&options);
pool.append(vec![item("a"), item("b"), item("c")]);
let taken: Vec<String> = pool.take().iter().map(|i| i.text().into_owned()).collect();
assert_eq!(taken, vec!["c", "b", "a"]);
}