Browse Source

Implement joystick tests

Implement tests for joystick axis, hat, and button events.
master
TheoryOfNekomata 1 year ago
parent
commit
881b415d60
8 changed files with 482 additions and 112 deletions
  1. +5
    -4
      CMakeLists.txt
  2. +6
    -0
      __mocks__/SDL_events.mock.h
  3. +21
    -0
      __mocks__/SDL_joystick.mock.h
  4. +22
    -6
      src/packages/game/input/IZ_joystick.c
  5. +7
    -0
      src/packages/game/input/IZ_joystick.h
  6. +4
    -1
      src/packages/game/input/IZ_keyboard.c
  7. +0
    -101
      src/packages/game/input/IZ_keyboard.test.c
  8. +417
    -0
      src/packages/game/input/input.test.c

+ 5
- 4
CMakeLists.txt View File

@@ -56,7 +56,6 @@ add_executable(
src/packages/test/IZ_test.h

__mocks__/minIni.mock.h
__mocks__/SDL_keyboard.mock.h

src/packages/game/IZ_config.h
src/packages/game/__mocks__/IZ_config.mock.h
@@ -74,17 +73,19 @@ add_executable(

__mocks__/minIni.mock.h
__mocks__/SDL_keyboard.mock.h
__mocks__/SDL_events.mock.h
__mocks__/SDL_joystick.mock.h

src/packages/game/IZ_config.h
src/packages/game/__mocks__/IZ_config.mock.h

src/packages/game/input/IZ_keyboard.h
src/packages/game/input/IZ_keyboard.c
src/packages/game/input/IZ_keyboard.test.c

# src/packages/game/input/IZ_joystick.h
# src/packages/game/input/IZ_joystick.c
src/packages/game/input/IZ_joystick.h
src/packages/game/input/IZ_joystick.c

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

if (WIN32)


+ 6
- 0
__mocks__/SDL_events.mock.h View File

@@ -0,0 +1,6 @@
#ifndef SDL_EVENTS_MOCK_H
#define SDL_EVENTS_MOCK_H

#include "SDL_keyboard.mock.h"

#endif

+ 21
- 0
__mocks__/SDL_joystick.mock.h View File

@@ -0,0 +1,21 @@
#ifndef SDL_JOYSTICK_MOCK_H
#define SDL_JOYSTICK_MOCK_H

#include "../src/packages/test/IZ_test.h"

typedef struct _SDL_Joystick {} SDL_Joystick;

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

mock(SDL_NumJoysticks) int SDL_NumJoysticks(void) {
mock_return(SDL_NumJoysticks) 1;
}

mock(SDL_JoystickInstanceID) int SDL_JoystickInstanceID(SDL_Joystick* joystick) {
mock_return(SDL_JoystickInstanceID) 0;
}

#endif

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

@@ -19,21 +19,32 @@ void IZ_HandleJoystickDeviceEvents(SDL_Event e, IZ_JoystickState* state) {

void IZ_HandleJoystickAxisEvents(SDL_Event e, IZ_JoystickState* state, IZ_Action* action) {
if (e.type == SDL_JOYAXISMOTION) {
if (e.jaxis.axis == 0 || e.jaxis.axis == 3) {
if (
e.jaxis.axis == IZ_JOYAXIS_DIRECTION_HORIZONTAL1
|| e.jaxis.axis == IZ_JOYAXIS_DIRECTION_HORIZONTAL2
) {
*action &= ~(0x1 << IZ_ACTION_INDEX_RIGHT);
*action &= ~(0x1 << IZ_ACTION_INDEX_LEFT);
if (e.jaxis.value > state->config.axis_threshold) {
*action |= (0x1 << IZ_ACTION_INDEX_RIGHT);
} else if (e.jaxis.value <= -state->config.axis_threshold) {
return;
}
if (e.jaxis.value <= -state->config.axis_threshold) {
*action |= (0x1 << IZ_ACTION_INDEX_LEFT);
}
return;
}
if (e.jaxis.axis == 1 || e.jaxis.axis == 4) {
if (
e.jaxis.axis == IZ_JOYAXIS_DIRECTION_VERTICAL1
|| e.jaxis.axis == IZ_JOYAXIS_DIRECTION_VERTICAL2
) {
*action &= ~(0x1 << IZ_ACTION_INDEX_UP);
*action &= ~(0x1 << IZ_ACTION_INDEX_DOWN);
if (e.jaxis.value > -state->config.axis_threshold) {
if (e.jaxis.value > state->config.axis_threshold) {
*action |= (0x1 << IZ_ACTION_INDEX_DOWN);
} else if (e.jaxis.value <= -state->config.axis_threshold) {
return;
}
if (e.jaxis.value <= -state->config.axis_threshold) {
*action |= (0x1 << IZ_ACTION_INDEX_UP);
}
}
@@ -56,10 +67,15 @@ void IZ_HandleJoystickButtonEvents(SDL_Event e, IZ_JoystickState* state, IZ_Acti
for (uint8_t i = 4; i < CONTROLS; i += 1) {
if (e.jbutton.button == state->config.control_mapping[i]) {
const uint16_t bitflag = (0x1 << i);

if (e.type == SDL_JOYBUTTONDOWN) {
*action |= bitflag;
} else if (e.type == SDL_JOYBUTTONUP) {
break;
}

if (e.type == SDL_JOYBUTTONUP) {
*action &= ~bitflag;
break;
}
}
}


+ 7
- 0
src/packages/game/input/IZ_joystick.h View File

@@ -9,6 +9,13 @@

typedef uint8_t IZ_PadButton;

typedef enum {
IZ_JOYAXIS_DIRECTION_HORIZONTAL1 = 0,
IZ_JOYAXIS_DIRECTION_VERTICAL1 = 1,
IZ_JOYAXIS_DIRECTION_HORIZONTAL2 = 3,
IZ_JOYAXIS_DIRECTION_VERTICAL2 = 4,
} IZ_JoyAxisDirection;

typedef struct {
uint16_t axis_threshold;
IZ_PadButton control_mapping[CONTROLS];


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

@@ -6,8 +6,11 @@ void IZ_HandleKeyboardEvents(SDL_Event e, IZ_KeyboardState* state, IZ_Action* ac
const uint16_t bitflag = (0x1 << i);
if (e.type == SDL_KEYDOWN) {
*action |= bitflag;
} else if (e.type == SDL_KEYUP) {
break;
}
if (e.type == SDL_KEYUP) {
*action &= ~bitflag;
break;
}
}
}


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

@@ -1,101 +0,0 @@
#include "../../../__mocks__/SDL_keyboard.mock.h"
#include "../../../__mocks__/minIni.mock.h"
#include "../__mocks__/IZ_config.mock.h"
#include "IZ_keyboard.h"

spec("input/keyboard") {
describe("HandleKeyboardEvents") {
static SDL_Event e;
static IZ_KeyboardState state;
static IZ_Action action;

for (uint8_t i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_KEYDOWN;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
action = 0;

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

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_KEYUP;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
action = ~0;

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

describe("LoadKeyboardConfig") {
static IZ_KeyboardConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_gets);
}

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

IZ_LoadKeyboardConfig(&config, 0);

check(
mock_is_called(IZ_GetConfigPath),
"SDL_GetBasePath() not called."
);

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)
);
}
}

describe("SaveKeyboardConfig") {
static IZ_KeyboardConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_puts);
}

before_each() {
for (uint8_t i = 0; i < CONTROLS; i += 1) {
config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
}
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS);

IZ_SaveKeyboardConfig(&config, 0);

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)
);
}
}
}

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

@@ -0,0 +1,417 @@
#include "../../../__mocks__/SDL_keyboard.mock.h"
#include "../../../__mocks__/SDL_joystick.mock.h"
#include "../../../__mocks__/minIni.mock.h"
#include "../__mocks__/IZ_config.mock.h"
#include "IZ_keyboard.h"
#include "IZ_joystick.h"

short int GenerateAxisValueWithinThreshold(short int threshold) {
return rand() % threshold;
}

short int GenerateAxisValueOutsideThreshold(short int threshold) {
return threshold + (rand() % (RAND_MAX - threshold - 1)) + 1;
}

spec("input") {
describe("joystick") {
describe("HandleJoystickEvents") {
static SDL_Event e;
static IZ_JoystickState state;
static IZ_Action action;

describe("on axis motion events") {
before_each() {
e.type = SDL_JOYAXISMOTION;
state.config.axis_threshold = 8000;
}

describe("on primary horizontal direction") {
before_each() {
e.jaxis.axis = IZ_JOYAXIS_DIRECTION_HORIZONTAL1;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
}
}

describe("on secondary horizontal direction") {
before_each() {
e.jaxis.axis = IZ_JOYAXIS_DIRECTION_HORIZONTAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_RIGHT),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_LEFT),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
}
}

describe("on primary vertical direction") {
before_each() {
e.jaxis.axis = IZ_JOYAXIS_DIRECTION_VERTICAL1;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
}
}

describe("on secondary vertical direction") {
before_each() {
e.jaxis.axis = IZ_JOYAXIS_DIRECTION_VERTICAL2;
}

it("handles positive motion") {
e.jaxis.value = GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_DOWN),
"Action not set."
);
}

it("handles negative motion") {
e.jaxis.value = -GenerateAxisValueOutsideThreshold(state.config.axis_threshold);
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1 << IZ_ACTION_INDEX_UP),
"Action not set."
);
}

