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;
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/// ```ignore
30/// fn load_file_content_as_string() -> TestResult<String> {
31///     let file_stream = load_file().err_to_test_failure()?;
32///     Ok(file_stream.to_string())
33/// }
34/// ```
35///
36/// The `Err` variant contains a [`TestAssertionFailure`] which carries the data
37/// of the (fatal) assertion failure which generated this result. Non-fatal
38/// assertion failures, which log the failure and report the test as having
39/// failed but allow it to continue running, are not encoded in this type.
40pub type TestResult<T> = core::result::Result<T, TestAssertionFailure>;
41
42/// Alias for [TestResult] to ease porting from [googletest](https://docs.rs/googletest).
43#[cfg(feature = "googletest-compat")]
44#[cfg_attr(feature = "googletest-migrate", deprecated(note = "Use TestResult instead"))]
45pub type Result<T> = TestResult<T>;
46
47/// Returns a [`Result`] corresponding to the outcome of the currently running
48/// test.
49///
50/// This returns `Result::Err` precisely if the current test has recorded at
51/// least one test assertion failure via [`expect_that!`][crate::expect_that],
52/// [`expect_pred!`][crate::expect_pred], or [`TestResultExt::and_log_failure`].
53/// It can be used in concert with the `?` operator to continue execution of the
54/// test conditionally on there not having been any failure yet.
55///
56/// This requires the use of the [`#[test_that::test]`][crate::test] attribute
57/// macro.
58///
59/// ```
60/// # use test_that::prelude::*;
61/// # #[cfg(feature = "non-fatal-assertions")] {
62/// # /* Make sure this also compiles as a doctest.
63/// #[test_that::test]
64/// # */
65/// # fn foo() -> u32 { 1 }
66/// # fn bar() -> u32 { 2 }
67/// fn should_fail_and_not_execute_last_assertion() -> TestResult<()> {
68/// #   test_that::internal::test_outcome::TestOutcome::init_current_test_outcome();
69///     expect_that!(foo(), eq(2));     // May fail, but will not abort the test.
70///     expect_that!(bar(), gt(1));     // May fail, but will not abort the test.
71///     verify_current_test_outcome()?; // Aborts the test if one of the previous assertions failed.
72///     verify_that!(foo(), gt(0))      // Does not execute if the line above aborts.
73/// }
74/// # verify_that!(should_fail_and_not_execute_last_assertion(), err(displays_as(contains_substring("Test failed")))).unwrap();
75/// # }
76/// ```
77#[cfg(feature = "std")]
78pub fn verify_current_test_outcome() -> TestResult<()> {
79    TestOutcome::get_current_test_outcome()
80}
81
82/// Adds to `Result` support for Test That! functionality.
83pub trait TestResultExt {
84    /// If `self` is a `Result::Err`, writes to `stdout` a failure report
85    /// and marks the test failed. Otherwise, does nothing.
86    ///
87    /// This can be used for non-fatal test assertions, for example:
88    ///
89    /// ```
90    /// # use test_that::prelude::*;
91    /// # use test_that::internal::test_outcome::TestOutcome;
92    /// # #[cfg(feature = "std")]
93    /// # TestOutcome::init_current_test_outcome();
94    /// let actual = 42;
95    /// verify_that!(actual, eq(42)).and_log_failure();
96    ///                                  // Test still passing; nothing happens
97    /// verify_that!(actual, eq(10)).and_log_failure();
98    ///                          // Test now fails and failure output to stdout
99    /// verify_that!(actual, eq(100)).and_log_failure();
100    ///               // Test still fails and new failure also output to stdout
101    /// # #[cfg(feature = "std")]
102    /// # TestOutcome::close_current_test_outcome::<&str>(Ok(())).unwrap_err();
103    /// ```
104    fn and_log_failure(self);
105
106    /// Adds `message` to the logged failure message if `self` is a
107    /// `Result::Err`. Otherwise, does nothing.
108    ///
109    /// If this method is called more than once, only `message` from the last
110    /// invocation is output.
111    ///
112    /// For example:
113    ///
114    /// ```
115    /// # use test_that::prelude::*;
116    /// # fn should_fail() -> TestResult<()> {
117    /// let actual = 0;
118    /// verify_that!(actual, eq(42)).failure_message("Actual was wrong!")?;
119    /// # Ok(())
120    /// # }
121    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual was wrong"))))
122    /// #     .unwrap();
123    /// ```
124    ///
125    /// results in the following failure message:
126    ///
127    /// ```text
128    /// Expected: actual equal to 42
129    ///   but was: 0
130    /// Actual was wrong!
131    /// ```
132    ///
133    /// One can pass a `String` too:
134    ///
135    /// ```
136    /// # use test_that::prelude::*;
137    /// # fn should_fail() -> TestResult<()> {
138    /// let actual = 0;
139    /// verify_that!(actual, eq(42))
140    ///    .failure_message(format!("Actual {} was wrong!", actual))?;
141    /// # Ok(())
142    /// # }
143    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual 0 was wrong"))))
144    /// #     .unwrap();
145    /// ```
146    ///
147    /// However, consider using [`TestResultExt::with_failure_message`]
148    /// instead in that case to avoid unnecessary memory allocation when the
149    /// message is not needed.
150    fn failure_message(self, message: impl Into<String>) -> Self;
151
152    /// Adds the output of the closure `provider` to the logged failure message
153    /// if `self` is a `Result::Err`. Otherwise, does nothing.
154    ///
155    /// This is analogous to [`TestResultExt::failure_message`] but
156    /// only executes the closure `provider` if it actually produces the
157    /// message, thus saving possible memory allocation.
158    ///
159    /// ```
160    /// # use test_that::prelude::*;
161    /// # fn should_fail() -> TestResult<()> {
162    /// let actual = 0;
163    /// verify_that!(actual, eq(42))
164    ///    .with_failure_message(|| format!("Actual {} was wrong!", actual))?;
165    /// # Ok(())
166    /// # }
167    /// # verify_that!(should_fail(), err(displays_as(contains_substring("Actual 0 was wrong"))))
168    /// #     .unwrap();
169    /// ```
170    fn with_failure_message(self, provider: impl FnOnce() -> String) -> Self;
171}
172
173impl<T> TestResultExt for core::result::Result<T, TestAssertionFailure> {
174    fn and_log_failure(self) {
175        TestOutcome::ensure_text_context_present();
176        if let Err(failure) = self {
177            failure.log();
178        }
179    }
180
181    fn failure_message(mut self, message: impl Into<String>) -> Self {
182        if let Err(ref mut failure) = self {
183            failure.custom_message = Some(message.into());
184        }
185        self
186    }
187
188    fn with_failure_message(mut self, provider: impl FnOnce() -> String) -> Self {
189        if let Err(ref mut failure) = self {
190            failure.custom_message = Some(provider());
191        }
192        self
193    }
194}
195
196/// Provides an extension method for converting an arbitrary type into a
197/// [`Result`].
198///
199/// A type can implement this trait to provide an easy way to return immediately
200/// from a test in conjunction with the `?` operator. This is useful for
201/// [`Result`] types whose `Result::Err` variant does not implement
202/// [`std::error::Error`].
203///
204/// There is an implementation of this trait for [`anyhow::Error`] (which does
205/// not implement `std::error::Error`) when the `anyhow` feature is enabled.
206/// Importing this trait allows one to easily map [`anyhow::Error`] to a test
207/// failure:
208///
209/// ```ignore
210/// #[test]
211/// fn should_work() -> Result<()> {
212///     let value = something_which_can_fail().or_fail()?;
213///     ...
214/// }
215///
216/// fn something_which_can_fail() -> anyhow::Result<...> { ... }
217/// ```
218pub trait OrFailExt<T> {
219    /// Converts this instance into a [`Result`].
220    ///
221    /// Typically, the `Self` type is itself a [`core::result::Result`]. This
222    /// method should then map the `Err` variant to a [`TestAssertionFailure`]
223    /// and leave the `Ok` variant unchanged.
224    fn or_fail(self) -> TestResult<T>;
225}
226
227#[cfg(feature = "anyhow")]
228impl<T> OrFailExt<T> for core::result::Result<T, anyhow::Error> {
229    fn or_fail(self) -> core::result::Result<T, TestAssertionFailure> {
230        self.map_err(|e| TestAssertionFailure::create(alloc::format!("{e:#}")))
231    }
232}
233
234#[cfg(feature = "proptest")]
235impl<OkT, CaseT: core::fmt::Debug> OrFailExt<OkT>
236    for core::result::Result<OkT, proptest::test_runner::TestError<CaseT>>
237{
238    fn or_fail(self) -> core::result::Result<OkT, TestAssertionFailure> {
239        self.map_err(|e| TestAssertionFailure::create(alloc::format!("{e}")))
240    }
241}