use super::*;
#[test]
fn test_create_batcher() {
let batcher = ContinuousBatcher::new(64, 4096);
assert_eq!(batcher.max_batch_size(), 64);
assert_eq!(batcher.max_seq_len(), 4096);
assert_eq!(batcher.running_count(), 0);
assert_eq!(batcher.waiting_count(), 0);
}
#[test]
fn test_add_request() {
let mut batcher = ContinuousBatcher::new(64, 4096);
let req = InferenceRequest::new(SeqId(1), vec![1, 2, 3], 100);
batcher.add_request(req);
assert_eq!(batcher.waiting_count(), 1);
}
#[test]
fn test_fcfs_scheduling() {
let mut batcher = ContinuousBatcher::new(64, 4096).with_policy(SchedulingPolicy::FCFS);
for i in 0..5 {
let req = InferenceRequest::new(SeqId(i), vec![1, 2, 3], 100);
batcher.add_request(req);
}
let schedule = batcher.schedule();
assert_eq!(schedule.batch_size, 5);
assert_eq!(schedule.sequence_ids[0], SeqId(0));
assert_eq!(schedule.sequence_ids[4], SeqId(4));
}
#[test]
fn test_sjf_scheduling() {
let mut batcher = ContinuousBatcher::new(64, 4096).with_policy(SchedulingPolicy::SJF);
let req1 = InferenceRequest::new(SeqId(1), vec![1; 100], 500); let req2 = InferenceRequest::new(SeqId(2), vec![1; 10], 50); let req3 = InferenceRequest::new(SeqId(3), vec![1; 50], 200);
batcher.add_request(req1);
batcher.add_request(req2);
batcher.add_request(req3);
let schedule = batcher.schedule();
assert_eq!(schedule.sequence_ids[0], SeqId(2)); }
#[test]
fn test_process_outputs() {
let mut batcher = ContinuousBatcher::new(64, 4096);
let req = InferenceRequest::new(SeqId(1), vec![1, 2, 3], 5);
batcher.add_request(req);
batcher.schedule();
for token in [10, 11, 12] {
batcher.process_outputs(vec![TokenOutput {
seq_id: SeqId(1),
token,
is_eos: false,
}]);
}
let seq = batcher.get_sequence(SeqId(1)).unwrap();
assert_eq!(seq.output_tokens.len(), 3);
assert_eq!(seq.output_tokens, vec![10, 11, 12]);
}
#[test]
fn test_preemption() {
let mut batcher = ContinuousBatcher::new(64, 4096).with_policy(SchedulingPolicy::Priority {
preempt_enabled: true,
});
for i in 0..5 {
let req = InferenceRequest::new(SeqId(i), vec![1; (i as usize + 1) * 100], 100);
batcher.add_request(req);
}
batcher.schedule();
let preempted = batcher.preempt(2);
assert_eq!(preempted.len(), 2);
assert_eq!(batcher.running_count(), 3);
assert_eq!(batcher.swapped_count(), 2);
}
#[test]
fn test_ema() {
let mut ema = ExponentialMovingAverage::new(0.5);
ema.update(1.0);
assert!((ema.value() - 1.0).abs() < 0.01);
ema.update(0.0);
assert!((ema.value() - 0.5).abs() < 0.01);
ema.update(0.0);
assert!((ema.value() - 0.25).abs() < 0.01);
}
#[test]
fn test_speculative_output() {
let output = SpeculativeOutput {
accepted: vec![1, 2, 3],
rejection_idx: Some(3),
target_token: 100,
draft_count: 5,
};
assert!((output.acceptance_rate() - 0.6).abs() < 0.01);
assert_eq!(output.total_tokens(), 4);
}
#[test]
fn test_speculative_decoder() {
let mut decoder = SpeculativeDecoder::new(5);
for _ in 0..10 {
let draft = vec![1, 2, 3, 4, 5];
let target_probs = vec![(1, 0.9), (2, 0.9), (3, 0.9), (4, 0.9), (5, 0.9), (6, 0.9)];
decoder.simulate_step(&draft, &target_probs);
}
assert!(decoder.acceptance_rate() > 0.9);
assert!(decoder.speedup() > 4.0);
}
#[test]
fn test_speculative_decoder_partial_accept() {
let mut decoder = SpeculativeDecoder::new(5);
let draft = vec![1, 2, 3, 4, 5];
let target_probs = vec![(1, 0.9), (2, 0.9), (99, 0.9), (4, 0.9), (5, 0.9)];
let output = decoder.simulate_step(&draft, &target_probs);
assert_eq!(output.accepted.len(), 2); assert_eq!(output.rejection_idx, Some(2));
assert_eq!(output.target_token, 99);
}
#[test]
fn test_batch_schedule() {
let schedule = BatchSchedule::empty();
assert!(schedule.is_empty());
assert_eq!(schedule.batch_size, 0);
}
#[test]
fn test_throughput_tracking() {
let mut batcher = ContinuousBatcher::new(64, 4096);
let req = InferenceRequest::new(SeqId(1), vec![1, 2, 3], 100);
batcher.add_request(req);
batcher.schedule();
for i in 0..10 {
batcher.process_outputs(vec![TokenOutput {
seq_id: SeqId(1),
token: i,
is_eos: false,
}]);
}
assert!(batcher.stats().total_tokens == 10);
}