it("handles neutral motion") {
e.jaxis.value = GenerateAxisValueWithinThreshold(state.config.axis_threshold);;
action = 0;

printf("(axis value: %d) ", e.jaxis.value);
IZ_HandleJoystickEvents(e, &state, &action);
check(
action == 0,
"Action not set."
);
}
}
}

describe("on hat motion events") {
before_each() {
e.type = SDL_JOYHATMOTION;
}

for (uint8_t i = 0; i < 4; i += 1) {
it("handles motion for %s action", ACTION_NAMES[i]) {
e.jhat.value = (0x1u << i);
action = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1u << i),
"Action not set."
);
}

it("handles motion for %s deactivation", ACTION_NAMES[i]) {
e.jhat.value = 0;
action = ~0;

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

describe("on button events") {
for (uint8_t i = 4; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONDOWN;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_CONTROLS[0][i];
action = 0;

IZ_HandleJoystickEvents(e, &state, &action);
check(
action == (0x1u << i),
"Action not set."
);
}

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_JOYBUTTONUP;
e.jbutton.button = IZ_DEFAULT_JOYSTICK_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_CONTROLS[0][i];
action = ~0;

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

describe("LoadJoystickConfig") {
static IZ_JoystickConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_getl);
}

it("calls load method") {
mock_set_expected_calls(ini_getl, CONTROLS - 4 + 1);

IZ_LoadJoystickConfig(&config, 0);

check(
mock_is_called(IZ_GetConfigPath),
"SDL_GetBasePath() not called."
);

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)
);
}
}

