summaryrefslogtreecommitdiffstats
path: root/remoting/protocol/jingle_chromoting_connection_unittest.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/protocol/jingle_chromoting_connection_unittest.cc')
-rw-r--r--remoting/protocol/jingle_chromoting_connection_unittest.cc579
1 files changed, 579 insertions, 0 deletions
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