base: Extract integration tests

The extracted tests rely on access to system devices
or global state that prevent them from being run in parallel
or in restricted environments.

As an integration test they will be executed separately and
single threaded.

Updates the test runner to ensure integration tests are actually
run single threaded as intended.

BUG=b:244623061
TEST=./tools/run_tests base:\* --repeat 100 -p x86_64/mingw64/aarch64

Change-Id: I4267b9f79055208aca86796d902da251816bcada
Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3971025
Reviewed-by: Daniel Verkamp <dverkamp@chromium.org>
Commit-Queue: Dennis Kempin <denniskempin@google.com>
This commit is contained in:
Dennis Kempin 2022-10-20 20:31:30 +00:00 committed by crosvm LUCI
parent 08bd3b167c
commit b896b869e4
15 changed files with 917 additions and 856 deletions

View file

@ -668,8 +668,6 @@ pub fn number_of_logical_cores() -> Result<usize> {
mod tests {
use std::io::Write;
use libc::EBADF;
use super::*;
#[test]
@ -682,35 +680,4 @@ mod tests {
tx.write(&[0u8; 8])
.expect_err("Write after fill didn't fail");
}
#[test]
fn safe_descriptor_from_path_valid() {
assert!(safe_descriptor_from_path(Path::new("/proc/self/fd/2"))
.unwrap()
.is_some());
}
#[test]
fn safe_descriptor_from_path_invalid_integer() {
assert_eq!(
safe_descriptor_from_path(Path::new("/proc/self/fd/blah")),
Err(Error::new(EINVAL))
);
}
#[test]
fn safe_descriptor_from_path_invalid_fd() {
assert_eq!(
safe_descriptor_from_path(Path::new("/proc/self/fd/42")),
Err(Error::new(EBADF))
);
}
#[test]
fn safe_descriptor_from_path_none() {
assert_eq!(
safe_descriptor_from_path(Path::new("/something/else")).unwrap(),
None
);
}
}

View file

@ -930,16 +930,8 @@ impl Drop for UnlinkUnixSeqpacketListener {
#[cfg(test)]
mod tests {
use std::env;
use std::io::ErrorKind;
use std::path::PathBuf;
use super::*;
fn tmpdir() -> PathBuf {
env::temp_dir()
}
#[test]
fn sockaddr_un_zero_length_input() {
let _res = sockaddr_un(Path::new("")).expect("sockaddr_un failed");
@ -984,231 +976,4 @@ mod tests {
assert_eq!(addr_char, ref_char);
}
}
#[test]
fn unix_seqpacket_path_not_exists() {
let res = UnixSeqpacket::connect("/path/not/exists");
assert!(res.is_err());
}
#[test]
fn unix_seqpacket_listener_path() {
let mut socket_path = tmpdir();
socket_path.push("unix_seqpacket_listener_path");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path)
.expect("failed to create UnixSeqpacketListener"),
);
let listener_path = listener.path().expect("failed to get socket listener path");
assert_eq!(socket_path, listener_path);
}
#[test]
fn unix_seqpacket_listener_from_fd() {
let mut socket_path = tmpdir();
socket_path.push("unix_seqpacket_listener_from_fd");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path)
.expect("failed to create UnixSeqpacketListener"),
);
// UnixSeqpacketListener should succeed on a valid listening descriptor.
let good_dup = UnixSeqpacketListener::bind(&format!("/proc/self/fd/{}", unsafe {
libc::dup(listener.as_raw_fd())
}));
let good_dup_path = good_dup
.expect("failed to create dup UnixSeqpacketListener")
.path();
// Path of socket created by descriptor should be hidden.
assert!(good_dup_path.is_err());
// UnixSeqpacketListener must fail on an existing non-listener socket.
let s1 =
UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
let bad_dup = UnixSeqpacketListener::bind(&format!("/proc/self/fd/{}", unsafe {
libc::dup(s1.as_raw_fd())
}));
assert!(bad_dup.is_err());
}
#[test]
fn unix_seqpacket_path_exists_pass() {
let mut socket_path = tmpdir();
socket_path.push("path_to_socket");
let _listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path)
.expect("failed to create UnixSeqpacketListener"),
);
let _res =
UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
}
#[test]
fn unix_seqpacket_path_listener_accept_with_timeout() {
let mut socket_path = tmpdir();
socket_path.push("path_listerner_accept_with_timeout");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path)
.expect("failed to create UnixSeqpacketListener"),
);
for d in [Duration::from_millis(10), Duration::ZERO] {
let _ = listener.accept_with_timeout(d).expect_err(&format!(
"UnixSeqpacket::accept_with_timeout {:?} connected",
d
));
let s1 = UnixSeqpacket::connect(socket_path.as_path())
.unwrap_or_else(|_| panic!("UnixSeqpacket::connect {:?} failed", d));
let s2 = listener
.accept_with_timeout(d)
.unwrap_or_else(|_| panic!("UnixSeqpacket::accept {:?} failed", d));
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
}
#[test]
fn unix_seqpacket_path_listener_accept() {
let mut socket_path = tmpdir();
socket_path.push("path_listerner_accept");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path)
.expect("failed to create UnixSeqpacketListener"),
);
let s1 =
UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
let s2 = listener.accept().expect("UnixSeqpacket::accept failed");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
#[test]
fn unix_seqpacket_zero_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
// Timeouts less than a microsecond are too small and round to zero.
s1.set_read_timeout(Some(Duration::from_nanos(10)))
.expect_err("successfully set zero timeout");
}
#[test]
fn unix_seqpacket_read_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
s1.set_read_timeout(Some(Duration::from_millis(1)))
.expect("failed to set read timeout for socket");
let _ = s1.recv(&mut [0]);
}
#[test]
fn unix_seqpacket_write_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
s1.set_write_timeout(Some(Duration::from_millis(1)))
.expect("failed to set write timeout for socket");
}
#[test]
fn unix_seqpacket_send_recv() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
#[test]
fn unix_seqpacket_send_fragments() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14, 15, 16];
s1.send(data1).expect("failed to send data1");
s1.send(data2).expect("failed to send data2");
let recv_data = &mut [0; 32];
let size = s2.recv(recv_data).expect("failed to recv data");
assert_eq!(size, data1.len());
assert_eq!(data1, &recv_data[0..size]);
let size = s2.recv(recv_data).expect("failed to recv data");
assert_eq!(size, data2.len());
assert_eq!(data2, &recv_data[0..size]);
}
#[test]
fn unix_seqpacket_get_readable_bytes() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), 0);
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), data1.len());
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), 0);
}
#[test]
fn unix_seqpacket_next_packet_size() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
assert_eq!(s2.next_packet_size().unwrap(), 5);
s1.set_read_timeout(Some(Duration::from_micros(1)))
.expect("failed to set read timeout");
assert_eq!(
s1.next_packet_size().unwrap_err().kind(),
ErrorKind::WouldBlock
);
drop(s2);
assert_eq!(
s1.next_packet_size().unwrap_err().kind(),
ErrorKind::ConnectionReset
);
}
#[test]
fn unix_seqpacket_recv_to_vec() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
let recv_data = &mut vec![];
s2.recv_to_vec(recv_data).expect("failed to recv data");
assert_eq!(recv_data, &mut vec![0, 1, 2, 3, 4]);
}
#[test]
fn unix_seqpacket_recv_as_vec() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
let recv_data = s2.recv_as_vec().expect("failed to recv data");
assert_eq!(recv_data, vec![0, 1, 2, 3, 4]);
}
}

