General Upgrade, NEW: WebServers, Better Docs
Some checks failed
CI / checks (push) Failing after 2m23s
Some checks failed
CI / checks (push) Failing after 2m23s
This commit is contained in:
parent
5f11d476b6
commit
08aa193fd1
119 changed files with 10029 additions and 4883 deletions
321
transport/tests/generic_pipe.rs
Normal file
321
transport/tests/generic_pipe.rs
Normal file
|
|
@ -0,0 +1,321 @@
|
|||
#![cfg(feature = "pipes")]
|
||||
|
||||
use async_trait::async_trait;
|
||||
use mtp_codec::CommunicationValue;
|
||||
use mtp_common::CommunicationError;
|
||||
use mtp_transport::{
|
||||
GenericReceiver, GenericSender, Policy, TransportConnection, TransportEvent,
|
||||
TransportRecvStream, TransportSendStream,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt, DuplexStream, duplex};
|
||||
use tokio::sync::{Mutex, mpsc};
|
||||
|
||||
struct MockSendStream {
|
||||
inner: DuplexStream,
|
||||
}
|
||||
|
||||
impl AsyncWrite for MockSendStream {
|
||||
fn poll_write(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> std::task::Poll<std::io::Result<usize>> {
|
||||
std::pin::Pin::new(&mut self.inner).poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
std::pin::Pin::new(&mut self.inner).poll_flush(cx)
|
||||
}
|
||||
|
||||
fn poll_shutdown(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
std::pin::Pin::new(&mut self.inner).poll_shutdown(cx)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TransportSendStream for MockSendStream {
|
||||
async fn write_all(&mut self, buf: &[u8]) -> Result<(), CommunicationError> {
|
||||
AsyncWriteExt::write_all(&mut self.inner, buf)
|
||||
.await
|
||||
.map_err(|_| CommunicationError::StreamError)
|
||||
}
|
||||
|
||||
async fn finish(&mut self) -> Result<(), CommunicationError> {
|
||||
self.inner
|
||||
.shutdown()
|
||||
.await
|
||||
.map_err(|_| CommunicationError::StreamError)
|
||||
}
|
||||
}
|
||||
|
||||
struct MockRecvStream {
|
||||
inner: DuplexStream,
|
||||
}
|
||||
|
||||
impl AsyncRead for MockRecvStream {
|
||||
fn poll_read(
|
||||
mut self: std::pin::Pin<&mut Self>,
|
||||
cx: &mut std::task::Context<'_>,
|
||||
buf: &mut tokio::io::ReadBuf<'_>,
|
||||
) -> std::task::Poll<std::io::Result<()>> {
|
||||
std::pin::Pin::new(&mut self.inner).poll_read(cx, buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TransportRecvStream for MockRecvStream {
|
||||
async fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), CommunicationError> {
|
||||
AsyncReadExt::read_exact(&mut self.inner, buf)
|
||||
.await
|
||||
.map(|_| ())
|
||||
.map_err(|_| CommunicationError::StreamError)
|
||||
}
|
||||
|
||||
async fn read_chunk(&mut self, max: usize) -> Result<Option<Vec<u8>>, CommunicationError> {
|
||||
let mut buf = vec![0u8; max];
|
||||
match AsyncReadExt::read(&mut self.inner, &mut buf).await {
|
||||
Ok(0) => Ok(None),
|
||||
Ok(n) => {
|
||||
buf.truncate(n);
|
||||
Ok(Some(buf))
|
||||
}
|
||||
Err(_) => Err(CommunicationError::StreamError),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct MockTransportConnection {
|
||||
pair_tx: mpsc::Sender<DuplexStream>,
|
||||
pair_rx: Arc<Mutex<mpsc::Receiver<DuplexStream>>>,
|
||||
}
|
||||
|
||||
impl MockTransportConnection {
|
||||
fn pair() -> (Self, Self) {
|
||||
let (tx_a, rx_a) = mpsc::channel(16);
|
||||
let (tx_b, rx_b) = mpsc::channel(16);
|
||||
(
|
||||
Self {
|
||||
pair_tx: tx_a,
|
||||
pair_rx: Arc::new(Mutex::new(rx_b)),
|
||||
},
|
||||
Self {
|
||||
pair_tx: tx_b,
|
||||
pair_rx: Arc::new(Mutex::new(rx_a)),
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl TransportConnection for MockTransportConnection {
|
||||
type SendStream = MockSendStream;
|
||||
type RecvStream = MockRecvStream;
|
||||
|
||||
async fn open_uni(&self) -> Result<Self::SendStream, CommunicationError> {
|
||||
let (local, remote) = duplex(65536);
|
||||
self.pair_tx
|
||||
.send(remote)
|
||||
.await
|
||||
.map_err(|_| CommunicationError::StreamError)?;
|
||||
Ok(MockSendStream { inner: local })
|
||||
}
|
||||
|
||||
async fn accept_uni(&self) -> Result<Self::RecvStream, CommunicationError> {
|
||||
let remote = self
|
||||
.pair_rx
|
||||
.lock()
|
||||
.await
|
||||
.recv()
|
||||
.await
|
||||
.ok_or(CommunicationError::StreamClosed)?;
|
||||
Ok(MockRecvStream { inner: remote })
|
||||
}
|
||||
|
||||
fn close_reason(&self) -> Option<CommunicationError> {
|
||||
None
|
||||
}
|
||||
|
||||
fn close(&self, _code: u32, _reason: &[u8]) {}
|
||||
}
|
||||
|
||||
async fn mock_connected_pair() -> (MockTransportConnection, MockTransportConnection) {
|
||||
MockTransportConnection::pair()
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_open_pipe_and_receive_reader() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let pipe_writer = sender.open_pipe(42, "test-pipe").await?;
|
||||
|
||||
let pipe_reader = receiver.receive_pipe().await?;
|
||||
assert_eq!(pipe_reader.pipe_id(), 42);
|
||||
assert_eq!(pipe_reader.description(), "test-pipe");
|
||||
|
||||
drop(pipe_writer);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pipe_raw_data_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let mut pipe_writer = sender.open_pipe(1, "data-pipe").await?;
|
||||
|
||||
let data = b"hello through the pipe";
|
||||
AsyncWriteExt::write_all(&mut pipe_writer, data).await?;
|
||||
pipe_writer.finish_async().await?;
|
||||
|
||||
let mut pipe_reader = receiver.receive_pipe().await?;
|
||||
let mut buf = vec![0u8; data.len()];
|
||||
AsyncReadExt::read_exact(&mut pipe_reader, &mut buf).await?;
|
||||
assert_eq!(&buf, data);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_pipe_large_payload() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let mut pipe_writer = sender.open_pipe(7, "big-pipe").await?;
|
||||
|
||||
let data: Vec<u8> = (0..256 * 1024).map(|i| (i % 256) as u8).collect();
|
||||
let data_clone = data.clone();
|
||||
let write_handle = tokio::spawn(async move {
|
||||
AsyncWriteExt::write_all(&mut pipe_writer, &data_clone)
|
||||
.await
|
||||
.map_err(|_| CommunicationError::StreamError)?;
|
||||
pipe_writer.finish_async().await
|
||||
});
|
||||
|
||||
let mut pipe_reader = receiver.receive_pipe().await?;
|
||||
let mut buf = Vec::new();
|
||||
AsyncReadExt::read_to_end(&mut pipe_reader, &mut buf).await?;
|
||||
assert_eq!(buf, data);
|
||||
|
||||
write_handle.await??;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_receive_event_dispatches_pipe() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let mut pipe_writer = sender.open_pipe(99, "event-pipe").await?;
|
||||
|
||||
match receiver.receive_event().await? {
|
||||
TransportEvent::Pipe(mut reader) => {
|
||||
assert_eq!(reader.pipe_id(), 99);
|
||||
assert_eq!(reader.description(), "event-pipe");
|
||||
|
||||
let data = b"event dispatch test";
|
||||
AsyncWriteExt::write_all(&mut pipe_writer, data).await?;
|
||||
pipe_writer.finish_async().await?;
|
||||
|
||||
let mut buf = vec![0u8; data.len()];
|
||||
AsyncReadExt::read_exact(&mut reader, &mut buf).await?;
|
||||
assert_eq!(&buf, data);
|
||||
}
|
||||
TransportEvent::Message(_) => panic!("expected Pipe event, got Message"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_try_receive_pipe_returns_none_when_empty() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let _sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let result = receiver.try_receive_pipe()?;
|
||||
assert!(result.is_none());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_regular_messages_still_work_alongside_pipes() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let msg = CommunicationValue::new(mtp_codec::CommunicationType::Pong);
|
||||
sender.send(&msg).await?;
|
||||
|
||||
let _pipe_writer = sender.open_pipe(1, "mixed-pipe").await?;
|
||||
|
||||
let received = receiver.receive().await?;
|
||||
assert_eq!(
|
||||
received.get_type(),
|
||||
mtp_codec::CommunicationType::Pong
|
||||
.try_to_id(&mtp_codec::TypeMap::latest())
|
||||
.unwrap()
|
||||
);
|
||||
|
||||
let pipe_reader = receiver.receive_pipe().await?;
|
||||
assert_eq!(pipe_reader.pipe_id(), 1);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_multiple_pipes() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (conn_a, conn_b) = mock_connected_pair().await;
|
||||
let policy = Arc::new(Policy::default());
|
||||
let sender = GenericSender::new(conn_a, policy.clone());
|
||||
let receiver = GenericReceiver::new(conn_b, policy);
|
||||
|
||||
let mut pw1 = sender.open_pipe(10, "first").await?;
|
||||
let mut pw2 = sender.open_pipe(20, "second").await?;
|
||||
|
||||
let r1 = receiver.receive_pipe().await?;
|
||||
assert_eq!(r1.pipe_id(), 10);
|
||||
let r2 = receiver.receive_pipe().await?;
|
||||
assert_eq!(r2.pipe_id(), 20);
|
||||
|
||||
let data1 = b"pipe-one-data";
|
||||
AsyncWriteExt::write_all(&mut pw1, data1).await?;
|
||||
pw1.finish_async().await?;
|
||||
|
||||
let data2 = b"pipe-two-data";
|
||||
AsyncWriteExt::write_all(&mut pw2, data2).await?;
|
||||
pw2.finish_async().await?;
|
||||
|
||||
let mut buf1 = vec![0u8; data1.len()];
|
||||
let mut reader1 = r1;
|
||||
AsyncReadExt::read_exact(&mut reader1, &mut buf1).await?;
|
||||
assert_eq!(&buf1, data1);
|
||||
|
||||
let mut buf2 = vec![0u8; data2.len()];
|
||||
let mut reader2 = r2;
|
||||
AsyncReadExt::read_exact(&mut reader2, &mut buf2).await?;
|
||||
assert_eq!(&buf2, data2);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
use std::net::{IpAddr, Ipv4Addr};
|
||||
|
||||
use mtp_codec::{CommunicationType, CommunicationValue, DataType, DataValue, TypeMap};
|
||||
use mtp_transport::{Host, Policy, Receiver, Sender, connect, host};
|
||||
use mtp_transport::{
|
||||
ClientConfig as TransportClientConfig, Host, HostConfig as TransportHostConfig, Policy,
|
||||
Receiver, Sender, connect, connect_with_config, host, host_with_config,
|
||||
};
|
||||
|
||||
fn generate_self_signed_cert() -> (Vec<u8>, Vec<u8>) {
|
||||
let key_pair = rcgen::KeyPair::generate().expect("failed to generate self-signed key pair");
|
||||
|
|
@ -15,7 +18,10 @@ fn generate_self_signed_cert() -> (Vec<u8>, Vec<u8>) {
|
|||
(cert_pem.into_bytes(), key_pem.into_bytes())
|
||||
}
|
||||
|
||||
async fn start_test_host(cert_pem: Vec<u8>, key_pem: Vec<u8>) -> Result<Host, Box<dyn std::error::Error>> {
|
||||
async fn start_test_host(
|
||||
cert_pem: Vec<u8>,
|
||||
key_pem: Vec<u8>,
|
||||
) -> Result<Host, Box<dyn std::error::Error>> {
|
||||
Ok(host(
|
||||
IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
0,
|
||||
|
|
@ -26,12 +32,16 @@ async fn start_test_host(cert_pem: Vec<u8>, key_pem: Vec<u8>) -> Result<Host, Bo
|
|||
.await?)
|
||||
}
|
||||
|
||||
async fn connect_to_host(h: &Host, cert_pem: Vec<u8>) -> Result<(Sender, Receiver), Box<dyn std::error::Error>> {
|
||||
async fn connect_to_host(
|
||||
h: &Host,
|
||||
cert_pem: Vec<u8>,
|
||||
) -> Result<(Sender, Receiver), Box<dyn std::error::Error>> {
|
||||
let url = format!("https://127.0.0.1:{}", h.local_addr().port());
|
||||
Ok(connect(&url, Some(cert_pem), Policy::default()).await?)
|
||||
}
|
||||
|
||||
async fn connected_pair() -> Result<(Host, Sender, Receiver, Sender, Receiver), Box<dyn std::error::Error>> {
|
||||
async fn connected_pair()
|
||||
-> Result<(Host, Sender, Receiver, Sender, Receiver), Box<dyn std::error::Error>> {
|
||||
let (cert_pem, key_pem) = generate_self_signed_cert();
|
||||
let mut h = start_test_host(cert_pem.clone(), key_pem).await?;
|
||||
let (client_tx, client_rx) = connect_to_host(&h, cert_pem).await?;
|
||||
|
|
@ -41,7 +51,9 @@ async fn connected_pair() -> Result<(Host, Sender, Receiver, Sender, Receiver),
|
|||
|
||||
fn numbered_message(comm_type: CommunicationType, value: u128, tm: &TypeMap) -> CommunicationValue {
|
||||
CommunicationValue::new(comm_type).add_data(
|
||||
DataType::PqSignature.to_id(tm),
|
||||
DataType::PqSignature
|
||||
.try_to_id(tm)
|
||||
.expect("test type must be mapped"),
|
||||
DataValue::UnsignedNumber(value),
|
||||
)
|
||||
}
|
||||
|
|
@ -52,7 +64,10 @@ fn assert_numbered_message(
|
|||
value: u128,
|
||||
tm: &TypeMap,
|
||||
) {
|
||||
assert_eq!(message.get_type(), comm_type.to_id(tm));
|
||||
assert_eq!(
|
||||
message.get_type(),
|
||||
comm_type.try_to_id(tm).expect("test type must be mapped")
|
||||
);
|
||||
assert_eq!(
|
||||
message.get_data(DataType::PqSignature).clone(),
|
||||
DataValue::UnsignedNumber(value)
|
||||
|
|
@ -69,6 +84,32 @@ async fn test_host_start_and_stop() -> Result<(), Box<dyn std::error::Error>> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_explicit_development_tls() -> Result<(), Box<dyn std::error::Error>> {
|
||||
// The insecure-tls feature requires MTP_INSECURE_TLS=1 at runtime.
|
||||
// SAFETY: test is single-threaded; no concurrent readers of this env var.
|
||||
unsafe {
|
||||
std::env::set_var("MTP_INSECURE_TLS", "1");
|
||||
}
|
||||
|
||||
let mut h = host_with_config(
|
||||
IpAddr::V4(Ipv4Addr::LOCALHOST),
|
||||
0,
|
||||
TransportHostConfig::self_signed(Policy::default()),
|
||||
)
|
||||
.await?;
|
||||
let url = format!("https://127.0.0.1:{}", h.local_addr().port());
|
||||
let client_config =
|
||||
TransportClientConfig::new(Policy::default()).with_insecure_certificate_verification();
|
||||
|
||||
let (client_tx, _client_rx) = connect_with_config(&url, client_config).await?;
|
||||
let (_host_tx, _host_rx) = h.next().await.ok_or("host did not accept connection")?;
|
||||
|
||||
client_tx.close();
|
||||
h.shutdown();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_send_receive_roundtrip() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let (_h, client_tx, client_rx, host_tx, host_rx) = connected_pair().await?;
|
||||
|
|
@ -142,7 +183,12 @@ async fn test_close_detection() -> Result<(), Box<dyn std::error::Error>> {
|
|||
// Host should still receive the message
|
||||
let tm = TypeMap::latest();
|
||||
let received = host_rx.receive().await?;
|
||||
assert_eq!(received.get_type(), CommunicationType::Ping.to_id(&tm));
|
||||
assert_eq!(
|
||||
received.get_type(),
|
||||
CommunicationType::Ping
|
||||
.try_to_id(&tm)
|
||||
.expect("test type must be mapped")
|
||||
);
|
||||
|
||||
// Host should get an error or closed signal on next receive
|
||||
let result = host_rx.receive().await;
|
||||
|
|
@ -204,7 +250,8 @@ async fn test_drop_receiver_keeps_sender_alive() -> Result<(), Box<dyn std::erro
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_persistent_stream_reopens_after_local_finish() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn test_persistent_stream_reopens_after_local_finish()
|
||||
-> Result<(), Box<dyn std::error::Error>> {
|
||||
let (_h, client_tx, client_rx, host_tx, host_rx) = connected_pair().await?;
|
||||
|
||||
let tm = TypeMap::latest();
|
||||
|
|
@ -239,14 +286,12 @@ async fn test_receiver_backpressure_with_small_queue() -> Result<(), Box<dyn std
|
|||
for i in 0..8u128 {
|
||||
client_tx
|
||||
.send(&numbered_message(CommunicationType::Ping, i, &tm))
|
||||
.await
|
||||
?;
|
||||
.await?;
|
||||
}
|
||||
|
||||
for i in 0..8u128 {
|
||||
let received = tokio::time::timeout(std::time::Duration::from_secs(5), host_rx.receive())
|
||||
.await?
|
||||
?;
|
||||
let received =
|
||||
tokio::time::timeout(std::time::Duration::from_secs(5), host_rx.receive()).await??;
|
||||
assert_numbered_message(&received, CommunicationType::Ping, i, &tm);
|
||||
}
|
||||
|
||||
|
|
@ -269,9 +314,7 @@ async fn test_max_frames_per_stream_enforced() -> Result<(), Box<dyn std::error:
|
|||
)
|
||||
.await?;
|
||||
let url = format!("https://127.0.0.1:{}", h.local_addr().port());
|
||||
let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default())
|
||||
.await
|
||||
?;
|
||||
let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default()).await?;
|
||||
let (_host_tx, host_rx) = h.next().await.ok_or("host did not accept connection")?;
|
||||
|
||||
let tm = TypeMap::latest();
|
||||
|
|
@ -293,7 +336,8 @@ async fn test_max_frames_per_stream_enforced() -> Result<(), Box<dyn std::error:
|
|||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_semaphore_saturation_with_concurrent_streams() -> Result<(), Box<dyn std::error::Error>> {
|
||||
async fn test_semaphore_saturation_with_concurrent_streams()
|
||||
-> Result<(), Box<dyn std::error::Error>> {
|
||||
let (cert_pem, key_pem) = generate_self_signed_cert();
|
||||
let policy = Policy::default()
|
||||
.with_send_mode(mtp_transport::SendMode::SingleStreamPerMessage)
|
||||
|
|
@ -308,8 +352,7 @@ async fn test_semaphore_saturation_with_concurrent_streams() -> Result<(), Box<d
|
|||
)
|
||||
.await?;
|
||||
let url = format!("https://127.0.0.1:{}", h.local_addr().port());
|
||||
let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default())
|
||||
.await?;
|
||||
let (client_tx, _client_rx) = connect(&url, Some(cert_pem), Policy::default()).await?;
|
||||
let (_host_tx, host_rx) = h.next().await.ok_or("host did not accept connection")?;
|
||||
|
||||
let tm = TypeMap::latest();
|
||||
|
|
@ -327,9 +370,8 @@ async fn test_semaphore_saturation_with_concurrent_streams() -> Result<(), Box<d
|
|||
}
|
||||
|
||||
for i in 0..6u128 {
|
||||
let received = tokio::time::timeout(std::time::Duration::from_secs(5), host_rx.receive())
|
||||
.await?
|
||||
?;
|
||||
let received =
|
||||
tokio::time::timeout(std::time::Duration::from_secs(5), host_rx.receive()).await??;
|
||||
assert_numbered_message(&received, CommunicationType::Ping, i, &tm);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue