blob: 3db0b2e5c93bfc7f7bbb700e0b73123e47666c48 [file] [log] [blame]
// Copyright 2015 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 <utility>
#include "base/bind.h"
#include "base/macros.h"
#include "base/memory/ptr_util.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "remoting/base/constants.h"
#include "remoting/protocol/fake_session.h"
#include "remoting/protocol/fake_video_renderer.h"
#include "remoting/protocol/ice_connection_to_client.h"
#include "remoting/protocol/ice_connection_to_host.h"
#include "remoting/protocol/protocol_mock_objects.h"
#include "remoting/protocol/transport_context.h"
#include "remoting/protocol/video_stream.h"
#include "remoting/protocol/webrtc_connection_to_client.h"
#include "remoting/protocol/webrtc_connection_to_host.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_capturer.h"
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
using ::testing::_;
using ::testing::InvokeWithoutArgs;
using ::testing::NotNull;
using ::testing::StrictMock;
namespace remoting {
namespace protocol {
namespace {
MATCHER_P(EqualsCapabilitiesMessage, message, "") {
return arg.capabilities() == message.capabilities();
}
MATCHER_P(EqualsKeyEvent, event, "") {
return arg.usb_keycode() == event.usb_keycode() &&
arg.pressed() == event.pressed();
}
ACTION_P(QuitRunLoop, run_loop) {
run_loop->Quit();
}
class MockConnectionToHostEventCallback
: public ConnectionToHost::HostEventCallback {
public:
MockConnectionToHostEventCallback() {}
~MockConnectionToHostEventCallback() override {}
MOCK_METHOD2(OnConnectionState,
void(ConnectionToHost::State state, ErrorCode error));
MOCK_METHOD1(OnConnectionReady, void(bool ready));
MOCK_METHOD2(OnRouteChanged,
void(const std::string& channel_name,
const TransportRoute& route));
};
class TestScreenCapturer : public webrtc::DesktopCapturer {
public:
TestScreenCapturer() {}
~TestScreenCapturer() override {}
// webrtc::DesktopCapturer interface.
void Start(Callback* callback) override {
callback_ = callback;
}
void Capture(const webrtc::DesktopRegion& region) override {
// Return black 100x100 frame.
std::unique_ptr<webrtc::DesktopFrame> frame(
new webrtc::BasicDesktopFrame(webrtc::DesktopSize(100, 100)));
memset(frame->data(), 0, frame->stride() * frame->size().height());
// Set updated_region only for the first frame, as the frame content
// doesn't change.
if (!first_frame_sent_) {
first_frame_sent_ = true;
frame->mutable_updated_region()->SetRect(
webrtc::DesktopRect::MakeSize(frame->size()));
}
callback_->OnCaptureResult(webrtc::DesktopCapturer::Result::SUCCESS,
std::move(frame));
}
private:
Callback* callback_ = nullptr;
bool first_frame_sent_ = false;
};
} // namespace
class ConnectionTest : public testing::Test,
public testing::WithParamInterface<bool> {
public:
ConnectionTest() {}
void DestroyHost() {
host_connection_.reset();
run_loop_->Quit();
}
protected:
bool is_using_webrtc() { return GetParam(); }
void SetUp() override {
// Create fake sessions.
host_session_ = new FakeSession();
owned_client_session_.reset(new FakeSession());
client_session_ = owned_client_session_.get();
// Create Connection objects.
if (is_using_webrtc()) {
host_connection_.reset(new WebrtcConnectionToClient(
base::WrapUnique(host_session_),
TransportContext::ForTests(protocol::TransportRole::SERVER),
message_loop_.task_runner()));
client_connection_.reset(new WebrtcConnectionToHost());
} else {
host_connection_.reset(new IceConnectionToClient(
base::WrapUnique(host_session_),
TransportContext::ForTests(protocol::TransportRole::SERVER),
message_loop_.task_runner(), message_loop_.task_runner()));
client_connection_.reset(new IceConnectionToHost());
}
// Setup host side.
host_connection_->SetEventHandler(&host_event_handler_);
host_connection_->set_clipboard_stub(&host_clipboard_stub_);
host_connection_->set_host_stub(&host_stub_);
host_connection_->set_input_stub(&host_input_stub_);
// Setup client side.
client_connection_->set_client_stub(&client_stub_);
client_connection_->set_clipboard_stub(&client_clipboard_stub_);
client_connection_->set_video_renderer(&client_video_renderer_);
}
void Connect() {
{
testing::InSequence sequence;
EXPECT_CALL(host_event_handler_, OnConnectionAuthenticating());
EXPECT_CALL(host_event_handler_, OnConnectionAuthenticated());
}
EXPECT_CALL(host_event_handler_, OnConnectionChannelsConnected())
.WillOnce(InvokeWithoutArgs(this, &ConnectionTest::OnHostConnected));
EXPECT_CALL(host_event_handler_, OnRouteChange(_, _))
.Times(testing::AnyNumber());
{
testing::InSequence sequence;
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::CONNECTING, OK));
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::AUTHENTICATED, OK));
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::CONNECTED, OK))
.WillOnce(InvokeWithoutArgs(
this, &ConnectionTest::OnClientConnected));
}
EXPECT_CALL(client_event_handler_, OnRouteChanged(_, _))
.Times(testing::AnyNumber());
client_connection_->Connect(
std::move(owned_client_session_),
TransportContext::ForTests(protocol::TransportRole::CLIENT),
&client_event_handler_);
client_session_->SimulateConnection(host_session_);
run_loop_.reset(new base::RunLoop());
run_loop_->Run();
EXPECT_TRUE(client_connected_);
EXPECT_TRUE(host_connected_);
}
void TearDown() override {
client_connection_.reset();
host_connection_.reset();
base::RunLoop().RunUntilIdle();
}
void OnHostConnected() {
host_connected_ = true;
if (client_connected_ && run_loop_)
run_loop_->Quit();
}
void OnClientConnected() {
client_connected_ = true;
if (host_connected_ && run_loop_)
run_loop_->Quit();
}
void WaitFirstVideoFrame() {
base::RunLoop run_loop;
// Expect frames to be passed to FrameConsumer when WebRTC is used, or to
// VideoStub otherwise.
if (is_using_webrtc()) {
client_video_renderer_.GetFrameConsumer()->set_on_frame_callback(
base::Bind(&base::RunLoop::Quit, base::Unretained(&run_loop)));
} else {
client_video_renderer_.GetVideoStub()->set_on_frame_callback(
base::Bind(&base::RunLoop::Quit, base::Unretained(&run_loop)));
}
run_loop.Run();
if (is_using_webrtc()) {
EXPECT_EQ(
client_video_renderer_.GetFrameConsumer()->received_frames().size(),
1U);
EXPECT_EQ(
client_video_renderer_.GetVideoStub()->received_packets().size(), 0U);
client_video_renderer_.GetFrameConsumer()->set_on_frame_callback(
base::Closure());
} else {
EXPECT_EQ(
client_video_renderer_.GetFrameConsumer()->received_frames().size(),
0U);
EXPECT_EQ(
client_video_renderer_.GetVideoStub()->received_packets().size(), 1U);
client_video_renderer_.GetVideoStub()->set_on_frame_callback(
base::Closure());
}
}
void WaitFirstFrameStats() {
if (!client_video_renderer_.GetFrameStatsConsumer()
->received_stats()
.empty()) {
return;
}
base::RunLoop run_loop;
client_video_renderer_.GetFrameStatsConsumer()->set_on_stats_callback(
base::Bind(&base::RunLoop::Quit, base::Unretained(&run_loop)));
run_loop.Run();
client_video_renderer_.GetFrameStatsConsumer()->set_on_stats_callback(
base::Closure());
EXPECT_FALSE(client_video_renderer_.GetFrameStatsConsumer()
->received_stats()
.empty());
}
base::MessageLoopForIO message_loop_;
std::unique_ptr<base::RunLoop> run_loop_;
MockConnectionToClientEventHandler host_event_handler_;
MockClipboardStub host_clipboard_stub_;
MockHostStub host_stub_;
MockInputStub host_input_stub_;
std::unique_ptr<ConnectionToClient> host_connection_;
FakeSession* host_session_; // Owned by |host_connection_|.
bool host_connected_ = false;
MockConnectionToHostEventCallback client_event_handler_;
MockClientStub client_stub_;
MockClipboardStub client_clipboard_stub_;
FakeVideoRenderer client_video_renderer_;
std::unique_ptr<ConnectionToHost> client_connection_;
FakeSession* client_session_; // Owned by |client_connection_|.
std::unique_ptr<FakeSession> owned_client_session_;
bool client_connected_ = false;
private:
DISALLOW_COPY_AND_ASSIGN(ConnectionTest);
};
INSTANTIATE_TEST_CASE_P(Ice, ConnectionTest, ::testing::Values(false));
INSTANTIATE_TEST_CASE_P(Webrtc, ConnectionTest, ::testing::Values(true));
TEST_P(ConnectionTest, RejectConnection) {
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::CONNECTING, OK));
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::CLOSED, OK));
client_connection_->Connect(
std::move(owned_client_session_),
TransportContext::ForTests(protocol::TransportRole::CLIENT),
&client_event_handler_);
client_session_->event_handler()->OnSessionStateChange(Session::CLOSED);
}
TEST_P(ConnectionTest, Disconnect) {
Connect();
EXPECT_CALL(client_event_handler_,
OnConnectionState(ConnectionToHost::CLOSED, OK));
EXPECT_CALL(host_event_handler_, OnConnectionClosed(OK));
client_session_->Close(OK);
base::RunLoop().RunUntilIdle();
}
TEST_P(ConnectionTest, Control) {
Connect();
Capabilities capabilities_msg;
capabilities_msg.set_capabilities("test_capability");
base::RunLoop run_loop;
EXPECT_CALL(client_stub_,
SetCapabilities(EqualsCapabilitiesMessage(capabilities_msg)))
.WillOnce(QuitRunLoop(&run_loop));
// Send capabilities from the host.
host_connection_->client_stub()->SetCapabilities(capabilities_msg);
run_loop.Run();
}
TEST_P(ConnectionTest, Events) {
Connect();
KeyEvent event;
event.set_usb_keycode(3);
event.set_pressed(true);
base::RunLoop run_loop;
EXPECT_CALL(host_event_handler_, OnInputEventReceived(_));
EXPECT_CALL(host_input_stub_, InjectKeyEvent(EqualsKeyEvent(event)))
.WillOnce(QuitRunLoop(&run_loop));
// Send key event from the client.
client_connection_->input_stub()->InjectKeyEvent(event);
run_loop.Run();
}
TEST_P(ConnectionTest, Video) {
Connect();
std::unique_ptr<VideoStream> video_stream =
host_connection_->StartVideoStream(
base::MakeUnique<TestScreenCapturer>());
WaitFirstVideoFrame();
}
// Verifies that the VideoStream doesn't loose any video frames while the
// connection is being established.
TEST_P(ConnectionTest, VideoWithSlowSignaling) {
// Add signaling delay to slow down connection handshake.
host_session_->set_signaling_delay(base::TimeDelta::FromMilliseconds(100));
client_session_->set_signaling_delay(base::TimeDelta::FromMilliseconds(100));
Connect();
std::unique_ptr<VideoStream> video_stream =
host_connection_->StartVideoStream(
base::WrapUnique(new TestScreenCapturer()));
WaitFirstVideoFrame();
}
TEST_P(ConnectionTest, DestroyOnIncomingMessage) {
Connect();
KeyEvent event;
event.set_usb_keycode(3);
event.set_pressed(true);
base::RunLoop run_loop;
EXPECT_CALL(host_event_handler_, OnInputEventReceived(_));
EXPECT_CALL(host_input_stub_, InjectKeyEvent(EqualsKeyEvent(event)))
.WillOnce(DoAll(InvokeWithoutArgs(this, &ConnectionTest::DestroyHost),
QuitRunLoop(&run_loop)));
// Send key event from the client.
client_connection_->input_stub()->InjectKeyEvent(event);
run_loop.Run();
}
TEST_P(ConnectionTest, VideoStats) {
// Currently this test only works for WebRTC because for ICE connections stats
// are reported by SoftwareVideoRenderer which is not used in this test.
// TODO(sergeyu): Fix this.
if (!is_using_webrtc())
return;
Connect();
base::TimeTicks start_time = base::TimeTicks::Now();
std::unique_ptr<VideoStream> video_stream =
host_connection_->StartVideoStream(
base::MakeUnique<TestScreenCapturer>());
// Simulate an input invent injected at the start.
video_stream->OnInputEventReceived(start_time.ToInternalValue());
WaitFirstVideoFrame();
base::TimeTicks finish_time = base::TimeTicks::Now();
WaitFirstFrameStats();
const FrameStats& stats =
client_video_renderer_.GetFrameStatsConsumer()->received_stats().front();
EXPECT_TRUE(stats.host_stats.frame_size > 0);
EXPECT_TRUE(stats.host_stats.latest_event_timestamp == start_time);
EXPECT_TRUE(stats.host_stats.capture_delay != base::TimeDelta::Max());
EXPECT_TRUE(stats.host_stats.capture_overhead_delay !=
base::TimeDelta::Max());
EXPECT_TRUE(stats.host_stats.encode_delay != base::TimeDelta::Max());
EXPECT_TRUE(stats.host_stats.send_pending_delay != base::TimeDelta::Max());
EXPECT_FALSE(stats.client_stats.time_received.is_null());
EXPECT_FALSE(stats.client_stats.time_decoded.is_null());
EXPECT_FALSE(stats.client_stats.time_rendered.is_null());
EXPECT_TRUE(start_time + stats.host_stats.capture_pending_delay +
stats.host_stats.capture_delay +
stats.host_stats.capture_overhead_delay +
stats.host_stats.encode_delay +
stats.host_stats.send_pending_delay <=
stats.client_stats.time_received);
EXPECT_TRUE(stats.client_stats.time_received <=
stats.client_stats.time_decoded);
EXPECT_TRUE(stats.client_stats.time_decoded <=
stats.client_stats.time_rendered);
EXPECT_TRUE(stats.client_stats.time_rendered <= finish_time);
}
} // namespace protocol
} // namespace remoting