#[cfg(test)]
mod tests {
use crate::generation::cache::{Beam, KVCache};
use crate::tensor::Tensor;
use std::sync::Arc;
#[test]
fn test_kvcache_new_is_empty() {
let cache = KVCache::new();
assert!(cache.keys.is_empty());
assert!(cache.values.is_empty());
assert_eq!(cache.seq_len, 0);
}
#[test]
fn test_kvcache_default_matches_new() {
let a = KVCache::new();
let b = KVCache::default();
assert_eq!(a.seq_len, b.seq_len);
assert_eq!(a.keys.len(), b.keys.len());
}
#[test]
fn test_kvcache_clear_resets_all_fields() {
let mut cache = KVCache::new();
cache.seq_len = 5;
cache.clear();
assert_eq!(cache.seq_len, 0);
assert!(cache.keys.is_empty());
assert!(cache.values.is_empty());
}
#[test]
fn test_kvcache_get_layer_out_of_bounds_returns_none() {
let cache = KVCache::new();
let result = cache.get_layer(0);
assert!(result.is_none());
}
#[test]
fn test_kvcache_get_layer_large_index_returns_none() {
let cache = KVCache::new();
assert!(cache.get_layer(999).is_none());
}
#[test]
fn test_kvcache_seq_len_starts_at_zero() {
let cache = KVCache::new();
assert_eq!(cache.seq_len, 0);
}
#[test]
fn test_kvcache_clear_idempotent() {
let mut cache = KVCache::new();
cache.clear();
cache.clear();
assert_eq!(cache.seq_len, 0);
assert!(cache.keys.is_empty());
}
#[test]
fn test_beam_new_creates_unfinished_beam() {
let beam = Beam::new(vec![1, 2, 3], -0.5);
assert_eq!(beam.tokens, vec![1, 2, 3]);
assert!((beam.score - (-0.5)).abs() < 1e-6);
assert!(!beam.finished);
assert!(beam.cache.is_none());
}
#[test]
fn test_beam_new_empty_tokens() {
let beam = Beam::new(vec![], 0.0);
assert!(beam.tokens.is_empty());
assert_eq!(beam.score, 0.0);
assert!(!beam.finished);
}
#[test]
fn test_beam_extend_appends_token_and_accumulates_score() {
let beam = Beam::new(vec![10, 20], -1.0);
let extended = beam.extend(30, -0.5);
assert_eq!(extended.tokens, vec![10, 20, 30]);
assert!((extended.score - (-1.5)).abs() < 1e-6);
assert!(!extended.finished);
}
#[test]
fn test_beam_extend_does_not_mutate_original() {
let beam = Beam::new(vec![1], 0.0);
let _extended = beam.extend(2, -1.0);
assert_eq!(beam.tokens, vec![1]);
assert_eq!(beam.score, 0.0);
}
#[test]
fn test_beam_finalize_sets_finished_flag() {
let mut beam = Beam::new(vec![5], -0.3);
assert!(!beam.finished);
beam.finalize();
assert!(beam.finished);
}
#[test]
fn test_beam_finalize_idempotent() {
let mut beam = Beam::new(vec![1], 0.0);
beam.finalize();
beam.finalize();
assert!(beam.finished);
}
#[test]
fn test_beam_normalized_score_single_token() {
let beam = Beam::new(vec![42], -2.0);
let normalized = beam.get_normalized_score();
assert!((normalized - (-2.0)).abs() < 1e-6);
}
#[test]
fn test_beam_normalized_score_multiple_tokens() {
let beam = Beam::new(vec![1, 2, 3, 4], -8.0);
let normalized = beam.get_normalized_score();
assert!((normalized - (-2.0)).abs() < 1e-6);
}
#[test]
fn test_beam_normalized_score_empty_tokens_returns_zero() {
let beam = Beam::new(vec![], 0.0);
let normalized = beam.get_normalized_score();
assert_eq!(normalized, 0.0);
}
#[test]
fn test_beam_extend_chain() {
let mut beam = Beam::new(vec![], 0.0);
let mut s = 42u64;
for i in 0..5usize {
s = s.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
let score = -((s % 1000) as f32) / 1000.0;
beam = beam.extend(i, score);
}
assert_eq!(beam.tokens.len(), 5);
}
#[test]
fn test_beam_clone_is_independent() {
let beam = Beam::new(vec![1, 2], -0.5);
let mut cloned = beam.clone();
cloned.finalize();
assert!(!beam.finished);
assert!(cloned.finished);
}
#[test]
fn test_beam_extend_preserves_cache_none() {
let beam = Beam::new(vec![1], 0.0);
let extended = beam.extend(2, -1.0);
assert!(extended.cache.is_none());
}
#[test]
fn test_beam_normalized_score_positive() {
let beam = Beam::new(vec![1, 2], 4.0);
let normalized = beam.get_normalized_score();
assert!((normalized - 2.0).abs() < 1e-6);
}
#[test]
fn test_beam_score_accumulation_zero() {
let beam = Beam::new(vec![1], 0.0);
let extended = beam.extend(2, 0.0);
assert_eq!(extended.score, 0.0);
}
#[test]
fn test_kvcache_multiple_clears() {
let mut cache = KVCache::new();
for _ in 0..10 {
cache.clear();
}
assert_eq!(cache.seq_len, 0);
}
#[test]
fn test_kvcache_push_layer_does_not_move_the_sequence_forward() {
let mut cache = KVCache::new();
for _ in 0..4 {
let key = Tensor::from_vec(vec![0.0_f32; 2], &[2]).expect("tensor");
let value = Tensor::from_vec(vec![0.0_f32; 2], &[2]).expect("tensor");
cache.push_layer(key, value).expect("push layer");
}
assert_eq!(cache.num_layers(), 4);
assert_eq!(cache.seq_len, 0, "layers are not token positions");
cache.advance(1);
assert_eq!(cache.seq_len, 1);
}
#[test]
fn test_kvcache_set_layer_out_of_range_is_error() {
let mut cache = KVCache::new();
let key = Tensor::from_vec(vec![0.0_f32], &[1]).expect("tensor");
let value = Tensor::from_vec(vec![0.0_f32], &[1]).expect("tensor");
assert!(cache.set_layer(0, key, value).is_err());
}
#[test]
fn test_beam_extend_shares_the_cache_instead_of_deep_copying() {
let mut cache = KVCache::new();
let key = Tensor::from_vec(vec![1.0_f32; 64], &[64]).expect("tensor");
let value = Tensor::from_vec(vec![2.0_f32; 64], &[64]).expect("tensor");
cache.push_layer(key, value).expect("push layer");
cache.advance(1);
let beam = Beam::new(vec![1], 0.0).with_cache(cache);
let child = beam.extend(2, -0.5);
let parent_cache = beam.cache.as_ref().expect("parent cache");
let child_cache = child.cache.as_ref().expect("child cache");
assert!(
Arc::ptr_eq(parent_cache, child_cache),
"extend must share the cache allocation"
);
assert_eq!(child.tokens, vec![1, 2]);
assert!((child.score - (-0.5)).abs() < 1e-6);
}
#[test]
fn test_beam_cache_mut_is_copy_on_write() {
let mut cache = KVCache::new();
cache.advance(3);
let beam = Beam::new(vec![1], 0.0).with_cache(cache);
let mut child = beam.extend(2, 0.0);
child.cache_mut().expect("cache").advance(1);
assert_eq!(beam.cache.as_ref().expect("parent").seq_len, 3);
assert_eq!(child.cache.as_ref().expect("child").seq_len, 4);
assert!(!Arc::ptr_eq(
beam.cache.as_ref().expect("parent"),
child.cache.as_ref().expect("child")
));
}
#[test]
fn test_beam_length_normalized_score_uses_the_penalty() {
let short = Beam::new(vec![1, 2], -2.0);
let long = Beam::new(vec![1, 2, 3, 4], -4.0);
assert!(short.length_normalized_score(0.5) > long.length_normalized_score(0.5));
assert!(long.length_normalized_score(2.0) > short.length_normalized_score(2.0));
}
}