From e3d2654bd2918928c90509f1f4ccc56f89126539 Mon Sep 17 00:00:00 2001 From: mahmatias Date: Sun, 18 Oct 2026 11:41:14 -0300 Subject: [PATCH 1/3] fix(tests): make Windows path spellings match folder-tree widget ids The folder tree writes FolderNode.path with forward slashes on every platform, but tests_folder_drop built its widget ids from to_string_lossy, which gives backslashes on Windows: the 8 tests never found their rows there. row_path() normalizes the way tests_sync already does. Same class of fix in tests_export (path comparison) and an unused-import guard in catalog's staging tests (the module's only test is unix-only). --- crates/catalog/src/store.rs | 2 ++ crates/engine/src/tests_export.rs | 9 +++++++-- crates/ui-egui/src/tests_folder_drop.rs | 16 +++++++++++----- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/crates/catalog/src/store.rs b/crates/catalog/src/store.rs index 2f3dc4912..7382022cf 100644 --- a/crates/catalog/src/store.rs +++ b/crates/catalog/src/store.rs @@ -244,6 +244,8 @@ impl Store for MemStore { #[cfg(test)] mod tests_staging { + // the module's only test is unix-only; the import would be unused on windows + #[cfg(unix)] use super::*; /// A link planted at a staging name (`.tmp`) is removed, never followed: the file it points to keeps its diff --git a/crates/engine/src/tests_export.rs b/crates/engine/src/tests_export.rs index 0ac8df804..e7afc1a8b 100644 --- a/crates/engine/src/tests_export.rs +++ b/crates/engine/src/tests_export.rs @@ -991,8 +991,13 @@ fn same_folder_as_original_exports_next_to_each_photo() { let exists = |p: &str| std::path::Path::new(p).exists(); let files = crate::export::export_batch(&mut s, &ids, &o, &to, &mut crate::export::write_file, &exists).unwrap(); let written: Vec<&str> = files.iter().map(|f| f["path"].as_str().unwrap()).collect(); - let mut want = vec![a.join("Exports/IMG_1.jpg").to_string_lossy().to_string(), b.join("Exports/IMG_1.jpg").to_string_lossy().to_string()]; - let mut got: Vec = written.iter().map(|p| p.to_string()).collect(); + let norm = |s: &str| s.replace('\\', "/"); + let mut want: Vec = + [a.join("Exports/IMG_1.jpg").to_string_lossy().to_string(), b.join("Exports/IMG_1.jpg").to_string_lossy().to_string()] + .iter() + .map(|p| norm(p)) + .collect(); + let mut got: Vec = written.iter().map(|p| norm(p)).collect(); want.sort(); got.sort(); assert_eq!(got, want); diff --git a/crates/ui-egui/src/tests_folder_drop.rs b/crates/ui-egui/src/tests_folder_drop.rs index 9b4cb860a..f4a761cec 100644 --- a/crates/ui-egui/src/tests_folder_drop.rs +++ b/crates/ui-egui/src/tests_folder_drop.rs @@ -50,8 +50,14 @@ fn widget(h: &Headless, id: &str) -> Option { h.app.widgets.iter().find(|(w, _)| w == id).map(|(_, r)| *r) } +/// A folder's path as its row writes it: `FolderNode.path` uses `/` on every platform, while +/// `to_string_lossy` gives Windows `\`. +fn row_path(dir: &Path) -> String { + dir.to_string_lossy().replace('\\', "/") +} + fn folder_row(h: &Headless, dir: &Path) -> Option { - widget(h, &format!("source:libfolder:{}", dir.to_string_lossy())) + widget(h, &format!("source:libfolder:{}", row_path(dir))) } fn id_of(h: &Headless, file: &str) -> u64 { @@ -180,7 +186,7 @@ fn photos_already_in_the_folder_stay_put() { let (root, trip, home) = folders("here"); let mut h = app(&[(trip.join("beach.png"), false), (home.join("garden.png"), false)]); // open the folder holding both - let toggle = widget(&h, &format!("libraryFolderToggle:{}", root.to_string_lossy())).unwrap(); + let toggle = widget(&h, &format!("libraryFolderToggle:{}", row_path(&root))).unwrap(); h.app.synthetic.push(egui::Event::PointerMoved(toggle.center())); h.app.synthetic.push(egui::Event::PointerButton { pos: toggle.center(), @@ -284,7 +290,7 @@ fn cancelling_the_question_moves_nothing() { let target = if folder_row(&h, &home).is_some() { target } else { - click(&mut h, &format!("libraryFolderToggle:{}", root.to_string_lossy())); + click(&mut h, &format!("libraryFolderToggle:{}", row_path(&root))); folder_row(&h, &home).unwrap() }; let undo = h.app.session.undo.len(); @@ -302,7 +308,7 @@ fn dont_ask_again_moves_the_next_drop_at_once() { let (root, trip, home) = folders("dont-ask"); std::fs::copy(trip.join("beach.png"), trip.join("dunes.png")).unwrap(); let mut h = app(&[(trip.join("beach.png"), false), (trip.join("dunes.png"), false), (home.join("garden.png"), false)]); - click(&mut h, &format!("libraryFolderToggle:{}", root.to_string_lossy())); + click(&mut h, &format!("libraryFolderToggle:{}", row_path(&root))); let target = folder_row(&h, &home).unwrap(); let beach = id_of(&h, "beach.png"); h.app.session.selection = lightcraft_engine::Selection::single(lightcraft_catalog::PhotoId(beach)); @@ -327,7 +333,7 @@ fn dont_ask_again_moves_the_next_drop_at_once() { fn a_question_left_open_while_another_library_opens_moves_nothing() { let (root, trip, home) = folders("other-library"); let mut h = app(&[(trip.join("beach.png"), false), (home.join("garden.png"), false)]); - click(&mut h, &format!("libraryFolderToggle:{}", root.to_string_lossy())); + click(&mut h, &format!("libraryFolderToggle:{}", row_path(&root))); let beach = id_of(&h, "beach.png"); let target = folder_row(&h, &home).unwrap(); drop_beach(&mut h, beach, target); From 54c65051919df8f79ef80a33edece1e21587e039 Mon Sep 17 00:00:00 2001 From: mahmatias Date: Mon, 19 Oct 2026 10:56:13 -0300 Subject: [PATCH 2/3] test(slideshow): drop the template cache after the hand-written bad files The folder's mtime may not move from the save's listing to the list that follows (same clock tick on a fast machine), so the stale cache hid the bad files and problems came back empty - deterministic on a warm build. The test drops the cache as its own wild.json case already did (and as templates_changed documents). --- crates/engine/src/tests_slide_template.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/engine/src/tests_slide_template.rs b/crates/engine/src/tests_slide_template.rs index f7a899c43..9d3e9c807 100644 --- a/crates/engine/src/tests_slide_template.rs +++ b/crates/engine/src/tests_slide_template.rs @@ -248,6 +248,9 @@ fn bad_files_are_reported_and_skipped() { } std::fs::create_dir_all(folder.join("folder.json")).unwrap(); std::fs::write(folder.join("notes.txt"), b"ignored").unwrap(); + // written by hand, not through LightCraft: the folder's time may not have moved since the + // save's listing (same clock tick), so the cached listing is dropped as a write would drop it + s.slide_templates_cache = None; let r = run(&mut s, "slideshow.templates.list", json!({})); let user: Vec<_> = r["templates"].as_array().unwrap().iter().filter(|t| t["builtin"] == false).collect(); assert_eq!(user.len(), 1, "only the good one: {r}"); From 4c4f4ca2150bce902c87a4ab6d46c510cf5d15b7 Mon Sep 17 00:00:00 2001 From: mahmatias Date: Mon, 19 Oct 2026 13:21:18 -0300 Subject: [PATCH 3/3] Contributors: add mahmatias --- contributors/people.toml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/contributors/people.toml b/contributors/people.toml index 08d002e8b..6f0bd6085 100644 --- a/contributors/people.toml +++ b/contributors/people.toml @@ -57,3 +57,7 @@ sync_github_name = false [people.egegl] real_name = "Ege Gursel" sync_github_name = true + +[people.mahmatias] +display_name = "Márcio Matias de Andrade" +sync_github_name = true