Index: remoting/host/client_session_unittest.cc |
diff --git a/remoting/host/client_session_unittest.cc b/remoting/host/client_session_unittest.cc |
index f28050a9a43fab6da148ac0752f2736c928c3826..d9127dc2f427f91946aadc893dc25015f72384d9 100644 |
--- a/remoting/host/client_session_unittest.cc |
+++ b/remoting/host/client_session_unittest.cc |
@@ -2,17 +2,24 @@ |
// Use of this source code is governed by a BSD-style license that can be |
// found in the LICENSE file. |
+#include <algorithm> |
+#include <string> |
+#include <vector> |
+ |
#include "base/message_loop/message_loop.h" |
+#include "base/strings/string_util.h" |
#include "base/test/test_simple_task_runner.h" |
#include "remoting/base/auto_thread_task_runner.h" |
#include "remoting/base/constants.h" |
#include "remoting/host/audio_capturer.h" |
#include "remoting/host/client_session.h" |
#include "remoting/host/desktop_environment.h" |
+#include "remoting/host/host_extension.h" |
#include "remoting/host/host_mock_objects.h" |
#include "remoting/host/screen_capturer_fake.h" |
#include "remoting/protocol/protocol_mock_objects.h" |
#include "testing/gmock/include/gmock/gmock-matchers.h" |
+#include "testing/gmock_mutant.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" |
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
@@ -31,9 +38,11 @@ using protocol::SessionConfig; |
using testing::_; |
using testing::AnyNumber; |
using testing::AtMost; |
+using testing::CreateFunctor; |
using testing::DeleteArg; |
using testing::DoAll; |
using testing::Expectation; |
+using testing::Invoke; |
using testing::Return; |
using testing::ReturnRef; |
using testing::Sequence; |
@@ -42,6 +51,8 @@ using testing::StrictMock; |
namespace { |
+const char kDefaultTestCapability[] = "default"; |
+ |
ACTION_P2(InjectClipboardEvent, connection, event) { |
connection->clipboard_stub()->InjectClipboardEvent(event); |
} |
@@ -67,8 +78,75 @@ ACTION_P2(DeliverClientMessage, client_session, message) { |
client_session->DeliverClientMessage(message); |
} |
+ACTION_P2(AddHostCapabilities, client_session, capability) { |
+ client_session->AddHostCapabilities(capability); |
} |
+// Matches a |protocol::Capabilities| argument against a list of capabilities |
+// formatted as a space-separated string. |
+MATCHER_P(EqCapabilities, expected_capabilities, "") { |
+ if (!arg.has_capabilities()) |
+ return false; |
+ |
+ std::vector<std::string> words_args; |
+ std::vector<std::string> words_expected; |
+ Tokenize(arg.capabilities(), " ", &words_args); |
+ Tokenize(expected_capabilities, " ", &words_expected); |
+ std::sort(words_args.begin(), words_args.end()); |
+ std::sort(words_expected.begin(), words_expected.end()); |
+ return words_args == words_expected; |
+} |
+ |
+// |HostExtension| implementation that can handle an extension message type and |
+// provide capabilities. |
+class FakeExtension : public HostExtension { |
+ public: |
+ FakeExtension(const std::string& message_type, |
+ const std::string& capabilities); |
+ virtual ~FakeExtension(); |
+ |
+ virtual std::string GetCapabilities() OVERRIDE; |
+ virtual scoped_ptr<HostExtensionSession> CreateExtensionSession( |
+ ClientSession* client_session) OVERRIDE; |
+ |
+ bool message_handled() { |
+ return message_handled_; |
+ } |
+ |
+ private: |
+ class FakeExtensionSession : public HostExtensionSession { |
+ public: |
+ FakeExtensionSession(FakeExtension* extension); |
+ virtual ~FakeExtensionSession(); |
+ |
+ virtual bool OnExtensionMessage( |
+ ClientSession* client_session, |
+ const protocol::ExtensionMessage& message) OVERRIDE; |
+ |
+ private: |
+ FakeExtension* extension_; |
+ }; |
+ |
+ std::string message_type_; |
+ std::string capabilities_; |
+ bool message_handled_; |
+}; |
+ |
+typedef std::vector<HostExtension*> HostExtensionList; |
+ |
+void CreateExtensionSessions(const HostExtensionList& extensions, |
+ ClientSession* client_session) { |
+ for (HostExtensionList::const_iterator extension = extensions.begin(); |
+ extension != extensions.end(); ++extension) { |
+ scoped_ptr<HostExtensionSession> extension_session = |
+ (*extension)->CreateExtensionSession(client_session); |
+ if (extension_session) |
+ client_session->AddExtensionSession(extension_session.Pass()); |
+ } |
+} |
+ |
+} // namespace |
+ |
class ClientSessionTest : public testing::Test { |
public: |
ClientSessionTest() : client_jid_("user@domain/rest-of-jid") {} |
@@ -100,6 +178,10 @@ class ClientSessionTest : public testing::Test { |
// the input pipe line and starts video capturing. |
void ConnectClientSession(); |
+ // Creates expectations to send an extension message and to disconnect |
+ // afterwards. |
+ void SendExtensionMessageAndDisconnect(const std::string& message_type); |
+ |
// Invoked when the last reference to the AutoThreadTaskRunner has been |
// released and quits the message loop to finish the test. |
void QuitMainMessageLoop(); |
@@ -131,6 +213,41 @@ class ClientSessionTest : public testing::Test { |
scoped_ptr<MockDesktopEnvironmentFactory> desktop_environment_factory_; |
}; |
+FakeExtension::FakeExtension(const std::string& message_type, |
+ const std::string& capabilities) |
+ : message_type_(message_type), |
+ capabilities_(capabilities), |
+ message_handled_(false) { |
+} |
+ |
+FakeExtension::~FakeExtension() {} |
+ |
+std::string FakeExtension::GetCapabilities() { |
+ return capabilities_; |
+} |
+ |
+scoped_ptr<HostExtensionSession> FakeExtension::CreateExtensionSession( |
+ ClientSession* client_session) { |
+ return scoped_ptr<HostExtensionSession>(new FakeExtensionSession(this)); |
+} |
+ |
+FakeExtension::FakeExtensionSession::FakeExtensionSession( |
+ FakeExtension* extension) |
+ : extension_(extension) { |
+} |
+ |
+FakeExtension::FakeExtensionSession::~FakeExtensionSession() {} |
+ |
+bool FakeExtension::FakeExtensionSession::OnExtensionMessage( |
+ ClientSession* client_session, |
+ const protocol::ExtensionMessage& message) { |
+ if (message.type() == extension_->message_type_) { |
+ extension_->message_handled_ = true; |
+ return true; |
+ } |
+ return false; |
+} |
+ |
void ClientSessionTest::SetUp() { |
// Arrange to run |message_loop_| until no components depend on it. |
scoped_refptr<AutoThreadTaskRunner> ui_task_runner = new AutoThreadTaskRunner( |
@@ -180,6 +297,11 @@ void ClientSessionTest::SetUp() { |
desktop_environment_factory_.get(), |
base::TimeDelta(), |
NULL)); |
+ |
+ // By default, client will report the same capabilities as the host. |
+ EXPECT_CALL(client_stub_, SetCapabilities(_)) |
+ .Times(AtMost(1)) |
+ .WillOnce(Invoke(client_session_.get(), &ClientSession::SetCapabilities)); |
} |
void ClientSessionTest::TearDown() { |
@@ -211,7 +333,8 @@ DesktopEnvironment* ClientSessionTest::CreateDesktopEnvironment() { |
EXPECT_CALL(*desktop_environment, CreateVideoCapturerPtr()) |
.WillOnce(Invoke(this, &ClientSessionTest::CreateVideoCapturer)); |
EXPECT_CALL(*desktop_environment, GetCapabilities()) |
- .Times(AtMost(1)); |
+ .Times(AtMost(1)) |
+ .WillOnce(Return(kDefaultTestCapability)); |
EXPECT_CALL(*desktop_environment, SetCapabilities(_)) |
.Times(AtMost(1)); |
@@ -232,6 +355,23 @@ void ClientSessionTest::ConnectClientSession() { |
client_session_->OnConnectionChannelsConnected(client_session_->connection()); |
} |
+void ClientSessionTest::SendExtensionMessageAndDisconnect( |
Wez
2014/05/28 23:03:41
Sorry, this was not a good name for me to suggest.
dcaiafa
2014/05/29 00:03:16
Done.
|
+ const std::string& message_type) { |
+ protocol::ExtensionMessage message; |
+ message.set_type(message_type); |
+ message.set_data("data"); |
+ |
+ Expectation authenticated = |
+ EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_)) |
+ .WillOnce(Return(true)); |
+ EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_)) |
+ .After(authenticated) |
+ .WillOnce(DoAll( |
+ DeliverClientMessage(client_session_.get(), message), |
+ InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession), |
+ InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession))); |
+} |
+ |
void ClientSessionTest::QuitMainMessageLoop() { |
message_loop_.PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); |
} |
@@ -599,4 +739,63 @@ TEST_F(ClientSessionTest, EnableGnubbyAuth) { |
message_loop_.Run(); |
} |
+// Verifies that messages can be handled by extensions. |
+TEST_F(ClientSessionTest, ExtensionMessages_MessageHandled) { |
+ FakeExtension extension1("ext1", "cap1"); |
+ FakeExtension extension2("ext2", "cap2"); |
+ FakeExtension extension3("ext3", "cap3"); |
+ HostExtensionList extensions; |
+ extensions.push_back(&extension1); |
+ extensions.push_back(&extension2); |
+ extensions.push_back(&extension3); |
+ |
+ EXPECT_CALL(session_event_handler_, OnClientCapabilities(_)) |
+ .WillOnce(Invoke(CreateFunctor(&CreateExtensionSessions, extensions))); |
+ |
+ SendExtensionMessageAndDisconnect("ext2"); |
+ ConnectClientSession(); |
+ message_loop_.Run(); |
+ |
+ EXPECT_FALSE(extension1.message_handled()); |
+ EXPECT_TRUE(extension2.message_handled()); |
+ EXPECT_FALSE(extension3.message_handled()); |
+} |
+ |
+// Verifies that extension messages not handled by extensions don't result in a |
+// crash. |
+TEST_F(ClientSessionTest, ExtensionMessages_MessageNotHandled) { |
+ FakeExtension extension1("ext1", "cap1"); |
+ HostExtensionList extensions; |
+ extensions.push_back(&extension1); |
+ |
+ EXPECT_CALL(session_event_handler_, OnClientCapabilities(_)) |
+ .WillOnce(Invoke(CreateFunctor(&CreateExtensionSessions, extensions))); |
+ |
+ SendExtensionMessageAndDisconnect("extX"); |
+ ConnectClientSession(); |
+ message_loop_.Run(); |
+ |
+ EXPECT_FALSE(extension1.message_handled()); |
+} |
+ |
+TEST_F(ClientSessionTest, ReportCapabilities) { |
+ Expectation authenticated = |
+ EXPECT_CALL(session_event_handler_, OnSessionAuthenticated(_)) |
+ .WillOnce(DoAll( |
+ AddHostCapabilities(client_session_.get(), "capX capZ"), |
+ AddHostCapabilities(client_session_.get(), ""), |
+ AddHostCapabilities(client_session_.get(), "capY"), |
+ Return(true))); |
+ EXPECT_CALL(client_stub_, |
+ SetCapabilities(EqCapabilities("capX capY capZ default"))); |
+ EXPECT_CALL(session_event_handler_, OnSessionChannelsConnected(_)) |
+ .After(authenticated) |
+ .WillOnce(DoAll( |
+ InvokeWithoutArgs(this, &ClientSessionTest::DisconnectClientSession), |
+ InvokeWithoutArgs(this, &ClientSessionTest::StopClientSession))); |
+ |
+ ConnectClientSession(); |
+ message_loop_.Run(); |
+} |
+ |
} // namespace remoting |