sound: Add Test for vhost-device-sound

This PR adds test for uncovered code paths

Signed-off-by: Dorinda Bassey <dbassey@redhat.com>
This commit is contained in:
Dorinda Bassey 2023-11-07 10:02:04 +01:00 committed by Manos Pitsidianakis
parent 8e1e0a7a2a
commit 31212150c1
4 changed files with 228 additions and 4 deletions

View File

@ -1,5 +1,5 @@
{
"coverage_score": 56.58,
"coverage_score": 60.27,
"exclude_path": "",
"crate_features": ""
}

View File

@ -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)

View File

@ -212,6 +212,7 @@ impl TryFrom<Le32> 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<R: ByteValued>(
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::<R>() as u32,
VRING_DESC_F_NEXT as u16,
index + 1,
);
mem.write_obj::<R>(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::<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::<VirtioSndPcmSetParams>(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);
}
}

View File

@ -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);
}
}