Fuzzing
Probar includes fuzzing support for finding edge cases in game logic.
Random Walk Agent
#![allow(unused)] fn main() { use jugar_probar::{RandomWalkAgent, Seed}; let seed = Seed::from_u64(12345); let mut agent = RandomWalkAgent::new(seed); // Generate random inputs for each frame for frame in 0..1000 { let inputs = agent.next_inputs(); platform.process_inputs(&inputs); platform.advance_frame(1.0 / 60.0); } }
Fuzzing with Invariants
#![allow(unused)] fn main() { use jugar_probar::{fuzz_with_invariants, FuzzConfig, Invariant}; let invariants = vec![ Invariant::new("no_crashes", |state| state.is_valid()), Invariant::new("ball_visible", |state| { state.ball.x.is_finite() && state.ball.y.is_finite() }), Invariant::new("score_bounded", |state| { state.score_left <= 100 && state.score_right <= 100 }), ]; let config = FuzzConfig { iterations: 1000, frames_per_iteration: 500, seed: 42, }; let result = fuzz_with_invariants(config, invariants); if !result.all_passed { for failure in &result.failures { println!("Invariant '{}' failed at iteration {} frame {}", failure.invariant_name, failure.iteration, failure.frame); println!("Reproducing seed: {}", failure.seed); } } }
Input Generation Strategies
#![allow(unused)] fn main() { // Random inputs let mut agent = RandomWalkAgent::new(seed); // Biased toward movement let mut agent = RandomWalkAgent::new(seed) .with_key_probability("KeyW", 0.3) .with_key_probability("KeyS", 0.3) .with_key_probability("Space", 0.1); // Chaos monkey (random everything) let mut agent = ChaosAgent::new(seed); // Adversarial (try to break the game) let mut agent = AdversarialAgent::new(seed) .target_invariant(|state| state.ball.y >= 0.0); }
Property-Based Testing
#![allow(unused)] fn main() { use proptest::prelude::*; proptest! { #[test] fn ball_stays_in_bounds(seed in 0u64..10000) { let config = SimulationConfig::new(seed, 1000); let result = run_simulation(config, |_| vec![]); prop_assert!(result.final_state.ball.x >= 0.0); prop_assert!(result.final_state.ball.x <= 800.0); prop_assert!(result.final_state.ball.y >= 0.0); prop_assert!(result.final_state.ball.y <= 600.0); } #[test] fn score_is_valid( seed in 0u64..10000, frames in 100u32..5000 ) { let config = SimulationConfig::new(seed, frames); let result = run_simulation(config, |_| vec![]); prop_assert!(result.final_state.score_left <= 10); prop_assert!(result.final_state.score_right <= 10); } } }
Seed Management
#![allow(unused)] fn main() { use jugar_probar::Seed; // From u64 let seed = Seed::from_u64(42); // From bytes let seed = Seed::from_bytes(&[1, 2, 3, 4, 5, 6, 7, 8]); // Random let seed = Seed::random(); // Get value for reproduction println!("Failing seed: {}", seed.as_u64()); }
Reproducing Failures
When fuzzing finds a failure, reproduce it:
#![allow(unused)] fn main() { #[test] fn reproduce_bug_12345() { // Seed from fuzzing failure let seed = Seed::from_u64(12345); let config = SimulationConfig::new(seed.as_u64(), 500); let result = run_simulation(config, |_| vec![]); // This should fail with the original bug assert!(result.final_state.ball.y >= 0.0); } }
Fuzzing Configuration
#![allow(unused)] fn main() { pub struct FuzzConfig { pub iterations: u32, // Number of random runs pub frames_per_iteration: u32, // Frames per run pub seed: u64, // Base seed pub timeout_seconds: u32, // Max time per iteration pub parallel: bool, // Run in parallel pub save_failures: bool, // Save failing cases } let config = FuzzConfig { iterations: 10000, frames_per_iteration: 1000, seed: 42, timeout_seconds: 10, parallel: true, save_failures: true, }; }
Shrinking
When a failure is found, Probar automatically shrinks the input:
#![allow(unused)] fn main() { let result = fuzz_with_shrinking(config, invariants); if let Some(failure) = result.first_failure { println!("Original failure at frame {}", failure.original_frame); println!("Shrunk to frame {}", failure.shrunk_frame); println!("Minimal inputs: {:?}", failure.minimal_inputs); } }
Continuous Fuzzing
Run fuzzing in CI:
# Run fuzzing for 10 minutes
FUZZ_DURATION=600 cargo test fuzz_ -- --ignored
# Or via make
make fuzz-ci