1 use clap::{arg, error::ErrorKind, Arg, ArgAction, ArgGroup, Command, Id};
2
3 use super::utils;
4
5 #[test]
required_group_missing_arg()6 fn 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"]
non_existing_arg()20 fn 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"]
unique_group_name()31 fn 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"]
groups_new_of_arg_name()43 fn 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"]
arg_group_new_of_arg_name()52 fn 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]
group_single_value()60 fn 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]
group_empty()74 fn 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]
group_required_flags_empty()89 fn 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]
group_multi_value_single_arg()106 fn 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]
empty_group()121 fn 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")]
req_group_usage_string()133 fn req_group_usage_string() {
134 static REQ_GROUP_USAGE: &str = "error: the following required arguments were not provided:
135 <base|--delete>
136
137 Usage: clap-test <base|--delete>
138
139 For 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")]
req_group_with_conflict_usage_string()158 fn req_group_with_conflict_usage_string() {
159 static REQ_GROUP_CONFLICT_USAGE: &str = "\
160 error: the argument '--delete' cannot be used with '[base]'
161
162 Usage: clap-test <base|--delete>
163
164 For 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")]
req_group_with_conflict_usage_string_only_options()188 fn req_group_with_conflict_usage_string_only_options() {
189 static REQ_GROUP_CONFLICT_ONLY_OPTIONS: &str = "\
190 error: the argument '--delete' cannot be used with '--all'
191
192 Usage: clap-test <--all|--delete>
193
194 For 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]
required_group_multiple_args()216 fn 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]
group_multiple_args_error()241 fn 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]
group_overrides_required()253 fn 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]
group_usage_use_val_name()266 fn group_usage_use_val_name() {
267 static GROUP_USAGE_USE_VAL_NAME: &str = "\
268 Usage: prog <A>
269
270 Arguments:
271 [A]
272
273 Options:
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]
group_acts_like_arg()283 fn 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]
conflict_with_overlapping_group_in_error()306 fn conflict_with_overlapping_group_in_error() {
307 static ERR: &str = "\
308 error: the argument '--major' cannot be used with '--minor'
309
310 Usage: prog --major
311
312 For 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]
requires_group_with_overlapping_group_in_error()325 fn requires_group_with_overlapping_group_in_error() {
326 static ERR: &str = "\
327 error: the following required arguments were not provided:
328 <--in|--spec>
329
330 Usage: prog --config <--in|--spec>
331
332 For 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]
347 fn 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