#![cfg(not(debug_assertions))]
use crate::{ZstdCompressor, ZstdDecompressor};
use haagenti_core::{Compressor, Decompressor};
use std::time::{Duration, Instant};
const MIN_COMPRESS_THROUGHPUT_1KB: f64 = 10.0; const MIN_COMPRESS_THROUGHPUT_4KB: f64 = 15.0; const MIN_COMPRESS_THROUGHPUT_16KB: f64 = 20.0; const MIN_COMPRESS_THROUGHPUT_64KB: f64 = 15.0;
const MIN_DECOMPRESS_THROUGHPUT_1KB: f64 = 30.0; const MIN_DECOMPRESS_THROUGHPUT_16KB: f64 = 40.0; const MIN_DECOMPRESS_THROUGHPUT_64KB: f64 = 50.0;
fn generate_text_data(size: usize) -> Vec<u8> {
let words = [
"the ",
"quick ",
"brown ",
"fox ",
"jumps ",
"over ",
"lazy ",
"dog ",
"compression ",
"algorithm ",
"performance ",
"benchmark ",
"testing ",
];
let mut data = Vec::with_capacity(size);
let mut i = 0;
while data.len() < size {
let word = words[i % words.len()];
data.extend_from_slice(word.as_bytes());
i += 1;
}
data.truncate(size);
data
}
fn generate_binary_data(size: usize) -> Vec<u8> {
let mut data = Vec::with_capacity(size);
let mut val: u32 = 0x12345678;
while data.len() < size {
data.extend_from_slice(&val.to_le_bytes());
val = val.wrapping_mul(1103515245).wrapping_add(12345);
}
data.truncate(size);
data
}
fn measure_throughput<F>(iterations: usize, data_size: usize, mut f: F) -> f64
where
F: FnMut(),
{
for _ in 0..5 {
f();
}
let start = Instant::now();
for _ in 0..iterations {
f();
}
let elapsed = start.elapsed();
let total_bytes = data_size * iterations;
let throughput_mib = total_bytes as f64 / elapsed.as_secs_f64() / (1024.0 * 1024.0);
throughput_mib
}
#[test]
fn test_compress_throughput_1kb_text() {
let compressor = ZstdCompressor::new();
let data = generate_text_data(1024);
let throughput = measure_throughput(1000, data.len(), || {
let _ = compressor.compress(&data);
});
assert!(
throughput >= MIN_COMPRESS_THROUGHPUT_1KB,
"Compression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 1KB text",
throughput,
MIN_COMPRESS_THROUGHPUT_1KB
);
}
#[test]
fn test_compress_throughput_4kb_text() {
let compressor = ZstdCompressor::new();
let data = generate_text_data(4096);
let throughput = measure_throughput(500, data.len(), || {
let _ = compressor.compress(&data);
});
assert!(
throughput >= MIN_COMPRESS_THROUGHPUT_4KB,
"Compression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 4KB text",
throughput,
MIN_COMPRESS_THROUGHPUT_4KB
);
}
#[test]
fn test_compress_throughput_16kb_text() {
let compressor = ZstdCompressor::new();
let data = generate_text_data(16384);
let throughput = measure_throughput(200, data.len(), || {
let _ = compressor.compress(&data);
});
assert!(
throughput >= MIN_COMPRESS_THROUGHPUT_16KB,
"Compression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 16KB text",
throughput,
MIN_COMPRESS_THROUGHPUT_16KB
);
}
#[test]
fn test_compress_throughput_64kb_binary() {
let compressor = ZstdCompressor::new();
let data = generate_binary_data(65536);
let throughput = measure_throughput(100, data.len(), || {
let _ = compressor.compress(&data);
});
assert!(
throughput >= MIN_COMPRESS_THROUGHPUT_64KB,
"Compression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 64KB binary",
throughput,
MIN_COMPRESS_THROUGHPUT_64KB
);
}
#[test]
fn test_decompress_throughput_1kb() {
let compressor = ZstdCompressor::new();
let decompressor = ZstdDecompressor::new();
let data = generate_text_data(1024);
let compressed = compressor.compress(&data).unwrap();
let throughput = measure_throughput(2000, data.len(), || {
let _ = decompressor.decompress(&compressed);
});
assert!(
throughput >= MIN_DECOMPRESS_THROUGHPUT_1KB,
"Decompression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 1KB",
throughput,
MIN_DECOMPRESS_THROUGHPUT_1KB
);
}
#[test]
fn test_decompress_throughput_16kb() {
let compressor = ZstdCompressor::new();
let decompressor = ZstdDecompressor::new();
let data = generate_text_data(16384);
let compressed = compressor.compress(&data).unwrap();
let throughput = measure_throughput(500, data.len(), || {
let _ = decompressor.decompress(&compressed);
});
assert!(
throughput >= MIN_DECOMPRESS_THROUGHPUT_16KB,
"Decompression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 16KB",
throughput,
MIN_DECOMPRESS_THROUGHPUT_16KB
);
}
#[test]
fn test_decompress_throughput_64kb() {
let compressor = ZstdCompressor::new();
let decompressor = ZstdDecompressor::new();
let data = generate_binary_data(65536);
let compressed = compressor.compress(&data).unwrap();
let throughput = measure_throughput(200, data.len(), || {
let _ = decompressor.decompress(&compressed);
});
assert!(
throughput >= MIN_DECOMPRESS_THROUGHPUT_64KB,
"Decompression throughput {:.1} MiB/s below minimum {:.1} MiB/s for 64KB",
throughput,
MIN_DECOMPRESS_THROUGHPUT_64KB
);
}
#[test]
fn test_huffman_encode_throughput() {
use crate::huffman::HuffmanEncoder;
let sample = generate_text_data(4096);
let encoder = HuffmanEncoder::build(&sample).unwrap();
let data = generate_text_data(65536);
let throughput = measure_throughput(100, data.len(), || {
let _ = encoder.encode(&data);
});
assert!(
throughput >= 30.0,
"Huffman encode throughput {:.1} MiB/s below minimum 30 MiB/s",
throughput
);
}
#[test]
fn test_match_finder_throughput() {
use crate::compress::MatchFinder;
let data = generate_text_data(65536);
let mut finder = MatchFinder::new(6);
let throughput = measure_throughput(50, data.len(), || {
let _ = finder.find_matches(&data);
});
assert!(
throughput >= 20.0,
"Match finder throughput {:.1} MiB/s below minimum 20 MiB/s",
throughput
);
}
#[test]
fn test_compression_ratio_text() {
let compressor = ZstdCompressor::new();
let data = generate_text_data(8192);
let compressed = compressor.compress(&data).unwrap();
let ratio = data.len() as f64 / compressed.len() as f64;
assert!(
ratio >= 1.5,
"Compression ratio {:.2}x below minimum 1.5x for text data",
ratio
);
}
#[test]
fn test_no_compression_regression_binary() {
let compressor = ZstdCompressor::new();
let data = generate_binary_data(65536);
let compressed = compressor.compress(&data).unwrap();
assert!(
compressed.len() <= data.len() + 100,
"Binary data expanded too much: {} -> {} bytes",
data.len(),
compressed.len()
);
}
#[test]
fn test_small_input_latency() {
let compressor = ZstdCompressor::new();
let decompressor = ZstdDecompressor::new();
let data = b"Hello, World!";
let start = Instant::now();
for _ in 0..10000 {
let compressed = compressor.compress(data).unwrap();
let _ = decompressor.decompress(&compressed).unwrap();
}
let elapsed = start.elapsed();
let avg_latency = elapsed / 10000;
assert!(
avg_latency < Duration::from_micros(200),
"Small input latency {:?} exceeds 200µs threshold",
avg_latency
);
}