#[cfg(test)]
mod tests {
use crate::apr::*;
use std::collections::HashMap;
#[test]
fn test_matmul_scaling() {
let x = vec![2.0, 2.0];
let w = vec![1.0, 1.0];
let result = crate::apr::matmul(&x, &w, 1, 2, 1);
assert_eq!(result.len(), 1);
assert!((result[0] - 4.0).abs() < 1e-6);
}
#[test]
fn test_get_tensor_f32_f16_dtype() {
let f16_data = vec![
0x00, 0x3C, 0x00, 0x40, 0x00, 0x3C, 0x00, 0x40, ];
let model_data = create_test_apr_model_with_dtype(1, &f16_data);
let model = AprV2Model::from_bytes(model_data).expect("should load");
let result = model.get_tensor_f32("typed.weight");
assert!(result.is_ok());
let floats = result.expect("APR operation failed");
assert_eq!(floats.len(), 4);
assert!((floats[0] - 1.0).abs() < 0.1);
assert!((floats[1] - 2.0).abs() < 0.1);
}
#[ignore = "APR dtype parsing bug - needs investigation"]
#[test]
fn test_get_tensor_f32_q8_0_dtype() {
let mut q8_data = vec![0u8; 34];
q8_data[0] = 0x00;
q8_data[1] = 0x3C; for i in 0..32 {
q8_data[2 + i] = i as u8;
}
let metadata = r#"{"architecture":"test"}"#;
let metadata_bytes = metadata.as_bytes();
let metadata_padded_size = metadata_bytes.len().div_ceil(64) * 64;
let tensor_entry = create_binary_tensor_entry("typed.weight", 10, &[32], 0, 34);
let tensor_index_offset = HEADER_SIZE as u64 + metadata_padded_size as u64;
let data_offset = tensor_index_offset + tensor_entry.len() as u64;
let total_size = data_offset as usize + 34;
let mut model_data = vec![0u8; total_size];
model_data[0..4].copy_from_slice(&MAGIC);
model_data[4] = 2;
model_data[5] = 0;
model_data[8..12].copy_from_slice(&1u32.to_le_bytes());
model_data[12..20].copy_from_slice(&(HEADER_SIZE as u64).to_le_bytes());
model_data[20..24].copy_from_slice(&(metadata_bytes.len() as u32).to_le_bytes());
model_data[24..32].copy_from_slice(&tensor_index_offset.to_le_bytes());
model_data[32..40].copy_from_slice(&data_offset.to_le_bytes());
model_data[HEADER_SIZE..HEADER_SIZE + metadata_bytes.len()].copy_from_slice(metadata_bytes);
let idx_start = tensor_index_offset as usize;
model_data[idx_start..idx_start + tensor_entry.len()].copy_from_slice(&tensor_entry);
let data_start = data_offset as usize;
model_data[data_start..data_start + 34].copy_from_slice(&q8_data);
let model = AprV2Model::from_bytes(model_data).expect("should load");
let result = model.get_tensor_f32("typed.weight");
assert!(result.is_ok());
let floats = result.expect("APR operation failed");
assert_eq!(floats.len(), 32);
}
#[test]
fn test_get_tensor_f32_unsupported_dtype() {
let bf16_data = vec![0x00, 0x3F, 0x80, 0x00]; let model_data = create_test_apr_model_with_dtype(2, &bf16_data);
let model = AprV2Model::from_bytes(model_data).expect("should load");
let result = model.get_tensor_f32("typed.weight");
assert!(result.is_err());
}
#[test]
fn test_get_tensor_f32_out_of_bounds() {
let mut data = vec![0u8; 128];
data[0..4].copy_from_slice(&MAGIC);
data[4] = 2;
data[5] = 0;
data[8..12].copy_from_slice(&1u32.to_le_bytes()); data[12..20].copy_from_slice(&64u64.to_le_bytes()); data[20..24].copy_from_slice(&0u32.to_le_bytes()); data[24..32].copy_from_slice(&64u64.to_le_bytes()); data[32..40].copy_from_slice(&100u64.to_le_bytes());
let tensor_entry = create_binary_tensor_entry("oob.weight", 0, &[1000], 0, 4000);
data[64..64 + tensor_entry.len()].copy_from_slice(&tensor_entry);
let model = AprV2Model::from_bytes(data).expect("should load");
let result = model.get_tensor_f32("oob.weight");
assert!(result.is_err()); }
#[test]
fn test_decode_tokens_gpt2_special() {
let vocab = vec![
"Ġhello".to_string(), "Ċ".to_string(), "ĉ".to_string(), ];
let result = AprV2Model::decode_tokens(&vocab, &[0, 1, 2]);
assert!(result.contains("hello"));
assert!(result.contains('\n'));
assert!(result.contains('\t'));
}
#[test]
fn test_decode_tokens_empty_string_token() {
let vocab = vec![String::new(), "a".to_string()];
let result = AprV2Model::decode_tokens(&vocab, &[0, 1]);
assert!(result.contains('a'));
}
#[test]
fn test_f16_to_f32_smallest_positive_normal() {
let result = crate::apr::f16_to_f32(0x0400);
assert!(result > 0.0 && result < 1e-4);
}
#[test]
fn test_f16_to_f32_largest_normal() {
let result = crate::apr::f16_to_f32(0x7BFF);
assert!((result - 65504.0).abs() < 10.0);
}
#[test]
fn test_f16_to_f32_negative_normal() {
let result = crate::apr::f16_to_f32(0xC000);
assert!((result + 2.0).abs() < 0.01);
}
#[test]
fn test_f16_to_f32_subnormal_nonzero() {
for mant in [1u16, 10, 100, 0x3FF] {
let result = crate::apr::f16_to_f32(mant);
assert!(result > 0.0, "Subnormal {mant:#x} should be positive");
}
}
#[test]
fn test_bpe_encode_with_newline() {
let mut token_to_id = HashMap::new();
token_to_id.insert("Ċ".to_string(), 0); let result = bpe_encode("\n", &token_to_id, &[], &HashMap::new());
assert_eq!(result, vec![0]);
}
#[test]
fn test_bpe_encode_with_tab() {
let mut token_to_id = HashMap::new();
token_to_id.insert("ĉ".to_string(), 0); let result = bpe_encode("\t", &token_to_id, &[], &HashMap::new());
assert_eq!(result, vec![0]);
}
#[test]
fn test_bpe_encode_with_space() {
let mut token_to_id = HashMap::new();
token_to_id.insert("Ġ".to_string(), 0); let result = bpe_encode(" ", &token_to_id, &[], &HashMap::new());
assert_eq!(result, vec![0]);
}
#[test]
fn test_bpe_encode_mixed() {
let mut token_to_id = HashMap::new();
token_to_id.insert("a".to_string(), 0);
token_to_id.insert("Ġ".to_string(), 1); token_to_id.insert("b".to_string(), 2);
let result = bpe_encode("a b", &token_to_id, &[], &HashMap::new());
assert_eq!(result, vec![0, 1, 2]);
}
#[test]
fn test_bpe_encode_multiple_merges() {
let mut token_to_id = HashMap::new();
token_to_id.insert("a".to_string(), 0);
token_to_id.insert("b".to_string(), 1);
token_to_id.insert("c".to_string(), 2);
token_to_id.insert("ab".to_string(), 3);
token_to_id.insert("abc".to_string(), 4);
let merges = vec![
("a".to_string(), "b".to_string()),
("ab".to_string(), "c".to_string()),
];
let result = bpe_encode("abc", &token_to_id, &merges, &HashMap::new());
assert!(!result.is_empty());
}
include!("tests_binary_tensor_helpers.rs");
include!("tests_dequantize.rs");
include!("tests_byte_02.rs");
}