• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 Guillaume Pinot (@TeXitoi) <texitoi@texitoi.eu>,
2 // Kevin Knapp (@kbknapp) <kbknapp@gmail.com>, and
3 // Ana Hobden (@hoverbear) <operator@hoverbear.org>
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10 //
11 // This work was derived from Structopt (https://github.com/TeXitoi/structopt)
12 // commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
13 // MIT/Apache 2.0 license.
14 
15 use crate::utils;
16 
17 use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
18 
19 #[test]
doc_comments()20 fn doc_comments() {
21     /// Lorem ipsum
22     #[derive(Parser, PartialEq, Debug)]
23     struct LoremIpsum {
24         /// Fooify a bar
25         /// and a baz
26         #[arg(short, long)]
27         foo: bool,
28     }
29 
30     let help = utils::get_long_help::<LoremIpsum>();
31     assert!(help.contains("Lorem ipsum"));
32     assert!(help.contains("Fooify a bar and a baz"));
33 }
34 
35 #[test]
help_is_better_than_comments()36 fn help_is_better_than_comments() {
37     /// Lorem ipsum
38     #[derive(Parser, PartialEq, Debug)]
39     #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
40     struct LoremIpsum {
41         /// Fooify a bar
42         #[arg(short, long, help = "DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES")]
43         foo: bool,
44     }
45 
46     let help = utils::get_long_help::<LoremIpsum>();
47     assert!(help.contains("Dolor sit amet"));
48     assert!(!help.contains("Lorem ipsum"));
49     assert!(help.contains("DO NOT PASS A BAR"));
50 }
51 
52 #[test]
empty_line_in_doc_comment_is_double_linefeed()53 fn empty_line_in_doc_comment_is_double_linefeed() {
54     /// Foo.
55     ///
56     /// Bar
57     #[derive(Parser, PartialEq, Debug)]
58     #[command(name = "lorem-ipsum")]
59     struct LoremIpsum {}
60 
61     let help = utils::get_long_help::<LoremIpsum>();
62     assert!(help.starts_with(
63         "\
64 Foo.
65 
66 Bar
67 
68 Usage:"
69     ));
70 }
71 
72 #[test]
field_long_doc_comment_both_help_long_help()73 fn field_long_doc_comment_both_help_long_help() {
74     /// Lorem ipsumclap
75     #[derive(Parser, PartialEq, Debug)]
76     #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
77     struct LoremIpsum {
78         /// Dot is removed from multiline comments.
79         ///
80         /// Long help
81         #[arg(long)]
82         foo: bool,
83 
84         /// Dot is removed from one short comment.
85         #[arg(long)]
86         bar: bool,
87     }
88 
89     let short_help = utils::get_help::<LoremIpsum>();
90     let long_help = utils::get_long_help::<LoremIpsum>();
91 
92     assert!(short_help.contains("Dot is removed from one short comment"));
93     assert!(!short_help.contains("Dot is removed from one short comment."));
94     assert!(short_help.contains("Dot is removed from multiline comments"));
95     assert!(!short_help.contains("Dot is removed from multiline comments."));
96     assert!(long_help.contains("Long help"));
97     assert!(!short_help.contains("Long help"));
98 }
99 
100 #[test]
top_long_doc_comment_both_help_long_help()101 fn top_long_doc_comment_both_help_long_help() {
102     /// Lorem ipsumclap
103     #[derive(Parser, Debug)]
104     #[command(name = "lorem-ipsum", about = "Dolor sit amet")]
105     struct LoremIpsum {
106         #[command(subcommand)]
107         foo: SubCommand,
108     }
109 
110     #[derive(Parser, Debug)]
111     pub enum SubCommand {
112         /// DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES
113         ///
114         /// Or something else
115         Foo {
116             #[arg(help = "foo")]
117             bars: String,
118         },
119     }
120 
121     let short_help = utils::get_help::<LoremIpsum>();
122     let long_help = utils::get_subcommand_long_help::<LoremIpsum>("foo");
123 
124     assert!(!short_help.contains("Or something else"));
125     assert!(long_help.contains("DO NOT PASS A BAR UNDER ANY CIRCUMSTANCES"));
126     assert!(long_help.contains("Or something else"));
127 }
128 
129 #[test]
verbatim_doc_comment()130 fn verbatim_doc_comment() {
131     /// DANCE!
132     ///
133     ///                    ()
134     ///                    |
135     ///               (   ()   )
136     ///     ) ________    //  )
137     ///  ()  |\       \  //
138     /// ( \\__ \ ______\//
139     ///    \__) |       |
140     ///      |  |       |
141     ///       \ |       |
142     ///        \|_______|
143     ///        //    \\
144     ///       ((     ||
145     ///        \\    ||
146     ///      ( ()    ||
147     ///       (      () ) )
148     #[derive(Parser, Debug)]
149     #[command(verbatim_doc_comment)]
150     struct SeeFigure1 {
151         #[arg(long)]
152         foo: bool,
153     }
154 
155     let help = utils::get_long_help::<SeeFigure1>();
156     let sample = r#"
157                    ()
158                    |
159               (   ()   )
160     ) ________    //  )
161  ()  |\       \  //
162 ( \\__ \ ______\//
163    \__) |       |
164      |  |       |
165       \ |       |
166        \|_______|
167        //    \\
168       ((     ||
169        \\    ||
170      ( ()    ||
171       (      () ) )"#;
172 
173     assert!(help.contains(sample))
174 }
175 
176 #[test]
verbatim_doc_comment_field()177 fn verbatim_doc_comment_field() {
178     #[derive(Parser, Debug)]
179     struct Command {
180         /// This help ends in a period.
181         #[arg(long, verbatim_doc_comment)]
182         foo: bool,
183         /// This help does not end in a period.
184         #[arg(long)]
185         bar: bool,
186     }
187 
188     let help = utils::get_long_help::<Command>();
189 
190     assert!(help.contains("This help ends in a period."));
191     assert!(help.contains("This help does not end in a period"));
192 }
193 
194 #[test]
multiline_separates_default()195 fn multiline_separates_default() {
196     #[derive(Parser, Debug)]
197     struct Command {
198         /// Multiline
199         ///
200         /// Doc comment
201         #[arg(long, default_value = "x")]
202         x: String,
203     }
204 
205     let help = utils::get_long_help::<Command>();
206     assert!(!help.contains("Doc comment [default"));
207     assert!(help.lines().any(|s| s.trim().starts_with("[default")));
208 
209     // The short help should still have the default on the same line
210     let help = utils::get_help::<Command>();
211     assert!(help.contains("Multiline [default"));
212 }
213 
214 #[test]
value_enum_multiline_doc_comment()215 fn value_enum_multiline_doc_comment() {
216     #[derive(Parser, Debug)]
217     struct Command {
218         x: LoremIpsum,
219     }
220 
221     #[derive(ValueEnum, Clone, PartialEq, Debug)]
222     enum LoremIpsum {
223         /// Doc comment summary
224         ///
225         /// The doc comment body is ignored
226         Bar,
227     }
228 
229     let help = utils::get_long_help::<Command>();
230 
231     assert!(help.contains("Doc comment summary"));
232 
233     // There is no long help text for possible values. The long help only contains the summary.
234     assert!(!help.contains("The doc comment body is ignored"));
235 }
236 
237 #[test]
doc_comment_about_handles_both_abouts()238 fn doc_comment_about_handles_both_abouts() {
239     /// Opts doc comment summary
240     #[derive(Parser, Debug)]
241     pub struct Opts {
242         #[command(subcommand)]
243         pub cmd: Sub,
244     }
245 
246     /// Sub doc comment summary
247     ///
248     /// Sub doc comment body
249     #[derive(Parser, PartialEq, Eq, Debug)]
250     pub enum Sub {
251         Compress { output: String },
252     }
253 
254     let cmd = Opts::command();
255     assert_eq!(
256         cmd.get_about().map(|s| s.to_string()),
257         Some("Opts doc comment summary".to_owned())
258     );
259     // clap will fallback to `about` on `None`.  The main care about is not providing a `Sub` doc
260     // comment.
261     assert_eq!(cmd.get_long_about(), None);
262 }
263 
264 #[test]
respect_subcommand_doc_comment()265 fn respect_subcommand_doc_comment() {
266     #[derive(Parser, Debug)]
267     pub enum Cmd {
268         /// For child
269         #[command(subcommand)]
270         Child(Child),
271     }
272 
273     #[derive(Subcommand, Debug)]
274     pub enum Child {
275         One,
276         Twp,
277     }
278 
279     const OUTPUT: &str = "\
280 Usage: cmd <COMMAND>
281 
282 Commands:
283   child  For child
284   help   Print this message or the help of the given subcommand(s)
285 
286 Options:
287   -h, --help  Print help
288 ";
289     utils::assert_output::<Cmd>("cmd --help", OUTPUT, false);
290 }
291 
292 #[test]
force_long_help()293 fn force_long_help() {
294     /// Lorem ipsum
295     #[derive(Parser, PartialEq, Debug)]
296     struct LoremIpsum {
297         /// Fooify a bar
298         /// and a baz.
299         #[arg(short, long, long_help)]
300         foo: bool,
301     }
302 
303     let help = utils::get_long_help::<LoremIpsum>();
304     assert!(help.contains("Fooify a bar and a baz."));
305 }
306