1use alloy_dyn_abi::Word;
2use alloy_primitives::{Address, I256, Sign, U256};
3use proptest::{prelude::*, test_runner::TestRunner};
4use rand::seq::IndexedRandom;
5use std::fmt::Debug;
6
7static INTERESTING_8: &[i8] = &[-128, -1, 0, 1, 16, 32, 64, 100, 127];
9
10static INTERESTING_16: &[i16] = &[
12 -128, -1, 0, 1, 16, 32, 64, 100, 127, -32768, -129, 128, 255, 256, 512, 1000, 1024, 4096, 32767,
13];
14
15static INTERESTING_32: &[i32] = &[
17 -128,
18 -1,
19 0,
20 1,
21 16,
22 32,
23 64,
24 100,
25 127,
26 -32768,
27 -129,
28 128,
29 255,
30 256,
31 512,
32 1000,
33 1024,
34 4096,
35 32767,
36 -2147483648,
37 -100663046,
38 -32769,
39 32768,
40 65535,
41 65536,
42 100663045,
43 2147483647,
44];
45
46static THREE_SIGMA_MULTIPLIERS: &[f64] = &[0.1, 0.25, 0.5, 1.0, 2.0, 5.0, 10.0];
50
51pub(crate) trait IncrementDecrementMutator: Sized + Copy + Debug {
53 fn validate(old: Self, new: Self, size: usize) -> Option<Self>;
54
55 #[instrument(
56 name = "mutator::increment_decrement",
57 level = "trace",
58 skip(size, test_runner),
59 ret
60 )]
61 fn increment_decrement(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
62 let mutated = if test_runner.rng().random::<bool>() {
63 self.wrapping_add(Self::ONE)
64 } else {
65 self.wrapping_sub(Self::ONE)
66 };
67 Self::validate(self, mutated, size)
68 }
69
70 fn wrapping_add(self, rhs: Self) -> Self;
71 fn wrapping_sub(self, rhs: Self) -> Self;
72 const ONE: Self;
73}
74
75macro_rules! impl_increment_decrement_mutator {
76 ($ty:ty, $validate_fn:path) => {
77 impl IncrementDecrementMutator for $ty {
78 fn validate(old: Self, new: Self, size: usize) -> Option<Self> {
79 $validate_fn(old, new, size)
80 }
81
82 fn wrapping_add(self, rhs: Self) -> Self {
83 Self::wrapping_add(self, rhs)
84 }
85
86 fn wrapping_sub(self, rhs: Self) -> Self {
87 Self::wrapping_sub(self, rhs)
88 }
89
90 const ONE: Self = Self::ONE;
91 }
92 };
93}
94
95impl_increment_decrement_mutator!(U256, validate_uint_mutation);
96impl_increment_decrement_mutator!(I256, validate_int_mutation);
97
98pub(crate) trait GaussianNoiseMutator: Sized + Copy + Debug {
100 fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
101}
102
103impl GaussianNoiseMutator for U256 {
104 #[instrument(
105 name = "U256::mutate_with_gaussian_noise",
106 level = "trace",
107 skip(size, test_runner),
108 ret
109 )]
110 fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
111 let scale_factor = sample_gaussian_scale(&mut test_runner.rng())?;
112 let mut bytes: [u8; 32] = self.to_be_bytes();
113 apply_scale_to_bytes(&mut bytes[32 - size / 8..], scale_factor)?;
114 validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
115 }
116}
117
118impl GaussianNoiseMutator for I256 {
119 #[instrument(
120 name = "I256::mutate_with_gaussian_noise",
121 level = "trace",
122 skip(size, test_runner),
123 ret
124 )]
125 fn mutate_with_gaussian_noise(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
126 let scale_factor = sample_gaussian_scale(&mut test_runner.rng())?;
127 let mut bytes: [u8; 32] = self.to_be_bytes();
128 apply_scale_to_bytes(&mut bytes[32 - size / 8..], scale_factor)?;
129 validate_int_mutation(self, Self::from_be_bytes(bytes), size)
130 }
131}
132
133pub trait BoundMutator: Sized + Copy + Debug {
136 fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self>;
137}
138
139impl BoundMutator for U256 {
140 #[instrument(name = "U256::bound", level = "trace", skip(test_runner), ret)]
141 fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self> {
142 if min > max || self < min || self > max || min == max {
143 return None;
144 }
145
146 let rng = test_runner.rng();
147
148 loop {
149 let bits = rng.random_range(8..=256);
150 let mask = (Self::ONE << bits) - Self::ONE;
151 let candidate = Self::from(rng.random::<u128>()) & mask;
152
153 let candidate = min + (candidate % ((max - min).saturating_add(Self::ONE)));
155
156 if candidate != self {
157 return Some(candidate);
158 }
159 }
160 }
161}
162
163impl BoundMutator for I256 {
164 #[instrument(name = "I256::bound", level = "trace", skip(test_runner), ret)]
165 fn bound(self, min: Self, max: Self, test_runner: &mut TestRunner) -> Option<Self> {
166 if min > max || self < min || self > max || min == max {
167 return None;
168 }
169
170 let rng = test_runner.rng();
171
172 loop {
173 let bits = rng.random_range(8..=255);
174 let mask = (U256::ONE << bits) - U256::ONE;
175 let rand_u = U256::from(rng.next_u64()) | (U256::from(rng.next_u64()) << 64);
176 let unsigned_candidate = rand_u & mask;
177
178 let signed_candidate = {
179 let midpoint = U256::ONE << (bits - 1);
180 if unsigned_candidate < midpoint {
181 Self::from_raw(unsigned_candidate)
182 } else {
183 Self::from_raw(unsigned_candidate) - Self::from_raw(U256::ONE << bits)
184 }
185 };
186
187 let range = max.saturating_sub(min).saturating_add(Self::ONE).unsigned_abs();
189 let wrapped = Self::from_raw(U256::from(signed_candidate.unsigned_abs()) % range);
190 let candidate =
191 if signed_candidate.is_negative() { max - wrapped } else { min + wrapped };
192
193 if candidate != self {
194 return Some(candidate);
195 }
196 }
197 }
198}
199
200pub(crate) trait BitMutator: Sized + Copy + Debug {
202 fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
203}
204
205impl BitMutator for U256 {
206 #[instrument(name = "U256::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
207 fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
208 let mut bytes: [u8; 32] = self.to_be_bytes();
209 flip_random_bit_in_slice(&mut bytes[32 - size / 8..], test_runner)?;
210 validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
211 }
212}
213
214impl BitMutator for I256 {
215 #[instrument(name = "I256::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
216 fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
217 let mut bytes: [u8; 32] = self.to_be_bytes();
218 flip_random_bit_in_slice(&mut bytes[32 - size / 8..], test_runner)?;
219 validate_int_mutation(self, Self::from_be_bytes(bytes), size)
220 }
221}
222
223impl BitMutator for Address {
224 #[instrument(name = "Address::flip_random_bit", level = "trace", skip(_size, test_runner), ret)]
225 fn flip_random_bit(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
226 let mut mutated = self;
227 flip_random_bit_in_slice(mutated.as_mut_slice(), test_runner)?;
228 (self != mutated).then_some(mutated)
229 }
230}
231
232impl BitMutator for Word {
233 #[instrument(name = "Word::flip_random_bit", level = "trace", skip(size, test_runner), ret)]
234 fn flip_random_bit(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
235 let mut bytes = self;
236 let slice = &mut bytes[..size];
237 flip_random_bit_in_slice(slice, test_runner)?;
238 (self != bytes).then_some(bytes)
239 }
240}
241
242pub(crate) trait InterestingWordMutator: Sized + Copy + Debug {
245 fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
246 fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
247 fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self>;
248}
249
250impl InterestingWordMutator for U256 {
251 #[instrument(
252 name = "U256::mutate_interesting_byte",
253 level = "trace",
254 skip(size, test_runner),
255 ret
256 )]
257 fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
258 let mut bytes: [u8; 32] = self.to_be_bytes();
259 mutate_interesting_byte_slice(&mut bytes[32 - size / 8..], test_runner)?;
260 validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
261 }
262
263 #[instrument(
264 name = "U256::mutate_interesting_word",
265 level = "trace",
266 skip(size, test_runner),
267 ret
268 )]
269 fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
270 let mut bytes: [u8; 32] = self.to_be_bytes();
271 mutate_interesting_word_slice(&mut bytes[32 - size / 8..], test_runner)?;
272 validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
273 }
274
275 #[instrument(
276 name = "U256::mutate_interesting_dword",
277 level = "trace",
278 skip(size, test_runner),
279 ret
280 )]
281 fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
282 let mut bytes: [u8; 32] = self.to_be_bytes();
283 mutate_interesting_dword_slice(&mut bytes[32 - size / 8..], test_runner)?;
284 validate_uint_mutation(self, Self::from_be_bytes(bytes), size)
285 }
286}
287
288impl InterestingWordMutator for I256 {
289 #[instrument(
290 name = "I256::mutate_interesting_byte",
291 level = "trace",
292 skip(size, test_runner),
293 ret
294 )]
295 fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
296 let mut bytes: [u8; 32] = self.to_be_bytes();
297 mutate_interesting_byte_slice(&mut bytes[32 - size / 8..], test_runner)?;
298 validate_int_mutation(self, Self::from_be_bytes(bytes), size)
299 }
300
301 #[instrument(
302 name = "I256::mutate_interesting_word",
303 level = "trace",
304 skip(size, test_runner),
305 ret
306 )]
307 fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
308 let mut bytes: [u8; 32] = self.to_be_bytes();
309 mutate_interesting_word_slice(&mut bytes[32 - size / 8..], test_runner)?;
310 validate_int_mutation(self, Self::from_be_bytes(bytes), size)
311 }
312
313 #[instrument(
314 name = "I256::mutate_interesting_dword",
315 level = "trace",
316 skip(size, test_runner),
317 ret
318 )]
319 fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
320 let mut bytes: [u8; 32] = self.to_be_bytes();
321 mutate_interesting_dword_slice(&mut bytes[32 - size / 8..], test_runner)?;
322 validate_int_mutation(self, Self::from_be_bytes(bytes), size)
323 }
324}
325
326impl InterestingWordMutator for Address {
327 #[instrument(
328 name = "Address::mutate_interesting_byte",
329 level = "trace",
330 skip(_size, test_runner),
331 ret
332 )]
333 fn mutate_interesting_byte(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
334 let mut mutated = self;
335 mutate_interesting_byte_slice(mutated.as_mut_slice(), test_runner)?;
336 (self != mutated).then_some(mutated)
337 }
338
339 #[instrument(
340 name = "Address::mutate_interesting_word",
341 level = "trace",
342 skip(_size, test_runner),
343 ret
344 )]
345 fn mutate_interesting_word(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
346 let mut mutated = self;
347 mutate_interesting_word_slice(mutated.as_mut_slice(), test_runner)?;
348 (self != mutated).then_some(mutated)
349 }
350
351 #[instrument(
352 name = "Address::mutate_interesting_dword",
353 level = "trace",
354 skip(_size, test_runner),
355 ret
356 )]
357 fn mutate_interesting_dword(self, _size: usize, test_runner: &mut TestRunner) -> Option<Self> {
358 let mut mutated = self;
359 mutate_interesting_dword_slice(mutated.as_mut_slice(), test_runner)?;
360 (self != mutated).then_some(mutated)
361 }
362}
363
364impl InterestingWordMutator for Word {
365 #[instrument(
366 name = "Word::mutate_interesting_byte",
367 level = "trace",
368 skip(size, test_runner),
369 ret
370 )]
371 fn mutate_interesting_byte(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
372 let mut bytes = self;
373 let slice = &mut bytes[..size];
374 mutate_interesting_byte_slice(slice, test_runner)?;
375 (self != bytes).then_some(bytes)
376 }
377
378 #[instrument(
379 name = "Word::mutate_interesting_word",
380 level = "trace",
381 skip(size, test_runner),
382 ret
383 )]
384 fn mutate_interesting_word(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
385 let mut bytes = self;
386 let slice = &mut bytes[..size];
387 mutate_interesting_word_slice(slice, test_runner)?;
388 (self != bytes).then_some(bytes)
389 }
390
391 #[instrument(
392 name = "Word::mutate_interesting_dword",
393 level = "trace",
394 skip(size, test_runner),
395 ret
396 )]
397 fn mutate_interesting_dword(self, size: usize, test_runner: &mut TestRunner) -> Option<Self> {
398 let mut bytes = self;
399 let slice = &mut bytes[..size];
400 mutate_interesting_dword_slice(slice, test_runner)?;
401 (self != bytes).then_some(bytes)
402 }
403}
404
405fn flip_random_bit_in_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
407 if bytes.is_empty() {
408 return None;
409 }
410 let bit_index = test_runner.rng().random_range(0..(bytes.len() * 8));
411 bytes[bit_index / 8] ^= 1 << (bit_index % 8);
412 Some(())
413}
414
415fn mutate_interesting_byte_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
418 let index = test_runner.rng().random_range(0..bytes.len());
419 let val = *INTERESTING_8.choose(&mut test_runner.rng())? as u8;
420 bytes[index] = val;
421 Some(())
422}
423
424fn mutate_interesting_word_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
427 if bytes.len() < 2 {
428 return None;
429 }
430 let index = test_runner.rng().random_range(0..=bytes.len() - 2);
431 let val = *INTERESTING_16.choose(&mut test_runner.rng())? as u16;
432 bytes[index..index + 2].copy_from_slice(&val.to_be_bytes());
433 Some(())
434}
435
436fn mutate_interesting_dword_slice(bytes: &mut [u8], test_runner: &mut TestRunner) -> Option<()> {
439 if bytes.len() < 4 {
440 return None;
441 }
442 let index = test_runner.rng().random_range(0..=bytes.len() - 4);
443 let val = *INTERESTING_32.choose(&mut test_runner.rng())? as u32;
444 bytes[index..index + 4].copy_from_slice(&val.to_be_bytes());
445 Some(())
446}
447
448fn sample_gaussian_scale<R: Rng>(rng: &mut R) -> Option<f64> {
459 let num_samples = 8;
460 let chosen_3rd_sigma = *THREE_SIGMA_MULTIPLIERS.choose(rng).unwrap_or(&1.0);
461
462 let mut sum = 0.0;
463 for _ in 0..num_samples {
464 sum += rng.random::<f64>();
465 }
466
467 let standard_normal = sum - (num_samples as f64 / 2.0);
468 let mut scale_factor = (chosen_3rd_sigma / 3.0) * standard_normal;
469 scale_factor += 1.0;
470
471 if scale_factor < 0.0 || (scale_factor - 1.0).abs() < f64::EPSILON {
472 None
473 } else {
474 Some(scale_factor)
475 }
476}
477
478fn apply_scale_to_bytes(bytes: &mut [u8], scale_factor: f64) -> Option<()> {
481 let mut carry_down = 0.0;
482
483 for i in (0..bytes.len()).rev() {
484 let byte_val = bytes[i] as f64;
485 let scaled = (byte_val + carry_down * 256.0) * scale_factor;
486
487 if i == 0 && scaled >= 256.0 {
488 for b in bytes.iter_mut() {
489 *b = 0xFF;
490 }
491 return Some(());
492 }
493
494 bytes[i] = (scaled % 256.0).floor() as u8;
495
496 let mut carry_up = (scaled / 256.0).floor();
497 carry_down = (scaled % 1.0) / scale_factor;
498
499 let mut j = i;
500 while carry_up > 0.0 && j > 0 {
502 j -= 1;
503 let new_val = bytes[j] as f64 + carry_up;
504 if j == 0 && new_val >= 256.0 {
505 for b in bytes.iter_mut() {
506 *b = 0xFF;
507 }
508 return Some(());
509 }
510 bytes[j] = (new_val % 256.0).floor() as u8;
511 carry_up = (new_val / 256.0).floor();
512 }
513 }
514
515 Some(())
516}
517
518fn validate_uint_mutation(original: U256, mutated: U256, size: usize) -> Option<U256> {
521 if mutated == original {
523 return None;
524 }
525
526 let max = if size < 256 { (U256::from(1) << size) - U256::from(1) } else { U256::MAX };
528 (mutated <= max).then_some(mutated)
529}
530
531fn validate_int_mutation(original: I256, mutated: I256, size: usize) -> Option<I256> {
534 if mutated == original {
536 return None;
537 }
538
539 let max = I256::overflowing_from_sign_and_abs(
542 Sign::Positive,
543 (U256::from(1) << (size - 1)) - U256::from(1),
544 )
545 .0;
546 let min = I256::overflowing_from_sign_and_abs(Sign::Negative, U256::from(1) << (size - 1)).0;
547 match mutated.sign() {
548 Sign::Positive => mutated <= max,
549 Sign::Negative => mutated >= min,
550 }
551 .then_some(mutated)
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use proptest::test_runner::Config;
558
559 #[test]
560 fn validate_uint_mutation_accepts_inclusive_max() {
561 for size in [8usize, 16, 32, 64, 128] {
564 let max = (U256::from(1) << size) - U256::from(1);
565 assert_eq!(
566 super::validate_uint_mutation(U256::ZERO, max, size),
567 Some(max),
568 "size={size}"
569 );
570 }
571 let original = U256::from(1);
572 assert_eq!(super::validate_uint_mutation(original, U256::MAX, 256), Some(U256::MAX));
573 }
574
575 #[test]
576 fn validate_int_mutation_accepts_inclusive_bounds() {
577 for size in [8usize, 16, 32, 64, 128] {
580 let max = I256::overflowing_from_sign_and_abs(
581 Sign::Positive,
582 (U256::from(1) << (size - 1)) - U256::from(1),
583 )
584 .0;
585 let min =
586 I256::overflowing_from_sign_and_abs(Sign::Negative, U256::from(1) << (size - 1)).0;
587 assert_eq!(
588 super::validate_int_mutation(I256::ZERO, max, size),
589 Some(max),
590 "size={size} max"
591 );
592 assert_eq!(
593 super::validate_int_mutation(I256::ZERO, min, size),
594 Some(min),
595 "size={size} min"
596 );
597 }
598 assert_eq!(super::validate_int_mutation(I256::ZERO, I256::MAX, 256), Some(I256::MAX));
599 assert_eq!(super::validate_int_mutation(I256::ZERO, I256::MIN, 256), Some(I256::MIN));
600 }
601
602 #[test]
603 fn test_mutate_uint() {
604 let mut runner = TestRunner::new(Config::default());
605 let size = 32;
606
607 let test_values =
608 vec![U256::ZERO, U256::ONE, U256::from(12345u64), U256::from(255), U256::MAX];
609
610 #[track_caller]
611 fn validate_mutation(value: U256, mutated: Option<U256>) {
612 assert!(
613 mutated.is_none() || mutated.is_some_and(|m| m != value),
614 "Mutation failed: value = {value:?}, mutated = {mutated:?}"
615 );
616 }
617
618 for value in test_values {
619 for _ in 0..100 {
620 validate_mutation(value, U256::increment_decrement(value, size, &mut runner));
621 validate_mutation(value, U256::flip_random_bit(value, size, &mut runner));
622 validate_mutation(value, U256::mutate_interesting_byte(value, size, &mut runner));
623 validate_mutation(value, U256::mutate_interesting_word(value, size, &mut runner));
624 validate_mutation(value, U256::mutate_interesting_dword(value, size, &mut runner));
625 }
626 }
627 }
628
629 #[test]
630 fn test_mutate_int() {
631 let mut runner = TestRunner::new(Config::default());
632 let size = 32;
633
634 let test_values = vec![
635 I256::ZERO,
636 I256::ONE,
637 I256::MINUS_ONE,
638 I256::from_dec_str("12345").unwrap(),
639 I256::from_dec_str("-54321").unwrap(),
640 I256::from_dec_str("340282366920938463463374607431768211455").unwrap(),
641 I256::from_dec_str("-340282366920938463463374607431768211455").unwrap(),
642 ];
643
644 #[track_caller]
645 fn validate_mutation(value: I256, mutated: Option<I256>) {
646 assert!(
647 mutated.is_none() || mutated.is_some_and(|m| m != value),
648 "Mutation failed: value = {value:?}, mutated = {mutated:?}"
649 );
650 }
651
652 for value in test_values {
653 for _ in 0..100 {
654 validate_mutation(value, I256::increment_decrement(value, size, &mut runner));
655 validate_mutation(value, I256::flip_random_bit(value, size, &mut runner));
656 validate_mutation(value, I256::mutate_interesting_byte(value, size, &mut runner));
657 validate_mutation(value, I256::mutate_interesting_word(value, size, &mut runner));
658 validate_mutation(value, I256::mutate_interesting_dword(value, size, &mut runner));
659 }
660 }
661 }
662
663 #[test]
664 fn test_mutate_address() {
665 let mut runner = TestRunner::new(Config::default());
666 let value = Address::random();
667
668 #[track_caller]
669 fn validate_mutation(value: Address, mutated: Option<Address>) {
670 assert!(
671 mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
672 "Mutation failed for value: {value:?}, result: {mutated:?}"
673 );
674 }
675
676 for _ in 0..100 {
677 validate_mutation(value, Address::flip_random_bit(value, 20, &mut runner));
678 validate_mutation(value, Address::mutate_interesting_byte(value, 20, &mut runner));
679 validate_mutation(value, Address::mutate_interesting_word(value, 20, &mut runner));
680 validate_mutation(value, Address::mutate_interesting_dword(value, 20, &mut runner));
681 }
682 }
683
684 #[test]
685 fn test_mutate_word() {
686 let mut runner = TestRunner::new(Config::default());
687 let value = Word::random();
688
689 #[track_caller]
690 fn validate_mutation(value: Word, mutated: Option<Word>) {
691 assert!(
692 mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
693 "Mutation failed for value: {value:?}, result: {mutated:?}"
694 );
695 }
696
697 for _ in 0..100 {
698 validate_mutation(value, Word::flip_random_bit(value, 32, &mut runner));
699 validate_mutation(value, Word::mutate_interesting_byte(value, 32, &mut runner));
700 validate_mutation(value, Word::mutate_interesting_word(value, 32, &mut runner));
701 validate_mutation(value, Word::mutate_interesting_dword(value, 32, &mut runner));
702 }
703 }
704
705 #[test]
706 fn test_mutate_interesting_word_too_small_returns_none() {
707 let mut runner = TestRunner::new(Config::default());
708 let value = U256::from(123);
709 assert!(U256::mutate_interesting_word(value, 8, &mut runner).is_none());
710 }
711
712 #[test]
713 fn test_mutate_interesting_dword_too_small_returns_none() {
714 let mut runner = TestRunner::new(Config::default());
715 let value = I256::from_dec_str("123").unwrap();
716 assert!(I256::mutate_interesting_dword(value, 16, &mut runner).is_none());
717 }
718
719 #[test]
720 fn test_u256_bound() {
721 let mut runner = TestRunner::new(Config::default());
722 let min = U256::from(0u64);
723 let max = U256::from(200u64);
724 let original = U256::from(100u64);
725
726 for _ in 0..50 {
727 let result = original.bound(min, max, &mut runner);
728 assert!(result.is_some(), "Mutation should occur");
729
730 let mutated = result.unwrap();
731 assert!(mutated >= min, "Mutated value >= min");
732 assert!(mutated <= max, "Mutated value <= max");
733 assert_ne!(mutated, original, "mutated value should differ from original");
734 }
735
736 let result = original.bound(U256::MIN, U256::MAX, &mut runner);
738 assert!(result.is_some(), "Mutation should occur");
739 }
740
741 #[test]
742 fn test_i256_bound() {
743 let mut runner = TestRunner::new(Config::default());
744 let min = I256::from_dec_str("-100").unwrap();
745 let max = I256::from_dec_str("100").unwrap();
746 let original = I256::from_dec_str("10").unwrap();
747
748 for _ in 0..50 {
749 let result = original.bound(min, max, &mut runner);
750 assert!(result.is_some(), "Mutation should occur");
751
752 let mutated = result.unwrap();
753 assert!(mutated >= min, "Mutated value >= min");
754 assert!(mutated <= max, "Mutated value <= max");
755 assert_ne!(mutated, original, "Mutated value should not equal current");
756 }
757
758 let result = original.bound(I256::MIN, I256::MAX, &mut runner);
760 assert!(result.is_some(), "Mutation should occur");
761 }
762}