use llm_manager::models::*;
#[test]
fn strip_gguf_strips_lowercase_extension() {
assert_eq!(strip_gguf("model.gguf"), "model");
}
#[test]
fn strip_gguf_strips_uppercase_extension() {
assert_eq!(strip_gguf("model.GGUF"), "model");
}
#[test]
fn strip_gguf_no_extension_returns_as_is() {
assert_eq!(strip_gguf("model.bin"), "model.bin");
}
#[test]
fn strip_gguf_already_stripped() {
assert_eq!(strip_gguf("model"), "model");
}
#[test]
fn strip_gguf_with_dots_in_name() {
assert_eq!(strip_gguf("qwen2.5-7b.gguf"), "qwen2.5-7b");
}
#[test]
fn clean_host_empty_returns_loopback() {
assert_eq!(clean_host(""), "127.0.0.1");
}
#[test]
fn clean_host_ipv4_unchanged() {
assert_eq!(clean_host("192.168.1.1"), "192.168.1.1");
}
#[test]
fn clean_host_ipv6_wrapped_in_brackets() {
assert_eq!(clean_host("::1"), "[::1]");
}
#[test]
fn clean_host_ipv6_full() {
assert_eq!(clean_host("2001:db8::1"), "[2001:db8::1]");
}
#[test]
fn clean_host_with_display_suffix() {
assert_eq!(clean_host("localhost (127.0.0.1)"), "localhost");
}
#[test]
fn clean_host_trims_whitespace() {
assert_eq!(clean_host(" 192.168.1.1 "), "192.168.1.1");
}
#[test]
fn format_host_empty() {
assert_eq!(format_host(""), "localhost (127.0.0.1)");
}
#[test]
fn format_host_loopback() {
assert_eq!(format_host("127.0.0.1"), "localhost (127.0.0.1)");
}
#[test]
fn format_host_custom_returns_as_is() {
assert_eq!(format_host("192.168.1.100"), "192.168.1.100");
}
#[test]
fn search_sort_next_cycles_through_all() {
let mut sort = SearchSort::Relevance;
let expected = [
SearchSort::Downloads,
SearchSort::Likes,
SearchSort::Trending,
SearchSort::CreatedAt,
SearchSort::Relevance,
];
for exp in &expected {
sort = sort.next();
assert_eq!(sort, *exp);
}
}
#[test]
fn search_sort_label_all() {
assert_eq!(SearchSort::Relevance.label(), "Relevance");
assert_eq!(SearchSort::Downloads.label(), "Downloads");
assert_eq!(SearchSort::Likes.label(), "Likes");
assert_eq!(SearchSort::Trending.label(), "Trending");
assert_eq!(SearchSort::CreatedAt.label(), "Created");
}
#[test]
fn cache_quant_type_next_cycles_through_all() {
let mut t = CacheQuantType::F32;
let expected = [
CacheQuantType::F16,
CacheQuantType::BF16,
CacheQuantType::Q8_0,
CacheQuantType::Q5_1,
CacheQuantType::Q5_0,
CacheQuantType::Q4_1,
CacheQuantType::Q4_0,
CacheQuantType::Iq4Nl,
CacheQuantType::F32,
];
for exp in &expected {
t = t.next();
assert_eq!(t, *exp);
}
}
#[test]
fn cache_quant_type_prev_cycles_through_all() {
let mut t = CacheQuantType::F32;
let expected = [
CacheQuantType::Iq4Nl,
CacheQuantType::Q4_0,
CacheQuantType::Q4_1,
CacheQuantType::Q5_0,
CacheQuantType::Q5_1,
CacheQuantType::Q8_0,
CacheQuantType::BF16,
CacheQuantType::F16,
CacheQuantType::F32,
];
for exp in &expected {
t = t.prev();
assert_eq!(t, *exp);
}
}
#[test]
fn cache_quant_type_from_u8_all_values() {
assert_eq!(CacheQuantType::from_u8(0), CacheQuantType::F32);
assert_eq!(CacheQuantType::from_u8(1), CacheQuantType::F16);
assert_eq!(CacheQuantType::from_u8(2), CacheQuantType::BF16);
assert_eq!(CacheQuantType::from_u8(3), CacheQuantType::Q8_0);
assert_eq!(CacheQuantType::from_u8(4), CacheQuantType::Q5_1);
assert_eq!(CacheQuantType::from_u8(5), CacheQuantType::Q5_0);
assert_eq!(CacheQuantType::from_u8(6), CacheQuantType::Q4_1);
assert_eq!(CacheQuantType::from_u8(7), CacheQuantType::Q4_0);
assert_eq!(CacheQuantType::from_u8(8), CacheQuantType::Iq4Nl);
assert_eq!(CacheQuantType::from_u8(99), CacheQuantType::F16);
}
#[test]
fn cache_quant_type_from_str_all() {
assert_eq!(CacheQuantType::from("F32"), CacheQuantType::F32);
assert_eq!(CacheQuantType::from("F16"), CacheQuantType::F16);
assert_eq!(CacheQuantType::from("BF16"), CacheQuantType::BF16);
assert_eq!(CacheQuantType::from("Q8_0"), CacheQuantType::Q8_0);
assert_eq!(CacheQuantType::from("Q4_0"), CacheQuantType::Q4_0);
assert_eq!(CacheQuantType::from("Q4_1"), CacheQuantType::Q4_1);
assert_eq!(CacheQuantType::from("Iq4Nl"), CacheQuantType::Iq4Nl);
assert_eq!(CacheQuantType::from("Q5_0"), CacheQuantType::Q5_0);
assert_eq!(CacheQuantType::from("Q5_1"), CacheQuantType::Q5_1);
assert_eq!(CacheQuantType::from("unknown"), CacheQuantType::F16);
}
#[test]
fn cache_quant_type_display_all() {
assert_eq!(format!("{}", CacheQuantType::F32), "f32");
assert_eq!(format!("{}", CacheQuantType::F16), "f16");
assert_eq!(format!("{}", CacheQuantType::BF16), "bf16");
assert_eq!(format!("{}", CacheQuantType::Q8_0), "q8_0");
assert_eq!(format!("{}", CacheQuantType::Q4_0), "q4_0");
assert_eq!(format!("{}", CacheQuantType::Q4_1), "q4_1");
assert_eq!(format!("{}", CacheQuantType::Iq4Nl), "iq4_nl");
assert_eq!(format!("{}", CacheQuantType::Q5_0), "q5_0");
assert_eq!(format!("{}", CacheQuantType::Q5_1), "q5_1");
}
#[test]
fn cache_quant_type_default_is_f16() {
assert_eq!(CacheQuantType::default(), CacheQuantType::F16);
}
#[test]
fn cache_type_display_all() {
assert_eq!(format!("{}", CacheType::F16), "f16");
assert_eq!(format!("{}", CacheType::BF16), "bf16");
assert_eq!(format!("{}", CacheType::Fq8_0), "fq8_0");
assert_eq!(format!("{}", CacheType::Fq4_1), "fq4_1");
}
#[test]
fn cache_type_default_is_f16() {
assert_eq!(CacheType::default(), CacheType::F16);
}
#[test]
fn split_mode_display_all() {
assert_eq!(format!("{}", SplitMode::None), "none");
assert_eq!(format!("{}", SplitMode::Layer), "layer");
assert_eq!(format!("{}", SplitMode::Row), "row");
assert_eq!(format!("{}", SplitMode::Tensor), "tensor");
}
#[test]
fn split_mode_default_is_layer() {
assert_eq!(SplitMode::default(), SplitMode::Layer);
}
#[test]
fn num_mode_display_all() {
assert_eq!(format!("{}", NumMode::None), "none");
assert_eq!(format!("{}", NumMode::Distribute), "distribute");
assert_eq!(format!("{}", NumMode::Isolate), "isolate");
assert_eq!(format!("{}", NumMode::Numactl), "numactl");
}
#[test]
fn num_mode_default_is_none() {
assert_eq!(NumMode::default(), NumMode::None);
}
#[test]
fn rope_scaling_display_all() {
assert_eq!(format!("{}", RopeScaling::None), "none");
assert_eq!(format!("{}", RopeScaling::Linear), "linear");
assert_eq!(format!("{}", RopeScaling::Yarn), "yarn");
}
#[test]
fn rope_scaling_default_is_none() {
assert_eq!(RopeScaling::default(), RopeScaling::None);
}
#[test]
fn mirostat_display_all() {
assert_eq!(format!("{}", Mirostat::Off), "off");
assert_eq!(format!("{}", Mirostat::V1), "1");
assert_eq!(format!("{}", Mirostat::Mirostat2), "2");
}
#[test]
fn mirostat_default_is_off() {
assert_eq!(Mirostat::default(), Mirostat::Off);
}
#[test]
fn samplers_default_contains_expected_order() {
let s = Samplers::default();
let parts: Vec<&str> = s.0.split(';').collect();
assert!(parts.contains(&"penalties"));
assert!(parts.contains(&"dry"));
assert!(parts.contains(&"top_k"));
assert!(parts.contains(&"temperature"));
}
#[test]
fn backend_slug_all_variants() {
assert_eq!(Backend::Cpu.slug(), "cpu");
assert_eq!(Backend::Vulkan.slug(), "vulkan");
assert_eq!(Backend::Rocm.slug(), "rocm");
assert_eq!(Backend::RocmLemonade.slug(), "rocm-lemonade");
assert_eq!(Backend::Cuda.slug(), "cuda");
assert_eq!(Backend::CpuArm64.slug(), "cpu-arm64");
assert_eq!(Backend::CpuWindows.slug(), "win-cpu");
assert_eq!(Backend::VulkanWindows.slug(), "win-vulkan");
assert_eq!(Backend::CudaWindows12_4.slug(), "win-cuda-12.4");
assert_eq!(Backend::CudaWindows13_1.slug(), "win-cuda-13.1");
assert_eq!(Backend::HipWindows.slug(), "win-hip");
assert_eq!(Backend::CpuMacosArm64.slug(), "macos-arm64");
assert_eq!(Backend::CpuMacosX64.slug(), "macos-x64");
}
#[test]
fn backend_is_linux_variants() {
assert!(Backend::Cpu.is_linux());
assert!(Backend::Vulkan.is_linux());
assert!(Backend::Rocm.is_linux());
assert!(Backend::RocmLemonade.is_linux());
assert!(Backend::Cuda.is_linux());
assert!(Backend::CpuArm64.is_linux());
}
#[test]
fn backend_is_linux_non_linux() {
assert!(!Backend::CpuWindows.is_linux());
assert!(!Backend::VulkanWindows.is_linux());
assert!(!Backend::CudaWindows12_4.is_linux());
assert!(!Backend::CudaWindows13_1.is_linux());
assert!(!Backend::HipWindows.is_linux());
assert!(!Backend::CpuMacosArm64.is_linux());
assert!(!Backend::CpuMacosX64.is_linux());
}
#[test]
fn backend_is_windows_variants() {
assert!(Backend::CpuWindows.is_windows());
assert!(Backend::VulkanWindows.is_windows());
assert!(Backend::CudaWindows12_4.is_windows());
assert!(Backend::CudaWindows13_1.is_windows());
assert!(Backend::HipWindows.is_windows());
}
#[test]
fn backend_is_windows_non_windows() {
assert!(!Backend::Cpu.is_windows());
assert!(!Backend::Vulkan.is_windows());
assert!(!Backend::Rocm.is_windows());
assert!(!Backend::RocmLemonade.is_windows());
assert!(!Backend::Cuda.is_windows());
}
#[test]
fn backend_is_macos_variants() {
assert!(Backend::CpuMacosArm64.is_macos());
assert!(Backend::CpuMacosX64.is_macos());
}
#[test]
fn backend_is_macos_non_macos() {
assert!(!Backend::Cpu.is_macos());
assert!(!Backend::Vulkan.is_macos());
assert!(!Backend::Cuda.is_macos());
}
#[test]
fn backend_from_str_variants() {
assert_eq!(Backend::from_str("cpu"), Backend::Cpu);
assert_eq!(Backend::from_str("CPU"), Backend::Cpu);
assert_eq!(Backend::from_str("vulkan"), Backend::Vulkan);
assert_eq!(Backend::from_str("vk"), Backend::Vulkan);
assert_eq!(Backend::from_str("rocm"), Backend::Rocm);
assert_eq!(Backend::from_str("ro"), Backend::Rocm);
assert_eq!(Backend::from_str("rocm-lemonade"), Backend::RocmLemonade);
assert_eq!(Backend::from_str("cuda"), Backend::Cuda);
assert_eq!(Backend::from_str("cu"), Backend::Cuda);
assert_eq!(Backend::from_str("unknown"), Backend::Cpu);
}
#[test]
fn backend_default_is_cpu() {
assert_eq!(Backend::default(), Backend::Cpu);
}
#[test]
fn gpu_layers_mode_default_is_auto() {
assert_eq!(GpuLayersMode::default(), GpuLayersMode::Auto);
}
#[test]
fn server_mode_display_all() {
assert_eq!(format!("{}", ServerMode::Normal), "Normal");
assert_eq!(format!("{}", ServerMode::Router), "Router (XP!)");
assert_eq!(format!("{}", ServerMode::Bench), "Bench GPU");
assert_eq!(format!("{}", ServerMode::BenchTune), "BenchTune");
}
#[test]
fn server_mode_default_is_normal() {
assert_eq!(ServerMode::default(), ServerMode::Normal);
}
#[test]
fn bench_tune_mode_default_is_full() {
assert_eq!(BenchTuneMode::default(), BenchTuneMode::Full);
}
#[test]
fn estimate_vram_cpu_only_returns_zero() {
let mut settings = ModelSettings::default();
settings.gpu_layers_mode = GpuLayersMode::Specific(0);
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
assert_eq!(result, 0);
}
#[test]
fn estimate_vram_all_layers_uses_all() {
let mut settings = ModelSettings::default();
settings.gpu_layers_mode = GpuLayersMode::All;
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
assert!(result > 0);
}
#[test]
fn estimate_vram_flash_attn_reduces_vram() {
let mut settings_no_flash = ModelSettings::default();
settings_no_flash.flash_attn = false;
let settings_flash = ModelSettings::default();
let no_flash = estimate_vram_mib(
4000,
&settings_no_flash,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
let flash = estimate_vram_mib(
4000,
&settings_flash,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
assert!(flash < no_flash);
}
#[test]
fn estimate_vram_unified_cache_reduces_vram() {
let settings_normal = ModelSettings::default();
let mut settings_unified = ModelSettings::default();
settings_unified.uniform_cache = true;
settings_unified.parallel = 4;
let normal = estimate_vram_mib(
4000,
&settings_normal,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
let unified = estimate_vram_mib(
4000,
&settings_unified,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
assert!(unified < normal);
}
#[test]
fn estimate_vram_gqa_reduces_kv_cache() {
let settings = ModelSettings::default();
let with_gqa = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
let without_gqa = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(32), 8192);
assert!(with_gqa < without_gqa);
}
#[test]
fn estimate_vram_quantization_affects_size() {
let mut settings_f32 = ModelSettings::default();
settings_f32.cache_type_k = Some(CacheTypeK::F32);
settings_f32.cache_type_v = Some(CacheTypeV::F32);
let mut settings_q4 = ModelSettings::default();
settings_q4.cache_type_k = Some(CacheTypeK::Q4_0);
settings_q4.cache_type_v = Some(CacheTypeV::Q4_0);
let f32_vram = estimate_vram_mib(4000, &settings_f32, 32, Some(4096), Some(32), Some(8), 8192);
let q4_vram = estimate_vram_mib(4000, &settings_q4, 32, Some(4096), Some(32), Some(8), 8192);
assert!(q4_vram < f32_vram);
}
#[test]
fn estimate_vram_zero_total_layers() {
let settings = ModelSettings::default();
let result = estimate_vram_mib(4000, &settings, 0, None, None, None, 0);
assert_eq!(result, 0);
}
#[test]
fn estimate_vram_increases_with_context_length() {
let mut settings_small = ModelSettings::default();
settings_small.context_length = 2048;
let mut settings_large = ModelSettings::default();
settings_large.context_length = 65536;
let small = estimate_vram_mib(
4000,
&settings_small,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
let large = estimate_vram_mib(
4000,
&settings_large,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
assert!(large > small);
}
#[test]
fn estimate_vram_increases_with_batch_size() {
let mut settings_small = ModelSettings::default();
settings_small.batch_size = 128;
let mut settings_large = ModelSettings::default();
settings_large.batch_size = 2048;
let small = estimate_vram_mib(
4000,
&settings_small,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
let large = estimate_vram_mib(
4000,
&settings_large,
32,
Some(4096),
Some(32),
Some(8),
8192,
);
assert!(large > small);
}
#[test]
fn estimate_vram_auto_uses_heuristic() {
let mut settings = ModelSettings::default();
settings.gpu_layers_mode = GpuLayersMode::Auto;
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
assert!(result > 0);
}
#[test]
fn estimate_vram_specific_layers() {
let mut settings = ModelSettings::default();
settings.gpu_layers_mode = GpuLayersMode::Specific(16);
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
assert!(result > 0);
}
#[test]
fn estimate_vram_specific_zero_returns_zero() {
let mut settings = ModelSettings::default();
settings.gpu_layers_mode = GpuLayersMode::Specific(0);
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 8192);
assert_eq!(result, 0);
}
#[test]
fn estimate_vram_no_gpu_memory_total() {
let settings = ModelSettings::default();
let result = estimate_vram_mib(4000, &settings, 32, Some(4096), Some(32), Some(8), 0);
assert!(result > 0);
}
#[test]
fn download_state_new_initial_values() {
let ds = DownloadState::new("model-id".into(), "file.gguf".into(), 1024);
assert_eq!(ds.model_id, "model-id");
assert_eq!(ds.filename, "file.gguf");
assert_eq!(ds.total_bytes, 1024);
assert_eq!(ds.downloaded_bytes, 0);
assert!(!ds.cancelled);
assert_eq!(ds.download_state, 1); assert!(ds.dest.is_none());
}
#[test]
fn server_metrics_default_all_zero() {
let m = ServerMetrics::default();
assert!(!m.loaded);
assert_eq!(m.tps, 0.0);
assert_eq!(m.prompt_tps, 0.0);
assert_eq!(m.cpu_usage, 0.0);
assert_eq!(m.gpu_mem_used, 0);
assert_eq!(m.gpu_mem_total, 0);
assert_eq!(m.ram_used, 0);
assert_eq!(m.ctx_used, 0);
assert_eq!(m.ctx_max, 0);
assert_eq!(m.total_vram_used, 0);
assert_eq!(m.decoded_tokens, 0);
assert_eq!(m.latency_per_token_ms, 0.0);
assert_eq!(m.prompt_latency_ms, 0.0);
}
#[test]
fn model_state_loaded_has_port_pid() {
let state = ModelState::Loaded {
port: 8080,
pid: 12345,
};
assert!(matches!(
state,
ModelState::Loaded {
port: 8080,
pid: 12345
}
));
}
#[test]
fn model_state_failed_has_error() {
let state = ModelState::Failed {
error: "OOM".into(),
};
assert!(matches!(state, ModelState::Failed { .. }));
}
#[test]
fn bench_tune_progress_from_status_running() {
let status = BenchTuneStatus::Running {
current: 1,
total: 10,
progress: 10.0,
current_params: BenchTuneParamValue {
temperature: None,
top_p: None,
top_k: None,
repeat_penalty: None,
context_length: None,
batch_size: None,
flash_attn: None,
threads: None,
expert_count: None,
spec_type: None,
draft_tokens: None,
},
};
let progress = BenchTuneProgress::from_status(&status);
assert!(matches!(progress, Some(BenchTuneProgress::Running { .. })));
}
#[test]
fn bench_tune_progress_from_status_completed() {
let status = BenchTuneStatus::Completed {
total_tests: 10,
successful_tests: 9,
elapsed: std::time::Duration::from_secs(60),
};
let progress = BenchTuneProgress::from_status(&status);
assert!(matches!(
progress,
Some(BenchTuneProgress::Completed { .. })
));
}
#[test]
fn bench_tune_progress_from_status_error() {
let status = BenchTuneStatus::Error {
error: "fail".into(),
};
let progress = BenchTuneProgress::from_status(&status);
assert!(matches!(progress, Some(BenchTuneProgress::Error { .. })));
}
#[test]
fn load_progress_default() {
let p = LoadProgress::default();
assert!(p.layers_total.is_none());
assert!(p.layers_loaded.is_none());
assert!(p.tensors_total.is_none());
assert_eq!(p.tensors_loaded, 0);
assert!(p.buffers.is_empty());
}
#[test]
fn bench_tune_param_value_eq_with_some_none() {
let a = BenchTuneParamValue {
temperature: Some(0.8),
top_p: None,
top_k: None,
repeat_penalty: None,
context_length: None,
batch_size: None,
flash_attn: None,
threads: None,
expert_count: None,
spec_type: None,
draft_tokens: None,
};
let b = BenchTuneParamValue {
temperature: Some(0.8),
top_p: None,
top_k: None,
repeat_penalty: None,
context_length: None,
batch_size: None,
flash_attn: None,
threads: None,
expert_count: None,
spec_type: None,
draft_tokens: None,
};
assert_eq!(a, b);
}
#[test]
fn bench_tune_param_value_ne_different_values() {
let a = BenchTuneParamValue {
temperature: Some(0.8),
top_p: None,
top_k: None,
repeat_penalty: None,
context_length: None,
batch_size: None,
flash_attn: None,
threads: None,
expert_count: None,
spec_type: None,
draft_tokens: None,
};
let b = BenchTuneParamValue {
temperature: Some(0.7),
top_p: None,
top_k: None,
repeat_penalty: None,
context_length: None,
batch_size: None,
flash_attn: None,
threads: None,
expert_count: None,
spec_type: None,
draft_tokens: None,
};
assert_ne!(a, b);
}
#[test]
fn bench_tune_param_eq_same_values() {
let a = BenchTuneParam {
name: "temperature".into(),
min: 0.4,
max: 1.0,
step: 0.1,
enabled: true,
};
let b = BenchTuneParam {
name: "temperature".into(),
min: 0.4,
max: 1.0,
step: 0.1,
enabled: true,
};
assert_eq!(a, b);
}
#[test]
fn bench_tune_param_ne_different_name() {
let a = BenchTuneParam {
name: "temperature".into(),
min: 0.4,
max: 1.0,
step: 0.1,
enabled: true,
};
let b = BenchTuneParam {
name: "top_p".into(),
min: 0.4,
max: 1.0,
step: 0.1,
enabled: true,
};
assert_ne!(a, b);
}
#[test]
fn search_result_serializable() {
let sr = SearchResult {
model_id: "org/model".into(),
model_name: "Model".into(),
tags: vec!["gguf".into()],
downloads: 1000,
likes: 50,
pipeline_tag: Some("text-generation".into()),
size: Some(5_000_000_000),
parameters: Some("7B".into()),
capabilities: vec!["chat".into()],
context_length: Some(32768),
readme: None,
quantization: Some("Q4_K_M".into()),
license: Some("mit".into()),
trending_score: 100,
created_at: Some("2024-01-01".into()),
downloaded: false,
};
let json = serde_json::to_string(&sr).expect("should serialize");
assert!(json.contains("org/model"));
let deserialized: SearchResult = serde_json::from_str(&json).expect("should deserialize");
assert_eq!(deserialized.model_id, "org/model");
}
#[test]
fn backend_display_uses_slug() {
assert_eq!(format!("{}", Backend::Cpu), "cpu");
assert_eq!(format!("{}", Backend::Vulkan), "vulkan");
assert_eq!(format!("{}", Backend::RocmLemonade), "rocm-lemonade");
assert_eq!(format!("{}", Backend::Cuda), "cuda");
}
#[test]
fn bench_tune_config_new_has_default_params() {
let config = BenchTuneConfig::new("/path/to/model.gguf".into(), 3, "test prompt".into());
assert_eq!(config.model_path.to_string_lossy(), "/path/to/model.gguf");
assert_eq!(config.num_iterations, 3);
assert_eq!(config.prompt, "test prompt");
assert_eq!(config.params_to_test.len(), 8);
assert_eq!(config.test_duration, std::time::Duration::from_secs(30));
}