2D Run-and-gun shooter inspired by One Man's Doomsday, Counter-Strike, and Metal Slug.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

470 lines
13 KiB

  1. #if defined IZ_MACOS
  2. #include <SDL.h>
  3. #endif
  4. #include <bdd-for-c.h>
  5. #include <subprojects/SDL/SDL_keyboard.mock.h>
  6. #include <subprojects/SDL/SDL_gamecontroller.mock.h>
  7. #include <subprojects/SDL/SDL_stdinc.mock.h>
  8. #include <subprojects/minIni/minIni.mock.h>
  9. #include <subprojects/portmidi/portmidi.mock.h>
  10. #include <stdinc/IZ_string.mock.h>
  11. #include <stdinc/IZ_stdlib.mock.h>
  12. #include <log/IZ_log.mock.h>
  13. #include <subprojects/ini-config/source/ini-config.mock.h>
  14. #include <game/input/IZ_keyboard.h>
  15. #include <game/input/IZ_gamecontroller.h>
  16. #include <game/input/IZ_midi.h>
  17. spec("input") {
  18. describe("gamecontroller") {
  19. describe("Initialize") {
  20. static IZ_GameControllerState state[IZ_PLAYERS];
  21. after_each() {
  22. mock_reset(IZ_memcpy);
  23. }
  24. after_each() {
  25. mock_reset(SDL_NumJoysticks);
  26. }
  27. after_each() {
  28. mock_reset(SDL_GameControllerOpen);
  29. }
  30. after_each() {
  31. mock_reset(INI_ConfigSave);
  32. }
  33. after_each() {
  34. mock_reset(INI_ConfigInitialize);
  35. }
  36. it("sets initial state") {
  37. IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  38. check(mock_is_called(IZ_memcpy), "Initial state not loaded.");
  39. check(mock_is_called(SDL_NumJoysticks), "Connected joysticks not checked.");
  40. }
  41. it("calls load method") {
  42. IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  43. check(mock_is_called(INI_ConfigInitialize), "Config load function not called.");
  44. }
  45. it("calls save method") {
  46. IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  47. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  48. }
  49. it("opens device handles") {
  50. mock_set_expected_calls(SDL_GameControllerOpen, MOCK_OPEN_JOYSTICKS);
  51. IZ_GameControllerInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  52. check(
  53. mock_get_expected_calls(SDL_GameControllerOpen) == mock_get_actual_calls(SDL_GameControllerOpen),
  54. "Call count mismatch for SDL_GameControllerOpen() (expected %u, received %u).",
  55. mock_get_expected_calls(SDL_GameControllerOpen),
  56. mock_get_actual_calls(SDL_GameControllerOpen)
  57. );
  58. }
  59. }
  60. describe("HandleEvents") {
  61. static SDL_Event e;
  62. static IZ_GameControllerState state[IZ_PLAYERS] = {};
  63. static IZ_Action action[IZ_PLAYERS] = {};
  64. u8 p;
  65. for (p = 0; p < IZ_PLAYERS; p += 1) {
  66. describe("on player %u", p) {
  67. describe("on axis motion events") {
  68. before_each() {
  69. state[p].config.axis_threshold = IZ_GAME_CONTROLLER_AXIS_THRESHOLD;
  70. }
  71. for (u8 i = 0; i < SDL_CONTROLLER_AXIS_MAX; i += 1) {
  72. static const char* axis_name;
  73. axis_name = SDL_GameControllerGetStringForAxis(i);
  74. it("handles %s positive motion activation", axis_name) {
  75. e.type = SDL_CONTROLLERAXISMOTION;
  76. e.caxis.axis = i;
  77. e.caxis.value = IZ_GAME_CONTROLLER_AXIS_THRESHOLD + 1;
  78. sprintf(state[p].config.control_mapping[0], "axis:+%s", axis_name);
  79. action[p] = 0;
  80. IZ_GameControllerHandleEvents(&state, &action, e);
  81. check(action[p] != 0, "Action not set.");
  82. }
  83. it("handles %s negative motion activation", axis_name) {
  84. e.type = SDL_CONTROLLERAXISMOTION;
  85. e.caxis.axis = i;
  86. e.caxis.value = -(IZ_GAME_CONTROLLER_AXIS_THRESHOLD + 1);
  87. sprintf(state[p].config.control_mapping[0], "axis:-%s", axis_name);
  88. action[p] = 0;
  89. IZ_GameControllerHandleEvents(&state, &action, e);
  90. check(action[p] != 0, "Action not set.");
  91. }
  92. it("handles %s positive motion deactivation", axis_name) {
  93. e.type = SDL_CONTROLLERAXISMOTION;
  94. e.caxis.axis = i;
  95. e.caxis.value = IZ_GAME_CONTROLLER_AXIS_THRESHOLD - 1;
  96. sprintf(state[p].config.control_mapping[0], "axis:+%s", axis_name);
  97. action[p] = 1;
  98. IZ_GameControllerHandleEvents(&state, &action, e);
  99. check(action[p] != 1, "Action not unset. %d", action[p]);
  100. }
  101. it("handles %s negative motion deactivation", axis_name) {
  102. e.type = SDL_CONTROLLERAXISMOTION;
  103. e.caxis.axis = i;
  104. e.caxis.value = -(IZ_GAME_CONTROLLER_AXIS_THRESHOLD - 1);
  105. sprintf(state[p].config.control_mapping[0], "axis:-%s", axis_name);
  106. action[p] = 1;
  107. IZ_GameControllerHandleEvents(&state, &action, e);
  108. check(action[p] != 1, "Action not unset. %d", action[p]);
  109. }
  110. }
  111. }
  112. describe("on button events") {
  113. for (u8 i = 0; i < SDL_CONTROLLER_BUTTON_MAX; i += 1) {
  114. static const char* button_name;
  115. button_name = SDL_GameControllerGetStringForButton(i);
  116. it("handles %s action activation", button_name) {
  117. e.type = SDL_CONTROLLERBUTTONDOWN;
  118. e.cbutton.button = i;
  119. sprintf(state[p].config.control_mapping[0], "button:%s", button_name);
  120. action[p] = 0;
  121. IZ_GameControllerHandleEvents(&state, &action, e);
  122. check(action[p] != 0, "Action not set.");
  123. }
  124. it("handles %s action deactivation", button_name) {
  125. e.type = SDL_CONTROLLERBUTTONUP;
  126. e.cbutton.button = i;
  127. sprintf(state[p].config.control_mapping[0], "button:%s", button_name);
  128. action[p] = 1;
  129. IZ_GameControllerHandleEvents(&state, &action, e);
  130. check(action[p] != 1, "Action not unset.");
  131. }
  132. }
  133. }
  134. }
  135. }
  136. }
  137. describe("SaveConfig") {
  138. static IZ_GameControllerState state[IZ_PLAYERS];
  139. after_each() {
  140. mock_reset(INI_ConfigSave);
  141. }
  142. before_each() {
  143. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  144. for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
  145. IZ_memcpy(
  146. state[p].config.control_mapping[i],
  147. IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH,
  148. IZ_GAME_CONTROLLER_DEFAULT_STATE[p].config.control_mapping[i],
  149. IZ_GAME_CONTROLLER_MAX_CONTROL_NAME_LENGTH
  150. );
  151. }
  152. }
  153. }
  154. it("calls save method") {
  155. IZ_GameControllerSaveConfig(&state, IZ_CONFIG_GAME_PATH);
  156. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  157. }
  158. }
  159. describe("Teardown") {
  160. static void* device = (void*) 1;
  161. static IZ_GameControllerState state[IZ_PLAYERS] = {};
  162. before_each() {
  163. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  164. state[p].device = device;
  165. }
  166. }
  167. after_each() {
  168. mock_reset(SDL_GameControllerClose);
  169. }
  170. it("closes opened devices") {
  171. mock_set_expected_calls(SDL_GameControllerClose, IZ_PLAYERS);
  172. IZ_GameControllerTeardown(&state);
  173. check(
  174. mock_get_expected_calls(SDL_GameControllerClose) == mock_get_actual_calls(SDL_GameControllerClose),
  175. "Call count mismatch for SDL_GameControllerClose() (expected %u, received %u).",
  176. mock_get_expected_calls(SDL_GameControllerClose),
  177. mock_get_actual_calls(SDL_GameControllerClose)
  178. );
  179. }
  180. }
  181. }
  182. describe("keyboard") {
  183. describe("Initialize") {
  184. static IZ_KeyboardState state[IZ_PLAYERS] = {};
  185. after_each() {
  186. mock_reset(IZ_memcpy);
  187. }
  188. after_each() {
  189. mock_reset(INI_ConfigInitialize);
  190. }
  191. after_each() {
  192. mock_reset(INI_ConfigSave);
  193. }
  194. before_each() {
  195. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  196. for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
  197. state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  198. }
  199. }
  200. }
  201. it("sets initial state") {
  202. IZ_KeyboardInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  203. check(mock_is_called(IZ_memcpy), "Initial state not loaded.");
  204. }
  205. it("calls load method") {
  206. IZ_KeyboardInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  207. check(mock_is_called(INI_ConfigInitialize), "Config load function not called.");
  208. }
  209. it("calls save method") {
  210. IZ_KeyboardInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  211. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  212. }
  213. }
  214. describe("HandleEvents") {
  215. static SDL_Event e;
  216. static IZ_KeyboardState state[IZ_PLAYERS] = {};
  217. static IZ_Action action[IZ_PLAYERS] = {};
  218. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  219. describe("on player %u", p) {
  220. for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
  221. it("handles %s action activation", IZ_ACTION_NAMES[i]) {
  222. e.type = SDL_KEYDOWN;
  223. e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  224. state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  225. action[p] = 0;
  226. IZ_KeyboardHandleEvents(&state, &action, e);
  227. check(
  228. action[p] == (0x1 << i),
  229. "Action not set."
  230. );
  231. }
  232. it("handles %s action deactivation", IZ_ACTION_NAMES[i]) {
  233. e.type = SDL_KEYUP;
  234. e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  235. state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  236. action[p] = ~0;
  237. IZ_KeyboardHandleEvents(&state, &action, e);
  238. check(
  239. !(action[p] & (0x1 << i)),
  240. "Action not unset."
  241. );
  242. }
  243. }
  244. }
  245. }
  246. }
  247. describe("SaveConfig") {
  248. static IZ_KeyboardState state[IZ_PLAYERS] = {};
  249. after_each() {
  250. mock_reset(INI_ConfigSave);
  251. }
  252. before_each() {
  253. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  254. for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
  255. state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i];
  256. }
  257. }
  258. }
  259. it("calls save method") {
  260. IZ_KeyboardSaveConfig(&state, IZ_CONFIG_GAME_PATH);
  261. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  262. }
  263. }
  264. }
  265. describe("midi") {
  266. describe("Initialize") {
  267. static IZ_MIDIInputState state[IZ_PLAYERS];
  268. after_each() {
  269. mock_reset(IZ_memcpy);
  270. }
  271. after_each() {
  272. mock_reset(Pm_CountDevices);
  273. }
  274. after_each() {
  275. mock_reset(Pm_OpenInput);
  276. }
  277. after_each() {
  278. mock_reset(INI_ConfigSave);
  279. }
  280. after_each() {
  281. mock_reset(INI_ConfigInitialize);
  282. }
  283. after_each() {
  284. mock_reset(INI_ConfigSave);
  285. }
  286. after_each() {
  287. mock_reset(INI_ConfigInitialize);
  288. }
  289. it("sets initial state") {
  290. IZ_MIDIInputInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  291. check(mock_is_called(IZ_memcpy), "Initial state not loaded.");
  292. check(mock_is_called(Pm_CountDevices), "Connected MIDI devices not checked.");
  293. }
  294. it("calls load method") {
  295. IZ_MIDIInputInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  296. check(mock_is_called(INI_ConfigInitialize), "Config load function not called.");
  297. }
  298. it("calls save method") {
  299. IZ_MIDIInputInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  300. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  301. }
  302. it("opens device handles") {
  303. mock_set_expected_calls(Pm_OpenInput, MOCK_OPEN_JOYSTICKS);
  304. IZ_MIDIInputInitialize(&state, IZ_CONFIG_GAME_PATH, 0, NULL);
  305. check(
  306. mock_get_expected_calls(Pm_OpenInput) == mock_get_actual_calls(Pm_OpenInput),
  307. "Call count mismatch for Pm_OpenInput() (expected %u, received %u).",
  308. mock_get_expected_calls(Pm_OpenInput),
  309. mock_get_actual_calls(Pm_OpenInput)
  310. );
  311. }
  312. }
  313. describe("SaveConfig") {
  314. static IZ_MIDIInputState state[IZ_PLAYERS];
  315. after_each() {
  316. mock_reset(INI_ConfigSave);
  317. }
  318. after_each() {
  319. mock_reset(INI_ConfigSave);
  320. }
  321. it("calls save method") {
  322. IZ_MIDIInputSaveConfig(&state, IZ_CONFIG_GAME_PATH);
  323. check(mock_is_called(INI_ConfigSave), "Config save function not called.");
  324. }
  325. }
  326. describe("HandleEvents") {
  327. static PmEvent e;
  328. static IZ_MIDIInputState state[IZ_PLAYERS] = {};
  329. static IZ_Action action[IZ_PLAYERS] = {};
  330. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  331. describe("on player %u", p) {
  332. for (u8 i = 0; i < IZ_CONTROLS; i += 1) {
  333. it("handles %s action activation", IZ_ACTION_NAMES[i]) {
  334. e.message = MIDI_MESSAGE_NOTEON | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8);
  335. state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i];
  336. action[p] = 0;
  337. IZ_MIDIInputHandleEvents(&state, &action, e);
  338. check(
  339. action[p] == (0x1 << i),
  340. "Action not set."
  341. );
  342. }
  343. it("handles %s action deactivation", IZ_ACTION_NAMES[i]) {
  344. e.message = MIDI_MESSAGE_NOTEOFF | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8);
  345. state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i];
  346. action[p] = ~0;
  347. IZ_MIDIInputHandleEvents(&state, &action, e);
  348. check(
  349. !(action[p] & (0x1 << i)),
  350. "Action not unset."
  351. );
  352. }
  353. }
  354. }
  355. }
  356. }
  357. describe("Teardown") {
  358. static PmStream* stream;
  359. static IZ_MIDIInputState state[IZ_PLAYERS] = {};
  360. before_each() {
  361. for (u8 p = 0; p < IZ_PLAYERS; p += 1) {
  362. state[p].stream = &stream;
  363. }
  364. }
  365. after_each() {
  366. mock_reset(Pm_Close);
  367. }
  368. it("closes opened devices") {
  369. mock_set_expected_calls(Pm_Close, IZ_PLAYERS);
  370. IZ_MIDIInputTeardown(&state);
  371. check(
  372. mock_get_expected_calls(Pm_Close) == mock_get_actual_calls(Pm_Close),
  373. "Call count mismatch for Pm_Close() (expected %u, received %u).",
  374. mock_get_expected_calls(Pm_Close),
  375. mock_get_actual_calls(Pm_Close)
  376. );
  377. }
  378. }
  379. }
  380. }