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>> = const { 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 `#[gtest]`. 42 /// 43 /// **For internal use only. API stablility is not guaranteed!** 44 #[doc(hidden)] init_current_test_outcome()45 pub fn init_current_test_outcome() { 46 Self::with_current_test_outcome(|mut current_test_outcome| { 47 *current_test_outcome = Some(TestOutcome::Success); 48 }) 49 } 50 51 /// Evaluates the current test's [`TestOutcome`], producing a suitable 52 /// `Result`. 53 /// 54 /// The parameter `result` is the value returned by the test function 55 /// itself. This returns `Result::Err` with a `Display`-formatted string of 56 /// the error if `result` is `Result::Err`. 57 /// 58 /// Otherwise, this returns `Result::Err` precisely when a test failure has 59 /// been recorded with 60 /// [`and_log_failure`](crate::GoogleTestSupport::and_log_failure). 61 /// 62 /// **For internal use only. API stablility is not guaranteed!** 63 #[doc(hidden)] close_current_test_outcome<E: Display>( inner_result: Result<(), E>, ) -> Result<(), TestFailure>64 pub fn close_current_test_outcome<E: Display>( 65 inner_result: Result<(), E>, 66 ) -> Result<(), TestFailure> { 67 TestOutcome::with_current_test_outcome(|mut outcome| { 68 let outer_result = match &*outcome { 69 Some(TestOutcome::Success) => match inner_result { 70 Ok(()) => Ok(()), 71 Err(_) => Err(TestFailure), 72 }, 73 Some(TestOutcome::Failure) => Err(TestFailure), 74 None => { 75 panic!("No test context found. This indicates a bug in GoogleTest.") 76 } 77 }; 78 if let Err(fatal_assertion_failure) = inner_result { 79 println!("{fatal_assertion_failure}"); 80 } 81 *outcome = None; 82 outer_result 83 }) 84 } 85 86 /// Returns a `Result` corresponding to the outcome of the currently running 87 /// test. 88 #[track_caller] 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_test_context_present()120 pub(crate) fn ensure_test_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 gtest? 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 location: Location, 166 } 167 168 /// A code location. 169 /// 170 /// `std::panic::Location` does not provide a constructor, hence we cannot 171 /// construct a fake value. 172 /// 173 /// **For internal use only. API stablility is not guaranteed!** 174 #[doc(hidden)] 175 #[derive(Clone)] 176 enum Location { 177 Real(&'static std::panic::Location<'static>), 178 Fake { file: &'static str, line: u32, column: u32 }, 179 } 180 181 impl Display for Location { fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result182 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 183 match self { 184 Location::Real(l) => write!(f, "{}", l), 185 Location::Fake { file, line, column } => write!(f, "{}:{}:{}", file, line, column), 186 } 187 } 188 } 189 190 impl TestAssertionFailure { 191 /// Creates a new instance with the given `description`. 192 /// 193 /// **For internal use only. API stablility is not guaranteed!** 194 #[track_caller] create(description: String) -> Self195 pub fn create(description: String) -> Self { 196 Self { 197 description, 198 custom_message: None, 199 location: Location::Real(std::panic::Location::caller()), 200 } 201 } 202 203 /// Set `location`` to a fake value. 204 /// 205 /// **For internal use only. API stablility is not guaranteed!** with_fake_location(mut self, file: &'static str, line: u32, column: u32) -> Self206 pub fn with_fake_location(mut self, file: &'static str, line: u32, column: u32) -> Self { 207 self.location = Location::Fake { file, line, column }; 208 self 209 } 210 log(&self)211 pub(crate) fn log(&self) { 212 TestOutcome::fail_current_test(); 213 println!("{}", self); 214 } 215 } 216 217 impl Display for TestAssertionFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>218 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 219 writeln!(f, "{}", self.description)?; 220 if let Some(custom_message) = &self.custom_message { 221 writeln!(f, "{}", custom_message)?; 222 } 223 writeln!(f, " at {}", self.location) 224 } 225 } 226 227 // The standard Rust test harness outputs the TestAssertionFailure with the 228 // Debug trait. We want the output to be formatted, so we use a custom Debug 229 // implementation which defers to Display. 230 impl Debug for TestAssertionFailure { fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>231 fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { 232 Display::fmt(self, f) 233 } 234 } 235 236 impl<T: std::error::Error> From<T> for TestAssertionFailure { 237 #[track_caller] from(value: T) -> Self238 fn from(value: T) -> Self { 239 TestAssertionFailure::create(format!("{value}")) 240 } 241 } 242 243 #[cfg(feature = "proptest")] 244 impl From<TestAssertionFailure> for proptest::test_runner::TestCaseError { from(value: TestAssertionFailure) -> Self245 fn from(value: TestAssertionFailure) -> Self { 246 proptest::test_runner::TestCaseError::Fail(format!("{value}").into()) 247 } 248 } 249