1 //! Tests for config settings.
3 use cargo
::core
::{PackageIdSpec, Shell}
;
4 use cargo
::util
::config
::{self, Config, Definition, SslVersionConfig, StringList}
;
5 use cargo
::util
::interning
::InternedString
;
6 use cargo
::util
::toml
::{self as cargo_toml, VecStringOrBool as VSOB}
;
7 use cargo
::CargoResult
;
8 use cargo_test_support
::compare
;
9 use cargo_test_support
::{panic_error, paths, project, symlink_supported, t}
;
10 use serde
::Deserialize
;
11 use std
::borrow
::Borrow
;
12 use std
::collections
::{BTreeMap, HashMap}
;
16 use std
::path
::{Path, PathBuf}
;
18 /// Helper for constructing a `Config` object.
19 pub struct ConfigBuilder
{
20 env
: HashMap
<String
, String
>,
21 unstable
: Vec
<String
>,
22 config_args
: Vec
<String
>,
24 enable_nightly_features
: bool
,
28 pub fn new() -> ConfigBuilder
{
32 config_args
: Vec
::new(),
34 enable_nightly_features
: false,
38 /// Passes a `-Z` flag.
39 pub fn unstable_flag(&mut self, s
: impl Into
<String
>) -> &mut Self {
40 self.unstable
.push(s
.into());
44 /// Sets an environment variable.
45 pub fn env(&mut self, key
: impl Into
<String
>, val
: impl Into
<String
>) -> &mut Self {
46 self.env
.insert(key
.into(), val
.into());
50 /// Unconditionally enable nightly features, even on stable channels.
51 pub fn nightly_features_allowed(&mut self, allowed
: bool
) -> &mut Self {
52 self.enable_nightly_features
= allowed
;
56 /// Passes a `--config` flag.
57 pub fn config_arg(&mut self, arg
: impl Into
<String
>) -> &mut Self {
58 self.config_args
.push(arg
.into());
62 /// Sets the current working directory where config files will be loaded.
63 pub fn cwd(&mut self, path
: impl AsRef
<Path
>) -> &mut Self {
64 self.cwd
= Some(paths
::root().join(path
.as_ref()));
68 /// Creates the `Config`.
69 pub fn build(&self) -> Config
{
70 self.build_err().unwrap()
73 /// Creates the `Config`, returning a Result.
74 pub fn build_err(&self) -> CargoResult
<Config
> {
75 let output
= Box
::new(fs
::File
::create(paths
::root().join("shell.out")).unwrap());
76 let shell
= Shell
::from_write(output
);
77 let cwd
= self.cwd
.clone().unwrap_or_else(|| paths
::root());
78 let homedir
= paths
::home();
79 let mut config
= Config
::new(shell
, cwd
, homedir
);
80 config
.nightly_features_allowed
= self.enable_nightly_features
|| !self.unstable
.is_empty();
81 config
.set_env(self.env
.clone());
82 config
.set_search_stop_path(paths
::root());
98 fn new_config() -> Config
{
99 ConfigBuilder
::new().build()
102 /// Read the output from Config.
103 pub fn read_output(config
: Config
) -> String
{
104 drop(config
); // Paranoid about flushing the file.
105 let path
= paths
::root().join("shell.out");
106 fs
::read_to_string(path
).unwrap()
110 fn read_env_vars_for_config() {
122 .file("src/lib.rs", "")
128 assert_eq!(env::var("NUM_JOBS").unwrap(), "100");
134 p
.cargo("check").env("CARGO_BUILD_JOBS", "100").run();
137 pub fn write_config(config
: &str) {
138 write_config_at(paths
::root().join(".cargo/config"), config
);
141 pub fn write_config_at(path
: impl AsRef
<Path
>, contents
: &str) {
142 let path
= paths
::root().join(path
.as_ref());
143 fs
::create_dir_all(path
.parent().unwrap()).unwrap();
144 fs
::write(path
, contents
).unwrap();
147 pub fn write_config_toml(config
: &str) {
148 write_config_at(paths
::root().join(".cargo/config.toml"), config
);
152 fn symlink_file(target
: &Path
, link
: &Path
) -> io
::Result
<()> {
153 os
::unix
::fs
::symlink(target
, link
)
157 fn symlink_file(target
: &Path
, link
: &Path
) -> io
::Result
<()> {
158 os
::windows
::fs
::symlink_file(target
, link
)
161 fn symlink_config_to_config_toml() {
162 let toml_path
= paths
::root().join(".cargo/config.toml");
163 let symlink_path
= paths
::root().join(".cargo/config");
164 t
!(symlink_file(&toml_path
, &symlink_path
));
168 pub fn assert_error
<E
: Borrow
<anyhow
::Error
>>(error
: E
, msgs
: &str) {
177 format
!("Caused by:\n {}", e
)
182 assert_match(msgs
, &causes
);
186 pub fn assert_match(expected
: &str, actual
: &str) {
187 if let Err(e
) = compare
::match_exact(expected
, actual
, "output", "", None
) {
201 let config
= new_config();
203 #[derive(Debug, Deserialize, Eq, PartialEq)]
207 let s
: S
= config
.get("S").unwrap();
208 assert_eq
!(s
, S { f1: Some(123) }
);
209 let config
= ConfigBuilder
::new().env("CARGO_S_F1", "456").build();
210 let s
: S
= config
.get("S").unwrap();
211 assert_eq
!(s
, S { f1: Some(456) }
);
216 fn environment_variable_casing() {
217 // Issue #11814: Environment variable names are case-insensitive on Windows.
218 let config
= ConfigBuilder
::new()
220 .env("Two-Words", "abc")
221 .env("two_words", "def")
224 let var
= config
.get_env("PATH").unwrap();
225 assert_eq
!(var
, String
::from("abc"));
227 let var
= config
.get_env("path").unwrap();
228 assert_eq
!(var
, String
::from("abc"));
230 let var
= config
.get_env("TWO-WORDS").unwrap();
231 assert_eq
!(var
, String
::from("abc"));
233 // Make sure that we can still distinguish between dashes and underscores
234 // in variable names.
235 let var
= config
.get_env("Two_Words").unwrap();
236 assert_eq
!(var
, String
::from("def"));
240 fn config_works_with_extension() {
248 let config
= new_config();
250 assert_eq
!(config
.get
::<Option
<i32>>("foo.f1").unwrap(), Some(1));
254 fn config_ambiguous_filename_symlink_doesnt_warn() {
255 // Windows requires special permissions to create symlinks.
256 // If we don't have permission, just skip this test.
257 if !symlink_supported() {
268 symlink_config_to_config_toml();
270 let config
= new_config();
272 assert_eq
!(config
.get
::<Option
<i32>>("foo.f1").unwrap(), Some(1));
274 // It should NOT have warned for the symlink.
275 let output
= read_output(config
);
276 assert_eq
!(output
, "");
280 fn config_ambiguous_filename() {
295 let config
= new_config();
297 // It should use the value from the one without the extension for
298 // backwards compatibility.
299 assert_eq
!(config
.get
::<Option
<i32>>("foo.f1").unwrap(), Some(1));
301 // But it also should have warned.
302 let output
= read_output(config
);
304 warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config`
306 assert_match(expected
, &output
);
310 fn config_unused_fields() {
318 let config
= ConfigBuilder
::new()
319 .env("CARGO_S_UNUSED2", "1")
320 .env("CARGO_S2_UNUSED", "2")
323 #[derive(Debug, Deserialize, Eq, PartialEq)]
327 // This prints a warning (verified below).
328 let s
: S
= config
.get("S").unwrap();
329 assert_eq
!(s
, S { f1: None }
);
330 // This does not print anything, we cannot easily/reliably warn for
331 // environment variables.
332 let s
: S
= config
.get("S2").unwrap();
333 assert_eq
!(s
, S { f1: None }
);
335 // Verify the warnings.
336 let output
= read_output(config
);
338 warning: unused config key `S.unused` in `[..]/.cargo/config`
340 assert_match(expected
, &output
);
344 fn config_load_toml_profile() {
352 debug-assertions = true
355 overflow-checks = true
358 [profile.dev.build-override]
361 [profile.dev.package.bar]
366 dir-name = 'without-lto'
371 let config
= ConfigBuilder
::new()
372 .unstable_flag("advanced-env")
373 .env("CARGO_PROFILE_DEV_CODEGEN_UNITS", "5")
374 .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_CODEGEN_UNITS", "11")
375 .env("CARGO_PROFILE_DEV_PACKAGE_env_CODEGEN_UNITS", "13")
376 .env("CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL", "2")
379 // TODO: don't use actual `tomlprofile`.
380 let p
: cargo_toml
::TomlProfile
= config
.get("profile.dev").unwrap();
381 let mut packages
= BTreeMap
::new();
383 cargo_toml
::ProfilePackageSpec
::Spec(::cargo
::core
::PackageIdSpec
::parse("bar").unwrap());
384 let o_profile
= cargo_toml
::TomlProfile
{
385 opt_level
: Some(cargo_toml
::TomlOptLevel("2".to_string())),
386 codegen_units
: Some(9),
389 packages
.insert(key
, o_profile
);
391 cargo_toml
::ProfilePackageSpec
::Spec(::cargo
::core
::PackageIdSpec
::parse("env").unwrap());
392 let o_profile
= cargo_toml
::TomlProfile
{
393 codegen_units
: Some(13),
396 packages
.insert(key
, o_profile
);
400 cargo_toml
::TomlProfile
{
401 opt_level
: Some(cargo_toml
::TomlOptLevel("s".to_string())),
402 lto
: Some(cargo_toml
::StringOrBool
::Bool(true)),
403 codegen_units
: Some(5),
404 debug
: Some(cargo_toml
::U32OrBool
::Bool(true)),
405 debug_assertions
: Some(true),
407 panic
: Some("abort".to_string()),
408 overflow_checks
: Some(true),
409 incremental
: Some(true),
410 package
: Some(packages
),
411 build_override
: Some(Box
::new(cargo_toml
::TomlProfile
{
412 opt_level
: Some(cargo_toml
::TomlOptLevel("1".to_string())),
413 codegen_units
: Some(11),
420 let p
: cargo_toml
::TomlProfile
= config
.get("profile.no-lto").unwrap();
423 cargo_toml
::TomlProfile
{
424 lto
: Some(cargo_toml
::StringOrBool
::Bool(false)),
425 dir_name
: Some(InternedString
::new("without-lto")),
426 inherits
: Some(InternedString
::new("dev")),
433 fn profile_env_var_prefix() {
434 // Check for a bug with collision on DEBUG vs DEBUG_ASSERTIONS.
435 let config
= ConfigBuilder
::new()
436 .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false")
438 let p
: cargo_toml
::TomlProfile
= config
.get("profile.dev").unwrap();
439 assert_eq
!(p
.debug_assertions
, Some(false));
440 assert_eq
!(p
.debug
, None
);
442 let config
= ConfigBuilder
::new()
443 .env("CARGO_PROFILE_DEV_DEBUG", "1")
445 let p
: cargo_toml
::TomlProfile
= config
.get("profile.dev").unwrap();
446 assert_eq
!(p
.debug_assertions
, None
);
447 assert_eq
!(p
.debug
, Some(cargo_toml
::U32OrBool
::U32(1)));
449 let config
= ConfigBuilder
::new()
450 .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false")
451 .env("CARGO_PROFILE_DEV_DEBUG", "1")
453 let p
: cargo_toml
::TomlProfile
= config
.get("profile.dev").unwrap();
454 assert_eq
!(p
.debug_assertions
, Some(false));
455 assert_eq
!(p
.debug
, Some(cargo_toml
::U32OrBool
::U32(1)));
459 fn config_deserialize_any() {
460 // Some tests to exercise deserialize_any for deserializers that need to
461 // be told the format.
471 let config
= ConfigBuilder
::new()
472 .unstable_flag("advanced-env")
473 .env("CARGO_ENVB", "false")
474 .env("CARGO_C", "['d']")
475 .env("CARGO_ENVL", "['a', 'b']")
477 assert_eq
!(config
.get
::<VSOB
>("a").unwrap(), VSOB
::Bool(true));
479 config
.get
::<VSOB
>("b").unwrap(),
480 VSOB
::VecString(vec
!["b".to_string()])
483 config
.get
::<VSOB
>("c").unwrap(),
484 VSOB
::VecString(vec
!["c".to_string(), "d".to_string()])
486 assert_eq
!(config
.get
::<VSOB
>("envb").unwrap(), VSOB
::Bool(false));
488 config
.get
::<VSOB
>("envl").unwrap(),
489 VSOB
::VecString(vec
!["a".to_string(), "b".to_string()])
492 // Demonstrate where merging logic isn't very smart. This could be improved.
493 let config
= ConfigBuilder
::new().env("CARGO_A", "x y").build();
495 config
.get
::<VSOB
>("a").unwrap_err(),
497 error in environment variable `CARGO_A`: could not load config key `a`
500 invalid type: string \"x y\", expected a boolean or vector of strings",
504 let config
= ConfigBuilder
::new()
505 .unstable_flag("advanced-env")
506 .env("CARGO_B", "d e")
507 .env("CARGO_C", "f g")
510 config
.get
::<VSOB
>("b").unwrap(),
511 VSOB
::VecString(vec
!["b".to_string(), "d".to_string(), "e".to_string()])
514 config
.get
::<VSOB
>("c").unwrap(),
515 VSOB
::VecString(vec
!["c".to_string(), "f".to_string(), "g".to_string()])
519 // This test demonstrates that ConfigValue::merge isn't very smart.
520 // It would be nice if it was smarter.
521 let config
= ConfigBuilder
::new().config_arg("a = ['a']").build_err();
525 failed to merge --config key `a` into `[..]/.cargo/config`
528 failed to merge config value from `--config cli option` into `[..]/.cargo/config`: \
529 expected boolean, but found array",
532 // config-cli and advanced-env
533 let config
= ConfigBuilder
::new()
534 .unstable_flag("advanced-env")
535 .config_arg("b=['clib']")
536 .config_arg("c=['clic']")
537 .env("CARGO_B", "env1 env2")
538 .env("CARGO_C", "['e1', 'e2']")
541 config
.get
::<VSOB
>("b").unwrap(),
542 VSOB
::VecString(vec
![
550 config
.get
::<VSOB
>("c").unwrap(),
551 VSOB
::VecString(vec
![
561 fn config_toml_errors() {
569 let config
= new_config();
573 .get
::<cargo_toml
::TomlProfile
>("profile.dev")
576 error in [..]/.cargo/config: could not load config key `profile.dev.opt-level`
579 must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"foo\"",
582 let config
= ConfigBuilder
::new()
583 .env("CARGO_PROFILE_DEV_OPT_LEVEL", "asdf")
587 config
.get
::<cargo_toml
::TomlProfile
>("profile.dev").unwrap_err(),
589 error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: could not load config key `profile.dev.opt-level`
592 must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"asdf\"",
608 let config
= ConfigBuilder
::new()
609 .unstable_flag("advanced-env")
610 .env("CARGO_NEST_foo_f2", "3")
611 .env("CARGO_NESTE_foo_f1", "1")
612 .env("CARGO_NESTE_foo_f2", "3")
613 .env("CARGO_NESTE_bar_asdf", "3")
616 type Nested
= HashMap
<String
, HashMap
<String
, u8>>;
618 let n
: Nested
= config
.get("nest").unwrap();
619 let mut expected
= HashMap
::new();
620 let mut foo
= HashMap
::new();
621 foo
.insert("f1".to_string(), 1);
622 foo
.insert("f2".to_string(), 3);
623 expected
.insert("foo".to_string(), foo
);
624 let mut bar
= HashMap
::new();
625 bar
.insert("asdf".to_string(), 3);
626 expected
.insert("bar".to_string(), bar
);
627 assert_eq
!(n
, expected
);
629 let n
: Nested
= config
.get("neste").unwrap();
630 assert_eq
!(n
, expected
);
644 let config
= ConfigBuilder
::new()
645 .env("CARGO_E_S", "asdf")
646 .env("CARGO_E_BIG", "123456789")
649 config
.get
::<i64>("foo").unwrap_err(),
650 "missing config key `foo`",
653 config
.get
::<i64>("foo.bar").unwrap_err(),
654 "missing config key `foo.bar`",
657 config
.get
::<i64>("S.f2").unwrap_err(),
658 "error in [..]/.cargo/config: `S.f2` expected an integer, but found a string",
661 config
.get
::<u8>("S.big").unwrap_err(),
663 error in [..].cargo/config: could not load config key `S.big`
666 invalid value: integer `123456789`, expected u8",
669 // Environment variable type errors.
671 config
.get
::<i64>("e.s").unwrap_err(),
672 "error in environment variable `CARGO_E_S`: invalid digit found in string",
675 config
.get
::<i8>("e.big").unwrap_err(),
677 error in environment variable `CARGO_E_BIG`: could not load config key `e.big`
680 invalid value: integer `123456789`, expected i8",
683 #[derive(Debug, Deserialize)]
691 assert_error(config
.get
::<S
>("S").unwrap_err(), "missing field `f3`");
695 fn config_get_option() {
703 let config
= ConfigBuilder
::new().env("CARGO_BAR_ASDF", "3").build();
705 assert_eq
!(config
.get
::<Option
<i32>>("a").unwrap(), None
);
706 assert_eq
!(config
.get
::<Option
<i32>>("a.b").unwrap(), None
);
707 assert_eq
!(config
.get
::<Option
<i32>>("foo.f1").unwrap(), Some(1));
708 assert_eq
!(config
.get
::<Option
<i32>>("bar.asdf").unwrap(), Some(3));
709 assert_eq
!(config
.get
::<Option
<i32>>("bar.zzzz").unwrap(), None
);
713 fn config_bad_toml() {
714 write_config("asdf");
715 let config
= new_config();
717 config
.get
::<i32>("foo").unwrap_err(),
719 could not load Cargo configuration
722 could not parse TOML configuration in `[..]/.cargo/config`
725 could not parse input as TOML
728 TOML parse error at line 1, column 5
737 fn config_get_list() {
755 type L
= Vec
<String
>;
757 let config
= ConfigBuilder
::new()
758 .unstable_flag("advanced-env")
759 .env("CARGO_L4", "['three', 'four']")
760 .env("CARGO_L5", "['a']")
761 .env("CARGO_ENV_EMPTY", "[]")
762 .env("CARGO_ENV_BLANK", "")
763 .env("CARGO_ENV_NUM", "1")
764 .env("CARGO_ENV_NUM_LIST", "[1]")
765 .env("CARGO_ENV_TEXT", "asdf")
766 .env("CARGO_LEPAIR", "['a', 'b']")
767 .env("CARGO_NESTED2_L", "['z']")
768 .env("CARGO_NESTEDE_L", "['env']")
769 .env("CARGO_BAD_ENV", "[zzz]")
772 assert_eq
!(config
.get
::<L
>("unset").unwrap(), vec
![] as Vec
<String
>);
773 assert_eq
!(config
.get
::<L
>("l1").unwrap(), vec
![] as Vec
<String
>);
774 assert_eq
!(config
.get
::<L
>("l2").unwrap(), vec
!["one", "two"]);
776 config
.get
::<L
>("l3").unwrap_err(),
778 invalid configuration for key `l3`
779 expected a list, but found a integer for `l3` in [..]/.cargo/config",
782 config
.get
::<L
>("l4").unwrap(),
783 vec
!["one", "two", "three", "four"]
785 assert_eq
!(config
.get
::<L
>("l5").unwrap(), vec
!["a"]);
786 assert_eq
!(config
.get
::<L
>("env-empty").unwrap(), vec
![] as Vec
<String
>);
787 assert_eq
!(config
.get
::<L
>("env-blank").unwrap(), vec
![] as Vec
<String
>);
788 assert_eq
!(config
.get
::<L
>("env-num").unwrap(), vec
!["1".to_string()]);
790 config
.get
::<L
>("env-num-list").unwrap_err(),
791 "error in environment variable `CARGO_ENV_NUM_LIST`: \
792 expected string, found integer",
795 config
.get
::<L
>("env-text").unwrap(),
796 vec
!["asdf".to_string()]
798 // "invalid number" here isn't the best error, but I think it's just toml.rs.
800 config
.get
::<L
>("bad-env").unwrap_err(),
802 error in environment variable `CARGO_BAD_ENV`: could not parse TOML list: TOML parse error at line 1, column 2
811 // Try some other sequence-like types.
814 .get
::<(String
, String
, String
, String
)>("l4")
823 assert_eq
!(config
.get
::<(String
,)>("l5").unwrap(), ("a".to_string(),));
826 #[derive(Debug, Deserialize, Eq, PartialEq)]
827 struct TupS(String
, String
);
829 config
.get
::<TupS
>("lepair").unwrap(),
830 TupS("a".to_string(), "b".to_string())
833 // Nested with an option.
834 #[derive(Debug, Deserialize, Eq, PartialEq)]
836 l
: Option
<Vec
<String
>>,
838 assert_eq
!(config
.get
::<S
>("nested-empty").unwrap(), S { l: None }
);
840 config
.get
::<S
>("nested").unwrap(),
842 l
: Some(vec
!["x".to_string()]),
846 config
.get
::<S
>("nested2").unwrap(),
848 l
: Some(vec
!["y".to_string(), "z".to_string()]),
852 config
.get
::<S
>("nestede").unwrap(),
854 l
: Some(vec
!["env".to_string()]),
860 fn config_get_other_types() {
868 let config
= ConfigBuilder
::new()
869 .env("CARGO_NSE", "987")
870 .env("CARGO_NS2", "654")
873 #[derive(Debug, Deserialize, Eq, PartialEq)]
874 #[serde(transparent)]
876 assert_eq
!(config
.get
::<NewS
>("ns").unwrap(), NewS(123));
877 assert_eq
!(config
.get
::<NewS
>("ns2").unwrap(), NewS(654));
878 assert_eq
!(config
.get
::<NewS
>("nse").unwrap(), NewS(987));
880 config
.get
::<NewS
>("unset").unwrap_err(),
881 "missing config key `unset`",
886 fn config_relative_path() {
887 write_config(&format
!(
894 paths
::home().display(),
897 let config
= ConfigBuilder
::new()
898 .env("CARGO_EPATH", "a/b")
899 .env("CARGO_P3", "d/e")
904 .get
::<config
::ConfigRelativePath
>("p1")
906 .resolve_path(&config
),
907 paths
::root().join("foo/bar")
911 .get
::<config
::ConfigRelativePath
>("p2")
913 .resolve_path(&config
),
914 paths
::root().join("../abc")
918 .get
::<config
::ConfigRelativePath
>("p3")
920 .resolve_path(&config
),
921 paths
::root().join("d/e")
925 .get
::<config
::ConfigRelativePath
>("abs")
927 .resolve_path(&config
),
932 .get
::<config
::ConfigRelativePath
>("epath")
934 .resolve_path(&config
),
935 paths
::root().join("a/b")
940 fn config_get_integers() {
945 i64max = 9223372036854775807
949 let config
= ConfigBuilder
::new()
950 .env("CARGO_EPOS", "123456789")
951 .env("CARGO_ENEG", "-1")
952 .env("CARGO_EI64MAX", "9223372036854775807")
956 config
.get
::<u64>("i64max").unwrap(),
957 9_223_372_036_854_775_807
960 config
.get
::<i64>("i64max").unwrap(),
961 9_223_372_036_854_775_807
964 config
.get
::<u64>("ei64max").unwrap(),
965 9_223_372_036_854_775_807
968 config
.get
::<i64>("ei64max").unwrap(),
969 9_223_372_036_854_775_807
973 config
.get
::<u32>("nneg").unwrap_err(),
975 error in [..].cargo/config: could not load config key `nneg`
978 invalid value: integer `-123456789`, expected u32",
981 config
.get
::<u32>("eneg").unwrap_err(),
983 error in environment variable `CARGO_ENEG`: could not load config key `eneg`
986 invalid value: integer `-1`, expected u32",
989 config
.get
::<i8>("npos").unwrap_err(),
991 error in [..].cargo/config: could not load config key `npos`
994 invalid value: integer `123456789`, expected i8",
997 config
.get
::<i8>("epos").unwrap_err(),
999 error in environment variable `CARGO_EPOS`: could not load config key `epos`
1002 invalid value: integer `123456789`, expected i8",
1007 fn config_get_ssl_version_missing() {
1015 let config
= new_config();
1018 .get
::<Option
<SslVersionConfig
>>("http.ssl-version")
1024 fn config_get_ssl_version_single() {
1028 ssl-version = 'tlsv1.2'
1032 let config
= new_config();
1035 .get
::<Option
<SslVersionConfig
>>("http.ssl-version")
1039 SslVersionConfig
::Single(v
) => assert_eq
!(&v
, "tlsv1.2"),
1040 SslVersionConfig
::Range(_
) => panic
!("Did not expect ssl version min/max."),
1045 fn config_get_ssl_version_min_max() {
1049 ssl-version.min = 'tlsv1.2'
1050 ssl-version.max = 'tlsv1.3'
1054 let config
= new_config();
1057 .get
::<Option
<SslVersionConfig
>>("http.ssl-version")
1061 SslVersionConfig
::Single(_
) => panic
!("Did not expect exact ssl version."),
1062 SslVersionConfig
::Range(range
) => {
1063 assert_eq
!(range
.min
, Some(String
::from("tlsv1.2")));
1064 assert_eq
!(range
.max
, Some(String
::from("tlsv1.3")));
1070 fn config_get_ssl_version_both_forms_configured() {
1071 // this is not allowed
1075 ssl-version = 'tlsv1.1'
1076 ssl-version.min = 'tlsv1.2'
1077 ssl-version.max = 'tlsv1.3'
1081 let config
= new_config();
1085 .get
::<SslVersionConfig
>("http.ssl-version")
1088 could not load Cargo configuration
1091 could not parse TOML configuration in `[..]/.cargo/config`
1094 could not parse input as TOML
1097 TOML parse error at line 3, column 1
1099 3 | ssl-version.min = 'tlsv1.2'
1101 dotted key `ssl-version` attempted to extend non-table type (string)
1107 /// Assert that unstable options can be configured with the `unstable` table in
1108 /// cargo config files
1109 fn unstable_table_notation() {
1113 print-im-a-teapot = true
1116 let config
= ConfigBuilder
::new().nightly_features_allowed(true).build();
1117 assert_eq
!(config
.cli_unstable().print_im_a_teapot
, true);
1121 /// Assert that dotted notation works for configuring unstable options
1122 fn unstable_dotted_notation() {
1125 unstable.print-im-a-teapot = true
1128 let config
= ConfigBuilder
::new().nightly_features_allowed(true).build();
1129 assert_eq
!(config
.cli_unstable().print_im_a_teapot
, true);
1133 /// Assert that Zflags on the CLI take precedence over those from config
1134 fn unstable_cli_precedence() {
1137 unstable.print-im-a-teapot = true
1140 let config
= ConfigBuilder
::new().nightly_features_allowed(true).build();
1141 assert_eq
!(config
.cli_unstable().print_im_a_teapot
, true);
1143 let config
= ConfigBuilder
::new()
1144 .unstable_flag("print-im-a-teapot=no")
1146 assert_eq
!(config
.cli_unstable().print_im_a_teapot
, false);
1150 /// Assert that attempting to set an unstable flag that doesn't exist via config
1151 /// is ignored on stable
1152 fn unstable_invalid_flag_ignored_on_stable() {
1155 unstable.an-invalid-flag = 'yes'
1158 assert
!(ConfigBuilder
::new().build_err().is_ok());
1162 /// Assert that unstable options can be configured with the `unstable` table in
1163 /// cargo config files
1164 fn unstable_flags_ignored_on_stable() {
1168 print-im-a-teapot = true
1171 // Enforce stable channel even when testing on nightly.
1172 let config
= ConfigBuilder
::new().nightly_features_allowed(false).build();
1173 assert_eq
!(config
.cli_unstable().print_im_a_teapot
, false);
1177 fn table_merge_failure() {
1178 // Config::merge fails to merge entries in two tables.
1180 "foo/.cargo/config",
1194 #[derive(Debug, Deserialize)]
1199 let config
= ConfigBuilder
::new().cwd("foo").build();
1201 config
.get
::<Table
>("table").unwrap_err(),
1203 could not load Cargo configuration
1206 failed to merge configuration at `[..]/.cargo/config`
1209 failed to merge key `table` between [..]/foo/.cargo/config and [..]/.cargo/config
1212 failed to merge key `key` between [..]/foo/.cargo/config and [..]/.cargo/config
1215 failed to merge config value from `[..]/.cargo/config` into `[..]/foo/.cargo/config`: \
1216 expected array, but found string",
1221 fn non_string_in_array() {
1222 // Currently only strings are supported.
1223 write_config("foo = [1, 2, 3]");
1224 let config
= new_config();
1226 config
.get
::<Vec
<i32>>("foo").unwrap_err(),
1228 could not load Cargo configuration
1231 failed to load TOML configuration from `[..]/.cargo/config`
1234 failed to parse key `foo`
1237 expected string but found integer in list",
1242 fn struct_with_opt_inner_struct() {
1243 // Struct with a key that is Option of another struct.
1244 // Check that can be defined with environment variable.
1245 #[derive(Deserialize)]
1249 #[derive(Deserialize)]
1251 inner
: Option
<Inner
>,
1253 let config
= ConfigBuilder
::new()
1254 .env("CARGO_FOO_INNER_VALUE", "12")
1256 let f
: Foo
= config
.get("foo").unwrap();
1257 assert_eq
!(f
.inner
.unwrap().value
.unwrap(), 12);
1261 fn struct_with_default_inner_struct() {
1262 // Struct with serde defaults.
1263 // Check that can be defined with environment variable.
1264 #[derive(Deserialize, Default)]
1269 #[derive(Deserialize, Default)]
1274 let config
= ConfigBuilder
::new()
1275 .env("CARGO_FOO_INNER_VALUE", "12")
1277 let f
: Foo
= config
.get("foo").unwrap();
1278 assert_eq
!(f
.inner
.value
, 12);
1282 fn overlapping_env_config() {
1283 // Issue where one key is a prefix of another.
1284 #[derive(Deserialize)]
1285 #[serde(rename_all = "kebab-case")]
1288 debug_assertions
: Option
<bool
>,
1290 let config
= ConfigBuilder
::new()
1291 .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1294 let s
: Ambig
= config
.get("ambig").unwrap();
1295 assert_eq
!(s
.debug_assertions
, Some(true));
1296 assert_eq
!(s
.debug
, None
);
1298 let config
= ConfigBuilder
::new().env("CARGO_AMBIG_DEBUG", "0").build();
1299 let s
: Ambig
= config
.get("ambig").unwrap();
1300 assert_eq
!(s
.debug_assertions
, None
);
1301 assert_eq
!(s
.debug
, Some(0));
1303 let config
= ConfigBuilder
::new()
1304 .env("CARGO_AMBIG_DEBUG", "1")
1305 .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1307 let s
: Ambig
= config
.get("ambig").unwrap();
1308 assert_eq
!(s
.debug_assertions
, Some(true));
1309 assert_eq
!(s
.debug
, Some(1));
1313 fn overlapping_env_with_defaults_errors_out() {
1314 // Issue where one key is a prefix of another.
1315 // This is a limitation of mapping environment variables on to a hierarchy.
1316 // Check that we error out when we hit ambiguity in this way, rather than
1317 // the more-surprising defaulting through.
1318 // If, in the future, we can handle this more correctly, feel free to delete
1320 #[derive(Deserialize, Default)]
1321 #[serde(default, rename_all = "kebab-case")]
1324 debug_assertions
: bool
,
1326 let config
= ConfigBuilder
::new()
1327 .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1329 let err
= config
.get
::<Ambig
>("ambig").err().unwrap();
1330 assert
!(format
!("{}", err
).contains("missing config key `ambig.debug`"));
1332 let config
= ConfigBuilder
::new().env("CARGO_AMBIG_DEBUG", "5").build();
1333 let s
: Ambig
= config
.get("ambig").unwrap();
1334 assert_eq
!(s
.debug_assertions
, bool
::default());
1335 assert_eq
!(s
.debug
, 5);
1337 let config
= ConfigBuilder
::new()
1338 .env("CARGO_AMBIG_DEBUG", "1")
1339 .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1341 let s
: Ambig
= config
.get("ambig").unwrap();
1342 assert_eq
!(s
.debug_assertions
, true);
1343 assert_eq
!(s
.debug
, 1);
1347 fn struct_with_overlapping_inner_struct_and_defaults() {
1348 // Struct with serde defaults.
1349 // Check that can be defined with environment variable.
1350 #[derive(Deserialize, Default)]
1356 // Containing struct with a prefix of inner
1358 // This is a limitation of mapping environment variables on to a hierarchy.
1359 // Check that we error out when we hit ambiguity in this way, rather than
1360 // the more-surprising defaulting through.
1361 // If, in the future, we can handle this more correctly, feel free to delete
1363 #[derive(Deserialize, Default)]
1365 struct PrefixContainer
{
1369 let config
= ConfigBuilder
::new()
1370 .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
1373 .get
::<PrefixContainer
>("prefixcontainer")
1376 assert
!(format
!("{}", err
).contains("missing config key `prefixcontainer.inn`"));
1377 let config
= ConfigBuilder
::new()
1378 .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
1379 .env("CARGO_PREFIXCONTAINER_INN", "true")
1381 let f
: PrefixContainer
= config
.get("prefixcontainer").unwrap();
1382 assert_eq
!(f
.inner
.value
, 12);
1383 assert_eq
!(f
.inn
, true);
1385 // Containing struct where the inner value's field is a prefix of another
1387 // This is a limitation of mapping environment variables on to a hierarchy.
1388 // Check that we error out when we hit ambiguity in this way, rather than
1389 // the more-surprising defaulting through.
1390 // If, in the future, we can handle this more correctly, feel free to delete
1392 #[derive(Deserialize, Default)]
1394 struct InversePrefixContainer
{
1398 let config
= ConfigBuilder
::new()
1399 .env("CARGO_INVERSEPREFIXCONTAINER_INNER_VALUE", "12")
1401 let f
: InversePrefixContainer
= config
.get("inverseprefixcontainer").unwrap();
1402 assert_eq
!(f
.inner_field
, bool
::default());
1403 assert_eq
!(f
.inner
.value
, 12);
1407 fn string_list_tricky_env() {
1408 // Make sure StringList handles typed env values.
1409 let config
= ConfigBuilder
::new()
1410 .env("CARGO_KEY1", "123")
1411 .env("CARGO_KEY2", "true")
1412 .env("CARGO_KEY3", "1 2")
1414 let x
= config
.get
::<StringList
>("key1").unwrap();
1415 assert_eq
!(x
.as_slice(), &["123".to_string()]);
1416 let x
= config
.get
::<StringList
>("key2").unwrap();
1417 assert_eq
!(x
.as_slice(), &["true".to_string()]);
1418 let x
= config
.get
::<StringList
>("key3").unwrap();
1419 assert_eq
!(x
.as_slice(), &["1".to_string(), "2".to_string()]);
1423 fn string_list_wrong_type() {
1424 // What happens if StringList is given then wrong type.
1425 write_config("some_list = 123");
1426 let config
= ConfigBuilder
::new().build();
1428 config
.get
::<StringList
>("some_list").unwrap_err(),
1430 invalid configuration for key `some_list`
1431 expected a string or array of strings, but found a integer for `some_list` in [..]/.cargo/config",
1434 write_config("some_list = \"1 2\"");
1435 let config
= ConfigBuilder
::new().build();
1436 let x
= config
.get
::<StringList
>("some_list").unwrap();
1437 assert_eq
!(x
.as_slice(), &["1".to_string(), "2".to_string()]);
1441 fn string_list_advanced_env() {
1442 // StringList with advanced env.
1443 let config
= ConfigBuilder
::new()
1444 .unstable_flag("advanced-env")
1445 .env("CARGO_KEY1", "[]")
1446 .env("CARGO_KEY2", "['1 2', '3']")
1447 .env("CARGO_KEY3", "[123]")
1449 let x
= config
.get
::<StringList
>("key1").unwrap();
1450 assert_eq
!(x
.as_slice(), &[] as &[String
]);
1451 let x
= config
.get
::<StringList
>("key2").unwrap();
1452 assert_eq
!(x
.as_slice(), &["1 2".to_string(), "3".to_string()]);
1454 config
.get
::<StringList
>("key3").unwrap_err(),
1455 "error in environment variable `CARGO_KEY3`: expected string, found integer",
1460 fn parse_strip_with_string() {
1468 let config
= new_config();
1470 let p
: cargo_toml
::TomlProfile
= config
.get("profile.release").unwrap();
1471 let strip
= p
.strip
.unwrap();
1474 cargo_toml
::StringOrBool
::String("debuginfo".to_string())
1479 fn cargo_target_empty_cfg() {
1487 let config
= new_config();
1490 config
.target_dir().unwrap_err(),
1491 "the target directory is set to an empty string in [..]/.cargo/config",
1496 fn cargo_target_empty_env() {
1497 let project
= project().build();
1499 project
.cargo("check")
1500 .env("CARGO_TARGET_DIR", "")
1501 .with_stderr("error: the target directory is set to an empty string in the `CARGO_TARGET_DIR` environment variable")
1507 fn all_profile_options() {
1508 // Check that all profile options can be serialized/deserialized.
1509 let base_settings
= cargo_toml
::TomlProfile
{
1510 opt_level
: Some(cargo_toml
::TomlOptLevel("0".to_string())),
1511 lto
: Some(cargo_toml
::StringOrBool
::String("thin".to_string())),
1512 codegen_backend
: Some(InternedString
::new("example")),
1513 codegen_units
: Some(123),
1514 debug
: Some(cargo_toml
::U32OrBool
::U32(1)),
1515 split_debuginfo
: Some("packed".to_string()),
1516 debug_assertions
: Some(true),
1518 panic
: Some("abort".to_string()),
1519 overflow_checks
: Some(true),
1520 incremental
: Some(true),
1521 dir_name
: Some(InternedString
::new("dir_name")),
1522 inherits
: Some(InternedString
::new("debug")),
1523 strip
: Some(cargo_toml
::StringOrBool
::String("symbols".to_string())),
1525 build_override
: None
,
1528 let mut overrides
= BTreeMap
::new();
1529 let key
= cargo_toml
::ProfilePackageSpec
::Spec(PackageIdSpec
::parse("foo").unwrap());
1530 overrides
.insert(key
, base_settings
.clone());
1531 let profile
= cargo_toml
::TomlProfile
{
1532 build_override
: Some(Box
::new(base_settings
.clone())),
1533 package
: Some(overrides
),
1536 let profile_toml
= toml
::to_string(&profile
).unwrap();
1537 let roundtrip
: cargo_toml
::TomlProfile
= toml
::from_str(&profile_toml
).unwrap();
1538 let roundtrip_toml
= toml
::to_string(&roundtrip
).unwrap();
1539 compare
::assert_match_exact(&profile_toml
, &roundtrip_toml
);
1543 fn value_in_array() {
1544 // Value<String> in an array should work
1545 let root_path
= paths
::root().join(".cargo/config.toml");
1551 \"example.com ...\",
1552 \"example.net ...\",
1557 let foo_path
= paths
::root().join("foo/.cargo/config.toml");
1563 \"example.org ...\",
1568 let config
= ConfigBuilder
::new()
1570 // environment variables don't actually work for known-hosts due to
1571 // space splitting, but this is included here just to validate that
1572 // they work (particularly if other Vec<Value> config vars are added
1574 .env("CARGO_NET_SSH_KNOWN_HOSTS", "env-example")
1576 let net_config
= config
.net_config().unwrap();
1584 assert_eq
!(kh
.len(), 4);
1585 assert_eq
!(kh
[0].val
, "example.org ...");
1586 assert_eq
!(kh
[0].definition
, Definition
::Path(foo_path
.clone()));
1587 assert_eq
!(kh
[1].val
, "example.com ...");
1588 assert_eq
!(kh
[1].definition
, Definition
::Path(root_path
.clone()));
1589 assert_eq
!(kh
[2].val
, "example.net ...");
1590 assert_eq
!(kh
[2].definition
, Definition
::Path(root_path
.clone()));
1591 assert_eq
!(kh
[3].val
, "env-example");
1594 Definition
::Environment("CARGO_NET_SSH_KNOWN_HOSTS".to_string())