| // 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/ref_counted.h" |
| #include "base/waitable_event.h" |
| #include "media/base/data_buffer.h" |
| #include "remoting/jingle_glue/jingle_channel.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/base/stream.h" |
| |
| using testing::_; |
| using testing::Return; |
| using testing::Mock; |
| using testing::SetArgumentPointee; |
| |
| namespace remoting { |
| |
| namespace { |
| // Size of test buffer in bytes. |
| const size_t kBufferSize = 100; |
| } // namespace |
| |
| class MockJingleChannelCallback : public JingleChannel::Callback { |
| public: |
| MOCK_METHOD2(OnStateChange, void(JingleChannel*, JingleChannel::State)); |
| MOCK_METHOD2(OnPacketReceived, void(JingleChannel*, |
| 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() { } |
| |
| // A helper that calls OnStreamEvent(). Need this because we want |
| // to call it on the jingle thread. |
| static void StreamEvent(JingleChannel* channel, |
| talk_base::StreamInterface* stream, |
| int event, int error, |
| base::WaitableEvent* done_event) { |
| channel->OnStreamEvent(stream, event, error); |
| if (done_event) |
| done_event->Signal(); |
| } |
| |
| static void OnClosed(bool* called) { |
| *called = true; |
| } |
| |
| protected: |
| virtual void SetUp() { |
| stream_ = new MockStream(); // Freed by the channel. |
| channel_ = new JingleChannel(&callback_); |
| channel_->thread_ = &thread_; |
| channel_->stream_.reset(stream_); |
| } |
| |
| JingleThread thread_; |
| MockStream* stream_; |
| MockJingleChannelCallback callback_; |
| scoped_refptr<JingleChannel> channel_; |
| }; |
| |
| TEST_F(JingleChannelTest, Init) { |
| EXPECT_CALL(*stream_, GetState()) |
| .Times(1) |
| .WillRepeatedly(Return(talk_base::SS_OPENING)); |
| |
| EXPECT_CALL(callback_, OnStateChange(channel_.get(), |
| JingleChannel::CONNECTING)) |
| .Times(1); |
| |
| thread_.Start(); |
| |
| EXPECT_EQ(JingleChannel::INITIALIZING, channel_->state()); |
| channel_->Init(&thread_, stream_, "user@domain.com"); |
| EXPECT_EQ(JingleChannel::CONNECTING, channel_->state()); |
| channel_->closed_ = true; |
| |
| thread_.Stop(); |
| } |
| |
| TEST_F(JingleChannelTest, Write) { |
| scoped_refptr<media::DataBuffer> data = new media::DataBuffer(kBufferSize); |
| data->SetDataSize(kBufferSize); |
| |
| EXPECT_CALL(*stream_, Write(static_cast<const void*>(data->GetData()), |
| kBufferSize, _, _)) |
| .WillOnce(DoAll(SetArgumentPointee<2>(kBufferSize), |
| Return(talk_base::SR_SUCCESS))); |
| |
| channel_->state_ = JingleChannel::OPEN; |
| thread_.Start(); |
| channel_->Write(data); |
| thread_.Stop(); |
| channel_->closed_ = true; |
| } |
| |
| TEST_F(JingleChannelTest, Read) { |
| scoped_refptr<media::DataBuffer> data = new media::DataBuffer(kBufferSize); |
| data->SetDataSize(kBufferSize); |
| |
| EXPECT_CALL(callback_, OnPacketReceived(channel_.get(), _)) |
| .Times(1); |
| |
| EXPECT_CALL(*stream_, GetAvailable(_)) |
| .WillOnce(DoAll(SetArgumentPointee<0>(kBufferSize), |
| Return(true))) |
| .WillOnce(DoAll(SetArgumentPointee<0>(0), |
| Return(true))); |
| |
| EXPECT_CALL(*stream_, Read(_, kBufferSize, _, _)) |
| .WillOnce(DoAll(SetArgumentPointee<2>(kBufferSize), |
| Return(talk_base::SR_SUCCESS))); |
| |
| channel_->state_ = JingleChannel::OPEN; |
| thread_.Start(); |
| |
| base::WaitableEvent done_event(true, false); |
| thread_.message_loop()->PostTask(FROM_HERE, NewRunnableFunction( |
| &JingleChannelTest::StreamEvent, channel_.get(), stream_, |
| talk_base::SE_READ, 0, &done_event)); |
| done_event.Wait(); |
| |
| thread_.Stop(); |
| |
| channel_->closed_ = true; |
| } |
| |
| TEST_F(JingleChannelTest, Close) { |
| EXPECT_CALL(*stream_, Close()).Times(1); |
| // Don't expect any calls except Close(). |
| EXPECT_CALL(*stream_, GetAvailable(_)).Times(0); |
| EXPECT_CALL(*stream_, Read(_, _, _, _)).Times(0); |
| EXPECT_CALL(callback_, OnPacketReceived(_, _)).Times(0); |
| |
| thread_.Start(); |
| channel_->Close(); |
| // Verify that the channel doesn't call callback anymore. |
| thread_.message_loop()->PostTask(FROM_HERE, NewRunnableFunction( |
| &JingleChannelTest::StreamEvent, channel_.get(), stream_, |
| talk_base::SE_READ, 0, static_cast<base::WaitableEvent*>(NULL))); |
| thread_.Stop(); |
| } |
| |
| TEST_F(JingleChannelTest, ClosedTask) { |
| EXPECT_CALL(*stream_, Close()) |
| .Times(1); |
| |
| thread_.Start(); |
| bool closed = false; |
| channel_->Close(NewRunnableFunction(&JingleChannelTest::OnClosed, |
| &closed)); |
| thread_.Stop(); |
| EXPECT_TRUE(closed); |
| } |
| |
| TEST_F(JingleChannelTest, DoubleClose) { |
| EXPECT_CALL(*stream_, Close()) |
| .Times(1); |
| |
| thread_.Start(); |
| bool closed1 = false; |
| channel_->Close(NewRunnableFunction(&JingleChannelTest::OnClosed, |
| &closed1)); |
| bool closed2 = false; |
| channel_->Close(NewRunnableFunction(&JingleChannelTest::OnClosed, |
| &closed2)); |
| thread_.Stop(); |
| EXPECT_TRUE(closed1 && closed2); |
| } |
| |
| } // namespace remoting |