proptest/test_runner/failure_persistence/
mod.rs

1//-
2// Copyright 2017, 2018, 2019 The proptest developers
3//
4// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
7// option. This file may not be copied, modified, or distributed
8// except according to those terms.
9
10use crate::std_facade::{fmt, Box, Vec};
11use core::any::Any;
12use core::fmt::Display;
13use core::result::Result;
14use core::str::FromStr;
15
16#[cfg(feature = "std")]
17#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
18mod file;
19mod map;
20mod noop;
21
22#[cfg(feature = "std")]
23pub use self::file::*;
24pub use self::map::*;
25
26use crate::test_runner::Seed;
27
28/// Opaque struct representing a seed which can be persisted.
29///
30/// The `Display` and `FromStr` implementations go to and from the format
31/// Proptest uses for its persistence file.
32#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
33pub struct PersistedSeed(pub(crate) Seed);
34
35impl Display for PersistedSeed {
36    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
37        write!(f, "{}", self.0.to_persistence())
38    }
39}
40
41impl FromStr for PersistedSeed {
42    type Err = ();
43
44    fn from_str(s: &str) -> Result<Self, ()> {
45        Seed::from_persistence(s).map(PersistedSeed).ok_or(())
46    }
47}
48
49/// Provides external persistence for historical test failures by storing seeds.
50///
51/// **Note**: Implementing `load_persisted_failures` and
52/// `save_persisted_failures` is **deprecated** and these methods will be
53/// removed in proptest 0.10.0. Instead, implement `load_persisted_failures2`
54/// and `save_persisted_failures2`.
55pub trait FailurePersistence: Send + Sync + fmt::Debug {
56    /// Supply seeds associated with the given `source_file` that may be used
57    /// by a `TestRunner`'s random number generator in order to consistently
58    /// recreate a previously-failing `Strategy`-provided value.
59    ///
60    /// The default implementation is **for backwards compatibility**. It
61    /// delegates to `load_persisted_failures` and converts the results into
62    /// XorShift seeds.
63    #[allow(deprecated)]
64    fn load_persisted_failures2(
65        &self,
66        source_file: Option<&'static str>,
67    ) -> Vec<PersistedSeed> {
68        self.load_persisted_failures(source_file)
69            .into_iter()
70            .map(|seed| PersistedSeed(Seed::XorShift(seed)))
71            .collect()
72    }
73
74    /// Use `load_persisted_failures2` instead.
75    ///
76    /// This function inadvertently exposes the implementation of seeds prior
77    /// to Proptest 0.9.1 and only works with XorShift seeds.
78    #[deprecated]
79    #[allow(unused_variables)]
80    fn load_persisted_failures(
81        &self,
82        source_file: Option<&'static str>,
83    ) -> Vec<[u8; 16]> {
84        panic!("load_persisted_failures2 not implemented");
85    }
86
87    /// Store a new failure-generating seed associated with the given `source_file`.
88    ///
89    /// The default implementation is **for backwards compatibility**. It
90    /// delegates to `save_persisted_failure` if `seed` is a XorShift seed.
91    #[allow(deprecated)]
92    fn save_persisted_failure2(
93        &mut self,
94        source_file: Option<&'static str>,
95        seed: PersistedSeed,
96        shrunken_value: &dyn fmt::Debug,
97    ) {
98        match seed.0 {
99            Seed::XorShift(seed) => {
100                self.save_persisted_failure(source_file, seed, shrunken_value)
101            }
102            _ => (),
103        }
104    }
105
106    /// Use `save_persisted_failures2` instead.
107    ///
108    /// This function inadvertently exposes the implementation of seeds prior
109    /// to Proptest 0.9.1 and only works with XorShift seeds.
110    #[deprecated]
111    #[allow(unused_variables)]
112    fn save_persisted_failure(
113        &mut self,
114        source_file: Option<&'static str>,
115        seed: [u8; 16],
116        shrunken_value: &dyn fmt::Debug,
117    ) {
118        panic!("save_persisted_failure2 not implemented");
119    }
120
121    /// Delegate method for producing a trait object usable with `Clone`
122    fn box_clone(&self) -> Box<dyn FailurePersistence>;
123
124    /// Equality testing delegate required due to constraints of trait objects.
125    fn eq(&self, other: &dyn FailurePersistence) -> bool;
126
127    /// Assistant method for trait object comparison.
128    fn as_any(&self) -> &dyn Any;
129}
130
131impl<'a, 'b> PartialEq<dyn FailurePersistence + 'b>
132    for dyn FailurePersistence + 'a
133{
134    fn eq(&self, other: &(dyn FailurePersistence + 'b)) -> bool {
135        FailurePersistence::eq(self, other)
136    }
137}
138
139impl Clone for Box<dyn FailurePersistence> {
140    fn clone(&self) -> Box<dyn FailurePersistence> {
141        self.box_clone()
142    }
143}
144
145#[cfg(test)]
146mod tests {
147    use super::PersistedSeed;
148    use crate::test_runner::rng::Seed;
149
150    pub const INC_SEED: PersistedSeed = PersistedSeed(Seed::XorShift([
151        0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
152    ]));
153
154    pub const HI_PATH: Option<&str> = Some("hi");
155    pub const UNREL_PATH: Option<&str> = Some("unrelated");
156}