From 1d74fdc59f244bce89e5f0e49e041dc3fe1a4582 Mon Sep 17 00:00:00 2001 From: Cathal Date: Tue, 1 Jul 2025 21:23:55 +0100 Subject: [PATCH] debugger: Filter test executables by metadata profile in Cargo locator (#33126) Closes #33114 Release Notes: - debugger: Ensure Cargo locator only targets relevant executables. --- crates/project/src/debugger/locators/cargo.rs | 43 ++++++++++++++++--- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/crates/project/src/debugger/locators/cargo.rs b/crates/project/src/debugger/locators/cargo.rs index caa563eb6b88b3f1c2692dab36897e7094186987..bad7dfe9f8947810346c06493d47d5f0b4c89c22 100644 --- a/crates/project/src/debugger/locators/cargo.rs +++ b/crates/project/src/debugger/locators/cargo.rs @@ -4,9 +4,11 @@ use dap::{DapLocator, DebugRequest, adapters::DebugAdapterName}; use gpui::SharedString; use serde_json::{Value, json}; use smol::{ + Timer, io::AsyncReadExt, process::{Command, Stdio}, }; +use std::time::Duration; use task::{BuildTaskDefinition, DebugScenario, ShellBuilder, SpawnInTerminal, TaskTemplate}; pub(crate) struct CargoLocator; @@ -25,14 +27,29 @@ async fn find_best_executable(executables: &[String], test_name: &str) -> Option continue; }; let mut test_lines = String::default(); - if let Some(mut stdout) = child.stdout.take() { - stdout.read_to_string(&mut test_lines).await.ok(); + let exec_result = smol::future::race( + async { + if let Some(mut stdout) = child.stdout.take() { + stdout.read_to_string(&mut test_lines).await?; + } + Ok(()) + }, + async { + Timer::after(Duration::from_secs(3)).await; + anyhow::bail!("Timed out waiting for executable stdout") + }, + ); + + if let Err(err) = exec_result.await { + log::warn!("Failed to list tests for {executable}: {err}"); + } else { for line in test_lines.lines() { if line.contains(&test_name) { return Some(executable.clone()); } } } + let _ = child.kill(); } None } @@ -126,10 +143,28 @@ impl DapLocator for CargoLocator { let status = child.status().await?; anyhow::ensure!(status.success(), "Cargo command failed"); + let is_test = build_config + .args + .first() + .map_or(false, |arg| arg == "test" || arg == "t"); + let executables = output .lines() .filter(|line| !line.trim().is_empty()) .filter_map(|line| serde_json::from_str(line).ok()) + .filter(|json: &Value| { + let is_test_binary = json + .get("profile") + .and_then(|profile| profile.get("test")) + .and_then(Value::as_bool) + .unwrap_or(false); + + if is_test { + is_test_binary + } else { + !is_test_binary + } + }) .filter_map(|json: Value| { json.get("executable") .and_then(Value::as_str) @@ -140,10 +175,6 @@ impl DapLocator for CargoLocator { !executables.is_empty(), "Couldn't get executable in cargo locator" ); - let is_test = build_config - .args - .first() - .map_or(false, |arg| arg == "test" || arg == "t"); let mut test_name = None; if is_test {