use hojicha_core::{
commands,
core::{Cmd, Model},
event::Event,
};
use hojicha_runtime::program::{Program, ProgramOptions};
use hojicha_runtime::resource_limits::{ResourceLimits, ResourceMonitor};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use std::time::Duration;
#[derive(Clone)]
struct StressTestModel {
counter: Arc<AtomicUsize>,
}
impl Model for StressTestModel {
type Message = ();
fn init(&mut self) -> Cmd<Self::Message> {
Cmd::noop()
}
fn update(&mut self, _event: Event<Self::Message>) -> Cmd<Self::Message> {
let mut cmds = vec![];
for _ in 0..100 {
let counter = self.counter.clone();
cmds.push(commands::custom_async(move || {
let counter = counter.clone();
async move {
tokio::time::sleep(Duration::from_millis(10)).await;
counter.fetch_add(1, Ordering::SeqCst);
Some(())
}
}));
}
commands::batch(cmds)
}
fn view(&self) -> String {
format!("Counter: {}", self.counter.load(Ordering::SeqCst))
}
}
#[test]
fn test_async_task_limits_compile() {
let counter = Arc::new(AtomicUsize::new(0));
let model = StressTestModel {
counter: counter.clone(),
};
let limits = ResourceLimits::default().with_max_tasks(10);
let options = ProgramOptions::new()
.with_resource_limits(limits)
.without_renderer()
.headless();
let _program = Program::with_options(model, options);
}
#[tokio::test]
async fn test_resource_monitor_stats() {
let monitor = ResourceMonitor::with_limits(ResourceLimits::default().with_max_tasks(5));
let _p1 = monitor.try_acquire_task_permit().await.unwrap();
let _p2 = monitor.try_acquire_task_permit().await.unwrap();
let stats = monitor.stats();
assert_eq!(stats.active_tasks, 2);
assert_eq!(stats.total_spawned, 2);
assert_eq!(stats.peak_concurrent, 2);
let _p3 = monitor.try_acquire_task_permit().await.unwrap();
let _p4 = monitor.try_acquire_task_permit().await.unwrap();
let _p5 = monitor.try_acquire_task_permit().await.unwrap();
let result = monitor.try_acquire_task_permit().await;
assert!(result.is_err());
let stats = monitor.stats();
assert_eq!(stats.active_tasks, 5);
assert_eq!(stats.total_rejected, 1);
}
#[test]
fn test_resource_limits_builder() {
let limits = ResourceLimits::default()
.with_max_tasks(100)
.with_max_recursion(50);
assert_eq!(limits.max_concurrent_tasks, 100);
assert_eq!(limits.max_recursion_depth, 50);
assert_eq!(limits.task_warning_threshold, 80);
let unlimited = ResourceLimits::unlimited();
assert_eq!(unlimited.max_concurrent_tasks, usize::MAX);
assert!(!unlimited.log_warnings);
assert!(!unlimited.reject_when_full);
}