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,
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")
55 .optopt("", "logfile", "Write logs to the specified file", "PATH")
59 "don't capture stdout/stderr of each \
60 task, allow printing directly",
65 "Number of threads used for running tests \
72 "Skip tests whose names contain FILTER (this flag can \
73 be used multiple times)",
79 "Display one character per test instead of one line. \
80 Alias to --format=terse",
82 .optflag("", "exact", "Exactly match filters rather than by substring")
86 "Configure coloring of output:
87 auto = colorize if stdout is a tty and tests are run on serially (default);
88 always = always colorize output;
89 never = never colorize output;",
95 "Configure formatting of output:
96 pretty = Print verbose output;
97 terse = Display one character per test;
98 json = Output a json document;
99 junit = Output a JUnit document",
100 "pretty|terse|json|junit",
102 .optflag("", "show-output", "Show captured stdout of successful tests")
106 "Enable nightly-only flags:
107 unstable-options = Allow use of experimental features",
113 "Show execution time of each test. Available values:
114 plain = do not colorize the execution time (default);
115 colored = colorize output according to the `color` parameter value;
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",
131 "Treat excess of the test execution time limit as error.
133 Threshold values for this option can be configured via
134 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
135 `RUST_TEST_TIME_DOCTEST` environment variables.
137 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
139 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
145 fn usage(binary: &str, options: &getopts::Options) {
146 let message = format!("Usage: {} [OPTIONS] [FILTERS...]", binary);
150 The FILTER string is tested against the name of all tests, and only those
151 tests whose names contain the filter are run. Multiple filter strings may
152 be passed, which will run all tests matching any of the filters.
154 By default, all tests are run in parallel. This can be altered with the
155 --test-threads flag or the RUST_TEST_THREADS environment variable when running
158 All tests have their standard output and standard error captured by default.
159 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
160 environment variable to a value other than "0". Logging is not captured by default.
164 `#[test]` - Indicates a function is a test to be run. This function
166 `#[bench]` - Indicates a function is a benchmark to be run. This
167 function takes one argument (test::Bencher).
168 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
169 the code causes a panic (an assertion failure or panic!)
170 A message may be provided, which the failure string must
171 contain: #[should_panic(expected = "foo")].
172 `#[ignore]` - When applied to a function which is already attributed as a
173 test, then the test runner will ignore these tests during
174 normal test runs. Running with --ignored or --include-ignored will run
176 usage = options.usage(&message)
180 /// Parses command line arguments into test options.
181 /// Returns `None` if help was requested (since we only show help message and don't run tests),
182 /// returns `Some(Err(..))` if provided arguments are incorrect,
183 /// otherwise creates a `TestOpts` object and returns it.
184 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
186 let opts = optgroups();
187 let args = args.get(1..).unwrap_or(args);
188 let matches = match opts.parse(args) {
190 Err(f) => return Some(Err(f.to_string())),
193 // Check if help was requested.
194 if matches.opt_present("h") {
195 // Show help and do nothing more.
196 usage(&args[0], &opts);
200 // Actually parse the opts.
201 let opts_result = parse_opts_impl(matches);
206 // Gets the option value and checks if unstable features are enabled.
207 macro_rules! unstable_optflag {
208 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
209 let opt = $matches.opt_present($option_name);
210 if !$allow_unstable && opt {
212 "The \"{}\" flag is only accepted on the nightly compiler with -Z unstable-options",
221 // Implementation of `parse_opts` that doesn't care about help message
222 // and returns a `Result`.
223 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
224 let allow_unstable = get_allow_unstable(&matches)?;
227 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
228 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
229 let time_options = get_time_options(&matches, allow_unstable)?;
231 let include_ignored = matches.opt_present("include-ignored");
232 let quiet = matches.opt_present("quiet");
233 let exact = matches.opt_present("exact");
234 let list = matches.opt_present("list");
235 let skip = matches.opt_strs("skip");
237 let bench_benchmarks = matches.opt_present("bench");
238 let run_tests = !bench_benchmarks || matches.opt_present("test");
240 let logfile = get_log_file(&matches)?;
241 let run_ignored = get_run_ignored(&matches, include_ignored)?;
242 let filters = matches.free.clone();
243 let nocapture = get_nocapture(&matches)?;
244 let test_threads = get_test_threads(&matches)?;
245 let color = get_color_config(&matches)?;
246 let format = get_format(&matches, quiet, allow_unstable)?;
248 let options = Options::new().display_output(matches.opt_present("show-output"));
250 let test_opts = TestOpts {
254 force_run_in_process,
255 exclude_should_panic,
272 // FIXME: Copied from librustc_ast until linkage errors are resolved. Issue #47566
273 fn is_nightly() -> bool {
274 // Whether this is a feature-staged build, i.e., on the beta or stable channel
275 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
276 // Whether we should enable unstable features for bootstrapping
277 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
279 bootstrap || !disable_unstable_features
282 // Gets the CLI options associated with `report-time` feature.
284 matches: &getopts::Matches,
285 allow_unstable: bool,
286 ) -> OptPartRes<Option<TestTimeOptions>> {
287 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
288 let colored_opt_str = matches.opt_str("report-time");
289 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
290 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
292 // If `ensure-test-time` option is provided, time output is enforced,
293 // so user won't be confused if any of tests will silently fail.
294 let options = if report_time || ensure_test_time {
295 if ensure_test_time && !report_time {
296 report_time_colored = true;
298 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
306 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
307 let test_threads = match matches.opt_str("test-threads") {
308 Some(n_str) => match n_str.parse::<usize>() {
309 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
313 "argument for --test-threads must be a number > 0 \
326 matches: &getopts::Matches,
328 allow_unstable: bool,
329 ) -> OptPartRes<OutputFormat> {
330 let format = match matches.opt_str("format").as_deref() {
331 None if quiet => OutputFormat::Terse,
332 Some("pretty") | None => OutputFormat::Pretty,
333 Some("terse") => OutputFormat::Terse,
336 return Err("The \"json\" format is only accepted on the nightly compiler".into());
342 return Err("The \"junit\" format is only accepted on the nightly compiler".into());
348 "argument for --format must be pretty, terse, json or junit (was \
358 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
359 let color = match matches.opt_str("color").as_deref() {
360 Some("auto") | None => ColorConfig::AutoColor,
361 Some("always") => ColorConfig::AlwaysColor,
362 Some("never") => ColorConfig::NeverColor,
366 "argument for --color must be auto, always, or never (was \
376 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
377 let mut nocapture = matches.opt_present("nocapture");
379 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
380 Ok(val) => &val != "0",
388 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
389 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
391 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
393 (true, false) => RunIgnored::Yes,
394 (false, true) => RunIgnored::Only,
395 (false, false) => RunIgnored::No,
401 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
402 let mut allow_unstable = false;
404 if let Some(opt) = matches.opt_str("Z") {
406 return Err("the option `Z` is only accepted on the nightly compiler".into());
410 "unstable-options" => {
411 allow_unstable = true;
414 return Err("Unrecognized option to `Z`".into());
422 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
423 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));