| // Copyright 2024 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "remoting/protocol/ice_config_fetcher_cloud.h" |
| |
| #include <memory> |
| #include <optional> |
| #include <utility> |
| |
| #include "base/functional/bind.h" |
| #include "base/test/mock_callback.h" |
| #include "base/test/task_environment.h" |
| #include "remoting/base/http_status.h" |
| #include "remoting/base/instance_identity_token_getter.h" |
| #include "remoting/base/passthrough_oauth_token_getter.h" |
| #include "remoting/base/protobuf_http_test_responder.h" |
| #include "remoting/proto/google/internal/remoting/cloud/v1alpha/network_traversal_service.pb.h" |
| #include "remoting/protocol/ice_config.h" |
| #include "remoting/protocol/ice_config_fetcher.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace remoting::protocol { |
| |
| namespace { |
| |
| using testing::_; |
| |
| using MockOnResultCallback = |
| base::MockCallback<IceConfigFetcher::OnIceConfigCallback>; |
| |
| using ::google::internal::remoting::cloud::v1alpha::GenerateIceConfigResponse; |
| using ::google::internal::remoting::cloud::v1alpha::StunServer; |
| using ::google::internal::remoting::cloud::v1alpha::TurnServer; |
| |
| constexpr int kLifetimeDurationSeconds = 43200; |
| |
| class FakeInstanceIdentityTokenGetter : public InstanceIdentityTokenGetter { |
| public: |
| FakeInstanceIdentityTokenGetter() = default; |
| FakeInstanceIdentityTokenGetter(const FakeInstanceIdentityTokenGetter&) = |
| delete; |
| FakeInstanceIdentityTokenGetter& operator=( |
| const FakeInstanceIdentityTokenGetter&) = delete; |
| ~FakeInstanceIdentityTokenGetter() override = default; |
| |
| void RetrieveToken(TokenCallback on_token) override { |
| std::move(on_token).Run("header.payload.signature"); |
| } |
| }; |
| |
| } // namespace |
| |
| class IceConfigFetcherCloudTest : public testing::Test { |
| protected: |
| std::unique_ptr<MockOnResultCallback> SendRequest( |
| base::RunLoop* run_loop_to_quit, |
| std::optional<IceConfig>& out_config) { |
| auto mock_on_result = std::make_unique<MockOnResultCallback>(); |
| EXPECT_CALL(*mock_on_result, Run(_)) |
| .WillOnce([=, &out_config](std::optional<IceConfig> ice_config) { |
| out_config = std::move(ice_config); |
| run_loop_to_quit->Quit(); |
| }); |
| |
| fetcher_.GetIceConfig(mock_on_result->Get()); |
| return mock_on_result; |
| } |
| |
| base::test::TaskEnvironment task_environment_{ |
| base::test::TaskEnvironment::TimeSource::MOCK_TIME}; |
| ProtobufHttpTestResponder test_responder_; |
| PassthroughOAuthTokenGetter oauth_token_getter_{{"blergh", "blargh"}}; |
| FakeInstanceIdentityTokenGetter instance_identity_token_getter_; |
| IceConfigFetcherCloud fetcher_{test_responder_.GetUrlLoaderFactory(), |
| &oauth_token_getter_, |
| &instance_identity_token_getter_}; |
| }; |
| |
| TEST_F(IceConfigFetcherCloudTest, StunOnlyResponse) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| auto now = base::Time::Now(); |
| GenerateIceConfigResponse response; |
| response.mutable_lifetime_duration()->set_seconds(kLifetimeDurationSeconds); |
| auto* stun_server = response.add_stun_servers(); |
| stun_server->add_urls("stun:stun_server.com:18344"); |
| test_responder_.AddResponseToMostRecentRequestUrl(response); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(received_config.has_value()); |
| |
| EXPECT_EQ(received_config->turn_servers.size(), 0U); |
| EXPECT_EQ(received_config->stun_servers.size(), 1U); |
| EXPECT_EQ(received_config->max_bitrate_kbps, 0); |
| EXPECT_GE(received_config->expiration_time, |
| now + base::Seconds(kLifetimeDurationSeconds)); |
| } |
| |
| TEST_F(IceConfigFetcherCloudTest, TurnOnlyResponse) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| auto now = base::Time::Now(); |
| GenerateIceConfigResponse response; |
| response.mutable_lifetime_duration()->set_seconds(kLifetimeDurationSeconds); |
| auto* turn_server_1 = response.add_turn_servers(); |
| turn_server_1->add_urls("turn:the_server.com"); |
| turn_server_1->add_urls("turn:the_server_1.com"); |
| turn_server_1->set_username("123"); |
| turn_server_1->set_credential("abc"); |
| auto* turn_server_2 = response.add_turn_servers(); |
| turn_server_2->add_urls("turns:the_server.com"); |
| turn_server_2->add_urls("turns:the_server_1.com"); |
| turn_server_2->set_username("123"); |
| turn_server_2->set_credential("abc"); |
| turn_server_2->set_max_rate_kbps(42); |
| auto* turn_server_3 = response.add_turn_servers(); |
| turn_server_3->add_urls("turn:my_server.com"); |
| turn_server_3->set_username("123"); |
| turn_server_3->set_credential("abc"); |
| test_responder_.AddResponseToMostRecentRequestUrl(response); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(received_config.has_value()); |
| |
| EXPECT_EQ(received_config->turn_servers.size(), 5U); |
| EXPECT_EQ(received_config->stun_servers.size(), 0U); |
| EXPECT_EQ(received_config->max_bitrate_kbps, 42); |
| EXPECT_GE(received_config->expiration_time, |
| now + base::Seconds(kLifetimeDurationSeconds)); |
| } |
| |
| TEST_F(IceConfigFetcherCloudTest, MaxBitrateTest) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| GenerateIceConfigResponse response; |
| response.mutable_lifetime_duration()->set_seconds(kLifetimeDurationSeconds); |
| auto* turn_server_1 = response.add_turn_servers(); |
| turn_server_1->add_urls("turn:the_server.com"); |
| turn_server_1->set_username("123"); |
| turn_server_1->set_credential("abc"); |
| turn_server_1->set_max_rate_kbps(10); |
| auto* turn_server_2 = response.add_turn_servers(); |
| turn_server_2->add_urls("turns:the_server.com"); |
| turn_server_2->set_username("123"); |
| turn_server_2->set_credential("abc"); |
| turn_server_2->set_max_rate_kbps(5); // Lowest value should be chosen. |
| auto* turn_server_3 = response.add_turn_servers(); |
| turn_server_3->add_urls("turn:my_server.com"); |
| turn_server_3->set_username("123"); |
| turn_server_3->set_credential("abc"); |
| turn_server_3->set_max_rate_kbps(7); |
| test_responder_.AddResponseToMostRecentRequestUrl(response); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(received_config.has_value()); |
| EXPECT_EQ(received_config->max_bitrate_kbps, 5); |
| } |
| |
| TEST_F(IceConfigFetcherCloudTest, StunAndTurnServersInResponse) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| auto now = base::Time::Now(); |
| GenerateIceConfigResponse response; |
| response.mutable_lifetime_duration()->set_seconds(kLifetimeDurationSeconds); |
| auto* turn_server = response.add_turn_servers(); |
| turn_server->add_urls("turns:the_server.com"); |
| turn_server->set_username("123"); |
| turn_server->set_credential("abc"); |
| auto* stun_server = response.add_stun_servers(); |
| stun_server->add_urls("stun:stun_server.com:18344"); |
| test_responder_.AddResponseToMostRecentRequestUrl(response); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(received_config.has_value()); |
| |
| EXPECT_EQ(received_config->turn_servers.size(), 1U); |
| EXPECT_EQ(received_config->stun_servers.size(), 1U); |
| EXPECT_GE(received_config->expiration_time, |
| now + base::Seconds(kLifetimeDurationSeconds)); |
| } |
| |
| TEST_F(IceConfigFetcherCloudTest, EmptyResponse) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| auto now = base::Time::Now(); |
| GenerateIceConfigResponse response; |
| response.mutable_lifetime_duration()->set_seconds(kLifetimeDurationSeconds); |
| test_responder_.AddResponseToMostRecentRequestUrl(response); |
| run_loop.Run(); |
| |
| ASSERT_TRUE(received_config.has_value()); |
| |
| EXPECT_EQ(received_config->turn_servers.size(), 0U); |
| EXPECT_EQ(received_config->stun_servers.size(), 0U); |
| EXPECT_LT(received_config->expiration_time, |
| now + base::Seconds(kLifetimeDurationSeconds)); |
| } |
| |
| TEST_F(IceConfigFetcherCloudTest, FailedRequest) { |
| base::RunLoop run_loop; |
| std::optional<IceConfig> received_config; |
| auto mock_on_result = SendRequest(&run_loop, received_config); |
| |
| ASSERT_GT(test_responder_.GetNumPending(), 0); |
| test_responder_.AddErrorToMostRecentRequestUrl( |
| HttpStatus(HttpStatus::Code::INVALID_ARGUMENT, "")); |
| run_loop.Run(); |
| |
| EXPECT_FALSE(received_config.has_value()); |
| } |
| |
| } // namespace remoting::protocol |