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