Skip to main content

jugar_probar/coverage/
executor.rs

1//! Coverage Executor with Work-Stealing (Heijunka)
2//!
3//! Per spec ยง5.4: Heijunka-balanced coverage executor
4//!
5//! Uses work-stealing scheduler for parallel coverage collection.
6
7use super::{CoverageReport, Superblock, SuperblockId};
8
9/// Result of executing a superblock
10#[derive(Debug, Clone)]
11pub struct SuperblockResult {
12    /// Superblock that was executed
13    pub id: SuperblockId,
14    /// Whether execution succeeded
15    pub success: bool,
16    /// Error message if failed
17    pub error: Option<String>,
18}
19
20/// Heijunka-balanced coverage executor with superblock scheduling
21#[derive(Debug)]
22pub struct CoverageExecutor {
23    /// Superblocks to execute
24    superblocks: Vec<Superblock>,
25    /// Number of workers
26    worker_count: usize,
27    /// Enable work stealing
28    work_stealing: bool,
29}
30
31impl CoverageExecutor {
32    /// Create a new executor
33    #[must_use]
34    pub fn new(superblocks: Vec<Superblock>) -> Self {
35        Self {
36            superblocks,
37            worker_count: num_cpus(),
38            work_stealing: true,
39        }
40    }
41
42    /// Set the number of workers
43    #[must_use]
44    pub fn with_workers(mut self, count: usize) -> Self {
45        self.worker_count = count;
46        self
47    }
48
49    /// Enable or disable work stealing
50    #[must_use]
51    pub fn with_work_stealing(mut self, enabled: bool) -> Self {
52        self.work_stealing = enabled;
53        self
54    }
55
56    /// Execute coverage collection for all superblocks
57    ///
58    /// In a full implementation, this would use Simular's WorkStealingMonteCarlo
59    /// for parallel execution.
60    pub fn execute<F>(&self, test_fn: F) -> CoverageReport
61    where
62        F: Fn(&Superblock) -> SuperblockResult + Send + Sync,
63    {
64        // Calculate total blocks
65        let total_blocks = self.superblocks.iter().map(|sb| sb.block_count()).sum();
66
67        let mut report = CoverageReport::new(total_blocks);
68
69        // Execute each superblock (sequentially for now)
70        // In production, this would use work-stealing parallel execution
71        for superblock in &self.superblocks {
72            let result = test_fn(superblock);
73            if result.success {
74                // Record hits for all blocks in the superblock
75                for block in superblock.iter() {
76                    report.record_hit(*block);
77                }
78            }
79        }
80
81        report
82    }
83
84    /// Get the number of superblocks
85    #[must_use]
86    pub fn superblock_count(&self) -> usize {
87        self.superblocks.len()
88    }
89
90    /// Get the total number of blocks across all superblocks
91    #[must_use]
92    pub fn total_block_count(&self) -> usize {
93        self.superblocks.iter().map(|sb| sb.block_count()).sum()
94    }
95
96    /// Get the worker count
97    #[must_use]
98    pub fn worker_count(&self) -> usize {
99        self.worker_count
100    }
101}
102
103/// Get the number of CPUs (simplified)
104fn num_cpus() -> usize {
105    // In a real implementation, this would detect CPU count
106    4
107}