diff --git a/crates/engine/src/cmd/layout/tests.rs b/crates/engine/src/cmd/layout/tests.rs index 75489a8a7..d4c82e17d 100644 --- a/crates/engine/src/cmd/layout/tests.rs +++ b/crates/engine/src/cmd/layout/tests.rs @@ -340,6 +340,16 @@ fn plot_path_extension_picks_the_format_like_export() { assert!(!dir.join("noext").exists()); let e = s.execute("plot", &json!({"path": path("y.png"), "device": "CADCraft PDF"})).unwrap_err().to_string(); assert!(e.contains("writes pdf") && e.contains("png"), "{e}"); + // A device that resolves to none, or a null/unknown `output`, names no format: the refusal stands. + for p in [ + json!({"path": path("x.txt"), "device": "None"}), + json!({"path": path("x.txt"), "output": null}), + json!({"path": path("x.txt"), "output": "bogus"}), + ] { + let e = s.execute("plot", &p).unwrap_err().to_string(); + assert!(e.contains("png") && e.contains("svg") && e.contains("pdf"), "{e}"); + assert!(!dir.join("x.txt").exists(), "nothing written under an unknown extension"); + } // An explicit output still decides for an odd name. assert_eq!(s.execute("plot", &json!({"path": path("z.txt"), "output": "pdf"})).unwrap()["format"], "pdf"); std::fs::remove_dir_all(&dir).ok(); diff --git a/crates/engine/src/cmd/plotters.rs b/crates/engine/src/cmd/plotters.rs index d9aed574f..a8ac31818 100644 --- a/crates/engine/src/cmd/plotters.rs +++ b/crates/engine/src/cmd/plotters.rs @@ -300,8 +300,8 @@ pub(crate) fn plot_options(s: &Session, p: &Value, cmd: &str) -> Result { // refuse it, as EXPORT does, unless `output` or a device says what to write. if cmd == "plot" && by_path.is_none() - && p.get("output").is_none() - && !given.is_some_and(|n| !n.is_empty()) + && !p.get("output").and_then(Value::as_str).is_some_and(|o| PlotOutput::parse(o).is_some()) + && !(dev.is_some() && given.is_some_and(|n| !n.is_empty())) && let Some(path) = str_param(p, "path") { return Err(bad(