From 36add5930f690d3de2fa2a50aed7292b05664ddf Mon Sep 17 00:00:00 2001 From: ualtinok <94532+ualtinok@users.noreply.github.com> Date: Sun, 18 Oct 2026 02:58:27 +0200 Subject: [PATCH 1/2] mason: make configure applicability test event-based Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com> --- crates/aft/src/lsp/manager.rs | 25 +++++-- .../aft/tests/integration/configure_test.rs | 68 +++++++++---------- 2 files changed, 51 insertions(+), 42 deletions(-) diff --git a/crates/aft/src/lsp/manager.rs b/crates/aft/src/lsp/manager.rs index 1dc8276cd..9029ec9c7 100644 --- a/crates/aft/src/lsp/manager.rs +++ b/crates/aft/src/lsp/manager.rs @@ -7208,11 +7208,12 @@ pub fn walk_applicable_area( }) } -/// Test hook: stretch the applicability walk so a test can prove the walk -/// does not block other users of the language-server manager. Debug builds -/// only. `AFT_TEST_APPLICABILITY_WALK_DELAY_MS` is the delay in milliseconds; -/// when `AFT_TEST_APPLICABILITY_WALK_SIGNAL` names a file, it is written as the -/// delay starts so the test knows the walk is under way. +/// Test hook: hold the applicability walk so a test can prove that configure +/// does not wait for it. Debug builds only. `AFT_TEST_APPLICABILITY_WALK_DELAY_MS` +/// caps the hold in milliseconds. When `AFT_TEST_APPLICABILITY_WALK_SIGNAL` +/// names a file, it is written as the hold starts; when +/// `AFT_TEST_APPLICABILITY_WALK_RELEASE` names a file, the hook waits for it to +/// appear or until the cap expires. fn delay_applicability_walk_for_test() { #[cfg(debug_assertions)] if let Some(delay_ms) = std::env::var("AFT_TEST_APPLICABILITY_WALK_DELAY_MS") @@ -7222,7 +7223,19 @@ fn delay_applicability_walk_for_test() { if let Some(signal) = std::env::var_os("AFT_TEST_APPLICABILITY_WALK_SIGNAL") { let _ = std::fs::write(signal, b"walking"); } - std::thread::sleep(Duration::from_millis(delay_ms)); + if let Some(release) = std::env::var_os("AFT_TEST_APPLICABILITY_WALK_RELEASE") { + let release = PathBuf::from(release); + let deadline = Instant::now() + Duration::from_millis(delay_ms); + while !release.exists() { + let remaining = deadline.saturating_duration_since(Instant::now()); + if remaining.is_zero() { + break; + } + std::thread::sleep(remaining.min(Duration::from_millis(20))); + } + } else { + std::thread::sleep(Duration::from_millis(delay_ms)); + } } } diff --git a/crates/aft/tests/integration/configure_test.rs b/crates/aft/tests/integration/configure_test.rs index f8bcccc01..7b3ab2014 100644 --- a/crates/aft/tests/integration/configure_test.rs +++ b/crates/aft/tests/integration/configure_test.rs @@ -1047,10 +1047,9 @@ fn configure_rejects_malformed_lsp_paths_extra() { assert!(shutdown.success()); } -/// Configure used to walk the whole project on its bind path to decide which -/// language servers apply, so a large or busy checkout held the configure -/// reply, and with it a route bind's deadline, for as long as the walk took. -/// The walk now runs after the reply, on its own thread. +/// Configure's project applicability walk runs on its own thread. Holding the +/// walk at entry while waiting for configure's reply proves that the reply does +/// not depend on the walk completing. #[test] fn configure_answers_before_the_language_server_applicability_walk() { let temp_dir = tempfile::tempdir().expect("create fixture"); @@ -1058,47 +1057,44 @@ fn configure_answers_before_the_language_server_applicability_walk() { fs::create_dir_all(project.join("src")).expect("create project"); fs::write(project.join("src/main.rs"), "fn main() {}\n").expect("write source"); let signal = temp_dir.path().join("walk-started"); + let release = temp_dir.path().join("release-walk"); let mut aft = AftProcess::spawn_with_env(&[ ( "AFT_TEST_APPLICABILITY_WALK_DELAY_MS", - std::ffi::OsStr::new("10000"), + std::ffi::OsStr::new("120000"), ), ("AFT_TEST_APPLICABILITY_WALK_SIGNAL", signal.as_os_str()), + ("AFT_TEST_APPLICABILITY_WALK_RELEASE", release.as_os_str()), ]); - let started = Instant::now(); - let configure = aft.send_with_timeout( - &json!({ - "id": "cfg-applicability-walk", - "command": "configure", - "harness": "opencode", - "project_root": project, - "config": user_config(json!({ - "indexes": { "trigram": false, "semantic": false, "callgraph": false } - })), - }) - .to_string(), - Duration::from_secs(30), - ); - let elapsed = started.elapsed(); + let request = json!({ + "id": "cfg-applicability-walk", + "command": "configure", + "harness": "opencode", + "project_root": project, + "config": user_config(json!({ + "indexes": { "trigram": false, "semantic": false, "callgraph": false } + })), + }) + .to_string(); + thread::scope(|scope| { + let configure = scope.spawn(|| aft.send_with_timeout(&request, Duration::from_secs(30))); + let deadline = Instant::now() + Duration::from_secs(30); + while !signal.exists() { + assert!( + Instant::now() < deadline, + "the applicability walk never started after configure" + ); + thread::sleep(Duration::from_millis(20)); + } - assert_eq!( - configure["success"], true, - "configure failed: {configure:#}" - ); - assert!( - elapsed < Duration::from_secs(1), - "configure waited {elapsed:?} for a 10 s applicability walk" - ); - // The walk still runs, after the reply. - let deadline = Instant::now() + Duration::from_secs(10); - while !signal.exists() { - assert!( - Instant::now() < deadline, - "the applicability walk never started after configure" + let configure = configure.join().expect("configure request thread panicked"); + assert_eq!( + configure["success"], true, + "configure failed: {configure:#}" ); - thread::sleep(Duration::from_millis(20)); - } + fs::write(&release, b"release").expect("release applicability walk"); + }); let shutdown = aft.shutdown(); assert!(shutdown.success()); } From 2ada8b1d359b161e0f8dffaca22ddfdfca5bcff7 Mon Sep 17 00:00:00 2001 From: ualtinok <94532+ualtinok@users.noreply.github.com> Date: Sun, 18 Oct 2026 03:13:41 +0200 Subject: [PATCH 2/2] release notes and search-quality descriptor for configure-walk-event-test Co-authored-by: alfonso-aft <289616620+alfonso-aft@users.noreply.github.com> --- .../slice-descriptors/land-configure-walk-event-test.json | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 benchmarks/aft-search/slice-descriptors/land-configure-walk-event-test.json diff --git a/benchmarks/aft-search/slice-descriptors/land-configure-walk-event-test.json b/benchmarks/aft-search/slice-descriptors/land-configure-walk-event-test.json new file mode 100644 index 000000000..ebd574711 --- /dev/null +++ b/benchmarks/aft-search/slice-descriptors/land-configure-walk-event-test.json @@ -0,0 +1,8 @@ +{ + "targeted_mechanism": "none", + "kind": "harness", + "slice_class": "non_ranking", + "fixtures": [ + "harness-goldens" + ] +}