Merge pull request #703 from didpublishing/fix/windows-path-tests

Tests: six engine tests no longer assume the working directory's drive on Windows
This commit is contained in:
Brandon Thomas
2026-10-12 03:02:44 -04:00
3 changed files with 33 additions and 19 deletions
+7 -1
View File
@@ -210,7 +210,13 @@ mod tests {
let mut paths: Vec<String> =
o.project.items.values().filter_map(|i| if let ItemKind::Footage(f) = &i.kind { Some(f.path.clone()) } else { None }).collect();
paths.sort();
assert_eq!(paths, ["/nonexistent/ec/abs.png", clip.to_str().unwrap(), "media/ec-relative-gone.png"]);
// Windows joins the stored "media/..." onto the project's folder and leaves a path with mixed
// separators; `Path` equality compares components, so both spellings are the same file.
let expected = ["/nonexistent/ec/abs.png", clip.to_str().unwrap(), "media/ec-relative-gone.png"];
assert_eq!(paths.len(), expected.len(), "{paths:?}");
for (got, want) in paths.iter().zip(expected) {
assert_eq!(std::path::Path::new(got), std::path::Path::new(want), "{paths:?}");
}
assert!(!o.is_dirty(), "finding the files is no edit");
let r = o.execute("footage.check", json!({"wait": true})).unwrap();
assert_eq!(r["missing"], 2);
+5 -5
View File
@@ -67,11 +67,11 @@ fn add_configure_render() {
let r = s.execute("renderQueue.setOutputModule", json!({"item": id, "format": "png", "channels": "rgba"})).unwrap();
assert!(r["outputPath"].as_str().unwrap().ends_with("_[#####].png"), "{r}");
assert!(s.execute("renderQueue.setOutputModule", json!({"item": id, "format": "jpeg", "channels": "rgba"})).is_err());
let r = s.execute("renderQueue.setOutput", json!({"item": id, "path": "/tmp/rq/out.mov"})).unwrap();
let r = s.execute("renderQueue.setOutput", json!({"item": id, "path": crate::tests_stubs::tmp_out("rq/out.mov")})).unwrap();
assert_eq!(r["output"]["format"], "ProRes", "Output To with .mov switches to ProRes");
assert_eq!(std::path::Path::new(r["outputPath"].as_str().unwrap()), std::path::absolute("/tmp/rq/out.mov").unwrap());
assert_eq!(std::path::Path::new(r["outputPath"].as_str().unwrap()), std::path::PathBuf::from(crate::tests_stubs::tmp_out("rq/out.mov")));
// A second item that fails, a third that is unqueued.
let b = s.execute("renderQueue.add", json!({"format": "gif", "output": "/tmp/rq/fail.gif"})).unwrap();
let b = s.execute("renderQueue.add", json!({"format": "gif", "output": crate::tests_stubs::tmp_out("rq/fail.gif")})).unwrap();
let c = s.execute("renderQueue.add", json!({"output": "/tmp/rq/skip.mp4"})).unwrap();
s.execute("renderQueue.setRender", json!({"item": c["item"], "render": false})).unwrap();
assert_eq!(s.project.render_queue[2].status.label(), "Unqueued");
@@ -79,12 +79,12 @@ fn add_configure_render() {
assert_eq!(r["items"].as_array().unwrap().len(), 2);
assert_eq!(
ex.log.lock().unwrap().iter().map(std::path::PathBuf::from).collect::<Vec<_>>(),
[std::path::absolute("/tmp/rq/out.mov").unwrap(), std::path::absolute("/tmp/rq/fail.gif").unwrap()]
[std::path::PathBuf::from(crate::tests_stubs::tmp_out("rq/out.mov")), std::path::PathBuf::from(crate::tests_stubs::tmp_out("rq/fail.gif"))]
);
let q = &s.project.render_queue;
assert_eq!(q[0].status.label(), "Done");
assert!(q[0].started.is_some() && q[0].render_time.is_some());
assert_eq!(std::path::Path::new(q[0].last_output.as_deref().unwrap()), std::path::absolute("/tmp/rq/out.mov").unwrap());
assert_eq!(std::path::Path::new(q[0].last_output.as_deref().unwrap()), std::path::PathBuf::from(crate::tests_stubs::tmp_out("rq/out.mov")));
assert_eq!(q[1].status, RenderStatus::Failed("boom".into()));
assert_eq!(q[2].status.label(), "Unqueued");
assert!(s.drain_events().iter().any(|e| matches!(e, Event::Toast { error: true, .. })));
+21 -13
View File
@@ -270,18 +270,26 @@ fn rq() -> (Session, Arc<MockExporter>) {
(s, ex)
}
/// An output path that is absolute on every platform. `/tmp/...` has no drive letter on Windows,
/// where it means "the current drive" to the OS but resolves against the home folder's drive in
/// the render queue, so a test cannot know which drive to expect.
pub(crate) fn tmp_out(rel: &str) -> String {
let base = std::env::temp_dir().join("effectcraft-rq-tests");
rel.split('/').fold(base, |p, part| p.join(part)).to_string_lossy().into_owned()
}
#[test]
fn add_output_module_encodes_twice() {
let (mut s, ex) = rq();
assert!(!s.is_enabled("render.addOutputModule"));
s.execute("renderQueue.add", json!({"output": "/tmp/om/main.mp4"})).unwrap();
s.execute("renderQueue.add", json!({"output": tmp_out("om/main.mp4")})).unwrap();
let r = s.execute("render.addOutputModule", json!({"format": "png"})).unwrap();
assert_eq!(r["module"], 2);
assert_eq!(r["outputModules"], 2);
let extra = r["extraOutputs"][0]["outputPath"].as_str().unwrap().to_string();
assert!(extra.ends_with(".png"), "{extra}");
// Module 2 is editable on its own.
s.execute("renderQueue.setOutput", json!({"index": 1, "module": 2, "path": "/tmp/om/second.mov"})).unwrap();
s.execute("renderQueue.setOutput", json!({"index": 1, "module": 2, "path": tmp_out("om/second.mov")})).unwrap();
assert!(s.execute("renderQueue.setOutput", json!({"index": 1, "module": 3, "path": "/tmp/x.mov"})).is_err());
s.execute("renderQueue.render", json!({})).unwrap();
assert_eq!(
@@ -294,10 +302,10 @@ fn add_output_module_encodes_twice() {
(format.to_string(), std::path::PathBuf::from(path))
})
.collect::<Vec<_>>(),
[("H264".to_string(), std::path::absolute("/tmp/om/main.mp4").unwrap()), ("ProRes".to_string(), std::path::absolute("/tmp/om/second.mov").unwrap())]
[("H264".to_string(), std::path::PathBuf::from(tmp_out("om/main.mp4"))), ("ProRes".to_string(), std::path::PathBuf::from(tmp_out("om/second.mov")))]
);
assert_eq!(s.project.render_queue[0].status.label(), "Done");
assert_eq!(std::path::Path::new(s.project.render_queue[0].last_output.as_deref().unwrap()), std::path::absolute("/tmp/om/main.mp4").unwrap());
assert_eq!(std::path::Path::new(s.project.render_queue[0].last_output.as_deref().unwrap()), std::path::PathBuf::from(tmp_out("om/main.mp4")));
s.execute("edit.undo", json!({})).unwrap();
assert_eq!(s.project.render_queue[0].extra_outputs.len(), 1, "undo the Output To edit");
}
@@ -311,7 +319,7 @@ fn pre_render_imports_and_replaces() {
let outer = s.active_comp_id().unwrap();
s.execute("layer.addItem", json!({"item": inner.0})).unwrap();
s.open_comp(inner);
let r = s.execute("render.preRender", json!({"output": "/tmp/pre/inner.mov"})).unwrap();
let r = s.execute("render.preRender", json!({"output": tmp_out("pre/inner.mov")})).unwrap();
assert_eq!(r["postRenderAction"], "Import & Replace Usage");
assert_eq!(s.project.render_queue[0].post_render, PostRenderAction::ImportAndReplace);
s.execute("renderQueue.render", json!({})).unwrap();
@@ -319,7 +327,7 @@ fn pre_render_imports_and_replaces() {
let l = &s.project.comp(outer).unwrap().layers[0];
let LayerSource::Footage { item } = l.source else { panic!("not replaced: {:?}", l.source) };
assert!(
matches!(&s.project.item(item).unwrap().kind, effectcraft_project::ItemKind::Footage(f) if std::path::Path::new(&f.path) == std::path::absolute("/tmp/pre/inner.mov").unwrap())
matches!(&s.project.item(item).unwrap().kind, effectcraft_project::ItemKind::Footage(f) if std::path::Path::new(&f.path) == std::path::PathBuf::from(tmp_out("pre/inner.mov")))
);
s.execute("edit.undo", json!({})).unwrap();
assert!(matches!(s.project.comp(outer).unwrap().layers[0].source, LayerSource::Comp { .. }));
@@ -331,23 +339,23 @@ fn post_render_actions_import_and_set_proxy() {
let cid = s.active_comp_id().unwrap();
let items_before = s.project.items.len();
// Import: the rendered file becomes a footage item.
let a = s.execute("renderQueue.add", json!({"output": "/tmp/post/a.mov"})).unwrap();
let a = s.execute("renderQueue.add", json!({"output": tmp_out("post/a.mov")})).unwrap();
s.execute("renderQueue.setOutputModule", json!({"item": a["item"], "postRenderAction": "import"})).unwrap();
assert_eq!(s.project.render_queue[0].post_render, PostRenderAction::Import);
s.execute("renderQueue.render", json!({})).unwrap();
assert_eq!(s.project.items.len(), items_before + 1);
assert!(s.project.items.values().any(
|i| matches!(&i.kind, effectcraft_project::ItemKind::Footage(f) if std::path::Path::new(&f.path) == std::path::absolute("/tmp/post/a.mov").unwrap())
|i| matches!(&i.kind, effectcraft_project::ItemKind::Footage(f) if std::path::Path::new(&f.path) == std::path::PathBuf::from(tmp_out("post/a.mov")))
));
// Set Proxy: the comp gets the render as its proxy.
let b = s.execute("renderQueue.add", json!({"output": "/tmp/post/b.mov"})).unwrap();
let b = s.execute("renderQueue.add", json!({"output": tmp_out("post/b.mov")})).unwrap();
s.execute("renderQueue.setOutputModule", json!({"item": b["item"], "postRenderAction": "Set Proxy"})).unwrap();
s.execute("renderQueue.render", json!({})).unwrap();
let px = s.project.item(cid).unwrap().proxy.clone().expect("proxy set");
assert_eq!(std::path::Path::new(&px.footage.path), std::path::absolute("/tmp/post/b.mov").unwrap());
assert_eq!(std::path::Path::new(&px.footage.path), std::path::PathBuf::from(tmp_out("post/b.mov")));
// None: nothing happens.
let n = s.project.items.len();
let c = s.execute("renderQueue.add", json!({"output": "/tmp/post/c.mov"})).unwrap();
let c = s.execute("renderQueue.add", json!({"output": tmp_out("post/c.mov")})).unwrap();
s.execute("renderQueue.setOutputModule", json!({"item": c["item"], "postRenderAction": "none"})).unwrap();
s.execute("renderQueue.render", json!({})).unwrap();
assert_eq!(s.project.items.len(), n);
@@ -426,9 +434,9 @@ fn save_frame_as_queues_with_the_frame_defaults() {
#[test]
fn log_notify_and_overflow_settings() {
let (mut s, _) = rq();
let a = s.execute("renderQueue.add", json!({"output": "/tmp/n/a.mp4", "log": "plusPerFrameInfo"})).unwrap();
let a = s.execute("renderQueue.add", json!({"output": tmp_out("n/a.mp4"), "log": "plusPerFrameInfo"})).unwrap();
assert_eq!(a["logLabel"], "Plus Per Frame Info");
assert_eq!(std::path::Path::new(a["logPath"].as_str().unwrap()), std::path::absolute("/tmp/n/a_RenderLog.txt").unwrap());
assert_eq!(std::path::Path::new(a["logPath"].as_str().unwrap()), std::path::PathBuf::from(tmp_out("n/a_RenderLog.txt")));
s.execute("renderQueue.setLog", json!({"item": a["item"], "log": "plusSettings"})).unwrap();
assert_eq!(s.project.render_queue[0].log, effectcraft_project::render_queue::RenderLog::PlusSettings);
s.execute("renderQueue.setOverflowFolders", json!({"folders": ["/tmp/o1", " ", "/tmp/o2"]})).unwrap();