blob: 209ec743b1ef818007fbfa57faa9c6ba79f56b91 [file] [log] [blame]
/*
* Copyright 2014 Google Inc. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "base/circular_buffer_shell.h"
#include <string.h>
#include "base/memory/scoped_ptr.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace {
// 100 characters, repeating every 16 characters.
const char kTestData[] =
"0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"
"01234567890ABCDEF0123456789ABCDEF0123";
// 100 characters, repeating every 17 characters.
#define UNSET_DATA \
"GHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVW" \
"GHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTUVWGHIJKLMNOPQRSTU"
const char kUnsetData[] = UNSET_DATA;
const size_t kUnsetSize = 1024;
// Like memcmp, but reports which index and values failed.
void IsSame(const char* expected, const char* actual, size_t length) {
for (size_t i = 0; i < length; ++i) {
if (expected[i] != actual[i]) {
EXPECT_EQ(expected[i], actual[i]) << "at " << i;
return;
}
}
}
size_t read_pos = 0;
size_t write_pos = 0;
// If the test uses testWrite and TestRead, then it needs to call ClearPos to
// avoid contamination from previous tests.
void ClearPos() {
read_pos = 0;
write_pos = 0;
}
void TestWrite(base::CircularBufferShell* circular_buffer, size_t to_write) {
size_t before_length = circular_buffer->GetLength();
size_t bytes_written = kUnsetSize;
bool result =
circular_buffer->Write(kTestData + write_pos, to_write, &bytes_written);
EXPECT_EQ(true, result);
EXPECT_EQ(to_write, bytes_written);
EXPECT_EQ(before_length + to_write, circular_buffer->GetLength());
write_pos += to_write;
}
void TestRead(base::CircularBufferShell* circular_buffer, size_t to_read) {
size_t before_length = circular_buffer->GetLength();
char data[] = UNSET_DATA UNSET_DATA;
char* buffer = data + strlen(kUnsetData);
size_t bytes_read = kUnsetSize;
circular_buffer->Read(buffer, to_read, &bytes_read);
EXPECT_EQ(to_read, bytes_read);
EXPECT_EQ(before_length - to_read, circular_buffer->GetLength());
IsSame(kTestData + read_pos, buffer, to_read);
IsSame(kUnsetData + to_read, buffer + to_read, to_read);
IsSame(kUnsetData + strlen(kUnsetData) - to_read,
buffer - to_read, to_read);
read_pos += to_read;
}
} // namespace
// --- Sunny Day Tests ---
TEST(CircularBufferShellTest, Construct) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
}
TEST(CircularBufferShellTest, SimpleWriteAndRead) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
TestWrite(circular_buffer.get(), 15);
TestRead(circular_buffer.get(), 5);
TestRead(circular_buffer.get(), 4);
TestRead(circular_buffer.get(), 3);
TestRead(circular_buffer.get(), 2);
TestRead(circular_buffer.get(), 1);
}
TEST(CircularBufferShellTest, ReadWriteOverBoundary) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
// Fill the buffer.
TestWrite(circular_buffer.get(), 20);
// Read half the data from the front.
TestRead(circular_buffer.get(), 10);
// Fill the back half, making the data wrap around the end.
TestWrite(circular_buffer.get(), 10);
// Read the whole thing, which should require two memcpys.
TestRead(circular_buffer.get(), 20);
// Fill the buffer, again around the end, should require two memcpys.
TestWrite(circular_buffer.get(), 20);
// Read the buffer to verify, should again require two memcpys.
TestRead(circular_buffer.get(), 20);
}
TEST(CircularBufferShellTest, ExpandWhileNotWrapped) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
// Set the size with the first write.
TestWrite(circular_buffer.get(), 5);
// Expand with the second write.
TestWrite(circular_buffer.get(), 5);
// Read to verify the data is intact
TestRead(circular_buffer.get(), 10);
}
TEST(CircularBufferShellTest, ExpandWhileNotWrapped2) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
// Set the size with the first write.
TestWrite(circular_buffer.get(), 5);
// Read a couple out so that the data doesn't start at the beginning of the
// buffer.
TestRead(circular_buffer.get(), 2);
// Expand with the second write.
TestWrite(circular_buffer.get(), 5);
// Read to verify the data is intact
TestRead(circular_buffer.get(), 8);
}
TEST(CircularBufferShellTest, ExpandWhileWrapped) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
// Set the size with the first write.
TestWrite(circular_buffer.get(), 10);
// Read front half.
TestRead(circular_buffer.get(), 5);
// Wrap with second write
TestWrite(circular_buffer.get(), 5);
// Write again to expand while wrapped.
TestWrite(circular_buffer.get(), 5);
// Read to verify the data is intact
TestRead(circular_buffer.get(), 15);
}
// --- Rainy Day Tests ---
TEST(CircularBufferShellTest, WriteTooMuch) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
{
size_t bytes_written = kUnsetSize;
bool result = circular_buffer->Write(kTestData, 25, &bytes_written);
EXPECT_EQ(false, result);
EXPECT_EQ(kUnsetSize, bytes_written);
EXPECT_EQ(0, circular_buffer->GetLength());
}
}
TEST(CircularBufferShellTest, ReadEmpty) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
EXPECT_EQ(circular_buffer->GetLength(), 0);
{
char buffer[] = UNSET_DATA;
size_t bytes_read = kUnsetSize;
circular_buffer->Read(buffer, 0, &bytes_read);
EXPECT_EQ(0, bytes_read);
EXPECT_EQ(0, circular_buffer->GetLength());
IsSame(kUnsetData, buffer, 10);
}
{
char buffer[] = UNSET_DATA;
size_t bytes_read = kUnsetSize;
circular_buffer->Read(buffer, 10, &bytes_read);
EXPECT_EQ(0, bytes_read);
EXPECT_EQ(0, circular_buffer->GetLength());
IsSame(kUnsetData, buffer, 10);
}
}
TEST(CircularBufferShellTest, ReadToNull) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(20));
{
size_t bytes_read = kUnsetSize;
circular_buffer->Read(NULL, 0, &bytes_read);
EXPECT_EQ(0, bytes_read);
}
}
TEST(CircularBufferShellTest, Peek) {
const size_t kMaxCapacity = 20;
base::CircularBufferShell circular_buffer(kMaxCapacity);
size_t bytes_peeked;
size_t peek_offset = 0;
circular_buffer.Write(kTestData, kMaxCapacity, NULL);
// Peek with offset 0.
{
char destination[] = UNSET_DATA;
circular_buffer.Peek(destination + 9, 10, peek_offset, &bytes_peeked);
EXPECT_EQ(10, bytes_peeked);
IsSame(UNSET_DATA, destination, 9);
IsSame(UNSET_DATA + 9 + bytes_peeked,
destination + 9 + bytes_peeked,
sizeof(UNSET_DATA) - 9 - bytes_peeked);
IsSame(kTestData, destination + 9, 10);
peek_offset += bytes_peeked;
}
// Peek with non-zero offset.
{
char destination[] = UNSET_DATA;
circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
EXPECT_EQ(7, bytes_peeked);
IsSame(UNSET_DATA, destination, 9);
IsSame(UNSET_DATA + 9 + bytes_peeked,
destination + 9 + bytes_peeked,
sizeof(UNSET_DATA) - 9 - bytes_peeked);
IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
peek_offset += bytes_peeked;
}
// Peek more data than available.
{
char destination[] = UNSET_DATA;
circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
EXPECT_EQ(3, bytes_peeked);
IsSame(UNSET_DATA, destination, 9);
IsSame(UNSET_DATA + 9 + bytes_peeked,
destination + 9 + bytes_peeked,
sizeof(UNSET_DATA) - 9 - bytes_peeked);
IsSame(kTestData + peek_offset, destination + 9, bytes_peeked);
peek_offset += bytes_peeked;
}
// Peek an empty buffer.
{
char destination[] = UNSET_DATA;
circular_buffer.Peek(destination + 9, 7, peek_offset, &bytes_peeked);
IsSame(UNSET_DATA, destination, sizeof(destination));
EXPECT_EQ(0, bytes_peeked);
// Verify that we are actually peeking instead of reading.
EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
}
}
TEST(CircularBufferShellTest, Skip) {
const size_t kMaxCapacity = 20;
base::CircularBufferShell circular_buffer(kMaxCapacity);
char destination[] = UNSET_DATA UNSET_DATA;
size_t bytes;
circular_buffer.Write(kTestData, kMaxCapacity, NULL);
circular_buffer.Skip(10, &bytes);
EXPECT_EQ(10, bytes);
EXPECT_EQ(kMaxCapacity - 10, circular_buffer.GetLength());
circular_buffer.Read(destination, kMaxCapacity, &bytes);
EXPECT_EQ(kMaxCapacity - 10, bytes);
IsSame(kTestData + 10, destination, bytes);
}
TEST(CircularBufferShellTest, PeekWrapped) {
const size_t kMaxCapacity = 20;
base::CircularBufferShell circular_buffer(kMaxCapacity);
char destination[] = UNSET_DATA;
size_t bytes_peeked;
circular_buffer.Write(kTestData, kMaxCapacity, NULL);
// Skip 10 bytes to free some space.
circular_buffer.Skip(10, &bytes_peeked);
// Fill the free space with new data.
circular_buffer.Write(kTestData + kMaxCapacity, 10, NULL);
EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
// Peek with a non-zero offset.
circular_buffer.Peek(destination, kMaxCapacity, 5, &bytes_peeked);
EXPECT_EQ(kMaxCapacity - 5, bytes_peeked);
IsSame(kTestData + 15, destination, bytes_peeked);
}
TEST(CircularBufferShellTest, SkipWrapped) {
const size_t kMaxCapacity = 20;
base::CircularBufferShell circular_buffer(kMaxCapacity);
char destination[] = UNSET_DATA;
size_t bytes;
circular_buffer.Write(kTestData, kMaxCapacity, NULL);
circular_buffer.Skip(10, &bytes);
circular_buffer.Write(kTestData + kMaxCapacity, 10, NULL);
EXPECT_EQ(kMaxCapacity, circular_buffer.GetLength());
circular_buffer.Skip(kMaxCapacity - 5, NULL);
EXPECT_EQ(5, circular_buffer.GetLength());
circular_buffer.Read(destination, 5, &bytes);
EXPECT_EQ(5, bytes);
IsSame(kTestData + kMaxCapacity + 5, destination, 5);
}
// --- Legacy Tests ---
TEST(CircularBufferShellTest, Basic) {
const int max_buffer_length = 10;
const int kReadSize1 = 4;
const int kReadSize2 = 2;
const int kReadSize3 = 4;
const int kReadSize4 = 6;
// Create a Circular Buffer.
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(max_buffer_length));
ASSERT_TRUE(circular_buffer);
EXPECT_EQ(circular_buffer->GetLength(), 0);
char read_buffer[20];
size_t bytes_read = 0;
size_t bytes_written = 0;
// Read 4 bytes, got read_pos 0, write_pos 0
circular_buffer->Read(read_buffer, kReadSize1, &bytes_read);
EXPECT_EQ(bytes_read, 0);
// Write 5 bytes, got read_pos 0, write_pos 5
const char write_buffer[] = "hello";
circular_buffer->Write(write_buffer, strlen(write_buffer), &bytes_written);
EXPECT_EQ(bytes_written, strlen(write_buffer));
EXPECT_EQ(circular_buffer->GetLength(), strlen(write_buffer));
// Write 1 byte, increased buffer size to 10, read_pos 0, write_pos 6
const char write_buffer2[] = " ";
circular_buffer->Write(write_buffer2, strlen(write_buffer2), &bytes_written);
EXPECT_EQ(bytes_written, strlen(write_buffer2));
EXPECT_EQ(circular_buffer->GetLength(),
strlen(write_buffer) + strlen(write_buffer2));
// Read 2 bytes, got read_pos 2, write_pos 6
circular_buffer->Read(read_buffer, kReadSize2, &bytes_read);
EXPECT_EQ(0, memcmp(read_buffer, "he", kReadSize2));
EXPECT_EQ(bytes_read, kReadSize2);
EXPECT_EQ(circular_buffer->GetLength(),
strlen(write_buffer) + strlen(write_buffer2) - kReadSize2);
// Write 6 bytes, got read_pos 2, write_pos 2, full of data
const char write_buffer3[] = "world!";
circular_buffer->Write(write_buffer3, strlen(write_buffer3), &bytes_written);
EXPECT_EQ(bytes_written, strlen(write_buffer3));
EXPECT_EQ(circular_buffer->GetLength(),
strlen(write_buffer) + strlen(write_buffer2) +
strlen(write_buffer3) - kReadSize2);
// Read 4 bytes, got read_pos 6, write_pos 2
circular_buffer->Read(read_buffer, kReadSize3, &bytes_read);
EXPECT_EQ(bytes_read, kReadSize3);
EXPECT_EQ(0, memcmp(read_buffer, "llo ", kReadSize3));
EXPECT_EQ(circular_buffer->GetLength(),
strlen(write_buffer) + strlen(write_buffer2) +
strlen(write_buffer3) - kReadSize2 - kReadSize3);
// Read 6 bytes, got read_pos 2, write_pos 2, empty
circular_buffer->Read(read_buffer, kReadSize4, &bytes_read);
EXPECT_EQ(bytes_read, kReadSize4);
EXPECT_EQ(0, memcmp(read_buffer, "world!", kReadSize4));
EXPECT_EQ(circular_buffer->GetLength(), 0);
}
TEST(CircularBufferShellTest, CycleReadWrite) {
const int max_buffer_length = 5000;
// Create a Circular Buffer.
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(max_buffer_length));
ASSERT_TRUE(circular_buffer);
EXPECT_EQ(circular_buffer->GetLength(), 0);
size_t bytes_written = 0;
size_t bytes_read = 0;
char write_buffer[500];
char read_buffer[2000];
for (int i = 0; i < 50; ++i) {
circular_buffer->Write(write_buffer, sizeof(write_buffer), &bytes_written);
EXPECT_EQ(bytes_written, sizeof(write_buffer));
EXPECT_EQ(circular_buffer->GetLength(), sizeof(write_buffer));
circular_buffer->Write(write_buffer, sizeof(write_buffer), &bytes_written);
EXPECT_EQ(bytes_written, sizeof(write_buffer));
EXPECT_EQ(circular_buffer->GetLength(),
sizeof(write_buffer) + sizeof(write_buffer));
circular_buffer->Read(read_buffer, sizeof(read_buffer), &bytes_read);
EXPECT_EQ(bytes_read, sizeof(write_buffer) + sizeof(write_buffer));
EXPECT_EQ(circular_buffer->GetLength(), 0);
}
}
TEST(CircularBufferShellTest, IncreaseMaxCapacityTo) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(0));
EXPECT_EQ(circular_buffer->GetMaxCapacity(), 0);
// Increase max capacity by 20 to allow for expanding.
circular_buffer->IncreaseMaxCapacityTo(20);
EXPECT_EQ(circular_buffer->GetMaxCapacity(), 20);
// Set the size with the first write.
TestWrite(circular_buffer.get(), 10);
// Expand to the full capacity with the second write.
TestWrite(circular_buffer.get(), 10);
// Verify if the buffer is full by check the failure of writing one byte.
char ch = 'a';
size_t bytes_written = 0;
ASSERT_FALSE(circular_buffer->Write(&ch, 1, &bytes_written));
// Increase max capacity to 30 to allow for further expanding.
circular_buffer->IncreaseMaxCapacityTo(30);
EXPECT_EQ(circular_buffer->GetMaxCapacity(), 30);
// Expand to capacity with the fourth write.
TestWrite(circular_buffer.get(), 10);
// Verify if the buffer is full by check the failure of writing one byte.
ASSERT_FALSE(circular_buffer->Write(&ch, 1, &bytes_written));
// Drain the circular_buffer.
EXPECT_EQ(circular_buffer->GetLength(), 30);
TestRead(circular_buffer.get(), 30);
EXPECT_EQ(circular_buffer->GetLength(), 0);
}
TEST(CircularBufferShellTest, IncreaseMaxCapacityToWrapped) {
ClearPos();
scoped_ptr<base::CircularBufferShell> circular_buffer(
new base::CircularBufferShell(10));
EXPECT_EQ(circular_buffer->GetMaxCapacity(), 10);
// Expand to the full capacity with the first write.
TestWrite(circular_buffer.get(), 10);
// Partial read
TestRead(circular_buffer.get(), 5);
// The buffer is wrapped after the second write.
TestWrite(circular_buffer.get(), 5);
// Verify if the buffer is full by check the failure of writing one byte.
char ch = 'a';
size_t bytes_written = 0;
ASSERT_FALSE(circular_buffer->Write(&ch, 1, &bytes_written));
// Increase max capacity to 20 to allow for further expanding.
circular_buffer->IncreaseMaxCapacityTo(20);
EXPECT_EQ(circular_buffer->GetMaxCapacity(), 20);
// Expand to capacity with the fourth write.
TestWrite(circular_buffer.get(), 10);
// Verify if the buffer is full by check the failure of writing one byte.
ASSERT_FALSE(circular_buffer->Write(&ch, 1, &bytes_written));
// Drain the circular_buffer.
EXPECT_EQ(circular_buffer->GetLength(), 20);
TestRead(circular_buffer.get(), 20);
EXPECT_EQ(circular_buffer->GetLength(), 0);
}