• 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::{
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 /// ```
34 pub 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.
set_up() -> crate::Result<Self>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.
tear_down(self) -> crate::Result<()>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 /// ```
62 pub 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.
set_up() -> crate::Result<Self>68     fn set_up() -> crate::Result<Self>;
69 }
70 
71 /// Generic adapter to implement `ConsumableFixture` on any type implementing
72 /// `Default`.
73 pub struct FixtureOf<T>(T);
74 
75 impl<T: Default> ConsumableFixture for FixtureOf<T> {
set_up() -> crate::Result<Self>76     fn set_up() -> crate::Result<Self> {
77         Ok(Self(T::default()))
78     }
79 }
80 
81 impl<T> Deref for FixtureOf<T> {
82     type Target = T;
83 
deref(&self) -> &Self::Target84     fn deref(&self) -> &Self::Target {
85         &self.0
86     }
87 }
88 
89 impl<T> DerefMut for FixtureOf<T> {
deref_mut(&mut self) -> &mut Self::Target90     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 /// ```
107 pub 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.
set_up_once() -> crate::Result<Self>113     fn set_up_once() -> crate::Result<Self>;
114 }
115 
116 impl<F: StaticFixture + 'static> Fixture for &'static F {
set_up() -> crate::Result<Self>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`.
tear_down(self) -> crate::Result<()>132     fn tear_down(self) -> crate::Result<()> {
133         Ok(())
134     }
135 }
136 
137 #[cfg(test)]
138 mod 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 
148     #[test]
fixture_no_fixture() -> Result<()>149     fn fixture_no_fixture() -> Result<()> {
150         Ok(())
151     }
152 
153     struct AlwaysSucceed;
154 
155     impl Fixture for AlwaysSucceed {
set_up() -> crate::Result<Self>156         fn set_up() -> crate::Result<Self> {
157             Ok(Self)
158         }
159 
tear_down(self) -> crate::Result<()>160         fn tear_down(self) -> crate::Result<()> {
161             Ok(())
162         }
163     }
164 
165     #[test]
fixture_one_fixture(_: &AlwaysSucceed) -> Result<()>166     fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> {
167         Ok(())
168     }
169 
170     #[test]
fixture_three_fixtures( _: &AlwaysSucceed, _: &AlwaysSucceed, _: &AlwaysSucceed, ) -> Result<()>171     fn fixture_three_fixtures(
172         _: &AlwaysSucceed,
173         _: &AlwaysSucceed,
174         _: &AlwaysSucceed,
175     ) -> Result<()> {
176         Ok(())
177     }
178 
179     struct NotAFixture {
180         a_field: i32,
181     }
182 
183     impl Default for NotAFixture {
default() -> Self184         fn default() -> Self {
185             Self { a_field: 33 }
186         }
187     }
188 
189     #[test]
fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()>190     fn fixture_of_non_fixture(not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
191         verify_that!(not_a_fixture.a_field, eq(33))
192     }
193 
194     #[test]
fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()>195     fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf<NotAFixture>) -> Result<()> {
196         not_a_fixture.a_field += 10;
197         verify_that!(not_a_fixture.a_field, eq(43))
198     }
199     struct PanickyFixture;
200 
201     impl Fixture for PanickyFixture {
set_up() -> crate::Result<Self>202         fn set_up() -> crate::Result<Self> {
203             Ok(Self)
204         }
205 
tear_down(self) -> crate::Result<()>206         fn tear_down(self) -> crate::Result<()> {
207             panic!("Whoooops");
208         }
209     }
210 
211     #[test]
212     #[should_panic(expected = "Whoooops")]
fixture_teardown_called_even_if_test_fail(_: &PanickyFixture)213     fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) {
214         panic!("Test failed");
215     }
216 
217     struct FailingTearDown;
218 
219     impl Fixture for FailingTearDown {
set_up() -> crate::Result<Self>220         fn set_up() -> crate::Result<Self> {
221             Ok(Self)
222         }
223 
tear_down(self) -> crate::Result<()>224         fn tear_down(self) -> crate::Result<()> {
225             Err(googletest::TestAssertionFailure::create("It must fail!".into()))
226         }
227     }
228 
229     struct OnlyOnce;
230 
231     impl StaticFixture for OnlyOnce {
set_up_once() -> crate::Result<Self>232         fn set_up_once() -> crate::Result<Self> {
233             static ONCE: Once = Once::new();
234             assert!(!ONCE.is_completed());
235             ONCE.call_once(|| {});
236             Ok(Self)
237         }
238     }
239 
240     #[test]
static_fixture_works(_: &&OnlyOnce)241     fn static_fixture_works(_: &&OnlyOnce) {}
242 
243     #[test]
static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce)244     fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) {
245         // checks it points to the same memory address.
246         let once: *const OnlyOnce = *once;
247         let twice: *const OnlyOnce = *twice;
248         expect_eq!(once, twice);
249     }
250 
251     struct AnotherStaticFixture;
252 
253     impl StaticFixture for AnotherStaticFixture {
set_up_once() -> crate::Result<Self>254         fn set_up_once() -> crate::Result<Self> {
255             Ok(Self)
256         }
257     }
258 
259     #[test]
static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture)260     fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {}
261 }
262