diff --git a/crates/engine/src/footage_check.rs b/crates/engine/src/footage_check.rs index f5db284e..423fd69f 100644 --- a/crates/engine/src/footage_check.rs +++ b/crates/engine/src/footage_check.rs @@ -210,7 +210,13 @@ mod tests { let mut paths: Vec = 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); diff --git a/crates/engine/src/rq_tests.rs b/crates/engine/src/rq_tests.rs index fe376668..ff845cbc 100644 --- a/crates/engine/src/rq_tests.rs +++ b/crates/engine/src/rq_tests.rs @@ -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::>(), - [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, .. }))); diff --git a/crates/engine/src/tests_stubs.rs b/crates/engine/src/tests_stubs.rs index 46d5d17f..edf77355 100644 --- a/crates/engine/src/tests_stubs.rs +++ b/crates/engine/src/tests_stubs.rs @@ -270,18 +270,26 @@ fn rq() -> (Session, Arc) { (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::>(), - [("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();