10 个版本
0.2.3 | 2024 年 1 月 30 日 |
---|---|
0.2.2 | 2024 年 1 月 16 日 |
0.1.5 | 2024 年 1 月 11 日 |
#191 in 并发
每月下载 74 次
48KB
1K SLoC
reord
以可重复的方式多线程运行您的测试。
此 crate 提供测试工具,以验证您的代码是否符合某些并发条件。
它将以随机顺序交错运行多个异步任务,以验证大多数执行路径是否验证了测试声明的属性。
此外,如果配置为,它可以在每次理论上的锁冲突后等待一段时间,如果锁被获取了两次则失败。这在尝试使用外部工具加锁或实现自定义锁时很有用。
为什么不选择 loom
?
loom
是一个完整的模型检查器,可以完全验证您的代码是否正确。如果您的代码可以由 loom
检查,那么请务必这样做,loom
在检查您的 crate 方面将比 reord
做得更好!
但是,loom
要求您将所有互斥访问替换为 loom
的互斥访问。这并不总是可能的,例如,当您有一些被 FFI 捕获的互斥锁时。或者更糟糕的是,这个 crate 被写出来的原因,当您试图验证 postgreSQL 事务是否以适当的锁定行为写入时。
reord
究竟做了什么?
reord
不会对您的代码进行模型检查。它只会运行一次,就像它在多线程中运行一样。然而,它会为您提供一个主要的东西:确定性的交错,这将允许您重现您的错误。
这使得 reord
适合在 fuzzer 中使用,以多次运行您的测试,并使用不同的代码路径。
如何使用 reord
?
您可以在生产代码中(并且应该)散布调用 reord
函数,因为它只有在设置了 reord/test
功能时才会编译为空操作。 reord
能够在任何 reord::point().await
调用处混合您的线程。请注意,调用任何 reord
函数隐式地意味着调用 reord::point
,除了由于缺少 async Drop
而释放锁的情况。
需要注意的是,reord
一次只能运行一个任务。所以如果您获取锁,必须意识到这一点,以避免一个任务在另一个任务持有的锁上阻塞。有两种表示锁的方法。一种是在获取锁之前放置 reord::Lock
,它表示获取了锁。它应该放在获取锁之前,并返回一个锁卫兵,应该像真实的锁卫兵一样保持,以便 reord
锁在真实锁解锁时解锁。
reord
还支持“模糊”锁:您不确定是否会锁定的锁。例如,在使用 postgresql 时,事务内部的多数查询将获取难以量化的锁。例如,它可能获取页级锁,这时基本上无法猜测哪些其他查询可能会受到影响。为了做到这一点,您应该在模糊锁之前放置一个 reord::maybe_lock().await
调用,并在之后放置一个 reord::point().await
调用。
reord
还支持检查您的锁实际上是否锁定您期望锁定的内容。这可能非常有用,如果您不信任您的锁实现(或您使用的外部锁),或者您如何使用 reord
仪器化了您的锁。为此,您应该设置适当的配置选项。
在任何情况下,您都应该在每次锁获取活动之后尝试设置一个 reord::point
,以便 reord
可以高效地执行检查。然而,您不必为了这个原因使代码变得丑陋,直到您从 reord
获得虚假的测试失败:在没有这样的 reord::point
的情况下,最糟糕的事情是 reord
虚假地认为锁正常工作,而实际上没有,或者任务仍在进行中时任务被阻塞。在任何情况下,这只会使测试稍微难以复制,但它们仍然比没有 reord
时更容易复制。因此,除非您确实需要它,否则您可能不应该在锁获取活动中仪器化 ?
调用。
然而,检查锁以及模糊锁意味着在继续之前等待一些延迟。因此,尽可能少地这样做,因为它会在实际有锁竞争时使测试(或更重要的是,模糊器)运行速度大大减慢。
最后,当您需要调试失败的测试时,您可以
- 获取执行失败的测试所使用的种子
- 使用相同的种子再次运行测试,但启用
reord/tracing
功能 - 在
TRACE
级别读取日志,以识别确切的交织模式。
reord
代码比较冗长,每次任务切换时都会通知您。它还提供了一个 INFO
级别的范围,即使是对您自己的日志,也能让您知道正在执行的任务。
局限性
鉴于便利性是一个目标,reord
使用全局变量来代理正在运行的测试信息。这意味着像 cargo test
这样的多线程测试框架不能与多个 reord
测试一起使用。当有使用 reord
的测试时,您应该使用 cargo nextest
。
依赖项
~2.8–4MB
~67K SLoC