| //- |
| // Copyright 2018 The proptest developers |
| // |
| // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your |
| // option. This file may not be copied, modified, or distributed |
| // except according to those terms. |
| |
| use crate::std_facade::Box; |
| use crate::std_facade::fmt; |
| #[cfg(feature = "std")] use std::collections::HashMap; |
| |
| use crate::test_runner::errors::TestCaseResult; |
| |
| /// A key used for the result cache. |
| /// |
| /// The capabilities of this structure are currently quite limited; all one can |
| /// do with safe code is get the `&dyn Debug` of the test input value. This may |
| /// improve in the future, particularly at such a time that specialisation |
| /// becomes stable. |
| #[derive(Debug)] |
| pub struct ResultCacheKey<'a> { |
| value: &'a dyn fmt::Debug, |
| } |
| |
| impl<'a> ResultCacheKey<'a> { |
| pub(crate) fn new(value: &'a dyn fmt::Debug) -> Self { |
| Self { value } |
| } |
| |
| /// Return the test input value as an `&dyn Debug`. |
| pub fn value_debug(&self) -> &dyn fmt::Debug { |
| self.value |
| } |
| } |
| |
| /// An object which can cache the outcomes of tests. |
| pub trait ResultCache { |
| /// Convert the given cache key into a `u64` representing that value. The |
| /// u64 is used as the key below. |
| /// |
| /// This is a separate step so that ownership of the key value can be |
| /// handed off to user code without needing to be able to clone it. |
| fn key(&self, key: &ResultCacheKey) -> u64; |
| /// Save `result` as the outcome associated with the test input in `key`. |
| /// |
| /// `result` is passed as a reference so that the decision to clone depends |
| /// on whether the cache actually plans on storing it. |
| fn put(&mut self, key: u64, result: &TestCaseResult); |
| /// If `put()` has been called with a semantically equivalent `key`, return |
| /// the saved result. Otherwise, return `None`. |
| fn get(&self, key: u64) -> Option<&TestCaseResult>; |
| } |
| |
| #[cfg(feature = "std")] |
| #[derive(Debug, Default, Clone)] |
| struct BasicResultCache { |
| entries: HashMap<u64, TestCaseResult>, |
| } |
| |
| #[cfg(feature = "std")] |
| impl ResultCache for BasicResultCache { |
| fn key(&self, val: &ResultCacheKey) -> u64 { |
| use std::collections::hash_map::DefaultHasher; |
| use std::io::{self, Write}; |
| use std::hash::Hasher; |
| |
| struct HashWriter(DefaultHasher); |
| impl io::Write for HashWriter { |
| fn write(&mut self, buf: &[u8]) -> io::Result<usize> { |
| self.0.write(buf); |
| Ok(buf.len()) |
| } |
| |
| fn flush(&mut self) -> io::Result<()> { |
| Ok(()) |
| } |
| } |
| |
| let mut hash = HashWriter(DefaultHasher::default()); |
| write!(hash, "{:?}", val).expect("Debug format returned Err"); |
| hash.0.finish() |
| } |
| |
| fn put(&mut self, key: u64, result: &TestCaseResult) { |
| self.entries.insert(key, result.clone()); |
| } |
| |
| fn get(&self, key: u64) -> Option<&TestCaseResult> { |
| self.entries.get(&key) |
| } |
| } |
| |
| /// A basic result cache. |
| /// |
| /// Values are identified by their `Debug` string representation. |
| #[cfg(feature = "std")] |
| pub fn basic_result_cache() -> Box<dyn ResultCache> { |
| Box::new(BasicResultCache::default()) |
| } |
| |
| pub(crate) struct NoOpResultCache; |
| impl ResultCache for NoOpResultCache { |
| fn key(&self, _: &ResultCacheKey) -> u64 { 0 } |
| fn put(&mut self, _: u64, _: &TestCaseResult) { } |
| fn get(&self, _: u64) -> Option<&TestCaseResult> { None } |
| } |
| |
| /// A result cache that does nothing. |
| /// |
| /// This is the default value of `ProptestConfig.result_cache`. |
| pub fn noop_result_cache() -> Box<dyn ResultCache> { |
| Box::new(NoOpResultCache) |
| } |