From b944bee72d68d83d0bc4e967d880a6c5086f302c Mon Sep 17 00:00:00 2001 From: Jiahao XU Date: Fri, 22 Jul 2022 19:08:12 +1000 Subject: [PATCH] Add unit tests for mod `read::streams` Signed-off-by: Jiahao XU --- src/read/stream.rs | 201 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) diff --git a/src/read/stream.rs b/src/read/stream.rs index 44208a7e..0ed8d1f0 100644 --- a/src/read/stream.rs +++ b/src/read/stream.rs @@ -210,3 +210,204 @@ impl ZipStreamFileMetadata { self.0.unix_mode() } } + +#[cfg(test)] +mod test { + use super::*; + use std::collections::BTreeMap; + use std::io; + + struct DummyVisitor; + impl ZipStreamVisitor for DummyVisitor { + fn visit_file(&mut self, _file: &mut ZipFile<'_>) -> ZipResult<()> { + Ok(()) + } + + fn visit_additional_metadata( + &mut self, + _metadata: &ZipStreamFileMetadata, + ) -> ZipResult<()> { + Ok(()) + } + } + + #[derive(Default, Debug, Eq, PartialEq)] + struct CounterVisitor(u64, u64); + impl ZipStreamVisitor for CounterVisitor { + fn visit_file(&mut self, _file: &mut ZipFile<'_>) -> ZipResult<()> { + self.0 += 1; + Ok(()) + } + + fn visit_additional_metadata( + &mut self, + _metadata: &ZipStreamFileMetadata, + ) -> ZipResult<()> { + self.1 += 1; + Ok(()) + } + } + + #[test] + fn invalid_offset() { + ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/invalid_offset.zip" + ))) + .visit(&mut DummyVisitor) + .unwrap_err(); + } + + #[test] + fn invalid_offset2() { + ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/invalid_offset2.zip" + ))) + .visit(&mut DummyVisitor) + .unwrap_err(); + } + + #[test] + fn zip64_with_leading_junk() { + let reader = ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/zip64_demo.zip" + ))); + let mut v = CounterVisitor::default(); + reader.visit(&mut v).unwrap(); + + assert_eq!(v, CounterVisitor(1, 1)); + } + + #[test] + fn zip_contents() { + let reader = ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/mimetype.zip" + ))); + + struct V(bool); + impl ZipStreamVisitor for V { + fn visit_file(&mut self, file: &mut ZipFile<'_>) -> ZipResult<()> { + if !self.0 { + self.0 = true; + assert_eq!(file.central_header_start(), 77); + } + + Ok(()) + } + fn visit_additional_metadata( + &mut self, + _metadata: &ZipStreamFileMetadata, + ) -> ZipResult<()> { + Ok(()) + } + } + + reader.visit(&mut V(false)).unwrap(); + } + + #[test] + fn zip_read_streaming() { + let reader = ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/mimetype.zip" + ))); + + #[derive(Default)] + struct V { + filenames: BTreeMap, u8>, + } + impl ZipStreamVisitor for V { + fn visit_file(&mut self, file: &mut ZipFile<'_>) -> ZipResult<()> { + if file.is_file() { + self.filenames.insert(file.name().into(), 1); + } + + Ok(()) + } + fn visit_additional_metadata( + &mut self, + metadata: &ZipStreamFileMetadata, + ) -> ZipResult<()> { + if metadata.is_file() { + let v = self.filenames.get_mut(metadata.name()).unwrap(); + *v += 1; + } + + Ok(()) + } + } + + let mut visitor = V::default(); + reader.visit(&mut visitor).unwrap(); + for (filename, cnt) in visitor.filenames { + assert_eq!(cnt, 2, "{filename} does not have visit_additional_metadata"); + } + } + + #[test] + fn file_and_dir_predicates() { + let reader = ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/files_and_dirs.zip" + ))); + + #[derive(Default)] + struct V { + filenames: BTreeMap, u8>, + } + impl ZipStreamVisitor for V { + fn visit_file(&mut self, file: &mut ZipFile<'_>) -> ZipResult<()> { + let full_name = file.enclosed_name().unwrap(); + let file_name = full_name.file_name().unwrap().to_str().unwrap(); + assert!( + (file_name.starts_with("dir") && file.is_dir()) + || (file_name.starts_with("file") && file.is_file()) + ); + + if file.is_file() { + self.filenames.insert(file.name().into(), 1); + } + + Ok(()) + } + fn visit_additional_metadata( + &mut self, + metadata: &ZipStreamFileMetadata, + ) -> ZipResult<()> { + if metadata.is_file() { + let v = self.filenames.get_mut(metadata.name()).unwrap(); + *v += 1; + } + + Ok(()) + } + } + + let mut visitor = V::default(); + reader.visit(&mut visitor).unwrap(); + for (filename, cnt) in visitor.filenames { + assert_eq!(cnt, 2, "{filename} does not have visit_additional_metadata"); + } + } + + /// test case to ensure we don't preemptively over allocate based on the + /// declared number of files in the CDE of an invalid zip when the number of + /// files declared is more than the alleged offset in the CDE + #[test] + fn invalid_cde_number_of_files_allocation_smaller_offset() { + ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/invalid_cde_number_of_files_allocation_smaller_offset.zip" + ))) + .visit(&mut DummyVisitor) + .unwrap_err(); + } + + /// test case to ensure we don't preemptively over allocate based on the + /// declared number of files in the CDE of an invalid zip when the number of + /// files declared is less than the alleged offset in the CDE + #[test] + fn invalid_cde_number_of_files_allocation_greater_offset() { + ZipStreamReader::new(io::Cursor::new(include_bytes!( + "../../tests/data/invalid_cde_number_of_files_allocation_greater_offset.zip" + ))) + .visit(&mut DummyVisitor) + .unwrap_err(); + } +}