1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8 
9 mod utils;
10 
11 use structopt::StructOpt;
12 use utils::*;
13 
14 #[derive(StructOpt, PartialEq, Debug)]
15 enum Opt {
16     /// Fetch stuff from GitHub
17     Fetch {
18         #[structopt(long)]
19         all: bool,
20         #[structopt(short, long)]
21         /// Overwrite local branches.
22         force: bool,
23         repo: String,
24     },
25 
26     Add {
27         #[structopt(short, long)]
28         interactive: bool,
29         #[structopt(short, long)]
30         verbose: bool,
31     },
32 }
33 
34 #[test]
test_fetch()35 fn test_fetch() {
36     assert_eq!(
37         Opt::Fetch {
38             all: true,
39             force: false,
40             repo: "origin".to_string()
41         },
42         Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "--all", "origin"]))
43     );
44     assert_eq!(
45         Opt::Fetch {
46             all: false,
47             force: true,
48             repo: "origin".to_string()
49         },
50         Opt::from_clap(&Opt::clap().get_matches_from(&["test", "fetch", "-f", "origin"]))
51     );
52 }
53 
54 #[test]
test_add()55 fn test_add() {
56     assert_eq!(
57         Opt::Add {
58             interactive: false,
59             verbose: false
60         },
61         Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add"]))
62     );
63     assert_eq!(
64         Opt::Add {
65             interactive: true,
66             verbose: true
67         },
68         Opt::from_clap(&Opt::clap().get_matches_from(&["test", "add", "-i", "-v"]))
69     );
70 }
71 
72 #[test]
test_no_parse()73 fn test_no_parse() {
74     let result = Opt::clap().get_matches_from_safe(&["test", "badcmd", "-i", "-v"]);
75     assert!(result.is_err());
76 
77     let result = Opt::clap().get_matches_from_safe(&["test", "add", "--badoption"]);
78     assert!(result.is_err());
79 
80     let result = Opt::clap().get_matches_from_safe(&["test"]);
81     assert!(result.is_err());
82 }
83 
84 #[derive(StructOpt, PartialEq, Debug)]
85 enum Opt2 {
86     DoSomething { arg: String },
87 }
88 
89 #[test]
90 /// This test is specifically to make sure that hyphenated subcommands get
91 /// processed correctly.
test_hyphenated_subcommands()92 fn test_hyphenated_subcommands() {
93     assert_eq!(
94         Opt2::DoSomething {
95             arg: "blah".to_string()
96         },
97         Opt2::from_clap(&Opt2::clap().get_matches_from(&["test", "do-something", "blah"]))
98     );
99 }
100 
101 #[derive(StructOpt, PartialEq, Debug)]
102 enum Opt3 {
103     Add,
104     Init,
105     Fetch,
106 }
107 
108 #[test]
test_null_commands()109 fn test_null_commands() {
110     assert_eq!(
111         Opt3::Add,
112         Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "add"]))
113     );
114     assert_eq!(
115         Opt3::Init,
116         Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "init"]))
117     );
118     assert_eq!(
119         Opt3::Fetch,
120         Opt3::from_clap(&Opt3::clap().get_matches_from(&["test", "fetch"]))
121     );
122 }
123 
124 #[derive(StructOpt, PartialEq, Debug)]
125 #[structopt(about = "Not shown")]
126 struct Add {
127     file: String,
128 }
129 /// Not shown
130 #[derive(StructOpt, PartialEq, Debug)]
131 struct Fetch {
132     remote: String,
133 }
134 #[derive(StructOpt, PartialEq, Debug)]
135 enum Opt4 {
136     // Not shown
137     /// Add a file
138     Add(Add),
139     Init,
140     /// download history from remote
141     Fetch(Fetch),
142 }
143 
144 #[test]
test_tuple_commands()145 fn test_tuple_commands() {
146     assert_eq!(
147         Opt4::Add(Add {
148             file: "f".to_string()
149         }),
150         Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "add", "f"]))
151     );
152     assert_eq!(
153         Opt4::Init,
154         Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "init"]))
155     );
156     assert_eq!(
157         Opt4::Fetch(Fetch {
158             remote: "origin".to_string()
159         }),
160         Opt4::from_clap(&Opt4::clap().get_matches_from(&["test", "fetch", "origin"]))
161     );
162 
163     let output = get_long_help::<Opt4>();
164 
165     assert!(output.contains("download history from remote"));
166     assert!(output.contains("Add a file"));
167     assert!(!output.contains("Not shown"));
168 }
169 
170 #[test]
enum_in_enum_subsubcommand()171 fn enum_in_enum_subsubcommand() {
172     #[derive(StructOpt, Debug, PartialEq)]
173     pub enum Opt {
174         Daemon(DaemonCommand),
175     }
176 
177     #[derive(StructOpt, Debug, PartialEq)]
178     pub enum DaemonCommand {
179         Start,
180         Stop,
181     }
182 
183     let result = Opt::clap().get_matches_from_safe(&["test"]);
184     assert!(result.is_err());
185 
186     let result = Opt::clap().get_matches_from_safe(&["test", "daemon"]);
187     assert!(result.is_err());
188 
189     let result = Opt::from_iter(&["test", "daemon", "start"]);
190     assert_eq!(Opt::Daemon(DaemonCommand::Start), result);
191 }
192 
193 #[test]
flatten_enum()194 fn flatten_enum() {
195     #[derive(StructOpt, Debug, PartialEq)]
196     struct Opt {
197         #[structopt(flatten)]
198         sub_cmd: SubCmd,
199     }
200     #[derive(StructOpt, Debug, PartialEq)]
201     enum SubCmd {
202         Foo,
203         Bar,
204     }
205 
206     assert!(Opt::from_iter_safe(&["test"]).is_err());
207     assert_eq!(
208         Opt::from_iter(&["test", "foo"]),
209         Opt {
210             sub_cmd: SubCmd::Foo
211         }
212     );
213 }
214 
215 #[test]
external_subcommand()216 fn external_subcommand() {
217     #[derive(Debug, PartialEq, StructOpt)]
218     struct Opt {
219         #[structopt(subcommand)]
220         sub: Subcommands,
221     }
222 
223     #[derive(Debug, PartialEq, StructOpt)]
224     enum Subcommands {
225         Add,
226         Remove,
227         #[structopt(external_subcommand)]
228         Other(Vec<String>),
229     }
230 
231     assert_eq!(
232         Opt::from_iter(&["test", "add"]),
233         Opt {
234             sub: Subcommands::Add
235         }
236     );
237 
238     assert_eq!(
239         Opt::from_iter(&["test", "remove"]),
240         Opt {
241             sub: Subcommands::Remove
242         }
243     );
244 
245     assert_eq!(
246         Opt::from_iter(&["test", "git", "status"]),
247         Opt {
248             sub: Subcommands::Other(vec!["git".into(), "status".into()])
249         }
250     );
251 
252     assert!(Opt::from_iter_safe(&["test"]).is_err());
253 }
254 
255 #[test]
external_subcommand_os_string()256 fn external_subcommand_os_string() {
257     use std::ffi::OsString;
258 
259     #[derive(Debug, PartialEq, StructOpt)]
260     struct Opt {
261         #[structopt(subcommand)]
262         sub: Subcommands,
263     }
264 
265     #[derive(Debug, PartialEq, StructOpt)]
266     enum Subcommands {
267         #[structopt(external_subcommand)]
268         Other(Vec<OsString>),
269     }
270 
271     assert_eq!(
272         Opt::from_iter(&["test", "git", "status"]),
273         Opt {
274             sub: Subcommands::Other(vec!["git".into(), "status".into()])
275         }
276     );
277 
278     assert!(Opt::from_iter_safe(&["test"]).is_err());
279 }
280 
281 #[test]
external_subcommand_optional()282 fn external_subcommand_optional() {
283     #[derive(Debug, PartialEq, StructOpt)]
284     struct Opt {
285         #[structopt(subcommand)]
286         sub: Option<Subcommands>,
287     }
288 
289     #[derive(Debug, PartialEq, StructOpt)]
290     enum Subcommands {
291         #[structopt(external_subcommand)]
292         Other(Vec<String>),
293     }
294 
295     assert_eq!(
296         Opt::from_iter(&["test", "git", "status"]),
297         Opt {
298             sub: Some(Subcommands::Other(vec!["git".into(), "status".into()]))
299         }
300     );
301 
302     assert_eq!(Opt::from_iter(&["test"]), Opt { sub: None });
303 }
304 
305 #[test]
skip_subcommand()306 fn skip_subcommand() {
307     #[derive(Debug, PartialEq, StructOpt)]
308     struct Opt {
309         #[structopt(subcommand)]
310         sub: Subcommands,
311     }
312 
313     #[derive(Debug, PartialEq, StructOpt)]
314     enum Subcommands {
315         Add,
316         Remove,
317 
318         #[allow(dead_code)]
319         #[structopt(skip)]
320         Skip,
321     }
322 
323     assert_eq!(
324         Opt::from_iter(&["test", "add"]),
325         Opt {
326             sub: Subcommands::Add
327         }
328     );
329 
330     assert_eq!(
331         Opt::from_iter(&["test", "remove"]),
332         Opt {
333             sub: Subcommands::Remove
334         }
335     );
336 
337     let res = Opt::from_iter_safe(&["test", "skip"]);
338     assert!(
339         matches!(
340             res,
341             Err(clap::Error {
342                 kind: clap::ErrorKind::UnknownArgument,
343                 ..
344             })
345         ),
346         "Unexpected result: {:?}",
347         res
348     );
349 }
350