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_abs = (U256::from(1) << (size - 1)) - U256::from(1);
541 match mutated.sign() {
542 Sign::Positive => mutated < I256::overflowing_from_sign_and_abs(Sign::Positive, max_abs).0,
543 Sign::Negative => mutated > I256::overflowing_from_sign_and_abs(Sign::Negative, max_abs).0,
544 }
545 .then_some(mutated)
546}
547
548#[cfg(test)]
549mod tests {
550 use super::*;
551 use proptest::test_runner::Config;
552
553 #[test]
554 fn validate_uint_mutation_accepts_inclusive_max() {
555 for size in [8usize, 16, 32, 64, 128] {
558 let max = (U256::from(1) << size) - U256::from(1);
559 assert_eq!(
560 super::validate_uint_mutation(U256::ZERO, max, size),
561 Some(max),
562 "size={size}"
563 );
564 }
565 let original = U256::from(1);
566 assert_eq!(super::validate_uint_mutation(original, U256::MAX, 256), Some(U256::MAX));
567 }
568
569 #[test]
570 fn test_mutate_uint() {
571 let mut runner = TestRunner::new(Config::default());
572 let size = 32;
573
574 let test_values =
575 vec![U256::ZERO, U256::ONE, U256::from(12345u64), U256::from(255), U256::MAX];
576
577 #[track_caller]
578 fn validate_mutation(value: U256, mutated: Option<U256>) {
579 assert!(
580 mutated.is_none() || mutated.is_some_and(|m| m != value),
581 "Mutation failed: value = {value:?}, mutated = {mutated:?}"
582 );
583 }
584
585 for value in test_values {
586 for _ in 0..100 {
587 validate_mutation(value, U256::increment_decrement(value, size, &mut runner));
588 validate_mutation(value, U256::flip_random_bit(value, size, &mut runner));
589 validate_mutation(value, U256::mutate_interesting_byte(value, size, &mut runner));
590 validate_mutation(value, U256::mutate_interesting_word(value, size, &mut runner));
591 validate_mutation(value, U256::mutate_interesting_dword(value, size, &mut runner));
592 }
593 }
594 }
595
596 #[test]
597 fn test_mutate_int() {
598 let mut runner = TestRunner::new(Config::default());
599 let size = 32;
600
601 let test_values = vec![
602 I256::ZERO,
603 I256::ONE,
604 I256::MINUS_ONE,
605 I256::from_dec_str("12345").unwrap(),
606 I256::from_dec_str("-54321").unwrap(),
607 I256::from_dec_str("340282366920938463463374607431768211455").unwrap(),
608 I256::from_dec_str("-340282366920938463463374607431768211455").unwrap(),
609 ];
610
611 #[track_caller]
612 fn validate_mutation(value: I256, mutated: Option<I256>) {
613 assert!(
614 mutated.is_none() || mutated.is_some_and(|m| m != value),
615 "Mutation failed: value = {value:?}, mutated = {mutated:?}"
616 );
617 }
618
619 for value in test_values {
620 for _ in 0..100 {
621 validate_mutation(value, I256::increment_decrement(value, size, &mut runner));
622 validate_mutation(value, I256::flip_random_bit(value, size, &mut runner));
623 validate_mutation(value, I256::mutate_interesting_byte(value, size, &mut runner));
624 validate_mutation(value, I256::mutate_interesting_word(value, size, &mut runner));
625 validate_mutation(value, I256::mutate_interesting_dword(value, size, &mut runner));
626 }
627 }
628 }
629
630 #[test]
631 fn test_mutate_address() {
632 let mut runner = TestRunner::new(Config::default());
633 let value = Address::random();
634
635 #[track_caller]
636 fn validate_mutation(value: Address, mutated: Option<Address>) {
637 assert!(
638 mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
639 "Mutation failed for value: {value:?}, result: {mutated:?}"
640 );
641 }
642
643 for _ in 0..100 {
644 validate_mutation(value, Address::flip_random_bit(value, 20, &mut runner));
645 validate_mutation(value, Address::mutate_interesting_byte(value, 20, &mut runner));
646 validate_mutation(value, Address::mutate_interesting_word(value, 20, &mut runner));
647 validate_mutation(value, Address::mutate_interesting_dword(value, 20, &mut runner));
648 }
649 }
650
651 #[test]
652 fn test_mutate_word() {
653 let mut runner = TestRunner::new(Config::default());
654 let value = Word::random();
655
656 #[track_caller]
657 fn validate_mutation(value: Word, mutated: Option<Word>) {
658 assert!(
659 mutated.is_none() || mutated.is_some_and(|mutated| mutated != value),
660 "Mutation failed for value: {value:?}, result: {mutated:?}"
661 );
662 }
663
664 for _ in 0..100 {
665 validate_mutation(value, Word::flip_random_bit(value, 32, &mut runner));
666 validate_mutation(value, Word::mutate_interesting_byte(value, 32, &mut runner));
667 validate_mutation(value, Word::mutate_interesting_word(value, 32, &mut runner));
668 validate_mutation(value, Word::mutate_interesting_dword(value, 32, &mut runner));
669 }
670 }
671
672 #[test]
673 fn test_mutate_interesting_word_too_small_returns_none() {
674 let mut runner = TestRunner::new(Config::default());
675 let value = U256::from(123);
676 assert!(U256::mutate_interesting_word(value, 8, &mut runner).is_none());
677 }
678
679 #[test]
680 fn test_mutate_interesting_dword_too_small_returns_none() {
681 let mut runner = TestRunner::new(Config::default());
682 let value = I256::from_dec_str("123").unwrap();
683 assert!(I256::mutate_interesting_dword(value, 16, &mut runner).is_none());
684 }
685
686 #[test]
687 fn test_u256_bound() {
688 let mut runner = TestRunner::new(Config::default());
689 let min = U256::from(0u64);
690 let max = U256::from(200u64);
691 let original = U256::from(100u64);
692
693 for _ in 0..50 {
694 let result = original.bound(min, max, &mut runner);
695 assert!(result.is_some(), "Mutation should occur");
696
697 let mutated = result.unwrap();
698 assert!(mutated >= min, "Mutated value >= min");
699 assert!(mutated <= max, "Mutated value <= max");
700 assert_ne!(mutated, original, "mutated value should differ from original");
701 }
702
703 let result = original.bound(U256::MIN, U256::MAX, &mut runner);
705 assert!(result.is_some(), "Mutation should occur");
706 }
707
708 #[test]
709 fn test_i256_bound() {
710 let mut runner = TestRunner::new(Config::default());
711 let min = I256::from_dec_str("-100").unwrap();
712 let max = I256::from_dec_str("100").unwrap();
713 let original = I256::from_dec_str("10").unwrap();
714
715 for _ in 0..50 {
716 let result = original.bound(min, max, &mut runner);
717 assert!(result.is_some(), "Mutation should occur");
718
719 let mutated = result.unwrap();
720 assert!(mutated >= min, "Mutated value >= min");
721 assert!(mutated <= max, "Mutated value <= max");
722 assert_ne!(mutated, original, "Mutated value should not equal current");
723 }
724
725 let result = original.bound(I256::MIN, I256::MAX, &mut runner);
727 assert!(result.is_some(), "Mutation should occur");
728 }
729}