Skip to main content

cbtop/fuzz/
types.rs

1//! Fuzz testing types: FuzzResult, FuzzFailure, FuzzInputValidator, FuzzValidationError
2
3use std::fmt;
4
5/// Result of a fuzz test run
6#[derive(Debug, Clone)]
7pub struct FuzzResult {
8    /// Target name
9    pub target: String,
10    /// Number of test cases run
11    pub test_cases: u64,
12    /// Number of failures found
13    pub failures: u64,
14    /// Edge coverage percentage (0-100)
15    pub coverage_percent: f64,
16    /// Specific failure details
17    pub failure_details: Vec<FuzzFailure>,
18    /// Duration of the fuzz run
19    pub duration_secs: f64,
20}
21
22impl FuzzResult {
23    /// Create a new fuzz result
24    pub fn new(target: &str) -> Self {
25        Self {
26            target: target.to_string(),
27            test_cases: 0,
28            failures: 0,
29            coverage_percent: 0.0,
30            failure_details: Vec::new(),
31            duration_secs: 0.0,
32        }
33    }
34
35    /// Record a successful test case
36    pub fn record_success(&mut self) {
37        self.test_cases += 1;
38    }
39
40    /// Record a failure
41    pub fn record_failure(&mut self, input: String, error: String) {
42        self.test_cases += 1;
43        self.failures += 1;
44        self.failure_details.push(FuzzFailure { input, error });
45    }
46
47    /// Check if all tests passed
48    pub fn passed(&self) -> bool {
49        self.failures == 0
50    }
51
52    /// Calculate failure rate
53    pub fn failure_rate(&self) -> f64 {
54        if self.test_cases == 0 {
55            0.0
56        } else {
57            (self.failures as f64 / self.test_cases as f64) * 100.0
58        }
59    }
60}
61
62/// Details of a fuzz failure
63#[derive(Debug, Clone)]
64pub struct FuzzFailure {
65    /// Input that caused the failure
66    pub input: String,
67    /// Error message
68    pub error: String,
69}
70
71/// Input validator for fuzz testing
72#[derive(Debug, Clone, Default)]
73pub struct FuzzInputValidator {
74    /// Maximum string length allowed
75    pub max_string_len: usize,
76    /// Maximum numeric value allowed
77    pub max_numeric: f64,
78    /// Minimum numeric value allowed
79    pub min_numeric: f64,
80    /// Allow NaN values
81    pub allow_nan: bool,
82    /// Allow infinity values
83    pub allow_infinity: bool,
84    /// Allow negative values
85    pub allow_negative: bool,
86    /// Allow zero
87    pub allow_zero: bool,
88}
89
90impl FuzzInputValidator {
91    /// Create a new validator with default settings
92    pub fn new() -> Self {
93        Self {
94            max_string_len: 1024,
95            max_numeric: 1e15,
96            min_numeric: -1e15,
97            allow_nan: false,
98            allow_infinity: false,
99            allow_negative: true,
100            allow_zero: true,
101        }
102    }
103
104    /// Validate a floating-point value
105    pub fn validate_float(&self, value: f64) -> Result<f64, FuzzValidationError> {
106        if value.is_nan() && !self.allow_nan {
107            return Err(FuzzValidationError::NaN);
108        }
109        if value.is_infinite() && !self.allow_infinity {
110            return Err(FuzzValidationError::Infinity);
111        }
112        if value < 0.0 && !self.allow_negative {
113            return Err(FuzzValidationError::NegativeValue(value));
114        }
115        if value == 0.0 && !self.allow_zero {
116            return Err(FuzzValidationError::ZeroValue);
117        }
118        if value > self.max_numeric {
119            return Err(FuzzValidationError::TooLarge(value));
120        }
121        if value < self.min_numeric {
122            return Err(FuzzValidationError::TooSmall(value));
123        }
124        Ok(value)
125    }
126
127    /// Validate a string
128    pub fn validate_string<'a>(&self, value: &'a str) -> Result<&'a str, FuzzValidationError> {
129        if value.len() > self.max_string_len {
130            return Err(FuzzValidationError::StringTooLong(value.len()));
131        }
132        // Check for valid UTF-8 (already guaranteed by &str, but check control chars)
133        if value
134            .chars()
135            .any(|c| c.is_control() && c != '\n' && c != '\t')
136        {
137            return Err(FuzzValidationError::InvalidControlChars);
138        }
139        Ok(value)
140    }
141
142    /// Validate an integer
143    pub fn validate_u64(&self, value: u64) -> Result<u64, FuzzValidationError> {
144        let max = self.max_numeric as u64;
145        if value > max {
146            return Err(FuzzValidationError::TooLarge(value as f64));
147        }
148        if value == 0 && !self.allow_zero {
149            return Err(FuzzValidationError::ZeroValue);
150        }
151        Ok(value)
152    }
153
154    /// Create a validator for positive-only values
155    pub fn positive_only() -> Self {
156        Self {
157            allow_negative: false,
158            allow_zero: false,
159            ..Self::new()
160        }
161    }
162
163    /// Create a validator for non-negative values
164    pub fn non_negative() -> Self {
165        Self {
166            allow_negative: false,
167            ..Self::new()
168        }
169    }
170
171    /// Create a strict validator (no special float values)
172    pub fn strict() -> Self {
173        Self {
174            allow_nan: false,
175            allow_infinity: false,
176            max_numeric: 1e12,
177            min_numeric: -1e12,
178            ..Self::new()
179        }
180    }
181}
182
183/// Validation errors from fuzz testing
184#[derive(Debug, Clone, PartialEq)]
185pub enum FuzzValidationError {
186    /// NaN value encountered
187    NaN,
188    /// Infinity value encountered
189    Infinity,
190    /// Negative value not allowed
191    NegativeValue(f64),
192    /// Zero value not allowed
193    ZeroValue,
194    /// Value too large
195    TooLarge(f64),
196    /// Value too small
197    TooSmall(f64),
198    /// String too long
199    StringTooLong(usize),
200    /// Invalid control characters
201    InvalidControlChars,
202    /// Integer overflow
203    IntegerOverflow,
204    /// Division by zero
205    DivisionByZero,
206    /// Empty input
207    EmptyInput,
208    /// Invalid format
209    InvalidFormat(String),
210}
211
212impl fmt::Display for FuzzValidationError {
213    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214        match self {
215            FuzzValidationError::NaN => write!(f, "NaN value not allowed"),
216            FuzzValidationError::Infinity => write!(f, "Infinity value not allowed"),
217            FuzzValidationError::NegativeValue(v) => write!(f, "Negative value not allowed: {}", v),
218            FuzzValidationError::ZeroValue => write!(f, "Zero value not allowed"),
219            FuzzValidationError::TooLarge(v) => write!(f, "Value too large: {}", v),
220            FuzzValidationError::TooSmall(v) => write!(f, "Value too small: {}", v),
221            FuzzValidationError::StringTooLong(len) => write!(f, "String too long: {} chars", len),
222            FuzzValidationError::InvalidControlChars => write!(f, "Invalid control characters"),
223            FuzzValidationError::IntegerOverflow => write!(f, "Integer overflow"),
224            FuzzValidationError::DivisionByZero => write!(f, "Division by zero"),
225            FuzzValidationError::EmptyInput => write!(f, "Empty input"),
226            FuzzValidationError::InvalidFormat(s) => write!(f, "Invalid format: {}", s),
227        }
228    }
229}
230
231impl std::error::Error for FuzzValidationError {}