View file

@ -176,248 +176,3 @@ pub fn wait_for_interrupt() -> Result<()> {
Err(err) => Err(Error::WaitForSignal(err)),
}
}
#[cfg(test)]
mod tests {
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::mem::zeroed;
use std::ptr::null;
use std::ptr::null_mut;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::Once;
use std::thread::sleep;
use std::thread::spawn;
use std::time::Duration;
use std::time::Instant;
use libc::sigaction;
use super::super::gettid;
use super::super::kill;
use super::super::Pid;
use super::*;
const TEST_SIGNAL: Signal = Signal::User1;
const TEST_SIGNALS: &[Signal] = &[Signal::User1, Signal::User2];
static TEST_SIGNAL_COUNTER: AtomicUsize = AtomicUsize::new(0);
/// Only allows one test case to execute at a time.
fn get_mutex() -> MutexGuard<'static, ()> {
static INIT: Once = Once::new();
static mut VAL: Option<Arc<Mutex<()>>> = None;
INIT.call_once(|| {
let val = Some(Arc::new(Mutex::new(())));
// Safe because the mutation is protected by the Once.
unsafe { VAL = val }
});
// Safe mutation only happens in the Once.
unsafe { VAL.as_ref() }.unwrap().lock().unwrap()
}
fn reset_counter() {
TEST_SIGNAL_COUNTER.swap(0, Ordering::SeqCst);
}
fn get_sigaction(signal: Signal) -> Result<sigaction> {
// Safe because sigaction is owned and expected to be initialized ot zeros.
let mut sigact: sigaction = unsafe { zeroed() };
if unsafe { sigaction(signal.into(), null(), &mut sigact) } < 0 {
Err(Error::Sigaction(signal, ErrnoError::last()))
} else {
Ok(sigact)
}
}
/// Safety:
/// This is only safe if the signal handler set in sigaction is safe.
unsafe fn restore_sigaction(signal: Signal, sigact: sigaction) -> Result<sigaction> {
if sigaction(signal.into(), &sigact, null_mut()) < 0 {
Err(Error::Sigaction(signal, ErrnoError::last()))
} else {
Ok(sigact)
}
}
/// Safety:
/// Safe if the signal handler for Signal::User1 is safe.
unsafe fn send_test_signal() {
kill(gettid(), Signal::User1.into()).unwrap()
}
macro_rules! assert_counter_eq {
($compare_to:expr) => {{
let expected: usize = $compare_to;
let got: usize = TEST_SIGNAL_COUNTER.load(Ordering::SeqCst);
if got != expected {
panic!(
"wrong signal counter value: got {}; expected {}",
got, expected
);
}
}};
}
struct TestHandler;
/// # Safety
/// Safe because handle_signal is async-signal safe.
unsafe impl SignalHandler for TestHandler {
fn handle_signal(signal: Signal) {
if TEST_SIGNAL == signal {
TEST_SIGNAL_COUNTER.fetch_add(1, Ordering::SeqCst);
}
}
}
#[test]
fn scopedsignalhandler_success() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
reset_counter();
assert_counter_eq!(0);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
let handler = ScopedSignalHandler::new::<TestHandler>(&[TEST_SIGNAL]).unwrap();
assert!(!has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because test_handler is safe.
unsafe { send_test_signal() };
// Give the handler time to run in case it is on a different thread.
for _ in 1..40 {
if TEST_SIGNAL_COUNTER.load(Ordering::SeqCst) > 0 {
break;
}
sleep(Duration::from_millis(250));
}
assert_counter_eq!(1);
drop(handler);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
}
#[test]
fn scopedsignalhandler_handleralreadyset() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
reset_counter();
assert_counter_eq!(0);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because TestHandler is async-signal safe.
let handler = ScopedSignalHandler::new::<TestHandler>(&[TEST_SIGNAL]).unwrap();
assert!(!has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because TestHandler is async-signal safe.
assert!(matches!(
ScopedSignalHandler::new::<TestHandler>(TEST_SIGNALS),
Err(Error::HandlerAlreadySet(Signal::User1))
));
assert_counter_eq!(0);
drop(handler);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
}
/// Stores the thread used by WaitForInterruptHandler.
static WAIT_FOR_INTERRUPT_THREAD_ID: AtomicI32 = AtomicI32::new(0);
/// Forwards SIGINT to the appropriate thread.
struct WaitForInterruptHandler;
/// # Safety
/// Safe because handle_signal is async-signal safe.
unsafe impl SignalHandler for WaitForInterruptHandler {
fn handle_signal(_: Signal) {
let tid = WAIT_FOR_INTERRUPT_THREAD_ID.load(Ordering::SeqCst);
// If the thread ID is set and executed on the wrong thread, forward the signal.
if tid != 0 && gettid() != tid {
// Safe because the handler is safe and the target thread id is expecting the signal.
unsafe { kill(tid, Signal::Interrupt.into()) }.unwrap();
}
}
}
/// Query /proc/${tid}/status for its State and check if it is either S (sleeping) or in
/// D (disk sleep).
fn thread_is_sleeping(tid: Pid) -> result::Result<bool, ErrnoError> {
const PREFIX: &str = "State:";
let mut status_reader = BufReader::new(File::open(format!("/proc/{}/status", tid))?);
let mut line = String::new();
loop {
let count = status_reader.read_line(&mut line)?;
if count == 0 {
return Err(ErrnoError::new(libc::EIO));
}
if let Some(stripped) = line.strip_prefix(PREFIX) {
return Ok(matches!(
stripped.trim_start().chars().next(),
Some('S') | Some('D')
));
}
line.clear();
}
}
/// Wait for a process to block either in a sleeping or disk sleep state.
fn wait_for_thread_to_sleep(tid: Pid, timeout: Duration) -> result::Result<(), ErrnoError> {
let start = Instant::now();
loop {
if thread_is_sleeping(tid)? {
return Ok(());
}
if start.elapsed() > timeout {
return Err(ErrnoError::new(libc::EAGAIN));
}
sleep(Duration::from_millis(50));
}
}
#[test]
fn waitforinterrupt_success() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
let to_restore = get_sigaction(Signal::Interrupt).unwrap();
clear_signal_handler(Signal::Interrupt.into()).unwrap();
// Safe because TestHandler is async-signal safe.
let handler =
ScopedSignalHandler::new::<WaitForInterruptHandler>(&[Signal::Interrupt]).unwrap();
let tid = gettid();
WAIT_FOR_INTERRUPT_THREAD_ID.store(tid, Ordering::SeqCst);
let join_handle = spawn(move || -> result::Result<(), ErrnoError> {
// Wait unitl the thread is ready to receive the signal.
wait_for_thread_to_sleep(tid, Duration::from_secs(10)).unwrap();
// Safe because the SIGINT handler is safe.
unsafe { kill(tid, Signal::Interrupt.into()) }
});
let wait_ret = wait_for_interrupt();
let join_ret = join_handle.join();
drop(handler);
// Safe because we are restoring the previous SIGINT handler.
unsafe { restore_sigaction(Signal::Interrupt, to_restore) }.unwrap();
wait_ret.unwrap();
join_ret.unwrap().unwrap();
}
}

