// Copyright (c) 2012 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 <cstdlib> #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/time/time.h" #include "media/base/data_buffer.h" #include "media/base/seekable_buffer.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { class SeekableBufferTest : public testing::Test { public: SeekableBufferTest() : buffer_(kBufferSize, kBufferSize) { } protected: static const int kDataSize = 409600; static const int kBufferSize = 4096; static const int kWriteSize = 512; virtual void SetUp() { // Note: We use srand() and rand() rather than base::RandXXX() to improve // unit test performance. We don't need good random numbers, just // something that generates "mixed data." const unsigned int kKnownSeed = 0x98765432; srand(kKnownSeed); // Create random test data samples. for (int i = 0; i < kDataSize; i++) data_[i] = static_cast<char>(rand()); } int GetRandomInt(int maximum) { return rand() % (maximum + 1); } SeekableBuffer buffer_; uint8 data_[kDataSize]; uint8 write_buffer_[kDataSize]; }; TEST_F(SeekableBufferTest, RandomReadWrite) { int write_position = 0; int read_position = 0; while (read_position < kDataSize) { // Write a random amount of data. int write_size = GetRandomInt(kBufferSize); write_size = std::min(write_size, kDataSize - write_position); bool should_append = buffer_.Append(data_ + write_position, write_size); write_position += write_size; EXPECT_GE(write_position, read_position); EXPECT_EQ(write_position - read_position, buffer_.forward_bytes()); EXPECT_EQ(should_append, buffer_.forward_bytes() < kBufferSize) << "Incorrect buffer full reported"; // Peek a random amount of data. int copy_size = GetRandomInt(kBufferSize); int bytes_copied = buffer_.Peek(write_buffer_, copy_size); EXPECT_GE(copy_size, bytes_copied); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, bytes_copied)); // Read a random amount of data. int read_size = GetRandomInt(kBufferSize); int bytes_read = buffer_.Read(write_buffer_, read_size); EXPECT_GE(read_size, bytes_read); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, bytes_read)); read_position += bytes_read; EXPECT_GE(write_position, read_position); EXPECT_EQ(write_position - read_position, buffer_.forward_bytes()); } } TEST_F(SeekableBufferTest, ReadWriteSeek) { const int kReadSize = kWriteSize / 4; for (int i = 0; i < 10; ++i) { // Write until buffer is full. for (int j = 0; j < kBufferSize; j += kWriteSize) { bool should_append = buffer_.Append(data_ + j, kWriteSize); EXPECT_EQ(j < kBufferSize - kWriteSize, should_append) << "Incorrect buffer full reported"; EXPECT_EQ(j + kWriteSize, buffer_.forward_bytes()); } // Simulate a read and seek pattern. Each loop reads 4 times, each time // reading a quarter of |kWriteSize|. int read_position = 0; int forward_bytes = kBufferSize; for (int j = 0; j < kBufferSize; j += kWriteSize) { // Read. EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); forward_bytes -= kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); read_position += kReadSize; // Seek forward. EXPECT_TRUE(buffer_.Seek(2 * kReadSize)); forward_bytes -= 2 * kReadSize; read_position += 2 * kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); // Copy. EXPECT_EQ(kReadSize, buffer_.Peek(write_buffer_, kReadSize)); EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); // Read. EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); forward_bytes -= kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); read_position += kReadSize; // Seek backward. EXPECT_TRUE(buffer_.Seek(-3 * static_cast<int32>(kReadSize))); forward_bytes += 3 * kReadSize; read_position -= 3 * kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); // Copy. EXPECT_EQ(kReadSize, buffer_.Peek(write_buffer_, kReadSize)); EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); // Read. EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); forward_bytes -= kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); read_position += kReadSize; // Copy. EXPECT_EQ(kReadSize, buffer_.Peek(write_buffer_, kReadSize)); EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); // Read. EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); forward_bytes -= kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, kReadSize)); read_position += kReadSize; // Seek forward. EXPECT_TRUE(buffer_.Seek(kReadSize)); forward_bytes -= kReadSize; read_position += kReadSize; EXPECT_EQ(forward_bytes, buffer_.forward_bytes()); } } } TEST_F(SeekableBufferTest, BufferFull) { const int kMaxWriteSize = 2 * kBufferSize; // Write and expect the buffer to be not full. for (int i = 0; i < kBufferSize - kWriteSize; i += kWriteSize) { EXPECT_TRUE(buffer_.Append(data_ + i, kWriteSize)); EXPECT_EQ(i + kWriteSize, buffer_.forward_bytes()); } // Write until we have kMaxWriteSize bytes in the buffer. Buffer is full in // these writes. for (int i = buffer_.forward_bytes(); i < kMaxWriteSize; i += kWriteSize) { EXPECT_FALSE(buffer_.Append(data_ + i, kWriteSize)); EXPECT_EQ(i + kWriteSize, buffer_.forward_bytes()); } // Read until the buffer is empty. int read_position = 0; while (buffer_.forward_bytes()) { // Read a random amount of data. int read_size = GetRandomInt(kBufferSize); int forward_bytes = buffer_.forward_bytes(); int bytes_read = buffer_.Read(write_buffer_, read_size); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, bytes_read)); if (read_size > forward_bytes) EXPECT_EQ(forward_bytes, bytes_read); else EXPECT_EQ(read_size, bytes_read); read_position += bytes_read; EXPECT_GE(kMaxWriteSize, read_position); EXPECT_EQ(kMaxWriteSize - read_position, buffer_.forward_bytes()); } // Expects we have no bytes left. EXPECT_EQ(0, buffer_.forward_bytes()); EXPECT_EQ(0, buffer_.Read(write_buffer_, 1)); } TEST_F(SeekableBufferTest, SeekBackward) { EXPECT_EQ(0, buffer_.forward_bytes()); EXPECT_EQ(0, buffer_.backward_bytes()); EXPECT_FALSE(buffer_.Seek(1)); EXPECT_FALSE(buffer_.Seek(-1)); const int kReadSize = 256; // Write into buffer until it's full. for (int i = 0; i < kBufferSize; i += kWriteSize) { // Write a random amount of data. buffer_.Append(data_ + i, kWriteSize); } // Read until buffer is empty. for (int i = 0; i < kBufferSize; i += kReadSize) { EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); EXPECT_EQ(0, memcmp(write_buffer_, data_ + i, kReadSize)); } // Seek backward. EXPECT_TRUE(buffer_.Seek(-static_cast<int32>(kBufferSize))); EXPECT_FALSE(buffer_.Seek(-1)); // Read again. for (int i = 0; i < kBufferSize; i += kReadSize) { EXPECT_EQ(kReadSize, buffer_.Read(write_buffer_, kReadSize)); EXPECT_EQ(0, memcmp(write_buffer_, data_ + i, kReadSize)); } } TEST_F(SeekableBufferTest, GetCurrentChunk) { const int kSeekSize = kWriteSize / 3; scoped_refptr<DataBuffer> buffer = DataBuffer::CopyFrom(data_, kWriteSize); const uint8* data; int size; EXPECT_FALSE(buffer_.GetCurrentChunk(&data, &size)); buffer_.Append(buffer.get()); EXPECT_TRUE(buffer_.GetCurrentChunk(&data, &size)); EXPECT_EQ(data, buffer->data()); EXPECT_EQ(size, buffer->data_size()); buffer_.Seek(kSeekSize); EXPECT_TRUE(buffer_.GetCurrentChunk(&data, &size)); EXPECT_EQ(data, buffer->data() + kSeekSize); EXPECT_EQ(size, buffer->data_size() - kSeekSize); } TEST_F(SeekableBufferTest, SeekForward) { int write_position = 0; int read_position = 0; while (read_position < kDataSize) { for (int i = 0; i < 10 && write_position < kDataSize; ++i) { // Write a random amount of data. int write_size = GetRandomInt(kBufferSize); write_size = std::min(write_size, kDataSize - write_position); bool should_append = buffer_.Append(data_ + write_position, write_size); write_position += write_size; EXPECT_GE(write_position, read_position); EXPECT_EQ(write_position - read_position, buffer_.forward_bytes()); EXPECT_EQ(should_append, buffer_.forward_bytes() < kBufferSize) << "Incorrect buffer full status reported"; } // Read a random amount of data. int seek_size = GetRandomInt(kBufferSize); if (buffer_.Seek(seek_size)) read_position += seek_size; EXPECT_GE(write_position, read_position); EXPECT_EQ(write_position - read_position, buffer_.forward_bytes()); // Read a random amount of data. int read_size = GetRandomInt(kBufferSize); int bytes_read = buffer_.Read(write_buffer_, read_size); EXPECT_GE(read_size, bytes_read); EXPECT_EQ(0, memcmp(write_buffer_, data_ + read_position, bytes_read)); read_position += bytes_read; EXPECT_GE(write_position, read_position); EXPECT_EQ(write_position - read_position, buffer_.forward_bytes()); } } TEST_F(SeekableBufferTest, AllMethods) { EXPECT_EQ(0, buffer_.Read(write_buffer_, 0)); EXPECT_EQ(0, buffer_.Read(write_buffer_, 1)); EXPECT_TRUE(buffer_.Seek(0)); EXPECT_FALSE(buffer_.Seek(-1)); EXPECT_FALSE(buffer_.Seek(1)); EXPECT_EQ(0, buffer_.forward_bytes()); EXPECT_EQ(0, buffer_.backward_bytes()); } TEST_F(SeekableBufferTest, GetTime) { const int64 kNoTS = kNoTimestamp().ToInternalValue(); const struct { int64 first_time_useconds; int64 duration_useconds; int consume_bytes; int64 expected_time; } tests[] = { { kNoTS, 1000000, 0, kNoTS }, { kNoTS, 4000000, 0, kNoTS }, { kNoTS, 8000000, 0, kNoTS }, { kNoTS, 1000000, kWriteSize / 2, kNoTS }, { kNoTS, 4000000, kWriteSize / 2, kNoTS }, { kNoTS, 8000000, kWriteSize / 2, kNoTS }, { kNoTS, 1000000, kWriteSize, kNoTS }, { kNoTS, 4000000, kWriteSize, kNoTS }, { kNoTS, 8000000, kWriteSize, kNoTS }, { 0, 1000000, 0, 0 }, { 0, 4000000, 0, 0 }, { 0, 8000000, 0, 0 }, { 0, 1000000, kWriteSize / 2, 500000 }, { 0, 4000000, kWriteSize / 2, 2000000 }, { 0, 8000000, kWriteSize / 2, 4000000 }, { 0, 1000000, kWriteSize, 1000000 }, { 0, 4000000, kWriteSize, 4000000 }, { 0, 8000000, kWriteSize, 8000000 }, { 5, 1000000, 0, 5 }, { 5, 4000000, 0, 5 }, { 5, 8000000, 0, 5 }, { 5, 1000000, kWriteSize / 2, 500005 }, { 5, 4000000, kWriteSize / 2, 2000005 }, { 5, 8000000, kWriteSize / 2, 4000005 }, { 5, 1000000, kWriteSize, 1000005 }, { 5, 4000000, kWriteSize, 4000005 }, { 5, 8000000, kWriteSize, 8000005 }, }; // current_time() must initially return kNoTimestamp(). EXPECT_EQ(kNoTimestamp().ToInternalValue(), buffer_.current_time().ToInternalValue()); scoped_refptr<DataBuffer> buffer = DataBuffer::CopyFrom(data_, kWriteSize); for (size_t i = 0; i < ARRAYSIZE_UNSAFE(tests); ++i) { buffer->set_timestamp(base::TimeDelta::FromMicroseconds( tests[i].first_time_useconds)); buffer->set_duration(base::TimeDelta::FromMicroseconds( tests[i].duration_useconds)); buffer_.Append(buffer.get()); EXPECT_TRUE(buffer_.Seek(tests[i].consume_bytes)); int64 actual = buffer_.current_time().ToInternalValue(); EXPECT_EQ(tests[i].expected_time, actual) << "With test = { start:" << tests[i].first_time_useconds << ", duration:" << tests[i].duration_useconds << ", consumed:" << tests[i].consume_bytes << " }\n"; buffer_.Clear(); } } } // namespace media