blob: 022dd28694bd7e72849735dc9efdf1e27d5669be [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include <numeric>
#include <utility>
#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "base/macros.h"
#include "base/message_loop/message_pump_type.h"
#include "base/rand_util.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool/thread_pool_instance.h"
#include "base/task_runner_util.h"
#include "base/test/task_environment.h"
#include "base/threading/thread_task_runner_handle.h"
#include "jingle/glue/thread_wrapper.h"
#include "net/base/network_change_notifier.h"
#include "net/test/test_data_directory.h"
#include "net/url_request/url_request_context_getter.h"
#include "remoting/base/rsa_key_pair.h"
#include "remoting/base/url_request.h"
#include "remoting/client/audio/audio_player.h"
#include "remoting/client/chromoting_client.h"
#include "remoting/client/client_context.h"
#include "remoting/client/client_user_interface.h"
#include "remoting/client/software_video_renderer.h"
#include "remoting/host/chromoting_host.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/fake_desktop_environment.h"
#include "remoting/protocol/auth_util.h"
#include "remoting/protocol/client_authentication_config.h"
#include "remoting/protocol/frame_consumer.h"
#include "remoting/protocol/frame_stats.h"
#include "remoting/protocol/jingle_session_manager.h"
#include "remoting/protocol/me2me_host_authenticator_factory.h"
#include "remoting/protocol/session_config.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/protocol/video_frame_pump.h"
#include "remoting/protocol/video_renderer.h"
#include "remoting/signaling/fake_signal_strategy.h"
#include "remoting/test/cyclic_frame_generator.h"
#include "remoting/test/fake_network_dispatcher.h"
#include "remoting/test/fake_port_allocator.h"
#include "remoting/test/fake_socket_factory.h"
#include "remoting/test/scroll_frame_generator.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace remoting {
using base::test::TaskEnvironment;
using protocol::ChannelConfig;
namespace {
const char kHostJid[] = "host_jid@example.com/host";
const char kHostOwner[] = "jane.doe@example.com";
const char kClientJid[] = "jane.doe@example.com/client";
const char kHostId[] = "ABC123";
const char kHostPin[] = "123456";
struct NetworkPerformanceParams {
// |buffer_s| defines buffer size in seconds. actual buffer size is calculated
// based on bandwidth_kbps
NetworkPerformanceParams(int bandwidth_kbps,
double buffer_s,
double latency_average_ms,
double latency_stddev_ms,
double out_of_order_rate,
double signaling_latency_ms)
: bandwidth_kbps(bandwidth_kbps),
max_buffers(buffer_s * bandwidth_kbps * 1000 / 8),
latency_average(base::TimeDelta::FromMillisecondsD(latency_average_ms)),
latency_stddev(base::TimeDelta::FromMillisecondsD(latency_stddev_ms)),
out_of_order_rate(out_of_order_rate),
signaling_latency(
base::TimeDelta::FromMillisecondsD(signaling_latency_ms)) {}
int bandwidth_kbps;
int max_buffers;
base::TimeDelta latency_average;
base::TimeDelta latency_stddev;
double out_of_order_rate;
base::TimeDelta signaling_latency;
};
class FakeCursorShapeStub : public protocol::CursorShapeStub {
public:
FakeCursorShapeStub() = default;
~FakeCursorShapeStub() override = default;
// protocol::CursorShapeStub interface.
void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape) override {}
};
} // namespace
class ProtocolPerfTest
: public testing::Test,
public testing::WithParamInterface<NetworkPerformanceParams>,
public ClientUserInterface,
public protocol::FrameConsumer,
public protocol::FrameStatsConsumer,
public HostStatusObserver {
public:
ProtocolPerfTest()
: task_environment_(TaskEnvironment::MainThreadType::IO),
host_thread_("host"),
capture_thread_("capture"),
encode_thread_("encode"),
decode_thread_("decode") {
host_thread_.StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
capture_thread_.Start();
encode_thread_.Start();
decode_thread_.Start();
network_change_notifier_ = net::NetworkChangeNotifier::CreateIfNeeded();
desktop_environment_factory_.reset(
new FakeDesktopEnvironmentFactory(capture_thread_.task_runner()));
}
virtual ~ProtocolPerfTest() {
host_thread_.task_runner()->DeleteSoon(FROM_HERE, host_.release());
host_thread_.task_runner()->DeleteSoon(FROM_HERE,
host_signaling_.release());
base::RunLoop().RunUntilIdle();
}
// ClientUserInterface interface.
void OnConnectionState(protocol::ConnectionToHost::State state,
protocol::ErrorCode error) override {
if (state == protocol::ConnectionToHost::CONNECTED) {
client_connected_ = true;
if (host_connected_)
connecting_loop_->Quit();
}
}
void OnConnectionReady(bool ready) override {}
void OnRouteChanged(const std::string& channel_name,
const protocol::TransportRoute& route) override {}
void SetCapabilities(const std::string& capabilities) override {}
void SetPairingResponse(
const protocol::PairingResponse& pairing_response) override {}
void DeliverHostMessage(const protocol::ExtensionMessage& message) override {}
void SetDesktopSize(const webrtc::DesktopSize& size,
const webrtc::DesktopVector& dpi) override {}
protocol::ClipboardStub* GetClipboardStub() override { return nullptr; }
protocol::CursorShapeStub* GetCursorShapeStub() override {
return &cursor_shape_stub_;
}
protocol::KeyboardLayoutStub* GetKeyboardLayoutStub() override {
return nullptr;
}
// protocol::FrameConsumer interface.
std::unique_ptr<webrtc::DesktopFrame> AllocateFrame(
const webrtc::DesktopSize& size) override {
return std::make_unique<webrtc::BasicDesktopFrame>(size);
}
void DrawFrame(std::unique_ptr<webrtc::DesktopFrame> frame,
base::OnceClosure done) override {
last_video_frame_ = std::move(frame);
if (on_frame_task_)
on_frame_task_.Run();
if (done)
std::move(done).Run();
}
protocol::FrameConsumer::PixelFormat GetPixelFormat() override {
return FORMAT_BGRA;
}
// FrameStatsConsumer interface.
void OnVideoFrameStats(const protocol::FrameStats& frame_stats) override {
// Ignore store stats for empty frames.
if (!frame_stats.host_stats.frame_size)
return;
frame_stats_.push_back(frame_stats);
if (waiting_frame_stats_loop_ &&
frame_stats_.size() >= num_expected_frame_stats_) {
waiting_frame_stats_loop_->Quit();
}
}
// HostStatusObserver interface.
void OnClientAuthenticated(const std::string& jid) override {
if (event_timestamp_source_) {
auto& session = host_->client_sessions_for_tests().front();
session->SetEventTimestampsSourceForTests(
std::move(event_timestamp_source_));
}
}
void OnClientConnected(const std::string& jid) override {
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProtocolPerfTest::OnHostConnectedMainThread,
base::Unretained(this)));
}
protected:
void WaitConnected() {
client_connected_ = false;
host_connected_ = false;
connecting_loop_.reset(new base::RunLoop());
connecting_loop_->Run();
ASSERT_TRUE(client_connected_ && host_connected_);
}
void OnHostConnectedMainThread() {
host_connected_ = true;
if (client_connected_)
connecting_loop_->Quit();
}
std::unique_ptr<webrtc::DesktopFrame> ReceiveFrame() {
last_video_frame_.reset();
waiting_frames_loop_.reset(new base::RunLoop());
on_frame_task_ = waiting_frames_loop_->QuitClosure();
waiting_frames_loop_->Run();
waiting_frames_loop_.reset();
EXPECT_TRUE(last_video_frame_);
return std::move(last_video_frame_);
}
void WaitFrameStats(int num_frames) {
num_expected_frame_stats_ = num_frames;
waiting_frame_stats_loop_.reset(new base::RunLoop());
waiting_frame_stats_loop_->Run();
waiting_frame_stats_loop_.reset();
EXPECT_GE(frame_stats_.size(), num_expected_frame_stats_);
}
// Creates test host and client and starts connection between them. Caller
// should call WaitConnected() to wait until connection is established. The
// host is started on |host_thread_| while the client works on the main
// thread.
void StartHostAndClient(bool use_webrtc) {
fake_network_dispatcher_ = new FakeNetworkDispatcher();
client_signaling_.reset(
new FakeSignalStrategy(SignalingAddress(kClientJid)));
jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
protocol_config_ = protocol::CandidateSessionConfig::CreateDefault();
protocol_config_->DisableAudioChannel();
protocol_config_->set_webrtc_supported(use_webrtc);
protocol_config_->set_ice_supported(!use_webrtc);
host_thread_.task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&ProtocolPerfTest::StartHost, base::Unretained(this)));
}
void StartHost() {
DCHECK(host_thread_.task_runner()->BelongsToCurrentThread());
jingle_glue::JingleThreadWrapper::EnsureForCurrentMessageLoop();
host_signaling_.reset(new FakeSignalStrategy(SignalingAddress(kHostJid)));
host_signaling_->set_send_delay(GetParam().signaling_latency);
host_signaling_->ConnectTo(client_signaling_.get());
protocol::NetworkSettings network_settings(
protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
std::unique_ptr<FakePortAllocatorFactory> port_allocator_factory(
new FakePortAllocatorFactory(fake_network_dispatcher_));
port_allocator_factory->socket_factory()->SetBandwidth(
GetParam().bandwidth_kbps * 1000 / 8, GetParam().max_buffers);
port_allocator_factory->socket_factory()->SetLatency(
GetParam().latency_average, GetParam().latency_stddev);
port_allocator_factory->socket_factory()->set_out_of_order_rate(
GetParam().out_of_order_rate);
scoped_refptr<protocol::TransportContext> transport_context(
new protocol::TransportContext(std::move(port_allocator_factory),
nullptr, network_settings,
protocol::TransportRole::SERVER));
std::unique_ptr<protocol::SessionManager> session_manager(
new protocol::JingleSessionManager(host_signaling_.get()));
session_manager->set_protocol_config(protocol_config_->Clone());
// Encoder runs on a separate thread, main thread is used for everything
// else.
host_.reset(new ChromotingHost(
desktop_environment_factory_.get(), std::move(session_manager),
transport_context, host_thread_.task_runner(),
encode_thread_.task_runner(),
DesktopEnvironmentOptions::CreateDefault()));
base::FilePath certs_dir(net::GetTestCertsDirectory());
std::string host_cert;
ASSERT_TRUE(base::ReadFileToString(
certs_dir.AppendASCII("unittest.selfsigned.der"), &host_cert));
base::FilePath key_path = certs_dir.AppendASCII("unittest.key.bin");
std::string key_string;
ASSERT_TRUE(base::ReadFileToString(key_path, &key_string));
std::string key_base64;
base::Base64Encode(key_string, &key_base64);
scoped_refptr<RsaKeyPair> key_pair = RsaKeyPair::FromString(key_base64);
ASSERT_TRUE(key_pair.get());
std::string host_pin_hash =
protocol::GetSharedSecretHash(kHostId, kHostPin);
std::unique_ptr<protocol::AuthenticatorFactory> auth_factory =
protocol::Me2MeHostAuthenticatorFactory::CreateWithPin(
kHostOwner, host_cert, key_pair, std::vector<std::string>(),
host_pin_hash, nullptr);
host_->SetAuthenticatorFactory(std::move(auth_factory));
host_->status_monitor()->AddStatusObserver(this);
host_->Start(kHostOwner);
task_environment_.GetMainThreadTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&ProtocolPerfTest::StartClientAfterHost,
base::Unretained(this)));
}
void StartClientAfterHost() {
client_signaling_->set_send_delay(GetParam().signaling_latency);
client_signaling_->ConnectTo(host_signaling_.get());
protocol::NetworkSettings network_settings(
protocol::NetworkSettings::NAT_TRAVERSAL_OUTGOING);
// Initialize client.
client_context_.reset(
new ClientContext(base::ThreadTaskRunnerHandle::Get()));
client_context_->Start();
std::unique_ptr<FakePortAllocatorFactory> port_allocator_factory(
new FakePortAllocatorFactory(fake_network_dispatcher_));
client_socket_factory_ = port_allocator_factory->socket_factory();
port_allocator_factory->socket_factory()->SetBandwidth(
GetParam().bandwidth_kbps * 1000 / 8, GetParam().max_buffers);
port_allocator_factory->socket_factory()->SetLatency(
GetParam().latency_average, GetParam().latency_stddev);
port_allocator_factory->socket_factory()->set_out_of_order_rate(
GetParam().out_of_order_rate);
scoped_refptr<protocol::TransportContext> transport_context(
new protocol::TransportContext(std::move(port_allocator_factory),
nullptr, network_settings,
protocol::TransportRole::CLIENT));
protocol::ClientAuthenticationConfig client_auth_config;
client_auth_config.host_id = kHostId;
client_auth_config.fetch_secret_callback = base::BindRepeating(
&ProtocolPerfTest::FetchPin, base::Unretained(this));
video_renderer_.reset(new SoftwareVideoRenderer(this));
video_renderer_->Initialize(*client_context_, this);
client_.reset(new ChromotingClient(client_context_.get(), this,
video_renderer_.get(), nullptr));
client_->set_protocol_config(protocol_config_->Clone());
client_->Start(client_signaling_.get(), client_auth_config,
transport_context, kHostJid, std::string());
}
void FetchPin(
bool pairing_supported,
const protocol::SecretFetchedCallback& secret_fetched_callback) {
secret_fetched_callback.Run(kHostPin);
}
void MeasureTotalLatency(bool use_webrtc);
void MeasureScrollPerformance(bool use_webrtc);
TaskEnvironment task_environment_;
scoped_refptr<FakeNetworkDispatcher> fake_network_dispatcher_;
base::Thread host_thread_;
base::Thread capture_thread_;
base::Thread encode_thread_;
base::Thread decode_thread_;
std::unique_ptr<FakeDesktopEnvironmentFactory> desktop_environment_factory_;
scoped_refptr<protocol::InputEventTimestampsSource> event_timestamp_source_;
FakeCursorShapeStub cursor_shape_stub_;
std::unique_ptr<protocol::CandidateSessionConfig> protocol_config_;
std::unique_ptr<FakeSignalStrategy> host_signaling_;
std::unique_ptr<FakeSignalStrategy> client_signaling_;
std::unique_ptr<ChromotingHost> host_;
std::unique_ptr<ClientContext> client_context_;
std::unique_ptr<SoftwareVideoRenderer> video_renderer_;
std::unique_ptr<ChromotingClient> client_;
FakePacketSocketFactory* client_socket_factory_;
std::unique_ptr<base::RunLoop> connecting_loop_;
std::unique_ptr<base::RunLoop> waiting_frames_loop_;
std::unique_ptr<base::RunLoop> waiting_frame_stats_loop_;
size_t num_expected_frame_stats_;
bool client_connected_;
bool host_connected_;
base::RepeatingClosure on_frame_task_;
std::unique_ptr<VideoPacket> last_video_packet_;
std::unique_ptr<webrtc::DesktopFrame> last_video_frame_;
std::vector<protocol::FrameStats> frame_stats_;
std::unique_ptr<net::NetworkChangeNotifier> network_change_notifier_;
private:
DISALLOW_COPY_AND_ASSIGN(ProtocolPerfTest);
};
INSTANTIATE_TEST_SUITE_P(
NoDelay,
ProtocolPerfTest,
::testing::Values(NetworkPerformanceParams(0, 0, 0, 0, 0.0, 0)));
INSTANTIATE_TEST_SUITE_P(
HighLatency,
ProtocolPerfTest,
::testing::Values(NetworkPerformanceParams(0, 0, 300, 30, 0.0, 0),
NetworkPerformanceParams(0, 0, 30, 10, 0.0, 0)));
INSTANTIATE_TEST_SUITE_P(
OutOfOrder,
ProtocolPerfTest,
::testing::Values(NetworkPerformanceParams(0, 0, 2, 0, 0.01, 0),
NetworkPerformanceParams(0, 0, 30, 1, 0.01, 0),
NetworkPerformanceParams(0, 0, 30, 1, 0.1, 0),
NetworkPerformanceParams(0, 0, 300, 20, 0.01, 0),
NetworkPerformanceParams(0, 0, 300, 20, 0.1, 0)));
INSTANTIATE_TEST_SUITE_P(
LimitedBandwidth,
ProtocolPerfTest,
::testing::Values(
// 100 Mbps
NetworkPerformanceParams(100000, 0.25, 2, 1, 0.0, 0),
NetworkPerformanceParams(100000, 1.0, 2, 1, 0.0, 0),
// 8 Mbps
NetworkPerformanceParams(8000, 0.25, 30, 5, 0.01, 0),
NetworkPerformanceParams(8000, 1.0, 30, 5, 0.01, 0),
// 2 Mbps
NetworkPerformanceParams(2000, 0.25, 30, 5, 0.01, 0),
NetworkPerformanceParams(2000, 1.0, 30, 5, 0.01, 0),
// 800 kbps
NetworkPerformanceParams(800, 0.25, 130, 5, 0.00, 0),
NetworkPerformanceParams(800, 1.0, 130, 5, 0.00, 0)));
INSTANTIATE_TEST_SUITE_P(
SlowSignaling,
ProtocolPerfTest,
::testing::Values(NetworkPerformanceParams(8000, 0.25, 30, 0, 0.0, 50),
NetworkPerformanceParams(8000, 0.25, 30, 0, 0.0, 500)));
// TotalLatency[Ice|Webrtc] tests measure video latency in the case when the
// whole screen is updated occasionally. It's intended to simulate the case when
// user actions (e.g. Alt-Tab, click on the task bar) cause whole screen to be
// updated.
void ProtocolPerfTest::MeasureTotalLatency(bool use_webrtc) {
scoped_refptr<test::CyclicFrameGenerator> frame_generator =
test::CyclicFrameGenerator::Create();
desktop_environment_factory_->set_frame_generator(base::BindRepeating(
&test::CyclicFrameGenerator::GenerateFrame, frame_generator));
event_timestamp_source_ = frame_generator;
StartHostAndClient(use_webrtc);
ASSERT_NO_FATAL_FAILURE(WaitConnected());
int total_frames = 0;
const base::TimeDelta kWarmUpTime = base::TimeDelta::FromSeconds(2);
const base::TimeDelta kTestTime = base::TimeDelta::FromSeconds(5);
base::TimeTicks start_time = base::TimeTicks::Now();
while ((base::TimeTicks::Now() - start_time) < (kWarmUpTime + kTestTime)) {
ReceiveFrame();
++total_frames;
}
WaitFrameStats(total_frames);
int warm_up_frames = 0;
int big_update_count = 0;
base::TimeDelta total_latency_big_updates;
int small_update_count = 0;
base::TimeDelta total_latency_small_updates;
for (int i = 0; i < total_frames; ++i) {
const protocol::FrameStats& stats = frame_stats_[i];
// CyclicFrameGenerator::TakeLastEventTimestamps() always returns non-null
// timestamps.
CHECK(!stats.host_stats.latest_event_timestamp.is_null());
test::CyclicFrameGenerator::ChangeInfoList changes =
frame_generator->GetChangeList(stats.host_stats.latest_event_timestamp);
// Allow 2 seconds for the connection to warm-up, e.g. to get bandwidth
// estimate, etc. These frames are ignored when calculating stats below.
if (stats.client_stats.time_rendered < (start_time + kWarmUpTime)) {
++warm_up_frames;
continue;
}
for (auto& change_info : changes) {
base::TimeDelta latency =
stats.client_stats.time_rendered - change_info.timestamp;
switch (change_info.type) {
case test::CyclicFrameGenerator::ChangeType::NO_CHANGES:
NOTREACHED();
break;
case test::CyclicFrameGenerator::ChangeType::FULL:
total_latency_big_updates += latency;
++big_update_count;
break;
case test::CyclicFrameGenerator::ChangeType::CURSOR:
total_latency_small_updates += latency;
++small_update_count;
break;
}
}
}
WaitFrameStats(total_frames);
CHECK(big_update_count);
VLOG(0) << "Average latency for big updates: "
<< (total_latency_big_updates / big_update_count).InMillisecondsF();
if (small_update_count) {
VLOG(0)
<< "Average latency for small updates: "
<< (total_latency_small_updates / small_update_count).InMillisecondsF();
}
double average_bwe =
std::accumulate(frame_stats_.begin() + warm_up_frames,
frame_stats_.begin() + total_frames, 0.0,
[](double sum, const protocol::FrameStats& stats) {
return sum + stats.host_stats.bandwidth_estimate_kbps;
}) /
(total_frames - warm_up_frames);
VLOG(0) << "Average BW estimate: " << average_bwe
<< " (actual: " << GetParam().bandwidth_kbps << ")";
}
TEST_P(ProtocolPerfTest, TotalLatencyIce) {
MeasureTotalLatency(false);
}
TEST_P(ProtocolPerfTest, TotalLatencyWebrtc) {
MeasureTotalLatency(true);
}
// ScrollPerformance[Ice|Webrtc] tests simulate whole screen being scrolled
// continuously. They measure FPS and video latency.
void ProtocolPerfTest::MeasureScrollPerformance(bool use_webrtc) {
scoped_refptr<test::ScrollFrameGenerator> frame_generator =
new test::ScrollFrameGenerator();
desktop_environment_factory_->set_frame_generator(base::BindRepeating(
&test::ScrollFrameGenerator::GenerateFrame, frame_generator));
event_timestamp_source_ = frame_generator;
StartHostAndClient(use_webrtc);
ASSERT_NO_FATAL_FAILURE(WaitConnected());
const base::TimeDelta kWarmUpTime = base::TimeDelta::FromSeconds(2);
const base::TimeDelta kTestTime = base::TimeDelta::FromSeconds(2);
int num_frames = 0;
int warm_up_frames = 0;
base::TimeTicks start_time = base::TimeTicks::Now();
while ((base::TimeTicks::Now() - start_time) < (kTestTime + kWarmUpTime)) {
ReceiveFrame();
++num_frames;
// Allow 2 seconds for the connection to warm-up, e.g. to get bandwidth
// estimate, etc. These frames are ignored when calculating stats below.
if ((base::TimeTicks::Now() - start_time) < kWarmUpTime) {
++warm_up_frames;
client_socket_factory_->ResetStats();
}
}
base::TimeDelta total_time = (base::TimeTicks::Now() - start_time);
WaitFrameStats(warm_up_frames + num_frames);
int total_size =
std::accumulate(frame_stats_.begin() + warm_up_frames,
frame_stats_.begin() + warm_up_frames + num_frames, 0,
[](int sum, const protocol::FrameStats& stats) {
return sum + stats.host_stats.frame_size;
});
base::TimeDelta latency_sum = std::accumulate(
frame_stats_.begin() + warm_up_frames,
frame_stats_.begin() + warm_up_frames + num_frames, base::TimeDelta(),
[](base::TimeDelta sum, const protocol::FrameStats& stats) {
return sum + (stats.client_stats.time_rendered -
stats.host_stats.latest_event_timestamp);
});
double average_bwe =
std::accumulate(frame_stats_.begin() + warm_up_frames,
frame_stats_.begin() + warm_up_frames + num_frames, 0.0,
[](double sum, const protocol::FrameStats& stats) {
return sum + stats.host_stats.bandwidth_estimate_kbps;
}) /
num_frames;
VLOG(0) << "FPS: " << num_frames / total_time.InSecondsF();
VLOG(0) << "Average latency: " << latency_sum.InMillisecondsF() / num_frames
<< " ms";
VLOG(0) << "Total size: " << total_size << " bytes";
VLOG(0) << "Bandwidth utilization: "
<< 100 * total_size / (total_time.InSecondsF() *
GetParam().bandwidth_kbps * 1000 / 8)
<< "%";
VLOG(0) << "Network buffer delay (bufferbloat), average: "
<< client_socket_factory_->average_buffer_delay().InMilliseconds()
<< " ms, max:"
<< client_socket_factory_->max_buffer_delay().InMilliseconds()
<< " ms";
VLOG(0) << "Packet drop rate: " << client_socket_factory_->drop_rate();
VLOG(0) << "Average BW estimate: " << average_bwe
<< " (actual: " << GetParam().bandwidth_kbps << ")";
}
TEST_P(ProtocolPerfTest, ScrollPerformanceIce) {
MeasureScrollPerformance(false);
}
TEST_P(ProtocolPerfTest, ScrollPerformanceWebrtc) {
MeasureScrollPerformance(true);
}
} // namespace remoting