mirror of
https://github.com/osiweb/unified_retro_keyboard.git
synced 2024-11-29 20:49:17 +00:00
506 lines
14 KiB
C
506 lines
14 KiB
C
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "unity.h"
|
|
#include "asdf.h"
|
|
#include "asdf_arch.h"
|
|
#include "asdf_ascii.h"
|
|
#include "asdf_modifiers.h"
|
|
#include "asdf_keymaps.h"
|
|
#include "asdf_keymap_defs.h"
|
|
#include "asdf_buffer.h"
|
|
#include "asdf_repeat.h"
|
|
|
|
#define A 'a'
|
|
#define B 'b'
|
|
|
|
#define TEST_STRING "abcdefghijklmnop"
|
|
|
|
#define NUM_REPEATS (ASDF_KEYCODE_BUFFER_SIZE - 2)
|
|
|
|
#define TESTMAP(row, col, mapname, mapname2) \
|
|
do { \
|
|
asdf_keycode_t expected = mapname##_matrix[(row)][(col)]; \
|
|
asdf_keycode_t result = asdf_keymaps_get_code((row), (col), MOD_##mapname2##_MAP); \
|
|
TEST_ASSERT_EQUAL_INT(expected, result); \
|
|
} while (0);
|
|
|
|
#define TESTPLAIN(row, col, n) TESTMAP((row), (col), PLAIN, n)
|
|
#define TESTSHIFT(row, col, n) TESTMAP((row), (col), SHIFT, n)
|
|
#define TESTCAPS(row, col, n) TESTMAP((row), (col), CAPS, n)
|
|
#define TESTCTRL(row, col, n) TESTMAP((row), (col), CTRL, n)
|
|
|
|
const char test_string[] = TEST_STRING;
|
|
|
|
const asdf_keycode_t key_a = A;
|
|
const asdf_keycode_t key_b = B;
|
|
|
|
typedef struct {
|
|
int32_t row;
|
|
int32_t col;
|
|
} coord_t;
|
|
|
|
|
|
ASDF_TEST_DECLARATIONS;
|
|
|
|
static uint32_t key_matrix[ASDF_NUM_ROWS];
|
|
|
|
|
|
void setUp(void)
|
|
{
|
|
asdf_buffer_init();
|
|
asdf_init();
|
|
|
|
// initialize simulated key matrix
|
|
for (uint32_t i = 0; i < ASDF_NUM_ROWS; i++) {
|
|
key_matrix[i] = 0;
|
|
}
|
|
}
|
|
|
|
void tearDown(void) {}
|
|
|
|
coord_t *find_code(asdf_keycode_t code)
|
|
{
|
|
uint32_t done = 0;
|
|
static coord_t location = { .row = -1, .col = -1 };
|
|
|
|
for (uint32_t row = 0; !done && (row < ASDF_NUM_ROWS); row++) {
|
|
for (uint32_t col = 0; !done && (col < ASDF_NUM_COLS); col++) {
|
|
if (test_PLAIN_matrix[row][col] == code) {
|
|
done = 1;
|
|
location.row = row;
|
|
location.col = col;
|
|
}
|
|
}
|
|
}
|
|
return &location;
|
|
}
|
|
|
|
|
|
asdf_keycode_t shifted(asdf_keycode_t code)
|
|
{
|
|
coord_t *location = find_code(code);
|
|
return test_SHIFT_matrix[location->row][location->col];
|
|
}
|
|
|
|
asdf_keycode_t caps(asdf_keycode_t code)
|
|
{
|
|
coord_t *xy = find_code(code);
|
|
return test_CAPS_matrix[xy->row][xy->col];
|
|
}
|
|
|
|
asdf_keycode_t ctrl(asdf_keycode_t code)
|
|
{
|
|
coord_t *xy = find_code(code);
|
|
return test_CTRL_matrix[xy->row][xy->col];
|
|
}
|
|
|
|
|
|
void keyscan_delay(int32_t ticks)
|
|
{
|
|
for (; ticks; ticks--) {
|
|
asdf_keyscan();
|
|
}
|
|
}
|
|
|
|
|
|
void press_no_debounce(asdf_keycode_t code)
|
|
{
|
|
coord_t *location = find_code(code);
|
|
key_matrix[location->row] |= (1 << location->col);
|
|
}
|
|
|
|
void release_no_debounce(asdf_keycode_t code)
|
|
{
|
|
coord_t *location = find_code(code);
|
|
key_matrix[location->row] &= ~(1 << location->col);
|
|
}
|
|
|
|
void press(asdf_keycode_t code)
|
|
{
|
|
press_no_debounce(code);
|
|
keyscan_delay(ASDF_DEBOUNCE_TIME_MS);
|
|
}
|
|
|
|
void release(asdf_keycode_t code)
|
|
{
|
|
release_no_debounce(code);
|
|
keyscan_delay(ASDF_DEBOUNCE_TIME_MS);
|
|
}
|
|
|
|
|
|
asdf_cols_t asdf_arch_read_row(uint8_t row)
|
|
{
|
|
return key_matrix[row];
|
|
}
|
|
|
|
|
|
|
|
// No repsonse to a keypress before it is debounced.
|
|
void pressing_a_gives_nothing_before_debounce(void)
|
|
{
|
|
press_no_debounce(key_a);
|
|
|
|
// no keypress after only ASDF_DEBOUNCE_TIME_MS -1 ticks (not yet debounced):
|
|
keyscan_delay(ASDF_DEBOUNCE_TIME_MS - 1);
|
|
TEST_ASSERT_EQUAL_INT32(ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing 'A' gives 'a'
|
|
void pressing_a_gives_a(void)
|
|
{
|
|
press_no_debounce(key_a);
|
|
|
|
// no keypress after only ASDF_DEBOUNCE_TIME_MS -1 ticks (not yet debounced):
|
|
keyscan_delay(ASDF_DEBOUNCE_TIME_MS - 1);
|
|
TEST_ASSERT_EQUAL_INT32(ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
|
|
// allow the key to finish debounce
|
|
keyscan_delay(1);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (int32_t) asdf_next_code());
|
|
|
|
// no more codes in the buffer.
|
|
TEST_ASSERT_EQUAL_INT32(ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing SHIFT+A gives 'A'
|
|
void pressing_shift_a_gives_shifted_a(void)
|
|
{
|
|
press(ACTION_SHIFT);
|
|
press(key_a);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) shifted(key_a), (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing CAPS+A gives 'A'
|
|
void pressing_caps_a_gives_caps_a(void)
|
|
{
|
|
press(ACTION_CAPS);
|
|
release(ACTION_CAPS);
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) caps(key_a), (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing CTRL+A gives 0x01 (Ctrl-A)
|
|
void pressing_ctrl_a_gives_ctrl_a(void)
|
|
{
|
|
press(ACTION_CTRL);
|
|
press(key_a);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ctrl(key_a), (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing REPT+A repeats 'a'
|
|
void pressing_rept_a_repeats_a(void)
|
|
{
|
|
press(ACTION_REPEAT);
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing REPT+SHIFT+A repeats 'A'
|
|
void pressing_shift_rept_a_repeats_shifted_a(void)
|
|
{
|
|
press(ACTION_REPEAT);
|
|
press(ACTION_SHIFT);
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) shifted(key_a), (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) shifted(key_a), (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing REPT+CAPS+A repeats 'A'
|
|
void pressing_caps_rept_a_repeats_caps_a(void)
|
|
{
|
|
press(ACTION_REPEAT);
|
|
press(ACTION_CAPS);
|
|
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) caps(key_a), (uint32_t) asdf_next_code());
|
|
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) caps(key_a), (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing REPT+CTRL+A repeats CTRL-A
|
|
void pressing_ctrl_rept_a_repeats_ctrl_a(void)
|
|
{
|
|
press(ACTION_REPEAT);
|
|
press(ACTION_CTRL);
|
|
|
|
press(key_a);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ctrl(key_a), (uint32_t) asdf_next_code());
|
|
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
for (int i = 0; i < NUM_REPEATS + 1; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ctrl(key_a), (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// pressing and holding 'A' autorepeats 'A'
|
|
void holding_a_autorepeats_a(void)
|
|
{
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing and holding 'A' autorepeats 'A'
|
|
void holding_a_autorepeats_slow_then_fast(void)
|
|
{
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing and holding 'A' and 'B' within less than debounce interval
|
|
// eventually returns 'A' then 'B'
|
|
void pressing_a_then_b_before_debounce_gives_a_then_b(void)
|
|
{
|
|
press_no_debounce(key_a);
|
|
|
|
// press B very quickly after a
|
|
keyscan_delay(1);
|
|
|
|
press(key_b);
|
|
|
|
// first get back A
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// next get back B
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
|
|
// and then verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing and holding a series of keys (up to buffer size) in rapid
|
|
// succession, allowing each key to debounce, before sending the next, sends all
|
|
// the keys in order. (n-key rollover)
|
|
void test_key_sequence_nkro(void)
|
|
{
|
|
for (int i = 0; i < (int32_t) strlen(test_string); i++) {
|
|
press((asdf_keycode_t) test_string[i]);
|
|
}
|
|
|
|
for (int i = 0; i < (int32_t) strlen(test_string); i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) test_string[i], (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// and then verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// pressing and holding a series of keys (up to buffer size) in rapid succession
|
|
// without waiting for prior keys to debounce, eventually debounces and sends
|
|
// all the keys in order. (n-key rollover)
|
|
void test_key_sequence_nkro_simultaneous_debounce(void)
|
|
{
|
|
for (int i = 0; i < (int32_t) strlen(test_string); i++) {
|
|
press_no_debounce((asdf_keycode_t) test_string[i]);
|
|
keyscan_delay(1);
|
|
}
|
|
|
|
// several keys are already debounced, but now make sure they all are:
|
|
keyscan_delay(ASDF_DEBOUNCE_TIME_MS);
|
|
|
|
for (int i = 0; i < (int32_t) strlen(test_string); i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) test_string[i], (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// and then verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// holding 'A' then pressing 'B' before autorepeat interval and holding 'B' gives 'A', then repeats
|
|
// 'B'
|
|
void holding_a_briefly_then_holding_b_gives_a_and_repeats_b(void)
|
|
{
|
|
press(key_a);
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS / 2);
|
|
press(key_b);
|
|
|
|
// hold "a" and "b" for autorepeat delay:
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS);
|
|
|
|
// hold "a" and "b" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
// should get "a" back, then "b"
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
|
|
// now get back NUM_REEPEATS repetitions of "b"
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and then verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
// holding 'B' while repeating 'A' starts autorepeat delay, then starts repeating 'B'
|
|
void holding_a_then_holding_b_autorepeats_a_then_autorepeats_b(void)
|
|
{
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for AUTOREPEAT delay
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
// empty the buffer to make room for 'B'
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// now press "b" while "a" is autorepeating:
|
|
|
|
press(key_b);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for autorepeat delay
|
|
keyscan_delay(ASDF_AUTOREPEAT_TIME_MS);
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
// empty the buffer to make room for 'B'
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
// Pressing and holding 'A' then holding 'B' with repeat key held repeats 'A' then 'B'
|
|
void repeating_with_a_then_adding_b_repeats_a_then_repeats_b(void)
|
|
{
|
|
press(ACTION_REPEAT);
|
|
press(key_a);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
// empty the buffer to make room for 'B'
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_a, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// now press "b" while "a" is autorepeating:
|
|
|
|
press(key_b);
|
|
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
|
|
// hold "a" for NUM_REPEATS repeat cycles:
|
|
keyscan_delay(NUM_REPEATS * ASDF_REPEAT_TIME_MS);
|
|
|
|
// empty the buffer to make room for 'B'
|
|
for (int i = 0; i < NUM_REPEATS; i++) {
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) key_b, (uint32_t) asdf_next_code());
|
|
}
|
|
|
|
// and verify there are no more codes in buffer:
|
|
TEST_ASSERT_EQUAL_INT32((int32_t) ASDF_INVALID_CODE, (int32_t) asdf_next_code());
|
|
}
|
|
|
|
|
|
int main(void)
|
|
{
|
|
UNITY_BEGIN();
|
|
RUN_TEST(pressing_a_gives_nothing_before_debounce);
|
|
RUN_TEST(pressing_a_gives_a);
|
|
RUN_TEST(pressing_shift_a_gives_shifted_a);
|
|
RUN_TEST(pressing_caps_a_gives_caps_a);
|
|
RUN_TEST(pressing_ctrl_a_gives_ctrl_a);
|
|
RUN_TEST(pressing_rept_a_repeats_a);
|
|
RUN_TEST(pressing_shift_rept_a_repeats_shifted_a);
|
|
RUN_TEST(pressing_caps_rept_a_repeats_caps_a);
|
|
RUN_TEST(holding_a_autorepeats_a);
|
|
RUN_TEST(holding_a_autorepeats_slow_then_fast);
|
|
RUN_TEST(pressing_a_then_b_before_debounce_gives_a_then_b);
|
|
RUN_TEST(test_key_sequence_nkro);
|
|
RUN_TEST(test_key_sequence_nkro_simultaneous_debounce);
|
|
RUN_TEST(holding_a_briefly_then_holding_b_gives_a_and_repeats_b);
|
|
RUN_TEST(holding_a_then_holding_b_autorepeats_a_then_autorepeats_b);
|
|
RUN_TEST(repeating_with_a_then_adding_b_repeats_a_then_repeats_b);
|
|
|
|
|
|
|
|
return UNITY_END();
|
|
}
|