use corophage::coroutine::Co;
use corophage::prelude::*;
use corophage::{asynk, sync};
#[test]
fn cancelled_display() {
assert_eq!(format!("{}", Cancelled), "computation was cancelled");
}
#[test]
fn cancelled_is_error() {
fn accepts_error(_: &dyn std::error::Error) {}
accepts_error(&Cancelled);
}
#[test]
fn cancelled_error_source_is_none() {
let err: &dyn std::error::Error = &Cancelled;
assert!(err.source().is_none());
}
#[test]
fn cancelled_question_mark_propagation() {
fn might_fail() -> Result<(), Box<dyn std::error::Error>> {
Err(Cancelled)?;
Ok(())
}
assert_eq!(
might_fail().unwrap_err().to_string(),
"computation was cancelled"
);
}
#[test]
fn cancelled_copy_and_eq() {
let a = Cancelled;
let b = a; assert_eq!(a, b); }
struct Ask(pub &'static str);
impl Effect for Ask {
type Resume<'r> = &'static str;
}
type AskEffects = Effects![Ask];
#[test]
fn sync_single_effect_resume() {
let co: Co<'_, AskEffects, &'static str> =
Co::new(|yielder| async move { yielder.yield_(Ask("q")).await });
let result = sync::run(co, &mut hlist![|Ask(_)| Control::resume("42")]);
assert_eq!(result, Ok("42"));
}
#[test]
fn sync_single_effect_cancel() {
let co: Co<'_, AskEffects, &'static str> = Co::new(|yielder| async move {
yielder.yield_(Ask("forbidden")).await;
"unreachable"
});
let result = sync::run(co, &mut hlist![|_: Ask| Control::cancel()]);
assert_eq!(result, Err(Cancelled));
}
#[test]
fn sync_single_effect_multiple_yields() {
let co: Co<'_, AskEffects, (&'static str, &'static str, &'static str)> =
Co::new(|yielder| async move {
let a = yielder.yield_(Ask("q1")).await;
let b = yielder.yield_(Ask("q2")).await;
let c = yielder.yield_(Ask("q3")).await;
(a, b, c)
});
let mut state: u32 = 0;
let result = sync::run_stateful(
co,
&mut state,
&mut hlist![|s: &mut u32, _: Ask| {
*s += 1;
Control::resume(match *s {
1 => "one",
2 => "two",
_ => "three",
})
}],
);
assert_eq!(result, Ok(("one", "two", "three")));
assert_eq!(state, 3);
}
#[test]
fn sync_handler_accumulates_effects() {
let co: Co<'_, AskEffects, ()> = Co::new(|yielder| async move {
yielder.yield_(Ask("q1")).await;
yielder.yield_(Ask("q2")).await;
yielder.yield_(Ask("q3")).await;
});
let mut log: Vec<&str> = vec![];
let result = sync::run(
co,
&mut hlist![|Ask(q)| {
log.push(q);
Control::resume("_")
}],
);
assert_eq!(result, Ok(()));
assert_eq!(log, ["q1", "q2", "q3"]);
}
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn async_single_effect_with_real_sleep() {
use std::time::Duration;
let co: Co<'_, AskEffects, &'static str> =
Co::new(|yielder| async move { yielder.yield_(Ask("q")).await });
let result = asynk::run(
co,
&mut hlist![async |_: Ask| {
tokio::time::sleep(Duration::from_millis(1)).await;
Control::resume("answered")
}],
)
.await;
assert_eq!(result, Ok("answered"));
}
#[tokio::test]
#[cfg_attr(miri, ignore)]
async fn async_handler_accumulates_via_refcell() {
use std::cell::RefCell;
let log: RefCell<Vec<&str>> = RefCell::new(vec![]);
let co: Co<'_, AskEffects, ()> = Co::new(|yielder| async move {
yielder.yield_(Ask("a")).await;
yielder.yield_(Ask("b")).await;
});
let result = asynk::run(
co,
&mut hlist![async |Ask(q): Ask| {
log.borrow_mut().push(q);
Control::resume("_")
}],
)
.await;
assert_eq!(result, Ok(()));
assert_eq!(log.into_inner(), ["a", "b"]);
}