use std::io::Cursor;
use riegeli::{
CompressionType, ReaderOptions, RecordReader, RecordWriter, RiegeliError, WriterOptions,
};
fn write_records(records: &[&[u8]], opts: WriterOptions) -> Vec<u8> {
let mut cursor = Cursor::new(Vec::<u8>::new());
{
let mut w = RecordWriter::new(&mut cursor, opts).expect("writer::new");
for rec in records {
w.write_record(rec).expect("write_record");
}
w.flush().expect("flush");
}
cursor.into_inner()
}
fn read_all(data: Vec<u8>) -> Vec<Vec<u8>> {
let mut reader =
RecordReader::new(Cursor::new(data), ReaderOptions::new()).expect("reader::new");
let mut records = Vec::new();
while let Some(rec) = reader.read_record().expect("read_record") {
records.push(rec);
}
records
}
const BLOCK_SIZE: u64 = 65536;
#[test]
fn initial_padding_aligns_empty_file() {
let data = write_records(&[], WriterOptions::new().initial_padding(BLOCK_SIZE));
assert_eq!(
data.len() as u64 % BLOCK_SIZE,
0,
"empty file with initial_padding({BLOCK_SIZE}) must have size % {BLOCK_SIZE} == 0, got {}",
data.len()
);
}
#[test]
fn initial_padding_aligns_small_file() {
let data = write_records(
&[b"hello", b"world"],
WriterOptions::new().initial_padding(BLOCK_SIZE),
);
assert_eq!(
data.len() as u64 % BLOCK_SIZE,
0,
"small file with initial_padding({BLOCK_SIZE}) must have size % {BLOCK_SIZE} == 0, got {}",
data.len()
);
}
#[test]
fn initial_padding_aligns_multi_block_file() {
let record: Vec<u8> = vec![0xAB; 1000];
let records: Vec<&[u8]> = (0..300).map(|_| record.as_slice()).collect();
let data = write_records(&records, WriterOptions::new().initial_padding(BLOCK_SIZE));
assert_eq!(
data.len() as u64 % BLOCK_SIZE,
0,
"multi-block file with initial_padding({BLOCK_SIZE}) must have size % {BLOCK_SIZE} == 0, got {}",
data.len()
);
let got = read_all(data);
assert_eq!(got.len(), 300);
for (i, rec) in got.iter().enumerate() {
assert_eq!(rec.as_slice(), record.as_slice(), "record {i} mismatch");
}
}
#[test]
fn concatenated_padded_files_readable() {
let records_a: &[&[u8]] = &[b"file_a_rec1", b"file_a_rec2", b"file_a_rec3"];
let records_b: &[&[u8]] = &[b"file_b_rec1", b"file_b_rec2"];
let opts = WriterOptions::new().initial_padding(BLOCK_SIZE);
let file_a = write_records(records_a, opts.clone());
let file_b = write_records(records_b, opts);
assert_eq!(
file_a.len() as u64 % BLOCK_SIZE,
0,
"file_a not block-aligned"
);
assert_eq!(
file_b.len() as u64 % BLOCK_SIZE,
0,
"file_b not block-aligned"
);
let mut combined = file_a;
combined.extend_from_slice(&file_b);
let got = read_all(combined);
let expected: Vec<&[u8]> = records_a.iter().chain(records_b.iter()).copied().collect();
assert_eq!(
got.len(),
expected.len(),
"concatenated file: expected {} records, got {}",
expected.len(),
got.len()
);
for (i, (expected_rec, got_rec)) in expected.iter().zip(got.iter()).enumerate() {
assert_eq!(
got_rec.as_slice(),
*expected_rec,
"record {i} mismatch in concatenated file"
);
}
}
#[test]
fn compression_type_try_from_unknown_returns_err() {
let unknown_bytes: &[u8] = &[0x01, 0x7f, 0xfe, 0xff, b'x', b'r'];
for &b in unknown_bytes {
let result = CompressionType::try_from(b);
assert!(
matches!(result, Err(RiegeliError::UnknownCompressionType(_))),
"expected UnknownCompressionType for byte {b:#04x}, got {result:?}"
);
}
}