Skip to content

Commit 49e04a9

Browse files
committed
refactor: Inline proptest-arbitrary-interop
The crate is unmaintained and, for the purpose of testing on wasm32, unusable. Because 1. the code has seen very little activity since its inception, 2. there are only a little more than 100 lines of code, and 3. it has worked perfectly well for all our needs so far, I think the risks associated with inlining are acceptable.
1 parent 855e473 commit 49e04a9

17 files changed

+219
-21
lines changed

twenty-first/Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ macro_rules_attr = "0.1.3"
4848
test-strategy = "=0.4.3"
4949
trybuild = "1.0"
5050
proptest = { version = "1.7.0", default-features = false, features = ["std"] }
51-
proptest-arbitrary-interop = { git = "https://github.com/dan-da/proptest-arbitrary-interop.git", version = "0.1", rev = "d9fcf5b" }
5251

5352
[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
5453
criterion = { package = "codspeed-criterion-compat", version = "4.0", features = ["html_reports"] }

twenty-first/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ pub mod prelude;
2020
pub mod tip5;
2121
pub mod util_types;
2222

23+
#[cfg(test)]
24+
#[cfg_attr(coverage_nightly, feature(coverage_attribute))]
25+
mod proptest_arbitrary_interop;
26+
2327
// This is needed for `#[derive(BFieldCodec)]` macro to work consistently across crates.
2428
// Specifically:
2529
// From inside the `twenty-first` crate, we need to refer to `twenty-first` by `crate`.

twenty-first/src/math/b_field_element.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -820,12 +820,12 @@ mod tests {
820820

821821
use itertools::izip;
822822
use proptest::prelude::*;
823-
use proptest_arbitrary_interop::arb;
824823
use rand::random;
825824

826825
use crate::math::b_field_element::*;
827826
use crate::math::other::random_elements;
828827
use crate::math::polynomial::Polynomial;
828+
use crate::proptest_arbitrary_interop::arb;
829829
use crate::tests::proptest;
830830
use crate::tests::test;
831831

twenty-first/src/math/bfield_codec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -601,10 +601,10 @@ mod tests {
601601
use proptest::collection::vec;
602602
use proptest::prelude::*;
603603
use proptest::test_runner::TestCaseResult;
604-
use proptest_arbitrary_interop::arb;
605604

606605
use super::*;
607606
use crate::prelude::*;
607+
use crate::proptest_arbitrary_interop::arb;
608608
use crate::tests::proptest;
609609
use crate::tests::test;
610610

twenty-first/src/math/ntt.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -331,13 +331,13 @@ mod tests {
331331
use num_traits::Zero;
332332
use proptest::collection::vec;
333333
use proptest::prelude::*;
334-
use proptest_arbitrary_interop::arb;
335334

336335
use super::*;
337336
use crate::math::other::random_elements;
338337
use crate::math::traits::PrimitiveRootOfUnity;
339338
use crate::math::x_field_element::EXTENSION_DEGREE;
340339
use crate::prelude::*;
340+
use crate::proptest_arbitrary_interop::arb;
341341
use crate::tests::proptest;
342342
use crate::tests::test;
343343
use crate::xfe;

twenty-first/src/math/polynomial.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2707,10 +2707,10 @@ mod tests {
27072707
use proptest::collection::size_range;
27082708
use proptest::collection::vec;
27092709
use proptest::prelude::*;
2710-
use proptest_arbitrary_interop::arb;
27112710

27122711
use super::*;
27132712
use crate::prelude::*;
2713+
use crate::proptest_arbitrary_interop::arb;
27142714
use crate::tests::proptest;
27152715
use crate::tests::test;
27162716

twenty-first/src/math/x_field_element.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -669,14 +669,14 @@ mod tests {
669669
use num_traits::ConstOne;
670670
use proptest::collection::vec;
671671
use proptest::prelude::*;
672-
use proptest_arbitrary_interop::arb;
673672

674673
use super::*;
675674
use crate::bfe;
676675
use crate::math::b_field_element::*;
677676
use crate::math::ntt::intt;
678677
use crate::math::ntt::ntt;
679678
use crate::math::other::random_elements;
679+
use crate::proptest_arbitrary_interop::arb;
680680
use crate::tests::proptest;
681681
use crate::tests::test;
682682

twenty-first/src/math/zerofier_tree.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,11 +106,11 @@ mod tests {
106106
use num_traits::Zero;
107107
use proptest::collection::vec;
108108
use proptest::prop_assert_eq;
109-
use proptest_arbitrary_interop::arb;
110109

111110
use crate::math::zerofier_tree::ZerofierTree;
112111
use crate::prelude::BFieldElement;
113112
use crate::prelude::Polynomial;
113+
use crate::proptest_arbitrary_interop::arb;
114114
use crate::tests::proptest;
115115
use crate::tests::test;
116116

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
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+
}

twenty-first/src/tip5/digest.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,11 @@ pub(crate) mod tests {
279279
use proptest::collection::vec;
280280
use proptest::prelude::Arbitrary as ProptestArbitrary;
281281
use proptest::prelude::*;
282-
use proptest_arbitrary_interop::arb;
283282

284283
use super::*;
285284
use crate::error::ParseBFieldElementError;
286285
use crate::prelude::*;
286+
use crate::proptest_arbitrary_interop::arb;
287287
use crate::tests::proptest;
288288
use crate::tests::test;
289289

0 commit comments

Comments
 (0)