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.
117 Threshold values for colorized output can be configured via
118 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
119 `RUST_TEST_TIME_DOCTEST` environment variables.
121 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
122 Durations must be specified in milliseconds, e.g. `500,2000` means that the warn time
123 is 0.5 seconds, and the critical time is 2 seconds.
125 Not available for --format=terse",
130 "Treat excess of the test execution time limit as error.
132 Threshold values for this option can be configured via
133 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
134 `RUST_TEST_TIME_DOCTEST` environment variables.
136 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
138 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
141 .optflag("", "shuffle", "Run tests in random order")
145 "Run tests in random order; seed the random number generator with SEED",
151 fn usage(binary: &str, options: &getopts::Options) {
152 let message = format!("Usage: {binary} [OPTIONS] [FILTERS...]");
156 The FILTER string is tested against the name of all tests, and only those
157 tests whose names contain the filter are run. Multiple filter strings may
158 be passed, which will run all tests matching any of the filters.
160 By default, all tests are run in parallel. This can be altered with the
161 --test-threads flag or the RUST_TEST_THREADS environment variable when running
164 By default, the tests are run in alphabetical order. Use --shuffle or set
165 RUST_TEST_SHUFFLE to run the tests in random order. Pass the generated
166 "shuffle seed" to --shuffle-seed (or set RUST_TEST_SHUFFLE_SEED) to run the
167 tests in the same order again. Note that --shuffle and --shuffle-seed do not
168 affect whether the tests are run in parallel.
170 All tests have their standard output and standard error captured by default.
171 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
172 environment variable to a value other than "0". Logging is not captured by default.
176 `#[test]` - Indicates a function is a test to be run. This function
178 `#[bench]` - Indicates a function is a benchmark to be run. This
179 function takes one argument (test::Bencher).
180 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
181 the code causes a panic (an assertion failure or panic!)
182 A message may be provided, which the failure string must
183 contain: #[should_panic(expected = "foo")].
184 `#[ignore]` - When applied to a function which is already attributed as a
185 test, then the test runner will ignore these tests during
186 normal test runs. Running with --ignored or --include-ignored will run
188 usage = options.usage(&message)
192 /// Parses command line arguments into test options.
193 /// Returns `None` if help was requested (since we only show help message and don't run tests),
194 /// returns `Some(Err(..))` if provided arguments are incorrect,
195 /// otherwise creates a `TestOpts` object and returns it.
196 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
198 let opts = optgroups();
199 let args = args.get(1..).unwrap_or(args);
200 let matches = match opts.parse(args) {
202 Err(f) => return Some(Err(f.to_string())),
205 // Check if help was requested.
206 if matches.opt_present("h") {
207 // Show help and do nothing more.
208 usage(&args[0], &opts);
212 // Actually parse the opts.
213 let opts_result = parse_opts_impl(matches);
218 // Gets the option value and checks if unstable features are enabled.
219 macro_rules! unstable_optflag {
220 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
221 let opt = $matches.opt_present($option_name);
222 if !$allow_unstable && opt {
224 "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options",
233 // Gets the option value and checks if unstable features are enabled.
234 macro_rules! unstable_optopt {
235 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
236 let opt = $matches.opt_str($option_name);
237 if !$allow_unstable && opt.is_some() {
239 "The \"{}\" option is only accepted on the nightly compiler with -Z unstable-options",
248 // Implementation of `parse_opts` that doesn't care about help message
249 // and returns a `Result`.
250 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
251 let allow_unstable = get_allow_unstable(&matches)?;
254 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
255 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
256 let time_options = get_time_options(&matches, allow_unstable)?;
257 let shuffle = get_shuffle(&matches, allow_unstable)?;
258 let shuffle_seed = get_shuffle_seed(&matches, allow_unstable)?;
260 let include_ignored = matches.opt_present("include-ignored");
261 let quiet = matches.opt_present("quiet");
262 let exact = matches.opt_present("exact");
263 let list = matches.opt_present("list");
264 let skip = matches.opt_strs("skip");
266 let bench_benchmarks = matches.opt_present("bench");
267 let run_tests = !bench_benchmarks || matches.opt_present("test");
269 let logfile = get_log_file(&matches)?;
270 let run_ignored = get_run_ignored(&matches, include_ignored)?;
271 let filters = matches.free.clone();
272 let nocapture = get_nocapture(&matches)?;
273 let test_threads = get_test_threads(&matches)?;
274 let color = get_color_config(&matches)?;
275 let format = get_format(&matches, quiet, allow_unstable)?;
277 let options = Options::new().display_output(matches.opt_present("show-output"));
279 let test_opts = TestOpts {
283 force_run_in_process,
284 exclude_should_panic,
303 // FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
304 fn is_nightly() -> bool {
305 // Whether this is a feature-staged build, i.e., on the beta or stable channel
306 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
307 // Whether we should enable unstable features for bootstrapping
308 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
310 bootstrap || !disable_unstable_features
313 // Gets the CLI options associated with `report-time` feature.
315 matches: &getopts::Matches,
316 allow_unstable: bool,
317 ) -> OptPartRes<Option<TestTimeOptions>> {
318 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
319 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
321 // If `ensure-test-time` option is provided, time output is enforced,
322 // so user won't be confused if any of tests will silently fail.
323 let options = if report_time || ensure_test_time {
324 Some(TestTimeOptions::new_from_env(ensure_test_time))
332 fn get_shuffle(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<bool> {
333 let mut shuffle = unstable_optflag!(matches, allow_unstable, "shuffle");
334 if !shuffle && allow_unstable {
335 shuffle = match env::var("RUST_TEST_SHUFFLE") {
336 Ok(val) => &val != "0",
344 fn get_shuffle_seed(matches: &getopts::Matches, allow_unstable: bool) -> OptPartRes<Option<u64>> {
345 let mut shuffle_seed = match unstable_optopt!(matches, allow_unstable, "shuffle-seed") {
346 Some(n_str) => match n_str.parse::<u64>() {
350 "argument for --shuffle-seed must be a number \
359 if shuffle_seed.is_none() && allow_unstable {
360 shuffle_seed = match env::var("RUST_TEST_SHUFFLE_SEED") {
361 Ok(val) => match val.parse::<u64>() {
363 Err(_) => panic!("RUST_TEST_SHUFFLE_SEED is `{val}`, should be a number."),
372 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
373 let test_threads = match matches.opt_str("test-threads") {
374 Some(n_str) => match n_str.parse::<usize>() {
375 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
379 "argument for --test-threads must be a number > 0 \
392 matches: &getopts::Matches,
394 allow_unstable: bool,
395 ) -> OptPartRes<OutputFormat> {
396 let format = match matches.opt_str("format").as_deref() {
397 None if quiet => OutputFormat::Terse,
398 Some("pretty") | None => OutputFormat::Pretty,
399 Some("terse") => OutputFormat::Terse,
402 return Err("The \"json\" format is only accepted on the nightly compiler".into());
408 return Err("The \"junit\" format is only accepted on the nightly compiler".into());
414 "argument for --format must be pretty, terse, json or junit (was \
424 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
425 let color = match matches.opt_str("color").as_deref() {
426 Some("auto") | None => ColorConfig::AutoColor,
427 Some("always") => ColorConfig::AlwaysColor,
428 Some("never") => ColorConfig::NeverColor,
432 "argument for --color must be auto, always, or never (was \
442 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
443 let mut nocapture = matches.opt_present("nocapture");
445 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
446 Ok(val) => &val != "0",
454 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
455 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
457 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
459 (true, false) => RunIgnored::Yes,
460 (false, true) => RunIgnored::Only,
461 (false, false) => RunIgnored::No,
467 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
468 let mut allow_unstable = false;
470 if let Some(opt) = matches.opt_str("Z") {
472 return Err("the option `Z` is only accepted on the nightly compiler".into());
476 "unstable-options" => {
477 allow_unstable = true;
480 return Err("Unrecognized option to `Z`".into());
488 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
489 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));