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