1 //! Module converting command-line arguments into test configuration.
4 use std::path::PathBuf;
6 use super::helpers::isatty;
7 use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
8 use super::time::TestTimeOptions;
13 pub filter: Option<String>,
14 pub filter_exact: bool,
15 pub force_run_in_process: bool,
16 pub exclude_should_panic: bool,
17 pub run_ignored: RunIgnored,
19 pub bench_benchmarks: bool,
20 pub logfile: Option<PathBuf>,
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>,
31 pub fn use_color(&self) -> bool {
33 ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
34 ColorConfig::AlwaysColor => true,
35 ColorConfig::NeverColor => false,
40 /// Result of parsing the options.
41 pub type OptRes = Result<TestOpts, String>;
42 /// Result of parsing the option part.
43 type OptPartRes<T> = Result<T, String>;
45 fn 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")
49 .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
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)")
58 "Write logs to the specified file instead \
65 "don't capture stdout/stderr of each \
66 task, allow printing directly",
71 "Number of threads used for running tests \
78 "Skip tests whose names contain FILTER (this flag can \
79 be used multiple times)",
85 "Display one character per test instead of one line. \
86 Alias to --format=terse",
88 .optflag("", "exact", "Exactly match filters rather than by substring")
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;",
101 "Configure formatting of output:
102 pretty = Print verbose output;
103 terse = Display one character per test;
104 json = Output a json document",
107 .optflag("", "show-output", "Show captured stdout of successful tests")
111 "Enable nightly-only flags:
112 unstable-options = Allow use of experimental features",
118 "Show execution time of each test. Available values:
119 plain = do not colorize the execution time (default);
120 colored = colorize output according to the `color` parameter value;
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.
126 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
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.
130 Not available for --format=terse",
136 "Treat excess of the test execution time limit as error.
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.
142 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
144 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
150 fn usage(binary: &str, options: &getopts::Options) {
151 let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
155 The FILTER string is tested against the name of all tests, and only those
156 tests whose names contain the filter are run.
158 By 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
162 All tests have their standard output and standard error captured by default.
163 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
164 environment variable to a value other than "0". Logging is not captured by default.
168 `#[test]` - Indicates a function is a test to be run. This function
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
180 usage = options.usage(&message)
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.
188 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
190 let opts = optgroups();
191 let args = args.get(1..).unwrap_or(args);
192 let matches = match opts.parse(args) {
194 Err(f) => return Some(Err(f.to_string())),
197 // Check if help was requested.
198 if matches.opt_present("h") {
199 // Show help and do nothing more.
200 usage(&args[0], &opts);
204 // Actually parse the opts.
205 let opts_result = parse_opts_impl(matches);
210 // Gets the option value and checks if unstable features are enabled.
211 macro_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 {
216 "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options",
225 // Implementation of `parse_opts` that doesn't care about help message
226 // and returns a `Result`.
227 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
228 let allow_unstable = get_allow_unstable(&matches)?;
231 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
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)?;
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");
241 let bench_benchmarks = matches.opt_present("bench");
242 let run_tests = !bench_benchmarks || matches.opt_present("test");
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)?;
252 let options = Options::new().display_output(matches.opt_present("show-output"));
254 let test_opts = TestOpts {
258 force_run_in_process,
259 exclude_should_panic,
276 // FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
277 fn 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();
283 bootstrap || !disable_unstable_features
286 // Gets the CLI options associated with `report-time` feature.
288 matches: &getopts::Matches,
289 allow_unstable: bool,
290 ) -> OptPartRes<Option<TestTimeOptions>> {
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");
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;
302 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
310 fn 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()),
317 "argument for --test-threads must be a number > 0 \
330 matches: &getopts::Matches,
332 allow_unstable: bool,
333 ) -> OptPartRes<OutputFormat> {
334 let format = match matches.opt_str("format").as_deref() {
335 None if quiet => OutputFormat::Terse,
336 Some("pretty") | None => OutputFormat::Pretty,
337 Some("terse") => OutputFormat::Terse,
340 return Err("The \"json\" format is only accepted on the nightly compiler".into());
347 "argument for --format must be pretty, terse, or json (was \
357 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
358 let color = match matches.opt_str("color").as_deref() {
359 Some("auto") | None => ColorConfig::AutoColor,
360 Some("always") => ColorConfig::AlwaysColor,
361 Some("never") => ColorConfig::NeverColor,
365 "argument for --color must be auto, always, or never (was \
375 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
376 let mut nocapture = matches.opt_present("nocapture");
378 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
379 Ok(val) => &val != "0",
387 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
388 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
390 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
392 (true, false) => RunIgnored::Yes,
393 (false, true) => RunIgnored::Only,
394 (false, false) => RunIgnored::No,
400 fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
401 let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };
406 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
407 let mut allow_unstable = false;
409 if let Some(opt) = matches.opt_str("Z") {
411 return Err("the option `Z` is only accepted on the nightly compiler".into());
415 "unstable-options" => {
416 allow_unstable = true;
419 return Err("Unrecognized option to `Z`".into());
427 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
428 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));