use std::io::Write;
use gwseq_io::source::{self, ByteSource};
struct Fixture {
path: std::path::PathBuf,
data: Vec<u8>,
}
impl Fixture {
fn new(tag: &str, len: usize) -> Self {
let mut state: u32 = 0x9E37_79B9;
let data: Vec<u8> = (0..len)
.map(|_| {
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
(state & 0xFF) as u8
})
.collect();
let mut path = std::env::temp_dir();
path.push(format!("gwseq-stack-{tag}-{}.bin", std::process::id()));
let mut file = std::fs::File::create(&path).unwrap();
file.write_all(&data).unwrap();
file.sync_all().unwrap();
Self { path, data }
}
fn as_str(&self) -> &str {
self.path.to_str().unwrap()
}
}
impl Drop for Fixture {
fn drop(&mut self) {
let _ = std::fs::remove_file(&self.path);
}
}
#[test]
fn every_range_matches_the_file_at_every_block_size() {
let fixture = Fixture::new("ranges", 300_000);
let on_disk = std::fs::read(&fixture.path).unwrap();
assert_eq!(on_disk, fixture.data);
for block_size in [1u64, 3, 512, 4096, 32_768, 1 << 20] {
let source = source::open(fixture.as_str(), Some(block_size), Some(8)).unwrap();
assert_eq!(source.len().unwrap(), on_disk.len() as u64);
let offsets = [
0u64,
1,
block_size.saturating_sub(1),
block_size,
block_size + 1,
block_size * 3 - 1,
150_000,
299_999,
300_000,
400_000,
];
for offset in offsets {
for len in [0usize, 1, 2, 511, 4097, 100_000] {
let got = source.read_at(offset, len).unwrap();
let start = (offset as usize).min(on_disk.len());
let end = (start + len).min(on_disk.len());
assert_eq!(
&got[..],
&on_disk[start..end],
"block_size={block_size} offset={offset} len={len}"
);
}
}
}
}
#[test]
fn reading_the_whole_file_in_one_call_matches() {
let fixture = Fixture::new("whole", 250_000);
let on_disk = std::fs::read(&fixture.path).unwrap();
for block_size in [1024u64, 32_768] {
let source = source::open(fixture.as_str(), Some(block_size), Some(2)).unwrap();
assert_eq!(&source.read_to_end(0).unwrap()[..], &on_disk[..]);
}
}
#[test]
fn a_default_open_uses_the_recommended_settings() {
let fixture = Fixture::new("defaults", 100_000);
let on_disk = std::fs::read(&fixture.path).unwrap();
let source = source::open(fixture.as_str(), None, None).unwrap();
assert_eq!(
&source.read_at(1234, 5678).unwrap()[..],
&on_disk[1234..6912]
);
}
#[test]
fn threads_reading_overlapping_ranges_all_get_the_right_bytes() {
let fixture = Fixture::new("threads", 1 << 18);
let on_disk = std::sync::Arc::new(std::fs::read(&fixture.path).unwrap());
let source = source::open(fixture.as_str(), Some(4096), Some(16)).unwrap();
let threads: Vec<_> = (0..8u64)
.map(|t| {
let source = source.clone();
let on_disk = on_disk.clone();
std::thread::spawn(move || {
for i in 0..400u64 {
let offset = (t * 4093 + i * 271) % 250_000;
let len = 1 + ((t + i) % 9000) as usize;
let got = source.read_at(offset, len).unwrap();
let end = (offset as usize + len).min(on_disk.len());
assert_eq!(&got[..], &on_disk[offset as usize..end]);
}
})
})
.collect();
for t in threads {
t.join().unwrap();
}
}