vsock: increase test coverage

Add more tests in vhost-device-vsock to increase the coverage.

Some of them are really simple and perhaps nonsensical (tests for
Debug, Clone, etc.), but for now we don't know how to disable this
in the tool, so let's cover these cases.

The vhost-device-vsock functions coverage increases from 70.73% to
90.45%:
    Filename             Functions  Missed Functions  Executed
    ----------------------------------------------------------
    main.rs                     51                12    76.47%
    rxops.rs                     8                 0   100.00%
    rxqueue.rs                  20                 0   100.00%
    thread_backend.rs           20                 3    85.00%
    txbuf.rs                    17                 0   100.00%
    vhu_vsock.rs                37                 1    97.30%
    vhu_vsock_thread.rs         40                 5    87.50%
    vsock_conn.rs               27                 0   100.00%
    ----------------------------------------------------------
    TOTAL                      220                21    90.45%

Closes #229

Signed-off-by: Stefano Garzarella <sgarzare@redhat.com>
This commit is contained in:
Stefano Garzarella 2023-11-06 15:53:51 +01:00 committed by Manos Pitsidianakis
parent 197dac1c81
commit 5847489855
11 changed files with 194 additions and 6 deletions

1
Cargo.lock generated
View file

@ -963,6 +963,7 @@ dependencies = [
name = "vhost-device-vsock"
version = "0.1.0"
dependencies = [
"assert_matches",
"byteorder",
"clap",
"config",

View file

@ -1,5 +1,5 @@
{
"coverage_score": 73.42,
"coverage_score": 77.28,
"exclude_path": "",
"crate_features": ""
}

View file

@ -31,5 +31,6 @@ serde = { version = "1", features = ["derive"] }
serde_yaml = "0.9"
[dev-dependencies]
assert_matches = "1.5"
virtio-queue = { version = "0.9", features = ["test-utils"] }
tempfile = "3.6.0"

View file

@ -320,6 +320,7 @@ fn main() {
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
use std::fs::File;
use std::io::Write;
use tempfile::tempdir;
@ -554,12 +555,19 @@ mod tests {
)
.unwrap();
let vring_workers = daemon.get_epoll_handlers();
let mut epoll_handlers = daemon.get_epoll_handlers();
// VhostUserVsockBackend support a single thread that handles the TX and RX queues
assert_eq!(backend.threads.len(), 1);
assert_eq!(vring_workers.len(), backend.threads.len());
assert_eq!(epoll_handlers.len(), backend.threads.len());
for thread in backend.threads.iter() {
thread
.lock()
.unwrap()
.register_listeners(epoll_handlers.remove(0));
}
test_dir.close().unwrap();
}
@ -603,8 +611,48 @@ mod tests {
),
];
start_backend_servers(&configs).unwrap_err();
let error = start_backend_servers(&configs).unwrap_err();
assert_matches!(
error,
BackendError::CouldNotCreateBackend(vhu_vsock::Error::CidAlreadyInUse)
);
assert_eq!(
format!("{error:?}"),
"CouldNotCreateBackend(CidAlreadyInUse)"
);
test_dir.close().unwrap();
}
#[test]
fn test_main_structs() {
let error = parse_vm_params("").unwrap_err();
assert_matches!(error, VmArgsParseError::BadArgument);
assert_eq!(format!("{error:?}"), "BadArgument");
let args = VsockArgs {
param: None,
vm: None,
config: None,
};
let error = Vec::<VsockConfig>::try_from(args).unwrap_err();
assert_matches!(error, CliError::NoArgsProvided);
assert_eq!(format!("{error:?}"), "NoArgsProvided");
let args = VsockArgs::from_args(0, "", "", 0, "");
assert_eq!(format!("{args:?}"), "VsockArgs { param: Some(VsockParam { guest_cid: 0, socket: \"\", uds_path: \"\", tx_buffer_size: 0, groups: \"\" }), vm: None, config: None }");
let param = args.param.unwrap().clone();
assert_eq!(format!("{param:?}"), "VsockParam { guest_cid: 0, socket: \"\", uds_path: \"\", tx_buffer_size: 0, groups: \"\" }");
let config = ConfigFileVsockParam {
guest_cid: None,
socket: String::new(),
uds_path: String::new(),
tx_buffer_size: None,
groups: None,
}
.clone();
assert_eq!(format!("{config:?}"), "ConfigFileVsockParam { guest_cid: None, socket: \"\", uds_path: \"\", tx_buffer_size: None, groups: None }");
}
}

