Skip to content

Commit 07ae540

Browse files
committed
Auto merge of #17056 - HKalbasi:test-explorer, r=HKalbasi
Run cargo test per workspace in the test explorer fix #16875 fix #17022
2 parents 657b33b + 964afc9 commit 07ae540

File tree

6 files changed

+60
-29
lines changed

6 files changed

+60
-29
lines changed

crates/flycheck/src/command.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,13 @@
44
use std::{
55
ffi::OsString,
66
fmt, io,
7+
marker::PhantomData,
78
path::PathBuf,
89
process::{ChildStderr, ChildStdout, Command, Stdio},
910
};
1011

1112
use command_group::{CommandGroup, GroupChild};
12-
use crossbeam_channel::{unbounded, Receiver, Sender};
13+
use crossbeam_channel::Sender;
1314
use stdx::process::streaming_output;
1415

1516
/// Cargo output is structured as a one JSON per line. This trait abstracts parsing one line of
@@ -99,10 +100,10 @@ pub(crate) struct CommandHandle<T> {
99100
/// a read syscall dropping and therefore terminating the process is our best option.
100101
child: JodGroupChild,
101102
thread: stdx::thread::JoinHandle<io::Result<(bool, String)>>,
102-
pub(crate) receiver: Receiver<T>,
103103
program: OsString,
104104
arguments: Vec<OsString>,
105105
current_dir: Option<PathBuf>,
106+
_phantom: PhantomData<T>,
106107
}
107108

