Skip to content

Commit e8c3598

Browse files
committed
test: compare event codes against the local Linux headers
1 parent ebf6a42 commit e8c3598

1 file changed

Lines changed: 142 additions & 1 deletion

File tree

tests/local.rs

Lines changed: 142 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
use std::io;
1+
//! Tests against the local system.
2+
3+
use core::{any::type_name, str::FromStr};
4+
use std::{collections::HashMap, fs, io};
5+
6+
use evdevil::event::{Abs, Key, Led, Misc, Rel, Sound, Switch};
27

38
/// Tests that all devices on the local system can be enumerated, and that `EventReader`
49
/// successfully synchronizes.
@@ -12,3 +17,139 @@ fn enumerate_local_devices() -> io::Result<()> {
1217

1318
Ok(())
1419
}
20+
21+
/// Parses the system's local `input-event-codes.h` and compares our event code values against it.
22+
///
23+
/// Depending on what system runs the test, the header file might be missing some newer constants,
24+
/// but all constants that are present should match `evdevil`'s own.
25+
#[test]
26+
#[cfg_attr(not(target_os = "linux"), ignore = "only works on Linux")]
27+
fn event_codes() -> io::Result<()> {
28+
static PATH: &str = "/usr/include/linux/input-event-codes.h";
29+
static SKIP: &[&str] = &["KEY_MIN_INTERESTING"];
30+
31+
let contents = match fs::read_to_string(PATH) {
32+
Ok(contents) => contents,
33+
Err(e) => {
34+
return Err(io::Error::new(
35+
e.kind(),
36+
format!("failed to read '{PATH}': {e}"),
37+
));
38+
}
39+
};
40+
41+
let mut defines = HashMap::new();
42+
for line in contents.lines() {
43+
let Some(line) = line.strip_prefix("#define") else {
44+
continue;
45+
};
46+
47+
let mut split = line.split_ascii_whitespace();
48+
let name = split.next().unwrap();
49+
let Some(value) = split.next() else {
50+
continue;
51+
};
52+
53+
if SKIP.contains(&name) {
54+
continue;
55+
}
56+
57+
if name.ends_with("_CNT") || name.ends_with("_MAX") {
58+
continue;
59+
}
60+
61+
let value = match defines.get(value) {
62+
Some(val) => *val,
63+
None => {
64+
let n = if value.starts_with("0x") {
65+
u32::from_str_radix(&value[2..], 16)
66+
} else {
67+
u32::from_str(value)
68+
};
69+
match n {
70+
Ok(n) => n,
71+
Err(e) => {
72+
panic!("failed to parse value '{value}' of constant '{name}': {e}");
73+
}
74+
}
75+
}
76+
};
77+
defines.insert(name, value);
78+
79+
// FIXME: we skip `InputProp`, `EventType`, and `Syn` because they aren't parseable
80+
// (not sure if `FromStr` would be useful there)
81+
if name.starts_with("KEY_") || name.starts_with("BTN_") {
82+
check::<Key>(name, value);
83+
} else if name.starts_with("REL_") {
84+
check::<Rel>(name, value);
85+
} else if name.starts_with("ABS_") {
86+
check::<Abs>(name, value);
87+
} else if name.starts_with("SW_") {
88+
check::<Switch>(name, value);
89+
} else if name.starts_with("MSC_") {
90+
check::<Misc>(name, value);
91+
} else if name.starts_with("LED_") {
92+
check::<Led>(name, value);
93+
} else if name.starts_with("SND_") && !name.starts_with("SND_PROFILE") {
94+
check::<Sound>(name, value);
95+
} else {
96+
continue;
97+
}
98+
}
99+
100+
Ok(())
101+
}
102+
103+
trait Raw {
104+
fn raw(self) -> u32;
105+
}
106+
impl Raw for Key {
107+
fn raw(self) -> u32 {
108+
self.raw().into()
109+
}
110+
}
111+
impl Raw for Rel {
112+
fn raw(self) -> u32 {
113+
self.raw().into()
114+
}
115+
}
116+
impl Raw for Abs {
117+
fn raw(self) -> u32 {
118+
self.raw().into()
119+
}
120+
}
121+
impl Raw for Switch {
122+
fn raw(self) -> u32 {
123+
self.raw().into()
124+
}
125+
}
126+
impl Raw for Misc {
127+
fn raw(self) -> u32 {
128+
self.raw().into()
129+
}
130+
}
131+
impl Raw for Led {
132+
fn raw(self) -> u32 {
133+
self.raw().into()
134+
}
135+
}
136+
impl Raw for Sound {
137+
fn raw(self) -> u32 {
138+
self.raw().into()
139+
}
140+
}
141+
fn check<T>(s: &str, expected_value: u32)
142+
where
143+
T: FromStr + Raw,
144+
{
145+
match T::from_str(s) {
146+
Ok(t) => {
147+
assert_eq!(t.raw(), expected_value);
148+
}
149+
Err(_) => {
150+
// This is not a fatal error because it most often indicates that a newly added constant
151+
// hasn't been added to `evdevil` yet.
152+
eprintln!("failed to parse '{s}' as a {}", type_name::<T>());
153+
}
154+
}
155+
}

0 commit comments

Comments
 (0)