diff --git a/Cargo.lock b/Cargo.lock index f46a20ba7..dbbff8a54 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -52,6 +52,56 @@ dependencies = [ "libc", ] +[[package]] +name = "anstream" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" +dependencies = [ + "windows-sys 0.60.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.60.2", +] + [[package]] name = "anyhow" version = "1.0.99" @@ -512,6 +562,12 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + [[package]] name = "combine" version = "4.6.6" @@ -787,6 +843,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "env_filter" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0" +dependencies = [ + "log", +] + [[package]] name = "env_logger" version = "0.10.1" @@ -800,6 +865,18 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.11.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "log", +] + [[package]] name = "equivalent" version = "1.0.1" @@ -1411,6 +1488,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + [[package]] name = "isahc" version = "1.7.2" @@ -1554,7 +1637,7 @@ name = "libwebrtc" version = "0.3.14" dependencies = [ "cxx", - "env_logger", + "env_logger 0.10.1", "jni", "js-sys", "lazy_static", @@ -1626,6 +1709,7 @@ dependencies = [ "semver", "serde", "serde_json", + "test-log", "thiserror", "tokio", ] @@ -1668,7 +1752,7 @@ dependencies = [ "console-subscriber", "dashmap", "downcast-rs", - "env_logger", + "env_logger 0.10.1", "futures-util", "imgproc", "jni", @@ -1727,9 +1811,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.20" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" dependencies = [ "value-bag", ] @@ -1821,6 +1905,16 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nu-ansi-term" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +dependencies = [ + "overload", + "winapi", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -1855,6 +1949,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "once_cell_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4895175b425cb1f87721b59f0f286c2092bd4af812243672510e1ac53e2e0ad" + [[package]] name = "openssl" version = "0.10.61" @@ -1909,6 +2009,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "overload" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" + [[package]] name = "parking" version = "2.2.0" @@ -2803,6 +2909,28 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "test-log" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e33b98a582ea0be1168eba097538ee8dd4bbe0f2b01b22ac92ea30054e5be7b" +dependencies = [ + "env_logger 0.11.8", + "test-log-macros", + "tracing-subscriber", +] + +[[package]] +name = "test-log-macros" +version = "0.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "451b374529930d7601b1eef8d32bc79ae870b6079b069401709c2a8bf9e75f36" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.50", +] + [[package]] name = "thiserror" version = "1.0.51" @@ -3083,6 +3211,17 @@ dependencies = [ "tracing", ] +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-subscriber" version = "0.3.18" @@ -3090,6 +3229,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" dependencies = [ "matchers", + "nu-ansi-term", "once_cell", "parking_lot", "regex", @@ -3097,6 +3237,7 @@ dependencies = [ "thread_local", "tracing", "tracing-core", + "tracing-log", ] [[package]] @@ -3202,6 +3343,12 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + [[package]] name = "valuable" version = "0.1.0" @@ -3359,7 +3506,7 @@ dependencies = [ "cc", "cxx", "cxx-build", - "env_logger", + "env_logger 0.10.1", "glob", "log", "webrtc-sys-build", @@ -3430,6 +3577,12 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + [[package]] name = "windows-sys" version = "0.45.0" @@ -3457,6 +3610,15 @@ dependencies = [ "windows-targets 0.52.0", ] +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.3", +] + [[package]] name = "windows-targets" version = "0.42.2" @@ -3502,6 +3664,23 @@ dependencies = [ "windows_x86_64_msvc 0.52.0", ] +[[package]] +name = "windows-targets" +version = "0.53.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe6031c4041849d7c496a8ded650796e7b6ecc19df1a431c1a363342e5dc91" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -3520,6 +3699,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -3538,6 +3723,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -3556,6 +3747,18 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -3574,6 +3777,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -3592,6 +3801,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -3610,6 +3825,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -3628,6 +3849,12 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winreg" version = "0.50.0" diff --git a/livekit/Cargo.toml b/livekit/Cargo.toml index bbaae8e33..21d412fb2 100644 --- a/livekit/Cargo.toml +++ b/livekit/Cargo.toml @@ -47,3 +47,4 @@ bmrng = "0.5.2" [dev-dependencies] anyhow = "1.0.99" +test-log = "0.2.18" diff --git a/livekit/tests/common/e2e.rs b/livekit/tests/common/e2e.rs index 676c758c4..baeb566b9 100644 --- a/livekit/tests/common/e2e.rs +++ b/livekit/tests/common/e2e.rs @@ -1,10 +1,13 @@ use anyhow::{Context, Result}; -use futures_util::future::try_join_all; +use chrono::Utc; use libwebrtc::native::create_random_uuid; use livekit::{Room, RoomEvent, RoomOptions}; use livekit_api::access_token::{AccessToken, VideoGrants}; use std::{env, time::Duration}; -use tokio::sync::mpsc::UnboundedReceiver; +use tokio::{ + sync::mpsc::UnboundedReceiver, + time::{self, timeout}, +}; struct TestEnvironment { api_key: String, @@ -38,19 +41,35 @@ pub async fn test_rooms(count: usize) -> Result>>()?; - let rooms = try_join_all(tokens.into_iter().map(|token| { + let mut rooms = Vec::with_capacity(count); + for token in tokens { let server_url = test_env.server_url.clone(); - async move { - let options = RoomOptions::default(); - Room::connect(&server_url, &token, options).await.context("Failed to connect to room") + let options = RoomOptions::default(); + rooms.push( + Room::connect(&server_url, &token, options) + .await + .context("Failed to connect to room")?, + ); + } + + // Wait for participant visibility across all room connections. When using a + // local SFU, this takes significantly longer and can lead to intermittently failing tests. + let all_connected_time = Utc::now(); + let wait_participant_visibility = async { + while rooms.iter().any(|(room, _)| room.remote_participants().len() != count - 1) { + time::sleep(Duration::from_millis(10)).await; } - })) - .await?; + log::info!("All participants visible after {}", Utc::now() - all_connected_time); + }; + timeout(Duration::from_secs(5), wait_participant_visibility) + .await + .context("Not all participants became visible")?; Ok(rooms) } diff --git a/livekit/tests/data_channel_test.rs b/livekit/tests/data_channel_test.rs index 921386f31..ae0bf89b7 100644 --- a/livekit/tests/data_channel_test.rs +++ b/livekit/tests/data_channel_test.rs @@ -1,79 +1,74 @@ #[cfg(feature = "__lk-e2e-test")] use { crate::common::test_rooms, - anyhow::{anyhow, Result}, + anyhow::{Ok, Result}, livekit::{DataPacket, RoomEvent, SimulateScenario}, std::{sync::Arc, time::Duration}, - tokio::{sync::oneshot, time}, + tokio::{ + time::{self, timeout}, + try_join, + }, }; mod common; #[cfg(feature = "__lk-e2e-test")] -#[tokio::test] +#[test_log::test(tokio::test)] async fn test_reliable_retry() -> Result<()> { + use anyhow::Context; + const ITERATIONS: usize = 128; const PAYLOAD_SIZE: usize = 4096; - // Set up test rooms let mut rooms = test_rooms(2).await?; - let (sending_room, _) = rooms.pop().unwrap(); let (receiving_room, mut receiving_event_rx) = rooms.pop().unwrap(); - - let sending_room = Arc::new(sending_room); - let receiving_room = Arc::new(receiving_room); + let (sending_room, _) = rooms.pop().unwrap(); let receiving_identity = receiving_room.local_participant().identity(); - let (fulfill, expectation) = oneshot::channel(); + let sending_room = Arc::new(sending_room); tokio::spawn({ let sending_room = sending_room.clone(); async move { time::sleep(Duration::from_millis(200)).await; _ = sending_room.simulate_scenario(SimulateScenario::SignalReconnect).await; - println!("Reconnecting sending room"); - } - }); - tokio::spawn({ - let receiving_room = receiving_room.clone(); - async move { - time::sleep(Duration::from_millis(400)).await; - _ = receiving_room.simulate_scenario(SimulateScenario::SignalReconnect).await; - println!("Reconnecting receiving room"); + log::info!("Reconnecting sending room"); } }); - tokio::spawn({ - let fulfill = fulfill; - async move { - let mut packets_received = 0; - while let Some(event) = receiving_event_rx.recv().await { - if let RoomEvent::DataReceived { payload, .. } = event { - assert_eq!(payload.len(), PAYLOAD_SIZE); - packets_received += 1; - if packets_received == ITERATIONS { - fulfill.send(()).ok(); - break; - } - } - } - } + tokio::spawn(async move { + time::sleep(Duration::from_millis(400)).await; + _ = receiving_room.simulate_scenario(SimulateScenario::SignalReconnect).await; + log::info!("Reconnecting receiving room"); }); - for _ in 0..ITERATIONS { - let packet = DataPacket { - reliable: true, - payload: [0xFA; PAYLOAD_SIZE].to_vec(), - destination_identities: vec![receiving_identity.clone()], - ..Default::default() - }; - sending_room.local_participant().publish_data(packet).await?; - time::sleep(Duration::from_millis(10)).await; - } + let send_packets = async move { + for _ in 0..ITERATIONS { + let packet = DataPacket { + reliable: true, + payload: [0xFA; PAYLOAD_SIZE].to_vec(), + destination_identities: vec![receiving_identity.clone()], + ..Default::default() + }; + sending_room.local_participant().publish_data(packet).await?; + time::sleep(Duration::from_millis(10)).await; + } + Ok(()) + }; - match time::timeout(Duration::from_secs(15), expectation).await { - Ok(Ok(())) => Ok(()), - Ok(Err(_)) => Err(anyhow!("Not all packets were received")), - Err(_) => Err(anyhow!("Timed out waiting for packets")), - } + let receive_packets = async move { + let mut packets_received = 0; + while let Some(event) = receiving_event_rx.recv().await { + let RoomEvent::DataReceived { .. } = event else { continue }; + packets_received += 1; + if packets_received == ITERATIONS { + break; + } + } + Ok(()) + }; + timeout(Duration::from_secs(15), async { try_join!(send_packets, receive_packets) }) + .await? + .context("Not all packets received before timeout")?; + Ok(()) } diff --git a/livekit/tests/data_stream_test.rs b/livekit/tests/data_stream_test.rs index 5540fa8ce..c15ba6af9 100644 --- a/livekit/tests/data_stream_test.rs +++ b/livekit/tests/data_stream_test.rs @@ -1,13 +1,10 @@ #[cfg(feature = "__lk-e2e-test")] use { crate::common::test_rooms, - anyhow::{anyhow, Context, Ok, Result}, + anyhow::{anyhow, Ok, Result}, chrono::{TimeDelta, Utc}, - livekit::{ - OperationType, RoomEvent, StreamByteOptions, StreamReader, StreamTextOptions, - TextStreamInfo, - }, - std::{sync::Mutex, time::Duration}, + livekit::{RoomEvent, StreamByteOptions, StreamReader, StreamTextOptions}, + std::time::Duration, tokio::{time::timeout, try_join}, }; diff --git a/livekit/tests/room_test.rs b/livekit/tests/room_test.rs new file mode 100644 index 000000000..82a96d042 --- /dev/null +++ b/livekit/tests/room_test.rs @@ -0,0 +1,52 @@ +#[cfg(feature = "__lk-e2e-test")] +use { + anyhow::Result, + chrono::{TimeDelta, TimeZone, Utc}, + common::test_rooms, + livekit::{ConnectionState, ParticipantKind}, +}; + +mod common; + +#[cfg(feature = "__lk-e2e-test")] +#[test_log::test(tokio::test)] +async fn test_connect() -> Result<()> { + let (room, _) = test_rooms(1).await?.pop().unwrap(); + + assert_eq!(room.connection_state(), ConnectionState::Connected); + assert!(room.name().starts_with("test_room_")); + assert!(room.remote_participants().is_empty()); + + let creation_time = Utc.timestamp_opt(room.creation_time(), 0).unwrap(); + assert!(creation_time.signed_duration_since(Utc::now()).abs() <= TimeDelta::seconds(10)); + + let local_participant = room.local_participant(); + assert!(local_participant.sid().as_str().starts_with("PA_")); + assert_eq!(local_participant.identity().as_str(), "p0"); + assert_eq!(local_participant.name(), "Participant 0"); + assert_eq!(local_participant.kind(), ParticipantKind::Standard); + + Ok(()) +} + +#[cfg(feature = "__lk-e2e-test")] +#[test_log::test(tokio::test)] +async fn test_connect_multiple() -> Result<()> { + let mut rooms = test_rooms(2).await?; + + let (second_room, _) = rooms.pop().unwrap(); + let (first_room, _) = rooms.pop().unwrap(); + + assert_eq!(first_room.name(), second_room.name(), "Participants are in different rooms"); + + assert!(second_room + .remote_participants() + .get(&first_room.local_participant().identity()) + .is_some()); + assert!(first_room + .remote_participants() + .get(&second_room.local_participant().identity()) + .is_some()); + + Ok(()) +}