From 31212150c1effc2def9fcb9e4c03e361c42eb5b5 Mon Sep 17 00:00:00 2001 From: Dorinda Bassey Date: Tue, 7 Nov 2023 10:02:04 +0100 Subject: [PATCH] sound: Add Test for vhost-device-sound This PR adds test for uncovered code paths Signed-off-by: Dorinda Bassey --- staging/coverage_config_x86_64.json | 2 +- staging/vhost-device-sound/src/device.rs | 54 ++++++++++- staging/vhost-device-sound/src/lib.rs | 109 ++++++++++++++++++++++- staging/vhost-device-sound/src/stream.rs | 67 ++++++++++++++ 4 files changed, 228 insertions(+), 4 deletions(-) diff --git a/staging/coverage_config_x86_64.json b/staging/coverage_config_x86_64.json index 0d9322d..97224b5 100644 --- a/staging/coverage_config_x86_64.json +++ b/staging/coverage_config_x86_64.json @@ -1,5 +1,5 @@ { - "coverage_score": 56.58, + "coverage_score": 60.27, "exclude_path": "", "crate_features": "" } diff --git a/staging/vhost-device-sound/src/device.rs b/staging/vhost-device-sound/src/device.rs index 7b39849..0e3b609 100644 --- a/staging/vhost-device-sound/src/device.rs +++ b/staging/vhost-device-sound/src/device.rs @@ -922,7 +922,59 @@ mod tests { let vring = VringRwLock::new(mem, 0x1000).unwrap(); vring.set_queue_info(0x100, 0x200, 0x300).unwrap(); vring.set_queue_ready(true); - t.process_control(&vring, &audio_backend).unwrap(); + + // Test control msgs with three descriptors + let ctrl_msg_descs = [ + ControlMessageKind::PcmInfo, + ControlMessageKind::ChmapInfo, + ControlMessageKind::JackInfo, + ]; + for code in ctrl_msg_descs { + let req = VirtioSoundHeader { + code: Le32::from(code as u32), + }; + let addr_req = 0x10_0000; + let descs = [ + Descriptor::new(addr_req, 0x100, 0, 0), // request + Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), // response + ]; + + let (vring, mem) = setup_descs(&descs); + mem.memory() + .write_obj(req, GuestAddress(addr_req)) + .expect("writing to succeed"); + t.mem = Some(mem.clone()); + t.process_control(&vring, &audio_backend).unwrap(); + } + + // Test control msgs with two descriptors + let ctrl_descs = [ + ControlMessageKind::JackRemap, + ControlMessageKind::PcmSetParams, + ControlMessageKind::PcmPrepare, + ControlMessageKind::PcmRelease, + ControlMessageKind::PcmStart, + ControlMessageKind::PcmStop, + ]; + for code in ctrl_descs { + let req = VirtioSoundHeader { + code: Le32::from(code as u32), + }; + let addr_req = 0x10_0000; + let descs = [ + Descriptor::new(addr_req, 0x100, 0, 0), // request + Descriptor::new(0x20_0000, 0x100, VRING_DESC_F_WRITE as u16, 0), + ]; + + let (vring, mem) = setup_descs(&descs); + mem.memory() + .write_obj(req, GuestAddress(addr_req)) + .expect("writing to succeed"); + t.mem = Some(mem.clone()); + t.process_control(&vring, &audio_backend).unwrap(); + } + t.process_io(&vring, &audio_backend, Direction::Output) .unwrap(); t.process_io(&vring, &audio_backend, Direction::Input) diff --git a/staging/vhost-device-sound/src/lib.rs b/staging/vhost-device-sound/src/lib.rs index e9fa0a9..84f71dd 100644 --- a/staging/vhost-device-sound/src/lib.rs +++ b/staging/vhost-device-sound/src/lib.rs @@ -212,6 +212,7 @@ impl TryFrom for ControlMessageKind { } } +#[cfg_attr(test, derive(Clone))] pub struct ControlMessage { pub kind: ControlMessageKind, pub code: u32, @@ -382,10 +383,77 @@ pub fn start_backend_server(config: SoundConfig) { mod tests { use std::sync::Arc; - use vm_memory::{GuestMemoryAtomic, GuestMemoryMmap}; + use vhost_user_backend::{VringRwLock, VringT}; + use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; + use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue, QueueOwnedT}; + use vm_memory::{ + Address, ByteValued, GuestAddress, GuestAddressSpace, GuestMemoryAtomic, GuestMemoryMmap, + }; use super::*; - use crate::ControlMessageKind; + use crate::{ControlMessageKind, SoundDescriptorChain}; + + // Prepares a single chain of descriptors + fn prepare_desc_chain( + start_addr: GuestAddress, + hdr: R, + response_len: u32, + ) -> SoundDescriptorChain { + let mem = &GuestMemoryMmap::<()>::from_ranges(&[(start_addr, 0x1000)]).unwrap(); + let vq = MockSplitQueue::new(mem, 16); + let mut next_addr = vq.desc_table().total_size() + 0x100; + let mut index = 0; + + let desc_out = Descriptor::new( + next_addr, + std::mem::size_of::() as u32, + VRING_DESC_F_NEXT as u16, + index + 1, + ); + + mem.write_obj::(hdr, desc_out.addr()).unwrap(); + vq.desc_table().store(index, desc_out).unwrap(); + next_addr += u64::from(desc_out.len()); + index += 1; + + // In response descriptor + let desc_in = Descriptor::new(next_addr, response_len, VRING_DESC_F_WRITE as u16, 0); + vq.desc_table().store(index, desc_in).unwrap(); + + // Put the descriptor index 0 in the first available ring position. + mem.write_obj(0u16, vq.avail_addr().unchecked_add(4)) + .unwrap(); + + // Set `avail_idx` to 1. + mem.write_obj(1u16, vq.avail_addr().unchecked_add(2)) + .unwrap(); + + // Create descriptor chain from pre-filled memory + vq.create_queue::() + .unwrap() + .iter(GuestMemoryAtomic::new(mem.clone()).memory()) + .unwrap() + .next() + .unwrap() + } + + fn ctrl_msg() -> ControlMessage { + let hdr = VirtioSndPcmSetParams::default(); + let memr = GuestMemoryAtomic::new( + GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(), + ); + let vring = VringRwLock::new(memr, 0x1000).unwrap(); + let mem = &GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(); + let vq = MockSplitQueue::new(mem, 16); + let next_addr = vq.desc_table().total_size() + 0x100; + ControlMessage { + kind: ControlMessageKind::JackInfo, + code: 0, + desc_chain: prepare_desc_chain::(GuestAddress(0), hdr, 1), + descriptor: Descriptor::new(next_addr, 0x200, VRING_DESC_F_NEXT as u16, 1), + vring, + } + } #[test] fn test_sound_server() { @@ -439,4 +507,41 @@ mod tests { Err(InvalidControlMessage(invalid_value)) ); } + + #[test] + fn test_try_from_valid_output() { + let val = virtio_sound::VIRTIO_SND_D_OUTPUT; + assert_eq!(Direction::try_from(val).unwrap(), Direction::Output); + + let val = virtio_sound::VIRTIO_SND_D_INPUT; + assert_eq!(Direction::try_from(val).unwrap(), Direction::Input); + + let val = 42; + Direction::try_from(val).unwrap_err(); + } + + #[test] + fn test_display() { + let error = InvalidControlMessage(42); + let formatted_error = format!("{}", error); + assert_eq!(formatted_error, "Invalid control message code 42"); + } + + #[test] + fn test_into_error() { + let error = InvalidControlMessage(42); + let _error: Error = error.into(); + + // Test from stream Error + let stream_error = stream::Error::DescriptorReadFailed; + let _error: Error = stream_error.into(); + } + + #[test] + fn test_debug_format() { + let ctrl_msg = ctrl_msg(); + let debug_output = format!("{:?}", ctrl_msg); + let expected_output = "ControlMessage { kind: JackInfo, code: 0 }".to_string(); + assert_eq!(debug_output, expected_output); + } } diff --git a/staging/vhost-device-sound/src/stream.rs b/staging/vhost-device-sound/src/stream.rs index 89e1255..8dfb4cc 100644 --- a/staging/vhost-device-sound/src/stream.rs +++ b/staging/vhost-device-sound/src/stream.rs @@ -335,6 +335,8 @@ impl Drop for Buffer { #[cfg(test)] mod tests { + use std::fmt::Write; + use vhost_user_backend::{VringRwLock, VringT}; use virtio_bindings::bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE}; use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue, QueueOwnedT}; @@ -408,6 +410,28 @@ mod tests { } } + #[test] + fn test_display_fmt() { + assert_eq!(&PCMState::Stop.to_string(), "VIRTIO_SND_R_PCM_STOP"); + } + + #[test] + fn test_logging() { + let data_descriptor = Descriptor::new(0, 0, 0, 0); + let msg = iomsg(); + let message = Arc::new(msg); + let direction = Direction::Input; + let buffer = Buffer::new(data_descriptor, message, direction); + assert_eq!(format!("{direction:?}"), "Input"); + assert_eq!( + format!("{buffer:?}"), + format!( + "Buffer {{ pos: 0, direction: Input, message: {:?} }}", + &Arc::as_ptr(&buffer.message) + ) + ); + } + #[test] fn test_pcm_state_transitions() { let mut state = PCMState::new(); @@ -546,4 +570,47 @@ mod tests { let mut buf = vec![0; 5]; buffer.read_output(&mut buf).unwrap(); } + + #[test] + fn test_buffer_write_input() { + let msg = iomsg(); + let message = Arc::new(msg); + let desc_msg = iomsg(); + let mut buffer = Buffer::new( + desc_msg.desc_chain.clone().readable().next().unwrap(), + message, + Direction::Input, + ); + + let buf = vec![0; 5]; + buffer.write_input(&buf).unwrap(); + } + + #[test] + fn test_buffer_fn() { + let data_descriptor = Descriptor::new(0, 0, 0, 0); + let msg = iomsg(); + let message = Arc::new(msg); + let direction = Direction::Input; + let buffer = Buffer::new(data_descriptor, message, direction); + + assert_eq!(buffer.desc_len() as usize, buffer.pos); + assert_eq!(buffer.desc_len(), 0); + assert_eq!(buffer.direction, Direction::Input); + + // Test debug format representation for Buffer + let mut debug_output = String::new(); + + // Format the Debug representation into the String. + write!(&mut debug_output, "{:?}", buffer).unwrap(); + + let expected_debug = format!( + "Buffer {{ pos: {}, direction: {:?}, message: {:?} }}", + buffer.pos, + buffer.direction, + Arc::as_ptr(&buffer.message) + ); + + assert_eq!(debug_output, expected_debug); + } }