| // Copyright 2022 Google LLC |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| use std::cell::{RefCell, RefMut}; |
| use std::fmt::{Debug, Display, Error, Formatter}; |
| use std::thread_local; |
| |
| /// The outcome hitherto of running a test. |
| /// |
| /// This is kept as a running record as the test progresses. One can access it |
| /// with `TestOutcome::with_current_test_outcome`. |
| /// |
| /// **For internal use only. API stablility is not guaranteed!** |
| #[doc(hidden)] |
| pub enum TestOutcome { |
| /// The test ran or is currently running and no assertions have failed. |
| Success, |
| /// The test ran or is currently running and at least one assertion has |
| /// failed. |
| Failure, |
| } |
| |
| thread_local! { |
| static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = RefCell::new(None); |
| } |
| |
| impl TestOutcome { |
| /// Resets the current test's [`TestOutcome`]. |
| /// |
| /// This is intended only for use by the attribute macro |
| /// `#[googletest::test]`. |
| /// |
| /// **For internal use only. API stablility is not guaranteed!** |
| #[doc(hidden)] |
| pub fn init_current_test_outcome() { |
| Self::with_current_test_outcome(|mut current_test_outcome| { |
| *current_test_outcome = Some(TestOutcome::Success); |
| }) |
| } |
| |
| /// Evaluates the current test's [`TestOutcome`], producing a suitable |
| /// `Result`. |
| /// |
| /// The parameter `result` is the value returned by the test function |
| /// itself. This returns `Result::Err` with a `Display`-formatted string of |
| /// the error if `result` is `Result::Err`. |
| /// |
| /// Otherwise, this returns `Result::Err` precisely when a test failure has |
| /// been recorded with |
| /// [`and_log_failure`](crate::GoogleTestSupport::and_log_failure). |
| /// |
| /// **For internal use only. API stablility is not guaranteed!** |
| #[doc(hidden)] |
| pub fn close_current_test_outcome<E: Display>( |
| inner_result: Result<(), E>, |
| ) -> Result<(), TestFailure> { |
| TestOutcome::with_current_test_outcome(|mut outcome| { |
| let outer_result = match &*outcome { |
| Some(TestOutcome::Success) => match inner_result { |
| Ok(()) => Ok(()), |
| Err(_) => Err(TestFailure), |
| }, |
| Some(TestOutcome::Failure) => Err(TestFailure), |
| None => { |
| panic!("No test context found. This indicates a bug in GoogleTest.") |
| } |
| }; |
| if let Err(fatal_assertion_failure) = inner_result { |
| println!("{fatal_assertion_failure}"); |
| } |
| *outcome = None; |
| outer_result |
| }) |
| } |
| |
| /// Returns a `Result` corresponding to the outcome of the currently running |
| /// test. |
| pub(crate) fn get_current_test_outcome() -> Result<(), TestAssertionFailure> { |
| TestOutcome::with_current_test_outcome(|mut outcome| { |
| let outcome = outcome |
| .as_mut() |
| .expect("No test context found. This indicates a bug in GoogleTest."); |
| match outcome { |
| TestOutcome::Success => Ok(()), |
| TestOutcome::Failure => Err(TestAssertionFailure::create("Test failed".into())), |
| } |
| }) |
| } |
| |
| /// Records that the currently running test has failed. |
| fn fail_current_test() { |
| TestOutcome::with_current_test_outcome(|mut outcome| { |
| let outcome = outcome |
| .as_mut() |
| .expect("No test context found. This indicates a bug in GoogleTest."); |
| *outcome = TestOutcome::Failure; |
| }) |
| } |
| |
| /// Runs `action` with the [`TestOutcome`] for the currently running test. |
| /// |
| /// This is primarily intended for use by assertion macros like |
| /// `expect_that!`. |
| fn with_current_test_outcome<T>(action: impl FnOnce(RefMut<Option<TestOutcome>>) -> T) -> T { |
| CURRENT_TEST_OUTCOME.with(|current_test_outcome| action(current_test_outcome.borrow_mut())) |
| } |
| |
| /// Ensure that there is a test context present and panic if there is not. |
| pub(crate) fn ensure_text_context_present() { |
| TestOutcome::with_current_test_outcome(|outcome| { |
| outcome.as_ref().expect( |
| " |
| No test context found. |
| * Did you annotate the test with googletest::test? |
| * Is the assertion running in the original test thread? |
| ", |
| ); |
| }) |
| } |
| } |
| |
| /// A marking struct indicating that a test has failed. |
| /// |
| /// This exists to implement the [Error][std::error::Error] trait. It displays |
| /// to a message indicating that the actual test assertion failure messages are |
| /// in the text above. |
| pub struct TestFailure; |
| |
| impl std::error::Error for TestFailure {} |
| |
| impl std::fmt::Debug for TestFailure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
| writeln!(f, "See failure output above")?; |
| Ok(()) |
| } |
| } |
| |
| impl std::fmt::Display for TestFailure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
| writeln!(f, "See failure output above")?; |
| Ok(()) |
| } |
| } |
| |
| /// A report that a single test assertion failed. |
| /// |
| /// **For internal use only. API stablility is not guaranteed!** |
| #[doc(hidden)] |
| #[derive(Clone)] |
| pub struct TestAssertionFailure { |
| /// A human-readable formatted string describing the error. |
| pub description: String, |
| pub custom_message: Option<String>, |
| } |
| |
| impl TestAssertionFailure { |
| /// Creates a new instance with the given `description`. |
| /// |
| /// **For internal use only. API stablility is not guaranteed!** |
| pub fn create(description: String) -> Self { |
| Self { description, custom_message: None } |
| } |
| |
| pub(crate) fn log(&self) { |
| TestOutcome::fail_current_test(); |
| println!("{}", self); |
| } |
| } |
| |
| impl Display for TestAssertionFailure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
| writeln!(f, "{}", self.description)?; |
| if let Some(custom_message) = &self.custom_message { |
| writeln!(f, "{}", custom_message)?; |
| } |
| Ok(()) |
| } |
| } |
| |
| // The standard Rust test harness outputs the TestAssertionFailure with the |
| // Debug trait. We want the output to be formatted, so we use a custom Debug |
| // implementation which defers to Display. |
| impl Debug for TestAssertionFailure { |
| fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { |
| Display::fmt(self, f) |
| } |
| } |
| |
| impl<T: std::error::Error> From<T> for TestAssertionFailure { |
| fn from(value: T) -> Self { |
| TestAssertionFailure::create(format!("{value}")) |
| } |
| } |
| |
| #[cfg(feature = "proptest")] |
| impl From<TestAssertionFailure> for proptest::test_runner::TestCaseError { |
| fn from(value: TestAssertionFailure) -> Self { |
| proptest::test_runner::TestCaseError::Fail(format!("{value}").into()) |
| } |
| } |