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) { values = stream([]) }", "main() (values: AsyncStream) { values = dever.task.ticks(1) }", "main(values: AsyncStream) () { 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>) () { close(stream(values)) }", "C013", ), ( "main(values: AsyncStream>) () { 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) () { close(state) }\nmain(state: Stream) () { 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) {{ 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) (output: AsyncStream) { output = values }\ntype Box { values: AsyncStream }\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) {{ 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) (output: AsyncStream) { output = values }\ncollect(value: Result, state: List) (next: List) { 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) () {{ lost = reduce(collect, {input}, []) }}" )) .unwrap_err() .code, "C012", "{input}" ); assert!(linked(&format!("{prefix}main(values: Channel) (output: List) {{ output = reduce(collect, {input}, []) }}")).is_ok()); } assert!( linked(&format!( "{prefix}main(values: Channel) () {{ close(forward(stream(values))) }}" )) .is_ok() ); }