vhost_user: add more unit test cases
Add more unit test cases for vhost-user protocol. Signed-off-by: Liu Jiang <gerry@linux.alibaba.com>
This commit is contained in:
parent
56b823482b
commit
ec6eae722e
9 changed files with 417 additions and 81 deletions
|
|
@ -1 +1 @@
|
|||
{"coverage_score": 73.3, "exclude_path": "src/vhost_kern/", "crate_features": ""}
|
||||
{"coverage_score": 78.9, "exclude_path": "src/vhost_kern/", "crate_features": "vhost-user-master,vhost-user-slave"}
|
||||
|
|
|
|||
12
src/lib.rs
12
src/lib.rs
|
|
@ -74,7 +74,7 @@ pub enum Error {
|
|||
IoctlError(std::io::Error),
|
||||
/// Error from IO subsystem.
|
||||
IOError(std::io::Error),
|
||||
#[cfg(feature = "vhost-user-master")]
|
||||
#[cfg(feature = "vhost-user")]
|
||||
/// Error from the vhost-user subsystem.
|
||||
VhostUserProtocol(vhost_user::Error),
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ impl std::fmt::Display for Error {
|
|||
Error::VhostOpen(e) => write!(f, "failure in opening vhost file: {}", e),
|
||||
#[cfg(feature = "vhost-kern")]
|
||||
Error::IoctlError(e) => write!(f, "failure in vhost ioctl: {}", e),
|
||||
#[cfg(feature = "vhost-user-master")]
|
||||
#[cfg(feature = "vhost-user")]
|
||||
Error::VhostUserProtocol(e) => write!(f, "vhost-user: {}", e),
|
||||
}
|
||||
}
|
||||
|
|
@ -151,4 +151,12 @@ mod tests {
|
|||
|
||||
assert_eq!(format!("{:?}", Error::AvailAddress), "AvailAddress");
|
||||
}
|
||||
|
||||
#[cfg(feature = "vhost-user")]
|
||||
#[test]
|
||||
fn test_convert_from_vhost_user_error() {
|
||||
let e: Error = vhost_user::Error::OversizedMsg.into();
|
||||
|
||||
assert_eq!(format!("{}", e), "vhost-user: oversized message");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -606,29 +606,32 @@ fn get_sub_iovs_offset(iov_lens: &[usize], skip_size: usize) -> (usize, usize) {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
use super::*;
|
||||
use std::fs::File;
|
||||
use std::io::{Read, Seek, SeekFrom, Write};
|
||||
use std::os::unix::io::FromRawFd;
|
||||
use vmm_sys_util::rand::rand_alphanumerics;
|
||||
use vmm_sys_util::tempfile::TempFile;
|
||||
|
||||
const UNIX_SOCKET_LISTENER: &'static str = "/tmp/vhost_user_test_rust_listener";
|
||||
const UNIX_SOCKET_CONNECTION: &'static str = "/tmp/vhost_user_test_rust_connection";
|
||||
const UNIX_SOCKET_DATA: &'static str = "/tmp/vhost_user_test_rust_data";
|
||||
const UNIX_SOCKET_FD: &'static str = "/tmp/vhost_user_test_rust_fd";
|
||||
const UNIX_SOCKET_SEND: &'static str = "/tmp/vhost_user_test_rust_send";
|
||||
fn temp_path() -> String {
|
||||
format!(
|
||||
"/tmp/vhost_test_{}",
|
||||
rand_alphanumerics(8).to_str().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn create_listener() {
|
||||
let listener = Listener::new(UNIX_SOCKET_LISTENER, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
|
||||
assert!(listener.as_raw_fd() > 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn accept_connection() {
|
||||
let listener = Listener::new(UNIX_SOCKET_CONNECTION, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
|
||||
// accept on a fd without incoming connection
|
||||
|
|
@ -638,9 +641,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn send_data() {
|
||||
let listener = Listener::new(UNIX_SOCKET_DATA, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_DATA).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
|
||||
let sock = listener.accept().unwrap().unwrap();
|
||||
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
|
||||
|
||||
|
|
@ -663,9 +667,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn send_fd() {
|
||||
let listener = Listener::new(UNIX_SOCKET_FD, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_FD).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
|
||||
let sock = listener.accept().unwrap().unwrap();
|
||||
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
|
||||
|
||||
|
|
@ -816,9 +821,10 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn send_recv() {
|
||||
let listener = Listener::new(UNIX_SOCKET_SEND, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(UNIX_SOCKET_SEND).unwrap();
|
||||
let mut master = Endpoint::<MasterReq>::connect(&path).unwrap();
|
||||
let sock = listener.accept().unwrap().unwrap();
|
||||
let mut slave = Endpoint::<MasterReq>::from_stream(sock);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// Copyright (C) 2019 Alibaba Cloud Computing. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
use super::message::*;
|
||||
use super::*;
|
||||
use std::os::unix::io::RawFd;
|
||||
|
||||
pub const MAX_QUEUE_NUM: usize = 2;
|
||||
pub const MAX_VRING_NUM: usize = 256;
|
||||
|
|
@ -34,7 +35,7 @@ impl DummySlaveReqHandler {
|
|||
}
|
||||
}
|
||||
|
||||
impl VhostUserSlaveReqHandler for DummySlaveReqHandler {
|
||||
impl VhostUserSlaveReqHandlerMut for DummySlaveReqHandler {
|
||||
fn set_owner(&mut self) -> Result<()> {
|
||||
if self.owned {
|
||||
return Err(Error::InvalidOperation);
|
||||
|
|
@ -83,30 +84,10 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
|
||||
Ok(VhostUserProtocolFeatures::all())
|
||||
}
|
||||
|
||||
fn set_protocol_features(&mut self, features: u64) -> Result<()> {
|
||||
// Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must
|
||||
// support this message even before VHOST_USER_SET_FEATURES was
|
||||
// called.
|
||||
// What happens if the master calls set_features() with
|
||||
// VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this
|
||||
// interface?
|
||||
self.acked_protocol_features = features;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_mem_table(&mut self, _ctx: &[VhostUserMemoryRegion], _fds: &[RawFd]) -> Result<()> {
|
||||
// TODO
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_queue_num(&mut self) -> Result<u64> {
|
||||
Ok(MAX_QUEUE_NUM as u64)
|
||||
}
|
||||
|
||||
fn set_vring_num(&mut self, index: u32, num: u32) -> Result<()> {
|
||||
if index as usize >= self.queue_num || num == 0 || num as usize > MAX_VRING_NUM {
|
||||
return Err(Error::InvalidParam);
|
||||
|
|
@ -199,6 +180,25 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn get_protocol_features(&mut self) -> Result<VhostUserProtocolFeatures> {
|
||||
Ok(VhostUserProtocolFeatures::all())
|
||||
}
|
||||
|
||||
fn set_protocol_features(&mut self, features: u64) -> Result<()> {
|
||||
// Note: slave that reported VHOST_USER_F_PROTOCOL_FEATURES must
|
||||
// support this message even before VHOST_USER_SET_FEATURES was
|
||||
// called.
|
||||
// What happens if the master calls set_features() with
|
||||
// VHOST_USER_F_PROTOCOL_FEATURES cleared after calling this
|
||||
// interface?
|
||||
self.acked_protocol_features = features;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_queue_num(&mut self) -> Result<u64> {
|
||||
Ok(MAX_QUEUE_NUM as u64)
|
||||
}
|
||||
|
||||
fn set_vring_enable(&mut self, index: u32, enable: bool) -> Result<()> {
|
||||
// This request should be handled only when VHOST_USER_F_PROTOCOL_FEATURES
|
||||
// has been negotiated.
|
||||
|
|
@ -222,7 +222,7 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler {
|
|||
size: u32,
|
||||
_flags: VhostUserConfigFlags,
|
||||
) -> Result<Vec<u8>> {
|
||||
if self.acked_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
|
||||
if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
|
||||
return Err(Error::InvalidOperation);
|
||||
} else if offset < VHOST_USER_CONFIG_OFFSET
|
||||
|| offset >= VHOST_USER_CONFIG_SIZE
|
||||
|
|
@ -236,7 +236,7 @@ impl VhostUserSlaveReqHandler for DummySlaveReqHandler {
|
|||
|
||||
fn set_config(&mut self, offset: u32, buf: &[u8], _flags: VhostUserConfigFlags) -> Result<()> {
|
||||
let size = buf.len() as u32;
|
||||
if self.acked_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
|
||||
if self.acked_protocol_features & VhostUserProtocolFeatures::CONFIG.bits() == 0 {
|
||||
return Err(Error::InvalidOperation);
|
||||
} else if offset < VHOST_USER_CONFIG_OFFSET
|
||||
|| offset >= VHOST_USER_CONFIG_SIZE
|
||||
|
|
|
|||
|
|
@ -393,7 +393,10 @@ impl VhostUserMaster for Master {
|
|||
return error_code(VhostUserError::SlaveInternalError);
|
||||
} else if body_reply.size != body.size || body_reply.size as usize != buf.len() {
|
||||
return error_code(VhostUserError::InvalidMessage);
|
||||
} else if body_reply.offset != body.offset {
|
||||
return error_code(VhostUserError::InvalidMessage);
|
||||
}
|
||||
|
||||
Ok((body_reply, buf_reply))
|
||||
}
|
||||
|
||||
|
|
@ -571,6 +574,7 @@ impl MasterInternal {
|
|||
) -> VhostUserResult<(T, Vec<u8>, Option<Vec<RawFd>>)> {
|
||||
if mem::size_of::<T>() > MAX_MSG_SIZE
|
||||
|| hdr.get_size() as usize <= mem::size_of::<T>()
|
||||
|| hdr.get_size() as usize > MAX_MSG_SIZE
|
||||
|| hdr.is_reply()
|
||||
{
|
||||
return Err(VhostUserError::InvalidParam);
|
||||
|
|
@ -586,11 +590,8 @@ impl MasterInternal {
|
|||
{
|
||||
Endpoint::<MasterReq>::close_rfds(rfds);
|
||||
return Err(VhostUserError::InvalidMessage);
|
||||
} else if bytes > MAX_MSG_SIZE - mem::size_of::<T>() {
|
||||
} else if bytes != buf.len() {
|
||||
return Err(VhostUserError::InvalidMessage);
|
||||
} else if bytes < buf.len() {
|
||||
// It's safe because we have checked the buffer size
|
||||
unsafe { buf.set_len(bytes) };
|
||||
}
|
||||
Ok((body, buf, rfds))
|
||||
}
|
||||
|
|
@ -638,11 +639,14 @@ impl MasterInternal {
|
|||
mod tests {
|
||||
use super::super::connection::Listener;
|
||||
use super::*;
|
||||
use vmm_sys_util::rand::rand_alphanumerics;
|
||||
|
||||
const UNIX_SOCKET_MASTER: &'static str = "/tmp/vhost_user_test_rust_master";
|
||||
const UNIX_SOCKET_MASTER2: &'static str = "/tmp/vhost_user_test_rust_master2";
|
||||
const UNIX_SOCKET_MASTER3: &'static str = "/tmp/vhost_user_test_rust_master3";
|
||||
const UNIX_SOCKET_MASTER4: &'static str = "/tmp/vhost_user_test_rust_master4";
|
||||
fn temp_path() -> String {
|
||||
format!(
|
||||
"/tmp/vhost_test_{}",
|
||||
rand_alphanumerics(8).to_str().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn create_pair(path: &str) -> (Master, Endpoint<MasterReq>) {
|
||||
let listener = Listener::new(path, true).unwrap();
|
||||
|
|
@ -653,14 +657,15 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn create_master() {
|
||||
let listener = Listener::new(UNIX_SOCKET_MASTER, true).unwrap();
|
||||
let path = temp_path();
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
|
||||
let master = Master::connect(UNIX_SOCKET_MASTER, 1).unwrap();
|
||||
let master = Master::connect(&path, 1).unwrap();
|
||||
let mut slave = Endpoint::<MasterReq>::from_stream(listener.accept().unwrap().unwrap());
|
||||
|
||||
assert!(master.as_raw_fd() > 0);
|
||||
// Send two messages continuously
|
||||
master.set_owner().unwrap();
|
||||
master.reset_owner().unwrap();
|
||||
|
|
@ -679,24 +684,24 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_create_failure() {
|
||||
let _ = Listener::new(UNIX_SOCKET_MASTER2, true).unwrap();
|
||||
let _ = Listener::new(UNIX_SOCKET_MASTER2, false).is_err();
|
||||
assert!(Master::connect(UNIX_SOCKET_MASTER2, 1).is_err());
|
||||
let path = temp_path();
|
||||
let _ = Listener::new(&path, true).unwrap();
|
||||
let _ = Listener::new(&path, false).is_err();
|
||||
assert!(Master::connect(&path, 1).is_err());
|
||||
|
||||
let listener = Listener::new(UNIX_SOCKET_MASTER2, true).unwrap();
|
||||
assert!(Listener::new(UNIX_SOCKET_MASTER2, false).is_err());
|
||||
let listener = Listener::new(&path, true).unwrap();
|
||||
assert!(Listener::new(&path, false).is_err());
|
||||
listener.set_nonblocking(true).unwrap();
|
||||
|
||||
let _master = Master::connect(UNIX_SOCKET_MASTER2, 1).unwrap();
|
||||
let _master = Master::connect(&path, 1).unwrap();
|
||||
let _slave = listener.accept().unwrap().unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_features() {
|
||||
let (master, mut peer) = create_pair(UNIX_SOCKET_MASTER3);
|
||||
let path = temp_path();
|
||||
let (master, mut peer) = create_pair(&path);
|
||||
|
||||
master.set_owner().unwrap();
|
||||
let (hdr, rfds) = peer.recv_header().unwrap();
|
||||
|
|
@ -713,6 +718,9 @@ mod tests {
|
|||
let (_hdr, rfds) = peer.recv_header().unwrap();
|
||||
assert!(rfds.is_none());
|
||||
|
||||
let hdr = VhostUserMsgHeader::new(MasterReq::SET_FEATURES, 0x4, 8);
|
||||
let msg = VhostUserU64::new(0x15);
|
||||
peer.send_message(&hdr, &msg, None).unwrap();
|
||||
master.set_features(0x15).unwrap();
|
||||
let (_hdr, msg, rfds) = peer.recv_body::<VhostUserU64>().unwrap();
|
||||
assert!(rfds.is_none());
|
||||
|
|
@ -726,9 +734,9 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_protocol_features() {
|
||||
let (mut master, mut peer) = create_pair(UNIX_SOCKET_MASTER4);
|
||||
let path = temp_path();
|
||||
let (mut master, mut peer) = create_pair(&path);
|
||||
|
||||
master.set_owner().unwrap();
|
||||
let (hdr, rfds) = peer.recv_header().unwrap();
|
||||
|
|
@ -775,14 +783,4 @@ mod tests {
|
|||
peer.send_message(&hdr, &msg, None).unwrap();
|
||||
assert!(master.get_protocol_features().is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_set_mem_table() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_ring_num() {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -377,3 +377,102 @@ impl<S: VhostUserMasterReqHandler> AsRawFd for MasterReqHandler<S> {
|
|||
self.sub_sock.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[cfg(feature = "vhost-user-slave")]
|
||||
use crate::vhost_user::SlaveFsCacheReq;
|
||||
#[cfg(feature = "vhost-user-slave")]
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
struct MockMasterReqHandler {}
|
||||
|
||||
impl VhostUserMasterReqHandlerMut for MockMasterReqHandler {
|
||||
/// Handle virtio-fs map file requests from the slave.
|
||||
fn fs_slave_map(&mut self, _fs: &VhostUserFSSlaveMsg, fd: RawFd) -> HandlerResult<u64> {
|
||||
// Safe because we have just received the rawfd from kernel.
|
||||
unsafe { libc::close(fd) };
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
/// Handle virtio-fs unmap file requests from the slave.
|
||||
fn fs_slave_unmap(&mut self, _fs: &VhostUserFSSlaveMsg) -> HandlerResult<u64> {
|
||||
Err(std::io::Error::from_raw_os_error(libc::ENOSYS))
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_new_master_req_handler() {
|
||||
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
|
||||
let mut handler = MasterReqHandler::new(backend).unwrap();
|
||||
|
||||
assert!(handler.get_tx_raw_fd() >= 0);
|
||||
assert!(handler.as_raw_fd() >= 0);
|
||||
handler.check_state().unwrap();
|
||||
|
||||
assert_eq!(handler.error, None);
|
||||
handler.set_failed(libc::EAGAIN);
|
||||
assert_eq!(handler.error, Some(libc::EAGAIN));
|
||||
handler.check_state().unwrap_err();
|
||||
}
|
||||
|
||||
#[cfg(feature = "vhost-user-slave")]
|
||||
#[test]
|
||||
fn test_master_slave_req_handler() {
|
||||
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
|
||||
let mut handler = MasterReqHandler::new(backend).unwrap();
|
||||
|
||||
let fd = unsafe { libc::dup(handler.get_tx_raw_fd()) };
|
||||
if fd < 0 {
|
||||
panic!("failed to duplicated tx fd!");
|
||||
}
|
||||
let stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||
let fs_cache = SlaveFsCacheReq::from_stream(stream);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let res = handler.handle_request().unwrap();
|
||||
assert_eq!(res, 0);
|
||||
handler.handle_request().unwrap_err();
|
||||
});
|
||||
|
||||
fs_cache
|
||||
.fs_slave_map(&VhostUserFSSlaveMsg::default(), fd)
|
||||
.unwrap();
|
||||
// When REPLY_ACK has not been negotiated, the master has no way to detect failure from
|
||||
// slave side.
|
||||
fs_cache
|
||||
.fs_slave_unmap(&VhostUserFSSlaveMsg::default())
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "vhost-user-slave")]
|
||||
#[test]
|
||||
fn test_master_slave_req_handler_with_ack() {
|
||||
let backend = Arc::new(Mutex::new(MockMasterReqHandler {}));
|
||||
let mut handler = MasterReqHandler::new(backend).unwrap();
|
||||
handler.set_reply_ack_flag(true);
|
||||
|
||||
let fd = unsafe { libc::dup(handler.get_tx_raw_fd()) };
|
||||
if fd < 0 {
|
||||
panic!("failed to duplicated tx fd!");
|
||||
}
|
||||
let stream = unsafe { UnixStream::from_raw_fd(fd) };
|
||||
let fs_cache = SlaveFsCacheReq::from_stream(stream);
|
||||
|
||||
std::thread::spawn(move || {
|
||||
let res = handler.handle_request().unwrap();
|
||||
assert_eq!(res, 0);
|
||||
handler.handle_request().unwrap_err();
|
||||
});
|
||||
|
||||
fs_cache.set_reply_ack_flag(true);
|
||||
fs_cache
|
||||
.fs_slave_map(&VhostUserFSSlaveMsg::default(), fd)
|
||||
.unwrap();
|
||||
fs_cache
|
||||
.fs_slave_unmap(&VhostUserFSSlaveMsg::default())
|
||||
.unwrap_err();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -175,21 +175,32 @@ pub type Result<T> = std::result::Result<T, Error>;
|
|||
/// Result of request handler.
|
||||
pub type HandlerResult<T> = std::result::Result<T, IOError>;
|
||||
|
||||
#[cfg(all(test, feature = "vhost-user-master", feature = "vhost-user-slave"))]
|
||||
#[cfg(all(test, feature = "vhost-user-slave"))]
|
||||
mod dummy_slave;
|
||||
|
||||
#[cfg(all(test, feature = "vhost-user-master", feature = "vhost-user-slave"))]
|
||||
mod tests {
|
||||
use std::os::unix::io::AsRawFd;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::thread;
|
||||
use vmm_sys_util::rand::rand_alphanumerics;
|
||||
|
||||
use super::dummy_slave::{DummySlaveReqHandler, VIRTIO_FEATURES};
|
||||
use super::message::*;
|
||||
use super::*;
|
||||
use crate::backend::VhostBackend;
|
||||
use std::sync::{Arc, Barrier, Mutex};
|
||||
use std::thread;
|
||||
use crate::{VhostUserMemoryRegionInfo, VringConfigData};
|
||||
|
||||
fn temp_path() -> String {
|
||||
format!(
|
||||
"/tmp/vhost_test_{}",
|
||||
rand_alphanumerics(8).to_str().unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn create_slave<S: VhostUserSlaveReqHandler>(
|
||||
path: &str,
|
||||
backend: Arc<Mutex<S>>,
|
||||
backend: Arc<S>,
|
||||
) -> (Master, SlaveReqHandler<S>) {
|
||||
let listener = Listener::new(path, true).unwrap();
|
||||
let mut slave_listener = SlaveListener::new(listener, backend).unwrap();
|
||||
|
|
@ -208,8 +219,8 @@ mod tests {
|
|||
#[test]
|
||||
fn test_set_owner() {
|
||||
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let (master, mut slave) =
|
||||
create_slave("/tmp/vhost_user_lib_unit_test_owner", slave_be.clone());
|
||||
let path = temp_path();
|
||||
let (master, mut slave) = create_slave(&path, slave_be.clone());
|
||||
|
||||
assert_eq!(slave_be.lock().unwrap().owned, false);
|
||||
master.set_owner().unwrap();
|
||||
|
|
@ -224,9 +235,9 @@ mod tests {
|
|||
fn test_set_features() {
|
||||
let mbar = Arc::new(Barrier::new(2));
|
||||
let sbar = mbar.clone();
|
||||
let path = temp_path();
|
||||
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let (mut master, mut slave) =
|
||||
create_slave("/tmp/vhost_user_lib_unit_test_feature", slave_be.clone());
|
||||
let (mut master, mut slave) = create_slave(&path, slave_be.clone());
|
||||
|
||||
thread::spawn(move || {
|
||||
slave.handle_request().unwrap();
|
||||
|
|
@ -263,4 +274,157 @@ mod tests {
|
|||
|
||||
mbar.wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_master_slave_process() {
|
||||
let mbar = Arc::new(Barrier::new(2));
|
||||
let sbar = mbar.clone();
|
||||
let path = temp_path();
|
||||
let slave_be = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let (mut master, mut slave) = create_slave(&path, slave_be.clone());
|
||||
|
||||
thread::spawn(move || {
|
||||
// set_own()
|
||||
slave.handle_request().unwrap();
|
||||
assert_eq!(slave_be.lock().unwrap().owned, true);
|
||||
|
||||
// get/set_features()
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
assert_eq!(
|
||||
slave_be.lock().unwrap().acked_features,
|
||||
VIRTIO_FEATURES & !0x1
|
||||
);
|
||||
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
assert_eq!(
|
||||
slave_be.lock().unwrap().acked_protocol_features,
|
||||
VhostUserProtocolFeatures::all().bits()
|
||||
);
|
||||
|
||||
// get_queue_num()
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
// set_mem_table()
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
// get/set_config()
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
// set_slave_request_fd
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
// set_vring_enable
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
/*
|
||||
// set_log_base,set_log_fd()
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
*/
|
||||
|
||||
// set_vring_xxx
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
slave.handle_request().unwrap();
|
||||
|
||||
sbar.wait();
|
||||
});
|
||||
|
||||
master.set_owner().unwrap();
|
||||
|
||||
// set virtio features
|
||||
let features = master.get_features().unwrap();
|
||||
assert_eq!(features, VIRTIO_FEATURES);
|
||||
master.set_features(VIRTIO_FEATURES & !0x1).unwrap();
|
||||
|
||||
// set vhost protocol features
|
||||
let features = master.get_protocol_features().unwrap();
|
||||
assert_eq!(features.bits(), VhostUserProtocolFeatures::all().bits());
|
||||
master.set_protocol_features(features).unwrap();
|
||||
|
||||
let num = master.get_queue_num().unwrap();
|
||||
assert_eq!(num, 2);
|
||||
|
||||
let eventfd = vmm_sys_util::eventfd::EventFd::new(0).unwrap();
|
||||
let mem = [VhostUserMemoryRegionInfo {
|
||||
guest_phys_addr: 0,
|
||||
memory_size: 0x10_0000,
|
||||
userspace_addr: 0,
|
||||
mmap_offset: 0,
|
||||
mmap_handle: eventfd.as_raw_fd(),
|
||||
}];
|
||||
master.set_mem_table(&mem).unwrap();
|
||||
|
||||
master
|
||||
.set_config(0x100, VhostUserConfigFlags::WRITABLE, &[0xa5u8])
|
||||
.unwrap();
|
||||
let buf = [0x0u8; 4];
|
||||
let (reply_body, reply_payload) = master
|
||||
.get_config(0x100, 4, VhostUserConfigFlags::empty(), &buf)
|
||||
.unwrap();
|
||||
let offset = reply_body.offset;
|
||||
assert_eq!(offset, 0x100);
|
||||
assert_eq!(reply_payload[0], 0xa5);
|
||||
|
||||
master.set_slave_request_fd(eventfd.as_raw_fd()).unwrap();
|
||||
master.set_vring_enable(0, true).unwrap();
|
||||
|
||||
/*
|
||||
master.set_log_base(0, Some(eventfd.as_raw_fd())).unwrap();
|
||||
master.set_log_fd(eventfd.as_raw_fd()).unwrap();
|
||||
*/
|
||||
|
||||
master.set_vring_num(0, 256).unwrap();
|
||||
master.set_vring_base(0, 0).unwrap();
|
||||
let config = VringConfigData {
|
||||
queue_max_size: 256,
|
||||
queue_size: 128,
|
||||
flags: VhostUserVringAddrFlags::VHOST_VRING_F_LOG.bits(),
|
||||
desc_table_addr: 0x1000,
|
||||
used_ring_addr: 0x2000,
|
||||
avail_ring_addr: 0x3000,
|
||||
log_addr: Some(0x4000),
|
||||
};
|
||||
master.set_vring_addr(0, &config).unwrap();
|
||||
master.set_vring_call(0, &eventfd).unwrap();
|
||||
master.set_vring_kick(0, &eventfd).unwrap();
|
||||
master.set_vring_err(0, &eventfd).unwrap();
|
||||
|
||||
mbar.wait();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_display() {
|
||||
assert_eq!(format!("{}", Error::InvalidParam), "invalid parameters");
|
||||
assert_eq!(format!("{}", Error::InvalidOperation), "invalid operation");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_should_reconnect() {
|
||||
assert_eq!(Error::PartialMessage.should_reconnect(), true);
|
||||
assert_eq!(Error::SlaveInternalError.should_reconnect(), true);
|
||||
assert_eq!(Error::MasterInternalError.should_reconnect(), true);
|
||||
assert_eq!(Error::InvalidParam.should_reconnect(), false);
|
||||
assert_eq!(Error::InvalidOperation.should_reconnect(), false);
|
||||
assert_eq!(Error::InvalidMessage.should_reconnect(), false);
|
||||
assert_eq!(Error::IncorrectFds.should_reconnect(), false);
|
||||
assert_eq!(Error::OversizedMsg.should_reconnect(), false);
|
||||
assert_eq!(Error::FeatureMismatch.should_reconnect(), false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_error_from_sys_util_error() {
|
||||
let e: Error = vmm_sys_util::errno::Error::new(libc::EAGAIN.into()).into();
|
||||
if let Error::SocketRetry(e1) = e {
|
||||
assert_eq!(e1.raw_os_error().unwrap(), libc::EAGAIN);
|
||||
} else {
|
||||
panic!("invalid error code conversion!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,3 +44,43 @@ impl<S: VhostUserSlaveReqHandler> SlaveListener<S> {
|
|||
self.listener.set_nonblocking(block)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::*;
|
||||
use crate::vhost_user::dummy_slave::DummySlaveReqHandler;
|
||||
|
||||
#[test]
|
||||
fn test_slave_listener_set_nonblocking() {
|
||||
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let listener =
|
||||
Listener::new("/tmp/vhost_user_lib_unit_test_slave_nonblocking", true).unwrap();
|
||||
let slave_listener = SlaveListener::new(listener, backend).unwrap();
|
||||
|
||||
slave_listener.set_nonblocking(true).unwrap();
|
||||
slave_listener.set_nonblocking(false).unwrap();
|
||||
slave_listener.set_nonblocking(false).unwrap();
|
||||
slave_listener.set_nonblocking(true).unwrap();
|
||||
slave_listener.set_nonblocking(true).unwrap();
|
||||
}
|
||||
|
||||
#[cfg(feature = "vhost-user-master")]
|
||||
#[test]
|
||||
fn test_slave_listener_accept() {
|
||||
use super::super::Master;
|
||||
|
||||
let path = "/tmp/vhost_user_lib_unit_test_slave_accept";
|
||||
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let listener = Listener::new(path, true).unwrap();
|
||||
let mut slave_listener = SlaveListener::new(listener, backend).unwrap();
|
||||
|
||||
slave_listener.set_nonblocking(true).unwrap();
|
||||
assert!(slave_listener.accept().unwrap().is_none());
|
||||
assert!(slave_listener.accept().unwrap().is_none());
|
||||
|
||||
let _master = Master::connect(path, 1).unwrap();
|
||||
let _slave = slave_listener.accept().unwrap().unwrap();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -743,3 +743,24 @@ impl<S: VhostUserSlaveReqHandler> AsRawFd for SlaveReqHandler<S> {
|
|||
self.main_sock.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::os::unix::io::AsRawFd;
|
||||
|
||||
use super::*;
|
||||
use crate::vhost_user::dummy_slave::DummySlaveReqHandler;
|
||||
|
||||
#[test]
|
||||
fn test_slave_req_handler_new() {
|
||||
let (p1, _p2) = UnixStream::pair().unwrap();
|
||||
let endpoint = Endpoint::<MasterReq>::from_stream(p1);
|
||||
let backend = Arc::new(Mutex::new(DummySlaveReqHandler::new()));
|
||||
let mut handler = SlaveReqHandler::new(endpoint, backend);
|
||||
|
||||
handler.check_state().unwrap();
|
||||
handler.set_failed(libc::EAGAIN);
|
||||
handler.check_state().unwrap_err();
|
||||
assert!(handler.as_raw_fd() >= 0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue