#1244: test that a native save failing partway leaves the file as it was

This commit is contained in:
bflatastic 2026-10-12 10:42:13 +07:00
parent ecf361c9d7
commit e2f0aed1bf
1 changed files with 43 additions and 0 deletions

View File

@ -442,3 +442,46 @@ fn a_native_save_streams_the_bytes_the_encoder_makes() {
assert_eq!(e.to_string(), "disk full");
let _ = std::fs::remove_dir_all(&d);
}
/// A native save whose file write fails partway (a full disk) leaves the file as it was, and no
/// temporary file behind.
#[test]
fn a_save_that_fails_while_streaming_leaves_the_file_as_it_was() {
/// Takes `left` bytes, then fails as a full disk does.
struct FailAfter<'a> {
inner: &'a mut dyn std::io::Write,
left: usize,
}
impl std::io::Write for FailAfter<'_> {
fn write(&mut self, b: &[u8]) -> std::io::Result<usize> {
if self.left == 0 {
return Err(std::io::Error::other("disk full"));
}
let n = b.len().min(self.left);
self.left -= n;
self.inner.write(&b[..n])
}
fn flush(&mut self) -> std::io::Result<()> {
self.inner.flush()
}
}
let d = tmp_dir("stream-fail");
let mut s = session(2000.0, 2000.0, 1);
// Enough objects that the file is written in several pieces, the failure in the middle.
for i in 0..1500 {
s.execute("shape.rectangle", &json!({"x": i % 100 * 20, "y": i / 100 * 20, "width": 10, "height": 10})).unwrap();
}
for compress in [false, true] {
let path = d.join("a.vectorcraft").to_string_lossy().to_string();
s.execute("file.saveCopy", &json!({"path": path, "compress": compress})).unwrap();
let before = std::fs::read(&path).unwrap();
s.execute("shape.ellipse", &json!({"x": 0, "y": 0, "width": 50, "height": 50})).unwrap();
let plan = save_plan(&s, SaveMode::Copy, &json!({"path": path, "compress": compress})).unwrap();
let job = save_job(&mut s, plan).unwrap();
let e = job.write_streamed(&mut |p, fill| super::write_file_with(p, &mut |w| fill(&mut FailAfter { inner: w, left: 10_000 }))).unwrap_err();
assert!(e.to_string().contains("disk full") || e.to_string().contains("couldn't be encoded"), "{e}");
assert_eq!(std::fs::read(&path).unwrap(), before, "compress: {compress}");
assert_eq!(std::fs::read_dir(&d).unwrap().count(), 1, "no temporary file is left");
}
let _ = std::fs::remove_dir_all(&d);
}