manager_test.cxx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. //
  2. // manager_test.cxx
  3. // graphics
  4. //
  5. // Created by Sam Jaffe on 6/6/19.
  6. // Copyright © 2019 Sam Jaffe. All rights reserved.
  7. //
  8. #include <gmock/gmock.h>
  9. #include "../src/helper.hpp"
  10. #include "game/graphics/manager.hpp"
  11. #include "game/graphics/material.hpp"
  12. #include "game/graphics/shader.hpp"
  13. #include "game/graphics/shader_program.hpp"
  14. #include "game/graphics/texture.hpp"
  15. #ifdef __APPLE__
  16. namespace env { namespace detail {
  17. extern void bundle(std::string const &);
  18. }}
  19. #endif
  20. using testing::AllOf;
  21. using testing::AnyNumber;
  22. using testing::Eq;
  23. using testing::Ge;
  24. using testing::Le;
  25. using testing::Ne;
  26. using testing::SizeIs;
  27. using testing::_;
  28. struct mock_manager_impl : graphics::manager {
  29. using shader = graphics::shader;
  30. using shader_program = graphics::shader_program;
  31. using texture = graphics::texture;
  32. MOCK_CONST_METHOD2(compile_shader,
  33. shader(graphics::shaders::type, std::string const &));
  34. MOCK_CONST_METHOD2(compile_program,
  35. shader_program(identity<shader>, identity<shader>));
  36. shader compile(graphics::shaders::type t, std::string const & s) const {
  37. return compile_shader(t, s);
  38. }
  39. shader_program compile(identity<shader> f, identity<shader> v) const {
  40. return compile_program(f, v);
  41. }
  42. MOCK_CONST_METHOD3(compile, texture(graphics::textures::format, math::vec2i,
  43. void const *));
  44. };
  45. template <typename T> identity<T> cast(unsigned int id) {
  46. return *reinterpret_cast<identity<T> *>(&id);
  47. }
  48. auto cast_p(unsigned int id) { return cast<graphics::shader_program>(id); }
  49. auto cast_s(unsigned int id) { return cast<graphics::shader>(id); }
  50. class ManagerTest : public testing::Test {
  51. public:
  52. void SetUp() override;
  53. mock_manager_impl mock;
  54. private:
  55. struct {
  56. graphics::texture operator()(math::vec2i const & sz) { return {++i, sz}; }
  57. unsigned int i{0};
  58. } next_tex;
  59. struct {
  60. graphics::shader operator()(graphics::shaders::type tp,
  61. std::string const & path) {
  62. return {++i, tp, path};
  63. }
  64. unsigned int i{0};
  65. } next_shader;
  66. struct {
  67. graphics::shader_program operator()(identity<graphics::shader> f,
  68. identity<graphics::shader> v) {
  69. return {++i, f, v};
  70. }
  71. unsigned int i{0};
  72. } next_program;
  73. };
  74. void ManagerTest::SetUp() {
  75. #ifdef __APPLE__
  76. env::detail::bundle("leumasjaffe.graphics-test");
  77. #endif
  78. using testing::Invoke;
  79. using testing::WithArg;
  80. // Start with a new set of IDs
  81. ON_CALL(mock, compile(_, _, _))
  82. .WillByDefault(WithArg<1>(Invoke(std::ref(next_tex))));
  83. ON_CALL(mock, compile_shader(_, _))
  84. .WillByDefault(Invoke(std::ref(next_shader)));
  85. ON_CALL(mock, compile_program(_, _))
  86. .WillByDefault(Invoke(std::ref(next_program)));
  87. }
  88. TEST_F(ManagerTest, DoesNotGreedilyCompile) {
  89. EXPECT_CALL(mock, compile(_, _, _)).Times(0);
  90. EXPECT_CALL(mock, compile_shader(_, _)).Times(0);
  91. EXPECT_CALL(mock, compile_program(_, _)).Times(0);
  92. }
  93. using MaterialTest = ManagerTest;
  94. // TEST_F(MaterialTest, ThrowsExceptionIfNoTexOrUniform) {
  95. // EXPECT_CALL(mock, compile(_, _, _)).Times(AnyNumber());
  96. // // TODO (sjaffe): Throw a specific exception here, since throw; kills us
  97. // EXPECT_ANY_THROW(mock.get(cast_p(1), "", ""));
  98. //}
  99. TEST_F(MaterialTest, GeneratesUniformTexturesIfNoTexFile) {
  100. using graphics::materials::uniform;
  101. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  102. EXPECT_NO_THROW(mock.get(cast_p(1), "", "u_normalMap"));
  103. }
  104. TEST_F(MaterialTest, CreatedMaterialCanBeRefetched) {
  105. // Three times and no more
  106. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  107. auto id_1 = mock.get(cast_p(1), "", "u_normalMap");
  108. auto id_2 = mock.get(cast_p(1), "", "u_normalMap");
  109. EXPECT_THAT(id_1, Eq(id_2));
  110. }
  111. TEST_F(MaterialTest, CanGetMaterialFromId) {
  112. auto material_id = mock.get(cast_p(1), "", "u_normalMap");
  113. auto material = mock.get(material_id);
  114. // Ensure that the material is the 'same one' i.e.
  115. // mock.get(mock.get(id)) == mock.get(id)
  116. EXPECT_THAT(material, Eq(material_id));
  117. }
  118. TEST_F(MaterialTest, UniformMaterialIsOneByOne) {
  119. using graphics::materials::uniform;
  120. auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  121. // Uniforms are always sized 1x1
  122. EXPECT_THAT(material.size, Eq(make_vector(1, 1)));
  123. }
  124. TEST_F(MaterialTest, UniformMaterialHasDataBindingToNormalTex) {
  125. using graphics::materials::uniform;
  126. auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  127. EXPECT_THAT(material.uniforms, SizeIs(1));
  128. // Because we never initialize any textures, we are within the first three
  129. // texture ID units.
  130. EXPECT_THAT(material.uniforms[0].texture.id, AllOf(Ge(1), Le(3)));
  131. // Test the mapping from "u_normalMap" to uniform::NORMAL
  132. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  133. }
  134. TEST_F(MaterialTest, DifferentProgramMakesDifferentMaterial) {
  135. using graphics::materials::uniform;
  136. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  137. auto id_1 = mock.get(cast_p(1), "", "u_normalMap");
  138. auto id_2 = mock.get(cast_p(2), "", "u_normalMap");
  139. EXPECT_THAT(id_1, Ne(id_2));
  140. }
  141. TEST_F(MaterialTest, DifferentProgramDoesntChangeUniformId) {
  142. using graphics::materials::uniform;
  143. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  144. auto mat_1 = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  145. auto mat_2 = mock.get(mock.get(cast_p(2), "", "u_normalMap"));
  146. EXPECT_THAT(mat_1.uniforms[0].texture, Eq(mat_2.uniforms[0].texture));
  147. }
  148. TEST_F(MaterialTest, TexFileSkipsUniformLoad) {
  149. using graphics::materials::uniform;
  150. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(1);
  151. auto material =
  152. mock.get(mock.get(cast_p(1), "resources/black.bmp", "u_normalMap"));
  153. EXPECT_THAT(material.uniforms, SizeIs(1));
  154. // Because we never initialize any textures, we are within the first three
  155. // texture ID units.
  156. EXPECT_THAT(material.uniforms[0].texture.id, Eq(1));
  157. // Test the mapping from "u_normalMap" to uniform::NORMAL
  158. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  159. }
  160. TEST_F(MaterialTest, MissingFileNoExcept) {
  161. EXPECT_NO_THROW(
  162. mock.get(mock.get(cast_p(1), "resources/missing.png", "u_normalMap")));
  163. }
  164. TEST_F(MaterialTest, MissingFileFallsBackOnUniform) {
  165. using graphics::materials::uniform;
  166. auto material =
  167. mock.get(mock.get(cast_p(1), "resources/missing.png", "u_normalMap"));
  168. EXPECT_THAT(material.uniforms, SizeIs(1));
  169. // Because we never initialize any textures, we are within the first three
  170. // texture ID units.
  171. EXPECT_THAT(material.uniforms[0].texture.id, AllOf(Ge(1), Le(3)));
  172. // Test the mapping from "u_normalMap" to uniform::NORMAL
  173. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  174. }
  175. using ShaderTest = ManagerTest;
  176. TEST_F(ShaderTest, CanCreateShaders) {
  177. using graphics::shaders::type;
  178. EXPECT_CALL(mock, compile_shader(_, _)).Times(1);
  179. EXPECT_NO_THROW(mock.get(type::FRAGMENT, "A"));
  180. }
  181. TEST_F(ShaderTest, CreatedShaderCanBeRefetched) {
  182. using graphics::shaders::type;
  183. EXPECT_CALL(mock, compile_shader(_, _)).Times(1);
  184. auto id_1 = mock.get(type::FRAGMENT, "A");
  185. auto id_2 = mock.get(type::FRAGMENT, "A");
  186. EXPECT_THAT(id_1, Eq(id_2));
  187. }
  188. TEST_F(ShaderTest, DifferentTypeMakesDifferentShader) {
  189. using graphics::shaders::type;
  190. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  191. auto id_1 = mock.get(type::FRAGMENT, "A");
  192. auto id_2 = mock.get(type::VERTEX, "A");
  193. EXPECT_THAT(id_1, Ne(id_2));
  194. }
  195. using ShaderProgramTest = ManagerTest;
  196. TEST_F(ShaderProgramTest, CreatingProgramCreatesTwoShaders) {
  197. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  198. EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1);
  199. EXPECT_NO_THROW(mock.get("A", "B"));
  200. }
  201. TEST_F(ShaderProgramTest, CreatedProgramCanBeRefetched) {
  202. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  203. EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1);
  204. auto id_1 = mock.get("A", "B");
  205. auto id_2 = mock.get("A", "B");
  206. EXPECT_THAT(id_1, Eq(id_2));
  207. }