mod common;
use std::future::Future;
use std::time::Duration;
use common::Certs;
use futures::future::{Either, select};
use futures::io::{AsyncReadExt, AsyncWriteExt};
use weida::{Runtime, RuntimeConfig, TransferMeta};
const DEADLINE: Duration = Duration::from_secs(10);
fn within<F: Future>(body: F) -> F::Output {
let (tx, rx) = futures::channel::oneshot::channel::<()>();
std::thread::spawn(move || {
std::thread::sleep(DEADLINE);
let _ = tx.send(());
});
futures::executor::block_on(async move {
let deadline = Box::pin(async move {
let _ = rx.await;
});
match select(Box::pin(body), deadline).await {
Either::Left((out, _)) => out,
Either::Right(((), _)) => panic!("operation timed out after {DEADLINE:?}"),
}
})
}
#[test]
fn a_req_rep_round_trip_runs_without_a_tokio_executor() {
assert!(
tokio::runtime::Handle::try_current().is_err(),
"this test must run with no ambient tokio runtime"
);
let certs = Certs::generate();
let runtime = Runtime::owned(RuntimeConfig::default()).expect("owned runtime");
let listener = runtime.listener();
let replier = listener.replier("/transform").expect("replier");
within(async {
let binding = listener
.bind_quic(
"127.0.0.1:0".parse().expect("loopback address"),
certs.server_tls(),
)
.await
.expect("bind");
let url = format!(
"weida://127.0.0.1:{}/transform",
binding.local_addr().port()
);
let server = async {
let mut request = replier.accept().await.expect("accept");
let mut body = request.take_body();
let mut out = request
.reply(TransferMeta::default())
.await
.expect("open reply");
let mut received = Vec::new();
body.read_to_end(&mut received)
.await
.expect("read the request body");
received.make_ascii_uppercase();
AsyncWriteExt::write_all(&mut out, &received)
.await
.expect("write the reply");
out.finish().expect("finish the reply");
};
let client = async {
let requester = runtime.requester(certs.client_tls());
requester.connect(&url).await.expect("connect");
let (mut transfer, reply) = requester
.open(TransferMeta::default())
.await
.expect("open the exchange");
AsyncWriteExt::write_all(&mut transfer, b"hello weida")
.await
.expect("write the request");
transfer.finish().expect("finish the request");
let mut answer = reply.recv().await.expect("reply");
let mut bytes = Vec::new();
answer
.read_to_end(&mut bytes)
.await
.expect("read the reply body");
bytes
};
let ((), answer) = futures::future::join(server, client).await;
assert_eq!(answer, b"HELLO WEIDA");
runtime.shutdown().await;
});
}
#[test]
fn a_push_pull_transfer_runs_without_a_tokio_executor() {
assert!(
tokio::runtime::Handle::try_current().is_err(),
"this test must run with no ambient tokio runtime"
);
let certs = Certs::generate();
let runtime = Runtime::owned(RuntimeConfig::default()).expect("owned runtime");
let listener = runtime.listener();
let puller = listener.puller("/jobs").expect("puller");
within(async {
let binding = listener
.bind_quic(
"127.0.0.1:0".parse().expect("loopback address"),
certs.server_tls(),
)
.await
.expect("bind");
let url = format!("weida://127.0.0.1:{}/jobs", binding.local_addr().port());
let pusher = runtime.pusher(certs.client_tls());
pusher.connect(&url).await.expect("connect");
let send = async {
let mut transfer = pusher
.open(TransferMeta::default())
.await
.expect("open the transfer");
AsyncWriteExt::write_all(&mut transfer, b"job one")
.await
.expect("write the payload");
transfer.finish().expect("finish the transfer");
};
let receive = async {
let mut incoming = puller.recv().await.expect("recv");
let mut body = Vec::new();
incoming
.read_to_end(&mut body)
.await
.expect("read the payload");
body
};
let ((), body) = futures::future::join(send, receive).await;
assert_eq!(body, b"job one");
runtime.shutdown().await;
});
}
#[test]
fn a_pub_sub_fan_out_runs_without_a_tokio_executor() {
assert!(
tokio::runtime::Handle::try_current().is_err(),
"this test must run with no ambient tokio runtime"
);
let certs = Certs::generate();
let runtime = Runtime::owned(RuntimeConfig::default()).expect("owned runtime");
let listener = runtime.listener();
let publisher = listener.publisher("/md").expect("publisher");
within(async {
let binding = listener
.bind_quic(
"127.0.0.1:0".parse().expect("loopback address"),
certs.server_tls(),
)
.await
.expect("bind");
let url = format!("weida://127.0.0.1:{}/md", binding.local_addr().port());
let subscriber = runtime.subscriber(certs.client_tls());
subscriber.connect(&url).await.expect("connect");
subscriber.subscribe("").await.expect("subscribe");
let mut reached = 0;
while reached == 0 {
reached = publisher.publish("px.eur", &b"4.02"[..]).expect("publish");
if reached == 0 {
std::thread::yield_now();
}
}
assert_eq!(reached, 1);
let mut incoming = subscriber.recv().await.expect("recv");
assert_eq!(incoming.meta().topic.as_deref(), Some("px.eur"));
let mut body = Vec::new();
incoming
.read_to_end(&mut body)
.await
.expect("read the payload");
assert_eq!(body, b"4.02");
runtime.shutdown().await;
});
}