use crate::result::{ProbarError, ProbarResult};
use std::collections::HashMap;
use std::fmt;
#[derive(Debug, Clone)]
pub struct Variable {
pub name: String,
pub value: f64,
pub unit: Option<String>,
}
impl Variable {
#[must_use]
pub fn new(name: &str, value: f64) -> Self {
Self {
name: name.to_string(),
value,
unit: None,
}
}
#[must_use]
pub fn with_unit(name: &str, value: f64, unit: &str) -> Self {
Self {
name: name.to_string(),
value,
unit: Some(unit.to_string()),
}
}
}
impl fmt::Display for Variable {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match &self.unit {
Some(unit) => write!(f, "{} = {} {}", self.name, self.value, unit),
None => write!(f, "{} = {}", self.name, self.value),
}
}
}
#[derive(Debug, Clone, Default)]
pub struct EquationContext {
variables: HashMap<String, f64>,
}
impl EquationContext {
#[must_use]
pub fn new() -> Self {
Self::default()
}
pub fn set(&mut self, name: &str, value: f64) -> &mut Self {
self.variables.insert(name.to_string(), value);
self
}
#[must_use]
pub fn get(&self, name: &str) -> Option<f64> {
self.variables.get(name).copied()
}
#[must_use]
pub fn has(&self, name: &str) -> bool {
self.variables.contains_key(name)
}
#[must_use]
pub fn variables(&self) -> Vec<&str> {
self.variables.keys().map(String::as_str).collect()
}
#[must_use]
pub fn from_variables(vars: &[Variable]) -> Self {
let mut ctx = Self::new();
for var in vars {
ctx.set(&var.name, var.value);
}
ctx
}
}
#[derive(Debug, Clone)]
pub struct EquationResult {
pub name: String,
pub passed: bool,
pub expected: f64,
pub actual: f64,
pub tolerance: f64,
pub difference: f64,
pub relative_difference: f64,
pub message: String,
}
impl EquationResult {
#[must_use]
fn new(name: &str, expected: f64, actual: f64, tolerance: f64) -> Self {
let difference = (expected - actual).abs();
let relative_difference = if expected.abs() > f64::EPSILON {
(difference / expected.abs()) * 100.0
} else {
0.0
};
let passed = difference <= tolerance;
let message = if passed {
format!(
"{}: expected {} ≈ {} (diff: {:.6}, tolerance: {})",
name, expected, actual, difference, tolerance
)
} else {
format!(
"{}: FAILED - expected {} but got {} (diff: {:.6} > tolerance: {})",
name, expected, actual, difference, tolerance
)
};
Self {
name: name.to_string(),
passed,
expected,
actual,
tolerance,
difference,
relative_difference,
message,
}
}
}
impl fmt::Display for EquationResult {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
#[derive(Debug)]
pub struct EquationVerifier {
name: String,
tolerance: f64,
results: Vec<EquationResult>,
}
impl EquationVerifier {
#[must_use]
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
tolerance: 1e-6,
results: Vec::new(),
}
}
#[must_use]
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.tolerance = tolerance;
self
}
#[must_use]
pub fn name(&self) -> &str {
&self.name
}
#[must_use]
pub fn tolerance(&self) -> f64 {
self.tolerance
}
pub fn verify_eq(&mut self, name: &str, expected: f64, actual: f64) -> &mut Self {
let result = EquationResult::new(name, expected, actual, self.tolerance);
self.results.push(result);
self
}
pub fn verify_eq_with_tolerance(
&mut self,
name: &str,
expected: f64,
actual: f64,
tolerance: f64,
) -> &mut Self {
let result = EquationResult::new(name, expected, actual, tolerance);
self.results.push(result);
self
}
pub fn verify_in_range(&mut self, name: &str, value: f64, min: f64, max: f64) -> &mut Self {
let passed = value >= min && value <= max;
let midpoint = (min + max) / 2.0;
let difference = if passed {
0.0
} else if value < min {
min - value
} else {
value - max
};
let message = if passed {
format!("{}: {} is within [{}, {}]", name, value, min, max)
} else {
format!(
"{}: FAILED - {} is outside range [{}, {}]",
name, value, min, max
)
};
self.results.push(EquationResult {
name: name.to_string(),
passed,
expected: midpoint,
actual: value,
tolerance: (max - min) / 2.0,
difference,
relative_difference: 0.0,
message,
});
self
}
pub fn verify_non_negative(&mut self, name: &str, value: f64) -> &mut Self {
let passed = value >= 0.0;
let message = if passed {
format!("{}: {} >= 0", name, value)
} else {
format!("{}: FAILED - {} < 0", name, value)
};
self.results.push(EquationResult {
name: name.to_string(),
passed,
expected: 0.0,
actual: value,
tolerance: 0.0,
difference: if passed { 0.0 } else { -value },
relative_difference: 0.0,
message,
});
self
}
pub fn verify_positive(&mut self, name: &str, value: f64) -> &mut Self {
let passed = value > 0.0;
let message = if passed {
format!("{}: {} > 0", name, value)
} else {
format!("{}: FAILED - {} <= 0", name, value)
};
self.results.push(EquationResult {
name: name.to_string(),
passed,
expected: f64::EPSILON,
actual: value,
tolerance: 0.0,
difference: if passed { 0.0 } else { -value },
relative_difference: 0.0,
message,
});
self
}
#[must_use]
pub fn results(&self) -> &[EquationResult] {
&self.results
}
#[must_use]
pub fn all_passed(&self) -> bool {
self.results.iter().all(|r| r.passed)
}
#[must_use]
pub fn failures(&self) -> Vec<&EquationResult> {
self.results.iter().filter(|r| !r.passed).collect()
}
#[must_use]
pub fn passed_count(&self) -> usize {
self.results.iter().filter(|r| r.passed).count()
}
#[must_use]
pub fn failed_count(&self) -> usize {
self.results.iter().filter(|r| !r.passed).count()
}
pub fn assert_all(&self) -> ProbarResult<()> {
if self.all_passed() {
Ok(())
} else {
let failures: Vec<String> = self.failures().iter().map(|r| r.message.clone()).collect();
Err(ProbarError::AssertionFailed {
message: format!(
"Equation verification '{}' failed:\n{}",
self.name,
failures.join("\n")
),
})
}
}
pub fn clear(&mut self) {
self.results.clear();
}
}
#[derive(Debug)]
pub struct KinematicVerifier {
verifier: EquationVerifier,
}
impl KinematicVerifier {
#[must_use]
pub fn new() -> Self {
Self {
verifier: EquationVerifier::new("kinematics").with_tolerance(1e-4),
}
}
#[must_use]
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.verifier = self.verifier.with_tolerance(tolerance);
self
}
pub fn verify_velocity(
&mut self,
v: f64, v0: f64, a: f64, t: f64, ) -> &mut Self {
let expected = v0 + a * t;
self.verifier.verify_eq("v = v0 + at", expected, v);
self
}
pub fn verify_position(
&mut self,
x: f64, x0: f64, v0: f64, a: f64, t: f64, ) -> &mut Self {
let expected = x0 + v0 * t + 0.5 * a * t * t;
self.verifier
.verify_eq("x = x0 + v0*t + 0.5*a*t²", expected, x);
self
}
pub fn verify_velocity_squared(
&mut self,
v: f64, v0: f64, a: f64, x: f64, x0: f64, ) -> &mut Self {
let expected = v0 * v0 + 2.0 * a * (x - x0);
self.verifier
.verify_eq("v² = v0² + 2a(x-x0)", expected, v * v);
self
}
#[must_use]
pub fn verifier(&self) -> &EquationVerifier {
&self.verifier
}
pub fn assert_all(&self) -> ProbarResult<()> {
self.verifier.assert_all()
}
}
impl Default for KinematicVerifier {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct EnergyVerifier {
verifier: EquationVerifier,
}
impl EnergyVerifier {
#[must_use]
pub fn new() -> Self {
Self {
verifier: EquationVerifier::new("energy").with_tolerance(1e-4),
}
}
#[must_use]
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.verifier = self.verifier.with_tolerance(tolerance);
self
}
pub fn verify_kinetic_energy(
&mut self,
ke: f64, m: f64, v: f64, ) -> &mut Self {
let expected = 0.5 * m * v * v;
self.verifier.verify_eq("KE = 0.5*m*v²", expected, ke);
self
}
pub fn verify_potential_energy(
&mut self,
pe: f64, m: f64, g: f64, h: f64, ) -> &mut Self {
let expected = m * g * h;
self.verifier.verify_eq("PE = m*g*h", expected, pe);
self
}
pub fn verify_conservation(
&mut self,
ke_initial: f64,
pe_initial: f64,
ke_final: f64,
pe_final: f64,
) -> &mut Self {
let total_initial = ke_initial + pe_initial;
let total_final = ke_final + pe_final;
self.verifier
.verify_eq("E_total conserved", total_initial, total_final);
self
}
pub fn verify_work_energy(&mut self, work: f64, ke_initial: f64, ke_final: f64) -> &mut Self {
let delta_ke = ke_final - ke_initial;
self.verifier.verify_eq("W = ΔKE", work, delta_ke);
self
}
#[must_use]
pub fn verifier(&self) -> &EquationVerifier {
&self.verifier
}
pub fn assert_all(&self) -> ProbarResult<()> {
self.verifier.assert_all()
}
}
impl Default for EnergyVerifier {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct MomentumVerifier {
verifier: EquationVerifier,
}
impl MomentumVerifier {
#[must_use]
pub fn new() -> Self {
Self {
verifier: EquationVerifier::new("momentum").with_tolerance(1e-4),
}
}
#[must_use]
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.verifier = self.verifier.with_tolerance(tolerance);
self
}
pub fn verify_momentum(
&mut self,
p: f64, m: f64, v: f64, ) -> &mut Self {
let expected = m * v;
self.verifier.verify_eq("p = m*v", expected, p);
self
}
pub fn verify_conservation(
&mut self,
m1: f64,
v1_initial: f64,
m2: f64,
v2_initial: f64,
v1_final: f64,
v2_final: f64,
) -> &mut Self {
let p_initial = m1 * v1_initial + m2 * v2_initial;
let p_final = m1 * v1_final + m2 * v2_final;
self.verifier
.verify_eq("p_total conserved", p_initial, p_final);
self
}
pub fn verify_elastic_collision(
&mut self,
m1: f64,
v1_initial: f64,
m2: f64,
v2_initial: f64,
v1_final: f64,
v2_final: f64,
) -> &mut Self {
self.verify_conservation(m1, v1_initial, m2, v2_initial, v1_final, v2_final);
let ke_initial = 0.5 * m1 * v1_initial * v1_initial + 0.5 * m2 * v2_initial * v2_initial;
let ke_final = 0.5 * m1 * v1_final * v1_final + 0.5 * m2 * v2_final * v2_final;
self.verifier
.verify_eq("KE conserved (elastic)", ke_initial, ke_final);
self
}
#[must_use]
pub fn verifier(&self) -> &EquationVerifier {
&self.verifier
}
pub fn assert_all(&self) -> ProbarResult<()> {
self.verifier.assert_all()
}
}
impl Default for MomentumVerifier {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug)]
pub struct InvariantVerifier {
verifier: EquationVerifier,
}
impl InvariantVerifier {
#[must_use]
pub fn new(name: &str) -> Self {
Self {
verifier: EquationVerifier::new(name).with_tolerance(1e-6),
}
}
#[must_use]
pub fn with_tolerance(mut self, tolerance: f64) -> Self {
self.verifier = self.verifier.with_tolerance(tolerance);
self
}
pub fn verify_score_non_negative(&mut self, score: f64) -> &mut Self {
self.verifier.verify_non_negative("score >= 0", score);
self
}
pub fn verify_health(&mut self, health: f64, max_health: f64) -> &mut Self {
self.verifier
.verify_in_range("health", health, 0.0, max_health);
self
}
pub fn verify_position_bounds(
&mut self,
x: f64,
y: f64,
min_x: f64,
max_x: f64,
min_y: f64,
max_y: f64,
) -> &mut Self {
self.verifier.verify_in_range("x position", x, min_x, max_x);
self.verifier.verify_in_range("y position", y, min_y, max_y);
self
}
pub fn verify_speed_limit(&mut self, vx: f64, vy: f64, max_speed: f64) -> &mut Self {
let speed = (vx * vx + vy * vy).sqrt();
self.verifier
.verify_in_range("speed", speed, 0.0, max_speed);
self
}
pub fn verify_entity_count(&mut self, count: usize, expected: usize) -> &mut Self {
self.verifier
.verify_eq("entity count", expected as f64, count as f64);
self
}
pub fn verify_custom(&mut self, name: &str, expected: f64, actual: f64) -> &mut Self {
self.verifier.verify_eq(name, expected, actual);
self
}
#[must_use]
pub fn verifier(&self) -> &EquationVerifier {
&self.verifier
}
pub fn assert_all(&self) -> ProbarResult<()> {
self.verifier.assert_all()
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
mod variable_tests {
use super::*;
#[test]
fn test_new() {
let var = Variable::new("x", 10.0);
assert_eq!(var.name, "x");
assert!((var.value - 10.0).abs() < f64::EPSILON);
assert!(var.unit.is_none());
}
#[test]
fn test_with_unit() {
let var = Variable::with_unit("velocity", 5.0, "m/s");
assert_eq!(var.unit, Some("m/s".to_string()));
}
#[test]
fn test_display() {
let var1 = Variable::new("x", 10.0);
assert_eq!(format!("{}", var1), "x = 10");
let var2 = Variable::with_unit("v", 5.0, "m/s");
assert_eq!(format!("{}", var2), "v = 5 m/s");
}
}
mod equation_context_tests {
use super::*;
#[test]
fn test_new() {
let ctx = EquationContext::new();
assert!(ctx.variables().is_empty());
}
#[test]
fn test_set_and_get() {
let mut ctx = EquationContext::new();
ctx.set("x", 10.0);
assert!(ctx.has("x"));
assert_eq!(ctx.get("x"), Some(10.0));
assert_eq!(ctx.get("y"), None);
}
#[test]
fn test_from_variables() {
let vars = vec![Variable::new("x", 1.0), Variable::new("y", 2.0)];
let ctx = EquationContext::from_variables(&vars);
assert_eq!(ctx.get("x"), Some(1.0));
assert_eq!(ctx.get("y"), Some(2.0));
}
}
mod equation_verifier_tests {
use super::*;
#[test]
fn test_new() {
let verifier = EquationVerifier::new("test");
assert_eq!(verifier.name(), "test");
assert!(verifier.results().is_empty());
}
#[test]
fn test_verify_eq_pass() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("1 + 1 = 2", 2.0, 2.0);
assert!(verifier.all_passed());
assert_eq!(verifier.passed_count(), 1);
}
#[test]
fn test_verify_eq_fail() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("1 + 1 = 3", 3.0, 2.0);
assert!(!verifier.all_passed());
assert_eq!(verifier.failed_count(), 1);
}
#[test]
fn test_verify_eq_with_tolerance() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq_with_tolerance("approx", 1.0, 1.001, 0.01);
assert!(verifier.all_passed());
}
#[test]
fn test_verify_in_range_pass() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_in_range("value", 5.0, 0.0, 10.0);
assert!(verifier.all_passed());
}
#[test]
fn test_verify_in_range_fail() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_in_range("value", 15.0, 0.0, 10.0);
assert!(!verifier.all_passed());
}
#[test]
fn test_verify_non_negative_pass() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_non_negative("positive", 5.0);
verifier.verify_non_negative("zero", 0.0);
assert!(verifier.all_passed());
}
#[test]
fn test_verify_non_negative_fail() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_non_negative("negative", -5.0);
assert!(!verifier.all_passed());
}
#[test]
fn test_verify_positive_pass() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_positive("positive", 5.0);
assert!(verifier.all_passed());
}
#[test]
fn test_verify_positive_fail() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_positive("zero", 0.0);
assert!(!verifier.all_passed());
}
#[test]
fn test_assert_all_pass() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("test", 1.0, 1.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_assert_all_fail() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("test", 1.0, 2.0);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_clear() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("test", 1.0, 1.0);
verifier.clear();
assert!(verifier.results().is_empty());
}
}
mod kinematic_verifier_tests {
use super::*;
#[test]
fn test_verify_velocity() {
let mut verifier = KinematicVerifier::new();
verifier.verify_velocity(20.0, 10.0, 2.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_position() {
let mut verifier = KinematicVerifier::new();
verifier.verify_position(75.0, 0.0, 10.0, 2.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_velocity_squared() {
let mut verifier = KinematicVerifier::new();
let v = (300.0_f64).sqrt();
verifier.verify_velocity_squared(v, 10.0, 2.0, 50.0, 0.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_free_fall() {
let mut verifier = KinematicVerifier::new();
let g = 9.81;
let t = 2.0;
let v = g * t; let y = 0.5 * g * t * t;
verifier.verify_velocity(v, 0.0, g, t);
verifier.verify_position(y, 0.0, 0.0, g, t);
assert!(verifier.assert_all().is_ok());
}
}
mod energy_verifier_tests {
use super::*;
#[test]
fn test_verify_kinetic_energy() {
let mut verifier = EnergyVerifier::new();
verifier.verify_kinetic_energy(9.0, 2.0, 3.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_potential_energy() {
let mut verifier = EnergyVerifier::new();
verifier.verify_potential_energy(50.0, 1.0, 10.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_conservation() {
let mut verifier = EnergyVerifier::new();
verifier.verify_conservation(50.0, 100.0, 100.0, 50.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_pendulum_energy() {
let mut verifier = EnergyVerifier::new().with_tolerance(0.01);
let m = 1.0;
let g = 9.81;
let pe_top = m * g * 1.0;
let ke_top = 0.0;
let v_bottom = (2.0_f64 * g * 1.0).sqrt();
let ke_bottom = 0.5 * m * v_bottom * v_bottom;
let pe_bottom = 0.0;
verifier.verify_conservation(ke_top, pe_top, ke_bottom, pe_bottom);
assert!(verifier.assert_all().is_ok());
}
}
mod momentum_verifier_tests {
use super::*;
#[test]
fn test_verify_momentum() {
let mut verifier = MomentumVerifier::new();
verifier.verify_momentum(10.0, 2.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_conservation() {
let mut verifier = MomentumVerifier::new();
verifier.verify_conservation(1.0, 10.0, 1.0, 0.0, 0.0, 10.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_elastic_collision() {
let mut verifier = MomentumVerifier::new();
verifier.verify_elastic_collision(1.0, 10.0, 1.0, 0.0, 0.0, 10.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_pong_collision() {
let mut verifier = MomentumVerifier::new().with_tolerance(0.1);
let ball_mass = 1.0;
let ball_v_initial = 10.0;
let ball_v_final = -10.0;
let ke_initial = 0.5 * ball_mass * ball_v_initial * ball_v_initial;
let ke_final = 0.5 * ball_mass * ball_v_final * ball_v_final;
verifier
.verifier
.verify_eq("KE conserved", ke_initial, ke_final);
assert!(verifier.assert_all().is_ok());
}
}
mod invariant_verifier_tests {
use super::*;
#[test]
fn test_verify_score_non_negative() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_score_non_negative(100.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_health() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_health(50.0, 100.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_position_bounds() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_position_bounds(400.0, 300.0, 0.0, 800.0, 0.0, 600.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_speed_limit() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_speed_limit(3.0, 4.0, 10.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_entity_count() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_entity_count(10, 10);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_game_frame_invariants() {
let mut verifier = InvariantVerifier::new("pong").with_tolerance(0.001);
let score = 5.0;
let ball_x = 400.0;
let ball_y = 300.0;
let ball_vx = 5.0;
let ball_vy = -3.0;
let max_speed = 10.0;
verifier
.verify_score_non_negative(score)
.verify_position_bounds(ball_x, ball_y, 0.0, 800.0, 0.0, 600.0)
.verify_speed_limit(ball_vx, ball_vy, max_speed);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_verify_custom() {
let mut verifier = InvariantVerifier::new("custom");
verifier.verify_custom("custom_check", 42.0, 42.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_invariant_verifier_accessor() {
let verifier = InvariantVerifier::new("test");
assert_eq!(verifier.verifier().name(), "test");
}
}
mod additional_coverage_tests {
use super::*;
#[test]
fn test_equation_result_display() {
let result = EquationResult::new("test_eq", 1.0, 1.0, 0.001);
let display = format!("{}", result);
assert!(display.contains("test_eq"));
assert!(display.contains("expected"));
}
#[test]
fn test_equation_result_relative_difference_zero_expected() {
let result = EquationResult::new("zero_expected", 0.0, 0.0001, 0.001);
assert!((result.relative_difference - 0.0).abs() < f64::EPSILON);
}
#[test]
fn test_equation_result_failed_message() {
let result = EquationResult::new("fail_test", 10.0, 5.0, 0.001);
assert!(!result.passed);
assert!(result.message.contains("FAILED"));
assert!(result.message.contains('>'));
}
#[test]
fn test_equation_verifier_tolerance_accessor() {
let verifier = EquationVerifier::new("test").with_tolerance(0.01);
assert!((verifier.tolerance() - 0.01).abs() < f64::EPSILON);
}
#[test]
fn test_equation_verifier_failures() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_eq("pass", 1.0, 1.0);
verifier.verify_eq("fail1", 10.0, 5.0);
verifier.verify_eq("fail2", 20.0, 15.0);
let failures = verifier.failures();
assert_eq!(failures.len(), 2);
assert!(failures.iter().any(|f| f.name == "fail1"));
assert!(failures.iter().any(|f| f.name == "fail2"));
}
#[test]
fn test_verify_in_range_below_min() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_in_range("below_min", -5.0, 0.0, 10.0);
assert!(!verifier.all_passed());
let result = &verifier.results()[0];
assert!(!result.passed);
assert!((result.difference - 5.0).abs() < f64::EPSILON);
assert!(result.message.contains("outside range"));
}
#[test]
fn test_verify_positive_negative_value() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_positive("negative", -5.0);
assert!(!verifier.all_passed());
let result = &verifier.results()[0];
assert!(!result.passed);
assert!((result.difference - 5.0).abs() < f64::EPSILON);
}
#[test]
fn test_kinematic_verifier_default() {
let verifier = KinematicVerifier::default();
assert_eq!(verifier.verifier().name(), "kinematics");
}
#[test]
fn test_kinematic_verifier_accessor() {
let verifier = KinematicVerifier::new();
let inner = verifier.verifier();
assert_eq!(inner.name(), "kinematics");
}
#[test]
fn test_energy_verifier_default() {
let verifier = EnergyVerifier::default();
assert_eq!(verifier.verifier().name(), "energy");
}
#[test]
fn test_energy_verifier_work_energy() {
let mut verifier = EnergyVerifier::new();
verifier.verify_work_energy(50.0, 50.0, 100.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_energy_verifier_accessor() {
let verifier = EnergyVerifier::new();
let inner = verifier.verifier();
assert_eq!(inner.name(), "energy");
}
#[test]
fn test_momentum_verifier_default() {
let verifier = MomentumVerifier::default();
assert_eq!(verifier.verifier().name(), "momentum");
}
#[test]
fn test_momentum_verifier_accessor() {
let verifier = MomentumVerifier::new();
let inner = verifier.verifier();
assert_eq!(inner.name(), "momentum");
}
#[test]
fn test_equation_context_chaining() {
let mut ctx = EquationContext::new();
ctx.set("a", 1.0).set("b", 2.0).set("c", 3.0);
assert_eq!(ctx.get("a"), Some(1.0));
assert_eq!(ctx.get("b"), Some(2.0));
assert_eq!(ctx.get("c"), Some(3.0));
}
#[test]
fn test_equation_context_has_missing() {
let ctx = EquationContext::new();
assert!(!ctx.has("missing"));
}
#[test]
fn test_equation_result_relative_difference_calculation() {
let result = EquationResult::new("rel_diff", 100.0, 90.0, 1.0);
assert!((result.relative_difference - 10.0).abs() < f64::EPSILON);
}
#[test]
fn test_verifier_chaining() {
let mut verifier = EquationVerifier::new("chain");
verifier
.verify_eq("eq1", 1.0, 1.0)
.verify_eq("eq2", 2.0, 2.0)
.verify_non_negative("nn", 5.0)
.verify_positive("pos", 1.0)
.verify_in_range("range", 5.0, 0.0, 10.0);
assert!(verifier.all_passed());
assert_eq!(verifier.results().len(), 5);
}
#[test]
fn test_invariant_verifier_health_out_of_range() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_health(150.0, 100.0);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_invariant_verifier_speed_exceeds_limit() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_speed_limit(8.0, 6.0, 5.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_kinematic_verifier_with_tolerance() {
let mut verifier = KinematicVerifier::new().with_tolerance(0.1);
verifier.verify_velocity(20.05, 10.0, 2.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_momentum_verifier_with_tolerance() {
let mut verifier = MomentumVerifier::new().with_tolerance(0.1);
verifier.verify_momentum(10.05, 2.0, 5.0);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_invariant_verifier_with_tolerance() {
let mut verifier = InvariantVerifier::new("test").with_tolerance(0.1);
verifier.verify_custom("approx", 10.0, 10.05);
assert!(verifier.assert_all().is_ok());
}
#[test]
fn test_kinematic_verifier_failed_assertion() {
let mut verifier = KinematicVerifier::new();
verifier.verify_velocity(100.0, 10.0, 2.0, 5.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_energy_verifier_failed_assertion() {
let mut verifier = EnergyVerifier::new();
verifier.verify_kinetic_energy(100.0, 2.0, 3.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_momentum_verifier_failed_assertion() {
let mut verifier = MomentumVerifier::new();
verifier.verify_momentum(100.0, 2.0, 5.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_invariant_verifier_failed_assertion() {
let mut verifier = InvariantVerifier::new("test");
verifier.verify_score_non_negative(-10.0);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_verify_in_range_at_boundaries() {
let mut verifier = EquationVerifier::new("boundaries");
verifier.verify_in_range("at_min", 0.0, 0.0, 10.0);
verifier.verify_in_range("at_max", 10.0, 0.0, 10.0);
assert!(verifier.all_passed());
}
#[test]
fn test_equation_context_variables_list() {
let mut ctx = EquationContext::new();
ctx.set("x", 1.0).set("y", 2.0);
let vars = ctx.variables();
assert_eq!(vars.len(), 2);
assert!(vars.contains(&"x"));
assert!(vars.contains(&"y"));
}
#[test]
fn test_equation_verifier_empty_results() {
let verifier = EquationVerifier::new("empty");
assert!(verifier.all_passed()); assert!(verifier.failures().is_empty());
assert_eq!(verifier.passed_count(), 0);
assert_eq!(verifier.failed_count(), 0);
}
#[test]
fn test_equation_context_from_empty_variables() {
let vars: Vec<Variable> = vec![];
let ctx = EquationContext::from_variables(&vars);
assert!(ctx.variables().is_empty());
}
#[test]
fn test_variable_clone() {
let var1 = Variable::with_unit("velocity", 10.0, "m/s");
let var2 = var1;
assert_eq!(var2.name, "velocity");
assert!((var2.value - 10.0).abs() < f64::EPSILON);
assert_eq!(var2.unit, Some("m/s".to_string()));
}
#[test]
fn test_equation_context_clone() {
let mut ctx1 = EquationContext::new();
ctx1.set("x", 5.0);
let ctx2 = ctx1.clone();
assert_eq!(ctx2.get("x"), Some(5.0));
}
#[test]
fn test_equation_result_clone() {
let result1 = EquationResult::new("clone_test", 10.0, 10.0, 0.001);
let result2 = result1;
assert_eq!(result2.name, "clone_test");
assert!(result2.passed);
}
#[test]
fn test_equation_verifier_multiple_failures() {
let mut verifier = EquationVerifier::new("multi_fail");
verifier.verify_eq("fail1", 1.0, 100.0);
verifier.verify_eq("fail2", 2.0, 200.0);
verifier.verify_eq("fail3", 3.0, 300.0);
let result = verifier.assert_all();
assert!(result.is_err());
if let Err(ProbarError::AssertionFailed { message }) = result {
assert!(message.contains("fail1"));
assert!(message.contains("fail2"));
assert!(message.contains("fail3"));
}
}
#[test]
fn test_verify_in_range_message_passed() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_in_range("in_bounds", 5.0, 0.0, 10.0);
let result = &verifier.results()[0];
assert!(result.message.contains("is within"));
}
#[test]
fn test_verify_non_negative_message() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_non_negative("positive_val", 5.0);
let result = &verifier.results()[0];
assert!(result.message.contains(">= 0"));
}
#[test]
fn test_verify_positive_message() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_positive("pos_val", 5.0);
let result = &verifier.results()[0];
assert!(result.message.contains("> 0"));
}
#[test]
fn test_equation_result_all_fields() {
let result = EquationResult::new("field_test", 100.0, 95.0, 1.0);
assert_eq!(result.name, "field_test");
assert!(!result.passed); assert!((result.expected - 100.0).abs() < f64::EPSILON);
assert!((result.actual - 95.0).abs() < f64::EPSILON);
assert!((result.tolerance - 1.0).abs() < f64::EPSILON);
assert!((result.difference - 5.0).abs() < f64::EPSILON);
assert!((result.relative_difference - 5.0).abs() < f64::EPSILON); }
#[test]
fn test_energy_verifier_conservation_fails() {
let mut verifier = EnergyVerifier::new();
verifier.verify_conservation(50.0, 100.0, 80.0, 80.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_kinematic_verifier_position_fails() {
let mut verifier = KinematicVerifier::new();
verifier.verify_position(100.0, 0.0, 10.0, 2.0, 5.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_kinematic_verifier_velocity_squared_fails() {
let mut verifier = KinematicVerifier::new();
verifier.verify_velocity_squared(100.0, 10.0, 2.0, 50.0, 0.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_momentum_conservation_fails() {
let mut verifier = MomentumVerifier::new();
verifier.verify_conservation(1.0, 10.0, 1.0, 0.0, 5.0, 3.0); assert!(verifier.assert_all().is_err());
}
#[test]
fn test_elastic_collision_ke_fails() {
let mut verifier = MomentumVerifier::new();
verifier.verify_elastic_collision(1.0, 10.0, 1.0, 0.0, 5.0, 5.0);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_invariant_entity_count_fails() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_entity_count(5, 10);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_invariant_position_out_of_bounds() {
let mut verifier = InvariantVerifier::new("game");
verifier.verify_position_bounds(900.0, 700.0, 0.0, 800.0, 0.0, 600.0);
assert!(verifier.assert_all().is_err());
}
#[test]
fn test_verify_negative_value_non_negative() {
let mut verifier = EquationVerifier::new("test");
verifier.verify_non_negative("neg", -10.0);
let result = &verifier.results()[0];
assert!((result.difference - 10.0).abs() < f64::EPSILON);
assert!(result.message.contains("< 0"));
}
#[test]
fn test_verifier_debug_impl() {
let verifier = EquationVerifier::new("debug_test");
let debug_str = format!("{:?}", verifier);
assert!(debug_str.contains("debug_test"));
}
#[test]
fn test_kinematic_verifier_debug_impl() {
let verifier = KinematicVerifier::new();
let debug_str = format!("{:?}", verifier);
assert!(debug_str.contains("KinematicVerifier"));
}
#[test]
fn test_energy_verifier_debug_impl() {
let verifier = EnergyVerifier::new();
let debug_str = format!("{:?}", verifier);
assert!(debug_str.contains("EnergyVerifier"));
}
#[test]
fn test_momentum_verifier_debug_impl() {
let verifier = MomentumVerifier::new();
let debug_str = format!("{:?}", verifier);
assert!(debug_str.contains("MomentumVerifier"));
}
#[test]
fn test_invariant_verifier_debug_impl() {
let verifier = InvariantVerifier::new("test");
let debug_str = format!("{:?}", verifier);
assert!(debug_str.contains("InvariantVerifier"));
}
#[test]
fn test_variable_debug_impl() {
let var = Variable::new("x", 5.0);
let debug_str = format!("{:?}", var);
assert!(debug_str.contains("Variable"));
}
#[test]
fn test_equation_context_debug_impl() {
let ctx = EquationContext::new();
let debug_str = format!("{:?}", ctx);
assert!(debug_str.contains("EquationContext"));
}
#[test]
fn test_equation_result_debug_impl() {
let result = EquationResult::new("test", 1.0, 1.0, 0.001);
let debug_str = format!("{:?}", result);
assert!(debug_str.contains("EquationResult"));
}
#[test]
fn test_equation_context_default() {
let ctx = EquationContext::default();
assert!(ctx.variables().is_empty());
}
#[test]
fn test_energy_work_energy_fails() {
let mut verifier = EnergyVerifier::new();
verifier.verify_work_energy(100.0, 50.0, 100.0); assert!(verifier.assert_all().is_err());
}
}
}