manager_test.cxx 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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::_;
  21. using testing::AllOf;
  22. using testing::AnyNumber;
  23. using testing::Eq;
  24. using testing::Ge;
  25. using testing::Le;
  26. using testing::Ne;
  27. using testing::SizeIs;
  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 TextureTest = ManagerTest;
  94. TEST_F(TextureTest, ThrowsOnNonExistantFile) {
  95. EXPECT_THROW(mock.get("resources/missing.png"), std::runtime_error);
  96. }
  97. TEST_F(TextureTest, NoExceptIfFileExists) {
  98. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(1);
  99. EXPECT_NO_THROW(mock.get("resources/black.bmp"));
  100. }
  101. TEST_F(TextureTest, CreatedTextureCanBeRefetched) {
  102. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(1);
  103. auto id_1 = mock.get("resources/black.bmp");
  104. auto id_2 = mock.get("resources/black.bmp");
  105. EXPECT_THAT(id_1, Eq(id_2));
  106. }
  107. TEST_F(TextureTest, CanGetTextureFromId) {
  108. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(1);
  109. auto texture_id = mock.get("resources/black.bmp");
  110. auto texture = mock.get(texture_id);
  111. EXPECT_THAT(texture, Eq(texture_id));
  112. }
  113. TEST_F(TextureTest, SizeOfTexturePassedIntoCompile) {
  114. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(0);
  115. EXPECT_CALL(mock, compile(_, make_vector(2, 2), _)).Times(1);
  116. mock.get("resources/black2.bmp");
  117. }
  118. using MaterialTest = ManagerTest;
  119. // TEST_F(MaterialTest, ThrowsExceptionIfNoTexOrUniform) {
  120. // EXPECT_CALL(mock, compile(_, _, _)).Times(AnyNumber());
  121. // // TODO (sjaffe): Throw a specific exception here, since throw; kills us
  122. // EXPECT_ANY_THROW(mock.get(cast_p(1), "", ""));
  123. //}
  124. TEST_F(MaterialTest, GeneratesUniformTexturesIfNoTexFile) {
  125. using graphics::materials::uniform;
  126. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  127. EXPECT_NO_THROW(mock.get(cast_p(1), "", "u_normalMap"));
  128. }
  129. TEST_F(MaterialTest, CreatedMaterialCanBeRefetched) {
  130. // Three times and no more
  131. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  132. auto id_1 = mock.get(cast_p(1), "", "u_normalMap");
  133. auto id_2 = mock.get(cast_p(1), "", "u_normalMap");
  134. EXPECT_THAT(id_1, Eq(id_2));
  135. }
  136. TEST_F(MaterialTest, CanGetMaterialFromId) {
  137. auto material_id = mock.get(cast_p(1), "", "u_normalMap");
  138. auto material = mock.get(material_id);
  139. // Ensure that the material is the 'same one' i.e.
  140. // mock.get(mock.get(id)) == mock.get(id)
  141. EXPECT_THAT(material, Eq(material_id));
  142. }
  143. TEST_F(MaterialTest, UniformMaterialIsOneByOne) {
  144. auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  145. // Uniforms are always sized 1x1
  146. EXPECT_THAT(material.size, Eq(make_vector(1, 1)));
  147. }
  148. TEST_F(MaterialTest, SizeCapturesTextureSize) {
  149. auto material =
  150. mock.get(mock.get(cast_p(1), "resources/black2.bmp", "u_normalMap"));
  151. EXPECT_THAT(material.size, Eq(make_vector(2, 2)));
  152. }
  153. TEST_F(MaterialTest, UniformMaterialHasDataBindingToNormalTex) {
  154. using graphics::materials::uniform;
  155. auto material = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  156. EXPECT_THAT(material.uniforms, SizeIs(1));
  157. // Because we never initialize any textures, we are within the first three
  158. // texture ID units.
  159. EXPECT_THAT(material.uniforms[0].texture.id, AllOf(Ge(1), Le(3)));
  160. // Test the mapping from "u_normalMap" to uniform::NORMAL
  161. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  162. }
  163. TEST_F(MaterialTest, DifferentProgramMakesDifferentMaterial) {
  164. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  165. auto id_1 = mock.get(cast_p(1), "", "u_normalMap");
  166. auto id_2 = mock.get(cast_p(2), "", "u_normalMap");
  167. EXPECT_THAT(id_1, Ne(id_2));
  168. }
  169. TEST_F(MaterialTest, DifferentProgramDoesntChangeUniformId) {
  170. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(3);
  171. auto mat_1 = mock.get(mock.get(cast_p(1), "", "u_normalMap"));
  172. auto mat_2 = mock.get(mock.get(cast_p(2), "", "u_normalMap"));
  173. EXPECT_THAT(mat_1.uniforms[0].texture, Eq(mat_2.uniforms[0].texture));
  174. }
  175. TEST_F(MaterialTest, TexFileSkipsUniformLoad) {
  176. using graphics::materials::uniform;
  177. EXPECT_CALL(mock, compile(_, make_vector(1, 1), _)).Times(1);
  178. auto material =
  179. mock.get(mock.get(cast_p(1), "resources/black.bmp", "u_normalMap"));
  180. EXPECT_THAT(material.uniforms, SizeIs(1));
  181. // Because we never initialize any textures, we are within the first three
  182. // texture ID units.
  183. EXPECT_THAT(material.uniforms[0].texture.id, Eq(1));
  184. // Test the mapping from "u_normalMap" to uniform::NORMAL
  185. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  186. }
  187. TEST_F(MaterialTest, MissingFileNoExcept) {
  188. EXPECT_NO_THROW(
  189. mock.get(mock.get(cast_p(1), "resources/missing.png", "u_normalMap")));
  190. }
  191. TEST_F(MaterialTest, MissingFileFallsBackOnUniform) {
  192. using graphics::materials::uniform;
  193. auto material =
  194. mock.get(mock.get(cast_p(1), "resources/missing.png", "u_normalMap"));
  195. EXPECT_THAT(material.uniforms, SizeIs(1));
  196. // Because we never initialize any textures, we are within the first three
  197. // texture ID units.
  198. EXPECT_THAT(material.uniforms[0].texture.id, AllOf(Ge(1), Le(3)));
  199. // Test the mapping from "u_normalMap" to uniform::NORMAL
  200. EXPECT_THAT(material.uniforms[0].uniform_id, Eq(uniform::NORMAL));
  201. }
  202. using ShaderTest = ManagerTest;
  203. TEST_F(ShaderTest, CanCreateShaders) {
  204. using graphics::shaders::type;
  205. EXPECT_CALL(mock, compile_shader(_, _)).Times(1);
  206. EXPECT_NO_THROW(mock.get(type::FRAGMENT, "A"));
  207. }
  208. TEST_F(ShaderTest, CreatedShaderCanBeRefetched) {
  209. using graphics::shaders::type;
  210. EXPECT_CALL(mock, compile_shader(_, _)).Times(1);
  211. auto id_1 = mock.get(type::FRAGMENT, "A");
  212. auto id_2 = mock.get(type::FRAGMENT, "A");
  213. EXPECT_THAT(id_1, Eq(id_2));
  214. }
  215. TEST_F(ShaderTest, DifferentTypeMakesDifferentShader) {
  216. using graphics::shaders::type;
  217. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  218. auto id_1 = mock.get(type::FRAGMENT, "A");
  219. auto id_2 = mock.get(type::VERTEX, "A");
  220. EXPECT_THAT(id_1, Ne(id_2));
  221. }
  222. using ShaderProgramTest = ManagerTest;
  223. TEST_F(ShaderProgramTest, CreatingProgramCreatesTwoShaders) {
  224. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  225. EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1);
  226. EXPECT_NO_THROW(mock.get("A", "B"));
  227. }
  228. TEST_F(ShaderProgramTest, CreatedProgramCanBeRefetched) {
  229. EXPECT_CALL(mock, compile_shader(_, _)).Times(2);
  230. EXPECT_CALL(mock, compile_program(cast_s(1), cast_s(2))).Times(1);
  231. auto id_1 = mock.get("A", "B");
  232. auto id_2 = mock.get("A", "B");
  233. EXPECT_THAT(id_1, Eq(id_2));
  234. }