use lz4::file::{lz4_read_frame, lz4_write_frame, Lz4ReadFile, Lz4WriteFile};
use lz4::frame::types::{BlockSizeId, ContentChecksum, FrameInfo, Preferences};
use std::io::{Cursor, Read, Write};
fn compress_to_frame(src: &[u8]) -> Vec<u8> {
lz4_write_frame(src, Vec::new()).expect("lz4_write_frame failed")
}
fn decompress_frame(frame: &[u8]) -> Vec<u8> {
let mut out = Vec::new();
lz4_read_frame(Cursor::new(frame), &mut out).expect("lz4_read_frame failed");
out
}
fn cycling_bytes(len: usize) -> Vec<u8> {
(0u8..=255).cycle().take(len).collect()
}
#[test]
fn convenience_round_trip_small() {
let original = b"Hello, LZ4 world! This is a test.";
let compressed = compress_to_frame(original);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn convenience_round_trip_empty() {
let original: &[u8] = b"";
let compressed = compress_to_frame(original);
assert!(
!compressed.is_empty(),
"compressed frame must not be empty even for empty input"
);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered.as_slice(), original);
}
#[test]
fn convenience_round_trip_single_byte() {
let original = b"x";
let compressed = compress_to_frame(original);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered.as_slice(), original.as_ref());
}
#[test]
fn convenience_round_trip_multi_block() {
let original: Vec<u8> = cycling_bytes(200 * 1024);
let compressed = compress_to_frame(&original);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn convenience_round_trip_large_repetitive() {
let original: Vec<u8> = b"AAAA".iter().cycle().take(512 * 1024).cloned().collect();
let compressed = compress_to_frame(&original);
assert!(compressed.len() < original.len() / 2);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn convenience_round_trip_incompressible() {
let original: Vec<u8> = cycling_bytes(32 * 1024);
let compressed = compress_to_frame(&original);
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn write_frame_returns_inner_writer() {
let data = b"test data";
let result: Vec<u8> = lz4_write_frame(data, Vec::new()).expect("should succeed");
assert!(
!result.is_empty(),
"returned Vec must contain the LZ4 frame bytes"
);
}
#[test]
fn write_file_open_default_prefs() {
let mut buf = Vec::new();
{
let writer = Lz4WriteFile::open(&mut buf, None).expect("open should succeed");
writer.finish().expect("finish should succeed");
}
assert!(
buf.len() >= 7,
"frame must contain at least header + end-mark"
);
let magic = u32::from_le_bytes(buf[0..4].try_into().unwrap());
assert_eq!(magic, 0x184D2204, "frame must start with LZ4 magic number");
}
#[test]
fn write_file_open_with_preferences() {
let prefs = Preferences {
frame_info: FrameInfo {
block_size_id: BlockSizeId::Max256Kb,
content_checksum_flag: ContentChecksum::Enabled,
..FrameInfo::default()
},
..Preferences::default()
};
let compressed = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), Some(&prefs)).expect("open");
lz4w.write_all(b"hello preferences").unwrap();
lz4w.finish().expect("finish")
};
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, b"hello preferences");
}
#[test]
fn write_file_chunked_writes() {
let original: Vec<u8> = cycling_bytes(8 * 1024);
let chunked = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open");
for chunk in original.chunks(256) {
lz4w.write_all(chunk).unwrap();
}
lz4w.finish().expect("finish")
};
let one_shot = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open");
lz4w.write_all(&original).unwrap();
lz4w.finish().expect("finish")
};
assert_eq!(decompress_frame(&chunked), original);
assert_eq!(decompress_frame(&one_shot), original);
}
#[test]
fn write_file_write_returns_buf_len() {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open");
let data = b"some data here";
let written = lz4w.write(data).expect("write");
assert_eq!(written, data.len());
lz4w.finish().expect("finish");
}
#[test]
fn write_file_empty_write() {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open");
let written = lz4w.write(b"").expect("write empty");
assert_eq!(written, 0);
lz4w.finish().expect("finish");
}
#[test]
fn write_file_multiple_finish_not_called_drop_finalizes() {
let mut buf = Vec::new();
{
let mut lz4w = Lz4WriteFile::open(&mut buf, None).expect("open");
lz4w.write_all(b"dropped without finish").unwrap();
}
let recovered = decompress_frame(&buf);
assert_eq!(recovered, b"dropped without finish");
}
#[test]
fn write_file_finish_takes_inner_writer() {
let data = b"finish returns inner";
let inner: Vec<u8> = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open");
lz4w.write_all(data).unwrap();
lz4w.finish().expect("finish")
};
let recovered = decompress_frame(&inner);
assert_eq!(recovered, data);
}
#[test]
fn read_file_open_valid_frame() {
let compressed = compress_to_frame(b"valid frame");
let _reader = Lz4ReadFile::open(Cursor::new(compressed)).expect("open should succeed");
}
#[test]
fn read_file_open_empty_input_fails() {
let result = Lz4ReadFile::open(Cursor::new(b""));
assert!(result.is_err(), "open on empty input must fail");
}
#[test]
fn read_file_open_corrupt_magic_fails() {
let corrupt = vec![
0xDEu8, 0xAD, 0xBE, 0xEF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
];
let result = Lz4ReadFile::open(Cursor::new(corrupt));
assert!(result.is_err(), "open on corrupt magic must fail");
}
#[test]
fn read_file_read_full_small() {
let original = b"small read test";
let compressed = compress_to_frame(original);
let mut lz4r = Lz4ReadFile::open(Cursor::new(compressed)).expect("open");
let mut out = vec![0u8; 1024];
let n = lz4r.read(&mut out).unwrap();
assert_eq!(&out[..n], original);
}
#[test]
fn read_file_read_chunks_reassemble_correctly() {
let original: Vec<u8> = cycling_bytes(16 * 1024);
let compressed = compress_to_frame(&original);
let mut lz4r = Lz4ReadFile::open(Cursor::new(compressed)).expect("open");
let mut recovered = Vec::new();
let mut tmp = [0u8; 512];
loop {
let n = lz4r.read(&mut tmp).unwrap();
if n == 0 {
break;
}
recovered.extend_from_slice(&tmp[..n]);
}
assert_eq!(recovered, original);
}
#[test]
fn read_file_returns_zero_at_eof() {
let original = b"eof test";
let compressed = compress_to_frame(original);
let mut lz4r = Lz4ReadFile::open(Cursor::new(compressed)).expect("open");
let mut out = vec![0u8; 4096];
loop {
let n = lz4r.read(&mut out).unwrap();
if n == 0 {
break;
}
}
let n = lz4r.read(&mut out).unwrap();
assert_eq!(n, 0);
}
#[test]
fn read_file_multi_block_frame() {
let original: Vec<u8> = cycling_bytes(150 * 1024);
let compressed = compress_to_frame(&original);
let mut lz4r = Lz4ReadFile::open(Cursor::new(compressed)).expect("open");
let mut recovered = Vec::new();
let mut tmp = [0u8; 65536];
loop {
let n = lz4r.read(&mut tmp).unwrap();
if n == 0 {
break;
}
recovered.extend_from_slice(&tmp[..n]);
}
assert_eq!(recovered, original);
}
#[test]
fn streaming_write_then_streaming_read() {
let original: Vec<u8> = b"streaming parity test"
.iter()
.cycle()
.take(32 * 1024)
.cloned()
.collect();
let compressed = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), None).expect("open writer");
for chunk in original.chunks(1024) {
lz4w.write_all(chunk).unwrap();
}
lz4w.finish().expect("finish")
};
let mut lz4r = Lz4ReadFile::open(Cursor::new(&compressed)).expect("open reader");
let mut recovered = Vec::new();
let mut tmp = [0u8; 3 * 1024];
loop {
let n = lz4r.read(&mut tmp).unwrap();
if n == 0 {
break;
}
recovered.extend_from_slice(&tmp[..n]);
}
assert_eq!(recovered, original);
}
#[test]
fn round_trip_with_content_checksum() {
let prefs = Preferences {
frame_info: FrameInfo {
content_checksum_flag: ContentChecksum::Enabled,
..FrameInfo::default()
},
..Preferences::default()
};
let original = b"checksum round trip";
let compressed = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), Some(&prefs)).expect("open");
lz4w.write_all(original).unwrap();
lz4w.finish().expect("finish")
};
let recovered = decompress_frame(&compressed);
assert_eq!(recovered.as_slice(), original.as_ref());
}
#[test]
fn round_trip_max1mb_block_size() {
let prefs = Preferences {
frame_info: FrameInfo {
block_size_id: BlockSizeId::Max1Mb,
..FrameInfo::default()
},
..Preferences::default()
};
let original: Vec<u8> = cycling_bytes(2 * 1024 * 1024);
let compressed = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), Some(&prefs)).expect("open");
lz4w.write_all(&original).unwrap();
lz4w.finish().expect("finish")
};
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn round_trip_max256kb_block_size() {
let prefs = Preferences {
frame_info: FrameInfo {
block_size_id: BlockSizeId::Max256Kb,
..FrameInfo::default()
},
..Preferences::default()
};
let original: Vec<u8> = cycling_bytes(500 * 1024);
let compressed = {
let mut lz4w = Lz4WriteFile::open(Vec::new(), Some(&prefs)).expect("open");
lz4w.write_all(&original).unwrap();
lz4w.finish().expect("finish")
};
let recovered = decompress_frame(&compressed);
assert_eq!(recovered, original);
}
#[test]
fn compressed_frame_starts_with_lz4_magic() {
let compressed = compress_to_frame(b"magic check");
assert!(compressed.len() >= 4);
let magic = u32::from_le_bytes(compressed[0..4].try_into().unwrap());
assert_eq!(magic, 0x184D2204u32);
}
#[test]
fn compressed_output_smaller_than_repetitive_input() {
let original: Vec<u8> = vec![b'A'; 64 * 1024];
let compressed = compress_to_frame(&original);
assert!(
compressed.len() < original.len(),
"compressed ({} bytes) should be < original ({} bytes) for repetitive data",
compressed.len(),
original.len()
);
}