mirror of
https://github.com/storytold/photocraft.git
synced 2026-11-14 14:12:44 +00:00
One unreadable tile used to block editing, saving and exporting every open document (spill::check_integrity was process-global). Add Tile/Surface/Document::scratch_health (free while no tile has failed) and Document::for_each_surface (ops' history walk now delegates to it). edit_with checks the document before and after, jobs the document they captured, import the result, save/export the document written; check_since is now generation-only so a failure or recovery during an operation refuses it once and a retry works. Caches check their own sources and purge once per new failure instead of every frame. Fault-injection tests use a duplicated handle (the Unix file has no name).
50 lines
2.2 KiB
Rust
50 lines
2.2 KiB
Rust
#![cfg(not(target_arch = "wasm32"))]
|
|
|
|
use photocraft_color::{ColorMode, PixelFormat, SampleType};
|
|
use photocraft_doc::{Document, Effect, Layer};
|
|
use photocraft_geom::{Rect, Size};
|
|
use photocraft_raster::spill;
|
|
use std::io::{Read, Seek, SeekFrom, Write};
|
|
|
|
#[test]
|
|
fn failed_effect_build_is_not_reused_after_original_pixels_recover() {
|
|
let dir = std::env::temp_dir().join(format!("photocraft-compose-scratch-retry-{}", std::process::id()));
|
|
spill::configure(Some(&dir), 1).unwrap();
|
|
let mut doc = Document::new("Shadow", Size::new(64, 64), ColorMode::Rgb, SampleType::U8);
|
|
let mut background = Layer::raster("Background", PixelFormat::RGBA8);
|
|
background.surface_mut().unwrap().fill_rect(Rect::from_xywh(0, 0, 64, 64), &[1.0; 4]);
|
|
let mut foreground = Layer::raster("Shadow", PixelFormat::RGBA8);
|
|
foreground.surface_mut().unwrap().fill_rect(Rect::new(16, 16, 48, 48), &[1.0, 0.0, 0.0, 1.0]);
|
|
foreground.effects.items.push(Effect::default_drop_shadow());
|
|
doc.layers.extend([background, foreground]);
|
|
let expected = photocraft_compose::flatten(&doc).px.clone();
|
|
// Keep the known-good bounds, but force a new effect-map build from the damaged source.
|
|
photocraft_compose::purge_effect_cache();
|
|
spill::settle();
|
|
assert_eq!(spill::stats().resident_bytes, 0);
|
|
let mut file = spill::scratch_handle_for_tests().unwrap();
|
|
file.seek(SeekFrom::Start(0)).unwrap();
|
|
let mut good = Vec::new();
|
|
file.read_to_end(&mut good).unwrap();
|
|
file.seek(SeekFrom::Start(0)).unwrap();
|
|
file.write_all(&vec![0; good.len()]).unwrap();
|
|
file.sync_all().unwrap();
|
|
drop(photocraft_compose::flatten(&doc));
|
|
assert!(spill::check_integrity().is_err());
|
|
|
|
file.seek(SeekFrom::Start(0)).unwrap();
|
|
file.write_all(&good).unwrap();
|
|
file.sync_all().unwrap();
|
|
for layer in &doc.layers {
|
|
for (_, tile) in layer.surface().unwrap().tiles() {
|
|
tile.try_bytes().unwrap();
|
|
}
|
|
}
|
|
assert!(spill::check_integrity().is_ok());
|
|
assert_eq!(photocraft_compose::flatten(&doc).px, expected);
|
|
photocraft_compose::purge_effect_cache();
|
|
drop((doc, file));
|
|
spill::configure(None, 0).unwrap();
|
|
let _ = std::fs::remove_dir_all(dir);
|
|
}
|