3 个版本

0.0.2-alpha.02019年10月22日
0.0.1 2019年10月12日
0.0.1-alpha.02019年10月22日

71#确定性

MIT 许可证

70KB
1.5K SLoC

模拟

模拟的目标是提供一组低级组件,这些组件可以用于编写适用于 FoundationDB 风格模拟测试 的应用程序。

模拟是 Tokio 的一种抽象,允许应用程序开发者编写对非确定性源通用的应用程序。此外,模拟还提供了时间、调度、网络和 IO 的确定性模拟。

调度和时间

模拟提供了一个模拟时间源。模拟时间仅在执行者没有更多工作可做时才会前进。这可以用来强制任务执行的确定性重排序。

当时间前进时,它会立即前进到一个允许执行者取得进展的值。依赖于超时的应用程序可以在测试特定执行顺序所需时间的几分之一内进行测试。

网络

模拟包含一个内存网络。应用程序可以使用 Environment::bindEnvironment::connect 在组件之间创建内存连接。这些内存连接将自动注入延迟和断开故障,取决于初始种子值。

故障

基于可初始化的随机数生成器注入故障,导致 IO 延迟和断开。这足以触发高级组件(如消息重排序)中的错误。

通过消除非确定性源,并将故障注入基于可初始化的随机数生成器,可以在几秒钟内运行数以千计的测试,并具有不同的故障注入。这允许测试不同的执行顺序。如果特定的种子导致失败的执行顺序,开发者可以使用种子值来调试和修复他们的应用程序。

一旦错误被修复,种子值可以用来设置回归测试,以确保问题保持修复。

示例

   use simulation::{Environment, TcpListener};
   use futures::{SinkExt, StreamExt};
   use std::{io, net, time};
   use tokio::codec::{Framed, LinesCodec};

   /// Start a client request handler which will write greetings to clients.
   async fn handle<E>(env: E, socket: <E::TcpListener as TcpListener>::Stream, addr: net::SocketAddr)
   where
       E: Environment,
   {
       // delay the response, in deterministic mode this will immediately progress time.
       env.delay_from(time::Duration::from_secs(1));
       println!("handling connection from {:?}", addr);
       let mut transport = Framed::new(socket, LinesCodec::new());
       if let Err(e) = transport.send(String::from("Hello World!")).await {
           println!("failed to send response: {:?}", e);
       }
   }

   /// Start a server which will bind to the provided addr and repyl to clients.
   async fn server<E>(env: E, addr: net::SocketAddr) -> Result<(), io::Error>
   where
       E: Environment,
   {
       let mut listener = env.bind(addr).await?;

       while let Ok((socket, addr)) = listener.accept().await {
           let request = handle(env.clone(), socket, addr);
           env.spawn(request)
       }
       Ok(())
   }


   /// Create a client which will read a message from the server
   async fn client<E>(env: E, addr: net::SocketAddr) -> Result<(), io::Error>
   where
       E: Environment,
   {
       loop {
           match env.connect(addr).await {
               Err(_) => {
                   // Sleep if the connection was rejected, retrying later.
                   // In deterministic mode, this will just reorder task execution
                   // without waiting for time to advance.
                   env.delay_from(time::Duration::from_secs(1)).await;
                   continue;
               }
               Ok(conn) => {
                   let mut transport = Framed::new(conn, LinesCodec::new());
                   let result = transport.next().await.unwrap().unwrap();
                   assert_eq!(result, "Hello World!");
                   return Ok(());
               }
           }
       }
   }
   #[test]
   fn test() {
       // This particular example will fail with a seed value of 22 due to not handling disconnects.
       let mut runtime = simulation::deterministic::DeterministicRuntime::new_with_seed(1).unwrap();
       let handle = runtime.handle();
       runtime.block_on(async {
           let bind_addr: net::SocketAddr = "127.0.0.1:8080".parse().unwrap();
           let server = server(handle.clone(), bind_addr);
           handle.spawn(async move {
               server.await.unwrap();
           });
           client(handle, bind_addr).await.unwrap();
       })
   }

许可证: MIT

依赖项

~9.5MB
~162K SLoC