// 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/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