feat: publish Dever core with project scaffolding and versioned AI skills
This commit is contained in:
1 parent
70fa73cdc2
commit
ac458ffa7f
960 files changed
+221319
-1163
No files matched your search
@@ -0,0 +1,295 @@
|
||||
use std::sync::atomic::AtomicUsize;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::{Arc, mpsc};
|
||||
use std::time::Duration;
|
||||
|
||||
use dever_runtime::{channel::Channel, task};
|
||||
|
||||
struct DropSignal(Arc<AtomicBool>);
|
||||
|
||||
impl Drop for DropSignal {
|
||||
fn drop(&mut self) {
|
||||
self.0.store(true, Ordering::SeqCst);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tasks_return_values_and_stop_waits_for_cleanup() {
|
||||
task::run_entry(async {
|
||||
let task = task::run(async { Ok::<_, String>(42_i64) }).await?;
|
||||
assert_eq!(task::wait(task).await?, 42);
|
||||
|
||||
let started = Channel::new(1)?;
|
||||
let child_started = started.clone();
|
||||
let dropped = Arc::new(AtomicBool::new(false));
|
||||
let child_dropped = Arc::clone(&dropped);
|
||||
let task = task::run(async move {
|
||||
let _drop_signal = DropSignal(child_dropped);
|
||||
child_started.send(()).await?;
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(started.receive().await?, Some(()));
|
||||
task::stop(task).await?;
|
||||
assert!(dropped.load(Ordering::SeqCst));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_failure_cancels_and_drains_remaining_tasks() {
|
||||
task::run_entry(async {
|
||||
let started = Channel::new(1)?;
|
||||
let child_started = started.clone();
|
||||
let dropped = Arc::new(AtomicBool::new(false));
|
||||
let child_dropped = Arc::clone(&dropped);
|
||||
let mut group = task::Group::new(2)?;
|
||||
group
|
||||
.run(async move {
|
||||
let _drop_signal = DropSignal(child_dropped);
|
||||
child_started.send(()).await?;
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(started.receive().await?, Some(()));
|
||||
group
|
||||
.run(async { Err::<(), _>("expected child failure".to_owned()) })
|
||||
.await?;
|
||||
|
||||
assert_eq!(group.wait().await.unwrap_err(), "expected child failure");
|
||||
assert!(dropped.load(Ordering::SeqCst));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn group_capacity_applies_backpressure_before_starting_more_work() {
|
||||
task::run_entry(async {
|
||||
let release = Channel::new(1)?;
|
||||
let wait_for_release = release.clone();
|
||||
let released = Arc::new(AtomicBool::new(false));
|
||||
let release_recorded = Arc::clone(&released);
|
||||
let mut group = task::Group::new(1)?;
|
||||
group
|
||||
.run(async move {
|
||||
wait_for_release.receive().await?;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
let unblock = task::run(async move {
|
||||
task::sleep(1).await?;
|
||||
release_recorded.store(true, Ordering::SeqCst);
|
||||
release.send(()).await
|
||||
})
|
||||
.await?;
|
||||
group.run(async { Ok(()) }).await?;
|
||||
assert!(released.load(Ordering::SeqCst));
|
||||
group.wait().await?;
|
||||
task::wait(unblock).await?;
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn channels_are_bounded_and_close_after_buffered_values() {
|
||||
task::run_entry(async {
|
||||
let channel = Channel::new(1)?;
|
||||
channel.send(7_i64).await?;
|
||||
channel.close().await?;
|
||||
assert_eq!(channel.receive().await?, Some(7));
|
||||
assert_eq!(channel.receive().await?, None);
|
||||
assert!(channel.send(8).await.is_err());
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
assert!(Channel::<i64>::new(0).is_err());
|
||||
assert!(Channel::<i64>::new(65_537).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn closing_a_channel_wakes_blocked_senders_and_receivers() {
|
||||
task::run_entry(async {
|
||||
let full = Channel::new(1)?;
|
||||
full.send(1_i64).await?;
|
||||
let blocked_sender = full.clone();
|
||||
let send = task::run(async move { blocked_sender.send(2).await }).await?;
|
||||
full.close().await?;
|
||||
assert_eq!(task::wait(send).await.unwrap_err(), "channel is closed");
|
||||
assert_eq!(full.receive().await?, Some(1));
|
||||
assert_eq!(full.receive().await?, None);
|
||||
|
||||
let empty = Channel::<i64>::new(1)?;
|
||||
let blocked_receiver = empty.clone();
|
||||
let receive = task::run(async move { blocked_receiver.receive().await }).await?;
|
||||
empty.close().await?;
|
||||
assert_eq!(task::wait(receive).await?, None);
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parallel_and_blocking_use_the_bounded_blocking_pool() {
|
||||
task::run_entry(async {
|
||||
task::sleep(0).await?;
|
||||
assert_eq!(
|
||||
task::sleep(-1).await,
|
||||
Err("sleep duration must not be negative")
|
||||
);
|
||||
assert_eq!(task::parallel(|| Ok::<_, String>(6 * 7)).await?, 42);
|
||||
assert_eq!(task::blocking(|| Ok::<_, String>("done")).await?, "done");
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn runtime_configuration_rejects_unbounded_thread_counts() {
|
||||
let result = task::run_entry_with(
|
||||
task::RuntimeConfig {
|
||||
worker_threads: task::MAX_ASYNC_WORKERS + 1,
|
||||
max_blocking_threads: 1,
|
||||
task_capacity: 1,
|
||||
},
|
||||
async { Ok::<_, String>(()) },
|
||||
);
|
||||
assert!(result.unwrap_err().contains("async worker count"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn root_and_parent_failures_wait_for_descendant_cleanup() {
|
||||
let root_dropped = Arc::new(AtomicBool::new(false));
|
||||
let root_child_dropped = Arc::clone(&root_dropped);
|
||||
let root_result = task::run_entry(async move {
|
||||
let started = Channel::new(1)?;
|
||||
let child_started = started.clone();
|
||||
let _child = task::run(async move {
|
||||
let _drop_signal = DropSignal(root_child_dropped);
|
||||
child_started.send(()).await?;
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(started.receive().await?, Some(()));
|
||||
Err::<(), _>("root failure".to_owned())
|
||||
});
|
||||
assert_eq!(root_result.unwrap_err(), "root failure");
|
||||
assert!(root_dropped.load(Ordering::SeqCst));
|
||||
|
||||
let descendant_dropped = Arc::new(AtomicBool::new(false));
|
||||
let child_dropped = Arc::clone(&descendant_dropped);
|
||||
task::run_entry(async move {
|
||||
let child_started = Channel::new(1)?;
|
||||
let wait_for_child = child_started.clone();
|
||||
let parent_ready = Channel::new(1)?;
|
||||
let notify_root = parent_ready.clone();
|
||||
let parent = task::run(async move {
|
||||
let _descendant = task::run(async move {
|
||||
let _drop_signal = DropSignal(child_dropped);
|
||||
child_started.send(()).await?;
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(wait_for_child.receive().await?, Some(()));
|
||||
notify_root.send(()).await?;
|
||||
Err::<(), _>("parent failure".to_owned())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(parent_ready.receive().await?, Some(()));
|
||||
assert_eq!(task::wait(parent).await.unwrap_err(), "parent failure");
|
||||
assert!(descendant_dropped.load(Ordering::SeqCst));
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let group_dropped = Arc::new(AtomicBool::new(false));
|
||||
let group_child_dropped = Arc::clone(&group_dropped);
|
||||
let group_result = task::run_entry(async move {
|
||||
let started = Channel::new(1)?;
|
||||
let child_started = started.clone();
|
||||
let mut group = task::Group::new(1)?;
|
||||
group
|
||||
.run(async move {
|
||||
let _drop_signal = DropSignal(group_child_dropped);
|
||||
child_started.send(()).await?;
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
assert_eq!(started.receive().await?, Some(()));
|
||||
Err::<(), _>("group parent failure".to_owned())
|
||||
});
|
||||
assert_eq!(group_result.unwrap_err(), "group parent failure");
|
||||
assert!(group_dropped.load(Ordering::SeqCst));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn stopping_a_task_waits_for_started_blocking_work() {
|
||||
let finished = Arc::new(AtomicBool::new(false));
|
||||
let blocking_finished = Arc::clone(&finished);
|
||||
let (started, running) = mpsc::channel();
|
||||
task::run_entry_with(
|
||||
task::RuntimeConfig {
|
||||
worker_threads: 1,
|
||||
max_blocking_threads: 1,
|
||||
task_capacity: 4,
|
||||
},
|
||||
async move {
|
||||
let child = task::run(async move {
|
||||
task::blocking(move || {
|
||||
started.send(()).unwrap();
|
||||
std::thread::sleep(Duration::from_millis(20));
|
||||
blocking_finished.store(true, Ordering::SeqCst);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
})
|
||||
.await?;
|
||||
running.recv_timeout(Duration::from_secs(1)).unwrap();
|
||||
task::stop(child).await?;
|
||||
assert!(finished.load(Ordering::SeqCst));
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn async_parallel_each_shares_the_configured_blocking_limit() {
|
||||
let active = Arc::new(AtomicUsize::new(0));
|
||||
let peak = Arc::new(AtomicUsize::new(0));
|
||||
task::run_entry_with(
|
||||
task::RuntimeConfig {
|
||||
worker_threads: 2,
|
||||
max_blocking_threads: 2,
|
||||
task_capacity: 4,
|
||||
},
|
||||
async {
|
||||
let run_batch = |active: Arc<AtomicUsize>, peak: Arc<AtomicUsize>| async move {
|
||||
task::parallel_each(0..32, 256, move |_| {
|
||||
let current = active.fetch_add(1, Ordering::SeqCst) + 1;
|
||||
peak.fetch_max(current, Ordering::SeqCst);
|
||||
std::thread::sleep(Duration::from_millis(1));
|
||||
active.fetch_sub(1, Ordering::SeqCst);
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
};
|
||||
let first = task::run(run_batch(Arc::clone(&active), Arc::clone(&peak))).await?;
|
||||
let second = task::run(run_batch(Arc::clone(&active), Arc::clone(&peak))).await?;
|
||||
task::wait(first).await?;
|
||||
task::wait(second).await?;
|
||||
assert_eq!(peak.load(Ordering::SeqCst), 2);
|
||||
Ok(())
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
Reference in new issue
Block a user