use std::io::Write;
use tempfile::NamedTempFile;
use falcon_mdf::blocks::*;
use falcon_mdf::parser::Mf4Version;
fn create_minimal_mf4() -> Vec<u8> {
let mut data = Vec::new();
let id_block = create_id_block(420); data.extend_from_slice(&id_block);
let _hd_offset = 64u64;
let dg_offset = 168u64; let hd_block = create_hd_block(dg_offset);
data.extend_from_slice(&hd_block);
let cg_offset = 232u64; let dt_offset = 496u64;
let dg_block = create_dg_block(0, cg_offset, dt_offset);
data.extend_from_slice(&dg_block);
let cn_offset = 336u64; let cg_block = create_cg_block(0, cn_offset, 5); data.extend_from_slice(&cg_block);
let cn_block = create_cn_block(0); data.extend_from_slice(&cn_block);
while data.len() < dt_offset as usize {
data.push(0);
}
let sample_data: [f64; 5] = [1.0, 2.0, 3.0, 4.0, 5.0];
let dt_block = create_dt_block(&sample_data);
data.extend_from_slice(&dt_block);
data
}
fn create_id_block(version: u16) -> Vec<u8> {
let mut block = vec![0u8; 64];
block[0..8].copy_from_slice(b"MDF ");
block[8..16].copy_from_slice(b"4.20 ");
block[16..24].copy_from_slice(b"TestProg");
block[28..30].copy_from_slice(&version.to_le_bytes());
block
}
fn create_hd_block(dg_first: u64) -> Vec<u8> {
let mut block = vec![0u8; 104];
block[0..4].copy_from_slice(b"##HD");
block[8..16].copy_from_slice(&104u64.to_le_bytes()); block[16..24].copy_from_slice(&6u64.to_le_bytes());
block[24..32].copy_from_slice(&dg_first.to_le_bytes());
let start_time_ns: i64 = 1_640_000_000_000_000_000;
block[72..80].copy_from_slice(&start_time_ns.to_le_bytes());
block
}
fn create_dg_block(dg_next: u64, cg_first: u64, data_link: u64) -> Vec<u8> {
let mut block = vec![0u8; 64];
block[0..4].copy_from_slice(b"##DG");
block[8..16].copy_from_slice(&64u64.to_le_bytes());
block[16..24].copy_from_slice(&4u64.to_le_bytes());
block[24..32].copy_from_slice(&dg_next.to_le_bytes());
block[32..40].copy_from_slice(&cg_first.to_le_bytes());
block[40..48].copy_from_slice(&data_link.to_le_bytes());
block[56] = 0;
block
}
fn create_cg_block(cg_next: u64, cn_first: u64, cycle_count: u64) -> Vec<u8> {
let mut block = vec![0u8; 104];
block[0..4].copy_from_slice(b"##CG");
block[8..16].copy_from_slice(&104u64.to_le_bytes());
block[16..24].copy_from_slice(&6u64.to_le_bytes());
block[24..32].copy_from_slice(&cg_next.to_le_bytes());
block[32..40].copy_from_slice(&cn_first.to_le_bytes());
block[72..80].copy_from_slice(&0u64.to_le_bytes()); block[80..88].copy_from_slice(&cycle_count.to_le_bytes()); block[88..90].copy_from_slice(&0u16.to_le_bytes()); block[90..92].copy_from_slice(&('.' as u16).to_le_bytes()); block[96..100].copy_from_slice(&8u32.to_le_bytes()); block[100..104].copy_from_slice(&0u32.to_le_bytes());
block
}
fn create_cn_block(cn_next: u64) -> Vec<u8> {
let mut block = vec![0u8; 160];
block[0..4].copy_from_slice(b"##CN");
block[8..16].copy_from_slice(&160u64.to_le_bytes());
block[16..24].copy_from_slice(&8u64.to_le_bytes());
block[24..32].copy_from_slice(&cn_next.to_le_bytes());
block[88] = 0; block[89] = 0; block[90] = 4; block[91] = 0; block[92..96].copy_from_slice(&0u32.to_le_bytes()); block[96..100].copy_from_slice(&64u32.to_le_bytes()); block[100..104].copy_from_slice(&0u32.to_le_bytes());
block
}
fn create_dt_block(samples: &[f64]) -> Vec<u8> {
let data_size = samples.len() * 8;
let total_size = BLOCK_HEADER_SIZE + data_size;
let mut block = vec![0u8; total_size];
block[0..4].copy_from_slice(b"##DT");
block[8..16].copy_from_slice(&(total_size as u64).to_le_bytes());
block[16..24].copy_from_slice(&0u64.to_le_bytes());
for (i, &sample) in samples.iter().enumerate() {
let offset = BLOCK_HEADER_SIZE + i * 8;
block[offset..offset + 8].copy_from_slice(&sample.to_le_bytes());
}
block
}
#[test]
fn test_version_parsing() {
assert!(matches!(
Mf4Version::from_version_number(400),
Mf4Version::V4_0 { .. }
));
assert!(matches!(
Mf4Version::from_version_number(410),
Mf4Version::V4_1 { .. }
));
assert!(matches!(
Mf4Version::from_version_number(420),
Mf4Version::V4_2 { .. }
));
let unknown = Mf4Version::from_version_number(500);
assert!(!unknown.is_supported());
}
#[test]
fn test_id_block_parsing() {
let data = create_id_block(420);
let id = IdBlock::parse(&data).unwrap();
assert_eq!(id.version_major(), 4);
assert_eq!(id.version_minor(), 20);
assert!(id.is_finalized());
}
#[test]
fn test_hd_block_parsing() {
let data = create_hd_block(168);
let hd = HdBlock::parse(&data, 64).unwrap();
assert_eq!(hd.dg_first, 168);
assert_eq!(hd.fh_first, 0);
}
#[test]
fn test_dg_block_parsing() {
let data = create_dg_block(0, 200, 300);
let dg = DgBlock::parse(&data, 168).unwrap();
assert_eq!(dg.dg_next, 0);
assert_eq!(dg.cg_first, 200);
assert_eq!(dg.data, 300);
assert_eq!(dg.rec_id_size, 0);
}
#[test]
fn test_cg_block_parsing() {
let data = create_cg_block(0, 500, 1000);
let cg = CgBlock::parse(&data, 232).unwrap();
assert_eq!(cg.cg_next, 0);
assert_eq!(cg.cn_first, 500);
assert_eq!(cg.cycle_count, 1000);
assert_eq!(cg.data_bytes, 8);
}
#[test]
fn test_cn_block_parsing() {
let data = create_cn_block(0);
let cn = CnBlock::parse(&data, 336).unwrap();
assert_eq!(cn.cn_next, 0);
assert_eq!(cn.channel_type, ChannelType::FixedLength);
assert_eq!(cn.data_type, DataType::FloatLe);
assert_eq!(cn.bit_count, 64);
}
#[test]
fn test_dt_block_parsing() {
let samples = [1.0f64, 2.0, 3.0];
let data = create_dt_block(&samples);
let dt = DtBlock::parse(&data, 0).unwrap();
assert_eq!(dt.data_length, 24); }
#[test]
fn test_conversion_identity() {
let conv = Conversion::None;
assert_eq!(conv.convert(42.0, false), 42.0);
}
#[test]
fn test_conversion_linear() {
let conv = Conversion::Linear {
offset: 10.0,
factor: 2.0,
};
assert!((conv.convert(5.0, false) - 20.0).abs() < 0.001);
}
#[test]
fn test_mf4_file_structure() {
let mf4_data = create_minimal_mf4();
let mut temp_file = NamedTempFile::new().unwrap();
temp_file.write_all(&mf4_data).unwrap();
temp_file.flush().unwrap();
let result = falcon_mdf::Mf4File::open(temp_file.path());
assert!(
result.is_ok(),
"Failed to open test MF4: {:?}",
result.err()
);
let file = result.unwrap();
assert!(matches!(file.version(), Mf4Version::V4_2 { .. }));
assert_eq!(file.data_group_count(), 1);
}