| Index: media/blink/multibuffer_unittest.cc
|
| diff --git a/media/blink/multibuffer_unittest.cc b/media/blink/multibuffer_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..2d1439ca02cf2b803398363bdca9ad0be369602d
|
| --- /dev/null
|
| +++ b/media/blink/multibuffer_unittest.cc
|
| @@ -0,0 +1,498 @@
|
| +// Copyright 2015 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 <string>
|
| +#include <vector>
|
| +
|
| +#include "base/bind.h"
|
| +#include "base/callback_helpers.h"
|
| +#include "media/blink/multibuffer.h"
|
| +#include "media/blink/multibuffer_reader.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +const int kBlockSizeShift = 8;
|
| +const size_t kBlockSize = 1UL << kBlockSizeShift;
|
| +
|
| +namespace media {
|
| +class TestMultiBufferDataProvider;
|
| +
|
| +std::vector<TestMultiBufferDataProvider*> writers;
|
| +
|
| +class TestMultiBufferDataProvider : public media::MultiBuffer::DataProvider {
|
| + public:
|
| + TestMultiBufferDataProvider(MultiBufferBlockId pos,
|
| + size_t file_size,
|
| + int max_blocks_after_defer,
|
| + bool must_read_whole_file) :
|
| + pos_(pos),
|
| + blocks_until_deferred_(1 << 30),
|
| + max_blocks_after_defer_(max_blocks_after_defer),
|
| + file_size_(file_size),
|
| + must_read_whole_file_(must_read_whole_file) {
|
| + writers.push_back(this);
|
| + }
|
| +
|
| + ~TestMultiBufferDataProvider() override {
|
| + if (must_read_whole_file_) {
|
| + CHECK_GE(pos_.block_num() * kBlockSize, file_size_);
|
| + }
|
| + for (size_t i = 0; i < writers.size(); i++) {
|
| + if (writers[i] == this) {
|
| + writers[i] = writers.back();
|
| + writers.pop_back();
|
| + return;
|
| + }
|
| + }
|
| + LOG(FATAL) << "Couldn't find myself in writers!";
|
| + }
|
| +
|
| + MultiBufferBlockId Tell() const override {
|
| + return pos_;
|
| + }
|
| +
|
| + bool Available() const override {
|
| + return !fifo_.empty();
|
| + }
|
| +
|
| + scoped_refptr<DataBuffer> Read() override {
|
| + DCHECK(Available());
|
| + scoped_refptr<DataBuffer> ret = fifo_.front();
|
| + fifo_.pop_front();
|
| + ++pos_;
|
| + return ret;
|
| + }
|
| +
|
| + void SetAvailableCallback(const base::Closure& cb) override {
|
| + DCHECK(!Available());
|
| + cb_ = cb;
|
| + }
|
| +
|
| + void SetDeferred(bool deferred) override {
|
| + if (deferred) {
|
| + if (max_blocks_after_defer_ > 0) {
|
| + blocks_until_deferred_ = rand() % max_blocks_after_defer_;
|
| + } else if (max_blocks_after_defer_ < 0) {
|
| + blocks_until_deferred_ = - max_blocks_after_defer_;
|
| + } else {
|
| + blocks_until_deferred_ = 0;
|
| + }
|
| + } else {
|
| + blocks_until_deferred_ = 1 << 30;
|
| + }
|
| + }
|
| +
|
| + bool Advance() {
|
| + if (blocks_until_deferred_ == 0) return false;
|
| + --blocks_until_deferred_;
|
| +
|
| + bool ret = true;
|
| + scoped_refptr<media::DataBuffer> block = new media::DataBuffer(kBlockSize);
|
| + size_t x = 0;
|
| + size_t byte_pos = (fifo_.size() + pos_.block_num()) * kBlockSize;
|
| + for (x = 0; x < kBlockSize; x++, byte_pos++) {
|
| + if (byte_pos >= file_size_) break;
|
| + block->writable_data()[x] = (byte_pos * 15485863) >> 16;
|
| + }
|
| + block->set_data_size(x);
|
| + fifo_.push_back(block);
|
| + if (byte_pos == file_size_) {
|
| + fifo_.push_back(DataBuffer::CreateEOSBuffer());
|
| + ret = false;
|
| + }
|
| + cb_.Run();
|
| + return ret;
|
| + }
|
| +
|
| + private:
|
| + std::deque<scoped_refptr<media::DataBuffer> > fifo_;
|
| + MultiBufferBlockId pos_;
|
| + int32_t blocks_until_deferred_;
|
| + int32_t max_blocks_after_defer_;
|
| + size_t file_size_;
|
| + bool must_read_whole_file_;
|
| + base::Closure cb_;
|
| +};
|
| +
|
| +class TestMultiBuffer : public media::MultiBuffer {
|
| + public:
|
| + explicit TestMultiBuffer(int32_t shift) :
|
| + media::MultiBuffer(shift),
|
| + create_ok_(true),
|
| + max_writers_(10000),
|
| + file_size_(1 << 30),
|
| + max_blocks_after_defer_(0),
|
| + must_read_whole_file_(false),
|
| + writers_created_(0) {
|
| + }
|
| +
|
| + void SetMaxWriters(size_t max_writers) {
|
| + max_writers_ = max_writers;
|
| + }
|
| +
|
| + void CheckPresentState() {
|
| + MultiBufferBlockId last;
|
| + for (DataMap::iterator i = data_.begin();
|
| + i != data_.end();
|
| + ++i) {
|
| + CHECK(i->second); // Null poineters are not allowed in data_
|
| + CHECK_NE(!!pinned_[i->first], lru_.Contains(i->first))
|
| + << " i->first = " << i->first;
|
| +
|
| + if (!last.SameUrl(i->first)) {
|
| + last = MultiBufferBlockId(i->first.url_data(), 0);
|
| + }
|
| + while (last < i->first) {
|
| + CHECK_EQ(0, present_[last]) << " last = " << last;
|
| + ++last;
|
| + }
|
| + last = i->first;
|
| + CHECK_EQ(1, present_[last]) << " last = " << last;
|
| + ++last;
|
| + }
|
| + }
|
| +
|
| + void CheckLRUState() {
|
| + for (DataMap::iterator i = data_.begin();
|
| + i != data_.end();
|
| + ++i) {
|
| + CHECK(i->second); // Null poineters are not allowed in data_
|
| + CHECK_NE(!!pinned_[i->first], lru_.Contains(i->first))
|
| + << " i->first = " << i->first;
|
| + CHECK_EQ(1, present_[i->first]) << " i->first = " << i->first;
|
| + }
|
| + }
|
| +
|
| + void SetFileSize(size_t file_size) {
|
| + file_size_ = file_size;
|
| + }
|
| +
|
| + void SetMaxBlocksAfterDefer(int32_t max_blocks_after_defer) {
|
| + max_blocks_after_defer_ = max_blocks_after_defer;
|
| + }
|
| +
|
| + void SetMustReadWholeFile(bool must_read_whole_file) {
|
| + must_read_whole_file_ = must_read_whole_file;
|
| + }
|
| +
|
| + int32_t writers_created() const {
|
| + return writers_created_;
|
| + }
|
| + protected:
|
| + DataProvider* CreateWriter(const MultiBufferBlockId& pos) override {
|
| + DCHECK(create_ok_);
|
| + CHECK_LE(writers.size(), max_writers_);
|
| + writers_created_++;
|
| + return new TestMultiBufferDataProvider(
|
| + pos, file_size_, max_blocks_after_defer_, must_read_whole_file_);
|
| + }
|
| + void Prune(size_t max_to_free) override {
|
| + // Prune should not cause additional writers to be spawned.
|
| + create_ok_ = false;
|
| + MultiBuffer::Prune(max_to_free);
|
| + create_ok_ = true;
|
| + }
|
| + private:
|
| + bool create_ok_;
|
| + size_t max_writers_;
|
| + size_t file_size_;
|
| + int32_t max_blocks_after_defer_;
|
| + bool must_read_whole_file_;
|
| + int32_t writers_created_;
|
| +};
|
| +
|
| +}
|
| +
|
| +class MultiBufferTest : public testing::Test {
|
| + public:
|
| + MultiBufferTest() :
|
| + multibuffer_(kBlockSizeShift) {
|
| + }
|
| +
|
| + void Advance() {
|
| + CHECK(media::writers.size());
|
| + media::writers[rand() % media::writers.size()]->Advance();
|
| + }
|
| +
|
| + bool AdvanceAll() {
|
| + bool advanced = false;
|
| + for (size_t i = 0; i < media::writers.size(); i++) {
|
| + advanced |= media::writers[i]->Advance();
|
| + }
|
| + multibuffer_.CheckLRUState();
|
| + return advanced;
|
| + }
|
| +
|
| + scoped_refptr<media::UrlData> GetUrl() {
|
| + return url_index_.GetByUrl(GURL("http://x"), media::UrlData::kUnspecified);
|
| + }
|
| +
|
| + protected:
|
| + media::TestMultiBuffer multibuffer_;
|
| + media::UrlIndex url_index_;
|
| +};
|
| +
|
| +TEST_F(MultiBufferTest, ReadAll) {
|
| + multibuffer_.SetMaxWriters(1);
|
| + size_t pos = 0;
|
| + size_t end = 10000;
|
| + multibuffer_.SetFileSize(10000);
|
| + multibuffer_.SetMustReadWholeFile(true);
|
| + media::MultiBufferReader reader(&multibuffer_,
|
| + GetUrl(),
|
| + pos,
|
| + end,
|
| + base::Callback<void(int64_t, int64_t)>());
|
| + reader.SetMaxBuffer(2000, 5000);
|
| + reader.SetPreload(1000, 1000);
|
| + while (pos < end) {
|
| + unsigned char buffer[27];
|
| + buffer[17] = 17;
|
| + size_t to_read = std::min<size_t>(end - pos, 17);
|
| + int64 bytes_read = reader.TryRead(buffer, to_read);
|
| + if (bytes_read) {
|
| + EXPECT_EQ(buffer[17], 17);
|
| + for (size_t i = 0; i < bytes_read; i++) {
|
| + unsigned char expected = (pos * 15485863) >> 16;
|
| + EXPECT_EQ(expected, buffer[i]) << " pos = " << pos;
|
| + pos++;
|
| + }
|
| + } else {
|
| + Advance();
|
| + }
|
| + }
|
| +}
|
| +
|
| +TEST_F(MultiBufferTest, UpdateUrlData) {
|
| + multibuffer_.SetMaxWriters(1);
|
| + size_t pos = 0;
|
| + size_t end = 10000;
|
| + multibuffer_.SetFileSize(10000);
|
| + scoped_refptr<media::UrlData> a = GetUrl();
|
| + scoped_refptr<media::UrlData> b =
|
| + url_index_.GetByUrl(GURL("http://y"), media::UrlData::kUnspecified);
|
| + media::MultiBufferReader reader(&multibuffer_,
|
| + a,
|
| + pos,
|
| + end,
|
| + base::Callback<void(int64_t, int64_t)>());
|
| + reader.SetPreload(1000, 1000);
|
| + multibuffer_.UpdateUrlData(a, b);
|
| + EXPECT_EQ(b, reader.GetUrlData());
|
| +}
|
| +
|
| +
|
| +TEST_F(MultiBufferTest, ReadAllAdvanceFirst) {
|
| + multibuffer_.SetMaxWriters(1);
|
| + size_t pos = 0;
|
| + size_t end = 10000;
|
| + multibuffer_.SetFileSize(10000);
|
| + multibuffer_.SetMustReadWholeFile(true);
|
| + media::MultiBufferReader reader(&multibuffer_,
|
| + GetUrl(),
|
| + pos,
|
| + end,
|
| + base::Callback<void(int64_t, int64_t)>());
|
| + reader.SetMaxBuffer(2000, 5000);
|
| + reader.SetPreload(1000, 1000);
|
| + while (pos < end) {
|
| + unsigned char buffer[27];
|
| + buffer[17] = 17;
|
| + size_t to_read = std::min<size_t>(end - pos, 17);
|
| + while (AdvanceAll());
|
| + int64 bytes = reader.TryRead(buffer, to_read);
|
| + EXPECT_GT(bytes, 0);
|
| + EXPECT_EQ(buffer[17], 17);
|
| + for (size_t i = 0; i < bytes; i++) {
|
| + unsigned char expected = (pos * 15485863) >> 16;
|
| + EXPECT_EQ(expected, buffer[i]) << " pos = " << pos;
|
| + pos++;
|
| + }
|
| + }
|
| +}
|
| +
|
| +// Checks that if the data provider provides too much data after we told it
|
| +// to defer, we kill it.
|
| +TEST_F(MultiBufferTest, ReadAllAdvanceFirst_NeverDefer) {
|
| + multibuffer_.SetMaxWriters(1);
|
| + size_t pos = 0;
|
| + size_t end = 10000;
|
| + multibuffer_.SetFileSize(10000);
|
| + multibuffer_.SetMaxBlocksAfterDefer(-10000);
|
| + scoped_refptr<media::UrlData> url_data = GetUrl();
|
| + url_data->set_range_supported();
|
| + media::MultiBufferReader reader(&multibuffer_,
|
| + url_data,
|
| + pos,
|
| + end,
|
| + base::Callback<void(int64_t, int64_t)>());
|
| + reader.SetMaxBuffer(2000, 5000);
|
| + reader.SetPreload(1000, 1000);
|
| + while (pos < end) {
|
| + unsigned char buffer[27];
|
| + buffer[17] = 17;
|
| + size_t to_read = std::min<size_t>(end - pos, 17);
|
| + while (AdvanceAll());
|
| + int64 bytes = reader.TryRead(buffer, to_read);
|
| + EXPECT_GT(bytes, 0);
|
| + EXPECT_EQ(buffer[17], 17);
|
| + for (size_t i = 0; i < bytes; i++) {
|
| + unsigned char expected = (pos * 15485863) >> 16;
|
| + EXPECT_EQ(expected, buffer[i]) << " pos = " << pos;
|
| + pos++;
|
| + }
|
| + }
|
| + EXPECT_GT(multibuffer_.writers_created(), 1);
|
| +}
|
| +
|
| +// Same as ReadAllAdvanceFirst_NeverDefer, but the url doesn't support
|
| +// ranges, so we don't destroy it no matter how much data it provides.
|
| +TEST_F(MultiBufferTest, ReadAllAdvanceFirst_NeverDefer2) {
|
| + multibuffer_.SetMaxWriters(1);
|
| + size_t pos = 0;
|
| + size_t end = 10000;
|
| + multibuffer_.SetFileSize(10000);
|
| + multibuffer_.SetMustReadWholeFile(true);
|
| + multibuffer_.SetMaxBlocksAfterDefer(-10000);
|
| + scoped_refptr<media::UrlData> url_data = GetUrl();
|
| + media::MultiBufferReader reader(&multibuffer_,
|
| + url_data,
|
| + pos,
|
| + end,
|
| + base::Callback<void(int64_t, int64_t)>());
|
| + reader.SetMaxBuffer(2000, 5000);
|
| + reader.SetPreload(1000, 1000);
|
| + while (pos < end) {
|
| + unsigned char buffer[27];
|
| + buffer[17] = 17;
|
| + size_t to_read = std::min<size_t>(end - pos, 17);
|
| + while (AdvanceAll());
|
| + int64 bytes = reader.TryRead(buffer, to_read);
|
| + EXPECT_GT(bytes, 0);
|
| + EXPECT_EQ(buffer[17], 17);
|
| + for (size_t i = 0; i < bytes; i++) {
|
| + unsigned char expected = (pos * 15485863) >> 16;
|
| + EXPECT_EQ(expected, buffer[i]) << " pos = " << pos;
|
| + pos++;
|
| + }
|
| + }
|
| +}
|
| +
|
| +class ReadHelper {
|
| + public:
|
| + ReadHelper(scoped_refptr<media::UrlData> url,
|
| + size_t end,
|
| + size_t max_read_size,
|
| + media::MultiBuffer* multibuffer) :
|
| + pos_(0),
|
| + end_(end),
|
| + max_read_size_(max_read_size),
|
| + read_size_(0),
|
| + reader_(multibuffer,
|
| + url,
|
| + pos_,
|
| + end_,
|
| + base::Callback<void(int64_t, int64_t)>()) {
|
| + reader_.SetMaxBuffer(2000, 5000);
|
| + reader_.SetPreload(1000, 1000);
|
| + }
|
| +
|
| + bool Read() {
|
| + if (read_size_ == 0) return true;
|
| + unsigned char buffer[read_size_];
|
| + CHECK_EQ(pos_, reader_.Tell());
|
| + int64 bytes_read = reader_.TryRead(buffer, read_size_);
|
| + if (bytes_read) {
|
| + for (size_t i = 0; i < bytes_read; i++) {
|
| + unsigned char expected = (pos_ * 15485863) >> 16;
|
| + EXPECT_EQ(expected, buffer[i]) << " pos = " << pos_;
|
| + pos_++;
|
| + }
|
| + CHECK_EQ(pos_, reader_.Tell());
|
| + return true;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| + void StartRead() {
|
| + CHECK_EQ(pos_, reader_.Tell());
|
| + read_size_ = std::min(1 + rand() % (max_read_size_ - 1), end_ - pos_);
|
| + if (!Read()) {
|
| + reader_.Wait(read_size_,
|
| + base::Bind(&ReadHelper::WaitCB, base::Unretained(this)));
|
| + }
|
| + }
|
| +
|
| + void WaitCB() {
|
| + CHECK(Read());
|
| + }
|
| +
|
| + void Seek() {
|
| + pos_ = rand() % end_;
|
| + reader_.Seek(pos_);
|
| + CHECK_EQ(pos_, reader_.Tell());
|
| + }
|
| +
|
| + private:
|
| + size_t pos_;
|
| + size_t end_;
|
| + size_t max_read_size_;
|
| + size_t read_size_;
|
| + media::MultiBufferReader reader_;
|
| +};
|
| +
|
| +TEST_F(MultiBufferTest, RandomTest) {
|
| + size_t file_size = 1000000;
|
| + multibuffer_.SetFileSize(file_size);
|
| + multibuffer_.SetMaxBlocksAfterDefer(10);
|
| + std::vector<ReadHelper*> read_helpers;
|
| + scoped_refptr<media::UrlData> url = GetUrl();
|
| + for (size_t i = 0; i < 20; i++) {
|
| + read_helpers.push_back(new ReadHelper(url, file_size, 1000, &multibuffer_));
|
| + }
|
| + for (int i = 0; i < 10000; i++) {
|
| + if (rand() & 1) {
|
| + if (!media::writers.empty()) Advance();
|
| + } else {
|
| + size_t j = rand() % read_helpers.size();
|
| + if (rand() % 100 < 3)
|
| + read_helpers[j]->Seek();
|
| + read_helpers[j]->StartRead();
|
| + multibuffer_.CheckLRUState();
|
| + }
|
| + }
|
| + multibuffer_.CheckPresentState();
|
| + while (!read_helpers.empty()) {
|
| + delete read_helpers.back();
|
| + read_helpers.pop_back();
|
| + }
|
| +}
|
| +
|
| +TEST_F(MultiBufferTest, RandomTest_RangeSupported) {
|
| + size_t file_size = 1000000;
|
| + multibuffer_.SetFileSize(file_size);
|
| + multibuffer_.SetMaxBlocksAfterDefer(10);
|
| + std::vector<ReadHelper*> read_helpers;
|
| + scoped_refptr<media::UrlData> url = GetUrl();
|
| + url->set_range_supported();
|
| + for (size_t i = 0; i < 20; i++) {
|
| + read_helpers.push_back(new ReadHelper(url, file_size, 1000, &multibuffer_));
|
| + }
|
| + for (int i = 0; i < 10000; i++) {
|
| + if (rand() & 1) {
|
| + if (!media::writers.empty()) Advance();
|
| + } else {
|
| + size_t j = rand() % read_helpers.size();
|
| + if (rand() % 100 < 3)
|
| + read_helpers[j]->Seek();
|
| + read_helpers[j]->StartRead();
|
| + multibuffer_.CheckLRUState();
|
| + }
|
| + }
|
| + multibuffer_.CheckPresentState();
|
| + while (!read_helpers.empty()) {
|
| + delete read_helpers.back();
|
| + read_helpers.pop_back();
|
| + }
|
| +}
|
|
|