1 //! Module converting command-line arguments into test configuration.
4 use std::path::PathBuf;
7 use super::options::{RunIgnored, ColorConfig, OutputFormat, Options};
8 use super::time::TestTimeOptions;
9 use super::helpers::isatty;
14 pub filter: Option<String>,
15 pub filter_exact: bool,
16 pub force_run_in_process: bool,
17 pub exclude_should_panic: bool,
18 pub run_ignored: RunIgnored,
20 pub bench_benchmarks: bool,
21 pub logfile: Option<PathBuf>,
23 pub color: ColorConfig,
24 pub format: OutputFormat,
25 pub test_threads: Option<usize>,
26 pub skip: Vec<String>,
27 pub time_options: Option<TestTimeOptions>,
32 pub fn use_color(&self) -> bool {
34 ColorConfig::AutoColor => !self.nocapture && isatty::stdout_isatty(),
35 ColorConfig::AlwaysColor => true,
36 ColorConfig::NeverColor => false,
41 /// Result of parsing the options.
42 pub type OptRes = Result<TestOpts, String>;
43 /// Result of parsing the option part.
44 type OptPartRes<T> = Result<T, String>;
46 fn optgroups() -> getopts::Options {
47 let mut opts = getopts::Options::new();
48 opts.optflag("", "include-ignored", "Run ignored and not ignored tests")
49 .optflag("", "ignored", "Run only ignored tests")
50 .optflag("", "force-run-in-process", "Forces tests to run in-process when panic=abort")
51 .optflag("", "exclude-should-panic", "Excludes tests marked as should_panic")
52 .optflag("", "test", "Run tests and not benchmarks")
53 .optflag("", "bench", "Run benchmarks instead of tests")
54 .optflag("", "list", "List all tests and benchmarks")
55 .optflag("h", "help", "Display this message (longer with --help)")
59 "Write logs to the specified file instead \
66 "don't capture stdout/stderr of each \
67 task, allow printing directly",
72 "Number of threads used for running tests \
79 "Skip tests whose names contain FILTER (this flag can \
80 be used multiple times)",
86 "Display one character per test instead of one line. \
87 Alias to --format=terse",
92 "Exactly match filters rather than by substring",
97 "Configure coloring of output:
98 auto = colorize if stdout is a tty and tests are run on serially (default);
99 always = always colorize output;
100 never = never colorize output;",
106 "Configure formatting of output:
107 pretty = Print verbose output;
108 terse = Display one character per test;
109 json = Output a json document",
115 "Show captured stdout of successful tests"
120 "Enable nightly-only flags:
121 unstable-options = Allow use of experimental features",
127 "Show execution time of each test. Awailable values:
128 plain = do not colorize the execution time (default);
129 colored = colorize output according to the `color` parameter value;
131 Threshold values for colorized output can be configured via
132 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
133 `RUST_TEST_TIME_DOCTEST` environment variables.
135 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
137 Not available for --format=terse",
143 "Treat excess of the test execution time limit as error.
145 Threshold values for this option can be configured via
146 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
147 `RUST_TEST_TIME_DOCTEST` environment variables.
149 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
151 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
157 fn usage(binary: &str, options: &getopts::Options) {
158 let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
162 The FILTER string is tested against the name of all tests, and only those
163 tests whose names contain the filter are run.
165 By default, all tests are run in parallel. This can be altered with the
166 --test-threads flag or the RUST_TEST_THREADS environment variable when running
169 All tests have their standard output and standard error captured by default.
170 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
171 environment variable to a value other than "0". Logging is not captured by default.
175 `#[test]` - Indicates a function is a test to be run. This function
177 `#[bench]` - Indicates a function is a benchmark to be run. This
178 function takes one argument (test::Bencher).
179 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
180 the code causes a panic (an assertion failure or panic!)
181 A message may be provided, which the failure string must
182 contain: #[should_panic(expected = "foo")].
183 `#[ignore]` - When applied to a function which is already attributed as a
184 test, then the test runner will ignore these tests during
185 normal test runs. Running with --ignored or --include-ignored will run
187 usage = options.usage(&message)
191 /// Parses command line arguments into test options.
192 /// Returns `None` if help was requested (since we only show help message and don't run tests),
193 /// returns `Some(Err(..))` if provided arguments are incorrect,
194 /// otherwise creates a `TestOpts` object and returns it.
195 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
197 let opts = optgroups();
198 let args = args.get(1..).unwrap_or(args);
199 let matches = match opts.parse(args) {
201 Err(f) => return Some(Err(f.to_string())),
204 // Check if help was requested.
205 if matches.opt_present("h") {
206 // Show help and do nothing more.
207 usage(&args[0], &opts);
211 // Actually parse the opts.
212 let opts_result = parse_opts_impl(matches);
217 // Gets the option value and checks if unstable features are enabled.
218 macro_rules! unstable_optflag {
219 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
220 let opt = $matches.opt_present($option_name);
221 if !$allow_unstable && opt {
223 "The \"{}\" flag is only accepted on the nightly compiler",
232 // Implementation of `parse_opts` that doesn't care about help message
233 // and returns a `Result`.
234 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
235 let allow_unstable = get_allow_unstable(&matches)?;
238 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
239 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
240 let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
241 let time_options = get_time_options(&matches, allow_unstable)?;
243 let quiet = matches.opt_present("quiet");
244 let exact = matches.opt_present("exact");
245 let list = matches.opt_present("list");
246 let skip = matches.opt_strs("skip");
248 let bench_benchmarks = matches.opt_present("bench");
249 let run_tests = !bench_benchmarks || matches.opt_present("test");
251 let logfile = get_log_file(&matches)?;
252 let run_ignored = get_run_ignored(&matches, include_ignored)?;
253 let filter = get_filter(&matches)?;
254 let nocapture = get_nocapture(&matches)?;
255 let test_threads = get_test_threads(&matches)?;
256 let color = get_color_config(&matches)?;
257 let format = get_format(&matches, quiet, allow_unstable)?;
259 let options = Options::new().display_output(matches.opt_present("show-output"));
261 let test_opts = TestOpts {
265 force_run_in_process,
266 exclude_should_panic,
283 // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
284 fn is_nightly() -> bool {
285 // Whether this is a feature-staged build, i.e., on the beta or stable channel
286 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
287 // Whether we should enable unstable features for bootstrapping
288 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
290 bootstrap || !disable_unstable_features
293 // Gets the CLI options assotiated with `report-time` feature.
295 matches: &getopts::Matches,
296 allow_unstable: bool)
297 -> OptPartRes<Option<TestTimeOptions>> {
298 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
299 let colored_opt_str = matches.opt_str("report-time");
300 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
301 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
303 // If `ensure-test-time` option is provided, time output is enforced,
304 // so user won't be confused if any of tests will silently fail.
305 let options = if report_time || ensure_test_time {
306 if ensure_test_time && !report_time {
307 report_time_colored = true;
309 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
317 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
318 let test_threads = match matches.opt_str("test-threads") {
319 Some(n_str) => match n_str.parse::<usize>() {
320 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
324 "argument for --test-threads must be a number > 0 \
337 matches: &getopts::Matches,
340 ) -> OptPartRes<OutputFormat> {
341 let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
342 None if quiet => OutputFormat::Terse,
343 Some("pretty") | None => OutputFormat::Pretty,
344 Some("terse") => OutputFormat::Terse,
348 "The \"json\" format is only accepted on the nightly compiler".into(),
356 "argument for --format must be pretty, terse, or json (was \
366 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
367 let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
368 Some("auto") | None => ColorConfig::AutoColor,
369 Some("always") => ColorConfig::AlwaysColor,
370 Some("never") => ColorConfig::NeverColor,
374 "argument for --color must be auto, always, or never (was \
384 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
385 let mut nocapture = matches.opt_present("nocapture");
387 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
388 Ok(val) => &val != "0",
396 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
397 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
400 "the options --include-ignored and --ignored are mutually exclusive".into(),
403 (true, false) => RunIgnored::Yes,
404 (false, true) => RunIgnored::Only,
405 (false, false) => RunIgnored::No,
411 fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
412 let filter = if !matches.free.is_empty() {
413 Some(matches.free[0].clone())
421 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
422 let mut allow_unstable = false;
424 if let Some(opt) = matches.opt_str("Z") {
427 "the option `Z` is only accepted on the nightly compiler".into(),
432 "unstable-options" => {
433 allow_unstable = true;
436 return Err("Unrecognized option to `Z`".into());
444 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
445 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));