From c1d79f04d812fd39b374bb3c40b4963b819ecba3 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 10 Jun 2026 15:31:59 -0600 Subject: [PATCH 1/4] Added remaining room options --- include/livekit/room.h | 21 +++++++--- src/ffi_client.cpp | 49 +++-------------------- src/room_proto_converter.cpp | 39 ++++++++++++++++++ src/room_proto_converter.h | 2 + src/tests/unit/test_room.cpp | 76 +++++++++++++++++++++++++++++++++++- 5 files changed, 137 insertions(+), 50 deletions(-) diff --git a/include/livekit/room.h b/include/livekit/room.h index 74ea94d6..b0d6c46d 100644 --- a/include/livekit/room.h +++ b/include/livekit/room.h @@ -16,6 +16,8 @@ #pragma once +#include +#include #include #include #include @@ -74,19 +76,28 @@ struct RoomOptions { /// - remote audio/video frames bool auto_subscribe = true; + /// Enable adaptive stream so the server optimizes subscribed video layers. + bool adaptive_stream = false; + /// Enable dynacast (server sends optimal layers depending on subscribers). bool dynacast = false; + /// Optional end-to-end encryption settings. + std::optional encryption; + + /// Optional WebRTC configuration (ICE policy, servers, etc.) + std::optional rtc_config; + + /// Number of retries for the initial room join after the first attempt. + std::uint32_t join_retries = 3; + /// Enable single peer connection mode. When true, uses one RTCPeerConnection /// for both publishing and subscribing instead of two separate connections. /// Falls back to dual peer connection if the server doesn't support single PC. bool single_peer_connection = true; - /// Optional WebRTC configuration (ICE policy, servers, etc.) - std::optional rtc_config; - - /// Optional end-to-end encryption settings. - std::optional encryption; + /// Timeout for each individual signal connection attempt. + std::chrono::milliseconds connect_timeout = std::chrono::seconds(5); }; /// Represents a LiveKit room session. diff --git a/src/ffi_client.cpp b/src/ffi_client.cpp index 0966816f..d9af0b79 100644 --- a/src/ffi_client.cpp +++ b/src/ffi_client.cpp @@ -20,11 +20,9 @@ #include #include "data_track.pb.h" -#include "e2ee.pb.h" #include "ffi.pb.h" #include "livekit/build.h" #include "livekit/data_track_error.h" -#include "livekit/e2ee.h" #include "livekit/ffi_handle.h" #include "livekit/room.h" #include "livekit/rpc_error.h" @@ -331,50 +329,13 @@ std::future FfiClient::connectAsync(const std::string& u connect->set_url(url); connect->set_token(token); connect->set_request_async_id(async_id); - auto* opts = connect->mutable_options(); - opts->set_auto_subscribe(options.auto_subscribe); - opts->set_dynacast(options.dynacast); - opts->set_single_peer_connection(options.single_peer_connection); + connect->mutable_options()->CopyFrom(toProto(options)); LK_LOG_DEBUG( - "[FfiClient] connectAsync: auto_subscribe={}, dynacast={}, " - "single_peer_connection={}", - options.auto_subscribe, options.dynacast, options.single_peer_connection); - - // --- E2EE / encryption (optional) --- - if (options.encryption.has_value()) { - const E2EEOptions& e2ee = *options.encryption; - const auto& kpo = e2ee.key_provider_options; - - auto* enc = opts->mutable_encryption(); - enc->set_encryption_type(static_cast(e2ee.encryption_type)); - enc->mutable_key_provider_options()->CopyFrom(toProto(kpo)); - } - - // --- RTC configuration (optional) --- - if (options.rtc_config.has_value()) { - const RtcConfig& rc = *options.rtc_config; - auto* rtc = opts->mutable_rtc_config(); - - rtc->set_ice_transport_type(static_cast(rc.ice_transport_type)); - rtc->set_continual_gathering_policy(static_cast(rc.continual_gathering_policy)); - - for (const IceServer& ice : rc.ice_servers) { - auto* s = rtc->add_ice_servers(); - - // proto: repeated string urls = 1 - if (!ice.url.empty()) { - s->add_urls(ice.url); - } - if (!ice.username.empty()) { - s->set_username(ice.username); - } - if (!ice.credential.empty()) { - // proto: password = 3 - s->set_password(ice.credential); - } - } - } + "[FfiClient] connectAsync: auto_subscribe={}, adaptive_stream={}, dynacast={}, " + "single_peer_connection={}, join_retries={}, connect_timeout_ms={}", + options.auto_subscribe, options.adaptive_stream, options.dynacast, options.single_peer_connection, + options.join_retries, options.connect_timeout.count()); try { const proto::FfiResponse resp = sendRequest(req); diff --git a/src/room_proto_converter.cpp b/src/room_proto_converter.cpp index 9fff5eb8..3b7a0148 100644 --- a/src/room_proto_converter.cpp +++ b/src/room_proto_converter.cpp @@ -17,6 +17,7 @@ #include "room_proto_converter.h" #include "livekit/data_stream.h" +#include "livekit/room.h" #include "room.pb.h" namespace livekit { @@ -402,6 +403,44 @@ proto::KeyProviderOptions toProto(const KeyProviderOptions& in) { return out; } +proto::RoomOptions toProto(const RoomOptions& in) { + proto::RoomOptions out; + out.set_auto_subscribe(in.auto_subscribe); + out.set_adaptive_stream(in.adaptive_stream); + out.set_dynacast(in.dynacast); + + if (in.encryption) { + auto* encryption = out.mutable_encryption(); + encryption->set_encryption_type(static_cast(in.encryption->encryption_type)); + encryption->mutable_key_provider_options()->CopyFrom(toProto(in.encryption->key_provider_options)); + } + + if (in.rtc_config) { + auto* rtc = out.mutable_rtc_config(); + rtc->set_ice_transport_type(static_cast(in.rtc_config->ice_transport_type)); + rtc->set_continual_gathering_policy( + static_cast(in.rtc_config->continual_gathering_policy)); + + for (const IceServer& ice : in.rtc_config->ice_servers) { + auto* server = rtc->add_ice_servers(); + if (!ice.url.empty()) { + server->add_urls(ice.url); + } + if (!ice.username.empty()) { + server->set_username(ice.username); + } + if (!ice.credential.empty()) { + server->set_password(ice.credential); + } + } + } + + out.set_join_retries(in.join_retries); + out.set_single_peer_connection(in.single_peer_connection); + out.set_connect_timeout_ms(static_cast(in.connect_timeout.count())); + return out; +} + proto::AudioEncoding toProto(const AudioEncodingOptions& in) { proto::AudioEncoding msg; msg.set_max_bitrate(in.max_bitrate); diff --git a/src/room_proto_converter.h b/src/room_proto_converter.h index 9b9d3d67..2189097c 100644 --- a/src/room_proto_converter.h +++ b/src/room_proto_converter.h @@ -29,6 +29,7 @@ namespace livekit { enum class RpcErrorCode; class RemoteParticipant; struct ByteStreamInfo; +struct RoomOptions; struct TextStreamInfo; // --------- basic helper conversions --------- @@ -73,6 +74,7 @@ LIVEKIT_INTERNAL_API RoomMovedEvent roomMovedFromProto(const proto::RoomInfo& in // --------- room options conversions --------- LIVEKIT_INTERNAL_API proto::KeyProviderOptions toProto(const KeyProviderOptions& in); +LIVEKIT_INTERNAL_API proto::RoomOptions toProto(const RoomOptions& in); LIVEKIT_INTERNAL_API proto::AudioEncoding toProto(const AudioEncodingOptions& in); LIVEKIT_INTERNAL_API AudioEncodingOptions fromProto(const proto::AudioEncoding& in); diff --git a/src/tests/unit/test_room.cpp b/src/tests/unit/test_room.cpp index c33df213..74c6705a 100644 --- a/src/tests/unit/test_room.cpp +++ b/src/tests/unit/test_room.cpp @@ -17,6 +17,10 @@ #include #include +#include + +#include "room_proto_converter.h" + namespace livekit::test { class RoomTest : public ::testing::Test { @@ -47,9 +51,79 @@ TEST_F(RoomTest, RoomOptionsDefaults) { RoomOptions options; EXPECT_TRUE(options.auto_subscribe) << "auto_subscribe should default to true"; + EXPECT_FALSE(options.adaptive_stream) << "adaptive_stream should default to false"; EXPECT_FALSE(options.dynacast) << "dynacast should default to false"; - EXPECT_FALSE(options.rtc_config.has_value()) << "rtc_config should not have a value by default"; EXPECT_FALSE(options.encryption.has_value()) << "encryption should not have a value by default"; + EXPECT_FALSE(options.rtc_config.has_value()) << "rtc_config should not have a value by default"; + EXPECT_EQ(options.join_retries, 3U) << "join_retries should default to Rust SDK behavior"; + EXPECT_TRUE(options.single_peer_connection) << "single_peer_connection should default to true"; + EXPECT_EQ(options.connect_timeout, std::chrono::seconds(5)) << "connect_timeout should default to Rust SDK behavior"; +} + +TEST_F(RoomTest, RoomOptionsToProtoSerializesDefaults) { + const proto::RoomOptions proto_options = toProto(RoomOptions{}); + + EXPECT_TRUE(proto_options.has_auto_subscribe()); + EXPECT_TRUE(proto_options.auto_subscribe()); + EXPECT_TRUE(proto_options.has_adaptive_stream()); + EXPECT_FALSE(proto_options.adaptive_stream()); + EXPECT_TRUE(proto_options.has_dynacast()); + EXPECT_FALSE(proto_options.dynacast()); + EXPECT_FALSE(proto_options.has_encryption()); + EXPECT_FALSE(proto_options.has_rtc_config()); + EXPECT_TRUE(proto_options.has_join_retries()); + EXPECT_EQ(proto_options.join_retries(), 3U); + EXPECT_TRUE(proto_options.has_single_peer_connection()); + EXPECT_TRUE(proto_options.single_peer_connection()); + EXPECT_TRUE(proto_options.has_connect_timeout_ms()); + EXPECT_EQ(proto_options.connect_timeout_ms(), 5000U); +} + +TEST_F(RoomTest, RoomOptionsProtoConverter) { + RoomOptions options; + options.auto_subscribe = false; + options.adaptive_stream = true; + options.dynacast = true; + E2EEOptions encryption; + encryption.key_provider_options.shared_key = std::vector{'s', 'e', 'c', 'r', 'e', 't'}; + options.encryption = encryption; + RtcConfig rtc_config; + rtc_config.ice_transport_type = proto::TRANSPORT_ALL; + rtc_config.continual_gathering_policy = proto::GATHER_CONTINUALLY; + rtc_config.ice_servers.push_back({"stun:stun.l.google.com:19302", "", ""}); + rtc_config.ice_servers.push_back({"turn:turn.example.com:3478", "user", "pass"}); + options.rtc_config = rtc_config; + options.join_retries = 8; + options.single_peer_connection = false; + options.connect_timeout = std::chrono::milliseconds(750); + + const proto::RoomOptions proto_options = toProto(options); + + EXPECT_TRUE(proto_options.has_auto_subscribe()); + EXPECT_FALSE(proto_options.auto_subscribe()); + EXPECT_TRUE(proto_options.has_adaptive_stream()); + EXPECT_TRUE(proto_options.adaptive_stream()); + EXPECT_TRUE(proto_options.has_dynacast()); + EXPECT_TRUE(proto_options.dynacast()); + ASSERT_TRUE(proto_options.has_encryption()); + EXPECT_EQ(proto_options.encryption().encryption_type(), + static_cast(encryption.encryption_type)); + ASSERT_TRUE(proto_options.encryption().has_key_provider_options()); + EXPECT_EQ(proto_options.encryption().key_provider_options().shared_key(), "secret"); + ASSERT_TRUE(proto_options.has_rtc_config()); + EXPECT_EQ(proto_options.rtc_config().ice_transport_type(), proto::TRANSPORT_ALL); + EXPECT_EQ(proto_options.rtc_config().continual_gathering_policy(), proto::GATHER_CONTINUALLY); + ASSERT_EQ(proto_options.rtc_config().ice_servers_size(), 2); + EXPECT_EQ(proto_options.rtc_config().ice_servers(0).urls(0), "stun:stun.l.google.com:19302"); + EXPECT_EQ(proto_options.rtc_config().ice_servers(1).urls(0), "turn:turn.example.com:3478"); + EXPECT_EQ(proto_options.rtc_config().ice_servers(1).username(), "user"); + EXPECT_EQ(proto_options.rtc_config().ice_servers(1).password(), "pass"); + EXPECT_TRUE(proto_options.has_join_retries()); + EXPECT_EQ(proto_options.join_retries(), 8U); + EXPECT_TRUE(proto_options.has_single_peer_connection()); + EXPECT_FALSE(proto_options.single_peer_connection()); + EXPECT_TRUE(proto_options.has_connect_timeout_ms()); + EXPECT_EQ(proto_options.connect_timeout_ms(), 750U); } TEST_F(RoomTest, RtcConfigDefaults) { From ef58c4908a4c6d0d0d64eb97a3c3ee81b0fbe725 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 10 Jun 2026 16:16:17 -0600 Subject: [PATCH 2/4] Optionals, better doc comment/logging --- include/livekit/room.h | 21 +++++++++++++++++---- src/ffi_client.cpp | 23 +++++++++++++++++++++-- src/room_proto_converter.cpp | 12 +++++++++--- src/tests/unit/test_room.cpp | 15 ++++++--------- 4 files changed, 53 insertions(+), 18 deletions(-) diff --git a/include/livekit/room.h b/include/livekit/room.h index b0d6c46d..e467d650 100644 --- a/include/livekit/room.h +++ b/include/livekit/room.h @@ -76,8 +76,17 @@ struct RoomOptions { /// - remote audio/video frames bool auto_subscribe = true; - /// Enable adaptive stream so the server optimizes subscribed video layers. - bool adaptive_stream = false; + /// Enable adaptive stream for subscribed video tracks. + /// + /// When enabled, the SDK tells the server it may adjust the video layers sent + /// to this client based on what the application is currently rendering. This + /// lets the server pause or downscale subscribed video that is off-screen, + /// hidden, or only needed at a smaller size, reducing downstream bandwidth and + /// decode work. This affects media received by this room; use @ref dynacast + /// to control how this client publishes layers to others. + /// + /// If unset, the Rust SDK default is used. + std::optional adaptive_stream; /// Enable dynacast (server sends optimal layers depending on subscribers). bool dynacast = false; @@ -89,7 +98,9 @@ struct RoomOptions { std::optional rtc_config; /// Number of retries for the initial room join after the first attempt. - std::uint32_t join_retries = 3; + /// + /// If unset, the Rust SDK default is used. + std::optional join_retries; /// Enable single peer connection mode. When true, uses one RTCPeerConnection /// for both publishing and subscribing instead of two separate connections. @@ -97,7 +108,9 @@ struct RoomOptions { bool single_peer_connection = true; /// Timeout for each individual signal connection attempt. - std::chrono::milliseconds connect_timeout = std::chrono::seconds(5); + /// + /// If unset, the Rust SDK default is used. + std::optional connect_timeout; }; /// Represents a LiveKit room session. diff --git a/src/ffi_client.cpp b/src/ffi_client.cpp index d9af0b79..1497ac50 100644 --- a/src/ffi_client.cpp +++ b/src/ffi_client.cpp @@ -18,6 +18,8 @@ #include #include +#include +#include #include "data_track.pb.h" #include "ffi.pb.h" @@ -34,11 +36,27 @@ namespace livekit { namespace { + inline void logAndThrow(const std::string& error_msg) { LK_LOG_ERROR("LiveKit SDK Error: {}", error_msg); throw std::runtime_error(error_msg); } +// Helper for debug logging of optional values +const auto optional_to_string = [](const auto& value) -> std::string { + if (!value) { + return ""; + } + using Value = std::decay_t; + if constexpr (std::is_same_v) { + return *value ? "true" : "false"; + } else if constexpr (std::is_same_v) { + return std::to_string(value->count()); + } else { + return std::to_string(*value); + } +}; + Result subscribeDataTrackFailure(SubscribeDataTrackErrorCode code, const std::string& message) { LK_LOG_WARN("Subscribe data track failed: code={} message={}", static_cast(code), message); @@ -334,8 +352,9 @@ std::future FfiClient::connectAsync(const std::string& u LK_LOG_DEBUG( "[FfiClient] connectAsync: auto_subscribe={}, adaptive_stream={}, dynacast={}, " "single_peer_connection={}, join_retries={}, connect_timeout_ms={}", - options.auto_subscribe, options.adaptive_stream, options.dynacast, options.single_peer_connection, - options.join_retries, options.connect_timeout.count()); + options.auto_subscribe, optional_to_string(options.adaptive_stream), options.dynacast, + options.single_peer_connection, optional_to_string(options.join_retries), + optional_to_string(options.connect_timeout)); try { const proto::FfiResponse resp = sendRequest(req); diff --git a/src/room_proto_converter.cpp b/src/room_proto_converter.cpp index 3b7a0148..53b49c59 100644 --- a/src/room_proto_converter.cpp +++ b/src/room_proto_converter.cpp @@ -406,7 +406,9 @@ proto::KeyProviderOptions toProto(const KeyProviderOptions& in) { proto::RoomOptions toProto(const RoomOptions& in) { proto::RoomOptions out; out.set_auto_subscribe(in.auto_subscribe); - out.set_adaptive_stream(in.adaptive_stream); + if (in.adaptive_stream) { + out.set_adaptive_stream(*in.adaptive_stream); + } out.set_dynacast(in.dynacast); if (in.encryption) { @@ -435,9 +437,13 @@ proto::RoomOptions toProto(const RoomOptions& in) { } } - out.set_join_retries(in.join_retries); + if (in.join_retries) { + out.set_join_retries(*in.join_retries); + } out.set_single_peer_connection(in.single_peer_connection); - out.set_connect_timeout_ms(static_cast(in.connect_timeout.count())); + if (in.connect_timeout) { + out.set_connect_timeout_ms(static_cast(in.connect_timeout->count())); + } return out; } diff --git a/src/tests/unit/test_room.cpp b/src/tests/unit/test_room.cpp index 74c6705a..2e8a0ae0 100644 --- a/src/tests/unit/test_room.cpp +++ b/src/tests/unit/test_room.cpp @@ -51,13 +51,13 @@ TEST_F(RoomTest, RoomOptionsDefaults) { RoomOptions options; EXPECT_TRUE(options.auto_subscribe) << "auto_subscribe should default to true"; - EXPECT_FALSE(options.adaptive_stream) << "adaptive_stream should default to false"; + EXPECT_FALSE(options.adaptive_stream.has_value()) << "adaptive_stream should defer to Rust default"; EXPECT_FALSE(options.dynacast) << "dynacast should default to false"; EXPECT_FALSE(options.encryption.has_value()) << "encryption should not have a value by default"; EXPECT_FALSE(options.rtc_config.has_value()) << "rtc_config should not have a value by default"; - EXPECT_EQ(options.join_retries, 3U) << "join_retries should default to Rust SDK behavior"; + EXPECT_FALSE(options.join_retries.has_value()) << "join_retries should defer to Rust default"; EXPECT_TRUE(options.single_peer_connection) << "single_peer_connection should default to true"; - EXPECT_EQ(options.connect_timeout, std::chrono::seconds(5)) << "connect_timeout should default to Rust SDK behavior"; + EXPECT_FALSE(options.connect_timeout.has_value()) << "connect_timeout should defer to Rust default"; } TEST_F(RoomTest, RoomOptionsToProtoSerializesDefaults) { @@ -65,18 +65,15 @@ TEST_F(RoomTest, RoomOptionsToProtoSerializesDefaults) { EXPECT_TRUE(proto_options.has_auto_subscribe()); EXPECT_TRUE(proto_options.auto_subscribe()); - EXPECT_TRUE(proto_options.has_adaptive_stream()); - EXPECT_FALSE(proto_options.adaptive_stream()); + EXPECT_FALSE(proto_options.has_adaptive_stream()); EXPECT_TRUE(proto_options.has_dynacast()); EXPECT_FALSE(proto_options.dynacast()); EXPECT_FALSE(proto_options.has_encryption()); EXPECT_FALSE(proto_options.has_rtc_config()); - EXPECT_TRUE(proto_options.has_join_retries()); - EXPECT_EQ(proto_options.join_retries(), 3U); + EXPECT_FALSE(proto_options.has_join_retries()); EXPECT_TRUE(proto_options.has_single_peer_connection()); EXPECT_TRUE(proto_options.single_peer_connection()); - EXPECT_TRUE(proto_options.has_connect_timeout_ms()); - EXPECT_EQ(proto_options.connect_timeout_ms(), 5000U); + EXPECT_FALSE(proto_options.has_connect_timeout_ms()); } TEST_F(RoomTest, RoomOptionsProtoConverter) { From e98646ea4a4efb5fdc5903d71224d2b68ee802b7 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 10 Jun 2026 17:29:29 -0600 Subject: [PATCH 3/4] Add proper unit test --- src/tests/unit/test_room.cpp | 62 +++++++++++++++++++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/src/tests/unit/test_room.cpp b/src/tests/unit/test_room.cpp index 2e8a0ae0..e0a1aa87 100644 --- a/src/tests/unit/test_room.cpp +++ b/src/tests/unit/test_room.cpp @@ -18,14 +18,42 @@ #include #include +#include +#include +#include #include "room_proto_converter.h" namespace livekit::test { +namespace { + +void setRustLogForRetryTest() { +#ifdef _WIN32 + _putenv_s("RUST_LOG", "warn"); +#else + setenv("RUST_LOG", "warn", 1); +#endif +} + +std::size_t countOccurrences(const std::string& haystack, const std::string& needle) { + std::size_t count = 0; + std::size_t pos = 0; + while ((pos = haystack.find(needle, pos)) != std::string::npos) { + ++count; + pos += needle.size(); + } + return count; +} + +} // namespace + class RoomTest : public ::testing::Test { protected: - void SetUp() override { livekit::initialize(livekit::LogLevel::Info); } + void SetUp() override { + setRustLogForRetryTest(); + livekit::initialize(livekit::LogLevel::Info); + } void TearDown() override { livekit::shutdown(); } }; @@ -35,6 +63,8 @@ TEST_F(RoomTest, ConnectWithoutInitialize) { livekit::shutdown(); Room room; + + // Default room options okay here, will return before FFI layer since not initialized bool result = room.connect("wss://localhost:7880", "test", livekit::RoomOptions()); EXPECT_FALSE(result) << "Connecting without initializing should return false"; EXPECT_TRUE(room.localParticipant().expired()) << "Local participant should be empty after failed connect"; @@ -123,6 +153,36 @@ TEST_F(RoomTest, RoomOptionsProtoConverter) { EXPECT_EQ(proto_options.connect_timeout_ms(), 750U); } +// This test validates the join retries behavior when connecting to an invalid URL +// It sets the RUST_LOG env variable to capture the retry logs +TEST_F(RoomTest, RoomOptionsJoinRetries) { + constexpr std::uint32_t kJoinRetries = 10; + + Room room; + RoomOptions options; + options.join_retries = kJoinRetries; + + testing::internal::CaptureStderr(); + const bool result = room.connect("not-a-livekit-url", "test-token", options); + const std::string stderr_output = testing::internal::GetCapturedStderr(); + + EXPECT_FALSE(result) << "Connecting with an invalid URL should fail"; + EXPECT_TRUE(room.localParticipant().expired()) << "Local participant should be empty after failed connect"; + EXPECT_TRUE(room.remoteParticipants().empty()) << "Remote participants should be empty after failed connect"; + + // Do the below that way we can print stderr only once if there was a string change to the output + const bool has_failure = HasFailure(); + + EXPECT_NE(stderr_output.find("Room::connect failed:"), std::string::npos); + EXPECT_EQ(countOccurrences(stderr_output, "retrying..."), kJoinRetries); + EXPECT_NE(stderr_output.find("retrying... (10/10)"), std::string::npos); + + if (!has_failure && HasFailure()) { + std::cerr << "### One or more checks failed due to log format changing. Captured stderr output below ###\n"; + std::cerr << stderr_output << "\n"; + } +} + TEST_F(RoomTest, RtcConfigDefaults) { RtcConfig config; From a4f29d37cbc057d6b9956dc6a9f4e5b7f98501b6 Mon Sep 17 00:00:00 2001 From: Alan George Date: Wed, 10 Jun 2026 18:07:41 -0600 Subject: [PATCH 4/4] Fix env var setting --- src/tests/unit/test_room.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/tests/unit/test_room.cpp b/src/tests/unit/test_room.cpp index e0a1aa87..56a8f634 100644 --- a/src/tests/unit/test_room.cpp +++ b/src/tests/unit/test_room.cpp @@ -28,14 +28,23 @@ namespace livekit::test { namespace { +constexpr const char* kRustRetryLogFilter = "livekit::rtc_engine=warn,livekit_ffi::server::room=error"; + void setRustLogForRetryTest() { #ifdef _WIN32 - _putenv_s("RUST_LOG", "warn"); + _putenv_s("RUST_LOG", kRustRetryLogFilter); #else - setenv("RUST_LOG", "warn", 1); + setenv("RUST_LOG", kRustRetryLogFilter, 1); #endif } +// Configure RUST_LOG during static initialization, before any test can initialize +// the Rust FFI logger (which reads env vars once at construction time). +const bool kRustLogConfiguredBeforeTests = [] { + setRustLogForRetryTest(); + return true; +}(); + std::size_t countOccurrences(const std::string& haystack, const std::string& needle) { std::size_t count = 0; std::size_t pos = 0;