1 //! Tests for config settings.
2 
3 use cargo::core::Shell;
4 use cargo::util::config::{self, Config, SslVersionConfig, StringList};
5 use cargo::util::interning::InternedString;
6 use cargo::util::toml::{self, VecStringOrBool as VSOB};
7 use cargo::CargoResult;
8 use cargo_test_support::{normalized_lines_match, paths, project, t};
9 use serde::Deserialize;
10 use std::borrow::Borrow;
11 use std::collections::{BTreeMap, HashMap};
12 use std::fs;
13 use std::io;
14 use std::os;
15 use std::path::{Path, PathBuf};
16 
17 /// Helper for constructing a `Config` object.
18 pub struct ConfigBuilder {
19     env: HashMap<String, String>,
20     unstable: Vec<String>,
21     config_args: Vec<String>,
22     cwd: Option<PathBuf>,
23     enable_nightly_features: bool,
24 }
25 
26 impl ConfigBuilder {
new() -> ConfigBuilder27     pub fn new() -> ConfigBuilder {
28         ConfigBuilder {
29             env: HashMap::new(),
30             unstable: Vec::new(),
31             config_args: Vec::new(),
32             cwd: None,
33             enable_nightly_features: false,
34         }
35     }
36 
37     /// Passes a `-Z` flag.
unstable_flag(&mut self, s: impl Into<String>) -> &mut Self38     pub fn unstable_flag(&mut self, s: impl Into<String>) -> &mut Self {
39         self.unstable.push(s.into());
40         self
41     }
42 
43     /// Sets an environment variable.
env(&mut self, key: impl Into<String>, val: impl Into<String>) -> &mut Self44     pub fn env(&mut self, key: impl Into<String>, val: impl Into<String>) -> &mut Self {
45         self.env.insert(key.into(), val.into());
46         self
47     }
48 
49     /// Unconditionaly enable nightly features, even on stable channels.
nightly_features_allowed(&mut self, allowed: bool) -> &mut Self50     pub fn nightly_features_allowed(&mut self, allowed: bool) -> &mut Self {
51         self.enable_nightly_features = allowed;
52         self
53     }
54 
55     /// Passes a `--config` flag.
config_arg(&mut self, arg: impl Into<String>) -> &mut Self56     pub fn config_arg(&mut self, arg: impl Into<String>) -> &mut Self {
57         if !self.unstable.iter().any(|s| s == "unstable-options") {
58             // --config is current unstable
59             self.unstable_flag("unstable-options");
60         }
61         self.config_args.push(arg.into());
62         self
63     }
64 
65     /// Sets the current working directory where config files will be loaded.
cwd(&mut self, path: impl AsRef<Path>) -> &mut Self66     pub fn cwd(&mut self, path: impl AsRef<Path>) -> &mut Self {
67         self.cwd = Some(paths::root().join(path.as_ref()));
68         self
69     }
70 
71     /// Creates the `Config`.
build(&self) -> Config72     pub fn build(&self) -> Config {
73         self.build_err().unwrap()
74     }
75 
76     /// Creates the `Config`, returning a Result.
build_err(&self) -> CargoResult<Config>77     pub fn build_err(&self) -> CargoResult<Config> {
78         let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap());
79         let shell = Shell::from_write(output);
80         let cwd = self.cwd.clone().unwrap_or_else(|| paths::root());
81         let homedir = paths::home();
82         let mut config = Config::new(shell, cwd, homedir);
83         config.nightly_features_allowed = self.enable_nightly_features || !self.unstable.is_empty();
84         config.set_env(self.env.clone());
85         config.set_search_stop_path(paths::root());
86         config.configure(
87             0,
88             false,
89             None,
90             false,
91             false,
92             false,
93             &None,
94             &self.unstable,
95             &self.config_args,
96         )?;
97         Ok(config)
98     }
99 }
100 
new_config() -> Config101 fn new_config() -> Config {
102     ConfigBuilder::new().build()
103 }
104 
105 /// Read the output from Config.
read_output(config: Config) -> String106 pub fn read_output(config: Config) -> String {
107     drop(config); // Paranoid about flushing the file.
108     let path = paths::root().join("shell.out");
109     fs::read_to_string(path).unwrap()
110 }
111 
112 #[cargo_test]
read_env_vars_for_config()113 fn read_env_vars_for_config() {
114     let p = project()
115         .file(
116             "Cargo.toml",
117             r#"
118                 [package]
119                 name = "foo"
120                 authors = []
121                 version = "0.0.0"
122                 build = "build.rs"
123             "#,
124         )
125         .file("src/lib.rs", "")
126         .file(
127             "build.rs",
128             r#"
129                 use std::env;
130                 fn main() {
131                     assert_eq!(env::var("NUM_JOBS").unwrap(), "100");
132                 }
133             "#,
134         )
135         .build();
136 
137     p.cargo("build").env("CARGO_BUILD_JOBS", "100").run();
138 }
139 
write_config(config: &str)140 pub fn write_config(config: &str) {
141     write_config_at(paths::root().join(".cargo/config"), config);
142 }
143 
write_config_at(path: impl AsRef<Path>, contents: &str)144 pub fn write_config_at(path: impl AsRef<Path>, contents: &str) {
145     let path = paths::root().join(path.as_ref());
146     fs::create_dir_all(path.parent().unwrap()).unwrap();
147     fs::write(path, contents).unwrap();
148 }
149 
write_config_toml(config: &str)150 fn write_config_toml(config: &str) {
151     write_config_at(paths::root().join(".cargo/config.toml"), config);
152 }
153 
154 // Several test fail on windows if the user does not have permission to
155 // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of
156 // disabling these test on Windows, use this function to test whether we
157 // have permission, and return otherwise. This way, we still don't run these
158 // tests most of the time, but at least we do if the user has the right
159 // permissions.
160 // This function is derived from libstd fs tests.
got_symlink_permission() -> bool161 pub fn got_symlink_permission() -> bool {
162     if cfg!(unix) {
163         return true;
164     }
165     let link = paths::root().join("some_hopefully_unique_link_name");
166     let target = paths::root().join("nonexisting_target");
167 
168     match symlink_file(&target, &link) {
169         Ok(_) => true,
170         // ERROR_PRIVILEGE_NOT_HELD = 1314
171         Err(ref err) if err.raw_os_error() == Some(1314) => false,
172         Err(_) => true,
173     }
174 }
175 
176 #[cfg(unix)]
symlink_file(target: &Path, link: &Path) -> io::Result<()>177 fn symlink_file(target: &Path, link: &Path) -> io::Result<()> {
178     os::unix::fs::symlink(target, link)
179 }
180 
181 #[cfg(windows)]
symlink_file(target: &Path, link: &Path) -> io::Result<()>182 fn symlink_file(target: &Path, link: &Path) -> io::Result<()> {
183     os::windows::fs::symlink_file(target, link)
184 }
185 
symlink_config_to_config_toml()186 fn symlink_config_to_config_toml() {
187     let toml_path = paths::root().join(".cargo/config.toml");
188     let symlink_path = paths::root().join(".cargo/config");
189     t!(symlink_file(&toml_path, &symlink_path));
190 }
191 
192 #[track_caller]
assert_error<E: Borrow<anyhow::Error>>(error: E, msgs: &str)193 pub fn assert_error<E: Borrow<anyhow::Error>>(error: E, msgs: &str) {
194     let causes = error
195         .borrow()
196         .chain()
197         .enumerate()
198         .map(|(i, e)| {
199             if i == 0 {
200                 e.to_string()
201             } else {
202                 format!("Caused by:\n  {}", e)
203             }
204         })
205         .collect::<Vec<_>>()
206         .join("\n\n");
207     assert_match(msgs, &causes);
208 }
209 
210 #[track_caller]
assert_match(expected: &str, actual: &str)211 pub fn assert_match(expected: &str, actual: &str) {
212     if !normalized_lines_match(expected, actual, None) {
213         panic!(
214             "Did not find expected:\n{}\nActual:\n{}\n",
215             expected, actual
216         );
217     }
218 }
219 
220 #[cargo_test]
get_config()221 fn get_config() {
222     write_config(
223         "\
224 [S]
225 f1 = 123
226 ",
227     );
228 
229     let config = new_config();
230 
231     #[derive(Debug, Deserialize, Eq, PartialEq)]
232     struct S {
233         f1: Option<i64>,
234     }
235     let s: S = config.get("S").unwrap();
236     assert_eq!(s, S { f1: Some(123) });
237     let config = ConfigBuilder::new().env("CARGO_S_F1", "456").build();
238     let s: S = config.get("S").unwrap();
239     assert_eq!(s, S { f1: Some(456) });
240 }
241 
242 #[cargo_test]
config_works_with_extension()243 fn config_works_with_extension() {
244     write_config_toml(
245         "\
246 [foo]
247 f1 = 1
248 ",
249     );
250 
251     let config = new_config();
252 
253     assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
254 }
255 
256 #[cargo_test]
config_ambiguous_filename_symlink_doesnt_warn()257 fn config_ambiguous_filename_symlink_doesnt_warn() {
258     // Windows requires special permissions to create symlinks.
259     // If we don't have permission, just skip this test.
260     if !got_symlink_permission() {
261         return;
262     };
263 
264     write_config_toml(
265         "\
266 [foo]
267 f1 = 1
268 ",
269     );
270 
271     symlink_config_to_config_toml();
272 
273     let config = new_config();
274 
275     assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
276 
277     // It should NOT have warned for the symlink.
278     let output = read_output(config);
279     let unexpected = "\
280 warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config`
281 ";
282     if normalized_lines_match(unexpected, &output, None) {
283         panic!(
284             "Found unexpected:\n{}\nActual error:\n{}\n",
285             unexpected, output
286         );
287     }
288 }
289 
290 #[cargo_test]
config_ambiguous_filename()291 fn config_ambiguous_filename() {
292     write_config(
293         "\
294 [foo]
295 f1 = 1
296 ",
297     );
298 
299     write_config_toml(
300         "\
301 [foo]
302 f1 = 2
303 ",
304     );
305 
306     let config = new_config();
307 
308     // It should use the value from the one without the extension for
309     // backwards compatibility.
310     assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
311 
312     // But it also should have warned.
313     let output = read_output(config);
314     let expected = "\
315 warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config`
316 ";
317     assert_match(expected, &output);
318 }
319 
320 #[cargo_test]
config_unused_fields()321 fn config_unused_fields() {
322     write_config(
323         "\
324 [S]
325 unused = 456
326 ",
327     );
328 
329     let config = ConfigBuilder::new()
330         .env("CARGO_S_UNUSED2", "1")
331         .env("CARGO_S2_UNUSED", "2")
332         .build();
333 
334     #[derive(Debug, Deserialize, Eq, PartialEq)]
335     struct S {
336         f1: Option<i64>,
337     }
338     // This prints a warning (verified below).
339     let s: S = config.get("S").unwrap();
340     assert_eq!(s, S { f1: None });
341     // This does not print anything, we cannot easily/reliably warn for
342     // environment variables.
343     let s: S = config.get("S2").unwrap();
344     assert_eq!(s, S { f1: None });
345 
346     // Verify the warnings.
347     let output = read_output(config);
348     let expected = "\
349 warning: unused config key `S.unused` in `[..]/.cargo/config`
350 ";
351     assert_match(expected, &output);
352 }
353 
354 #[cargo_test]
config_load_toml_profile()355 fn config_load_toml_profile() {
356     write_config(
357         "\
358 [profile.dev]
359 opt-level = 's'
360 lto = true
361 codegen-units=4
362 debug = true
363 debug-assertions = true
364 rpath = true
365 panic = 'abort'
366 overflow-checks = true
367 incremental = true
368 
369 [profile.dev.build-override]
370 opt-level = 1
371 
372 [profile.dev.package.bar]
373 codegen-units = 9
374 
375 [profile.no-lto]
376 inherits = 'dev'
377 dir-name = 'without-lto'
378 lto = false
379 ",
380     );
381 
382     let config = ConfigBuilder::new()
383         .unstable_flag("advanced-env")
384         .env("CARGO_PROFILE_DEV_CODEGEN_UNITS", "5")
385         .env("CARGO_PROFILE_DEV_BUILD_OVERRIDE_CODEGEN_UNITS", "11")
386         .env("CARGO_PROFILE_DEV_PACKAGE_env_CODEGEN_UNITS", "13")
387         .env("CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL", "2")
388         .build();
389 
390     // TODO: don't use actual `tomlprofile`.
391     let p: toml::TomlProfile = config.get("profile.dev").unwrap();
392     let mut packages = BTreeMap::new();
393     let key = toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("bar").unwrap());
394     let o_profile = toml::TomlProfile {
395         opt_level: Some(toml::TomlOptLevel("2".to_string())),
396         codegen_units: Some(9),
397         ..Default::default()
398     };
399     packages.insert(key, o_profile);
400     let key = toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("env").unwrap());
401     let o_profile = toml::TomlProfile {
402         codegen_units: Some(13),
403         ..Default::default()
404     };
405     packages.insert(key, o_profile);
406 
407     assert_eq!(
408         p,
409         toml::TomlProfile {
410             opt_level: Some(toml::TomlOptLevel("s".to_string())),
411             lto: Some(toml::StringOrBool::Bool(true)),
412             codegen_units: Some(5),
413             debug: Some(toml::U32OrBool::Bool(true)),
414             debug_assertions: Some(true),
415             rpath: Some(true),
416             panic: Some("abort".to_string()),
417             overflow_checks: Some(true),
418             incremental: Some(true),
419             package: Some(packages),
420             build_override: Some(Box::new(toml::TomlProfile {
421                 opt_level: Some(toml::TomlOptLevel("1".to_string())),
422                 codegen_units: Some(11),
423                 ..Default::default()
424             })),
425             ..Default::default()
426         }
427     );
428 
429     let p: toml::TomlProfile = config.get("profile.no-lto").unwrap();
430     assert_eq!(
431         p,
432         toml::TomlProfile {
433             lto: Some(toml::StringOrBool::Bool(false)),
434             dir_name: Some(InternedString::new("without-lto")),
435             inherits: Some(InternedString::new("dev")),
436             ..Default::default()
437         }
438     );
439 }
440 
441 #[cargo_test]
profile_env_var_prefix()442 fn profile_env_var_prefix() {
443     // Check for a bug with collision on DEBUG vs DEBUG_ASSERTIONS.
444     let config = ConfigBuilder::new()
445         .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false")
446         .build();
447     let p: toml::TomlProfile = config.get("profile.dev").unwrap();
448     assert_eq!(p.debug_assertions, Some(false));
449     assert_eq!(p.debug, None);
450 
451     let config = ConfigBuilder::new()
452         .env("CARGO_PROFILE_DEV_DEBUG", "1")
453         .build();
454     let p: toml::TomlProfile = config.get("profile.dev").unwrap();
455     assert_eq!(p.debug_assertions, None);
456     assert_eq!(p.debug, Some(toml::U32OrBool::U32(1)));
457 
458     let config = ConfigBuilder::new()
459         .env("CARGO_PROFILE_DEV_DEBUG_ASSERTIONS", "false")
460         .env("CARGO_PROFILE_DEV_DEBUG", "1")
461         .build();
462     let p: toml::TomlProfile = config.get("profile.dev").unwrap();
463     assert_eq!(p.debug_assertions, Some(false));
464     assert_eq!(p.debug, Some(toml::U32OrBool::U32(1)));
465 }
466 
467 #[cargo_test]
config_deserialize_any()468 fn config_deserialize_any() {
469     // Some tests to exercise deserialize_any for deserializers that need to
470     // be told the format.
471     write_config(
472         "\
473 a = true
474 b = ['b']
475 c = ['c']
476 ",
477     );
478 
479     // advanced-env
480     let config = ConfigBuilder::new()
481         .unstable_flag("advanced-env")
482         .env("CARGO_ENVB", "false")
483         .env("CARGO_C", "['d']")
484         .env("CARGO_ENVL", "['a', 'b']")
485         .build();
486     assert_eq!(config.get::<VSOB>("a").unwrap(), VSOB::Bool(true));
487     assert_eq!(
488         config.get::<VSOB>("b").unwrap(),
489         VSOB::VecString(vec!["b".to_string()])
490     );
491     assert_eq!(
492         config.get::<VSOB>("c").unwrap(),
493         VSOB::VecString(vec!["c".to_string(), "d".to_string()])
494     );
495     assert_eq!(config.get::<VSOB>("envb").unwrap(), VSOB::Bool(false));
496     assert_eq!(
497         config.get::<VSOB>("envl").unwrap(),
498         VSOB::VecString(vec!["a".to_string(), "b".to_string()])
499     );
500 
501     // Demonstrate where merging logic isn't very smart. This could be improved.
502     let config = ConfigBuilder::new().env("CARGO_A", "x y").build();
503     assert_error(
504         config.get::<VSOB>("a").unwrap_err(),
505         "\
506 error in environment variable `CARGO_A`: could not load config key `a`
507 
508 Caused by:
509   invalid type: string \"x y\", expected a boolean or vector of strings",
510     );
511 
512     // Normal env.
513     let config = ConfigBuilder::new()
514         .unstable_flag("advanced-env")
515         .env("CARGO_B", "d e")
516         .env("CARGO_C", "f g")
517         .build();
518     assert_eq!(
519         config.get::<VSOB>("b").unwrap(),
520         VSOB::VecString(vec!["b".to_string(), "d".to_string(), "e".to_string()])
521     );
522     assert_eq!(
523         config.get::<VSOB>("c").unwrap(),
524         VSOB::VecString(vec!["c".to_string(), "f".to_string(), "g".to_string()])
525     );
526 
527     // config-cli
528     // This test demonstrates that ConfigValue::merge isn't very smart.
529     // It would be nice if it was smarter.
530     let config = ConfigBuilder::new().config_arg("a = ['a']").build_err();
531     assert_error(
532         config.unwrap_err(),
533         "\
534 failed to merge --config key `a` into `[..]/.cargo/config`
535 
536 Caused by:
537   failed to merge config value from `--config cli option` into `[..]/.cargo/config`: \
538 expected boolean, but found array",
539     );
540 
541     // config-cli and advanced-env
542     let config = ConfigBuilder::new()
543         .unstable_flag("advanced-env")
544         .config_arg("b=['clib']")
545         .config_arg("c=['clic']")
546         .env("CARGO_B", "env1 env2")
547         .env("CARGO_C", "['e1', 'e2']")
548         .build();
549     assert_eq!(
550         config.get::<VSOB>("b").unwrap(),
551         VSOB::VecString(vec![
552             "b".to_string(),
553             "clib".to_string(),
554             "env1".to_string(),
555             "env2".to_string()
556         ])
557     );
558     assert_eq!(
559         config.get::<VSOB>("c").unwrap(),
560         VSOB::VecString(vec![
561             "c".to_string(),
562             "clic".to_string(),
563             "e1".to_string(),
564             "e2".to_string()
565         ])
566     );
567 }
568 
569 #[cargo_test]
config_toml_errors()570 fn config_toml_errors() {
571     write_config(
572         "\
573 [profile.dev]
574 opt-level = 'foo'
575 ",
576     );
577 
578     let config = new_config();
579 
580     assert_error(
581         config.get::<toml::TomlProfile>("profile.dev").unwrap_err(),
582         "\
583 error in [..]/.cargo/config: could not load config key `profile.dev.opt-level`
584 
585 Caused by:
586   must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"foo\"",
587     );
588 
589     let config = ConfigBuilder::new()
590         .env("CARGO_PROFILE_DEV_OPT_LEVEL", "asdf")
591         .build();
592 
593     assert_error(
594         config.get::<toml::TomlProfile>("profile.dev").unwrap_err(),
595         "\
596 error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: could not load config key `profile.dev.opt-level`
597 
598 Caused by:
599   must be `0`, `1`, `2`, `3`, `s` or `z`, but found the string: \"asdf\"",
600     );
601 }
602 
603 #[cargo_test]
load_nested()604 fn load_nested() {
605     write_config(
606         "\
607 [nest.foo]
608 f1 = 1
609 f2 = 2
610 [nest.bar]
611 asdf = 3
612 ",
613     );
614 
615     let config = ConfigBuilder::new()
616         .unstable_flag("advanced-env")
617         .env("CARGO_NEST_foo_f2", "3")
618         .env("CARGO_NESTE_foo_f1", "1")
619         .env("CARGO_NESTE_foo_f2", "3")
620         .env("CARGO_NESTE_bar_asdf", "3")
621         .build();
622 
623     type Nested = HashMap<String, HashMap<String, u8>>;
624 
625     let n: Nested = config.get("nest").unwrap();
626     let mut expected = HashMap::new();
627     let mut foo = HashMap::new();
628     foo.insert("f1".to_string(), 1);
629     foo.insert("f2".to_string(), 3);
630     expected.insert("foo".to_string(), foo);
631     let mut bar = HashMap::new();
632     bar.insert("asdf".to_string(), 3);
633     expected.insert("bar".to_string(), bar);
634     assert_eq!(n, expected);
635 
636     let n: Nested = config.get("neste").unwrap();
637     assert_eq!(n, expected);
638 }
639 
640 #[cargo_test]
get_errors()641 fn get_errors() {
642     write_config(
643         "\
644 [S]
645 f1 = 123
646 f2 = 'asdf'
647 big = 123456789
648 ",
649     );
650 
651     let config = ConfigBuilder::new()
652         .env("CARGO_E_S", "asdf")
653         .env("CARGO_E_BIG", "123456789")
654         .build();
655     assert_error(
656         config.get::<i64>("foo").unwrap_err(),
657         "missing config key `foo`",
658     );
659     assert_error(
660         config.get::<i64>("foo.bar").unwrap_err(),
661         "missing config key `foo.bar`",
662     );
663     assert_error(
664         config.get::<i64>("S.f2").unwrap_err(),
665         "error in [..]/.cargo/config: `S.f2` expected an integer, but found a string",
666     );
667     assert_error(
668         config.get::<u8>("S.big").unwrap_err(),
669         "\
670 error in [..].cargo/config: could not load config key `S.big`
671 
672 Caused by:
673   invalid value: integer `123456789`, expected u8",
674     );
675 
676     // Environment variable type errors.
677     assert_error(
678         config.get::<i64>("e.s").unwrap_err(),
679         "error in environment variable `CARGO_E_S`: invalid digit found in string",
680     );
681     assert_error(
682         config.get::<i8>("e.big").unwrap_err(),
683         "\
684 error in environment variable `CARGO_E_BIG`: could not load config key `e.big`
685 
686 Caused by:
687   invalid value: integer `123456789`, expected i8",
688     );
689 
690     #[derive(Debug, Deserialize)]
691     struct S {
692         f1: i64,
693         f2: String,
694         f3: i64,
695         big: i64,
696     }
697     assert_error(config.get::<S>("S").unwrap_err(), "missing field `f3`");
698 }
699 
700 #[cargo_test]
config_get_option()701 fn config_get_option() {
702     write_config(
703         "\
704 [foo]
705 f1 = 1
706 ",
707     );
708 
709     let config = ConfigBuilder::new().env("CARGO_BAR_ASDF", "3").build();
710 
711     assert_eq!(config.get::<Option<i32>>("a").unwrap(), None);
712     assert_eq!(config.get::<Option<i32>>("a.b").unwrap(), None);
713     assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1));
714     assert_eq!(config.get::<Option<i32>>("bar.asdf").unwrap(), Some(3));
715     assert_eq!(config.get::<Option<i32>>("bar.zzzz").unwrap(), None);
716 }
717 
718 #[cargo_test]
config_bad_toml()719 fn config_bad_toml() {
720     write_config("asdf");
721     let config = new_config();
722     assert_error(
723         config.get::<i32>("foo").unwrap_err(),
724         "\
725 could not load Cargo configuration
726 
727 Caused by:
728   could not parse TOML configuration in `[..]/.cargo/config`
729 
730 Caused by:
731   could not parse input as TOML
732 
733 Caused by:
734   expected an equals, found eof at line 1 column 5",
735     );
736 }
737 
738 #[cargo_test]
config_get_list()739 fn config_get_list() {
740     write_config(
741         "\
742 l1 = []
743 l2 = ['one', 'two']
744 l3 = 123
745 l4 = ['one', 'two']
746 
747 [nested]
748 l = ['x']
749 
750 [nested2]
751 l = ['y']
752 
753 [nested-empty]
754 ",
755     );
756 
757     type L = Vec<String>;
758 
759     let config = ConfigBuilder::new()
760         .unstable_flag("advanced-env")
761         .env("CARGO_L4", "['three', 'four']")
762         .env("CARGO_L5", "['a']")
763         .env("CARGO_ENV_EMPTY", "[]")
764         .env("CARGO_ENV_BLANK", "")
765         .env("CARGO_ENV_NUM", "1")
766         .env("CARGO_ENV_NUM_LIST", "[1]")
767         .env("CARGO_ENV_TEXT", "asdf")
768         .env("CARGO_LEPAIR", "['a', 'b']")
769         .env("CARGO_NESTED2_L", "['z']")
770         .env("CARGO_NESTEDE_L", "['env']")
771         .env("CARGO_BAD_ENV", "[zzz]")
772         .build();
773 
774     assert_eq!(config.get::<L>("unset").unwrap(), vec![] as Vec<String>);
775     assert_eq!(config.get::<L>("l1").unwrap(), vec![] as Vec<String>);
776     assert_eq!(config.get::<L>("l2").unwrap(), vec!["one", "two"]);
777     assert_error(
778         config.get::<L>("l3").unwrap_err(),
779         "\
780 invalid configuration for key `l3`
781 expected a list, but found a integer for `l3` in [..]/.cargo/config",
782     );
783     assert_eq!(
784         config.get::<L>("l4").unwrap(),
785         vec!["one", "two", "three", "four"]
786     );
787     assert_eq!(config.get::<L>("l5").unwrap(), vec!["a"]);
788     assert_eq!(config.get::<L>("env-empty").unwrap(), vec![] as Vec<String>);
789     assert_eq!(config.get::<L>("env-blank").unwrap(), vec![] as Vec<String>);
790     assert_eq!(config.get::<L>("env-num").unwrap(), vec!["1".to_string()]);
791     assert_error(
792         config.get::<L>("env-num-list").unwrap_err(),
793         "error in environment variable `CARGO_ENV_NUM_LIST`: \
794          expected string, found integer",
795     );
796     assert_eq!(
797         config.get::<L>("env-text").unwrap(),
798         vec!["asdf".to_string()]
799     );
800     // "invalid number" here isn't the best error, but I think it's just toml.rs.
801     assert_error(
802         config.get::<L>("bad-env").unwrap_err(),
803         "error in environment variable `CARGO_BAD_ENV`: \
804          could not parse TOML list: invalid TOML value, did you mean to use a quoted string? at line 1 column 8",
805     );
806 
807     // Try some other sequence-like types.
808     assert_eq!(
809         config
810             .get::<(String, String, String, String)>("l4")
811             .unwrap(),
812         (
813             "one".to_string(),
814             "two".to_string(),
815             "three".to_string(),
816             "four".to_string()
817         )
818     );
819     assert_eq!(config.get::<(String,)>("l5").unwrap(), ("a".to_string(),));
820 
821     // Tuple struct
822     #[derive(Debug, Deserialize, Eq, PartialEq)]
823     struct TupS(String, String);
824     assert_eq!(
825         config.get::<TupS>("lepair").unwrap(),
826         TupS("a".to_string(), "b".to_string())
827     );
828 
829     // Nested with an option.
830     #[derive(Debug, Deserialize, Eq, PartialEq)]
831     struct S {
832         l: Option<Vec<String>>,
833     }
834     assert_eq!(config.get::<S>("nested-empty").unwrap(), S { l: None });
835     assert_eq!(
836         config.get::<S>("nested").unwrap(),
837         S {
838             l: Some(vec!["x".to_string()]),
839         }
840     );
841     assert_eq!(
842         config.get::<S>("nested2").unwrap(),
843         S {
844             l: Some(vec!["y".to_string(), "z".to_string()]),
845         }
846     );
847     assert_eq!(
848         config.get::<S>("nestede").unwrap(),
849         S {
850             l: Some(vec!["env".to_string()]),
851         }
852     );
853 }
854 
855 #[cargo_test]
config_get_other_types()856 fn config_get_other_types() {
857     write_config(
858         "\
859 ns = 123
860 ns2 = 456
861 ",
862     );
863 
864     let config = ConfigBuilder::new()
865         .env("CARGO_NSE", "987")
866         .env("CARGO_NS2", "654")
867         .build();
868 
869     #[derive(Debug, Deserialize, Eq, PartialEq)]
870     #[serde(transparent)]
871     struct NewS(i32);
872     assert_eq!(config.get::<NewS>("ns").unwrap(), NewS(123));
873     assert_eq!(config.get::<NewS>("ns2").unwrap(), NewS(654));
874     assert_eq!(config.get::<NewS>("nse").unwrap(), NewS(987));
875     assert_error(
876         config.get::<NewS>("unset").unwrap_err(),
877         "missing config key `unset`",
878     );
879 }
880 
881 #[cargo_test]
config_relative_path()882 fn config_relative_path() {
883     write_config(&format!(
884         "\
885 p1 = 'foo/bar'
886 p2 = '../abc'
887 p3 = 'b/c'
888 abs = '{}'
889 ",
890         paths::home().display(),
891     ));
892 
893     let config = ConfigBuilder::new()
894         .env("CARGO_EPATH", "a/b")
895         .env("CARGO_P3", "d/e")
896         .build();
897 
898     assert_eq!(
899         config
900             .get::<config::ConfigRelativePath>("p1")
901             .unwrap()
902             .resolve_path(&config),
903         paths::root().join("foo/bar")
904     );
905     assert_eq!(
906         config
907             .get::<config::ConfigRelativePath>("p2")
908             .unwrap()
909             .resolve_path(&config),
910         paths::root().join("../abc")
911     );
912     assert_eq!(
913         config
914             .get::<config::ConfigRelativePath>("p3")
915             .unwrap()
916             .resolve_path(&config),
917         paths::root().join("d/e")
918     );
919     assert_eq!(
920         config
921             .get::<config::ConfigRelativePath>("abs")
922             .unwrap()
923             .resolve_path(&config),
924         paths::home()
925     );
926     assert_eq!(
927         config
928             .get::<config::ConfigRelativePath>("epath")
929             .unwrap()
930             .resolve_path(&config),
931         paths::root().join("a/b")
932     );
933 }
934 
935 #[cargo_test]
config_get_integers()936 fn config_get_integers() {
937     write_config(
938         "\
939 npos = 123456789
940 nneg = -123456789
941 i64max = 9223372036854775807
942 ",
943     );
944 
945     let config = ConfigBuilder::new()
946         .env("CARGO_EPOS", "123456789")
947         .env("CARGO_ENEG", "-1")
948         .env("CARGO_EI64MAX", "9223372036854775807")
949         .build();
950 
951     assert_eq!(
952         config.get::<u64>("i64max").unwrap(),
953         9_223_372_036_854_775_807
954     );
955     assert_eq!(
956         config.get::<i64>("i64max").unwrap(),
957         9_223_372_036_854_775_807
958     );
959     assert_eq!(
960         config.get::<u64>("ei64max").unwrap(),
961         9_223_372_036_854_775_807
962     );
963     assert_eq!(
964         config.get::<i64>("ei64max").unwrap(),
965         9_223_372_036_854_775_807
966     );
967 
968     assert_error(
969         config.get::<u32>("nneg").unwrap_err(),
970         "\
971 error in [..].cargo/config: could not load config key `nneg`
972 
973 Caused by:
974   invalid value: integer `-123456789`, expected u32",
975     );
976     assert_error(
977         config.get::<u32>("eneg").unwrap_err(),
978         "\
979 error in environment variable `CARGO_ENEG`: could not load config key `eneg`
980 
981 Caused by:
982   invalid value: integer `-1`, expected u32",
983     );
984     assert_error(
985         config.get::<i8>("npos").unwrap_err(),
986         "\
987 error in [..].cargo/config: could not load config key `npos`
988 
989 Caused by:
990   invalid value: integer `123456789`, expected i8",
991     );
992     assert_error(
993         config.get::<i8>("epos").unwrap_err(),
994         "\
995 error in environment variable `CARGO_EPOS`: could not load config key `epos`
996 
997 Caused by:
998   invalid value: integer `123456789`, expected i8",
999     );
1000 }
1001 
1002 #[cargo_test]
config_get_ssl_version_missing()1003 fn config_get_ssl_version_missing() {
1004     write_config(
1005         "\
1006 [http]
1007 hello = 'world'
1008 ",
1009     );
1010 
1011     let config = new_config();
1012 
1013     assert!(config
1014         .get::<Option<SslVersionConfig>>("http.ssl-version")
1015         .unwrap()
1016         .is_none());
1017 }
1018 
1019 #[cargo_test]
config_get_ssl_version_single()1020 fn config_get_ssl_version_single() {
1021     write_config(
1022         "\
1023 [http]
1024 ssl-version = 'tlsv1.2'
1025 ",
1026     );
1027 
1028     let config = new_config();
1029 
1030     let a = config
1031         .get::<Option<SslVersionConfig>>("http.ssl-version")
1032         .unwrap()
1033         .unwrap();
1034     match a {
1035         SslVersionConfig::Single(v) => assert_eq!(&v, "tlsv1.2"),
1036         SslVersionConfig::Range(_) => panic!("Did not expect ssl version min/max."),
1037     };
1038 }
1039 
1040 #[cargo_test]
config_get_ssl_version_min_max()1041 fn config_get_ssl_version_min_max() {
1042     write_config(
1043         "\
1044 [http]
1045 ssl-version.min = 'tlsv1.2'
1046 ssl-version.max = 'tlsv1.3'
1047 ",
1048     );
1049 
1050     let config = new_config();
1051 
1052     let a = config
1053         .get::<Option<SslVersionConfig>>("http.ssl-version")
1054         .unwrap()
1055         .unwrap();
1056     match a {
1057         SslVersionConfig::Single(_) => panic!("Did not expect exact ssl version."),
1058         SslVersionConfig::Range(range) => {
1059             assert_eq!(range.min, Some(String::from("tlsv1.2")));
1060             assert_eq!(range.max, Some(String::from("tlsv1.3")));
1061         }
1062     };
1063 }
1064 
1065 #[cargo_test]
config_get_ssl_version_both_forms_configured()1066 fn config_get_ssl_version_both_forms_configured() {
1067     // this is not allowed
1068     write_config(
1069         "\
1070 [http]
1071 ssl-version = 'tlsv1.1'
1072 ssl-version.min = 'tlsv1.2'
1073 ssl-version.max = 'tlsv1.3'
1074 ",
1075     );
1076 
1077     let config = new_config();
1078 
1079     assert_error(
1080         config
1081             .get::<SslVersionConfig>("http.ssl-version")
1082             .unwrap_err(),
1083         "\
1084 could not load Cargo configuration
1085 
1086 Caused by:
1087   could not parse TOML configuration in `[..]/.cargo/config`
1088 
1089 Caused by:
1090   could not parse input as TOML
1091 
1092 Caused by:
1093   dotted key attempted to extend non-table type at line 2 column 15",
1094     );
1095     assert!(config
1096         .get::<Option<SslVersionConfig>>("http.ssl-version")
1097         .unwrap()
1098         .is_none());
1099 }
1100 
1101 #[cargo_test]
1102 /// Assert that unstable options can be configured with the `unstable` table in
1103 /// cargo config files
unstable_table_notation()1104 fn unstable_table_notation() {
1105     write_config(
1106         "\
1107 [unstable]
1108 print-im-a-teapot = true
1109 ",
1110     );
1111     let config = ConfigBuilder::new().nightly_features_allowed(true).build();
1112     assert_eq!(config.cli_unstable().print_im_a_teapot, true);
1113 }
1114 
1115 #[cargo_test]
1116 /// Assert that dotted notation works for configuring unstable options
unstable_dotted_notation()1117 fn unstable_dotted_notation() {
1118     write_config(
1119         "\
1120 unstable.print-im-a-teapot = true
1121 ",
1122     );
1123     let config = ConfigBuilder::new().nightly_features_allowed(true).build();
1124     assert_eq!(config.cli_unstable().print_im_a_teapot, true);
1125 }
1126 
1127 #[cargo_test]
1128 /// Assert that Zflags on the CLI take precedence over those from config
unstable_cli_precedence()1129 fn unstable_cli_precedence() {
1130     write_config(
1131         "\
1132 unstable.print-im-a-teapot = true
1133 ",
1134     );
1135     let config = ConfigBuilder::new().nightly_features_allowed(true).build();
1136     assert_eq!(config.cli_unstable().print_im_a_teapot, true);
1137 
1138     let config = ConfigBuilder::new()
1139         .unstable_flag("print-im-a-teapot=no")
1140         .build();
1141     assert_eq!(config.cli_unstable().print_im_a_teapot, false);
1142 }
1143 
1144 #[cargo_test]
1145 /// Assert that atempting to set an unstable flag that doesn't exist via config
1146 /// is ignored on stable
unstable_invalid_flag_ignored_on_stable()1147 fn unstable_invalid_flag_ignored_on_stable() {
1148     write_config(
1149         "\
1150 unstable.an-invalid-flag = 'yes'
1151 ",
1152     );
1153     assert!(ConfigBuilder::new().build_err().is_ok());
1154 }
1155 
1156 #[cargo_test]
1157 /// Assert that unstable options can be configured with the `unstable` table in
1158 /// cargo config files
unstable_flags_ignored_on_stable()1159 fn unstable_flags_ignored_on_stable() {
1160     write_config(
1161         "\
1162 [unstable]
1163 print-im-a-teapot = true
1164 ",
1165     );
1166     // Enforce stable channel even when testing on nightly.
1167     let config = ConfigBuilder::new().nightly_features_allowed(false).build();
1168     assert_eq!(config.cli_unstable().print_im_a_teapot, false);
1169 }
1170 
1171 #[cargo_test]
table_merge_failure()1172 fn table_merge_failure() {
1173     // Config::merge fails to merge entries in two tables.
1174     write_config_at(
1175         "foo/.cargo/config",
1176         "
1177         [table]
1178         key = ['foo']
1179         ",
1180     );
1181     write_config_at(
1182         ".cargo/config",
1183         "
1184         [table]
1185         key = 'bar'
1186         ",
1187     );
1188 
1189     #[derive(Debug, Deserialize)]
1190     struct Table {
1191         key: StringList,
1192     }
1193     let config = ConfigBuilder::new().cwd("foo").build();
1194     assert_error(
1195         config.get::<Table>("table").unwrap_err(),
1196         "\
1197 could not load Cargo configuration
1198 
1199 Caused by:
1200   failed to merge configuration at `[..]/.cargo/config`
1201 
1202 Caused by:
1203   failed to merge key `table` between [..]/foo/.cargo/config and [..]/.cargo/config
1204 
1205 Caused by:
1206   failed to merge key `key` between [..]/foo/.cargo/config and [..]/.cargo/config
1207 
1208 Caused by:
1209   failed to merge config value from `[..]/.cargo/config` into `[..]/foo/.cargo/config`: \
1210   expected array, but found string",
1211     );
1212 }
1213 
1214 #[cargo_test]
non_string_in_array()1215 fn non_string_in_array() {
1216     // Currently only strings are supported.
1217     write_config("foo = [1, 2, 3]");
1218     let config = new_config();
1219     assert_error(
1220         config.get::<Vec<i32>>("foo").unwrap_err(),
1221         "\
1222 could not load Cargo configuration
1223 
1224 Caused by:
1225   failed to load TOML configuration from `[..]/.cargo/config`
1226 
1227 Caused by:
1228   failed to parse key `foo`
1229 
1230 Caused by:
1231   expected string but found integer in list",
1232     );
1233 }
1234 
1235 #[cargo_test]
struct_with_opt_inner_struct()1236 fn struct_with_opt_inner_struct() {
1237     // Struct with a key that is Option of another struct.
1238     // Check that can be defined with environment variable.
1239     #[derive(Deserialize)]
1240     struct Inner {
1241         value: Option<i32>,
1242     }
1243     #[derive(Deserialize)]
1244     struct Foo {
1245         inner: Option<Inner>,
1246     }
1247     let config = ConfigBuilder::new()
1248         .env("CARGO_FOO_INNER_VALUE", "12")
1249         .build();
1250     let f: Foo = config.get("foo").unwrap();
1251     assert_eq!(f.inner.unwrap().value.unwrap(), 12);
1252 }
1253 
1254 #[cargo_test]
struct_with_default_inner_struct()1255 fn struct_with_default_inner_struct() {
1256     // Struct with serde defaults.
1257     // Check that can be defined with environment variable.
1258     #[derive(Deserialize, Default)]
1259     #[serde(default)]
1260     struct Inner {
1261         value: i32,
1262     }
1263     #[derive(Deserialize, Default)]
1264     #[serde(default)]
1265     struct Foo {
1266         inner: Inner,
1267     }
1268     let config = ConfigBuilder::new()
1269         .env("CARGO_FOO_INNER_VALUE", "12")
1270         .build();
1271     let f: Foo = config.get("foo").unwrap();
1272     assert_eq!(f.inner.value, 12);
1273 }
1274 
1275 #[cargo_test]
overlapping_env_config()1276 fn overlapping_env_config() {
1277     // Issue where one key is a prefix of another.
1278     #[derive(Deserialize)]
1279     #[serde(rename_all = "kebab-case")]
1280     struct Ambig {
1281         debug: Option<u32>,
1282         debug_assertions: Option<bool>,
1283     }
1284     let config = ConfigBuilder::new()
1285         .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1286         .build();
1287 
1288     let s: Ambig = config.get("ambig").unwrap();
1289     assert_eq!(s.debug_assertions, Some(true));
1290     assert_eq!(s.debug, None);
1291 
1292     let config = ConfigBuilder::new().env("CARGO_AMBIG_DEBUG", "0").build();
1293     let s: Ambig = config.get("ambig").unwrap();
1294     assert_eq!(s.debug_assertions, None);
1295     assert_eq!(s.debug, Some(0));
1296 
1297     let config = ConfigBuilder::new()
1298         .env("CARGO_AMBIG_DEBUG", "1")
1299         .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1300         .build();
1301     let s: Ambig = config.get("ambig").unwrap();
1302     assert_eq!(s.debug_assertions, Some(true));
1303     assert_eq!(s.debug, Some(1));
1304 }
1305 
1306 #[cargo_test]
overlapping_env_with_defaults_errors_out()1307 fn overlapping_env_with_defaults_errors_out() {
1308     // Issue where one key is a prefix of another.
1309     // This is a limitation of mapping environment variables on to a hierarchy.
1310     // Check that we error out when we hit ambiguity in this way, rather than
1311     // the more-surprising defaulting through.
1312     // If, in the future, we can handle this more correctly, feel free to delete
1313     // this test.
1314     #[derive(Deserialize, Default)]
1315     #[serde(default, rename_all = "kebab-case")]
1316     struct Ambig {
1317         debug: u32,
1318         debug_assertions: bool,
1319     }
1320     let config = ConfigBuilder::new()
1321         .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1322         .build();
1323     let err = config.get::<Ambig>("ambig").err().unwrap();
1324     assert!(format!("{}", err).contains("missing config key `ambig.debug`"));
1325 
1326     let config = ConfigBuilder::new().env("CARGO_AMBIG_DEBUG", "5").build();
1327     let s: Ambig = config.get("ambig").unwrap();
1328     assert_eq!(s.debug_assertions, bool::default());
1329     assert_eq!(s.debug, 5);
1330 
1331     let config = ConfigBuilder::new()
1332         .env("CARGO_AMBIG_DEBUG", "1")
1333         .env("CARGO_AMBIG_DEBUG_ASSERTIONS", "true")
1334         .build();
1335     let s: Ambig = config.get("ambig").unwrap();
1336     assert_eq!(s.debug_assertions, true);
1337     assert_eq!(s.debug, 1);
1338 }
1339 
1340 #[cargo_test]
struct_with_overlapping_inner_struct_and_defaults()1341 fn struct_with_overlapping_inner_struct_and_defaults() {
1342     // Struct with serde defaults.
1343     // Check that can be defined with environment variable.
1344     #[derive(Deserialize, Default)]
1345     #[serde(default)]
1346     struct Inner {
1347         value: i32,
1348     }
1349 
1350     // Containing struct with a prefix of inner
1351     //
1352     // This is a limitation of mapping environment variables on to a hierarchy.
1353     // Check that we error out when we hit ambiguity in this way, rather than
1354     // the more-surprising defaulting through.
1355     // If, in the future, we can handle this more correctly, feel free to delete
1356     // this case.
1357     #[derive(Deserialize, Default)]
1358     #[serde(default)]
1359     struct PrefixContainer {
1360         inn: bool,
1361         inner: Inner,
1362     }
1363     let config = ConfigBuilder::new()
1364         .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
1365         .build();
1366     let err = config
1367         .get::<PrefixContainer>("prefixcontainer")
1368         .err()
1369         .unwrap();
1370     assert!(format!("{}", err).contains("missing config key `prefixcontainer.inn`"));
1371     let config = ConfigBuilder::new()
1372         .env("CARGO_PREFIXCONTAINER_INNER_VALUE", "12")
1373         .env("CARGO_PREFIXCONTAINER_INN", "true")
1374         .build();
1375     let f: PrefixContainer = config.get("prefixcontainer").unwrap();
1376     assert_eq!(f.inner.value, 12);
1377     assert_eq!(f.inn, true);
1378 
1379     // Containing struct where the inner value's field is a prefix of another
1380     //
1381     // This is a limitation of mapping environment variables on to a hierarchy.
1382     // Check that we error out when we hit ambiguity in this way, rather than
1383     // the more-surprising defaulting through.
1384     // If, in the future, we can handle this more correctly, feel free to delete
1385     // this case.
1386     #[derive(Deserialize, Default)]
1387     #[serde(default)]
1388     struct InversePrefixContainer {
1389         inner_field: bool,
1390         inner: Inner,
1391     }
1392     let config = ConfigBuilder::new()
1393         .env("CARGO_INVERSEPREFIXCONTAINER_INNER_VALUE", "12")
1394         .build();
1395     let f: InversePrefixContainer = config.get("inverseprefixcontainer").unwrap();
1396     assert_eq!(f.inner_field, bool::default());
1397     assert_eq!(f.inner.value, 12);
1398 }
1399 
1400 #[cargo_test]
string_list_tricky_env()1401 fn string_list_tricky_env() {
1402     // Make sure StringList handles typed env values.
1403     let config = ConfigBuilder::new()
1404         .env("CARGO_KEY1", "123")
1405         .env("CARGO_KEY2", "true")
1406         .env("CARGO_KEY3", "1 2")
1407         .build();
1408     let x = config.get::<StringList>("key1").unwrap();
1409     assert_eq!(x.as_slice(), &["123".to_string()]);
1410     let x = config.get::<StringList>("key2").unwrap();
1411     assert_eq!(x.as_slice(), &["true".to_string()]);
1412     let x = config.get::<StringList>("key3").unwrap();
1413     assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]);
1414 }
1415 
1416 #[cargo_test]
string_list_wrong_type()1417 fn string_list_wrong_type() {
1418     // What happens if StringList is given then wrong type.
1419     write_config("some_list = 123");
1420     let config = ConfigBuilder::new().build();
1421     assert_error(
1422         config.get::<StringList>("some_list").unwrap_err(),
1423         "\
1424 invalid configuration for key `some_list`
1425 expected a string or array of strings, but found a integer for `some_list` in [..]/.cargo/config",
1426     );
1427 
1428     write_config("some_list = \"1 2\"");
1429     let config = ConfigBuilder::new().build();
1430     let x = config.get::<StringList>("some_list").unwrap();
1431     assert_eq!(x.as_slice(), &["1".to_string(), "2".to_string()]);
1432 }
1433 
1434 #[cargo_test]
string_list_advanced_env()1435 fn string_list_advanced_env() {
1436     // StringList with advanced env.
1437     let config = ConfigBuilder::new()
1438         .unstable_flag("advanced-env")
1439         .env("CARGO_KEY1", "[]")
1440         .env("CARGO_KEY2", "['1 2', '3']")
1441         .env("CARGO_KEY3", "[123]")
1442         .build();
1443     let x = config.get::<StringList>("key1").unwrap();
1444     assert_eq!(x.as_slice(), &[] as &[String]);
1445     let x = config.get::<StringList>("key2").unwrap();
1446     assert_eq!(x.as_slice(), &["1 2".to_string(), "3".to_string()]);
1447     assert_error(
1448         config.get::<StringList>("key3").unwrap_err(),
1449         "error in environment variable `CARGO_KEY3`: expected string, found integer",
1450     );
1451 }
1452 
1453 #[cargo_test]
parse_strip_with_string()1454 fn parse_strip_with_string() {
1455     write_config(
1456         "\
1457 [profile.release]
1458 strip = 'debuginfo'
1459 ",
1460     );
1461 
1462     let config = new_config();
1463 
1464     let p: toml::TomlProfile = config.get("profile.release").unwrap();
1465     let strip = p.strip.unwrap();
1466     assert_eq!(strip, toml::StringOrBool::String("debuginfo".to_string()));
1467 }
1468 
1469 #[cargo_test]
cargo_target_empty_cfg()1470 fn cargo_target_empty_cfg() {
1471     write_config(
1472         "\
1473 [build]
1474 target-dir = ''
1475 ",
1476     );
1477 
1478     let config = new_config();
1479 
1480     assert_error(
1481         config.target_dir().unwrap_err(),
1482         "the target directory is set to an empty string in [..]/.cargo/config",
1483     );
1484 }
1485 
1486 #[cargo_test]
cargo_target_empty_env()1487 fn cargo_target_empty_env() {
1488     let project = project().build();
1489 
1490     project.cargo("build")
1491         .env("CARGO_TARGET_DIR", "")
1492         .with_stderr("error: the target directory is set to an empty string in the `CARGO_TARGET_DIR` environment variable")
1493         .with_status(101)
1494         .run()
1495 }
1496