use moonpool_sim::{SimWorld, StorageConfiguration, set_sim_seed};
use std::time::Duration;
fn local_runtime() -> tokio::runtime::LocalRuntime {
tokio::runtime::Builder::new_current_thread()
.enable_io()
.enable_time()
.build_local(Default::default())
.expect("Failed to build local runtime")
}
#[test]
fn test_fast_local_configuration_values() {
let config = StorageConfiguration::fast_local();
assert_eq!(config.iops, 1_000_000);
assert_eq!(config.bandwidth, 1_000_000_000);
let one_us = Duration::from_micros(1);
assert_eq!(config.read_latency.start, one_us);
assert_eq!(config.read_latency.end, one_us);
assert_eq!(config.write_latency.start, one_us);
assert_eq!(config.write_latency.end, one_us);
assert_eq!(config.sync_latency.start, one_us);
assert_eq!(config.sync_latency.end, one_us);
assert_eq!(config.read_fault_probability, 0.0);
assert_eq!(config.write_fault_probability, 0.0);
assert_eq!(config.crash_fault_probability, 0.0);
assert_eq!(config.misdirect_write_probability, 0.0);
assert_eq!(config.misdirect_read_probability, 0.0);
assert_eq!(config.phantom_write_probability, 0.0);
assert_eq!(config.sync_failure_probability, 0.0);
}
#[test]
fn test_default_configuration_values() {
let config = StorageConfiguration::default();
assert_eq!(config.iops, 25_000);
assert_eq!(config.bandwidth, 150_000_000);
assert_eq!(config.read_latency.start, Duration::from_micros(50));
assert_eq!(config.read_latency.end, Duration::from_micros(200));
assert_eq!(config.write_latency.start, Duration::from_micros(100));
assert_eq!(config.write_latency.end, Duration::from_micros(500));
assert_eq!(config.sync_latency.start, Duration::from_millis(1));
assert_eq!(config.sync_latency.end, Duration::from_millis(5));
assert_eq!(config.read_fault_probability, 0.0);
assert_eq!(config.write_fault_probability, 0.0);
assert_eq!(config.crash_fault_probability, 0.0);
assert_eq!(config.misdirect_write_probability, 0.0);
assert_eq!(config.misdirect_read_probability, 0.0);
assert_eq!(config.phantom_write_probability, 0.0);
assert_eq!(config.sync_failure_probability, 0.0);
}
#[test]
fn test_new_equals_default() {
let config_new = StorageConfiguration::new();
let config_default = StorageConfiguration::default();
assert_eq!(config_new.iops, config_default.iops);
assert_eq!(config_new.bandwidth, config_default.bandwidth);
assert_eq!(config_new.read_latency, config_default.read_latency);
assert_eq!(config_new.write_latency, config_default.write_latency);
assert_eq!(config_new.sync_latency, config_default.sync_latency);
}
#[test]
fn test_random_for_seed_determinism() {
local_runtime().block_on(async {
let seed = 12345u64;
set_sim_seed(seed);
let config1 = StorageConfiguration::random_for_seed();
set_sim_seed(seed);
let config2 = StorageConfiguration::random_for_seed();
assert_eq!(config1.iops, config2.iops, "IOPS should be deterministic");
assert_eq!(
config1.bandwidth, config2.bandwidth,
"Bandwidth should be deterministic"
);
assert_eq!(
config1.read_latency, config2.read_latency,
"Read latency should be deterministic"
);
assert_eq!(
config1.write_latency, config2.write_latency,
"Write latency should be deterministic"
);
assert_eq!(
config1.sync_latency, config2.sync_latency,
"Sync latency should be deterministic"
);
assert_eq!(
config1.read_fault_probability, config2.read_fault_probability,
"Read fault probability should be deterministic"
);
assert_eq!(
config1.write_fault_probability, config2.write_fault_probability,
"Write fault probability should be deterministic"
);
});
}
#[test]
fn test_random_for_seed_varies_by_seed() {
local_runtime().block_on(async {
set_sim_seed(111);
let config1 = StorageConfiguration::random_for_seed();
set_sim_seed(222);
let config2 = StorageConfiguration::random_for_seed();
set_sim_seed(333);
let config3 = StorageConfiguration::random_for_seed();
let all_same_iops = config1.iops == config2.iops && config2.iops == config3.iops;
let all_same_bandwidth =
config1.bandwidth == config2.bandwidth && config2.bandwidth == config3.bandwidth;
assert!(
!all_same_iops || !all_same_bandwidth,
"Different seeds should produce different configs"
);
println!("Seed 111: IOPS={}, BW={}", config1.iops, config1.bandwidth);
println!("Seed 222: IOPS={}, BW={}", config2.iops, config2.bandwidth);
println!("Seed 333: IOPS={}, BW={}", config3.iops, config3.bandwidth);
});
}
#[test]
fn test_random_for_seed_value_ranges() {
local_runtime().block_on(async {
for seed in 1..10 {
set_sim_seed(seed);
let config = StorageConfiguration::random_for_seed();
assert!(
config.iops >= 10_000 && config.iops < 100_000,
"IOPS {} should be in range [10000, 100000)",
config.iops
);
assert!(
config.bandwidth >= 50_000_000 && config.bandwidth < 500_000_000,
"Bandwidth {} should be in range [50M, 500M)",
config.bandwidth
);
assert!(
config.read_fault_probability >= 0.0 && config.read_fault_probability <= 0.001,
"Read fault prob {} too high",
config.read_fault_probability
);
assert!(
config.write_fault_probability >= 0.0 && config.write_fault_probability <= 0.001,
"Write fault prob {} too high",
config.write_fault_probability
);
}
});
}
#[test]
fn test_configuration_clone() {
let original = StorageConfiguration::default();
let cloned = original.clone();
assert_eq!(original.iops, cloned.iops);
assert_eq!(original.bandwidth, cloned.bandwidth);
assert_eq!(original.read_latency, cloned.read_latency);
assert_eq!(original.write_latency, cloned.write_latency);
assert_eq!(original.sync_latency, cloned.sync_latency);
assert_eq!(
original.read_fault_probability,
cloned.read_fault_probability
);
}
#[test]
fn test_custom_configuration() {
let custom = StorageConfiguration {
iops: 50_000,
bandwidth: 200_000_000,
read_latency: Duration::from_micros(30)..Duration::from_micros(100),
write_latency: Duration::from_micros(50)..Duration::from_micros(200),
sync_latency: Duration::from_millis(2)..Duration::from_millis(8),
read_fault_probability: 0.01,
write_fault_probability: 0.02,
crash_fault_probability: 0.001,
misdirect_write_probability: 0.0005,
misdirect_read_probability: 0.0005,
phantom_write_probability: 0.001,
sync_failure_probability: 0.005,
};
assert_eq!(custom.iops, 50_000);
assert_eq!(custom.bandwidth, 200_000_000);
assert_eq!(custom.read_latency.start, Duration::from_micros(30));
assert_eq!(custom.read_latency.end, Duration::from_micros(100));
assert_eq!(custom.read_fault_probability, 0.01);
assert_eq!(custom.write_fault_probability, 0.02);
assert_eq!(custom.crash_fault_probability, 0.001);
}
#[test]
fn test_simworld_set_storage_config() {
local_runtime().block_on(async {
let mut sim = SimWorld::new();
let default_iops = sim.with_storage_config(|c| c.iops);
assert_eq!(default_iops, 25_000);
sim.set_storage_config(StorageConfiguration::fast_local());
let fast_iops = sim.with_storage_config(|c| c.iops);
assert_eq!(fast_iops, 1_000_000);
let custom = StorageConfiguration {
iops: 42_000,
..StorageConfiguration::default()
};
sim.set_storage_config(custom);
let custom_iops = sim.with_storage_config(|c| c.iops);
assert_eq!(custom_iops, 42_000);
});
}
#[test]
fn test_hdd_like_configuration() {
let hdd_config = StorageConfiguration {
iops: 150, bandwidth: 150_000_000, read_latency: Duration::from_millis(5)..Duration::from_millis(15),
write_latency: Duration::from_millis(5)..Duration::from_millis(15),
sync_latency: Duration::from_millis(10)..Duration::from_millis(50),
read_fault_probability: 0.0,
write_fault_probability: 0.0,
crash_fault_probability: 0.0,
misdirect_write_probability: 0.0,
misdirect_read_probability: 0.0,
phantom_write_probability: 0.0,
sync_failure_probability: 0.0,
};
assert_eq!(hdd_config.iops, 150);
assert!(hdd_config.read_latency.start >= Duration::from_millis(1));
}
#[test]
fn test_nvme_like_configuration() {
let nvme_config = StorageConfiguration {
iops: 500_000, bandwidth: 3_500_000_000, read_latency: Duration::from_micros(10)..Duration::from_micros(50),
write_latency: Duration::from_micros(10)..Duration::from_micros(50),
sync_latency: Duration::from_micros(100)..Duration::from_micros(500),
read_fault_probability: 0.0,
write_fault_probability: 0.0,
crash_fault_probability: 0.0,
misdirect_write_probability: 0.0,
misdirect_read_probability: 0.0,
phantom_write_probability: 0.0,
sync_failure_probability: 0.0,
};
assert_eq!(nvme_config.iops, 500_000);
assert!(nvme_config.read_latency.start < Duration::from_micros(100));
}
#[test]
fn test_fault_probability_boundaries() {
let no_faults = StorageConfiguration::fast_local();
assert_eq!(no_faults.read_fault_probability, 0.0);
let all_faults = StorageConfiguration {
read_fault_probability: 1.0,
write_fault_probability: 1.0,
crash_fault_probability: 1.0,
misdirect_write_probability: 1.0,
misdirect_read_probability: 1.0,
phantom_write_probability: 1.0,
sync_failure_probability: 1.0,
..StorageConfiguration::fast_local()
};
assert_eq!(all_faults.read_fault_probability, 1.0);
assert_eq!(all_faults.sync_failure_probability, 1.0);
let partial_faults = StorageConfiguration {
read_fault_probability: 0.001,
write_fault_probability: 0.0001,
..StorageConfiguration::fast_local()
};
assert!((partial_faults.read_fault_probability - 0.001).abs() < f64::EPSILON);
}
#[test]
fn test_configuration_debug() {
let config = StorageConfiguration::fast_local();
let debug_str = format!("{:?}", config);
assert!(debug_str.contains("iops"));
assert!(debug_str.contains("bandwidth"));
assert!(debug_str.contains("read_latency"));
}