Make test harness arguments configurable and not --nocapture.

* Added config `runnables.extraTestBinaryArgs` to control the args.
* The default is `--show-output` rather than `--nocapture` to prevent
  unreadable output when 2 or more tests fail or print output at once.
* Renamed variables in `CargoTargetSpec::runnable_args()` for clarity.

Fixes <https://github.com/rust-lang/rust-analyzer/issues/12737>.
This commit is contained in:
Kevin Reid 2024-04-18 17:06:30 -07:00
parent af1fd88c4d
commit db292bd89e
5 changed files with 70 additions and 29 deletions

View file

@ -33,53 +33,55 @@ impl CargoTargetSpec {
kind: &RunnableKind, kind: &RunnableKind,
cfg: &Option<CfgExpr>, cfg: &Option<CfgExpr>,
) -> (Vec<String>, Vec<String>) { ) -> (Vec<String>, Vec<String>) {
let mut args = Vec::new(); let extra_test_binary_args = snap.config.runnables().extra_test_binary_args;
let mut extra_args = Vec::new();
let mut cargo_args = Vec::new();
let mut executable_args = Vec::new();
match kind { match kind {
RunnableKind::Test { test_id, attr } => { RunnableKind::Test { test_id, attr } => {
args.push("test".to_owned()); cargo_args.push("test".to_owned());
extra_args.push(test_id.to_string()); executable_args.push(test_id.to_string());
if let TestId::Path(_) = test_id { if let TestId::Path(_) = test_id {
extra_args.push("--exact".to_owned()); executable_args.push("--exact".to_owned());
} }
extra_args.push("--nocapture".to_owned()); executable_args.extend(extra_test_binary_args);
if attr.ignore { if attr.ignore {
extra_args.push("--ignored".to_owned()); executable_args.push("--ignored".to_owned());
} }
} }
RunnableKind::TestMod { path } => { RunnableKind::TestMod { path } => {
args.push("test".to_owned()); cargo_args.push("test".to_owned());
extra_args.push(path.clone()); executable_args.push(path.clone());
extra_args.push("--nocapture".to_owned()); executable_args.extend(extra_test_binary_args);
} }
RunnableKind::Bench { test_id } => { RunnableKind::Bench { test_id } => {
args.push("bench".to_owned()); cargo_args.push("bench".to_owned());
extra_args.push(test_id.to_string()); executable_args.push(test_id.to_string());
if let TestId::Path(_) = test_id { if let TestId::Path(_) = test_id {
extra_args.push("--exact".to_owned()); executable_args.push("--exact".to_owned());
} }
extra_args.push("--nocapture".to_owned()); executable_args.extend(extra_test_binary_args);
} }
RunnableKind::DocTest { test_id } => { RunnableKind::DocTest { test_id } => {
args.push("test".to_owned()); cargo_args.push("test".to_owned());
args.push("--doc".to_owned()); cargo_args.push("--doc".to_owned());
extra_args.push(test_id.to_string()); executable_args.push(test_id.to_string());
extra_args.push("--nocapture".to_owned()); executable_args.extend(extra_test_binary_args);
} }
RunnableKind::Bin => { RunnableKind::Bin => {
let subcommand = match spec { let subcommand = match spec {
Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => "test", Some(CargoTargetSpec { target_kind: TargetKind::Test, .. }) => "test",
_ => "run", _ => "run",
}; };
args.push(subcommand.to_owned()); cargo_args.push(subcommand.to_owned());
} }
} }
let (allowed_features, target_required_features) = if let Some(mut spec) = spec { let (allowed_features, target_required_features) = if let Some(mut spec) = spec {
let allowed_features = mem::take(&mut spec.features); let allowed_features = mem::take(&mut spec.features);
let required_features = mem::take(&mut spec.required_features); let required_features = mem::take(&mut spec.required_features);
spec.push_to(&mut args, kind); spec.push_to(&mut cargo_args, kind);
(allowed_features, required_features) (allowed_features, required_features)
} else { } else {
(Default::default(), Default::default()) (Default::default(), Default::default())
@ -89,10 +91,10 @@ impl CargoTargetSpec {
match &cargo_config.features { match &cargo_config.features {
CargoFeatures::All => { CargoFeatures::All => {
args.push("--all-features".to_owned()); cargo_args.push("--all-features".to_owned());
for feature in target_required_features { for feature in target_required_features {
args.push("--features".to_owned()); cargo_args.push("--features".to_owned());
args.push(feature); cargo_args.push(feature);
} }
} }
CargoFeatures::Selected { features, no_default_features } => { CargoFeatures::Selected { features, no_default_features } => {
@ -108,16 +110,16 @@ impl CargoTargetSpec {
feats.dedup(); feats.dedup();
for feature in feats { for feature in feats {
args.push("--features".to_owned()); cargo_args.push("--features".to_owned());
args.push(feature); cargo_args.push(feature);
} }
if *no_default_features { if *no_default_features {
args.push("--no-default-features".to_owned()); cargo_args.push("--no-default-features".to_owned());
} }
} }
} }
(args, extra_args) (cargo_args, executable_args)
} }
pub(crate) fn for_file( pub(crate) fn for_file(

View file

@ -392,6 +392,14 @@ config_data! {
/// Additional arguments to be passed to cargo for runnables such as /// Additional arguments to be passed to cargo for runnables such as
/// tests or binaries. For example, it may be `--release`. /// tests or binaries. For example, it may be `--release`.
runnables_extraArgs: Vec<String> = vec![], runnables_extraArgs: Vec<String> = vec![],
/// Additional arguments to be passed through Cargo to launched tests, benchmarks, or
/// doc-tests.
///
/// Unless the launched target uses a
/// [custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
/// they will end up being interpreted as options to
/// [`rustc`s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
runnables_extraTestBinaryArgs: Vec<String> = vec!["--show-output".to_owned()],
/// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private /// Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private
/// projects, or "discover" to try to automatically find it if the `rustc-dev` component /// projects, or "discover" to try to automatically find it if the `rustc-dev` component
@ -823,6 +831,8 @@ pub struct RunnablesConfig {
pub override_cargo: Option<String>, pub override_cargo: Option<String>,
/// Additional arguments for the `cargo`, e.g. `--release`. /// Additional arguments for the `cargo`, e.g. `--release`.
pub cargo_extra_args: Vec<String>, pub cargo_extra_args: Vec<String>,
/// Additional arguments for the binary being run, if it is a test or benchmark.
pub extra_test_binary_args: Vec<String>,
} }
/// Configuration for workspace symbol search requests. /// Configuration for workspace symbol search requests.
@ -1749,6 +1759,7 @@ impl Config {
RunnablesConfig { RunnablesConfig {
override_cargo: self.runnables_command().clone(), override_cargo: self.runnables_command().clone(),
cargo_extra_args: self.runnables_extraArgs().clone(), cargo_extra_args: self.runnables_extraArgs().clone(),
extra_test_binary_args: self.runnables_extraTestBinaryArgs().clone(),
} }
} }

View file

@ -157,7 +157,7 @@ fn main() {}
{ {
"args": { "args": {
"cargoArgs": ["test", "--package", "foo", "--test", "spam"], "cargoArgs": ["test", "--package", "foo", "--test", "spam"],
"executableArgs": ["test_eggs", "--exact", "--nocapture"], "executableArgs": ["test_eggs", "--exact", "--show-output"],
"cargoExtraArgs": [], "cargoExtraArgs": [],
"overrideCargo": null, "overrideCargo": null,
"workspaceRoot": server.path().join("foo") "workspaceRoot": server.path().join("foo")
@ -190,7 +190,7 @@ fn main() {}
"cargoExtraArgs": [], "cargoExtraArgs": [],
"executableArgs": [ "executableArgs": [
"", "",
"--nocapture" "--show-output"
] ]
}, },
"kind": "cargo", "kind": "cargo",

View file

@ -851,6 +851,24 @@ Command to be executed instead of 'cargo' for runnables.
-- --
Additional arguments to be passed to cargo for runnables such as Additional arguments to be passed to cargo for runnables such as
tests or binaries. For example, it may be `--release`. tests or binaries. For example, it may be `--release`.
--
[[rust-analyzer.runnables.extraTestBinaryArgs]]rust-analyzer.runnables.extraTestBinaryArgs::
+
--
Default:
----
[
"--show-output"
]
----
Additional arguments to be passed through Cargo to launched tests, benchmarks, or
doc-tests.
Unless the launched target uses a
[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),
they will end up being interpreted as options to
[`rustc`s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).
-- --
[[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`):: [[rust-analyzer.rustc.source]]rust-analyzer.rustc.source (default: `null`)::
+ +

View file

@ -1594,6 +1594,16 @@
"type": "string" "type": "string"
} }
}, },
"rust-analyzer.runnables.extraTestBinaryArgs": {
"markdownDescription": "Additional arguments to be passed through Cargo to launched tests, benchmarks, or\ndoc-tests.\n\nUnless the launched target uses a\n[custom test harness](https://doc.rust-lang.org/cargo/reference/cargo-targets.html#the-harness-field),\nthey will end up being interpreted as options to\n[`rustc`s built-in test harness (“libtest”)](https://doc.rust-lang.org/rustc/tests/index.html#cli-arguments).",
"default": [
"--show-output"
],
"type": "array",
"items": {
"type": "string"
}
},
"rust-analyzer.rustc.source": { "rust-analyzer.rustc.source": {
"markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.", "markdownDescription": "Path to the Cargo.toml of the rust compiler workspace, for usage in rustc_private\nprojects, or \"discover\" to try to automatically find it if the `rustc-dev` component\nis installed.\n\nAny project which uses rust-analyzer with the rustcPrivate\ncrates must set `[package.metadata.rust-analyzer] rustc_private=true` to use it.\n\nThis option does not take effect until rust-analyzer is restarted.",
"default": null, "default": null,