Rust网络模拟库tor-rtmock的使用:Tor运行时模拟与异步网络测试工具

Rust网络模拟库tor-rtmock的使用:Tor运行时模拟与异步网络测试工具

tor-rtmock概述

tor-rtmock是一个支持对tor-rtcompat异步运行时进行模拟的库。它提供了可以部分或完全覆盖Runtime特性的模拟实现,主要用于测试目的。

这个库是Arti项目的一部分,Arti是一个用Rust实现Tor的项目。它被用于为Arti中依赖异步运行时的高级crate编写测试。

主要功能

  • 模拟时间流逝(通过SimpleMockTimeProviderMockExecutor
  • 模拟网络连接(通过MockNetRuntime
  • 仅用于测试目的

综合示例

以下是一个完整的示例,展示了如何使用tor-rtmock来测试一个依赖网络连接和超时处理的函数:

# #[cfg(miri)] // miri cannot do CLOCK_REALTIME
# return;
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 futures::StreamExt as _;
use std::io::ErrorKind;
use tor_rtmock::{MockRuntime, /*MockNetRuntime,*/ net::MockNetwork};
use tor_rtcompat::{NetStreamProvider as _, NetStreamListener as _};

// 要测试的代码:
// 连接到addr,发送问候信息,并返回服务器的响应
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?
}

// 在测试模块中:
MockRuntime::test_with_various(|rt| async move {
    // 提供的`rt`有一个空的模拟网络
    // 我们将其包装在测试中使用的非空网络视图上
    let fake_internet = MockNetwork::new();

    // 创建一个假装我们在服务器地址的视图
    let sip: IpAddr = "198.51.100.99".parse().unwrap();
    let srt = fake_internet.builder().add_address(sip).runtime(rt.clone());

    // 创建一个假装我们在客户端地址的视图
    let cip: IpAddr = "198.51.100.7".parse().unwrap();
    let crt = fake_internet.builder().add_address(cip).runtime(rt.clone());

    // 辅助函数:生成一个任务来执行`converse`并报告其结果
    // 返回一个oneshot::Receiver,当`converse`返回时变为ready
    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; // 让它运行到尽可能远的地方
    assert!(ret_fut.t极速赛车开奖结果历史
    assert!(poll!(&mut ret_fut).is_pending()); // 替代检查,适用于任何future
    rt.advance_by(Duration::from_secs(4)).await; // 运行4秒,<超时
    assert!(ret_fut.try_recv().unwrap().is_none()); // 它仍然没有完成
    rt.advance_by(Duration::from_secs(1)).await; // 再运行1秒,达到超时
    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 incoming_streams = listener.incoming();
    let mut ret_fut = spawn_test(saddr);
    let (mut conn, caddr) = incoming_streams.next().await.unwrap().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);
});

完整示例代码解释

这个示例展示了如何测试一个网络连接函数converse,该函数会:

  1. 连接到指定地址
  2. 发送"Hello world!\r\n"消息
  3. 读取服务器响应
  4. 在5秒内超时

测试分为三个场景:

  1. 测试无服务器监听的情况(应返回连接拒绝错误)
  2. 测试有服务器监听但不响应的情况(应超时)
  3. 测试正常工作流程(成功连接并交换数据)

通过MockRuntimeMockNetwork,我们能够:

  • 完全控制时间流逝(无需实际等待)
  • 模拟网络连接(无需实际网络)
  • 验证各种边界条件

安装

将以下内容添加到你的Cargo.toml中:

tor-rtmock = "0.32.0"

许可证

MIT OR Apache-2.0


1 回复

Rust网络模拟库tor-rtmock的使用:Tor运行时模拟与异步网络测试工具

介绍

tor-rtmock 是一个用于模拟 Tor 网络运行时的 Rust 库,主要用于异步网络测试场景。它允许开发者在测试环境中模拟 Tor 网络行为,而无需连接到真实的 Tor 网络,特别适合需要隔离网络环境的单元测试和集成测试。

这个库是 Arti 项目(Tor 的 Rust 实现)的一部分,提供了对 Tor 运行时组件的模拟实现,包括模拟网络连接、模拟目录服务等。

主要特性

  • 模拟 Tor 网络连接行为
  • 支持异步测试环境
  • 可配置的模拟网络行为
  • async-stdtokio 兼容
  • 提供测试断言工具

完整示例代码

下面是一个完整的测试示例,展示了 tor-rtmock 的主要功能:

use tor_rtmock::{MockSleepRuntime, ConnBehavior, Delay};
use tor_rtcompat::TcpProvider;
use std::time::Duration;
use std::sync::Arc;

#[tokio::test]
async fn test_tor_network_simulation() {
    // 1. 创建模拟运行时
    let runtime = MockSleepRuntime::new();
    
    // 2. 模拟普通HTTP连接
    runtime.network().add_connection(
        "http.example.com:80",
        b"HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello Tor!",
    );
    
    // 3. 模拟延迟连接
    runtime.network().add_connection_with_delay(
        "slow.example.com:80",
        b"HTTP/1.1 200 OK\r\n\r\nDelayed response",
        Delay::new(Duration::from_secs(3)),
    );
    
    // 4. 模拟自定义行为连接
    let custom_behavior = ConnBehavior::new()
        .read_delay(Duration::from_millis(200))
        .read_data(b"Custom behavior data")
        .close_after(1);
        
    runtime.network().add_connection_with_behavior(
        "custom.example.com:80",
        Arc::new(custom_behavior),
    );
    
    // 测试普通连接
    let mut http_conn = runtime.connect("http.example.com:80").await.unwrap();
    let mut http_buf = [0u8; 50];
    let http_len = http_conn.read(&mut http_buf).await.unwrap();
    assert_eq!(&http_buf[..http_len], b"HTTP/1.1 200 OK\r\nContent-Length: 11\r\n\r\nHello Tor!");
    
    // 测试超时处理
    let timeout_result = tokio::time::timeout(
        Duration::from_secs(1),
        runtime.connect("slow.example.com:80")
    ).await;
    assert!(timeout_result.is_err());
    
    // 测试自定义行为
    let mut custom_conn = runtime.connect("custom.example.com:80").await.unwrap();
    let mut custom_buf = [0u8; 50];
    let custom_len = custom_conn.read(&mut custom_buf).await.unwrap();
    assert_eq!(&custom_buf[..custom_len], b"Custom behavior data");
    
    // 验证网络调用
    let calls = runtime.network().check_calls();
    assert!(calls.contains(&"http.example.com:80".to_string()));
    assert!(calls.contains(&"slow.example.com:80".to_string()));
    assert!(calls.contains(&"custom.example.com:80".to_string()));
}

测试技巧

  1. 隔离测试:每个测试用例应该使用独立的 MockSleepRuntime 实例
  2. 验证调用:可以使用 runtime.network().check_calls() 验证预期的网络调用是否发生
  3. 时间控制:模拟时间可以加速测试,避免真实等待
  4. 错误注入:可以轻松模拟各种网络错误情况

注意事项

  • 主要用于测试环境,不适合生产环境
  • 模拟行为需要手动配置,可能无法覆盖所有真实网络情况
  • 与真实 Tor 网络行为可能存在差异,关键功能仍需真实环境测试

通过 tor-rtmock,开发者可以高效地编写与 Tor 网络交互的测试代码,而无需依赖外部网络环境或真实的 Tor 连接。

回到顶部