#![allow(clippy::field_reassign_with_default)]
use super::{CompilationOutcome, DecisionTrace};
#[derive(Debug, Clone)]
#[allow(dead_code)] pub(crate) struct Session {
pub(crate) id: String,
pub(crate) decisions: Vec<DecisionTrace>,
pub(crate) outcome: CompilationOutcome,
pub(crate) fix_diff: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct DecisionStats {
pub success_count: u32,
pub fail_count: u32,
pub total_success: u32,
pub total_fail: u32,
}
impl DecisionStats {
#[must_use]
pub fn tarantula_score(&self) -> f32 {
if self.total_fail == 0 || self.fail_count == 0 {
return 0.0;
}
let fail_freq = self.fail_count as f32 / self.total_fail as f32;
let success_freq = if self.total_success > 0 {
self.success_count as f32 / self.total_success as f32
} else {
0.0
};
if fail_freq + success_freq < f32::EPSILON {
0.0
} else {
fail_freq / (fail_freq + success_freq)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_decision_stats_tarantula() {
let mut stats = DecisionStats::default();
stats.success_count = 2;
stats.fail_count = 8;
stats.total_success = 10;
stats.total_fail = 10;
assert!((stats.tarantula_score() - 0.8).abs() < 0.01);
}
#[test]
fn test_decision_stats_tarantula_no_failures() {
let stats =
DecisionStats { success_count: 5, fail_count: 0, total_success: 5, total_fail: 0 };
assert_eq!(stats.tarantula_score(), 0.0);
}
#[test]
fn test_decision_stats_tarantula_only_failures() {
let stats =
DecisionStats { success_count: 0, fail_count: 5, total_success: 0, total_fail: 5 };
assert_eq!(stats.tarantula_score(), 1.0);
}
}
#[cfg(test)]
mod prop_tests {
use super::*;
use proptest::prelude::*;
proptest! {
#[test]
fn prop_tarantula_score_bounded(
success in 0u32..100,
fail in 0u32..100,
total_success in 1u32..100,
total_fail in 1u32..100
) {
let stats = DecisionStats {
success_count: success.min(total_success),
fail_count: fail.min(total_fail),
total_success,
total_fail,
};
let score = stats.tarantula_score();
prop_assert!(score >= 0.0);
prop_assert!(score <= 1.0);
}
}
}