109 lines
3.6 KiB
Rust
109 lines
3.6 KiB
Rust
//! # Keyforkd Test Utilities
|
|
//!
|
|
//! This module adds a helper to set up a Tokio runtime, start a Tokio runtime with a given seed,
|
|
//! start a Keyfork server on that runtime, and run a given test closure.
|
|
|
|
use crate::{middleware, Keyforkd, ServiceBuilder, UnixServer};
|
|
|
|
use tokio::runtime::Builder;
|
|
|
|
use keyfork_bug::bug;
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
#[error("This error can never be instantiated")]
|
|
#[doc(hidden)]
|
|
pub struct InfallibleError {
|
|
protected: (),
|
|
}
|
|
|
|
/// An infallible result. This type can be used to represent a function that should never error.
|
|
///
|
|
/// ```rust
|
|
/// use keyforkd::test_util::Infallible;
|
|
/// let closure = || {
|
|
/// Infallible::Ok(())
|
|
/// };
|
|
/// assert!(closure().is_ok());
|
|
/// ```
|
|
pub type Infallible<T> = std::result::Result<T, InfallibleError>;
|
|
|
|
/// Run a test making use of a Keyforkd server. The test may use a seed (the first argument) from a
|
|
/// test suite, or (as shown in the example below) a simple seed may be used solely to ensure
|
|
/// the server is capable of being interacted with. The test is in the form of a closure, expected
|
|
/// to return a [`Result`] where success is a unit type (test passed) and the error is any error
|
|
/// that happened during the test (alternatively, a panic may be used, and will be returned as an
|
|
/// error).
|
|
///
|
|
/// # Panics
|
|
/// The function may panic if any errors arise while configuring and using the Tokio multithreaded
|
|
/// runtime.
|
|
///
|
|
/// # Examples
|
|
/// ```rust
|
|
/// use std::os::unix::net::UnixStream;
|
|
/// let seed = b"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
|
|
/// keyforkd::test_util::run_test(seed.as_slice(), |path| {
|
|
/// UnixStream::connect(&path).map(|_| ())
|
|
/// }).unwrap();
|
|
/// ```
|
|
#[allow(clippy::missing_errors_doc)]
|
|
pub fn run_test<F, E>(seed: &[u8], closure: F) -> Result<(), E>
|
|
where
|
|
F: FnOnce(&std::path::Path) -> Result<(), E> + Send + 'static,
|
|
E: Send + 'static,
|
|
{
|
|
let rt = Builder::new_multi_thread()
|
|
.worker_threads(2)
|
|
.enable_io()
|
|
.build()
|
|
.expect(bug!(
|
|
"can't make tokio threaded IO runtime, should be enabled via feature flags"
|
|
));
|
|
let socket_dir = tempfile::tempdir().expect(bug!("can't create tempdir"));
|
|
let socket_path = socket_dir.path().join("keyforkd.sock");
|
|
let result = rt.block_on(async move {
|
|
let (tx, mut rx) = tokio::sync::mpsc::channel(1);
|
|
let server_handle = tokio::spawn({
|
|
let socket_path = socket_path.clone();
|
|
let seed = seed.to_vec();
|
|
async move {
|
|
let mut server =
|
|
UnixServer::bind(&socket_path).expect(bug!("can't bind unix socket"));
|
|
tx.send(())
|
|
.await
|
|
.expect(bug!("couldn't send server start signal"));
|
|
let service = ServiceBuilder::new()
|
|
.layer(middleware::BincodeLayer::new())
|
|
.service(Keyforkd::new(seed.to_vec()));
|
|
server.run(service).await.expect(bug!("Unable to start service"));
|
|
}
|
|
});
|
|
|
|
rx.recv()
|
|
.await
|
|
.expect(bug!("can't receive server start signal from channel"));
|
|
let test_handle = tokio::task::spawn_blocking(move || closure(&socket_path));
|
|
|
|
let result = test_handle.await;
|
|
server_handle.abort();
|
|
result
|
|
});
|
|
if let Err(e) = result {
|
|
if e.is_panic() {
|
|
std::panic::resume_unwind(e.into_panic());
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_run_test() {
|
|
let seed = b"beefbeef";
|
|
run_test(seed, |_path| Infallible::Ok(())).expect("infallible");
|
|
}
|
|
}
|