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