Skip to main content

googletest/
fixtures.rs

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
15use std::{
16    any::{Any, TypeId},
17    collections::HashMap,
18    ops::{Deref, DerefMut},
19    sync::{Mutex, OnceLock},
20};
21
22/// Interface for structure to be set up and torn down as part of a test.
23/// Types implementing `Fixture` can be passed as a reference argument to a
24/// test function.
25///
26/// ```ignore
27/// struct MyFixture { ... }
28///
29/// impl Fixture for MyFixture { ... }
30///
31/// #[gtest]
32/// fn test_with_fixture(my_fixture: &MyFixture) {...}
33/// ```
34pub trait Fixture: Sized {
35    /// Factory method of the `Fixture`.
36    ///
37    /// This method is called by the test harness before the test case
38    /// that uses this fixture. If this method returns an `Err(...)`,
39    /// then the test case is not evaluated, automatically fails, and
40    /// only the fixtures previously set up are torn down.
41    fn set_up() -> crate::Result<Self>;
42
43    /// Clean up method for the fixture.
44    ///
45    /// This method is called by the test harness after the test case
46    /// that uses this fixture. If the `Fixture` has been set up, the
47    /// test harness will call this method, even if the test case failed
48    /// or panicked.
49    fn tear_down(self) -> crate::Result<()>;
50}
51
52/// Interface for structure to be set up before the test case.
53/// Types implementing `ConsumableFixture` can be passed by value to
54/// a test function.
55///
56/// ```ignore
57/// struct MyFixture { ... }
58///
59/// impl ConsumableFixture for MyFixture { ... }
60///
61/// #[gtest]
62/// fn test_with_fixture(my_fixture: MyFixture) {...}
63/// ```
64pub trait ConsumableFixture: Sized {
65    /// Factory method of the `ConsumableFixture`.
66    ///
67    /// This method is called by the test harness before the test case
68    /// that uses this fixture. If this method returns an `Err(...)`,
69    /// then the test case is not evaluated, automatically fails, and
70    /// only the fixtures previously set up are torn down.
71    fn set_up() -> crate::Result<Self>;
72}
73
74/// Generic adapter to implement `ConsumableFixture` on any type implementing
75/// `Default`.
76pub struct FixtureOf<T>(T);
77
78impl<T: Default> ConsumableFixture for FixtureOf<T> {
79    fn set_up() -> crate::Result<Self> {
80        Ok(Self(T::default()))
81    }
82}
83
84impl<T> Deref for FixtureOf<T> {
85    type Target = T;
86
87    fn deref(&self) -> &Self::Target {
88        &self.0
89    }
90}
91
92impl<T> DerefMut for FixtureOf<T> {
93    fn deref_mut(&mut self) -> &mut Self::Target {
94        &mut self.0
95    }
96}
97
98/// Interface for structure to be set up only once before all tests.
99/// Types implementing `StaticFixture` can be passed as a double referenced
100/// argument to a test function.
101///
102/// ```ignore
103/// struct MyFixture{ ... }
104///
105/// impl StaticFixture for MyFixture { ... }
106///
107/// #[gtest]
108/// fn test_with_fixture(my_fixture: &&MyFixture){...}
109/// ```
110pub trait StaticFixture: Sized + Sync + Send {
111    /// Factory method of the `StaticFixture`.
112    ///
113    /// This method is called by the test harness before the first test case
114    /// using this fixture. If this method returns an `Err(...)`, then every
115    /// test case using this fixture is not evaluated and automatically fails.
116    fn set_up_once() -> crate::Result<Self>;
117}
118
119impl<F: StaticFixture + 'static> Fixture for &'static F {
120    fn set_up() -> crate::Result<Self> {
121        static ONCE_FIXTURE_REPO: OnceLock<
122            Mutex<HashMap<TypeId, &'static (dyn Any + Sync + Send)>>,
123        > = OnceLock::new();
124        let mut map = ONCE_FIXTURE_REPO.get_or_init(|| Mutex::new(HashMap::new())).lock()?;
125        let any =
126            map.entry(TypeId::of::<F>()).or_insert_with(|| Box::leak(Box::new(F::set_up_once())));
127        match any.downcast_ref::<crate::Result<F>>() {
128            Some(Ok(fixture)) => Ok(fixture),
129            Some(Err(e)) => Err(e.clone()),
130            None => panic!("Downcast failed. This is a bug in GoogleTest Rust"),
131        }
132    }
133
134    // Note that this is `&F` being torn down, not `F`.
135    fn tear_down(self) -> crate::Result<()> {
136        Ok(())
137    }
138}
139
140#[cfg(test)]
141mod tests {
142
143    use std::sync::Once;
144
145    use super::FixtureOf;
146    use super::StaticFixture;
147    use crate as googletest;
148    use crate::prelude::*;
149    use crate::test;
150    use crate::Result;
151
152    #[test]
153    fn fixture_no_fixture() -> Result<()> {
154        Ok(())
155    }
156
157    struct AlwaysSucceed;
158
159    impl Fixture for AlwaysSucceed {
160        fn set_up() -> crate::Result<Self> {
161            Ok(Self)
162        }
163
164        fn tear_down(self) -> crate::Result<()> {
165            Ok(())
166        }
167    }
168
169    #[test]
170    fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> {
171        Ok(())
172    }
173
174    #[test]
175    fn fixture_three_fixtures(
176        _: &AlwaysSucceed,
177        _: &AlwaysSucceed,
178        _: &AlwaysSucceed,
179    ) -> Result<()> {
180        Ok(())
181    }
182
183    struct NotAFixture {
184        a_field: i32,
185    }
186
187    impl Default for NotAFixture {
188        fn default() -> Self {
189            Self { a_field: 33 }
190        }
191    }
192
193    #[test]
194    fn fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
195        verify_that!(not_a_fixture.a_field, eq(33))
196    }
197
198    #[test]
199    fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
200        not_a_fixture.a_field += 10;
201        verify_that!(not_a_fixture.a_field, eq(43))
202    }
203    struct PanickyFixture;
204
205    impl Fixture for PanickyFixture {
206        fn set_up() -> crate::Result<Self> {
207            Ok(Self)
208        }
209
210        fn tear_down(self) -> crate::Result<()> {
211            panic!("Whoooops");
212        }
213    }
214
215    #[test]
216    #[should_panic(expected = "Whoooops")]
217    fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) -> Result<()> {
218        Err(googletest::TestAssertionFailure::create("It must fail!".into()))
219    }
220
221    struct AbortIfNotTornDownFixture {
222        has_been_torn_down: bool,
223    }
224
225    impl Fixture for AbortIfNotTornDownFixture {
226        fn set_up() -> crate::Result<Self> {
227            Ok(Self { has_been_torn_down: false })
228        }
229        fn tear_down(mut self) -> crate::Result<()> {
230            self.has_been_torn_down = true;
231            Ok(())
232        }
233    }
234
235    impl Drop for AbortIfNotTornDownFixture {
236        fn drop(&mut self) {
237            if !self.has_been_torn_down {
238                eprintln!("AbortIfNotTornDownFixture was not torn down");
239                std::process::abort();
240            }
241        }
242    }
243
244    #[test]
245    #[should_panic(expected = "simple_fail")]
246    fn fixture_torn_down_after_test_panics(f: &AbortIfNotTornDownFixture) {
247        expect_that!(f.has_been_torn_down, eq(false));
248        panic!("simple_fail");
249    }
250
251    struct OnlyOnce;
252
253    impl StaticFixture for OnlyOnce {
254        fn set_up_once() -> crate::Result<Self> {
255            static ONCE: Once = Once::new();
256            assert!(!ONCE.is_completed());
257            ONCE.call_once(|| {});
258            Ok(Self)
259        }
260    }
261
262    #[test]
263    fn static_fixture_works(_: &&OnlyOnce) {}
264
265    #[test]
266    fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) {
267        // checks it points to the same memory address.
268        let once: *const OnlyOnce = *once;
269        let twice: *const OnlyOnce = *twice;
270        expect_eq!(once, twice);
271    }
272
273    struct AnotherStaticFixture;
274
275    impl StaticFixture for AnotherStaticFixture {
276        fn set_up_once() -> crate::Result<Self> {
277            Ok(Self)
278        }
279    }
280
281    #[test]
282    fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {}
283
284    struct FailingSetUp;
285
286    impl Fixture for FailingSetUp {
287        fn set_up() -> crate::Result<Self> {
288            Err(googletest::TestAssertionFailure::create("sad fixture".into()))
289        }
290
291        fn tear_down(self) -> crate::Result<()> {
292            unreachable!();
293        }
294    }
295
296    #[test]
297    #[should_panic(expected = "See failure output above")]
298    fn failing_fixture_causes_test_failure(_: &FailingSetUp) {
299        unreachable!()
300    }
301
302    struct FailingTearDown;
303
304    impl Fixture for FailingTearDown {
305        fn set_up() -> crate::Result<Self> {
306            Ok(Self)
307        }
308
309        fn tear_down(self) -> crate::Result<()> {
310            Err(googletest::TestAssertionFailure::create("It must fail!".into()))
311        }
312    }
313
314    #[test]
315    #[should_panic(expected = "See failure output above")]
316    fn failing_teardown_causes_test_failure(_: &FailingTearDown) {}
317}