View file

@ -3,52 +3,3 @@
// found in the LICENSE file.
pub use super::target_os::syslog::PlatformSyslog;
pub use super::RawDescriptor;
#[cfg(test)]
mod tests {
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use crate::syslog::*;
#[test]
fn fds() {
ensure_inited().unwrap();
let mut fds = Vec::new();
push_descriptors(&mut fds);
assert!(!fds.is_empty());
for fd in fds {
assert!(fd >= 0);
}
}
#[test]
fn syslog_file() {
ensure_inited().unwrap();
let mut file = tempfile::tempfile().expect("failed to create tempfile");
let syslog_file = file.try_clone().expect("error cloning shared memory file");
let state = State::new(LogConfig {
pipe: Some(Box::new(syslog_file)),
..Default::default()
})
.unwrap();
const TEST_STR: &str = "hello shared memory file";
state.log(
&log::RecordBuilder::new()
.level(Level::Error)
.args(format_args!("{}", TEST_STR))
.build(),
);
file.seek(SeekFrom::Start(0))
.expect("error seeking shared memory file");
let mut buf = String::new();
file.read_to_string(&mut buf)
.expect("error reading shared memory file");
assert!(buf.contains(TEST_STR));
}
}

View file

@ -191,93 +191,3 @@ impl AsRawDescriptor for RecvTube {
self.0.as_raw_descriptor()
}
}
#[cfg(test)]
mod test {
use std::time;
use super::*;
use crate::EventContext;
use crate::EventToken;
use crate::ReadNotifier;
#[derive(EventToken, Debug, Eq, PartialEq, Copy, Clone)]
enum Token {
ReceivedData,
}
const EVENT_WAIT_TIME: time::Duration = time::Duration::from_secs(10);
#[test]
fn test_serialize_tube_new() {
let (sock_send, sock_recv) =
StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Message).unwrap();
let tube_send = Tube::new(sock_send).unwrap();
let tube_recv = Tube::new(sock_recv).unwrap();
// Serialize the Tube
let msg_serialize = SerializeDescriptors::new(&tube_send);
let serialized = serde_json::to_vec(&msg_serialize).unwrap();
let msg_descriptors = msg_serialize.into_descriptors();
// Deserialize the Tube
let mut msg_descriptors_safe = msg_descriptors
.into_iter()
.map(|v| Some(unsafe { SafeDescriptor::from_raw_descriptor(v) }))
.collect();
let tube_deserialized: Tube = deserialize_with_descriptors(
|| serde_json::from_slice(&serialized),
&mut msg_descriptors_safe,
)
.unwrap();
// Send a message through deserialized Tube
tube_deserialized.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let event_ctx: EventContext<Token> =
EventContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)])
.unwrap();
let events = event_ctx.wait_timeout(EVENT_WAIT_TIME).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn test_send_recv_new_from_seqpacket() {
let (sock_send, sock_recv) = UnixSeqpacket::pair().unwrap();
let tube_send = Tube::new_from_unix_seqpacket(sock_send);
let tube_recv = Tube::new_from_unix_seqpacket(sock_recv);
tube_send.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let event_ctx: EventContext<Token> =
EventContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)])
.unwrap();
let events = event_ctx.wait_timeout(EVENT_WAIT_TIME).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn test_tube_new_byte_mode_error() {
let (sock_byte_mode, _) =
StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte).unwrap();
let tube_error = Tube::new(sock_byte_mode);
assert!(tube_error.is_err());
}
}

View file

