Be closer to the (maybe future) layout of the crate itself, and similar to how it's done throughout gitoxide.
Unified · Split
| @@ -0,0 +1,6 @@ | ||
| 1 | +type Result = std::result::Result<(), Box<dyn std::error::Error>>; | |
| 2 | + | |
| 3 | +mod git_config; | |
| 4 | +mod parser; | |
| 5 | +mod value; | |
| 6 | +mod values; | |
File renamed without changes.
| @@ -1,12 +0,0 @@ | ||
| 1 | −// See https://matklad.github.io/2021/02/27/delete-cargo-integration-tests.html | |
| 2 | −// for an explanation of why the integration tests are laid out like this. | |
| 3 | −// | |
| 4 | −// TL;DR single mod makes integration tests faster to compile, test, and with | |
| 5 | −// less build artifacts. | |
| 6 | − | |
| 7 | −type Result = std::result::Result<(), Box<dyn std::error::Error>>; | |
| 8 | − | |
| 9 | −mod git_config_integration_tests; | |
| 10 | −mod parser_integration_tests; | |
| 11 | −mod value_integration_tests; | |
| 12 | −mod values_integration_tests; | |
| @@ -1,403 +0,0 @@ | ||
| 1 | −/// Converts string to byte slice | |
| 2 | −#[cfg(test)] | |
| 3 | −fn b(s: &str) -> &[u8] { | |
| 4 | − s.as_bytes() | |
| 5 | −} | |
| 6 | − | |
| 7 | −#[cfg(test)] | |
| 8 | −mod normalize { | |
| 9 | − use git_config::values::normalize_str; | |
| 10 | − use std::borrow::Cow; | |
| 11 | − | |
| 12 | − #[test] | |
| 13 | − fn not_modified_is_borrowed() { | |
| 14 | − assert_eq!(normalize_str("hello world"), Cow::Borrowed(b"hello world")); | |
| 15 | − } | |
| 16 | − | |
| 17 | − #[test] | |
| 18 | − fn modified_is_owned() { | |
| 19 | − assert_eq!( | |
| 20 | − normalize_str("hello \"world\""), | |
| 21 | − Cow::<[u8]>::Owned(b"hello world".to_vec()) | |
| 22 | − ); | |
| 23 | − } | |
| 24 | − | |
| 25 | − #[test] | |
| 26 | − fn all_quoted_is_optimized() { | |
| 27 | − assert_eq!(normalize_str("\"hello world\""), Cow::Borrowed(b"hello world")); | |
| 28 | − } | |
| 29 | − | |
| 30 | − #[test] | |
| 31 | − fn all_quote_optimization_is_correct() { | |
| 32 | − assert_eq!(normalize_str(r#""hello" world\""#), Cow::Borrowed(b"hello world\"")); | |
| 33 | − } | |
| 34 | − | |
| 35 | − #[test] | |
| 36 | − fn quotes_right_next_to_each_other() { | |
| 37 | − assert_eq!( | |
| 38 | − normalize_str("\"hello\"\" world\""), | |
| 39 | − Cow::<[u8]>::Owned(b"hello world".to_vec()) | |
| 40 | − ); | |
| 41 | − } | |
| 42 | − | |
| 43 | − #[test] | |
| 44 | − fn escaped_quotes_are_kept() { | |
| 45 | − assert_eq!( | |
| 46 | − normalize_str(r#""hello \"\" world""#), | |
| 47 | − Cow::<[u8]>::Owned(b"hello \"\" world".to_vec()) | |
| 48 | − ); | |
| 49 | − } | |
| 50 | − | |
| 51 | − #[test] | |
| 52 | − fn empty_string() { | |
| 53 | − assert_eq!(normalize_str(""), Cow::Borrowed(b"")); | |
| 54 | − } | |
| 55 | − | |
| 56 | − #[test] | |
| 57 | − fn empty_normalized_string_is_optimized() { | |
| 58 | − assert_eq!(normalize_str("\"\""), Cow::Borrowed(b"")); | |
| 59 | − } | |
| 60 | −} | |
| 61 | − | |
| 62 | −#[cfg(test)] | |
| 63 | −mod boolean { | |
| 64 | − use crate::values_integration_tests::b; | |
| 65 | − use git_config::values::{Boolean, TrueVariant}; | |
| 66 | − use std::convert::TryFrom; | |
| 67 | − | |
| 68 | − #[test] | |
| 69 | − fn from_str_false() { | |
| 70 | − assert_eq!(Boolean::try_from(b("no")), Ok(Boolean::False("no".into()))); | |
| 71 | − assert_eq!(Boolean::try_from(b("off")), Ok(Boolean::False("off".into()))); | |
| 72 | − assert_eq!(Boolean::try_from(b("false")), Ok(Boolean::False("false".into()))); | |
| 73 | − assert_eq!(Boolean::try_from(b("zero")), Ok(Boolean::False("zero".into()))); | |
| 74 | − assert_eq!(Boolean::try_from(b("\"\"")), Ok(Boolean::False("\"\"".into()))); | |
| 75 | − } | |
| 76 | − | |
| 77 | − #[test] | |
| 78 | − fn from_str_true() { | |
| 79 | − assert_eq!( | |
| 80 | − Boolean::try_from(b("yes")), | |
| 81 | − Ok(Boolean::True(TrueVariant::Explicit("yes".into()))) | |
| 82 | − ); | |
| 83 | − assert_eq!( | |
| 84 | − Boolean::try_from(b("on")), | |
| 85 | − Ok(Boolean::True(TrueVariant::Explicit("on".into()))) | |
| 86 | − ); | |
| 87 | − assert_eq!( | |
| 88 | − Boolean::try_from(b("true")), | |
| 89 | − Ok(Boolean::True(TrueVariant::Explicit("true".into()))) | |
| 90 | − ); | |
| 91 | − assert_eq!( | |
| 92 | − Boolean::try_from(b("one")), | |
| 93 | − Ok(Boolean::True(TrueVariant::Explicit("one".into()))) | |
| 94 | − ); | |
| 95 | − } | |
| 96 | − | |
| 97 | − #[test] | |
| 98 | − fn ignores_case() { | |
| 99 | − // Random subset | |
| 100 | − for word in &["no", "yes", "off", "true", "zero"] { | |
| 101 | − let first: bool = Boolean::try_from(b(word)).unwrap().into(); | |
| 102 | − let second: bool = Boolean::try_from(b(&*word.to_uppercase())).unwrap().into(); | |
| 103 | − assert_eq!(first, second); | |
| 104 | − } | |
| 105 | − } | |
| 106 | − | |
| 107 | − #[test] | |
| 108 | − fn from_str_err() { | |
| 109 | − assert!(Boolean::try_from(b("yesn't")).is_err()); | |
| 110 | − assert!(Boolean::try_from(b("yesno")).is_err()); | |
| 111 | − } | |
| 112 | −} | |
| 113 | − | |
| 114 | −#[cfg(test)] | |
| 115 | −mod integer { | |
| 116 | − use crate::values_integration_tests::b; | |
| 117 | − use git_config::values::{Integer, IntegerSuffix}; | |
| 118 | − use std::convert::TryFrom; | |
| 119 | − | |
| 120 | − #[test] | |
| 121 | − fn from_str_no_suffix() { | |
| 122 | − assert_eq!(Integer::try_from(b("1")).unwrap(), Integer { value: 1, suffix: None }); | |
| 123 | − | |
| 124 | − assert_eq!( | |
| 125 | − Integer::try_from(b("-1")).unwrap(), | |
| 126 | − Integer { | |
| 127 | − value: -1, | |
| 128 | − suffix: None | |
| 129 | − } | |
| 130 | − ); | |
| 131 | − } | |
| 132 | − | |
| 133 | − #[test] | |
| 134 | − fn from_str_with_suffix() { | |
| 135 | − assert_eq!( | |
| 136 | − Integer::try_from(b("1k")).unwrap(), | |
| 137 | − Integer { | |
| 138 | − value: 1, | |
| 139 | − suffix: Some(IntegerSuffix::Kibi), | |
| 140 | − } | |
| 141 | − ); | |
| 142 | − | |
| 143 | − assert_eq!( | |
| 144 | − Integer::try_from(b("1m")).unwrap(), | |
| 145 | − Integer { | |
| 146 | − value: 1, | |
| 147 | − suffix: Some(IntegerSuffix::Mebi), | |
| 148 | − } | |
| 149 | − ); | |
| 150 | − | |
| 151 | − assert_eq!( | |
| 152 | − Integer::try_from(b("1g")).unwrap(), | |
| 153 | − Integer { | |
| 154 | − value: 1, | |
| 155 | − suffix: Some(IntegerSuffix::Gibi), | |
| 156 | − } | |
| 157 | − ); | |
| 158 | − } | |
| 159 | − | |
| 160 | − #[test] | |
| 161 | − fn invalid_from_str() { | |
| 162 | − assert!(Integer::try_from(b("")).is_err()); | |
| 163 | − assert!(Integer::try_from(b("-")).is_err()); | |
| 164 | − assert!(Integer::try_from(b("k")).is_err()); | |
| 165 | − assert!(Integer::try_from(b("m")).is_err()); | |
| 166 | − assert!(Integer::try_from(b("g")).is_err()); | |
| 167 | − assert!(Integer::try_from(b("123123123123123123123123")).is_err()); | |
| 168 | − assert!(Integer::try_from(b("gg")).is_err()); | |
| 169 | − } | |
| 170 | − | |
| 171 | − #[test] | |
| 172 | − fn as_decimal() { | |
| 173 | − assert_eq!( | |
| 174 | − Integer::try_from(b("12")).unwrap().as_decimal().unwrap(), | |
| 175 | − 12, | |
| 176 | − "works without suffix" | |
| 177 | − ); | |
| 178 | − assert_eq!( | |
| 179 | − Integer::try_from(b("13k")).unwrap().as_decimal().unwrap(), | |
| 180 | − 13 * 1024, | |
| 181 | − "works with kilobyte suffix" | |
| 182 | − ); | |
| 183 | − assert_eq!( | |
| 184 | − Integer::try_from(b("14m")).unwrap().as_decimal().unwrap(), | |
| 185 | − 14 * 1048576, | |
| 186 | − "works with megabyte suffix" | |
| 187 | − ); | |
| 188 | − assert_eq!( | |
| 189 | − Integer::try_from(b("15g")).unwrap().as_decimal().unwrap(), | |
| 190 | − 15 * 1073741824, | |
| 191 | − "works with gigabyte suffix" | |
| 192 | − ); | |
| 193 | − | |
| 194 | − let max_i64 = format!("{}g", i64::MAX); | |
| 195 | − assert!( | |
| 196 | − Integer::try_from(b(&max_i64)).unwrap().as_decimal().is_none(), | |
| 197 | − "overflow results in None" | |
| 198 | − ); | |
| 199 | − } | |
| 200 | −} | |
| 201 | − | |
| 202 | −#[cfg(test)] | |
| 203 | −mod color_value { | |
| 204 | − use git_config::values::ColorValue; | |
| 205 | − use std::str::FromStr; | |
| 206 | − | |
| 207 | − #[test] | |
| 208 | − fn non_bright() { | |
| 209 | − assert_eq!(ColorValue::from_str("normal"), Ok(ColorValue::Normal)); | |
| 210 | − assert_eq!(ColorValue::from_str("black"), Ok(ColorValue::Black)); | |
| 211 | − assert_eq!(ColorValue::from_str("red"), Ok(ColorValue::Red)); | |
| 212 | − assert_eq!(ColorValue::from_str("green"), Ok(ColorValue::Green)); | |
| 213 | − assert_eq!(ColorValue::from_str("yellow"), Ok(ColorValue::Yellow)); | |
| 214 | − assert_eq!(ColorValue::from_str("blue"), Ok(ColorValue::Blue)); | |
| 215 | − assert_eq!(ColorValue::from_str("magenta"), Ok(ColorValue::Magenta)); | |
| 216 | − assert_eq!(ColorValue::from_str("cyan"), Ok(ColorValue::Cyan)); | |
| 217 | − assert_eq!(ColorValue::from_str("white"), Ok(ColorValue::White)); | |
| 218 | − } | |
| 219 | − | |
| 220 | − #[test] | |
| 221 | − fn bright() { | |
| 222 | − assert_eq!(ColorValue::from_str("brightblack"), Ok(ColorValue::BrightBlack)); | |
| 223 | − assert_eq!(ColorValue::from_str("brightred"), Ok(ColorValue::BrightRed)); | |
| 224 | − assert_eq!(ColorValue::from_str("brightgreen"), Ok(ColorValue::BrightGreen)); | |
| 225 | − assert_eq!(ColorValue::from_str("brightyellow"), Ok(ColorValue::BrightYellow)); | |
| 226 | − assert_eq!(ColorValue::from_str("brightblue"), Ok(ColorValue::BrightBlue)); | |
| 227 | − assert_eq!(ColorValue::from_str("brightmagenta"), Ok(ColorValue::BrightMagenta)); | |
| 228 | − assert_eq!(ColorValue::from_str("brightcyan"), Ok(ColorValue::BrightCyan)); | |
| 229 | − assert_eq!(ColorValue::from_str("brightwhite"), Ok(ColorValue::BrightWhite)); | |
| 230 | − } | |
| 231 | − | |
| 232 | − #[test] | |
| 233 | − fn ansi() { | |
| 234 | − assert_eq!(ColorValue::from_str("255"), Ok(ColorValue::Ansi(255))); | |
| 235 | − assert_eq!(ColorValue::from_str("0"), Ok(ColorValue::Ansi(0))); | |
| 236 | − } | |
| 237 | − | |
| 238 | − #[test] | |
| 239 | − fn hex() { | |
| 240 | − assert_eq!(ColorValue::from_str("#ff0010"), Ok(ColorValue::Rgb(255, 0, 16))); | |
| 241 | − assert_eq!(ColorValue::from_str("#ffffff"), Ok(ColorValue::Rgb(255, 255, 255))); | |
| 242 | − assert_eq!(ColorValue::from_str("#000000"), Ok(ColorValue::Rgb(0, 0, 0))); | |
| 243 | − } | |
| 244 | − | |
| 245 | − #[test] | |
| 246 | − fn invalid() { | |
| 247 | − assert!(ColorValue::from_str("brightnormal").is_err()); | |
| 248 | − assert!(ColorValue::from_str("").is_err()); | |
| 249 | − assert!(ColorValue::from_str("bright").is_err()); | |
| 250 | − assert!(ColorValue::from_str("256").is_err()); | |
| 251 | − assert!(ColorValue::from_str("#").is_err()); | |
| 252 | − assert!(ColorValue::from_str("#fff").is_err()); | |
| 253 | − assert!(ColorValue::from_str("#gggggg").is_err()); | |
| 254 | − } | |
| 255 | −} | |
| 256 | − | |
| 257 | −#[cfg(test)] | |
| 258 | −mod color_attribute { | |
| 259 | − use git_config::values::ColorAttribute; | |
| 260 | − use std::str::FromStr; | |
| 261 | − | |
| 262 | − #[test] | |
| 263 | − fn non_inverted() { | |
| 264 | − assert_eq!(ColorAttribute::from_str("bold"), Ok(ColorAttribute::Bold)); | |
| 265 | − assert_eq!(ColorAttribute::from_str("dim"), Ok(ColorAttribute::Dim)); | |
| 266 | − assert_eq!(ColorAttribute::from_str("ul"), Ok(ColorAttribute::Ul)); | |
| 267 | − assert_eq!(ColorAttribute::from_str("blink"), Ok(ColorAttribute::Blink)); | |
| 268 | − assert_eq!(ColorAttribute::from_str("reverse"), Ok(ColorAttribute::Reverse)); | |
| 269 | − assert_eq!(ColorAttribute::from_str("italic"), Ok(ColorAttribute::Italic)); | |
| 270 | − assert_eq!(ColorAttribute::from_str("strike"), Ok(ColorAttribute::Strike)); | |
| 271 | − } | |
| 272 | − | |
| 273 | − #[test] | |
| 274 | − fn inverted_no_dash() { | |
| 275 | − assert_eq!(ColorAttribute::from_str("nobold"), Ok(ColorAttribute::NoBold)); | |
| 276 | − assert_eq!(ColorAttribute::from_str("nodim"), Ok(ColorAttribute::NoDim)); | |
| 277 | − assert_eq!(ColorAttribute::from_str("noul"), Ok(ColorAttribute::NoUl)); | |
| 278 | − assert_eq!(ColorAttribute::from_str("noblink"), Ok(ColorAttribute::NoBlink)); | |
| 279 | − assert_eq!(ColorAttribute::from_str("noreverse"), Ok(ColorAttribute::NoReverse)); | |
| 280 | − assert_eq!(ColorAttribute::from_str("noitalic"), Ok(ColorAttribute::NoItalic)); | |
| 281 | − assert_eq!(ColorAttribute::from_str("nostrike"), Ok(ColorAttribute::NoStrike)); | |
| 282 | − } | |
| 283 | − | |
| 284 | − #[test] | |
| 285 | − fn inverted_dashed() { | |
| 286 | − assert_eq!(ColorAttribute::from_str("no-bold"), Ok(ColorAttribute::NoBold)); | |
| 287 | − assert_eq!(ColorAttribute::from_str("no-dim"), Ok(ColorAttribute::NoDim)); | |
| 288 | − assert_eq!(ColorAttribute::from_str("no-ul"), Ok(ColorAttribute::NoUl)); | |
| 289 | − assert_eq!(ColorAttribute::from_str("no-blink"), Ok(ColorAttribute::NoBlink)); | |
| 290 | − assert_eq!(ColorAttribute::from_str("no-reverse"), Ok(ColorAttribute::NoReverse)); | |
| 291 | − assert_eq!(ColorAttribute::from_str("no-italic"), Ok(ColorAttribute::NoItalic)); | |
| 292 | − assert_eq!(ColorAttribute::from_str("no-strike"), Ok(ColorAttribute::NoStrike)); | |
| 293 | − } | |
| 294 | − | |
| 295 | − #[test] | |
| 296 | − fn invalid() { | |
| 297 | − assert!(ColorAttribute::from_str("a").is_err()); | |
| 298 | − assert!(ColorAttribute::from_str("no bold").is_err()); | |
| 299 | − assert!(ColorAttribute::from_str("").is_err()); | |
| 300 | − assert!(ColorAttribute::from_str("no").is_err()); | |
| 301 | − assert!(ColorAttribute::from_str("no-").is_err()); | |
| 302 | − } | |
| 303 | −} | |
| 304 | −#[cfg(test)] | |
| 305 | −mod interpolate_tests { | |
| 306 | − use crate::values_integration_tests::b; | |
| 307 | − use git_config::values::path::interpolate; | |
| 308 | − use git_config::values::Path; | |
| 309 | − use std::borrow::Cow; | |
| 310 | − | |
| 311 | − #[test] | |
| 312 | − fn no_interpolation_for_paths_without_tilde_or_prefix() { | |
| 313 | − let path = &b"/foo/bar"[..]; | |
| 314 | − let actual = Path::from(Cow::Borrowed(path)); | |
| 315 | − assert_eq!(&*actual, path); | |
| 316 | − assert!( | |
| 317 | − matches!(&actual.value, Cow::Borrowed(_)), | |
| 318 | − "it does not unnecessarily copy values" | |
| 319 | − ); | |
| 320 | − } | |
| 321 | − | |
| 322 | − #[test] | |
| 323 | − fn empty_path_is_error() { | |
| 324 | − assert!(matches!( | |
| 325 | − Path::from(Cow::Borrowed(b(""))).interpolate(None), | |
| 326 | − Err(interpolate::Error::Missing { what: "path" }) | |
| 327 | − )); | |
| 328 | − } | |
| 329 | − | |
| 330 | − #[test] | |
| 331 | − fn prefix_substitutes_git_install_dir() { | |
| 332 | − for git_install_dir in &["/tmp/git", "C:\\git"] { | |
| 333 | − for (val, expected) in &[ | |
| 334 | − (&b"%(prefix)/foo/bar"[..], "foo/bar"), | |
| 335 | − (b"%(prefix)/foo\\bar", "foo\\bar"), | |
| 336 | − ] { | |
| 337 | − let expected = | |
| 338 | − &std::path::PathBuf::from(format!("{}{}{}", git_install_dir, std::path::MAIN_SEPARATOR, expected)); | |
| 339 | − assert_eq!( | |
| 340 | − &*Path::from(Cow::Borrowed(*val)) | |
| 341 | − .interpolate(Some(std::path::Path::new(git_install_dir))) | |
| 342 | − .unwrap(), | |
| 343 | − expected, | |
| 344 | − "prefix interpolation keeps separators as they are" | |
| 345 | − ); | |
| 346 | − } | |
| 347 | − } | |
| 348 | − } | |
| 349 | − | |
| 350 | − #[test] | |
| 351 | − fn prefix_substitution_skipped_with_dot_slash() { | |
| 352 | − let path = "./%(prefix)/foo/bar"; | |
| 353 | − let git_install_dir = "/tmp/git"; | |
| 354 | − assert_eq!( | |
| 355 | − Path::from(Cow::Borrowed(b(path))) | |
| 356 | − .interpolate(Some(std::path::Path::new(git_install_dir))) | |
| 357 | − .unwrap(), | |
| 358 | − std::path::Path::new(path) | |
| 359 | − ); | |
| 360 | − } | |
| 361 | − | |
| 362 | − #[test] | |
| 363 | − fn tilde_substitutes_current_user() { | |
| 364 | − let path = &b"~/foo/bar"[..]; | |
| 365 | − let expected = format!( | |
| 366 | − "{}{}foo/bar", | |
| 367 | − dirs::home_dir().expect("empty home").display(), | |
| 368 | − std::path::MAIN_SEPARATOR | |
| 369 | − ); | |
| 370 | − assert_eq!( | |
| 371 | − Path::from(Cow::Borrowed(path)).interpolate(None).unwrap().as_ref(), | |
| 372 | − std::path::Path::new(&expected), | |
| 373 | − "note that path separators are not turned into slashes as we work with `std::path::Path`" | |
| 374 | − ); | |
| 375 | − } | |
| 376 | − | |
| 377 | − #[cfg(target_os = "windows")] | |
| 378 | − #[test] | |
| 379 | − fn tilde_with_given_user_is_unsupported_on_windows() { | |
| 380 | − assert!(matches!( | |
| 381 | − Path::from(Cow::Borrowed(&b"~baz/foo/bar"[..])).interpolate(None), | |
| 382 | − Err(interpolate::Error::UserInterpolationUnsupported) | |
| 383 | − )); | |
| 384 | − } | |
| 385 | − | |
| 386 | − #[cfg(not(target_os = "windows"))] | |
| 387 | − #[test] | |
| 388 | − fn tilde_with_given_user() { | |
| 389 | − let user = std::env::var("USER").unwrap(); | |
| 390 | − let home = std::env::var("HOME").unwrap(); | |
| 391 | − let specific_user_home = format!("~{}", user); | |
| 392 | − | |
| 393 | − for path_suffix in &["foo/bar", "foo\\bar", ""] { | |
| 394 | − let path = format!("{}{}{}", specific_user_home, std::path::MAIN_SEPARATOR, path_suffix); | |
| 395 | − let expected = format!("{}{}{}", home, std::path::MAIN_SEPARATOR, path_suffix); | |
| 396 | − assert_eq!( | |
| 397 | − Path::from(Cow::Borrowed(b(&path))).interpolate(None).unwrap(), | |
| 398 | − std::path::Path::new(&expected), | |
| 399 | − "it keeps path separators as is" | |
| 400 | − ); | |
| 401 | − } | |
| 402 | − } | |
| 403 | −} | |
File renamed without changes.
File renamed without changes.
| @@ -0,0 +1,48 @@ | ||
| 1 | +use crate::values::b; | |
| 2 | +use git_config::values::{Boolean, TrueVariant}; | |
| 3 | +use std::convert::TryFrom; | |
| 4 | + | |
| 5 | +#[test] | |
| 6 | +fn from_str_false() { | |
| 7 | + assert_eq!(Boolean::try_from(b("no")), Ok(Boolean::False("no".into()))); | |
| 8 | + assert_eq!(Boolean::try_from(b("off")), Ok(Boolean::False("off".into()))); | |
| 9 | + assert_eq!(Boolean::try_from(b("false")), Ok(Boolean::False("false".into()))); | |
| 10 | + assert_eq!(Boolean::try_from(b("zero")), Ok(Boolean::False("zero".into()))); | |
| 11 | + assert_eq!(Boolean::try_from(b("\"\"")), Ok(Boolean::False("\"\"".into()))); | |
| 12 | +} | |
| 13 | + | |
| 14 | +#[test] | |
| 15 | +fn from_str_true() { | |
| 16 | + assert_eq!( | |
| 17 | + Boolean::try_from(b("yes")), | |
| 18 | + Ok(Boolean::True(TrueVariant::Explicit("yes".into()))) | |
| 19 | + ); | |
| 20 | + assert_eq!( | |
| 21 | + Boolean::try_from(b("on")), | |
| 22 | + Ok(Boolean::True(TrueVariant::Explicit("on".into()))) | |
| 23 | + ); | |
| 24 | + assert_eq!( | |
| 25 | + Boolean::try_from(b("true")), | |
| 26 | + Ok(Boolean::True(TrueVariant::Explicit("true".into()))) | |
| 27 | + ); | |
| 28 | + assert_eq!( | |
| 29 | + Boolean::try_from(b("one")), | |
| 30 | + Ok(Boolean::True(TrueVariant::Explicit("one".into()))) | |
| 31 | + ); | |
| 32 | +} | |
| 33 | + | |
| 34 | +#[test] | |
| 35 | +fn ignores_case() { | |
| 36 | + // Random subset | |
| 37 | + for word in &["no", "yes", "off", "true", "zero"] { | |
| 38 | + let first: bool = Boolean::try_from(b(word)).unwrap().into(); | |
| 39 | + let second: bool = Boolean::try_from(b(&*word.to_uppercase())).unwrap().into(); | |
| 40 | + assert_eq!(first, second); | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +#[test] | |
| 45 | +fn from_str_err() { | |
| 46 | + assert!(Boolean::try_from(b("yesn't")).is_err()); | |
| 47 | + assert!(Boolean::try_from(b("yesno")).is_err()); | |
| 48 | +} | |
| @@ -0,0 +1,44 @@ | ||
| 1 | +use git_config::values::ColorAttribute; | |
| 2 | +use std::str::FromStr; | |
| 3 | + | |
| 4 | +#[test] | |
| 5 | +fn non_inverted() { | |
| 6 | + assert_eq!(ColorAttribute::from_str("bold"), Ok(ColorAttribute::Bold)); | |
| 7 | + assert_eq!(ColorAttribute::from_str("dim"), Ok(ColorAttribute::Dim)); | |
| 8 | + assert_eq!(ColorAttribute::from_str("ul"), Ok(ColorAttribute::Ul)); | |
| 9 | + assert_eq!(ColorAttribute::from_str("blink"), Ok(ColorAttribute::Blink)); | |
| 10 | + assert_eq!(ColorAttribute::from_str("reverse"), Ok(ColorAttribute::Reverse)); | |
| 11 | + assert_eq!(ColorAttribute::from_str("italic"), Ok(ColorAttribute::Italic)); | |
| 12 | + assert_eq!(ColorAttribute::from_str("strike"), Ok(ColorAttribute::Strike)); | |
| 13 | +} | |
| 14 | + | |
| 15 | +#[test] | |
| 16 | +fn inverted_no_dash() { | |
| 17 | + assert_eq!(ColorAttribute::from_str("nobold"), Ok(ColorAttribute::NoBold)); | |
| 18 | + assert_eq!(ColorAttribute::from_str("nodim"), Ok(ColorAttribute::NoDim)); | |
| 19 | + assert_eq!(ColorAttribute::from_str("noul"), Ok(ColorAttribute::NoUl)); | |
| 20 | + assert_eq!(ColorAttribute::from_str("noblink"), Ok(ColorAttribute::NoBlink)); | |
| 21 | + assert_eq!(ColorAttribute::from_str("noreverse"), Ok(ColorAttribute::NoReverse)); | |
| 22 | + assert_eq!(ColorAttribute::from_str("noitalic"), Ok(ColorAttribute::NoItalic)); | |
| 23 | + assert_eq!(ColorAttribute::from_str("nostrike"), Ok(ColorAttribute::NoStrike)); | |
| 24 | +} | |
| 25 | + | |
| 26 | +#[test] | |
| 27 | +fn inverted_dashed() { | |
| 28 | + assert_eq!(ColorAttribute::from_str("no-bold"), Ok(ColorAttribute::NoBold)); | |
| 29 | + assert_eq!(ColorAttribute::from_str("no-dim"), Ok(ColorAttribute::NoDim)); | |
| 30 | + assert_eq!(ColorAttribute::from_str("no-ul"), Ok(ColorAttribute::NoUl)); | |
| 31 | + assert_eq!(ColorAttribute::from_str("no-blink"), Ok(ColorAttribute::NoBlink)); | |
| 32 | + assert_eq!(ColorAttribute::from_str("no-reverse"), Ok(ColorAttribute::NoReverse)); | |
| 33 | + assert_eq!(ColorAttribute::from_str("no-italic"), Ok(ColorAttribute::NoItalic)); | |
| 34 | + assert_eq!(ColorAttribute::from_str("no-strike"), Ok(ColorAttribute::NoStrike)); | |
| 35 | +} | |
| 36 | + | |
| 37 | +#[test] | |
| 38 | +fn invalid() { | |
| 39 | + assert!(ColorAttribute::from_str("a").is_err()); | |
| 40 | + assert!(ColorAttribute::from_str("no bold").is_err()); | |
| 41 | + assert!(ColorAttribute::from_str("").is_err()); | |
| 42 | + assert!(ColorAttribute::from_str("no").is_err()); | |
| 43 | + assert!(ColorAttribute::from_str("no-").is_err()); | |
| 44 | +} | |
| @@ -0,0 +1,51 @@ | ||
| 1 | +use git_config::values::ColorValue; | |
| 2 | +use std::str::FromStr; | |
| 3 | + | |
| 4 | +#[test] | |
| 5 | +fn non_bright() { | |
| 6 | + assert_eq!(ColorValue::from_str("normal"), Ok(ColorValue::Normal)); | |
| 7 | + assert_eq!(ColorValue::from_str("black"), Ok(ColorValue::Black)); | |
| 8 | + assert_eq!(ColorValue::from_str("red"), Ok(ColorValue::Red)); | |
| 9 | + assert_eq!(ColorValue::from_str("green"), Ok(ColorValue::Green)); | |
| 10 | + assert_eq!(ColorValue::from_str("yellow"), Ok(ColorValue::Yellow)); | |
| 11 | + assert_eq!(ColorValue::from_str("blue"), Ok(ColorValue::Blue)); | |
| 12 | + assert_eq!(ColorValue::from_str("magenta"), Ok(ColorValue::Magenta)); | |
| 13 | + assert_eq!(ColorValue::from_str("cyan"), Ok(ColorValue::Cyan)); | |
| 14 | + assert_eq!(ColorValue::from_str("white"), Ok(ColorValue::White)); | |
| 15 | +} | |
| 16 | + | |
| 17 | +#[test] | |
| 18 | +fn bright() { | |
| 19 | + assert_eq!(ColorValue::from_str("brightblack"), Ok(ColorValue::BrightBlack)); | |
| 20 | + assert_eq!(ColorValue::from_str("brightred"), Ok(ColorValue::BrightRed)); | |
| 21 | + assert_eq!(ColorValue::from_str("brightgreen"), Ok(ColorValue::BrightGreen)); | |
| 22 | + assert_eq!(ColorValue::from_str("brightyellow"), Ok(ColorValue::BrightYellow)); | |
| 23 | + assert_eq!(ColorValue::from_str("brightblue"), Ok(ColorValue::BrightBlue)); | |
| 24 | + assert_eq!(ColorValue::from_str("brightmagenta"), Ok(ColorValue::BrightMagenta)); | |
| 25 | + assert_eq!(ColorValue::from_str("brightcyan"), Ok(ColorValue::BrightCyan)); | |
| 26 | + assert_eq!(ColorValue::from_str("brightwhite"), Ok(ColorValue::BrightWhite)); | |
| 27 | +} | |
| 28 | + | |
| 29 | +#[test] | |
| 30 | +fn ansi() { | |
| 31 | + assert_eq!(ColorValue::from_str("255"), Ok(ColorValue::Ansi(255))); | |
| 32 | + assert_eq!(ColorValue::from_str("0"), Ok(ColorValue::Ansi(0))); | |
| 33 | +} | |
| 34 | + | |
| 35 | +#[test] | |
| 36 | +fn hex() { | |
| 37 | + assert_eq!(ColorValue::from_str("#ff0010"), Ok(ColorValue::Rgb(255, 0, 16))); | |
| 38 | + assert_eq!(ColorValue::from_str("#ffffff"), Ok(ColorValue::Rgb(255, 255, 255))); | |
| 39 | + assert_eq!(ColorValue::from_str("#000000"), Ok(ColorValue::Rgb(0, 0, 0))); | |
| 40 | +} | |
| 41 | + | |
| 42 | +#[test] | |
| 43 | +fn invalid() { | |
| 44 | + assert!(ColorValue::from_str("brightnormal").is_err()); | |
| 45 | + assert!(ColorValue::from_str("").is_err()); | |
| 46 | + assert!(ColorValue::from_str("bright").is_err()); | |
| 47 | + assert!(ColorValue::from_str("256").is_err()); | |
| 48 | + assert!(ColorValue::from_str("#").is_err()); | |
| 49 | + assert!(ColorValue::from_str("#fff").is_err()); | |
| 50 | + assert!(ColorValue::from_str("#gggggg").is_err()); | |
| 51 | +} | |
| @@ -0,0 +1,84 @@ | ||
| 1 | +use crate::values::b; | |
| 2 | +use git_config::values::{Integer, IntegerSuffix}; | |
| 3 | +use std::convert::TryFrom; | |
| 4 | + | |
| 5 | +#[test] | |
| 6 | +fn from_str_no_suffix() { | |
| 7 | + assert_eq!(Integer::try_from(b("1")).unwrap(), Integer { value: 1, suffix: None }); | |
| 8 | + | |
| 9 | + assert_eq!( | |
| 10 | + Integer::try_from(b("-1")).unwrap(), | |
| 11 | + Integer { | |
| 12 | + value: -1, | |
| 13 | + suffix: None | |
| 14 | + } | |
| 15 | + ); | |
| 16 | +} | |
| 17 | + | |
| 18 | +#[test] | |
| 19 | +fn from_str_with_suffix() { | |
| 20 | + assert_eq!( | |
| 21 | + Integer::try_from(b("1k")).unwrap(), | |
| 22 | + Integer { | |
| 23 | + value: 1, | |
| 24 | + suffix: Some(IntegerSuffix::Kibi), | |
| 25 | + } | |
| 26 | + ); | |
| 27 | + | |
| 28 | + assert_eq!( | |
| 29 | + Integer::try_from(b("1m")).unwrap(), | |
| 30 | + Integer { | |
| 31 | + value: 1, | |
| 32 | + suffix: Some(IntegerSuffix::Mebi), | |
| 33 | + } | |
| 34 | + ); | |
| 35 | + | |
| 36 | + assert_eq!( | |
| 37 | + Integer::try_from(b("1g")).unwrap(), | |
| 38 | + Integer { | |
| 39 | + value: 1, | |
| 40 | + suffix: Some(IntegerSuffix::Gibi), | |
| 41 | + } | |
| 42 | + ); | |
| 43 | +} | |
| 44 | + | |
| 45 | +#[test] | |
| 46 | +fn invalid_from_str() { | |
| 47 | + assert!(Integer::try_from(b("")).is_err()); | |
| 48 | + assert!(Integer::try_from(b("-")).is_err()); | |
| 49 | + assert!(Integer::try_from(b("k")).is_err()); | |
| 50 | + assert!(Integer::try_from(b("m")).is_err()); | |
| 51 | + assert!(Integer::try_from(b("g")).is_err()); | |
| 52 | + assert!(Integer::try_from(b("123123123123123123123123")).is_err()); | |
| 53 | + assert!(Integer::try_from(b("gg")).is_err()); | |
| 54 | +} | |
| 55 | + | |
| 56 | +#[test] | |
| 57 | +fn as_decimal() { | |
| 58 | + assert_eq!( | |
| 59 | + Integer::try_from(b("12")).unwrap().as_decimal().unwrap(), | |
| 60 | + 12, | |
| 61 | + "works without suffix" | |
| 62 | + ); | |
| 63 | + assert_eq!( | |
| 64 | + Integer::try_from(b("13k")).unwrap().as_decimal().unwrap(), | |
| 65 | + 13 * 1024, | |
| 66 | + "works with kilobyte suffix" | |
| 67 | + ); | |
| 68 | + assert_eq!( | |
| 69 | + Integer::try_from(b("14m")).unwrap().as_decimal().unwrap(), | |
| 70 | + 14 * 1048576, | |
| 71 | + "works with megabyte suffix" | |
| 72 | + ); | |
| 73 | + assert_eq!( | |
| 74 | + Integer::try_from(b("15g")).unwrap().as_decimal().unwrap(), | |
| 75 | + 15 * 1073741824, | |
| 76 | + "works with gigabyte suffix" | |
| 77 | + ); | |
| 78 | + | |
| 79 | + let max_i64 = format!("{}g", i64::MAX); | |
| 80 | + assert!( | |
| 81 | + Integer::try_from(b(&max_i64)).unwrap().as_decimal().is_none(), | |
| 82 | + "overflow results in None" | |
| 83 | + ); | |
| 84 | +} | |
| @@ -0,0 +1,16 @@ | ||
| 1 | +/// Converts string to byte slice | |
| 2 | +fn b(s: &str) -> &[u8] { | |
| 3 | + s.as_bytes() | |
| 4 | +} | |
| 5 | + | |
| 6 | +mod normalize; | |
| 7 | + | |
| 8 | +mod boolean; | |
| 9 | + | |
| 10 | +mod integer; | |
| 11 | + | |
| 12 | +mod color_value; | |
| 13 | + | |
| 14 | +mod color_attribute; | |
| 15 | + | |
| 16 | +mod path; | |
| @@ -0,0 +1,51 @@ | ||
| 1 | +use git_config::values::normalize_str; | |
| 2 | +use std::borrow::Cow; | |
| 3 | + | |
| 4 | +#[test] | |
| 5 | +fn not_modified_is_borrowed() { | |
| 6 | + assert_eq!(normalize_str("hello world"), Cow::Borrowed(b"hello world")); | |
| 7 | +} | |
| 8 | + | |
| 9 | +#[test] | |
| 10 | +fn modified_is_owned() { | |
| 11 | + assert_eq!( | |
| 12 | + normalize_str("hello \"world\""), | |
| 13 | + Cow::<[u8]>::Owned(b"hello world".to_vec()) | |
| 14 | + ); | |
| 15 | +} | |
| 16 | + | |
| 17 | +#[test] | |
| 18 | +fn all_quoted_is_optimized() { | |
| 19 | + assert_eq!(normalize_str("\"hello world\""), Cow::Borrowed(b"hello world")); | |
| 20 | +} | |
| 21 | + | |
| 22 | +#[test] | |
| 23 | +fn all_quote_optimization_is_correct() { | |
| 24 | + assert_eq!(normalize_str(r#""hello" world\""#), Cow::Borrowed(b"hello world\"")); | |
| 25 | +} | |
| 26 | + | |
| 27 | +#[test] | |
| 28 | +fn quotes_right_next_to_each_other() { | |
| 29 | + assert_eq!( | |
| 30 | + normalize_str("\"hello\"\" world\""), | |
| 31 | + Cow::<[u8]>::Owned(b"hello world".to_vec()) | |
| 32 | + ); | |
| 33 | +} | |
| 34 | + | |
| 35 | +#[test] | |
| 36 | +fn escaped_quotes_are_kept() { | |
| 37 | + assert_eq!( | |
| 38 | + normalize_str(r#""hello \"\" world""#), | |
| 39 | + Cow::<[u8]>::Owned(b"hello \"\" world".to_vec()) | |
| 40 | + ); | |
| 41 | +} | |
| 42 | + | |
| 43 | +#[test] | |
| 44 | +fn empty_string() { | |
| 45 | + assert_eq!(normalize_str(""), Cow::Borrowed(b"")); | |
| 46 | +} | |
| 47 | + | |
| 48 | +#[test] | |
| 49 | +fn empty_normalized_string_is_optimized() { | |
| 50 | + assert_eq!(normalize_str("\"\""), Cow::Borrowed(b"")); | |
| 51 | +} | |
| @@ -0,0 +1,97 @@ | ||
| 1 | +use crate::values::b; | |
| 2 | +use git_config::values::path::interpolate; | |
| 3 | +use git_config::values::Path; | |
| 4 | +use std::borrow::Cow; | |
| 5 | + | |
| 6 | +#[test] | |
| 7 | +fn no_interpolation_for_paths_without_tilde_or_prefix() { | |
| 8 | + let path = &b"/foo/bar"[..]; | |
| 9 | + let actual = Path::from(Cow::Borrowed(path)); | |
| 10 | + assert_eq!(&*actual, path); | |
| 11 | + assert!( | |
| 12 | + matches!(&actual.value, Cow::Borrowed(_)), | |
| 13 | + "it does not unnecessarily copy values" | |
| 14 | + ); | |
| 15 | +} | |
| 16 | + | |
| 17 | +#[test] | |
| 18 | +fn empty_path_is_error() { | |
| 19 | + assert!(matches!( | |
| 20 | + Path::from(Cow::Borrowed(b(""))).interpolate(None), | |
| 21 | + Err(interpolate::Error::Missing { what: "path" }) | |
| 22 | + )); | |
| 23 | +} | |
| 24 | + | |
| 25 | +#[test] | |
| 26 | +fn prefix_substitutes_git_install_dir() { | |
| 27 | + for git_install_dir in &["/tmp/git", "C:\\git"] { | |
| 28 | + for (val, expected) in &[ | |
| 29 | + (&b"%(prefix)/foo/bar"[..], "foo/bar"), | |
| 30 | + (b"%(prefix)/foo\\bar", "foo\\bar"), | |
| 31 | + ] { | |
| 32 | + let expected = | |
| 33 | + &std::path::PathBuf::from(format!("{}{}{}", git_install_dir, std::path::MAIN_SEPARATOR, expected)); | |
| 34 | + assert_eq!( | |
| 35 | + &*Path::from(Cow::Borrowed(*val)) | |
| 36 | + .interpolate(Some(std::path::Path::new(git_install_dir))) | |
| 37 | + .unwrap(), | |
| 38 | + expected, | |
| 39 | + "prefix interpolation keeps separators as they are" | |
| 40 | + ); | |
| 41 | + } | |
| 42 | + } | |
| 43 | +} | |
| 44 | + | |
| 45 | +#[test] | |
| 46 | +fn prefix_substitution_skipped_with_dot_slash() { | |
| 47 | + let path = "./%(prefix)/foo/bar"; | |
| 48 | + let git_install_dir = "/tmp/git"; | |
| 49 | + assert_eq!( | |
| 50 | + Path::from(Cow::Borrowed(b(path))) | |
| 51 | + .interpolate(Some(std::path::Path::new(git_install_dir))) | |
| 52 | + .unwrap(), | |
| 53 | + std::path::Path::new(path) | |
| 54 | + ); | |
| 55 | +} | |
| 56 | + | |
| 57 | +#[test] | |
| 58 | +fn tilde_substitutes_current_user() { | |
| 59 | + let path = &b"~/foo/bar"[..]; | |
| 60 | + let expected = format!( | |
| 61 | + "{}{}foo/bar", | |
| 62 | + dirs::home_dir().expect("empty home").display(), | |
| 63 | + std::path::MAIN_SEPARATOR | |
| 64 | + ); | |
| 65 | + assert_eq!( | |
| 66 | + Path::from(Cow::Borrowed(path)).interpolate(None).unwrap().as_ref(), | |
| 67 | + std::path::Path::new(&expected), | |
| 68 | + "note that path separators are not turned into slashes as we work with `std::path::Path`" | |
| 69 | + ); | |
| 70 | +} | |
| 71 | + | |
| 72 | +#[cfg(target_os = "windows")] | |
| 73 | +#[test] | |
| 74 | +fn tilde_with_given_user_is_unsupported_on_windows() { | |
| 75 | + assert!(matches!( | |
| 76 | + Path::from(Cow::Borrowed(&b"~baz/foo/bar"[..])).interpolate(None), | |
| 77 | + Err(interpolate::Error::UserInterpolationUnsupported) | |
| 78 | + )); | |
| 79 | +} | |
| 80 | + | |
| 81 | +#[cfg(not(target_os = "windows"))] | |
| 82 | +#[test] | |
| 83 | +fn tilde_with_given_user() { | |
| 84 | + let user = std::env::var("USER").unwrap(); | |
| 85 | + let home = std::env::var("HOME").unwrap(); | |
| 86 | + let specific_user_home = format!("~{}", user); | |
| 87 | + | |
| 88 | + for path_suffix in &["foo/bar", "foo\\bar", ""] { | |
| 89 | + let path = format!("{}{}{}", specific_user_home, std::path::MAIN_SEPARATOR, path_suffix); | |
| 90 | + let expected = format!("{}{}{}", home, std::path::MAIN_SEPARATOR, path_suffix); | |
| 91 | + assert_eq!( | |
| 92 | + Path::from(Cow::Borrowed(b(&path))).interpolate(None).unwrap(), | |
| 93 | + std::path::Path::new(&expected), | |
| 94 | + "it keeps path separators as is" | |
| 95 | + ); | |
| 96 | + } | |
| 97 | +} | |