#![allow(clippy::must_use_candidate)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_const_for_fn)]
#![allow(clippy::return_self_not_must_use)]
#![allow(clippy::uninlined_format_args)]
#![allow(clippy::format_push_string)]
#![allow(clippy::doc_markdown)]
#![allow(clippy::cast_precision_loss)]
#![allow(clippy::items_after_statements)]
#![allow(clippy::manual_saturating_arithmetic)]
#![allow(clippy::use_self)]
use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum StressMode {
#[default]
Atomics,
WorkerMsg,
Render,
Trace,
Full,
}
impl std::fmt::Display for StressMode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Atomics => write!(f, "atomics"),
Self::WorkerMsg => write!(f, "worker-msg"),
Self::Render => write!(f, "render"),
Self::Trace => write!(f, "trace"),
Self::Full => write!(f, "full"),
}
}
}
impl std::str::FromStr for StressMode {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_str() {
"atomics" => Ok(Self::Atomics),
"worker-msg" | "workermsg" | "worker_msg" => Ok(Self::WorkerMsg),
"render" => Ok(Self::Render),
"trace" => Ok(Self::Trace),
"full" => Ok(Self::Full),
_ => Err(format!("Unknown stress mode: {}", s)),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressConfig {
pub mode: StressMode,
pub duration_secs: u64,
pub concurrency: u32,
pub target_ops_per_sec: u64,
pub warmup_secs: u64,
}
impl Default for StressConfig {
fn default() -> Self {
Self {
mode: StressMode::Atomics,
duration_secs: 30,
concurrency: 4,
target_ops_per_sec: 0,
warmup_secs: 5,
}
}
}
impl StressConfig {
pub fn atomics(duration_secs: u64, concurrency: u32) -> Self {
Self {
mode: StressMode::Atomics,
duration_secs,
concurrency,
..Default::default()
}
}
pub fn worker_msg(duration_secs: u64, concurrency: u32) -> Self {
Self {
mode: StressMode::WorkerMsg,
duration_secs,
concurrency,
..Default::default()
}
}
pub fn render(duration_secs: u64) -> Self {
Self {
mode: StressMode::Render,
duration_secs,
concurrency: 1,
..Default::default()
}
}
pub fn trace(duration_secs: u64) -> Self {
Self {
mode: StressMode::Trace,
duration_secs,
concurrency: 1,
..Default::default()
}
}
pub fn full(duration_secs: u64, concurrency: u32) -> Self {
Self {
mode: StressMode::Full,
duration_secs,
concurrency,
..Default::default()
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressResult {
pub mode: StressMode,
pub duration: Duration,
pub total_ops: u64,
pub ops_per_sec: f64,
pub passed: bool,
pub pass_criteria: String,
pub actual_value: String,
pub memory: MemoryStats,
pub latency: LatencyStats,
pub errors: Vec<StressError>,
}
impl StressResult {
pub fn new(mode: StressMode) -> Self {
Self {
mode,
duration: Duration::ZERO,
total_ops: 0,
ops_per_sec: 0.0,
passed: false,
pass_criteria: String::new(),
actual_value: String::new(),
memory: MemoryStats::default(),
latency: LatencyStats::default(),
errors: Vec::new(),
}
}
pub fn pass(mut self, criteria: &str, actual: &str) -> Self {
self.passed = true;
self.pass_criteria = criteria.to_string();
self.actual_value = actual.to_string();
self
}
pub fn fail(mut self, criteria: &str, actual: &str) -> Self {
self.passed = false;
self.pass_criteria = criteria.to_string();
self.actual_value = actual.to_string();
self
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct MemoryStats {
pub initial_bytes: u64,
pub final_bytes: u64,
pub peak_bytes: u64,
pub stable: bool,
}
impl MemoryStats {
pub fn growth_percent(&self) -> f64 {
if self.initial_bytes == 0 {
return 0.0;
}
((self.final_bytes as f64 - self.initial_bytes as f64) / self.initial_bytes as f64) * 100.0
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct LatencyStats {
pub min_us: u64,
pub max_us: u64,
pub mean_us: u64,
pub p50_us: u64,
pub p95_us: u64,
pub p99_us: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StressError {
pub kind: StressErrorKind,
pub message: String,
pub time_offset: Duration,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum StressErrorKind {
LockTimeout,
QueueOverflow,
FrameDrop,
OutOfMemory,
WorkerCrash,
Other,
}
impl std::fmt::Display for StressErrorKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::LockTimeout => write!(f, "Lock Timeout"),
Self::QueueOverflow => write!(f, "Queue Overflow"),
Self::FrameDrop => write!(f, "Frame Drop"),
Self::OutOfMemory => write!(f, "Out of Memory"),
Self::WorkerCrash => write!(f, "Worker Crash"),
Self::Other => write!(f, "Other"),
}
}
}
#[derive(Debug)]
pub struct StressRunner {
config: StressConfig,
}
impl StressRunner {
pub fn new(config: StressConfig) -> Self {
Self { config }
}
pub fn run(&self) -> StressResult {
match self.config.mode {
StressMode::Atomics => self.run_atomics_stress(),
StressMode::WorkerMsg => self.run_worker_msg_stress(),
StressMode::Render => self.run_render_stress(),
StressMode::Trace => self.run_trace_stress(),
StressMode::Full => self.run_full_stress(),
}
}
fn run_atomics_stress(&self) -> StressResult {
let start = Instant::now();
let mut result = StressResult::new(StressMode::Atomics);
let mut ops: u64 = 0;
let target_duration = Duration::from_secs(self.config.duration_secs);
while start.elapsed() < target_duration {
for _ in 0..1000 {
ops += 1;
std::hint::black_box(ops.wrapping_mul(31));
}
}
let elapsed = start.elapsed();
result.duration = elapsed;
result.total_ops = ops;
result.ops_per_sec = ops as f64 / elapsed.as_secs_f64();
const PASS_THRESHOLD: f64 = 10_000.0;
let ops_per_sec = result.ops_per_sec;
let criteria = format!("atomics throughput > {} ops/sec", PASS_THRESHOLD);
let actual = format!("{:.0} ops/sec", ops_per_sec);
if ops_per_sec >= PASS_THRESHOLD {
result = result.pass(&criteria, &actual);
} else {
result = result.fail(&criteria, &actual);
}
result.memory.stable = true;
result
}
fn run_worker_msg_stress(&self) -> StressResult {
let start = Instant::now();
let mut result = StressResult::new(StressMode::WorkerMsg);
let mut messages: u64 = 0;
let target_duration = Duration::from_secs(self.config.duration_secs);
while start.elapsed() < target_duration {
for _ in 0..500 {
messages += 1;
let payload = std::hint::black_box(vec![0u8; 64]);
std::hint::black_box(payload.len());
}
}
let elapsed = start.elapsed();
result.duration = elapsed;
result.total_ops = messages;
result.ops_per_sec = messages as f64 / elapsed.as_secs_f64();
const PASS_THRESHOLD: f64 = 5_000.0;
let ops_per_sec = result.ops_per_sec;
let criteria = format!("message throughput > {} msg/sec", PASS_THRESHOLD);
let actual = format!("{:.0} msg/sec", ops_per_sec);
if ops_per_sec >= PASS_THRESHOLD {
result = result.pass(&criteria, &actual);
} else {
result = result.fail(&criteria, &actual);
}
result.memory.stable = true;
result
}
fn run_render_stress(&self) -> StressResult {
let start = Instant::now();
let mut result = StressResult::new(StressMode::Render);
let target_duration = Duration::from_secs(self.config.duration_secs);
let frame_budget = Duration::from_micros(16_667); let mut frames: u64 = 0;
let mut dropped_frames: u64 = 0;
while start.elapsed() < target_duration {
let frame_start = Instant::now();
for _ in 0..1000 {
std::hint::black_box(frames.wrapping_add(1));
}
let frame_time = frame_start.elapsed();
frames += 1;
if frame_time > frame_budget {
dropped_frames += 1;
}
if frame_time < frame_budget {
std::thread::sleep(frame_budget.checked_sub(frame_time).unwrap());
}
}
let elapsed = start.elapsed();
result.duration = elapsed;
result.total_ops = frames;
result.ops_per_sec = frames as f64 / elapsed.as_secs_f64();
let drop_rate = dropped_frames as f64 / frames as f64;
let fps = result.ops_per_sec;
let actual = format!("{:.1} FPS, {:.1}% drops", fps, drop_rate * 100.0);
if drop_rate < 0.05 {
result = result.pass("60 FPS maintained (< 5% drops)", &actual);
} else {
result = result.fail("60 FPS maintained (< 5% drops)", &actual);
result.errors.push(StressError {
kind: StressErrorKind::FrameDrop,
message: format!("{} frames dropped", dropped_frames),
time_offset: elapsed,
});
}
result.memory.stable = true;
result
}
fn run_trace_stress(&self) -> StressResult {
let start = Instant::now();
let mut result = StressResult::new(StressMode::Trace);
let _target_duration = Duration::from_secs(self.config.duration_secs);
let baseline_start = Instant::now();
let mut baseline_ops: u64 = 0;
let baseline_duration = Duration::from_secs(self.config.duration_secs / 2);
while baseline_start.elapsed() < baseline_duration {
for _ in 0..1000 {
baseline_ops += 1;
std::hint::black_box(baseline_ops);
}
}
let baseline_elapsed = baseline_start.elapsed();
let baseline_rate = baseline_ops as f64 / baseline_elapsed.as_secs_f64();
let traced_start = Instant::now();
let mut traced_ops: u64 = 0;
let traced_duration = Duration::from_secs(self.config.duration_secs / 2);
while traced_start.elapsed() < traced_duration {
for _ in 0..1000 {
traced_ops += 1;
std::hint::black_box(std::time::Instant::now());
std::hint::black_box(traced_ops);
}
}
let traced_elapsed = traced_start.elapsed();
let traced_rate = traced_ops as f64 / traced_elapsed.as_secs_f64();
let elapsed = start.elapsed();
result.duration = elapsed;
result.total_ops = baseline_ops + traced_ops;
result.ops_per_sec = traced_rate;
let overhead = if baseline_rate > 0.0 {
((baseline_rate - traced_rate) / baseline_rate) * 100.0
} else {
0.0
};
if overhead < 5.0 {
result = result.pass(
"tracing overhead < 5%",
&format!("{:.2}% overhead", overhead),
);
} else {
result = result.fail(
"tracing overhead < 5%",
&format!("{:.2}% overhead", overhead),
);
}
result.memory.stable = true;
result
}
fn run_full_stress(&self) -> StressResult {
let start = Instant::now();
let mut result = StressResult::new(StressMode::Full);
let sub_duration = self.config.duration_secs / 4;
let atomics_config = StressConfig::atomics(sub_duration, self.config.concurrency);
let atomics_result = StressRunner::new(atomics_config).run();
let worker_config = StressConfig::worker_msg(sub_duration, self.config.concurrency);
let worker_result = StressRunner::new(worker_config).run();
let render_config = StressConfig::render(sub_duration);
let render_result = StressRunner::new(render_config).run();
let trace_config = StressConfig::trace(sub_duration);
let trace_result = StressRunner::new(trace_config).run();
let elapsed = start.elapsed();
result.duration = elapsed;
result.total_ops = atomics_result.total_ops
+ worker_result.total_ops
+ render_result.total_ops
+ trace_result.total_ops;
let all_passed = atomics_result.passed
&& worker_result.passed
&& render_result.passed
&& trace_result.passed;
if all_passed {
result = result.pass(
"all stress tests pass",
&format!(
"atomics: {}, worker: {}, render: {}, trace: {}",
if atomics_result.passed { "✓" } else { "✗" },
if worker_result.passed { "✓" } else { "✗" },
if render_result.passed { "✓" } else { "✗" },
if trace_result.passed { "✓" } else { "✗" },
),
);
} else {
result = result.fail(
"all stress tests pass",
&format!(
"atomics: {}, worker: {}, render: {}, trace: {}",
if atomics_result.passed { "✓" } else { "✗" },
if worker_result.passed { "✓" } else { "✗" },
if render_result.passed { "✓" } else { "✗" },
if trace_result.passed { "✓" } else { "✗" },
),
);
}
result.errors.extend(atomics_result.errors);
result.errors.extend(worker_result.errors);
result.errors.extend(render_result.errors);
result.errors.extend(trace_result.errors);
result.memory.stable = atomics_result.memory.stable
&& worker_result.memory.stable
&& render_result.memory.stable
&& trace_result.memory.stable;
result
}
}
pub fn render_stress_report(result: &StressResult) -> String {
let mut output = String::new();
let status = if result.passed {
"✅ PASS"
} else {
"❌ FAIL"
};
output.push_str(&format!("STRESS TEST: {} [{}]\n", result.mode, status));
output.push_str("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n");
output.push_str(&format!("Duration: {:?}\n", result.duration));
output.push_str(&format!("Operations: {}\n", result.total_ops));
output.push_str(&format!(
"Throughput: {:.0} ops/sec\n\n",
result.ops_per_sec
));
output.push_str("Pass Criteria:\n");
output.push_str(&format!(" Expected: {}\n", result.pass_criteria));
output.push_str(&format!(" Actual: {}\n\n", result.actual_value));
output.push_str("Memory:\n");
output.push_str(&format!(
" Stable: {}\n",
if result.memory.stable {
"Yes"
} else {
"No (potential leak)"
}
));
if result.memory.initial_bytes > 0 {
output.push_str(&format!(
" Growth: {:.1}%\n",
result.memory.growth_percent()
));
}
if !result.errors.is_empty() {
output.push_str(&format!("\nErrors ({}):\n", result.errors.len()));
for err in &result.errors {
output.push_str(&format!(
" [{:?}] {} - {}\n",
err.time_offset, err.kind, err.message
));
}
}
output
}
pub fn render_stress_json(result: &StressResult) -> String {
serde_json::to_string_pretty(result).unwrap_or_else(|_| "{}".to_string())
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
use std::str::FromStr;
#[test]
fn test_stress_mode_from_str() {
assert_eq!(
StressMode::from_str("atomics").unwrap(),
StressMode::Atomics
);
assert_eq!(
StressMode::from_str("worker-msg").unwrap(),
StressMode::WorkerMsg
);
assert_eq!(StressMode::from_str("render").unwrap(), StressMode::Render);
assert_eq!(StressMode::from_str("trace").unwrap(), StressMode::Trace);
assert_eq!(StressMode::from_str("full").unwrap(), StressMode::Full);
}
#[test]
fn test_stress_mode_display() {
assert_eq!(StressMode::Atomics.to_string(), "atomics");
assert_eq!(StressMode::WorkerMsg.to_string(), "worker-msg");
assert_eq!(StressMode::Render.to_string(), "render");
}
#[test]
fn test_stress_config_atomics() {
let config = StressConfig::atomics(30, 4);
assert_eq!(config.mode, StressMode::Atomics);
assert_eq!(config.duration_secs, 30);
assert_eq!(config.concurrency, 4);
}
#[test]
fn test_stress_config_worker_msg() {
let config = StressConfig::worker_msg(60, 8);
assert_eq!(config.mode, StressMode::WorkerMsg);
assert_eq!(config.duration_secs, 60);
assert_eq!(config.concurrency, 8);
}
#[test]
fn test_stress_result_pass() {
let result = StressResult::new(StressMode::Atomics).pass("> 10k ops/sec", "15000 ops/sec");
assert!(result.passed);
assert_eq!(result.pass_criteria, "> 10k ops/sec");
assert_eq!(result.actual_value, "15000 ops/sec");
}
#[test]
fn test_stress_result_fail() {
let result = StressResult::new(StressMode::Atomics).fail("> 10k ops/sec", "5000 ops/sec");
assert!(!result.passed);
}
#[test]
fn test_memory_stats_growth() {
let stats = MemoryStats {
initial_bytes: 1000,
final_bytes: 1100,
peak_bytes: 1200,
stable: true,
};
assert!((stats.growth_percent() - 10.0).abs() < 0.001);
}
#[test]
fn test_stress_error_kind_display() {
assert_eq!(StressErrorKind::LockTimeout.to_string(), "Lock Timeout");
assert_eq!(StressErrorKind::QueueOverflow.to_string(), "Queue Overflow");
assert_eq!(StressErrorKind::FrameDrop.to_string(), "Frame Drop");
}
#[test]
fn test_run_atomics_stress() {
let config = StressConfig::atomics(1, 1); let runner = StressRunner::new(config);
let result = runner.run();
assert_eq!(result.mode, StressMode::Atomics);
assert!(result.total_ops > 0);
assert!(result.ops_per_sec > 0.0);
assert!(result.passed); }
#[test]
fn test_run_worker_msg_stress() {
let config = StressConfig::worker_msg(1, 1);
let runner = StressRunner::new(config);
let result = runner.run();
assert_eq!(result.mode, StressMode::WorkerMsg);
assert!(result.total_ops > 0);
assert!(result.passed);
}
#[test]
fn test_run_render_stress() {
let config = StressConfig::render(1);
let runner = StressRunner::new(config);
let result = runner.run();
assert_eq!(result.mode, StressMode::Render);
assert!(result.total_ops > 0);
assert!(result.ops_per_sec > 50.0 && result.ops_per_sec < 70.0);
}
#[test]
fn test_run_trace_stress() {
let config = StressConfig::trace(2); let runner = StressRunner::new(config);
let result = runner.run();
assert_eq!(result.mode, StressMode::Trace);
assert!(result.total_ops > 0);
}
#[test]
fn test_run_full_stress() {
let config = StressConfig::full(4, 1); let runner = StressRunner::new(config);
let result = runner.run();
assert_eq!(result.mode, StressMode::Full);
assert!(result.total_ops > 0);
}
#[test]
fn test_render_stress_report() {
let mut result = StressResult::new(StressMode::Atomics);
result.duration = Duration::from_secs(30);
result.total_ops = 300_000;
result.ops_per_sec = 10_000.0;
result = result.pass("> 10k ops/sec", "10000 ops/sec");
let report = render_stress_report(&result);
assert!(report.contains("STRESS TEST: atomics"));
assert!(report.contains("PASS"));
assert!(report.contains("10000 ops/sec"));
}
#[test]
fn test_render_stress_json() {
let result = StressResult::new(StressMode::Atomics);
let json = render_stress_json(&result);
assert!(json.contains("Atomics"));
assert!(json.contains("mode"));
}
#[test]
fn test_stress_mode_display_all() {
assert_eq!(format!("{}", StressMode::Atomics), "atomics");
assert_eq!(format!("{}", StressMode::WorkerMsg), "worker-msg");
assert_eq!(format!("{}", StressMode::Render), "render");
assert_eq!(format!("{}", StressMode::Trace), "trace");
assert_eq!(format!("{}", StressMode::Full), "full");
}
#[test]
fn test_stress_mode_from_str_all_variants() {
assert_eq!(
"atomics".parse::<StressMode>().unwrap(),
StressMode::Atomics
);
assert_eq!(
"worker-msg".parse::<StressMode>().unwrap(),
StressMode::WorkerMsg
);
assert_eq!(
"workermsg".parse::<StressMode>().unwrap(),
StressMode::WorkerMsg
);
assert_eq!(
"worker_msg".parse::<StressMode>().unwrap(),
StressMode::WorkerMsg
);
assert_eq!("render".parse::<StressMode>().unwrap(), StressMode::Render);
assert_eq!("trace".parse::<StressMode>().unwrap(), StressMode::Trace);
assert_eq!("full".parse::<StressMode>().unwrap(), StressMode::Full);
}
#[test]
fn test_stress_mode_from_str_unknown() {
let result = "unknown_mode".parse::<StressMode>();
assert!(result.is_err());
assert!(result.unwrap_err().contains("Unknown stress mode"));
}
#[test]
fn test_stress_config_render() {
let config = StressConfig::render(45);
assert_eq!(config.mode, StressMode::Render);
assert_eq!(config.duration_secs, 45);
assert_eq!(config.concurrency, 1);
}
#[test]
fn test_stress_config_trace() {
let config = StressConfig::trace(20);
assert_eq!(config.mode, StressMode::Trace);
assert_eq!(config.duration_secs, 20);
assert_eq!(config.concurrency, 1);
}
#[test]
fn test_stress_config_full() {
let config = StressConfig::full(120, 16);
assert_eq!(config.mode, StressMode::Full);
assert_eq!(config.duration_secs, 120);
assert_eq!(config.concurrency, 16);
}
#[test]
fn test_stress_config_default() {
let config = StressConfig::default();
assert_eq!(config.mode, StressMode::Atomics);
assert_eq!(config.duration_secs, 30);
assert_eq!(config.concurrency, 4);
assert_eq!(config.target_ops_per_sec, 0);
assert_eq!(config.warmup_secs, 5);
}
#[test]
fn test_memory_stats_growth_zero_initial() {
let stats = MemoryStats {
initial_bytes: 0,
final_bytes: 1000,
peak_bytes: 1000,
stable: true,
};
assert_eq!(stats.growth_percent(), 0.0);
}
#[test]
fn test_stress_error_kind_display_all() {
assert_eq!(StressErrorKind::LockTimeout.to_string(), "Lock Timeout");
assert_eq!(StressErrorKind::QueueOverflow.to_string(), "Queue Overflow");
assert_eq!(StressErrorKind::FrameDrop.to_string(), "Frame Drop");
assert_eq!(StressErrorKind::OutOfMemory.to_string(), "Out of Memory");
assert_eq!(StressErrorKind::WorkerCrash.to_string(), "Worker Crash");
assert_eq!(StressErrorKind::Other.to_string(), "Other");
}
#[test]
fn test_render_stress_report_with_memory_growth() {
let mut result = StressResult::new(StressMode::Atomics);
result.duration = Duration::from_secs(30);
result.total_ops = 300_000;
result.ops_per_sec = 10_000.0;
result.memory = MemoryStats {
initial_bytes: 1000,
final_bytes: 1500,
peak_bytes: 1600,
stable: false,
};
result = result.fail("> 10k ops/sec", "10000 ops/sec");
let report = render_stress_report(&result);
assert!(report.contains("No (potential leak)"));
assert!(report.contains("Growth:"));
assert!(report.contains("50.0%"));
}
#[test]
fn test_render_stress_report_with_errors() {
let mut result = StressResult::new(StressMode::Render);
result.duration = Duration::from_secs(30);
result.errors.push(StressError {
kind: StressErrorKind::FrameDrop,
message: "10 frames dropped".to_string(),
time_offset: Duration::from_secs(15),
});
result.errors.push(StressError {
kind: StressErrorKind::LockTimeout,
message: "Lock timed out".to_string(),
time_offset: Duration::from_secs(20),
});
let report = render_stress_report(&result);
assert!(report.contains("Errors (2)"));
assert!(report.contains("Frame Drop"));
assert!(report.contains("Lock Timeout"));
}
#[test]
fn test_latency_stats_default() {
let stats = LatencyStats::default();
assert_eq!(stats.min_us, 0);
assert_eq!(stats.max_us, 0);
assert_eq!(stats.mean_us, 0);
assert_eq!(stats.p50_us, 0);
assert_eq!(stats.p95_us, 0);
assert_eq!(stats.p99_us, 0);
}
#[test]
fn test_stress_error_serialization() {
let error = StressError {
kind: StressErrorKind::OutOfMemory,
message: "Allocation failed".to_string(),
time_offset: Duration::from_millis(500),
};
let json = serde_json::to_string(&error).unwrap();
assert!(json.contains("OutOfMemory"));
assert!(json.contains("Allocation failed"));
let parsed: StressError = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.kind, StressErrorKind::OutOfMemory);
}
#[test]
fn test_stress_mode_default() {
let mode = StressMode::default();
assert_eq!(mode, StressMode::Atomics);
}
#[test]
fn test_memory_stats_default() {
let stats = MemoryStats::default();
assert_eq!(stats.initial_bytes, 0);
assert_eq!(stats.final_bytes, 0);
assert_eq!(stats.peak_bytes, 0);
assert!(!stats.stable);
}
#[test]
fn test_stress_result_new() {
let result = StressResult::new(StressMode::WorkerMsg);
assert_eq!(result.mode, StressMode::WorkerMsg);
assert!(!result.passed);
assert!(result.pass_criteria.is_empty());
assert!(result.actual_value.is_empty());
assert!(result.errors.is_empty());
}
#[test]
fn test_render_stress_json_error_handling() {
let result = StressResult::new(StressMode::Atomics);
let json = render_stress_json(&result);
assert!(!json.is_empty());
assert!(json.starts_with('{'));
}
#[test]
fn test_stress_config_serde() {
let config = StressConfig::atomics(60, 8);
let json = serde_json::to_string(&config).unwrap();
assert!(json.contains("Atomics"));
assert!(json.contains("60"));
assert!(json.contains('8'));
let parsed: StressConfig = serde_json::from_str(&json).unwrap();
assert_eq!(parsed.mode, StressMode::Atomics);
assert_eq!(parsed.duration_secs, 60);
}
}