#[test]
fn test_inference_result_clone_preserves_all() {
let original = InferenceResult {
text: "test text".to_string(),
tokens: vec![100, 200, 300],
input_token_count: 1,
generated_token_count: 2,
inference_ms: 999.9,
tok_per_sec: 2.0,
load_ms: 111.1,
format: "APR".to_string(),
used_gpu: false,
};
let cloned = original.clone();
assert_eq!(original.text, cloned.text);
assert_eq!(original.tokens, cloned.tokens);
assert_eq!(original.input_token_count, cloned.input_token_count);
assert_eq!(original.generated_token_count, cloned.generated_token_count);
assert!((original.inference_ms - cloned.inference_ms).abs() < f64::EPSILON);
assert!((original.tok_per_sec - cloned.tok_per_sec).abs() < f64::EPSILON);
assert!((original.load_ms - cloned.load_ms).abs() < f64::EPSILON);
assert_eq!(original.format, cloned.format);
assert_eq!(original.used_gpu, cloned.used_gpu);
}
#[test]
fn test_format_detection_gguf_version_variations() {
use crate::format::{detect_format, ModelFormat};
let v3_data = vec![0x47, 0x47, 0x55, 0x46, 0x03, 0x00, 0x00, 0x00];
let result = detect_format(&v3_data);
assert!(matches!(result, Ok(ModelFormat::Gguf)));
let v2_data = vec![0x47, 0x47, 0x55, 0x46, 0x02, 0x00, 0x00, 0x00];
let result = detect_format(&v2_data);
assert!(matches!(result, Ok(ModelFormat::Gguf)));
}
#[test]
fn test_format_detection_apr_versions() {
use crate::format::{detect_format, ModelFormat};
let v1_data = b"APR1xxxx";
let result = detect_format(v1_data);
assert!(matches!(result, Ok(ModelFormat::Apr)));
let v2_data = b"APR2xxxx";
let result = detect_format(v2_data);
assert!(matches!(result, Ok(ModelFormat::Apr)));
}
#[test]
fn test_format_detection_safetensors_various_sizes() {
use crate::format::{detect_format, ModelFormat};
for size in [64u64, 128, 256, 512, 1024, 2048, 4096] {
let data = size.to_le_bytes();
let result = detect_format(&data);
assert!(
matches!(result, Ok(ModelFormat::SafeTensors)),
"Failed for size: {}",
size
);
}
}
#[test]
fn test_run_inference_symlink_path() {
let config = InferenceConfig::new("/models/latest-model.gguf");
assert!(config.model_path.to_str().unwrap().contains("latest-model"));
}
#[test]
fn test_run_inference_hidden_file() {
let config = InferenceConfig::new("/models/.hidden-model.gguf");
assert!(config
.model_path
.to_str()
.unwrap()
.contains(".hidden-model"));
}
#[test]
fn test_inference_config_builder_order_independence() {
let config1 = InferenceConfig::new("/m.gguf")
.with_prompt("p")
.with_max_tokens(50)
.with_temperature(0.5)
.with_top_k(20)
.without_gpu()
.with_verbose(true)
.with_trace(true);
let config2 = InferenceConfig::new("/m.gguf")
.with_trace(true)
.with_verbose(true)
.without_gpu()
.with_top_k(20)
.with_temperature(0.5)
.with_max_tokens(50)
.with_prompt("p");
assert_eq!(config1.prompt, config2.prompt);
assert_eq!(config1.max_tokens, config2.max_tokens);
assert!((config1.temperature - config2.temperature).abs() < f32::EPSILON);
assert_eq!(config1.top_k, config2.top_k);
assert_eq!(config1.no_gpu, config2.no_gpu);
assert_eq!(config1.verbose, config2.verbose);
assert_eq!(config1.trace, config2.trace);
}
#[test]
fn test_path_extension_handling() {
let extensions = [".gguf", ".apr", ".safetensors", ".bin", ".model", ""];
for ext in extensions {
let path = format!("/models/model{}", ext);
let config = InferenceConfig::new(&path);
assert!(config.model_path.to_str().unwrap().ends_with(ext) || ext.is_empty());
}
}
#[test]
fn test_numeric_boundary_values() {
let config = InferenceConfig::new("/m.gguf")
.with_max_tokens(usize::MAX)
.with_temperature(f32::MAX)
.with_top_k(usize::MAX);
assert_eq!(config.max_tokens, usize::MAX);
assert_eq!(config.temperature, f32::MAX);
assert_eq!(config.top_k, usize::MAX);
let config = InferenceConfig::new("/m.gguf")
.with_max_tokens(0)
.with_temperature(f32::MIN)
.with_top_k(0);
assert_eq!(config.max_tokens, 0);
assert_eq!(config.temperature, f32::MIN);
assert_eq!(config.top_k, 0);
}
#[test]
fn test_temperature_special_values() {
let config = InferenceConfig::new("/m.gguf").with_temperature(f32::INFINITY);
assert!(config.temperature.is_infinite());
let config = InferenceConfig::new("/m.gguf").with_temperature(f32::NEG_INFINITY);
assert!(config.temperature.is_infinite());
assert!(config.temperature.is_sign_negative());
let config = InferenceConfig::new("/m.gguf").with_temperature(f32::NAN);
assert!(config.temperature.is_nan());
}
#[test]
fn test_run_inference_dispatches_to_gguf_branch() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".gguf").expect("create temp");
let mut data = Vec::new();
data.extend_from_slice(b"GGUF"); data.extend_from_slice(&3u32.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes()); data.extend_from_slice(&0u64.to_le_bytes()); data.extend_from_slice(&[0u8; 100]);
temp.write_all(&data).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path())
.with_prompt("Test")
.with_max_tokens(1)
.with_verbose(false);
let result = run_inference(&config);
assert!(result.is_err());
}
#[test]
fn test_run_inference_dispatches_to_apr_branch() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".apr").expect("create temp");
let mut data = Vec::new();
data.extend_from_slice(b"APR2"); data.extend_from_slice(&[0u8; 100]); temp.write_all(&data).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path())
.with_prompt("Hello")
.with_max_tokens(2);
let result = run_inference(&config);
let _ = result;
}
#[test]
fn test_run_inference_dispatches_to_safetensors_branch() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".safetensors").expect("create temp");
let header_size: u64 = 50;
let mut data = Vec::new();
data.extend_from_slice(&header_size.to_le_bytes());
let json = r#"{"metadata":{}}"#;
data.extend_from_slice(json.as_bytes());
data.extend_from_slice(&[0u8; 50]); temp.write_all(&data).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path())
.with_prompt("Test")
.with_max_tokens(1);
let result = run_inference(&config);
assert!(result.is_err());
}
#[test]
fn test_run_inference_io_error_message_content() {
let config = InferenceConfig::new("/does/not/exist/model.gguf");
let result = run_inference(&config);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Failed to read") || err_msg.contains("IO error"),
"Error message should mention read failure: {}",
err_msg
);
}
#[test]
fn test_run_inference_format_error_message_content() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".bin").expect("create temp");
temp.write_all(&[0u8; 16]).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path());
let result = run_inference(&config);
assert!(result.is_err());
let err_msg = result.unwrap_err().to_string();
assert!(
err_msg.contains("Format") || err_msg.contains("format"),
"Error message should mention format: {}",
err_msg
);
}
#[test]
fn test_input_tokens_takes_priority_in_gguf_path() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".gguf").expect("create temp");
let mut data = Vec::new();
data.extend_from_slice(b"GGUF");
data.extend_from_slice(&3u32.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&[0u8; 100]);
temp.write_all(&data).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path())
.with_prompt("This is ignored")
.with_input_tokens(vec![1, 2, 3])
.with_max_tokens(5);
assert_eq!(config.input_tokens, Some(vec![1, 2, 3]));
assert_eq!(config.prompt, Some("This is ignored".to_string()));
let _result = run_inference(&config);
}
#[test]
fn test_verbose_mode_doesnt_panic() {
use std::io::Write;
use tempfile::NamedTempFile;
let mut temp = NamedTempFile::with_suffix(".gguf").expect("create temp");
let mut data = Vec::new();
data.extend_from_slice(b"GGUF");
data.extend_from_slice(&3u32.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&0u64.to_le_bytes());
data.extend_from_slice(&[0u8; 100]);
temp.write_all(&data).expect("write");
temp.flush().expect("flush");
let config = InferenceConfig::new(temp.path())
.with_prompt("Test")
.with_verbose(true)
.with_max_tokens(1);
let _result = run_inference(&config);
}
#[test]
fn test_architecture_detection_all_variants() {
let test_cases = [
("qwen2-7b-instruct.gguf", "Qwen2"),
("Qwen-7B-Chat.gguf", "Qwen2"),
("QWEN_7B.gguf", "Qwen2"),
("llama-3.1-8b-instruct.gguf", "LLaMA"),
("Llama-2-7b-chat.gguf", "LLaMA"),
("LLAMA3.gguf", "LLaMA"),
("mistral-7b-instruct-v0.2.gguf", "Mistral"),
("Mistral-7B.gguf", "Mistral"),
("MISTRAL_LARGE.gguf", "Mistral"),
("phi-2.gguf", "Phi"),
("Phi-3-mini.gguf", "Phi"),
("PHI2.gguf", "Phi"),
("custom-model.gguf", "Transformer"),
("gpt2.gguf", "Transformer"),
("my-finetuned-model.gguf", "Transformer"),
];
for (filename, expected_arch) in test_cases {
let path = PathBuf::from(format!("/models/{}", filename));
let arch = path.file_stem().and_then(|s| s.to_str()).map(|s| {
if s.to_lowercase().contains("qwen") {
"Qwen2"
} else if s.to_lowercase().contains("llama") {
"LLaMA"
} else if s.to_lowercase().contains("mistral") {
"Mistral"
} else if s.to_lowercase().contains("phi") {
"Phi"
} else {
"Transformer"
}
});
assert_eq!(arch, Some(expected_arch), "Failed for: {}", filename);
}
}
#[test]
fn test_prefault_mmap_various_patterns() {
let patterns: Vec<Vec<u8>> = vec![
vec![0u8; 8192], vec![255u8; 8192], (0..8192u16).map(|i| (i % 256) as u8).collect(), vec![0xAA; 8192], vec![0x55; 8192], ];
for pattern in patterns {
prefault_mmap(&pattern);
}
}
#[test]
fn test_prefault_mmap_exactly_one_page_minus_one() {
let data = vec![1u8; 4095];
prefault_mmap(&data);
}
#[test]
fn test_prefault_mmap_exactly_two_pages() {
let data = vec![2u8; 8192];
prefault_mmap(&data);
}
#[test]
fn test_prefault_mmap_many_pages() {
let data = vec![3u8; 4096 * 100]; prefault_mmap(&data);
}