mirror of
https://github.com/rust-lang/cargo.git
synced 2025-10-01 11:30:39 +00:00
Rework test error handling
This commit is contained in:
parent
e5ec3a8ff9
commit
23735d4c09
@ -73,12 +73,5 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
|
|||||||
let bench_args = bench_args.chain(args.get_many::<String>("args").unwrap_or_default());
|
let bench_args = bench_args.chain(args.get_many::<String>("args").unwrap_or_default());
|
||||||
let bench_args = bench_args.map(String::as_str).collect::<Vec<_>>();
|
let bench_args = bench_args.map(String::as_str).collect::<Vec<_>>();
|
||||||
|
|
||||||
let err = ops::run_benches(&ws, &ops, &bench_args)?;
|
ops::run_benches(&ws, &ops, &bench_args)
|
||||||
match err {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(err) => Err(match err.code {
|
|
||||||
Some(i) => CliError::new(anyhow::format_err!("bench failed"), i),
|
|
||||||
None => CliError::new(err.into(), 101),
|
|
||||||
}),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
use crate::command_prelude::*;
|
use crate::command_prelude::*;
|
||||||
use anyhow::Error;
|
|
||||||
use cargo::ops;
|
use cargo::ops;
|
||||||
|
|
||||||
pub fn cli() -> App {
|
pub fn cli() -> App {
|
||||||
@ -110,18 +109,5 @@ pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
|
|||||||
compile_opts,
|
compile_opts,
|
||||||
};
|
};
|
||||||
|
|
||||||
let err = ops::run_tests(&ws, &ops, &test_args)?;
|
ops::run_tests(&ws, &ops, &test_args)
|
||||||
match err {
|
|
||||||
None => Ok(()),
|
|
||||||
Some(err) => {
|
|
||||||
let context = anyhow::format_err!("{}", err.hint(&ws, &ops.compile_opts));
|
|
||||||
let e = match err.code {
|
|
||||||
// Don't show "process didn't exit successfully" for simple errors.
|
|
||||||
Some(i) if cargo_util::is_simple_exit_code(i) => CliError::new(context, i),
|
|
||||||
Some(i) => CliError::new(Error::from(err).context(context), i),
|
|
||||||
None => CliError::new(Error::from(err).context(context), 101),
|
|
||||||
};
|
|
||||||
Err(e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,11 @@ use crate::core::shell::Verbosity;
|
|||||||
use crate::core::{TargetKind, Workspace};
|
use crate::core::{TargetKind, Workspace};
|
||||||
use crate::ops;
|
use crate::ops;
|
||||||
use crate::util::errors::CargoResult;
|
use crate::util::errors::CargoResult;
|
||||||
use crate::util::{add_path_args, CargoTestError, Config, Test};
|
use crate::util::{add_path_args, CliError, CliResult, Config};
|
||||||
|
use anyhow::format_err;
|
||||||
use cargo_util::{ProcessBuilder, ProcessError};
|
use cargo_util::{ProcessBuilder, ProcessError};
|
||||||
use std::ffi::OsString;
|
use std::ffi::OsString;
|
||||||
|
use std::fmt::Write;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
pub struct TestOptions {
|
pub struct TestOptions {
|
||||||
@ -14,61 +16,87 @@ pub struct TestOptions {
|
|||||||
pub no_fail_fast: bool,
|
pub no_fail_fast: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_tests(
|
/// The kind of test.
|
||||||
ws: &Workspace<'_>,
|
///
|
||||||
options: &TestOptions,
|
/// This is needed because `Unit` does not track whether or not something is a
|
||||||
test_args: &[&str],
|
/// benchmark.
|
||||||
) -> CargoResult<Option<CargoTestError>> {
|
#[derive(Copy, Clone)]
|
||||||
|
enum TestKind {
|
||||||
|
Test,
|
||||||
|
Bench,
|
||||||
|
Doctest,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A unit that failed to run.
|
||||||
|
struct UnitTestError {
|
||||||
|
unit: Unit,
|
||||||
|
kind: TestKind,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnitTestError {
|
||||||
|
/// Returns the CLI args needed to target this unit.
|
||||||
|
fn cli_args(&self, ws: &Workspace<'_>, opts: &ops::CompileOptions) -> String {
|
||||||
|
let mut args = if opts.spec.needs_spec_flag(ws) {
|
||||||
|
format!("-p {} ", self.unit.pkg.name())
|
||||||
|
} else {
|
||||||
|
String::new()
|
||||||
|
};
|
||||||
|
let mut add = |which| write!(args, "--{which} {}", self.unit.target.name()).unwrap();
|
||||||
|
|
||||||
|
match self.kind {
|
||||||
|
TestKind::Test | TestKind::Bench => match self.unit.target.kind() {
|
||||||
|
TargetKind::Lib(_) => args.push_str("--lib"),
|
||||||
|
TargetKind::Bin => add("bin"),
|
||||||
|
TargetKind::Test => add("test"),
|
||||||
|
TargetKind::Bench => add("bench"),
|
||||||
|
TargetKind::ExampleLib(_) | TargetKind::ExampleBin => add("example"),
|
||||||
|
TargetKind::CustomBuild => panic!("unexpected CustomBuild kind"),
|
||||||
|
},
|
||||||
|
TestKind::Doctest => args.push_str("--doc"),
|
||||||
|
}
|
||||||
|
args
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compiles and runs tests.
|
||||||
|
///
|
||||||
|
/// On error, the returned [`CliError`] will have the appropriate process exit
|
||||||
|
/// code that Cargo should use.
|
||||||
|
pub fn run_tests(ws: &Workspace<'_>, options: &TestOptions, test_args: &[&str]) -> CliResult {
|
||||||
let compilation = compile_tests(ws, options)?;
|
let compilation = compile_tests(ws, options)?;
|
||||||
|
|
||||||
if options.no_run {
|
if options.no_run {
|
||||||
if !options.compile_opts.build_config.emit_json() {
|
if !options.compile_opts.build_config.emit_json() {
|
||||||
display_no_run_information(ws, test_args, &compilation, "unittests")?;
|
display_no_run_information(ws, test_args, &compilation, "unittests")?;
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
let (test, mut errors) = run_unit_tests(ws.config(), options, test_args, &compilation)?;
|
let mut errors = run_unit_tests(ws, options, test_args, &compilation, TestKind::Test)?;
|
||||||
|
|
||||||
// If we have an error and want to fail fast, then return.
|
let doctest_errors = run_doc_tests(ws, options, test_args, &compilation)?;
|
||||||
if !errors.is_empty() && !options.no_fail_fast {
|
errors.extend(doctest_errors);
|
||||||
return Ok(Some(CargoTestError::new(test, errors)));
|
no_fail_fast_err(ws, &options.compile_opts, &errors)
|
||||||
}
|
|
||||||
|
|
||||||
let (doctest, docerrors) = run_doc_tests(ws, options, test_args, &compilation)?;
|
|
||||||
let test = if docerrors.is_empty() { test } else { doctest };
|
|
||||||
errors.extend(docerrors);
|
|
||||||
if errors.is_empty() {
|
|
||||||
Ok(None)
|
|
||||||
} else {
|
|
||||||
Ok(Some(CargoTestError::new(test, errors)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_benches(
|
/// Compiles and runs benchmarks.
|
||||||
ws: &Workspace<'_>,
|
///
|
||||||
options: &TestOptions,
|
/// On error, the returned [`CliError`] will have the appropriate process exit
|
||||||
args: &[&str],
|
/// code that Cargo should use.
|
||||||
) -> CargoResult<Option<CargoTestError>> {
|
pub fn run_benches(ws: &Workspace<'_>, options: &TestOptions, args: &[&str]) -> CliResult {
|
||||||
let compilation = compile_tests(ws, options)?;
|
let compilation = compile_tests(ws, options)?;
|
||||||
|
|
||||||
if options.no_run {
|
if options.no_run {
|
||||||
if !options.compile_opts.build_config.emit_json() {
|
if !options.compile_opts.build_config.emit_json() {
|
||||||
display_no_run_information(ws, args, &compilation, "benches")?;
|
display_no_run_information(ws, args, &compilation, "benches")?;
|
||||||
}
|
}
|
||||||
|
return Ok(());
|
||||||
return Ok(None);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut args = args.to_vec();
|
let mut args = args.to_vec();
|
||||||
args.push("--bench");
|
args.push("--bench");
|
||||||
|
|
||||||
let (test, errors) = run_unit_tests(ws.config(), options, &args, &compilation)?;
|
let errors = run_unit_tests(ws, options, &args, &compilation, TestKind::Bench)?;
|
||||||
|
no_fail_fast_err(ws, &options.compile_opts, &errors)
|
||||||
match errors.len() {
|
|
||||||
0 => Ok(None),
|
|
||||||
_ => Ok(Some(CargoTestError::new(test, errors))),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
|
fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<Compilation<'a>> {
|
||||||
@ -78,12 +106,17 @@ fn compile_tests<'a>(ws: &Workspace<'a>, options: &TestOptions) -> CargoResult<C
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Runs the unit and integration tests of a package.
|
/// Runs the unit and integration tests of a package.
|
||||||
|
///
|
||||||
|
/// Returns a `Vec` of tests that failed when `--no-fail-fast` is used.
|
||||||
|
/// If `--no-fail-fast` is *not* used, then this returns an `Err`.
|
||||||
fn run_unit_tests(
|
fn run_unit_tests(
|
||||||
config: &Config,
|
ws: &Workspace<'_>,
|
||||||
options: &TestOptions,
|
options: &TestOptions,
|
||||||
test_args: &[&str],
|
test_args: &[&str],
|
||||||
compilation: &Compilation<'_>,
|
compilation: &Compilation<'_>,
|
||||||
) -> CargoResult<(Test, Vec<ProcessError>)> {
|
test_kind: TestKind,
|
||||||
|
) -> Result<Vec<UnitTestError>, CliError> {
|
||||||
|
let config = ws.config();
|
||||||
let cwd = config.cwd();
|
let cwd = config.cwd();
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
|
|
||||||
@ -110,46 +143,32 @@ fn run_unit_tests(
|
|||||||
.shell()
|
.shell()
|
||||||
.verbose(|shell| shell.status("Running", &cmd))?;
|
.verbose(|shell| shell.status("Running", &cmd))?;
|
||||||
|
|
||||||
let result = cmd.exec();
|
if let Err(e) = cmd.exec() {
|
||||||
|
let code = fail_fast_code(&e);
|
||||||
if let Err(e) = result {
|
let unit_err = UnitTestError {
|
||||||
let e = e.downcast::<ProcessError>()?;
|
unit: unit.clone(),
|
||||||
errors.push((
|
kind: test_kind,
|
||||||
unit.target.kind().clone(),
|
};
|
||||||
unit.target.name().to_string(),
|
report_test_error(ws, &options.compile_opts, &unit_err, e);
|
||||||
unit.pkg.name().to_string(),
|
errors.push(unit_err);
|
||||||
e,
|
|
||||||
));
|
|
||||||
if !options.no_fail_fast {
|
if !options.no_fail_fast {
|
||||||
break;
|
return Err(CliError::code(code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(errors)
|
||||||
if errors.len() == 1 {
|
|
||||||
let (kind, name, pkg_name, e) = errors.pop().unwrap();
|
|
||||||
Ok((
|
|
||||||
Test::UnitTest {
|
|
||||||
kind,
|
|
||||||
name,
|
|
||||||
pkg_name,
|
|
||||||
},
|
|
||||||
vec![e],
|
|
||||||
))
|
|
||||||
} else {
|
|
||||||
Ok((
|
|
||||||
Test::Multiple,
|
|
||||||
errors.into_iter().map(|(_, _, _, e)| e).collect(),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Runs doc tests.
|
||||||
|
///
|
||||||
|
/// Returns a `Vec` of tests that failed when `--no-fail-fast` is used.
|
||||||
|
/// If `--no-fail-fast` is *not* used, then this returns an `Err`.
|
||||||
fn run_doc_tests(
|
fn run_doc_tests(
|
||||||
ws: &Workspace<'_>,
|
ws: &Workspace<'_>,
|
||||||
options: &TestOptions,
|
options: &TestOptions,
|
||||||
test_args: &[&str],
|
test_args: &[&str],
|
||||||
compilation: &Compilation<'_>,
|
compilation: &Compilation<'_>,
|
||||||
) -> CargoResult<(Test, Vec<ProcessError>)> {
|
) -> Result<Vec<UnitTestError>, CliError> {
|
||||||
let config = ws.config();
|
let config = ws.config();
|
||||||
let mut errors = Vec::new();
|
let mut errors = Vec::new();
|
||||||
let doctest_xcompile = config.cli_unstable().doctest_xcompile;
|
let doctest_xcompile = config.cli_unstable().doctest_xcompile;
|
||||||
@ -258,16 +277,24 @@ fn run_doc_tests(
|
|||||||
.shell()
|
.shell()
|
||||||
.verbose(|shell| shell.status("Running", p.to_string()))?;
|
.verbose(|shell| shell.status("Running", p.to_string()))?;
|
||||||
if let Err(e) = p.exec() {
|
if let Err(e) = p.exec() {
|
||||||
let e = e.downcast::<ProcessError>()?;
|
let code = fail_fast_code(&e);
|
||||||
errors.push(e);
|
let unit_err = UnitTestError {
|
||||||
|
unit: unit.clone(),
|
||||||
|
kind: TestKind::Doctest,
|
||||||
|
};
|
||||||
|
report_test_error(ws, &options.compile_opts, &unit_err, e);
|
||||||
|
errors.push(unit_err);
|
||||||
if !options.no_fail_fast {
|
if !options.no_fail_fast {
|
||||||
return Ok((Test::Doc, errors));
|
return Err(CliError::code(code));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok((Test::Doc, errors))
|
Ok(errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Displays human-readable descriptions of the test executables.
|
||||||
|
///
|
||||||
|
/// This is used when `cargo test --no-run` is used.
|
||||||
fn display_no_run_information(
|
fn display_no_run_information(
|
||||||
ws: &Workspace<'_>,
|
ws: &Workspace<'_>,
|
||||||
test_args: &[&str],
|
test_args: &[&str],
|
||||||
@ -303,6 +330,11 @@ fn display_no_run_information(
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a [`ProcessBuilder`] for executing a single test.
|
||||||
|
///
|
||||||
|
/// Returns a tuple `(exe_display, process)` where `exe_display` is a string
|
||||||
|
/// to display that describes the executable path in a human-readable form.
|
||||||
|
/// `process` is the `ProcessBuilder` to use for executing the test.
|
||||||
fn cmd_builds(
|
fn cmd_builds(
|
||||||
config: &Config,
|
config: &Config,
|
||||||
cwd: &Path,
|
cwd: &Path,
|
||||||
@ -341,3 +373,67 @@ fn cmd_builds(
|
|||||||
|
|
||||||
Ok((exe_display, cmd))
|
Ok((exe_display, cmd))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the error code to use when *not* using `--no-fail-fast`.
|
||||||
|
///
|
||||||
|
/// Cargo will return the error code from the test process itself. If some
|
||||||
|
/// other error happened (like a failure to launch the process), then it will
|
||||||
|
/// return a standard 101 error code.
|
||||||
|
///
|
||||||
|
/// When using `--no-fail-fast`, Cargo always uses the 101 exit code (since
|
||||||
|
/// there may not be just one process to report).
|
||||||
|
fn fail_fast_code(error: &anyhow::Error) -> i32 {
|
||||||
|
if let Some(proc_err) = error.downcast_ref::<ProcessError>() {
|
||||||
|
if let Some(code) = proc_err.code {
|
||||||
|
return code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
101
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the `CliError` when using `--no-fail-fast` and there is at least
|
||||||
|
/// one error.
|
||||||
|
fn no_fail_fast_err(
|
||||||
|
ws: &Workspace<'_>,
|
||||||
|
opts: &ops::CompileOptions,
|
||||||
|
errors: &[UnitTestError],
|
||||||
|
) -> CliResult {
|
||||||
|
// TODO: This could be improved by combining the flags on a single line when feasible.
|
||||||
|
let args: Vec<_> = errors
|
||||||
|
.iter()
|
||||||
|
.map(|unit_err| format!(" `{}`", unit_err.cli_args(ws, opts)))
|
||||||
|
.collect();
|
||||||
|
let message = match errors.len() {
|
||||||
|
0 => return Ok(()),
|
||||||
|
1 => format!("1 target failed:\n{}", args.join("\n")),
|
||||||
|
n => format!("{n} targets failed:\n{}", args.join("\n")),
|
||||||
|
};
|
||||||
|
Err(anyhow::Error::msg(message).into())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Displays an error on the console about a test failure.
|
||||||
|
fn report_test_error(
|
||||||
|
ws: &Workspace<'_>,
|
||||||
|
opts: &ops::CompileOptions,
|
||||||
|
unit_err: &UnitTestError,
|
||||||
|
test_error: anyhow::Error,
|
||||||
|
) {
|
||||||
|
let which = match unit_err.kind {
|
||||||
|
TestKind::Test => "test failed",
|
||||||
|
TestKind::Bench => "bench failed",
|
||||||
|
TestKind::Doctest => "doctest failed",
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut err = format_err!("{}, to rerun pass `{}`", which, unit_err.cli_args(ws, opts));
|
||||||
|
// Don't show "process didn't exit successfully" for simple errors.
|
||||||
|
// libtest exits with 101 for normal errors.
|
||||||
|
let is_simple = test_error
|
||||||
|
.downcast_ref::<ProcessError>()
|
||||||
|
.and_then(|proc_err| proc_err.code)
|
||||||
|
.map_or(false, |code| code == 101);
|
||||||
|
if !is_simple {
|
||||||
|
err = test_error.context(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
crate::display_error(&err, &mut ws.config().shell());
|
||||||
|
}
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
#![allow(unknown_lints)]
|
#![allow(unknown_lints)]
|
||||||
|
|
||||||
use crate::core::{TargetKind, Workspace};
|
|
||||||
use crate::ops::CompileOptions;
|
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use cargo_util::ProcessError;
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
@ -197,91 +194,6 @@ impl<'a> Iterator for ManifestCauses<'a> {
|
|||||||
|
|
||||||
impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
|
impl<'a> ::std::iter::FusedIterator for ManifestCauses<'a> {}
|
||||||
|
|
||||||
// =============================================================================
|
|
||||||
// Cargo test errors.
|
|
||||||
|
|
||||||
/// Error when testcases fail
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CargoTestError {
|
|
||||||
pub test: Test,
|
|
||||||
pub desc: String,
|
|
||||||
pub code: Option<i32>,
|
|
||||||
pub causes: Vec<ProcessError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for CargoTestError {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.desc.fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for CargoTestError {}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub enum Test {
|
|
||||||
Multiple,
|
|
||||||
Doc,
|
|
||||||
UnitTest {
|
|
||||||
kind: TargetKind,
|
|
||||||
name: String,
|
|
||||||
pkg_name: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CargoTestError {
|
|
||||||
pub fn new(test: Test, errors: Vec<ProcessError>) -> Self {
|
|
||||||
if errors.is_empty() {
|
|
||||||
panic!("Cannot create CargoTestError from empty Vec")
|
|
||||||
}
|
|
||||||
let desc = errors
|
|
||||||
.iter()
|
|
||||||
.map(|error| error.desc.clone())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
.join("\n");
|
|
||||||
CargoTestError {
|
|
||||||
test,
|
|
||||||
desc,
|
|
||||||
code: errors[0].code,
|
|
||||||
causes: errors,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hint(&self, ws: &Workspace<'_>, opts: &CompileOptions) -> String {
|
|
||||||
match self.test {
|
|
||||||
Test::UnitTest {
|
|
||||||
ref kind,
|
|
||||||
ref name,
|
|
||||||
ref pkg_name,
|
|
||||||
} => {
|
|
||||||
let pkg_info = if opts.spec.needs_spec_flag(ws) {
|
|
||||||
format!("-p {} ", pkg_name)
|
|
||||||
} else {
|
|
||||||
String::new()
|
|
||||||
};
|
|
||||||
|
|
||||||
match *kind {
|
|
||||||
TargetKind::Bench => {
|
|
||||||
format!("test failed, to rerun pass '{}--bench {}'", pkg_info, name)
|
|
||||||
}
|
|
||||||
TargetKind::Bin => {
|
|
||||||
format!("test failed, to rerun pass '{}--bin {}'", pkg_info, name)
|
|
||||||
}
|
|
||||||
TargetKind::Lib(_) => format!("test failed, to rerun pass '{}--lib'", pkg_info),
|
|
||||||
TargetKind::Test => {
|
|
||||||
format!("test failed, to rerun pass '{}--test {}'", pkg_info, name)
|
|
||||||
}
|
|
||||||
TargetKind::ExampleBin | TargetKind::ExampleLib(_) => {
|
|
||||||
format!("test failed, to rerun pass '{}--example {}", pkg_info, name)
|
|
||||||
}
|
|
||||||
_ => "test failed.".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Test::Doc => "test failed, to rerun pass '--doc'".into(),
|
|
||||||
_ => "test failed.".into(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =============================================================================
|
// =============================================================================
|
||||||
// CLI errors
|
// CLI errors
|
||||||
|
|
||||||
|
@ -6,8 +6,8 @@ pub use self::config::{homedir, Config, ConfigValue};
|
|||||||
pub(crate) use self::counter::MetricsCounter;
|
pub(crate) use self::counter::MetricsCounter;
|
||||||
pub use self::dependency_queue::DependencyQueue;
|
pub use self::dependency_queue::DependencyQueue;
|
||||||
pub use self::diagnostic_server::RustfixDiagnosticServer;
|
pub use self::diagnostic_server::RustfixDiagnosticServer;
|
||||||
pub use self::errors::{internal, CargoResult, CliResult, Test};
|
pub use self::errors::CliError;
|
||||||
pub use self::errors::{CargoTestError, CliError};
|
pub use self::errors::{internal, CargoResult, CliResult};
|
||||||
pub use self::flock::{FileLock, Filesystem};
|
pub use self::flock::{FileLock, Filesystem};
|
||||||
pub use self::graph::Graph;
|
pub use self::graph::Graph;
|
||||||
pub use self::hasher::StableHasher;
|
pub use self::hasher::StableHasher;
|
||||||
|
@ -1153,7 +1153,7 @@ fn test_bench_no_fail_fast() {
|
|||||||
let p = project()
|
let p = project()
|
||||||
.file("Cargo.toml", &basic_bin_manifest("foo"))
|
.file("Cargo.toml", &basic_bin_manifest("foo"))
|
||||||
.file(
|
.file(
|
||||||
"src/foo.rs",
|
"src/main.rs",
|
||||||
r#"
|
r#"
|
||||||
#![feature(test)]
|
#![feature(test)]
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
@ -1177,15 +1177,36 @@ fn test_bench_no_fail_fast() {
|
|||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
|
.file(
|
||||||
|
"benches/b1.rs",
|
||||||
|
r#"
|
||||||
|
#![feature(test)]
|
||||||
|
extern crate test;
|
||||||
|
#[bench]
|
||||||
|
fn b1_fail(_b: &mut test::Bencher) { assert_eq!(1, 2); }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
p.cargo("bench --no-fail-fast -- --test-threads=1")
|
p.cargo("bench --no-fail-fast -- --test-threads=1")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_contains("[RUNNING] [..] (target/release/deps/foo-[..][EXE])")
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] foo v0.5.0 [..]
|
||||||
|
[FINISHED] bench [..]
|
||||||
|
[RUNNING] unittests src/main.rs (target/release/deps/foo[..])
|
||||||
|
[ERROR] bench failed, to rerun pass `--bin foo`
|
||||||
|
[RUNNING] benches/b1.rs (target/release/deps/b1[..])
|
||||||
|
[ERROR] bench failed, to rerun pass `--bench b1`
|
||||||
|
[ERROR] 2 targets failed:
|
||||||
|
`--bin foo`
|
||||||
|
`--bench b1`
|
||||||
|
",
|
||||||
|
)
|
||||||
.with_stdout_contains("running 2 tests")
|
.with_stdout_contains("running 2 tests")
|
||||||
.with_stderr_contains("[RUNNING] [..] (target/release/deps/foo-[..][EXE])")
|
|
||||||
.with_stdout_contains("test bench_hello [..]")
|
.with_stdout_contains("test bench_hello [..]")
|
||||||
.with_stdout_contains("test bench_nope [..]")
|
.with_stdout_contains("test bench_nope [..]")
|
||||||
|
.with_stdout_contains("test b1_fail [..]")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ use cargo_test_support::{
|
|||||||
basic_bin_manifest, basic_lib_manifest, basic_manifest, cargo_exe, project,
|
basic_bin_manifest, basic_lib_manifest, basic_manifest, cargo_exe, project,
|
||||||
};
|
};
|
||||||
use cargo_test_support::{cross_compile, paths};
|
use cargo_test_support::{cross_compile, paths};
|
||||||
use cargo_test_support::{rustc_host, sleep_ms};
|
use cargo_test_support::{rustc_host, rustc_host_env, sleep_ms};
|
||||||
use std::fs;
|
use std::fs;
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
@ -377,7 +377,7 @@ fn cargo_test_failing_test_in_bin() {
|
|||||||
[COMPILING] foo v0.5.0 ([CWD])
|
[COMPILING] foo v0.5.0 ([CWD])
|
||||||
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
||||||
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
||||||
[ERROR] test failed, to rerun pass '--bin foo'",
|
[ERROR] test failed, to rerun pass `--bin foo`",
|
||||||
)
|
)
|
||||||
.with_stdout_contains(
|
.with_stdout_contains(
|
||||||
"
|
"
|
||||||
@ -426,7 +426,7 @@ fn cargo_test_failing_test_in_test() {
|
|||||||
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
||||||
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
||||||
[RUNNING] [..] (target/debug/deps/footest-[..][EXE])
|
[RUNNING] [..] (target/debug/deps/footest-[..][EXE])
|
||||||
[ERROR] test failed, to rerun pass '--test footest'",
|
[ERROR] test failed, to rerun pass `--test footest`",
|
||||||
)
|
)
|
||||||
.with_stdout_contains("running 0 tests")
|
.with_stdout_contains("running 0 tests")
|
||||||
.with_stdout_contains(
|
.with_stdout_contains(
|
||||||
@ -464,7 +464,7 @@ fn cargo_test_failing_test_in_lib() {
|
|||||||
[COMPILING] foo v0.5.0 ([CWD])
|
[COMPILING] foo v0.5.0 ([CWD])
|
||||||
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
||||||
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
||||||
[ERROR] test failed, to rerun pass '--lib'",
|
[ERROR] test failed, to rerun pass `--lib`",
|
||||||
)
|
)
|
||||||
.with_stdout_contains(
|
.with_stdout_contains(
|
||||||
"\
|
"\
|
||||||
@ -2466,19 +2466,20 @@ fn no_fail_fast() {
|
|||||||
.build();
|
.build();
|
||||||
p.cargo("test --no-fail-fast")
|
p.cargo("test --no-fail-fast")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stderr_contains(
|
.with_stderr(
|
||||||
"\
|
"\
|
||||||
[COMPILING] foo v0.0.1 ([..])
|
[COMPILING] foo v0.0.1 [..]
|
||||||
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
|
[FINISHED] test [..]
|
||||||
[RUNNING] [..] (target/debug/deps/foo-[..][EXE])
|
[RUNNING] unittests src/lib.rs (target/debug/deps/foo[..])
|
||||||
[RUNNING] [..] (target/debug/deps/test_add_one-[..][EXE])",
|
[RUNNING] tests/test_add_one.rs (target/debug/deps/test_add_one[..])
|
||||||
|
[ERROR] test failed, to rerun pass `--test test_add_one`
|
||||||
|
[RUNNING] tests/test_sub_one.rs (target/debug/deps/test_sub_one[..])
|
||||||
|
[DOCTEST] foo
|
||||||
|
[ERROR] 1 target failed:
|
||||||
|
`--test test_add_one`
|
||||||
|
",
|
||||||
)
|
)
|
||||||
.with_stdout_contains("running 0 tests")
|
.with_stdout_contains("running 0 tests")
|
||||||
.with_stderr_contains(
|
|
||||||
"\
|
|
||||||
[RUNNING] [..] (target/debug/deps/test_sub_one-[..][EXE])
|
|
||||||
[DOCTEST] foo",
|
|
||||||
)
|
|
||||||
.with_stdout_contains("test result: FAILED. [..]")
|
.with_stdout_contains("test result: FAILED. [..]")
|
||||||
.with_stdout_contains("test sub_one_test ... ok")
|
.with_stdout_contains("test sub_one_test ... ok")
|
||||||
.with_stdout_contains_n("test [..] ... ok", 3)
|
.with_stdout_contains_n("test [..] ... ok", 3)
|
||||||
@ -3576,10 +3577,7 @@ fn test_hint_not_masked_by_doctest() {
|
|||||||
.with_status(101)
|
.with_status(101)
|
||||||
.with_stdout_contains("test this_fails ... FAILED")
|
.with_stdout_contains("test this_fails ... FAILED")
|
||||||
.with_stdout_contains("[..]this_works (line [..]ok")
|
.with_stdout_contains("[..]this_works (line [..]ok")
|
||||||
.with_stderr_contains(
|
.with_stderr_contains("[ERROR] test failed, to rerun pass `--test integ`")
|
||||||
"[ERROR] test failed, to rerun pass \
|
|
||||||
'--test integ'",
|
|
||||||
)
|
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3590,24 +3588,131 @@ fn test_hint_workspace_virtual() {
|
|||||||
"Cargo.toml",
|
"Cargo.toml",
|
||||||
r#"
|
r#"
|
||||||
[workspace]
|
[workspace]
|
||||||
members = ["a", "b"]
|
members = ["a", "b", "c"]
|
||||||
"#,
|
"#,
|
||||||
)
|
)
|
||||||
.file("a/Cargo.toml", &basic_manifest("a", "0.1.0"))
|
.file("a/Cargo.toml", &basic_manifest("a", "0.1.0"))
|
||||||
.file("a/src/lib.rs", "#[test] fn t1() {}")
|
.file("a/src/lib.rs", "#[test] fn t1() {}")
|
||||||
.file("b/Cargo.toml", &basic_manifest("b", "0.1.0"))
|
.file("b/Cargo.toml", &basic_manifest("b", "0.1.0"))
|
||||||
.file("b/src/lib.rs", "#[test] fn t1() {assert!(false)}")
|
.file("b/src/lib.rs", "#[test] fn t1() {assert!(false)}")
|
||||||
|
.file("c/Cargo.toml", &basic_manifest("c", "0.1.0"))
|
||||||
|
.file(
|
||||||
|
"c/src/lib.rs",
|
||||||
|
r#"
|
||||||
|
/// ```rust
|
||||||
|
/// assert_eq!(1, 2);
|
||||||
|
/// ```
|
||||||
|
pub fn foo() {}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"c/src/main.rs",
|
||||||
|
r#"
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_main() { assert_eq!(1, 2); }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"c/tests/t1.rs",
|
||||||
|
r#"
|
||||||
|
#[test]
|
||||||
|
fn from_int_test() { assert_eq!(1, 2); }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"c/examples/ex1.rs",
|
||||||
|
r#"
|
||||||
|
fn main() {}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn from_example() { assert_eq!(1, 2); }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
// This does not use #[bench] since it is unstable. #[test] works just
|
||||||
|
// the same for our purpose of checking the hint.
|
||||||
|
.file(
|
||||||
|
"c/benches/b1.rs",
|
||||||
|
r#"
|
||||||
|
#[test]
|
||||||
|
fn from_bench() { assert_eq!(1, 2); }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
// This depends on Units being sorted so that `b` fails first.
|
||||||
p.cargo("test")
|
p.cargo("test")
|
||||||
.with_stderr_contains("[ERROR] test failed, to rerun pass '-p b --lib'")
|
.with_stderr_unordered(
|
||||||
|
"\
|
||||||
|
[COMPILING] c v0.1.0 [..]
|
||||||
|
[COMPILING] a v0.1.0 [..]
|
||||||
|
[COMPILING] b v0.1.0 [..]
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/a[..])
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/b[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p b --lib`
|
||||||
|
",
|
||||||
|
)
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.run();
|
.run();
|
||||||
p.cargo("test")
|
p.cargo("test")
|
||||||
.cwd("b")
|
.cwd("b")
|
||||||
.with_stderr_contains("[ERROR] test failed, to rerun pass '--lib'")
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] unittests src/lib.rs ([ROOT]/foo/target/debug/deps/b[..])
|
||||||
|
[ERROR] test failed, to rerun pass `--lib`
|
||||||
|
",
|
||||||
|
)
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.run();
|
.run();
|
||||||
|
p.cargo("test --no-fail-fast")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/a[..])
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/b[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p b --lib`
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/c[..])
|
||||||
|
[RUNNING] unittests src/main.rs (target/debug/deps/c[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p c --bin c`
|
||||||
|
[RUNNING] tests/t1.rs (target/debug/deps/t1[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p c --test t1`
|
||||||
|
[DOCTEST] a
|
||||||
|
[DOCTEST] b
|
||||||
|
[DOCTEST] c
|
||||||
|
[ERROR] doctest failed, to rerun pass `-p c --doc`
|
||||||
|
[ERROR] 4 targets failed:
|
||||||
|
`-p b --lib`
|
||||||
|
`-p c --bin c`
|
||||||
|
`-p c --test t1`
|
||||||
|
`-p c --doc`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_status(101)
|
||||||
|
.run();
|
||||||
|
// Check others that are not in the default set.
|
||||||
|
p.cargo("test -p c --examples --benches --no-fail-fast")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] c v0.1.0 [..]
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] unittests src/lib.rs (target/debug/deps/c[..])
|
||||||
|
[RUNNING] unittests src/main.rs (target/debug/deps/c[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p c --bin c`
|
||||||
|
[RUNNING] benches/b1.rs (target/debug/deps/b1[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p c --bench b1`
|
||||||
|
[RUNNING] unittests examples/ex1.rs (target/debug/examples/ex1[..])
|
||||||
|
[ERROR] test failed, to rerun pass `-p c --example ex1`
|
||||||
|
[ERROR] 3 targets failed:
|
||||||
|
`-p c --bin c`
|
||||||
|
`-p c --bench b1`
|
||||||
|
`-p c --example ex1`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_status(101)
|
||||||
|
.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cargo_test]
|
#[cargo_test]
|
||||||
@ -3630,11 +3735,11 @@ fn test_hint_workspace_nonvirtual() {
|
|||||||
.build();
|
.build();
|
||||||
|
|
||||||
p.cargo("test --workspace")
|
p.cargo("test --workspace")
|
||||||
.with_stderr_contains("[ERROR] test failed, to rerun pass '-p a --lib'")
|
.with_stderr_contains("[ERROR] test failed, to rerun pass `-p a --lib`")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.run();
|
.run();
|
||||||
p.cargo("test -p a")
|
p.cargo("test -p a")
|
||||||
.with_stderr_contains("[ERROR] test failed, to rerun pass '-p a --lib'")
|
.with_stderr_contains("[ERROR] test failed, to rerun pass `-p a --lib`")
|
||||||
.with_status(101)
|
.with_status(101)
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
@ -4494,3 +4599,108 @@ fn test_workspaces_cwd() {
|
|||||||
.with_stdout_contains("test test_integration_deep_cwd ... ok")
|
.with_stdout_contains("test test_integration_deep_cwd ... ok")
|
||||||
.run();
|
.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn execution_error() {
|
||||||
|
// Checks the behavior when a test fails to launch.
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"tests/t1.rs",
|
||||||
|
r#"
|
||||||
|
#[test]
|
||||||
|
fn foo() {}
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
let key = format!("CARGO_TARGET_{}_RUNNER", rustc_host_env());
|
||||||
|
p.cargo("test")
|
||||||
|
.env(&key, "does_not_exist")
|
||||||
|
// The actual error is usually "no such file", but on Windows it has a
|
||||||
|
// custom message. Since matching against the error string produced by
|
||||||
|
// Rust is not very reliable, this just uses `[..]`.
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] foo v0.0.1 [..]
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] tests/t1.rs (target/debug/deps/t1[..])
|
||||||
|
error: test failed, to rerun pass `--test t1`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
could not execute process `does_not_exist [ROOT]/foo/target/debug/deps/t1[..]` (never executed)
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
[..]
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_status(101)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cargo_test]
|
||||||
|
fn nonzero_exit_status() {
|
||||||
|
// Tests for nonzero exit codes from tests.
|
||||||
|
let p = project()
|
||||||
|
.file(
|
||||||
|
"tests/t1.rs",
|
||||||
|
r#"
|
||||||
|
#[test]
|
||||||
|
fn t() { panic!("this is a normal error") }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.file(
|
||||||
|
"tests/t2.rs",
|
||||||
|
r#"
|
||||||
|
#[test]
|
||||||
|
fn t() { std::process::exit(4) }
|
||||||
|
"#,
|
||||||
|
)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
p.cargo("test --test t1")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] foo [..]
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] tests/t1.rs (target/debug/deps/t1[..])
|
||||||
|
error: test failed, to rerun pass `--test t1`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_stdout_contains("[..]this is a normal error[..]")
|
||||||
|
.with_status(101)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
p.cargo("test --test t2")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[COMPILING] foo v0.0.1 [..]
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] tests/t2.rs (target/debug/deps/t2[..])
|
||||||
|
error: test failed, to rerun pass `--test t2`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_status(4)
|
||||||
|
.run();
|
||||||
|
|
||||||
|
// no-fail-fast always uses 101
|
||||||
|
p.cargo("test --no-fail-fast")
|
||||||
|
.with_stderr(
|
||||||
|
"\
|
||||||
|
[FINISHED] test [..]
|
||||||
|
[RUNNING] tests/t1.rs (target/debug/deps/t1[..])
|
||||||
|
error: test failed, to rerun pass `--test t1`
|
||||||
|
[RUNNING] tests/t2.rs (target/debug/deps/t2[..])
|
||||||
|
error: test failed, to rerun pass `--test t2`
|
||||||
|
|
||||||
|
Caused by:
|
||||||
|
process didn't exit successfully: `[ROOT]/foo/target/debug/deps/t2[..]` (exit [..]: 4)
|
||||||
|
error: 2 targets failed:
|
||||||
|
`--test t1`
|
||||||
|
`--test t2`
|
||||||
|
",
|
||||||
|
)
|
||||||
|
.with_status(101)
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user