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 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("", "exclude-should-panic", "Excludes tests marked as should_panic")
50 .optflag("", "test", "Run tests and not benchmarks")
51 .optflag("", "bench", "Run benchmarks instead of tests")
52 .optflag("", "list", "List all tests and benchmarks")
53 .optflag("h", "help", "Display this message (longer with --help)")
57 "Write logs to the specified file instead \
64 "don't capture stdout/stderr of each \
65 task, allow printing directly",
70 "Number of threads used for running tests \
77 "Skip tests whose names contain FILTER (this flag can \
78 be used multiple times)",
84 "Display one character per test instead of one line. \
85 Alias to --format=terse",
90 "Exactly match filters rather than by substring",
95 "Configure coloring of output:
96 auto = colorize if stdout is a tty and tests are run on serially (default);
97 always = always colorize output;
98 never = never colorize output;",
104 "Configure formatting of output:
105 pretty = Print verbose output;
106 terse = Display one character per test;
107 json = Output a json document",
113 "Show captured stdout of successful tests"
118 "Enable nightly-only flags:
119 unstable-options = Allow use of experimental features",
125 "Show execution time of each test. Awailable values:
126 plain = do not colorize the execution time (default);
127 colored = colorize output according to the `color` parameter value;
129 Threshold values for colorized output can be configured via
130 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
131 `RUST_TEST_TIME_DOCTEST` environment variables.
133 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
135 Not available for --format=terse",
141 "Treat excess of the test execution time limit as error.
143 Threshold values for this option can be configured via
144 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
145 `RUST_TEST_TIME_DOCTEST` environment variables.
147 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
149 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
155 fn usage(binary: &str, options: &getopts::Options) {
156 let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
160 The FILTER string is tested against the name of all tests, and only those
161 tests whose names contain the filter are run.
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 All tests have their standard output and standard error captured by default.
168 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
169 environment variable to a value other than "0". Logging is not captured by default.
173 `#[test]` - Indicates a function is a test to be run. This function
175 `#[bench]` - Indicates a function is a benchmark to be run. This
176 function takes one argument (test::Bencher).
177 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
178 the code causes a panic (an assertion failure or panic!)
179 A message may be provided, which the failure string must
180 contain: #[should_panic(expected = "foo")].
181 `#[ignore]` - When applied to a function which is already attributed as a
182 test, then the test runner will ignore these tests during
183 normal test runs. Running with --ignored or --include-ignored will run
185 usage = options.usage(&message)
189 /// Parses command line arguments into test options.
190 /// Returns `None` if help was requested (since we only show help message and don't run tests),
191 /// returns `Some(Err(..))` if provided arguments are incorrect,
192 /// otherwise creates a `TestOpts` object and returns it.
193 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
195 let opts = optgroups();
196 let args = args.get(1..).unwrap_or(args);
197 let matches = match opts.parse(args) {
199 Err(f) => return Some(Err(f.to_string())),
202 // Check if help was requested.
203 if matches.opt_present("h") {
204 // Show help and do nothing more.
205 usage(&args[0], &opts);
209 // Actually parse the opts.
210 let opts_result = parse_opts_impl(matches);
215 // Gets the option value and checks if unstable features are enabled.
216 macro_rules! unstable_optflag {
217 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
218 let opt = $matches.opt_present($option_name);
219 if !$allow_unstable && opt {
221 "The \"{}\" flag is only accepted on the nightly compiler",
230 // Implementation of `parse_opts` that doesn't care about help message
231 // and returns a `Result`.
232 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
233 let allow_unstable = get_allow_unstable(&matches)?;
236 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
237 let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
238 let time_options = get_time_options(&matches, allow_unstable)?;
240 let quiet = matches.opt_present("quiet");
241 let exact = matches.opt_present("exact");
242 let list = matches.opt_present("list");
243 let skip = matches.opt_strs("skip");
245 let bench_benchmarks = matches.opt_present("bench");
246 let run_tests = !bench_benchmarks || matches.opt_present("test");
248 let logfile = get_log_file(&matches)?;
249 let run_ignored = get_run_ignored(&matches, include_ignored)?;
250 let filter = get_filter(&matches)?;
251 let nocapture = get_nocapture(&matches)?;
252 let test_threads = get_test_threads(&matches)?;
253 let color = get_color_config(&matches)?;
254 let format = get_format(&matches, quiet, allow_unstable)?;
256 let options = Options::new().display_output(matches.opt_present("show-output"));
258 let test_opts = TestOpts {
262 exclude_should_panic,
279 // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
280 fn is_nightly() -> bool {
281 // Whether this is a feature-staged build, i.e., on the beta or stable channel
282 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
283 // Whether we should enable unstable features for bootstrapping
284 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
286 bootstrap || !disable_unstable_features
289 // Gets the CLI options assotiated with `report-time` feature.
291 matches: &getopts::Matches,
292 allow_unstable: bool)
293 -> OptPartRes<Option<TestTimeOptions>> {
294 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
295 let colored_opt_str = matches.opt_str("report-time");
296 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
297 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
299 // If `ensure-test-time` option is provided, time output is enforced,
300 // so user won't be confused if any of tests will silently fail.
301 let options = if report_time || ensure_test_time {
302 if ensure_test_time && !report_time {
303 report_time_colored = true;
305 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
313 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
314 let test_threads = match matches.opt_str("test-threads") {
315 Some(n_str) => match n_str.parse::<usize>() {
316 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
320 "argument for --test-threads must be a number > 0 \
332 fn get_format(matches: &getopts::Matches, quiet: bool, allow_unstable: bool) -> OptPartRes<OutputFormat> {
333 let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
334 None if quiet => OutputFormat::Terse,
335 Some("pretty") | None => OutputFormat::Pretty,
336 Some("terse") => OutputFormat::Terse,
340 "The \"json\" format is only accepted on the nightly compiler".into(),
348 "argument for --format must be pretty, terse, or json (was \
358 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
359 let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
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")) {
392 "the options --include-ignored and --ignored are mutually exclusive".into(),
395 (true, false) => RunIgnored::Yes,
396 (false, true) => RunIgnored::Only,
397 (false, false) => RunIgnored::No,
403 fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
404 let filter = if !matches.free.is_empty() {
405 Some(matches.free[0].clone())
413 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
414 let mut allow_unstable = false;
416 if let Some(opt) = matches.opt_str("Z") {
419 "the option `Z` is only accepted on the nightly compiler".into(),
424 "unstable-options" => {
425 allow_unstable = true;
428 return Err("Unrecognized option to `Z`".into());
436 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
437 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));