use ruchy::runtime::deterministic::{DeterministicRng, MockTime};
use ruchy::runtime::repl::Repl;
use ruchy::runtime::replay::{DeterministicRepl, Divergence, ResourceUsage, StateCheckpoint};
use std::{collections::HashMap, env};
#[test]
fn test_mock_time_new() {
let mock_time = MockTime::new();
assert_eq!(mock_time.now(), 0);
}
#[test]
fn test_mock_time_default() {
let mock_time = MockTime::default();
assert_eq!(mock_time.now(), 0);
}
#[test]
fn test_mock_time_advance() {
let mut mock_time = MockTime::new();
assert_eq!(mock_time.now(), 0);
mock_time.advance(1000);
assert_eq!(mock_time.now(), 1000);
mock_time.advance(500);
assert_eq!(mock_time.now(), 1500);
}
#[test]
fn test_mock_time_large_advance() {
let mut mock_time = MockTime::new();
mock_time.advance(u64::MAX / 2);
let first_time = mock_time.now();
mock_time.advance(100);
assert_eq!(mock_time.now(), first_time + 100);
}
#[test]
fn test_mock_time_overflow_handling() {
let mut mock_time = MockTime::new();
mock_time.advance(u64::MAX - 1000);
let before = mock_time.now();
mock_time.advance(500);
let after = mock_time.now();
assert!(after > before);
assert_eq!(after, before + 500);
}
#[test]
fn test_deterministic_rng_new() {
let _rng = DeterministicRng::new(42);
}
#[test]
fn test_deterministic_rng_next() {
let mut rng = DeterministicRng::new(123);
let first = rng.next();
let second = rng.next();
let third = rng.next();
assert_ne!(first, second);
assert_ne!(second, third);
assert_ne!(first, third);
}
#[test]
fn test_deterministic_rng_deterministic() {
let mut rng1 = DeterministicRng::new(999);
let mut rng2 = DeterministicRng::new(999);
for _ in 0..10 {
assert_eq!(rng1.next(), rng2.next());
}
}
#[test]
fn test_deterministic_rng_different_seeds() {
let mut rng1 = DeterministicRng::new(111);
let mut rng2 = DeterministicRng::new(222);
let val1 = rng1.next();
let val2 = rng2.next();
assert_ne!(val1, val2);
}
#[test]
fn test_deterministic_rng_reset() {
let mut rng = DeterministicRng::new(555);
let first_sequence = [rng.next(), rng.next(), rng.next()];
rng.reset();
let second_sequence = [rng.next(), rng.next(), rng.next()];
assert_eq!(first_sequence, second_sequence);
}
#[test]
fn test_deterministic_rng_long_sequence() {
let mut rng = DeterministicRng::new(777);
let mut values = Vec::new();
for _ in 0..100 {
values.push(rng.next());
}
rng.reset();
for i in 0..100 {
assert_eq!(values[i], rng.next());
}
}
#[test]
fn test_deterministic_rng_wrapping_behavior() {
let mut rng = DeterministicRng::new(u64::MAX);
let value1 = rng.next();
let value2 = rng.next();
assert_ne!(value1, value2);
assert!(value1 != 0 || value2 != 0); }
#[test]
fn test_execute_with_seed_basic() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("42", 12345);
assert!(result.output.is_ok());
assert!(!result.state_hash.is_empty());
assert!(result.resource_usage.cpu_ns > 0);
}
#[test]
fn test_execute_with_seed_error_handling() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("undefined_variable", 12345);
assert!(result.output.is_err());
assert!(!result.state_hash.is_empty());
}
#[test]
fn test_execute_with_seed_resource_usage() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("let x = 42", 12345);
assert!(result.resource_usage.cpu_ns > 0);
assert!(result.resource_usage.heap_bytes >= 0);
assert!(result.resource_usage.stack_depth >= 0);
}
#[test]
fn test_execute_with_seed_state_hash() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("let y = 99", 12345);
assert!(result.output.is_ok());
assert!(!result.state_hash.is_empty());
assert!(result.state_hash.chars().all(|c| c.is_ascii_hexdigit()));
}
#[test]
fn test_checkpoint_empty_repl() {
let repl = Repl::new(std::env::temp_dir()).unwrap();
let checkpoint = DeterministicRepl::checkpoint(&repl);
assert!(checkpoint.bindings.is_empty());
assert!(checkpoint.type_environment.is_empty());
assert!(!checkpoint.state_hash.is_empty());
assert_eq!(checkpoint.resource_usage.cpu_ns, 0);
}
#[test]
fn test_checkpoint_with_variables() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
repl.eval("let x = 10").unwrap();
repl.eval("let name = \"test\"").unwrap();
let checkpoint = DeterministicRepl::checkpoint(&repl);
assert!(!checkpoint.bindings.is_empty());
assert!(!checkpoint.state_hash.is_empty());
}
#[test]
fn test_restore_empty_checkpoint() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
repl.eval("let x = 1").unwrap();
repl.eval("let y = 2").unwrap();
let empty_checkpoint = StateCheckpoint {
bindings: HashMap::new(),
type_environment: HashMap::new(),
state_hash: "empty".to_string(),
resource_usage: ResourceUsage {
heap_bytes: 0,
stack_depth: 0,
cpu_ns: 0,
},
};
let result = DeterministicRepl::restore(&mut repl, &empty_checkpoint);
assert!(result.is_ok());
}
#[test]
fn test_restore_with_values() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let mut checkpoint = StateCheckpoint {
bindings: HashMap::new(),
type_environment: HashMap::new(),
state_hash: "test".to_string(),
resource_usage: ResourceUsage {
heap_bytes: 0,
stack_depth: 0,
cpu_ns: 0,
},
};
checkpoint
.bindings
.insert("test_var".to_string(), "42".to_string());
let result = DeterministicRepl::restore(&mut repl, &checkpoint);
assert!(result.is_ok());
}
#[test]
fn test_validate_determinism_identical() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let mut repl2 = Repl::new(std::env::temp_dir()).unwrap();
repl1.eval("let a = 1").unwrap();
repl2.eval("let a = 1").unwrap();
let validation = DeterministicRepl::validate_determinism(&repl1, &repl2);
assert!(validation.is_deterministic);
assert!(validation.divergences.is_empty());
}
#[test]
fn test_validate_determinism_different_values() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let mut repl2 = Repl::new(std::env::temp_dir()).unwrap();
repl1.eval("let x = 10").unwrap();
repl2.eval("let x = 20").unwrap();
let validation = DeterministicRepl::validate_determinism(&repl1, &repl2);
assert!(!validation.is_deterministic);
assert!(!validation.divergences.is_empty());
match &validation.divergences[0] {
Divergence::State {
expected_hash: _,
actual_hash: _,
} => {
}
_ => panic!("Expected State divergence"),
}
}
#[test]
fn test_validate_determinism_missing_variable() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let repl2 = Repl::new(std::env::temp_dir()).unwrap();
repl1.eval("let missing = 42").unwrap();
let validation = DeterministicRepl::validate_determinism(&repl1, &repl2);
assert!(!validation.is_deterministic);
assert!(!validation.divergences.is_empty());
}
#[test]
fn test_validate_determinism_empty_repls() {
let repl1 = Repl::new(std::env::temp_dir()).unwrap();
let repl2 = Repl::new(std::env::temp_dir()).unwrap();
let validation = DeterministicRepl::validate_determinism(&repl1, &repl2);
assert!(validation.is_deterministic);
assert!(validation.divergences.is_empty());
}
#[test]
fn test_deterministic_execution_same_seed() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let mut repl2 = Repl::new(std::env::temp_dir()).unwrap();
let result1 = repl1.execute_with_seed("let x = 42", 12345);
let result2 = repl2.execute_with_seed("let x = 42", 12345);
assert!(result1.output.is_ok());
assert!(result2.output.is_ok());
assert_eq!(result1.state_hash, result2.state_hash);
}
#[test]
fn test_checkpoint_restore_cycle() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
repl.eval("let x = 10").unwrap();
repl.eval("let y = 20").unwrap();
let checkpoint = DeterministicRepl::checkpoint(&repl);
repl.eval("let x = 99").unwrap();
let restore_result = DeterministicRepl::restore(&mut repl, &checkpoint);
assert!(restore_result.is_ok());
}
#[test]
fn test_resource_usage_structure() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("42", 100);
let usage = &result.resource_usage;
assert!(usage.heap_bytes >= 0);
assert!(usage.stack_depth >= 0);
assert!(usage.cpu_ns > 0);
}
#[test]
fn test_state_hash_consistency() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let mut repl2 = Repl::new(std::env::temp_dir()).unwrap();
repl1.eval("let test = 123").unwrap();
repl2.eval("let test = 123").unwrap();
let result1 = repl1.execute_with_seed("test", 999);
let result2 = repl2.execute_with_seed("test", 999);
assert_eq!(result1.state_hash, result2.state_hash);
}
#[test]
fn test_value_conversion_unit() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("()", 12345);
assert!(result.output.is_ok());
}
#[test]
fn test_value_conversion_bool_true() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("true", 12345);
assert!(result.output.is_ok());
}
#[test]
fn test_value_conversion_bool_false() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("false", 12345);
assert!(result.output.is_ok());
}
#[test]
fn test_value_conversion_integer() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("42", 12345);
assert!(result.output.is_ok());
}
#[test]
fn test_value_conversion_string() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result = repl.execute_with_seed("\"hello\"", 12345);
assert!(result.output.is_ok());
}
#[test]
fn test_checkpoint_bindings_extraction() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
repl.eval("let extracted = 456").unwrap();
let checkpoint = DeterministicRepl::checkpoint(&repl);
assert!(!checkpoint.state_hash.is_empty());
assert!(checkpoint.resource_usage.heap_bytes >= 0);
}
#[test]
fn test_validation_divergence_detection() {
let mut repl1 = Repl::new(std::env::temp_dir()).unwrap();
let mut repl2 = Repl::new(std::env::temp_dir()).unwrap();
repl1.eval("let divergent = 100").unwrap();
repl2.eval("let divergent = 200").unwrap();
let validation = DeterministicRepl::validate_determinism(&repl1, &repl2);
assert!(!validation.is_deterministic);
let has_state_divergence = validation
.divergences
.iter()
.any(|d| matches!(d, Divergence::State { .. }));
assert!(has_state_divergence);
}
#[test]
fn test_multiple_executions_same_repl() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result1 = repl.execute_with_seed("let first = 1", 100);
let result2 = repl.execute_with_seed("let second = 2", 200);
assert!(result1.output.is_ok());
assert!(result2.output.is_ok());
assert_ne!(result1.state_hash, result2.state_hash); }
#[test]
fn test_error_state_handling() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let error_result = repl.execute_with_seed("invalid syntax +++", 999);
assert!(error_result.output.is_err());
assert!(!error_result.state_hash.is_empty());
assert!(error_result.resource_usage.cpu_ns > 0);
}
#[test]
fn test_resource_usage_accumulation() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let result1 = repl.execute_with_seed("let a = 1", 100);
let result2 = repl.execute_with_seed("let b = 2", 100);
assert!(result1.resource_usage.cpu_ns > 0);
assert!(result2.resource_usage.cpu_ns > 0);
assert!(result1.resource_usage.heap_bytes >= 0);
assert!(result2.resource_usage.heap_bytes >= 0);
}
#[test]
fn test_checkpoint_type_environment_empty() {
let repl = Repl::new(std::env::temp_dir()).unwrap();
let checkpoint = DeterministicRepl::checkpoint(&repl);
assert!(checkpoint.type_environment.is_empty());
}
#[test]
fn test_restore_value_types() {
let mut repl = Repl::new(std::env::temp_dir()).unwrap();
let mut checkpoint = StateCheckpoint {
bindings: HashMap::new(),
type_environment: HashMap::new(),
state_hash: "test".to_string(),
resource_usage: ResourceUsage {
heap_bytes: 0,
stack_depth: 0,
cpu_ns: 0,
},
};
checkpoint
.bindings
.insert("unit".to_string(), "Unit".to_string());
checkpoint
.bindings
.insert("bool_true".to_string(), "true".to_string());
checkpoint
.bindings
.insert("bool_false".to_string(), "false".to_string());
checkpoint
.bindings
.insert("integer".to_string(), "123".to_string());
checkpoint
.bindings
.insert("string".to_string(), "hello".to_string());
let result = DeterministicRepl::restore(&mut repl, &checkpoint);
assert!(result.is_ok());
}