• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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