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 filters: Vec<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,
25 pub shuffle_seed: Option<u64>,
26 pub test_threads: Option<usize>,
27 pub skip: Vec<String>,
28 pub time_options: Option<TestTimeOptions>,
33 pub fn use_color(&self) -> bool {
35 ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
36 ColorConfig::AlwaysColor => true,
37 ColorConfig::NeverColor => false,
42 /// Result of parsing the options.
43 pub type OptRes = Result<TestOpts, String>;
44 /// Result of parsing the option part.
45 type OptPartRes<T> = Result<T, String>;
47 fn optgroups() -> getopts::Options {
48 let mut opts = getopts::Options::new();
49 opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
50 .optflag("", "ignored", "Run only ignored tests")
51 .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
52 .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
53 .optflag("", "test", "Run tests and not benchmarks")
54 .optflag("", "bench", "Run benchmarks instead of tests")
55 .optflag("", "list", "List all tests and benchmarks")
56 .optflag("h", "help", "Display this message")
57 .optopt("", "logfile", "Write logs to the specified file", "PATH")
61 "don't capture stdout/stderr of each \
62 task, allow printing directly",
67 "Number of threads used for running tests \
74 "Skip tests whose names contain FILTER (this flag can \
75 be used multiple times)",
81 "Display one character per test instead of one line. \
82 Alias to --format=terse",
84 .optflag("", "exact", "Exactly match filters rather than by substring")
88 "Configure coloring of output:
89 auto = colorize if stdout is a tty and tests are run on serially (default);
90 always = always colorize output;
91 never = never colorize output;",
97 "Configure formatting of output:
98 pretty = Print verbose output;
99 terse = Display one character per test;
100 json = Output a json document;
101 junit = Output a JUnit document",
102 "pretty|terse|json|junit",
104 .optflag("", "show-output", "Show captured stdout of successful tests")
108 "Enable nightly-only flags:
109 unstable-options = Allow use of experimental features",
115 "Show execution time of each test. Available values:
116 plain = do not colorize the execution time (default);
117 colored = colorize output according to the `color` parameter value;
119 Threshold values for colorized output can be configured via
120 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
121 `RUST_TEST_TIME_DOCTEST` environment variables.
123 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
124 Durations must be specified in milliseconds, e.g. `500,2000` means that the warn time
125 is 0.5 seconds, and the critical time is 2 seconds.
127 Not available for --format=terse",
133 "Treat excess of the test execution time limit as error.
135 Threshold values for this option can be configured via
136 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
137 `RUST_TEST_TIME_DOCTEST` environment variables.
139 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
141 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
144 .optflag("", "shuffle", "Run tests in random order")
148 "Run tests in random order; seed the random number generator with SEED",
154 fn usage(binary: &str, options: &getopts::Options) {
155 let message = format!("Usage: {} [OPTIONS] [FILTERS...]", binary);
159 The FILTER string is tested against the name of all tests, and only those
160 tests whose names contain the filter are run. Multiple filter strings may
161 be passed, which will run all tests matching any of the filters.
163 By default, all tests are run in parallel. This can be altered with the
164 --test-threads flag or the RUST_TEST_THREADS environment variable when running
167 By default, the tests are run in alphabetical order. Use --shuffle or set
168 RUST_TEST_SHUFFLE to run the tests in random order. Pass the generated
169 "shuffle seed" to --shuffle-seed (or set RUST_TEST_SHUFFLE_SEED) to run the
170 tests in the same order again. Note that --shuffle and --shuffle-seed do not
171 affect whether the tests are run in parallel.
173 All tests have their standard output and standard error captured by default.
174 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
175 environment variable to a value other than "0". Logging is not captured by default.
179 `#[test]` - Indicates a function is a test to be run. This function
181 `#[bench]` - Indicates a function is a benchmark to be run. This
182 function takes one argument (test::Bencher).
183 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
184 the code causes a panic (an assertion failure or panic!)
185 A message may be provided, which the failure string must
186 contain: #[should_panic(expected = "foo")].
187 `#[ignore]` - When applied to a function which is already attributed as a
188 test, then the test runner will ignore these tests during
189 normal test runs. Running with --ignored or --include-ignored will run
191 usage = options.usage(&message)
195 /// Parses command line arguments into test options.
196 /// Returns `None` if help was requested (since we only show help message and don't run tests),
197 /// returns `Some(Err(..))` if provided arguments are incorrect,
198 /// otherwise creates a `TestOpts` object and returns it.
199 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
201 let opts = optgroups();
202 let args = args.get(1..).unwrap_or(args);
203 let matches = match opts.parse(args) {
205 Err(f) => return Some(Err(f.to_string())),
208 // Check if help was requested.
209 if matches.opt_present("h") {
210 // Show help and do nothing more.
211 usage(&args[0], &opts);
215 // Actually parse the opts.
216 let opts_result = parse_opts_impl(matches);
221 // Gets the option value and checks if unstable features are enabled.
222 macro_rules! unstable_optflag {
223 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
224 let opt = $matches.opt_present($option_name);
225 if !$allow_unstable && opt {
227 "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options",
236 // Gets the option value and checks if unstable features are enabled.
237 macro_rules! unstable_optopt {
238 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
239 let opt = $matches.opt_str($option_name);
240 if !$allow_unstable && opt.is_some() {
242 "The \"{}\" option is only accepted on the nightly compiler with -Z unstable-options",
251 // Implementation of `parse_opts` that doesn't care about help message
252 // and returns a `Result`.
253 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
254 let allow_unstable = get_allow_unstable(&matches)?;
257 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
258 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
259 let time_options = get_time_options(&matches, allow_unstable)?;
260 let shuffle = get_shuffle(&matches, allow_unstable)?;
261 let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?;
263 let include_ignored = matches.opt_present("include-ignored");
264 let quiet = matches.opt_present("quiet");
265 let exact = matches.opt_present("exact");
266 let list = matches.opt_present("list");
267 let skip = matches.opt_strs("skip");
269 let bench_benchmarks = matches.opt_present("bench");
270 let run_tests = !bench_benchmarks || matches.opt_present("test");
272 let logfile = get_log_file(&matches)?;
273 let run_ignored = get_run_ignored(&matches, include_ignored)?;
274 let filters = matches.free.clone();
275 let nocapture = get_nocapture(&matches)?;
276 let test_threads = get_test_threads(&matches)?;
277 let color = get_color_config(&matches)?;
278 let format = get_format(&matches, quiet, allow_unstable)?;
280 let options = Options::new().display_output(matches.opt_present("show-output"));
282 let test_opts = TestOpts {
286 force_run_in_process,
287 exclude_should_panic,
306 // FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
307 fn is_nightly() -> bool {
308 // Whether this is a feature-staged build, i.e., on the beta or stable channel
309 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
310 // Whether we should enable unstable features for bootstrapping
311 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
313 bootstrap || !disable_unstable_features
316 // Gets the CLI options associated with `report-time` feature.
318 matches: &getopts::Matches,
319 allow_unstable: bool,
320 ) -> OptPartRes<Option<TestTimeOptions>> {
321 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
322 let colored_opt_str = matches.opt_str("report-time");
323 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
324 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
326 // If `ensure-test-time` option is provided, time output is enforced,
327 // so user won't be confused if any of tests will silently fail.
328 let options = if report_time || ensure_test_time {
329 if ensure_test_time && !report_time {
330 report_time_colored = true;
332 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
340 fn get_shuffle(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<bool> {
341 let mut shuffle = unstable_optflag!(matches, allow_unstable, "shuffle");
342 if !shuffle && allow_unstable {
343 shuffle = match env::var("RUST_TEST_SHUFFLE") {
344 Ok(val) => &val != "0",
352 fn get_shuffle_seed(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<Option<u64>> {
353 let mut shuffle_seed = match unstable_optopt!(matches, allow_unstable, "shuffle-seed") {
354 Some(n_str) => match n_str.parse::<u64>() {
358 "argument for --shuffle-seed must be a number \
367 if shuffle_seed.is_none() && allow_unstable {
368 shuffle_seed = match env::var("RUST_TEST_SHUFFLE_SEED") {
369 Ok(val) => match val.parse::<u64>() {
371 Err(_) => panic!("RUST_TEST_SHUFFLE_SEED is `{}`, should be a number.", val),
380 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
381 let test_threads = match matches.opt_str("test-threads") {
382 Some(n_str) => match n_str.parse::<usize>() {
383 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
387 "argument for --test-threads must be a number > 0 \
400 matches: &getopts::Matches,
402 allow_unstable: bool,
403 ) -> OptPartRes<OutputFormat> {
404 let format = match matches.opt_str("format").as_deref() {
405 None if quiet => OutputFormat::Terse,
406 Some("pretty") | None => OutputFormat::Pretty,
407 Some("terse") => OutputFormat::Terse,
410 return Err("The \"json\" format is only accepted on the nightly compiler".into());
416 return Err("The \"junit\" format is only accepted on the nightly compiler".into());
422 "argument for --format must be pretty, terse, json or junit (was \
432 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
433 let color = match matches.opt_str("color").as_deref() {
434 Some("auto") | None => ColorConfig::AutoColor,
435 Some("always") => ColorConfig::AlwaysColor,
436 Some("never") => ColorConfig::NeverColor,
440 "argument for --color must be auto, always, or never (was \
450 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
451 let mut nocapture = matches.opt_present("nocapture");
453 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
454 Ok(val) => &val != "0",
462 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
463 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
465 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
467 (true, false) => RunIgnored::Yes,
468 (false, true) => RunIgnored::Only,
469 (false, false) => RunIgnored::No,
475 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
476 let mut allow_unstable = false;
478 if let Some(opt) = matches.opt_str("Z") {
480 return Err("the option `Z` is only accepted on the nightly compiler".into());
484 "unstable-options" => {
485 allow_unstable = true;
488 return Err("Unrecognized option to `Z`".into());
496 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
497 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));