108109
impl<T> fmt::Debug for CommandHandle<T> {
@@ -116,7 +117,7 @@ impl<T> fmt::Debug for CommandHandle<T> {
116117
}
117118

118119
impl<T: ParseFromLine> CommandHandle<T> {
119-
pub(crate) fn spawn(mut command: Command) -> std::io::Result<Self> {
120+
pub(crate) fn spawn(mut command: Command, sender: Sender<T>) -> std::io::Result<Self> {
120121
command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
121122
let mut child = command.group_spawn().map(JodGroupChild)?;
122123

@@ -127,13 +128,12 @@ impl<T: ParseFromLine> CommandHandle<T> {
127128
let stdout = child.0.inner().stdout.take().unwrap();
128129
let stderr = child.0.inner().stderr.take().unwrap();
129130

130-
let (sender, receiver) = unbounded();
131131
let actor = CargoActor::<T>::new(sender, stdout, stderr);
132132
let thread = stdx::thread::Builder::new(stdx::thread::ThreadIntent::Worker)
133133
.name("CommandHandle".to_owned())
134134
.spawn(move || actor.run())
135135
.expect("failed to spawn thread");
136-
Ok(CommandHandle { program, arguments, current_dir, child, thread, receiver })
136+
Ok(CommandHandle { program, arguments, current_dir, child, thread, _phantom: PhantomData })
137137
}
138138

139139
pub(crate) fn cancel(mut self) {

crates/flycheck/src/lib.rs

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,8 @@ struct FlycheckActor {
215215
/// have to wrap sub-processes output handling in a thread and pass messages
216216
/// back over a channel.
217217
command_handle: Option<CommandHandle<CargoCheckMessage>>,
218+
/// The receiver side of the channel mentioned above.
219+
command_receiver: Option<Receiver<CargoCheckMessage>>,
218220
}
219221

220222
enum Event {
@@ -240,6 +242,7 @@ impl FlycheckActor {
240242
sysroot_root,
241243
root: workspace_root,
242244
command_handle: None,
245+
command_receiver: None,
243246
}
244247
}
245248

@@ -248,14 +251,13 @@ impl FlycheckActor {
248251
}
249252

250253
fn next_event(&self, inbox: &Receiver<StateChange>) -> Option<Event> {
251-
let check_chan = self.command_handle.as_ref().map(|cargo| &cargo.receiver);
252254
if let Ok(msg) = inbox.try_recv() {
253255
// give restarts a preference so check outputs don't block a restart or stop
254256
return Some(Event::RequestStateChange(msg));
255257
}
256258
select! {
257259
recv(inbox) -> msg => msg.ok().map(Event::RequestStateChange),
258-
recv(check_chan.unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
260+
recv(self.command_receiver.as_ref().unwrap_or(&never())) -> msg => Some(Event::CheckEvent(msg.ok())),
259261
}
260262
}
261263

@@ -284,10 +286,12 @@ impl FlycheckActor {
284286
let formatted_command = format!("{:?}", command);
285287

286288
tracing::debug!(?command, "will restart flycheck");
287-
match CommandHandle::spawn(command) {
289+
let (sender, receiver) = unbounded();
290+
match CommandHandle::spawn(command, sender) {
288291
Ok(command_handle) => {
289292
tracing::debug!(command = formatted_command, "did restart flycheck");
290293
self.command_handle = Some(command_handle);
294+
self.command_receiver = Some(receiver);
291295
self.report_progress(Progress::DidStart);
292296
}
293297
Err(error) => {
@@ -303,6 +307,7 @@ impl FlycheckActor {
303307

304308
// Watcher finished
305309
let command_handle = self.command_handle.take().unwrap();
310+
self.command_receiver.take();
306311
let formatted_handle = format!("{:?}", command_handle);
307312

308313
let res = command_handle.join();

crates/flycheck/src/test_runner.rs

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
44
use std::process::Command;
55

6-
use crossbeam_channel::Receiver;
6+
use crossbeam_channel::Sender;
7+
use paths::AbsPath;
78
use serde::Deserialize;
89
use toolchain::Tool;
910

@@ -54,31 +55,34 @@ impl ParseFromLine for CargoTestMessage {
5455

5556
#[derive(Debug)]
5657
pub struct CargoTestHandle {
57-
handle: CommandHandle<CargoTestMessage>,
58+
_handle: CommandHandle<CargoTestMessage>,
5859
}
5960

6061
// Example of a cargo test command:
6162
// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
6263

6364
impl CargoTestHandle {
64-
pub fn new(path: Option<&str>, options: CargoOptions) -> std::io::Result<Self> {
65+
pub fn new(
66+
path: Option<&str>,
67+
options: CargoOptions,
68+
root: &AbsPath,
69+
sender: Sender<CargoTestMessage>,
70+
) -> std::io::Result<Self> {
6571
let mut cmd = Command::new(Tool::Cargo.path());
6672
cmd.env("RUSTC_BOOTSTRAP", "1");
6773
cmd.arg("test");
6874
cmd.arg("--workspace");
6975
// --no-fail-fast is needed to ensure that all requested tests will run
7076
cmd.arg("--no-fail-fast");
77+
cmd.arg("--manifest-path");
78+
cmd.arg(root.join("Cargo.toml"));
7179
options.apply_on_command(&mut cmd);
7280
cmd.arg("--");
7381
if let Some(path) = path {
7482
cmd.arg(path);
7583
}
7684
cmd.args(["-Z", "unstable-options"]);
7785
cmd.arg("--format=json");
78-
Ok(Self { handle: CommandHandle::spawn(cmd)? })
79-
}
80-
81-
pub fn receiver(&self) -> &Receiver<CargoTestMessage> {
82-
&self.handle.receiver
86+
Ok(Self { _handle: CommandHandle::spawn(cmd, sender)? })
8387
}
8488
}

crates/rust-analyzer/src/global_state.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,10 @@ pub(crate) struct GlobalState {
8686
pub(crate) last_flycheck_error: Option<String>,
8787

8888
// Test explorer
89-
pub(crate) test_run_session: Option<flycheck::CargoTestHandle>,
89+
pub(crate) test_run_session: Option<Vec<flycheck::CargoTestHandle>>,
90+
pub(crate) test_run_sender: Sender<flycheck::CargoTestMessage>,
91+
pub(crate) test_run_receiver: Receiver<flycheck::CargoTestMessage>,
92+
pub(crate) test_run_remaining_jobs: usize,
9093

9194
// VFS
9295
pub(crate) loader: Handle<Box<dyn vfs::loader::Handle>, Receiver<vfs::loader::Message>>,
@@ -191,6 +194,7 @@ impl GlobalState {
191194
analysis_host.update_lru_capacities(capacities);
192195
}
193196
let (flycheck_sender, flycheck_receiver) = unbounded();
197+
let (test_run_sender, test_run_receiver) = unbounded();
194198
let mut this = GlobalState {
195199
sender,
196200
req_queue: ReqQueue::default(),
@@ -219,6 +223,9 @@ impl GlobalState {
219223
last_flycheck_error: None,
220224

221225
test_run_session: None,
226+
test_run_sender,
227+
test_run_receiver,
228+
test_run_remaining_jobs: 0,
222229

223230
vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
224231
vfs_config_version: 0,

crates/rust-analyzer/src/handlers/request.rs

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -219,14 +219,28 @@ pub(crate) fn handle_run_test(
219219
.unwrap_or_default(),
220220
None => "".to_owned(),
221221
};
222-
let handle = if lca.is_empty() {
223-
flycheck::CargoTestHandle::new(None, state.config.cargo_test_options())
222+
let test_path = if lca.is_empty() {
223+
None
224224
} else if let Some((_, path)) = lca.split_once("::") {
225-
flycheck::CargoTestHandle::new(Some(path), state.config.cargo_test_options())
225+
Some(path)
226226
} else {
227-
flycheck::CargoTestHandle::new(None, state.config.cargo_test_options())
227+
None
228228
};
229-
state.test_run_session = Some(handle?);
229+
let mut handles = vec![];
230+
for ws in &*state.workspaces {
231+
if let ProjectWorkspace::Cargo { cargo, .. } = ws {
232+
let handle = flycheck::CargoTestHandle::new(
233+
test_path,
234+
state.config.cargo_test_options(),
235+
cargo.workspace_root(),
236+
state.test_run_sender.clone(),
237+
)?;
238+
handles.push(handle);
239+
}
240+
}
241+
// Each process send finished signal twice, once for stdout and once for stderr
242+
state.test_run_remaining_jobs = 2 * handles.len();
243+
state.test_run_session = Some(handles);
230244
Ok(())
231245
}
232246

crates/rust-analyzer/src/main_loop.rs

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77
};
88

99
use always_assert::always;
10-
use crossbeam_channel::{never, select, Receiver};
10+
use crossbeam_channel::{select, Receiver};
1111
use ide_db::base_db::{SourceDatabase, SourceDatabaseExt, VfsPath};
1212
use lsp_server::{Connection, Notification, Request};
1313
use lsp_types::{notification::Notification as _, TextDocumentIdentifier};
@@ -220,7 +220,7 @@ impl GlobalState {
220220
recv(self.flycheck_receiver) -> task =>
221221
Some(Event::Flycheck(task.unwrap())),
222222

223-
recv(self.test_run_session.as_ref().map(|s| s.receiver()).unwrap_or(&never())) -> task =>
223+
recv(self.test_run_receiver) -> task =>
224224
Some(Event::TestResult(task.unwrap())),
225225

226226
}
@@ -337,9 +337,7 @@ impl GlobalState {
337337
.entered();
338338
self.handle_cargo_test_msg(message);
339339
// Coalesce many test result event into a single loop turn
340-
while let Some(message) =
341-
self.test_run_session.as_ref().and_then(|r| r.receiver().try_recv().ok())
342-
{
340+
while let Ok(message) = self.test_run_receiver.try_recv() {
343341
self.handle_cargo_test_msg(message);
344342
}
345343
}
@@ -792,8 +790,11 @@ impl GlobalState {
792790
}
793791
flycheck::CargoTestMessage::Suite => (),
794792
flycheck::CargoTestMessage::Finished => {
795-
self.send_notification::<lsp_ext::EndRunTest>(());
796-
self.test_run_session = None;
793+
self.test_run_remaining_jobs = self.test_run_remaining_jobs.saturating_sub(1);
794+
if self.test_run_remaining_jobs == 0 {
795+
self.send_notification::<lsp_ext::EndRunTest>(());
796+
self.test_run_session = None;
797+
}
797798
}
798799
flycheck::CargoTestMessage::Custom { text } => {
799800
self.send_notification::<lsp_ext::AppendOutputToRunTest>(text);

0 commit comments

Comments
 (0)