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/// strct 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    /// If this method returns an `Err(...)`, then the test case is not
39    /// evaluated and only the fixtures previously set up are torn down.
40    fn set_up() -> crate::Result<Self>;
41
42    /// Clean up method for the fixture.
43    ///
44    /// This method is called by the test harness after the test case.
45    /// If the `Fixture` has been set up, the test harness will call this
46    /// method, even if the test case failed or panicked.
47    fn tear_down(self) -> crate::Result<()>;
48}
49
50/// Interface for structure to be set up before the test case.
51/// Types implementing `ConsumableFixture` can be passed by value to
52/// a test function.
53///
54/// ```ignore
55/// strct MyFixture { ... }
56///
57/// impl ConsumableFixture for MyFixture { ... }
58///
59/// #[gtest]
60/// fn test_with_fixture(my_fixture: MyFixture) {...}
61/// ```
62pub trait ConsumableFixture: Sized {
63    /// Factory method of the `ConsumableFixture`.
64    ///
65    /// This method is called by the test harness before the test case.
66    /// If this method returns an `Err(...)`, then the test case is not
67    /// evaluated.
68    fn set_up() -> crate::Result<Self>;
69}
70
71/// Generic adapter to implement `ConsumableFixture` on any type implementing
72/// `Default`.
73pub struct FixtureOf<T>(T);
74
75impl<T: Default> ConsumableFixture for FixtureOf<T> {
76    fn set_up() -> crate::Result<Self> {
77        Ok(Self(T::default()))
78    }
79}
80
81impl<T> Deref for FixtureOf<T> {
82    type Target = T;
83
84    fn deref(&self) -> &Self::Target {
85        &self.0
86    }
87}
88
89impl<T> DerefMut for FixtureOf<T> {
90    fn deref_mut(&mut self) -> &mut Self::Target {
91        &mut self.0
92    }
93}
94
95/// Interface for structure to be set up only once before all tests.
96/// Types implementing `StaticFixture` can be passed as a double referenced
97/// argument to a test function.
98///
99/// ```ignore
100/// strct MyFixture{ ... }
101///
102/// impl StaticFixture for MyFixture { ... }
103///
104/// #[gtest]
105/// fn test_with_fixture(my_fixture: &&MyFixture){...}
106/// ```
107pub trait StaticFixture: Sized + Sync + Send {
108    /// Factory method of the `StaticFixture`.
109    ///
110    /// This method is called by the test harness before the first test case
111    /// using this fixture. If this method returns an `Err(...)`, then every
112    /// test cases using this fixture are not evaluated.
113    fn set_up_once() -> crate::Result<Self>;
114}
115
116impl<F: StaticFixture + 'static> Fixture for &'static F {
117    fn set_up() -> crate::Result<Self> {
118        static ONCE_FIXTURE_REPO: OnceLock<
119            Mutex<HashMap<TypeId, &'static (dyn Any + Sync + Send)>>,
120        > = OnceLock::new();
121        let mut map = ONCE_FIXTURE_REPO.get_or_init(|| Mutex::new(HashMap::new())).lock()?;
122        let any =
123            map.entry(TypeId::of::<F>()).or_insert_with(|| Box::leak(Box::new(F::set_up_once())));
124        match any.downcast_ref::<crate::Result<F>>() {
125            Some(Ok(ref fixture)) => Ok(fixture),
126            Some(Err(e)) => Err(e.clone()),
127            None => panic!("Downcast failed. This is a bug in GoogleTest Rust"),
128        }
129    }
130
131    // Note that this is `&F` being torn down, not `F`.
132    fn tear_down(self) -> crate::Result<()> {
133        Ok(())
134    }
135}
136
137#[cfg(test)]
138mod tests {
139
140    use std::sync::Once;
141
142    use super::FixtureOf;
143    use super::StaticFixture;
144    use crate as googletest;
145    use crate::prelude::*;
146    use crate::test;
147    use crate::Result;
148
149    #[test]
150    fn fixture_no_fixture() -> Result<()> {
151        Ok(())
152    }
153
154    struct AlwaysSucceed;
155
156    impl Fixture for AlwaysSucceed {
157        fn set_up() -> crate::Result<Self> {
158            Ok(Self)
159        }
160
161        fn tear_down(self) -> crate::Result<()> {
162            Ok(())
163        }
164    }
165
166    #[test]
167    fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> {
168        Ok(())
169    }
170
171    #[test]
172    fn fixture_three_fixtures(
173        _: &AlwaysSucceed,
174        _: &AlwaysSucceed,
175        _: &AlwaysSucceed,
176    ) -> Result<()> {
177        Ok(())
178    }
179
180    struct NotAFixture {
181        a_field: i32,
182    }
183
184    impl Default for NotAFixture {
185        fn default() -> Self {
186            Self { a_field: 33 }
187        }
188    }
189
190    #[test]
191    fn fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
192        verify_that!(not_a_fixture.a_field, eq(33))
193    }
194
195    #[test]
196    fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
197        not_a_fixture.a_field += 10;
198        verify_that!(not_a_fixture.a_field, eq(43))
199    }
200    struct PanickyFixture;
201
202    impl Fixture for PanickyFixture {
203        fn set_up() -> crate::Result<Self> {
204            Ok(Self)
205        }
206
207        fn tear_down(self) -> crate::Result<()> {
208            panic!("Whoooops");
209        }
210    }
211
212    #[test]
213    #[should_panic(expected = "Whoooops")]
214    fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) {
215        panic!("Test failed");
216    }
217
218    struct FailingTearDown;
219
220    impl Fixture for FailingTearDown {
221        fn set_up() -> crate::Result<Self> {
222            Ok(Self)
223        }
224
225        fn tear_down(self) -> crate::Result<()> {
226            Err(googletest::TestAssertionFailure::create("It must fail!".into()))
227        }
228    }
229
230    struct OnlyOnce;
231
232    impl StaticFixture for OnlyOnce {
233        fn set_up_once() -> crate::Result<Self> {
234            static ONCE: Once = Once::new();
235            assert!(!ONCE.is_completed());
236            ONCE.call_once(|| {});
237            Ok(Self)
238        }
239    }
240
241    #[test]
242    fn static_fixture_works(_: &&OnlyOnce) {}
243
244    #[test]
245    fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) {
246        // checks it points to the same memory address.
247        let once: *const OnlyOnce = *once;
248        let twice: *const OnlyOnce = *twice;
249        expect_eq!(once, twice);
250    }
251
252    struct AnotherStaticFixture;
253
254    impl StaticFixture for AnotherStaticFixture {
255        fn set_up_once() -> crate::Result<Self> {
256            Ok(Self)
257        }
258    }
259
260    #[test]
261    fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {}
262}