libx52/daemon/x52d_profile_test.c

550 lines
16 KiB
C

/*
* Saitek X52 Pro MFD & LED driver - Profile module tests
*
* Copyright (C) 2021 Nirenjan Krishnan (nirenjan@nirenjan.org)
*
* SPDX-License-Identifier: GPL-2.0-only WITH Classpath-exception-2.0
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <setjmp.h>
#include <unistd.h>
#include <cmocka.h>
#include "libevdev/libevdev.h"
#include "libx52io.h"
#define PINELOG_MODULE X52D_MOD_PROFILE
#include "pinelog.h"
#include "x52d_config.h"
#include "x52d_const.h"
#include "x52d_keyboard.h"
#include "x52d_profile.h"
/* Stub keyboard: record (key_code, value) for assertions */
#define MAX_RECORDED 32
static struct { uint16_t code; int value; } key_record[MAX_RECORDED];
static size_t key_record_n;
int x52d_keyboard_evdev_key(uint16_t key_code, int value)
{
if (key_record_n < MAX_RECORDED) {
key_record[key_record_n].code = key_code;
key_record[key_record_n].value = value;
key_record_n++;
}
return 0;
}
bool x52d_keyboard_evdev_available(void)
{
return true;
}
static void key_record_reset(void)
{
key_record_n = 0;
}
static int key_code_from_name(const char *name)
{
int c = libevdev_event_code_from_name(EV_KEY, name);
return (c >= 0) ? c : -1;
}
static void write_profile_file(const char *path, const char *content)
{
FILE *f = fopen(path, "w");
assert_non_null(f);
assert_true(fputs(content, f) >= 0);
fclose(f);
}
static void test_profile_single_key(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
report.button[LIBX52IO_BTN_FIRE] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 1);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_E"));
assert_int_equal(key_record[0].value, 1);
memcpy(&prev, &report, sizeof(report));
report.button[LIBX52IO_BTN_FIRE] = false;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 1);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_E"));
assert_int_equal(key_record[0].value, 0);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_key_combo(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_A = key KEY_LEFTCTRL KEY_X\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
report.button[LIBX52IO_BTN_A] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 2);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_LEFTCTRL"));
assert_int_equal(key_record[0].value, 1);
assert_int_equal(key_record[1].code, key_code_from_name("KEY_X"));
assert_int_equal(key_record[1].value, 1);
memcpy(&prev, &report, sizeof(report));
report.button[LIBX52IO_BTN_A] = false;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 2);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_X"));
assert_int_equal(key_record[0].value, 0);
assert_int_equal(key_record[1].code, key_code_from_name("KEY_LEFTCTRL"));
assert_int_equal(key_record[1].value, 0);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_macro_button_down_only(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_T1_UP = macro KEY_A KEY_B\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
report.button[LIBX52IO_BTN_T1_UP] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
x52d_profile_macro_wait_drained();
assert_int_equal(key_record_n, 4);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_A"));
assert_int_equal(key_record[0].value, 1);
assert_int_equal(key_record[1].code, key_code_from_name("KEY_A"));
assert_int_equal(key_record[1].value, 0);
assert_int_equal(key_record[2].code, key_code_from_name("KEY_B"));
assert_int_equal(key_record[2].value, 1);
assert_int_equal(key_record[3].code, key_code_from_name("KEY_B"));
assert_int_equal(key_record[3].value, 0);
memcpy(&prev, &report, sizeof(report));
report.button[LIBX52IO_BTN_T1_UP] = false;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 0);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_macro_pipe_combo(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_T1_UP = macro KEY_LEFTCTRL KEY_C | KEY_A\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
report.button[LIBX52IO_BTN_T1_UP] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
x52d_profile_macro_wait_drained();
/* Step 1: Ctrl+C (down order: Ctrl, C; up order: C, Ctrl). Step 2: A (down, up) */
assert_int_equal(key_record_n, 6);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_LEFTCTRL"));
assert_int_equal(key_record[0].value, 1);
assert_int_equal(key_record[1].code, key_code_from_name("KEY_C"));
assert_int_equal(key_record[1].value, 1);
assert_int_equal(key_record[2].code, key_code_from_name("KEY_C"));
assert_int_equal(key_record[2].value, 0);
assert_int_equal(key_record[3].code, key_code_from_name("KEY_LEFTCTRL"));
assert_int_equal(key_record[3].value, 0);
assert_int_equal(key_record[4].code, key_code_from_name("KEY_A"));
assert_int_equal(key_record[4].value, 1);
assert_int_equal(key_record[5].code, key_code_from_name("KEY_A"));
assert_int_equal(key_record[5].value, 0);
memcpy(&prev, &report, sizeof(report));
report.button[LIBX52IO_BTN_T1_UP] = false;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 0);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_fallback_mode2_to_mode1(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 2;
report.button[LIBX52IO_BTN_FIRE] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 1);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_E"));
assert_int_equal(key_record[0].value, 1);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_shift_layer(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Profile]\n"
"ShiftButton=BTN_PINKY\n"
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E\n"
"[Mode1.Shift]\n"
"Button.BTN_FIRE = key KEY_F\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
report.button[LIBX52IO_BTN_PINKY] = true;
report.button[LIBX52IO_BTN_FIRE] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 1);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_F"));
assert_int_equal(key_record[0].value, 1);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_get_name(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
const char *name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Profile]\n"
"Name=My Test Profile\n"
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
name = x52d_profile_get_name();
assert_non_null(name);
assert_string_equal(name, "My Test Profile");
x52d_profile_exit();
name = x52d_profile_get_name();
assert_null(name);
unlink(path);
rmdir(tmpdir);
}
static void test_profile_action_name_key(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report, prev;
const char *action_name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E name \"Primary Fire\"\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
memset(&prev, 0, sizeof(prev));
report.mode = 1;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_FIRE);
assert_non_null(action_name);
assert_string_equal(action_name, "Primary Fire");
report.button[LIBX52IO_BTN_FIRE] = true;
key_record_reset();
x52d_profile_apply(&report, &prev);
assert_int_equal(key_record_n, 1);
assert_int_equal(key_record[0].code, key_code_from_name("KEY_E"));
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_action_name_unquoted(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report;
const char *action_name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_A = key KEY_X name YawLeft\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
report.mode = 1;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_A);
assert_non_null(action_name);
assert_string_equal(action_name, "YawLeft");
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_action_name_none(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report;
const char *action_name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
report.mode = 1;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_FIRE);
assert_null(action_name);
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_action_name_shift_layer(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report;
const char *action_name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Profile]\n"
"ShiftButton=BTN_PINKY\n"
"[Mode1]\n"
"Button.BTN_FIRE = key KEY_E name Unshifted\n"
"[Mode1.Shift]\n"
"Button.BTN_FIRE = key KEY_F name Shifted\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
report.mode = 1;
report.button[LIBX52IO_BTN_PINKY] = false;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_FIRE);
assert_non_null(action_name);
assert_string_equal(action_name, "Unshifted");
report.button[LIBX52IO_BTN_PINKY] = true;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_FIRE);
assert_non_null(action_name);
assert_string_equal(action_name, "Shifted");
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
static void test_profile_action_name_macro(void **state)
{
char tmpdir[] = "/tmp/x52d_profile_test_XXXXXX";
char path[256];
libx52io_report report;
const char *action_name;
(void)state;
assert_non_null(mkdtemp(tmpdir));
snprintf(path, sizeof(path), "%s/test.conf", tmpdir);
write_profile_file(path,
"[Mode1]\n"
"Button.BTN_T1_UP = macro KEY_A KEY_B name \"Simple macro\"\n");
x52d_config_set("Profiles", "Directory", tmpdir);
x52d_config_set("Profiles", "Profile", "test");
x52d_profile_init();
memset(&report, 0, sizeof(report));
report.mode = 1;
action_name = x52d_profile_get_action_name(&report, LIBX52IO_BTN_T1_UP);
assert_non_null(action_name);
assert_string_equal(action_name, "Simple macro");
x52d_profile_exit();
unlink(path);
rmdir(tmpdir);
}
int main(void)
{
const struct CMUnitTest tests[] = {
cmocka_unit_test(test_profile_single_key),
cmocka_unit_test(test_profile_key_combo),
cmocka_unit_test(test_profile_macro_button_down_only),
cmocka_unit_test(test_profile_macro_pipe_combo),
cmocka_unit_test(test_profile_fallback_mode2_to_mode1),
cmocka_unit_test(test_profile_shift_layer),
cmocka_unit_test(test_profile_get_name),
cmocka_unit_test(test_profile_action_name_key),
cmocka_unit_test(test_profile_action_name_unquoted),
cmocka_unit_test(test_profile_action_name_none),
cmocka_unit_test(test_profile_action_name_shift_layer),
cmocka_unit_test(test_profile_action_name_macro),
};
cmocka_set_message_output(CM_OUTPUT_TAP);
return cmocka_run_group_tests(tests, NULL, NULL);
}