Files
effectcraft/crates/ui-egui/tests/it/ui_export_overwrite.rs
T

111 lines
4.7 KiB
Rust

//! The control channel's `render.frame` and `ui.screenshot` write a PNG to the `path` they are
//! given, and never over a file the project reads (#1743): footage, a proxy or the project file.
use std::path::PathBuf;
use effectcraft_engine::Session;
use effectcraft_engine::color::Label;
use effectcraft_engine::project::{Footage, FootageKind, ItemKind};
use effectcraft_ui_egui::EffectcraftApp;
use effectcraft_ui_egui::control::{self, ControlRequest, Outcome};
use egui_kittest::Harness;
use serde_json::{Value, json};
const SOURCE: &[u8] = b"SOURCE FOOTAGE";
struct Rig {
h: Harness<'static, EffectcraftApp>,
dir: PathBuf,
}
impl Rig {
/// A project saved as `Project.ecproj` in a fresh folder, reading `clip.png` beside it.
fn new(name: &str) -> Rig {
let dir = std::env::temp_dir().join("effectcraft-ui-export-overwrite").join(format!("{}-{name}", std::process::id()));
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(dir.join("clip.png"), SOURCE).unwrap();
let mut s = Session::default();
s.execute("comp.new", json!({"name": "Main", "width": 16, "height": 16, "frameRate": 10, "duration": 0.5})).unwrap();
s.execute("file.saveAs", json!({"path": dir.join("Project.ecproj").to_string_lossy()})).unwrap();
let clip = Footage {
path: dir.join("clip.png").to_string_lossy().into_owned(),
kind: FootageKind::Still,
width: 16,
height: 16,
has_video: true,
..Default::default()
};
std::sync::Arc::make_mut(&mut s.project).add_item("clip.png", Label::Aqua, None, ItemKind::Footage(clip));
let mut app = Some(EffectcraftApp::new(s));
let mut h = Harness::builder().with_size(egui::vec2(1600.0, 1000.0)).build_eframe(|_| app.take().expect("app"));
h.run_steps(2);
Rig { h, dir }
}
fn text(&self, file: &str) -> String {
self.dir.join(file).to_string_lossy().into_owned()
}
fn read(&self, file: &str) -> Vec<u8> {
std::fs::read(self.dir.join(file)).unwrap()
}
/// A control-channel request, answered at once or not.
fn send(&mut self, method: &str, params: Value) -> Outcome {
let ctx = self.h.ctx.clone();
let (req, _rx) = ControlRequest::new(method, params);
let now = self.h.state().now();
control::handle(self.h.state_mut(), &ctx, &req, now)
}
/// The reply of a request that is answered at once.
fn reply(&mut self, method: &str, params: Value) -> Value {
match self.send(method, params) {
Outcome::Done(v) => v,
_ => panic!("{method}: no immediate reply"),
}
}
}
impl Drop for Rig {
fn drop(&mut self) {
let _ = std::fs::remove_dir_all(&self.dir);
}
}
#[test]
fn render_frame_never_writes_over_a_file_the_project_reads() {
let mut r = Rig::new("render-frame");
let project = r.read("Project.ecproj");
for (file, what) in [("clip.png", "footage"), ("Project.ecproj", "project")] {
let v = r.reply("render.frame", json!({"path": r.text(file)}));
let error = v["error"].as_str().unwrap_or_default();
assert!(v["ok"] == false && error.contains(file) && error.contains(what), "{file}: {v}");
}
assert_eq!(r.read("clip.png"), SOURCE, "the footage is as it was");
assert_eq!(r.read("Project.ecproj"), project, "the project file is as it was");
// Anywhere else it writes the PNG.
let v = r.reply("render.frame", json!({"path": r.text("frame.png")}));
assert_eq!(v["ok"], true, "{v}");
assert_eq!(&r.read("frame.png")[..8], b"\x89PNG\r\n\x1a\n");
let v = r.reply("render.frame", json!({"path": r.text("inline.png"), "base64": true, "write": true}));
assert_eq!(v["ok"], true, "{v}");
assert_eq!(v["result"]["png"].as_str(), Some(control::base64(&r.read("inline.png")).as_str()));
}
#[test]
fn screenshot_never_writes_over_a_file_the_project_reads() {
let mut r = Rig::new("screenshot");
let project = r.read("Project.ecproj");
for (file, what) in [("clip.png", "footage"), ("Project.ecproj", "project")] {
let Outcome::Done(v) = r.send("ui.screenshot", json!({"path": r.text(file)})) else { panic!("{file}: the screenshot was taken for it") };
let error = v["error"].as_str().unwrap_or_default();
assert!(v["ok"] == false && error.contains(file) && error.contains(what), "{file}: {v}");
}
assert_eq!(r.read("clip.png"), SOURCE, "the footage is as it was");
assert_eq!(r.read("Project.ecproj"), project, "the project file is as it was");
// Anywhere else it is taken (the frame that carries it comes later).
assert!(matches!(r.send("ui.screenshot", json!({"path": r.text("shot.png")})), Outcome::Screenshot { .. }));
}