Relation tester is a small testing utility for automatically checking the correctness of [Partial]Eq
, [Partial]Ord
, Hash
, and [DoubleEnded|Fused]Iterator
trait implementations. It's most useful when used in conjuction with quickcheck
or some other property-based testing framework.
Go to the docs!
Imagine a scenario where you have a type Foo
with a custom implementation of either PartialEq
, Eq
, PartialOrd
, or Ord
. By "custom" we mean hand-written as opposed to derived. The Rust compiler alone cannot verify the correctness of these implementations and thus it is up to you, the programmer, to uphold certain invariants about the specific binary relation that you're implementing. For example, if you implement PartialEq
for Foo
, you must guarantee that foo1 == foo2
implies foo2 == foo1
(symmetry).
Other traits such as Hash
and Iterator
mandate several invariants as well – some of which are very intuitive, and others which are not. It's especially common for less-than-perfect implementations of the std::iter
family of traits to introduce off-by-one bugs^1^3 among others.
The idea is, instead of keeping these invariants in your head whenever you go about manually implementing one of these traits in your codebase, you can add a Reltester check to your test suite and have a higher degree of confidence that your implementation is correct.
quickcheck
and proptest
. Calling the checkers on static, non-randomized values is possible but is less effective in catching bugs.Based on the traits that your type implements, call the appropriate checker(s):
reltester::eq
for Eq
;reltester::ord
for Ord
;reltester::partial_eq
for PartialEq
;reltester::partial_ord
for PartialOrd
;reltester::hash
for Hash
;reltester::iterator
for Iterator
;reltester::fused_iterator
for FusedIterator
;reltester::double_ended_iterator
for DoubleEndedIterator
;Some of these functions take multiple (two or three) values of the same type. This is because it takes up to three values to test some invariants.
Please refer to the documentation for more information. The reltester::invariants
module is available for more granular checks if you can't satisfy the type bounds of the main functions.
f32
(PartialEq
, PartialOrd
)```rust use reltester; use quickcheck_macros::quickcheck;
fn testf32(a: f32, b: f32, c: f32) -> bool {
// Let's check if f32
implements PartialEq
and PartialOrd
correctly
// (spoiler: it does).
reltester::partialeq(&a, &b, &c).isok()
&& reltester::partialord(&a, &b, &c).is_ok()
}
```
u32
(Hash
)```rust use reltester; use quickcheck_macros::quickcheck;
fn testu32(a: u32, b: u32) -> bool {
// Unlike f32
, u32
implements both Eq
and Hash
, which allows us to
// test Hash
invariants.
reltester::hash(&a, &b).isok()
}
```
Vec<u32>
(DoubleEndedIterator
, FusedIterator
, Iterator
)```rust use reltester; use quickcheck_macros::quickcheck;
fn testvecu32(nums: VecIterator
is implied and checked by both DoubleEndedIterator
and
// FusedIterator
.
reltester::doubleendediterator(nums.iter()).isok()
&& reltester::fusediterator(nums.iter()).is_ok()
}
```
Reltester is available under the terms of the MIT license.