@ -190,7 +190,7 @@ pub(crate) trait Syslog {
) -> Result<(Option<Box<dyn Log + Send>>, Option<RawDescriptor>), Error>;
}
pub(crate) struct State {
pub struct State {
/// Record filter
filter: env_logger::filter::Filter,
/// All the loggers we have

View file

@ -139,199 +139,3 @@ pub enum Error {
}
pub type Result<T> = std::result::Result<T, Error>;
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Barrier;
use std::thread;
use std::time::Duration;
use serde::Deserialize;
use serde::Serialize;
use super::*;
use crate::descriptor::FromRawDescriptor;
use crate::descriptor::SafeDescriptor;
use crate::platform::deserialize_with_descriptors;
use crate::platform::SerializeDescriptors;
use crate::Event;
use crate::EventToken;
use crate::ReadNotifier;
use crate::WaitContext;
#[derive(Serialize, Deserialize)]
struct DataStruct {
x: u32,
}
#[derive(EventToken, Debug, Eq, PartialEq, Copy, Clone)]
enum Token {
ReceivedData,
}
// Magics to identify which producer sent a message (& detect corruption).
const PRODUCER_ID_1: u32 = 801279273;
const PRODUCER_ID_2: u32 = 345234861;
#[track_caller]
fn test_event_pair(send: Event, recv: Event) {
send.signal().unwrap();
recv.wait_timeout(Duration::from_secs(1)).unwrap();
}
#[test]
fn send_recv_no_fd() {
let (s1, s2) = Tube::pair().unwrap();
let test_msg = "hello world";
s1.send(&test_msg).unwrap();
let recv_msg: String = s2.recv().unwrap();
assert_eq!(test_msg, recv_msg);
}
#[test]
fn send_recv_one_fd() {
#[derive(Serialize, Deserialize)]
struct EventStruct {
x: u32,
b: Event,
}
let (s1, s2) = Tube::pair().unwrap();
let test_msg = EventStruct {
x: 100,
b: Event::new().unwrap(),
};
s1.send(&test_msg).unwrap();
let recv_msg: EventStruct = s2.recv().unwrap();
assert_eq!(test_msg.x, recv_msg.x);
test_event_pair(test_msg.b, recv_msg.b);
}
/// Send messages to a Tube with the given identifier (see `consume_messages`; we use this to
/// track different message producers).
#[track_caller]
fn produce_messages(tube: SendTube, data: u32, barrier: Arc<Barrier>) -> SendTube {
let data = DataStruct { x: data };
barrier.wait();
for _ in 0..100 {
tube.send(&data).unwrap();
}
tube
}
/// Consumes the given number of messages from a Tube, returning the number messages read with
/// each producer ID.
#[track_caller]
fn consume_messages(
tube: RecvTube,
count: usize,
barrier: Arc<Barrier>,
) -> (RecvTube, usize, usize) {
barrier.wait();
let mut id1_count = 0usize;
let mut id2_count = 0usize;
for _ in 0..count {
let msg = tube.recv::<DataStruct>().unwrap();
match msg.x {
PRODUCER_ID_1 => id1_count += 1,
PRODUCER_ID_2 => id2_count += 1,
_ => panic!(
"want message with ID {} or {}; got message w/ ID {}.",
PRODUCER_ID_1, PRODUCER_ID_2, msg.x
),
}
}
(tube, id1_count, id2_count)
}
#[test]
fn test_serialize_tube_pair() {
let (tube_send, tube_recv) = Tube::pair().unwrap();
// Serialize the Tube
let msg_serialize = SerializeDescriptors::new(&tube_send);
let serialized = serde_json::to_vec(&msg_serialize).unwrap();
let msg_descriptors = msg_serialize.into_descriptors();
// Deserialize the Tube
let mut msg_descriptors_safe = msg_descriptors
.into_iter()
.map(|v| Some(unsafe { SafeDescriptor::from_raw_descriptor(v) }))
.collect();
let tube_deserialized: Tube = deserialize_with_descriptors(
|| serde_json::from_slice(&serialized),
&mut msg_descriptors_safe,
)
.unwrap();
// Send a message through deserialized Tube
tube_deserialized.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let wait_ctx: WaitContext<Token> =
WaitContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)])
.unwrap();
let events = wait_ctx.wait_timeout(Duration::from_secs(10)).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn send_recv_mpsc() {
let (p1, consumer) = Tube::directional_pair().unwrap();
let p2 = p1.try_clone().unwrap();
let start_block_p1 = Arc::new(Barrier::new(3));
let start_block_p2 = start_block_p1.clone();
let start_block_consumer = start_block_p1.clone();
let p1_thread = thread::spawn(move || produce_messages(p1, PRODUCER_ID_1, start_block_p1));
let p2_thread = thread::spawn(move || produce_messages(p2, PRODUCER_ID_2, start_block_p2));
let (_tube, id1_count, id2_count) = consume_messages(consumer, 200, start_block_consumer);
assert_eq!(id1_count, 100);
assert_eq!(id2_count, 100);
p1_thread.join().unwrap();
p2_thread.join().unwrap();
}
#[test]
fn send_recv_hash_map() {
let (s1, s2) = Tube::pair().unwrap();
let mut test_msg = HashMap::new();
test_msg.insert("Red".to_owned(), Event::new().unwrap());
test_msg.insert("White".to_owned(), Event::new().unwrap());
test_msg.insert("Blue".to_owned(), Event::new().unwrap());
test_msg.insert("Orange".to_owned(), Event::new().unwrap());
test_msg.insert("Green".to_owned(), Event::new().unwrap());
s1.send(&test_msg).unwrap();
let mut recv_msg: HashMap<String, Event> = s2.recv().unwrap();
let mut test_msg_keys: Vec<_> = test_msg.keys().collect();
test_msg_keys.sort();
let mut recv_msg_keys: Vec<_> = recv_msg.keys().collect();
recv_msg_keys.sort();
assert_eq!(test_msg_keys, recv_msg_keys);
for (key, test_event) in test_msg {
let recv_event = recv_msg.remove(&key).unwrap();
test_event_pair(test_event, recv_event);
}
}
}

197
base/tests/tube.rs Normal file
View file

