]> git.proxmox.com Git - rustc.git/blame - src/libtest/cli.rs
New upstream version 1.46.0~beta.2+dfsg1
[rustc.git] / src / libtest / cli.rs
CommitLineData
e74abb32
XL
1//! Module converting command-line arguments into test configuration.
2
3use std::env;
4use std::path::PathBuf;
e74abb32 5
e74abb32 6use super::helpers::isatty;
dfeec247
XL
7use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
8use super::time::TestTimeOptions;
e74abb32
XL
9
10#[derive(Debug)]
11pub struct TestOpts {
12 pub list: bool,
13 pub filter: Option<String>,
14 pub filter_exact: bool,
60c5eb7d 15 pub force_run_in_process: bool,
e74abb32
XL
16 pub exclude_should_panic: bool,
17 pub run_ignored: RunIgnored,
18 pub run_tests: bool,
19 pub bench_benchmarks: bool,
20 pub logfile: Option<PathBuf>,
21 pub nocapture: bool,
22 pub color: ColorConfig,
23 pub format: OutputFormat,
24 pub test_threads: Option<usize>,
25 pub skip: Vec<String>,
26 pub time_options: Option<TestTimeOptions>,
27 pub options: Options,
28}
29
30impl TestOpts {
31 pub fn use_color(&self) -> bool {
32 match self.color {
33 ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
34 ColorConfig::AlwaysColor => true,
35 ColorConfig::NeverColor => false,
36 }
37 }
38}
39
40/// Result of parsing the options.
41pub type OptRes = Result<TestOpts, String>;
42/// Result of parsing the option part.
43type OptPartRes<T> = Result<T, String>;
44
45fn optgroups() -> getopts::Options {
46 let mut opts = getopts::Options::new();
47 opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
48 .optflag("", "ignored", "Run only ignored tests")
60c5eb7d 49 .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
e74abb32
XL
50 .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
51 .optflag("", "test", "Run tests and not benchmarks")
52 .optflag("", "bench", "Run benchmarks instead of tests")
53 .optflag("", "list", "List all tests and benchmarks")
54 .optflag("h", "help", "Display this message (longer with --help)")
55 .optopt(
56 "",
57 "logfile",
58 "Write logs to the specified file instead \
59 of stdout",
60 "PATH",
61 )
62 .optflag(
63 "",
64 "nocapture",
65 "don't capture stdout/stderr of each \
66 task, allow printing directly",
67 )
68 .optopt(
69 "",
70 "test-threads",
71 "Number of threads used for running tests \
72 in parallel",
73 "n_threads",
74 )
75 .optmulti(
76 "",
77 "skip",
78 "Skip tests whose names contain FILTER (this flag can \
79 be used multiple times)",
80 "FILTER",
81 )
82 .optflag(
83 "q",
84 "quiet",
85 "Display one character per test instead of one line. \
86 Alias to --format=terse",
87 )
dfeec247 88 .optflag("", "exact", "Exactly match filters rather than by substring")
e74abb32
XL
89 .optopt(
90 "",
91 "color",
92 "Configure coloring of output:
93 auto = colorize if stdout is a tty and tests are run on serially (default);
94 always = always colorize output;
95 never = never colorize output;",
96 "auto|always|never",
97 )
98 .optopt(
99 "",
100 "format",
101 "Configure formatting of output:
102 pretty = Print verbose output;
103 terse = Display one character per test;
104 json = Output a json document",
105 "pretty|terse|json",
106 )
dfeec247 107 .optflag("", "show-output", "Show captured stdout of successful tests")
e74abb32
XL
108 .optopt(
109 "Z",
110 "",
111 "Enable nightly-only flags:
112 unstable-options = Allow use of experimental features",
113 "unstable-options",
114 )
115 .optflagopt(
116 "",
117 "report-time",
f035d41b 118 "Show execution time of each test. Available values:
e74abb32
XL
119 plain = do not colorize the execution time (default);
120 colored = colorize output according to the `color` parameter value;
121
122 Threshold values for colorized output can be configured via
123 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
124 `RUST_TEST_TIME_DOCTEST` environment variables.
125
126 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
dfeec247
XL
127 Durations must be specified in milliseconds, e.g. `500,2000` means that the warn time
128 is 0.5 seconds, and the critical time is 2 seconds.
e74abb32
XL
129
130 Not available for --format=terse",
dfeec247 131 "plain|colored",
e74abb32
XL
132 )
133 .optflag(
134 "",
135 "ensure-time",
136 "Treat excess of the test execution time limit as error.
137
138 Threshold values for this option can be configured via
139 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
140 `RUST_TEST_TIME_DOCTEST` environment variables.
141
142 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
143
144 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
dfeec247 145 ",
e74abb32
XL
146 );
147 opts
148}
149
150fn usage(binary: &str, options: &getopts::Options) {
151 let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
152 println!(
153 r#"{usage}
154
155The FILTER string is tested against the name of all tests, and only those
156tests whose names contain the filter are run.
157
158By default, all tests are run in parallel. This can be altered with the
159--test-threads flag or the RUST_TEST_THREADS environment variable when running
160tests (set it to 1).
161
162All tests have their standard output and standard error captured by default.
163This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
164environment variable to a value other than "0". Logging is not captured by default.
165
166Test Attributes:
167
168 `#[test]` - Indicates a function is a test to be run. This function
169 takes no arguments.
170 `#[bench]` - Indicates a function is a benchmark to be run. This
171 function takes one argument (test::Bencher).
172 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
173 the code causes a panic (an assertion failure or panic!)
174 A message may be provided, which the failure string must
175 contain: #[should_panic(expected = "foo")].
176 `#[ignore]` - When applied to a function which is already attributed as a
177 test, then the test runner will ignore these tests during
178 normal test runs. Running with --ignored or --include-ignored will run
179 these tests."#,
180 usage = options.usage(&message)
181 );
182}
183
184/// Parses command line arguments into test options.
185/// Returns `None` if help was requested (since we only show help message and don't run tests),
186/// returns `Some(Err(..))` if provided arguments are incorrect,
187/// otherwise creates a `TestOpts` object and returns it.
188pub fn parse_opts(args: &[String]) -> Option<OptRes> {
189 // Parse matches.
190 let opts = optgroups();
191 let args = args.get(1..).unwrap_or(args);
192 let matches = match opts.parse(args) {
193 Ok(m) => m,
194 Err(f) => return Some(Err(f.to_string())),
195 };
196
197 // Check if help was requested.
198 if matches.opt_present("h") {
199 // Show help and do nothing more.
200 usage(&args[0], &opts);
201 return None;
202 }
203
204 // Actually parse the opts.
205 let opts_result = parse_opts_impl(matches);
206
207 Some(opts_result)
208}
209
210// Gets the option value and checks if unstable features are enabled.
211macro_rules! unstable_optflag {
212 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
213 let opt = $matches.opt_present($option_name);
214 if !$allow_unstable && opt {
215 return Err(format!(
ba9703b0 216 "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options",
e74abb32
XL
217 $option_name
218 ));
219 }
220
221 opt
222 }};
223}
224
225// Implementation of `parse_opts` that doesn't care about help message
226// and returns a `Result`.
227fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
228 let allow_unstable = get_allow_unstable(&matches)?;
229
230 // Unstable flags
60c5eb7d 231 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
e74abb32
XL
232 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
233 let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
234 let time_options = get_time_options(&matches, allow_unstable)?;
235
236 let quiet = matches.opt_present("quiet");
237 let exact = matches.opt_present("exact");
238 let list = matches.opt_present("list");
239 let skip = matches.opt_strs("skip");
240
241 let bench_benchmarks = matches.opt_present("bench");
242 let run_tests = !bench_benchmarks || matches.opt_present("test");
243
244 let logfile = get_log_file(&matches)?;
245 let run_ignored = get_run_ignored(&matches, include_ignored)?;
246 let filter = get_filter(&matches)?;
247 let nocapture = get_nocapture(&matches)?;
248 let test_threads = get_test_threads(&matches)?;
249 let color = get_color_config(&matches)?;
250 let format = get_format(&matches, quiet, allow_unstable)?;
251
252 let options = Options::new().display_output(matches.opt_present("show-output"));
253
254 let test_opts = TestOpts {
255 list,
256 filter,
257 filter_exact: exact,
60c5eb7d 258 force_run_in_process,
e74abb32
XL
259 exclude_should_panic,
260 run_ignored,
261 run_tests,
262 bench_benchmarks,
263 logfile,
264 nocapture,
265 color,
266 format,
267 test_threads,
268 skip,
269 time_options,
270 options,
271 };
272
273 Ok(test_opts)
274}
275
74b04a01 276// FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
e74abb32
XL
277fn is_nightly() -> bool {
278 // Whether this is a feature-staged build, i.e., on the beta or stable channel
279 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
280 // Whether we should enable unstable features for bootstrapping
281 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
282
283 bootstrap || !disable_unstable_features
284}
285
74b04a01 286// Gets the CLI options associated with `report-time` feature.
e74abb32
XL
287fn get_time_options(
288 matches: &getopts::Matches,
dfeec247
XL
289 allow_unstable: bool,
290) -> OptPartRes<Option<TestTimeOptions>> {
e74abb32
XL
291 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
292 let colored_opt_str = matches.opt_str("report-time");
293 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
294 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
295
296 // If `ensure-test-time` option is provided, time output is enforced,
297 // so user won't be confused if any of tests will silently fail.
298 let options = if report_time || ensure_test_time {
299 if ensure_test_time && !report_time {
300 report_time_colored = true;
301 }
302 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
303 } else {
304 None
305 };
306
307 Ok(options)
308}
309
310fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
311 let test_threads = match matches.opt_str("test-threads") {
312 Some(n_str) => match n_str.parse::<usize>() {
313 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
314 Ok(n) => Some(n),
315 Err(e) => {
316 return Err(format!(
317 "argument for --test-threads must be a number > 0 \
318 (error: {})",
319 e
320 ));
321 }
322 },
323 None => None,
324 };
325
326 Ok(test_threads)
327}
328
329fn get_format(
330 matches: &getopts::Matches,
331 quiet: bool,
dfeec247 332 allow_unstable: bool,
e74abb32 333) -> OptPartRes<OutputFormat> {
f9f354fc 334 let format = match matches.opt_str("format").as_deref() {
e74abb32
XL
335 None if quiet => OutputFormat::Terse,
336 Some("pretty") | None => OutputFormat::Pretty,
337 Some("terse") => OutputFormat::Terse,
338 Some("json") => {
339 if !allow_unstable {
dfeec247 340 return Err("The \"json\" format is only accepted on the nightly compiler".into());
e74abb32
XL
341 }
342 OutputFormat::Json
343 }
344
345 Some(v) => {
346 return Err(format!(
347 "argument for --format must be pretty, terse, or json (was \
348 {})",
349 v
350 ));
351 }
352 };
353
354 Ok(format)
355}
356
357fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
f9f354fc 358 let color = match matches.opt_str("color").as_deref() {
e74abb32
XL
359 Some("auto") | None => ColorConfig::AutoColor,
360 Some("always") => ColorConfig::AlwaysColor,
361 Some("never") => ColorConfig::NeverColor,
362
363 Some(v) => {
364 return Err(format!(
365 "argument for --color must be auto, always, or never (was \
366 {})",
367 v
368 ));
369 }
370 };
371
372 Ok(color)
373}
374
375fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
376 let mut nocapture = matches.opt_present("nocapture");
377 if !nocapture {
378 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
379 Ok(val) => &val != "0",
380 Err(_) => false,
381 };
382 }
383
384 Ok(nocapture)
385}
386
387fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
388 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
389 (true, true) => {
dfeec247 390 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
e74abb32
XL
391 }
392 (true, false) => RunIgnored::Yes,
393 (false, true) => RunIgnored::Only,
394 (false, false) => RunIgnored::No,
395 };
396
397 Ok(run_ignored)
398}
399
400fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
dfeec247 401 let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };
e74abb32
XL
402
403 Ok(filter)
404}
405
406fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
407 let mut allow_unstable = false;
408
409 if let Some(opt) = matches.opt_str("Z") {
410 if !is_nightly() {
dfeec247 411 return Err("the option `Z` is only accepted on the nightly compiler".into());
e74abb32
XL
412 }
413
414 match &*opt {
415 "unstable-options" => {
416 allow_unstable = true;
417 }
418 _ => {
419 return Err("Unrecognized option to `Z`".into());
420 }
421 }
422 };
423
424 Ok(allow_unstable)
425}
426
427fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
428 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));
429
430 Ok(logfile)
431}