149 lines
5.1 KiB
Rust
149 lines
5.1 KiB
Rust
mod support;
|
|
|
|
use support::{checked, sources, stdout};
|
|
|
|
#[test]
|
|
fn async_program_compiles_and_runs_every_execution_class() {
|
|
let output = stdout(
|
|
r#"sleep_ok(result: dever.time.SleepResult.Done) () recover("test handles timer failures") {}
|
|
sleep_ok(result: dever.time.SleepResult.Failed(message)) () recover("test handles timer failures") {}
|
|
pause() (result: dever.time.SleepResult) { result = result(dever.time.sleep(1)) }
|
|
double(value: Int) (answer: Int) pure { answer = value * 2 }
|
|
delayed(value: Int, millis: Int) (answer: Int) {
|
|
sleep_ok(result(dever.task.sleep(millis)))
|
|
answer = value
|
|
}
|
|
send_value(channel: Channel<Int>, value: Int) () {
|
|
send(channel, value)
|
|
}
|
|
wait_until_stopped() () {
|
|
sleep_ok(result(dever.task.sleep(1000)))
|
|
}
|
|
public main() (direct: Int, first: Int, second: Int, received: Int?, grouped: Int?, computed: Int) {
|
|
direct = delayed(1, 1)
|
|
first_task = run(delayed(2, 10))
|
|
second_task = run(delayed(3, 1))
|
|
second = wait(second_task)
|
|
first = wait(first_task)
|
|
|
|
channel = channel(Int, 2)
|
|
sender = run(send_value(channel, 4))
|
|
received = receive(channel)
|
|
wait(sender)
|
|
|
|
workers = group(2)
|
|
run(workers, send_value(channel, 5))
|
|
wait(workers)
|
|
grouped = receive(channel)
|
|
close(channel)
|
|
|
|
cancelled = run(wait_until_stopped())
|
|
stop(cancelled)
|
|
computed = parallel(double(21))
|
|
sleep_ok(blocking(pause()))
|
|
}"#,
|
|
);
|
|
|
|
assert_eq!(
|
|
output,
|
|
"direct = 1\nfirst = 2\nsecond = 3\nreceived = 4\ngrouped = 5\ncomputed = 42\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn only_inferred_suspending_entries_start_the_shared_runtime() {
|
|
let sync_sources = sources("public main() (answer: Int) { answer = 1 }");
|
|
let sync =
|
|
dever_core::native::emit(&checked(&sync_sources), &sync_sources, "main.main").unwrap();
|
|
assert!(sync.contains("match entry()"), "{sync}");
|
|
assert!(!sync.contains("task::run_entry_typed(entry())"), "{sync}");
|
|
|
|
let async_sources = sources(
|
|
r#"sleep_ok(result: dever.time.SleepResult.Done) () recover("timer failure handled") {}
|
|
sleep_ok(result: dever.time.SleepResult.Failed(message)) () recover("timer failure handled") {}
|
|
public main() () { sleep_ok(result(dever.task.sleep(0))) }"#,
|
|
);
|
|
let asynchronous =
|
|
dever_core::native::emit(&checked(&async_sources), &async_sources, "main.main").unwrap();
|
|
assert!(
|
|
asynchronous.contains("match task::run_entry_typed(entry())"),
|
|
"{asynchronous}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn timer_suspension_is_inferred_without_a_source_modifier() {
|
|
let program_sources = sources(
|
|
"public main() (result: dever.time.SleepResult) { result = dever.system.task_sleep(1) }",
|
|
);
|
|
let generated =
|
|
dever_core::native::emit(&checked(&program_sources), &program_sources, "main.main")
|
|
.unwrap();
|
|
assert!(generated.contains("async fn"), "{generated}");
|
|
assert!(
|
|
generated.contains("task::run_entry_typed(entry())"),
|
|
"{generated}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn async_parallel_each_runs_outside_the_runtime_worker() {
|
|
let source = r#"sleep_ok(result: dever.time.SleepResult.Done) () recover("timer failure handled") {}
|
|
sleep_ok(result: dever.time.SleepResult.Failed(message)) () recover("timer failure handled") {}
|
|
visit(value: Int) () {}
|
|
public main() (answer: Int) {
|
|
sleep_ok(result(dever.task.sleep(0)))
|
|
parallel_each(visit, [1, 2, 3], 2)
|
|
parallel_each(visit, dever.bytes.from_text("ab"), 2)
|
|
answer = 7
|
|
}"#;
|
|
let program_sources = sources(source);
|
|
let generated =
|
|
dever_core::native::emit(&checked(&program_sources), &program_sources, "main.main")
|
|
.unwrap();
|
|
assert!(
|
|
generated.contains("task::parallel_each_typed(a0.into_values()"),
|
|
"{generated}"
|
|
);
|
|
assert_eq!(stdout(source), "answer = 7\n");
|
|
}
|
|
|
|
#[test]
|
|
fn nullable_channel_items_are_flattened_at_the_language_boundary() {
|
|
assert_eq!(
|
|
stdout(
|
|
"public main() (received: Int?) { channel = channel(Int?, 1)\nsend(channel, null)\nreceived = receive(channel)\nclose(channel) }",
|
|
),
|
|
"received = null\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn inferred_suspension_requires_explicit_transitive_blocking_boundaries() {
|
|
support::rejected(
|
|
r#"sleep_ok(result: dever.time.SleepResult.Done) () recover("timer failure handled") {}
|
|
sleep_ok(result: dever.time.SleepResult.Failed(message)) () recover("timer failure handled") {}
|
|
pause() (result: dever.time.SleepResult) { result = result(dever.time.sleep(1)) }
|
|
public main() (result: dever.time.SleepResult) {
|
|
sleep_ok(result(dever.task.sleep(0)))
|
|
result = pause()
|
|
}"#,
|
|
"cannot run inline inside a suspending function",
|
|
);
|
|
support::rejected(
|
|
r#"sleep_ok(result: dever.time.SleepResult.Done) () recover("timer failure handled") {}
|
|
sleep_ok(result: dever.time.SleepResult.Failed(message)) () recover("timer failure handled") {}
|
|
visit(value: Int) () {}
|
|
visit_all(values: List<Int>) () { parallel_each(visit, values, 2) }
|
|
public main() () {
|
|
sleep_ok(result(dever.task.sleep(0)))
|
|
visit_all([1, 2, 3])
|
|
}"#,
|
|
"cannot run inline inside a suspending function",
|
|
);
|
|
checked(&sources(
|
|
r#"visit(value: Int) () {}
|
|
visit_all(values: Stream<Int>) () { parallel_each(visit, values, 2) }"#,
|
|
));
|
|
}
|