diff --git a/crates/engine/src/tests/python_files.rs b/crates/engine/src/tests/python_files.rs index 6870664f2..5f6d3343c 100644 --- a/crates/engine/src/tests/python_files.rs +++ b/crates/engine/src/tests/python_files.rs @@ -243,6 +243,55 @@ fn a_py_formula_naming_a_missing_script_keeps_only_its_value() { assert_eq!(wb.sheet(0).unwrap().cell(CellRef::parse("A1").unwrap()).unwrap().py.as_ref().map(|p| p.code.as_str()), Some("5")); } +/// `bytes` with the part `name` stored uncompressed and one byte of its data changed, so the +/// archive is intact but reading the part fails its checksum. +fn with_damaged_part(bytes: &[u8], name: &str, marker: &str) -> Vec { + let mut z = zip::ZipArchive::new(std::io::Cursor::new(bytes)).unwrap(); + let mut out = zip::ZipWriter::new(std::io::Cursor::new(Vec::new())); + for i in 0..z.len() { + let mut f = z.by_index(i).unwrap(); + let entry = f.name().to_string(); + let mut data = Vec::new(); + f.read_to_end(&mut data).unwrap(); + let options = zip::write::SimpleFileOptions::default(); + let options = if entry == name { options.compression_method(zip::CompressionMethod::Stored) } else { options }; + out.start_file(entry, options).unwrap(); + out.write_all(&data).unwrap(); + } + let mut zipped = out.finish().unwrap().into_inner(); + let at = zipped.windows(marker.len()).position(|w| w == marker.as_bytes()).expect("the marker is stored as it is"); + zipped[at + 1] ^= 0x20; + zipped +} + +#[test] +fn a_damaged_python_part_is_a_warning_and_the_workbook_opens_with_its_values() { + let data = "_xlfn._xlws.PY(0,0)59"; + let good = excel_shaped(data, Some(&["5"])); + // The part's data fails its checksum (a damaged archive entry). + let damaged = with_damaged_part(&good, "xl/python/python1.xml", ""); + // The part is not well-formed XML, before its first script. + let broken = |at: &'static str, with: &'static str| { + with_parts(&good, move |name, text| if name == "xl/python/python1.xml" { text.replace(at, with) } else { text.to_string() }, &[]) + }; + let malformed = broken("", "