Issue #3405: Auto Import also waits for a scan, and its race tests apply the listing on cue

A Local browse that finishes replaces app.import, so Auto Import now waits for import::running (an import, a scan or a pending review) both before listing and before applying a listing. The tests apply the listing with tasks::wait_for while the other import or browse runs instead of relying on frame order.
This commit is contained in:
Antonio Sokic
2026-10-30 20:28:49 +01:00
parent 5260206fd4
commit 1c1bb4f632
2 changed files with 40 additions and 12 deletions
+5 -4
View File
@@ -85,7 +85,7 @@ impl LightcraftApp {
if let Some(folder) = self.session.import_defaults.auto_folder.clone() {
const LABEL: &str = "Auto Import";
if now - self.ui.auto_import_at >= 3.0
&& self.import.is_none()
&& !import::running(self)
&& self.sync_run.is_none()
&& self.lightroom.is_none()
&& !self.tasks.is_running(LABEL)
@@ -97,9 +97,10 @@ impl LightcraftApp {
Ok(l) => l,
Err(e) => return log::debug!("auto import: {e}"),
};
// an import, sync or Lightroom migration that started while the folder was listed: scanning
// now would mark the new files as tried without importing them; the next listing picks them up
if app.import.is_some() || app.sync_run.is_some() || app.lightroom.is_some() {
// an import, a scan (a finished Local browse replaces `app.import`), a sync or a Lightroom
// migration that started while the folder was listed: scanning now would mark the new files as
// tried without importing them; the next listing picks them up
if import::running(app) || app.sync_run.is_some() || app.lightroom.is_some() {
return;
}
let p = serde_json::json!({"listing": listing, "start": false});
+35 -8
View File
@@ -1108,21 +1108,48 @@ fn public_drop_take_preserves_reentered_barrier_order_and_clears_protection() {
}
}
/// Auto Import's second listing of `watch/w.png` on its worker thread (the first notes the file's size; the
/// second, size unchanged, imports it).
fn second_auto_import_listing(h: &mut Headless, dir: &Scratch) {
write_png(&dir.path("watch/w.png"), 3);
h.app.session.import_defaults.auto_folder = Some(dir.path("watch"));
assert!(h.step_until(T, |h| h.app.tasks.is_running("Auto Import")), "the watched folder is listed");
assert!(h.step_until(T, |h| !h.app.tasks.is_running("Auto Import")));
assert_eq!(photos(h), 0);
assert!(h.step_until(T, |h| h.app.tasks.is_running("Auto Import")), "and listed again");
}
/// Apply Auto Import's listing now, before another frame can change what runs.
fn apply_auto_import_listing(h: &mut Headless) {
let ctx = h.view.ctx.clone();
assert!(crate::tasks::wait_for(&mut h.app, &ctx, "Auto Import", T), "the listing ends");
}
/// An import that starts while Auto Import lists its watched folder: the new photo isn't marked as
/// tried and skipped, the next listing imports it once the other import is done.
#[test]
fn auto_import_waits_for_an_import_that_starts_during_its_listing() {
let dir = Scratch::new("auto-race");
let mut h = app(&dir);
write_png(&dir.path("watch/w.png"), 3);
h.app.session.import_defaults.auto_folder = Some(dir.path("watch"));
// the first listing notes the file's size, the second (size unchanged) imports it
assert!(h.step_until(T, |h| h.app.tasks.is_running("Auto Import")), "the watched folder is listed");
assert!(h.step_until(T, |h| !h.app.tasks.is_running("Auto Import")));
assert_eq!(photos(&h), 0);
assert!(h.step_until(T, |h| h.app.tasks.is_running("Auto Import")), "and listed again");
// the listing's result is applied on a later frame, after this import has started
second_auto_import_listing(&mut h, &dir);
h.app.import = Some(crate::import::ImportTask::new(vec![dir.path("trip")], json!({"mode": "add"}), 0, false));
apply_auto_import_listing(&mut h);
assert!(h.step_until(T, |h| photos(h) == 3), "the trip's two photos and the watched one arrive: {}", photos(&h));
h.app.session.import_defaults.auto_folder = None;
}
/// The same with a Local folder browse: a browse that finishes replaces `app.import`, so Auto Import
/// doesn't start one beside it.
#[test]
fn auto_import_waits_for_a_browse_that_starts_during_its_listing() {
let dir = Scratch::new("auto-browse");
let mut h = app(&dir);
second_auto_import_listing(&mut h, &dir);
let scan = crate::import::stall_scan_of(&mut h.app, true);
apply_auto_import_listing(&mut h);
assert!(h.app.import.is_none(), "no Auto Import for the browse to replace");
drop(scan);
assert!(h.step_until(T, |h| h.app.scan.is_none()), "the browse ends");
assert!(h.step_until(T, |h| photos(h) == 1), "and the watched photo arrives: {}", photos(&h));
h.app.session.import_defaults.auto_folder = None;
}