Lines
100 %
Functions
Branches
//! Support for mocking with `tor-rtcompat` asynchronous runtimes.
//!
//! # Overview
//! The `tor-rtcompat` crate defines a `Runtime` trait that represents
//! most of the common functionality of . This crate provides mock
//! implementations that override a `Runtime`, in whole or in part,
//! for testing purposes.
//! This crate is part of
//! [Arti](https://gitlab.torproject.org/tpo/core/arti/), a project to
//! implement [Tor](https://www.torproject.org/) in Rust.
//! It is used to write tests for higher-level
//! crates in Arti that rely on asynchronous runtimes.
//! This crate should should only be used for writing tests.
//! Currently, we support mocking the passage of time (via
//! [`MockSleepRuntime`]), and impersonating the internet (via
//! [`MockNetRuntime`]).
//! # Examples
//! Suppose you've written a function that relies on making a
//! connection to the network and possibly timing out:
//! ```
//! use tor_rtcompat::{Runtime,SleepProviderExt};
//! use std::{net::SocketAddr, io::Result, time::Duration, io::Error};
//! use futures::io::AsyncWriteExt;
//! async fn say_hi(runtime: impl Runtime, addr: &SocketAddr) -> Result<()> {
//! let delay = Duration::new(5,0);
//! runtime.timeout(delay, async {
//! let mut conn = runtime.connect(addr).await?;
//! conn.write_all(b"Hello world!\r\n").await?;
//! conn.close().await?;
//! Ok::<_,Error>(())
//! }).await??;
//! Ok(())
//! }
//! But how should you test this function?
//! You might try connecting to a well-known website to test the
//! connection case, and to a well-known black hole to test the
//! timeout case... but that's a bit undesirable. Your tests might be
//! running in a container with no internet access; and even if they
//! aren't, it isn't so great for your tests to rely on the actual
//! state of the internet. Similarly, if you make your timeout too long,
//! your tests might block for a long time; but if your timeout is too short,
//! the tests might fail on a slow machine or on a slow network.
//! Or, you could solve both of these problems by using `tor-rtmock`
//! to replace the internet _and_ the passage of time. (Here we're only
//! replacing the internet.)
//! # use tor_rtcompat::{Runtime,SleepProviderExt};
//! # use std::{net::SocketAddr, io::Result, time::Duration, io::Error};
//! # use futures::io::AsyncWriteExt;
//! #
//! # async fn say_hi(runtime: impl Runtime, addr: &SocketAddr) -> Result<()> {
//! # let delay = Duration::new(5,0);
//! # runtime.timeout(delay, async {
//! # let mut conn = runtime.connect(addr).await?;
//! # conn.write_all(b"Hello world!\r\n").await?;
//! # conn.close().await?;
//! # dbg!("okay apparently");
//! # Ok::<_,Error>(())
//! # }).await??;
//! # Ok(())
//! # }
//! use tor_rtmock::{MockSleepRuntime,MockNetRuntime,net::MockNetwork};
//! use tor_rtcompat::{TcpProvider,TcpListener};
//! use futures::io::AsyncReadExt;
//! tor_rtcompat::test_with_all_runtimes!(|rt| async move {
//! let addr1 = "198.51.100.7".parse().unwrap();
//! let addr2 = "198.51.100.99".parse().unwrap();
//! let sockaddr = "198.51.100.99:101".parse().unwrap();
//! // Make a runtime that pretends that we are at the first address...
//! let fake_internet = MockNetwork::new();
//! let rt1 = fake_internet.builder().add_address(addr1).runtime(rt.clone());
//! // ...and one that pretends we're listening at the second address.
//! let rt2 = fake_internet.builder().add_address(addr2).runtime(rt);
//! let listener = rt2.listen(&sockaddr).await.unwrap();
//! // Now we can test our function!
//! let (result1,output) = futures::join!(
//! say_hi(rt1, &sockaddr),
//! async {
//! let (mut conn,addr) = listener.accept().await.unwrap();
//! assert_eq!(addr.ip(), addr1);
//! let mut output = Vec::new();
//! conn.read_to_end(&mut output).await.unwrap();
//! output
//! });
//! assert!(result1.is_ok());
//! assert_eq!(&output[..], b"Hello world!\r\n");
//! (TODO: Add an example for the timeout case.)
#![deny(missing_docs)]
#![warn(noop_method_call)]
#![deny(unreachable_pub)]
#![warn(clippy::all)]
#![deny(clippy::await_holding_lock)]
#![deny(clippy::cargo_common_metadata)]
#![deny(clippy::cast_lossless)]
#![deny(clippy::checked_conversions)]
#![warn(clippy::cognitive_complexity)]
#![deny(clippy::debug_assert_with_mut_call)]
#![deny(clippy::exhaustive_enums)]
#![deny(clippy::exhaustive_structs)]
#![deny(clippy::expl_impl_clone_on_copy)]
#![deny(clippy::fallible_impl_from)]
#![deny(clippy::implicit_clone)]
#![deny(clippy::large_stack_arrays)]
#![warn(clippy::manual_ok_or)]
#![deny(clippy::missing_docs_in_private_items)]
#![deny(clippy::missing_panics_doc)]
#![warn(clippy::needless_borrow)]
#![warn(clippy::needless_pass_by_value)]
#![warn(clippy::option_option)]
#![warn(clippy::rc_buffer)]
#![deny(clippy::ref_option_ref)]
#![warn(clippy::semicolon_if_nothing_returned)]
#![warn(clippy::trait_duplication_in_bounds)]
#![deny(clippy::unnecessary_wraps)]
#![warn(clippy::unseparated_literal_suffix)]
#![deny(clippy::unwrap_used)]
pub mod io;
pub mod net;
pub mod time;
mod net_runtime;
mod sleep_runtime;
pub use net_runtime::MockNetRuntime;
pub use sleep_runtime::MockSleepRuntime;