If a timeout hits and events come in, it depends on whichever fd is read first on what order the events are handled. If the device is handled first, the events may be processed as if the timeout never happened.
This is particularly noticable in the test, if we call litest_timeout_..() without a subsequent litest_dispatch(), the timeout is usually ignored.
We need a mechanism to compare event timestamps to timer triggers and yield if the other one is earlier. This goes both ways, if litest_dispatch() didn't get called for a while we need to process any events with times before the timeout triggered.
Dump of some notes:
the two functions that matter here are evdev_device_dispatch() and libinput_timer_handler().
libinput_dispatch() would need to call into all sources with a get_timestamp(), that can return the next time to process events from that source. Put that into an ordered list, then start processing the sources according to that timestamp.
For timers we only have one event to process, but devices can have more than one event waiting. So ideally pass the timestamp for the _next_ device in the list into dispatch(). That way evdev_device_dispatch() can return the current event timestamp whenever that exceeds the other device's timestamp value, and be re-ordered into the list.
This does require us to store the event though, libevdev doesn't have a peek event function, so we have to copy it into our struct, with the usual "valid" marker flags.
Quick testing shows that this doesn't seem to really be an issue in real-world usage though the tests hit this quite easily.
closing as wontfix, this is a lot of effort just for the benefit of the tests. In real-world usage if the timeouts are hit before the event is processed something is going way too slow and we'll run into a bunch of other issues anyway.