#tor #arti #async #testing #async-write

tor-rtmock

测试 tor-rtcomapt 的模拟支持

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异步

Download history 260/week @ 2024-04-17 387/week @ 2024-04-24 308/week @ 2024-05-01 317/week @ 2024-05-08 232/week @ 2024-05-15 834/week @ 2024-05-22 538/week @ 2024-05-29 551/week @ 2024-06-05 1041/week @ 2024-06-12 1365/week @ 2024-06-19 1619/week @ 2024-06-26 538/week @ 2024-07-03 230/week @ 2024-07-10 818/week @ 2024-07-17 1023/week @ 2024-07-24 1494/week @ 2024-07-31

每月 3,605 次下载
用于 26 个 crate(10 个直接使用)

MIT/Apache

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

它支持模拟时间流逝(通过 SimpleMockTimeProviderMockExecutor),以及模拟互联网(通过 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