| use std::borrow::Borrow; |
| use std::collections; |
| use std::fs; |
| use std::io; |
| use std::os; |
| use std::path::Path; |
| |
| use cargo::core::{enable_nightly_features, Shell}; |
| use cargo::util::config::{self, Config, SslVersionConfig}; |
| use cargo::util::toml::{self, VecStringOrBool as VSOB}; |
| use cargo_test_support::{paths, project, t}; |
| use serde::Deserialize; |
| |
| fn lines_match(a: &str, b: &str) -> bool { |
| // Perform a small amount of normalization for filesystem paths before we |
| // send this to the `lines_match` function. |
| cargo_test_support::lines_match(&a.replace("\\", "/"), &b.replace("\\", "/")) |
| } |
| |
| #[cargo_test] |
| fn read_env_vars_for_config() { |
| let p = project() |
| .file( |
| "Cargo.toml", |
| r#" |
| [package] |
| name = "foo" |
| authors = [] |
| version = "0.0.0" |
| build = "build.rs" |
| "#, |
| ) |
| .file("src/lib.rs", "") |
| .file( |
| "build.rs", |
| r#" |
| use std::env; |
| fn main() { |
| assert_eq!(env::var("NUM_JOBS").unwrap(), "100"); |
| } |
| "#, |
| ) |
| .build(); |
| |
| p.cargo("build").env("CARGO_BUILD_JOBS", "100").run(); |
| } |
| |
| fn write_config(config: &str) { |
| let path = paths::root().join(".cargo/config"); |
| fs::create_dir_all(path.parent().unwrap()).unwrap(); |
| fs::write(path, config).unwrap(); |
| } |
| |
| fn write_config_toml(config: &str) { |
| let path = paths::root().join(".cargo/config.toml"); |
| fs::create_dir_all(path.parent().unwrap()).unwrap(); |
| fs::write(path, config).unwrap(); |
| } |
| |
| // Several test fail on windows if the user does not have permission to |
| // create symlinks (the `SeCreateSymbolicLinkPrivilege`). Instead of |
| // disabling these test on Windows, use this function to test whether we |
| // have permission, and return otherwise. This way, we still don't run these |
| // tests most of the time, but at least we do if the user has the right |
| // permissions. |
| // This function is derived from libstd fs tests. |
| pub fn got_symlink_permission() -> bool { |
| if cfg!(unix) { |
| return true; |
| } |
| let link = paths::root().join("some_hopefully_unique_link_name"); |
| let target = paths::root().join("nonexisting_target"); |
| |
| match symlink_file(&target, &link) { |
| Ok(_) => true, |
| // ERROR_PRIVILEGE_NOT_HELD = 1314 |
| Err(ref err) if err.raw_os_error() == Some(1314) => false, |
| Err(_) => true, |
| } |
| } |
| |
| #[cfg(unix)] |
| fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { |
| os::unix::fs::symlink(target, link) |
| } |
| |
| #[cfg(windows)] |
| fn symlink_file(target: &Path, link: &Path) -> io::Result<()> { |
| os::windows::fs::symlink_file(target, link) |
| } |
| |
| fn symlink_config_to_config_toml() { |
| let toml_path = paths::root().join(".cargo/config.toml"); |
| let symlink_path = paths::root().join(".cargo/config"); |
| t!(symlink_file(&toml_path, &symlink_path)); |
| } |
| |
| fn new_config(env: &[(&str, &str)]) -> Config { |
| enable_nightly_features(); // -Z advanced-env |
| let output = Box::new(fs::File::create(paths::root().join("shell.out")).unwrap()); |
| let shell = Shell::from_write(output); |
| let cwd = paths::root(); |
| let homedir = paths::home(); |
| let env = env |
| .iter() |
| .map(|(k, v)| (k.to_string(), v.to_string())) |
| .collect(); |
| let mut config = Config::new(shell, cwd, homedir); |
| config.set_env(env); |
| config |
| .configure( |
| 0, |
| None, |
| &None, |
| false, |
| false, |
| false, |
| &None, |
| &["advanced-env".into()], |
| ) |
| .unwrap(); |
| config |
| } |
| |
| fn assert_error<E: Borrow<failure::Error>>(error: E, msgs: &str) { |
| let causes = error |
| .borrow() |
| .iter_chain() |
| .map(|e| e.to_string()) |
| .collect::<Vec<_>>() |
| .join("\n"); |
| if !lines_match(msgs, &causes) { |
| panic!( |
| "Did not find expected:\n{}\nActual error:\n{}\n", |
| msgs, causes |
| ); |
| } |
| } |
| |
| #[cargo_test] |
| fn get_config() { |
| write_config( |
| "\ |
| [S] |
| f1 = 123 |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| #[derive(Debug, Deserialize, Eq, PartialEq)] |
| struct S { |
| f1: Option<i64>, |
| } |
| let s: S = config.get("S").unwrap(); |
| assert_eq!(s, S { f1: Some(123) }); |
| let config = new_config(&[("CARGO_S_F1", "456")]); |
| let s: S = config.get("S").unwrap(); |
| assert_eq!(s, S { f1: Some(456) }); |
| } |
| |
| #[cargo_test] |
| fn config_works_with_extension() { |
| write_config_toml( |
| "\ |
| [foo] |
| f1 = 1 |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1)); |
| } |
| |
| #[cargo_test] |
| fn config_ambiguous_filename_symlink_doesnt_warn() { |
| // Windows requires special permissions to create symlinks. |
| // If we don't have permission, just skip this test. |
| if !got_symlink_permission() { |
| return; |
| }; |
| |
| write_config_toml( |
| "\ |
| [foo] |
| f1 = 1 |
| ", |
| ); |
| |
| symlink_config_to_config_toml(); |
| |
| let config = new_config(&[]); |
| |
| assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1)); |
| |
| // It should NOT have warned for the symlink. |
| drop(config); // Paranoid about flushing the file. |
| let path = paths::root().join("shell.out"); |
| let output = fs::read_to_string(path).unwrap(); |
| let unexpected = "\ |
| warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config` |
| "; |
| if lines_match(unexpected, &output) { |
| panic!( |
| "Found unexpected:\n{}\nActual error:\n{}\n", |
| unexpected, output |
| ); |
| } |
| } |
| |
| #[cargo_test] |
| fn config_ambiguous_filename() { |
| write_config( |
| "\ |
| [foo] |
| f1 = 1 |
| ", |
| ); |
| |
| write_config_toml( |
| "\ |
| [foo] |
| f1 = 2 |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| // It should use the value from the one without the extension for |
| // backwards compatibility. |
| assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1)); |
| |
| // But it also should have warned. |
| drop(config); // Paranoid about flushing the file. |
| let path = paths::root().join("shell.out"); |
| let output = fs::read_to_string(path).unwrap(); |
| let expected = "\ |
| warning: Both `[..]/.cargo/config` and `[..]/.cargo/config.toml` exist. Using `[..]/.cargo/config` |
| "; |
| if !lines_match(expected, &output) { |
| panic!( |
| "Did not find expected:\n{}\nActual error:\n{}\n", |
| expected, output |
| ); |
| } |
| } |
| |
| #[cargo_test] |
| fn config_unused_fields() { |
| write_config( |
| "\ |
| [S] |
| unused = 456 |
| ", |
| ); |
| |
| let config = new_config(&[("CARGO_S_UNUSED2", "1"), ("CARGO_S2_UNUSED", "2")]); |
| |
| #[derive(Debug, Deserialize, Eq, PartialEq)] |
| struct S { |
| f1: Option<i64>, |
| } |
| // This prints a warning (verified below). |
| let s: S = config.get("S").unwrap(); |
| assert_eq!(s, S { f1: None }); |
| // This does not print anything, we cannot easily/reliably warn for |
| // environment variables. |
| let s: S = config.get("S2").unwrap(); |
| assert_eq!(s, S { f1: None }); |
| |
| // Verify the warnings. |
| drop(config); // Paranoid about flushing the file. |
| let path = paths::root().join("shell.out"); |
| let output = fs::read_to_string(path).unwrap(); |
| let expected = "\ |
| warning: unused key `S.unused` in config file `[..]/.cargo/config` |
| "; |
| if !lines_match(expected, &output) { |
| panic!( |
| "Did not find expected:\n{}\nActual error:\n{}\n", |
| expected, output |
| ); |
| } |
| } |
| |
| #[cargo_test] |
| fn config_load_toml_profile() { |
| write_config( |
| "\ |
| [profile.dev] |
| opt-level = 's' |
| lto = true |
| codegen-units=4 |
| debug = true |
| debug-assertions = true |
| rpath = true |
| panic = 'abort' |
| overflow-checks = true |
| incremental = true |
| |
| [profile.dev.build-override] |
| opt-level = 1 |
| |
| [profile.dev.package.bar] |
| codegen-units = 9 |
| |
| [profile.no-lto] |
| inherits = 'dev' |
| dir-name = 'without-lto' |
| lto = false |
| ", |
| ); |
| |
| let config = new_config(&[ |
| ("CARGO_PROFILE_DEV_CODEGEN_UNITS", "5"), |
| ("CARGO_PROFILE_DEV_BUILD_OVERRIDE_CODEGEN_UNITS", "11"), |
| ("CARGO_PROFILE_DEV_PACKAGE_env_CODEGEN_UNITS", "13"), |
| ("CARGO_PROFILE_DEV_PACKAGE_bar_OPT_LEVEL", "2"), |
| ]); |
| |
| // TODO: don't use actual `tomlprofile`. |
| let p: toml::TomlProfile = config.get("profile.dev").unwrap(); |
| let mut packages = collections::BTreeMap::new(); |
| let key = toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("bar").unwrap()); |
| let o_profile = toml::TomlProfile { |
| opt_level: Some(toml::TomlOptLevel("2".to_string())), |
| codegen_units: Some(9), |
| ..Default::default() |
| }; |
| packages.insert(key, o_profile); |
| let key = toml::ProfilePackageSpec::Spec(::cargo::core::PackageIdSpec::parse("env").unwrap()); |
| let o_profile = toml::TomlProfile { |
| codegen_units: Some(13), |
| ..Default::default() |
| }; |
| packages.insert(key, o_profile); |
| |
| assert_eq!( |
| p, |
| toml::TomlProfile { |
| opt_level: Some(toml::TomlOptLevel("s".to_string())), |
| lto: Some(toml::StringOrBool::Bool(true)), |
| codegen_units: Some(5), |
| debug: Some(toml::U32OrBool::Bool(true)), |
| debug_assertions: Some(true), |
| rpath: Some(true), |
| panic: Some("abort".to_string()), |
| overflow_checks: Some(true), |
| incremental: Some(true), |
| package: Some(packages), |
| build_override: Some(Box::new(toml::TomlProfile { |
| opt_level: Some(toml::TomlOptLevel("1".to_string())), |
| codegen_units: Some(11), |
| ..Default::default() |
| })), |
| ..Default::default() |
| } |
| ); |
| |
| let p: toml::TomlProfile = config.get("profile.no-lto").unwrap(); |
| assert_eq!( |
| p, |
| toml::TomlProfile { |
| lto: Some(toml::StringOrBool::Bool(false)), |
| dir_name: Some("without-lto".to_string()), |
| inherits: Some("dev".to_string()), |
| ..Default::default() |
| } |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_deserialize_any() { |
| // Some tests to exercise deserialize_any for deserializers that need to |
| // be told the format. |
| write_config( |
| "\ |
| a = true |
| b = ['b'] |
| c = ['c'] |
| ", |
| ); |
| |
| let config = new_config(&[ |
| ("CARGO_ENVB", "false"), |
| ("CARGO_C", "['d']"), |
| ("CARGO_ENVL", "['a', 'b']"), |
| ]); |
| |
| let a = config.get::<VSOB>("a").unwrap(); |
| match a { |
| VSOB::VecString(_) => panic!("expected bool"), |
| VSOB::Bool(b) => assert_eq!(b, true), |
| } |
| let b = config.get::<VSOB>("b").unwrap(); |
| match b { |
| VSOB::VecString(l) => assert_eq!(l, vec!["b".to_string()]), |
| VSOB::Bool(_) => panic!("expected list"), |
| } |
| let c = config.get::<VSOB>("c").unwrap(); |
| match c { |
| VSOB::VecString(l) => assert_eq!(l, vec!["c".to_string(), "d".to_string()]), |
| VSOB::Bool(_) => panic!("expected list"), |
| } |
| let envb = config.get::<VSOB>("envb").unwrap(); |
| match envb { |
| VSOB::VecString(_) => panic!("expected bool"), |
| VSOB::Bool(b) => assert_eq!(b, false), |
| } |
| let envl = config.get::<VSOB>("envl").unwrap(); |
| match envl { |
| VSOB::VecString(l) => assert_eq!(l, vec!["a".to_string(), "b".to_string()]), |
| VSOB::Bool(_) => panic!("expected list"), |
| } |
| } |
| |
| #[cargo_test] |
| fn config_toml_errors() { |
| write_config( |
| "\ |
| [profile.dev] |
| opt-level = 'foo' |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| assert_error( |
| config.get::<toml::TomlProfile>("profile.dev").unwrap_err(), |
| "error in [..]/.cargo/config: \ |
| could not load config key `profile.dev.opt-level`: \ |
| must be an integer, `z`, or `s`, but found: foo", |
| ); |
| |
| let config = new_config(&[("CARGO_PROFILE_DEV_OPT_LEVEL", "asdf")]); |
| |
| assert_error( |
| config.get::<toml::TomlProfile>("profile.dev").unwrap_err(), |
| "error in environment variable `CARGO_PROFILE_DEV_OPT_LEVEL`: \ |
| could not load config key `profile.dev.opt-level`: \ |
| must be an integer, `z`, or `s`, but found: asdf", |
| ); |
| } |
| |
| #[cargo_test] |
| fn load_nested() { |
| write_config( |
| "\ |
| [nest.foo] |
| f1 = 1 |
| f2 = 2 |
| [nest.bar] |
| asdf = 3 |
| ", |
| ); |
| |
| let config = new_config(&[ |
| ("CARGO_NEST_foo_f2", "3"), |
| ("CARGO_NESTE_foo_f1", "1"), |
| ("CARGO_NESTE_foo_f2", "3"), |
| ("CARGO_NESTE_bar_asdf", "3"), |
| ]); |
| |
| type Nested = collections::HashMap<String, collections::HashMap<String, u8>>; |
| |
| let n: Nested = config.get("nest").unwrap(); |
| let mut expected = collections::HashMap::new(); |
| let mut foo = collections::HashMap::new(); |
| foo.insert("f1".to_string(), 1); |
| foo.insert("f2".to_string(), 3); |
| expected.insert("foo".to_string(), foo); |
| let mut bar = collections::HashMap::new(); |
| bar.insert("asdf".to_string(), 3); |
| expected.insert("bar".to_string(), bar); |
| assert_eq!(n, expected); |
| |
| let n: Nested = config.get("neste").unwrap(); |
| assert_eq!(n, expected); |
| } |
| |
| #[cargo_test] |
| fn get_errors() { |
| write_config( |
| "\ |
| [S] |
| f1 = 123 |
| f2 = 'asdf' |
| big = 123456789 |
| ", |
| ); |
| |
| let config = new_config(&[("CARGO_E_S", "asdf"), ("CARGO_E_BIG", "123456789")]); |
| assert_error( |
| config.get::<i64>("foo").unwrap_err(), |
| "missing config key `foo`", |
| ); |
| assert_error( |
| config.get::<i64>("foo.bar").unwrap_err(), |
| "missing config key `foo.bar`", |
| ); |
| assert_error( |
| config.get::<i64>("S.f2").unwrap_err(), |
| "error in [..]/.cargo/config: `S.f2` expected an integer, but found a string", |
| ); |
| assert_error( |
| config.get::<u8>("S.big").unwrap_err(), |
| "error in [..].cargo/config: could not load config key `S.big`: \ |
| invalid value: integer `123456789`, expected u8", |
| ); |
| |
| // Environment variable type errors. |
| assert_error( |
| config.get::<i64>("e.s").unwrap_err(), |
| "error in environment variable `CARGO_E_S`: invalid digit found in string", |
| ); |
| assert_error( |
| config.get::<i8>("e.big").unwrap_err(), |
| "error in environment variable `CARGO_E_BIG`: \ |
| could not load config key `e.big`: \ |
| invalid value: integer `123456789`, expected i8", |
| ); |
| |
| #[derive(Debug, Deserialize)] |
| struct S { |
| f1: i64, |
| f2: String, |
| f3: i64, |
| big: i64, |
| } |
| assert_error( |
| config.get::<S>("S").unwrap_err(), |
| "missing config key `S.f3`", |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_get_option() { |
| write_config( |
| "\ |
| [foo] |
| f1 = 1 |
| ", |
| ); |
| |
| let config = new_config(&[("CARGO_BAR_ASDF", "3")]); |
| |
| assert_eq!(config.get::<Option<i32>>("a").unwrap(), None); |
| assert_eq!(config.get::<Option<i32>>("a.b").unwrap(), None); |
| assert_eq!(config.get::<Option<i32>>("foo.f1").unwrap(), Some(1)); |
| assert_eq!(config.get::<Option<i32>>("bar.asdf").unwrap(), Some(3)); |
| assert_eq!(config.get::<Option<i32>>("bar.zzzz").unwrap(), None); |
| } |
| |
| #[cargo_test] |
| fn config_bad_toml() { |
| write_config("asdf"); |
| let config = new_config(&[]); |
| assert_error( |
| config.get::<i32>("foo").unwrap_err(), |
| "\ |
| could not load Cargo configuration |
| |
| Caused by: |
| could not parse TOML configuration in `[..]/.cargo/config` |
| |
| Caused by: |
| could not parse input as TOML |
| |
| Caused by: |
| expected an equals, found eof at line 1 column 5", |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_get_list() { |
| write_config( |
| "\ |
| l1 = [] |
| l2 = ['one', 'two'] |
| l3 = 123 |
| l4 = ['one', 'two'] |
| |
| [nested] |
| l = ['x'] |
| |
| [nested2] |
| l = ['y'] |
| |
| [nested-empty] |
| ", |
| ); |
| |
| type L = Vec<String>; |
| |
| let config = new_config(&[ |
| ("CARGO_L4", "['three', 'four']"), |
| ("CARGO_L5", "['a']"), |
| ("CARGO_ENV_EMPTY", "[]"), |
| ("CARGO_ENV_BLANK", ""), |
| ("CARGO_ENV_NUM", "1"), |
| ("CARGO_ENV_NUM_LIST", "[1]"), |
| ("CARGO_ENV_TEXT", "asdf"), |
| ("CARGO_LEPAIR", "['a', 'b']"), |
| ("CARGO_NESTED2_L", "['z']"), |
| ("CARGO_NESTEDE_L", "['env']"), |
| ("CARGO_BAD_ENV", "[zzz]"), |
| ]); |
| |
| assert_eq!(config.get::<L>("unset").unwrap(), vec![] as Vec<String>); |
| assert_eq!(config.get::<L>("l1").unwrap(), vec![] as Vec<String>); |
| assert_eq!(config.get::<L>("l2").unwrap(), vec!["one", "two"]); |
| assert_error( |
| config.get::<L>("l3").unwrap_err(), |
| "\ |
| invalid configuration for key `l3` |
| expected a list, but found a integer for `l3` in [..]/.cargo/config", |
| ); |
| assert_eq!( |
| config.get::<L>("l4").unwrap(), |
| vec!["one", "two", "three", "four"] |
| ); |
| assert_eq!(config.get::<L>("l5").unwrap(), vec!["a"]); |
| assert_eq!(config.get::<L>("env-empty").unwrap(), vec![] as Vec<String>); |
| assert_error( |
| config.get::<L>("env-blank").unwrap_err(), |
| "error in environment variable `CARGO_ENV_BLANK`: \ |
| should have TOML list syntax, found ``", |
| ); |
| assert_error( |
| config.get::<L>("env-num").unwrap_err(), |
| "error in environment variable `CARGO_ENV_NUM`: \ |
| should have TOML list syntax, found `1`", |
| ); |
| assert_error( |
| config.get::<L>("env-num-list").unwrap_err(), |
| "error in environment variable `CARGO_ENV_NUM_LIST`: \ |
| expected string, found integer", |
| ); |
| assert_error( |
| config.get::<L>("env-text").unwrap_err(), |
| "error in environment variable `CARGO_ENV_TEXT`: \ |
| should have TOML list syntax, found `asdf`", |
| ); |
| // "invalid number" here isn't the best error, but I think it's just toml.rs. |
| assert_error( |
| config.get::<L>("bad-env").unwrap_err(), |
| "error in environment variable `CARGO_BAD_ENV`: \ |
| could not parse TOML list: invalid number at line 1 column 8", |
| ); |
| |
| // Try some other sequence-like types. |
| assert_eq!( |
| config |
| .get::<(String, String, String, String)>("l4") |
| .unwrap(), |
| ( |
| "one".to_string(), |
| "two".to_string(), |
| "three".to_string(), |
| "four".to_string() |
| ) |
| ); |
| assert_eq!(config.get::<(String,)>("l5").unwrap(), ("a".to_string(),)); |
| |
| // Tuple struct |
| #[derive(Debug, Deserialize, Eq, PartialEq)] |
| struct TupS(String, String); |
| assert_eq!( |
| config.get::<TupS>("lepair").unwrap(), |
| TupS("a".to_string(), "b".to_string()) |
| ); |
| |
| // Nested with an option. |
| #[derive(Debug, Deserialize, Eq, PartialEq)] |
| struct S { |
| l: Option<Vec<String>>, |
| } |
| assert_eq!(config.get::<S>("nested-empty").unwrap(), S { l: None }); |
| assert_eq!( |
| config.get::<S>("nested").unwrap(), |
| S { |
| l: Some(vec!["x".to_string()]), |
| } |
| ); |
| assert_eq!( |
| config.get::<S>("nested2").unwrap(), |
| S { |
| l: Some(vec!["y".to_string(), "z".to_string()]), |
| } |
| ); |
| assert_eq!( |
| config.get::<S>("nestede").unwrap(), |
| S { |
| l: Some(vec!["env".to_string()]), |
| } |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_get_other_types() { |
| write_config( |
| "\ |
| ns = 123 |
| ns2 = 456 |
| ", |
| ); |
| |
| let config = new_config(&[("CARGO_NSE", "987"), ("CARGO_NS2", "654")]); |
| |
| #[derive(Debug, Deserialize, Eq, PartialEq)] |
| #[serde(transparent)] |
| struct NewS(i32); |
| assert_eq!(config.get::<NewS>("ns").unwrap(), NewS(123)); |
| assert_eq!(config.get::<NewS>("ns2").unwrap(), NewS(654)); |
| assert_eq!(config.get::<NewS>("nse").unwrap(), NewS(987)); |
| assert_error( |
| config.get::<NewS>("unset").unwrap_err(), |
| "missing config key `unset`", |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_relative_path() { |
| write_config(&format!( |
| "\ |
| p1 = 'foo/bar' |
| p2 = '../abc' |
| p3 = 'b/c' |
| abs = '{}' |
| ", |
| paths::home().display(), |
| )); |
| |
| let config = new_config(&[("CARGO_EPATH", "a/b"), ("CARGO_P3", "d/e")]); |
| |
| assert_eq!( |
| config |
| .get::<config::ConfigRelativePath>("p1") |
| .unwrap() |
| .resolve_path(&config), |
| paths::root().join("foo/bar") |
| ); |
| assert_eq!( |
| config |
| .get::<config::ConfigRelativePath>("p2") |
| .unwrap() |
| .resolve_path(&config), |
| paths::root().join("../abc") |
| ); |
| assert_eq!( |
| config |
| .get::<config::ConfigRelativePath>("p3") |
| .unwrap() |
| .resolve_path(&config), |
| paths::root().join("d/e") |
| ); |
| assert_eq!( |
| config |
| .get::<config::ConfigRelativePath>("abs") |
| .unwrap() |
| .resolve_path(&config), |
| paths::home() |
| ); |
| assert_eq!( |
| config |
| .get::<config::ConfigRelativePath>("epath") |
| .unwrap() |
| .resolve_path(&config), |
| paths::root().join("a/b") |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_get_integers() { |
| write_config( |
| "\ |
| npos = 123456789 |
| nneg = -123456789 |
| i64max = 9223372036854775807 |
| ", |
| ); |
| |
| let config = new_config(&[ |
| ("CARGO_EPOS", "123456789"), |
| ("CARGO_ENEG", "-1"), |
| ("CARGO_EI64MAX", "9223372036854775807"), |
| ]); |
| |
| assert_eq!( |
| config.get::<u64>("i64max").unwrap(), |
| 9_223_372_036_854_775_807 |
| ); |
| assert_eq!( |
| config.get::<i64>("i64max").unwrap(), |
| 9_223_372_036_854_775_807 |
| ); |
| assert_eq!( |
| config.get::<u64>("ei64max").unwrap(), |
| 9_223_372_036_854_775_807 |
| ); |
| assert_eq!( |
| config.get::<i64>("ei64max").unwrap(), |
| 9_223_372_036_854_775_807 |
| ); |
| |
| assert_error( |
| config.get::<u32>("nneg").unwrap_err(), |
| "error in [..].cargo/config: \ |
| could not load config key `nneg`: \ |
| invalid value: integer `-123456789`, expected u32", |
| ); |
| assert_error( |
| config.get::<u32>("eneg").unwrap_err(), |
| "error in environment variable `CARGO_ENEG`: \ |
| could not load config key `eneg`: \ |
| invalid value: integer `-1`, expected u32", |
| ); |
| assert_error( |
| config.get::<i8>("npos").unwrap_err(), |
| "error in [..].cargo/config: \ |
| could not load config key `npos`: \ |
| invalid value: integer `123456789`, expected i8", |
| ); |
| assert_error( |
| config.get::<i8>("epos").unwrap_err(), |
| "error in environment variable `CARGO_EPOS`: \ |
| could not load config key `epos`: \ |
| invalid value: integer `123456789`, expected i8", |
| ); |
| } |
| |
| #[cargo_test] |
| fn config_get_ssl_version_missing() { |
| write_config( |
| "\ |
| [http] |
| hello = 'world' |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| assert!(config |
| .get::<Option<SslVersionConfig>>("http.ssl-version") |
| .unwrap() |
| .is_none()); |
| } |
| |
| #[cargo_test] |
| fn config_get_ssl_version_single() { |
| write_config( |
| "\ |
| [http] |
| ssl-version = 'tlsv1.2' |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| let a = config |
| .get::<Option<SslVersionConfig>>("http.ssl-version") |
| .unwrap() |
| .unwrap(); |
| match a { |
| SslVersionConfig::Single(v) => assert_eq!(&v, "tlsv1.2"), |
| SslVersionConfig::Range(_) => panic!("Did not expect ssl version min/max."), |
| }; |
| } |
| |
| #[cargo_test] |
| fn config_get_ssl_version_min_max() { |
| write_config( |
| "\ |
| [http] |
| ssl-version.min = 'tlsv1.2' |
| ssl-version.max = 'tlsv1.3' |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| let a = config |
| .get::<Option<SslVersionConfig>>("http.ssl-version") |
| .unwrap() |
| .unwrap(); |
| match a { |
| SslVersionConfig::Single(_) => panic!("Did not expect exact ssl version."), |
| SslVersionConfig::Range(range) => { |
| assert_eq!(range.min, Some(String::from("tlsv1.2"))); |
| assert_eq!(range.max, Some(String::from("tlsv1.3"))); |
| } |
| }; |
| } |
| |
| #[cargo_test] |
| fn config_get_ssl_version_both_forms_configured() { |
| // this is not allowed |
| write_config( |
| "\ |
| [http] |
| ssl-version = 'tlsv1.1' |
| ssl-version.min = 'tlsv1.2' |
| ssl-version.max = 'tlsv1.3' |
| ", |
| ); |
| |
| let config = new_config(&[]); |
| |
| assert!(config.get::<SslVersionConfig>("http.ssl-version").is_err()); |
| assert!(config |
| .get::<Option<SslVersionConfig>>("http.ssl-version") |
| .unwrap() |
| .is_none()); |
| } |