Refactor keyboard timing tests

This commit is contained in:
2023-12-16 19:40:57 +01:00
parent 607e5b02ef
commit fb57b0bdc6

View File

@ -86,6 +86,7 @@ pub async fn keypress_handler(
error!("first element in queue wasn't a modtap, wtf?"); error!("first element in queue wasn't a modtap, wtf?");
continue; continue;
}; };
debug!("modtap resolved as mod: {:?}", event.button);
slow_press(output, &event.button).await; slow_press(output, &event.button).await;
loop { loop {
@ -175,7 +176,7 @@ pub async fn keypress_handler(
if let Some(position_in_queue) = position_in_queue { if let Some(position_in_queue) = position_in_queue {
// If the modtap was still in the queue, it hasn't been resolved as a mod // If the modtap was still in the queue, it hasn't been resolved as a mod
// yet. Therefore, it is a Tap. Resolve all ModTaps before this one as Mods // yet. Therefore, it is a Tap. Resolve all ModTaps before this one as Mods
debug!("modtap was still in queue"); debug!("modtap was still in queue: {k:?}");
for _ in 0..position_in_queue { for _ in 0..position_in_queue {
let prev_event = queue.pop_front().unwrap(); let prev_event = queue.pop_front().unwrap();
debug!("pressing earlier event {:?}", prev_event); debug!("pressing earlier event {:?}", prev_event);
@ -219,7 +220,7 @@ pub async fn keypress_handler(
} }
} }
#[cfg(test)] #[cfg(all(target_arch = "x86_64", test))]
mod tests { mod tests {
use core::time; use core::time;
@ -233,12 +234,102 @@ mod tests {
}; };
use embassy_sync::{blocking_mutex::raw::NoopRawMutex, pubsub::PubSubChannel}; use embassy_sync::{blocking_mutex::raw::NoopRawMutex, pubsub::PubSubChannel};
use embassy_time::with_timeout; use embassy_time::with_timeout;
use log::info;
use tgnt::{button::Modifier, keys::Key}; use tgnt::{button::Modifier, keys::Key};
#[test] struct Test {
fn test_modtap_timings() { // button index, pressed, delay
simple_logger::SimpleLogger::new().init().unwrap(); input: Vec<(usize, bool, Duration)>,
expected: Vec<button::Event>,
}
macro_rules! timing_test {
($name:ident, $test:expr) => {
#[test]
fn $name() {
let test: Test = $test;
run_test(test);
}
};
}
const SHORT: Duration = Duration::from_millis(1);
const LONG: Duration = Duration::from_millis(160);
timing_test! {
modtap_mod,
Test {
input: vec![
(0, true, SHORT),
(1, true, SHORT),
(1, false, SHORT),
(0, false, SHORT),
],
expected: vec![
button::Event::PressMod(Modifier::LShift),
button::Event::PressKey(Key::B),
button::Event::ReleaseKey(Key::B),
button::Event::ReleaseMod(Modifier::LShift),
],
}
}
timing_test! {
modtap_tap,
Test {
input: vec![
(0, true, SHORT),
(1, true, SHORT),
(0, false, SHORT),
(1, false, SHORT),
],
expected: vec![
button::Event::PressKey(Key::A),
button::Event::ReleaseKey(Key::A),
button::Event::PressKey(Key::B),
button::Event::ReleaseKey(Key::B),
],
}
}
timing_test! { modtap_tap_2x, Test {
input: vec![
(0, true, SHORT),
(2, true, SHORT),
(2, false, SHORT),
(0, false, SHORT),
(0, true, SHORT),
(2, true, SHORT),
(2, false, SHORT),
(0, false, SHORT),
],
expected: vec![
button::Event::PressMod(Modifier::LShift),
button::Event::PressKey(Key::C),
button::Event::ReleaseKey(Key::C),
button::Event::ReleaseMod(Modifier::LShift),
button::Event::PressMod(Modifier::LShift),
button::Event::PressKey(Key::C),
button::Event::ReleaseKey(Key::C),
button::Event::ReleaseMod(Modifier::LShift),
],
}}
timing_test! { modtap_double_shift, Test {
input: vec![
(0, true, LONG),
(3, true, SHORT),
(0, false, LONG),
(3, false, SHORT),
],
expected: vec![
button::Event::PressMod(Modifier::LShift),
button::Event::ReleaseMod(Modifier::LShift),
button::Event::PressMod(Modifier::RShift),
button::Event::ReleaseMod(Modifier::RShift),
],
}}
fn run_test(test: Test) {
let _ = simple_logger::SimpleLogger::new().init();
let switch_events = PubSubChannel::<NoopRawMutex, switch::Event, 10, 1, 1>::new(); let switch_events = PubSubChannel::<NoopRawMutex, switch::Event, 10, 1, 1>::new();
let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new(); let button_events = PubSubChannel::<NoopRawMutex, button::Event, 10, 1, 1>::new();
@ -248,142 +339,76 @@ mod tests {
Button::ModTap(Key::A, Modifier::LShift), Button::ModTap(Key::A, Modifier::LShift),
Button::ModTap(Key::B, Modifier::LCtrl), Button::ModTap(Key::B, Modifier::LCtrl),
Button::Key(Key::C), Button::Key(Key::C),
Button::ModTap(Key::D, Modifier::RShift),
]; ];
struct Test { // future that iterates over the test input and sends switch events.
description: &'static str, let clicker = async {
// button index, pressed, delay for &(i, pressed, delay) in &test.input {
input: Vec<(usize, bool, Duration)>, let kind = if pressed {
expected: Vec<button::Event>, switch::EventKind::Press {
} button: buttons[i].clone(),
}
} else {
switch::EventKind::Release {
button: buttons[i].clone(),
after: time::Duration::ZERO, // ignore
}
};
let modtap_mod = Test { let event = switch::Event {
description: "modtap mod", source: Half::Left,
input: vec![ source_button: i,
(0, true, Duration::from_millis(25)), kind,
(1, true, Duration::from_millis(25)), };
(1, false, Duration::from_millis(25)),
(0, false, Duration::from_millis(25)), switch_events.publish_immediate(event);
],
expected: vec![ Timer::after(delay).await;
button::Event::PressMod(Modifier::LShift), }
button::Event::PressKey(Key::B),
button::Event::ReleaseKey(Key::B),
button::Event::ReleaseMod(Modifier::LShift),
],
}; };
let output_listener = async {
let mut got = Vec::new();
for _expected in &test.expected {
let timeout = LONG + Duration::from_millis(50);
let event = match with_timeout(timeout, button_sub.next_message()).await {
Ok(WaitResult::Message(event)) => event,
Ok(WaitResult::Lagged(_)) => return Err(("lagged", got)),
Err(_) => return Err(("timeout", got)),
};
let modtap_tap = Test { got.push(event.clone());
description: "modtap tap", }
input: vec![
(0, true, Duration::from_millis(25)),
(1, true, Duration::from_millis(25)),
(0, false, Duration::from_millis(25)),
(1, false, Duration::from_millis(25)),
],
expected: vec![
button::Event::PressKey(Key::A),
button::Event::ReleaseKey(Key::A),
button::Event::PressKey(Key::B),
button::Event::ReleaseKey(Key::B),
],
};
let modtap_tap_2x = Test { for (event, expected) in got.iter().zip(test.expected.iter()) {
description: "2x modtap tap", if event != expected {
input: vec![ return Err(("unexpected event", got));
(0, true, Duration::from_millis(25)),
(2, true, Duration::from_millis(25)),
(2, false, Duration::from_millis(25)),
(0, false, Duration::from_millis(25)),
(0, true, Duration::from_millis(25)),
(2, true, Duration::from_millis(25)),
(2, false, Duration::from_millis(25)),
(0, false, Duration::from_millis(25)),
],
expected: vec![
button::Event::PressMod(Modifier::LShift),
button::Event::PressKey(Key::C),
button::Event::ReleaseKey(Key::C),
button::Event::ReleaseMod(Modifier::LShift),
button::Event::PressMod(Modifier::LShift),
button::Event::PressKey(Key::C),
button::Event::ReleaseKey(Key::C),
button::Event::ReleaseMod(Modifier::LShift),
],
};
for test in [modtap_tap, modtap_mod, modtap_tap_2x] {
info!("running timing test test {:?}", test.description);
embassy_futures::block_on(async {
let r = select(
join(
async {
for &(i, pressed, delay) in &test.input {
let kind = if pressed {
switch::EventKind::Press {
button: buttons[i].clone(),
}
} else {
switch::EventKind::Release {
button: buttons[i].clone(),
after: time::Duration::ZERO, // ignore
}
};
let event = switch::Event {
source: Half::Left,
source_button: i,
kind,
};
switch_events.publish_immediate(event);
Timer::after(delay).await;
}
},
async {
let mut got = Vec::new();
for _expected in &test.expected {
let event = match with_timeout(
Duration::from_millis(200),
button_sub.next_message(),
)
.await
{
Ok(WaitResult::Message(event)) => event,
Ok(WaitResult::Lagged(_)) => return Err(("lagged", got)),
Err(_) => return Err(("timeout", got)),
};
got.push(event.clone());
}
for (event, expected) in got.iter().zip(test.expected.iter()) {
if event != expected {
return Err(("unexpected event", got));
}
}
Ok(())
},
),
convert_events(
&mut *switch_events.subscriber().unwrap(),
&mut *button_events.publisher().unwrap(),
),
)
.await;
match r {
Either::First(((), Ok(()))) => {}
Either::First(((), Err((msg, got)))) => panic!(
"timing test {:?} failed due to {msg}.\nexpected={:#?} got={:#?}",
test.description, test.expected, got
),
Either::Second(never) => never,
} }
}); }
}
Ok(())
};
embassy_futures::block_on(async {
let r = select(
join(clicker, output_listener),
keypress_handler(
&mut *switch_events.subscriber().unwrap(),
&mut *button_events.publisher().unwrap(),
),
)
.await;
match r {
Either::First(((), Ok(()))) => {}
Either::First(((), Err((msg, got)))) => panic!(
"timing test failed due to {msg}.\nexpected={:#?} got={:#?}",
test.expected, got
),
Either::Second(never) => never,
}
});
panic!();
} }
} }