Skip to main content

test_that/
result.rs

1// Copyright 2022 Google LLC
2// Copyright 2026 Bradford Hovinen <bradford@hovinen.me>
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//      http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::internal::test_outcome::{TestAssertionFailure, TestOutcome};
17use alloc::string::{String, ToString as _};
18
19/// A `Result` whose `Err` variant indicates a test failure.
20///
21/// The assertions [`verify_that!`][crate::verify_that],
22/// [`verify_pred!`][crate::verify_pred], and [`fail!`][crate::fail] evaluate
23/// to `TestResult<()>`. A test function may return `TestResult<()>` in
24/// combination with those macros to abort immediately on assertion failure.
25///
26/// This can be used with subroutines which may cause the test to fatally fail
27/// and which return some value needed by the caller. For example:
28///
29/// ```
30/// # use test_that::prelude::*;
31/// fn load_file_content_as_string() -> TestResult<String> {
32///     let file_stream = load_file().or_fail()?;
33///     Ok(file_stream.to_string())
34/// }
35/// # fn load_file() -> Option<String> { None }
36/// ```
37///
38/// The `Err` variant contains a [`TestAssertionFailure`] which carries the data
39/// of the (fatal) assertion failure which generated this result. Non-fatal
40/// assertion failures, which log the failure and report the test as having
41/// failed but allow it to continue running, are not encoded in this type.
42pub type TestResult<T> = core::result::Result<T, TestAssertionFailure>;
43
44/// Alias for [TestResult] to ease porting from [googletest](https://docs.rs/googletest).
45#[cfg(feature = "googletest-compat")]
46#[cfg_attr(feature = "googletest-migrate", deprecated(note = "Use TestResult instead"))]
47pub type Result<T> = TestResult<T>;
48
49/// Returns a [`Result`] corresponding to the outcome of the currently running
50/// test.
51///
52/// This returns `Result::Err` precisely if the current test has recorded at
53/// least one test assertion failure via [`expect_that!`][crate::expect_that],
54/// [`expect_pred!`][crate::expect_pred], or [`TestResultExt::and_log_failure`].
55/// It can be used in concert with the `?` operator to continue execution of the
56/// test conditionally on there not having been any failure yet.
57///
58/// This requires the use of the [`#[test_that::test]`][crate::test] attribute
59/// macro.
60///
61/// ```
62/// # use test_that::prelude::*;
63/// # #[cfg(feature = "non-fatal-assertions")] {
64/// # /* Make sure this also compiles as a doctest.
65/// #[test_that::test]
66/// # */
67/// # fn foo() -> u32 { 1 }
68/// # fn bar() -> u32 { 2 }
69/// fn should_fail_and_not_execute_last_assertion() -> TestResult<()> {
70/// #   test_that::internal::test_outcome::TestOutcome::init_current_test_outcome();
71///     expect_that!(foo(), eq(2));     // May fail, but will not abort the test.
72///     expect_that!(bar(), gt(1));     // May fail, but will not abort the test.
73///     verify_current_test_outcome()?; // Aborts the test if one of the previous assertions failed.
74///     verify_that!(foo(), gt(0))      // Does not execute if the line above aborts.
75/// }
76/// # verify_that!(should_fail_and_not_execute_last_assertion(), err(displays_as(contains_substring("Test failed")))).unwrap();
77/// # }
78/// ```
79#[cfg(feature = "std")]
80pub fn verify_current_test_outcome() -> TestResult<()> {
81    TestOutcome::get_current_test_outcome()
82}
83
84/// Adds to `Result` support for Test That! functionality.
85pub trait TestResultExt {
86    /// If `self` is a `Result::Err`, writes to `stdout` a failure report
87    /// and marks the test failed. Otherwise, does nothing.
88    ///
89    /// This can be used for non-fatal test assertions, for example:
90    ///
91    /// ```
92    /// # use test_that::prelude::*;
93    /// # use test_that::internal::test_outcome::TestOutcome;
94    /// # #[cfg(feature = "std")]
95    /// # TestOutcome::init_current_test_outcome();
96    /// let actual = 42;
97    /// verify_that!(actual, eq(42)).and_log_failure();
98    ///                                  // Test still passing; nothing happens
99    /// verify_that!(actual, eq(10)).and_log_failure();
100    ///                          // Test now fails and failure output to stdout
101    /// verify_that!(actual, eq(100)).and_log_failure();
102    ///               // Test still fails and new failure also output to stdout
103    /// # #[cfg(feature = "std")]
104    /// # TestOutcome::close_current_test_outcome::<&str>(Ok(())).unwrap_err();
105    /// ```
106    fn and_log_failure(self);
107
108    /// Adds `message` to the logged failure message if `self` is a
109    /// `Result::Err`. Otherwise, does nothing.
110    ///
111    /// If this method is called more than once, only `message` from the last
112    /// invocation is output.
113    ///
114    /// For example:
115    ///
116    /// ```
117    /// # use test_that::prelude::*;
118    /// # fn should_fail() -> TestResult<()> {
119    /// let actual = 0;
120    /// verify_that!(actual, eq(42)).failure_message("Actual was wrong!")?;
121    /// # Ok(())
122    /// # }
123    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual was wrong"))))
124    /// #     .unwrap();
125    /// ```
126    ///
127    /// results in the following failure message:
128    ///
129    /// ```text
130    /// Expected: actual equal to 42
131    ///   but was: 0
132    /// Actual was wrong!
133    /// ```
134    ///
135    /// One can pass a `String` too:
136    ///
137    /// ```
138    /// # use test_that::prelude::*;
139    /// # fn should_fail() -> TestResult<()> {
140    /// let actual = 0;
141    /// verify_that!(actual, eq(42))
142    ///    .failure_message(format!("Actual {} was wrong!", actual))?;
143    /// # Ok(())
144    /// # }
145    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual 0 was wrong"))))
146    /// #     .unwrap();
147    /// ```
148    ///
149    /// However, consider using [`TestResultExt::with_failure_message`]
150    /// instead in that case to avoid unnecessary memory allocation when the
151    /// message is not needed.
152    fn failure_message(self, message: impl Into<String>) -> Self;
153
154    /// Adds the output of the closure `provider` to the logged failure message
155    /// if `self` is a `Result::Err`. Otherwise, does nothing.
156    ///
157    /// This is analogous to [`TestResultExt::failure_message`] but
158    /// only executes the closure `provider` if it actually produces the
159    /// message, thus saving possible memory allocation.
160    ///
161    /// ```
162    /// # use test_that::prelude::*;
163    /// # fn should_fail() -> TestResult<()> {
164    /// let actual = 0;
165    /// verify_that!(actual, eq(42))
166    ///    .with_failure_message(|| format!("Actual {} was wrong!", actual))?;
167    /// # Ok(())
168    /// # }
169    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual 0 was wrong"))))
170    /// #     .unwrap();
171    /// ```
172    fn with_failure_message(self, provider: impl FnOnce() -> String) -> Self;
173}
174
175impl<T> TestResultExt for core::result::Result<T, TestAssertionFailure> {
176    fn and_log_failure(self) {
177        TestOutcome::ensure_text_context_present();
178        if let Err(failure) = self {
179            failure.log();
180        }
181    }
182
183    fn failure_message(mut self, message: impl Into<String>) -> Self {
184        if let Err(ref mut failure) = self {
185            failure.custom_message = Some(message.into());
186        }
187        self
188    }
189
190    fn with_failure_message(mut self, provider: impl FnOnce() -> String) -> Self {
191        if let Err(ref mut failure) = self {
192            failure.custom_message = Some(provider());
193        }
194        self
195    }
196}
197
198/// Provides an extension method for converting an arbitrary type into a
199/// [`TestResult`].
200///
201/// A type can implement this trait to provide an easy way to return immediately
202/// from a test in conjunction with the `?` operator. This is useful for
203/// [`Option`] as well as [`Result`] types whose `Result::Err` variant does not
204/// implement [`std::error::Error`].
205///
206/// There is an implementation of this trait for [`anyhow::Error`] (which does
207/// not implement `std::error::Error`) when the `anyhow` feature is enabled.
208/// Importing this trait allows one to easily map [`anyhow::Error`] to a test
209/// failure.
210///
211/// This is also implemented for [`Option`].
212///
213/// See [`or_fail`][OrFailExt::or_fail] for usage examples.
214pub trait OrFailExt<T> {
215    /// Converts this instance into a [`TestResult`].
216    ///
217    /// Invoking this method allows direct use of the `?` operator in tests.
218    /// For example, in the case of [`Option`]:
219    ///
220    /// ```
221    /// use test_that::prelude::*;
222    ///
223    /// # /* So that this compiles in the doctest context
224    /// #[test]
225    /// # */
226    /// fn fails_due_to_missing_element() -> TestResult<()> {
227    ///     let empty_hash_map = std::collections::HashMap::<u32, u32>::new();
228    ///     let value = empty_hash_map.get(&0).or_fail()?;
229    ///     Ok(())
230    /// }
231    ///
232    /// fails_due_to_missing_element().unwrap_err();
233    /// ```
234    ///
235    /// In the case of [`anyhow::Error`]:
236    ///
237    /// ```
238    /// use test_that::prelude::*;
239    ///
240    /// # /* So that this compiles in the doctest context
241    /// #[test]
242    /// # */
243    /// # #[cfg(feature = "anyhow")]
244    /// fn fails_due_to_anyhow_error() -> TestResult<()> {
245    ///     something_which_can_fail().or_fail()?;
246    ///     Ok(())
247    /// }
248    ///
249    /// # #[cfg(feature = "anyhow")]
250    /// fn something_which_can_fail() -> anyhow::Result<()> {
251    ///     anyhow::bail!("An error")
252    /// }
253    ///
254    /// # #[cfg(feature = "anyhow")]
255    /// fails_due_to_anyhow_error().unwrap_err();
256    /// ```
257    ///
258    /// Typically, the `Self` type is itself a [`Result`] or an [`Option`]. This
259    /// method should then map `None` or the `Err` variant to a
260    /// [`TestAssertionFailure`] and leave the `Some` or `Ok` variant unchanged.
261    fn or_fail(self) -> TestResult<T>;
262}
263
264#[cfg(feature = "anyhow")]
265impl<T> OrFailExt<T> for core::result::Result<T, anyhow::Error> {
266    fn or_fail(self) -> core::result::Result<T, TestAssertionFailure> {
267        self.map_err(|e| TestAssertionFailure::create(alloc::format!("{e:#}")))
268    }
269}
270
271#[cfg(feature = "proptest")]
272impl<OkT, CaseT: core::fmt::Debug> OrFailExt<OkT>
273    for core::result::Result<OkT, proptest::test_runner::TestError<CaseT>>
274{
275    fn or_fail(self) -> core::result::Result<OkT, TestAssertionFailure> {
276        self.map_err(|e| TestAssertionFailure::create(alloc::format!("{e}")))
277    }
278}
279
280impl<T> OrFailExt<T> for core::option::Option<T> {
281    fn or_fail(self) -> core::result::Result<T, TestAssertionFailure> {
282        self.ok_or_else(|| {
283            TestAssertionFailure::create("Expected Option to be Some but was None".to_string())
284        })
285    }
286}