126 lines
5.2 KiB
Rust
126 lines
5.2 KiB
Rust
use super::linked;
|
|
|
|
#[test]
|
|
fn async_streams_support_typed_sequential_consumers_and_infer_suspension() {
|
|
let source = "add(value: Int, state: Int) (next: Int) pure { next = value + state }\n main() (answer: Int) { answer = reduce(add, stream([1, 2]), 0) }";
|
|
let program = linked(source).unwrap();
|
|
let main = program
|
|
.functions
|
|
.iter()
|
|
.find(|function| function.signature.name == "main")
|
|
.unwrap();
|
|
assert!(main.signature.effects.suspend);
|
|
assert!(main.signature.effects.async_stream);
|
|
for source in [
|
|
"main() (values: AsyncStream<Int>) { values = stream([]) }",
|
|
"main() (values: AsyncStream<Int>) { values = dever.task.ticks(1) }",
|
|
"main(values: AsyncStream<Int>) () { close(values) }",
|
|
] {
|
|
let program = linked(source).unwrap();
|
|
let main = program
|
|
.functions
|
|
.iter()
|
|
.find(|function| function.signature.name == "main")
|
|
.unwrap();
|
|
assert!(!main.signature.effects.suspend);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn async_streams_reject_wrong_handlers_nontransferable_values_and_private_intrinsics() {
|
|
for (source, code) in [
|
|
(
|
|
"main(values: List<Stream<Int>>) () { close(stream(values)) }",
|
|
"C013",
|
|
),
|
|
(
|
|
"main(values: AsyncStream<Stream<Int>>) () { close(values) }",
|
|
"C013",
|
|
),
|
|
("main() () pure { close(stream([1])) }", "C013"),
|
|
("main() () { close(dever.system.taskTicks(1)) }", "C006"),
|
|
(
|
|
"copy(value: Int) (next: Int) { next = value }\nmain() () { each(copy, stream([1])) }",
|
|
"C005",
|
|
),
|
|
(
|
|
"consume(value: Int, state: Stream<Int>) () { close(state) }\nmain(state: Stream<Int>) () { each(consume, stream([1]), state) }",
|
|
"C013",
|
|
),
|
|
("main() () { close(stream(1)) }", "C005"),
|
|
] {
|
|
assert_eq!(linked(source).unwrap_err().code, code, "{source}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn streams_preserve_existing_error_obligations() {
|
|
let prefix = "type Result { Done\n error Failed(message: Text) }\nhandle(value: Result) () recover(\"handles stream errors\") {}\n";
|
|
assert!(
|
|
linked(&format!(
|
|
"{prefix}main() () {{ each(handle, stream([Result.Failed(\"reason\")])) }}"
|
|
))
|
|
.is_ok()
|
|
);
|
|
for body in [
|
|
"values = stream([Result.Failed(\"reason\")])\n close(values)",
|
|
"values = stream([Result.Failed(\"reason\")])",
|
|
] {
|
|
assert_eq!(
|
|
linked(&format!("{prefix}main() () {{ {body} }}"))
|
|
.unwrap_err()
|
|
.code,
|
|
"C012"
|
|
);
|
|
}
|
|
assert!(linked(&format!("{prefix}main() (values: AsyncStream<Result>) {{ values = stream([Result.Failed(\"reason\")]) }}")).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn closed_stream_aliases_cannot_discharge_unread_error_values() {
|
|
let prefix = "type Result { Done\n error Failed(message: Text) }\nhandle(value: Result) () recover(\"handles errors\") {}\nforward(values: AsyncStream<Result>) (output: AsyncStream<Result>) { output = values }\ntype Box { values: AsyncStream<Result> }\n";
|
|
for body in [
|
|
"values = stream([Result.Failed(\"reason\")])\n alias = values\n close(alias)\n each(handle, values)",
|
|
"values = stream([Result.Failed(\"reason\")])\n alias = forward(values)\n close(alias)\n each(handle, values)",
|
|
"values = stream([Result.Failed(\"reason\")])\n box = Box { values = values }\n close(box.values)\n each(handle, values)",
|
|
] {
|
|
assert_eq!(
|
|
linked(&format!("{prefix}main() () {{ {body} }}"))
|
|
.unwrap_err()
|
|
.code,
|
|
"C012",
|
|
"{body}"
|
|
);
|
|
}
|
|
assert_eq!(linked(&format!("{prefix}main() (output: AsyncStream<Result>) {{ values = stream([Result.Failed(\"reason\")])\n close(values)\n output = values }}")).unwrap_err().code, "C012");
|
|
assert!(linked(&format!("{prefix}main() () {{ original = [Result.Failed(\"reason\")]\n close(stream(original))\n each(handle, original) }}")).is_ok());
|
|
assert!(linked(&format!("{prefix}main() () {{ original = [Result.Failed(\"reason\")]\n first = stream(original)\n second = stream(original)\n close(first)\n each(handle, second) }}")).is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn forwarding_a_lazy_stream_keeps_future_item_error_obligations() {
|
|
let prefix = "type Result { Done\n error Failed(message: Text) }\nforward(values: AsyncStream<Result>) (output: AsyncStream<Result>) { output = values }\ncollect(value: Result, state: List<Result>) (next: List<Result>) { next = append(state, value) }\n";
|
|
for input in [
|
|
"stream(values)",
|
|
"forward(stream(values))",
|
|
"forward(forward(stream(values)))",
|
|
] {
|
|
assert_eq!(
|
|
linked(&format!(
|
|
"{prefix}main(values: Channel<Result>) () {{ lost = reduce(collect, {input}, []) }}"
|
|
))
|
|
.unwrap_err()
|
|
.code,
|
|
"C012",
|
|
"{input}"
|
|
);
|
|
assert!(linked(&format!("{prefix}main(values: Channel<Result>) (output: List<Result>) {{ output = reduce(collect, {input}, []) }}")).is_ok());
|
|
}
|
|
assert!(
|
|
linked(&format!(
|
|
"{prefix}main(values: Channel<Result>) () {{ close(forward(stream(values))) }}"
|
|
))
|
|
.is_ok()
|
|
);
|
|
}
|