1 // Copyright 2022 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 use std::cell::{RefCell, RefMut}; 16 use std::fmt::{Debug, Display, Error, Formatter}; 17 use std::thread_local; 18 19 /// The outcome hitherto of running a test. 20 /// 21 /// This is kept as a running record as the test progresses. One can access it 22 /// with `TestOutcome::with_current_test_outcome`. 23 /// 24 /// **For internal use only. API stablility is not guaranteed!** 25 #[doc(hidden)] 26 pub enum TestOutcome { 27 /// The test ran or is currently running and no assertions have failed. 28 Success, 29 /// The test ran or is currently running and at least one assertion has 30 /// failed. 31 Failure, 32 } 33 34 thread_local! { 35 static CURRENT_TEST_OUTCOME: RefCell<Option<TestOutcome>> = RefCell::new(None); 36 } 37 38 impl TestOutcome { 39 /// Resets the current test's [`TestOutcome`]. 40 /// 41 /// This is intended only for use by the attribute macro 42 /// `#[googletest::test]`. 43 /// 44 /// **For internal use only. API stablility is not guaranteed!** 45 #[doc(hidden)] init_current_test_outcome()46 pub fn init_current_test_outcome() { 47 Self::with_current_test_outcome(|mut current_test_outcome| { 48 *current_test_outcome = Some(TestOutcome::Success); 49 }) 50 } 51 52 /// Evaluates the current test's [`TestOutcome`], producing a suitable 53 /// `Result`. 54 /// 55 /// The parameter `result` is the value returned by the test function 56 /// itself. This returns `Result::Err` with a `Display`-formatted string of 57 /// the error if `result` is `Result::Err`. 58 /// 59 /// Otherwise, this returns `Result::Err` precisely when a test failure has 60 /// been recorded with 61 /// [`and_log_failure`](crate::GoogleTestSupport::and_log_failure). 62 /// 63 /// **For internal use only. API stablility is not guaranteed!** 64 #[doc(hidden)] close_current_test_outcome<E: Display>( inner_result: Result<(), E>, ) -> Result<(), TestFailure>65 pub fn close_current_test_outcome<E: Display>( 66 inner_result: Result<(), E>, 67 ) -> Result<(), TestFailure> { 68 TestOutcome::with_current_test_outcome(|mut outcome| { 69 let outer_result = match &*outcome { 70 Some(TestOutcome::Success) => match inner_result { 71 Ok(()) => Ok(()), 72 Err(_) => Err(TestFailure), 73 }, 74 Some(TestOutcome::Failure) => Err(TestFailure), 75 None => { 76 panic!("No test context found. This indicates a bug in GoogleTest.") 77 } 78 }; 79 if let Err(fatal_assertion_failure) = inner_result { 80 println!("{fatal_assertion_failure}"); 81 } 82 *outcome = None; 83 outer_result 84 }) 85 } 86 87 /// Returns a `Result` corresponding to the outcome of the currently running 88 /// test. get_current_test_outcome() -> Result<(), TestAssertionFailure>89 pub(crate) fn get_current_test_outcome() -> Result<(), TestAssertionFailure> { 90 TestOutcome::with_current_test_outcome(|mut outcome| { 91 let outcome = outcome 92 .as_mut() 93 .expect("No test context found. This indicates a bug in GoogleTest."); 94 match outcome { 95 TestOutcome::Success => Ok(()), 96 TestOutcome::Failure => Err(TestAssertionFailure::create("Test failed".into())), 97 } 98 }) 99 } 100 101 /// Records that the currently running test has failed. fail_current_test()102 fn fail_current_test() { 103 TestOutcome::with_current_test_outcome(|mut outcome| { 104 let outcome = outcome 105 .as_mut() 106 .expect("No test context found. This indicates a bug in GoogleTest."); 107 *outcome = TestOutcome::Failure; 108 }) 109 } 110 111 /// Runs `action` with the [`TestOutcome`] for the currently running test. 112 /// 113 /// This is primarily intended for use by assertion macros like 114 /// `expect_that!`. with_current_test_outcome<T>(action: impl FnOnce(RefMut<Option<TestOutcome>>) -> T) -> T115 fn with_current_test_outcome<T>(action: impl FnOnce(RefMut<Option<TestOutcome>>) -> T) -> T { 116 CURRENT_TEST_OUTCOME.with(|current_test_outcome| action(current_test_outcome.borrow_mut())) 117 } 118 119 /// Ensure that there is a test context present and panic if there is not. ensure_text_context_present()120 pub(crate) fn ensure_text_context_present() { 121 TestOutcome::with_current_test_outcome(|outcome| { 122 outcome.as_ref().expect( 123 " 124 No test context found. 125 * Did you annotate the test with googletest::test? 126 * Is the assertion running in the original test thread? 127 ", 128 ); 129 }) 130 } 131 } 132 133 /// A marking struct indicating that a test has failed. 134 /// 135 /// This exists to implement the [Error][std::error::Error] trait. It displays 136 /// to a message indicating that the actual test assertion failure messages are 137 /// in the text above. 138 pub struct TestFailure; 139 140 impl std::error::Error for TestFailure {} 141 142 impl std::fmt::Debug for TestFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>143 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 144 writeln!(f, "See failure output above")?; 145 Ok(()) 146 } 147 } 148 149 impl std::fmt::Display for TestFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>150 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 151 writeln!(f, "See failure output above")?; 152 Ok(()) 153 } 154 } 155 156 /// A report that a single test assertion failed. 157 /// 158 /// **For internal use only. API stablility is not guaranteed!** 159 #[doc(hidden)] 160 #[derive(Clone)] 161 pub struct TestAssertionFailure { 162 /// A human-readable formatted string describing the error. 163 pub description: String, 164 pub custom_message: Option<String>, 165 } 166 167 impl TestAssertionFailure { 168 /// Creates a new instance with the given `description`. 169 /// 170 /// **For internal use only. API stablility is not guaranteed!** create(description: String) -> Self171 pub fn create(description: String) -> Self { 172 Self { description, custom_message: None } 173 } 174 log(&self)175 pub(crate) fn log(&self) { 176 TestOutcome::fail_current_test(); 177 println!("{}", self); 178 } 179 } 180 181 impl Display for TestAssertionFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>182 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 183 writeln!(f, "{}", self.description)?; 184 if let Some(custom_message) = &self.custom_message { 185 writeln!(f, "{}", custom_message)?; 186 } 187 Ok(()) 188 } 189 } 190 191 // The standard Rust test harness outputs the TestAssertionFailure with the 192 // Debug trait. We want the output to be formatted, so we use a custom Debug 193 // implementation which defers to Display. 194 impl Debug for TestAssertionFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>195 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 196 Display::fmt(self, f) 197 } 198 } 199 200 impl<T: std::error::Error> From<T> for TestAssertionFailure { from(value: T) -> Self201 fn from(value: T) -> Self { 202 TestAssertionFailure::create(format!("{value}")) 203 } 204 } 205 206 #[cfg(feature = "proptest")] 207 impl From<TestAssertionFailure> for proptest::test_runner::TestCaseError { from(value: TestAssertionFailure) -> Self208 fn from(value: TestAssertionFailure) -> Self { 209 proptest::test_runner::TestCaseError::Fail(format!("{value}").into()) 210 } 211 } 212