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,201 @@
|
||||
mod support;
|
||||
|
||||
use std::sync::{
|
||||
Arc,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
use std::time::Duration;
|
||||
|
||||
use dever_runtime::{channel::Channel, task};
|
||||
|
||||
fn run(future: impl std::future::Future<Output = Result<(), String>>) {
|
||||
task::run_entry_with(
|
||||
task::RuntimeConfig {
|
||||
worker_threads: 1,
|
||||
max_blocking_threads: 2,
|
||||
task_capacity: 32,
|
||||
},
|
||||
future,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn source_runtime_configuration_validates_limits_and_starts_once() {
|
||||
assert!(task::configured(0, 1, 8).is_err());
|
||||
assert!(task::configured(1, -1, 8).is_err());
|
||||
assert!(task::configured(1, 1, 65537).is_err());
|
||||
let config = task::configured(1, 2, 8).unwrap();
|
||||
task::start_entry(config, async { Ok(()) }).unwrap();
|
||||
assert!(
|
||||
task::start_entry(config, async { Ok(()) })
|
||||
.unwrap_err()
|
||||
.contains("only be started once")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn timeout_drains_started_blocking_children_before_returning() {
|
||||
run(async {
|
||||
let started = Arc::new(tokio::sync::Semaphore::new(0));
|
||||
let finished = Arc::new(AtomicBool::new(false));
|
||||
let signal = started.clone();
|
||||
let completed = finished.clone();
|
||||
let job = task::run(async move {
|
||||
task::blocking(move || {
|
||||
signal.add_permits(1);
|
||||
std::thread::sleep(Duration::from_millis(40));
|
||||
completed.store(true, Ordering::Release);
|
||||
Ok(17)
|
||||
})
|
||||
.await
|
||||
})
|
||||
.await?;
|
||||
started.acquire().await.unwrap().forget();
|
||||
assert_eq!(task::wait_timeout(job, 1).await?, None);
|
||||
assert!(finished.load(Ordering::Acquire));
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn race_stops_losers_and_preserves_program_faults() {
|
||||
run(async {
|
||||
struct Dropped(Arc<AtomicBool>);
|
||||
impl Drop for Dropped {
|
||||
fn drop(&mut self) {
|
||||
self.0.store(true, Ordering::Release);
|
||||
}
|
||||
}
|
||||
let ended = Arc::new(AtomicBool::new(false));
|
||||
let owner = Dropped(ended.clone());
|
||||
let slow = task::run(async move {
|
||||
let _owner = owner;
|
||||
std::future::pending::<Result<i64, String>>().await
|
||||
})
|
||||
.await?;
|
||||
let fast = task::run(async { Ok(42) }).await?;
|
||||
assert_eq!(task::race(vec![slow, fast]).await?, 42);
|
||||
assert!(ended.load(Ordering::Acquire));
|
||||
let failed = task::run(async { Err::<i64, _>("original fault".into()) }).await?;
|
||||
let pending = task::run(std::future::pending::<Result<i64, String>>()).await?;
|
||||
assert_eq!(
|
||||
task::race(vec![failed, pending]).await.unwrap_err(),
|
||||
"original fault"
|
||||
);
|
||||
let failed = task::run(async { Err::<(), _>("timer fault".into()) }).await?;
|
||||
assert_eq!(
|
||||
task::wait_timeout(failed, 100).await.unwrap_err(),
|
||||
"timer fault"
|
||||
);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn channel_stream_drains_close_without_closing_independent_aliases() {
|
||||
run(async {
|
||||
let channel = Channel::new(2).unwrap();
|
||||
channel.send(Some(1)).await?;
|
||||
channel.send(None).await?;
|
||||
let stream = channel.clone().stream();
|
||||
assert_eq!(stream.pull().await, Some(Some(1)));
|
||||
stream.close();
|
||||
assert_eq!(channel.receive().await?, Some(None));
|
||||
channel.send(Some(2)).await?;
|
||||
channel.close().await?;
|
||||
let stream = channel.stream();
|
||||
assert_eq!(stream.pull().await, Some(Some(2)));
|
||||
assert_eq!(stream.pull().await, None);
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn ticks_delay_first_value_skip_missed_periods_and_close_pending_pull() {
|
||||
run(async {
|
||||
let ticks = task::ticks(20)?;
|
||||
assert!(
|
||||
tokio::time::timeout(Duration::from_millis(2), ticks.pull())
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
assert_eq!(ticks.pull().await, Some(1));
|
||||
tokio::time::sleep(Duration::from_millis(70)).await;
|
||||
assert_eq!(ticks.pull().await, Some(2));
|
||||
assert!(
|
||||
tokio::time::timeout(Duration::from_millis(2), ticks.pull())
|
||||
.await
|
||||
.is_err()
|
||||
);
|
||||
let waiter = ticks.clone();
|
||||
let pending = task::run(async move { Ok(waiter.pull().await) }).await?;
|
||||
ticks.close();
|
||||
assert_eq!(task::wait(pending).await?, None);
|
||||
assert!(task::ticks(0).is_err());
|
||||
Ok(())
|
||||
});
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn static_composition_rejects_wrong_shapes_and_reused_tasks() {
|
||||
support::rejected(
|
||||
"work() (value: Int) { value = 1 }\nfallback() (value: Text) { value = \"wrong\" }\nmain() () { job = run(work())\nvalue = timeout(job, 1, fallback) }",
|
||||
"signature",
|
||||
);
|
||||
support::rejected(
|
||||
"work() (value: Int) { value = 1 }\nmain() () { job = run(work())\nvalue = race(job, job) }",
|
||||
"consumed",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_composition_and_suspending_memory_handlers() {
|
||||
let source = r#"delayed(value: Int, millis: Int) (answer: Int) recover("test timer") {
|
||||
dever.task.sleep(millis)
|
||||
answer = value
|
||||
}
|
||||
fallback() (answer: Int) { answer = 9 }
|
||||
collect(value: Int, output: Channel<Int>) () { send(output, value) }
|
||||
add(value: Int, state: Int) (result: Int) { result = state + value }
|
||||
identity(value: Int) (result: Int) { result = value }
|
||||
pair(millis: Int) (number: Int, text: Text) recover("test timer") {
|
||||
dever.task.sleep(millis)
|
||||
number = 8
|
||||
text = "pair"
|
||||
}
|
||||
pair_timeout() (number: Int, text: Text) { number = 0
|
||||
text = "timeout" }
|
||||
worker() () {
|
||||
slow = run(delayed(1, 1000))
|
||||
timed = timeout(slow, 1, fallback)
|
||||
first = run(delayed(2, 1000))
|
||||
second = run(delayed(3, 1))
|
||||
fastest = race(first, second)
|
||||
values = each(add, [1, 2], 10)
|
||||
total = reduce(add, [1, 2, 3], 0)
|
||||
mapped = each(identity, [7, 8])
|
||||
mapped_total = sum(mapped)
|
||||
pair_task = run(pair(1000))
|
||||
pair_value = timeout(pair_task, 1, pair_timeout)
|
||||
pair_text = pair_value.text
|
||||
channel = channel(Int, 4)
|
||||
each(collect, [4, 5], channel)
|
||||
parallel_each(collect, dever.bytes.from_text("ab"), 2, channel)
|
||||
close(channel)
|
||||
received = reduce(add, stream(channel), 0)
|
||||
timer = dever.task.ticks(10)
|
||||
close(timer)
|
||||
blocking(dever.io.println(int.to_text(timed)))
|
||||
blocking(dever.io.println(int.to_text(fastest)))
|
||||
blocking(dever.io.println(int.to_text(sum(values))))
|
||||
blocking(dever.io.println(int.to_text(total)))
|
||||
blocking(dever.io.println(int.to_text(received)))
|
||||
blocking(dever.io.println(int.to_text(mapped_total)))
|
||||
blocking(dever.io.println(pair_text))
|
||||
}
|
||||
public main() () {
|
||||
worker()
|
||||
}"#;
|
||||
assert_eq!(support::stdout(source), "9\n3\n23\n6\n204\n15\ntimeout\n");
|
||||
}
|
||||
Reference in new issue
Block a user