#1290: a launch-list row waits for a re-check's answer

FredApps on #2689: a re-check started while the launch list is up (a
library not found looked for again, the window back to the front) never
marked its row as checking, so `apply_answers` skipped the new answer: a
library plugged back in stayed "not found" in the list. A row is re-armed
when its path is asked again about an answer it has (not after a failed
open, whose reason stays on the row). Test
a_library_found_again_comes_back_to_the_launch_list fails without it.

Fixes #2774

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JbBu4MRiaKxEbSrMTZiLj8
This commit is contained in:
francoisgn
2026-10-24 10:39:09 +03:00
co-authored by Claude Opus 5.5
parent 2ae63a189a
commit f1f169ede1
2 changed files with 52 additions and 0 deletions
@@ -345,6 +345,9 @@ fn start_checks(app: &mut LightcraftApp, paths: Vec<String>) {
if app.recent_checks.pending.contains_key(&path) {
continue;
}
// asked again about an answer it has (not after a failed open, which dropped it and says why
// on its row)
let recheck = app.recent_checks.known.contains_key(&path);
let check = app.recent_checks.check.clone();
let p = path.clone();
let started = crate::tasks::spawn(app, CHECK_LABEL, Some("library"), move || check(&p), {
@@ -358,6 +361,16 @@ fn start_checks(app: &mut LightcraftApp, paths: Vec<String>) {
});
match started {
Ok(()) => {
// a row of the launch list waits for this answer again (a library not found looked for
// again, a window back to the front): without it, the row kept its old answer
if recheck
&& let Some(l) =
app.library_problem.as_mut().and_then(|p| p.chooser.as_mut()).and_then(|c| c.libraries.iter_mut().find(|l| l.path == path))
{
l.checking = true;
l.slow = false;
l.unavailable = None;
}
app.recent_checks.pending.insert(path, web_time::Instant::now());
}
// no worker thread: say so on the row, and don't try again every frame
@@ -950,3 +950,42 @@ fn recent_libraries_are_checked_again_only_when_the_window_comes_back() {
drop(h);
let _ = std::fs::remove_dir_all(&dir);
}
/// A library the launch list showed as not found comes back to it once found again (a drive plugged
/// back in while the list is up): the row waits for the new check instead of keeping its old answer
/// (FredApps on #2689, #2774).
#[test]
fn a_library_found_again_comes_back_to_the_launch_list() {
use std::sync::atomic::{AtomicBool, Ordering};
let dir = temp("found-again");
let a = make_library(&dir.join("A"));
let plugged = std::sync::Arc::new(AtomicBool::new(false));
let p = plugged.clone();
let mut app = LightcraftApp::new(lightcraft_engine::Session::new().with_fs(), Services::default());
app.recent_checks = crate::panels::library_problem::RecentChecks::with_check(move |_| {
(!p.load(Ordering::SeqCst)).then_some(crate::panels::library_problem::Unavailable::NotFound)
})
.recheck_after(Duration::ZERO);
app.ui.settings.recent_libraries = vec![a.clone()];
app.library_problem = Some(LibraryProblem::choose_later(&app.ui.settings));
let mut h = Headless::new(app, [1300.0, 900.0], 1.0);
let row = |h: &Headless| h.app.library_problem.as_ref().and_then(|p| p.chooser.as_ref()).map(|c| c.libraries[0].clone()).unwrap();
assert!(
h.step_until(T, |h| {
let r = row(h);
!r.checking && r.unavailable.is_some()
}),
"not found first"
);
plugged.store(true, Ordering::SeqCst);
assert!(
h.step_until(T, |h| {
let r = row(h);
!r.checking && r.unavailable.is_none()
}),
"found again: it can be opened, {:?}",
row(&h)
);
drop(h);
let _ = std::fs::remove_dir_all(&dir);
}