Rollup merge of #139317 - Zalathar:hide-libtest, r=jieyouxu
compiletest: Encapsulate all of the code that touches libtest Compiletest currently relies on unstable libtest APIs in order to actually execute tests. That's unfortunate, but removing the dependency isn't trivial. However, we can make a small step towards removing the libtest dependency by encapsulating the libtest interactions into a single dedicated module. That makes it easier to see what parts of libtest are actually used. --- As a side-effect of moving the `test_opts` function into that dedicated module, this PR also ends up allowing `--fail-fast` to be passed on the command line, instead of requiring an environment variable. --- There is still (at least) one other aspect of the libtest dependency that this PR does not address, namely the fact that we rely on libtest's output capture (via unstable std APIs) to capture the output that we print during individual tests. I hope to do something about that at some point. r? jieyouxu
This commit is contained in:
commit
9d846764c4
5 changed files with 208 additions and 105 deletions
|
@ -9,9 +9,9 @@ use std::{fmt, iter};
|
||||||
use build_helper::git::GitConfig;
|
use build_helper::git::GitConfig;
|
||||||
use semver::Version;
|
use semver::Version;
|
||||||
use serde::de::{Deserialize, Deserializer, Error as _};
|
use serde::de::{Deserialize, Deserializer, Error as _};
|
||||||
use test::{ColorConfig, OutputFormat};
|
|
||||||
|
|
||||||
pub use self::Mode::*;
|
pub use self::Mode::*;
|
||||||
|
use crate::executor::{ColorConfig, OutputFormat};
|
||||||
use crate::util::{PathBufExt, add_dylib_path};
|
use crate::util::{PathBufExt, add_dylib_path};
|
||||||
|
|
||||||
macro_rules! string_enum {
|
macro_rules! string_enum {
|
||||||
|
@ -178,6 +178,10 @@ pub struct Config {
|
||||||
/// `true` to overwrite stderr/stdout files instead of complaining about changes in output.
|
/// `true` to overwrite stderr/stdout files instead of complaining about changes in output.
|
||||||
pub bless: bool,
|
pub bless: bool,
|
||||||
|
|
||||||
|
/// Stop as soon as possible after any test fails.
|
||||||
|
/// May run a few more tests before stopping, due to threading.
|
||||||
|
pub fail_fast: bool,
|
||||||
|
|
||||||
/// The library paths required for running the compiler.
|
/// The library paths required for running the compiler.
|
||||||
pub compile_lib_path: PathBuf,
|
pub compile_lib_path: PathBuf,
|
||||||
|
|
||||||
|
|
156
src/tools/compiletest/src/executor.rs
Normal file
156
src/tools/compiletest/src/executor.rs
Normal file
|
@ -0,0 +1,156 @@
|
||||||
|
//! This module encapsulates all of the code that interacts directly with
|
||||||
|
//! libtest, to execute the collected tests.
|
||||||
|
//!
|
||||||
|
//! This will hopefully make it easier to migrate away from libtest someday.
|
||||||
|
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::io;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use crate::common::{Config, TestPaths};
|
||||||
|
|
||||||
|
/// Delegates to libtest to run the list of collected tests.
|
||||||
|
///
|
||||||
|
/// Returns `Ok(true)` if all tests passed, or `Ok(false)` if one or more tests failed.
|
||||||
|
pub(crate) fn execute_tests(config: &Config, tests: Vec<CollectedTest>) -> io::Result<bool> {
|
||||||
|
let opts = test_opts(config);
|
||||||
|
let tests = tests.into_iter().map(|t| t.into_libtest()).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
test::run_tests_console(&opts, tests)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information needed to create a `test::TestDescAndFn`.
|
||||||
|
pub(crate) struct CollectedTest {
|
||||||
|
pub(crate) desc: CollectedTestDesc,
|
||||||
|
pub(crate) config: Arc<Config>,
|
||||||
|
pub(crate) testpaths: TestPaths,
|
||||||
|
pub(crate) revision: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Information needed to create a `test::TestDesc`.
|
||||||
|
pub(crate) struct CollectedTestDesc {
|
||||||
|
pub(crate) name: String,
|
||||||
|
pub(crate) ignore: bool,
|
||||||
|
pub(crate) ignore_message: Option<Cow<'static, str>>,
|
||||||
|
pub(crate) should_panic: ShouldPanic,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CollectedTest {
|
||||||
|
fn into_libtest(self) -> test::TestDescAndFn {
|
||||||
|
let Self { desc, config, testpaths, revision } = self;
|
||||||
|
let CollectedTestDesc { name, ignore, ignore_message, should_panic } = desc;
|
||||||
|
|
||||||
|
// Libtest requires the ignore message to be a &'static str, so we might
|
||||||
|
// have to leak memory to create it. This is fine, as we only do so once
|
||||||
|
// per test, so the leak won't grow indefinitely.
|
||||||
|
let ignore_message = ignore_message.map(|msg| match msg {
|
||||||
|
Cow::Borrowed(s) => s,
|
||||||
|
Cow::Owned(s) => &*String::leak(s),
|
||||||
|
});
|
||||||
|
|
||||||
|
let desc = test::TestDesc {
|
||||||
|
name: test::DynTestName(name),
|
||||||
|
ignore,
|
||||||
|
ignore_message,
|
||||||
|
source_file: "",
|
||||||
|
start_line: 0,
|
||||||
|
start_col: 0,
|
||||||
|
end_line: 0,
|
||||||
|
end_col: 0,
|
||||||
|
should_panic: should_panic.to_libtest(),
|
||||||
|
compile_fail: false,
|
||||||
|
no_run: false,
|
||||||
|
test_type: test::TestType::Unknown,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This closure is invoked when libtest returns control to compiletest
|
||||||
|
// to execute the test.
|
||||||
|
let testfn = test::DynTestFn(Box::new(move || {
|
||||||
|
crate::runtest::run(config, &testpaths, revision.as_deref());
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
|
||||||
|
test::TestDescAndFn { desc, testfn }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether console output should be colored or not.
|
||||||
|
#[derive(Copy, Clone, Default, Debug)]
|
||||||
|
pub enum ColorConfig {
|
||||||
|
#[default]
|
||||||
|
AutoColor,
|
||||||
|
AlwaysColor,
|
||||||
|
NeverColor,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ColorConfig {
|
||||||
|
fn to_libtest(self) -> test::ColorConfig {
|
||||||
|
match self {
|
||||||
|
Self::AutoColor => test::ColorConfig::AutoColor,
|
||||||
|
Self::AlwaysColor => test::ColorConfig::AlwaysColor,
|
||||||
|
Self::NeverColor => test::ColorConfig::NeverColor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Format of the test results output.
|
||||||
|
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
|
pub enum OutputFormat {
|
||||||
|
/// Verbose output
|
||||||
|
Pretty,
|
||||||
|
/// Quiet output
|
||||||
|
#[default]
|
||||||
|
Terse,
|
||||||
|
/// JSON output
|
||||||
|
Json,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl OutputFormat {
|
||||||
|
fn to_libtest(self) -> test::OutputFormat {
|
||||||
|
match self {
|
||||||
|
Self::Pretty => test::OutputFormat::Pretty,
|
||||||
|
Self::Terse => test::OutputFormat::Terse,
|
||||||
|
Self::Json => test::OutputFormat::Json,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Whether test is expected to panic or not.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub(crate) enum ShouldPanic {
|
||||||
|
No,
|
||||||
|
Yes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ShouldPanic {
|
||||||
|
fn to_libtest(self) -> test::ShouldPanic {
|
||||||
|
match self {
|
||||||
|
Self::No => test::ShouldPanic::No,
|
||||||
|
Self::Yes => test::ShouldPanic::Yes,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_opts(config: &Config) -> test::TestOpts {
|
||||||
|
test::TestOpts {
|
||||||
|
exclude_should_panic: false,
|
||||||
|
filters: config.filters.clone(),
|
||||||
|
filter_exact: config.filter_exact,
|
||||||
|
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
|
||||||
|
format: config.format.to_libtest(),
|
||||||
|
logfile: config.logfile.clone(),
|
||||||
|
run_tests: true,
|
||||||
|
bench_benchmarks: true,
|
||||||
|
nocapture: config.nocapture,
|
||||||
|
color: config.color.to_libtest(),
|
||||||
|
shuffle: false,
|
||||||
|
shuffle_seed: None,
|
||||||
|
test_threads: None,
|
||||||
|
skip: config.skip.clone(),
|
||||||
|
list: false,
|
||||||
|
options: test::Options::new(),
|
||||||
|
time_options: None,
|
||||||
|
force_run_in_process: false,
|
||||||
|
fail_fast: config.fail_fast,
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ use tracing::*;
|
||||||
|
|
||||||
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
|
use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
|
||||||
use crate::debuggers::{extract_cdb_version, extract_gdb_version};
|
use crate::debuggers::{extract_cdb_version, extract_gdb_version};
|
||||||
|
use crate::executor::{CollectedTestDesc, ShouldPanic};
|
||||||
use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
|
use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
|
||||||
use crate::header::needs::CachedNeedsConditions;
|
use crate::header::needs::CachedNeedsConditions;
|
||||||
use crate::util::static_regex;
|
use crate::util::static_regex;
|
||||||
|
@ -1355,15 +1356,15 @@ where
|
||||||
Some((min, max))
|
Some((min, max))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_test_description<R: Read>(
|
pub(crate) fn make_test_description<R: Read>(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
cache: &HeadersCache,
|
cache: &HeadersCache,
|
||||||
name: test::TestName,
|
name: String,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
src: R,
|
src: R,
|
||||||
test_revision: Option<&str>,
|
test_revision: Option<&str>,
|
||||||
poisoned: &mut bool,
|
poisoned: &mut bool,
|
||||||
) -> test::TestDesc {
|
) -> CollectedTestDesc {
|
||||||
let mut ignore = false;
|
let mut ignore = false;
|
||||||
let mut ignore_message = None;
|
let mut ignore_message = None;
|
||||||
let mut should_fail = false;
|
let mut should_fail = false;
|
||||||
|
@ -1387,10 +1388,7 @@ pub fn make_test_description<R: Read>(
|
||||||
match $e {
|
match $e {
|
||||||
IgnoreDecision::Ignore { reason } => {
|
IgnoreDecision::Ignore { reason } => {
|
||||||
ignore = true;
|
ignore = true;
|
||||||
// The ignore reason must be a &'static str, so we have to leak memory to
|
ignore_message = Some(reason.into());
|
||||||
// create it. This is fine, as the header is parsed only at the start of
|
|
||||||
// compiletest so it won't grow indefinitely.
|
|
||||||
ignore_message = Some(&*Box::leak(Box::<str>::from(reason)));
|
|
||||||
}
|
}
|
||||||
IgnoreDecision::Error { message } => {
|
IgnoreDecision::Error { message } => {
|
||||||
eprintln!("error: {}:{line_number}: {message}", path.display());
|
eprintln!("error: {}:{line_number}: {message}", path.display());
|
||||||
|
@ -1431,25 +1429,12 @@ pub fn make_test_description<R: Read>(
|
||||||
// since we run the pretty printer across all tests by default.
|
// since we run the pretty printer across all tests by default.
|
||||||
// If desired, we could add a `should-fail-pretty` annotation.
|
// If desired, we could add a `should-fail-pretty` annotation.
|
||||||
let should_panic = match config.mode {
|
let should_panic = match config.mode {
|
||||||
crate::common::Pretty => test::ShouldPanic::No,
|
crate::common::Pretty => ShouldPanic::No,
|
||||||
_ if should_fail => test::ShouldPanic::Yes,
|
_ if should_fail => ShouldPanic::Yes,
|
||||||
_ => test::ShouldPanic::No,
|
_ => ShouldPanic::No,
|
||||||
};
|
};
|
||||||
|
|
||||||
test::TestDesc {
|
CollectedTestDesc { name, ignore, ignore_message, should_panic }
|
||||||
name,
|
|
||||||
ignore,
|
|
||||||
ignore_message,
|
|
||||||
source_file: "",
|
|
||||||
start_line: 0,
|
|
||||||
start_col: 0,
|
|
||||||
end_line: 0,
|
|
||||||
end_col: 0,
|
|
||||||
should_panic,
|
|
||||||
compile_fail: false,
|
|
||||||
no_run: false,
|
|
||||||
test_type: test::TestType::Unknown,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
|
fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
|
||||||
|
|
|
@ -8,14 +8,15 @@ use super::{
|
||||||
parse_normalize_rule,
|
parse_normalize_rule,
|
||||||
};
|
};
|
||||||
use crate::common::{Config, Debugger, Mode};
|
use crate::common::{Config, Debugger, Mode};
|
||||||
|
use crate::executor::{CollectedTestDesc, ShouldPanic};
|
||||||
|
|
||||||
fn make_test_description<R: Read>(
|
fn make_test_description<R: Read>(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
name: test::TestName,
|
name: String,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
src: R,
|
src: R,
|
||||||
revision: Option<&str>,
|
revision: Option<&str>,
|
||||||
) -> test::TestDesc {
|
) -> CollectedTestDesc {
|
||||||
let cache = HeadersCache::load(config);
|
let cache = HeadersCache::load(config);
|
||||||
let mut poisoned = false;
|
let mut poisoned = false;
|
||||||
let test = crate::header::make_test_description(
|
let test = crate::header::make_test_description(
|
||||||
|
@ -233,7 +234,7 @@ fn parse_rs(config: &Config, contents: &str) -> EarlyProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_ignore(config: &Config, contents: &str) -> bool {
|
fn check_ignore(config: &Config, contents: &str) -> bool {
|
||||||
let tn = test::DynTestName(String::new());
|
let tn = String::new();
|
||||||
let p = Path::new("a.rs");
|
let p = Path::new("a.rs");
|
||||||
let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None);
|
let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None);
|
||||||
d.ignore
|
d.ignore
|
||||||
|
@ -242,13 +243,13 @@ fn check_ignore(config: &Config, contents: &str) -> bool {
|
||||||
#[test]
|
#[test]
|
||||||
fn should_fail() {
|
fn should_fail() {
|
||||||
let config: Config = cfg().build();
|
let config: Config = cfg().build();
|
||||||
let tn = test::DynTestName(String::new());
|
let tn = String::new();
|
||||||
let p = Path::new("a.rs");
|
let p = Path::new("a.rs");
|
||||||
|
|
||||||
let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
|
let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
|
||||||
assert_eq!(d.should_panic, test::ShouldPanic::No);
|
assert_eq!(d.should_panic, ShouldPanic::No);
|
||||||
let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None);
|
let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None);
|
||||||
assert_eq!(d.should_panic, test::ShouldPanic::Yes);
|
assert_eq!(d.should_panic, ShouldPanic::Yes);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub mod common;
|
||||||
pub mod compute_diff;
|
pub mod compute_diff;
|
||||||
mod debuggers;
|
mod debuggers;
|
||||||
pub mod errors;
|
pub mod errors;
|
||||||
|
mod executor;
|
||||||
pub mod header;
|
pub mod header;
|
||||||
mod json;
|
mod json;
|
||||||
mod raise_fd_limit;
|
mod raise_fd_limit;
|
||||||
|
@ -32,7 +33,6 @@ use std::{env, fs, vec};
|
||||||
|
|
||||||
use build_helper::git::{get_git_modified_files, get_git_untracked_files};
|
use build_helper::git::{get_git_modified_files, get_git_untracked_files};
|
||||||
use getopts::Options;
|
use getopts::Options;
|
||||||
use test::ColorConfig;
|
|
||||||
use tracing::*;
|
use tracing::*;
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
|
@ -41,6 +41,7 @@ use crate::common::{
|
||||||
CompareMode, Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
|
CompareMode, Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
|
||||||
output_base_dir, output_relative_path,
|
output_base_dir, output_relative_path,
|
||||||
};
|
};
|
||||||
|
use crate::executor::{CollectedTest, ColorConfig, OutputFormat};
|
||||||
use crate::header::HeadersCache;
|
use crate::header::HeadersCache;
|
||||||
use crate::util::logv;
|
use crate::util::logv;
|
||||||
|
|
||||||
|
@ -50,6 +51,12 @@ use crate::util::logv;
|
||||||
/// some code here that inspects environment variables or even runs executables
|
/// some code here that inspects environment variables or even runs executables
|
||||||
/// (e.g. when discovering debugger versions).
|
/// (e.g. when discovering debugger versions).
|
||||||
pub fn parse_config(args: Vec<String>) -> Config {
|
pub fn parse_config(args: Vec<String>) -> Config {
|
||||||
|
if env::var("RUST_TEST_NOCAPTURE").is_ok() {
|
||||||
|
eprintln!(
|
||||||
|
"WARNING: RUST_TEST_NOCAPTURE is not supported. Use the `--no-capture` flag instead."
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let mut opts = Options::new();
|
let mut opts = Options::new();
|
||||||
opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
|
opts.reqopt("", "compile-lib-path", "path to host shared libraries", "PATH")
|
||||||
.reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
|
.reqopt("", "run-lib-path", "path to target shared libraries", "PATH")
|
||||||
|
@ -128,6 +135,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
||||||
"bless",
|
"bless",
|
||||||
"overwrite stderr/stdout files instead of complaining about a mismatch",
|
"overwrite stderr/stdout files instead of complaining about a mismatch",
|
||||||
)
|
)
|
||||||
|
.optflag("", "fail-fast", "stop as soon as possible after any test fails")
|
||||||
.optflag("", "quiet", "print one character per test instead of one line")
|
.optflag("", "quiet", "print one character per test instead of one line")
|
||||||
.optopt("", "color", "coloring: auto, always, never", "WHEN")
|
.optopt("", "color", "coloring: auto, always, never", "WHEN")
|
||||||
.optflag("", "json", "emit json output instead of plaintext output")
|
.optflag("", "json", "emit json output instead of plaintext output")
|
||||||
|
@ -319,6 +327,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
||||||
|
|
||||||
Config {
|
Config {
|
||||||
bless: matches.opt_present("bless"),
|
bless: matches.opt_present("bless"),
|
||||||
|
fail_fast: matches.opt_present("fail-fast")
|
||||||
|
|| env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
|
||||||
|
|
||||||
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
|
compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
|
||||||
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
|
run_lib_path: make_absolute(opt_path(matches, "run-lib-path")),
|
||||||
rustc_path: opt_path(matches, "rustc-path"),
|
rustc_path: opt_path(matches, "rustc-path"),
|
||||||
|
@ -392,9 +403,9 @@ pub fn parse_config(args: Vec<String>) -> Config {
|
||||||
verbose: matches.opt_present("verbose"),
|
verbose: matches.opt_present("verbose"),
|
||||||
format: match (matches.opt_present("quiet"), matches.opt_present("json")) {
|
format: match (matches.opt_present("quiet"), matches.opt_present("json")) {
|
||||||
(true, true) => panic!("--quiet and --json are incompatible"),
|
(true, true) => panic!("--quiet and --json are incompatible"),
|
||||||
(true, false) => test::OutputFormat::Terse,
|
(true, false) => OutputFormat::Terse,
|
||||||
(false, true) => test::OutputFormat::Json,
|
(false, true) => OutputFormat::Json,
|
||||||
(false, false) => test::OutputFormat::Pretty,
|
(false, false) => OutputFormat::Pretty,
|
||||||
},
|
},
|
||||||
only_modified: matches.opt_present("only-modified"),
|
only_modified: matches.opt_present("only-modified"),
|
||||||
color,
|
color,
|
||||||
|
@ -525,8 +536,6 @@ pub fn run_tests(config: Arc<Config>) {
|
||||||
// Let tests know which target they're running as
|
// Let tests know which target they're running as
|
||||||
env::set_var("TARGET", &config.target);
|
env::set_var("TARGET", &config.target);
|
||||||
|
|
||||||
let opts = test_opts(&config);
|
|
||||||
|
|
||||||
let mut configs = Vec::new();
|
let mut configs = Vec::new();
|
||||||
if let Mode::DebugInfo = config.mode {
|
if let Mode::DebugInfo = config.mode {
|
||||||
// Debugging emscripten code doesn't make sense today
|
// Debugging emscripten code doesn't make sense today
|
||||||
|
@ -553,12 +562,12 @@ pub fn run_tests(config: Arc<Config>) {
|
||||||
tests.extend(collect_and_make_tests(c));
|
tests.extend(collect_and_make_tests(c));
|
||||||
}
|
}
|
||||||
|
|
||||||
tests.sort_by(|a, b| a.desc.name.as_slice().cmp(&b.desc.name.as_slice()));
|
tests.sort_by(|a, b| Ord::cmp(&a.desc.name, &b.desc.name));
|
||||||
|
|
||||||
// Delegate to libtest to filter and run the big list of structures created
|
// Delegate to libtest to filter and run the big list of structures created
|
||||||
// during test discovery. When libtest decides to run a test, it will invoke
|
// during test discovery. When libtest decides to run a test, it will
|
||||||
// the corresponding closure created by `make_test_closure`.
|
// return control to compiletest by invoking a closure.
|
||||||
let res = test::run_tests_console(&opts, tests);
|
let res = crate::executor::execute_tests(&config, tests);
|
||||||
|
|
||||||
// Check the outcome reported by libtest.
|
// Check the outcome reported by libtest.
|
||||||
match res {
|
match res {
|
||||||
|
@ -602,37 +611,6 @@ pub fn run_tests(config: Arc<Config>) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn test_opts(config: &Config) -> test::TestOpts {
|
|
||||||
if env::var("RUST_TEST_NOCAPTURE").is_ok() {
|
|
||||||
eprintln!(
|
|
||||||
"WARNING: RUST_TEST_NOCAPTURE is no longer used. \
|
|
||||||
Use the `--nocapture` flag instead."
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
test::TestOpts {
|
|
||||||
exclude_should_panic: false,
|
|
||||||
filters: config.filters.clone(),
|
|
||||||
filter_exact: config.filter_exact,
|
|
||||||
run_ignored: if config.run_ignored { test::RunIgnored::Yes } else { test::RunIgnored::No },
|
|
||||||
format: config.format,
|
|
||||||
logfile: config.logfile.clone(),
|
|
||||||
run_tests: true,
|
|
||||||
bench_benchmarks: true,
|
|
||||||
nocapture: config.nocapture,
|
|
||||||
color: config.color,
|
|
||||||
shuffle: false,
|
|
||||||
shuffle_seed: None,
|
|
||||||
test_threads: None,
|
|
||||||
skip: config.skip.clone(),
|
|
||||||
list: false,
|
|
||||||
options: test::Options::new(),
|
|
||||||
time_options: None,
|
|
||||||
force_run_in_process: false,
|
|
||||||
fail_fast: std::env::var_os("RUSTC_TEST_FAIL_FAST").is_some(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read-only context data used during test collection.
|
/// Read-only context data used during test collection.
|
||||||
struct TestCollectorCx {
|
struct TestCollectorCx {
|
||||||
config: Arc<Config>,
|
config: Arc<Config>,
|
||||||
|
@ -643,17 +621,17 @@ struct TestCollectorCx {
|
||||||
|
|
||||||
/// Mutable state used during test collection.
|
/// Mutable state used during test collection.
|
||||||
struct TestCollector {
|
struct TestCollector {
|
||||||
tests: Vec<test::TestDescAndFn>,
|
tests: Vec<CollectedTest>,
|
||||||
found_path_stems: HashSet<PathBuf>,
|
found_path_stems: HashSet<PathBuf>,
|
||||||
poisoned: bool,
|
poisoned: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates libtest structures for every test/revision in the test suite directory.
|
/// Creates test structures for every test/revision in the test suite directory.
|
||||||
///
|
///
|
||||||
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
|
/// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
|
||||||
/// regardless of whether any filters/tests were specified on the command-line,
|
/// regardless of whether any filters/tests were specified on the command-line,
|
||||||
/// because filtering is handled later by libtest.
|
/// because filtering is handled later by libtest.
|
||||||
pub fn collect_and_make_tests(config: Arc<Config>) -> Vec<test::TestDescAndFn> {
|
pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest> {
|
||||||
debug!("making tests from {}", config.src_test_suite_root.display());
|
debug!("making tests from {}", config.src_test_suite_root.display());
|
||||||
let common_inputs_stamp = common_inputs_stamp(&config);
|
let common_inputs_stamp = common_inputs_stamp(&config);
|
||||||
let modified_tests =
|
let modified_tests =
|
||||||
|
@ -882,7 +860,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
|
||||||
};
|
};
|
||||||
|
|
||||||
// For each revision (or the sole dummy revision), create and append a
|
// For each revision (or the sole dummy revision), create and append a
|
||||||
// `test::TestDescAndFn` that can be handed over to libtest.
|
// `CollectedTest` that can be handed over to the test executor.
|
||||||
collector.tests.extend(revisions.into_iter().map(|revision| {
|
collector.tests.extend(revisions.into_iter().map(|revision| {
|
||||||
// Create a test name and description to hand over to libtest.
|
// Create a test name and description to hand over to libtest.
|
||||||
let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
|
let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
|
||||||
|
@ -905,13 +883,14 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
|
||||||
if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
|
if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
|
||||||
desc.ignore = true;
|
desc.ignore = true;
|
||||||
// Keep this in sync with the "up-to-date" message detected by bootstrap.
|
// Keep this in sync with the "up-to-date" message detected by bootstrap.
|
||||||
desc.ignore_message = Some("up-to-date");
|
desc.ignore_message = Some("up-to-date".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create the callback that will run this test/revision when libtest calls it.
|
let config = Arc::clone(&cx.config);
|
||||||
let testfn = make_test_closure(Arc::clone(&cx.config), testpaths, revision);
|
let testpaths = testpaths.clone();
|
||||||
|
let revision = revision.map(str::to_owned);
|
||||||
|
|
||||||
test::TestDescAndFn { desc, testfn }
|
CollectedTest { desc, config, testpaths, revision }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1043,11 +1022,7 @@ impl Stamp {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a name for this test/revision that can be handed over to libtest.
|
/// Creates a name for this test/revision that can be handed over to libtest.
|
||||||
fn make_test_name(
|
fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String {
|
||||||
config: &Config,
|
|
||||||
testpaths: &TestPaths,
|
|
||||||
revision: Option<&str>,
|
|
||||||
) -> test::TestName {
|
|
||||||
// Print the name of the file, relative to the sources root.
|
// Print the name of the file, relative to the sources root.
|
||||||
let path = testpaths.file.strip_prefix(&config.src_root).unwrap();
|
let path = testpaths.file.strip_prefix(&config.src_root).unwrap();
|
||||||
let debugger = match config.debugger {
|
let debugger = match config.debugger {
|
||||||
|
@ -1059,32 +1034,14 @@ fn make_test_name(
|
||||||
None => String::new(),
|
None => String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
test::DynTestName(format!(
|
format!(
|
||||||
"[{}{}{}] {}{}",
|
"[{}{}{}] {}{}",
|
||||||
config.mode,
|
config.mode,
|
||||||
debugger,
|
debugger,
|
||||||
mode_suffix,
|
mode_suffix,
|
||||||
path.display(),
|
path.display(),
|
||||||
revision.map_or("".to_string(), |rev| format!("#{}", rev))
|
revision.map_or("".to_string(), |rev| format!("#{}", rev))
|
||||||
))
|
)
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a callback for this test/revision that libtest will call when it
|
|
||||||
/// decides to actually run the underlying test.
|
|
||||||
fn make_test_closure(
|
|
||||||
config: Arc<Config>,
|
|
||||||
testpaths: &TestPaths,
|
|
||||||
revision: Option<&str>,
|
|
||||||
) -> test::TestFn {
|
|
||||||
let testpaths = testpaths.clone();
|
|
||||||
let revision = revision.map(str::to_owned);
|
|
||||||
|
|
||||||
// This callback is the link between compiletest's test discovery code,
|
|
||||||
// and the parts of compiletest that know how to run an individual test.
|
|
||||||
test::DynTestFn(Box::new(move || {
|
|
||||||
runtest::run(config, &testpaths, revision.as_deref());
|
|
||||||
Ok(())
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks that test discovery didn't find any tests whose name stem is a prefix
|
/// Checks that test discovery didn't find any tests whose name stem is a prefix
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue