Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Fix failing device tests
  • Loading branch information
Henri committed Apr 15, 2025
commit c3d1a446c4af7b0aa67110dcd5165592f71520b7
2 changes: 2 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ jobs:
run: cargo test --verbose
- name: Doc
run: cargo doc --all-features
- name: Run device tests
run: sudo -E env "PATH=$PATH" cargo test --tests --features device-test

cross-linux:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ rust-version = "1.64"
serde = ["dep:serde"]
tokio = ["dep:tokio"]
stream-trait = ["tokio", "futures-core"]
device-test = []

[dependencies]
libc = { version = "0.2.121", features = ["extra_traits"]}
Expand Down
20 changes: 0 additions & 20 deletions src/sync_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,6 @@ pub use tokio_stream::EventStream;
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{get_test_device, key_event};

fn result_events_iter(
events: &[input_event],
Expand Down Expand Up @@ -957,23 +956,4 @@ mod tests {
assert_eq!(next(), (Err(false), None));
assert_eq!(next(), (Err(false), None));
}

#[test]
pub fn test_get_key_state() -> Result<(), Box<dyn std::error::Error>> {
let (input, mut output) = get_test_device()?;

output.emit(&[key_event(KeyCode::KEY_DOT, 1)])?;

assert_eq!(1, input.get_key_state()?.iter().count());
assert!(input
.get_key_state()?
.iter()
.all(|e| e.code() == KeyCode::KEY_DOT.code()));

output.emit(&[key_event(KeyCode::KEY_DOT, 0)])?;

assert_eq!(0, input.get_key_state()?.iter().count());

Ok(())
}
}
7 changes: 4 additions & 3 deletions src/tests/attribute_set.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
use crate::tests::get_test_device;
use crate::{AttributeSet, KeyCode};

