1 unstable release
0.1.0 | Jul 5, 2020 |
---|
#2185 in Procedural macros
20KB
383 lines
arbitrary-model-tests
This is an attempt at creating a convenient procedural macro to be used for testing stateful models (in particular, various kinds of data structures) against a trivial (but usually very inefficient) implementation that is semantically 100% equivalent to the target implementation but, in contrast, obviously correct. The purpose of the macro is to generate the boilerplate code for testing particular operations of the model so that the user-provided definition of the test for a given stateful structure becomes as succinct as possible.
This crate was inspired by the following works:
Example
See the HashMap
test for reference.
You can run it with cargo hfuzz
. First of all you'll need to install honggfuzz
along with its system dependencies. See this section for more details. When you're done, all you need to run the test:
cargo hfuzz run hash_map
DSL
This is the initial take at a DSL that describes the stateful model to be tested (std::collections::HashMap
in this case).
arbitrary_stateful_operations! {
model = ModelHashMap<K, V>,
tested = HashMap<K, V, BuildAHasher>,
type_parameters = <
K: Clone + Debug + Eq + Hash + Ord,
V: Clone + Debug + Eq + Ord
>,
methods {
equal {
fn clear(&mut self);
fn contains_key(&self, k: &K) -> bool;
fn get(&self, k: &K) -> Option<&V>;
fn get_key_value(&self, k: &K) -> Option<(&K, &V)>;
fn get_mut(&mut self, k: &K) -> Option<&mut V>;
fn insert(&mut self, k: K, v: V) -> Option<V>;
// Tested as invariants, so no longer needed.
// fn is_empty(&self) -> bool;
// fn len(&self) -> usize;
fn remove(&mut self, k: &K) -> Option<V>;
}
equal_with(sort_iterator) {
fn drain(&mut self) -> impl Iterator<Item = (K, V)>;
fn iter(&self) -> impl Iterator<Item = (&K, &V)>;
fn iter_mut(&self) -> impl Iterator<Item = (&K, &mut V)>;
fn keys(&self) -> impl Iterator<Item = &K>;
fn values(&self) -> impl Iterator<Item = &V>;
fn values_mut(&mut self) -> impl Iterator<Item = &mut V>;
}
}
pre {
let prev_capacity = tested.capacity();
}
post {
// A bit of a hack.
if &self == &Self::clear {
assert_eq!(tested.capacity(), prev_capacity);
}
assert!(tested.capacity() >= model.len());
assert_eq!(tested.is_empty(), model.is_empty());
assert_eq!(tested.len(), model.len());
}
}
Dependencies
~1.5MB
~38K SLoC