Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1664)

Unified Diff: media/blink/multibuffer_unittest.cc

Issue 1165903002: Multi reader/writer cache/buffer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: one more compile fix Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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..edd4abea63a683a87ceb6bc586fa828877d4af80
--- /dev/null
+++ b/media/blink/multibuffer_unittest.cc
@@ -0,0 +1,500 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
DaleCurtis 2015/10/19 21:45:25 Overall there's too much use of rand() in this fil
+// 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_;
DaleCurtis 2015/10/19 21:45:25 Again, don't use rand unless you're prepared to de
+ } 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] =
+ static_cast<uint8_t>((byte_pos * 15485863) >> 16);
+ }
+ block->set_data_size(static_cast<int>(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();
DaleCurtis 2015/10/19 21:45:25 Ditto.
+ }
+
+ 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_t bytes_read = reader.TryRead(buffer, to_read);
+ if (bytes_read) {
+ EXPECT_EQ(buffer[17], 17);
+ for (int64_t i = 0; i < bytes_read; i++) {
+ uint8_t expected = static_cast<uint8_t>((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_t bytes = reader.TryRead(buffer, to_read);
+ EXPECT_GT(bytes, 0);
+ EXPECT_EQ(buffer[17], 17);
+ for (int64_t i = 0; i < bytes; i++) {
+ uint8_t expected = static_cast<uint8_t>((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_t bytes = reader.TryRead(buffer, to_read);
+ EXPECT_GT(bytes, 0);
+ EXPECT_EQ(buffer[17], 17);
+ for (int64_t i = 0; i < bytes; i++) {
+ uint8_t expected = static_cast<uint8_t>((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_t bytes = reader.TryRead(buffer, to_read);
+ EXPECT_GT(bytes, 0);
+ EXPECT_EQ(buffer[17], 17);
+ for (int64_t i = 0; i < bytes; i++) {
+ uint8_t expected = static_cast<uint8_t>((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[4096];
+ CHECK_LE(read_size_, static_cast<int64_t>(sizeof(buffer)));
+ CHECK_EQ(pos_, reader_.Tell());
+ int64_t bytes_read = reader_.TryRead(buffer, read_size_);
+ if (bytes_read) {
+ for (int64_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:
+ int64_t pos_;
+ int64_t end_;
+ int64_t max_read_size_;
+ int64_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();
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698