use super::support::{Descriptor, File, Input, finish, memory, zip};
use std::{
io::Write,
process::{Command, Stdio},
};
use structured_asic::{Limits, Session, zip::Archive};
fn python(script: &str, input: &[u8]) -> Vec<u8> {
let mut child = Command::new("python3")
.args(["-c", script])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("interop-tests requires Python3 with its standard zipfile/zlib modules");
let mut stdin = child.stdin.take().unwrap();
let output = std::thread::scope(|scope| {
let writer = scope.spawn(move || {
stdin.write_all(input).unwrap();
});
let result = child.wait_with_output().unwrap();
writer.join().unwrap();
result
});
assert!(
output.status.success(),
"Python ZIP oracle: {}",
String::from_utf8_lossy(&output.stderr)
);
output.stdout
}
#[test]
fn python_zipfile_independently_reads_literal_zip64_deflate_and_descriptor_frames() {
let large = b"independent raw DEFLATE blocks".repeat(3000);
for zip64 in [false, true] {
for descriptor in [Descriptor::None, Descriptor::Unsigned, Descriptor::Signed] {
let mut file = File::stored(b"document.bin", &large);
file.zip64 = zip64;
file.deflate = true;
file.descriptor = descriptor;
let built = zip(&[file, File::stored(b"empty", b"")]);
let output = python(
r"
import io, sys, zipfile
with zipfile.ZipFile(io.BytesIO(sys.stdin.buffer.read())) as archive:
for entry in archive.infolist():
data = archive.read(entry)
sys.stdout.buffer.write(len(data).to_bytes(8, 'little'))
sys.stdout.buffer.write(data)
",
&built.bytes,
);
let mut expected = Vec::new();
expected.extend_from_slice(&(large.len() as u64).to_le_bytes());
expected.extend_from_slice(&large);
expected.extend_from_slice(&0_u64.to_le_bytes());
assert_eq!(output, expected);
}
}
}
#[test]
fn common_reader_accepts_independent_streamed_zip64_with_dynamic_huffman_blocks() {
let payload = b"dynamic Huffman repetition and original UTF-8 bytes\n".repeat(3000);
let bytes = python(
r"
import io, sys, zipfile
class Forward(io.BytesIO):
def seek(self, *args):
raise OSError('nonseekable fixture')
output = Forward()
data = sys.stdin.buffer.read()
with zipfile.ZipFile(output, 'w', compression=zipfile.ZIP_DEFLATED) as archive:
with archive.open('document.bin', 'w', force_zip64=True) as entry:
for at in range(0, len(data), 71):
entry.write(data[at:at+71])
archive.writestr('empty', b'')
sys.stdout.buffer.write(output.getvalue())
",
&payload,
);
let input = Input::new(bytes, 11);
let work = memory();
let session = Session::new(Limits::default(), work.clone());
let archive = finish(Archive::read(&input, &session, &work)).unwrap();
assert_eq!(archive.len(), 2);
assert!(archive.faults(&work).unwrap().next().is_none());
let member = archive.member(0, &work).unwrap().unwrap();
assert_eq!(finish(member.read_bytes(&work)).unwrap().as_ref(), payload);
assert!(
finish(archive.member(1, &work).unwrap().unwrap().read_bytes(&work))
.unwrap()
.is_empty()
);
drop(archive);
assert_eq!(work.used(), 0);
}