1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
use crate::prelude::*;
use std::fmt::Debug;
use std::time::Duration;

#[cfg(any(test, feature = "use-mocks"))]
pub use self::mocks::*;

/// Trait used by [`WorldInteractor`].
/// Implementors of this trait provide the actual code used for the performed actions
pub trait Interactable: Debug {
    /// Returns read-only descriptions for all objects either completely
    /// contained or intersecting with the given area.
    fn objects_in_area(&self, area: Aabb) -> Snapshot<'_>;

    /// Returns the amount of time that passed since the last call
    /// to the `step` function of [`Simulation`]
    fn elapsed_time_in_update(&self) -> Duration;
}

#[cfg(any(test, feature = "use-mocks"))]
mod mocks {
    use super::*;
    use std::cell::RefCell;
    use std::thread::panicking;

    /// Mock for [`Interactable`]
    ///
    /// [`Interactable`]: ../trait.Interactable.html
    #[derive(Debug, Default)]
    pub struct InteractableMock<'a> {
        expect_objects_in_area_and_return: Option<(Aabb, Snapshot<'a>)>,
        expect_elapsed_time_in_update_and_return: Option<(Duration,)>,

        objects_in_area_was_called: RefCell<bool>,
        elapsed_time_in_update_was_called: RefCell<bool>,
    }

    impl<'a> InteractableMock<'a> {
        /// Construct a new `InteractableMock`
        pub fn new() -> Self {
            Default::default()
        }

        /// expect call to `objects_in_area`
        pub fn expect_objects_in_area_and_return(
            &mut self,
            area: Aabb,
            return_value: Snapshot<'a>,
        ) {
            self.expect_objects_in_area_and_return = Some((area, return_value))
        }

        /// expect call to `elapsed_time_in_update`
        pub fn expect_elapsed_time_in_update_and_return(&mut self, return_value: Duration) {
            self.expect_elapsed_time_in_update_and_return = Some((return_value,))
        }
    }

    impl<'a> Interactable for InteractableMock<'a> {
        fn objects_in_area(&self, area: Aabb) -> Snapshot<'_> {
            *self.objects_in_area_was_called.borrow_mut() = true;

            let (expected_area, return_value) = self
                .expect_objects_in_area_and_return
                .clone()
                .expect("objects_in_area() was called unexpectedly");

            assert_eq!(
                expected_area, area,
                "objects_in_area() was called with {:?}, expected {:?}",
                area, expected_area
            );

            return_value.clone()
        }

        fn elapsed_time_in_update(&self) -> Duration {
            *self.elapsed_time_in_update_was_called.borrow_mut() = true;

            let (return_value,) = self
                .expect_elapsed_time_in_update_and_return
                .expect("elapsed_time_in_update() was called unexpectedly");

            return_value
        }
    }

    impl<'a> Drop for InteractableMock<'a> {
        fn drop(&mut self) {
            if panicking() {
                return;
            }

            assert!(
                self.expect_objects_in_area_and_return.is_some()
                    == *self.objects_in_area_was_called.borrow(),
                "objects_in_area() was not called, but expected"
            );

            assert!(
                self.expect_elapsed_time_in_update_and_return.is_some()
                    == *self.elapsed_time_in_update_was_called.borrow(),
                "elapsed_time_in_update() was not called, but expected"
            );
        }
    }
}