blob: be1bf0b60f21068959f0ad8b0d2f55e48a9e3e58 [file] [log] [blame]
// Copyright (c) 2011 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/memory/scoped_ptr.h"
#include "remoting/base/decoder.h"
#include "remoting/client/chromoting_view.h"
#include "remoting/proto/internal.pb.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
using ::testing::InSequence;
using ::testing::Return;
namespace remoting {
class MockDecoder : public Decoder {
public:
MockDecoder() {}
MOCK_METHOD4(BeginDecode, bool(scoped_refptr<media::VideoFrame> frame,
UpdatedRects* updated_rects,
Task* partial_decode_done,
Task* decode_done));
MOCK_METHOD1(PartialDecode, bool(ChromotingHostMessage* message));
MOCK_METHOD0(EndDecode, void());
MOCK_METHOD0(Encoding, VideoPacketFormat::Encoding());
MOCK_METHOD0(IsStarted, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockDecoder);
};
// Fake ChromotingView that provides stub implementations for all pure virtual
// methods. This is sufficient since we're only interested in testing the
// base class methods in this file.
class FakeView : public ChromotingView {
bool Initialize() { return false; }
void TearDown() {}
void Paint() {}
void SetSolidFill(uint32 color) {}
void UnsetSolidFill() {}
void SetConnectionState(ConnectionState s) {}
void SetViewport(int x, int y, int width, int height) {
frame_width_ = width;
frame_height_ = height;
}
void SetHostScreenSize(int width, int height) {}
void HandleBeginUpdateStream(ChromotingHostMessage* msg) {}
void HandleUpdateStreamPacket(ChromotingHostMessage* msg) {}
void HandleEndUpdateStream(ChromotingHostMessage* msg) {}
public:
// Testing accessors.
// These provide access to private/protected members of ChromotingView so
// that they can be tested/verified.
Decoder* get_decoder() {
return decoder_.get();
}
void set_decoder(Decoder* decoder) {
decoder_.reset(decoder);
}
// Testing wrappers for private setup/startup decoder routines.
bool setup_decoder(VideoPacketFormat::Encoding encoding) {
return SetupDecoder(encoding);
}
bool begin_decoding(Task* partial_decode_done, Task* decode_done) {
return BeginDecoding(partial_decode_done, decode_done);
}
bool decode(ChromotingHostMessage* msg) {
return Decode(msg);
}
bool end_decoding() {
return EndDecoding();
}
// Testing setup.
void set_test_viewport() {
SetViewport(0, 0, 640, 480);
}
};
// Verify the initial state.
TEST(ChromotingViewTest, InitialState) {
scoped_ptr<FakeView> view(new FakeView());
EXPECT_TRUE(view->get_decoder() == NULL);
}
// Test a simple decoder sequence:
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket:
// SetupDecoder - return false
// BeginDecoding
// Decode
// HandleEndUpdateStream:
// EndDecoding
TEST(ChromotingViewTest, DecodeSimple) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
// HandleBeginUpdateStream
// HandleUpdateStreamPacket
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
Decoder* decoder = view->get_decoder();
ASSERT_TRUE(decoder != NULL);
EXPECT_EQ(EncodingZlib, decoder->Encoding());
EXPECT_FALSE(decoder->IsStarted());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder = new MockDecoder();
view->set_decoder(mock_decoder);
EXPECT_CALL(*mock_decoder, Encoding())
.WillRepeatedly(Return(EncodingZlib));
{
InSequence s;
// BeginDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false));
EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
// Decode
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, PartialDecode(_))
.WillOnce(Return(true));
// EndDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, EndDecode())
.Times(1);
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false));
}
// decoder_->IsStarted() is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
}
// Test a three-packet decoder sequence:
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket: (1)
// SetupDecoder - return false
// BeginDecoding
// Decode
// HandleUpdateStreamPacket: (2)
// SetupDecoder - return true
// Decode
// HandleUpdateStreamPacket: (3)
// SetupDecoder - return true
// Decode
// HandleEndUpdateStream:
// EndDecoding
TEST(ChromotingViewTest, DecodeThreePackets) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
// HandleBeginUpdateStream
// HandleUpdateStreamPacket (1)
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
Decoder* decoder = view->get_decoder();
ASSERT_TRUE(decoder != NULL);
EXPECT_EQ(EncodingZlib, decoder->Encoding());
EXPECT_FALSE(decoder->IsStarted());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder = new MockDecoder();
view->set_decoder(mock_decoder);
EXPECT_CALL(*mock_decoder, Encoding())
.WillRepeatedly(Return(EncodingZlib));
EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, PartialDecode(_))
.Times(3)
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_decoder, EndDecode())
.Times(1);
{
InSequence s;
// BeginDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
// BeginDecoding (1)
// Decode (1)
// SetupDecoder (1)
// Decode (1)
// SetupDecoder (1)
// Decode (1)
// EndDecoding (1)
// Total = 7 calls
EXPECT_CALL(*mock_decoder, IsStarted())
.Times(7)
.WillRepeatedly(Return(true))
.RetiresOnSaturation();
// EndDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
}
// decoder_->IsStarted() is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleUpdateStreamPacket (2)
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
// Don't call BeginDecoding() because it is already started.
ASSERT_TRUE(view->decode(NULL));
// HandleUpdateStreamPacket (3)
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
// Don't call BeginDecoding() because it is already started.
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
}
// Test two update streams: (same packet encoding)
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket:
// SetupDecoder - return false
// BeginDecoding
// Decode
// HandleEndUpdateStream:
// EndDecoding
//
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket:
// SetupDecoder - return false
// BeginDecoding
// Decode
// HandleEndUpdateStream:
// EndDecoding
TEST(ChromotingViewTest, DecodeTwoStreams) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
// HandleBeginUpdateStream (update stream 1)
// HandleUpdateStreamPacket
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
Decoder* decoder = view->get_decoder();
ASSERT_TRUE(decoder != NULL);
EXPECT_EQ(EncodingZlib, decoder->Encoding());
EXPECT_FALSE(decoder->IsStarted());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder = new MockDecoder();
view->set_decoder(mock_decoder);
EXPECT_CALL(*mock_decoder, Encoding())
.WillRepeatedly(Return(EncodingZlib));
EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_decoder, PartialDecode(_))
.Times(2)
.WillRepeatedly(Return(true));
EXPECT_CALL(*mock_decoder, EndDecode())
.Times(2);
{
InSequence s;
// BeginDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
// BeginDecoding (1)
// Decode (1)
// EndDecoding (1)
// Total = 3 calls
EXPECT_CALL(*mock_decoder, IsStarted())
.Times(3)
.WillRepeatedly(Return(true))
.RetiresOnSaturation();
// EndDecoding (1)
// SetupDecoder (1)
// BeginDecoding (1)
// Total = 3 calls
EXPECT_CALL(*mock_decoder, IsStarted())
.Times(3)
.WillRepeatedly(Return(false))
.RetiresOnSaturation();
// BeginDecoding (1)
// Decode (1)
// EndDecoding (1)
// Total = 3 calls
EXPECT_CALL(*mock_decoder, IsStarted())
.Times(3)
.WillRepeatedly(Return(true))
.RetiresOnSaturation();
// EndDecoding
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
}
// |started| is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
// HandleBeginUpdateStream (update stream 2)
// HandleUpdateStreamPacket
Decoder* old_decoder = view->get_decoder();
view->setup_decoder(EncodingZlib);
// Verify we're using the same decoder since the encoding matches.
Decoder* new_decoder = view->get_decoder();
ASSERT_TRUE(new_decoder == old_decoder);
// |started| is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
}
// Test two update streams with different encodings:
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket: ('Zlib' encoded)
// SetupDecoder
// BeginDecoding
// Decode
// HandleEndUpdateStream:
// EndDecoding
//
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket: ('None' encoded)
// SetupDecoder
// BeginDecoding
// Decode
// HandleEndUpdateStream:
// EndDecoding
TEST(ChromotingViewTest, DecodeTwoStreamsDifferentEncodings) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
// HandleBeginUpdateStream (update stream 1)
// HandleUpdateStreamPacket
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
Decoder* decoder = view->get_decoder();
ASSERT_TRUE(decoder != NULL);
EXPECT_EQ(EncodingZlib, decoder->Encoding());
EXPECT_FALSE(decoder->IsStarted());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder1 = new MockDecoder();
view->set_decoder(mock_decoder1);
EXPECT_CALL(*mock_decoder1, Encoding())
.WillRepeatedly(Return(EncodingZlib));
EXPECT_CALL(*mock_decoder1, BeginDecode(_, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder1, PartialDecode(_))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder1, EndDecode());
{
InSequence s1;
// BeginDecoding
EXPECT_CALL(*mock_decoder1, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
// BeginDecoding (1)
// Decode (1)
// EndDecoding (1)
// Total = 3 calls
EXPECT_CALL(*mock_decoder1, IsStarted())
.Times(3)
.WillRepeatedly(Return(true))
.RetiresOnSaturation();
// EndDecoding (1)
// SetupDecoder (1)
// Total = 2 calls
EXPECT_CALL(*mock_decoder1, IsStarted())
.Times(2)
.WillRepeatedly(Return(false))
.RetiresOnSaturation();
}
// |started| is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
// HandleBeginUpdateStream (update stream 2)
// HandleUpdateStreamPacket
// Encoding for second stream is different from first, so this will
// create a new decoder.
ASSERT_TRUE(view->setup_decoder(EncodingNone));
// The decoder should be new.
EXPECT_NE(mock_decoder1, view->get_decoder());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder2 = new MockDecoder();
view->set_decoder(mock_decoder2);
EXPECT_CALL(*mock_decoder2, Encoding())
.WillRepeatedly(Return(EncodingNone));
EXPECT_CALL(*mock_decoder2, BeginDecode(_, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder2, PartialDecode(_))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder2, EndDecode());
{
InSequence s2;
// BeginDecoding
EXPECT_CALL(*mock_decoder2, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
// BeginDecoding (1)
// Decode (1)
// EndDecoding (1)
// Total = 3 calls
EXPECT_CALL(*mock_decoder2, IsStarted())
.Times(3)
.WillRepeatedly(Return(true))
.RetiresOnSaturation();
// EndDecoding
EXPECT_CALL(*mock_decoder2, IsStarted())
.WillOnce(Return(false))
.RetiresOnSaturation();
}
// |started| is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleEndUpdateStream
ASSERT_TRUE(view->end_decoding());
}
// Test failure when packets in a stream have mismatched encodings.
// HandleBeginUpdateStream:
// HandleUpdateStreamPacket: (1)
// SetupDecoder - encoding = Zlib
// BeginDecoding
// Decode
// HandleUpdateStreamPacket: (2)
// SetupDecoder - encoding = none
// DEATH
TEST(ChromotingViewTest, MismatchedEncodings) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
// HandleBeginUpdateStream
// HandleUpdateStreamPacket (1)
// encoding = Zlib
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
Decoder* decoder = view->get_decoder();
ASSERT_TRUE(decoder != NULL);
EXPECT_EQ(EncodingZlib, decoder->Encoding());
EXPECT_FALSE(decoder->IsStarted());
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder = new MockDecoder();
view->set_decoder(mock_decoder);
EXPECT_CALL(*mock_decoder, Encoding())
.WillRepeatedly(Return(EncodingZlib));
{
InSequence s;
// BeginDecoding().
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false));
EXPECT_CALL(*mock_decoder, BeginDecode(_, _, _, _))
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
// Decode().
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
EXPECT_CALL(*mock_decoder, PartialDecode(_))
.WillOnce(Return(true));
// SetupDecoder().
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(true));
}
// |started| is false, so we call begin_decoding().
ASSERT_TRUE(view->begin_decoding(NULL, NULL));
ASSERT_TRUE(view->decode(NULL));
// HandleUpdateStreamPacket (2)
// encoding = None
// Doesn't match previous packet encoding, so returns failure.
ASSERT_FALSE(view->setup_decoder(EncodingNone));
}
// Verify we fail when Decode() is called without first calling
// BeginDecoding().
TEST(ChromotingViewTest, MissingBegin) {
scoped_ptr<FakeView> view(new FakeView());
view->set_test_viewport();
ASSERT_TRUE(view->setup_decoder(EncodingZlib));
ASSERT_TRUE(view->get_decoder() != NULL);
// Overwrite |decoder_| with MockDecoder.
MockDecoder* mock_decoder = new MockDecoder();
view->set_decoder(mock_decoder);
EXPECT_CALL(*mock_decoder, IsStarted())
.WillOnce(Return(false));
// Call decode() without calling begin_decoding().
EXPECT_FALSE(view->decode(NULL));
}
// Test requesting a decoder for an invalid encoding.
TEST(ChromotingViewTest, InvalidEncoding) {
scoped_ptr<FakeView> view(new FakeView());
EXPECT_FALSE(view->setup_decoder(EncodingInvalid));
}
} // namespace remoting