Skip to content

#17470 - run unit tests at the crate level not workspace #17472

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion crates/flycheck/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ pub mod project_json;
mod test_runner;

use command::{CommandHandle, ParseFromLine};
pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState};
pub use test_runner::{CargoTestHandle, CargoTestMessage, TestState, TestTarget};

#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
pub enum InvocationStrategy {
Expand Down
23 changes: 21 additions & 2 deletions crates/flycheck/src/test_runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,19 +59,38 @@ pub struct CargoTestHandle {
}

// Example of a cargo test command:
// cargo test --workspace --no-fail-fast -- module::func -Z unstable-options --format=json
// cargo test --workspace --no-fail-fast -- -Z unstable-options --format=json
// or
// cargo test --package my-package --no-fail-fast -- module::func -Z unstable-options --format=json

#[derive(Debug)]
pub enum TestTarget {
Workspace,
Package(String),
}

impl CargoTestHandle {
pub fn new(
path: Option<&str>,
options: CargoOptions,
root: &AbsPath,
test_target: TestTarget,
sender: Sender<CargoTestMessage>,
) -> std::io::Result<Self> {
let mut cmd = Command::new(Tool::Cargo.path());
cmd.env("RUSTC_BOOTSTRAP", "1");
cmd.arg("test");
cmd.arg("--workspace");

match &test_target {
TestTarget::Package(package) => {
cmd.arg("--package");
cmd.arg(package);
}
TestTarget::Workspace => {
cmd.arg("--workspace");
}
};

// --no-fail-fast is needed to ensure that all requested tests will run
cmd.arg("--no-fail-fast");
cmd.arg("--manifest-path");
Expand Down
39 changes: 32 additions & 7 deletions crates/rust-analyzer/src/handlers/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use lsp_types::{
SemanticTokensResult, SymbolInformation, SymbolTag, TextDocumentIdentifier, Url, WorkspaceEdit,
};
use paths::Utf8PathBuf;
use project_model::{ManifestPath, ProjectWorkspaceKind, TargetKind};
use project_model::{CargoWorkspace, ManifestPath, ProjectWorkspaceKind, TargetKind};
use serde_json::json;
use stdx::{format_to, never};
use syntax::{algo, ast, AstNode, TextRange, TextSize};
Expand Down Expand Up @@ -199,14 +199,28 @@ pub(crate) fn handle_view_item_tree(
Ok(res)
}

// cargo test requires the real package name which might contain hyphens but
// the test identifier passed to this function is the namespace form where hyphens
// are replaced with underscores so we have to reverse this and find the real package name
fn find_package_name(namespace_root: &str, cargo: &CargoWorkspace) -> Option<String> {
cargo.packages().find_map(|p| {
let package_name = &cargo[p].name;
if package_name.replace('-', "_") == namespace_root {
Some(package_name.clone())
} else {
None
}
})
}

pub(crate) fn handle_run_test(
state: &mut GlobalState,
params: lsp_ext::RunTestParams,
) -> anyhow::Result<()> {
if let Some(_session) = state.test_run_session.take() {
state.send_notification::<lsp_ext::EndRunTest>(());
}
// We detect the lowest common ansector of all included tests, and
// We detect the lowest common ancestor of all included tests, and
// run it. We ignore excluded tests for now, the client will handle
// it for us.
let lca = match params.include {
Expand All @@ -225,20 +239,31 @@ pub(crate) fn handle_run_test(
.unwrap_or_default(),
None => "".to_owned(),
};
let test_path = if lca.is_empty() {
None
} else if let Some((_, path)) = lca.split_once("::") {
Some(path)
let (namespace_root, test_path) = if lca.is_empty() {
(None, None)
} else if let Some((namespace_root, path)) = lca.split_once("::") {
(Some(namespace_root), Some(path))
} else {
None
(Some(lca.as_str()), None)
};
let mut handles = vec![];
for ws in &*state.workspaces {
if let ProjectWorkspaceKind::Cargo { cargo, .. } = &ws.kind {
let test_target = if let Some(namespace_root) = namespace_root {
if let Some(package_name) = find_package_name(namespace_root, cargo) {
flycheck::TestTarget::Package(package_name)
} else {
flycheck::TestTarget::Workspace
}
} else {
flycheck::TestTarget::Workspace
};

let handle = flycheck::CargoTestHandle::new(
test_path,
state.config.cargo_test_options(),
cargo.workspace_root(),
test_target,
state.test_run_sender.clone(),
)?;
handles.push(handle);
Expand Down