manager_test.cxx 9.4 KB

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