diff --git a/tooling/fuzzer/src/strategies/int.rs b/tooling/fuzzer/src/strategies/int.rs index c529d2f9501..cad69dd42d2 100644 --- a/tooling/fuzzer/src/strategies/int.rs +++ b/tooling/fuzzer/src/strategies/int.rs @@ -1,94 +1,14 @@ use proptest::{ - strategy::{NewTree, Strategy, ValueTree}, + strategy::{NewTree, Strategy}, test_runner::TestRunner, }; use rand::Rng; -/// Value tree for signed ints (up to int256). -pub struct IntValueTree { - /// Lower base (by absolute value) - lo: i128, - /// Current value - curr: i128, - /// Higher base (by absolute value) - hi: i128, - /// If true cannot be simplified or complexified - fixed: bool, -} - -impl IntValueTree { - /// Create a new tree - /// # Arguments - /// * `start` - Starting value for the tree - /// * `fixed` - If `true` the tree would only contain one element and won't be simplified. - fn new(start: i128, fixed: bool) -> Self { - Self { lo: 0, curr: start, hi: start, fixed } - } - - fn reposition(&mut self) -> bool { - let interval = self.hi - self.lo; - let new_mid = self.lo + interval / 2i128; - - if new_mid == self.curr { - false - } else { - self.curr = new_mid; - true - } - } - - fn magnitude_greater(lhs: i128, rhs: i128) -> bool { - if lhs == 0 { - return false; - } - (lhs > rhs) ^ (lhs.is_negative()) - } -} - -impl ValueTree for IntValueTree { - type Value = i128; - - fn current(&self) -> Self::Value { - self.curr - } - - fn simplify(&mut self) -> bool { - if self.fixed || !Self::magnitude_greater(self.hi, self.lo) { - return false; - } - self.hi = self.curr; - self.reposition() - } - - fn complicate(&mut self) -> bool { - if self.fixed || !Self::magnitude_greater(self.hi, self.lo) { - return false; - } - - self.lo = if self.curr != i128::MIN && self.curr != i128::MAX { - self.curr + if self.hi.is_negative() { -1i128 } else { 1i128 } - } else { - self.curr - }; - - self.reposition() - } -} - -/// Value tree for signed ints (up to int256). -/// The strategy combines 3 different strategies, each assigned a specific weight: +/// Strategy for signed ints (up to i128). +/// The strategy combines 2 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` /// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around min, 0 and max possible value) -/// 3. Generate a value from a predefined fixtures set -/// -/// To define int fixtures: -/// - return an array of possible values for a parameter named `amount` declare a function `function -/// fixture_amount() public returns (int32[] memory)`. -/// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values -/// `function testFuzz_int32(int32 amount)`. -/// -/// If fixture is not a valid int type then error is raised and random value generated. #[derive(Debug)] pub struct IntStrategy { /// Bit size of int (e.g. 256) @@ -101,9 +21,8 @@ pub struct IntStrategy { impl IntStrategy { /// Create a new strategy. - /// #Arguments - /// * `bits` - Size of uint in bits - /// * `fixtures` - A set of fixed values to be generated (according to fixtures weight) + /// # Arguments + /// * `bits` - Size of int in bits pub fn new(bits: usize) -> Self { Self { bits, edge_weight: 10usize, random_weight: 50usize } } @@ -121,14 +40,14 @@ impl IntStrategy { 3 => self.type_max() - offset, _ => unreachable!(), }; - Ok(IntValueTree::new(start, false)) + Ok(proptest::num::i128::BinarySearch::new(start)) } fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); let start: i128 = rng.gen_range(self.type_min()..=self.type_max()); - Ok(IntValueTree::new(start, false)) + Ok(proptest::num::i128::BinarySearch::new(start)) } fn type_max(&self) -> i128 { @@ -149,7 +68,7 @@ impl IntStrategy { } impl Strategy for IntStrategy { - type Tree = IntValueTree; + type Tree = proptest::num::i128::BinarySearch; type Value = i128; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { @@ -162,24 +81,3 @@ impl Strategy for IntStrategy { } } } - -#[cfg(test)] -mod tests { - use crate::strategies::int::IntValueTree; - use proptest::strategy::ValueTree; - - #[test] - fn test_int_tree_complicate_should_not_overflow() { - let mut int_tree = IntValueTree::new(i128::MAX, false); - assert_eq!(int_tree.hi, i128::MAX); - assert_eq!(int_tree.curr, i128::MAX); - int_tree.complicate(); - assert_eq!(int_tree.lo, i128::MAX); - - let mut int_tree = IntValueTree::new(i128::MIN, false); - assert_eq!(int_tree.hi, i128::MIN); - assert_eq!(int_tree.curr, i128::MIN); - int_tree.complicate(); - assert_eq!(int_tree.lo, i128::MIN); - } -} diff --git a/tooling/fuzzer/src/strategies/uint.rs b/tooling/fuzzer/src/strategies/uint.rs index a7ecf89e5e1..8f5e2903e65 100644 --- a/tooling/fuzzer/src/strategies/uint.rs +++ b/tooling/fuzzer/src/strategies/uint.rs @@ -1,82 +1,14 @@ use proptest::{ - strategy::{NewTree, Strategy, ValueTree}, + strategy::{NewTree, Strategy}, test_runner::TestRunner, }; use rand::Rng; -/// Value tree for unsigned ints (up to uint256). -pub struct UintValueTree { - /// Lower base - lo: u128, - /// Current value - curr: u128, - /// Higher base - hi: u128, - /// If true cannot be simplified or complexified - fixed: bool, -} - -impl UintValueTree { - /// Create a new tree - /// # Arguments - /// * `start` - Starting value for the tree - /// * `fixed` - If `true` the tree would only contain one element and won't be simplified. - fn new(start: u128, fixed: bool) -> Self { - Self { lo: 0, curr: start, hi: start, fixed } - } - - fn reposition(&mut self) -> bool { - let interval = self.hi - self.lo; - let new_mid = self.lo + interval / 2; - - if new_mid == self.curr { - false - } else { - self.curr = new_mid; - true - } - } -} - -impl ValueTree for UintValueTree { - type Value = u128; - - fn current(&self) -> Self::Value { - self.curr - } - - fn simplify(&mut self) -> bool { - if self.fixed || (self.hi <= self.lo) { - return false; - } - self.hi = self.curr; - self.reposition() - } - - fn complicate(&mut self) -> bool { - if self.fixed || (self.hi <= self.lo) { - return false; - } - - self.lo = self.curr.wrapping_add(1); - self.reposition() - } -} - -/// Value tree for unsigned ints (up to uint256). -/// The strategy combines 3 different strategies, each assigned a specific weight: +/// Value tree for unsigned ints (up to u128). +/// The strategy combines 2 different strategies, each assigned a specific weight: /// 1. Generate purely random value in a range. This will first choose bit size uniformly (up `bits` /// param). Then generate a value for this bit size. /// 2. Generate a random value around the edges (+/- 3 around 0 and max possible value) -/// 3. Generate a value from a predefined fixtures set -/// -/// To define uint fixtures: -/// - return an array of possible values for a parameter named `amount` declare a function `function -/// fixture_amount() public returns (uint32[] memory)`. -/// - use `amount` named parameter in fuzzed test in order to include fixtures in fuzzed values -/// `function testFuzz_uint32(uint32 amount)`. -/// -/// If fixture is not a valid uint type then error is raised and random value generated. #[derive(Debug)] pub struct UintStrategy { /// Bit size of uint (e.g. 256) @@ -90,9 +22,8 @@ pub struct UintStrategy { impl UintStrategy { /// Create a new strategy. - /// #Arguments + /// # Arguments /// * `bits` - Size of uint in bits - /// * `fixtures` - A set of fixed values to be generated (according to fixtures weight) pub fn new(bits: usize) -> Self { Self { bits, edge_weight: 10usize, random_weight: 50usize } } @@ -103,14 +34,14 @@ impl UintStrategy { let is_min = rng.gen_bool(0.5); let offset = rng.gen_range(0..4); let start = if is_min { offset } else { self.type_max().saturating_sub(offset) }; - Ok(UintValueTree::new(start, false)) + Ok(proptest::num::u128::BinarySearch::new(start)) } fn generate_random_tree(&self, runner: &mut TestRunner) -> NewTree { let rng = runner.rng(); - let start: u128 = rng.gen_range(0..=self.type_max()); + let start = rng.gen_range(0..=self.type_max()); - Ok(UintValueTree::new(start, false)) + Ok(proptest::num::u128::BinarySearch::new(start)) } fn type_max(&self) -> u128 { @@ -123,30 +54,15 @@ impl UintStrategy { } impl Strategy for UintStrategy { - type Tree = UintValueTree; + type Tree = proptest::num::u128::BinarySearch; type Value = u128; fn new_tree(&self, runner: &mut TestRunner) -> NewTree { let total_weight = self.random_weight + self.edge_weight; let bias = runner.rng().gen_range(0..total_weight); - // randomly select one of 3 strategies + // randomly select one of 2 strategies match bias { x if x < self.edge_weight => self.generate_edge_tree(runner), _ => self.generate_random_tree(runner), } } } - -#[cfg(test)] -mod tests { - use crate::strategies::uint::UintValueTree; - use proptest::strategy::ValueTree; - - #[test] - fn test_uint_tree_complicate_max() { - let mut uint_tree = UintValueTree::new(u128::MAX, false); - assert_eq!(uint_tree.hi, u128::MAX); - assert_eq!(uint_tree.curr, u128::MAX); - uint_tree.complicate(); - assert_eq!(uint_tree.lo, u128::MIN); - } -}