use cfb::{CompoundFile, Version};
use std::io::{Cursor, Read, Seek, SeekFrom, Write};
fn pattern(len: usize, seed: u8) -> Vec<u8> {
(0..len).map(|i| (i as u8).wrapping_mul(31).wrapping_add(seed)).collect()
}
#[test]
fn multi_sector_writes_round_trip() {
let mut comp = CompoundFile::create_with_version(
Version::V3,
Cursor::new(Vec::new()),
)
.unwrap();
let a = pattern(10 * 1024, 1);
let b = pattern(10 * 1024 + 300, 2);
comp.create_stream("/a").unwrap().write_all(&a).unwrap();
comp.create_stream("/b").unwrap().write_all(&b).unwrap();
comp.remove_stream("/a").unwrap();
let c = pattern(15 * 1024 + 17, 3);
comp.create_stream("/c").unwrap().write_all(&c).unwrap();
let tail = pattern(3000, 4);
{
let mut stream = comp.open_stream("/b").unwrap();
stream.seek(SeekFrom::End(0)).unwrap();
stream.write_all(&tail).unwrap();
}
let bytes = comp.into_inner().into_inner();
assert_eq!(bytes.len() % 512, 0, "file is whole sectors");
let mut comp = CompoundFile::open_strict(Cursor::new(bytes)).unwrap();
let mut read = |path: &str| {
let mut data = Vec::new();
comp.open_stream(path).unwrap().read_to_end(&mut data).unwrap();
data
};
assert_eq!(read("/c"), c);
let mut b_expected = b.clone();
b_expected.extend_from_slice(&tail);
assert_eq!(read("/b"), b_expected);
}
#[test]
fn chains_that_outgrow_a_fat_sector_round_trip() {
let mut comp = CompoundFile::create_with_version(
Version::V3,
Cursor::new(Vec::new()),
)
.unwrap();
let data = pattern(600 * 512 + 100, 5);
comp.create_stream("/big").unwrap().write_all(&data).unwrap();
let small = pattern(5000, 6);
comp.create_stream("/small").unwrap().write_all(&small).unwrap();
let bytes = comp.into_inner().into_inner();
let mut comp = CompoundFile::open_strict(Cursor::new(bytes)).unwrap();
let mut back = Vec::new();
comp.open_stream("/big").unwrap().read_to_end(&mut back).unwrap();
assert_eq!(back, data);
back.clear();
comp.open_stream("/small").unwrap().read_to_end(&mut back).unwrap();
assert_eq!(back, small);
}
#[test]
fn unused_tail_of_last_sector_is_zero() {
let mut comp = CompoundFile::create_with_version(
Version::V3,
Cursor::new(Vec::new()),
)
.unwrap();
let data = vec![0xAB; 4096 + 100];
comp.create_stream("/s").unwrap().write_all(&data).unwrap();
let start_sector = {
let bytes = comp.into_inner().into_inner();
let start = bytes.windows(4).position(|w| w == [0xAB; 4]).unwrap();
assert_eq!(start % 512, 0);
(start, bytes)
};
let (start, bytes) = start_sector;
let end = start + data.len();
assert!(bytes[start..end].iter().all(|&b| b == 0xAB));
let sector_end = end.div_ceil(512) * 512;
assert!(bytes[end..sector_end].iter().all(|&b| b == 0));
}
#[test]
fn set_len_growth_is_zero_filled() {
let mut comp = CompoundFile::create(Cursor::new(Vec::new())).unwrap();
{
let mut stream = comp.create_stream("/s").unwrap();
stream.write_all(&vec![0xCD; 5000]).unwrap();
stream.set_len(20000).unwrap();
}
let mut back = Vec::new();
comp.open_stream("/s").unwrap().read_to_end(&mut back).unwrap();
assert_eq!(back.len(), 20000);
assert!(back[..5000].iter().all(|&b| b == 0xCD));
assert!(back[5000..].iter().all(|&b| b == 0));
}
#[test]
fn reads_across_sector_runs() {
let mut comp = CompoundFile::create_with_version(
Version::V3,
Cursor::new(Vec::new()),
)
.unwrap();
let a = pattern(2048, 7);
comp.create_stream("/a").unwrap().write_all(&a).unwrap();
comp.create_stream("/b").unwrap().write_all(&[1; 512]).unwrap();
{
let mut stream = comp.open_stream("/a").unwrap();
stream.seek(SeekFrom::End(0)).unwrap();
stream.write_all(&pattern(1024, 8)).unwrap();
}
let mut expected = a.clone();
expected.extend_from_slice(&pattern(1024, 8));
let mut stream = comp.open_stream("/a").unwrap();
let mut back = vec![0; expected.len()];
stream.read_exact(&mut back).unwrap();
assert_eq!(back, expected);
stream.seek(SeekFrom::Start(700)).unwrap();
let mut piece = vec![0; 2000];
stream.read_exact(&mut piece).unwrap();
assert_eq!(piece, &expected[700..2700]);
}