diff options
author | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-01 20:54:32 +0000 |
---|---|---|
committer | sergeyu@chromium.org <sergeyu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-01 20:54:32 +0000 |
commit | 10c94b3a6c2d47dbcb3be355dcbcaae0f8b024c6 (patch) | |
tree | 43da6baae2611225bf46bc65367dfc63fe895b43 /remoting | |
parent | e326bb9ab32dea06c9419ee774a9a893fb02da52 (diff) | |
download | chromium_src-10c94b3a6c2d47dbcb3be355dcbcaae0f8b024c6.zip chromium_src-10c94b3a6c2d47dbcb3be355dcbcaae0f8b024c6.tar.gz chromium_src-10c94b3a6c2d47dbcb3be355dcbcaae0f8b024c6.tar.bz2 |
Unittests for chromotocol code.
BUG=53986
TEST=Unittests FTW!
Review URL: http://codereview.chromium.org/3407007
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@61228 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/jingle_glue/channel_socket_adapter_unittest.cc | 121 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_channel_unittest.cc | 16 | ||||
-rw-r--r-- | remoting/jingle_glue/jingle_client.cc | 2 | ||||
-rw-r--r-- | remoting/jingle_glue/mock_objects.h | 18 | ||||
-rw-r--r-- | remoting/jingle_glue/stream_socket_adapter_unittest.cc | 151 | ||||
-rw-r--r-- | remoting/protocol/jingle_chromoting_connection.cc | 14 | ||||
-rw-r--r-- | remoting/protocol/jingle_chromoting_connection_unittest.cc | 579 | ||||
-rw-r--r-- | remoting/protocol/jingle_chromoting_server.cc | 7 | ||||
-rw-r--r-- | remoting/protocol/jingle_chromoting_server.h | 6 | ||||
-rw-r--r-- | remoting/protocol/session_manager_pair.cc | 91 | ||||
-rw-r--r-- | remoting/protocol/session_manager_pair.h | 70 | ||||
-rw-r--r-- | remoting/remoting.gyp | 6 |
12 files changed, 1057 insertions, 24 deletions
diff --git a/remoting/jingle_glue/channel_socket_adapter_unittest.cc b/remoting/jingle_glue/channel_socket_adapter_unittest.cc new file mode 100644 index 0000000..ac2c591 --- /dev/null +++ b/remoting/jingle_glue/channel_socket_adapter_unittest.cc @@ -0,0 +1,121 @@ +// Copyright (c) 2010 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 "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/socket/socket.h" +#include "remoting/jingle_glue/channel_socket_adapter.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/p2p/base/transportchannel.h" + +using net::IOBuffer; + +using testing::_; +using testing::Return; + +namespace remoting { + +namespace { +const int kBufferSize = 4096; +const char kTestData[] = "data"; +const int kTestDataSize = 4; +const int kTestError = -32123; +} // namespace + +class MockTransportChannel : public cricket::TransportChannel { + public: + MockTransportChannel() + : cricket::TransportChannel("", "") { + } + + MOCK_METHOD2(SendPacket, int(const char *data, size_t len)); + MOCK_METHOD2(SetOption, int(talk_base::Socket::Option opt, int value)); + MOCK_METHOD0(GetError, int()); +}; + +class TransportChannelSocketAdapterTest : public testing::Test { + public: + TransportChannelSocketAdapterTest() + : ALLOW_THIS_IN_INITIALIZER_LIST( + callback_(this, &TransportChannelSocketAdapterTest::Callback)), + callback_result_(0) { + } + + protected: + virtual void SetUp() { + target_.reset(new TransportChannelSocketAdapter(&channel_)); + } + + void Callback(int result) { + callback_result_ = result; + } + + MockTransportChannel channel_; + scoped_ptr<TransportChannelSocketAdapter> target_; + net::CompletionCallbackImpl<TransportChannelSocketAdapterTest> callback_; + int callback_result_; + MessageLoopForIO message_loop_; +}; + +// Verify that Read() returns net::ERR_IO_PENDING. +TEST_F(TransportChannelSocketAdapterTest, Read) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kBufferSize); + + int result = target_->Read(buffer, kBufferSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + channel_.SignalReadPacket(&channel_, kTestData, kTestDataSize); + EXPECT_EQ(kTestDataSize, callback_result_); +} + +// Verify that Read() after Close() returns error. +TEST_F(TransportChannelSocketAdapterTest, ReadClose) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kBufferSize); + + int result = target_->Read(buffer, kBufferSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + target_->Close(kTestError); + EXPECT_EQ(kTestError, callback_result_); + + // All Read() calls after Close() should return the error. + EXPECT_EQ(kTestError, target_->Read(buffer, kBufferSize, &callback_)); +} + +// Verify that Write sends the packet and returns correct result. +TEST_F(TransportChannelSocketAdapterTest, Write) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kTestDataSize); + + EXPECT_CALL(channel_, SendPacket(buffer->data(), kTestDataSize)) + .WillOnce(Return(kTestDataSize)); + + int result = target_->Write(buffer, kTestDataSize, &callback_); + EXPECT_EQ(kTestDataSize, result); +} + +// Verify that the message is still send if Write() is called while +// socket is not open yet, and that the callback is called. +TEST_F(TransportChannelSocketAdapterTest, WritePending) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kTestDataSize); + + EXPECT_CALL(channel_, SendPacket(buffer->data(), kTestDataSize)) + .Times(2) + .WillOnce(Return(SOCKET_ERROR)) + .WillOnce(Return(kTestDataSize)); + + EXPECT_CALL(channel_, GetError()) + .WillOnce(Return(EWOULDBLOCK)); + + int result = target_->Write(buffer, kTestDataSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + channel_.SignalWritableState(&channel_); + EXPECT_EQ(kTestDataSize, callback_result_); +} + +} // namespace remoting diff --git a/remoting/jingle_glue/jingle_channel_unittest.cc b/remoting/jingle_glue/jingle_channel_unittest.cc index 175d69c..97b5a17 100644 --- a/remoting/jingle_glue/jingle_channel_unittest.cc +++ b/remoting/jingle_glue/jingle_channel_unittest.cc @@ -7,6 +7,7 @@ #include "media/base/data_buffer.h" #include "remoting/jingle_glue/jingle_channel.h" #include "remoting/jingle_glue/jingle_thread.h" +#include "remoting/jingle_glue/mock_objects.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "third_party/libjingle/source/talk/base/stream.h" @@ -30,21 +31,6 @@ class MockJingleChannelCallback : public JingleChannel::Callback { scoped_refptr<media::DataBuffer>)); }; -class MockStream : public talk_base::StreamInterface { - public: - virtual ~MockStream() {} - MOCK_CONST_METHOD0(GetState, talk_base::StreamState()); - - MOCK_METHOD4(Read, talk_base::StreamResult(void*, size_t, size_t*, int*)); - MOCK_METHOD4(Write, talk_base::StreamResult(const void*, size_t, - size_t*, int*)); - MOCK_CONST_METHOD1(GetAvailable, bool(size_t*)); - MOCK_METHOD0(Close, void()); - - MOCK_METHOD3(PostEvent, void(talk_base::Thread*, int, int)); - MOCK_METHOD2(PostEvent, void(int, int)); -}; - class JingleChannelTest : public testing::Test { public: virtual ~JingleChannelTest() { } diff --git a/remoting/jingle_glue/jingle_client.cc b/remoting/jingle_glue/jingle_client.cc index c6206f7..e371850 100644 --- a/remoting/jingle_glue/jingle_client.cc +++ b/remoting/jingle_glue/jingle_client.cc @@ -35,7 +35,7 @@ class LocalTunnelSessionClient : public cricket::TunnelSessionClient { virtual cricket::TunnelSession* MakeTunnelSession( cricket::Session* session, talk_base::Thread* stream_thread, cricket::TunnelSessionRole role) { - session->transport()->set_allow_local_ips(true); + session->set_allow_local_ips(true); return new cricket::TunnelSession(this, session, stream_thread); } }; diff --git a/remoting/jingle_glue/mock_objects.h b/remoting/jingle_glue/mock_objects.h index c1a1e26..0698ca6 100644 --- a/remoting/jingle_glue/mock_objects.h +++ b/remoting/jingle_glue/mock_objects.h @@ -5,8 +5,10 @@ #ifndef REMOTING_JINGLE_GLUE_MOCK_OBJECTS_H_ #define REMOTING_JINGLE_GLUE_MOCK_OBJECTS_H_ +#include "media/base/data_buffer.h" #include "remoting/jingle_glue/jingle_channel.h" #include "testing/gmock/include/gmock/gmock.h" +#include "third_party/libjingle/source/talk/base/stream.h" namespace remoting { @@ -21,6 +23,22 @@ class MockJingleChannel : public JingleChannel { DISALLOW_COPY_AND_ASSIGN(MockJingleChannel); }; +class MockStream : public talk_base::StreamInterface { + public: + virtual ~MockStream() { } + + MOCK_CONST_METHOD0(GetState, talk_base::StreamState()); + + MOCK_METHOD4(Read, talk_base::StreamResult(void*, size_t, size_t*, int*)); + MOCK_METHOD4(Write, talk_base::StreamResult(const void*, size_t, + size_t*, int*)); + MOCK_CONST_METHOD1(GetAvailable, bool(size_t*)); + MOCK_METHOD0(Close, void()); + + MOCK_METHOD3(PostEvent, void(talk_base::Thread*, int, int)); + MOCK_METHOD2(PostEvent, void(int, int)); +}; + } // namespace remoting #endif // REMOTING_JINGLE_GLUE_MOCK_OBJECTS_H_ diff --git a/remoting/jingle_glue/stream_socket_adapter_unittest.cc b/remoting/jingle_glue/stream_socket_adapter_unittest.cc new file mode 100644 index 0000000..7d1dd62 --- /dev/null +++ b/remoting/jingle_glue/stream_socket_adapter_unittest.cc @@ -0,0 +1,151 @@ +// Copyright (c) 2010 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 "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/socket/socket.h" +#include "remoting/jingle_glue/stream_socket_adapter.h" +#include "remoting/jingle_glue/mock_objects.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/p2p/base/transportchannel.h" + +using net::IOBuffer; + +using testing::_; +using testing::Return; +using testing::SetArgumentPointee; + +namespace remoting { + +namespace { +const int kBufferSize = 4096; +const int kTestDataSize = 4; +const int kTestError = -32123; +} // namespace + +class StreamSocketAdapterTest : public testing::Test { + public: + StreamSocketAdapterTest() + : ALLOW_THIS_IN_INITIALIZER_LIST( + callback_(this, &StreamSocketAdapterTest::Callback)), + callback_result_(0) { + stream_ = new MockStream(); + target_.reset(new StreamSocketAdapter(stream_)); + } + + protected: + void Callback(int result) { + callback_result_ = result; + } + + // |stream_| must be allocated on the heap, because StreamSocketAdapter + // owns the object and it will free it in the end. + MockStream* stream_; + scoped_ptr<StreamSocketAdapter> target_; + net::CompletionCallbackImpl<StreamSocketAdapterTest> callback_; + int callback_result_; + MessageLoopForIO message_loop_; +}; + +// Verify that Read() calls Read() in stream. +TEST_F(StreamSocketAdapterTest, Read) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kBufferSize); + + EXPECT_CALL(*stream_, Read(buffer->data(), kBufferSize, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(kTestDataSize), + Return(talk_base::SR_SUCCESS))); + + int result = target_->Read(buffer, kBufferSize, &callback_); + EXPECT_EQ(kTestDataSize, result); + EXPECT_EQ(0, callback_result_); +} + +// Verify that read callback is called for pending reads. +TEST_F(StreamSocketAdapterTest, ReadPending) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kBufferSize); + + EXPECT_CALL(*stream_, Read(buffer->data(), kBufferSize, _, _)) + .Times(2) + .WillOnce(Return(talk_base::SR_BLOCK)) + .WillOnce(DoAll(SetArgumentPointee<2>(kTestDataSize), + Return(talk_base::SR_SUCCESS))); + + int result = target_->Read(buffer, kBufferSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + stream_->SignalEvent(stream_, talk_base::SE_READ, 0); + EXPECT_EQ(kTestDataSize, callback_result_); +} + +// Verify that Read() returns error after Close(). +TEST_F(StreamSocketAdapterTest, ReadClose) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kBufferSize); + + EXPECT_CALL(*stream_, Read(buffer->data(), kBufferSize, _, _)) + .WillOnce(Return(talk_base::SR_BLOCK)); + + int result = target_->Read(buffer, kBufferSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + EXPECT_CALL(*stream_, Close()); + target_->Close(kTestError); + EXPECT_EQ(kTestError, callback_result_); + + // All Read() calls after Close() should return the error. + EXPECT_EQ(kTestError, target_->Read(buffer, kBufferSize, &callback_)); +} + +// Verify that Write() calls stream's Write() and returns result. +TEST_F(StreamSocketAdapterTest, Write) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kTestDataSize); + + EXPECT_CALL(*stream_, Write(buffer->data(), kTestDataSize, _, _)) + .WillOnce(DoAll(SetArgumentPointee<2>(kTestDataSize), + Return(talk_base::SR_SUCCESS))); + + int result = target_->Write(buffer, kTestDataSize, &callback_); + EXPECT_EQ(kTestDataSize, result); + EXPECT_EQ(0, callback_result_); +} + +// Verify that write callback is called for pending writes. +TEST_F(StreamSocketAdapterTest, WritePending) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kTestDataSize); + + EXPECT_CALL(*stream_, Write(buffer->data(), kTestDataSize, _, _)) + .Times(2) + .WillOnce(Return(talk_base::SR_BLOCK)) + .WillOnce(DoAll(SetArgumentPointee<2>(kTestDataSize), + Return(talk_base::SR_SUCCESS))); + + int result = target_->Write(buffer, kTestDataSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + stream_->SignalEvent(stream_, talk_base::SE_WRITE, 0); + EXPECT_EQ(kTestDataSize, callback_result_); +} + +// Verify that Write() returns error after Close(). +TEST_F(StreamSocketAdapterTest, WriteClose) { + scoped_refptr<IOBuffer> buffer = new IOBuffer(kTestDataSize); + + EXPECT_CALL(*stream_, Write(buffer->data(), kTestDataSize, _, _)) + .WillOnce(Return(talk_base::SR_BLOCK)); + + int result = target_->Write(buffer, kTestDataSize, &callback_); + ASSERT_EQ(net::ERR_IO_PENDING, result); + + EXPECT_CALL(*stream_, Close()); + target_->Close(kTestError); + EXPECT_EQ(kTestError, callback_result_); + + // All Write() calls after Close() should return the error. + EXPECT_EQ(kTestError, target_->Write(buffer, kTestError, &callback_)); +} + +} // namespace remoting diff --git a/remoting/protocol/jingle_chromoting_connection.cc b/remoting/protocol/jingle_chromoting_connection.cc index 48e3ec0..758057c 100644 --- a/remoting/protocol/jingle_chromoting_connection.cc +++ b/remoting/protocol/jingle_chromoting_connection.cc @@ -6,6 +6,7 @@ #include "base/message_loop.h" #include "net/base/net_errors.h" +#include "remoting/base/constants.h" #include "remoting/jingle_glue/channel_socket_adapter.h" #include "remoting/jingle_glue/stream_socket_adapter.h" #include "remoting/protocol/jingle_chromoting_server.h" @@ -173,16 +174,21 @@ void JingleChromotingConnection::OnInitiate(bool incoming) { } void JingleChromotingConnection::OnAccept() { + const cricket::ContentInfo* content = + session_->remote_description()->FirstContentByType( + kChromotingXmlNamespace); + ASSERT(content != NULL); + // Create video RTP channels. video_rtp_channel_.reset(new TransportChannelSocketAdapter( - session_->CreateChannel(kVideoRtpChannelName))); + session_->CreateChannel(content->name, kVideoRtpChannelName))); video_rtcp_channel_.reset(new TransportChannelSocketAdapter( - session_->CreateChannel(kVideoRtcpChannelName))); + session_->CreateChannel(content->name, kVideoRtcpChannelName))); // Create events channel. events_channel_ = new PseudoTcpChannel(talk_base::Thread::Current(), session_); - events_channel_->Connect(kEventsChannelName); + events_channel_->Connect(content->name, kEventsChannelName); events_channel_adapter_.reset(new StreamSocketAdapter( events_channel_->GetStream())); @@ -190,7 +196,7 @@ void JingleChromotingConnection::OnAccept() { // TODO(sergeyu): Remove video channel when we are ready to switch to RTP. video_channel_ = new PseudoTcpChannel(talk_base::Thread::Current(), session_); - video_channel_->Connect(kVideoChannelName); + video_channel_->Connect(content->name, kVideoChannelName); video_channel_adapter_.reset(new StreamSocketAdapter( video_channel_->GetStream())); diff --git a/remoting/protocol/jingle_chromoting_connection_unittest.cc b/remoting/protocol/jingle_chromoting_connection_unittest.cc new file mode 100644 index 0000000..1f6f2e7 --- /dev/null +++ b/remoting/protocol/jingle_chromoting_connection_unittest.cc @@ -0,0 +1,579 @@ +// Copyright (c) 2010 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 "base/time.h" +#include "base/waitable_event.h" +#include "net/base/completion_callback.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/socket/socket.h" +#include "remoting/protocol/jingle_chromoting_connection.h" +#include "remoting/protocol/jingle_chromoting_server.h" +#include "remoting/protocol/session_manager_pair.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h" + +using testing::_; +using testing::DoAll; +using testing::Invoke; +using testing::InvokeWithoutArgs; +using testing::Return; +using testing::SaveArg; +using testing::SetArgumentPointee; +using testing::WithArg; + +namespace remoting { +class JingleChromotingConnectionTest; +} // namespace remoting + +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::JingleChromotingConnectionTest); + +namespace remoting { + +namespace { +const int kConnectionTimeoutMs = 10000; +const int kSendTimeoutMs = 20000; + +// Send 100 messages 1024 bytes each. For UDP messages are send with +// 10ms interval (about 1 seconds for 100 messages). +const int kMessageSize = 1024; +const int kMessages = 100; +const int kTestDataSize = kMessages * kMessageSize; +const int kUdpWriteDelayMs = 10; +} // namespace + +class MockServerCallback { + public: + MOCK_METHOD2(OnNewConnection, void(ChromotingConnection*, bool*)); +}; + +class MockConnectionCallback { + public: + MOCK_METHOD1(OnStateChange, void(ChromotingConnection::State)); +}; + +class JingleChromotingConnectionTest : public testing::Test { + public: + // Helper method to copy to set value of client_connection_. + void SetHostConnection(ChromotingConnection* connection) { + DCHECK(connection); + host_connection_ = connection; + host_connection_->SetStateChangeCallback( + NewCallback(&host_connection_callback_, + &MockConnectionCallback::OnStateChange)); + } + + protected: + virtual void SetUp() { + thread_.Start(); + } + + virtual void TearDown() { + CloseConnections(); + + if (host_server_) { + host_server_->Close(NewRunnableFunction( + &JingleChromotingConnectionTest::DoNothing)); + } + if (client_server_) { + client_server_->Close(NewRunnableFunction( + &JingleChromotingConnectionTest::DoNothing)); + } + thread_.Stop(); + } + + void CreateServerPair() { + // SessionManagerPair must be initialized on the jingle thread. + thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableMethod( + this, &JingleChromotingConnectionTest::DoCreateServerPair)); + SyncWithJingleThread(); + } + + void CloseConnections() { + if (host_connection_) { + host_connection_->Close(NewRunnableFunction( + &JingleChromotingConnectionTest::DoNothing)); + } + if (client_connection_) { + client_connection_->Close(NewRunnableFunction( + &JingleChromotingConnectionTest::DoNothing)); + } + SyncWithJingleThread(); + } + + void DoCreateServerPair() { + session_manager_pair_ = new SessionManagerPair(&thread_); + session_manager_pair_->Init(); + host_server_ = new JingleChromotingServer(thread_.message_loop()); + host_server_->set_allow_local_ips(true); + host_server_->Init(SessionManagerPair::kHostJid, + session_manager_pair_->host_session_manager(), + NewCallback(&host_server_callback_, + &MockServerCallback::OnNewConnection)); + client_server_ = new JingleChromotingServer(thread_.message_loop()); + client_server_->set_allow_local_ips(true); + client_server_->Init(SessionManagerPair::kClientJid, + session_manager_pair_->client_session_manager(), + NewCallback(&client_server_callback_, + &MockServerCallback::OnNewConnection)); + } + + void InitiateConnection() { + EXPECT_CALL(host_server_callback_, OnNewConnection(_, _)) + .WillOnce(DoAll( + WithArg<0>( + Invoke(this, + &JingleChromotingConnectionTest::SetHostConnection)), + SetArgumentPointee<1>(true))); + + base::WaitableEvent host_connected_event(false, false); + EXPECT_CALL(host_connection_callback_, + OnStateChange(ChromotingConnection::CONNECTING)) + .Times(1); + EXPECT_CALL(host_connection_callback_, + OnStateChange(ChromotingConnection::CONNECTED)) + .Times(1) + .WillOnce(InvokeWithoutArgs(&host_connected_event, + &base::WaitableEvent::Signal)); + + base::WaitableEvent client_connected_event(false, false); + EXPECT_CALL(client_connection_callback_, + OnStateChange(ChromotingConnection::CONNECTING)) + .Times(1); + EXPECT_CALL(client_connection_callback_, + OnStateChange(ChromotingConnection::CONNECTED)) + .Times(1) + .WillOnce(InvokeWithoutArgs(&client_connected_event, + &base::WaitableEvent::Signal)); + + client_connection_ = client_server_->Connect( + SessionManagerPair::kHostJid, + NewCallback(&client_connection_callback_, + &MockConnectionCallback::OnStateChange)); + + host_connected_event.TimedWait( + base::TimeDelta::FromMilliseconds(kConnectionTimeoutMs)); + client_connected_event.TimedWait( + base::TimeDelta::FromMilliseconds(kConnectionTimeoutMs)); + } + + static void SignalEvent(base::WaitableEvent* event) { + event->Signal(); + } + + static void DoNothing() { } + + void SyncWithJingleThread() { + base::WaitableEvent event(true, false); + thread_.message_loop()->PostTask( + FROM_HERE, NewRunnableFunction(&SignalEvent, &event)); + event.Wait(); + } + + JingleThread thread_; + scoped_refptr<SessionManagerPair> session_manager_pair_; + scoped_refptr<JingleChromotingServer> host_server_; + MockServerCallback host_server_callback_; + scoped_refptr<JingleChromotingServer> client_server_; + MockServerCallback client_server_callback_; + + scoped_refptr<ChromotingConnection> host_connection_; + MockConnectionCallback host_connection_callback_; + scoped_refptr<ChromotingConnection> client_connection_; + MockConnectionCallback client_connection_callback_; +}; + +class ChannelTesterBase : public base::RefCountedThreadSafe<ChannelTesterBase> { + public: + enum ChannelType { + EVENTS, + VIDEO, + VIDEO_RTP, + VIDEO_RTCP, + }; + + ChannelTesterBase(MessageLoop* message_loop, + ChromotingConnection* host_connection, + ChromotingConnection* client_connection) + : message_loop_(message_loop), + host_connection_(host_connection), + client_connection_(client_connection), + done_event_(true, false) { + } + + virtual ~ChannelTesterBase() { } + + void Start(ChannelType channel) { + message_loop_->PostTask( + FROM_HERE, NewRunnableMethod(this, &ChannelTesterBase::DoStart, + channel)); + } + + void WaitFinished() { + done_event_.TimedWait(base::TimeDelta::FromMilliseconds(kSendTimeoutMs)); + } + + virtual void CheckResults() = 0; + + protected: + void DoStart(ChannelType channel) { + socket_1_ = SelectChannel(host_connection_, channel); + socket_2_ = SelectChannel(client_connection_, channel); + + InitBuffers(); + DoRead(); + DoWrite(); + } + + virtual void InitBuffers() = 0; + virtual void DoWrite() = 0; + virtual void DoRead() = 0; + + net::Socket* SelectChannel(ChromotingConnection* connection, + ChannelType channel) { + switch (channel) { + case EVENTS: + return connection->GetEventsChannel(); + case VIDEO: + return connection->GetVideoChannel(); + case VIDEO_RTP: + return connection->GetVideoRtpChannel(); + case VIDEO_RTCP: + return connection->GetVideoRtcpChannel(); + default: + NOTREACHED(); + return NULL; + } + } + + MessageLoop* message_loop_; + scoped_refptr<ChromotingConnection> host_connection_; + scoped_refptr<ChromotingConnection> client_connection_; + net::Socket* socket_1_; + net::Socket* socket_2_; + base::WaitableEvent done_event_; +}; + +class TCPChannelTester : public ChannelTesterBase { + public: + TCPChannelTester(MessageLoop* message_loop, + ChromotingConnection* host_connection, + ChromotingConnection* client_connection) + : ChannelTesterBase(message_loop, host_connection, client_connection), + ALLOW_THIS_IN_INITIALIZER_LIST( + write_cb_(this, &TCPChannelTester::OnWritten)), + ALLOW_THIS_IN_INITIALIZER_LIST( + read_cb_(this, &TCPChannelTester::OnRead)), + write_errors_(0), + read_errors_(0) { + } + + virtual ~TCPChannelTester() { } + + virtual void CheckResults() { + EXPECT_EQ(0, write_errors_); + EXPECT_EQ(0, read_errors_); + + input_buffer_->set_offset(0); + ASSERT_EQ(kTestDataSize + kMessageSize, input_buffer_->capacity()); + + output_buffer_->SetOffset(0); + ASSERT_EQ(kTestDataSize, output_buffer_->size()); + + EXPECT_EQ(0, memcmp(output_buffer_->data(), + input_buffer_->data(), kTestDataSize)); + } + + protected: + virtual void InitBuffers() { + output_buffer_ = new net::DrainableIOBuffer( + new net::IOBuffer(kTestDataSize), kTestDataSize); + memset(output_buffer_->data(), 123, kTestDataSize); + + input_buffer_ = new net::GrowableIOBuffer(); + // Always keep kMessageSize bytes available at the end of the input buffer. + input_buffer_->SetCapacity(kMessageSize); + } + + virtual void DoWrite() { + int result = 1; + while (result > 0) { + if (output_buffer_->BytesRemaining() == 0) + break; + + int bytes_to_write = std::min(output_buffer_->BytesRemaining(), + kMessageSize); + result = socket_1_->Write(output_buffer_, bytes_to_write, &write_cb_); + HandleWriteResult(result); + }; + } + + void OnWritten(int result) { + HandleWriteResult(result); + DoWrite(); + } + + void HandleWriteResult(int result) { + if (result <= 0 && result != net::ERR_IO_PENDING) { + LOG(ERROR) << "Received error " << result << " when trying to write"; + write_errors_++; + done_event_.Signal(); + } else if (result > 0) { + output_buffer_->DidConsume(result); + } + } + + virtual void DoRead() { + int result = 1; + while (result > 0) { + input_buffer_->set_offset(input_buffer_->capacity() - kMessageSize); + + result = socket_2_->Read(input_buffer_, kMessageSize, &read_cb_); + HandleReadResult(result); + }; + } + + void OnRead(int result) { + HandleReadResult(result); + DoRead(); + } + + void HandleReadResult(int result) { + if (result <= 0 && result != net::ERR_IO_PENDING) { + if (!done_event_.IsSignaled()) { + LOG(ERROR) << "Received error " << result << " when trying to read"; + read_errors_++; + done_event_.Signal(); + } + } else if (result > 0) { + // Allocate memory for the next read. + input_buffer_->SetCapacity(input_buffer_->capacity() + result); + if (input_buffer_->capacity() == kTestDataSize + kMessageSize) + done_event_.Signal(); + } + } + + private: + scoped_refptr<net::DrainableIOBuffer> output_buffer_; + scoped_refptr<net::GrowableIOBuffer> input_buffer_; + + net::CompletionCallbackImpl<TCPChannelTester> write_cb_; + net::CompletionCallbackImpl<TCPChannelTester> read_cb_; + int write_errors_; + int read_errors_; +}; + +class UDPChannelTester : public ChannelTesterBase { + public: + UDPChannelTester(MessageLoop* message_loop, + ChromotingConnection* host_connection, + ChromotingConnection* client_connection) + : ChannelTesterBase(message_loop, host_connection, client_connection), + ALLOW_THIS_IN_INITIALIZER_LIST( + write_cb_(this, &UDPChannelTester::OnWritten)), + ALLOW_THIS_IN_INITIALIZER_LIST( + read_cb_(this, &UDPChannelTester::OnRead)), + write_errors_(0), + read_errors_(0), + packets_sent_(0), + packets_received_(0), + broken_packets_(0) { + } + + virtual ~UDPChannelTester() { } + + virtual void CheckResults() { + EXPECT_EQ(0, write_errors_); + EXPECT_EQ(0, read_errors_); + + EXPECT_EQ(0, broken_packets_); + + // Verify that we've received at least one packet. + EXPECT_GT(packets_received_, 0); + LOG(INFO) << "Received " << packets_received_ << " packets out of " + << kMessages; + } + + protected: + virtual void InitBuffers() { + } + + virtual void DoWrite() { + if (packets_sent_ >= kMessages) { + done_event_.Signal(); + return; + } + + scoped_refptr<net::IOBuffer> packet = new net::IOBuffer(kMessageSize); + memset(packet->data(), 123, kMessageSize); + sent_packets_[packets_sent_] = packet; + // Put index of this packet in the beginning of the packet body. + memcpy(packet->data(), &packets_sent_, sizeof(packets_sent_)); + + int result = socket_1_->Write(packet, kMessageSize, &write_cb_); + HandleWriteResult(result); + } + + void OnWritten(int result) { + HandleWriteResult(result); + } + + void HandleWriteResult(int result) { + if (result <= 0 && result != net::ERR_IO_PENDING) { + LOG(ERROR) << "Received error " << result << " when trying to write"; + write_errors_++; + done_event_.Signal(); + } else if (result > 0) { + EXPECT_EQ(kMessageSize, result); + packets_sent_++; + message_loop_->PostDelayedTask( + FROM_HERE, NewRunnableMethod(this, &UDPChannelTester::DoWrite), + kUdpWriteDelayMs); + } + } + + virtual void DoRead() { + int result = 1; + while (result > 0) { + int kReadSize = kMessageSize * 2; + read_buffer_ = new net::IOBuffer(kReadSize); + + result = socket_2_->Read(read_buffer_, kReadSize, &read_cb_); + HandleReadResult(result); + }; + } + + void OnRead(int result) { + HandleReadResult(result); + DoRead(); + } + + void HandleReadResult(int result) { + if (result <= 0 && result != net::ERR_IO_PENDING) { + // Error will be received after the socket is closed. + if (!done_event_.IsSignaled()) { + LOG(ERROR) << "Received error " << result << " when trying to read"; + read_errors_++; + done_event_.Signal(); + } + } else if (result > 0) { + packets_received_++; + if (kMessageSize != result) { + // Invalid packet size; + broken_packets_++; + } else { + // Validate packet body. + int packet_id; + memcpy(&packet_id, read_buffer_->data(), sizeof(packet_id)); + if (packet_id < 0 || packet_id >= kMessages) { + broken_packets_++; + } else { + if (memcmp(read_buffer_->data(), sent_packets_[packet_id]->data(), + kMessageSize) != 0) + broken_packets_++; + } + } + } + } + + private: + scoped_refptr<net::IOBuffer> sent_packets_[kMessages]; + scoped_refptr<net::IOBuffer> read_buffer_; + + net::CompletionCallbackImpl<UDPChannelTester> write_cb_; + net::CompletionCallbackImpl<UDPChannelTester> read_cb_; + int write_errors_; + int read_errors_; + int packets_sent_; + int packets_received_; + int broken_packets_; +}; + +// Verify that we can create and destory server objects without a connection. +TEST_F(JingleChromotingConnectionTest, CreateAndDestoy) { + CreateServerPair(); +} + +// Verify that incoming connection can be rejected, and that the status +// of the connection is set to CLOSED in this case. +TEST_F(JingleChromotingConnectionTest, RejectConnection) { + CreateServerPair(); + + // Reject incoming connection. + EXPECT_CALL(host_server_callback_, OnNewConnection(_, _)) + .WillOnce(SetArgumentPointee<1>(false)); + + base::WaitableEvent done_event(false, false); + EXPECT_CALL(client_connection_callback_, + OnStateChange(ChromotingConnection::CONNECTING)) + .Times(1); + EXPECT_CALL(client_connection_callback_, + OnStateChange(ChromotingConnection::CLOSED)) + .Times(1) + .WillOnce(InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)); + + client_connection_ = client_server_->Connect( + SessionManagerPair::kHostJid, + NewCallback(&client_connection_callback_, + &MockConnectionCallback::OnStateChange)); + + done_event.TimedWait( + base::TimeDelta::FromMilliseconds(kConnectionTimeoutMs)); +} + +// Verify that we can connect two endpoints. +TEST_F(JingleChromotingConnectionTest, Connect) { + CreateServerPair(); + InitiateConnection(); +} + +// Verify that data can be transmitted over the video channel. +TEST_F(JingleChromotingConnectionTest, TestVideoChannel) { + CreateServerPair(); + InitiateConnection(); + scoped_refptr<TCPChannelTester> tester = + new TCPChannelTester(thread_.message_loop(), host_connection_, + client_connection_); + tester->Start(ChannelTesterBase::VIDEO); + tester->WaitFinished(); + tester->CheckResults(); + + // Connections must be closed while |tester| still exists. + CloseConnections(); +} + +// Verify that data can be transmitted over the events channel. +TEST_F(JingleChromotingConnectionTest, TestEventsChannel) { + CreateServerPair(); + InitiateConnection(); + scoped_refptr<TCPChannelTester> tester = + new TCPChannelTester(thread_.message_loop(), host_connection_, + client_connection_); + tester->Start(ChannelTesterBase::EVENTS); + tester->WaitFinished(); + tester->CheckResults(); + + // Connections must be closed while |tester| still exists. + CloseConnections(); +} + +// Verify that data can be transmitted over the video RTP channel. +TEST_F(JingleChromotingConnectionTest, TestVideoRtpChannel) { + CreateServerPair(); + InitiateConnection(); + scoped_refptr<UDPChannelTester> tester = + new UDPChannelTester(thread_.message_loop(), host_connection_, + client_connection_); + tester->Start(ChannelTesterBase::VIDEO_RTP); + tester->WaitFinished(); + tester->CheckResults(); + + // Connections must be closed while |tester| still exists. + CloseConnections(); +} + +} // namespace remoting diff --git a/remoting/protocol/jingle_chromoting_server.cc b/remoting/protocol/jingle_chromoting_server.cc index 851221f..517967f 100644 --- a/remoting/protocol/jingle_chromoting_server.cc +++ b/remoting/protocol/jingle_chromoting_server.cc @@ -6,6 +6,7 @@ #include "base/message_loop.h" #include "remoting/base/constants.h" +#include "third_party/libjingle/source/talk/p2p/base/constants.h" #include "third_party/libjingle/source/talk/p2p/base/transport.h" #include "third_party/libjingle/source/talk/xmllite/xmlelement.h" @@ -125,7 +126,7 @@ void JingleChromotingServer::OnSessionCreate( DCHECK_EQ(message_loop(), MessageLoop::current()); // Allow local connections if neccessary. - session->transport()->set_allow_local_ips(allow_local_ips_); + session->set_allow_local_ips(allow_local_ips_); // If this is an outcoming session, the the connection object is already // created. @@ -161,10 +162,11 @@ void JingleChromotingServer::AcceptConnection( if (accept) session->Accept(CreateSessionDescription()); else - session->Reject(); + session->Reject(cricket::STR_TERMINATE_DECLINE); } bool JingleChromotingServer::ParseContent( + cricket::SignalingProtocol protocol, const buzz::XmlElement* element, const cricket::ContentDescription** content, cricket::ParseError* error) { @@ -190,6 +192,7 @@ bool JingleChromotingServer::ParseContent( // TODO(sergeyu): Add more information to the content description. E.g. // protocol version, etc. bool JingleChromotingServer::WriteContent( + cricket::SignalingProtocol protocol, const cricket::ContentDescription* content, buzz::XmlElement** elem, cricket::WriteError* error) { diff --git a/remoting/protocol/jingle_chromoting_server.h b/remoting/protocol/jingle_chromoting_server.h index 7a6fd84..7dff47d 100644 --- a/remoting/protocol/jingle_chromoting_server.h +++ b/remoting/protocol/jingle_chromoting_server.h @@ -86,10 +86,12 @@ class JingleChromotingServer bool received_initiate); virtual void OnSessionDestroy(cricket::Session* session); - virtual bool ParseContent(const buzz::XmlElement* elem, + virtual bool ParseContent(cricket::SignalingProtocol protocol, + const buzz::XmlElement* elem, const cricket::ContentDescription** content, cricket::ParseError* error); - virtual bool WriteContent(const cricket::ContentDescription* content, + virtual bool WriteContent(cricket::SignalingProtocol protocol, + const cricket::ContentDescription* content, buzz::XmlElement** elem, cricket::WriteError* error); diff --git a/remoting/protocol/session_manager_pair.cc b/remoting/protocol/session_manager_pair.cc new file mode 100644 index 0000000..2a1308c --- /dev/null +++ b/remoting/protocol/session_manager_pair.cc @@ -0,0 +1,91 @@ +// Copyright (c) 2010 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 "remoting/protocol/session_manager_pair.h" + +#include "base/logging.h" +#include "remoting/jingle_glue/jingle_thread.h" +#include "third_party/libjingle/source/talk/base/network.h" +#include "third_party/libjingle/source/talk/p2p/base/sessionmanager.h" +#include "third_party/libjingle/source/talk/p2p/client/basicportallocator.h" +#include "third_party/libjingle/source/talk/xmllite/xmlelement.h" + +namespace remoting { + +const char SessionManagerPair::kHostJid[] = "host1@gmail.com/123"; +const char SessionManagerPair::kClientJid[] = "host2@gmail.com/321"; + +SessionManagerPair::SessionManagerPair(JingleThread* thread) + : message_loop_(thread->message_loop()) { +} + +void SessionManagerPair::Init() { + DCHECK_EQ(message_loop_, MessageLoop::current()); + + network_manager_.reset(new talk_base::NetworkManager()); + + cricket::BasicPortAllocator* port_allocator = + new cricket::BasicPortAllocator(network_manager_.get()); + port_allocator_.reset(port_allocator); + + host_session_manager_.reset(new cricket::SessionManager(port_allocator)); + host_session_manager_->SignalOutgoingMessage.connect( + this, &SessionManagerPair::ProcessMessage); + host_session_manager_->SignalRequestSignaling.connect( + host_session_manager_.get(), &cricket::SessionManager::OnSignalingReady); + + client_session_manager_.reset(new cricket::SessionManager(port_allocator)); + client_session_manager_->SignalOutgoingMessage.connect( + this, &SessionManagerPair::ProcessMessage); + client_session_manager_->SignalRequestSignaling.connect( + client_session_manager_.get(), + &cricket::SessionManager::OnSignalingReady); +} + +cricket::SessionManager* SessionManagerPair::host_session_manager() { + return host_session_manager_.get(); +} + +cricket::SessionManager* SessionManagerPair::client_session_manager() { + return client_session_manager_.get(); +} + +void SessionManagerPair::ProcessMessage(cricket::SessionManager* manager, + const buzz::XmlElement* stanza) { + message_loop_->PostTask( + FROM_HERE, NewRunnableMethod(this, &SessionManagerPair::DoProcessMessage, + manager, new buzz::XmlElement(*stanza))); +} + +void SessionManagerPair::DoProcessMessage(cricket::SessionManager* manager, + buzz::XmlElement* stanza) { + DCHECK_EQ(message_loop_, MessageLoop::current()); + DCHECK(manager == host_session_manager_.get() || + manager == client_session_manager_.get()); + buzz::QName from_attr("", "from"); + buzz::QName to_attr("", "to"); + std::string to = stanza->Attr(to_attr); + if (to == kHostJid) { + DCHECK(manager == client_session_manager_.get()); + stanza->SetAttr(from_attr, kClientJid); + DeliverMessage(host_session_manager_.get(), stanza); + } else if (to == kClientJid) { + DCHECK(manager == host_session_manager_.get()); + stanza->SetAttr(from_attr, kHostJid); + DeliverMessage(client_session_manager_.get(), stanza); + } else { + LOG(ERROR) << "Dropping stanza sent to unknown jid " << to; + } + delete stanza; +} + +void SessionManagerPair::DeliverMessage(cricket::SessionManager* to, + buzz::XmlElement* stanza) { + if (to->IsSessionMessage(stanza)) { + to->OnIncomingMessage(stanza); + } +} + + +} // namespace remoting diff --git a/remoting/protocol/session_manager_pair.h b/remoting/protocol/session_manager_pair.h new file mode 100644 index 0000000..8fe0416 --- /dev/null +++ b/remoting/protocol/session_manager_pair.h @@ -0,0 +1,70 @@ +// Copyright (c) 2010 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. + +// SessionManagerPair class is used by unittests to create a pair of session +// managers connected to each other. These session managers are then can be +// passed to a pair of JingleChromotingConnection objects, so that it is +// possible to simulate connection between host and client. + +#ifndef REMOTING_PROTOCOL_MOCK_SESSION_MANAGER_H_ +#define REMOTING_PROTOCOL_MOCK_SESSION_MANAGER_H_ + +#include <base/ref_counted.h> +#include <base/scoped_ptr.h> + +#include "third_party/libjingle/source/talk/base/sigslot.h" + +class MessageLoop; + +namespace buzz { +class XmlElement; +} // namespace buzz + +namespace cricket { +class BasicPortAllocator; +class SessionManager; +} // namespace cricket + +namespace talk_base { +class NetworkManager; +} // namespace talk_base + +namespace remoting { + +class JingleThread; + +class SessionManagerPair + : public sigslot::has_slots<>, + public base::RefCountedThreadSafe<SessionManagerPair>{ + public: + static const char kHostJid[]; + static const char kClientJid[]; + + SessionManagerPair(JingleThread* thread); + + void Init(); + + // The session managers are named 'host' and 'client' just for convenience. + // Both can be used for client or host. + cricket::SessionManager* host_session_manager(); + cricket::SessionManager* client_session_manager(); + + private: + void ProcessMessage(cricket::SessionManager* manager, + const buzz::XmlElement* stanza); + void DoProcessMessage(cricket::SessionManager* manager, + buzz::XmlElement* stanza); + void DeliverMessage(cricket::SessionManager* to, + buzz::XmlElement* stanza); + + MessageLoop* message_loop_; + scoped_ptr<talk_base::NetworkManager> network_manager_; + scoped_ptr<cricket::BasicPortAllocator> port_allocator_; + scoped_ptr<cricket::SessionManager> host_session_manager_; + scoped_ptr<cricket::SessionManager> client_session_manager_; +}; + +} // namespace remoting + +#endif // REMOTING_PROTOCOL_MOCK_SESSION_MANAGER_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 7a87ce0..1d57e6e 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -387,6 +387,7 @@ 'chromoting_client', 'chromoting_host', 'chromoting_jingle_glue', + 'chromoting_protocol', '../base/base.gyp:base', '../base/base.gyp:base_i18n', '../base/base.gyp:test_support_base', @@ -426,11 +427,16 @@ 'host/mock_objects.h', 'host/session_manager_unittest.cc', 'host/test_key_pair.h', + 'jingle_glue/channel_socket_adapter_unittest.cc', 'jingle_glue/jingle_client_unittest.cc', 'jingle_glue/jingle_channel_unittest.cc', 'jingle_glue/jingle_thread_unittest.cc', 'jingle_glue/iq_request_unittest.cc', 'jingle_glue/mock_objects.h', + 'jingle_glue/stream_socket_adapter_unittest.cc', + 'protocol/jingle_chromoting_connection_unittest.cc', + 'protocol/session_manager_pair.cc', + 'protocol/session_manager_pair.h', 'run_all_unittests.cc', ], 'conditions': [ |