View file

@ -25,6 +25,14 @@ impl RxOps {
mod tests {
use super::*;
#[test]
fn test_rxops() {
let rx = RxOps::Request;
// Increase coverage testing Clone and Debug traits
assert_eq!(rx, rx.clone());
assert_eq!(format!("{rx:?}"), "Request");
}
#[test]
fn test_bitmask() {
assert_eq!(1, RxOps::Request.bitmask());

View file

@ -154,4 +154,11 @@ mod tests {
rxqueue.queue = 1;
assert!(rxqueue.pending_rx());
}
#[test]
fn test_debug() {
let rxqueue = RxQueue::new();
assert_eq!(format!("{rxqueue:?}"), "RxQueue { queue: 0 }");
}
}

View file

@ -419,6 +419,9 @@ mod tests {
assert!(vtp.recv_pkt(&mut packet).is_ok());
// TODO: it is a nop for now
vtp.enq_rst();
// cleanup
let _ = std::fs::remove_file(&vsock_peer_path);
let _ = std::fs::remove_file(&vsock_socket_path);
@ -430,6 +433,7 @@ mod tests {
fn test_vsock_thread_backend_sibling_vms() {
const CID: u64 = 3;
const SIBLING_CID: u64 = 4;
const SIBLING2_CID: u64 = 5;
const SIBLING_LISTENING_PORT: u32 = 1234;
let test_dir = tempdir().expect("Could not create a temp test directory.");
@ -449,6 +453,16 @@ mod tests {
.join("test_vsock_thread_backend_sibling.vsock")
.display()
.to_string();
let sibling2_vhost_socket_path = test_dir
.path()
.join("test_vsock_thread_backend_sibling2.socket")
.display()
.to_string();
let sibling2_vsock_socket_path = test_dir
.path()
.join("test_vsock_thread_backend_sibling2.vsock")
.display()
.to_string();
let cid_map: Arc<RwLock<CidMap>> = Arc::new(RwLock::new(HashMap::new()));
@ -463,8 +477,18 @@ mod tests {
.collect(),
);
let sibling2_config = VsockConfig::new(
SIBLING2_CID,
sibling2_vhost_socket_path,
sibling2_vsock_socket_path,
CONN_TX_BUF_SIZE,
vec!["group1"].into_iter().map(String::from).collect(),
);
let sibling_backend =
Arc::new(VhostUserVsockBackend::new(sibling_config, cid_map.clone()).unwrap());
let sibling2_backend =
Arc::new(VhostUserVsockBackend::new(sibling2_config, cid_map.clone()).unwrap());
let epoll_fd = epoll::create(false).unwrap();
@ -513,6 +537,15 @@ mod tests {
.thread_backend
.pending_raw_pkts());
packet.set_dst_cid(SIBLING2_CID);
assert!(vtp.send_pkt(&packet).is_ok());
// packet should be discarded since sibling2 is not in the same group
assert!(!sibling2_backend.threads[0]
.lock()
.unwrap()
.thread_backend
.pending_raw_pkts());
let mut recvd_pkt_raw = [0u8; PKT_HEADER_SIZE + DATA_LEN];
let (recvd_hdr_raw, recvd_data_raw) = recvd_pkt_raw.split_at_mut(PKT_HEADER_SIZE);

View file

@ -230,4 +230,14 @@ mod tests {
assert_eq!(cmp_vec, data[..n]);
}
}
#[test]
fn test_txbuf_debug() {
let loc_tx_buf = LocalTxBuf::new(1);
assert_eq!(
format!("{loc_tx_buf:?}"),
"LocalTxBuf { buf: [0], head: 0, tail: 0 }"
);
}
}

View file

@ -540,4 +540,28 @@ mod tests {
test_dir.close().unwrap();
}
#[test]
fn test_vhu_vsock_structs() {
let config = VsockConfig::new(0, String::new(), String::new(), 0, vec![String::new()]);
assert_eq!(format!("{config:?}"), "VsockConfig { guest_cid: 0, socket: \"\", uds_path: \"\", tx_buffer_size: 0, groups: [\"\"] }");
let conn_map = ConnMapKey::new(0, 0);
assert_eq!(
format!("{conn_map:?}"),
"ConnMapKey { local_port: 0, peer_port: 0 }"
);
assert_eq!(conn_map, conn_map.clone());
let virtio_config = VirtioVsockConfig::default();
assert_eq!(
format!("{virtio_config:?}"),
"VirtioVsockConfig { guest_cid: Le64(0) }"
);
assert_eq!(virtio_config, virtio_config.clone());
let error = Error::HandleEventNotEpollIn;
assert_eq!(format!("{error:?}"), "HandleEventNotEpollIn");
}
}

View file

@ -730,6 +730,7 @@ impl Drop for VhostUserVsockThread {
mod tests {
use super::*;
use std::collections::HashMap;
use std::io::Write;
use tempfile::tempdir;
use vm_memory::GuestAddress;
use vmm_sys_util::eventfd::EventFd;
@ -806,6 +807,21 @@ mod tests {
.push_back(ConnMapKey::new(0, 0));
assert!(t.process_rx(&vring, false).is_ok());
assert!(t.process_rx(&vring, true).is_ok());
assert!(t.process_raw_pkts(&vring, false).is_ok());
assert!(t.process_raw_pkts(&vring, true).is_ok());
VhostUserVsockThread::vring_handle_event(EventData {
vring: vring.clone(),
event_idx: false,
head_idx: 0,
used_len: 0,
});
VhostUserVsockThread::vring_handle_event(EventData {
vring,
event_idx: true,
head_idx: 0,
used_len: 0,
});
dummy_fd.write(1).unwrap();
@ -875,6 +891,46 @@ mod tests {
VhostUserVsockThread::new(vsock_socket_path2, 3, CONN_TX_BUF_SIZE, groups, cid_map);
assert!(t2.is_err());
test_dir.close().unwrap();
}
#[test]
fn test_vsock_thread_unix() {
let groups: Vec<String> = vec![String::from("default")];
let cid_map: Arc<RwLock<CidMap>> = Arc::new(RwLock::new(HashMap::new()));
let test_dir = tempdir().expect("Could not create a temp test directory.");
let vsock_path = test_dir
.path()
.join("test_vsock_thread.vsock")
.display()
.to_string();
let t = VhostUserVsockThread::new(vsock_path.clone(), 3, CONN_TX_BUF_SIZE, groups, cid_map);
let mut t = t.unwrap();
let mem = GuestMemoryAtomic::new(
GuestMemoryMmap::<()>::from_ranges(&[(GuestAddress(0), 0x10000)]).unwrap(),
);
t.mem = Some(mem.clone());
let mut uds = UnixStream::connect(vsock_path).unwrap();
t.process_backend_evt(EventSet::empty());
uds.write_all(b"CONNECT 1234\n").unwrap();
t.process_backend_evt(EventSet::empty());
// Write and read something from the Unix socket
uds.write_all(b"some data").unwrap();
let mut buf = vec![0u8; 16];
uds.set_nonblocking(true).unwrap();
// There isn't any peer responding, so we don't expect data
uds.read(&mut buf).unwrap_err();
t.process_backend_evt(EventSet::empty());
test_dir.close().unwrap();
}
}

View file

@ -22,7 +22,6 @@ use crate::{
vhu_vsock_thread::VhostUserVsockThread,
};
#[derive(Debug)]
pub(crate) struct VsockConnection<S> {
/// Host-side stream corresponding to this vsock connection.
pub stream: S,
@ -519,7 +518,8 @@ mod tests {
#[test]
fn test_vsock_conn_init() {
// new locally inititated connection
let dummy_file = VsockDummySocket::new();
let mut dummy_file = VsockDummySocket::new();
assert!(dummy_file.flush().is_ok());
let mut conn_local = VsockConnection::new_local_init(
dummy_file,
VSOCK_HOST_CID,