// // scene_test.cxx // engine-test // // Created by Sam Jaffe on 7/12/19. // Copyright © 2019 Sam Jaffe. All rights reserved. // #include "game/engine/scene.hpp" #include #include #include "game/engine/entity.hpp" #include "game/engine/game_dispatch.hpp" #include "mock_renderer.h" using testing::Values; struct test_scene : engine::scene { using engine::scene::scene; void update(float) override { check_collisions(); } void render() override {} void handle_key_event(engine::event::key_event) override {} void handle_mouse_event(engine::event::mouse_event) override {} bool in_bounds(math::dim2::point c) const { return engine::scene::in_bounds(c); } bool in_bounds(engine::collidable * c) const { return engine::scene::in_bounds(c); } graphics::manager const & graphics_manager() const { return engine::scene::graphics_manager(); } void add_with(engine::collision_t t, engine::collidable & c) { colliders[t].push_back(&c); } void add_as(engine::collision_t t, engine::collidable & c) { collidables[t].push_back(&c); } }; struct mock_collidable : engine::collidable { mock_collidable(math::dim2::rectangle bounds) : engine::collidable({bounds, bounds, cast(1), {}}) {} MOCK_METHOD1(collide, void(engine::collidable const &)); }; class SceneTest : public testing::Test { protected: void SetUp() override; void TearDown() override; test_scene & scene() const { return *scene_; } engine::game_dispatch & game() const { return *dispatch_; } std::shared_ptr renderer_; std::shared_ptr dispatch_; std::shared_ptr scene_; }; void SceneTest::SetUp() { renderer_.reset(new stub_renderer); math::vec2 screen{{1600.f, 900.f}}; env::resize_screen(math::vec2i(screen)); dispatch_.reset(new engine::game_dispatch(renderer_)); scene_.reset(new test_scene("test", screen, dispatch_)); } void SceneTest::TearDown() { scene_.reset(); dispatch_.reset(); renderer_.reset(); } using testing::Eq; using testing::Ref; TEST_F(SceneTest, SharedGraphicsManagerWithDispatch) { EXPECT_THAT(&scene().graphics_manager(), &game().graphics_manager()); } TEST_F(SceneTest, WillCollideOverlappingObjects) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}; scene().add_with(1, one); scene().add_as(1, two); EXPECT_CALL(one, collide(Ref(two))).Times(1); scene().update(0.f); } TEST_F(SceneTest, NonOverlappingDontCollide) { mock_collidable one{{{{0.f, 0.f}}, {{1.f, 1.f}}}}; mock_collidable two{{{{2.f, 2.f}}, {{1.f, 1.f}}}}; scene().add_with(1, one); scene().add_as(1, two); EXPECT_CALL(one, collide(Ref(two))).Times(0); scene().update(0.f); } TEST_F(SceneTest, CollisionIsOneWay) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}; scene().add_with(1, one); scene().add_as(1, two); EXPECT_CALL(one, collide(Ref(two))).Times(1); EXPECT_CALL(two, collide(Ref(one))).Times(0); scene().update(0.f); } TEST_F(SceneTest, ObjectCannotCollideSelf) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}; scene().add_with(1, one); scene().add_as(1, one); EXPECT_CALL(one, collide(Ref(one))).Times(0); scene().update(0.f); } TEST_F(SceneTest, CollisionMatchesAsWithKeys) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}, three{bnds}; scene().add_with(2, one); scene().add_as(1, two); scene().add_as(2, three); EXPECT_CALL(one, collide(Ref(two))).Times(0); EXPECT_CALL(one, collide(Ref(three))).Times(1); scene().update(0.f); } TEST_F(SceneTest, ObjectCanBeInMultipleCollidesWithGroups) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}, three{bnds}; scene().add_with(1, one); scene().add_with(2, one); scene().add_as(1, two); scene().add_as(2, three); EXPECT_CALL(one, collide(Ref(two))).Times(1); EXPECT_CALL(one, collide(Ref(three))).Times(1); scene().update(0.f); } TEST_F(SceneTest, ObjectCanBeInMultipleCollidableAsGroups) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}, three{bnds}; scene().add_with(1, one); scene().add_with(2, three); scene().add_as(1, two); scene().add_as(2, two); EXPECT_CALL(one, collide(Ref(two))).Times(1); EXPECT_CALL(three, collide(Ref(two))).Times(1); scene().update(0.f); } // TODO: Solve double-collision bug? TEST_F(SceneTest, DoubleCollisionCanOccur) { math::dim2::rectangle bnds{{{0.f, 0.f}}, {{1.f, 1.f}}}; mock_collidable one{bnds}, two{bnds}; scene().add_with(1, one); scene().add_with(2, one); scene().add_as(1, two); scene().add_as(2, two); EXPECT_CALL(one, collide(Ref(two))).Times(2); scene().update(0.f); } struct BoundsTest : SceneTest, testing::WithParamInterface {}; TEST_F(BoundsTest, BoundsCheckIsPointInScreenZone) { // Confirm that the size is as expected... EXPECT_THAT(scene().size(), Eq(math::vec2(1600.f, 900.f))); // Lower-Left Corner EXPECT_FALSE(scene().in_bounds(math::vec2(-1.f, 0.f))); EXPECT_FALSE(scene().in_bounds(math::vec2(0.f, -1.f))); EXPECT_TRUE(scene().in_bounds(math::vec2(0.f, 0.f))); // Lower-Right Corner EXPECT_FALSE(scene().in_bounds(math::vec2(1600.f, -1.f))); EXPECT_FALSE(scene().in_bounds(math::vec2(1601.f, 0.f))); EXPECT_TRUE(scene().in_bounds(math::vec2(1600.f, 0.f))); // Upper-Right Corner EXPECT_FALSE(scene().in_bounds(math::vec2(1601.f, 900.f))); EXPECT_FALSE(scene().in_bounds(math::vec2(1600.f, 901.f))); EXPECT_TRUE(scene().in_bounds(math::vec2(1600.f, 900.f))); // Upper-Left Corner EXPECT_FALSE(scene().in_bounds(math::vec2(0.f, 901.f))); EXPECT_FALSE(scene().in_bounds(math::vec2(-1.f, 900.f))); EXPECT_TRUE(scene().in_bounds(math::vec2(0.f, 900.f))); } TEST_P(BoundsTest, BoundsCheckOnCollidableIsAnyPointInBounds) { math::dim2::square bounds = {GetParam(), 2.f}; engine::collidable collide{{{}, bounds, cast(1), {}}}; EXPECT_TRUE(scene().in_bounds(&collide)); } INSTANTIATE_TEST_SUITE_P(Collidable, BoundsTest, Values(math::vec2(-1.f, -1.f), math::vec2(1599.f, -1.f), math::vec2(1599.f, 899.f), math::vec2(-1.f, 899.f)));