Browse Source

Cover all input methods on tests

Add tests for initialization and teardown for all input methods.
feature/data-structs
TheoryOfNekomata 1 year ago
parent
commit
52fb193b83
11 changed files with 471 additions and 16 deletions
  1. +4
    -0
      CMakeLists.txt
  2. +3
    -1
      __mocks__/SDL_joystick.mock.h
  3. +69
    -0
      __mocks__/portmidi.mock.h
  4. +4
    -1
      src/packages/game/IZ_app.c
  5. +1
    -1
      src/packages/game/geometry/geometry.test.c
  6. +8
    -4
      src/packages/game/input/IZ_input.c
  7. +1
    -1
      src/packages/game/input/IZ_input.h
  8. +3
    -0
      src/packages/game/input/IZ_joystick.c
  9. +22
    -6
      src/packages/game/input/IZ_midi.c
  10. +7
    -2
      src/packages/game/input/IZ_midi.h
  11. +349
    -0
      src/packages/game/input/input.test.c

+ 4
- 0
CMakeLists.txt View File

@@ -80,6 +80,7 @@ add_executable(
__mocks__/SDL_events.mock.h
__mocks__/SDL_joystick.mock.h
__mocks__/SDL_stdinc.mock.h
__mocks__/portmidi.mock.h

src/packages/game/IZ_config.h

@@ -89,6 +90,9 @@ add_executable(
src/packages/game/input/IZ_joystick.h
src/packages/game/input/IZ_joystick.c

src/packages/game/input/IZ_midi.h
src/packages/game/input/IZ_midi.c

src/packages/game/input/input.test.c
)



+ 3
- 1
__mocks__/SDL_joystick.mock.h View File

@@ -6,13 +6,15 @@

typedef struct _SDL_Joystick {} SDL_Joystick;

#define MOCK_OPEN_JOYSTICKS 1

mock(SDL_JoystickOpen) SDL_Joystick* SDL_JoystickOpen(i32 device_index) {
static SDL_Joystick joystick;
mock_return(SDL_JoystickOpen) &joystick;
}

mock(SDL_NumJoysticks) i32 SDL_NumJoysticks(void) {
mock_return(SDL_NumJoysticks) 1;
mock_return(SDL_NumJoysticks) MOCK_OPEN_JOYSTICKS;
}

mock(SDL_JoystickInstanceID) i32 SDL_JoystickInstanceID(SDL_Joystick* joystick) {


+ 69
- 0
__mocks__/portmidi.mock.h View File

@@ -0,0 +1,69 @@
#ifndef PORTMIDI_MOCK_H
#define PORTMIDI_MOCK_H

#define PORTMIDI_INCLUDED

#include "../src/packages/game/IZ_common.h"

typedef i32 PmDeviceID;
typedef void PortMidiStream;
typedef i32 PmTimestamp;
typedef PmTimestamp (*PmTimeProcPtr)(void *time_info);
typedef i32 PmMessage;
typedef struct {
PmMessage message;
PmTimestamp timestamp;
} PmEvent;
typedef struct {
int structVersion; /**< @brief this internal structure version */
const char *interf; /**< @brief underlying MIDI API, e.g.
"MMSystem" or "DirectX" */
char *name; /**< @brief device name, e.g. "USB MidiSport 1x1" */
int input; /**< @brief true iff input is available */
int output; /**< @brief true iff output is available */
int opened; /**< @brief used by generic PortMidi for error checking */
int is_virtual; /**< @brief true iff this is/was a virtual device */
} PmDeviceInfo;

#define PmStream PortMidiStream

static PmDeviceInfo MOCK_DEVICE_INFO = {
.output = 1,
.input = 1,
.name = "Mock MIDI Device",
.interf = "MMSystem",
.is_virtual = 0,
.opened = 0,
.structVersion = 1,
};

mock(Pm_Initialize) i32 Pm_Initialize(void) {
mock_return(Pm_Initialize) 0;
}

mock(Pm_GetDeviceInfo) const PmDeviceInfo* Pm_GetDeviceInfo(PmDeviceID id) {
mock_return(Pm_GetDeviceInfo) &MOCK_DEVICE_INFO;
}

mock(Pm_OpenInput) i32 Pm_OpenInput(
PortMidiStream** stream,
PmDeviceID inputDevice,
void* inputDriverInfo,
i32 bufferSize,
PmTimeProcPtr time_proc,
void* time_info
) {
mock_return(Pm_OpenInput) 0;
}

mock(Pm_Close) i32 Pm_Close(PortMidiStream* stream) {
mock_return(Pm_Close) 0;
}

#define MOCK_OPEN_MIDI_DEVICES 1

mock(Pm_CountDevices) i32 Pm_CountDevices(void) {
mock_return(Pm_CountDevices) MOCK_OPEN_MIDI_DEVICES;
}

#endif

+ 4
- 1
src/packages/game/IZ_app.c View File

@@ -19,7 +19,10 @@ IZ_ProcedureResult IZ_AppInitialize(IZ_App* app) {
return 2;
}

IZ_InputInitialize(config_path, &app->input_state);
if (IZ_InputInitialize(config_path, &app->input_state)) {
return 3;
}

IZ_PoolInitialize(&app->pool, POOL_MAX_SIZE);

// TODO put into its timer module


+ 1
- 1
src/packages/game/geometry/geometry.test.c View File

@@ -84,7 +84,7 @@ spec("geometry") {
.up = 69.f,
};

static IZ_GeoCoord s = 2.f;
static f32 s = 2.f;

static IZ_Vector2D expected = {
.right = 840.f,


+ 8
- 4
src/packages/game/input/IZ_input.c View File

@@ -9,7 +9,7 @@ void IZ_InputHandlePortMIDIEvents(PmEvent e, IZ_InputState* state) {
IZ_MIDIInputHandleEvents(e, &state->midi_input_state, &state->action);
}

void IZ_InputInitialize(const char* config_path, IZ_InputState* state) {
IZ_ProcedureResult IZ_InputInitialize(const char* config_path, IZ_InputState* state) {
*state = (IZ_InputState) {
.action = {},
.joystick_state = {},
@@ -17,25 +17,29 @@ void IZ_InputInitialize(const char* config_path, IZ_InputState* state) {
.keyboard_state = {},
};

IZ_ProcedureResult result = 0;

IZ_ProcedureResult joystick_result = IZ_JoystickInitialize(config_path, &state->joystick_state);
if (joystick_result) {
fprintf_s(stderr, "Error committing joystick config. Code: %u.\n", joystick_result);
result |= 1;
}

IZ_ProcedureResult keyboard_result = IZ_KeyboardInitialize(config_path, &state->keyboard_state);
if (keyboard_result) {
fprintf_s(stderr, "Error committing keyboard config. Code: %u.\n", keyboard_result);
result |= 2;
}

IZ_ProcedureResult midi_input_result = IZ_MIDIInputInitialize(config_path, &state->midi_input_state);
if (midi_input_result) {
fprintf_s(stderr, "Error committing MIDI input config. Code: %u.\n", midi_input_result);
result |= 4;
}

u8 player_index;
for (player_index = 0; player_index < PLAYERS; player_index += 1) {
state->action[player_index] = 0;
}

return result;
}

void IZ_InputTeardown(IZ_InputState* state) {


+ 1
- 1
src/packages/game/input/IZ_input.h View File

@@ -17,7 +17,7 @@ void IZ_InputHandleSDLEvents(SDL_Event, IZ_InputState*);

void IZ_InputHandlePortMIDIEvents(PmEvent, IZ_InputState*);

void IZ_InputInitialize(const char*, IZ_InputState*);
IZ_ProcedureResult IZ_InputInitialize(const char*, IZ_InputState*);

void IZ_InputTeardown(IZ_InputState*);



+ 3
- 0
src/packages/game/input/IZ_joystick.c View File

@@ -172,6 +172,9 @@ IZ_ProcedureResult IZ_JoystickInitialize(const char* config_path, IZ_JoystickSta
u8 joysticks_count = SDL_NumJoysticks();
u8 player_index;
for (player_index = 0; player_index < joysticks_count; player_index += 1) {
if (player_index >= PLAYERS) {
break;
}
(*state)[player_index].device = SDL_JoystickOpen(state[player_index]->config.device_id);
}



+ 22
- 6
src/packages/game/input/IZ_midi.c View File

@@ -18,15 +18,15 @@ char* IZ_MIDIGetNoteName(u8 midi_note) {

const u8 pitch_class = midi_note % 12;
const u8 octave = midi_note / 12;
static char note_name[4];
sprintf_s(note_name, 4, "%s%u", pitch_names[pitch_class], octave);
static char note_name[8];
sprintf_s(note_name, 8, "%s%u", pitch_names[pitch_class], octave);
return note_name;
}

u8 IZ_MIDIGetNoteFromName(char* name) {
char name_copy[4];
memcpy_s(name_copy, 4, name, 4);
_strlwr_s(name_copy, 4);
char name_copy[8];
memcpy_s(name_copy, 8, name, 8);
_strlwr_s(name_copy, 8);

u8 octave;
const char base_pitch_name[] = "c d ef g a b";
@@ -181,7 +181,23 @@ IZ_ProcedureResult IZ_MIDIInputInitialize(const char* config_path, IZ_MIDIInputS
}

u8 player_index;
for (player_index = 0; player_index < PLAYERS; player_index += 1) {
// TODO Pm_CountDevices(), only filter input devices
u8 midi_devices_count = Pm_CountDevices();
u8 input_midi_devices_count = 0;
u8 device_index;
for (device_index = 0; device_index < midi_devices_count; device_index += 1) {
if (!Pm_GetDeviceInfo(device_index)->output) {
continue;
}

// midi device can output messages for the app to receive
input_midi_devices_count += 1;
}

for (player_index = 0; player_index < input_midi_devices_count; player_index += 1) {
if (player_index >= PLAYERS) {
break;
}
(*state)[player_index].device_info = Pm_GetDeviceInfo((*state)[player_index].config.device_id);
(*state)[player_index].stream = NULL;
Pm_OpenInput(


+ 7
- 2
src/packages/game/input/IZ_midi.h View File

@@ -3,7 +3,12 @@

#include <SDL_stdinc.h>
#include <string.h>

#ifndef PORTMIDI_INCLUDED
#define PORTMIDI_INCLUDED
#include <portmidi.h>
#endif

#include <minIni.h>
#include "IZ_action.h"

@@ -77,8 +82,8 @@ static const IZ_MIDIInputState IZ_DEFAULT_MIDI_INPUT_STATE[PLAYERS] = {
56,
57,
},
.device_id = 0,
.channel = 0,
.device_id = 1,
.channel = 1,
},
.event_buffer = {},
.stream = NULL,


+ 349
- 0
src/packages/game/input/input.test.c View File

@@ -2,8 +2,10 @@
#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;
@@ -15,6 +17,76 @@ i16 GenerateAxisValueOutsideThreshold(u16 threshold) {

spec("input") {
describe("joystick") {
describe("Initialize") {
static IZ_JoystickState state[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("config.ini", &state);

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) * PLAYERS);

IZ_JoystickInitialize("config.ini", &state);

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) * PLAYERS);

IZ_JoystickInitialize("config.ini", &state);

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("config.ini", &state);

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[PLAYERS] = {};
@@ -289,9 +361,93 @@ spec("input") {
);
}
}

describe("Teardown") {
static SDL_Joystick device;
static IZ_JoystickState state[PLAYERS] = {};

before_each() {
for (u8 p = 0; p < PLAYERS; p += 1) {
state[p].device = &device;
}
}

after_each() {
mock_reset(SDL_JoystickClose);
}

it("closes opened devices") {
mock_set_expected_calls(SDL_JoystickClose, 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[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 < PLAYERS; p += 1) {
for (u8 i = 0; i < CONTROLS; i += 1) {
state[p].config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_STATE[p].config.control_mapping[i];
}
}
}

it("sets initial state") {
IZ_KeyboardInitialize("config.ini", &state);

check(mock_is_called(SDL_memcpy), "Initial state not loaded.");
}

it("calls load method") {
mock_set_expected_calls(ini_gets, CONTROLS * PLAYERS);

IZ_KeyboardInitialize("config.ini", &state);

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 * PLAYERS);

IZ_KeyboardInitialize("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("HandleEvents") {
static SDL_Event e;
static IZ_KeyboardState state[PLAYERS] = {};
@@ -359,4 +515,197 @@ spec("input") {
}
}
}

describe("midi") {
describe("Initialize") {
static IZ_MIDIInputState state[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("config.ini", &state);

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 * PLAYERS);
mock_set_expected_calls(ini_getl, 2 * PLAYERS);

IZ_MIDIInputInitialize("config.ini", &state);

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 * PLAYERS);
mock_set_expected_calls(ini_putl, 2 * PLAYERS);

IZ_MIDIInputInitialize("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)
);
}

it("opens device handles") {
mock_set_expected_calls(Pm_OpenInput, MOCK_OPEN_JOYSTICKS);

IZ_MIDIInputInitialize("config.ini", &state);

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[PLAYERS];

after_each() {
mock_reset(ini_puts);
}

after_each() {
mock_reset(ini_putl);
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS * PLAYERS);
mock_set_expected_calls(ini_putl, 2 * 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[PLAYERS] = {};
static IZ_Action action[PLAYERS] = {};

for (u8 p = 0; p < 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_DEFAULT_MIDI_INPUT_STATE[p].config.control_mapping[i] << 8);
state[p].config.control_mapping[i] = IZ_DEFAULT_MIDI_INPUT_STATE[p].config.control_mapping[i];
action[p] = 0;

IZ_MIDIInputHandleEvents(e, &state, &action);
check(
action[p] == (0x1 << i),
"Action not set."
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.message = IZ_MIDI_NOTE_OFF | (IZ_DEFAULT_MIDI_INPUT_STATE[p].config.control_mapping[i] << 8);
state[p].config.control_mapping[i] = IZ_DEFAULT_MIDI_INPUT_STATE[p].config.control_mapping[i];
action[p] = ~0;

IZ_MIDIInputHandleEvents(e, &state, &action);
check(
!(action[p] & (0x1 << i)),
"Action not unset."
);
}
}
}
}
}

describe("Teardown") {
static PmStream* stream;
static IZ_MIDIInputState state[PLAYERS] = {};

before_each() {
for (u8 p = 0; p < PLAYERS; p += 1) {
state[p].stream = &stream;
}
}

after_each() {
mock_reset(Pm_Close);
}

it("closes opened devices") {
mock_set_expected_calls(Pm_Close, 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)
);
}
}
}
}

Loading…
Cancel
Save