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