#include "../../../__mocks__/SDL_keyboard.mock.h" #include "../../../__mocks__/SDL_joystick.mock.h" #include "../../../__mocks__/SDL_stdinc.mock.h" #include "../../../__mocks__/minIni.mock.h" #include "../../../__mocks__/portmidi.mock.h" #include "IZ_keyboard.h" #include "IZ_joystick.h" #include "IZ_midi.h" i16 GenerateAxisValueWithinThreshold(u16 threshold) { return rand() % threshold; } i16 GenerateAxisValueOutsideThreshold(u16 threshold) { return threshold + (rand() % (RAND_MAX - threshold - 1)) + 1; } spec("input") { describe("joystick") { describe("Initialize") { static IZ_JoystickState state[IZ_PLAYERS]; after_each() { mock_reset(SDL_memcpy); } after_each() { mock_reset(SDL_NumJoysticks); } after_each() { mock_reset(SDL_JoystickOpen); } after_each() { mock_reset(ini_putl); } after_each() { mock_reset(ini_getl); } it("sets initial state") { IZ_JoystickInitialize(&state, "config.ini", 0, NULL); check(mock_is_called(SDL_memcpy), "Initial state not loaded."); check(mock_is_called(SDL_NumJoysticks), "Connected joysticks not checked."); } it("calls load method") { mock_set_expected_calls(ini_getl, ((CONTROLS - 4) + 2) * IZ_PLAYERS); IZ_JoystickInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_getl) == mock_get_actual_calls(ini_getl), "Call count mismatch for ini_getl() (expected %u, received %u).", mock_get_expected_calls(ini_getl), mock_get_actual_calls(ini_getl) ); } it("calls save method") { mock_set_expected_calls(ini_putl, ((CONTROLS - 4) + 2) * IZ_PLAYERS); IZ_JoystickInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_putl) == mock_get_actual_calls(ini_putl), "Call count mismatch for ini_putl() (expected %u, received %u).", mock_get_expected_calls(ini_putl), mock_get_actual_calls(ini_putl) ); } it("opens device handles") { mock_set_expected_calls(SDL_JoystickOpen, MOCK_OPEN_JOYSTICKS); IZ_JoystickInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(SDL_JoystickOpen) == mock_get_actual_calls(SDL_JoystickOpen), "Call count mismatch for SDL_JoystickOpen() (expected %u, received %u).", mock_get_expected_calls(SDL_JoystickOpen), mock_get_actual_calls(SDL_JoystickOpen) ); } } describe("HandleEvents") { static SDL_Event e; static IZ_JoystickState state[IZ_PLAYERS] = {}; static IZ_Action action[IZ_PLAYERS] = {}; u8 p; for (p = 0; p < IZ_PLAYERS; p += 1) { describe("on player %u", p) { describe("on axis motion events") { before_each() { e.type = SDL_JOYAXISMOTION; state[p].config.axis_threshold = 8000u; } describe("on primary horizontal direction") { before_each() { e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL1; } it("handles positive motion") { e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT), "Action not set." ); } it("handles negative motion") { e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_LEFT), "Action not set." ); } it("handles neutral motion") { e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == 0, "Action not set." ); } } describe("on secondary horizontal direction") { before_each() { e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_HORIZONTAL2; } it("handles positive motion") { e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_RIGHT), "Action not set." ); } it("handles negative motion") { e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_LEFT), "Action not set." ); } it("handles neutral motion") { e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == 0, "Action not set." ); } } describe("on primary vertical direction") { before_each() { e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL1; } it("handles positive motion") { e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_DOWN), "Action not set." ); } it("handles negative motion") { e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_UP), "Action not set." ); } it("handles neutral motion") { e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == 0, "Action not set." ); } } describe("on secondary vertical direction") { before_each() { e.jaxis.axis = IZ_JOY_AXIS_DIRECTION_VERTICAL2; } it("handles positive motion") { e.jaxis.value = GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_DOWN), "Action not set." ); } it("handles negative motion") { e.jaxis.value = -GenerateAxisValueOutsideThreshold(state[p].config.axis_threshold); action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1 << IZ_ACTION_INDEX_UP), "Action not set." ); } it("handles neutral motion") { e.jaxis.value = GenerateAxisValueWithinThreshold(state[p].config.axis_threshold);; action[p] = 0; printf("(axis value: %d) ", e.jaxis.value); IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == 0, "Action not set." ); } } } describe("on hat motion events") { before_each() { e.type = SDL_JOYHATMOTION; } for (u8 i = 0; i < 4; i += 1) { it("handles motion for %s action", ACTION_NAMES[i]) { e.jhat.value = (0x1u << i); action[p] = 0; IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1u << i), "Action not set." ); } it("handles motion for %s deactivation", ACTION_NAMES[i]) { e.jhat.value = 0; action[p] = ~0; IZ_JoystickHandleEvents(&state, &action, e); check( !(action[p] & (0x1 << i)), "Action not unset." ); } } } describe("on button events") { for (u8 i = 4; i < CONTROLS; i += 1) { it("handles %s action activation", ACTION_NAMES[i]) { e.type = SDL_JOYBUTTONDOWN; e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = 0; IZ_JoystickHandleEvents(&state, &action, e); check( action[p] == (0x1u << i), "Action not set." ); } it("handles %s action deactivation", ACTION_NAMES[i]) { e.type = SDL_JOYBUTTONUP; e.jbutton.button = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = ~0; IZ_JoystickHandleEvents(&state, &action, e); check( !(action[p] & (0x1 << i)), "Action not unset." ); } } } } } } describe("SaveConfig") { static IZ_JoystickState state[IZ_PLAYERS]; after_each() { mock_reset(ini_putl); } before_each() { for (u8 p = 0; p < IZ_PLAYERS; p += 1) { for (u8 i = 0; i < CONTROLS; i += 1) { state[p].config.control_mapping[i] = IZ_JOYSTICK_DEFAULT_STATE[p].config.control_mapping[i]; } } } it("calls save method") { mock_set_expected_calls(ini_putl, ((CONTROLS - 4) + 2) * IZ_PLAYERS); IZ_JoystickSaveConfig(&state, "config.ini"); check( mock_get_expected_calls(ini_putl) == mock_get_actual_calls(ini_putl), "Call count mismatch for ini_putl() (expected %u, received %u).", mock_get_expected_calls(ini_putl), mock_get_actual_calls(ini_putl) ); } } describe("Teardown") { static SDL_Joystick device; static IZ_JoystickState state[IZ_PLAYERS] = {}; before_each() { for (u8 p = 0; p < IZ_PLAYERS; p += 1) { state[p].device = &device; } } after_each() { mock_reset(SDL_JoystickClose); } it("closes opened devices") { mock_set_expected_calls(SDL_JoystickClose, IZ_PLAYERS); IZ_JoystickTeardown(&state); check( mock_get_expected_calls(SDL_JoystickClose) == mock_get_actual_calls(SDL_JoystickClose), "Call count mismatch for SDL_JoystickClose() (expected %u, received %u).", mock_get_expected_calls(SDL_JoystickClose), mock_get_actual_calls(SDL_JoystickClose) ); } } } describe("keyboard") { describe("Initialize") { static IZ_KeyboardState state[IZ_PLAYERS] = {}; after_each() { mock_reset(SDL_memcpy); } after_each() { mock_reset(ini_gets); } after_each() { mock_reset(ini_puts); } before_each() { for (u8 p = 0; p < IZ_PLAYERS; p += 1) { for (u8 i = 0; i < CONTROLS; i += 1) { state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; } } } it("sets initial state") { IZ_KeyboardInitialize(&state, "config.ini", 0, NULL); check(mock_is_called(SDL_memcpy), "Initial state not loaded."); } it("calls load method") { mock_set_expected_calls(ini_gets, CONTROLS * IZ_PLAYERS); IZ_KeyboardInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_gets) == mock_get_actual_calls(ini_gets), "Call count mismatch for ini_gets() (expected %u, received %u).", mock_get_expected_calls(ini_gets), mock_get_actual_calls(ini_gets) ); } it("calls save method") { mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS); IZ_KeyboardInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_puts) == mock_get_actual_calls(ini_puts), "Call count mismatch for ini_puts() (expected %u, received %u).", mock_get_expected_calls(ini_puts), mock_get_actual_calls(ini_puts) ); } } describe("HandleEvents") { static SDL_Event e; static IZ_KeyboardState state[IZ_PLAYERS] = {}; static IZ_Action action[IZ_PLAYERS] = {}; for (u8 p = 0; p < IZ_PLAYERS; p += 1) { describe("on player %u", p) { for (u8 i = 0; i < CONTROLS; i += 1) { it("handles %s action activation", ACTION_NAMES[i]) { e.type = SDL_KEYDOWN; e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = 0; IZ_KeyboardHandleEvents(&state, &action, e); check( action[p] == (0x1 << i), "Action not set." ); } it("handles %s action deactivation", ACTION_NAMES[i]) { e.type = SDL_KEYUP; e.key.keysym.sym = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = ~0; IZ_KeyboardHandleEvents(&state, &action, e); check( !(action[p] & (0x1 << i)), "Action not unset." ); } } } } } describe("SaveConfig") { static IZ_KeyboardState state[IZ_PLAYERS] = {}; after_each() { mock_reset(ini_puts); } before_each() { for (u8 p = 0; p < IZ_PLAYERS; p += 1) { for (u8 i = 0; i < CONTROLS; i += 1) { state[p].config.control_mapping[i] = IZ_KEYBOARD_DEFAULT_STATE[p].config.control_mapping[i]; } } } it("calls save method") { mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS); IZ_KeyboardSaveConfig("config.ini", &state); check( mock_get_expected_calls(ini_puts) == mock_get_actual_calls(ini_puts), "Call count mismatch for ini_puts() (expected %u, received %u).", mock_get_expected_calls(ini_puts), mock_get_actual_calls(ini_puts) ); } } } describe("midi") { describe("Initialize") { static IZ_MIDIInputState state[IZ_PLAYERS]; after_each() { mock_reset(SDL_memcpy); } after_each() { mock_reset(Pm_CountDevices); } after_each() { mock_reset(Pm_OpenInput); } after_each() { mock_reset(ini_puts); } after_each() { mock_reset(ini_gets); } after_each() { mock_reset(ini_putl); } after_each() { mock_reset(ini_getl); } it("sets initial state") { IZ_MIDIInputInitialize(&state, "config.ini", 0, NULL); check(mock_is_called(SDL_memcpy), "Initial state not loaded."); check(mock_is_called(Pm_CountDevices), "Connected MIDI devices not checked."); } it("calls load method") { mock_set_expected_calls(ini_gets, CONTROLS * IZ_PLAYERS); mock_set_expected_calls(ini_getl, 2 * IZ_PLAYERS); IZ_MIDIInputInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_gets) == mock_get_actual_calls(ini_gets), "Call count mismatch for ini_gets() (expected %u, received %u).", mock_get_expected_calls(ini_gets), mock_get_actual_calls(ini_gets) ); check( mock_get_expected_calls(ini_getl) == mock_get_actual_calls(ini_getl), "Call count mismatch for ini_getl() (expected %u, received %u).", mock_get_expected_calls(ini_getl), mock_get_actual_calls(ini_getl) ); } it("calls save method") { mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS); mock_set_expected_calls(ini_putl, 2 * IZ_PLAYERS); IZ_MIDIInputInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(ini_puts) == mock_get_actual_calls(ini_puts), "Call count mismatch for ini_puts() (expected %u, received %u).", mock_get_expected_calls(ini_puts), mock_get_actual_calls(ini_puts) ); check( mock_get_expected_calls(ini_putl) == mock_get_actual_calls(ini_putl), "Call count mismatch for ini_putl() (expected %u, received %u).", mock_get_expected_calls(ini_putl), mock_get_actual_calls(ini_putl) ); } it("opens device handles") { mock_set_expected_calls(Pm_OpenInput, MOCK_OPEN_JOYSTICKS); IZ_MIDIInputInitialize(&state, "config.ini", 0, NULL); check( mock_get_expected_calls(Pm_OpenInput) == mock_get_actual_calls(Pm_OpenInput), "Call count mismatch for Pm_OpenInput() (expected %u, received %u).", mock_get_expected_calls(Pm_OpenInput), mock_get_actual_calls(Pm_OpenInput) ); } } describe("SaveConfig") { static IZ_MIDIInputState state[IZ_PLAYERS]; after_each() { mock_reset(ini_puts); } after_each() { mock_reset(ini_putl); } it("calls save method") { mock_set_expected_calls(ini_puts, CONTROLS * IZ_PLAYERS); mock_set_expected_calls(ini_putl, 2 * IZ_PLAYERS); IZ_MIDIInputSaveConfig("config.ini", &state); check( mock_get_expected_calls(ini_puts) == mock_get_actual_calls(ini_puts), "Call count mismatch for ini_puts() (expected %u, received %u).", mock_get_expected_calls(ini_puts), mock_get_actual_calls(ini_puts) ); check( mock_get_expected_calls(ini_putl) == mock_get_actual_calls(ini_putl), "Call count mismatch for ini_putl() (expected %u, received %u).", mock_get_expected_calls(ini_putl), mock_get_actual_calls(ini_putl) ); } } describe("HandleEvents") { static PmEvent e; static IZ_MIDIInputState state[IZ_PLAYERS] = {}; static IZ_Action action[IZ_PLAYERS] = {}; for (u8 p = 0; p < IZ_PLAYERS; p += 1) { describe("on player %u", p) { for (u8 i = 0; i < CONTROLS; i += 1) { it("handles %s action activation", ACTION_NAMES[i]) { e.message = IZ_MIDI_NOTE_ON | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8); state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = 0; IZ_MIDIInputHandleEvents(&state, &action, e); check( action[p] == (0x1 << i), "Action not set." ); } it("handles %s action deactivation", ACTION_NAMES[i]) { e.message = IZ_MIDI_NOTE_OFF | (IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i] << 8); state[p].config.control_mapping[i] = IZ_MIDI_INPUT_DEFAULT_STATE[p].config.control_mapping[i]; action[p] = ~0; IZ_MIDIInputHandleEvents(&state, &action, e); check( !(action[p] & (0x1 << i)), "Action not unset." ); } } } } } describe("Teardown") { static PmStream* stream; static IZ_MIDIInputState state[IZ_PLAYERS] = {}; before_each() { for (u8 p = 0; p < IZ_PLAYERS; p += 1) { state[p].stream = &stream; } } after_each() { mock_reset(Pm_Close); } it("closes opened devices") { mock_set_expected_calls(Pm_Close, IZ_PLAYERS); IZ_MIDIInputTeardown(&state); check( mock_get_expected_calls(Pm_Close) == mock_get_actual_calls(Pm_Close), "Call count mismatch for Pm_Close() (expected %u, received %u).", mock_get_expected_calls(Pm_Close), mock_get_actual_calls(Pm_Close) ); } } } }