Skip to content

Commit 8b43626

Browse files
committed
Respect env defined in cargo.toml
1 parent 2eb6eba commit 8b43626

File tree

8 files changed

+242
-5
lines changed

8 files changed

+242
-5
lines changed

cargo-nextest/src/dispatch.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use clap::{ArgAction, Args, Parser, Subcommand, ValueEnum};
1212
use guppy::graph::PackageGraph;
1313
use itertools::Itertools;
1414
use nextest_filtering::FilteringExpr;
15-
use nextest_metadata::{BinaryListSummary, BuildPlatform};
15+
use nextest_metadata::{BinaryListSummary, BuildPlatform, EnvironmentMap};
1616
use nextest_runner::{
1717
cargo_config::{CargoConfigs, TargetTriple},
1818
config::{NextestConfig, NextestProfile, RetryPolicy, TestThreads, ToolConfigFile},
@@ -442,6 +442,7 @@ impl TestBuildFilter {
442442
binary_list: Arc<BinaryList>,
443443
test_filter_builder: TestFilterBuilder,
444444
runner: &TargetRunner,
445+
env: EnvironmentMap,
445446
reuse_build: &ReuseBuildInfo,
446447
) -> Result<TestList<'g>> {
447448
let path_mapper = make_path_mapper(
@@ -463,6 +464,7 @@ impl TestBuildFilter {
463464
rust_build_meta,
464465
&test_filter_builder,
465466
runner,
467+
env,
466468
// TODO: do we need to allow customizing this?
467469
num_cpus::get(),
468470
)
@@ -1037,11 +1039,13 @@ impl App {
10371039
test_filter_builder: TestFilterBuilder,
10381040
target_runner: &TargetRunner,
10391041
) -> Result<TestList> {
1042+
let env = self.base.cargo_configs.env()?;
10401043
self.build_filter.compute_test_list(
10411044
self.base.graph(),
10421045
binary_list,
10431046
test_filter_builder,
10441047
target_runner,
1048+
env,
10451049
&self.base.reuse_build,
10461050
)
10471051
}

cargo-nextest/src/tests_integration/fixtures.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,10 @@ pub(super) fn set_env_vars() {
138138
// This environment variable is required to test the #[bench] fixture. Note that THIS IS FOR
139139
// TEST CODE ONLY. NEVER USE THIS IN PRODUCTION.
140140
std::env::set_var("RUSTC_BOOTSTRAP", "1");
141+
142+
// Disable the tests which check for environment variables being set in `config.toml`, as they
143+
// won't be in the search path when running integration tests.
144+
std::env::set_var("__NEXTEST_NO_CHECK_CARGO_ENV_VARS", "1");
141145
}
142146

143147
#[track_caller]

fixtures/nextest-tests/.cargo/config

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,10 @@ replace-with = "vendored-sources"
1212

1313
[source.vendored-sources]
1414
directory = "vendor"
15+
16+
[env]
17+
__NEXTEST_ENV_VAR_FOR_TESTING_NOT_IN_PARENT_ENV = "if-this-value-is-present-then-test-passed"
18+
__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_NO_OVERRIDE = "if-this-value-is-present-then-test-failed"
19+
__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_OVERRIDDEN = { value = "if-this-value-is-present-then-test-passed", force = true }
20+
__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_RELATIVE_NO_OVERRIDE = { value = "if-this-value-is-present-then-test-failed", relative = true }
21+
__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_RELATIVE_OVERRIDDEN = { value = "if-this-value-is-present-then-test-passed", force = true, relative = true }

fixtures/nextest-tests/tests/basic.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,32 @@ fn test_cargo_env_vars() {
156156
// CARGO_PRIMARY_PACKAGE is missing at runtime
157157
// CARGO_TARGET_TMPDIR is missing at runtime
158158
// Dynamic library paths are tested by actually executing the tests -- they depend on the dynamic library.
159+
160+
if std::env::var("__NEXTEST_NO_CHECK_CARGO_ENV_VARS").is_err() {
161+
assert_eq!(
162+
std::env::var("__NEXTEST_ENV_VAR_FOR_TESTING_NOT_IN_PARENT_ENV").as_deref(),
163+
Ok("if-this-value-is-present-then-test-passed")
164+
);
165+
assert_eq!(
166+
std::env::var("__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_NO_OVERRIDE").as_deref(),
167+
Ok("if-this-value-is-present-then-test-passed")
168+
);
169+
assert_eq!(
170+
std::env::var("__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_OVERRIDDEN").as_deref(),
171+
Ok("if-this-value-is-present-then-test-passed")
172+
);
173+
assert_eq!(
174+
std::env::var("__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_RELATIVE_NO_OVERRIDE")
175+
.as_deref(),
176+
Ok("if-this-value-is-present-then-test-passed")
177+
);
178+
let overridden_path =
179+
std::env::var("__NEXTEST_ENV_VAR_FOR_TESTING_IN_PARENT_ENV_RELATIVE_OVERRIDDEN").unwrap();
180+
assert!(overridden_path.ends_with(&format!("{SEP}.cargo{SEP}if-this-value-is-present-then-test-passed", SEP = std::path::MAIN_SEPARATOR)),
181+
"Path {:?} should start end with the relative file path",
182+
overridden_path,
183+
);
184+
}
159185
}
160186

161187
#[test]

nextest-metadata/src/test_list.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,36 @@ use std::{
1212
process::Command,
1313
};
1414

15+
/// An environment variable set in `config.toml`. See https://doc.rust-lang.org/cargo/reference/config.html#env
16+
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
17+
pub struct CargoEnvironmentVariable {
18+
/// The source `config.toml` file. See
19+
/// https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure for the lookup
20+
/// order.
21+
pub source: Option<Utf8PathBuf>,
22+
23+
/// The name of the environment variable to set.
24+
pub name: String,
25+
26+
/// The value of the environment variable to set.
27+
pub value: String,
28+
29+
/// If the environment variable is already set in the environment, it is not reassigned unless
30+
/// `force` is set to `true`.
31+
pub force: bool,
32+
33+
/// Interpret the environment variable as a path relative to the directory containing the source
34+
/// `config.toml` file.
35+
pub relative: bool,
36+
}
37+
38+
/// A list of environment variables to set when running tests.
39+
///
40+
/// This is a `Vec` instead of a map because, on Windows, environment variables are case-insensitive
41+
/// but case-preserving. We produce the environment as-is and let the caller handle the case of
42+
/// duplicates.
43+
pub type EnvironmentMap = Vec<CargoEnvironmentVariable>;
44+
1545
/// Command builder for `cargo nextest list`.
1646
#[derive(Clone, Debug, Default)]
1747
pub struct ListCommand {

nextest-runner/src/cargo_config.rs

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
99
use crate::errors::{CargoConfigError, InvalidCargoCliConfigReason, TargetTripleError};
1010
use camino::{Utf8Path, Utf8PathBuf};
11+
use nextest_metadata::{CargoEnvironmentVariable, EnvironmentMap};
1112
use once_cell::sync::OnceCell;
1213
use serde::Deserialize;
1314
use std::{collections::BTreeMap, fmt};
@@ -241,6 +242,50 @@ impl CargoConfigs {
241242
&self.cwd
242243
}
243244

245+
/// The environment variables to set when running Cargo commands.
246+
pub fn env(&self) -> Result<EnvironmentMap, CargoConfigError> {
247+
let env = self
248+
.discovered_configs()?
249+
.filter_map(|config| match config {
250+
DiscoveredConfig::CliOption { config, source }
251+
| DiscoveredConfig::File { config, source } => Some((config, source)),
252+
DiscoveredConfig::Env => None,
253+
})
254+
.flat_map(|(config, source)| {
255+
let source = match source {
256+
CargoConfigSource::CliOption => None,
257+
CargoConfigSource::File(path) => Some(path.clone()),
258+
};
259+
config
260+
.env
261+
.clone()
262+
.into_iter()
263+
.map(move |(name, value)| (source.clone(), name, value))
264+
})
265+
.map(|(source, name, value)| match value {
266+
CargoConfigEnv::Value(value) => CargoEnvironmentVariable {
267+
source,
268+
name,
269+
value,
270+
force: false,
271+
relative: false,
272+
},
273+
CargoConfigEnv::Fields {
274+
value,
275+
force,
276+
relative,
277+
} => CargoEnvironmentVariable {
278+
source,
279+
name,
280+
value,
281+
force,
282+
relative,
283+
},
284+
})
285+
.collect();
286+
Ok(env)
287+
}
288+
244289
pub(crate) fn discovered_configs(
245290
&self,
246291
) -> Result<
@@ -493,11 +538,26 @@ fn load_file(
493538
Ok((CargoConfigSource::File(path), config))
494539
}
495540

541+
#[derive(Clone, Deserialize, Debug)]
542+
#[serde(untagged)]
543+
pub(crate) enum CargoConfigEnv {
544+
Value(String),
545+
Fields {
546+
value: String,
547+
#[serde(default)]
548+
force: bool,
549+
#[serde(default)]
550+
relative: bool,
551+
},
552+
}
553+
496554
#[derive(Deserialize, Debug)]
497555
pub(crate) struct CargoConfig {
498556
#[serde(default)]
499557
pub(crate) build: CargoConfigBuild,
500558
pub(crate) target: Option<BTreeMap<String, CargoConfigRunner>>,
559+
#[serde(default)]
560+
pub(crate) env: BTreeMap<String, CargoConfigEnv>,
501561
}
502562

503563
#[derive(Deserialize, Default, Debug)]
@@ -745,6 +805,20 @@ mod tests {
745805
assert_eq!(find_target_triple(&[], None, &dir_path, &dir_path), None);
746806
}
747807

808+
#[test]
809+
fn test_env_var_precedence() {
810+
let dir = setup_temp_dir().unwrap();
811+
let dir_path = Utf8PathBuf::try_from(dir.path().canonicalize().unwrap()).unwrap();
812+
let dir_foo_path = dir_path.join("foo");
813+
let dir_foo_bar_path = dir_foo_path.join("bar");
814+
815+
let configs =
816+
CargoConfigs::new_with_isolation(&[] as &[&str], &dir_foo_bar_path, &dir_path).unwrap();
817+
let env = configs.env().unwrap();
818+
let env_values: Vec<&str> = env.iter().map(|elem| elem.value.as_str()).collect();
819+
assert_eq!(env_values, vec!["foo-bar-config", "foo-config"]);
820+
}
821+
748822
fn setup_temp_dir() -> Result<TempDir> {
749823
let dir = tempfile::Builder::new()
750824
.tempdir()
@@ -794,11 +868,17 @@ mod tests {
794868
static FOO_CARGO_CONFIG_CONTENTS: &str = r#"
795869
[build]
796870
target = "x86_64-pc-windows-msvc"
871+
872+
[env]
873+
SOME_VAR = { value = "foo-config", force = true }
797874
"#;
798875

799876
static FOO_BAR_CARGO_CONFIG_CONTENTS: &str = r#"
800877
[build]
801878
target = "x86_64-unknown-linux-gnu"
879+
880+
[env]
881+
SOME_VAR = { value = "foo-bar-config", force = true }
802882
"#;
803883

804884
static FOO_EXTRA_CONFIG_CONTENTS: &str = r#"

0 commit comments

Comments
 (0)