summaryrefslogtreecommitdiffstats
path: root/remoting/base/codec_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/base/codec_test.cc')
-rw-r--r--remoting/base/codec_test.cc390
1 files changed, 390 insertions, 0 deletions
diff --git a/remoting/base/codec_test.cc b/remoting/base/codec_test.cc
new file mode 100644
index 0000000..b97293f
--- /dev/null
+++ b/remoting/base/codec_test.cc
@@ -0,0 +1,390 @@
+// 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_update_stream_packet()) {
+ decoder_->PartialDecode(message);
+ return;
+ }
+
+ if (message->has_begin_update_stream()) {
+ decoder_->BeginDecode(
+ frame_, &update_rects_,
+ NewRunnableMethod(this, &DecoderTester::OnPartialDecodeDone),
+ NewRunnableMethod(this, &DecoderTester::OnDecodeDone));
+ }
+
+ if (message->has_end_update_stream()) {
+ decoder_->EndDecode();
+ }
+ delete message;
+ return;
+ }
+
+ 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),
+ decoder_tester_(NULL),
+ 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);
+ }
+ } else {
+ 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);