Skip to main content

cbtop/fuzz/
suite.rs

1//! Fuzz suite, target config, and summary types
2
3use super::types::FuzzResult;
4use std::collections::HashMap;
5
6/// Fuzz target configuration
7#[derive(Debug, Clone)]
8pub struct FuzzTargetConfig {
9    /// Target name
10    pub name: String,
11    /// Number of iterations
12    pub iterations: u64,
13    /// Timeout in seconds
14    pub timeout_secs: u64,
15    /// Seed for reproducibility (None = random)
16    pub seed: Option<u64>,
17    /// Enable coverage tracking
18    pub track_coverage: bool,
19}
20
21impl FuzzTargetConfig {
22    /// Create a new fuzz target config
23    pub fn new(name: &str) -> Self {
24        Self {
25            name: name.to_string(),
26            iterations: 10000,
27            timeout_secs: 60,
28            seed: None,
29            track_coverage: true,
30        }
31    }
32
33    /// Set number of iterations
34    pub fn with_iterations(mut self, iterations: u64) -> Self {
35        self.iterations = iterations;
36        self
37    }
38
39    /// Set timeout
40    pub fn with_timeout(mut self, timeout_secs: u64) -> Self {
41        self.timeout_secs = timeout_secs;
42        self
43    }
44
45    /// Set seed for reproducibility
46    pub fn with_seed(mut self, seed: u64) -> Self {
47        self.seed = Some(seed);
48        self
49    }
50}
51
52/// Fuzz testing suite for cbtop components
53#[derive(Debug, Default)]
54pub struct FuzzSuite {
55    /// Results by target name
56    results: HashMap<String, FuzzResult>,
57    /// Total test cases
58    total_cases: u64,
59    /// Total failures
60    total_failures: u64,
61}
62
63impl FuzzSuite {
64    /// Create a new fuzz suite
65    pub fn new() -> Self {
66        Self::default()
67    }
68
69    /// Add a result
70    pub fn add_result(&mut self, result: FuzzResult) {
71        self.total_cases += result.test_cases;
72        self.total_failures += result.failures;
73        self.results.insert(result.target.clone(), result);
74    }
75
76    /// Get result for a target
77    pub fn get_result(&self, target: &str) -> Option<&FuzzResult> {
78        self.results.get(target)
79    }
80
81    /// Check if all tests passed
82    pub fn all_passed(&self) -> bool {
83        self.total_failures == 0
84    }
85
86    /// Get total test cases
87    pub fn total_cases(&self) -> u64 {
88        self.total_cases
89    }
90
91    /// Get total failures
92    pub fn total_failures(&self) -> u64 {
93        self.total_failures
94    }
95
96    /// Get all results
97    pub fn results(&self) -> &HashMap<String, FuzzResult> {
98        &self.results
99    }
100
101    /// Generate summary report
102    pub fn summary(&self) -> FuzzSummary {
103        let targets_passed = self.results.values().filter(|r| r.passed()).count();
104        let avg_coverage = if self.results.is_empty() {
105            0.0
106        } else {
107            self.results
108                .values()
109                .map(|r| r.coverage_percent)
110                .sum::<f64>()
111                / self.results.len() as f64
112        };
113
114        FuzzSummary {
115            total_targets: self.results.len(),
116            targets_passed,
117            total_cases: self.total_cases,
118            total_failures: self.total_failures,
119            avg_coverage,
120            overall_passed: self.all_passed(),
121        }
122    }
123}
124
125/// Summary of fuzz testing results
126#[derive(Debug, Clone)]
127pub struct FuzzSummary {
128    /// Total number of fuzz targets
129    pub total_targets: usize,
130    /// Number of targets that passed
131    pub targets_passed: usize,
132    /// Total test cases across all targets
133    pub total_cases: u64,
134    /// Total failures across all targets
135    pub total_failures: u64,
136    /// Average coverage percentage
137    pub avg_coverage: f64,
138    /// Whether all tests passed
139    pub overall_passed: bool,
140}
141
142impl FuzzSummary {
143    /// Get pass rate as percentage
144    pub fn pass_rate(&self) -> f64 {
145        if self.total_targets == 0 {
146            100.0
147        } else {
148            (self.targets_passed as f64 / self.total_targets as f64) * 100.0
149        }
150    }
151}