describe("SaveJoystickConfig") {
static IZ_JoystickConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_putl);
}

before_each() {
for (uint8_t i = 0; i < CONTROLS; i += 1) {
config.control_mapping[i] = IZ_DEFAULT_JOYSTICK_CONTROLS[0][i];
}
}

it("calls save method") {
mock_set_expected_calls(ini_putl, CONTROLS - 4 + 1);

IZ_SaveJoystickConfig(&config, 0);

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("keyboard") {
describe("HandleKeyboardEvents") {
static SDL_Event e;
static IZ_KeyboardState state;
static IZ_Action action;

for (uint8_t i = 0; i < CONTROLS; i += 1) {
it("handles %s action activation", ACTION_NAMES[i]) {
e.type = SDL_KEYDOWN;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
action = 0;

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

it("handles %s action deactivation", ACTION_NAMES[i]) {
e.type = SDL_KEYUP;
e.key.keysym.sym = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
state.config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
action = ~0;

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

describe("LoadKeyboardConfig") {
static IZ_KeyboardConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_gets);
}

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

IZ_LoadKeyboardConfig(&config, 0);

check(
mock_is_called(IZ_GetConfigPath),
"SDL_GetBasePath() not called."
);

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)
);
}
}

describe("SaveKeyboardConfig") {
static IZ_KeyboardConfig config;

after_each() {
mock_reset(IZ_GetConfigPath);
}

after_each() {
mock_reset(ini_puts);
}

before_each() {
for (uint8_t i = 0; i < CONTROLS; i += 1) {
config.control_mapping[i] = IZ_DEFAULT_KEYBOARD_CONTROLS[0][i];
}
}

it("calls save method") {
mock_set_expected_calls(ini_puts, CONTROLS);

IZ_SaveKeyboardConfig(&config, 0);

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)
);
}
}
}
}

Loading…
Cancel
Save