Skip to content

Commit eaac3ce

Browse files
committed
serial: add tests for internal handling of input
Signed-off-by: Adrian Catangiu <[email protected]>
1 parent 9391a24 commit eaac3ce

File tree

5 files changed

+166
-45
lines changed

5 files changed

+166
-45
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/devices/src/legacy/serial.rs

Lines changed: 103 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -308,85 +308,159 @@ impl EventHandler for Serial {
308308
mod tests {
309309
use super::*;
310310
use std::io;
311+
use std::io::Write;
312+
use std::os::unix::io::RawFd;
311313
use std::sync::{Arc, Mutex};
312314

315+
use polly::event_manager::EventManager;
316+
317+
struct SharedBufferInternal {
318+
read_buf: Vec<u8>,
319+
write_buf: Vec<u8>,
320+
evfd: EventFd,
321+
}
322+
313323
#[derive(Clone)]
314324
struct SharedBuffer {
315-
buf: Arc<Mutex<Vec<u8>>>,
325+
internal: Arc<Mutex<SharedBufferInternal>>,
316326
}
317327

318328
impl SharedBuffer {
319329
fn new() -> SharedBuffer {
320330
SharedBuffer {
321-
buf: Arc::new(Mutex::new(Vec::new())),
331+
internal: Arc::new(Mutex::new(SharedBufferInternal {
332+
read_buf: Vec::new(),
333+
write_buf: Vec::new(),
334+
evfd: EventFd::new(libc::EFD_NONBLOCK).unwrap(),
335+
})),
322336
}
323337
}
324338
}
325-
326339
impl io::Write for SharedBuffer {
327340
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
328-
self.buf.lock().unwrap().write(buf)
341+
self.internal.lock().unwrap().write_buf.write(buf)
329342
}
330343
fn flush(&mut self) -> io::Result<()> {
331-
self.buf.lock().unwrap().flush()
344+
self.internal.lock().unwrap().write_buf.flush()
345+
}
346+
}
347+
impl io::Read for SharedBuffer {
348+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
349+
self.internal.lock().unwrap().read_buf.as_slice().read(buf)
332350
}
333351
}
352+
impl AsRawFd for SharedBuffer {
353+
fn as_raw_fd(&self) -> RawFd {
354+
self.internal.lock().unwrap().evfd.as_raw_fd()
355+
}
356+
}
357+
impl ReadableFd for SharedBuffer {}
358+
359+
static RAW_INPUT_BUF: [u8; 3] = [b'a', b'b', b'c'];
334360

335361
#[test]
336-
fn serial_output() {
362+
fn test_serial_output() {
337363
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
338364
let serial_out = SharedBuffer::new();
339365

340366
let mut serial = Serial::new_out(intr_evt, Box::new(serial_out.clone()));
341367

368+
// Invalid write of multiple chars at once.
342369
serial.write(u64::from(DATA), &[b'x', b'y']);
343-
serial.write(u64::from(DATA), &[b'a']);
344-
serial.write(u64::from(DATA), &[b'b']);
345-
serial.write(u64::from(DATA), &[b'c']);
370+
// Valid one char at a time writes.
371+
RAW_INPUT_BUF
372+
.iter()
373+
.for_each(|&c| serial.write(u64::from(DATA), &[c]));
346374
assert_eq!(
347-
serial_out.buf.lock().unwrap().as_slice(),
348-
&[b'a', b'b', b'c']
375+
serial_out.internal.lock().unwrap().write_buf.as_slice(),
376+
&RAW_INPUT_BUF
349377
);
350378
}
351379

352380
#[test]
353-
fn serial_input() {
381+
fn test_serial_raw_input() {
354382
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
355383
let serial_out = SharedBuffer::new();
356384

357-
let mut serial =
358-
Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out.clone()));
385+
let mut serial = Serial::new_out(intr_evt.try_clone().unwrap(), Box::new(serial_out));
359386

360-
// write 1 to the interrupt event fd, so that read doesn't block in case the event fd
361-
// counter doesn't change (for 0 it blocks)
387+
// Write 1 to the interrupt event fd, so that read doesn't block in case the event fd
388+
// counter doesn't change (for 0 it blocks).
362389
assert!(intr_evt.write(1).is_ok());
363390
serial.write(u64::from(IER), &[IER_RECV_BIT]);
364-
serial.raw_input(&[b'a', b'b', b'c']).unwrap();
391+
serial.raw_input(&RAW_INPUT_BUF).unwrap();
365392

393+
// Verify the serial raised an interrupt.
366394
assert_eq!(intr_evt.read().unwrap(), 2);
367395

368-
// check if reading in a 2-length array doesn't have side effects
396+
// Check if reading in a 2-length array doesn't have side effects.
369397
let mut data = [0u8, 0u8];
370398
serial.read(u64::from(DATA), &mut data[..]);
371399
assert_eq!(data, [0u8, 0u8]);
372400

373401
let mut data = [0u8];
374402
serial.read(u64::from(LSR), &mut data[..]);
375403
assert_ne!(data[0] & LSR_DATA_BIT, 0);
376-
serial.read(u64::from(DATA), &mut data[..]);
377-
assert_eq!(data[0], b'a');
378-
serial.read(u64::from(DATA), &mut data[..]);
379-
assert_eq!(data[0], b'b');
380-
serial.read(u64::from(DATA), &mut data[..]);
381-
assert_eq!(data[0], b'c');
382404

383-
// check if reading from the largest u8 offset returns 0
405+
// Verify reading the previously inputted buffer.
406+
RAW_INPUT_BUF.iter().for_each(|&c| {
407+
serial.read(u64::from(DATA), &mut data[..]);
408+
assert_eq!(data[0], c);
409+
});
410+
411+
// Check if reading from the largest u8 offset returns 0.
384412
serial.read(0xff, &mut data[..]);
385413
assert_eq!(data[0], 0);
386414
}
387415

