Add tests for virtio input device

These tests would have caught chromium:939585

Test=cargo test -p devices

Change-Id: If980fe8b56b9d76e77b652fb727b8849dbedd9cc
Reviewed-on: https://chromium-review.googlesource.com/1512759
Commit-Ready: ChromeOS CL Exonerator Bot <chromiumos-cl-exonerator@appspot.gserviceaccount.com>
Tested-by: kokoro <noreply+kokoro@google.com>
Tested-by: Jorge Moreira Broche <jemoreira@google.com>
Reviewed-by: Dylan Reid <dgreid@chromium.org>
diff --git a/devices/src/virtio/input/event_source.rs b/devices/src/virtio/input/event_source.rs
index d75f124..5e24f08 100644
--- a/devices/src/virtio/input/event_source.rs
+++ b/devices/src/virtio/input/event_source.rs
@@ -135,7 +135,7 @@
 
 impl<T> EventSourceImpl<T>
 where
-    T: Read + Write + AsRawFd,
+    T: Read + Write,
 {
     // Receive events from the source and store them in a queue, unless they should be filtered out.
     fn receive_events<F: Fn(&input_event) -> bool>(&mut self, event_filter: F) -> Result<usize> {
@@ -332,3 +332,187 @@
         self.evt_source_impl.available_events()
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use data_model::{DataInit, Le16, Le32};
+    use std::cmp::min;
+    use std::io::Read;
+    use std::io::Write;
+    use std::mem::size_of;
+    use virtio::input::event_source::input_event;
+    use virtio::input::event_source::EventSourceImpl;
+    use virtio::input::virtio_input_event;
+
+    struct SourceMock {
+        events: Vec<u8>,
+    }
+
+    impl SourceMock {
+        fn new(evts: &Vec<input_event>) -> SourceMock {
+            let mut events: Vec<u8> = vec![];
+            for evt in evts {
+                for byte in evt.as_slice() {
+                    events.push(byte.clone());
+                }
+            }
+            SourceMock { events }
+        }
+    }
+
+    impl Read for SourceMock {
+        fn read(&mut self, buf: &mut [u8]) -> std::result::Result<usize, std::io::Error> {
+            let copy_size = min(buf.len(), self.events.len());
+            buf[..copy_size].copy_from_slice(&self.events[..copy_size]);
+            Ok(copy_size)
+        }
+    }
+    impl Write for SourceMock {
+        fn write(&mut self, buf: &[u8]) -> std::result::Result<usize, std::io::Error> {
+            Ok(buf.len())
+        }
+
+        fn flush(&mut self) -> std::result::Result<(), std::io::Error> {
+            Ok(())
+        }
+    }
+
+    #[test]
+    fn empty_new() {
+        let mut source = EventSourceImpl::new(SourceMock::new(&vec![]));
+        assert_eq!(
+            source.available_events(),
+            0,
+            "zero events should be available"
+        );
+        let mut buffer = [0u8, 80];
+        assert_eq!(
+            source.read(&mut buffer).unwrap(),
+            0,
+            "zero events should be read"
+        );
+    }
+
+    #[test]
+    fn empty_receive() {
+        let mut source = EventSourceImpl::new(SourceMock::new(&vec![]));
+        assert_eq!(
+            source.receive_events(|_| true).unwrap(),
+            0,
+            "zero events should be received"
+        );
+        let mut buffer = [0u8, 80];
+        assert_eq!(
+            source.read(&mut buffer).unwrap(),
+            0,
+            "zero events should be read"
+        );
+    }
+
+    fn instantiate_input_events(count: usize) -> Vec<input_event> {
+        let mut ret: Vec<input_event> = Vec::with_capacity(count);
+        for idx in 0..count {
+            ret.push(input_event {
+                timestamp_fields: [0, 0],
+                type_: 3 * (idx as u16) + 1,
+                code: 3 * (idx as u16) + 2,
+                value: 3 * (idx as u32) + 3,
+            });
+        }
+        ret
+    }
+
+    fn assert_events_match(e1: &virtio_input_event, e2: &input_event) {
+        assert_eq!(e1.type_, Le16::from(e2.type_), "type should match");
+        assert_eq!(e1.code, Le16::from(e2.code), "code should match");
+        assert_eq!(e1.value, Le32::from(e2.value), "value should match");
+    }
+
+    #[test]
+    fn partial_read() {
+        let evts = instantiate_input_events(4usize);
+        let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+        assert_eq!(
+            source.receive_events(|_| true).unwrap(),
+            evts.len(),
+            "should receive all events"
+        );
+        let mut evt = virtio_input_event {
+            type_: Le16::from(0),
+            code: Le16::from(0),
+            value: Le32::from(0),
+        };
+        assert_eq!(
+            source.read(evt.as_mut_slice()).unwrap(),
+            virtio_input_event::EVENT_SIZE,
+            "should read a single event"
+        );
+        assert_events_match(&evt, &evts[0]);
+    }
+
+    #[test]
+    fn exact_read() {
+        const EVENT_COUNT: usize = 4;
+        let evts = instantiate_input_events(EVENT_COUNT);
+        let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+        assert_eq!(
+            source.receive_events(|_| true).unwrap(),
+            evts.len(),
+            "should receive all events"
+        );
+        let mut vio_evts = [0u8; EVENT_COUNT * size_of::<virtio_input_event>()];
+        assert_eq!(
+            source.read(&mut vio_evts).unwrap(),
+            EVENT_COUNT * virtio_input_event::EVENT_SIZE,
+            "should read all events"
+        );
+
+        let mut chunks = vio_evts.chunks_exact(virtio_input_event::EVENT_SIZE);
+        for idx in 0..EVENT_COUNT {
+            let evt = virtio_input_event::from_slice(chunks.next().unwrap()).unwrap();
+            assert_events_match(&evt, &evts[idx]);
+        }
+    }
+
+    #[test]
+    fn over_read() {
+        const EVENT_COUNT: usize = 4;
+        let evts = instantiate_input_events(EVENT_COUNT);
+        let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+        assert_eq!(
+            source.receive_events(|_| true).unwrap(),
+            evts.len(),
+            "should receive all events"
+        );
+        let mut vio_evts = [0u8; 2 * EVENT_COUNT * size_of::<virtio_input_event>()];
+        assert_eq!(
+            source.read(&mut vio_evts).unwrap(),
+            EVENT_COUNT * virtio_input_event::EVENT_SIZE,
+            "should only read available events"
+        );
+
+        let mut chunks = vio_evts.chunks_exact(virtio_input_event::EVENT_SIZE);
+        for idx in 0..EVENT_COUNT {
+            let evt = virtio_input_event::from_slice(chunks.next().unwrap()).unwrap();
+            assert_events_match(&evt, &evts[idx]);
+        }
+    }
+
+    #[test]
+    fn incomplete_read() {
+        const EVENT_COUNT: usize = 4;
+        let evts = instantiate_input_events(EVENT_COUNT);
+        let mut source = EventSourceImpl::new(SourceMock::new(&evts));
+        assert_eq!(
+            source.receive_events(|_| true).unwrap(),
+            evts.len(),
+            "should receive all events"
+        );
+        let mut vio_evts = [0u8; 3];
+        assert_eq!(
+            source.read(&mut vio_evts).unwrap(),
+            0,
+            "shouldn't read incomplete events"
+        );
+    }
+}