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<Option<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 // FIXME: Copied from libsyntax until linkage errors are resolved. Issue #47566
190 fn is_nightly() -> bool {
191 // Whether this is a feature-staged build, i.e., on the beta or stable channel
192 let disable_unstable_features = option_env!("CFG_DISABLE_UNSTABLE_FEATURES").is_some();
193 // Whether we should enable unstable features for bootstrapping
194 let bootstrap = env::var("RUSTC_BOOTSTRAP").is_ok();
196 bootstrap || !disable_unstable_features
199 // Gets the option value and checks if unstable features are enabled.
200 macro_rules! unstable_optflag {
201 ($matches:ident, $allow_unstable:ident, $option_name:literal) => {{
202 let opt = $matches.opt_present($option_name);
203 if !$allow_unstable && opt {
204 return Some(Err(format!(
205 "The \"{}\" flag is only accepted on the nightly compiler",
214 // Gets the CLI options assotiated with `report-time` feature.
216 matches: &getopts::Matches,
217 allow_unstable: bool)
218 -> Option<OptPartRes<TestTimeOptions>> {
219 let report_time = unstable_optflag!(matches, allow_unstable, "report-time");
220 let colored_opt_str = matches.opt_str("report-time");
221 let mut report_time_colored = report_time && colored_opt_str == Some("colored".into());
222 let ensure_test_time = unstable_optflag!(matches, allow_unstable, "ensure-time");
224 // If `ensure-test-time` option is provided, time output is enforced,
225 // so user won't be confused if any of tests will silently fail.
226 let options = if report_time || ensure_test_time {
227 if ensure_test_time && !report_time {
228 report_time_colored = true;
230 Some(TestTimeOptions::new_from_env(ensure_test_time, report_time_colored))
238 // Parses command line arguments into test options
239 pub fn parse_opts(args: &[String]) -> Option<OptRes> {
240 let mut allow_unstable = false;
241 let opts = optgroups();
242 let args = args.get(1..).unwrap_or(args);
243 let matches = match opts.parse(args) {
245 Err(f) => return Some(Err(f.to_string())),
248 if let Some(opt) = matches.opt_str("Z") {
251 "the option `Z` is only accepted on the nightly compiler".into(),
256 "unstable-options" => {
257 allow_unstable = true;
260 return Some(Err("Unrecognized option to `Z`".into()));
265 if matches.opt_present("h") {
266 usage(&args[0], &opts);
270 let filter = if !matches.free.is_empty() {
271 Some(matches.free[0].clone())
276 let exclude_should_panic = unstable_optflag!(matches, allow_unstable, "exclude-should-panic");
278 let include_ignored = unstable_optflag!(matches, allow_unstable, "include-ignored");
280 let run_ignored = match (include_ignored, matches.opt_present("ignored")) {
283 "the options --include-ignored and --ignored are mutually exclusive".into(),
286 (true, false) => RunIgnored::Yes,
287 (false, true) => RunIgnored::Only,
288 (false, false) => RunIgnored::No,
290 let quiet = matches.opt_present("quiet");
291 let exact = matches.opt_present("exact");
292 let list = matches.opt_present("list");
294 let logfile = matches.opt_str("logfile");
295 let logfile = logfile.map(|s| PathBuf::from(&s));
297 let bench_benchmarks = matches.opt_present("bench");
298 let run_tests = !bench_benchmarks || matches.opt_present("test");
300 let mut nocapture = matches.opt_present("nocapture");
302 nocapture = match env::var("RUST_TEST_NOCAPTURE") {
303 Ok(val) => &val != "0",
308 let time_options = match get_time_options(&matches, allow_unstable) {
309 Some(Ok(val)) => val,
310 Some(Err(e)) => return Some(Err(e)),
311 None => panic!("Unexpected output from `get_time_options`"),
314 let test_threads = match matches.opt_str("test-threads") {
315 Some(n_str) => match n_str.parse::<usize>() {
316 Ok(0) => return Some(Err("argument for --test-threads must not be 0".to_string())),
319 return Some(Err(format!(
320 "argument for --test-threads must be a number > 0 \
329 let color = match matches.opt_str("color").as_ref().map(|s| &**s) {
330 Some("auto") | None => ColorConfig::AutoColor,
331 Some("always") => ColorConfig::AlwaysColor,
332 Some("never") => ColorConfig::NeverColor,
335 return Some(Err(format!(
336 "argument for --color must be auto, always, or never (was \
343 let format = match matches.opt_str("format").as_ref().map(|s| &**s) {
344 None if quiet => OutputFormat::Terse,
345 Some("pretty") | None => OutputFormat::Pretty,
346 Some("terse") => OutputFormat::Terse,
350 "The \"json\" format is only accepted on the nightly compiler".into(),
357 return Some(Err(format!(
358 "argument for --format must be pretty, terse, or json (was \
365 let test_opts = TestOpts {
369 exclude_should_panic,
378 skip: matches.opt_strs("skip"),
380 options: Options::new().display_output(matches.opt_present("show-output")),