Skip to main content

cbtop/fuzz/
edge_cases.rs

1//! Utility functions and edge-case testers for fuzz testing
2
3use std::fmt;
4
5/// Safe division that returns None on division by zero
6pub fn safe_div(a: f64, b: f64) -> Option<f64> {
7    if b == 0.0 || b.is_nan() {
8        None
9    } else {
10        let result = a / b;
11        if result.is_nan() || result.is_infinite() {
12            None
13        } else {
14            Some(result)
15        }
16    }
17}
18
19/// Checked addition that returns None on overflow
20pub fn checked_add_u64(a: u64, b: u64) -> Option<u64> {
21    a.checked_add(b)
22}
23
24/// Checked multiplication that returns None on overflow
25pub fn checked_mul_u64(a: u64, b: u64) -> Option<u64> {
26    a.checked_mul(b)
27}
28
29/// Bound a value to a range
30pub fn bound_value(value: f64, min: f64, max: f64) -> f64 {
31    if value.is_nan() {
32        (min + max) / 2.0
33    } else {
34        value.clamp(min, max)
35    }
36}
37
38/// Sanitize a float value for safe computation
39pub fn sanitize_float(value: f64) -> f64 {
40    if value.is_nan() {
41        0.0
42    } else if value.is_infinite() {
43        if value > 0.0 {
44            f64::MAX
45        } else {
46            f64::MIN
47        }
48    } else {
49        value
50    }
51}
52
53/// Test a function with edge case float inputs
54pub fn test_float_edge_cases<F, T>(f: F) -> Vec<(f64, Result<T, String>)>
55where
56    F: Fn(f64) -> T,
57    T: fmt::Debug,
58{
59    let edge_cases = [
60        0.0,
61        -0.0,
62        1.0,
63        -1.0,
64        f64::MIN,
65        f64::MAX,
66        f64::MIN_POSITIVE,
67        f64::EPSILON,
68        f64::NAN,
69        f64::INFINITY,
70        f64::NEG_INFINITY,
71        1e15,
72        -1e15,
73        1e-15,
74        -1e-15,
75    ];
76
77    edge_cases
78        .iter()
79        .map(|&x| {
80            let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(x)));
81            match result {
82                Ok(v) => (x, Ok(v)),
83                Err(e) => {
84                    let msg = if let Some(s) = e.downcast_ref::<&str>() {
85                        s.to_string()
86                    } else if let Some(s) = e.downcast_ref::<String>() {
87                        s.clone()
88                    } else {
89                        "Unknown panic".to_string()
90                    };
91                    (x, Err(msg))
92                }
93            }
94        })
95        .collect()
96}
97
98/// Test a function with edge case u64 inputs
99pub fn test_u64_edge_cases<F, T>(f: F) -> Vec<(u64, Result<T, String>)>
100where
101    F: Fn(u64) -> T,
102    T: fmt::Debug,
103{
104    let edge_cases = [
105        0u64,
106        1,
107        u64::MAX,
108        u64::MAX - 1,
109        u64::MAX / 2,
110        1000,
111        1_000_000,
112        1_000_000_000,
113    ];
114
115    edge_cases
116        .iter()
117        .map(|&x| {
118            let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| f(x)));
119            match result {
120                Ok(v) => (x, Ok(v)),
121                Err(e) => {
122                    let msg = if let Some(s) = e.downcast_ref::<&str>() {
123                        s.to_string()
124                    } else if let Some(s) = e.downcast_ref::<String>() {
125                        s.clone()
126                    } else {
127                        "Unknown panic".to_string()
128                    };
129                    (x, Err(msg))
130                }
131            }
132        })
133        .collect()
134}