From f8e15a7980cb3bede12307e29dd42ca16b0877b5 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Mon, 9 Nov 2026 12:21:32 +0800 Subject: [PATCH] fix(plugins): give test selection the command budget (#2258) * fix(plugins): give test selection the command budget * test(plugins): bound test selection timeouts by the command budget --- crates/ide-host/src/plugins/execution.rs | 7 ++++--- crates/ide-host/tests/plugins.rs | 3 ++- plugins/README.md | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/crates/ide-host/src/plugins/execution.rs b/crates/ide-host/src/plugins/execution.rs index 0bd499d00..9c1ccf8ae 100644 --- a/crates/ide-host/src/plugins/execution.rs +++ b/crates/ide-host/src/plugins/execution.rs @@ -301,7 +301,7 @@ impl PluginHost { Ok(Some((root, owner.to_owned(), id.to_owned()))) } - /// Derives replacement argv under the parser's soft/hard call budgets. + /// Derives replacement argv for a user action, under the command budget. pub fn select_tests(&self, client: &str, config: &str, tests: Vec>) -> IdeResult>> { if tests.is_empty() || tests.len() > 10000 @@ -317,9 +317,10 @@ impl PluginHost { self.changed(); } let invocation = ide_plugin::Invocation { workspace_root: Some(root), ..Default::default() }; - let hard = self.limits.edit_hard; + // A user action, not the edit path: allow the command budget (a cold instance may be compiling). + let timeout = self.limits.timeout; let started = Instant::now(); - let result = actor.call(Some(self.limits.edit_soft), move |rt| rt.select_tests(id, tests, invocation, hard)); + let result = actor.call(Some(timeout), move |rt| rt.select_tests(id, tests, invocation, timeout)); let args = self.finish_provider_call(&plugin, &actor, started, result).flatten(); Ok(args.filter(|args| args.len() <= 10000 && args.iter().map(String::len).sum::() <= 4 << 20)) } diff --git a/crates/ide-host/tests/plugins.rs b/crates/ide-host/tests/plugins.rs index f29f872c6..749af1155 100644 --- a/crates/ide-host/tests/plugins.rs +++ b/crates/ide-host/tests/plugins.rs @@ -6058,7 +6058,8 @@ fn api14_test_selection_traps_and_timeouts_are_unsupported() { f.host.refresh_run_configurations(&f.project); let started = Instant::now(); assert_eq!(f.host.select_tests("main", "plugin:execution:dynamic", vec![vec![failure.into()]]).unwrap(), None); - assert!(started.elapsed() < Duration::from_secs(5)); + // Selection is a user action: bounded by the command budget, not the edit path. + assert!(started.elapsed() < Limits::default().timeout + Duration::from_secs(2)); } } diff --git a/plugins/README.md b/plugins/README.md index 50098a029..a758b5ccd 100644 --- a/plugins/README.md +++ b/plugins/README.md @@ -2389,8 +2389,9 @@ A plugin using `framework = "plugin"` can implement `TestResults::select_tests` configuration id as `parse` and a list of test-tree paths: the names from the plugin's `test-start` events, from parent suite to test, exactly as shown in the tree. Return the **full replacement argument list**, including any required base -arguments, to run only those tests. `None` (the Rust SDK default), errors and -call timeouts leave test selection unsupported. An empty argument list is a +arguments, to run only those tests. The call uses the 5-second command budget. +`None` (the Rust SDK default), errors and call timeouts leave test selection +unsupported. An empty argument list is a supported selection. The IDE preserves the configuration's command, environment, working directory and run target.