// // manager_test.cxx // graphics // // Created by Sam Jaffe on 6/6/19. // Copyright © 2019 Sam Jaffe. All rights reserved. // #include #include "../src/helper.hpp" #include "game/graphics/manager.hpp" #include "game/graphics/material.hpp" #include "game/graphics/shader.hpp" #include "game/graphics/shader_program.hpp" #include "game/graphics/texture.hpp" using testing::AllOf; using testing::AnyNumber; using testing::Eq; using testing::Ge; using testing::Le; using testing::Ne; using testing::SizeIs; using testing::_; struct mock_manager_impl : graphics::manager { using shader = graphics::shader; using shader_program = graphics::shader_program; using texture = graphics::texture; MOCK_CONST_METHOD2(compile_shader, shader(graphics::shaders::type, std::string const &)); MOCK_CONST_METHOD2(compile_program, shader_program(identity, identity)); shader compile(graphics::shaders::type t, std::string const & s) const { return compile_shader(t, s); } shader_program compile(identity f, identity v) const { return compile_program(f, v); } MOCK_CONST_METHOD3(compile, texture(graphics::textures::format, math::vec2i, void const *)); }; template identity cast(unsigned int id) { return *reinterpret_cast *>(&id); } auto cast_p(unsigned int id) { return cast(id); } auto cast_s(unsigned int id) { return cast(id); } class ManagerTest : public testing::Test { public: void SetUp() override; mock_manager_impl mock; private: struct { graphics::texture operator()(math::vec2i const & sz) { return {++i, sz}; } unsigned int i{0}; } next_tex; struct { graphics::shader operator()(graphics::shaders::type tp, std::string const & path) { return {++i, tp, path}; } unsigned int i{0}; } next_shader; struct { graphics::shader_program operator()(identity f, identity v) { return {++i, f, v}; } unsigned int i{0}; } next_program; }; void ManagerTest::SetUp() { using testing::Invoke; using testing::WithArg; // Start with a new set of IDs ON_CALL(mock, compile(_, _, _)) .WillByDefault(WithArg<1>(Invoke(std::ref(next_tex)))); ON_CALL(mock, compile_shader(_, _)) .WillByDefault(Invoke(std::ref(next_shader))); ON_CALL(mock, compile_program(_, _)) .WillByDefault(Invoke(std::ref(next_program))); } TEST_F(ManagerTest, DoesNotGreedilyCompile) { EXPECT_CALL(mock, compile(_, _, _)).Times(0); EXPECT_CALL(mock, compile_shader(_, _)).Times(0); EXPECT_CALL(mock, compile_program(_, _)).Times(0); } using MaterialTest = ManagerTest; // TEST_F(MaterialTest, ThrowsExceptionIfNoTexOrUniform) { // EXPECT_CALL(mock, compile(_, _, _)).Times(AnyNumber()); // // TODO (sjaffe): Throw a specific exception here, since throw; kills us // EXPECT_ANY_THROW(mock.get(cast_p(1), "", "")); //} TEST_F(MaterialTest, GeneratesUniformTexturesIfNoTexFile) { using graphics::materials::uniform; EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3); EXPECT_NO_THROW(mock.get(cast_p(1), "", "u_normalMap")); } TEST_F(MaterialTest, CreatedMaterialCanBeRefetched) { // Three times and no more EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3); auto id_1 = mock.get(cast_p(1), "", "u_normalMap"); auto id_2 = mock.get(cast_p(1), "", "u_normalMap"); EXPECT_THAT(id_1, Eq(id_2)); } TEST_F(MaterialTest, CanGetMaterialFromId) { auto material_id = mock.get(cast_p(1), "", "u_normalMap"); auto material = mock.get(material_id); // Ensure that the material is the 'same one' i.e. // mock.get(mock.get(id)) == mock.get(id) EXPECT_THAT(material, Eq(material_id)); } TEST_F(MaterialTest, UniformMaterialIsOneByOne) { using graphics::materials::uniform; auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap")); // Uniforms are always sized 1x1 EXPECT_THAT(material.size, Eq(make_vector(1, 1))); } TEST_F(MaterialTest, UniformMaterialHasDataBindingToNormalTex) { using graphics::materials::uniform; auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap")); EXPECT_THAT(material.uniforms, SizeIs(1)); // Because we never initialize any textures, we are within the first three // texture ID units. EXPECT_THAT(material.uniforms[0].texture.id, AllOf(Ge(1), Le(3))); // Test the mapping from "u_normalMap" to uniform::NORMAL EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL)); } TEST_F(MaterialTest, DifferentProgramMakesDifferentMaterial) { using graphics::materials::uniform; EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3); auto id_1 = mock.get(cast_p(1), "", "u_normalMap"); auto id_2 = mock.get(cast_p(2), "", "u_normalMap"); EXPECT_THAT(id_1, Ne(id_2)); } TEST_F(MaterialTest, DifferentProgramDoesntChangeUniformId) { using graphics::materials::uniform; EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3); auto mat_1 = mock.get(mock.get(cast_p(1), "", "u_normalMap")); auto mat_2 = mock.get(mock.get(cast_p(2), "", "u_normalMap")); EXPECT_THAT(mat_1.uniforms[0].texture, Eq(mat_2.uniforms[0].texture)); } using ShaderTest = ManagerTest; TEST_F(ShaderTest, CanCreateShaders) { using graphics::shaders::type; EXPECT_CALL(mock, compile_shader(_, _)).Times(1); EXPECT_NO_THROW(mock.get(type::FRAGMENT, "A")); } TEST_F(ShaderTest, CreatedShaderCanBeRefetched) { using graphics::shaders::type; EXPECT_CALL(mock, compile_shader(_, _)).Times(1); auto id_1 = mock.get(type::FRAGMENT, "A"); auto id_2 = mock.get(type::FRAGMENT, "A"); EXPECT_THAT(id_1, Eq(id_2)); } TEST_F(ShaderTest, DifferentTypeMakesDifferentShader) { using graphics::shaders::type; EXPECT_CALL(mock, compile_shader(_, _)).Times(2); auto id_1 = mock.get(type::FRAGMENT, "A"); auto id_2 = mock.get(type::VERTEX, "A"); EXPECT_THAT(id_1, Ne(id_2)); } using ShaderProgramTest = ManagerTest; TEST_F(ShaderProgramTest, CreatingProgramCreatesTwoShaders) { EXPECT_CALL(mock, compile_shader(_, _)).Times(2); EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1); EXPECT_NO_THROW(mock.get("A", "B")); } TEST_F(ShaderProgramTest, CreatedProgramCanBeRefetched) { EXPECT_CALL(mock, compile_shader(_, _)).Times(2); EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1); auto id_1 = mock.get("A", "B"); auto id_2 = mock.get("A", "B"); EXPECT_THAT(id_1, Eq(id_2)); }