Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proof of concept: implement test ignore-by-panic #96574

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions library/test/src/console.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,19 @@ impl ConsoleTestState {
let TestDesc { name, ignore_message, .. } = test;
format!(
"{} {}",
match *result {
match result {
TestResult::TrOk => "ok".to_owned(),
TestResult::TrFailed => "failed".to_owned(),
TestResult::TrFailedMsg(ref msg) => format!("failed: {msg}"),
TestResult::TrFailedMsg(msg) => format!("failed: {msg}"),
TestResult::TrIgnored => {
if let Some(msg) = ignore_message {
format!("ignored: {msg}")
} else {
"ignored".to_owned()
}
}
TestResult::TrBench(ref bs) => fmt_bench_samples(bs),
TestResult::TrIgnoredMsg(msg) => format!("ignored: {msg}"),
TestResult::TrBench(bs) => fmt_bench_samples(bs),
TestResult::TrTimedFail => "failed (time limit exceeded)".to_owned(),
},
name,
Expand Down Expand Up @@ -194,7 +195,7 @@ fn handle_test_result(st: &mut ConsoleTestState, completed_test: CompletedTest)
st.passed += 1;
st.not_failures.push((test, stdout));
}
TestResult::TrIgnored => st.ignored += 1,
TestResult::TrIgnored | TestResult::TrIgnoredMsg(_) => st.ignored += 1,
TestResult::TrBench(bs) => {
st.metrics.insert_metric(
test.name.as_slice(),
Expand Down
15 changes: 12 additions & 3 deletions library/test/src/formatters/json.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
} else {
None
};
match *result {
match result {
TestResult::TrOk => {
self.write_event("test", desc.name.as_slice(), "ok", exec_time, stdout, None)
}
Expand All @@ -111,7 +111,7 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
Some(r#""reason": "time limit exceeded""#),
),

TestResult::TrFailedMsg(ref m) => self.write_event(
TestResult::TrFailedMsg(m) => self.write_event(
"test",
desc.name.as_slice(),
"failed",
Expand All @@ -131,7 +131,16 @@ impl<T: Write> OutputFormatter for JsonFormatter<T> {
.as_deref(),
),

TestResult::TrBench(ref bs) => {
TestResult::TrIgnoredMsg(msg) => self.write_event(
"test",
desc.name.as_slice(),
"ignored",
exec_time,
stdout,
Some(&*msg),
),

TestResult::TrBench(bs) => {
let median = bs.ns_iter_summ.median as usize;
let deviation = (bs.ns_iter_summ.max - bs.ns_iter_summ.min) as usize;

Expand Down
2 changes: 1 addition & 1 deletion library/test/src/formatters/junit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ impl<T: Write> OutputFormatter for JunitFormatter<T> {
for (desc, result, duration) in std::mem::replace(&mut self.results, Vec::new()) {
let (class_name, test_name) = parse_class_name(&desc);
match result {
TestResult::TrIgnored => { /* no-op */ }
TestResult::TrIgnored | TestResult::TrIgnoredMsg(_) => { /* no-op */ }
TestResult::TrFailed => {
self.write_message(&*format!(
"<testcase classname=\"{}\" \
Expand Down
7 changes: 4 additions & 3 deletions library/test/src/formatters/pretty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ impl<T: Write> PrettyFormatter<T> {
self.write_short_result("FAILED", term::color::RED)
}

pub fn write_ignored(&mut self, message: Option<&'static str>) -> io::Result<()> {
pub fn write_ignored(&mut self, message: Option<&str>) -> io::Result<()> {
if let Some(message) = message {
self.write_short_result(&format!("ignored, {}", message), term::color::YELLOW)
} else {
Expand Down Expand Up @@ -215,11 +215,12 @@ impl<T: Write> OutputFormatter for PrettyFormatter<T> {
self.write_test_name(desc)?;
}

match *result {
match result {
TestResult::TrOk => self.write_ok()?,
TestResult::TrFailed | TestResult::TrFailedMsg(_) => self.write_failed()?,
TestResult::TrIgnored => self.write_ignored(desc.ignore_message)?,
TestResult::TrBench(ref bs) => {
TestResult::TrIgnoredMsg(msg) => self.write_ignored(Some(msg))?,
TestResult::TrBench(bs) => {
self.write_bench()?;
self.write_plain(&format!(": {}", fmt_bench_samples(bs)))?;
}
Expand Down
2 changes: 1 addition & 1 deletion library/test/src/formatters/terse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ impl<T: Write> OutputFormatter for TerseFormatter<T> {
TestResult::TrFailed | TestResult::TrFailedMsg(_) | TestResult::TrTimedFail => {
self.write_failed()
}
TestResult::TrIgnored => self.write_ignored(),
TestResult::TrIgnored | TestResult::TrIgnoredMsg(_) => self.write_ignored(),
TestResult::TrBench(ref bs) => {
if self.is_multithreaded {
self.write_test_name(desc)?;
Expand Down
33 changes: 29 additions & 4 deletions library/test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,31 @@ use options::{Concurrent, RunStrategy};
use test_result::*;
use time::TestExecTime;

/// Panic payload to indicate that a test should be marked as ignored, rather
/// than failed.
///
/// # Examples
///
/// ```
/// #![feature(test)]
/// extern crate test;
/// # use std::panic::panic_any;
/// # fn compute_should_ignore() -> bool { true }
/// # fn do_real_test() { unreachable!() }
///
/// if compute_should_ignore() {
/// panic_any(test::IgnoreTest {
/// reason: Some("".into())
/// });
/// } else {
/// do_real_test();
/// }
/// ```
#[derive(Debug, Clone, Default)]
pub struct IgnoreTest {
pub reason: Option<String>,
}

// Process exit code to be used to indicate test failures.
const ERROR_EXIT_CODE: i32 = 101;

Expand Down Expand Up @@ -683,10 +708,10 @@ fn run_test_in_spawned_subprocess(desc: TestDesc, testfn: Box<dyn FnOnce() + Sen
builtin_panic_hook(info);
}

if let TrOk = test_result {
process::exit(test_result::TR_OK);
} else {
process::exit(test_result::TR_FAILED);
match test_result {
TrOk => process::exit(test_result::TR_OK),
TrIgnored | TrIgnoredMsg(_) => process::exit(test_result::TR_IGNORED),
_ => process::exit(test_result::TR_FAILED),
}
});
let record_result2 = record_result.clone();
Expand Down
23 changes: 18 additions & 5 deletions library/test/src/test_result.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use super::bench::BenchSamples;
use super::options::ShouldPanic;
use super::time;
use super::types::TestDesc;
use super::IgnoreTest;

pub use self::TestResult::*;

Expand All @@ -12,13 +13,15 @@ pub use self::TestResult::*;
// it means.
pub const TR_OK: i32 = 50;
pub const TR_FAILED: i32 = 51;
pub const TR_IGNORED: i32 = 52;

#[derive(Debug, Clone, PartialEq)]
pub enum TestResult {
TrOk,
TrFailed,
TrFailedMsg(String),
TrIgnored,
TrIgnoredMsg(String),
TrBench(BenchSamples),
TrTimedFail,
}
Expand All @@ -32,8 +35,8 @@ pub fn calc_result<'a>(
exec_time: &Option<time::TestExecTime>,
) -> TestResult {
let result = match (&desc.should_panic, task_result) {
(&ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk,
(&ShouldPanic::YesWithMessage(msg), Err(ref err)) => {
(ShouldPanic::No, Ok(())) | (&ShouldPanic::Yes, Err(_)) => TestResult::TrOk,
(ShouldPanic::YesWithMessage(msg), Err(err)) => {
let maybe_panic_str = err
.downcast_ref::<String>()
.map(|e| &**e)
Expand All @@ -53,15 +56,24 @@ pub fn calc_result<'a>(
r#"expected panic with string value,
found non-string value: `{:?}`
expected substring: `{:?}`"#,
(**err).type_id(),
(*err).type_id(),
msg
))
}
}
(&ShouldPanic::Yes, Ok(())) | (&ShouldPanic::YesWithMessage(_), Ok(())) => {
(ShouldPanic::Yes, Ok(())) | (ShouldPanic::YesWithMessage(_), Ok(())) => {
TestResult::TrFailedMsg("test did not panic as expected".to_string())
}
_ => TestResult::TrFailed,
(ShouldPanic::No, Err(err)) => {
if let Some(ignore) = err.downcast_ref::<IgnoreTest>() {
match &ignore.reason {
Some(reason) => TestResult::TrIgnoredMsg(reason.clone()),
None => TestResult::TrIgnored,
}
} else {
TestResult::TrFailed
}
}
};

// If test is already failed (or allowed to fail), do not change the result.
Expand Down Expand Up @@ -89,6 +101,7 @@ pub fn get_result_from_exit_code(
let result = match code {
TR_OK => TestResult::TrOk,
TR_FAILED => TestResult::TrFailed,
TR_IGNORED => TestResult::TrIgnored,
_ => TestResult::TrFailedMsg(format!("got unexpected return code {code}")),
};

Expand Down
Loading