// Copyright 2022 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. use std::{ any::{Any, TypeId}, collections::HashMap, ops::{Deref, DerefMut}, sync::{Mutex, OnceLock}, }; /// Interface for structure to be set up and torn down as part of a test. /// Types implementing `Fixture` can be passed as a reference argument to a /// test function. /// /// ```ignore /// strct MyFixture { ... } /// /// impl Fixture for MyFixture { ... } /// /// #[gtest] /// fn test_with_fixture(my_fixture: &MyFixture) {...} /// ``` pub trait Fixture: Sized { /// Factory method of the `Fixture`. /// /// This method is called by the test harness before the test case /// If this method returns an `Err(...)`, then the test case is not /// evaluated and only the fixtures previously set up are torn down. fn set_up() -> crate::Result; /// Clean up method for the fixture. /// /// This method is called by the test harness after the test case. /// If the `Fixture` has been set up, the test harness will call this /// method, even if the test case failed or panicked. fn tear_down(self) -> crate::Result<()>; } /// Interface for structure to be set up before the test case. /// Types implementing `ConsumableFixture` can be passed by value to /// a test function. /// /// ```ignore /// strct MyFixture { ... } /// /// impl ConsumableFixture for MyFixture { ... } /// /// #[gtest] /// fn test_with_fixture(my_fixture: MyFixture) {...} /// ``` pub trait ConsumableFixture: Sized { /// Factory method of the `ConsumableFixture`. /// /// This method is called by the test harness before the test case. /// If this method returns an `Err(...)`, then the test case is not /// evaluated. fn set_up() -> crate::Result; } /// Generic adapter to implement `ConsumableFixture` on any type implementing /// `Default`. pub struct FixtureOf(T); impl ConsumableFixture for FixtureOf { fn set_up() -> crate::Result { Ok(Self(T::default())) } } impl Deref for FixtureOf { type Target = T; fn deref(&self) -> &Self::Target { &self.0 } } impl DerefMut for FixtureOf { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } } /// Interface for structure to be set up only once before all tests. /// Types implementing `StaticFixture` can be passed as a double referenced /// argument to a test function. /// /// ```ignore /// strct MyFixture{ ... } /// /// impl StaticFixture for MyFixture { ... } /// /// #[gtest] /// fn test_with_fixture(my_fixture: &&MyFixture){...} /// ``` pub trait StaticFixture: Sized + Sync + Send { /// Factory method of the `StaticFixture`. /// /// This method is called by the test harness before the first test case /// using this fixture. If this method returns an `Err(...)`, then every /// test cases using this fixture are not evaluated. fn set_up_once() -> crate::Result; } impl Fixture for &'static F { fn set_up() -> crate::Result { static ONCE_FIXTURE_REPO: OnceLock< Mutex>, > = OnceLock::new(); let mut map = ONCE_FIXTURE_REPO.get_or_init(|| Mutex::new(HashMap::new())).lock()?; let any = map.entry(TypeId::of::()).or_insert_with(|| Box::leak(Box::new(F::set_up_once()))); match any.downcast_ref::>() { Some(Ok(ref fixture)) => Ok(fixture), Some(Err(e)) => Err(e.clone()), None => panic!("Downcast failed. This is a bug in GoogleTest Rust"), } } // Note that this is `&F` being torn down, not `F`. fn tear_down(self) -> crate::Result<()> { Ok(()) } } #[cfg(test)] mod tests { use std::sync::Once; use super::FixtureOf; use super::StaticFixture; use crate as googletest; use crate::prelude::*; use crate::test; #[test] fn fixture_no_fixture() -> Result<()> { Ok(()) } struct AlwaysSucceed; impl Fixture for AlwaysSucceed { fn set_up() -> crate::Result { Ok(Self) } fn tear_down(self) -> crate::Result<()> { Ok(()) } } #[test] fn fixture_one_fixture(_: &AlwaysSucceed) -> Result<()> { Ok(()) } #[test] fn fixture_three_fixtures( _: &AlwaysSucceed, _: &AlwaysSucceed, _: &AlwaysSucceed, ) -> Result<()> { Ok(()) } struct NotAFixture { a_field: i32, } impl Default for NotAFixture { fn default() -> Self { Self { a_field: 33 } } } #[test] fn fixture_of_non_fixture(not_a_fixture: FixtureOf) -> Result<()> { verify_that!(not_a_fixture.a_field, eq(33)) } #[test] fn fixture_of_non_fixture_mut(mut not_a_fixture: FixtureOf) -> Result<()> { not_a_fixture.a_field += 10; verify_that!(not_a_fixture.a_field, eq(43)) } struct PanickyFixture; impl Fixture for PanickyFixture { fn set_up() -> crate::Result { Ok(Self) } fn tear_down(self) -> crate::Result<()> { panic!("Whoooops"); } } #[test] #[should_panic(expected = "Whoooops")] fn fixture_teardown_called_even_if_test_fail(_: &PanickyFixture) { panic!("Test failed"); } struct FailingTearDown; impl Fixture for FailingTearDown { fn set_up() -> crate::Result { Ok(Self) } fn tear_down(self) -> crate::Result<()> { Err(googletest::TestAssertionFailure::create("It must fail!".into())) } } struct OnlyOnce; impl StaticFixture for OnlyOnce { fn set_up_once() -> crate::Result { static ONCE: Once = Once::new(); assert!(!ONCE.is_completed()); ONCE.call_once(|| {}); Ok(Self) } } #[test] fn static_fixture_works(_: &&OnlyOnce) {} #[test] fn static_fixture_same_static_fixture_twice(once: &&OnlyOnce, twice: &&OnlyOnce) { // checks it points to the same memory address. let once: *const OnlyOnce = *once; let twice: *const OnlyOnce = *twice; expect_eq!(once, twice); } struct AnotherStaticFixture; impl StaticFixture for AnotherStaticFixture { fn set_up_once() -> crate::Result { Ok(Self) } } #[test] fn static_fixture_two_different_static_fixtures(_: &&OnlyOnce, _: &&AnotherStaticFixture) {} }