1use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id}; 2 3use super::utils; 4 5#[test] 6fn required_group_missing_arg() { 7 let result = Command::new("group") 8 .arg(arg!(-f --flag "some flag")) 9 .arg(arg!( -c --color "some other flag")) 10 .group(ArgGroup::new("req").args(["flag", "color"]).required(true)) 11 .try_get_matches_from(vec![""]); 12 assert!(result.is_err()); 13 let err = result.err().unwrap(); 14 assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 15} 16 17#[cfg(debug_assertions)] 18#[test] 19#[should_panic = "Command group: Argument group 'req' contains non-existent argument"] 20fn non_existing_arg() { 21 let _ = Command::new("group") 22 .arg(arg!(-f --flag "some flag")) 23 .arg(arg!(-c --color "some other flag")) 24 .group(ArgGroup::new("req").args(["flg", "color"]).required(true)) 25 .try_get_matches_from(vec![""]); 26} 27 28#[cfg(debug_assertions)] 29#[test] 30#[should_panic = "Command group: Argument group name must be unique\n\n\t'req' is already in use"] 31fn unique_group_name() { 32 let _ = Command::new("group") 33 .arg(arg!(-f --flag "some flag")) 34 .arg(arg!(-c --color "some other flag")) 35 .group(ArgGroup::new("req").args(["flag"]).required(true)) 36 .group(ArgGroup::new("req").args(["color"]).required(true)) 37 .try_get_matches_from(vec![""]); 38} 39 40#[cfg(debug_assertions)] 41#[test] 42#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"] 43fn groups_new_of_arg_name() { 44 let _ = Command::new("group") 45 .arg(Arg::new("a").long("a").group("a")) 46 .try_get_matches_from(vec!["", "--a"]); 47} 48 49#[cfg(debug_assertions)] 50#[test] 51#[should_panic = "Command group: Argument group name 'a' must not conflict with argument name"] 52fn arg_group_new_of_arg_name() { 53 let _ = Command::new("group") 54 .arg(Arg::new("a").long("a").group("a")) 55 .group(ArgGroup::new("a")) 56 .try_get_matches_from(vec!["", "--a"]); 57} 58 59#[test] 60fn group_single_value() { 61 let res = Command::new("group") 62 .arg(arg!(-c --color [color] "some option")) 63 .arg(arg!(-n --hostname <name> "another option")) 64 .group(ArgGroup::new("grp").args(["hostname", "color"])) 65 .try_get_matches_from(vec!["", "-c", "blue"]); 66 assert!(res.is_ok(), "{}", res.unwrap_err()); 67 68 let m = res.unwrap(); 69 assert!(m.contains_id("grp")); 70 assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color"); 71} 72 73#[test] 74fn group_empty() { 75 let res = Command::new("group") 76 .arg(arg!(-f --flag "some flag")) 77 .arg(arg!(-c --color [color] "some option")) 78 .arg(arg!(-n --hostname <name> "another option")) 79 .group(ArgGroup::new("grp").args(["hostname", "color", "flag"])) 80 .try_get_matches_from(vec![""]); 81 assert!(res.is_ok(), "{}", res.unwrap_err()); 82 83 let m = res.unwrap(); 84 assert!(!m.contains_id("grp")); 85 assert!(m.get_one::<String>("grp").map(|v| v.as_str()).is_none()); 86} 87 88#[test] 89fn group_required_flags_empty() { 90 let result = Command::new("group") 91 .arg(arg!(-f --flag "some flag")) 92 .arg(arg!(-c --color "some option")) 93 .arg(arg!(-n --hostname <name> "another option")) 94 .group( 95 ArgGroup::new("grp") 96 .required(true) 97 .args(["hostname", "color", "flag"]), 98 ) 99 .try_get_matches_from(vec![""]); 100 assert!(result.is_err()); 101 let err = result.err().unwrap(); 102 assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 103} 104 105#[test] 106fn group_multi_value_single_arg() { 107 let res = Command::new("group") 108 .arg(arg!(-f --flag "some flag")) 109 .arg(arg!(-c --color <color> "some option").num_args(1..)) 110 .arg(arg!(-n --hostname <name> "another option")) 111 .group(ArgGroup::new("grp").args(["hostname", "color", "flag"])) 112 .try_get_matches_from(vec!["", "-c", "blue", "red", "green"]); 113 assert!(res.is_ok(), "{:?}", res.unwrap_err().kind()); 114 115 let m = res.unwrap(); 116 assert!(m.contains_id("grp")); 117 assert_eq!(m.get_one::<Id>("grp").map(|v| v.as_str()).unwrap(), "color"); 118} 119 120#[test] 121fn empty_group() { 122 let r = Command::new("empty_group") 123 .arg(arg!(-f --flag "some flag")) 124 .group(ArgGroup::new("vers").required(true)) 125 .try_get_matches_from(vec!["empty_prog"]); 126 assert!(r.is_err()); 127 let err = r.err().unwrap(); 128 assert_eq!(err.kind(), ErrorKind::MissingRequiredArgument); 129} 130 131#[test] 132#[cfg(feature = "error-context")] 133fn req_group_usage_string() { 134 static REQ_GROUP_USAGE: &str = "error: the following required arguments were not provided: 135 <base|--delete> 136 137Usage: clap-test <base|--delete> 138 139For more information, try '--help'. 140"; 141 142 let cmd = Command::new("req_group") 143 .arg(arg!([base] "Base commit")) 144 .arg(arg!( 145 -d --delete "Remove the base commit information" 146 )) 147 .group( 148 ArgGroup::new("base_or_delete") 149 .args(["base", "delete"]) 150 .required(true), 151 ); 152 153 utils::assert_output(cmd, "clap-test", REQ_GROUP_USAGE, true); 154} 155 156#[test] 157#[cfg(feature = "error-context")] 158fn req_group_with_conflict_usage_string() { 159 static REQ_GROUP_CONFLICT_USAGE: &str = "\ 160error: the argument '--delete' cannot be used with '[base]' 161 162Usage: clap-test <base|--delete> 163 164For more information, try '--help'. 165"; 166 167 let cmd = Command::new("req_group") 168 .arg(arg!([base] "Base commit").conflicts_with("delete")) 169 .arg(arg!( 170 -d --delete "Remove the base commit information" 171 )) 172 .group( 173 ArgGroup::new("base_or_delete") 174 .args(["base", "delete"]) 175 .required(true), 176 ); 177 178 utils::assert_output( 179 cmd, 180 "clap-test --delete base", 181 REQ_GROUP_CONFLICT_USAGE, 182 true, 183 ); 184} 185 186#[test] 187#[cfg(feature = "error-context")] 188fn req_group_with_conflict_usage_string_only_options() { 189 static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\ 190error: the argument '--delete' cannot be used with '--all' 191 192Usage: clap-test <--all|--delete> 193 194For more information, try '--help'. 195"; 196 197 let cmd = Command::new("req_group") 198 .arg(arg!(-a --all "All").conflicts_with("delete")) 199 .arg(arg!( 200 -d --delete "Remove the base commit information" 201 )) 202 .group( 203 ArgGroup::new("all_or_delete") 204 .args(["all", "delete"]) 205 .required(true), 206 ); 207 utils::assert_output( 208 cmd, 209 "clap-test --delete --all", 210 REQ_GROUP_CONFLICT_ONLY_OPTIONS, 211 true, 212 ); 213} 214 215#[test] 216fn required_group_multiple_args() { 217 let result = Command::new("group") 218 .arg(arg!(-f --flag "some flag").action(ArgAction::SetTrue)) 219 .arg(arg!(-c --color "some other flag").action(ArgAction::SetTrue)) 220 .group( 221 ArgGroup::new("req") 222 .args(["flag", "color"]) 223 .required(true) 224 .multiple(true), 225 ) 226 .try_get_matches_from(vec!["group", "-f", "-c"]); 227 assert!(result.is_ok(), "{}", result.unwrap_err()); 228 let m = result.unwrap(); 229 assert!(*m.get_one::<bool>("flag").expect("defaulted by clap")); 230 assert!(*m.get_one::<bool>("color").expect("defaulted by clap")); 231 assert_eq!( 232 &*m.get_many::<Id>("req") 233 .unwrap() 234 .map(|v| v.as_str()) 235 .collect::<Vec<_>>(), 236 ["flag", "color"] 237 ); 238} 239 240#[test] 241fn group_multiple_args_error() { 242 let result = Command::new("group") 243 .arg(arg!(-f --flag "some flag")) 244 .arg(arg!(-c --color "some other flag")) 245 .group(ArgGroup::new("req").args(["flag", "color"])) 246 .try_get_matches_from(vec!["group", "-f", "-c"]); 247 assert!(result.is_err()); 248 let err = result.unwrap_err(); 249 assert_eq!(err.kind(), ErrorKind::ArgumentConflict); 250} 251 252#[test] 253fn group_overrides_required() { 254 let command = Command::new("group") 255 .arg(arg!(--foo <FOO>).required(true)) 256 .arg(arg!(--bar <BAR>).required(true)) 257 .group(ArgGroup::new("group").args(["foo", "bar"]).required(true)); 258 let result = command.try_get_matches_from(vec!["group", "--foo", "value"]); 259 assert!(result.is_ok(), "{}", result.unwrap_err()); 260 let m = result.unwrap(); 261 assert!(m.contains_id("foo")); 262 assert!(!m.contains_id("bar")); 263} 264 265#[test] 266fn group_usage_use_val_name() { 267 static GROUP_USAGE_USE_VAL_NAME: &str = "\ 268Usage: prog <A> 269 270Arguments: 271 [A] 272 273Options: 274 -h, --help Print help 275"; 276 let cmd = Command::new("prog") 277 .arg(Arg::new("a").value_name("A")) 278 .group(ArgGroup::new("group").arg("a").required(true)); 279 utils::assert_output(cmd, "prog --help", GROUP_USAGE_USE_VAL_NAME, false); 280} 281 282#[test] 283fn group_acts_like_arg() { 284 let result = Command::new("prog") 285 .arg( 286 Arg::new("debug") 287 .long("debug") 288 .group("mode") 289 .action(ArgAction::SetTrue), 290 ) 291 .arg( 292 Arg::new("verbose") 293 .long("verbose") 294 .group("mode") 295 .action(ArgAction::SetTrue), 296 ) 297 .try_get_matches_from(vec!["prog", "--debug"]); 298 299 assert!(result.is_ok(), "{}", result.unwrap_err()); 300 let m = result.unwrap(); 301 assert!(m.contains_id("mode")); 302 assert_eq!(m.get_one::<clap::Id>("mode").unwrap(), "debug"); 303} 304 305#[test] 306fn conflict_with_overlapping_group_in_error() { 307 static ERR: &str = "\ 308error: the argument '--major' cannot be used with '--minor' 309 310Usage: prog --major 311 312For more information, try '--help'. 313"; 314 315 let cmd = Command::new("prog") 316 .group(ArgGroup::new("all").multiple(true)) 317 .arg(arg!(--major).group("vers").group("all")) 318 .arg(arg!(--minor).group("vers").group("all")) 319 .arg(arg!(--other).group("all")); 320 321 utils::assert_output(cmd, "prog --major --minor", ERR, true); 322} 323 324#[test] 325fn requires_group_with_overlapping_group_in_error() { 326 static ERR: &str = "\ 327error: the following required arguments were not provided: 328 <--in|--spec> 329 330Usage: prog --config <--in|--spec> 331 332For more information, try '--help'. 333"; 334 335 let cmd = Command::new("prog") 336 .group(ArgGroup::new("all").multiple(true)) 337 .group(ArgGroup::new("input").required(true)) 338 .arg(arg!(--in).group("input").group("all")) 339 .arg(arg!(--spec).group("input").group("all")) 340 .arg(arg!(--config).requires("input").group("all")); 341 342 utils::assert_output(cmd, "prog --config", ERR, true); 343} 344 345/* This is used to be fixed in a hack, we need to find a better way to fix it. 346#[test] 347fn issue_1794() { 348 let cmd = clap::Command::new("hello") 349 .bin_name("deno") 350 .arg(Arg::new("option1").long("option1").action(ArgAction::SetTrue)) 351 .arg(Arg::new("pos1").action(ArgAction::Set)) 352 .arg(Arg::new("pos2").action(ArgAction::Set)) 353 .group( 354 ArgGroup::new("arg1") 355 .args(["pos1", "option1"]) 356 .required(true), 357 ); 358 359 let m = cmd.clone().try_get_matches_from(["cmd", "pos1", "pos2"]).unwrap(); 360 assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), Some("pos1")); 361 assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("pos2")); 362 assert!(!*m.get_one::<bool>("option1").expect("defaulted by clap")); 363 364 let m = cmd 365 .clone() 366 .try_get_matches_from(["cmd", "--option1", "positional"]).unwrap(); 367 assert_eq!(m.get_one::<String>("pos1").map(|v| v.as_str()), None); 368 assert_eq!(m.get_one::<String>("pos2").map(|v| v.as_str()), Some("positional")); 369 assert!(*m.get_one::<bool>("option1").expect("defaulted by clap")); 370} 371*/ 372