1use clap::{arg, Arg, ArgAction, Command};
2
3#[test]
4fn opt_missing() {
5    let r = Command::new("df")
6        .arg(
7            Arg::new("color")
8                .long("color")
9                .default_value("auto")
10                .num_args(0..=1)
11                .require_equals(true)
12                .default_missing_value("always"),
13        )
14        .try_get_matches_from(vec![""]);
15    assert!(r.is_ok(), "{}", r.unwrap_err());
16    let m = r.unwrap();
17    assert!(m.contains_id("color"));
18    assert_eq!(
19        m.get_one::<String>("color").map(|v| v.as_str()).unwrap(),
20        "auto"
21    );
22    assert_eq!(
23        m.value_source("color").unwrap(),
24        clap::parser::ValueSource::DefaultValue
25    );
26    assert_eq!(m.index_of("color"), Some(1));
27}
28
29#[test]
30fn opt_present_with_missing_value() {
31    let r = Command::new("df")
32        .arg(
33            Arg::new("color")
34                .long("color")
35                .default_value("auto")
36                .num_args(0..=1)
37                .require_equals(true)
38                .default_missing_value("always"),
39        )
40        .try_get_matches_from(vec!["", "--color"]);
41    assert!(r.is_ok(), "{}", r.unwrap_err());
42    let m = r.unwrap();
43    assert!(m.contains_id("color"));
44    assert_eq!(
45        m.get_one::<String>("color").map(|v| v.as_str()).unwrap(),
46        "always"
47    );
48    assert_eq!(
49        m.value_source("color").unwrap(),
50        clap::parser::ValueSource::CommandLine
51    );
52    assert_eq!(m.index_of("color"), Some(2));
53}
54
55#[test]
56fn opt_present_with_value() {
57    let r = Command::new("df")
58        .arg(
59            Arg::new("color")
60                .long("color")
61                .default_value("auto")
62                .num_args(0..=1)
63                .require_equals(true)
64                .default_missing_value("always"),
65        )
66        .try_get_matches_from(vec!["", "--color=never"]);
67    assert!(r.is_ok(), "{}", r.unwrap_err());
68    let m = r.unwrap();
69    assert!(m.contains_id("color"));
70    assert_eq!(
71        m.get_one::<String>("color").map(|v| v.as_str()).unwrap(),
72        "never"
73    );
74    assert_eq!(
75        m.value_source("color").unwrap(),
76        clap::parser::ValueSource::CommandLine
77    );
78    assert_eq!(m.index_of("color"), Some(2));
79}
80
81#[test]
82fn opt_present_with_empty_value() {
83    let r = Command::new("df")
84        .arg(
85            Arg::new("color")
86                .long("color")
87                .default_value("auto")
88                .require_equals(true)
89                .default_missing_value("always"),
90        )
91        .try_get_matches_from(vec!["", "--color="]);
92    assert!(r.is_ok(), "{}", r.unwrap_err());
93    let m = r.unwrap();
94    assert!(m.contains_id("color"));
95    assert_eq!(
96        m.get_one::<String>("color").map(|v| v.as_str()).unwrap(),
97        ""
98    );
99    assert_eq!(
100        m.value_source("color").unwrap(),
101        clap::parser::ValueSource::CommandLine
102    );
103    assert_eq!(m.index_of("color"), Some(2));
104}
105
106//## `default_value`/`default_missing_value` non-interaction checks
107
108#[test]
109fn opt_default() {
110    // assert no change to usual argument handling when adding default_missing_value()
111    let r = Command::new("cmd")
112        .arg(
113            arg!(o: -o [opt] "some opt")
114                .default_value("default")
115                .default_missing_value("default_missing"),
116        )
117        .try_get_matches_from(vec![""]);
118    assert!(r.is_ok(), "{}", r.unwrap_err());
119    let m = r.unwrap();
120    assert!(m.contains_id("o"));
121    assert_eq!(
122        m.get_one::<String>("o").map(|v| v.as_str()).unwrap(),
123        "default"
124    );
125}
126
127#[test]
128fn opt_default_user_override() {
129    // assert no change to usual argument handling when adding default_missing_value()
130    let r = Command::new("cmd")
131        .arg(
132            arg!(o: -o [opt] "some opt")
133                .default_value("default")
134                .default_missing_value("default_missing"),
135        )
136        .try_get_matches_from(vec!["", "-o=value"]);
137    assert!(r.is_ok(), "{}", r.unwrap_err());
138    let m = r.unwrap();
139    assert!(m.contains_id("o"));
140    assert_eq!(
141        m.get_one::<String>("o").map(|v| v.as_str()).unwrap(),
142        "value"
143    );
144}
145
146#[test]
147fn default_missing_value_per_occurrence() {
148    // assert no change to usual argument handling when adding default_missing_value()
149    let r = Command::new("cmd")
150        .arg(
151            arg!(o: -o [opt] ... "some opt")
152                .default_value("default")
153                .default_missing_value("default_missing"),
154        )
155        .try_get_matches_from(vec!["", "-o", "-o=value", "-o"]);
156    assert!(r.is_ok(), "{}", r.unwrap_err());
157    let m = r.unwrap();
158    assert_eq!(
159        m.get_many::<String>("o")
160            .unwrap()
161            .map(|v| v.as_str())
162            .collect::<Vec<_>>(),
163        vec!["default_missing", "value", "default_missing"]
164    );
165}
166
167#[test]
168#[allow(clippy::bool_assert_comparison)]
169fn default_missing_value_flag_value() {
170    let cmd = Command::new("test").arg(
171        Arg::new("flag")
172            .long("flag")
173            .action(ArgAction::Set)
174            .num_args(0..=1)
175            .default_value("false")
176            .default_missing_value("true"),
177    );
178
179    let m = cmd.clone().try_get_matches_from(["test"]).unwrap();
180    assert!(m.contains_id("flag"));
181    assert_eq!(
182        m.get_one::<String>("flag").map(|v| v.as_str()),
183        Some("false")
184    );
185    assert_eq!(
186        m.value_source("flag").unwrap(),
187        clap::parser::ValueSource::DefaultValue
188    );
189
190    let m = cmd
191        .clone()
192        .try_get_matches_from(["test", "--flag"])
193        .unwrap();
194    assert!(m.contains_id("flag"));
195    assert_eq!(
196        m.get_one::<String>("flag").map(|v| v.as_str()),
197        Some("true")
198    );
199    assert_eq!(
200        m.value_source("flag").unwrap(),
201        clap::parser::ValueSource::CommandLine
202    );
203
204    let m = cmd
205        .clone()
206        .try_get_matches_from(["test", "--flag=true"])
207        .unwrap();
208    assert!(m.contains_id("flag"));
209    assert_eq!(
210        m.get_one::<String>("flag").map(|v| v.as_str()),
211        Some("true")
212    );
213    assert_eq!(
214        m.value_source("flag").unwrap(),
215        clap::parser::ValueSource::CommandLine
216    );
217
218    let m = cmd.try_get_matches_from(["test", "--flag=false"]).unwrap();
219    assert!(m.contains_id("flag"));
220    assert_eq!(
221        m.get_one::<String>("flag").map(|v| v.as_str()),
222        Some("false")
223    );
224    assert_eq!(
225        m.value_source("flag").unwrap(),
226        clap::parser::ValueSource::CommandLine
227    );
228}
229
230#[test]
231fn delimited_missing_value() {
232    let cmd = Command::new("test").arg(
233        Arg::new("flag")
234            .long("flag")
235            .default_value("one,two")
236            .default_missing_value("three,four")
237            .num_args(0..)
238            .value_delimiter(',')
239            .require_equals(true),
240    );
241
242    let m = cmd.clone().try_get_matches_from(["test"]).unwrap();
243    assert_eq!(
244        m.get_many::<String>("flag")
245            .unwrap()
246            .map(|s| s.as_str())
247            .collect::<Vec<_>>(),
248        vec!["one", "two"]
249    );
250
251    let m = cmd.try_get_matches_from(["test", "--flag"]).unwrap();
252    assert_eq!(
253        m.get_many::<String>("flag")
254            .unwrap()
255            .map(|s| s.as_str())
256            .collect::<Vec<_>>(),
257        vec!["three", "four"]
258    );
259}
260
261#[cfg(debug_assertions)]
262#[test]
263#[cfg(feature = "error-context")]
264#[should_panic = "Argument `arg`'s default_missing_value=\"value\" failed validation: error: invalid value 'value' for '[arg]'"]
265fn default_missing_values_are_possible_values() {
266    use clap::{Arg, Command};
267
268    let _ = Command::new("test")
269        .arg(
270            Arg::new("arg")
271                .value_parser(["one", "two"])
272                .default_missing_value("value"),
273        )
274        .try_get_matches();
275}
276
277#[cfg(debug_assertions)]
278#[test]
279#[cfg(feature = "error-context")]
280#[should_panic = "Argument `arg`'s default_missing_value=\"value\" failed validation: error: invalid value 'value' for '[arg]"]
281fn default_missing_values_are_valid() {
282    use clap::{Arg, Command};
283
284    let _ = Command::new("test")
285        .arg(
286            Arg::new("arg")
287                .value_parser(clap::value_parser!(u32))
288                .default_missing_value("value"),
289        )
290        .try_get_matches();
291}
292
293#[test]
294fn valid_index() {
295    let m = Command::new("df")
296        .arg(
297            Arg::new("color")
298                .long("color")
299                .default_value("auto")
300                .num_args(0..=1)
301                .require_equals(true)
302                .default_missing_value("always"),
303        )
304        .arg(Arg::new("sync").long("sync").action(ArgAction::SetTrue))
305        .try_get_matches_from(vec!["df", "--color", "--sync"])
306        .unwrap();
307    assert!(m.contains_id("color"));
308    assert_eq!(
309        m.get_one::<String>("color").map(|v| v.as_str()).unwrap(),
310        "always"
311    );
312    assert_eq!(
313        m.value_source("color").unwrap(),
314        clap::parser::ValueSource::CommandLine
315    );
316
317    // Make sure the index reflects `--color`s position and not something else
318    assert_eq!(m.index_of("color"), Some(2));
319}
320