@ -0,0 +1,197 @@
// Copyright 2021 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::collections::HashMap;
use std::sync::Arc;
use std::sync::Barrier;
use std::thread;
use std::time::Duration;
use base::RecvTube;
use base::SendTube;
use base::Tube;
use serde::Deserialize;
use serde::Serialize;
use base::descriptor::FromRawDescriptor;
use base::descriptor::SafeDescriptor;
use base::platform::deserialize_with_descriptors;
use base::platform::SerializeDescriptors;
use base::Event;
use base::EventToken;
use base::ReadNotifier;
use base::WaitContext;
#[derive(Serialize, Deserialize)]
struct DataStruct {
x: u32,
}
#[derive(EventToken, Debug, Eq, PartialEq, Copy, Clone)]
enum Token {
ReceivedData,
}
// Magics to identify which producer sent a message (& detect corruption).
const PRODUCER_ID_1: u32 = 801279273;
const PRODUCER_ID_2: u32 = 345234861;
#[track_caller]
fn test_event_pair(send: Event, recv: Event) {
send.signal().unwrap();
recv.wait_timeout(Duration::from_secs(1)).unwrap();
}
#[test]
fn send_recv_no_fd() {
let (s1, s2) = Tube::pair().unwrap();
let test_msg = "hello world";
s1.send(&test_msg).unwrap();
let recv_msg: String = s2.recv().unwrap();
assert_eq!(test_msg, recv_msg);
}
#[test]
fn send_recv_one_fd() {
#[derive(Serialize, Deserialize)]
struct EventStruct {
x: u32,
b: Event,
}
let (s1, s2) = Tube::pair().unwrap();
let test_msg = EventStruct {
x: 100,
b: Event::new().unwrap(),
};
s1.send(&test_msg).unwrap();
let recv_msg: EventStruct = s2.recv().unwrap();
assert_eq!(test_msg.x, recv_msg.x);
test_event_pair(test_msg.b, recv_msg.b);
}
/// Send messages to a Tube with the given identifier (see `consume_messages`; we use this to
/// track different message producers).
#[track_caller]
fn produce_messages(tube: SendTube, data: u32, barrier: Arc<Barrier>) -> SendTube {
let data = DataStruct { x: data };
barrier.wait();
for _ in 0..100 {
tube.send(&data).unwrap();
}
tube
}
/// Consumes the given number of messages from a Tube, returning the number messages read with
/// each producer ID.
#[track_caller]
fn consume_messages(
tube: RecvTube,
count: usize,
barrier: Arc<Barrier>,
) -> (RecvTube, usize, usize) {
barrier.wait();
let mut id1_count = 0usize;
let mut id2_count = 0usize;
for _ in 0..count {
let msg = tube.recv::<DataStruct>().unwrap();
match msg.x {
PRODUCER_ID_1 => id1_count += 1,
PRODUCER_ID_2 => id2_count += 1,
_ => panic!(
"want message with ID {} or {}; got message w/ ID {}.",
PRODUCER_ID_1, PRODUCER_ID_2, msg.x
),
}
}
(tube, id1_count, id2_count)
}
#[test]
fn test_serialize_tube_pair() {
let (tube_send, tube_recv) = Tube::pair().unwrap();
// Serialize the Tube
let msg_serialize = SerializeDescriptors::new(&tube_send);
let serialized = serde_json::to_vec(&msg_serialize).unwrap();
let msg_descriptors = msg_serialize.into_descriptors();
// Deserialize the Tube
let mut msg_descriptors_safe = msg_descriptors
.into_iter()
.map(|v| Some(unsafe { SafeDescriptor::from_raw_descriptor(v) }))
.collect();
let tube_deserialized: Tube = deserialize_with_descriptors(
|| serde_json::from_slice(&serialized),
&mut msg_descriptors_safe,
)
.unwrap();
// Send a message through deserialized Tube
tube_deserialized.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let wait_ctx: WaitContext<Token> =
WaitContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)]).unwrap();
let events = wait_ctx.wait_timeout(Duration::from_secs(10)).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn send_recv_mpsc() {
let (p1, consumer) = Tube::directional_pair().unwrap();
let p2 = p1.try_clone().unwrap();
let start_block_p1 = Arc::new(Barrier::new(3));
let start_block_p2 = start_block_p1.clone();
let start_block_consumer = start_block_p1.clone();
let p1_thread = thread::spawn(move || produce_messages(p1, PRODUCER_ID_1, start_block_p1));
let p2_thread = thread::spawn(move || produce_messages(p2, PRODUCER_ID_2, start_block_p2));
let (_tube, id1_count, id2_count) = consume_messages(consumer, 200, start_block_consumer);
assert_eq!(id1_count, 100);
assert_eq!(id2_count, 100);
p1_thread.join().unwrap();
p2_thread.join().unwrap();
}
#[test]
fn send_recv_hash_map() {
let (s1, s2) = Tube::pair().unwrap();
let mut test_msg = HashMap::new();
test_msg.insert("Red".to_owned(), Event::new().unwrap());
test_msg.insert("White".to_owned(), Event::new().unwrap());
test_msg.insert("Blue".to_owned(), Event::new().unwrap());
test_msg.insert("Orange".to_owned(), Event::new().unwrap());
test_msg.insert("Green".to_owned(), Event::new().unwrap());
s1.send(&test_msg).unwrap();
let mut recv_msg: HashMap<String, Event> = s2.recv().unwrap();
let mut test_msg_keys: Vec<_> = test_msg.keys().collect();
test_msg_keys.sort();
let mut recv_msg_keys: Vec<_> = recv_msg.keys().collect();
recv_msg_keys.sort();
assert_eq!(test_msg_keys, recv_msg_keys);
for (key, test_event) in test_msg {
let recv_event = recv_msg.remove(&key).unwrap();
test_event_pair(test_event, recv_event);
}
}

49
base/tests/unix/main.rs Normal file
View file

@ -0,0 +1,49 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#![cfg(unix)]
use std::path::Path;
use base::safe_descriptor_from_path;
use base::Error;
use libc::EBADF;
use libc::EINVAL;
/// Runs all unix specific integration tests in a single binary.
mod net;
mod scoped_signal_handler;
mod syslog;
mod tube;
#[test]
fn safe_descriptor_from_path_valid() {
assert!(safe_descriptor_from_path(Path::new("/proc/self/fd/2"))
.unwrap()
.is_some());
}
#[test]
fn safe_descriptor_from_path_invalid_integer() {
assert_eq!(
safe_descriptor_from_path(Path::new("/proc/self/fd/blah")),
Err(Error::new(EINVAL))
);
}
#[test]
fn safe_descriptor_from_path_invalid_fd() {
assert_eq!(
safe_descriptor_from_path(Path::new("/proc/self/fd/42")),
Err(Error::new(EBADF))
);
}
#[test]
fn safe_descriptor_from_path_none() {
assert_eq!(
safe_descriptor_from_path(Path::new("/something/else")).unwrap(),
None
);
}

237
base/tests/unix/net.rs Normal file
View file

