1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//-
// 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)
}