#[cfg(feature = "__lk-e2e-test")]
use {
crate::common::{test_rooms, test_rooms_with_options, TestRoomOptions},
anyhow::{anyhow, Ok, Result},
chrono::{TimeDelta, Utc},
livekit::{
data_stream::backend::pseudo_random_text, RoomDataStreamOptions, RoomEvent, RoomOptions,
StreamByteOptions, StreamError, StreamReader, StreamTextOptions,
},
rand::{rngs::StdRng, RngCore, SeedableRng},
std::time::Duration,
tokio::{time::timeout, try_join},
};
mod common;
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_send_bytes() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let sender_identity = sending_room.local_participant().identity();
const BYTES_TO_SEND: &[u8] = &[0xFA; 16];
let send_text = async move {
let options = StreamByteOptions::new_with_topic("some-topic");
let stream_info =
sending_room.local_participant().send_bytes(BYTES_TO_SEND, options).await?;
assert!(!stream_info.id.is_empty());
assert!(
stream_info.timestamp.signed_duration_since(Utc::now()).abs() <= TimeDelta::seconds(1)
);
assert!(stream_info.total_length.is_some());
assert_eq!(stream_info.mime_type, "application/octet-stream");
assert_eq!(stream_info.topic, "some-topic");
assert_eq!(stream_info.is_compressed, true);
assert_eq!(stream_info.is_inline, true);
Ok(())
};
let receive_text = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::ByteStreamOpened { reader, topic, participant_identity } = event else {
continue;
};
assert_eq!(topic, "some-topic");
assert_eq!(participant_identity, sender_identity);
let Some(reader) = reader.take() else {
return Err(anyhow!("Failed to take reader"));
};
assert_eq!(reader.read_all().await?, BYTES_TO_SEND);
break;
}
Ok(())
};
timeout(Duration::from_secs(5), async { try_join!(send_text, receive_text) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_send_large_compressible_text() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let text = pseudo_random_text(50_000);
let expected = text.clone();
let send = async move {
let options = StreamTextOptions::new_with_topic("some-topic");
let stream_info = sending_room.local_participant().send_text(&text, options).await?;
assert_eq!(stream_info.is_compressed, true);
assert_eq!(stream_info.is_inline, false);
Ok(())
};
let receive = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::TextStreamOpened { reader, topic, .. } = event else {
continue;
};
assert_eq!(topic, "some-topic");
let reader = reader.take().ok_or_else(|| anyhow!("Failed to take reader"))?;
assert_eq!(reader.read_all().await?, expected);
break;
}
Ok(())
};
timeout(Duration::from_secs(10), async { try_join!(send, receive) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_send_large_incompressible_random_bytes() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let mut rng = StdRng::seed_from_u64(0xC0FFEE);
let mut payload = vec![0u8; 1_800_000];
rng.fill_bytes(&mut payload);
let expected = payload.clone();
let send = async move {
let options = StreamByteOptions::new_with_topic("some-topic");
let stream_info = sending_room.local_participant().send_bytes(&payload, options).await?;
assert_eq!(stream_info.is_compressed, false, "is_compressed was not false");
assert_eq!(stream_info.is_inline, false, "is_inline was not false");
Ok(())
};
let receive = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::ByteStreamOpened { reader, topic, .. } = event else {
continue;
};
assert_eq!(topic, "some-topic");
let reader = reader.take().ok_or_else(|| anyhow!("Failed to take reader"))?;
assert_eq!(reader.read_all().await?, expected);
break;
}
Ok(())
};
timeout(Duration::from_secs(10), async { try_join!(send, receive) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_send_large_bytes() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let payload: Vec<u8> = (0..50_000u32).map(|i| (i % 251) as u8).collect();
let expected = payload.clone();
let send = async move {
let options = StreamByteOptions::new_with_topic("some-topic");
let stream_info = sending_room.local_participant().send_bytes(&payload, options).await?;
assert_eq!(stream_info.is_compressed, true, "is_compressed was not true");
assert_eq!(stream_info.is_inline, true, "is_inline was not true");
Ok(())
};
let receive = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::ByteStreamOpened { reader, topic, .. } = event else {
continue;
};
assert_eq!(topic, "some-topic");
let reader = reader.take().ok_or_else(|| anyhow!("Failed to take reader"))?;
assert_eq!(reader.read_all().await?, expected);
break;
}
Ok(())
};
timeout(Duration::from_secs(10), async { try_join!(send, receive) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_data_stream_compress_false() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let payload = vec![0xFFu8; 50_000];
let expected = payload.clone();
let send = async move {
let options = StreamByteOptions::new_with_topic("some-topic").with_compress(false); let stream_info = sending_room.local_participant().send_bytes(&payload, options).await?;
assert_eq!(stream_info.is_compressed, false, "is_compressed was not false");
assert_eq!(stream_info.is_inline, false, "is_inline was not false");
Ok(())
};
let receive = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::ByteStreamOpened { reader, topic, .. } = event else {
continue;
};
assert_eq!(topic, "some-topic");
let reader = reader.take().ok_or_else(|| anyhow!("Failed to take reader"))?;
assert_eq!(reader.read_all().await?, expected);
break;
}
Ok(())
};
timeout(Duration::from_secs(10), async { try_join!(send, receive) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_receiver_rejects_oversized_payload() -> Result<()> {
const MAX_PAYLOAD_BYTES: usize = 1_000;
let mut receiver_options = RoomOptions::default();
receiver_options.data_stream =
RoomDataStreamOptions::default().with_max_payload_byte_length(MAX_PAYLOAD_BYTES);
let mut rooms =
test_rooms_with_options([receiver_options.into(), TestRoomOptions::default()]).await?;
let (_, mut receiving_event_rx) = rooms.remove(0);
let (sending_room, _) = rooms.remove(0);
let text = pseudo_random_text(50_000);
let send = async move {
let options = StreamTextOptions::new_with_topic("some-topic");
let stream_info = sending_room.local_participant().send_text(&text, options).await?;
assert_eq!(stream_info.is_compressed, true, "expected a compressed stream");
assert_eq!(stream_info.is_inline, false, "expected a chunked (non-inline) stream");
Ok(())
};
let receive = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::TextStreamOpened { reader, topic, .. } = event else {
continue;
};
assert_eq!(topic, "some-topic");
let reader = reader.take().ok_or_else(|| anyhow!("Failed to take reader"))?;
let result = reader.read_all().await;
assert!(
matches!(result, Err(StreamError::PayloadTooLarge)),
"expected StreamError::PayloadTooLarge, got {:?}",
result
);
break;
}
Ok(())
};
timeout(Duration::from_secs(10), async { try_join!(send, receive) }).await??;
Ok(())
}
#[cfg(feature = "__lk-e2e-test")]
#[tokio::test]
async fn test_send_text() -> Result<()> {
let mut rooms = test_rooms(2).await?;
let (sending_room, _) = rooms.pop().unwrap();
let (_, mut receiving_event_rx) = rooms.pop().unwrap();
let sender_identity = sending_room.local_participant().identity();
const TEXT_TO_SEND: &str = "some-text";
let send_text = async move {
let options = StreamTextOptions::new_with_topic("some-topic");
let stream_info = sending_room.local_participant().send_text(TEXT_TO_SEND, options).await?;
assert!(!stream_info.id.is_empty());
assert!(
stream_info.timestamp.signed_duration_since(Utc::now()).abs() <= TimeDelta::seconds(1)
);
assert!(stream_info.total_length.is_some());
assert_eq!(stream_info.mime_type, "text/plain");
assert_eq!(stream_info.topic, "some-topic");
Ok(())
};
let receive_text = async move {
while let Some(event) = receiving_event_rx.recv().await {
let RoomEvent::TextStreamOpened { reader, topic, participant_identity } = event else {
continue;
};
assert_eq!(topic, "some-topic");
assert_eq!(participant_identity, sender_identity);
let Some(reader) = reader.take() else {
return Err(anyhow!("Failed to take reader"));
};
assert_eq!(reader.read_all().await?, TEXT_TO_SEND);
break;
}
Ok(())
};
timeout(Duration::from_secs(5), async { try_join!(send_text, receive_text) }).await??;
Ok(())
}