#[cfg(test)]
mod tests {
use crate::price_level::PriceLevelStatistics;
use std::str::FromStr;
use std::sync::Arc;
use std::sync::atomic::Ordering;
use std::thread;
use std::time::{Duration, SystemTime, UNIX_EPOCH};
#[test]
fn test_new() {
let stats = PriceLevelStatistics::new();
assert_eq!(stats.orders_added(), 0);
assert_eq!(stats.orders_removed(), 0);
assert_eq!(stats.orders_executed(), 0);
assert_eq!(stats.quantity_executed(), 0);
assert_eq!(stats.value_executed(), 0);
assert_eq!(stats.last_execution_time.load(Ordering::Relaxed), 0);
assert!(stats.first_arrival_time.load(Ordering::Relaxed) > 0);
assert_eq!(stats.sum_waiting_time.load(Ordering::Relaxed), 0);
}
#[test]
fn test_record_execution_error_paths() {
let stats = PriceLevelStatistics::new();
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
assert!(stats.record_execution(1, 100, now + 1_000).is_err());
assert!(stats.record_execution(u64::MAX, u128::MAX, 0).is_err());
}
#[test]
fn test_default() {
let stats = PriceLevelStatistics::default();
assert_eq!(stats.orders_added(), 0);
assert_eq!(stats.orders_removed(), 0);
assert_eq!(stats.orders_executed(), 0);
}
#[test]
fn test_record_operations() {
let stats = PriceLevelStatistics::new();
for _ in 0..5 {
stats.record_order_added();
}
assert_eq!(stats.orders_added(), 5);
for _ in 0..3 {
stats.record_order_removed();
}
assert_eq!(stats.orders_removed(), 3);
assert!(stats.record_execution(10, 100, 0).is_ok()); assert_eq!(stats.orders_executed(), 1);
assert_eq!(stats.quantity_executed(), 10);
assert_eq!(stats.value_executed(), 1000); assert!(stats.last_execution_time.load(Ordering::Relaxed) > 0);
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
- 1000;
thread::sleep(Duration::from_millis(10));
assert!(stats.record_execution(5, 200, timestamp).is_ok());
assert_eq!(stats.orders_executed(), 2);
assert_eq!(stats.quantity_executed(), 15); assert_eq!(stats.value_executed(), 2000); assert!(stats.sum_waiting_time.load(Ordering::Relaxed) >= 1000); }
#[test]
fn test_average_execution_price() {
let stats = PriceLevelStatistics::new();
assert_eq!(stats.average_execution_price(), None);
assert!(stats.record_execution(10, 100, 0).is_ok()); assert!(stats.record_execution(20, 150, 0).is_ok());
let avg_price = stats.average_execution_price().unwrap();
assert!((avg_price - 133.33).abs() < 0.01);
}
#[test]
fn test_average_waiting_time() {
let stats = PriceLevelStatistics::new();
assert_eq!(stats.average_waiting_time(), None);
let now = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64;
assert!(stats.record_execution(10, 100, now - 1000).is_ok()); assert!(stats.record_execution(20, 150, now - 3000).is_ok());
let avg_wait = stats.average_waiting_time().unwrap();
assert!((1900.0..=2100.0).contains(&avg_wait));
}
#[test]
fn test_time_since_last_execution() {
let stats = PriceLevelStatistics::new();
assert_eq!(stats.time_since_last_execution(), None);
assert!(stats.record_execution(10, 100, 0).is_ok());
thread::sleep(Duration::from_millis(10));
let time_since = stats.time_since_last_execution().unwrap();
assert!(time_since > 0);
}
#[test]
fn test_reset() {
let stats = PriceLevelStatistics::new();
stats.record_order_added();
stats.record_order_removed();
assert!(stats.record_execution(10, 100, 0).is_ok());
assert_eq!(stats.orders_added(), 1);
assert_eq!(stats.orders_removed(), 1);
assert_eq!(stats.orders_executed(), 1);
stats.reset();
assert_eq!(stats.orders_added(), 0);
assert_eq!(stats.orders_removed(), 0);
assert_eq!(stats.orders_executed(), 0);
assert_eq!(stats.quantity_executed(), 0);
assert_eq!(stats.value_executed(), 0);
assert_eq!(stats.last_execution_time.load(Ordering::Relaxed), 0);
assert!(stats.first_arrival_time.load(Ordering::Relaxed) > 0);
assert_eq!(stats.sum_waiting_time.load(Ordering::Relaxed), 0);
}
#[test]
fn test_display() {
let stats = PriceLevelStatistics::new();
stats.record_order_added();
stats.record_order_removed();
assert!(stats.record_execution(10, 100, 0).is_ok());
let display_str = stats.to_string();
assert!(display_str.starts_with("PriceLevelStatistics:"));
assert!(display_str.contains("orders_added=1"));
assert!(display_str.contains("orders_removed=1"));
assert!(display_str.contains("orders_executed=1"));
assert!(display_str.contains("quantity_executed=10"));
assert!(display_str.contains("value_executed=1000"));
}
#[test]
fn test_from_str() {
let input = "PriceLevelStatistics:orders_added=5;orders_removed=3;orders_executed=2;quantity_executed=15;value_executed=2000;last_execution_time=1616823000000;first_arrival_time=1616823000001;sum_waiting_time=1000";
let stats = PriceLevelStatistics::from_str(input).unwrap();
assert_eq!(stats.orders_added(), 5);
assert_eq!(stats.orders_removed(), 3);
assert_eq!(stats.orders_executed(), 2);
assert_eq!(stats.quantity_executed(), 15);
assert_eq!(stats.value_executed(), 2000);
assert_eq!(
stats.last_execution_time.load(Ordering::Relaxed),
1616823000000
);
assert_eq!(
stats.first_arrival_time.load(Ordering::Relaxed),
1616823000001
);
assert_eq!(stats.sum_waiting_time.load(Ordering::Relaxed), 1000);
}
#[test]
fn test_from_str_invalid_format() {
let input = "InvalidFormat";
assert!(PriceLevelStatistics::from_str(input).is_err());
}
#[test]
fn test_from_str_missing_field() {
let input = "PriceLevelStatistics:orders_added=5;orders_removed=3;orders_executed=2;quantity_executed=15;value_executed=2000;last_execution_time=1616823000000;first_arrival_time=1616823000001";
assert!(PriceLevelStatistics::from_str(input).is_err());
}
#[test]
fn test_from_str_invalid_field_value() {
let input = "PriceLevelStatistics:orders_added=invalid;orders_removed=3;orders_executed=2;quantity_executed=15;value_executed=2000;last_execution_time=1616823000000;first_arrival_time=1616823000001;sum_waiting_time=1000";
assert!(PriceLevelStatistics::from_str(input).is_err());
}
#[test]
fn test_serialize_deserialize_json() {
let stats = PriceLevelStatistics::new();
stats.record_order_added();
stats.record_order_removed();
assert!(stats.record_execution(10, 100, 0).is_ok());
let json = serde_json::to_string(&stats).unwrap();
assert!(json.contains("\"orders_added\":1"));
assert!(json.contains("\"orders_removed\":1"));
assert!(json.contains("\"orders_executed\":1"));
assert!(json.contains("\"quantity_executed\":10"));
assert!(json.contains("\"value_executed\":1000"));
let deserialized: PriceLevelStatistics = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.orders_added(), 1);
assert_eq!(deserialized.orders_removed(), 1);
assert_eq!(deserialized.orders_executed(), 1);
assert_eq!(deserialized.quantity_executed(), 10);
assert_eq!(deserialized.value_executed(), 1000);
}
#[test]
fn test_round_trip_display_parse() {
let stats = PriceLevelStatistics::new();
let current_time: u64 = 1616823000000;
stats
.last_execution_time
.store(current_time, Ordering::Relaxed);
stats
.first_arrival_time
.store(current_time + 1, Ordering::Relaxed);
stats.record_order_added();
stats.record_order_added();
stats.record_order_removed();
stats.orders_executed.store(2, Ordering::Relaxed);
stats.quantity_executed.store(15, Ordering::Relaxed);
stats.value_executed.store(2000, Ordering::Relaxed);
stats.sum_waiting_time.store(1000, Ordering::Relaxed);
let string_representation = stats.to_string();
let parsed = PriceLevelStatistics::from_str(&string_representation).unwrap();
assert_eq!(parsed.orders_added(), stats.orders_added());
assert_eq!(parsed.orders_removed(), stats.orders_removed());
assert_eq!(parsed.orders_executed(), stats.orders_executed());
assert_eq!(parsed.quantity_executed(), stats.quantity_executed());
assert_eq!(parsed.value_executed(), stats.value_executed());
assert_eq!(
parsed.last_execution_time.load(Ordering::Relaxed),
stats.last_execution_time.load(Ordering::Relaxed)
);
assert_eq!(
parsed.first_arrival_time.load(Ordering::Relaxed),
stats.first_arrival_time.load(Ordering::Relaxed)
);
assert_eq!(
parsed.sum_waiting_time.load(Ordering::Relaxed),
stats.sum_waiting_time.load(Ordering::Relaxed)
);
}
#[test]
fn test_thread_safety() {
let stats = PriceLevelStatistics::new();
let stats_arc = Arc::new(stats);
let mut handles = vec![];
for _ in 0..10 {
let stats_clone = Arc::clone(&stats_arc);
let handle = thread::spawn(move || {
for _ in 0..100 {
stats_clone.record_order_added();
stats_clone.record_order_removed();
if let Err(error) = stats_clone.record_execution(1, 100, 0) {
panic!("record_execution failed in thread: {error}");
}
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
assert_eq!(stats_arc.orders_added(), 1000); assert_eq!(stats_arc.orders_removed(), 1000);
assert_eq!(stats_arc.orders_executed(), 1000);
assert_eq!(stats_arc.quantity_executed(), 1000);
assert_eq!(stats_arc.value_executed(), 100000); }
#[test]
fn test_statistics_reset_and_verify() {
let stats = PriceLevelStatistics::new();
stats.record_order_added();
stats.record_order_added();
stats.record_order_removed();
assert!(stats.record_execution(10, 100, 0).is_ok());
assert_eq!(stats.orders_added(), 2);
assert_eq!(stats.orders_removed(), 1);
assert_eq!(stats.orders_executed(), 1);
stats.reset();
assert_eq!(stats.orders_added(), 0);
assert_eq!(stats.orders_removed(), 0);
assert_eq!(stats.orders_executed(), 0);
assert_eq!(stats.quantity_executed(), 0);
assert_eq!(stats.value_executed(), 0);
assert_eq!(stats.last_execution_time.load(Ordering::Relaxed), 0);
assert!(stats.first_arrival_time.load(Ordering::Relaxed) > 0);
assert_eq!(stats.sum_waiting_time.load(Ordering::Relaxed), 0);
}
#[test]
fn test_statistics_serialize_deserialize_fields() {
let stats = PriceLevelStatistics::new();
stats.orders_added.store(1, Ordering::Relaxed);
stats.orders_removed.store(2, Ordering::Relaxed);
stats.orders_executed.store(3, Ordering::Relaxed);
stats.quantity_executed.store(4, Ordering::Relaxed);
stats.value_executed.store(5, Ordering::Relaxed);
stats.last_execution_time.store(6, Ordering::Relaxed);
stats.first_arrival_time.store(7, Ordering::Relaxed);
stats.sum_waiting_time.store(8, Ordering::Relaxed);
let serialized = serde_json::to_string(&stats).unwrap();
assert!(serialized.contains("\"orders_added\":1"));
assert!(serialized.contains("\"orders_removed\":2"));
assert!(serialized.contains("\"orders_executed\":3"));
assert!(serialized.contains("\"quantity_executed\":4"));
assert!(serialized.contains("\"value_executed\":5"));
assert!(serialized.contains("\"last_execution_time\":6"));
assert!(serialized.contains("\"first_arrival_time\":7"));
assert!(serialized.contains("\"sum_waiting_time\":8"));
let deserialized: PriceLevelStatistics = serde_json::from_str(&serialized).unwrap();
assert_eq!(deserialized.orders_added(), 1);
assert_eq!(deserialized.orders_removed(), 2);
assert_eq!(deserialized.orders_executed(), 3);
assert_eq!(deserialized.quantity_executed(), 4);
assert_eq!(deserialized.value_executed(), 5);
assert_eq!(deserialized.last_execution_time.load(Ordering::Relaxed), 6);
assert_eq!(deserialized.first_arrival_time.load(Ordering::Relaxed), 7);
assert_eq!(deserialized.sum_waiting_time.load(Ordering::Relaxed), 8);
}
#[test]
fn test_statistics_visitor_missing_fields() {
let json = r#"{
"orders_added": 1,
"orders_removed": 2,
"orders_executed": 3
}"#;
let deserialized: PriceLevelStatistics = serde_json::from_str(json).unwrap();
assert_eq!(deserialized.orders_added(), 1);
assert_eq!(deserialized.orders_removed(), 2);
assert_eq!(deserialized.orders_executed(), 3);
assert_eq!(deserialized.quantity_executed(), 0);
assert_eq!(deserialized.value_executed(), 0);
}
}