Fix shrinker impl and implement 3rd strategy of bin_search reduction.
This commit is contained in:
		
							parent
							
								
									70ea3c9598
								
							
						
					
					
						commit
						3df5bcd96d
					
				|  | @ -371,13 +371,10 @@ impl CheckedModules { | ||||||
| 
 | 
 | ||||||
|         let mut module_src = IndexMap::new(); |         let mut module_src = IndexMap::new(); | ||||||
| 
 | 
 | ||||||
|         println!("Looking for modules definitions"); |  | ||||||
| 
 |  | ||||||
|         for module in self.values() { |         for module in self.values() { | ||||||
|             for def in module.ast.definitions() { |             for def in module.ast.definitions() { | ||||||
|                 match def { |                 match def { | ||||||
|                     Definition::Fn(func) => { |                     Definition::Fn(func) => { | ||||||
|                         println!("Found function: {}", func.name); |  | ||||||
|                         functions.insert( |                         functions.insert( | ||||||
|                             FunctionAccessKey { |                             FunctionAccessKey { | ||||||
|                                 module_name: module.name.clone(), |                                 module_name: module.name.clone(), | ||||||
|  |  | ||||||
|  | @ -549,11 +549,20 @@ impl<'a> Counterexample<'a> { | ||||||
|             // also the element itself, which may involve more than one choice.
 |             // also the element itself, which may involve more than one choice.
 | ||||||
|             let mut k = 8; |             let mut k = 8; | ||||||
|             while k > 0 { |             while k > 0 { | ||||||
|                 if k > self.choices.len() { |                 let (mut i, mut underflow) = if self.choices.len() < k { | ||||||
|                     break; |                     (0, true) | ||||||
|  |                 } else { | ||||||
|  |                     (self.choices.len() - k, false) | ||||||
|  |                 }; | ||||||
|  | 
 | ||||||
|  |                 while !underflow { | ||||||
|  |                     if i >= self.choices.len() { | ||||||
|  |                         (i, underflow) = i.overflowing_sub(1); | ||||||
|  |                         continue; | ||||||
|                     } |                     } | ||||||
| 
 | 
 | ||||||
|                 for (i, j) in (0..=self.choices.len() - k).map(|i| (i, i + k)).rev() { |                     let j = i + k; | ||||||
|  | 
 | ||||||
|                     let mut choices = [ |                     let mut choices = [ | ||||||
|                         &self.choices[..i], |                         &self.choices[..i], | ||||||
|                         if j < self.choices.len() { |                         if j < self.choices.len() { | ||||||
|  | @ -564,10 +573,7 @@ impl<'a> Counterexample<'a> { | ||||||
|                     ] |                     ] | ||||||
|                     .concat(); |                     .concat(); | ||||||
| 
 | 
 | ||||||
|                     if self.consider(&choices) { |                     if !self.consider(&choices) { | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                         // Perform an extra reduction step that decrease the size of choices near
 |                         // Perform an extra reduction step that decrease the size of choices near
 | ||||||
|                         // the end, to cope with dependencies between choices, e.g. drawing a
 |                         // the end, to cope with dependencies between choices, e.g. drawing a
 | ||||||
|                         // number as a list length, and then drawing that many elements.
 |                         // number as a list length, and then drawing that many elements.
 | ||||||
|  | @ -576,9 +582,12 @@ impl<'a> Counterexample<'a> { | ||||||
|                         if i > 0 && choices[i - 1] > 0 { |                         if i > 0 && choices[i - 1] > 0 { | ||||||
|                             choices[i - 1] -= 1; |                             choices[i - 1] -= 1; | ||||||
|                             if self.consider(&choices) { |                             if self.consider(&choices) { | ||||||
|                             break; |                                 i += 1; | ||||||
|                             }; |                             }; | ||||||
|                         } |                         } | ||||||
|  | 
 | ||||||
|  |                         (i, underflow) = i.overflowing_sub(1); | ||||||
|  |                     } | ||||||
|                 } |                 } | ||||||
| 
 | 
 | ||||||
|                 k /= 2 |                 k /= 2 | ||||||
|  | @ -591,11 +600,25 @@ impl<'a> Counterexample<'a> { | ||||||
|             while k > 1 { |             while k > 1 { | ||||||
|                 let mut i: isize = self.choices.len() as isize - k; |                 let mut i: isize = self.choices.len() as isize - k; | ||||||
|                 while i >= 0 { |                 while i >= 0 { | ||||||
|                     i -= if self.zeroes(i, k) { k } else { 1 } |                     let ivs = (i..i + k).map(|j| (j as usize, 0)).collect::<Vec<_>>(); | ||||||
|  |                     i -= if self.replace(ivs) { k } else { 1 } | ||||||
|                 } |                 } | ||||||
|                 k /= 2 |                 k /= 2 | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|  |             // Replace choices with smaller value, by doing a binary search. This will replace n
 | ||||||
|  |             // with 0 or n - 1, if possible, but will also more efficiently replace it with, a
 | ||||||
|  |             // smaller number than doing multiple subtractions would.
 | ||||||
|  |             let (mut i, mut underflow) = if self.choices.is_empty() { | ||||||
|  |                 (0, true) | ||||||
|  |             } else { | ||||||
|  |                 (self.choices.len() - 1, false) | ||||||
|  |             }; | ||||||
|  |             while !underflow { | ||||||
|  |                 self.binary_search_replace(0, self.choices[i], |v| vec![(i, v)]); | ||||||
|  |                 (i, underflow) = i.overflowing_sub(1); | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|             // TODO: Remaining shrinking strategies...
 |             // TODO: Remaining shrinking strategies...
 | ||||||
|             //
 |             //
 | ||||||
|             // - Swaps
 |             // - Swaps
 | ||||||
|  | @ -610,15 +633,42 @@ impl<'a> Counterexample<'a> { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // Replace a block between indices 'i' and 'k' by zeroes.
 |     /// Try to replace a value with a smaller value by doing a binary search between
 | ||||||
|     fn zeroes(&mut self, i: isize, k: isize) -> bool { |     /// two extremes. This converges relatively fast in order to shrink down values.
 | ||||||
|  |     /// fast.
 | ||||||
|  |     fn binary_search_replace<F>(&mut self, lo: u32, hi: u32, f: F) -> u32 | ||||||
|  |     where | ||||||
|  |         F: Fn(u32) -> Vec<(usize, u32)>, | ||||||
|  |     { | ||||||
|  |         if self.replace(f(lo)) { | ||||||
|  |             return lo; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         let mut lo = lo; | ||||||
|  |         let mut hi = hi; | ||||||
|  | 
 | ||||||
|  |         while lo + 1 < hi { | ||||||
|  |             let mid = lo + (hi - lo) / 2; | ||||||
|  |             if self.replace(f(mid)) { | ||||||
|  |                 hi = mid; | ||||||
|  |             } else { | ||||||
|  |                 lo = mid; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         hi | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // Replace values in the choices vector, based on the index-value list provided
 | ||||||
|  |     // and consider the resulting choices.
 | ||||||
|  |     fn replace(&mut self, ivs: Vec<(usize, u32)>) -> bool { | ||||||
|         let mut choices = self.choices.clone(); |         let mut choices = self.choices.clone(); | ||||||
| 
 | 
 | ||||||
|         for j in i..(i + k) { |         for (i, v) in ivs { | ||||||
|             if j >= self.choices.len() as isize { |             if i >= choices.len() { | ||||||
|                 return false; |                 return false; | ||||||
|             } |             } | ||||||
|             choices[j as usize] = 0; |             choices[i] = v; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         self.consider(&choices) |         self.consider(&choices) | ||||||
|  | @ -980,6 +1030,26 @@ mod test { | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |     impl PropertyTest { | ||||||
|  |         fn expect_failure(&self, seed: u32) -> Counterexample { | ||||||
|  |             let (next_prng, value) = Prng::from_seed(seed) | ||||||
|  |                 .sample(&self.fuzzer.program) | ||||||
|  |                 .expect("running seeded Prng cannot fail."); | ||||||
|  | 
 | ||||||
|  |             let result = self.eval(&value); | ||||||
|  | 
 | ||||||
|  |             if result.failed(self.can_error) { | ||||||
|  |                 return Counterexample { | ||||||
|  |                     value, | ||||||
|  |                     choices: next_prng.choices(), | ||||||
|  |                     property: self, | ||||||
|  |                 }; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             unreachable!("Prng constructed from a seed necessarily yield a seed."); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|     #[test] |     #[test] | ||||||
|     fn test_prop_basic() { |     fn test_prop_basic() { | ||||||
|         let prop = property(indoc! { r#" |         let prop = property(indoc! { r#" | ||||||
|  | @ -991,13 +1061,18 @@ mod test { | ||||||
|         assert!(prop.run(42).is_success()); |         assert!(prop.run(42).is_success()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     //    fn counterexample<'a>(choices: &'a [u32], property: &'a PropertyTest) -> Counterexample<'a> {
 |     #[test] | ||||||
|     //        let value = todo!();
 |     fn test_prop_always_odd() { | ||||||
|     //
 |         let prop = property(indoc! { r#" | ||||||
|     //        Counterexample {
 |             test foo(n: Int via int()) { | ||||||
|     //            value,
 |                 n % 2 == 0 | ||||||
|     //            choices: choices.to_vec(),
 |             } | ||||||
|     //            property,
 |         "#});
 | ||||||
|     //        }
 | 
 | ||||||
|     //    }
 |         let mut counterexample = prop.expect_failure(12); | ||||||
|  | 
 | ||||||
|  |         counterexample.simplify(); | ||||||
|  | 
 | ||||||
|  |         assert_eq!(counterexample.choices, vec![1]); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	 KtorZ
						KtorZ