diff --git a/tests/test.rs b/tests/test.rs index 34ba6a8..0e9bdd2 100644 --- a/tests/test.rs +++ b/tests/test.rs @@ -1,4 +1,84 @@ +use byteorder::{ReadBytesExt, WriteBytesExt}; use paste::paste; +use std::io::{self, Cursor, Read, Seek, SeekFrom}; + +/// A reader that tracks how many times bytes in the inner reader been read. Useful to check read coverage. +#[derive(Debug)] +pub struct ReadCounter { + inner: T, + reads: io::Cursor>, +} + +impl ReadCounter { + pub fn new(inner: T) -> Self { + ReadCounter { + inner, + reads: Cursor::new(vec![]), + } + } + pub fn new_size(inner: T, size: usize) -> Self { + ReadCounter { + inner, + reads: Cursor::new(vec![0; size]), + } + } + pub fn into_reads(self) -> Vec { + self.reads.into_inner() + } +} + +impl Seek for ReadCounter +where + T: Seek, +{ + fn seek(&mut self, style: SeekFrom) -> io::Result { + self.reads.seek(style).unwrap(); + self.inner.seek(style) + } +} + +impl Read for ReadCounter +where + T: Read, +{ + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let read = self.inner.read(buf); + if let Ok(read) = read { + for _ in 0..read { + let r = match self.reads.read_u8() { + Ok(r) => { + self.reads.seek(SeekFrom::Current(-1)).unwrap(); + Ok(r) + } + Err(ref e) if e.kind() == io::ErrorKind::UnexpectedEof => Ok(0), + Err(e) => Err(e), + } + .unwrap(); + self.reads.write_u8(r + 1).unwrap(); + } + } + read + } +} + +mod test { + #[test] + fn test_read_counter() { + use byteorder::{ReadBytesExt, LE}; + use std::io::{Cursor, Seek, SeekFrom}; + + let source = Cursor::new(vec![1, 2, 3, 4, 5, 6, 7, 8, 9]); + let mut proxy = super::ReadCounter::new(source); + + proxy.seek(SeekFrom::Start(3)).unwrap(); + proxy.read_u8().unwrap(); + proxy.seek(SeekFrom::Current(-1)).unwrap(); + proxy.read_u8().unwrap(); + proxy.read_u16::().unwrap(); + + assert_eq!(proxy.reads.into_inner(), vec![0, 0, 0, 2, 1, 1]); + } +} static AES_KEY: &str = "lNJbw660IOC+kU7cnVQ1oeqrXyhk4J6UAZrCBbcnp94="; @@ -35,10 +115,16 @@ macro_rules! encryptindex { .and_then(|bytes| { aes::Aes256Dec::new_from_slice(bytes).map_err(|_| unpak::Error::Aes) }).unwrap(); + + + let mut inner_reader = std::io::Cursor::new(include_bytes!(concat!("packs/pack_", $version, $compress, $encrypt, $encryptindex, ".pak"))); + let len = inner_reader.seek(SeekFrom::End(0)).unwrap(); + let mut pak = unpak::PakReader::new_any( - std::io::Cursor::new(include_bytes!(concat!("packs/pack_", $version, $compress, $encrypt, $encryptindex, ".pak"))), + ReadCounter::new_size(inner_reader, len as usize), Some(key), ).unwrap(); + assert_eq!(pak.mount_point(), "../mount/point/root/"); assert_eq!(pak.version(), $exp_version); use std::collections::HashSet; @@ -57,6 +143,13 @@ macro_rules! encryptindex { name => panic!("unrecognized file {}", name) } } + + for r in pak.into_reader().into_reads() { + // sanity check. a pak file can be constructed with a lot of dead space + // which wouldn't have to be read, but so far all bytes in paks generated + // by UnrealPak are meaningful + assert!(r > 0, "every byte has been read at least once"); + } } } )*