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); |