| Index: remoting/base/codec_test.cc
|
| diff --git a/remoting/base/codec_test.cc b/remoting/base/codec_test.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..7cf6d666cd06986fd45bdee9cded4ee42286ece7
|
| --- /dev/null
|
| +++ b/remoting/base/codec_test.cc
|
| @@ -0,0 +1,388 @@
|
| +// 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 <deque>
|
| +#include <stdlib.h>
|
| +
|
| +#include "gfx/rect.h"
|
| +#include "media/base/video_frame.h"
|
| +#include "remoting/base/codec_test.h"
|
| +#include "remoting/base/encoder.h"
|
| +#include "remoting/base/mock_objects.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +static const int kWidth = 320;
|
| +static const int kHeight = 240;
|
| +static const int kBytesPerPixel = 4;
|
| +
|
| +// Some sample rects for testing.
|
| +static const gfx::Rect kTestRects[] = {
|
| + gfx::Rect(0, 0, kWidth, kHeight),
|
| + gfx::Rect(0, 0, kWidth / 2, kHeight / 2),
|
| + gfx::Rect(kWidth / 2, kHeight / 2, kWidth / 2, kHeight / 2),
|
| + gfx::Rect(16, 16, 16, 16),
|
| + gfx::Rect(128, 64, 32, 32)
|
| +};
|
| +
|
| +namespace remoting {
|
| +
|
| +// A class to test that the state transition of the Encoder is correct.
|
| +class EncoderStateTester {
|
| + public:
|
| + EncoderStateTester()
|
| + : next_state_(Encoder::EncodingStarting) {
|
| + }
|
| +
|
| + ~EncoderStateTester() {
|
| + EXPECT_EQ(Encoder::EncodingStarting, next_state_);
|
| + }
|
| +
|
| + // Set the state output of the Encoder.
|
| + void ReceivedState(Encoder::EncodingState state) {
|
| + if (state & Encoder::EncodingStarting) {
|
| + EXPECT_EQ(Encoder::EncodingStarting, next_state_);
|
| + next_state_ = Encoder::EncodingInProgress | Encoder::EncodingEnded;
|
| + } else {
|
| + EXPECT_FALSE(next_state_ & Encoder::EncodingStarting);
|
| + }
|
| +
|
| + if (state & Encoder::EncodingInProgress) {
|
| + EXPECT_TRUE(next_state_ & Encoder::EncodingInProgress);
|
| + }
|
| +
|
| + if (state & Encoder::EncodingEnded) {
|
| + EXPECT_TRUE(next_state_ & Encoder::EncodingEnded);
|
| + next_state_ = Encoder::EncodingStarting;
|
| + }
|
| + }
|
| +
|
| + private:
|
| + Encoder::EncodingState next_state_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(EncoderStateTester);
|
| +};
|
| +
|
| +// A class to test the message output of the encoder.
|
| +class EncoderMessageTester {
|
| + public:
|
| + EncoderMessageTester()
|
| + : begin_rect_(0),
|
| + rect_data_(0),
|
| + end_rect_(0),
|
| + state_(kWaitingForBeginRect),
|
| + strict_(false) {
|
| + }
|
| +
|
| + ~EncoderMessageTester() {
|
| + EXPECT_EQ(begin_rect_, end_rect_);
|
| + EXPECT_EQ(kWaitingForBeginRect, state_);
|
| + }
|
| +
|
| + // Test that we received the correct message.
|
| + void ReceivedMessage(HostMessage* message) {
|
| + EXPECT_TRUE(message->has_update_stream_packet());
|
| +
|
| + if (state_ == kWaitingForBeginRect) {
|
| + EXPECT_TRUE(message->update_stream_packet().has_begin_rect());
|
| + state_ = kWaitingForRectData;
|
| + ++begin_rect_;
|
| +
|
| + if (strict_) {
|
| + gfx::Rect rect = rects_.front();
|
| + rects_.pop_front();
|
| + EXPECT_EQ(rect.x(), message->update_stream_packet().begin_rect().x());
|
| + EXPECT_EQ(rect.y(), message->update_stream_packet().begin_rect().y());
|
| + EXPECT_EQ(rect.width(),
|
| + message->update_stream_packet().begin_rect().width());
|
| + EXPECT_EQ(rect.height(),
|
| + message->update_stream_packet().begin_rect().height());
|
| + }
|
| + } else {
|
| + EXPECT_FALSE(message->update_stream_packet().has_begin_rect());
|
| + }
|
| +
|
| + if (state_ == kWaitingForRectData) {
|
| + if (message->update_stream_packet().has_rect_data()) {
|
| + ++rect_data_;
|
| + }
|
| +
|
| + if (message->update_stream_packet().has_end_rect()) {
|
| + // Expect that we have received some data.
|
| + EXPECT_GT(rect_data_, 0);
|
| + rect_data_ = 0;
|
| + state_ = kWaitingForBeginRect;
|
| + ++end_rect_;
|
| + }
|
| + }
|
| + }
|
| +
|
| + void set_strict(bool strict) {
|
| + strict_ = strict;
|
| + }
|
| +
|
| + void AddRects(const gfx::Rect* rects, int count) {
|
| + rects_.insert(rects_.begin() + rects_.size(), rects, rects + count);
|
| + }
|
| +
|
| + private:
|
| + enum State {
|
| + kWaitingForBeginRect,
|
| + kWaitingForRectData,
|
| + };
|
| +
|
| + int begin_rect_;
|
| + int rect_data_;
|
| + int end_rect_;
|
| + State state_;
|
| + bool strict_;
|
| +
|
| + std::deque<gfx::Rect> rects_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(EncoderMessageTester);
|
| +};
|
| +
|
| +class DecoderTester {
|
| + public:
|
| + DecoderTester(Decoder* decoder)
|
| + : strict_(false),
|
| + decoder_(decoder),
|
| + decode_done_(false) {
|
| + media::VideoFrame::CreateFrame(media::VideoFrame::RGB32,
|
| + kWidth, kHeight,
|
| + base::TimeDelta(),
|
| + base::TimeDelta(), &frame_);
|
| + EXPECT_TRUE(frame_.get());
|
| + }
|
| +
|
| + void ReceivedMessage(HostMessage* message) {
|
| + if (message->has_begin_update_stream()) {
|
| + delete message;
|
| + decoder_->BeginDecode(
|
| + frame_, &update_rects_,
|
| + NewRunnableMethod(this, &DecoderTester::OnPartialDecodeDone),
|
| + NewRunnableMethod(this, &DecoderTester::OnDecodeDone));
|
| + }
|
| +
|
| + if (message->has_update_stream_packet()) {
|
| + decoder_->PartialDecode(message);
|
| + }
|
| +
|
| + if (message->has_end_update_stream()) {
|
| + delete message;
|
| + decoder_->EndDecode();
|
| + }
|
| + }
|
| +
|
| + void set_strict(bool strict) {
|
| + strict_ = strict;
|
| + }
|
| +
|
| + void set_capture_data(scoped_refptr<CaptureData> data) {
|
| + capture_data_ = data;
|
| + }
|
| +
|
| + void AddRects(const gfx::Rect* rects, int count) {
|
| + rects_.insert(rects_.begin() + rects_.size(), rects, rects + count);
|
| + }
|
| +
|
| + bool decode_done() const { return decode_done_; }
|
| + void reset_decode_done() { decode_done_ = false; }
|
| +
|
| + private:
|
| + void OnPartialDecodeDone() {
|
| + if (!strict_)
|
| + return;
|
| + for (size_t i = 0; i < update_rects_.size(); ++i) {
|
| + EXPECT_FALSE(rects_.empty());
|
| + gfx::Rect rect = rects_.front();
|
| + rects_.pop_front();
|
| + EXPECT_EQ(rect, update_rects_[i]);
|
| + }
|
| + }
|
| +
|
| + void OnDecodeDone() {
|
| + decode_done_ = true;
|
| + if (!strict_)
|
| + return;
|
| +
|
| + EXPECT_TRUE(capture_data_.get());
|
| + for (int i = 0; i < DataPlanes::kPlaneCount; ++i) {
|
| + if (!frame_->data(i) || !capture_data_->data_planes().data[i])
|
| + continue;
|
| + // TODO(hclam): HAndle YUV.
|
| + int size = capture_data_->data_planes().strides[i] * kHeight;
|
| + EXPECT_EQ(0, memcmp(capture_data_->data_planes().data[i],
|
| + frame_->data(i), size));
|
| + }
|
| + }
|
| +
|
| + bool strict_;
|
| + std::deque<gfx::Rect> rects_;
|
| + UpdatedRects update_rects_;
|
| + Decoder* decoder_;
|
| + scoped_refptr<media::VideoFrame> frame_;
|
| + scoped_refptr<CaptureData> capture_data_;
|
| + bool decode_done_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(DecoderTester);
|
| +};
|
| +
|
| +class EncoderTester {
|
| + public:
|
| + EncoderTester(EncoderMessageTester* message_tester,
|
| + EncoderStateTester* state_tester)
|
| + : message_tester_(message_tester),
|
| + state_tester_(state_tester),
|
| + data_available_(0) {
|
| + }
|
| +
|
| + ~EncoderTester() {
|
| + EXPECT_GT(data_available_, 0);
|
| + }
|
| +
|
| + void DataAvailable(HostMessage* message,
|
| + Encoder::EncodingState state) {
|
| + ++data_available_;
|
| + message_tester_->ReceivedMessage(message);
|
| + state_tester_->ReceivedState(state);
|
| +
|
| + // Send the message to the DecoderTester.
|
| + if (decoder_tester_) {
|
| + if (state & Encoder::EncodingStarting) {
|
| + HostMessage* begin_update = new HostMessage();
|
| + begin_update->mutable_begin_update_stream();
|
| + decoder_tester_->ReceivedMessage(begin_update);
|
| + }
|
| +
|
| + if (state & Encoder::EncodingInProgress) {
|
| + decoder_tester_->ReceivedMessage(message);
|
| + }
|
| +
|
| + if (state & Encoder::EncodingEnded) {
|
| + HostMessage* end_update = new HostMessage();
|
| + end_update->mutable_end_update_stream();
|
| + decoder_tester_->ReceivedMessage(end_update);
|
| + }
|
| + return;
|
| + }
|
| + delete message;
|
| + }
|
| +
|
| + void AddRects(const gfx::Rect* rects, int count) {
|
| + message_tester_->AddRects(rects, count);
|
| + }
|
| +
|
| + void set_decoder_tester(DecoderTester* decoder_tester) {
|
| + decoder_tester_ = decoder_tester;
|
| + }
|
| +
|
| + private:
|
| + EncoderMessageTester* message_tester_;
|
| + EncoderStateTester* state_tester_;
|
| + DecoderTester* decoder_tester_;
|
| + int data_available_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(EncoderTester);
|
| +};
|
| +
|
| +scoped_refptr<CaptureData> PrepareEncodeData(PixelFormat format,
|
| + uint8** memory) {
|
| + // TODO(hclam): Support also YUV format.
|
| + CHECK(format == PixelFormatRgb32);
|
| + int size = kWidth * kHeight * kBytesPerPixel;
|
| +
|
| + *memory = new uint8[size];
|
| + srand(0);
|
| + for (int i = 0; i < size; ++i) {
|
| + (*memory)[i] = rand() % 256;
|
| + }
|
| +
|
| + DataPlanes planes;
|
| + memset(planes.data, 0, sizeof(planes.data));
|
| + memset(planes.strides, 0, sizeof(planes.strides));
|
| + planes.data[0] = *memory;
|
| + planes.strides[0] = kWidth * kBytesPerPixel;
|
| +
|
| + scoped_refptr<CaptureData> data =
|
| + new CaptureData(planes, kWidth, kHeight, format);
|
| + return data;
|
| +}
|
| +
|
| +static void TestEncodingRects(Encoder* encoder,
|
| + EncoderTester* tester,
|
| + scoped_refptr<CaptureData> data,
|
| + const gfx::Rect* rects, int count) {
|
| + data->mutable_dirty_rects().clear();
|
| + data->mutable_dirty_rects().insert(
|
| + data->mutable_dirty_rects().begin(), rects, rects + count);
|
| + tester->AddRects(rects, count);
|
| +
|
| + encoder->Encode(data, true,
|
| + NewCallback(tester, &EncoderTester::DataAvailable));
|
| +}
|
| +
|
| +void TestEncoder(Encoder* encoder, bool strict) {
|
| + EncoderMessageTester message_tester;
|
| + message_tester.set_strict(strict);
|
| +
|
| + EncoderStateTester state_tester;
|
| + EncoderTester tester(&message_tester, &state_tester);
|
| +
|
| + uint8* memory;
|
| + scoped_refptr<CaptureData> data =
|
| + PrepareEncodeData(PixelFormatRgb32, &memory);
|
| +
|
| + TestEncodingRects(encoder, &tester, data, kTestRects, 1);
|
| + TestEncodingRects(encoder, &tester, data, kTestRects + 1, 1);
|
| + TestEncodingRects(encoder, &tester, data, kTestRects + 2, 1);
|
| + TestEncodingRects(encoder, &tester, data, kTestRects + 3, 2);
|
| + delete memory;
|
| +}
|
| +
|
| +static void TestEncodingRects(Encoder* encoder,
|
| + EncoderTester* encoder_tester,
|
| + DecoderTester* decoder_tester,
|
| + scoped_refptr<CaptureData> data,
|
| + const gfx::Rect* rects, int count) {
|
| + data->mutable_dirty_rects().clear();
|
| + data->mutable_dirty_rects().insert(
|
| + data->mutable_dirty_rects().begin(), rects, rects + count);
|
| + encoder_tester->AddRects(rects, count);
|
| + decoder_tester->AddRects(rects, count);
|
| + decoder_tester->reset_decode_done();
|
| +
|
| + encoder->Encode(data, true,
|
| + NewCallback(encoder_tester, &EncoderTester::DataAvailable));
|
| + EXPECT_TRUE(decoder_tester->decode_done());
|
| +}
|
| +
|
| +void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict) {
|
| + EncoderMessageTester message_tester;
|
| + message_tester.set_strict(strict);
|
| +
|
| + EncoderStateTester state_tester;
|
| + EncoderTester encoder_tester(&message_tester, &state_tester);
|
| +
|
| + uint8* memory;
|
| + scoped_refptr<CaptureData> data =
|
| + PrepareEncodeData(PixelFormatRgb32, &memory);
|
| + DecoderTester decoder_tester(decoder);
|
| + decoder_tester.set_strict(strict);
|
| + decoder_tester.set_capture_data(data);
|
| + encoder_tester.set_decoder_tester(&decoder_tester);
|
| +
|
| + TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
|
| + kTestRects, 1);
|
| + TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
|
| + kTestRects + 1, 1);
|
| + TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
|
| + kTestRects + 2, 1);
|
| + TestEncodingRects(encoder, &encoder_tester, &decoder_tester, data,
|
| + kTestRects + 3, 2);
|
| + delete memory;
|
| +}
|
| +
|
| +} // namespace remoting
|
| +
|
| +DISABLE_RUNNABLE_METHOD_REFCOUNT(remoting::DecoderTester);
|
|
|