1use std::ffi::OsString; 2 3use super::utils; 4 5use clap::{arg, error::ErrorKind, Arg, ArgAction, Command}; 6 7static ALLOW_EXT_SC: &str = "\ 8Usage: clap-test [COMMAND] 9 10Options: 11 -h, --help Print help 12 -V, --version Print version 13"; 14 15static DONT_COLLAPSE_ARGS: &str = "\ 16Usage: clap-test [arg1] [arg2] [arg3] 17 18Arguments: 19 [arg1] some 20 [arg2] some 21 [arg3] some 22 23Options: 24 -h, --help Print help 25 -V, --version Print version 26"; 27 28#[test] 29fn sub_command_negate_required() { 30 Command::new("sub_command_negate") 31 .subcommand_negates_reqs(true) 32 .arg(Arg::new("test").required(true).index(1)) 33 .subcommand(Command::new("sub1")) 34 .try_get_matches_from(vec!["myprog", "sub1"]) 35 .unwrap(); 36} 37 38#[test] 39fn sub_command_negate_required_2() { 40 let result = Command::new("sub_command_negate") 41 .subcommand_negates_reqs(true) 42 .arg(Arg::new("test").required(true).index(1)) 43 .subcommand(Command::new("sub1")) 44 .try_get_matches_from(vec![""]); 45 assert!(result.is_err()); 46 let err = result.err().unwrap(); 47 assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 48} 49 50#[test] 51fn sub_command_required() { 52 let result = Command::new("sc_required") 53 .subcommand_required(true) 54 .subcommand(Command::new("sub1")) 55 .try_get_matches_from(vec![""]); 56 assert!(result.is_err()); 57 let err = result.err().unwrap(); 58 assert_eq!(err.kind(), ErrorKind::MissingSubcommand); 59} 60 61#[test] 62#[cfg(feature = "error-context")] 63fn sub_command_required_error() { 64 static ERROR: &str = "\ 65error: 'sc_required' requires a subcommand but one was not provided 66 [subcommands: sub1, help] 67 68Usage: sc_required <COMMAND> 69 70For more information, try '--help'. 71"; 72 73 let cmd = Command::new("sc_required") 74 .subcommand_required(true) 75 .subcommand(Command::new("sub1")); 76 utils::assert_output(cmd, "sc_required", ERROR, true); 77} 78 79#[test] 80fn arg_required_else_help() { 81 let result = Command::new("arg_required") 82 .arg_required_else_help(true) 83 .arg(Arg::new("test").index(1)) 84 .try_get_matches_from(vec![""]); 85 86 assert!(result.is_err()); 87 let err = result.err().unwrap(); 88 assert_eq!( 89 err.kind(), 90 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand 91 ); 92} 93 94#[test] 95fn arg_required_else_help_over_req_arg() { 96 let result = Command::new("arg_required") 97 .arg_required_else_help(true) 98 .arg(Arg::new("test").index(1).required(true)) 99 .try_get_matches_from(vec![""]); 100 101 assert!(result.is_err()); 102 let err = result.err().unwrap(); 103 assert_eq!( 104 err.kind(), 105 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand 106 ); 107} 108 109#[test] 110fn arg_required_else_help_over_req_subcommand() { 111 let result = Command::new("sub_required") 112 .arg_required_else_help(true) 113 .subcommand_required(true) 114 .subcommand(Command::new("sub1")) 115 .try_get_matches_from(vec![""]); 116 117 assert!(result.is_err()); 118 let err = result.err().unwrap(); 119 assert_eq!( 120 err.kind(), 121 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand 122 ); 123} 124 125#[test] 126fn arg_required_else_help_with_default() { 127 let result = Command::new("arg_required") 128 .arg_required_else_help(true) 129 .arg(arg!(--input <PATH>).default_value("-")) 130 .try_get_matches_from(vec![""]); 131 132 assert!(result.is_err()); 133 let err = result.err().unwrap(); 134 assert_eq!( 135 err.kind(), 136 ErrorKind::DisplayHelpOnMissingArgumentOrSubcommand 137 ); 138} 139 140#[test] 141fn arg_required_else_help_error_message() { 142 static ARG_REQUIRED_ELSE_HELP: &str = "\ 143Usage: test [OPTIONS] 144 145Options: 146 -i, --info Provides more info 147 -h, --help Print help 148 -V, --version Print version 149"; 150 151 let cmd = Command::new("test") 152 .arg_required_else_help(true) 153 .version("1.0") 154 .arg( 155 Arg::new("info") 156 .help("Provides more info") 157 .short('i') 158 .long("info") 159 .action(ArgAction::SetTrue), 160 ); 161 utils::assert_output( 162 cmd, 163 "test", 164 ARG_REQUIRED_ELSE_HELP, 165 true, // Unlike normal displaying of help, we should provide a fatal exit code 166 ); 167} 168 169#[cfg(not(feature = "suggestions"))] 170#[test] 171fn infer_subcommands_fail_no_args() { 172 let m = Command::new("prog") 173 .infer_subcommands(true) 174 .subcommand(Command::new("test")) 175 .subcommand(Command::new("temp")) 176 .try_get_matches_from(vec!["prog", "te"]); 177 assert!(m.is_err(), "{:#?}", m.unwrap()); 178 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); 179} 180 181#[cfg(feature = "suggestions")] 182#[test] 183fn infer_subcommands_fail_no_args() { 184 let m = Command::new("prog") 185 .infer_subcommands(true) 186 .subcommand(Command::new("test")) 187 .subcommand(Command::new("temp")) 188 .try_get_matches_from(vec!["prog", "te"]); 189 assert!(m.is_err(), "{:#?}", m.unwrap()); 190 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); 191} 192 193#[test] 194fn infer_subcommands_fail_with_args() { 195 let m = Command::new("prog") 196 .infer_subcommands(true) 197 .arg(Arg::new("some")) 198 .subcommand(Command::new("test")) 199 .subcommand(Command::new("temp")) 200 .try_get_matches_from(vec!["prog", "t"]); 201 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); 202 assert_eq!( 203 m.unwrap().get_one::<String>("some").map(|v| v.as_str()), 204 Some("t") 205 ); 206} 207 208#[test] 209fn infer_subcommands_fail_with_args2() { 210 let m = Command::new("prog") 211 .infer_subcommands(true) 212 .arg(Arg::new("some")) 213 .subcommand(Command::new("test")) 214 .subcommand(Command::new("temp")) 215 .try_get_matches_from(vec!["prog", "te"]); 216 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); 217 assert_eq!( 218 m.unwrap().get_one::<String>("some").map(|v| v.as_str()), 219 Some("te") 220 ); 221} 222 223#[test] 224fn infer_subcommands_pass() { 225 let m = Command::new("prog") 226 .infer_subcommands(true) 227 .subcommand(Command::new("test")) 228 .try_get_matches_from(vec!["prog", "te"]) 229 .unwrap(); 230 assert_eq!(m.subcommand_name(), Some("test")); 231} 232 233#[test] 234fn infer_subcommands_pass_close() { 235 let m = Command::new("prog") 236 .infer_subcommands(true) 237 .subcommand(Command::new("test")) 238 .subcommand(Command::new("temp")) 239 .try_get_matches_from(vec!["prog", "tes"]) 240 .unwrap(); 241 assert_eq!(m.subcommand_name(), Some("test")); 242} 243 244#[test] 245fn infer_subcommands_pass_exact_match() { 246 let m = Command::new("prog") 247 .infer_subcommands(true) 248 .subcommand(Command::new("test")) 249 .subcommand(Command::new("testa")) 250 .subcommand(Command::new("testb")) 251 .try_get_matches_from(vec!["prog", "test"]) 252 .unwrap(); 253 assert_eq!(m.subcommand_name(), Some("test")); 254} 255 256#[cfg(feature = "suggestions")] 257#[test] 258fn infer_subcommands_fail_suggestions() { 259 let m = Command::new("prog") 260 .infer_subcommands(true) 261 .subcommand(Command::new("test")) 262 .subcommand(Command::new("temp")) 263 .try_get_matches_from(vec!["prog", "temps"]); 264 assert!(m.is_err(), "{:#?}", m.unwrap()); 265 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); 266} 267 268#[cfg(not(feature = "suggestions"))] 269#[test] 270fn infer_subcommands_fail_suggestions() { 271 let m = Command::new("prog") 272 .infer_subcommands(true) 273 .subcommand(Command::new("test")) 274 .subcommand(Command::new("temp")) 275 .try_get_matches_from(vec!["prog", "temps"]); 276 assert!(m.is_err(), "{:#?}", m.unwrap()); 277 assert_eq!(m.unwrap_err().kind(), ErrorKind::InvalidSubcommand); 278} 279 280#[test] 281fn no_bin_name() { 282 let result = Command::new("arg_required") 283 .no_binary_name(true) 284 .arg(Arg::new("test").required(true).index(1)) 285 .try_get_matches_from(vec!["testing"]); 286 assert!(result.is_ok(), "{}", result.unwrap_err()); 287 let matches = result.unwrap(); 288 assert_eq!( 289 matches 290 .get_one::<String>("test") 291 .map(|v| v.as_str()) 292 .unwrap(), 293 "testing" 294 ); 295} 296 297#[test] 298fn skip_possible_values() { 299 static SKIP_POS_VALS: &str = "\ 300tests stuff 301 302Usage: test [OPTIONS] [arg1] 303 304Arguments: 305 [arg1] some pos arg 306 307Options: 308 -o, --opt <opt> some option 309 -h, --help Print help 310 -V, --version Print version 311"; 312 313 let cmd = Command::new("test") 314 .author("Kevin K.") 315 .about("tests stuff") 316 .version("1.3") 317 .hide_possible_values(true) 318 .args([ 319 arg!(-o --opt <opt> "some option").value_parser(["one", "two"]), 320 arg!([arg1] "some pos arg").value_parser(["three", "four"]), 321 ]); 322 323 utils::assert_output(cmd, "test --help", SKIP_POS_VALS, false); 324} 325 326#[test] 327fn stop_delim_values_only_pos_follows() { 328 let r = Command::new("onlypos") 329 .dont_delimit_trailing_values(true) 330 .args([arg!(f: -f <flag> "some opt"), arg!([arg] ... "some arg")]) 331 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); 332 assert!(r.is_ok(), "{}", r.unwrap_err()); 333 let m = r.unwrap(); 334 assert!(m.contains_id("arg")); 335 assert!(!m.contains_id("f")); 336 assert_eq!( 337 m.get_many::<String>("arg") 338 .unwrap() 339 .map(|v| v.as_str()) 340 .collect::<Vec<_>>(), 341 ["-f", "-g,x"] 342 ); 343} 344 345#[test] 346fn dont_delim_values_trailingvararg() { 347 let m = Command::new("positional") 348 .dont_delimit_trailing_values(true) 349 .arg(arg!([opt] ... "some pos").trailing_var_arg(true)) 350 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) 351 .unwrap(); 352 assert!(m.contains_id("opt")); 353 assert_eq!( 354 m.get_many::<String>("opt") 355 .unwrap() 356 .map(|v| v.as_str()) 357 .collect::<Vec<_>>(), 358 ["test", "--foo", "-Wl,-bar"] 359 ); 360} 361 362#[test] 363fn delim_values_only_pos_follows() { 364 let r = Command::new("onlypos") 365 .args([arg!(f: -f [flag] "some opt"), arg!([arg] ... "some arg")]) 366 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); 367 assert!(r.is_ok(), "{}", r.unwrap_err()); 368 let m = r.unwrap(); 369 assert!(m.contains_id("arg")); 370 assert!(!m.contains_id("f")); 371 assert_eq!( 372 m.get_many::<String>("arg") 373 .unwrap() 374 .map(|v| v.as_str()) 375 .collect::<Vec<_>>(), 376 ["-f", "-g,x"] 377 ); 378} 379 380#[test] 381fn delim_values_trailingvararg() { 382 let m = Command::new("positional") 383 .arg(arg!([opt] ... "some pos").trailing_var_arg(true)) 384 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) 385 .unwrap(); 386 assert!(m.contains_id("opt")); 387 assert_eq!( 388 m.get_many::<String>("opt") 389 .unwrap() 390 .map(|v| v.as_str()) 391 .collect::<Vec<_>>(), 392 ["test", "--foo", "-Wl,-bar"] 393 ); 394} 395 396#[test] 397fn delim_values_only_pos_follows_with_delim() { 398 let r = Command::new("onlypos") 399 .args([ 400 arg!(f: -f [flag] "some opt"), 401 arg!([arg] ... "some arg").value_delimiter(','), 402 ]) 403 .try_get_matches_from(vec!["", "--", "-f", "-g,x"]); 404 assert!(r.is_ok(), "{}", r.unwrap_err()); 405 let m = r.unwrap(); 406 assert!(m.contains_id("arg")); 407 assert!(!m.contains_id("f")); 408 assert_eq!( 409 m.get_many::<String>("arg") 410 .unwrap() 411 .map(|v| v.as_str()) 412 .collect::<Vec<_>>(), 413 ["-f", "-g", "x"] 414 ); 415} 416 417#[test] 418fn delim_values_trailingvararg_with_delim() { 419 let m = Command::new("positional") 420 .arg( 421 arg!([opt] ... "some pos") 422 .value_delimiter(',') 423 .trailing_var_arg(true), 424 ) 425 .try_get_matches_from(vec!["", "test", "--foo", "-Wl,-bar"]) 426 .unwrap(); 427 assert!(m.contains_id("opt")); 428 assert_eq!( 429 m.get_many::<String>("opt") 430 .unwrap() 431 .map(|v| v.as_str()) 432 .collect::<Vec<_>>(), 433 ["test", "--foo", "-Wl", "-bar"] 434 ); 435} 436 437#[test] 438fn leading_hyphen_short() { 439 let res = Command::new("leadhy") 440 .arg(Arg::new("some").allow_hyphen_values(true)) 441 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue)) 442 .try_get_matches_from(vec!["", "-bar", "-o"]); 443 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); 444 let m = res.unwrap(); 445 assert!(m.contains_id("some")); 446 assert!(m.contains_id("other")); 447 assert_eq!( 448 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(), 449 "-bar" 450 ); 451 assert_eq!( 452 *m.get_one::<bool>("other").expect("defaulted by clap"), 453 true 454 ); 455} 456 457#[test] 458fn leading_hyphen_long() { 459 let res = Command::new("leadhy") 460 .arg(Arg::new("some").allow_hyphen_values(true)) 461 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue)) 462 .try_get_matches_from(vec!["", "--bar", "-o"]); 463 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); 464 let m = res.unwrap(); 465 assert!(m.contains_id("some")); 466 assert!(m.contains_id("other")); 467 assert_eq!( 468 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(), 469 "--bar" 470 ); 471 assert_eq!( 472 *m.get_one::<bool>("other").expect("defaulted by clap"), 473 true 474 ); 475} 476 477#[test] 478fn leading_hyphen_opt() { 479 let res = Command::new("leadhy") 480 .arg( 481 Arg::new("some") 482 .action(ArgAction::Set) 483 .long("opt") 484 .allow_hyphen_values(true), 485 ) 486 .arg(Arg::new("other").short('o').action(ArgAction::SetTrue)) 487 .try_get_matches_from(vec!["", "--opt", "--bar", "-o"]); 488 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); 489 let m = res.unwrap(); 490 assert!(m.contains_id("some")); 491 assert!(m.contains_id("other")); 492 assert_eq!( 493 m.get_one::<String>("some").map(|v| v.as_str()).unwrap(), 494 "--bar" 495 ); 496 assert_eq!( 497 *m.get_one::<bool>("other").expect("defaulted by clap"), 498 true 499 ); 500} 501 502#[test] 503fn allow_negative_numbers_success() { 504 let res = Command::new("negnum") 505 .arg(Arg::new("panum").allow_negative_numbers(true)) 506 .arg( 507 Arg::new("onum") 508 .short('o') 509 .action(ArgAction::Set) 510 .allow_negative_numbers(true), 511 ) 512 .try_get_matches_from(vec!["negnum", "-20", "-o", "-1.2"]); 513 assert!(res.is_ok(), "Error: {:?}", res.unwrap_err().kind()); 514 let m = res.unwrap(); 515 assert_eq!( 516 m.get_one::<String>("panum").map(|v| v.as_str()).unwrap(), 517 "-20" 518 ); 519 assert_eq!( 520 m.get_one::<String>("onum").map(|v| v.as_str()).unwrap(), 521 "-1.2" 522 ); 523} 524 525#[test] 526fn allow_negative_numbers_fail() { 527 let res = Command::new("negnum") 528 .arg(Arg::new("panum").allow_negative_numbers(true)) 529 .arg( 530 Arg::new("onum") 531 .short('o') 532 .action(ArgAction::Set) 533 .allow_negative_numbers(true), 534 ) 535 .try_get_matches_from(vec!["negnum", "--foo", "-o", "-1.2"]); 536 assert!(res.is_err()); 537 assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument) 538} 539 540#[test] 541fn trailing_var_arg_with_hyphen_values_escape_first() { 542 assert_trailing_var_args(&["test", "--", "foo", "bar"], Some(&["foo", "bar"]), false); 543} 544 545#[test] 546fn trailing_var_arg_with_hyphen_values_escape_middle() { 547 assert_trailing_var_args( 548 &["test", "foo", "--", "bar"], 549 Some(&["foo", "--", "bar"]), 550 false, 551 ); 552} 553 554#[test] 555fn trailing_var_arg_with_hyphen_values_short_first() { 556 assert_trailing_var_args(&["test", "-p", "foo", "bar"], Some(&["foo", "bar"]), true); 557} 558 559#[test] 560fn trailing_var_arg_with_hyphen_values_short_middle() { 561 assert_trailing_var_args( 562 &["test", "foo", "-p", "bar"], 563 Some(&["foo", "-p", "bar"]), 564 false, 565 ); 566} 567 568#[test] 569fn trailing_var_arg_with_hyphen_values_long_first() { 570 assert_trailing_var_args( 571 &["test", "--prog", "foo", "bar"], 572 Some(&["foo", "bar"]), 573 true, 574 ); 575} 576 577#[test] 578fn trailing_var_arg_with_hyphen_values_long_middle() { 579 assert_trailing_var_args( 580 &["test", "foo", "--prog", "bar"], 581 Some(&["foo", "--prog", "bar"]), 582 false, 583 ); 584} 585 586#[track_caller] 587fn assert_trailing_var_args( 588 input: &[&str], 589 expected_var_arg: Option<&[&str]>, 590 expected_flag: bool, 591) { 592 let cmd = Command::new("test").arg(arg!(-p - -prog)).arg( 593 arg!([opt] ... "some pos") 594 .trailing_var_arg(true) 595 .allow_hyphen_values(true), 596 ); 597 let m = cmd.try_get_matches_from(input); 598 assert!( 599 m.is_ok(), 600 "failed with args {:?}: {}", 601 input, 602 m.unwrap_err() 603 ); 604 let m = m.unwrap(); 605 606 let actual_var_args = m 607 .get_many::<String>("opt") 608 .map(|v| v.map(|s| s.as_str()).collect::<Vec<_>>()); 609 assert_eq!(actual_var_args.as_deref(), expected_var_arg); 610 assert_eq!(m.get_flag("prog"), expected_flag); 611} 612 613#[test] 614fn disable_help_subcommand() { 615 let result = Command::new("disablehelp") 616 .disable_help_subcommand(true) 617 .subcommand(Command::new("sub1")) 618 .try_get_matches_from(vec!["", "help"]); 619 assert!(result.is_err()); 620 let err = result.err().unwrap(); 621 assert_eq!(err.kind(), ErrorKind::InvalidSubcommand); 622} 623 624#[test] 625fn dont_collapse_args() { 626 let cmd = Command::new("clap-test").version("v1.4.8").args([ 627 Arg::new("arg1").help("some"), 628 Arg::new("arg2").help("some"), 629 Arg::new("arg3").help("some"), 630 ]); 631 utils::assert_output(cmd, "clap-test --help", DONT_COLLAPSE_ARGS, false); 632} 633 634#[test] 635fn require_eq() { 636 static REQUIRE_EQUALS: &str = "\ 637Usage: clap-test --opt=<FILE> 638 639Options: 640 -o, --opt=<FILE> some 641 -h, --help Print help 642 -V, --version Print version 643"; 644 645 let cmd = Command::new("clap-test").version("v1.4.8").arg( 646 Arg::new("opt") 647 .long("opt") 648 .short('o') 649 .required(true) 650 .require_equals(true) 651 .value_name("FILE") 652 .help("some"), 653 ); 654 utils::assert_output(cmd, "clap-test --help", REQUIRE_EQUALS, false); 655} 656 657#[test] 658fn propagate_vals_down() { 659 let m = Command::new("myprog") 660 .arg(arg!([cmd] "command to run").global(true)) 661 .subcommand(Command::new("foo")) 662 .try_get_matches_from(vec!["myprog", "set", "foo"]); 663 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); 664 let m = m.unwrap(); 665 assert_eq!(m.get_one::<String>("cmd").map(|v| v.as_str()), Some("set")); 666 let sub_m = m.subcommand_matches("foo").unwrap(); 667 assert_eq!( 668 sub_m.get_one::<String>("cmd").map(|v| v.as_str()), 669 Some("set") 670 ); 671} 672 673#[test] 674fn allow_missing_positional() { 675 let m = Command::new("test") 676 .allow_missing_positional(true) 677 .arg(arg!([src] "some file").default_value("src")) 678 .arg(arg!(<dest> "some file")) 679 .try_get_matches_from(vec!["test", "file"]); 680 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); 681 let m = m.unwrap(); 682 assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), Some("src")); 683 assert_eq!( 684 m.get_one::<String>("dest").map(|v| v.as_str()), 685 Some("file") 686 ); 687} 688 689#[test] 690fn allow_missing_positional_no_default() { 691 let m = Command::new("test") 692 .allow_missing_positional(true) 693 .arg(arg!([src] "some file")) 694 .arg(arg!(<dest> "some file")) 695 .try_get_matches_from(vec!["test", "file"]); 696 assert!(m.is_ok(), "{:?}", m.unwrap_err().kind()); 697 let m = m.unwrap(); 698 assert_eq!(m.get_one::<String>("src").map(|v| v.as_str()), None); 699 assert_eq!( 700 m.get_one::<String>("dest").map(|v| v.as_str()), 701 Some("file") 702 ); 703} 704 705#[test] 706fn missing_positional_no_hyphen() { 707 let r = Command::new("bench") 708 .allow_missing_positional(true) 709 .arg(arg!([BENCH] "some bench")) 710 .arg(arg!([ARGS] ... "some args")) 711 .try_get_matches_from(vec!["bench", "foo", "arg1", "arg2", "arg3"]); 712 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); 713 714 let m = r.unwrap(); 715 716 let expected_bench = Some("foo"); 717 let expected_args = vec!["arg1", "arg2", "arg3"]; 718 719 assert_eq!( 720 m.get_one::<String>("BENCH").map(|v| v.as_str()), 721 expected_bench 722 ); 723 assert_eq!( 724 m.get_many::<String>("ARGS") 725 .unwrap() 726 .map(|v| v.as_str()) 727 .collect::<Vec<_>>(), 728 &*expected_args 729 ); 730} 731 732#[test] 733fn missing_positional_hyphen() { 734 let r = Command::new("bench") 735 .allow_missing_positional(true) 736 .arg(arg!([BENCH] "some bench")) 737 .arg(arg!([ARGS] ... "some args")) 738 .try_get_matches_from(vec!["bench", "--", "arg1", "arg2", "arg3"]); 739 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); 740 741 let m = r.unwrap(); 742 743 let expected_bench = None; 744 let expected_args = vec!["arg1", "arg2", "arg3"]; 745 746 assert_eq!( 747 m.get_one::<String>("BENCH").map(|v| v.as_str()), 748 expected_bench 749 ); 750 assert_eq!( 751 m.get_many::<String>("ARGS") 752 .unwrap() 753 .map(|v| v.as_str()) 754 .collect::<Vec<_>>(), 755 &*expected_args 756 ); 757} 758 759#[test] 760fn missing_positional_hyphen_far_back() { 761 let r = Command::new("bench") 762 .allow_missing_positional(true) 763 .arg(arg!([BENCH1] "some bench")) 764 .arg(arg!([BENCH2] "some bench")) 765 .arg(arg!([BENCH3] "some bench")) 766 .arg(arg!([ARGS] ... "some args")) 767 .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); 768 assert!(r.is_ok(), "{:?}", r.unwrap_err().kind()); 769 770 let m = r.unwrap(); 771 772 let expected_bench1 = Some("foo"); 773 let expected_bench2 = None; 774 let expected_bench3 = None; 775 let expected_args = vec!["arg1", "arg2", "arg3"]; 776 777 assert_eq!( 778 m.get_one::<String>("BENCH1").map(|v| v.as_str()), 779 expected_bench1 780 ); 781 assert_eq!( 782 m.get_one::<String>("BENCH2").map(|v| v.as_str()), 783 expected_bench2 784 ); 785 assert_eq!( 786 m.get_one::<String>("BENCH3").map(|v| v.as_str()), 787 expected_bench3 788 ); 789 assert_eq!( 790 m.get_many::<String>("ARGS") 791 .unwrap() 792 .map(|v| v.as_str()) 793 .collect::<Vec<_>>(), 794 &*expected_args 795 ); 796} 797 798#[test] 799fn missing_positional_hyphen_req_error() { 800 let r = Command::new("bench") 801 .allow_missing_positional(true) 802 .arg(arg!([BENCH1] "some bench")) 803 .arg(arg!(<BENCH2> "some bench")) 804 .arg(arg!([ARGS] ... "some args")) 805 .try_get_matches_from(vec!["bench", "foo", "--", "arg1", "arg2", "arg3"]); 806 assert!(r.is_err()); 807 assert_eq!(r.unwrap_err().kind(), ErrorKind::MissingRequiredArgument); 808} 809 810#[test] 811fn issue_1066_allow_leading_hyphen_and_unknown_args_option() { 812 let res = Command::new("prog") 813 .arg( 814 arg!(--"some-argument" <val>) 815 .required(true) 816 .allow_hyphen_values(true), 817 ) 818 .try_get_matches_from(vec!["prog", "-fish"]); 819 820 assert!(res.is_err()); 821 assert_eq!(res.unwrap_err().kind(), ErrorKind::UnknownArgument); 822} 823 824#[test] 825fn issue_1437_allow_hyphen_values_for_positional_arg() { 826 let m = Command::new("tmp") 827 .arg( 828 Arg::new("pat") 829 .allow_hyphen_values(true) 830 .required(true) 831 .action(ArgAction::Set), 832 ) 833 .try_get_matches_from(["tmp", "-file"]) 834 .unwrap(); 835 assert_eq!( 836 m.get_one::<String>("pat").map(|v| v.as_str()), 837 Some("-file") 838 ); 839} 840 841#[test] 842fn issue_3880_allow_long_flag_hyphen_value_for_positional_arg() { 843 let m = Command::new("prog") 844 .arg( 845 Arg::new("pat") 846 .allow_hyphen_values(true) 847 .required(true) 848 .action(ArgAction::Set), 849 ) 850 .try_get_matches_from(["", "--file"]) 851 .unwrap(); 852 853 assert_eq!( 854 m.get_one::<String>("pat").map(|v| v.as_str()), 855 Some("--file") 856 ); 857} 858 859#[test] 860fn issue_1093_allow_ext_sc() { 861 let cmd = Command::new("clap-test") 862 .version("v1.4.8") 863 .allow_external_subcommands(true); 864 utils::assert_output(cmd, "clap-test --help", ALLOW_EXT_SC, false); 865} 866 867#[test] 868fn allow_ext_sc_empty_args() { 869 let res = Command::new("clap-test") 870 .version("v1.4.8") 871 .allow_external_subcommands(true) 872 .try_get_matches_from(vec!["clap-test", "external-cmd"]); 873 874 assert!(res.is_ok(), "{}", res.unwrap_err()); 875 876 match res.unwrap().subcommand() { 877 Some((name, args)) => { 878 assert_eq!(name, "external-cmd"); 879 assert_eq!( 880 args.get_many::<OsString>("").unwrap().collect::<Vec<_>>(), 881 Vec::<&OsString>::new(), 882 ); 883 } 884 _ => unreachable!(), 885 } 886} 887 888#[test] 889fn allow_ext_sc_when_sc_required() { 890 let res = Command::new("clap-test") 891 .version("v1.4.8") 892 .allow_external_subcommands(true) 893 .subcommand_required(true) 894 .try_get_matches_from(vec!["clap-test", "external-cmd", "foo"]); 895 896 assert!(res.is_ok(), "{}", res.unwrap_err()); 897 898 match res.unwrap().subcommand() { 899 Some((name, args)) => { 900 assert_eq!(name, "external-cmd"); 901 assert_eq!( 902 args.get_many::<OsString>("") 903 .unwrap() 904 .cloned() 905 .collect::<Vec<_>>(), 906 vec![OsString::from("foo")] 907 ); 908 } 909 _ => unreachable!(), 910 } 911} 912 913#[test] 914fn external_subcommand_looks_like_built_in() { 915 let res = Command::new("cargo") 916 .version("1.26.0") 917 .allow_external_subcommands(true) 918 .subcommand(Command::new("install")) 919 .try_get_matches_from(vec!["cargo", "install-update", "foo"]); 920 assert!(res.is_ok(), "{}", res.unwrap_err()); 921 let m = res.unwrap(); 922 match m.subcommand() { 923 Some((name, args)) => { 924 assert_eq!(name, "install-update"); 925 assert_eq!( 926 args.get_many::<OsString>("") 927 .unwrap() 928 .cloned() 929 .collect::<Vec<_>>(), 930 vec![OsString::from("foo")] 931 ); 932 } 933 _ => panic!("external_subcommand didn't work"), 934 } 935} 936 937#[test] 938fn built_in_subcommand_escaped() { 939 let res = Command::new("cargo") 940 .version("1.26.0") 941 .allow_external_subcommands(true) 942 .subcommand(Command::new("install")) 943 .try_get_matches_from(vec!["cargo", "--", "install", "foo"]); 944 assert!(res.is_ok(), "{}", res.unwrap_err()); 945 let m = res.unwrap(); 946 match m.subcommand() { 947 Some((name, args)) => { 948 assert_eq!(name, "install"); 949 assert_eq!( 950 args.get_many::<OsString>("") 951 .unwrap() 952 .cloned() 953 .collect::<Vec<_>>(), 954 vec![OsString::from("foo")] 955 ); 956 } 957 _ => panic!("external_subcommand didn't work"), 958 } 959} 960 961#[test] 962fn aaos_opts_w_other_overrides() { 963 // opts with other overrides 964 let res = Command::new("posix") 965 .args_override_self(true) 966 .arg(arg!(--opt <val> "some option").action(ArgAction::Set)) 967 .arg( 968 arg!(--other <val> "some other option") 969 .overrides_with("opt") 970 .action(ArgAction::Set), 971 ) 972 .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); 973 assert!(res.is_ok(), "{}", res.unwrap_err()); 974 let m = res.unwrap(); 975 assert!(m.contains_id("opt")); 976 assert!(!m.contains_id("other")); 977 assert_eq!( 978 m.get_one::<String>("opt").map(|v| v.as_str()), 979 Some("other") 980 ); 981} 982 983#[test] 984fn aaos_opts_w_other_overrides_rev() { 985 // opts with other overrides, rev 986 let res = Command::new("posix") 987 .args_override_self(true) 988 .arg( 989 arg!(--opt <val> "some option") 990 .required(true) 991 .action(ArgAction::Set), 992 ) 993 .arg( 994 arg!(--other <val> "some other option") 995 .required(true) 996 .overrides_with("opt") 997 .action(ArgAction::Set), 998 ) 999 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); 1000 assert!(res.is_ok(), "{}", res.unwrap_err()); 1001 let m = res.unwrap(); 1002 assert!(!m.contains_id("opt")); 1003 assert!(m.contains_id("other")); 1004 assert_eq!( 1005 m.get_one::<String>("other").map(|v| v.as_str()), 1006 Some("val") 1007 ); 1008} 1009 1010#[test] 1011fn aaos_opts_w_other_overrides_2() { 1012 // opts with other overrides 1013 let res = Command::new("posix") 1014 .args_override_self(true) 1015 .arg( 1016 arg!(--opt <val> "some option") 1017 .overrides_with("other") 1018 .action(ArgAction::Set), 1019 ) 1020 .arg(arg!(--other <val> "some other option").action(ArgAction::Set)) 1021 .try_get_matches_from(vec!["", "--opt=some", "--other=test", "--opt=other"]); 1022 assert!(res.is_ok(), "{}", res.unwrap_err()); 1023 let m = res.unwrap(); 1024 assert!(m.contains_id("opt")); 1025 assert!(!m.contains_id("other")); 1026 assert_eq!( 1027 m.get_one::<String>("opt").map(|v| v.as_str()), 1028 Some("other") 1029 ); 1030} 1031 1032#[test] 1033fn aaos_opts_w_other_overrides_rev_2() { 1034 // opts with other overrides, rev 1035 let res = Command::new("posix") 1036 .args_override_self(true) 1037 .arg( 1038 arg!(--opt <val> "some option") 1039 .required(true) 1040 .overrides_with("other") 1041 .action(ArgAction::Set), 1042 ) 1043 .arg( 1044 arg!(--other <val> "some other option") 1045 .required(true) 1046 .action(ArgAction::Set), 1047 ) 1048 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--other=val"]); 1049 assert!(res.is_ok(), "{}", res.unwrap_err()); 1050 let m = res.unwrap(); 1051 assert!(!m.contains_id("opt")); 1052 assert!(m.contains_id("other")); 1053 assert_eq!( 1054 m.get_one::<String>("other").map(|v| v.as_str()), 1055 Some("val") 1056 ); 1057} 1058 1059#[test] 1060fn aaos_opts_w_override_as_conflict_1() { 1061 // opts with other overrides, rev 1062 let res = Command::new("posix") 1063 .arg( 1064 arg!(--opt <val> "some option") 1065 .required(true) 1066 .overrides_with("other") 1067 .action(ArgAction::Set), 1068 ) 1069 .arg( 1070 arg!(--other <val> "some other option") 1071 .required(true) 1072 .action(ArgAction::Set), 1073 ) 1074 .try_get_matches_from(vec!["", "--opt=some"]); 1075 assert!(res.is_ok(), "{}", res.unwrap_err()); 1076 let m = res.unwrap(); 1077 assert!(m.contains_id("opt")); 1078 assert!(!m.contains_id("other")); 1079 assert_eq!(m.get_one::<String>("opt").map(|v| v.as_str()), Some("some")); 1080} 1081 1082#[test] 1083fn aaos_opts_w_override_as_conflict_2() { 1084 // opts with other overrides, rev 1085 let res = Command::new("posix") 1086 .arg( 1087 arg!(--opt <val> "some option") 1088 .required(true) 1089 .overrides_with("other") 1090 .action(ArgAction::Set), 1091 ) 1092 .arg( 1093 arg!(--other <val> "some other option") 1094 .required(true) 1095 .action(ArgAction::Set), 1096 ) 1097 .try_get_matches_from(vec!["", "--other=some"]); 1098 assert!(res.is_ok(), "{}", res.unwrap_err()); 1099 let m = res.unwrap(); 1100 assert!(!m.contains_id("opt")); 1101 assert!(m.contains_id("other")); 1102 assert_eq!( 1103 m.get_one::<String>("other").map(|v| v.as_str()), 1104 Some("some") 1105 ); 1106} 1107 1108#[test] 1109fn aaos_opts_mult_req_delims() { 1110 // opts with multiple and require delims 1111 let res = Command::new("posix") 1112 .arg( 1113 arg!(--opt <val> ... "some option") 1114 .action(ArgAction::Set) 1115 .value_delimiter(',') 1116 .action(ArgAction::Append), 1117 ) 1118 .try_get_matches_from(vec!["", "--opt=some", "--opt=other", "--opt=one,two"]); 1119 assert!(res.is_ok(), "{}", res.unwrap_err()); 1120 let m = res.unwrap(); 1121 assert!(m.contains_id("opt")); 1122 assert_eq!( 1123 m.get_many::<String>("opt") 1124 .unwrap() 1125 .map(|v| v.as_str()) 1126 .collect::<Vec<_>>(), 1127 ["some", "other", "one", "two"] 1128 ); 1129} 1130 1131#[test] 1132fn aaos_opts_mult() { 1133 // opts with multiple 1134 let res = Command::new("posix") 1135 .arg( 1136 arg!(--opt <val> ... "some option") 1137 .num_args(1..) 1138 .action(ArgAction::Append), 1139 ) 1140 .try_get_matches_from(vec![ 1141 "", 1142 "--opt", 1143 "first", 1144 "overrides", 1145 "--opt", 1146 "some", 1147 "other", 1148 "val", 1149 ]); 1150 assert!(res.is_ok(), "{}", res.unwrap_err()); 1151 let m = res.unwrap(); 1152 assert!(m.contains_id("opt")); 1153 assert_eq!( 1154 m.get_many::<String>("opt") 1155 .unwrap() 1156 .map(|v| v.as_str()) 1157 .collect::<Vec<_>>(), 1158 ["first", "overrides", "some", "other", "val"] 1159 ); 1160} 1161 1162#[test] 1163fn aaos_pos_mult() { 1164 // opts with multiple 1165 let res = Command::new("posix") 1166 .arg(arg!([val] ... "some pos")) 1167 .try_get_matches_from(vec!["", "some", "other", "value"]); 1168 assert!(res.is_ok(), "{}", res.unwrap_err()); 1169 let m = res.unwrap(); 1170 assert!(m.contains_id("val")); 1171 assert_eq!( 1172 m.get_many::<String>("val") 1173 .unwrap() 1174 .map(|v| v.as_str()) 1175 .collect::<Vec<_>>(), 1176 ["some", "other", "value"] 1177 ); 1178} 1179 1180#[test] 1181fn aaos_option_use_delim_false() { 1182 let m = Command::new("posix") 1183 .args_override_self(true) 1184 .arg( 1185 arg!(--opt <val> "some option") 1186 .required(true) 1187 .action(ArgAction::Set), 1188 ) 1189 .try_get_matches_from(vec!["", "--opt=some,other", "--opt=one,two"]) 1190 .unwrap(); 1191 assert!(m.contains_id("opt")); 1192 assert_eq!( 1193 m.get_many::<String>("opt") 1194 .unwrap() 1195 .map(|v| v.as_str()) 1196 .collect::<Vec<_>>(), 1197 ["one,two"] 1198 ); 1199} 1200 1201#[test] 1202#[cfg(feature = "color")] 1203fn color_is_global() { 1204 let mut cmd = Command::new("myprog") 1205 .color(clap::ColorChoice::Never) 1206 .subcommand(Command::new("foo")); 1207 cmd.build(); 1208 assert_eq!(cmd.get_color(), clap::ColorChoice::Never); 1209 1210 let sub = cmd.get_subcommands().collect::<Vec<_>>()[0]; 1211 assert_eq!(sub.get_color(), clap::ColorChoice::Never); 1212} 1213