@ -0,0 +1,237 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::env;
use std::io::ErrorKind;
use std::os::unix::io::AsRawFd;
use std::path::PathBuf;
use std::time::Duration;
use base::UnixSeqpacket;
use base::UnixSeqpacketListener;
use base::UnlinkUnixSeqpacketListener;
fn tmpdir() -> PathBuf {
env::temp_dir()
}
#[test]
fn unix_seqpacket_path_not_exists() {
let res = UnixSeqpacket::connect("/path/not/exists");
assert!(res.is_err());
}
#[test]
fn unix_seqpacket_listener_path() {
let mut socket_path = tmpdir();
socket_path.push("unix_seqpacket_listener_path");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
);
let listener_path = listener.path().expect("failed to get socket listener path");
assert_eq!(socket_path, listener_path);
}
#[test]
fn unix_seqpacket_listener_from_fd() {
let mut socket_path = tmpdir();
socket_path.push("unix_seqpacket_listener_from_fd");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
);
// UnixSeqpacketListener should succeed on a valid listening descriptor.
let good_dup = UnixSeqpacketListener::bind(&format!("/proc/self/fd/{}", unsafe {
libc::dup(listener.as_raw_fd())
}));
let good_dup_path = good_dup
.expect("failed to create dup UnixSeqpacketListener")
.path();
// Path of socket created by descriptor should be hidden.
assert!(good_dup_path.is_err());
// UnixSeqpacketListener must fail on an existing non-listener socket.
let s1 = UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
let bad_dup = UnixSeqpacketListener::bind(&format!("/proc/self/fd/{}", unsafe {
libc::dup(s1.as_raw_fd())
}));
assert!(bad_dup.is_err());
}
#[test]
fn unix_seqpacket_path_exists_pass() {
let mut socket_path = tmpdir();
socket_path.push("path_to_socket");
let _listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
);
let _res =
UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
}
#[test]
fn unix_seqpacket_path_listener_accept_with_timeout() {
let mut socket_path = tmpdir();
socket_path.push("path_listerner_accept_with_timeout");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
);
for d in [Duration::from_millis(10), Duration::ZERO] {
let _ = listener.accept_with_timeout(d).expect_err(&format!(
"UnixSeqpacket::accept_with_timeout {:?} connected",
d
));
let s1 = UnixSeqpacket::connect(socket_path.as_path())
.unwrap_or_else(|_| panic!("UnixSeqpacket::connect {:?} failed", d));
let s2 = listener
.accept_with_timeout(d)
.unwrap_or_else(|_| panic!("UnixSeqpacket::accept {:?} failed", d));
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
}
#[test]
fn unix_seqpacket_path_listener_accept() {
let mut socket_path = tmpdir();
socket_path.push("path_listerner_accept");
let listener = UnlinkUnixSeqpacketListener(
UnixSeqpacketListener::bind(&socket_path).expect("failed to create UnixSeqpacketListener"),
);
let s1 = UnixSeqpacket::connect(socket_path.as_path()).expect("UnixSeqpacket::connect failed");
let s2 = listener.accept().expect("UnixSeqpacket::accept failed");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
#[test]
fn unix_seqpacket_zero_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
// Timeouts less than a microsecond are too small and round to zero.
s1.set_read_timeout(Some(Duration::from_nanos(10)))
.expect_err("successfully set zero timeout");
}
#[test]
fn unix_seqpacket_read_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
s1.set_read_timeout(Some(Duration::from_millis(1)))
.expect("failed to set read timeout for socket");
let _ = s1.recv(&mut [0]);
}
#[test]
fn unix_seqpacket_write_timeout() {
let (s1, _s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
s1.set_write_timeout(Some(Duration::from_millis(1)))
.expect("failed to set write timeout for socket");
}
#[test]
fn unix_seqpacket_send_recv() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14];
s2.send(data2).expect("failed to send data2");
s1.send(data1).expect("failed to send data1");
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(data1, recv_data);
s1.recv(recv_data).expect("failed to recv data");
assert_eq!(data2, recv_data);
}
#[test]
fn unix_seqpacket_send_fragments() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
let data2 = &[10, 11, 12, 13, 14, 15, 16];
s1.send(data1).expect("failed to send data1");
s1.send(data2).expect("failed to send data2");
let recv_data = &mut [0; 32];
let size = s2.recv(recv_data).expect("failed to recv data");
assert_eq!(size, data1.len());
assert_eq!(data1, &recv_data[0..size]);
let size = s2.recv(recv_data).expect("failed to recv data");
assert_eq!(size, data2.len());
assert_eq!(data2, &recv_data[0..size]);
}
#[test]
fn unix_seqpacket_get_readable_bytes() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), 0);
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), data1.len());
let recv_data = &mut [0; 5];
s2.recv(recv_data).expect("failed to recv data");
assert_eq!(s1.get_readable_bytes().unwrap(), 0);
assert_eq!(s2.get_readable_bytes().unwrap(), 0);
}
#[test]
fn unix_seqpacket_next_packet_size() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
assert_eq!(s2.next_packet_size().unwrap(), 5);
s1.set_read_timeout(Some(Duration::from_micros(1)))
.expect("failed to set read timeout");
assert_eq!(
s1.next_packet_size().unwrap_err().kind(),
ErrorKind::WouldBlock
);
drop(s2);
assert_eq!(
s1.next_packet_size().unwrap_err().kind(),
ErrorKind::ConnectionReset
);
}
#[test]
fn unix_seqpacket_recv_to_vec() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
let recv_data = &mut vec![];
s2.recv_to_vec(recv_data).expect("failed to recv data");
assert_eq!(recv_data, &mut vec![0, 1, 2, 3, 4]);
}
#[test]
fn unix_seqpacket_recv_as_vec() {
let (s1, s2) = UnixSeqpacket::pair().expect("failed to create socket pair");
let data1 = &[0, 1, 2, 3, 4];
s1.send(data1).expect("failed to send data");
let recv_data = s2.recv_as_vec().expect("failed to recv data");
assert_eq!(recv_data, vec![0, 1, 2, 3, 4]);
}

View file

