// Copyright (c) 2011 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 <string>
#include "base/bind.h"
#include "base/callback.h"
#include "base/memory/scoped_ptr.h"
#include "net/base/io_buffer.h"
#include "remoting/base/compound_buffer.h"
#include "testing/gtest/include/gtest/gtest.h"
using net::IOBuffer;
namespace remoting {
namespace {
const int kDataSize = 1024;
// Chunk sizes used to append and prepend data to the buffer.
const int kChunkSizes0[] = {kDataSize, -1};
const int kChunkSizes1[] = {1, 10, 20, -1};
// Chunk sizes used to test CopyFrom().
const int kCopySizes0[] = {10, 3, -1};
const int kCopySizes1[] = {20, -1};
const int kCropSizes[] = {1, -1};
} // namespace
class CompoundBufferTest : public testing::Test {
public:
// Following 5 methods are used with IterateOverPieces().
void Append(int pos, int size) {
target_.Append(data_.get(), data_->data() + pos, size);
}
void AppendCopyOf(int pos, int size) {
target_.AppendCopyOf(data_->data() + pos, size);
}
void Prepend(int pos, int size) {
target_.Prepend(data_.get(), data_->data() + kDataSize - pos - size, size);
}
void PrependCopyOf(int pos, int size) {
target_.PrependCopyOf(data_->data() + (kDataSize - pos - size), size);
}
void TestCopyFrom(int pos, int size) {
CompoundBuffer copy;
copy.CopyFrom(target_, pos, pos + size);
EXPECT_TRUE(CompareData(copy, data_->data() + pos, size));
}
void TestCropFront(int pos, int size) {
CompoundBuffer cropped;
cropped.CopyFrom(target_, 0, target_.total_bytes());
cropped.CropFront(pos);
EXPECT_TRUE(CompareData(cropped, data_->data() + pos,
target_.total_bytes() - pos));
}
void TestCropBack(int pos, int size) {
CompoundBuffer cropped;
cropped.CopyFrom(target_, 0, target_.total_bytes());
cropped.CropBack(pos);
EXPECT_TRUE(CompareData(cropped, data_->data(),
target_.total_bytes() - pos));
}
protected:
virtual void SetUp() {
data_ = new IOBuffer(kDataSize);
for (int i = 0; i < kDataSize; ++i) {
data_->data()[i] = i;
}
}
// Iterate over chunks of data with sizes specified in |sizes| in the
// interval [0..kDataSize]. |function| is called for each chunk.
void IterateOverPieces(const int sizes[],
const base::Callback<void(int, int)>& function) {
DCHECK_GT(sizes[0], 0);
int pos = 0;
int index = 0;
while (pos < kDataSize) {
int size = std::min(sizes[index], kDataSize - pos);
++index;
if (sizes[index] <= 0)
index = 0;
function.Run(pos, size);
pos += size;
}
}
bool CompareData(const CompoundBuffer& buffer, char* data, int size) {
scoped_refptr<IOBuffer> buffer_data = buffer.ToIOBufferWithSize();
return buffer.total_bytes() == size &&
memcmp(buffer_data->data(), data, size) == 0;
}
static size_t ReadFromInput(CompoundBufferInputStream* input,
void* data, size_t size) {
uint8* out = reinterpret_cast<uint8*>(data);
int out_size = size;
const void* in;
int in_size = 0;
while (true) {
if (!input->Next(&in, &in_size)) {
return size - out_size;
}
EXPECT_GT(in_size, -1);
if (out_size <= in_size) {
memcpy(out, in, out_size);
if (in_size > out_size) {
input->BackUp(in_size - out_size);
}
return size; // Copied all of it.
}
memcpy(out, in, in_size);
out += in_size;
out_size -= in_size;
}
}
static void ReadString(CompoundBufferInputStream* input,
const std::string& str) {
SCOPED_TRACE(str);
scoped_ptr<char[]> buffer(new char[str.size() + 1]);
buffer[str.size()] = '\0';
EXPECT_EQ(ReadFromInput(input, buffer.get(), str.size()), str.size());
EXPECT_STREQ(str.data(), buffer.get());
}
// Construct and prepare data in the |buffer|.
static void PrepareData(scoped_ptr<CompoundBuffer>* buffer) {
static const std::string kTestData =
"Hello world!"
"This is testing"
"MultipleArrayInputStream"
"for Chromoting";
// Determine how many segments to split kTestData. We split the data in
// 1 character, 2 characters, 1 character, 2 characters ...
int segments = (kTestData.length() / 3) * 2;
int remaining_chars = kTestData.length() % 3;
if (remaining_chars) {
if (remaining_chars == 1)
++segments;
else
segments += 2;
}
CompoundBuffer* result = new CompoundBuffer();
const char* data = kTestData.data();
for (int i = 0; i < segments; ++i) {
int size = i % 2 == 0 ? 1 : 2;
result->Append(new net::WrappedIOBuffer(data), size);
data += size;
}
result->Lock();
buffer->reset(result);
}
CompoundBuffer target_;
scoped_refptr<IOBuffer> data_;
};
TEST_F(CompoundBufferTest, Append) {
target_.Clear();
IterateOverPieces(kChunkSizes0, base::Bind(
&CompoundBufferTest::Append, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::Append, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
}
TEST_F(CompoundBufferTest, AppendCopyOf) {
target_.Clear();
IterateOverPieces(kChunkSizes0, base::Bind(
&CompoundBufferTest::AppendCopyOf, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::AppendCopyOf, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
}
TEST_F(CompoundBufferTest, Prepend) {
target_.Clear();
IterateOverPieces(kChunkSizes0, base::Bind(
&CompoundBufferTest::Prepend, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::Prepend, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
}
TEST_F(CompoundBufferTest, PrependCopyOf) {
target_.Clear();
IterateOverPieces(kChunkSizes0, base::Bind(
&CompoundBufferTest::PrependCopyOf, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::PrependCopyOf, base::Unretained(this)));
EXPECT_TRUE(CompareData(target_, data_->data(), kDataSize));
}
TEST_F(CompoundBufferTest, CropFront) {
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::Append, base::Unretained(this)));
IterateOverPieces(kCropSizes, base::Bind(
&CompoundBufferTest::TestCropFront, base::Unretained(this)));
}
TEST_F(CompoundBufferTest, CropBack) {
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::Append, base::Unretained(this)));
IterateOverPieces(kCropSizes, base::Bind(
&CompoundBufferTest::TestCropBack, base::Unretained(this)));
}
TEST_F(CompoundBufferTest, CopyFrom) {
target_.Clear();
IterateOverPieces(kChunkSizes1, base::Bind(
&CompoundBufferTest::Append, base::Unretained(this)));
{
SCOPED_TRACE("CopyFrom.kCopySizes0");
IterateOverPieces(kCopySizes0, base::Bind(
&CompoundBufferTest::TestCopyFrom, base::Unretained(this)));
}
{
SCOPED_TRACE("CopyFrom.kCopySizes1");
IterateOverPieces(kCopySizes1, base::Bind(
&CompoundBufferTest::TestCopyFrom, base::Unretained(this)));
}
}
TEST_F(CompoundBufferTest, InputStream) {
scoped_ptr<CompoundBuffer> buffer;
PrepareData(&buffer);
CompoundBufferInputStream stream(buffer.get());
ReadString(&stream, "Hello world!");
ReadString(&stream, "This ");
ReadString(&stream, "is test");
EXPECT_TRUE(stream.Skip(3));
ReadString(&stream, "MultipleArrayInput");
EXPECT_TRUE(stream.Skip(6));
ReadString(&stream, "f");
ReadString(&stream, "o");
ReadString(&stream, "r");
ReadString(&stream, " ");
ReadString(&stream, "Chromoting");
}
} // namespace remoting