vhost-device-console: improve tests

Add tests for the new input events and increase overall test coverage.

Signed-off-by: Timos Ampelikiotis <t.ampelikiotis@virtualopensystems.com>
This commit is contained in:
Timos Ampelikiotis 2024-10-10 11:46:52 +03:00 committed by Alex Bennée
parent c7397703d4
commit 8e18b8b92b
3 changed files with 111 additions and 91 deletions

View File

@ -1,5 +1,5 @@
{
"coverage_score": 80.31,
"coverage_score": 86.38,
"exclude_path": "",
"crate_features": ""
}

View File

@ -80,7 +80,7 @@ impl VuConsoleConfig {
}
}
// This is the public API through which an external program starts the
/// This is the public API through which an external program starts the
/// vhost-device-console backend server.
pub(crate) fn start_backend_server(
socket: PathBuf,
@ -240,71 +240,43 @@ mod tests {
assert!(VuConsoleConfig::try_from(args).is_ok());
}
fn test_backend_start_and_stop(args: ConsoleArgs) {
fn test_backend_start_and_stop(args: ConsoleArgs) -> Result<()> {
let config = VuConsoleConfig::try_from(args).expect("Wrong config");
let tcp_addrs = config.generate_tcp_addrs();
let backend = config.backend;
for (_socket, tcp_addr) in config
for (socket, tcp_addr) in config
.generate_socket_paths()
.into_iter()
.zip(tcp_addrs.iter())
{
let controller = ConsoleController::new(backend);
let arc_controller = Arc::new(RwLock::new(controller));
let vu_console_backend = Arc::new(RwLock::new(
VhostUserConsoleBackend::new(arc_controller)
.map_err(Error::CouldNotCreateBackend)
.expect("Fail create vhuconsole backend"),
));
let mut _daemon = VhostUserDaemon::new(
String::from("vhost-device-console-backend"),
vu_console_backend.clone(),
GuestMemoryAtomic::new(GuestMemoryMmap::new()),
)
.map_err(Error::CouldNotCreateDaemon)
.expect("Failed create daemon");
// Start the corresponinding console thread
let read_handle = if backend == BackendType::Nested {
VhostUserConsoleBackend::start_console_thread(&vu_console_backend)
} else {
VhostUserConsoleBackend::start_tcp_console_thread(
&vu_console_backend,
tcp_addr.clone(),
)
};
// Kill console input thread
vu_console_backend.read().unwrap().kill_console_thread();
// Wait for read thread to exit
assert_matches!(read_handle.join(), Ok(_));
start_backend_server(socket, tcp_addr.to_string(), backend)?;
}
Ok(())
}
#[test]
fn test_start_net_backend_success() {
fn test_start_backend_server_success() {
let args = ConsoleArgs {
socket_path: String::from("/tmp/vhost.sock").into(),
socket_path: String::from("/not_a_dir/vhost.sock").into(),
backend: BackendType::Network,
tcp_port: String::from("12345"),
socket_count: 1,
};
test_backend_start_and_stop(args);
assert!(test_backend_start_and_stop(args).is_err());
}
#[test]
fn test_start_nested_backend_success() {
let args = ConsoleArgs {
socket_path: String::from("/tmp/vhost.sock").into(),
backend: BackendType::Nested,
tcp_port: String::from("12345"),
fn test_start_backend_success() {
let config = VuConsoleConfig {
socket_path: String::from("/not_a_dir/vhost.sock").into(),
backend: BackendType::Network,
tcp_port: String::from("12346"),
socket_count: 1,
};
test_backend_start_and_stop(args);
assert!(start_backend(config).is_err());
}
}

View File

@ -817,6 +817,7 @@ impl VhostUserBackendMut for VhostUserConsoleBackend {
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
use virtio_bindings::virtio_ring::{VRING_DESC_F_NEXT, VRING_DESC_F_WRITE};
use virtio_queue::{mock::MockSplitQueue, Descriptor, Queue};
use vm_memory::{Bytes, GuestAddress, GuestMemoryAtomic, GuestMemoryMmap};
@ -839,15 +840,12 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
);
// Update memory
vu_console_backend.update_memory(mem.clone()).unwrap();
// Artificial Vring
let vring = VringRwLock::new(mem, 0x1000).unwrap();
vring.set_queue_info(0x100, 0x200, 0x300).unwrap();
vring.set_queue_ready(true);
@ -884,12 +882,10 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap(),
);
// Artificial Vring
let vring = VringRwLock::new(mem.clone(), 0x1000).unwrap();
// Empty descriptor chain should be ignored
@ -945,7 +941,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
// Test 1: Empty queue
@ -1020,7 +1015,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
// Test 1: Empty queue
@ -1071,7 +1065,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory & update device's memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
let mem_1 = GuestMemoryAtomic::new(mem.clone());
vu_console_backend.update_memory(mem_1.clone()).unwrap();
@ -1125,7 +1118,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
// Test 1: Empty queue
@ -1181,7 +1173,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
let desc_chain = build_desc_chain(&mem, 1, vec![0], 0x200);
let mem1 = GuestMemoryAtomic::new(mem.clone());
@ -1223,7 +1214,6 @@ mod tests {
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
// Artificial memory
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
// Test 1: Empty queue
@ -1238,7 +1228,8 @@ mod tests {
let mem1 = GuestMemoryAtomic::new(mem.clone());
let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
vu_console_backend.update_memory(mem1).unwrap();
assert!(!vu_console_backend
assert!(vu_console_backend
.process_rx_requests(vec![desc_chain], &vring)
.unwrap());
@ -1248,14 +1239,16 @@ mod tests {
let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
vu_console_backend.update_memory(mem1).unwrap();
let input_buffer = "Hello!".to_string();
let _ = vu_console_backend.rx_data_fifo.add(input_buffer.clone());
assert_eq!(
vu_console_backend
.process_rx_requests(vec![desc_chain], &vring)
.unwrap_err(),
Error::DescriptorWriteFailed
);
let input_buffer = b"Hello!";
// Add each byte individually to the rx_data_fifo
for &byte in input_buffer.clone().iter() {
let _ = vu_console_backend.rx_data_fifo.add(byte);
}
// available_data are 0 so min_limit is 0 too
assert!(vu_console_backend
.process_rx_requests(vec![desc_chain], &vring)
.unwrap());
// Test 4: Fill message to the buffer. Everything should work!
let desc_chain = build_desc_chain(&mem, 1, vec![VRING_DESC_F_WRITE as u16], 0x200);
@ -1263,8 +1256,10 @@ mod tests {
let vring = VringRwLock::new(mem1.clone(), 0x1000).unwrap();
vu_console_backend.update_memory(mem1).unwrap();
let input_buffer = "Hello!".to_string();
let _ = vu_console_backend.rx_data_fifo.add(input_buffer.clone());
let input_buffer = b"Hello!";
for &byte in input_buffer.clone().iter() {
let _ = vu_console_backend.rx_data_fifo.add(byte);
}
assert!(vu_console_backend
.process_rx_requests(vec![desc_chain.clone()], &vring)
.unwrap());
@ -1283,37 +1278,90 @@ mod tests {
.copied()
.collect();
assert_eq!(String::from_utf8(read_buffer).unwrap(), input_buffer);
}
#[test]
fn test_virtio_console_start_tcp_console_thread() {
let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
let vu_console_backend = Arc::new(RwLock::new(
VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend"),
));
let tcp_addr = "127.0.0.1:12345".to_string();
let read_handle = VhostUserConsoleBackend::start_tcp_console_thread(
&vu_console_backend,
tcp_addr.clone(),
);
vu_console_backend.read().unwrap().kill_console_thread();
assert!(read_handle.join().is_ok());
assert_eq!(read_buffer, input_buffer);
}
#[test]
fn test_virtio_console_start_nested_console_thread() {
let console_controller = Arc::new(RwLock::new(ConsoleController::new(BackendType::Nested)));
let vu_console_backend = Arc::new(RwLock::new(
VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend"),
));
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
let read_handle = VhostUserConsoleBackend::start_console_thread(&vu_console_backend);
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
let mem = GuestMemoryAtomic::new(mem);
vu_console_backend.update_memory(mem.clone()).unwrap();
let vring = VringRwLock::new(mem, 0x1000).unwrap();
vu_console_backend.read().unwrap().kill_console_thread();
assert!(read_handle.join().is_ok());
let input_data = b"H";
let cursor = Cursor::new(input_data.clone().to_vec());
// Replace stdin with a cursor for testing
vu_console_backend.stdin = Some(Box::new(cursor));
vu_console_backend.ready_to_write = true;
assert!(vu_console_backend
.handle_event(KEY_EFD, EventSet::IN, &[vring], 0)
.is_ok());
let received_byte = vu_console_backend.rx_data_fifo.peek();
// verify that the character has been received and is the one we sent
assert!(received_byte.clone().is_ok());
assert_eq!(received_byte.unwrap(), input_data[0]);
}
#[test]
fn test_virtio_console_tcp_console_read_func() {
let console_controller =
Arc::new(RwLock::new(ConsoleController::new(BackendType::Network)));
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
let mem = GuestMemoryAtomic::new(mem);
vu_console_backend.update_memory(mem.clone()).unwrap();
let vring = VringRwLock::new(mem, 0x1000).unwrap();
let input_data = b"H";
let cursor = Cursor::new(input_data.clone().to_vec());
// Replace stream with a cursor for testing
vu_console_backend.stream = Some(Box::new(cursor));
vu_console_backend.ready_to_write = true;
assert!(vu_console_backend
.handle_event(KEY_EFD, EventSet::IN, &[vring], 0)
.is_ok());
let received_byte = vu_console_backend.rx_data_fifo.peek();
// verify that the character has been received and is the one we sent
assert!(received_byte.clone().is_ok());
assert_eq!(received_byte.unwrap(), input_data[0]);
}
#[test]
fn test_virtio_console_tcp_console_write_func() {
let console_controller =
Arc::new(RwLock::new(ConsoleController::new(BackendType::Network)));
let mut vu_console_backend = VhostUserConsoleBackend::new(console_controller)
.expect("Failed create vhuconsole backend");
let mem = GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x1000)]).unwrap();
let mem = GuestMemoryAtomic::new(mem);
vu_console_backend.update_memory(mem.clone()).unwrap();
// Test 1: Call the actual read function
let cursor = Cursor::new(Vec::new());
vu_console_backend.stream = Some(Box::new(cursor));
vu_console_backend
.output_queue
.add("Test".to_string())
.unwrap();
vu_console_backend.write_tcp_stream();
// All data has been consumed by the cursor
assert_eq!(vu_console_backend.output_queue.size(), 0);
}
}