388416
#[test]
389-
fn serial_thr() {
417+
fn test_serial_input() {
418+
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
419+
let serial_in_out = SharedBuffer::new();
420+
421+
let mut serial = Serial::new_in_out(
422+
intr_evt.try_clone().unwrap(),
423+
Box::new(serial_in_out.clone()),
424+
Box::new(serial_in_out.clone()),
425+
);
426+
427+
// Write 1 to the interrupt event fd, so that read doesn't block in case the event fd
428+
// counter doesn't change (for 0 it blocks).
429+
assert!(intr_evt.write(1).is_ok());
430+
serial.write(u64::from(IER), &[IER_RECV_BIT]);
431+
432+
// Prepare the input buffer.
433+
{
434+
let mut guard = serial_in_out.internal.lock().unwrap();
435+
guard.read_buf.write_all(&RAW_INPUT_BUF).unwrap();
436+
guard.evfd.write(1).unwrap();
437+
}
438+
439+
let mut evmgr = EventManager::new().unwrap();
440+
let serial_wrap = Arc::new(Mutex::new(serial));
441+
evmgr.register(serial_wrap.clone()).unwrap();
442+
443+
// Run the event handler which should drive serial input.
444+
// There should be one event reported (which should have also handled serial input).
445+
assert_eq!(evmgr.run_timeout(50).unwrap(), 1);
446+
447+
// Verify the serial raised an interrupt.
448+
assert_eq!(intr_evt.read().unwrap(), 2);
449+
450+
let mut serial = serial_wrap.lock().unwrap();
451+
let mut data = [0u8];
452+
serial.read(u64::from(LSR), &mut data[..]);
453+
assert_ne!(data[0] & LSR_DATA_BIT, 0);
454+
455+
// Verify reading the previously inputted buffer.
456+
RAW_INPUT_BUF.iter().for_each(|&c| {
457+
serial.read(u64::from(DATA), &mut data[..]);
458+
assert_eq!(data[0], c);
459+
});
460+
}
461+
462+
#[test]
463+
fn test_serial_thr() {
390464
let intr_evt = EventFd::new(libc::EFD_NONBLOCK).unwrap();
391465
let mut serial = Serial::new_sink(intr_evt.try_clone().unwrap());
392466

@@ -405,7 +479,7 @@ mod tests {
405479
}
406480

407481
#[test]
408-
fn serial_dlab() {
482+
fn test_serial_dlab() {
409483
let mut serial = Serial::new_sink(EventFd::new(libc::EFD_NONBLOCK).unwrap());
410484

411485
serial.write(u64::from(LCR), &[LCR_DLAB_BIT as u8]);
@@ -422,7 +496,7 @@ mod tests {
422496
}
423497

424498
#[test]
425-
fn serial_modem() {
499+
fn test_serial_modem() {
426500
let mut serial = Serial::new_sink(EventFd::new(libc::EFD_NONBLOCK).unwrap());
427501

428502
serial.write(u64::from(MCR), &[MCR_LOOP_BIT as u8]);
@@ -444,7 +518,7 @@ mod tests {
444518
}
445519

446520
#[test]
447-
fn serial_scratch() {
521+
fn test_serial_scratch() {
448522
let mut serial = Serial::new_sink(EventFd::new(libc::EFD_NONBLOCK).unwrap());
449523

450524
serial.write(u64::from(SCR), &[0x12 as u8]);

src/vmm/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ cpuid = { path = "../cpuid" }
2929

3030
[dev-dependencies]
3131
tempfile = ">=3.0.2"
32+
vmm-sys-util = ">=0.2.1"

src/vmm/src/builder.rs

Lines changed: 60 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,20 @@ impl Display for StartMicrovmError {
169169
}
170170
}
171171

172+
// Wrapper over io::Stdin that implements `ReadableFd`.
173+
struct SerialStdin(io::Stdin);
174+
impl io::Read for SerialStdin {
175+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
176+
self.0.read(buf)
177+
}
178+
}
179+
impl AsRawFd for SerialStdin {
180+
fn as_raw_fd(&self) -> RawFd {
181+
self.0.as_raw_fd()
182+
}
183+
}
184+
impl devices::legacy::ReadableFd for SerialStdin {}
185+
172186
/// Builds and starts a microVM based on the current Firecracker VmResources configuration.
173187
///
174188
/// This is the default build recipe, one could build other microVM flavors by using the
@@ -201,23 +215,9 @@ pub fn build_microvm(
201215
let serial_device = if cfg!(target_arch = "x86_64")
202216
|| (cfg!(target_arch = "aarch64") && kernel_cmdline.as_str().contains("console="))
203217
{
204-
// Wrapper over io::Stdin that implements `ReadableFd`.
205-
struct SerialStdin(io::Stdin);
206-
impl io::Read for SerialStdin {
207-
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
208-
self.0.read(buf)
209-
}
210-
}
211-
impl AsRawFd for SerialStdin {
212-
fn as_raw_fd(&self) -> RawFd {
213-
self.0.as_raw_fd()
214-
}
215-
}
216-
impl devices::legacy::ReadableFd for SerialStdin {};
217-
let stdin_handle = SerialStdin(io::stdin());
218218
Some(setup_serial_device(
219219
event_manager,
220-
Box::new(stdin_handle),
220+
Box::new(SerialStdin(io::stdin())),
221221
Box::new(io::stdout()),
222222
)?)
223223
} else {
@@ -720,3 +720,48 @@ fn attach_vsock_device(
720720

721721
Ok(())
722722
}
723+
724+
#[cfg(test)]
725+
pub mod tests {
726+
extern crate vmm_sys_util;
727+
use self::vmm_sys_util::tempfile::TempFile;
728+
729+
use std::fs::File;
730+
731+
use super::*;
732+
use polly::event_manager::EventManager;
733+
734+
#[test]
735+
fn test_stdin_wrapper() {
736+
let wrapper = SerialStdin(io::stdin());
737+
assert_eq!(wrapper.as_raw_fd(), io::stdin().as_raw_fd())
738+
}
739+
740+
#[test]
741+
fn test_setup_serial_device() {
742+
// Wrapper over TempFile that implements `ReadableFd`.
743+
struct SerialInput(File);
744+
impl io::Read for SerialInput {
745+
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
746+
self.0.read(buf)
747+
}
748+
}
749+
impl AsRawFd for SerialInput {
750+
fn as_raw_fd(&self) -> RawFd {
751+
self.0.as_raw_fd()
752+
}
753+
}
754+
impl devices::legacy::ReadableFd for SerialInput {};
755+
756+
let read_tempfile = TempFile::new("/tmp/serial_read_").unwrap();
757+
let read_file = File::open(read_tempfile.as_path()).unwrap();
758+
let read_handle = SerialInput(read_file);
759+
let mut event_manager = EventManager::new().expect("Unable to create EventManager");
760+
setup_serial_device(
761+
&mut event_manager,
762+
Box::new(read_handle),
763+
Box::new(io::stdout()),
764+
)
765+
.unwrap();
766+
}
767+
}

tests/integration_tests/build/test_coverage.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919

2020
import host_tools.cargo_build as host # pylint: disable=import-error
2121

22-
COVERAGE_TARGET_PCT = 74.3
22+
COVERAGE_TARGET_PCT = 74.7
2323
COVERAGE_MAX_DELTA = 0.01
2424

2525
CARGO_KCOV_REL_PATH = os.path.join(host.CARGO_BUILD_REL_PATH, 'kcov')

0 commit comments

Comments
 (0)