summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-24 02:22:07 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-24 02:22:07 +0000
commitfb13c766d30c440a7b9a2850ceee5fc49a8903dc (patch)
tree1a6d425cdf3e8e09c7685e983932bc59f299d7b9 /remoting
parent7fb32637bd9968a4c4c9e1d0a08e9b3501b0f7f8 (diff)
downloadchromium_src-fb13c766d30c440a7b9a2850ceee5fc49a8903dc.zip
chromium_src-fb13c766d30c440a7b9a2850ceee5fc49a8903dc.tar.gz
chromium_src-fb13c766d30c440a7b9a2850ceee5fc49a8903dc.tar.bz2
Unit test for Encoder / Decoder for remoting
Unit test utils for Encoder and Decoder. The tests will cover the state and messages sent out of the Encoder and Decoder are in correct order. It also make sure Decoded output matches input to the Encoder. Review URL: http://codereview.chromium.org/3011010 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@53566 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/base/codec_test.cc390
-rw-r--r--remoting/base/codec_test.h37
-rw-r--r--remoting/base/decoder_verbatim.h2
-rw-r--r--remoting/base/decoder_verbatim_unittest.cc12
-rw-r--r--remoting/base/encoder_verbatim.cc10
-rw-r--r--remoting/base/encoder_verbatim_unittest.cc16
-rw-r--r--remoting/remoting.gyp3
7 files changed, 464 insertions, 6 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);
diff --git a/remoting/base/codec_test.h b/remoting/base/codec_test.h
new file mode 100644
index 0000000..d5702dd
--- /dev/null
+++ b/remoting/base/codec_test.h
@@ -0,0 +1,37 @@
+// 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.
+
+#ifndef REMOTING_BASE_CODEC_TEST_H_
+#define REMOTING_BASE_CODEC_TEST_H_
+
+#include "base/ref_counted.h"
+#include "media/base/video_frame.h"
+#include "remoting/base/capture_data.h"
+
+namespace remoting {
+
+class Decoder;
+class Encoder;
+
+// Prepare testing data for encoding. Memory created is written to |memory|.
+// Returns randomly generated data in CaptureData.
+scoped_refptr<CaptureData> PrepareEncodeData(PixelFormat format,
+ uint8** memory);
+
+// Generate test data and test the encoder for a regular encoding sequence.
+// This will test encoder test and the sequence of messages sent.
+//
+// If |strict| is set to true then this routine will make sure the updated
+// rects match dirty rects.
+void TestEncoder(Encoder* encoder, bool strict);
+
+// Generate test data and test the encoder and decoder pair.
+//
+// If |strict| is set to true, this routine will make sure the updated rects
+// are correct.
+void TestEncoderDecoder(Encoder* encoder, Decoder* decoder, bool strict);
+
+} // namespace remoting
+
+#endif // REMOTING_BASE_CODEC_TEST_H_
diff --git a/remoting/base/decoder_verbatim.h b/remoting/base/decoder_verbatim.h
index ba97b5e..22543f9 100644
--- a/remoting/base/decoder_verbatim.h
+++ b/remoting/base/decoder_verbatim.h
@@ -21,6 +21,8 @@ class DecoderVerbatim : public Decoder {
virtual bool PartialDecode(HostMessage* message);
virtual void EndDecode();
+ void set_reverse_rows(bool reverse) { reverse_rows_ = reverse; }
+
private:
bool HandleBeginRect(HostMessage* message);
bool HandleRectData(HostMessage* message);
diff --git a/remoting/base/decoder_verbatim_unittest.cc b/remoting/base/decoder_verbatim_unittest.cc
index f32b08c..947750a 100644
--- a/remoting/base/decoder_verbatim_unittest.cc
+++ b/remoting/base/decoder_verbatim_unittest.cc
@@ -3,7 +3,9 @@
// found in the LICENSE file.
#include "media/base/video_frame.h"
+#include "remoting/base/codec_test.h"
#include "remoting/base/decoder_verbatim.h"
+#include "remoting/base/encoder_verbatim.h"
#include "remoting/client/mock_objects.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -59,9 +61,6 @@ TEST(DecoderVerbatimTest, SimpleDecode) {
EXPECT_EQ(kWidth, frame->width());
EXPECT_EQ(kHeight, frame->height());
EXPECT_EQ(media::VideoFrame::ASCII, frame->format());
- // TODO(hclam): Enable this line.
- // EXPECT_EQ(0, memcmp(kData, frame->data(media::VideoFrame::kRGBPlane),
- // sizeof(kData)));
// Check the updated rects.
ASSERT_TRUE(rects.size() == 1);
@@ -69,4 +68,11 @@ TEST(DecoderVerbatimTest, SimpleDecode) {
EXPECT_EQ(kHeight, static_cast<size_t>(rects[0].height()));
}
+TEST(DecoderVerbatimTest, EncodeAndDecode) {
+ EncoderVerbatim encoder;
+ DecoderVerbatim decoder;
+ decoder.set_reverse_rows(false);
+ TestEncoderDecoder(&encoder, &decoder, true);
+}
+
} // namespace remoting
diff --git a/remoting/base/encoder_verbatim.cc b/remoting/base/encoder_verbatim.cc
index f6cc2ca..509daf9 100644
--- a/remoting/base/encoder_verbatim.cc
+++ b/remoting/base/encoder_verbatim.cc
@@ -71,14 +71,18 @@ bool EncoderVerbatim::EncodeRect(
&((*packet->mutable_rect_data()->mutable_data())[0]));
for (int i = 0; i < DataPlanes::kPlaneCount; ++i) {
- const uint8* in = capture_data->data_planes().data[i];
// Skip over planes that don't have data.
- if (!in) continue;
+ if (!capture_data->data_planes().data[i])
+ continue;
+
+ const uint8* in = capture_data->data_planes().data[i] +
+ y * capture_data->data_planes().strides[i] +
+ x * bytes_per_pixel;
// TODO(hclam): Handle YUV since the height would be different.
for (int j = 0; j < height; ++j) {
DCHECK_LE(row_size, capture_data->data_planes().strides[i]);
- memcpy(out, in, row_size);
+ memcpy(out, in, width * bytes_per_pixel);
in += capture_data->data_planes().strides[i];
out += row_size;
}
diff --git a/remoting/base/encoder_verbatim_unittest.cc b/remoting/base/encoder_verbatim_unittest.cc
new file mode 100644
index 0000000..1d4735c
--- /dev/null
+++ b/remoting/base/encoder_verbatim_unittest.cc
@@ -0,0 +1,16 @@
+// 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 "remoting/base/codec_test.h"
+#include "remoting/base/encoder_verbatim.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace remoting {
+
+TEST(EncoderVerbatimTest, TestEncoder) {
+ EncoderVerbatim encoder;
+ TestEncoder(&encoder, true);
+}
+
+} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 3c601d3..c7edcea 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -342,9 +342,12 @@
'../testing/gmock/include',
],
'sources': [
+ 'base/codec_test.cc',
+ 'base/codec_test.h',
'base/compressor_zlib_unittest.cc',
'base/decoder_verbatim_unittest.cc',
'base/decompressor_zlib_unittest.cc',
+ 'base/encoder_verbatim_unittest.cc',
# TODO(hclam): Enable VP8 in the build.
#'base/encoder_vp8_unittest.cc',
'base/mock_objects.h',