#[test]
pub fn test_iteration_keys() -> std::io::Result<()> {
let (input, _) = get_test_device()?;
let mut keys: AttributeSet<KeyCode> = AttributeSet::new();

let keys = input.supported_keys().unwrap();
for code in 1..59 {
keys.insert(KeyCode::new(code));
}

assert_eq!(58, keys.iter().count());

Expand Down
86 changes: 0 additions & 86 deletions src/tests/mod.rs
Original file line number Diff line number Diff line change
@@ -1,87 +1 @@
mod attribute_set;
mod compensate;

use crate::uinput::VirtualDevice;
use crate::{AttributeSet, BusType, Device, EventType, InputEvent, InputId, KeyCode, SwitchCode};
use std::path::PathBuf;
use std::thread;
use std::time::Duration;

pub fn key_click(key: KeyCode) -> Vec<InputEvent> {
vec![key_event(key, 1), key_event(key, 0)]
}

pub fn key_event(key: KeyCode, key_state: i32) -> InputEvent {
InputEvent::new(EventType::KEY.0, key.code(), key_state)
}

pub fn get_test_device() -> std::io::Result<(Device, VirtualDevice)> {
let (name, output) = get_device()?;

let mut input = Device::open(&name)?;

input.grab()?;

Ok((input, output))
}

pub fn get_device() -> std::io::Result<(PathBuf, VirtualDevice)> {
let mut keys: AttributeSet<KeyCode> = AttributeSet::new();
for code in 1..59 {
let key = KeyCode::new(code);
let name = format!("{:?}", key);
if name.starts_with("KEY_") {
keys.insert(key);
}
}

let mut sw: AttributeSet<SwitchCode> = AttributeSet::new();

sw.insert(SwitchCode::SW_LID);
sw.insert(SwitchCode::SW_TABLET_MODE);

let mut device = VirtualDevice::builder()?
.input_id(InputId::new(BusType::BUS_USB, 0x1234, 0x5678, 0x111))
.name("test device")
.with_keys(&keys)?
.with_switches(&sw)?
.build()?;

// Fetch name.
let d: Vec<std::path::PathBuf> = device
.enumerate_dev_nodes_blocking()?
.map(|p| p.unwrap())
.collect();

thread::sleep(Duration::from_millis(100)); // To avoid permission denied.

Ok((d.first().unwrap().clone(), device))
}

pub fn final_dot_state(start_state: i32, events: impl Iterator<Item = InputEvent>) -> i32 {
events.fold(start_state, |state, ev| {
if ev.event_type() == EventType::KEY && ev.code() == KeyCode::KEY_DOT.code() {
if ev.value() == 0 {
0
} else {
1
}
} else {
state
}
})
}

pub fn final_event_state(key: KeyCode, events: &Vec<InputEvent>) -> Option<i32> {
events.iter().fold(None, |state, ev| {
if ev.event_type() == EventType::KEY && ev.code() == key.code() {
if ev.value() == 0 {
Some(0)
} else {
Some(1)
}
} else {
state
}
})
}
86 changes: 86 additions & 0 deletions tests/common/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
#![allow(dead_code)]

use evdev::uinput::VirtualDevice;
use evdev::{AttributeSet, BusType, Device, EventType, InputEvent, InputId, KeyCode, SwitchCode};
use std::path::PathBuf;
use std::thread;
use std::time::Duration;

pub fn key_click(key: KeyCode) -> Vec<InputEvent> {
vec![key_event(key, 1), key_event(key, 0)]
}

pub fn key_event(key: KeyCode, key_state: i32) -> InputEvent {
InputEvent::new(EventType::KEY.0, key.code(), key_state)
}

pub fn get_test_device() -> std::io::Result<(Device, VirtualDevice)> {
let (name, output) = get_device()?;

let mut input = Device::open(&name)?;

input.grab()?;

Ok((input, output))
}

pub fn get_device() -> std::io::Result<(PathBuf, VirtualDevice)> {
let mut keys: AttributeSet<KeyCode> = AttributeSet::new();
for code in 1..59 {
let key = KeyCode::new(code);
let name = format!("{:?}", key);
if name.starts_with("KEY_") {
keys.insert(key);
}
}

let mut sw: AttributeSet<SwitchCode> = AttributeSet::new();

sw.insert(SwitchCode::SW_LID);
sw.insert(SwitchCode::SW_TABLET_MODE);

let mut device = VirtualDevice::builder()?
.input_id(InputId::new(BusType::BUS_USB, 0x1234, 0x5678, 0x111))
.name("test device")
.with_keys(&keys)?
.with_switches(&sw)?
.build()?;

// Fetch name.
let d: Vec<std::path::PathBuf> = device
.enumerate_dev_nodes_blocking()?
.map(|p| p.unwrap())
.collect();

thread::sleep(Duration::from_millis(100)); // To avoid permission denied.

Ok((d.first().unwrap().clone(), device))
}

pub fn final_dot_state(start_state: i32, events: impl Iterator<Item = InputEvent>) -> i32 {
events.fold(start_state, |state, ev| {
if ev.event_type() == EventType::KEY && ev.code() == KeyCode::KEY_DOT.code() {
if ev.value() == 0 {
0
} else {
1
}
} else {
state
}
})
}

pub fn final_event_state(key: KeyCode, events: &Vec<InputEvent>) -> Option<i32> {
events.iter().fold(None, |state, ev| {
if ev.event_type() == EventType::KEY && ev.code() == key.code() {
if ev.value() == 0 {
Some(0)
} else {
Some(1)
}
} else {
state
}
})
}
8 changes: 6 additions & 2 deletions src/tests/compensate.rs → tests/compensate.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
use crate::tests::{final_dot_state, final_event_state, get_test_device, key_click, key_event};
use crate::{EventType, InputEvent, KeyCode, SwitchCode};
#![cfg(feature = "device-test")]

mod common;

use common::{final_dot_state, final_event_state, get_test_device, key_click, key_event};
use evdev::{EventType, InputEvent, KeyCode, SwitchCode};

#[test]
pub fn test_compensate_keys() -> std::io::Result<()> {
Expand Down
25 changes: 25 additions & 0 deletions tests/device.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#![cfg(feature = "device-test")]

mod common;

use common::{get_test_device, key_event};
use evdev::KeyCode;

#[test]
pub fn test_get_key_state() -> Result<(), Box<dyn std::error::Error>> {
let (input, mut output) = get_test_device()?;

output.emit(&[key_event(KeyCode::KEY_DOT, 1)])?;

assert_eq!(1, input.get_key_state()?.iter().count());
assert!(input
.get_key_state()?
.iter()
.all(|e| e.code() == KeyCode::KEY_DOT.code()));

output.emit(&[key_event(KeyCode::KEY_DOT, 0)])?;

assert_eq!(0, input.get_key_state()?.iter().count());

Ok(())
}
Loading