rutenspitz

Crates.iorutenspitz
lib.rsrutenspitz
version0.3.0
sourcesrc
created_at2020-07-20 15:57:32.483969
updated_at2022-03-12 20:45:16.521063
descriptionА procedural macro to be used for testing/fuzzing stateful models against a semantically equivalent but obviously correct implementation
homepage
repositoryhttps://github.com/jakubadamw/rutenspitz
max_upload_size
id267287
size6,748
Jakub Wieczorek (jakubadamw)

documentation

README

rutenspitz

NOTE: This crate was previously called arbitrary-model-tests.

Build status crates.io License: MIT

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 {
        if op_name == "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());
    }
}

Debugging

See this guide.

Commit count: 76

cargo fmt