@ -0,0 +1,254 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::mem::zeroed;
use std::ptr::null;
use std::ptr::null_mut;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::AtomicUsize;
use std::sync::atomic::Ordering;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
use std::sync::Once;
use std::thread::sleep;
use std::thread::spawn;
use std::time::Duration;
use std::time::Instant;
use base::platform::scoped_signal_handler::Error;
use base::platform::scoped_signal_handler::Result;
use base::platform::Error as ErrnoError;
use base::sys::ScopedSignalHandler;
use base::sys::Signal;
use base::sys::clear_signal_handler;
use base::sys::has_default_signal_handler;
use base::sys::wait_for_interrupt;
use base::sys::SignalHandler;
use libc::sigaction;
use base::platform::gettid;
use base::platform::kill;
use base::platform::Pid;
const TEST_SIGNAL: Signal = Signal::User1;
const TEST_SIGNALS: &[Signal] = &[Signal::User1, Signal::User2];
static TEST_SIGNAL_COUNTER: AtomicUsize = AtomicUsize::new(0);
/// Only allows one test case to execute at a time.
fn get_mutex() -> MutexGuard<'static, ()> {
static INIT: Once = Once::new();
static mut VAL: Option<Arc<Mutex<()>>> = None;
INIT.call_once(|| {
let val = Some(Arc::new(Mutex::new(())));
// Safe because the mutation is protected by the Once.
unsafe { VAL = val }
});
// Safe mutation only happens in the Once.
unsafe { VAL.as_ref() }.unwrap().lock().unwrap()
}
fn reset_counter() {
TEST_SIGNAL_COUNTER.swap(0, Ordering::SeqCst);
}
fn get_sigaction(signal: Signal) -> Result<sigaction> {
// Safe because sigaction is owned and expected to be initialized ot zeros.
let mut sigact: sigaction = unsafe { zeroed() };
if unsafe { sigaction(signal.into(), null(), &mut sigact) } < 0 {
Err(Error::Sigaction(signal, ErrnoError::last()))
} else {
Ok(sigact)
}
}
/// Safety:
/// This is only safe if the signal handler set in sigaction is safe.
unsafe fn restore_sigaction(signal: Signal, sigact: sigaction) -> Result<sigaction> {
if sigaction(signal.into(), &sigact, null_mut()) < 0 {
Err(Error::Sigaction(signal, ErrnoError::last()))
} else {
Ok(sigact)
}
}
/// Safety:
/// Safe if the signal handler for Signal::User1 is safe.
unsafe fn send_test_signal() {
kill(gettid(), Signal::User1.into()).unwrap()
}
macro_rules! assert_counter_eq {
($compare_to:expr) => {{
let expected: usize = $compare_to;
let got: usize = TEST_SIGNAL_COUNTER.load(Ordering::SeqCst);
if got != expected {
panic!(
"wrong signal counter value: got {}; expected {}",
got, expected
);
}
}};
}
struct TestHandler;
/// # Safety
/// Safe because handle_signal is async-signal safe.
unsafe impl SignalHandler for TestHandler {
fn handle_signal(signal: Signal) {
if TEST_SIGNAL == signal {
TEST_SIGNAL_COUNTER.fetch_add(1, Ordering::SeqCst);
}
}
}
#[test]
fn scopedsignalhandler_success() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
reset_counter();
assert_counter_eq!(0);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
let handler = ScopedSignalHandler::new::<TestHandler>(&[TEST_SIGNAL]).unwrap();
assert!(!has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because test_handler is safe.
unsafe { send_test_signal() };
// Give the handler time to run in case it is on a different thread.
for _ in 1..40 {
if TEST_SIGNAL_COUNTER.load(Ordering::SeqCst) > 0 {
break;
}
sleep(Duration::from_millis(250));
}
assert_counter_eq!(1);
drop(handler);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
}
#[test]
fn scopedsignalhandler_handleralreadyset() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
reset_counter();
assert_counter_eq!(0);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because TestHandler is async-signal safe.
let handler = ScopedSignalHandler::new::<TestHandler>(&[TEST_SIGNAL]).unwrap();
assert!(!has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
// Safe because TestHandler is async-signal safe.
assert!(matches!(
ScopedSignalHandler::new::<TestHandler>(TEST_SIGNALS),
Err(Error::HandlerAlreadySet(Signal::User1))
));
assert_counter_eq!(0);
drop(handler);
assert!(has_default_signal_handler(TEST_SIGNAL.into()).unwrap());
}
/// Stores the thread used by WaitForInterruptHandler.
static WAIT_FOR_INTERRUPT_THREAD_ID: AtomicI32 = AtomicI32::new(0);
/// Forwards SIGINT to the appropriate thread.
struct WaitForInterruptHandler;
/// # Safety
/// Safe because handle_signal is async-signal safe.
unsafe impl SignalHandler for WaitForInterruptHandler {
fn handle_signal(_: Signal) {
let tid = WAIT_FOR_INTERRUPT_THREAD_ID.load(Ordering::SeqCst);
// If the thread ID is set and executed on the wrong thread, forward the signal.
if tid != 0 && gettid() != tid {
// Safe because the handler is safe and the target thread id is expecting the signal.
unsafe { kill(tid, Signal::Interrupt.into()) }.unwrap();
}
}
}
/// Query /proc/${tid}/status for its State and check if it is either S (sleeping) or in
/// D (disk sleep).
fn thread_is_sleeping(tid: Pid) -> std::result::Result<bool, ErrnoError> {
const PREFIX: &str = "State:";
let mut status_reader = BufReader::new(File::open(format!("/proc/{}/status", tid))?);
let mut line = String::new();
loop {
let count = status_reader.read_line(&mut line)?;
if count == 0 {
return Err(ErrnoError::new(libc::EIO));
}
if let Some(stripped) = line.strip_prefix(PREFIX) {
return Ok(matches!(
stripped.trim_start().chars().next(),
Some('S') | Some('D')
));
}
line.clear();
}
}
/// Wait for a process to block either in a sleeping or disk sleep state.
fn wait_for_thread_to_sleep(tid: Pid, timeout: Duration) -> std::result::Result<(), ErrnoError> {
let start = Instant::now();
loop {
if thread_is_sleeping(tid)? {
return Ok(());
}
if start.elapsed() > timeout {
return Err(ErrnoError::new(libc::EAGAIN));
}
sleep(Duration::from_millis(50));
}
}
#[test]
fn waitforinterrupt_success() {
// Prevent other test cases from running concurrently since the signal
// handlers are shared for the process.
let _guard = get_mutex();
let to_restore = get_sigaction(Signal::Interrupt).unwrap();
clear_signal_handler(Signal::Interrupt.into()).unwrap();
// Safe because TestHandler is async-signal safe.
let handler =
ScopedSignalHandler::new::<WaitForInterruptHandler>(&[Signal::Interrupt]).unwrap();
let tid = gettid();
WAIT_FOR_INTERRUPT_THREAD_ID.store(tid, Ordering::SeqCst);
let join_handle = spawn(move || -> std::result::Result<(), ErrnoError> {
// Wait unitl the thread is ready to receive the signal.
wait_for_thread_to_sleep(tid, Duration::from_secs(10)).unwrap();
// Safe because the SIGINT handler is safe.
unsafe { kill(tid, Signal::Interrupt.into()) }
});
let wait_ret = wait_for_interrupt();
let join_ret = join_handle.join();
drop(handler);
// Safe because we are restoring the previous SIGINT handler.
unsafe { restore_sigaction(Signal::Interrupt, to_restore) }.unwrap();
wait_ret.unwrap();
join_ret.unwrap().unwrap();
}

66
base/tests/unix/syslog.rs Normal file
View file

