mirror of
https://github.com/storytold/vectorcraft.git
synced 2026-11-14 20:36:02 +00:00
Tests: engine tests remove the temp folders they make
vectorcraft_testkit::temp_dir returns a bare path, so every engine test that wrote into one left the folder in the system temporary folder. - testkit: TempDir, a test's own folder made through temp_dir (same private creation and ownership checks) under a tag no other TempDir in the process uses, removed with its contents when dropped, also when the test panics. - Engine unit and integration tests hold a TempDir for the whole test instead of calling temp_dir: graph, fileio, compare, journal, library, shortcut set, spelling, store, web export, cancel, containers, build, color settings, command sweep, font folder, removed font, model-based and the font search fuzz. - The DCS and swatch library tests' own helpers and the PDF editing tests' shared vc-pdfedit-<pid> folder use a TempDir per test too. - The font and link file search, store, color settings and font folder helpers no longer delete the private folder temp_dir checked and make it again with default permissions.
This commit is contained in:
@@ -15,9 +15,8 @@ fn open(s: &mut Session, name: &str, bytes: &[u8]) -> Value {
|
||||
s.execute("document.open", &json!({"name": name, "dataBase64": vectorcraft_format::base64_encode(bytes)})).unwrap()
|
||||
}
|
||||
|
||||
fn tmp(name: &str) -> String {
|
||||
let dir = std::env::temp_dir().join(format!("vc-pdfedit-{}", std::process::id()));
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
/// The path of `name` in the test's folder `dir`.
|
||||
fn tmp(dir: &std::path::Path, name: &str) -> String {
|
||||
dir.join(name).to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
@@ -114,8 +113,9 @@ fn the_legacy_payload_name_is_accepted() {
|
||||
|
||||
#[test]
|
||||
fn ai_files_are_pdfs_that_save_and_reopen_editable() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
let path = tmp("art.ai");
|
||||
let path = tmp(&scratch, "art.ai");
|
||||
let r = s.execute("document.save", &json!({"path": path, "preserveEditing": false, "range": "1"})).unwrap();
|
||||
assert_eq!(r["path"], path.as_str());
|
||||
// As saved (Save stamps the File Info dates).
|
||||
@@ -134,11 +134,11 @@ fn ai_files_are_pdfs_that_save_and_reopen_editable() {
|
||||
let again = vectorcraft_pdf::editing(&std::fs::read(&path).unwrap()).unwrap();
|
||||
assert_eq!(vectorcraft_format::load(&again.data).unwrap().layers, s.doc().unwrap().doc.layers);
|
||||
// Save As also writes a plain PDF (the document then saves back as PDF); PNG stays an export.
|
||||
assert_eq!(s.execute("document.save", &json!({"path": tmp("x.pdf")})).unwrap()["format"], "pdf");
|
||||
assert!(s.execute("document.save", &json!({"path": tmp("x.png")})).is_err(), "PNG is written with document.export");
|
||||
assert_eq!(s.execute("document.save", &json!({"path": tmp(&scratch, "x.pdf")})).unwrap()["format"], "pdf");
|
||||
assert!(s.execute("document.save", &json!({"path": tmp(&scratch, "x.png")})).is_err(), "PNG is written with document.export");
|
||||
// A plain PDF named .ai opens as artwork, without a path to save over.
|
||||
let plain = b64(&s.execute("document.exportPdf", &json!({"preserveEditing": false})).unwrap());
|
||||
let plain_path = tmp("plain.ai");
|
||||
let plain_path = tmp(&scratch, "plain.ai");
|
||||
std::fs::write(&plain_path, plain).unwrap();
|
||||
let r = s.execute("document.open", &json!({"path": plain_path})).unwrap();
|
||||
assert_eq!(r["restored"], false);
|
||||
@@ -149,9 +149,10 @@ fn ai_files_are_pdfs_that_save_and_reopen_editable() {
|
||||
/// As does, leaving the document's own path alone.
|
||||
#[test]
|
||||
fn exports_write_ai_as_save_as_does() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
let before = comparable(&s.doc().unwrap().doc);
|
||||
let path = tmp("exported.ai");
|
||||
let path = tmp(&scratch, "exported.ai");
|
||||
let r = s.execute("document.export", &json!({"path": path})).unwrap();
|
||||
assert_eq!(r["format"], "ai", "{r}");
|
||||
let bytes = std::fs::read(&path).unwrap();
|
||||
@@ -225,12 +226,13 @@ fn ait_templates_open_their_document_untitled() {
|
||||
/// from Template opens it as an untitled copy of the document, layers and art included.
|
||||
#[test]
|
||||
fn save_as_template_writes_an_ait_that_new_from_template_opens_untitled() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
s.execute("layer.new", &json!({"name": "Notes"})).unwrap();
|
||||
s.execute("shape.ellipse", &json!({"x": 20, "y": 120, "width": 40, "height": 30})).unwrap();
|
||||
let before = comparable(&s.doc().unwrap().doc);
|
||||
assert_eq!(before.layers.len(), 2);
|
||||
let path = tmp("brochure template.ait");
|
||||
let path = tmp(&scratch, "brochure template.ait");
|
||||
let r = s.execute("file.saveAsTemplate", &json!({"path": path})).unwrap();
|
||||
assert_eq!(r["format"], "ait", "{r}");
|
||||
let bytes = std::fs::read(&path).unwrap();
|
||||
@@ -253,6 +255,7 @@ fn save_as_template_writes_an_ait_that_new_from_template_opens_untitled() {
|
||||
/// writes a `.ait`.
|
||||
#[test]
|
||||
fn save_as_template_names_its_file_after_the_template_format() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
for (name, format, saved) in [
|
||||
("card.png", None, "card.vctemplate"),
|
||||
@@ -261,7 +264,7 @@ fn save_as_template_names_its_file_after_the_template_format() {
|
||||
("card.pdf", Some("ait"), "card.ait"),
|
||||
("card v1.2", None, "card v1.2.vctemplate"),
|
||||
] {
|
||||
let (typed, want) = (tmp(&format!("named/{name}")), tmp(&format!("named/{saved}")));
|
||||
let (typed, want) = (tmp(&scratch, &format!("named/{name}")), tmp(&scratch, &format!("named/{saved}")));
|
||||
let _ = std::fs::create_dir_all(std::path::Path::new(&typed).parent().unwrap());
|
||||
let _ = std::fs::remove_file(&typed);
|
||||
let _ = std::fs::remove_file(&want);
|
||||
@@ -273,7 +276,7 @@ fn save_as_template_names_its_file_after_the_template_format() {
|
||||
assert!(ait || vectorcraft_format::load(&bytes).is_ok_and(|d| d.template), "{name}: a native template");
|
||||
assert!(!std::path::Path::new(&typed).exists(), "{name}: nothing written under the typed name");
|
||||
}
|
||||
for (cmd, p) in [("file.saveAs", json!({"format": "ait"})), ("document.save", json!({"path": tmp("card.ait")}))] {
|
||||
for (cmd, p) in [("file.saveAs", json!({"format": "ait"})), ("document.save", json!({"path": tmp(&scratch, "card.ait")}))] {
|
||||
let e = s.execute(cmd, &p).unwrap_err().to_string();
|
||||
assert!(e.contains("file.saveAsTemplate") && !e.contains("document.export"), "{cmd}: {e}");
|
||||
}
|
||||
@@ -284,8 +287,9 @@ fn save_as_template_names_its_file_after_the_template_format() {
|
||||
/// nothing writes keeps its own error.
|
||||
#[test]
|
||||
fn exports_of_an_ait_name_save_as_template() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
let path = tmp("exported.ait");
|
||||
let path = tmp(&scratch, "exported.ait");
|
||||
let _ = std::fs::remove_file(&path);
|
||||
for (cmd, p) in [
|
||||
("document.export", json!({"path": path})),
|
||||
@@ -305,8 +309,9 @@ fn exports_of_an_ait_name_save_as_template() {
|
||||
/// file; `aiVersion` "pdf" writes the PDF-compatible template.
|
||||
#[test]
|
||||
fn save_as_template_refuses_a_legacy_ait() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = rich();
|
||||
let path = tmp("legacy-ait/card.ait");
|
||||
let path = tmp(&scratch, "legacy-ait/card.ait");
|
||||
let dir = std::path::Path::new(&path).parent().unwrap().to_path_buf();
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
@@ -424,7 +429,8 @@ fn an_ai_of_the_arts_bounds_carries_no_editing_data() {
|
||||
let mut s = art_outside_the_artboards();
|
||||
let before = comparable(&s.doc().unwrap().doc);
|
||||
let left_out = |v: &Value| v["warnings"].as_array().unwrap().iter().any(|w| w == super::save::AI_EDITING_NEEDS_THE_ARTBOARDS);
|
||||
let path = tmp("art-bounds.ai");
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let path = tmp(&scratch, "art-bounds.ai");
|
||||
for extra in [json!({}), json!({"range": "2"}), json!({"editingData": false}), json!({"path": path})] {
|
||||
let mut params = json!({"format": "ai", "useArtboards": false});
|
||||
params.as_object_mut().unwrap().extend(extra.as_object().unwrap().clone());
|
||||
@@ -670,6 +676,7 @@ fn editing_copy(bytes: &[u8]) -> vectorcraft_doc::Document {
|
||||
/// writes the copy; the version 7 `.ai` keeps the compound path and the clipping group.
|
||||
#[test]
|
||||
fn transparency_clipping_and_opacity_masks_come_back_from_the_editing_copy() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = see_through_session();
|
||||
let before = (*s.doc().unwrap().doc).clone();
|
||||
// The mask's art is one path, and it reads back as that path.
|
||||
@@ -682,7 +689,7 @@ fn transparency_clipping_and_opacity_masks_come_back_from_the_editing_copy() {
|
||||
assert_eq!(see_through_outline(&editing_copy(&bytes)), want, "{opts}");
|
||||
}
|
||||
// Each artboard in a file of its own: the first holds all the art.
|
||||
let path = tmp("see-through.ai");
|
||||
let path = tmp(&scratch, "see-through.ai");
|
||||
let r = s.execute("document.save", &json!({"path": path, "separateArtboards": true})).unwrap();
|
||||
let files: Vec<String> = r["files"].as_array().unwrap().iter().map(|f| f.as_str().unwrap().to_string()).collect();
|
||||
assert_eq!(files.len(), 3, "{r}");
|
||||
@@ -812,7 +819,8 @@ fn image_outline(doc: &vectorcraft_doc::Document) -> Vec<String> {
|
||||
/// the linked image keeps its path and its path relative to the document's folder.
|
||||
#[test]
|
||||
fn images_come_back_from_the_editing_copy() {
|
||||
let dir = tmp("ai-images");
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let dir = tmp(&scratch, "ai-images");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(format!("{dir}/art")).unwrap();
|
||||
let png = |w: u32, h: u32, f: &dyn Fn(u32, u32) -> [u8; 4]| {
|
||||
@@ -882,6 +890,7 @@ fn type_outline(doc: &vectorcraft_doc::Document) -> Vec<String> {
|
||||
/// for other apps alone as type, with every save option that writes the copy.
|
||||
#[test]
|
||||
fn type_comes_back_from_the_editing_copy() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"width": 300, "height": 200})).unwrap();
|
||||
s.execute("text.create", &json!({"x": 20, "y": 40, "text": "Hello\nWorld", "size": 24})).unwrap();
|
||||
@@ -889,7 +898,7 @@ fn type_comes_back_from_the_editing_copy() {
|
||||
// The new type is selected.
|
||||
s.execute("object.rotate", &json!({"angle": 30})).unwrap();
|
||||
s.execute("text.create", &json!({"x": 20, "y": 120, "text": "Area type in a frame", "area": {"width": 120, "height": 60}})).unwrap();
|
||||
let path = tmp("type.ai");
|
||||
let path = tmp(&scratch, "type.ai");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
let before = type_outline(&s.doc().unwrap().doc);
|
||||
assert_eq!(before.len(), 3, "{before:#?}");
|
||||
@@ -971,6 +980,7 @@ fn gradient_session() -> Session {
|
||||
/// gradient.
|
||||
#[test]
|
||||
fn gradients_and_patterns_come_back_from_the_editing_copy() {
|
||||
let scratch = vectorcraft_testkit::TempDir::new("pdfedit");
|
||||
let mut s = gradient_session();
|
||||
let before = (*s.doc().unwrap().doc).clone();
|
||||
let want = paint_outline(&before);
|
||||
@@ -990,7 +1000,7 @@ fn gradients_and_patterns_come_back_from_the_editing_copy() {
|
||||
let copy = editing_copy(&b64(&r));
|
||||
assert_eq!(paint_outline(©), want, "{opts}");
|
||||
}
|
||||
let path = tmp("gradients.ai");
|
||||
let path = tmp(&scratch, "gradients.ai");
|
||||
let r = s.execute("document.save", &json!({"path": path, "separateArtboards": true})).unwrap();
|
||||
let files: Vec<String> = r["files"].as_array().unwrap().iter().map(|f| f.as_str().unwrap().to_string()).collect();
|
||||
assert_eq!(files.len(), 3, "{r}");
|
||||
|
||||
@@ -59,7 +59,7 @@ fn live_texts(s: &Session) -> Vec<String> {
|
||||
#[test]
|
||||
fn pdf_place_keeps_live_text_from_bytes_and_unlinked_paths() {
|
||||
let bytes = live_text_pdf();
|
||||
let dir = vectorcraft_testkit::temp_dir("pdf-place-live-text");
|
||||
let dir = vectorcraft_testkit::TempDir::new("pdf-place-live-text");
|
||||
let path = dir.join("text.pdf");
|
||||
std::fs::write(&path, &bytes).unwrap();
|
||||
for source in [json!({"name": "text.pdf", "dataBase64": vectorcraft_format::base64_encode(&bytes)}), json!({"path": path, "link": false})] {
|
||||
@@ -71,7 +71,6 @@ fn pdf_place_keeps_live_text_from_bytes_and_unlinked_paths() {
|
||||
assert_eq!(live_texts(&s), ["Second page"], "{r}");
|
||||
assert!(!r["warnings"].to_string().contains("outlines"), "{r}");
|
||||
}
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{EngineError, READ_ONLY_LOCATION, Session};
|
||||
|
||||
#[test]
|
||||
fn readonly_error_is_typed_and_other_writers_keep_their_error() {
|
||||
let dir = vectorcraft_testkit::temp_dir("save-refusal");
|
||||
let dir = vectorcraft_testkit::TempDir::new("save-refusal");
|
||||
let path = dir.join("Poster.vectorcraft").to_string_lossy().into_owned();
|
||||
for kind in [ErrorKind::PermissionDenied, ErrorKind::ReadOnlyFilesystem, ErrorKind::StorageFull, ErrorKind::NotFound, ErrorKind::Other] {
|
||||
let typed = write_save_file_with(&path, &mut |_| Err(io::Error::new(kind, "injected"))).unwrap_err();
|
||||
@@ -26,7 +26,6 @@ fn readonly_error_is_typed_and_other_writers_keep_their_error() {
|
||||
}
|
||||
assert!(!std::path::Path::new(&path).exists());
|
||||
}
|
||||
std::fs::remove_dir_all(dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1918,7 +1918,7 @@ fn series_colours_survive_native_save_open_and_export() {
|
||||
let second = group(s.doc().unwrap().doc.node(id).unwrap(), "2025").id;
|
||||
s.execute("paint.setFill", &json!({"ids": [first.0], "color": "#f00"})).unwrap();
|
||||
s.execute("paint.setFill", &json!({"ids": [second.0], "color": "#008080"})).unwrap();
|
||||
let dir = vectorcraft_testkit::temp_dir("graph-palette");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-palette");
|
||||
let path = dir.join("palette.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
@@ -2332,7 +2332,8 @@ fn series_types_survive_save_and_open_and_older_files_have_none() {
|
||||
s.execute("file.new", &json!({})).unwrap();
|
||||
let id = graph(&mut s, "column");
|
||||
s.execute("graph.setType", &json!({"seriesIndexes": [1], "type": "line"})).unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-kinds").join("kinds.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-kinds");
|
||||
let path = dir.join("kinds.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
assert_eq!(spec(&s, id).series_kinds, [None, Some(GraphKind::Line)]);
|
||||
@@ -2460,7 +2461,8 @@ fn value_axis_options_survive_save_and_open_and_are_left_out_when_unset() {
|
||||
let id = two_scale_graph(&mut s);
|
||||
s.execute("graph.setType", &json!({"valueAxis": "both", "separateScales": true, "rightAxisMin": 0, "rightAxisMax": 2000})).unwrap();
|
||||
s.execute("graph.setType", &json!({"seriesIndexes": [1], "valueAxis": "right"})).unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-axes").join("axes.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-axes");
|
||||
let path = dir.join("axes.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
let g = spec(&s, id);
|
||||
@@ -2662,7 +2664,8 @@ fn tick_and_label_options_survive_save_and_open_and_are_left_out_when_unset() {
|
||||
&json!({"tickLength": "full", "tickMarks": 3, "categoryTickLength": "short", "ticksBetweenLabels": true, "suffix": "%"}),
|
||||
)
|
||||
.unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-ticks").join("ticks.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-ticks");
|
||||
let path = dir.join("ticks.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
let g = spec(&s, id);
|
||||
@@ -2901,7 +2904,8 @@ fn designs_and_markers_survive_save_and_open() {
|
||||
save_design(&mut s, "Pill");
|
||||
let id = graph(&mut s, "line");
|
||||
s.execute("graph.marker", &json!({"seriesIndexes": [0], "design": "Pill"})).unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-designs").join("designs.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-designs");
|
||||
let path = dir.join("designs.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
assert_eq!(s.execute("graph.design", &json!({})).unwrap()["designs"], json!(["Pill"]));
|
||||
@@ -3299,7 +3303,8 @@ fn column_designs_survive_save_and_open_and_follow_their_design() {
|
||||
assert!(series_parts(&s, id, 1).iter().all(|n| matches!(n.kind, NodeKind::Path { .. })), "the line series");
|
||||
let v = s.execute("graph.column", &json!({"id": id.0, "seriesIndexes": [0]})).unwrap();
|
||||
assert_eq!(v, json!({"designs": ["Coin"], "design": "Coin", "columnType": "repeating", "each": 2.5, "fractions": "scale"}));
|
||||
let path = vectorcraft_testkit::temp_dir("graph-column-designs").join("columns.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-column-designs");
|
||||
let path = dir.join("columns.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
let options = ColumnDesign { design: "Coin".into(), column_type: ColumnType::Repeating, each: 2.5, fractions: Fractions::Scale };
|
||||
@@ -3821,7 +3826,8 @@ fn legends_in_wedges_put_the_series_labels_inside_the_pie() {
|
||||
s.execute("graph.setType", &json!({"legend": true})).unwrap();
|
||||
assert!(!spec(&s, id).pie_legend_in_wedges);
|
||||
assert!(s.execute("graph.setType", &json!({"pieLegend": "inside"})).is_err());
|
||||
let path = vectorcraft_testkit::temp_dir("graph-pie").join("pie.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-pie");
|
||||
let path = dir.join("pie.vectorcraft");
|
||||
s.execute("graph.setType", &json!({"pieLegend": "wedges", "piePosition": "stacked", "pieSort": "all"})).unwrap();
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
@@ -3962,7 +3968,8 @@ fn the_legend_across_top_runs_in_rows_above_the_plot() {
|
||||
assert!(ys.iter().all(|y| *y + 8.0 <= 100.0 - 8.0 + 1e-6), "{ys:?}");
|
||||
let xs: Vec<f64> = (0..6).map(|i| swatch(&s, i).x1).collect();
|
||||
assert!(xs.iter().all(|x| *x <= 400.0), "{xs:?}");
|
||||
let path = vectorcraft_testkit::temp_dir("graph-top").join("top.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-top");
|
||||
let path = dir.join("top.vectorcraft");
|
||||
s.execute("graph.setType", &json!({"dropShadow": true})).unwrap();
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
@@ -4081,7 +4088,8 @@ fn filled_lines_are_bands_in_the_series_colour() {
|
||||
s.execute("paint.setFill", &json!({"ids": [grp.0], "color": "#ff0000"})).unwrap();
|
||||
s.execute("graph.setData", &json!({"id": id.0, "rows": [[2], [3], [2]]})).unwrap();
|
||||
assert_eq!(series_parts(&s, id, 0)[0].appearance.fill_paint().color().unwrap().to_hex(), "#ff0000");
|
||||
let path = vectorcraft_testkit::temp_dir("graph-filled").join("filled.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-filled");
|
||||
let path = dir.join("filled.vectorcraft");
|
||||
s.execute("graph.setType", &json!({"firstRowInFront": true, "firstColumnInFront": true})).unwrap();
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
@@ -4208,7 +4216,8 @@ fn a_graph_labels_anti_aliasing_method_survives_it_being_drawn_again() {
|
||||
s.execute("edit.undo", &json!({})).unwrap();
|
||||
assert_eq!(spec(&s, id).label_styles.categories_anti_alias, None);
|
||||
s.execute("edit.redo", &json!({})).unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-label-anti-alias").join("labels.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-label-anti-alias");
|
||||
let path = dir.join("labels.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
assert_eq!(spec(&s, id).label_styles.categories_anti_alias, Some(TypeAntiAlias::Strong));
|
||||
@@ -4251,7 +4260,8 @@ fn formatting_one_label_keeps_that_kind_of_label() {
|
||||
assert_eq!(look("0").2, "#0000ff", "every value axis number");
|
||||
assert_eq!(look("0").1, 9.0);
|
||||
// Kept through save and open; nothing written when unset.
|
||||
let path = vectorcraft_testkit::temp_dir("graph-labels").join("labels.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-labels");
|
||||
let path = dir.join("labels.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
assert_eq!(spec(&s, id).label_styles.categories.as_ref().unwrap().size, 14.0);
|
||||
@@ -4420,7 +4430,8 @@ fn paragraph_formats_are_kept_when_the_graph_is_drawn_again() {
|
||||
let v = text_id(&s, col, "Axes", "4");
|
||||
s.execute("select.set", &json!({"ids": [v.0]})).unwrap();
|
||||
s.execute("text.setFormat", &json!({"composer": "singleLine"})).unwrap();
|
||||
let path = vectorcraft_testkit::temp_dir("graph-label-paras").join("labels.vectorcraft");
|
||||
let dir = vectorcraft_testkit::TempDir::new("graph-label-paras");
|
||||
let path = dir.join("labels.vectorcraft");
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
s.execute("document.open", &json!({"path": path})).unwrap();
|
||||
s.execute("select.set", &json!({"ids": [col.0]})).unwrap();
|
||||
|
||||
@@ -840,7 +840,7 @@ fn shape_builder_applies_a_merge_before_what_comes_next() {
|
||||
}),
|
||||
];
|
||||
let _proof = crate::tests_colormgmt::GLOBAL.lock().unwrap_or_else(std::sync::PoisonError::into_inner);
|
||||
let dir = vectorcraft_testkit::temp_dir("shape-builder-merge-first");
|
||||
let dir = vectorcraft_testkit::TempDir::new("shape-builder-merge-first");
|
||||
for (what, then) in next {
|
||||
let mut s = session();
|
||||
if what == "Select All Instances" {
|
||||
|
||||
@@ -22,7 +22,8 @@ fn revision(s: &Session) -> u64 {
|
||||
|
||||
#[test]
|
||||
fn a_pdf_export_cancelled_between_pages_stops_writes_nothing_and_changes_nothing() {
|
||||
let path = vectorcraft_testkit::temp_dir("cancel-pdf").join("out.pdf");
|
||||
let dir = vectorcraft_testkit::TempDir::new("cancel-pdf");
|
||||
let path = dir.join("out.pdf");
|
||||
std::fs::remove_file(&path).ok();
|
||||
let mut s = session(5);
|
||||
let before = (revision(&s), s.doc().unwrap().history.undo.len());
|
||||
@@ -66,7 +67,7 @@ fn a_cancelled_flattening_export_stops_before_the_pages() {
|
||||
|
||||
#[test]
|
||||
fn raster_exports_and_svg_stop_when_cancelled_too() {
|
||||
let dir = vectorcraft_testkit::temp_dir("cancel-formats");
|
||||
let dir = vectorcraft_testkit::TempDir::new("cancel-formats");
|
||||
let mut s = session(3);
|
||||
let token = CancelToken::new();
|
||||
token.cancel();
|
||||
|
||||
@@ -36,7 +36,7 @@ fn error(s: &mut Session, p: Value) -> String {
|
||||
|
||||
#[test]
|
||||
fn an_export_compares_equal_and_a_moved_square_counts_the_pixels_it_changed() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-moved");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-moved");
|
||||
let png = path_in(&dir, "art.png");
|
||||
let (mut s, id) = session();
|
||||
export(&mut s, &png);
|
||||
@@ -54,12 +54,11 @@ fn an_export_compares_equal_and_a_moved_square_counts_the_pixels_it_changed() {
|
||||
assert_eq!(r["rmse"].as_f64(), Some(rmse), "{r}");
|
||||
// Above every difference, nothing is over.
|
||||
assert_eq!(compare(&mut s, json!({"path": png, "threshold": 1}))["over"], json!(0));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_image_and_the_render_are_flattened_on_the_background() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-background");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-background");
|
||||
let png = path_in(&dir, "clear.png");
|
||||
// Transparent everywhere, with red in the color channels that the alpha hides.
|
||||
image::RgbaImage::from_pixel(80, 40, image::Rgba([255, 0, 0, 0])).save(&png).unwrap();
|
||||
@@ -81,12 +80,11 @@ fn the_image_and_the_render_are_flattened_on_the_background() {
|
||||
assert_eq!((r["rmse"].as_f64(), r["over"].as_u64()), (Some(rmse(255.0)), Some(1600)), "{r}");
|
||||
// A threshold just above 128/255 leaves the gray differences out.
|
||||
assert_eq!(compare(&mut s, json!({"path": png, "background": "#808080", "threshold": 0.51}))["over"], json!(0));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_image_of_other_proportions_is_refused_and_one_pixel_is_allowed() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-size");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-size");
|
||||
let (mut s, _) = session();
|
||||
let square = path_in(&dir, "square.png");
|
||||
image::RgbaImage::from_pixel(30, 30, image::Rgba([255; 4])).save(&square).unwrap();
|
||||
@@ -96,12 +94,11 @@ fn an_image_of_other_proportions_is_refused_and_one_pixel_is_allowed() {
|
||||
image::RgbaImage::from_pixel(80, 41, image::Rgba([255; 4])).save(&taller).unwrap();
|
||||
let r = compare(&mut s, json!({"path": taller}));
|
||||
assert_eq!((r["width"].as_u64(), r["height"].as_u64(), r["total"].as_u64()), (Some(80), Some(41), Some(3280)), "{r}");
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_export_whose_height_was_rounded_compares_equal() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-rounded");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-rounded");
|
||||
let png = path_in(&dir, "art.png");
|
||||
// 20.25 pt at 2 pixels a point is 40.5 rows, which the export rounds to 41.
|
||||
let mut s = Session::new();
|
||||
@@ -112,7 +109,6 @@ fn an_export_whose_height_was_rounded_compares_equal() {
|
||||
export(&mut s, &png);
|
||||
let r = compare(&mut s, json!({"path": png}));
|
||||
assert_eq!(r, json!({"width": 80, "height": 41, "rmse": 0.0, "over": 0, "total": 3280, "threshold": 0.08}));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -151,7 +147,7 @@ fn a_linked_image_is_drawn_from_its_file_as_a_png_export_draws_it() {
|
||||
|
||||
#[test]
|
||||
fn an_image_that_cannot_be_decoded_and_a_missing_font_warn_as_a_png_export_does() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-warnings");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-warnings");
|
||||
let png = path_in(&dir, "art.png");
|
||||
let (mut s, _) = session();
|
||||
export(&mut s, &png);
|
||||
@@ -167,12 +163,11 @@ fn an_image_that_cannot_be_decoded_and_a_missing_font_warn_as_a_png_export_does(
|
||||
assert!(warnings.iter().any(|w| w == "an image couldn't be decoded and is missing from the picture"), "{r}");
|
||||
assert!(warnings.iter().any(|w| w.as_str().unwrap().contains("Nonexistent Sans Probe")), "{r}");
|
||||
assert!(warnings.iter().any(|w| w.as_str().unwrap().contains("doesn't fit its frame")), "{r}");
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn diff_path_writes_each_pixels_differences() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-diff");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-diff");
|
||||
let png = path_in(&dir, "art.png");
|
||||
let diff = path_in(&dir, "diff.png");
|
||||
let (mut s, id) = session();
|
||||
@@ -188,12 +183,11 @@ fn diff_path_writes_each_pixels_differences() {
|
||||
}
|
||||
let changed = img.pixels().filter(|p| p.0[0] != 0).count();
|
||||
assert_eq!(changed as u64, r["over"].as_u64().unwrap());
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_unreadable_and_malformed_inputs_are_errors() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-errors");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-errors");
|
||||
let (mut s, _) = session();
|
||||
let png = path_in(&dir, "art.png");
|
||||
export(&mut s, &png);
|
||||
@@ -216,14 +210,13 @@ fn missing_unreadable_and_malformed_inputs_are_errors() {
|
||||
let e = error(&mut s, p.clone());
|
||||
assert!(e.contains(want), "{p}: {e}");
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
/// A planar TIFF whose header claims more strips than the decoder can count (2^31 + 1 rows a strip
|
||||
/// each, times 5 planes) is refused as too large to draw, without a panic.
|
||||
#[test]
|
||||
fn a_tiff_with_more_strips_than_can_be_counted_is_too_large() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-strips");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-strips");
|
||||
let (mut s, _) = session();
|
||||
let plane = |v: u16| vec![v; 6];
|
||||
let channels = [plane(200), plane(40), plane(10), plane(0), plane(90)];
|
||||
@@ -247,12 +240,11 @@ fn a_tiff_with_more_strips_than_can_be_counted_is_too_large() {
|
||||
std::fs::write(&tif, &file).unwrap();
|
||||
let e = error(&mut s, json!({"path": tif}));
|
||||
assert_eq!(e, "invalid parameters for `document.compare`: an image of 3 × 2147483649 pixels is too large to draw");
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn the_image_and_the_difference_image_stay_inside_the_automation_roots() {
|
||||
let dir = vectorcraft_testkit::temp_dir("compare-roots");
|
||||
let dir = vectorcraft_testkit::TempDir::new("compare-roots");
|
||||
for d in ["in", "out", "elsewhere"] {
|
||||
std::fs::create_dir_all(dir.join(d)).unwrap();
|
||||
}
|
||||
@@ -272,5 +264,4 @@ fn the_image_and_the_difference_image_stay_inside_the_automation_roots() {
|
||||
assert!(!std::path::Path::new(&stray).exists());
|
||||
let kept = path_in(&dir.join("out"), "diff.png");
|
||||
assert_eq!(run(json!({"path": inside, "diffPath": kept})).unwrap()["diffPath"], json!(kept));
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ fn an_images_stroke_with_outline_object_follows_its_opaque_area() {
|
||||
run(&mut s, "effect.apply", json!({"effect": "path.offsetPath", "params": {"offset": 3}, "item": 0}));
|
||||
assert_eq!([shade(&s, 61, 100), shade(&s, 64, 100)], ["white", "black"]);
|
||||
// Relinked to an opaque image, the stroke follows the frame 3 pt out: it shows from 43 to 50.
|
||||
let dir = vectorcraft_testkit::temp_dir("outline-object-relink");
|
||||
let dir = vectorcraft_testkit::TempDir::new("outline-object-relink");
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let path = dir.join("opaque.png");
|
||||
let mut png = vec![];
|
||||
|
||||
@@ -3,8 +3,6 @@
|
||||
//! the Links panel's Show DCS Transparency Interactions (`links.list` with `dcsTransparency`).
|
||||
//! The files are made from scratch as http://www.telegraphics.net/doc/DCS.txt describes them.
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde_json::{Value, json};
|
||||
use vectorcraft_color::{Color, Paint};
|
||||
use vectorcraft_doc::{Node, NodeKind};
|
||||
@@ -17,12 +15,9 @@ fn session() -> Session {
|
||||
s
|
||||
}
|
||||
|
||||
/// A fresh scratch folder for one test.
|
||||
fn temp_dir(name: &str) -> PathBuf {
|
||||
let d = std::env::temp_dir().join(format!("vc-dcs-{name}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
std::fs::create_dir_all(&d).unwrap();
|
||||
d
|
||||
/// A fresh scratch folder for one test, removed when dropped.
|
||||
fn temp_dir(name: &str) -> vectorcraft_testkit::TempDir {
|
||||
vectorcraft_testkit::TempDir::new(&format!("dcs-{name}"))
|
||||
}
|
||||
|
||||
/// A plate program: one 2 × 2 grayscale image (`gray`, row by row) on a 20 × 10 pt page.
|
||||
|
||||
@@ -14,12 +14,12 @@ use crate::cmd::findfiles::{self, Limits, Rules, Visitor};
|
||||
use crate::cmd::fontfiles::{self, FontSearch, MAX_FONT_FILE, Sought, copy_into};
|
||||
use crate::{EngineError, Session};
|
||||
|
||||
/// A fresh temporary folder (canonical, as a search reports it).
|
||||
fn folder(tag: &str) -> PathBuf {
|
||||
let dir = vectorcraft_testkit::temp_dir(&format!("fontfiles-{tag}"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
findfiles::plain(std::fs::canonicalize(&dir).unwrap())
|
||||
/// A fresh temporary folder, removed when the first part is dropped, and its path (canonical, as a
|
||||
/// search reports it).
|
||||
fn folder(tag: &str) -> (vectorcraft_testkit::TempDir, PathBuf) {
|
||||
let dir = vectorcraft_testkit::TempDir::new(&format!("fontfiles-{tag}"));
|
||||
let path = findfiles::plain(std::fs::canonicalize(&*dir).unwrap());
|
||||
(dir, path)
|
||||
}
|
||||
|
||||
/// A session whose folder searches may go anywhere in `root`, on two threads.
|
||||
@@ -103,7 +103,7 @@ fn missing_fonts_are_listed_symbols_included() {
|
||||
/// listed finds the other fonts' files.
|
||||
#[test]
|
||||
fn an_overlong_font_name_leaves_the_other_fonts_searchable() {
|
||||
let root = folder("long-name");
|
||||
let (_dir, root) = folder("long-name");
|
||||
std::fs::write(root.join("Findme.otf"), vectorcraft_testkit::fonts::renamed("Findme Sans 3")).unwrap();
|
||||
let mut s = session_for(&root);
|
||||
s.execute("file.new", &json!({"width": 300, "height": 300})).unwrap();
|
||||
@@ -120,7 +120,7 @@ fn an_overlong_font_name_leaves_the_other_fonts_searchable() {
|
||||
/// apps' folders, such as the attachments a mail app keeps in the home folder's Library.
|
||||
#[test]
|
||||
fn the_fonts_folder_next_to_a_document_in_another_apps_folder_isnt_offered() {
|
||||
let root = folder("next-to");
|
||||
let (_dir, root) = folder("next-to");
|
||||
let home = root.join("Users/me");
|
||||
let mut s = Session::new();
|
||||
s.search_rules = Some(Rules { home: Some(home.clone()), ..Rules::default() });
|
||||
@@ -140,7 +140,7 @@ fn the_fonts_folder_next_to_a_document_in_another_apps_folder_isnt_offered() {
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn the_fonts_folder_next_to_a_document_is_an_absolute_path() {
|
||||
let root = folder("relative");
|
||||
let (_dir, root) = folder("relative");
|
||||
std::fs::create_dir_all(root.join("pkg/Fonts")).unwrap();
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
let up = "../".repeat(cwd.components().count().saturating_sub(1));
|
||||
@@ -160,7 +160,7 @@ fn the_fonts_folder_next_to_a_document_is_an_absolute_path() {
|
||||
|
||||
#[test]
|
||||
fn a_search_finds_a_missing_fonts_file_by_its_names() {
|
||||
let root = folder("find");
|
||||
let (_dir, root) = folder("find");
|
||||
std::fs::create_dir_all(root.join("x/y")).unwrap();
|
||||
std::fs::write(root.join("x/y/Findme.otf"), vectorcraft_testkit::fonts::renamed("Findme Sans 3")).unwrap();
|
||||
std::fs::write(root.join("other.ttf"), bundled("SourceSerif4-Regular.ttf")).unwrap();
|
||||
@@ -199,7 +199,7 @@ fn a_search_finds_a_missing_fonts_file_by_its_names() {
|
||||
/// when they are inside them.
|
||||
#[test]
|
||||
fn a_search_shows_a_confined_caller_only_paths_inside_its_read_roots() {
|
||||
let root = folder("read-roots");
|
||||
let (_dir, root) = folder("read-roots");
|
||||
let font = vectorcraft_testkit::fonts::renamed("Findme Sans 3");
|
||||
let (home, wide) = (root.join("home"), root.join("lrf-wide"));
|
||||
for dir in [&home, &wide] {
|
||||
@@ -228,12 +228,11 @@ fn a_search_shows_a_confined_caller_only_paths_inside_its_read_roots() {
|
||||
assert!(finished(&mut s)["error"].as_str().is_some_and(|e| e.contains("lrf-wide")));
|
||||
let r = ask(&mut s, &narrow, json!({}));
|
||||
assert_eq!((r["state"].as_str(), r["error"].as_str(), r.get("folder")), (Some("failed"), Some("the folder can't be read"), None), "{r}");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn junk_search_params_never_fail_internally() {
|
||||
let root = folder("junk");
|
||||
let (_dir, root) = folder("junk");
|
||||
let mut s = session_for(&root);
|
||||
s.execute("file.new", &json!({"width": 300, "height": 300})).unwrap();
|
||||
text(&mut s, 20.0, "No Such Font Family", "Regular");
|
||||
@@ -297,7 +296,7 @@ impl Visitor for Blocked {
|
||||
|
||||
#[test]
|
||||
fn stop_ends_only_the_search_it_names() {
|
||||
let root = folder("stop");
|
||||
let (_dir, root) = folder("stop");
|
||||
std::fs::write(root.join("a.ttf"), b"font").unwrap();
|
||||
let mut s = session_for(&root);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
@@ -330,7 +329,7 @@ fn adding_fonts_needs_vectorcraft_s_fonts_folder() {
|
||||
|
||||
#[test]
|
||||
fn copying_never_replaces_a_file() {
|
||||
let root = folder("copy");
|
||||
let (_dir, root) = folder("copy");
|
||||
let (src, dest) = (root.join("src"), root.join("dest"));
|
||||
std::fs::create_dir_all(&src).unwrap();
|
||||
std::fs::create_dir_all(&dest).unwrap();
|
||||
@@ -376,12 +375,11 @@ fn copying_never_replaces_a_file() {
|
||||
assert_eq!(reasons[..2], ["larger than 256 MB", "not a file"]);
|
||||
assert!(r.copied.is_empty() && reasons.len() == 3, "{r:?}");
|
||||
assert!(!dest.join("huge.otf").exists() && !dest.join("folder.otf").exists());
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_font_files_are_copied() {
|
||||
let root = folder("allowed");
|
||||
let (_dir, root) = folder("allowed");
|
||||
let (src, dest) = (root.join("src"), root.join("dest"));
|
||||
std::fs::create_dir_all(&src).unwrap();
|
||||
std::fs::create_dir_all(&dest).unwrap();
|
||||
@@ -395,13 +393,12 @@ fn only_font_files_are_copied() {
|
||||
let not_a_font = |n: &str| (src.join(n), "not a font file".to_string());
|
||||
assert_eq!(r.skipped, [not_a_font("notes.txt"), not_a_font("Script")]);
|
||||
assert!(!dest.join("notes.txt").exists() && !dest.join("Script").exists());
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// A search reads no font file larger than Add Fonts copies, and finds a smaller copy of the font.
|
||||
#[test]
|
||||
fn a_search_skips_font_files_too_large_to_add() {
|
||||
let root = folder("too-large");
|
||||
let (_dir, root) = folder("too-large");
|
||||
let (big, small) = (root.join("big").join("Sizeme.otf"), root.join("small").join("Sizeme.otf"));
|
||||
let font = vectorcraft_testkit::fonts::renamed("Sizeme Sans 3");
|
||||
for path in [&big, &small] {
|
||||
@@ -415,7 +412,6 @@ fn a_search_skips_font_files_too_large_to_add() {
|
||||
s.execute("text.findFontFiles", &json!({"folder": root, "fonts": [{"family": "Sizeme Sans 3"}, {"family": "No Such Sans"}]})).unwrap();
|
||||
let r = finished(&mut s);
|
||||
assert_eq!(r["fonts"][0]["files"], json!([small]), "{r}");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// A suitcase font: a file without an extension or with `.suit`, empty but for the fonts in its
|
||||
@@ -424,7 +420,7 @@ fn a_search_skips_font_files_too_large_to_add() {
|
||||
#[test]
|
||||
fn suitcase_fonts_are_found_and_copied_with_their_fork() {
|
||||
use vectorcraft_testkit::fonts::{renamed, suitcase_fork};
|
||||
let root = folder("suitcase");
|
||||
let (_dir, root) = folder("suitcase");
|
||||
let (src, dest) = (root.join("src"), root.join("dest"));
|
||||
std::fs::create_dir_all(src.join("sub")).unwrap();
|
||||
std::fs::create_dir_all(&dest).unwrap();
|
||||
@@ -467,5 +463,4 @@ fn suitcase_fonts_are_found_and_copied_with_their_fork() {
|
||||
// The font scan reads the copies.
|
||||
let db = vectorcraft_text::FontDb::with_font_dirs(vec![dest.clone()]);
|
||||
assert_eq!((db.styles("Suitme Sans 3"), db.styles("Other Sans 33")), (vec!["Regular".to_string()], vec!["Regular".to_string()]));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
@@ -275,7 +275,8 @@ fn a_batch_fit_selection_replays_but_an_action_uses_the_current_selection() {
|
||||
|
||||
#[test]
|
||||
fn a_save_records_its_date_and_replays_it() {
|
||||
let path = vectorcraft_testkit::temp_dir("journal-save").join("a.vectorcraft").to_string_lossy().into_owned();
|
||||
let dir = vectorcraft_testkit::TempDir::new("journal-save");
|
||||
let path = dir.join("a.vectorcraft").to_string_lossy().into_owned();
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"created": null})).unwrap();
|
||||
s.execute("document.save", &json!({"path": path})).unwrap();
|
||||
@@ -290,7 +291,6 @@ fn a_save_records_its_date_and_replays_it() {
|
||||
assert_eq!(s.doc().unwrap().doc.metadata.modified, Some(2_000_000_000));
|
||||
assert!(s.execute("document.save", &json!({"path": path, "modified": "today"})).is_err());
|
||||
// Save As stamps the same way; Save a Copy leaves the document's dates (and records none).
|
||||
let dir = vectorcraft_testkit::temp_dir("journal-save");
|
||||
s.execute("file.saveAs", &json!({"path": dir.join("b.vectorcraft").to_string_lossy(), "modified": 2_000_000_100})).unwrap();
|
||||
assert_eq!(s.journal.last().unwrap().1["modified"], json!(2_000_000_100));
|
||||
s.execute("file.saveCopy", &json!({"path": dir.join("c.vectorcraft").to_string_lossy()})).unwrap();
|
||||
@@ -316,7 +316,8 @@ fn replayed(s: &Session) -> Session {
|
||||
|
||||
#[test]
|
||||
fn a_save_in_a_batch_records_its_date_and_replays_it() {
|
||||
let path = vectorcraft_testkit::temp_dir("journal-batch").join("a.vectorcraft").to_string_lossy().into_owned();
|
||||
let dir = vectorcraft_testkit::TempDir::new("journal-batch");
|
||||
let path = dir.join("a.vectorcraft").to_string_lossy().into_owned();
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"created": 1_000_000_000})).unwrap();
|
||||
let rect = json!({"x": 10, "y": 10, "width": 50, "height": 40});
|
||||
@@ -698,7 +699,8 @@ fn a_failed_batch_closes_the_documents_it_opened_and_restores_the_ones_it_closed
|
||||
|
||||
#[test]
|
||||
fn a_failed_batch_brings_back_a_document_it_reverted() {
|
||||
let path = vectorcraft_testkit::temp_dir("journal-batch").join("revert.vectorcraft").to_string_lossy().into_owned();
|
||||
let dir = vectorcraft_testkit::TempDir::new("journal-batch");
|
||||
let path = dir.join("revert.vectorcraft").to_string_lossy().into_owned();
|
||||
let mut s = Session::new();
|
||||
s.execute("file.new", &json!({"created": null})).unwrap();
|
||||
s.execute("document.save", &json!({"path": path, "modified": null})).unwrap();
|
||||
|
||||
@@ -816,7 +816,7 @@ fn style_order_of(s: &mut Session) -> Vec<String> {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn a_library_item_rename_is_saved_or_taken_back() {
|
||||
let dir = vectorcraft_testkit::temp_dir("library-rename-item");
|
||||
let dir = vectorcraft_testkit::TempDir::new("library-rename-item");
|
||||
let folder = dir.to_string_lossy().to_string();
|
||||
let mut s = session();
|
||||
s.libraries.set_dir(Some(folder.clone()));
|
||||
@@ -836,7 +836,6 @@ fn a_library_item_rename_is_saved_or_taken_back() {
|
||||
run(&mut s, "library.renameItem", json!({"kind": "charStyle", "item": "Text", "name": " Text "}));
|
||||
assert!(s.execute("library.renameItem", &json!({"kind": "charStyle", "item": "Text", "name": "Copy"})).is_err());
|
||||
assert_eq!(style_order_of(&mut s), ["Text"], "taken back");
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@@ -1056,7 +1055,8 @@ fn a_new_item_is_added_where_it_is_dropped() {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn an_add_that_cannot_be_placed_makes_no_library() {
|
||||
let folder = vectorcraft_testkit::temp_dir("library-add-unplaced").to_string_lossy().to_string();
|
||||
let dir = vectorcraft_testkit::TempDir::new("library-add-unplaced");
|
||||
let folder = dir.to_string_lossy().to_string();
|
||||
let mut s = session();
|
||||
s.libraries.set_dir(Some(folder.clone()));
|
||||
for bad in [json!({"kind": "fillColor", "color": "#808080", "group": "Nope"}), json!({"kind": "fillColor", "color": "#808080", "after": "Nope"})]
|
||||
@@ -1163,7 +1163,7 @@ fn a_library_of_one_repeated_name_imports_numbered() {
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[test]
|
||||
fn a_library_is_exported_to_a_file_and_imported_from_it() {
|
||||
let dir = vectorcraft_testkit::temp_dir("library-export");
|
||||
let dir = vectorcraft_testkit::TempDir::new("library-export");
|
||||
let path = dir.join("Brand.vclibrary").to_string_lossy().to_string();
|
||||
let (mut s, g, ..) = brand();
|
||||
run(&mut s, "library.createGroup", json!({"name": "Logos", "items": [{"kind": "graphic", "item": g}]}));
|
||||
|
||||
@@ -14,12 +14,12 @@ use crate::cmd::linkfiles::{self, LinkSearch, Sought};
|
||||
use crate::tests_links::{BLUE, RED, place, png};
|
||||
use crate::{EngineError, Session};
|
||||
|
||||
/// A fresh temporary folder (canonical, as a search reports it).
|
||||
fn folder(tag: &str) -> PathBuf {
|
||||
let dir = vectorcraft_testkit::temp_dir(&format!("linkfiles-{tag}"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
findfiles::plain(std::fs::canonicalize(&dir).unwrap())
|
||||
/// A fresh temporary folder, removed when the first part is dropped, and its path (canonical, as a
|
||||
/// search reports it).
|
||||
fn folder(tag: &str) -> (vectorcraft_testkit::TempDir, PathBuf) {
|
||||
let dir = vectorcraft_testkit::TempDir::new(&format!("linkfiles-{tag}"));
|
||||
let path = findfiles::plain(std::fs::canonicalize(&*dir).unwrap());
|
||||
(dir, path)
|
||||
}
|
||||
|
||||
/// Write `bytes` to the file at `path`, making its folders.
|
||||
@@ -80,7 +80,7 @@ fn found(r: &Value, name: &str, root: &Path) -> Vec<(String, String, bool)> {
|
||||
/// innermost out up to the first that differs.
|
||||
#[test]
|
||||
fn files_found_are_ranked_by_what_the_link_saved() {
|
||||
let root = folder("rank");
|
||||
let (_dir, root) = folder("rank");
|
||||
let mut s = session_for(&root);
|
||||
let (logo, photo) = (png(60, 40, RED), png(50, 30, BLUE));
|
||||
write(&root.join("orig/logo.png"), &logo);
|
||||
@@ -127,7 +127,7 @@ fn files_found_are_ranked_by_what_the_link_saved() {
|
||||
/// the files found are ranked by the folder names they share with that path.
|
||||
#[test]
|
||||
fn a_classic_mac_path_is_found_by_its_name_and_folders() {
|
||||
let root = folder("classic");
|
||||
let (_dir, root) = folder("classic");
|
||||
let mut s = session_for(&root);
|
||||
let photo = png(50, 30, BLUE);
|
||||
write(&root.join("orig/photo.png"), &photo);
|
||||
@@ -158,7 +158,7 @@ fn a_classic_mac_path_is_found_by_its_name_and_folders() {
|
||||
#[test]
|
||||
fn parent_components_in_a_saved_path_are_resolved_before_ranking() {
|
||||
for (tag, saved) in [("classic-up", "Old Disk:Projects:Art::photo.png"), ("slash-up", "/old/Projects/Art/../photo.png")] {
|
||||
let root = folder(tag);
|
||||
let (_dir, root) = folder(tag);
|
||||
let mut s = session_for(&root);
|
||||
let photo = png(50, 30, BLUE);
|
||||
write(&root.join("orig/photo.png"), &photo);
|
||||
@@ -188,7 +188,7 @@ fn parent_components_in_a_saved_path_are_resolved_before_ranking() {
|
||||
/// files, whatever their status.
|
||||
#[test]
|
||||
fn a_search_looks_for_the_missing_files_or_the_objects_given() {
|
||||
let root = folder("which");
|
||||
let (_dir, root) = folder("which");
|
||||
let mut s = session_for(&root);
|
||||
write(&root.join("orig/gone.png"), &png(40, 30, RED));
|
||||
write(&root.join("orig/here.png"), &png(40, 30, BLUE));
|
||||
@@ -225,7 +225,7 @@ fn a_search_looks_for_the_missing_files_or_the_objects_given() {
|
||||
/// other contents leaves it to search every folder.
|
||||
#[test]
|
||||
fn a_search_ends_once_every_linked_file_has_its_same_file() {
|
||||
let root = folder("early");
|
||||
let (_dir, root) = folder("early");
|
||||
let mut s = session_for(&root);
|
||||
s.search_threads = Some(1);
|
||||
let bytes = png(40, 30, RED);
|
||||
@@ -265,7 +265,7 @@ impl Visitor for Blocked {
|
||||
|
||||
#[test]
|
||||
fn stop_ends_only_the_search_it_names() {
|
||||
let root = folder("stop");
|
||||
let (_dir, root) = folder("stop");
|
||||
std::fs::write(root.join("a.png"), b"png").unwrap();
|
||||
let mut s = session_for(&root);
|
||||
let (tx, rx) = mpsc::channel();
|
||||
@@ -290,7 +290,7 @@ fn stop_ends_only_the_search_it_names() {
|
||||
/// roots, the replies are as before.
|
||||
#[test]
|
||||
fn a_search_shows_a_confined_caller_only_paths_inside_its_read_roots() {
|
||||
let root = folder("read-roots");
|
||||
let (_dir, root) = folder("read-roots");
|
||||
let mut s = session_for(&root);
|
||||
let photo = png(40, 30, RED);
|
||||
write(&root.join("lrf-away/photo.png"), &photo);
|
||||
@@ -343,14 +343,13 @@ fn a_search_shows_a_confined_caller_only_paths_inside_its_read_roots() {
|
||||
let r = ask(&mut s, &narrow, json!({}));
|
||||
assert_eq!((r["state"].as_str(), r["error"].as_str()), (Some("failed"), Some("the folder can't be read")), "{r}");
|
||||
hidden(&r);
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// A link's saved path relative to the document is shown to a confined caller when it leads to a
|
||||
/// file inside its read roots, and left out when `..` leads out of them.
|
||||
#[test]
|
||||
fn a_relative_link_path_is_judged_from_the_documents_folder() {
|
||||
let root = folder("read-roots-relative");
|
||||
let (_dir, root) = folder("read-roots-relative");
|
||||
let mut s = session_for(&root);
|
||||
let home = root.join("home");
|
||||
write(&root.join("lrf-away/photo.png"), &png(40, 30, RED));
|
||||
@@ -370,14 +369,13 @@ fn a_relative_link_path_is_judged_from_the_documents_folder() {
|
||||
assert_eq!(r["links"][0].get("path"), shown.then(|| json!(saved)).as_ref(), "{saved}: {r}");
|
||||
finished(&mut s);
|
||||
}
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// `image.info` leaves a linked file outside the confined caller's read roots unnamed (`link`
|
||||
/// null), as `links.info` leaves it out, and keeps the other fields.
|
||||
#[test]
|
||||
fn image_info_leaves_out_a_link_outside_the_read_roots() {
|
||||
let root = folder("read-roots-info");
|
||||
let (_dir, root) = folder("read-roots-info");
|
||||
let mut s = session_for(&root);
|
||||
let pic = root.join("lrf-away/photo.png");
|
||||
write(&pic, &png(40, 30, RED));
|
||||
@@ -391,12 +389,11 @@ fn image_info_leaves_out_a_link_outside_the_read_roots() {
|
||||
let info = ask(&mut s, "links.info");
|
||||
assert!(info.get("link").is_none() && !info.to_string().contains("lrf-away"), "{info}");
|
||||
assert_eq!(s.execute("image.info", &json!({"id": id})).unwrap()["link"], json!(pic));
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn relinking_with_copy_puts_the_file_in_the_documents_folder() {
|
||||
let root = folder("copy");
|
||||
let (_dir, root) = folder("copy");
|
||||
let mut s = session_for(&root);
|
||||
write(&root.join("orig/photo.png"), &png(40, 30, RED));
|
||||
let id = placed(&mut s, &root.join("orig/photo.png"));
|
||||
@@ -443,14 +440,13 @@ fn relinking_with_copy_puts_the_file_in_the_documents_folder() {
|
||||
let image = placed(&mut unsaved, &found);
|
||||
let r = unsaved.execute("links.relink", &json!({"ids": [image], "path": found, "copy": true}));
|
||||
assert!(matches!(&r, Err(EngineError::BadParams { msg, .. }) if msg.contains("save the document first")), "{r:?}");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// Copy takes images only: with copy, a call that includes a placed document fails and copies
|
||||
/// nothing; without it, the document is relinked where its file is.
|
||||
#[test]
|
||||
fn copy_refuses_placed_documents() {
|
||||
let root = folder("copy-placed");
|
||||
let (_dir, root) = folder("copy-placed");
|
||||
let mut s = session_for(&root);
|
||||
s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 50, "height": 50})).unwrap();
|
||||
let badge = root.join("badge.vectorcraft");
|
||||
@@ -471,7 +467,7 @@ fn copy_refuses_placed_documents() {
|
||||
#[cfg(unix)]
|
||||
#[test]
|
||||
fn a_copy_stays_inside_the_write_root() {
|
||||
let root = folder("roots");
|
||||
let (_dir, root) = folder("roots");
|
||||
let mut s = session_for(&root);
|
||||
write(&root.join("orig/photo.png"), &png(40, 30, RED));
|
||||
let id = placed(&mut s, &root.join("orig/photo.png"));
|
||||
@@ -487,14 +483,13 @@ fn a_copy_stays_inside_the_write_root() {
|
||||
assert!(matches!(&r, Err(EngineError::Other(m)) if m.contains("outside the write root")), "{r:?}");
|
||||
assert!(!root.join("doc/photo 2.png").exists());
|
||||
assert_eq!(std::fs::read(allowed.join("photo.png")).unwrap(), b"kept as it is");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
/// An SVG's linked files are read on the importer's own thread (it needs a deep stack): the roots
|
||||
/// in force on the thread that opens the SVG hold there too.
|
||||
#[test]
|
||||
fn an_svg_reads_its_links_within_the_read_root() {
|
||||
let root = folder("svg-roots");
|
||||
let (_dir, root) = folder("svg-roots");
|
||||
for dir in ["in", "out"] {
|
||||
write(&root.join(dir).join("dot.png"), &png(4, 4, RED));
|
||||
}
|
||||
@@ -513,12 +508,11 @@ fn an_svg_reads_its_links_within_the_read_root() {
|
||||
assert_eq!(import("in"), inside, "inside the read root: read as before");
|
||||
assert_ne!(import("out"), outside, "outside the read root: not read");
|
||||
});
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn junk_search_params_never_fail_internally() {
|
||||
let root = folder("junk");
|
||||
let (_dir, root) = folder("junk");
|
||||
let mut s = session_for(&root);
|
||||
write(&root.join("orig/a.png"), &png(4, 4, RED));
|
||||
let id = placed(&mut s, &root.join("orig/a.png"));
|
||||
|
||||
@@ -51,7 +51,7 @@ fn entries_keep_their_sections_and_the_command_counts_codes() {
|
||||
assert_eq!(r["codes"]["modifiers"], json!([{"value": 64, "count": 2}, {"value": 80, "count": 1}]));
|
||||
assert_eq!((r["unread"].as_array().unwrap().len(), r["truncated"].as_bool()), (0, Some(false)), "{r}");
|
||||
// The same from a file, and from its bytes.
|
||||
let dir = vectorcraft_testkit::temp_dir("shortcutset-read");
|
||||
let dir = vectorcraft_testkit::TempDir::new("shortcutset-read");
|
||||
let path = dir.join("Studio Keys.kys");
|
||||
std::fs::write(&path, text).unwrap();
|
||||
let mut s = Session::new();
|
||||
@@ -59,7 +59,6 @@ fn entries_keep_their_sections_and_the_command_counts_codes() {
|
||||
assert_eq!((r["set"].as_str(), r["entries"].as_array().unwrap().len()), (Some("Studio Keys"), 3));
|
||||
let b64 = vectorcraft_format::base64_encode(text.as_bytes());
|
||||
assert_eq!(s.execute("shortcutSet.read", &json!({ "dataBase64": b64 })).unwrap()["entries"].as_array().unwrap().len(), 3);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -124,7 +123,7 @@ fn caps_nesting_entries_and_names() {
|
||||
#[test]
|
||||
fn big_files_are_read_up_to_the_cap() {
|
||||
let text = format!("/Menus {{ /E {{ /Key 1 }} {} }}", " ".repeat(5 << 20));
|
||||
let dir = vectorcraft_testkit::temp_dir("shortcutset-big");
|
||||
let dir = vectorcraft_testkit::TempDir::new("shortcutset-big");
|
||||
let path = dir.join("Big.kys");
|
||||
std::fs::write(&path, &text).unwrap();
|
||||
let b64 = vectorcraft_format::base64_encode(text.as_bytes());
|
||||
@@ -145,7 +144,6 @@ fn big_files_are_read_up_to_the_cap() {
|
||||
assert_eq!(fits.len(), (4 << 20) + 3);
|
||||
std::fs::write(&path, &fits).unwrap();
|
||||
assert_eq!(s.execute("shortcutSet.read", &json!({ "path": path.to_string_lossy() })).unwrap()["truncated"], false);
|
||||
let _ = std::fs::remove_dir_all(dir);
|
||||
}
|
||||
|
||||
fn matched(s: &mut Session, p: Value) -> Vec<String> {
|
||||
|
||||
@@ -161,7 +161,7 @@ fn a_changed_word_replaces_the_old_one() {
|
||||
#[test]
|
||||
fn the_custom_dictionary_file_is_read_and_kept_up_to_date() {
|
||||
let _one_at_a_time = dictionary_lock();
|
||||
let dir = vectorcraft_testkit::temp_dir("spelling-dictionary");
|
||||
let dir = vectorcraft_testkit::TempDir::new("spelling-dictionary");
|
||||
let file = dir.join("Custom Dictionary.txt");
|
||||
std::fs::write(&file, "kqzvwx\nnot one word\n\n").unwrap();
|
||||
let mut s = session();
|
||||
@@ -224,7 +224,7 @@ fn in_dictionary(s: &mut Session, word: &str) -> bool {
|
||||
#[test]
|
||||
fn a_dictionary_file_that_cant_be_read_whole_is_never_written_over() {
|
||||
let _one_at_a_time = dictionary_lock();
|
||||
let dir = vectorcraft_testkit::temp_dir("spelling-unreadable");
|
||||
let dir = vectorcraft_testkit::TempDir::new("spelling-unreadable");
|
||||
// A folder where the file should be can't be read as one; a file past what craft-proof reads
|
||||
// would lose its other words if written back.
|
||||
let folder = dir.join("Custom Dictionary.txt");
|
||||
@@ -248,7 +248,7 @@ fn a_change_that_cant_be_saved_is_taken_back() {
|
||||
let _one_at_a_time = dictionary_lock();
|
||||
let mut s = session();
|
||||
run(&mut s, "text.spelling.addWord", json!({"word": "wkqzvx"}));
|
||||
let dir = vectorcraft_testkit::temp_dir("spelling-rollback");
|
||||
let dir = vectorcraft_testkit::TempDir::new("spelling-rollback");
|
||||
let folder = dir.join("Custom Dictionary.txt");
|
||||
std::fs::create_dir_all(&folder).unwrap();
|
||||
let mut failing = session();
|
||||
@@ -266,7 +266,7 @@ fn a_change_that_cant_be_saved_is_taken_back() {
|
||||
fn a_dictionary_edit_is_saved_whole_or_not_at_all() {
|
||||
let _one_at_a_time = dictionary_lock();
|
||||
const EDIT: &str = "text.spelling.editDictionary";
|
||||
let dir = vectorcraft_testkit::temp_dir("spelling-edit");
|
||||
let dir = vectorcraft_testkit::TempDir::new("spelling-edit");
|
||||
let file = dir.join("Custom Dictionary.txt");
|
||||
std::fs::write(&file, "pqzvka\npqzvkb\n").unwrap();
|
||||
let mut s = session();
|
||||
@@ -302,7 +302,7 @@ fn a_dictionary_edit_is_saved_whole_or_not_at_all() {
|
||||
fn a_word_another_window_saved_stays_when_this_save_fails() {
|
||||
let _one_at_a_time = dictionary_lock();
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let dir = vectorcraft_testkit::temp_dir("spelling-readonly");
|
||||
let dir = vectorcraft_testkit::TempDir::new("spelling-readonly");
|
||||
let file = dir.join("Custom Dictionary.txt");
|
||||
std::fs::write(&file, "").unwrap();
|
||||
let mut s = session();
|
||||
|
||||
@@ -7,10 +7,8 @@ use vectorcraft_color::cms;
|
||||
use crate::Session;
|
||||
use crate::store::{MAX_PRESET_FILE, file_stem, holds};
|
||||
|
||||
fn fresh_dir(tag: &str) -> std::path::PathBuf {
|
||||
let dir = vectorcraft_testkit::temp_dir(&format!("store-{tag}"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
dir
|
||||
fn fresh_dir(tag: &str) -> vectorcraft_testkit::TempDir {
|
||||
vectorcraft_testkit::TempDir::new(&format!("store-{tag}"))
|
||||
}
|
||||
|
||||
fn saved_names(s: &mut Session) -> Vec<String> {
|
||||
@@ -20,7 +18,8 @@ fn saved_names(s: &mut Session) -> Vec<String> {
|
||||
|
||||
#[test]
|
||||
fn saved_presets_come_back_in_the_next_session_and_renames_and_deletes_follow() {
|
||||
let dir = fresh_dir("presets").join("PDF Presets");
|
||||
let root = fresh_dir("presets");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Press", "description": "for the printer", "preset": "Press Quality"})).unwrap();
|
||||
@@ -42,7 +41,8 @@ fn saved_presets_come_back_in_the_next_session_and_renames_and_deletes_follow()
|
||||
|
||||
#[test]
|
||||
fn a_rename_that_cannot_be_written_keeps_the_preset_under_its_old_name() {
|
||||
let dir = fresh_dir("rename-fails").join("PDF Presets");
|
||||
let root = fresh_dir("rename-fails");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Web", "description": "for screens"})).unwrap();
|
||||
@@ -59,7 +59,8 @@ fn a_rename_that_cannot_be_written_keeps_the_preset_under_its_old_name() {
|
||||
|
||||
#[test]
|
||||
fn a_rename_that_only_changes_case_keeps_one_file() {
|
||||
let dir = fresh_dir("rename-case").join("PDF Presets");
|
||||
let root = fresh_dir("rename-case");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Web"})).unwrap();
|
||||
@@ -80,7 +81,8 @@ fn file_names(dir: &std::path::Path) -> Vec<String> {
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn a_rename_that_only_changes_case_and_cannot_be_written_keeps_the_old_file() {
|
||||
let dir = fresh_dir("rename-case-fails").join("PDF Presets");
|
||||
let root = fresh_dir("rename-case-fails");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Web", "description": "for screens"})).unwrap();
|
||||
@@ -103,7 +105,8 @@ fn a_rename_that_only_changes_case_and_cannot_be_written_keeps_the_old_file() {
|
||||
#[cfg(not(any(windows, target_vendor = "apple")))]
|
||||
#[test]
|
||||
fn a_rename_that_only_changes_case_and_cannot_be_moved_keeps_the_old_preset() {
|
||||
let dir = fresh_dir("rename-case-blocked").join("PDF Presets");
|
||||
let root = fresh_dir("rename-case-blocked");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Web", "description": "for screens"})).unwrap();
|
||||
@@ -118,7 +121,8 @@ fn a_rename_that_only_changes_case_and_cannot_be_moved_keeps_the_old_preset() {
|
||||
|
||||
#[test]
|
||||
fn a_folder_that_does_not_exist_yet_takes_the_presets_the_preferences_held() {
|
||||
let dir = fresh_dir("seed").join("PDF Presets");
|
||||
let root = fresh_dir("seed");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.execute("pdf.preset.save", &json!({"name": "From Preferences"})).unwrap();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
@@ -155,7 +159,8 @@ fn a_write_reads_the_file_it_replaces_only_one_byte_past_the_new_length() {
|
||||
|
||||
#[test]
|
||||
fn a_saved_preset_replaces_an_oversized_file_in_its_place() {
|
||||
let dir = fresh_dir("oversized-put").join("PDF Presets");
|
||||
let root = fresh_dir("oversized-put");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Web"})).unwrap();
|
||||
@@ -171,7 +176,8 @@ fn a_saved_preset_replaces_an_oversized_file_in_its_place() {
|
||||
|
||||
#[test]
|
||||
fn damaged_unsafe_and_oversized_preset_files_are_skipped() {
|
||||
let dir = fresh_dir("damaged").join("PDF Presets");
|
||||
let root = fresh_dir("damaged");
|
||||
let dir = root.join("PDF Presets");
|
||||
let mut a = Session::new();
|
||||
a.attach_pdf_preset_store(dir.clone());
|
||||
a.execute("pdf.preset.save", &json!({"name": "Good"})).unwrap();
|
||||
@@ -214,7 +220,8 @@ fn icc_named(name: &str) -> Vec<u8> {
|
||||
|
||||
#[test]
|
||||
fn stored_profiles_register_again_and_removing_one_removes_its_copy() {
|
||||
let dir = fresh_dir("profiles").join("Color Profiles");
|
||||
let root = fresh_dir("profiles");
|
||||
let dir = root.join("Color Profiles");
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
let name = "StoreRGB-1";
|
||||
let file = format!("{}.icc", file_stem(name));
|
||||
@@ -229,7 +236,8 @@ fn stored_profiles_register_again_and_removing_one_removes_its_copy() {
|
||||
|
||||
// A profile loaded from elsewhere is copied in, whatever the file is called.
|
||||
let other = "StoreRGB-2";
|
||||
let loose = vectorcraft_testkit::temp_dir("store-loose").join("whatever.icm");
|
||||
let loose_dir = vectorcraft_testkit::TempDir::new("store-loose");
|
||||
let loose = loose_dir.join("whatever.icm");
|
||||
std::fs::write(&loose, icc_named(other)).unwrap();
|
||||
let r = s.execute("color.loadProfile", &json!({"path": loose.to_str().unwrap()})).unwrap();
|
||||
assert_eq!((r["name"].as_str(), r["remembered"].as_bool()), (Some(other), Some(true)));
|
||||
|
||||
@@ -130,11 +130,9 @@ fn reset_defaults_restores_missing_swatches_or_replaces_them() {
|
||||
assert_eq!(s.paint.fill, Paint::solid(Color::from_hex("#abcdef").unwrap()), "the default fill is unlinked too");
|
||||
}
|
||||
|
||||
/// A fresh scratch folder for one test.
|
||||
fn temp_dir(name: &str) -> std::path::PathBuf {
|
||||
let d = std::env::temp_dir().join(format!("vc-swatchlib-{name}-{}", std::process::id()));
|
||||
let _ = std::fs::remove_dir_all(&d);
|
||||
d
|
||||
/// A fresh scratch folder for one test, removed when the returned value is dropped.
|
||||
fn temp_dir(name: &str) -> vectorcraft_testkit::TempDir {
|
||||
vectorcraft_testkit::TempDir::new(&format!("swatchlib-{name}"))
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -294,7 +294,7 @@ fn files_named_after_the_title_follow_windows_naming_rules() {
|
||||
|
||||
#[test]
|
||||
fn files_are_written_beside_the_path() {
|
||||
let dir = vectorcraft_testkit::temp_dir("webexport");
|
||||
let dir = vectorcraft_testkit::TempDir::new("webexport");
|
||||
let mut s = two_colours();
|
||||
let path = dir.join("art.gif").to_string_lossy().to_string();
|
||||
let r = run(&mut s, "document.exportForWeb", json!({"path": path}));
|
||||
@@ -324,7 +324,7 @@ fn an_export_says_when_an_image_could_not_be_decoded() {
|
||||
let r = run(&mut s, "document.exportForWeb.preview", json!({"format": format}));
|
||||
assert!(undecoded(&r), "{format} preview: {}", r["warnings"]);
|
||||
}
|
||||
let dir = vectorcraft_testkit::temp_dir("webexport-undecoded");
|
||||
let dir = vectorcraft_testkit::TempDir::new("webexport-undecoded");
|
||||
let page = dir.join("page.html").to_string_lossy().to_string();
|
||||
let r = run(&mut s, "document.exportForWeb", json!({"path": page, "output": "html"}));
|
||||
assert!(undecoded(&r), "{r}");
|
||||
|
||||
@@ -653,7 +653,7 @@ fn loading_the_working_rgb_profile_again_takes_effect_at_once() {
|
||||
bytes
|
||||
};
|
||||
let name = "Reload RGB";
|
||||
let dir = vectorcraft_testkit::temp_dir("reload-working-rgb");
|
||||
let dir = vectorcraft_testkit::TempDir::new("reload-working-rgb");
|
||||
let load = |s: &mut Session, builtin: &str| {
|
||||
let path = dir.join(format!("{}.icc", builtin.len()));
|
||||
std::fs::write(&path, renamed(builtin, name)).unwrap();
|
||||
@@ -734,11 +734,9 @@ impl Drop for ProofReset {
|
||||
}
|
||||
}
|
||||
|
||||
/// A folder of its own for the test `tag`, empty.
|
||||
fn fresh_dir(tag: &str) -> std::path::PathBuf {
|
||||
let dir = vectorcraft_testkit::temp_dir(&format!("color-settings-{tag}"));
|
||||
let _ = std::fs::remove_dir_all(&dir);
|
||||
dir
|
||||
/// A folder of its own for the test `tag`, empty, removed when dropped.
|
||||
fn fresh_dir(tag: &str) -> vectorcraft_testkit::TempDir {
|
||||
vectorcraft_testkit::TempDir::new(&format!("color-settings-{tag}"))
|
||||
}
|
||||
|
||||
/// An ICC file of an RGB profile named `name` (a built-in one with its description replaced by a
|
||||
@@ -785,7 +783,8 @@ fn color_settings_come_back_in_the_next_session() {
|
||||
let root = fresh_dir("color-settings");
|
||||
let (profiles, file) = (root.join("Color Profiles"), root.join("Color Settings.json"));
|
||||
let loaded = "StoreRGB-6";
|
||||
let icc = vectorcraft_testkit::temp_dir("store-color-settings").join("loaded.icc");
|
||||
let icc_dir = vectorcraft_testkit::TempDir::new("store-color-settings");
|
||||
let icc = icc_dir.join("loaded.icc");
|
||||
let _reset = ColorSettingsReset { profile: Some(loaded), paths: vec![root.clone(), icc.clone()] };
|
||||
std::fs::write(&icc, icc_named(loaded)).unwrap();
|
||||
|
||||
|
||||
@@ -12,10 +12,10 @@ use vectorcraft_testkit::invariants::{check_all, check_session, doc_json};
|
||||
use vectorcraft_testkit::strategies::{command_param_keys, junk_params_for_command, junk_values, param_keys, safe_value_for_command};
|
||||
|
||||
/// Commands that replace/close the active document or touch the filesystem are fine to call, but
|
||||
/// their file and folder params are redirected into a temp dir (`safe_value_for_command`, which
|
||||
/// `junk_params_for_command` applies, by the keys the command catalog names as files and folders).
|
||||
fn safe_path() -> String {
|
||||
vectorcraft_testkit::temp_dir("sweep").join("out.bin").to_string_lossy().to_string()
|
||||
/// their file and folder params are redirected into the test's temp dir `dir` (`safe_value_for_command`,
|
||||
/// which `junk_params_for_command` applies, by the keys the command catalog names as files and folders).
|
||||
fn safe_path(dir: &std::path::Path) -> String {
|
||||
dir.join("out.bin").to_string_lossy().to_string()
|
||||
}
|
||||
|
||||
/// `r`, after failing the case if the command panicked. The engine's guard reports a panic as
|
||||
@@ -219,7 +219,8 @@ fn junk_cases(c: &vectorcraft_engine::CommandSpec, path: &str) -> Vec<Value> {
|
||||
|
||||
#[test]
|
||||
fn every_command_with_junk_params() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let mut failures = vec![];
|
||||
let mut calls = 0usize;
|
||||
for fx in Fixture::ALL {
|
||||
@@ -311,8 +312,8 @@ fn names_location(spec: Option<&vectorcraft_engine::CommandSpec>, key: &str) ->
|
||||
/// command alone in at least one case ([`junk_cases`]).
|
||||
#[test]
|
||||
fn sweep_cases_name_files_only_inside_the_temp_folder() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::temp_dir("sweep");
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let mut unsent = vec![];
|
||||
let mut outside = std::collections::BTreeSet::new();
|
||||
for c in command_specs() {
|
||||
@@ -344,8 +345,8 @@ fn sweep_cases_name_files_only_inside_the_temp_folder() {
|
||||
/// `paths: [{ids, path}]`, each with a string in the temp folder, and a folder key a folder there.
|
||||
#[test]
|
||||
fn sweep_cases_send_the_file_keys_of_the_command_catalog() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::temp_dir("sweep");
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let spec = |id: &str| command_specs().iter().find(|c| c.id == id).unwrap();
|
||||
let inside = |v: &Value| v.as_str().is_some_and(|s| std::path::Path::new(s).starts_with(&dir));
|
||||
for (id, key) in [
|
||||
@@ -381,7 +382,8 @@ fn sweep_cases_send_the_file_keys_of_the_command_catalog() {
|
||||
/// document as it was, still applies after it.
|
||||
#[test]
|
||||
fn commands_that_do_not_wait_for_a_tool_edit_change_no_document() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let mut failures = vec![];
|
||||
for fx in [Fixture::Single, Fixture::Multi] {
|
||||
for c in command_specs().iter().filter(|c| !vectorcraft_engine::waits_for_tool_edit(c.id)) {
|
||||
@@ -1127,7 +1129,8 @@ fn serializable_cases(c: &vectorcraft_engine::CommandSpec, path: &str) -> Vec<Va
|
||||
/// After a successful fuzzed call, the document still round-trips and exports.
|
||||
#[test]
|
||||
fn fuzzed_calls_keep_documents_serializable() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let mut failures = vec![];
|
||||
for c in command_specs() {
|
||||
for p in serializable_cases(c, &path) {
|
||||
@@ -1196,7 +1199,8 @@ fn recovery_commands_with_a_damaged_store_and_junk_params() {
|
||||
] {
|
||||
store.write(name, bytes).unwrap();
|
||||
}
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
for fx in Fixture::ALL {
|
||||
for id in ["file.recovery.save", "file.recovery.list", "file.recovery.restore", "file.recovery.discard"] {
|
||||
let spec = command_specs().iter().find(|c| c.id == id).unwrap();
|
||||
@@ -1216,7 +1220,8 @@ fn recovery_commands_with_a_damaged_store_and_junk_params() {
|
||||
/// interaction or a broken tree.
|
||||
#[test]
|
||||
fn envelope_commands_with_junk_params_on_envelopes() {
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let makes: [(&str, Value); 3] = [
|
||||
("object.envelope.makeWithWarp", json!({"style": "twist", "bend": 80})),
|
||||
("object.envelope.makeWithMesh", json!({"rows": 2, "cols": 3})),
|
||||
@@ -1262,7 +1267,8 @@ fn envelope_commands_with_junk_params_on_envelopes() {
|
||||
#[test]
|
||||
fn text_commands_with_junk_params_on_inline_graphics() {
|
||||
use vectorcraft_testkit::fixtures::{ellipse, exec, id_of, select};
|
||||
let path = safe_path();
|
||||
let dir = vectorcraft_testkit::TempDir::new("sweep");
|
||||
let path = safe_path(&dir);
|
||||
let setup = || {
|
||||
let mut s = Fixture::Multi.session();
|
||||
let e = ellipse(&mut s, 400.0, 300.0, 20.0, 20.0);
|
||||
|
||||
@@ -29,10 +29,9 @@ fn finished(s: &mut Session) -> Value {
|
||||
|
||||
#[test]
|
||||
fn a_found_font_is_copied_without_replacing_a_file_and_its_type_redraws() {
|
||||
let root = vectorcraft_testkit::temp_dir("font-folder");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(root.join("Downloads/Deep")).unwrap();
|
||||
let root = plain(std::fs::canonicalize(&root).unwrap());
|
||||
let dir = vectorcraft_testkit::TempDir::new("font-folder");
|
||||
std::fs::create_dir_all(dir.join("Downloads/Deep")).unwrap();
|
||||
let root = plain(std::fs::canonicalize(&*dir).unwrap());
|
||||
let fonts = root.join("App Fonts");
|
||||
vectorcraft_text::set_app_font_dir(fonts.clone());
|
||||
assert!(vectorcraft_text::system_font_dirs().contains(&fonts));
|
||||
@@ -85,5 +84,4 @@ fn a_found_font_is_copied_without_replacing_a_file_and_its_type_redraws() {
|
||||
// Only files the last search found are copied.
|
||||
let r = s.execute("text.addFontFiles", &json!({"files": [root.join("other.ttf")]}));
|
||||
assert!(r.as_ref().is_err_and(|e| e.to_string().contains("the last text.findFontFiles search")), "{r:?}");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
@@ -6142,7 +6142,8 @@ fn findme_wanted() -> vectorcraft_text::WantedFonts {
|
||||
|
||||
/// Whether reading `bytes` as a font file a search reads panics.
|
||||
fn search_reads(what: &str, bytes: &[u8]) -> Result<Vec<usize>, TestCaseError> {
|
||||
let path = vectorcraft_testkit::temp_dir("font-search-fuzz").join(format!("{:?}.ttc", std::thread::current().id()));
|
||||
let dir = vectorcraft_testkit::TempDir::new("font-search-fuzz");
|
||||
let path = dir.join("font.ttc");
|
||||
std::fs::write(&path, bytes).unwrap();
|
||||
catch_quiet(|| findme_wanted().provided_by(&path)).map_err(|e| TestCaseError::fail(format!("{what}: a search reading the file panicked: {e}")))
|
||||
}
|
||||
|
||||
@@ -134,7 +134,8 @@ fn a_failed_batch_restores_the_file_the_history_and_a_drag_in_progress() {
|
||||
(doc_json(&st.doc), st.history.undo.len(), st.history.redo.len())
|
||||
};
|
||||
// Save As, then a failing step: the document keeps no file and stays modified.
|
||||
let path = vectorcraft_testkit::temp_dir("batch-rollback").join("named.vectorcraft").to_string_lossy().to_string();
|
||||
let dir = vectorcraft_testkit::TempDir::new("batch-rollback");
|
||||
let path = dir.join("named.vectorcraft").to_string_lossy().to_string();
|
||||
let save = json!({"command": "file.saveAs", "params": {"path": path}});
|
||||
assert!(s.execute("command.batch", &json!({"commands": [rect(0), save, fail]})).is_err());
|
||||
assert_eq!(state(&s), before);
|
||||
@@ -170,7 +171,7 @@ fn empty_batch_is_harmless() {
|
||||
#[test]
|
||||
fn save_open_via_commands_roundtrips() {
|
||||
let mut s = fixtures::rich_session();
|
||||
let dir = vectorcraft_testkit::temp_dir("engine-save");
|
||||
let dir = vectorcraft_testkit::TempDir::new("engine-save");
|
||||
let path = dir.join("rich.vectorcraft");
|
||||
exec(&mut s, "document.save", json!({"path": path.to_str().unwrap()}));
|
||||
let want = doc_json(&s.doc().unwrap().doc);
|
||||
|
||||
@@ -37,9 +37,7 @@ fn laid_out_in(s: &Session, id: u64) -> Vec<String> {
|
||||
|
||||
#[test]
|
||||
fn type_in_a_removed_font_falls_back_after_a_rescan() {
|
||||
let root = vectorcraft_testkit::temp_dir("removed-font");
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
std::fs::create_dir_all(&root).unwrap();
|
||||
let root = vectorcraft_testkit::TempDir::new("removed-font");
|
||||
vectorcraft_text::set_user_font_dirs(vec![root.clone()]);
|
||||
let file = root.join("Gonefont.otf");
|
||||
std::fs::write(&file, vectorcraft_testkit::fonts::renamed(FAMILY)).unwrap();
|
||||
@@ -72,5 +70,4 @@ fn type_in_a_removed_font_falls_back_after_a_rescan() {
|
||||
assert_eq!(status(&mut s), ("exact".into(), FAMILY.into()));
|
||||
assert!(listed(&mut s));
|
||||
assert_eq!(laid_out_in(&s, id), [FAMILY]);
|
||||
let _ = std::fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
@@ -85,6 +85,41 @@ pub fn temp_dir(tag: &str) -> std::path::PathBuf {
|
||||
dir
|
||||
}
|
||||
|
||||
/// A test's own temporary directory, removed with everything in it when dropped, also when the
|
||||
/// test panics. [`temp_dir`] makes it, with the same checks, under a tag no other `TempDir` in this
|
||||
/// process uses, so tests running alongside never share one. A removal that fails (a file still
|
||||
/// held open) is ignored; the next process with the same id empties the directory before use.
|
||||
pub struct TempDir(std::path::PathBuf);
|
||||
|
||||
impl TempDir {
|
||||
/// A new empty directory `vectorcraft-testkit-<tag>-<n>-<process id>` in the system temporary
|
||||
/// folder, where `n` counts the `TempDir`s made in this process.
|
||||
pub fn new(tag: &str) -> Self {
|
||||
static NEXT: std::sync::atomic::AtomicUsize = std::sync::atomic::AtomicUsize::new(0);
|
||||
let n = NEXT.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
|
||||
Self(temp_dir(&format!("{tag}-{n}")))
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for TempDir {
|
||||
type Target = std::path::PathBuf;
|
||||
fn deref(&self) -> &std::path::PathBuf {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<std::path::Path> for TempDir {
|
||||
fn as_ref(&self) -> &std::path::Path {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TempDir {
|
||||
fn drop(&mut self) {
|
||||
let _ = std::fs::remove_dir_all(&self.0);
|
||||
}
|
||||
}
|
||||
|
||||
/// How many shapes an SVG draws: its `<path>` elements and the basic shapes SVG export writes
|
||||
/// simple shapes as (`<rect>`, `<circle>`, `<ellipse>`, `<line>`, `<polyline>`, `<polygon>`).
|
||||
pub fn svg_shapes(svg: &str) -> usize {
|
||||
@@ -134,4 +169,40 @@ mod tests {
|
||||
assert!(super::temp_dir(tag).join("mine.txt").is_file(), "a later call keeps this process's files");
|
||||
std::fs::remove_dir_all(&dir).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_test_dir_is_removed_with_its_contents_when_dropped() {
|
||||
let dir = super::TempDir::new("dropped");
|
||||
let path = dir.to_path_buf();
|
||||
std::fs::create_dir_all(path.join("nested")).unwrap();
|
||||
std::fs::write(path.join("nested").join("a.txt"), b"a").unwrap();
|
||||
drop(dir);
|
||||
assert!(!path.exists(), "{} is left", path.display());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_test_dir_is_removed_when_its_test_panics() {
|
||||
let mut path = None;
|
||||
let r = super::catch_quiet::<()>(|| {
|
||||
let dir = super::TempDir::new("panicked");
|
||||
std::fs::write(dir.join("a.txt"), b"a").unwrap();
|
||||
path = Some(dir.to_path_buf());
|
||||
panic!("the test fails");
|
||||
});
|
||||
assert_eq!(r.err().as_deref(), Some("the test fails"));
|
||||
let path = path.unwrap();
|
||||
assert!(!path.exists(), "{} is left", path.display());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_dirs_with_one_tag_are_separate_and_private() {
|
||||
let (a, b) = (super::TempDir::new("shared-tag"), super::TempDir::new("shared-tag"));
|
||||
assert_ne!(*a, *b);
|
||||
std::fs::write(a.join("a.txt"), b"a").unwrap();
|
||||
assert_eq!(std::fs::read_dir(&*b).unwrap().count(), 0, "one test's files are not in another's directory");
|
||||
#[cfg(unix)]
|
||||
assert_eq!(std::os::unix::fs::PermissionsExt::mode(&std::fs::metadata(&*a).unwrap().permissions()) & 0o777, 0o700);
|
||||
drop(a);
|
||||
assert!(b.is_dir(), "dropping one leaves the other");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user