#[test]
fn test_verification_report_with_tensor_diffs() {
use std::collections::BTreeMap;
let mut tensor_diffs = BTreeMap::new();
tensor_diffs.insert("layer.0.weight".to_string(), 0.001);
tensor_diffs.insert("layer.1.weight".to_string(), 0.005);
let report = VerificationReport {
is_equivalent: true,
max_diff: 0.005,
mean_diff: 0.003,
tensor_diffs,
changed_metadata: vec!["version".to_string()],
failed_tensors: vec![],
};
assert!(report.passes_with_tolerance(0.01));
assert!(!report.passes_with_tolerance(0.001));
print_verification_json(&report);
}
#[test]
fn test_verification_report_not_equivalent_but_within_tolerance() {
let report = VerificationReport {
is_equivalent: false,
max_diff: 0.01,
mean_diff: 0.001,
tensor_diffs: std::collections::BTreeMap::new(),
changed_metadata: vec![],
failed_tensors: vec![],
};
assert!(report.passes_with_tolerance(0.1));
}
#[test]
fn test_tensor_fingerprint_all_fields() {
let fp = TensorFingerprint {
name: "model.layers.5.self_attn.q_proj.weight".to_string(),
shape: vec![4096, 4096],
dtype: "Q4_K_M".to_string(),
mean: -0.002,
std: 0.15,
min: -1.5,
max: 1.5,
p5: -0.25,
p25: -0.08,
p50: -0.001,
p75: 0.08,
p95: 0.25,
nan_count: 0,
inf_count: 0,
zero_fraction: 0.05,
checksum: 0xABCD_1234,
};
assert_eq!(fp.name, "model.layers.5.self_attn.q_proj.weight");
assert_eq!(fp.shape, vec![4096, 4096]);
assert_eq!(fp.dtype, "Q4_K_M");
assert!((fp.mean - (-0.002)).abs() < 0.001);
assert!((fp.std - 0.15).abs() < 0.001);
assert!((fp.min - (-1.5)).abs() < 0.001);
assert!((fp.max - 1.5).abs() < 0.001);
assert!((fp.p5 - (-0.25)).abs() < 0.001);
assert!((fp.p25 - (-0.08)).abs() < 0.001);
assert!((fp.p50 - (-0.001)).abs() < 0.001);
assert!((fp.p75 - 0.08).abs() < 0.001);
assert!((fp.p95 - 0.25).abs() < 0.001);
assert_eq!(fp.nan_count, 0);
assert_eq!(fp.inf_count, 0);
assert!((fp.zero_fraction - 0.05).abs() < 0.001);
assert_eq!(fp.checksum, 0xABCD_1234);
}
#[test]
fn test_strip_ansi_escape_followed_by_non_bracket() {
let text = "\x1bXhello";
let stripped = strip_ansi(text);
assert_eq!(stripped, "Xhello");
}
#[test]
fn test_strip_ansi_multiple_escapes_no_brackets() {
let text = "\x1b\x1b\x1bhello";
let stripped = strip_ansi(text);
assert_eq!(stripped, "hello");
}
#[test]
fn test_strip_ansi_escape_at_end_of_string() {
let text = "hello\x1b";
let stripped = strip_ansi(text);
assert_eq!(stripped, "hello");
}
#[test]
fn test_strip_ansi_escape_bracket_at_end() {
let text = "hello\x1b[";
let stripped = strip_ansi(text);
assert_eq!(stripped, "hello");
}
#[test]
fn test_normalize_tensor_name_layers_prefix_without_model() {
let result = normalize_tensor_name("layers.5.self_attn.q_proj.weight");
assert_eq!(result, "5.q_proj.weight");
}
#[test]
fn test_normalize_tensor_name_blk_with_self_attn() {
let result = normalize_tensor_name("blk.0.self_attn.q_proj.weight");
assert_eq!(result, "0.q_proj.weight");
}
#[test]
fn test_normalize_tensor_name_mlp_prefix_stripped() {
let result = normalize_tensor_name("0.mlp.gate_proj.weight");
assert_eq!(result, "0.gate_proj.weight");
}
#[test]
fn test_format_type_from_extension_case_sensitivity() {
let path = Path::new("model.GGUF");
let result = FormatType::from_extension(path);
assert!(result.is_ok() || result.is_err()); }
#[test]
fn test_format_type_from_extension_double_extension() {
let path = Path::new("model.tar.gguf");
let result = FormatType::from_extension(path);
assert!(result.is_ok());
assert_eq!(result.expect("format"), FormatType::Gguf);
}
#[test]
fn test_conversion_options_clone() {
let opts = ConversionOptions {
quantization: Some("int8".to_string()),
verify: true,
compute_stats: true,
tolerance: 0.01,
preserve_metadata: true,
add_provenance: true,
tokenizer_path: None,
};
let cloned = opts.clone();
assert_eq!(opts.quantization, cloned.quantization);
assert_eq!(opts.verify, cloned.verify);
assert_eq!(opts.compute_stats, cloned.compute_stats);
assert!((opts.tolerance - cloned.tolerance).abs() < 1e-10);
assert_eq!(opts.preserve_metadata, cloned.preserve_metadata);
assert_eq!(opts.add_provenance, cloned.add_provenance);
}
#[test]
fn t_f_rosetta_004_fingerprint_detects_single_byte_corruption() {
let original: Vec<f32> = (0..1000)
.map(|i| ((i as f32) * 0.00314159 - 1.5).sin() * 0.02)
.collect();
let (mean_a, std_a, min_a, max_a, _, _, _, _, _, _, _, _, checksum_a) =
compute_tensor_stats(&original);
let mut corrupted = original.clone();
let bits = corrupted[500].to_bits() ^ 0x8000_0000;
corrupted[500] = f32::from_bits(bits);
let (mean_b, std_b, min_b, max_b, _, _, _, _, _, _, _, _, checksum_b) =
compute_tensor_stats(&corrupted);
assert_ne!(
checksum_a, checksum_b,
"F-ROSETTA-004: Checksum must detect single-byte corruption"
);
let stats_differ = (mean_a - mean_b).abs() > 1e-10
|| (std_a - std_b).abs() > 1e-10
|| (min_a - min_b).abs() > 1e-10
|| (max_a - max_b).abs() > 1e-10;
assert!(
stats_differ,
"F-ROSETTA-004: At least one stat must differ after corruption"
);
}
#[test]
fn t_f_rosetta_004_fingerprint_stable_for_identical_data() {
let data: Vec<f32> = (0..500)
.map(|i| ((i as f32) * 0.007 - 1.75).cos() * 0.1)
.collect();
let (mean_a, std_a, _, _, _, _, _, _, _, _, _, _, checksum_a) = compute_tensor_stats(&data);
let (mean_b, std_b, _, _, _, _, _, _, _, _, _, _, checksum_b) = compute_tensor_stats(&data);
assert_eq!(
checksum_a, checksum_b,
"Identical data must produce identical checksums"
);
assert!(
(mean_a - mean_b).abs() < f32::EPSILON,
"Identical data must produce identical means"
);
assert!(
(std_a - std_b).abs() < f32::EPSILON,
"Identical data must produce identical stds"
);
}
#[test]
fn t_f_rosetta_004_fingerprint_detects_small_perturbation() {
let original: Vec<f32> = (0..100).map(|i| (i as f32) * 0.01).collect();
let mut perturbed = original.clone();
let bits = perturbed[50].to_bits() + 1;
perturbed[50] = f32::from_bits(bits);
let (_, _, _, _, _, _, _, _, _, _, _, _, checksum_a) = compute_tensor_stats(&original);
let (_, _, _, _, _, _, _, _, _, _, _, _, checksum_b) = compute_tensor_stats(&perturbed);
assert_ne!(
checksum_a, checksum_b,
"F-ROSETTA-004: Even 1 ULP change must produce different checksum"
);
}
#[test]
fn t_f_gt_002_mixed_quant_warning_safetensors_vs_gguf_q4k() {
let model_a = Path::new("model.safetensors");
let model_b = Path::new("model-q4_k_m.gguf");
let warning = super::check_mixed_quant_warning(model_a, model_b);
assert!(
warning.is_some(),
"F-GT-002: Must warn when comparing SafeTensors (unquantized) vs GGUF Q4_K_M"
);
let msg = warning.expect("checked above");
assert!(
msg.contains("F-GT-002"),
"Warning must cite F-GT-002: {msg}"
);
assert!(
msg.contains("mixed quantization") || msg.contains("Mixed quantization"),
"Warning must mention mixed quantization: {msg}"
);
}
#[test]
fn t_f_gt_002_no_warning_same_format() {
let model_a = Path::new("model-q4_k.gguf");
let model_b = Path::new("other-q4_k.gguf");
let warning = super::check_mixed_quant_warning(model_a, model_b);
assert!(
warning.is_none(),
"F-GT-002: No warning when both models are Q4_K GGUF"
);
}
#[test]
fn t_f_gt_002_warning_different_gguf_quants() {
let model_a = Path::new("model-q4_k.gguf");
let model_b = Path::new("model-q6_k.gguf");
let warning = super::check_mixed_quant_warning(model_a, model_b);
assert!(
warning.is_some(),
"F-GT-002: Must warn when comparing Q4_K vs Q6_K"
);
}
#[test]
fn t_f_gt_002_warning_apr_vs_safetensors() {
let model_a = Path::new("model-q4k.apr");
let model_b = Path::new("model.safetensors");
let warning = super::check_mixed_quant_warning(model_a, model_b);
assert!(
warning.is_some(),
"F-GT-002: Must warn when comparing APR Q4K vs SafeTensors (unquantized)"
);
}
#[test]
fn t_f_gt_002_no_warning_both_safetensors() {
let model_a = Path::new("model-part1.safetensors");
let model_b = Path::new("model-part2.safetensors");
let warning = super::check_mixed_quant_warning(model_a, model_b);
assert!(
warning.is_none(),
"F-GT-002: No warning when both are SafeTensors (same quant level)"
);
}