1 //! Module converting command-line arguments into test configuration.
5 use std::path::PathBuf;
7 use super::helpers::isatty;
8 use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
9 use super::time::TestTimeOptions;
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",
89 .optflag("", "exact", "Exactly match filters rather than by substring")
93 "Configure coloring of output:
94 auto = colorize if stdout is a tty and tests are run on serially (default);
95 always = always colorize output;
96 never = never colorize output;",
102 "Configure formatting of output:
103 pretty = Print verbose output;
104 terse = Display one character per test;
105 json = Output a json document",
108 .optflag("", "show-output", "Show captured stdout of successful tests")
112 "Enable nightly-only flags:
113 unstable-options = Allow use of experimental features",
119 "Show execution time of each test. Awailable values:
120 plain = do not colorize the execution time (default);
121 colored = colorize output according to the `color` parameter value;
123 Threshold values for colorized output can be configured via
124 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
125 `RUST_TEST_TIME_DOCTEST` environment variables.
127 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
129 Not available for --format=terse",
135 "Treat excess of the test execution time limit as error.
137 Threshold values for this option can be configured via
138 `RUST_TEST_TIME_UNIT`, `RUST_TEST_TIME_INTEGRATION` and
139 `RUST_TEST_TIME_DOCTEST` environment variables.
141 Expected format of environment variable is `VARIABLE=WARN_TIME,CRITICAL_TIME`.
143 `CRITICAL_TIME` here means the limit that should not be exceeded by test.
149 fn usage(binary: &str, options: &getopts::Options) {
150 let message = format!("Usage: {} [OPTIONS] [FILTER]", binary);
154 The FILTER string is tested against the name of all tests, and only those
155 tests whose names contain the filter are run.
157 By default, all tests are run in parallel. This can be altered with the
158 --test-threads flag or the RUST_TEST_THREADS environment variable when running
161 All tests have their standard output and standard error captured by default.
162 This can be overridden with the --nocapture flag or setting RUST_TEST_NOCAPTURE
163 environment variable to a value other than "0". Logging is not captured by default.
167 `#[test]` - Indicates a function is a test to be run. This function
169 `#[bench]` - Indicates a function is a benchmark to be run. This
170 function takes one argument (test::Bencher).
171 `#[should_panic]` - This function (also labeled with `#[test]`) will only pass if
172 the code causes a panic (an assertion failure or panic!)
173 A message may be provided, which the failure string must
174 contain: #[should_panic(expected = "foo")].
175 `#[ignore]` - When applied to a function which is already attributed as a
176 test, then the test runner will ignore these tests during
177 normal test runs. Running with --ignored or --include-ignored will run
179 usage = options.usage(&message)
183 /// Parses command line arguments into test options.
184 /// Returns `None` if help was requested (since we only show help message and don't run tests),
185 /// returns `Some(Err(..))` if provided arguments are incorrect,
186 /// otherwise creates a `TestOpts` object and returns it.
187 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
189 let opts = optgroups();
190 let args = args.get(1..).unwrap_or(args);
191 let matches = match opts.parse(args) {
193 Err(f) => return Some(Err(f.to_string())),
196 // Check if help was requested.
197 if matches.opt_present("h") {
198 // Show help and do nothing more.
199 usage(&args[0], &opts);
203 // Actually parse the opts.
204 let opts_result = parse_opts_impl(matches);
209 // Gets the option value and checks if unstable features are enabled.
210 macro_rules! unstable_optflag {
211 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
212 let opt = $matches.opt_present($option_name);
213 if !$allow_unstable && opt {
215 "The \"{}\" flag is only accepted on the nightly compiler",
224 // Implementation of `parse_opts` that doesn't care about help message
225 // and returns a `Result`.
226 fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
227 let allow_unstable = get_allow_unstable(&matches)?;
230 let force_run_in_process = unstable_optflag!(matches, allow_unstable, "force-run-in-process");
231 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
232 let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
233 let time_options = get_time_options(&matches, allow_unstable)?;
235 let quiet = matches.opt_present("quiet");
236 let exact = matches.opt_present("exact");
237 let list = matches.opt_present("list");
238 let skip = matches.opt_strs("skip");
240 let bench_benchmarks = matches.opt_present("bench");
241 let run_tests = !bench_benchmarks || matches.opt_present("test");
243 let logfile = get_log_file(&matches)?;
244 let run_ignored = get_run_ignored(&matches, include_ignored)?;
245 let filter = get_filter(&matches)?;
246 let nocapture = get_nocapture(&matches)?;
247 let test_threads = get_test_threads(&matches)?;
248 let color = get_color_config(&matches)?;
249 let format = get_format(&matches, quiet, allow_unstable)?;
251 let options = Options::new().display_output(matches.opt_present("show-output"));
253 let test_opts = TestOpts {
257 force_run_in_process,
258 exclude_should_panic,
275 // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
276 fn is_nightly() -> bool {
277 // Whether this is a feature-staged build, i.e., on the beta or stable channel
278 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
279 // Whether we should enable unstable features for bootstrapping
280 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
282 bootstrap || !disable_unstable_features
285 // Gets the CLI options assotiated with `report-time` feature.
287 matches: &getopts::Matches,
288 allow_unstable: bool,
289 ) -> OptPartRes<Option<TestTimeOptions>> {
290 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
291 let colored_opt_str = matches.opt_str("report-time");
292 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
293 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
295 // If `ensure-test-time` option is provided, time output is enforced,
296 // so user won't be confused if any of tests will silently fail.
297 let options = if report_time || ensure_test_time {
298 if ensure_test_time && !report_time {
299 report_time_colored = true;
301 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
309 fn get_test_threads(matches: &getopts::Matches) -> OptPartRes<Option<usize>> {
310 let test_threads = match matches.opt_str("test-threads") {
311 Some(n_str) => match n_str.parse::<usize>() {
312 Ok(0) => return Err("argument for --test-threads must not be 0".to_string()),
316 "argument for --test-threads must be a number > 0 \
329 matches: &getopts::Matches,
331 allow_unstable: bool,
332 ) -> 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,
339 return Err("The \"json\" format is only accepted on the nightly compiler".into());
346 "argument for --format must be pretty, terse, or json (was \
356 fn get_color_config(matches: &getopts::Matches) -> OptPartRes<ColorConfig> {
357 let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
358 Some("auto") | None => ColorConfig::AutoColor,
359 Some("always") => ColorConfig::AlwaysColor,
360 Some("never") => ColorConfig::NeverColor,
364 "argument for --color must be auto, always, or never (was \
374 fn get_nocapture(matches: &getopts::Matches) -> OptPartRes<bool> {
375 let mut nocapture = matches.opt_present("nocapture");
377 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
378 Ok(val) => &val != "0",
386 fn get_run_ignored(matches: &getopts::Matches, include_ignored: bool) -> OptPartRes<RunIgnored> {
387 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
389 return Err("the options --include-ignored and --ignored are mutually exclusive".into());
391 (true, false) => RunIgnored::Yes,
392 (false, true) => RunIgnored::Only,
393 (false, false) => RunIgnored::No,
399 fn get_filter(matches: &getopts::Matches) -> OptPartRes<Option<String>> {
400 let filter = if !matches.free.is_empty() { Some(matches.free[0].clone()) } else { None };
405 fn get_allow_unstable(matches: &getopts::Matches) -> OptPartRes<bool> {
406 let mut allow_unstable = false;
408 if let Some(opt) = matches.opt_str("Z") {
410 return Err("the option `Z` is only accepted on the nightly compiler".into());
414 "unstable-options" => {
415 allow_unstable = true;
418 return Err("Unrecognized option to `Z`".into());
426 fn get_log_file(matches: &getopts::Matches) -> OptPartRes<Option<PathBuf>> {
427 let logfile = matches.opt_str("logfile").map(|s| PathBuf::from(&s));