@ -0,0 +1,66 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::io::Read;
use std::io::Seek;
use std::io::SeekFrom;
use std::sync::Once;
use base::syslog::*;
static EARLY_INIT_ONCE: Once = Once::new();
pub fn setup() {
EARLY_INIT_ONCE.call_once(|| {
early_init();
});
}
#[test]
fn fds() {
setup();
let mut fds = Vec::new();
push_descriptors(&mut fds);
assert!(!fds.is_empty());
for fd in fds {
assert!(fd >= 0);
}
}
#[test]
fn syslog_file() {
setup();
let mut file = tempfile::tempfile().expect("failed to create tempfile");
let syslog_file = file.try_clone().expect("error cloning shared memory file");
let state = State::new(LogConfig {
pipe: Some(Box::new(syslog_file)),
..Default::default()
})
.unwrap();
const TEST_STR: &str = "hello shared memory file";
state.log(
&log::RecordBuilder::new()
.level(Level::Error)
.args(format_args!("{}", TEST_STR))
.build(),
);
file.seek(SeekFrom::Start(0))
.expect("error seeking shared memory file");
let mut buf = String::new();
file.read_to_string(&mut buf)
.expect("error reading shared memory file");
assert!(buf.contains(TEST_STR));
}
#[test]
fn macros() {
setup();
log::error!("this is an error {}", 3);
log::warn!("this is a warning {}", "uh oh");
log::info!("this is info {}", true);
log::debug!("this is debug info {:?}", Some("helpful stuff"));
}

96
base/tests/unix/tube.rs Normal file
View file

@ -0,0 +1,96 @@
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use std::time;
use base::deserialize_with_descriptors;
use base::BlockingMode;
use base::EventContext;
use base::EventToken;
use base::FramingMode;
use base::FromRawDescriptor;
use base::ReadNotifier;
use base::SafeDescriptor;
use base::SerializeDescriptors;
use base::StreamChannel;
use base::Tube;
use base::UnixSeqpacket;
#[derive(EventToken, Debug, Eq, PartialEq, Copy, Clone)]
enum Token {
ReceivedData,
}
const EVENT_WAIT_TIME: time::Duration = time::Duration::from_secs(10);
#[test]
fn test_serialize_tube_new() {
let (sock_send, sock_recv) =
StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Message).unwrap();
let tube_send = Tube::new(sock_send).unwrap();
let tube_recv = Tube::new(sock_recv).unwrap();
// Serialize the Tube
let msg_serialize = SerializeDescriptors::new(&tube_send);
let serialized = serde_json::to_vec(&msg_serialize).unwrap();
let msg_descriptors = msg_serialize.into_descriptors();
// Deserialize the Tube
let mut msg_descriptors_safe = msg_descriptors
.into_iter()
.map(|v| Some(unsafe { SafeDescriptor::from_raw_descriptor(v) }))
.collect();
let tube_deserialized: Tube = deserialize_with_descriptors(
|| serde_json::from_slice(&serialized),
&mut msg_descriptors_safe,
)
.unwrap();
// Send a message through deserialized Tube
tube_deserialized.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let event_ctx: EventContext<Token> =
EventContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)]).unwrap();
let events = event_ctx.wait_timeout(EVENT_WAIT_TIME).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn test_send_recv_new_from_seqpacket() {
let (sock_send, sock_recv) = UnixSeqpacket::pair().unwrap();
let tube_send = Tube::new_from_unix_seqpacket(sock_send);
let tube_recv = Tube::new_from_unix_seqpacket(sock_recv);
tube_send.send(&"hi".to_string()).unwrap();
// Wait for the message to arrive
let event_ctx: EventContext<Token> =
EventContext::build_with(&[(tube_recv.get_read_notifier(), Token::ReceivedData)]).unwrap();
let events = event_ctx.wait_timeout(EVENT_WAIT_TIME).unwrap();
let tokens: Vec<Token> = events
.iter()
.filter(|e| e.is_readable)
.map(|e| e.token)
.collect();
assert_eq!(tokens, vec! {Token::ReceivedData});
assert_eq!(tube_recv.recv::<String>().unwrap(), "hi");
}
#[test]
fn test_tube_new_byte_mode_error() {
let (sock_byte_mode, _) =
StreamChannel::pair(BlockingMode::Nonblocking, FramingMode::Byte).unwrap();
let tube_error = Tube::new(sock_byte_mode);
assert!(tube_error.is_err());
}

View file

@ -78,7 +78,6 @@ WIN64_DISABLED_CRATES = [
]
CRATE_OPTIONS: Dict[str, List[TestOption]] = {
"base": [TestOption.SINGLE_THREADED, TestOption.LARGE],
"cros_async": [TestOption.LARGE, TestOption.RUN_EXCLUSIVE],
"crosvm": [TestOption.SINGLE_THREADED, TestOption.UNIT_AS_INTEGRATION_TEST],
"crosvm_plugin": [

View file

@ -15,7 +15,7 @@ from pathlib import Path
from typing import Dict, Iterable, List, NamedTuple, Optional
from . import test_target, testvm
from .common import all_tracked_files
from .common import all_tracked_files, very_verbose
from .test_config import BUILD_FEATURES, CRATE_OPTIONS, TestOption
from .test_target import TestTarget, Triple
@ -262,10 +262,11 @@ def build_all_binaries(target: TestTarget, crosvm_direct: bool, instrument_cover
cargo_args = [
"--features=" + features,
f"--target={target.build_triple}",
"--verbose",
"--workspace",
*[f"--exclude={crate}" for crate in get_workspace_excludes(target.build_triple)],
]
if very_verbose():
cargo_args.append("--verbose")
cargo_args.extend(extra_args)
yield from cargo_build_executables(
@ -291,7 +292,13 @@ def get_test_timeout(target: TestTarget, executable: Executable):
return timeout * EMULATION_TIMEOUT_MULTIPLIER
def execute_test(target: TestTarget, attempts: int, collect_coverage: bool, executable: Executable):
def execute_test(
target: TestTarget,
attempts: int,
collect_coverage: bool,
integration_test: bool,
executable: Executable,
):
"""
Executes a single test on the given test targed
@ -301,7 +308,7 @@ def execute_test(target: TestTarget, attempts: int, collect_coverage: bool, exec
"""
options = CRATE_OPTIONS.get(executable.crate_name, [])
args: List[str] = []
if TestOption.SINGLE_THREADED in options:
if TestOption.SINGLE_THREADED in options or integration_test:
args += ["--test-threads=1"]
binary_path = executable.binary_path
@ -400,7 +407,9 @@ def execute_all(
sys.stdout.flush()
with Pool(PARALLELISM) as pool:
for result in pool.imap(
functools.partial(execute_test, unit_test_target, attempts, collect_coverage),
functools.partial(
execute_test, unit_test_target, attempts, collect_coverage, False
),
unit_tests,
):
print_test_progress(result)
@ -416,7 +425,9 @@ def execute_all(
)
sys.stdout.flush()
for executable in integration_tests:
result = execute_test(integration_test_target, attempts, collect_coverage, executable)
result = execute_test(
integration_test_target, attempts, collect_coverage, True, executable
)
print_test_progress(result)
yield result
print()