30 个版本 (18 个重大更改)
新版本 0.21.0 | 2024 年 8 月 1 日 |
---|---|
0.20.0 | 2024 年 6 月 27 日 |
0.19.0 | 2024 年 6 月 5 日 |
0.13.0 | 2024 年 2 月 5 日 |
0.0.0 | 2021 年 6 月 24 日 |
#70 在 异步 中
每月 3,605 次下载
用于 26 个 crate(10 个直接使用)
410KB
6.5K SLoC
tor-rtmock
支持使用 tor-rtcompat
异步运行时的模拟。
概述
tor-rtcompat
crate 定义了一个表示 . 的大多数公共功能的 Runtime
trait。此 crate 提供了模拟实现,用于测试目的,部分或全部覆盖 Runtime
。
此 crate 是 Arti 的一部分,Arti 是一个实现 Tor 的 Rust 项目。它用于编写依赖于异步运行时的 Arti 高级 crate 的测试。
此 crate 应仅用于编写测试。
编写测试的主要入口点是 MockRuntime
,尤其是 test_with_various
。
它支持模拟时间流逝(通过 SimpleMockTimeProvider
和 MockExecutor
),以及模拟互联网(通过 MockNetRuntime
)。
综合示例
假设您已经编写了一个依赖于连接到网络并可能超时的函数。
使用 tor-rtmock
,您可以测试此函数,替换互联网,和时间流逝。测试立即运行,即使测试超时也不会阻塞。并且它测试您的模拟服务器,而不进行任何实际的网络连接。
use tor_rtcompat::{Runtime, SleepProviderExt as _n};
use std::{io, net::{IpAddr, SocketAddr}, time::Duration};
use futures::{channel::oneshot, io::{AsyncReadExt as _, AsyncWriteExt as _}, poll};
use std::io::ErrorKind;
use tor_rtmock::{MockRuntime, /*MockNetRuntime,*/ net::MockNetwork};
use tor_rtcompat::{TcpProvider as _, TcpListener as _};
// Code to be tested:
/// Connects to `addr`, says hello, and returns whatever the server sent back
async fn converse(runtime: impl Runtime, addr: &SocketAddr) -> io::Result<Vec<u8>> {
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.flush().await?;
let mut response = vec![];
conn.read_to_end(&mut response).await?;
io::Result::Ok(response)
}).await?
}
// In test module:
MockRuntime::test_with_various(|rt| async move {
// The provided `rt` has an empty fake network.
// We wrap it up with views onto a nonempty one we're using for the test:
let fake_internet = MockNetwork::new();
// Make a view that pretends we're at the server address
let sip: IpAddr = "198.51.100.99".parse().unwrap();
let srt = fake_internet.builder().add_address(sip).runtime(rt.clone());
// Make a view that pretends we're at the client address
let cip: IpAddr = "198.51.100.7".parse().unwrap();
let crt = fake_internet.builder().add_address(cip).runtime(rt.clone());
// Helper to spawn a task to execute `converse` and report its results
//
// Returns a oneshot::Receiver that becomes ready when `converse` has returned
let spawn_test = |saddr| {
let (ret_tx, ret_rx) = oneshot::channel();
let crt = crt.clone();
rt.spawn_identified("function under test", async move {
let ret = converse(crt, &saddr).await;
ret_tx.send(ret).unwrap();
});
ret_rx
};
eprintln!("First test. Nothing is listening.");
let saddr = SocketAddr::new(sip, 1);
let ret = spawn_test(saddr).await.unwrap();
assert_eq!(ret.unwrap_err().kind(), ErrorKind::ConnectionRefused);
eprintln!("Second test. Listening, but no-one picks up the phone: timeout.");
let saddr = SocketAddr::new(sip, 2);
let listener = srt.listen(&saddr).await.unwrap();
let mut ret_fut = spawn_test(saddr);
rt.progress_until_stalled().await; // let it run as far as it can get
assert!(ret_fut.try_recv().unwrap().is_none()); // it hasn't completed right away
assert!(poll!(&mut ret_fut).is_pending()); // alternative check, works with any future
rt.advance_by(Duration::from_secs(4)).await; // run for 4 seconds, < timeout
assert!(ret_fut.try_recv().unwrap().is_none()); // it still hasn't completed
rt.advance_by(Duration::from_secs(1)).await; // run for 1 more, reaching timeout
let ret = ret_fut.try_recv().unwrap().unwrap();
assert_eq!(ret.unwrap_err().kind(), ErrorKind::TimedOut);
eprintln!("Third test. Working.");
let saddr = SocketAddr::new(sip, 3);
let listener = srt.listen(&saddr).await.unwrap();
let mut ret_fut = spawn_test(saddr);
let (mut conn, caddr) = listener.accept().await.unwrap();
eprintln!("listener accepted from {caddr:?}");
assert_eq!(caddr.ip(), cip);
let expect = b"Hello world!\r\n";
let mut output = vec![b'X'; expect.len()];
conn.read_exact(&mut output).await.unwrap();
eprintln!("listener received {output:?}");
assert_eq!(output, expect);
let reply_data = b"reply data";
conn.write(reply_data).await.unwrap();
conn.close().await.unwrap();
let ret = ret_fut.await.unwrap();
assert_eq!(ret.unwrap(), reply_data);
});
许可:MIT OR Apache-2.0
依赖关系
~12–21MB
~296K SLoC