use futures::FutureExt;
use mm1_address::address::Address;
use mm1_address::subnet::NetAddress;
use mm1_common::errors::error_of::ErrorOf;
use mm1_common::types::Never;
use mm1_core::context::{
Bind, BindArgs, BindErrorKind, Fork, ForkErrorKind, InitDone, Linking, Messaging, Now, Quit,
RecvErrorKind, Start, Stop, Watching,
};
use mm1_core::envelope::Envelope;
use mm1_proto_system::{SpawnErrorKind, StartErrorKind};
use tokio::sync::{mpsc, oneshot};
use tokio::time::Instant;
use crate::rt::{Query, TaskKey, TestContext, query};
impl TaskKey {
pub fn actor(address: Address) -> Self {
Self {
actor: address,
context: address,
}
}
pub fn fork(actor: Address, fork: Address) -> Self {
assert_ne!(actor, fork);
Self {
actor,
context: fork,
}
}
}
impl<R> Now for TestContext<R>
where
R: Send,
{
type Instant = Instant;
fn now(&self) -> Self::Instant {
Instant::now()
}
}
impl<R> Start<R> for TestContext<R>
where
R: Send,
{
async fn spawn(&mut self, runnable: R, link: bool) -> Result<Address, ErrorOf<SpawnErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Spawn {
task_key,
runnable,
link,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn start(
&mut self,
runnable: R,
link: bool,
start_timeout: std::time::Duration,
) -> Result<Address, ErrorOf<StartErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Start {
task_key,
runnable,
link,
start_timeout,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Bind<NetAddress> for TestContext<R>
where
R: Send + 'static,
{
async fn bind(&mut self, args: BindArgs<NetAddress>) -> Result<(), ErrorOf<BindErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
|outcome_tx| {
query::Bind {
task_key,
args,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Fork for TestContext<R>
where
R: Send + 'static,
{
async fn fork(&mut self) -> Result<Self, ErrorOf<ForkErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
|outcome_tx| {
query::Fork {
task_key,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn run<F, Fut>(mut self, fun: F)
where
F: FnOnce(Self) -> Fut,
F: Send + 'static,
Fut: Future + Send + 'static,
{
let queries_tx = self.queries_tx.clone();
let task_key = self.task_key;
let address_lease = self.address_lease.take();
let task_fut = async move {
let _ = fun(self).await;
}
.boxed();
invoke(
&queries_tx,
|outcome_tx| {
query::ForkRun {
task_key,
address_lease,
task_fut,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Quit for TestContext<R>
where
R: Send,
{
async fn quit_ok(&mut self) -> Never {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Quit {
task_key,
result: Ok(()),
outcome_tx,
}
},
OnRxFailure::Freeze,
)
.await
}
async fn quit_err<E>(&mut self, reason: E) -> Never
where
E: std::error::Error + Send + Sync + 'static,
{
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Quit {
task_key,
result: Err(Box::new(reason)),
outcome_tx,
}
},
OnRxFailure::Freeze,
)
.await
}
}
impl<R> Messaging for TestContext<R>
where
R: Send,
{
fn address(&self) -> Address {
self.task_key.context
}
async fn close(&mut self) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::RecvClose {
task_key,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn recv(&mut self) -> Result<Envelope, ErrorOf<RecvErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Recv {
task_key,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn send(
&mut self,
envelope: Envelope,
) -> Result<(), ErrorOf<mm1_core::context::SendErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Tell {
task_key,
to: envelope.header().to,
envelope,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn forward(
&mut self,
to: Address,
envelope: Envelope,
) -> Result<(), ErrorOf<mm1_core::context::SendErrorKind>> {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Tell {
task_key,
to,
envelope,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Watching for TestContext<R>
where
R: Send,
{
async fn watch(&mut self, peer: Address) -> mm1_proto_system::WatchRef {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Watch {
task_key,
peer,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn unwatch(&mut self, watch_ref: mm1_proto_system::WatchRef) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Unwatch {
task_key,
watch_ref,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Linking for TestContext<R>
where
R: Send,
{
async fn link(&mut self, peer: Address) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
|outcome_tx| {
query::Link {
task_key,
peer,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn unlink(&mut self, peer: Address) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
|outcome_tx| {
query::Unlink {
task_key,
peer,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn set_trap_exit(&mut self, enable: bool) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
|outcome_tx| {
query::SetTrapExit {
task_key,
enable,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> InitDone for TestContext<R>
where
R: Send,
{
async fn init_done(&mut self, address: Address) {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::InitDone {
task_key,
address,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
impl<R> Stop for TestContext<R>
where
R: Send,
{
async fn exit(&mut self, peer: Address) -> bool {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Exit {
task_key,
peer,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
async fn kill(&mut self, peer: Address) -> bool {
let task_key = self.task_key;
invoke(
&self.queries_tx,
move |outcome_tx| {
query::Kill {
task_key,
peer,
outcome_tx,
}
},
OnRxFailure::Panic,
)
.await
}
}
async fn invoke<R, F, Q, Out>(
queries_tx: &mpsc::UnboundedSender<Query<R>>,
make_query: F,
on_rx_failure: OnRxFailure,
) -> Out
where
F: FnOnce(oneshot::Sender<Out>) -> Q,
Q: Into<Query<R>>,
{
let (outcome_tx, outcome_rx) = oneshot::channel();
let query = make_query(outcome_tx);
queries_tx.send(query.into()).expect("tx failed");
match (outcome_rx.await, on_rx_failure) {
(Ok(ret), _) => ret,
(Err(reason), OnRxFailure::Panic) => panic!("rx failed: {reason}"),
(Err(_), OnRxFailure::Freeze) => std::future::pending().await,
}
}
enum OnRxFailure {
Panic,
Freeze,
}