|
| 1 | +//! Provides the necessary glue to reuse an implementation of |
| 2 | +//! [`arbitrary::Arbitrary`] as a [`proptest::strategy::Strategy`]. |
| 3 | +//! |
| 4 | +//! The central method is [`arb`]. |
| 5 | +//! |
| 6 | +//! # Origin |
| 7 | +//! |
| 8 | +//! This code is a copy of the unmaintained crate |
| 9 | +//! [`proptest-arbitrary-interop`][origin], with some additional improvements |
| 10 | +//! from open pull requests of the original's repository. |
| 11 | +//! |
| 12 | +//! [origin]: https://crates.io/crates/proptest-arbitrary-interop |
| 13 | +//! |
| 14 | +//! # Caveats |
| 15 | +//! |
| 16 | +//! It only works with types that implement [`arbitrary::Arbitrary`] in a |
| 17 | +//! particular fashion: those conforming to the requirements of [`ArbInterop`]. |
| 18 | +//! These are roughly "types that, when randomly-generated, don't retain |
| 19 | +//! pointers into the random-data buffer wrapped by the |
| 20 | +//! [`arbitrary::Unstructured`] they are generated from". Many implementations |
| 21 | +//! of [`arbitrary::Arbitrary`] will fit the bill, but certain kinds of |
| 22 | +//! "zero-copy" implementations of [`arbitrary::Arbitrary`] will not work. This |
| 23 | +//! requirement appears to be a necessary part of the semantic model of |
| 24 | +//! [`proptest`] – generated values have to own their pointer graph, no |
| 25 | +//! borrows. Patches welcome if you can figure out a way to not require it. |
| 26 | +
|
| 27 | +use core::fmt::Debug; |
| 28 | +use proptest::prelude::RngCore; |
| 29 | +use proptest::test_runner::TestRunner; |
| 30 | +use std::marker::PhantomData; |
| 31 | + |
| 32 | +/// The subset of possible [`arbitrary::Arbitrary`] implementations that this |
| 33 | +/// crate works with. The main concern here is the `for<'a> Arbitrary<'a>` |
| 34 | +/// business, which (in practice) decouples the generated `Arbitrary` value from |
| 35 | +/// the lifetime of the random buffer it's fed; I can't actually explain how, |
| 36 | +/// because Rust's type system is way over my head. |
| 37 | +pub trait ArbInterop: for<'a> arbitrary::Arbitrary<'a> + 'static + Debug + Clone {} |
| 38 | +impl<A> ArbInterop for A where A: for<'a> arbitrary::Arbitrary<'a> + 'static + Debug + Clone {} |
| 39 | + |
| 40 | +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] |
| 41 | +pub struct ArbStrategy<A: ArbInterop> { |
| 42 | + size: usize, |
| 43 | + _ph: PhantomData<A>, |
| 44 | +} |
| 45 | + |
| 46 | +#[derive(Debug)] |
| 47 | +pub struct ArbValueTree<A: Debug> { |
| 48 | + bytes: Vec<u8>, |
| 49 | + curr: A, |
| 50 | + prev: Option<A>, |
| 51 | + next: usize, |
| 52 | +} |
| 53 | + |
| 54 | +impl<A: ArbInterop> proptest::strategy::ValueTree for ArbValueTree<A> { |
| 55 | + type Value = A; |
| 56 | + |
| 57 | + fn current(&self) -> Self::Value { |
| 58 | + self.curr.clone() |
| 59 | + } |
| 60 | + |
| 61 | + fn simplify(&mut self) -> bool { |
| 62 | + if self.next == 0 { |
| 63 | + return false; |
| 64 | + } |
| 65 | + self.next -= 1; |
| 66 | + let Ok(simpler) = Self::gen_one_with_size(&self.bytes, self.next) else { |
| 67 | + return false; |
| 68 | + }; |
| 69 | + |
| 70 | + // Throw away the previous value and set the current value as prev. |
| 71 | + // Advance the iterator and set the current value to the next one. |
| 72 | + self.prev = Some(core::mem::replace(&mut self.curr, simpler)); |
| 73 | + |
| 74 | + true |
| 75 | + } |
| 76 | + |
| 77 | + fn complicate(&mut self) -> bool { |
| 78 | + // We can only complicate if we previously simplified. Complicating |
| 79 | + // twice in a row without interleaved simplification is guaranteed to |
| 80 | + // always yield false for the second call. |
| 81 | + let Some(prev) = self.prev.take() else { |
| 82 | + return false; |
| 83 | + }; |
| 84 | + |
| 85 | + // Throw away the current value! |
| 86 | + self.curr = prev; |
| 87 | + |
| 88 | + true |
| 89 | + } |
| 90 | +} |
| 91 | + |
| 92 | +impl<A: ArbInterop> ArbStrategy<A> { |
| 93 | + pub fn new(size: usize) -> Self { |
| 94 | + Self { |
| 95 | + size, |
| 96 | + _ph: PhantomData, |
| 97 | + } |
| 98 | + } |
| 99 | +} |
| 100 | + |
| 101 | +impl<A: ArbInterop> ArbValueTree<A> { |
| 102 | + fn gen_one_with_size(bytes: &[u8], size: usize) -> Result<A, arbitrary::Error> { |
| 103 | + let mut unstructured = arbitrary::Unstructured::new(&bytes[0..size]); |
| 104 | + |
| 105 | + A::arbitrary(&mut unstructured) |
| 106 | + } |
| 107 | + |
| 108 | + pub fn new(bytes: Vec<u8>) -> Result<Self, arbitrary::Error> { |
| 109 | + let next = bytes.len(); |
| 110 | + let curr = Self::gen_one_with_size(&bytes, next)?; |
| 111 | + |
| 112 | + Ok(Self { |
| 113 | + bytes, |
| 114 | + prev: None, |
| 115 | + curr, |
| 116 | + next, |
| 117 | + }) |
| 118 | + } |
| 119 | +} |
| 120 | + |
| 121 | +impl<A: ArbInterop> proptest::strategy::Strategy for ArbStrategy<A> { |
| 122 | + type Tree = ArbValueTree<A>; |
| 123 | + type Value = A; |
| 124 | + |
| 125 | + fn new_tree(&self, run: &mut TestRunner) -> proptest::strategy::NewTree<Self> { |
| 126 | + loop { |
| 127 | + let mut bytes = vec![0; self.size]; |
| 128 | + run.rng().fill_bytes(&mut bytes); |
| 129 | + match ArbValueTree::new(bytes) { |
| 130 | + Ok(v) => return Ok(v), |
| 131 | + |
| 132 | + // If the Arbitrary impl cannot construct a value from the given |
| 133 | + // bytes, try again. |
| 134 | + Err(e @ arbitrary::Error::IncorrectFormat) => run.reject_local(format!("{e}"))?, |
| 135 | + Err(e) => return Err(format!("{e}").into()), |
| 136 | + } |
| 137 | + } |
| 138 | + } |
| 139 | +} |
| 140 | + |
| 141 | +/// Constructs a [`proptest::strategy::Strategy`] for a given |
| 142 | +/// [`arbitrary::Arbitrary`] type, generating `size` bytes of random data as |
| 143 | +/// input to the [`arbitrary::Arbitrary`] type. |
| 144 | +pub fn arb_sized<A: ArbInterop>(size: usize) -> ArbStrategy<A> { |
| 145 | + ArbStrategy::new(size) |
| 146 | +} |
| 147 | + |
| 148 | +/// Calls [`arb_sized`](crate::arb_sized) with a best-effort guess for the size. |
| 149 | +/// |
| 150 | +/// In particular, if `A`'s [`size_hint`](Arbitrary::size_hint) is useful, the |
| 151 | +/// hint is used; otherwise, a default size of 256 is used. |
| 152 | +pub fn arb<A: ArbInterop>() -> ArbStrategy<A> { |
| 153 | + let (low, opt_high) = A::size_hint(0); |
| 154 | + let Some(high) = opt_high else { |
| 155 | + return arb_sized(256.max(2 * low)); |
| 156 | + }; |
| 157 | + |
| 158 | + arb_sized(high) |
| 159 | +} |
0 commit comments