blob: cebefb83f68575ac0f2437df0a3de729ef9d37ad [file] [log] [blame]
// 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