Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Property Testing: test generator coverage #1054

Open
Tracked by #1045
doug-q opened this issue May 15, 2024 · 0 comments
Open
Tracked by #1045

Property Testing: test generator coverage #1054

doug-q opened this issue May 15, 2024 · 0 comments

Comments

@doug-q
Copy link
Collaborator

doug-q commented May 15, 2024

We do not have confidence in the behaviour of the Arbitrary strategies that we use. proptest has no functionality analagous to QuickChecks Analysing test case distribution which is used to classify examples and verify that all categories are adequately covered. I do think this is possible to achieve in rust(see below), we could publish this independently of hugr or perhaps upstream to proptest.

Prototype proptest coverage

This borrow-checks but is otherwise untested:

#[derive(Debug,Clone)]
struct CoverageStrategy<Inner: Strategy>(
    Inner,
    std::sync::mpsc::Sender<Option<Inner::Value>>
);

impl<Inner: Strategy> CoverageStrategy<Inner> {
    fn report(&self, v: Inner::Value) -> Result<(), Reason>{
        self.1.send(Some(v)).map_err(|e| e.to_string().into())
    }
}

impl<Inner: Strategy> Strategy for CoverageStrategy<Inner> {
    type Tree = Inner::Tree;
    type Value = Inner::Value;

    fn new_tree(&self, runner: &mut TestRunner) -> NewTree<Self> {
        let tree = self.0.new_tree(runner)?;
        self.report(ValueTree::current(&tree))?;
        Ok(tree)
    }
}

pub fn coverage<V: std::fmt::Debug + Sync + Send + 'static>(expected: HashMap<String,f64>, classify: impl Fn(&V) -> String + Sync + Send, subject: impl Strategy<Value=V>) {
    let (tx_v, rx_v) = std::sync::mpsc::channel();

    let results = std::thread::scope(|scope| {
        let thread = scope.spawn(move || {
            let mut results: HashMap<String,usize> = Default::default();
            while let Some(v) = rx_v.recv().unwrap() {
                let label = classify(&v);
                if let Some(old) = results.insert(label.clone(), 1) {
                    results.insert(label, old+1);
                }
            }
            results
        });
        let mut tr = TestRunner::new(Default::default());
        tr.run(&CoverageStrategy(subject, tx_v), |_| Ok(())).unwrap();
        thread.join().unwrap();
    });

    println!("coverage: expected:\n {expected:?}\nresults:\n{results:?}")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant