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

Unified Diff: content/browser/fileapi/blob_reader_unittest.cc

Issue 1337153002: [Blob] BlobReader class & tests, and removal of all redundant reading. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fixed prod/debug flakiness Created 5 years, 3 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
« no previous file with comments | « no previous file | content/browser/fileapi/blob_url_request_job_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: content/browser/fileapi/blob_reader_unittest.cc
diff --git a/content/browser/fileapi/blob_reader_unittest.cc b/content/browser/fileapi/blob_reader_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..a7e095c802a48a9f3013eef06078e677cbbb4b76
--- /dev/null
+++ b/content/browser/fileapi/blob_reader_unittest.cc
@@ -0,0 +1,1111 @@
+// 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 "storage/browser/blob/blob_reader.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/callback.h"
+#include "base/files/file_path.h"
+#include "base/location.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop/message_loop.h"
+#include "base/run_loop.h"
+#include "base/strings/stringprintf.h"
+#include "base/task_runner.h"
+#include "base/time/time.h"
+#include "content/public/test/async_file_test_helper.h"
+#include "content/public/test/test_file_system_context.h"
+#include "net/base/completion_callback.h"
+#include "net/base/io_buffer.h"
+#include "net/base/net_errors.h"
+#include "net/base/test_completion_callback.h"
+#include "net/disk_cache/disk_cache.h"
+#include "storage/browser/blob/blob_data_builder.h"
+#include "storage/browser/blob/blob_data_handle.h"
+#include "storage/browser/blob/blob_storage_context.h"
+#include "storage/browser/fileapi/file_stream_reader.h"
+#include "storage/browser/fileapi/file_system_context.h"
+#include "storage/browser/fileapi/file_system_file_util.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "url/gurl.h"
+
+using base::FilePath;
+using content::AsyncFileTestHelper;
+using net::DrainableIOBuffer;
+using net::IOBuffer;
+
+namespace storage {
+namespace {
+
+const int kTestDiskCacheStreamIndex = 0;
+
+// Our disk cache tests don't need a real data handle since the tests themselves
+// scope the disk cache and entries.
+class EmptyDataHandle : public storage::BlobDataBuilder::DataHandle {
+ private:
+ ~EmptyDataHandle() override {}
+};
+
+// A disk_cache::Entry that arbitrarily delays the completion of a read
+// operation to allow testing some races without flake. This is particularly
+// relevant in this unit test, which uses the always-synchronous MEMORY_CACHE.
+class DelayedReadEntry : public disk_cache::Entry {
+ public:
+ explicit DelayedReadEntry(disk_cache::ScopedEntryPtr entry)
+ : entry_(entry.Pass()) {}
+ ~DelayedReadEntry() override { EXPECT_FALSE(HasPendingReadCallbacks()); }
+
+ bool HasPendingReadCallbacks() { return !pending_read_callbacks_.empty(); }
+
+ void RunPendingReadCallbacks() {
+ std::vector<base::Callback<void(void)>> callbacks;
+ pending_read_callbacks_.swap(callbacks);
+ for (const auto& callback : callbacks)
+ callback.Run();
+ }
+
+ // From disk_cache::Entry:
+ void Doom() override { entry_->Doom(); }
+
+ void Close() override { delete this; } // Note this is required by the API.
+
+ std::string GetKey() const override { return entry_->GetKey(); }
+
+ base::Time GetLastUsed() const override { return entry_->GetLastUsed(); }
+
+ base::Time GetLastModified() const override {
+ return entry_->GetLastModified();
+ }
+
+ int32 GetDataSize(int index) const override {
+ return entry_->GetDataSize(index);
+ }
+
+ int ReadData(int index,
+ int offset,
+ IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& original_callback) override {
+ net::TestCompletionCallback callback;
+ int rv = entry_->ReadData(index, offset, buf, buf_len, callback.callback());
+ DCHECK_NE(rv, net::ERR_IO_PENDING)
+ << "Test expects to use a MEMORY_CACHE instance, which is synchronous.";
+ pending_read_callbacks_.push_back(base::Bind(original_callback, rv));
+ return net::ERR_IO_PENDING;
+ }
+
+ int WriteData(int index,
+ int offset,
+ IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback,
+ bool truncate) override {
+ return entry_->WriteData(index, offset, buf, buf_len, callback, truncate);
+ }
+
+ int ReadSparseData(int64 offset,
+ IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) override {
+ return entry_->ReadSparseData(offset, buf, buf_len, callback);
+ }
+
+ int WriteSparseData(int64 offset,
+ IOBuffer* buf,
+ int buf_len,
+ const CompletionCallback& callback) override {
+ return entry_->WriteSparseData(offset, buf, buf_len, callback);
+ }
+
+ int GetAvailableRange(int64 offset,
+ int len,
+ int64* start,
+ const CompletionCallback& callback) override {
+ return entry_->GetAvailableRange(offset, len, start, callback);
+ }
+
+ bool CouldBeSparse() const override { return entry_->CouldBeSparse(); }
+
+ void CancelSparseIO() override { entry_->CancelSparseIO(); }
+
+ int ReadyForSparseIO(const CompletionCallback& callback) override {
+ return entry_->ReadyForSparseIO(callback);
+ }
+
+ private:
+ disk_cache::ScopedEntryPtr entry_;
+ std::vector<base::Callback<void(void)>> pending_read_callbacks_;
+};
+
+scoped_ptr<disk_cache::Backend> CreateInMemoryDiskCache(
+ const scoped_refptr<base::SingleThreadTaskRunner>& thread) {
+ scoped_ptr<disk_cache::Backend> cache;
+ net::TestCompletionCallback callback;
+ int rv = disk_cache::CreateCacheBackend(
+ net::MEMORY_CACHE, net::CACHE_BACKEND_DEFAULT, FilePath(), 0, false,
+ thread, nullptr, &cache, callback.callback());
+ EXPECT_EQ(net::OK, callback.GetResult(rv));
+
+ return cache.Pass();
+}
+
+disk_cache::ScopedEntryPtr CreateDiskCacheEntry(disk_cache::Backend* cache,
+ const char* key,
+ const std::string& data) {
+ disk_cache::Entry* temp_entry = nullptr;
+ net::TestCompletionCallback callback;
+ int rv = cache->CreateEntry(key, &temp_entry, callback.callback());
+ if (callback.GetResult(rv) != net::OK)
+ return nullptr;
+ disk_cache::ScopedEntryPtr entry(temp_entry);
+
+ scoped_refptr<net::StringIOBuffer> iobuffer = new net::StringIOBuffer(data);
+ rv = entry->WriteData(kTestDiskCacheStreamIndex, 0, iobuffer.get(),
+ iobuffer->size(), callback.callback(), false);
+ EXPECT_EQ(static_cast<int>(data.size()), callback.GetResult(rv));
+ return entry.Pass();
+}
+
+template <typename T>
+void SetValue(T* address, T value) {
+ *address = value;
+}
+
+class FakeFileStreamReader : public FileStreamReader {
+ public:
+ explicit FakeFileStreamReader(const std::string& contents)
+ : buffer_(new DrainableIOBuffer(
+ new net::StringIOBuffer(
+ scoped_ptr<std::string>(new std::string(contents))),
+ contents.size())),
+ net_error_(net::OK),
+ size_(contents.size()) {}
+ FakeFileStreamReader(const std::string& contents, uint64_t size)
+ : buffer_(new DrainableIOBuffer(
+ new net::StringIOBuffer(
+ scoped_ptr<std::string>(new std::string(contents))),
+ contents.size())),
+ net_error_(net::OK),
+ size_(size) {}
+
+ ~FakeFileStreamReader() override {}
+
+ void SetReturnError(int net_error) { net_error_ = net_error; }
+
+ void SetAsyncRunner(base::SingleThreadTaskRunner* runner) {
+ async_task_runner_ = runner;
+ }
+
+ int Read(net::IOBuffer* buf,
+ int buf_length,
+ const net::CompletionCallback& done) override {
+ DCHECK(buf);
+ // When async_task_runner_ is not set, return synchronously.
+ if (!async_task_runner_.get()) {
+ if (net_error_ == net::OK) {
+ return ReadImpl(buf, buf_length, net::CompletionCallback());
+ } else {
+ return net_error_;
+ }
+ }
+
+ // Otherwise always return asynchronously.
+ if (net_error_ == net::OK) {
+ async_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(base::IgnoreResult(&FakeFileStreamReader::ReadImpl),
+ base::Unretained(this), make_scoped_refptr(buf),
+ buf_length, done));
+ } else {
+ async_task_runner_->PostTask(FROM_HERE, base::Bind(done, net_error_));
+ }
+ return net::ERR_IO_PENDING;
+ }
+
+ int64 GetLength(const net::Int64CompletionCallback& size_callback) override {
+ // When async_task_runner_ is not set, return synchronously.
+ if (!async_task_runner_.get()) {
+ if (net_error_ == net::OK) {
+ return size_;
+ } else {
+ return net_error_;
+ }
+ }
+ if (net_error_ == net::OK) {
+ async_task_runner_->PostTask(FROM_HERE, base::Bind(size_callback, size_));
+ } else {
+ async_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(size_callback, static_cast<int64_t>(net_error_)));
+ }
+ return net::ERR_IO_PENDING;
+ }
+
+ private:
+ int ReadImpl(scoped_refptr<net::IOBuffer> buf,
+ int buf_length,
+ const net::CompletionCallback& done) {
+ CHECK_GE(buf_length, 0);
+ int length = std::min(buf_length, buffer_->BytesRemaining());
+ memcpy(buf->data(), buffer_->data(), length);
+ buffer_->DidConsume(length);
+ if (done.is_null()) {
+ return length;
+ }
+ done.Run(length);
+ return net::ERR_IO_PENDING;
+ }
+
+ scoped_refptr<net::DrainableIOBuffer> buffer_;
+ scoped_refptr<base::SingleThreadTaskRunner> async_task_runner_;
+ int net_error_;
+ uint64_t size_;
+
+ DISALLOW_COPY_AND_ASSIGN(FakeFileStreamReader);
+};
+
+class MockFileStreamReaderProvider
+ : public BlobReader::FileStreamReaderProvider {
+ public:
+ ~MockFileStreamReaderProvider() override {}
+
+ MOCK_METHOD4(CreateForLocalFileMock,
+ FileStreamReader*(base::TaskRunner* task_runner,
+ const FilePath& file_path,
+ int64_t initial_offset,
+ const base::Time& expected_modification_time));
+ MOCK_METHOD4(CreateFileStreamReaderMock,
+ FileStreamReader*(const GURL& filesystem_url,
+ int64_t offset,
+ int64_t max_bytes_to_read,
+ const base::Time& expected_modification_time));
+ // Since we're returning a move-only type, we have to do some delegation for
+ // gmock.
+ scoped_ptr<FileStreamReader> CreateForLocalFile(
+ base::TaskRunner* task_runner,
+ const base::FilePath& file_path,
+ int64_t initial_offset,
+ const base::Time& expected_modification_time) override {
+ return make_scoped_ptr(CreateForLocalFileMock(
+ task_runner, file_path, initial_offset, expected_modification_time));
+ }
+
+ scoped_ptr<FileStreamReader> CreateFileStreamReader(
+ const GURL& filesystem_url,
+ int64_t offset,
+ int64_t max_bytes_to_read,
+ const base::Time& expected_modification_time) override {
+ return make_scoped_ptr(CreateFileStreamReaderMock(
+ filesystem_url, offset, max_bytes_to_read, expected_modification_time));
+ }
+};
+
+} // namespace
+
+class BlobReaderTest : public ::testing::Test {
+ public:
+ BlobReaderTest() {}
+ ~BlobReaderTest() override {}
+
+ void TearDown() override {
+ reader_.reset();
+ blob_handle_.reset();
+ message_loop_.RunUntilIdle();
+ base::RunLoop().RunUntilIdle();
+ }
+
+ protected:
+ void InitializeReader(BlobDataBuilder* builder) {
+ blob_handle_ = builder ? context_.AddFinishedBlob(builder).Pass() : nullptr;
+ provider_ = new MockFileStreamReaderProvider();
+ scoped_ptr<BlobReader::FileStreamReaderProvider> temp_ptr(provider_);
+ reader_.reset(new BlobReader(blob_handle_.get(), temp_ptr.Pass(),
+ message_loop_.task_runner().get()));
+ }
+
+ // Takes ownership of the file reader (the blob reader takes ownership).
+ void ExpectLocalFileCall(const FilePath& file_path,
+ base::Time modification_time,
+ uint64_t initial_offset,
+ FakeFileStreamReader* reader) {
+ EXPECT_CALL(*provider_, CreateForLocalFileMock(
+ message_loop_.task_runner().get(), file_path,
+ initial_offset, modification_time))
+ .WillOnce(testing::Return(reader));
+ }
+
+ // Takes ownership of the file reader (the blob reader takes ownership).
+ void ExpectFileSystemCall(const GURL& filesystem_url,
+ int64_t offset,
+ int64_t max_bytes_to_read,
+ base::Time expected_modification_time,
+ FakeFileStreamReader* reader) {
+ EXPECT_CALL(*provider_, CreateFileStreamReaderMock(
+ filesystem_url, offset, max_bytes_to_read,
+ expected_modification_time))
+ .WillOnce(testing::Return(reader));
+ }
+
+ void CheckSizeCalculatedSynchronously(size_t expected_size, int async_size) {
+ EXPECT_EQ(-1, async_size);
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(expected_size, reader_->total_size());
+ EXPECT_TRUE(reader_->total_size_calculated());
+ }
+
+ void CheckSizeNotCalculatedYet(int async_size) {
+ EXPECT_EQ(-1, async_size);
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_FALSE(reader_->total_size_calculated());
+ }
+
+ void CheckSizeCalculatedAsynchronously(size_t expected_size,
+ int async_result) {
+ EXPECT_EQ(net::OK, async_result);
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(expected_size, reader_->total_size());
+ EXPECT_TRUE(reader_->total_size_calculated());
+ }
+
+ scoped_refptr<net::IOBuffer> CreateBuffer(uint64_t size) {
+ return scoped_refptr<net::IOBuffer>(
+ new net::IOBuffer(static_cast<size_t>(size)));
+ }
+
+ bool IsReaderTotalSizeCalculated() {
+ return reader_->total_size_calculated();
+ }
+
+ BlobStorageContext context_;
+ scoped_ptr<BlobDataHandle> blob_handle_;
+ MockFileStreamReaderProvider* provider_ = nullptr;
+ base::MessageLoop message_loop_;
+ scoped_ptr<BlobReader> reader_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(BlobReaderTest);
+};
+
+namespace {
+
+TEST_F(BlobReaderTest, BasicMemory) {
+ BlobDataBuilder b("uuid");
+ const std::string kData("Hello!!!");
+ const size_t kDataSize = 8ul;
+ b.AppendData(kData);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kDataSize, size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kDataSize));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kDataSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kDataSize, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "Hello!!!", kDataSize));
+}
+
+TEST_F(BlobReaderTest, BasicFile) {
+ BlobDataBuilder b("uuid");
+ const FilePath kPath = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+ b.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&b);
+
+ // Non-async reader.
+ ExpectLocalFileCall(kPath, kTime, 0, new FakeFileStreamReader(kData));
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kData.size(), static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "FileData!!!", kData.size()));
+}
+
+TEST_F(BlobReaderTest, BasicFileSystem) {
+ BlobDataBuilder b("uuid");
+ const GURL kURL("file://test_file/here.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+ b.AppendFileSystemFile(kURL, 0, kData.size(), kTime);
+ this->InitializeReader(&b);
+
+ // Non-async reader.
+ ExpectFileSystemCall(kURL, 0, kData.size(), kTime,
+ new FakeFileStreamReader(kData));
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kData.size(), static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "FileData!!!", kData.size()));
+}
+
+TEST_F(BlobReaderTest, BasicDiskCache) {
+ scoped_ptr<disk_cache::Backend> cache =
+ CreateInMemoryDiskCache(message_loop_.task_runner());
+ ASSERT_TRUE(cache);
+
+ BlobDataBuilder b("uuid");
+ const std::string kData = "Test Blob Data";
+ scoped_refptr<BlobDataBuilder::DataHandle> data_handle =
+ new EmptyDataHandle();
+ disk_cache::ScopedEntryPtr entry =
+ CreateDiskCacheEntry(cache.get(), "test entry", kData);
+ b.AppendDiskCacheEntry(data_handle, entry.get(), kTestDiskCacheStreamIndex);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kData.size(), static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "Test Blob Data", kData.size()));
+}
+
+TEST_F(BlobReaderTest, BufferLargerThanMemory) {
+ BlobDataBuilder b("uuid");
+ const std::string kData("Hello!!!");
+ const size_t kDataSize = 8ul;
+ const size_t kBufferSize = 10ul;
+ b.AppendData(kData);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kBufferSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kDataSize, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "Hello!!!", kDataSize));
+}
+
+TEST_F(BlobReaderTest, MemoryRange) {
+ BlobDataBuilder b("uuid");
+ const std::string kData("Hello!!!");
+ const size_t kDataSize = 8ul;
+ const size_t kSeekOffset = 2ul;
+ const uint64_t kReadLength = 4ull;
+ b.AppendData(kData);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(kReadLength);
+
+ reader_->SetReadRange(kSeekOffset, kReadLength);
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kDataSize - kSeekOffset, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kReadLength, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "llo!", kReadLength));
+}
+
+TEST_F(BlobReaderTest, BufferSmallerThanMemory) {
+ BlobDataBuilder b("uuid");
+ const std::string kData("Hello!!!");
+ const size_t kBufferSize = 4ul;
+ b.AppendData(kData);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kBufferSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kBufferSize, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "Hell", kBufferSize));
+
+ bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kBufferSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kBufferSize, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "o!!!", kBufferSize));
+}
+
+TEST_F(BlobReaderTest, SegmentedBufferAndMemory) {
+ BlobDataBuilder b("uuid");
+ const size_t kNumItems = 10;
+ const size_t kItemSize = 6;
+ const size_t kBufferSize = 10;
+ const size_t kTotalSize = kNumItems * kItemSize;
+ char current_value = 0;
+ for (size_t i = 0; i < kNumItems; i++) {
+ char buf[kItemSize];
+ for (size_t j = 0; j < kItemSize; j++) {
+ buf[j] = current_value++;
+ }
+ b.AppendData(buf, kItemSize);
+ }
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kTotalSize, size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ current_value = 0;
+ for (size_t i = 0; i < kTotalSize / kBufferSize; i++) {
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kBufferSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kBufferSize, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ for (size_t j = 0; j < kBufferSize; j++) {
+ EXPECT_EQ(current_value, buffer->data()[j]);
+ current_value++;
+ }
+ }
+}
+
+TEST_F(BlobReaderTest, FileAsync) {
+ BlobDataBuilder b("uuid");
+ const FilePath kPath = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+ b.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&b);
+
+ scoped_ptr<FakeFileStreamReader> reader(new FakeFileStreamReader(kData));
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+
+ ExpectLocalFileCall(kPath, kTime, 0, reader.release());
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeNotCalculatedYet(size_result);
+ message_loop_.RunUntilIdle();
+ CheckSizeCalculatedAsynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kData.size(), static_cast<size_t>(async_bytes_read));
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "FileData!!!", kData.size()));
+}
+
+TEST_F(BlobReaderTest, FileSystemAsync) {
+ BlobDataBuilder b("uuid");
+ const GURL kURL("file://test_file/here.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+ b.AppendFileSystemFile(kURL, 0, kData.size(), kTime);
+ this->InitializeReader(&b);
+
+ scoped_ptr<FakeFileStreamReader> reader(new FakeFileStreamReader(kData));
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+
+ ExpectFileSystemCall(kURL, 0, kData.size(), kTime, reader.release());
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeNotCalculatedYet(size_result);
+ message_loop_.RunUntilIdle();
+ CheckSizeCalculatedAsynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kData.size(), static_cast<size_t>(async_bytes_read));
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "FileData!!!", kData.size()));
+}
+
+TEST_F(BlobReaderTest, DiskCacheAsync) {
+ scoped_ptr<disk_cache::Backend> cache =
+ CreateInMemoryDiskCache(message_loop_.task_runner());
+ ASSERT_TRUE(cache);
+
+ BlobDataBuilder b("uuid");
+ const std::string kData = "Test Blob Data";
+ scoped_refptr<BlobDataBuilder::DataHandle> data_handle =
+ new EmptyDataHandle();
+ scoped_ptr<DelayedReadEntry> delayed_read_entry(new DelayedReadEntry(
+ CreateDiskCacheEntry(cache.get(), "test entry", kData).Pass()));
+ b.AppendDiskCacheEntry(data_handle, delayed_read_entry.get(),
+ kTestDiskCacheStreamIndex);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeCalculatedSynchronously(kData.size(), size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_TRUE(delayed_read_entry->HasPendingReadCallbacks());
+ delayed_read_entry->RunPendingReadCallbacks();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(kData.size(), static_cast<size_t>(async_bytes_read));
+ EXPECT_EQ(0, memcmp(buffer->data(), "Test Blob Data", kData.size()));
+}
+
+TEST_F(BlobReaderTest, FileRange) {
+ BlobDataBuilder b("uuid");
+ const FilePath kPath = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ // We check the offset in the ExpectLocalFileCall mock.
+ const std::string kRangeData = "leD";
+ const std::string kData = "FileData!!!";
+ const uint64_t kOffset = 2;
+ const uint64_t kReadLength = 3;
+ const base::Time kTime = base::Time::Now();
+ b.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&b);
+
+ scoped_ptr<FakeFileStreamReader> reader(new FakeFileStreamReader(kData));
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+ ExpectLocalFileCall(kPath, kTime, 0, reader.release());
+
+ // We create the reader again with the offset after the seek.
+ reader.reset(new FakeFileStreamReader(kRangeData));
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+ ExpectLocalFileCall(kPath, kTime, kOffset, reader.release());
+
+ int size_result = -1;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ message_loop_.RunUntilIdle();
+
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(kReadLength);
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->SetReadRange(kOffset, kReadLength));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kReadLength, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kReadLength, static_cast<size_t>(async_bytes_read));
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "leD", kReadLength));
+}
+
+TEST_F(BlobReaderTest, DiskCacheRange) {
+ scoped_ptr<disk_cache::Backend> cache =
+ CreateInMemoryDiskCache(message_loop_.task_runner());
+ ASSERT_TRUE(cache);
+
+ BlobDataBuilder b("uuid");
+ const std::string kData = "Test Blob Data";
+ const uint64_t kOffset = 2;
+ const uint64_t kReadLength = 3;
+ scoped_refptr<BlobDataBuilder::DataHandle> data_handle =
+ new EmptyDataHandle();
+ disk_cache::ScopedEntryPtr entry =
+ CreateDiskCacheEntry(cache.get(), "test entry", kData);
+ b.AppendDiskCacheEntry(data_handle, entry.get(), kTestDiskCacheStreamIndex);
+ this->InitializeReader(&b);
+
+ int size_result = -1;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(kReadLength);
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->SetReadRange(kOffset, kReadLength));
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->Read(buffer.get(), kReadLength, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(kReadLength, static_cast<size_t>(bytes_read));
+ EXPECT_EQ(0, async_bytes_read);
+ EXPECT_EQ(0, memcmp(buffer->data(), "st ", kReadLength));
+}
+
+TEST_F(BlobReaderTest, FileSomeAsyncSegmentedOffsetsUnknownSizes) {
+ // This tests includes:
+ // * Unknown file sizes (item length of uint64::max) for every other item.
+ // * Offsets for every 3rd file item.
+ // * Non-async reader for every 4th file item.
+ BlobDataBuilder b("uuid");
+ const FilePath kPathBase = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ const base::Time kTime = base::Time::Now();
+ const size_t kNumItems = 10;
+ const size_t kItemSize = 6;
+ const size_t kBufferSize = 10;
+ const size_t kTotalSize = kNumItems * kItemSize;
+ char current_value = 0;
+ // Create blob and reader.
+ for (size_t i = 0; i < kNumItems; i++) {
+ current_value += kItemSize;
+ FilePath path = kPathBase.Append(
+ FilePath::FromUTF8Unsafe(base::StringPrintf("%d", current_value)));
+ uint64_t offset = i % 3 == 0 ? 1 : 0;
+ uint64_t size =
+ i % 2 == 0 ? kItemSize : std::numeric_limits<uint64_t>::max();
+ b.AppendFile(path, offset, size, kTime);
+ }
+ this->InitializeReader(&b);
+
+ // Set expectations.
+ current_value = 0;
+ for (size_t i = 0; i < kNumItems; i++) {
+ uint64_t offset = i % 3 == 0 ? 1 : 0;
+ scoped_ptr<char[]> buf(new char[kItemSize + offset]);
+ if (offset > 0) {
+ memset(buf.get(), 7, offset);
+ }
+ for (size_t j = 0; j < kItemSize; j++) {
+ buf.get()[j + offset] = current_value++;
+ }
+ scoped_ptr<FakeFileStreamReader> reader(new FakeFileStreamReader(
+ std::string(buf.get() + offset, kItemSize), kItemSize + offset));
+ if (i % 4 != 0) {
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+ }
+ FilePath path = kPathBase.Append(
+ FilePath::FromUTF8Unsafe(base::StringPrintf("%d", current_value)));
+ ExpectLocalFileCall(path, kTime, offset, reader.release());
+ }
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeNotCalculatedYet(size_result);
+ message_loop_.RunUntilIdle();
+ CheckSizeCalculatedAsynchronously(kTotalSize, size_result);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
+
+ current_value = 0;
+ for (size_t i = 0; i < kTotalSize / kBufferSize; i++) {
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kBufferSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(kBufferSize, static_cast<size_t>(async_bytes_read));
+ for (size_t j = 0; j < kBufferSize; j++) {
+ EXPECT_EQ(current_value, buffer->data()[j]);
+ current_value++;
+ }
+ }
+}
+
+TEST_F(BlobReaderTest, MixedContent) {
+ // Includes data, a file, and a disk cache entry.
+ scoped_ptr<disk_cache::Backend> cache =
+ CreateInMemoryDiskCache(message_loop_.task_runner());
+ ASSERT_TRUE(cache);
+
+ BlobDataBuilder b("uuid");
+ const std::string kData1("Hello ");
+ const std::string kData2("there. ");
+ const std::string kData3("This ");
+ const std::string kData4("is multi-content.");
+ const uint64_t kDataSize = 35;
+
+ const base::Time kTime = base::Time::Now();
+ const FilePath kData1Path = FilePath::FromUTF8Unsafe("/fake/file.txt");
+
+ disk_cache::ScopedEntryPtr entry3 =
+ CreateDiskCacheEntry(cache.get(), "test entry", kData3);
+
+ b.AppendFile(kData1Path, 0, kData1.size(), kTime);
+ b.AppendData(kData2);
+ b.AppendDiskCacheEntry(
+ scoped_refptr<BlobDataBuilder::DataHandle>(new EmptyDataHandle()),
+ entry3.get(), kTestDiskCacheStreamIndex);
+ b.AppendData(kData4);
+
+ this->InitializeReader(&b);
+
+ scoped_ptr<FakeFileStreamReader> reader(new FakeFileStreamReader(kData1));
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+ ExpectLocalFileCall(kData1Path, kTime, 0, reader.release());
+
+ int size_result = -1;
+ EXPECT_FALSE(IsReaderTotalSizeCalculated());
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ CheckSizeNotCalculatedYet(size_result);
+ message_loop_.RunUntilIdle();
+ CheckSizeCalculatedAsynchronously(kDataSize, size_result);
+
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(kDataSize);
+
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kDataSize, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(0, async_bytes_read);
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::OK, reader_->net_error());
+ EXPECT_EQ(0, bytes_read);
+ EXPECT_EQ(kDataSize, static_cast<size_t>(async_bytes_read));
+ EXPECT_EQ(0, memcmp(buffer->data(), "Hello there. This is multi-content.",
+ kDataSize));
+}
+
+TEST_F(BlobReaderTest, StateErrors) {
+ // Test common variables
+ int bytes_read = -1;
+ int async_bytes_read = -1;
+ int size_result = -1;
+ const std::string kData("Hello!!!");
+
+ // Case: Blob handle is a nullptr.
+ InitializeReader(nullptr);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+ EXPECT_EQ(BlobReader::Status::NET_ERROR, reader_->SetReadRange(0, 10));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(10);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->Read(buffer.get(), 10, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+
+ // Case: Not calling CalculateSize before SetReadRange.
+ BlobDataBuilder builder1("uuid1");
+ builder1.AppendData(kData);
+ InitializeReader(&builder1);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR, reader_->SetReadRange(0, 10));
+ EXPECT_EQ(net::ERR_FAILED, reader_->net_error());
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->Read(buffer.get(), 10, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+
+ // Case: Not calling CalculateSize before Read.
+ BlobDataBuilder builder2("uuid2");
+ builder2.AppendData(kData);
+ InitializeReader(&builder2);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->Read(buffer.get(), 10, &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+}
+
+TEST_F(BlobReaderTest, FileErrorsSync) {
+ int size_result = -1;
+ const FilePath kPath = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+
+ // Case: Error on length query.
+ BlobDataBuilder builder1("uuid1");
+ builder1.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&builder1);
+ FakeFileStreamReader* reader = new FakeFileStreamReader(kData);
+ reader->SetReturnError(net::ERR_FILE_NOT_FOUND);
+ ExpectLocalFileCall(kPath, kTime, 0, reader);
+
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+
+ // Case: Error on read.
+ BlobDataBuilder builder2("uuid2");
+ builder2.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&builder2);
+ reader = new FakeFileStreamReader(kData);
+ ExpectLocalFileCall(kPath, kTime, 0, reader);
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ reader->SetReturnError(net::ERR_FILE_NOT_FOUND);
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+}
+
+TEST_F(BlobReaderTest, FileErrorsAsync) {
+ int size_result = -1;
+ const FilePath kPath = FilePath::FromUTF8Unsafe("/fake/file.txt");
+ const std::string kData = "FileData!!!";
+ const base::Time kTime = base::Time::Now();
+
+ // Case: Error on length query.
+ BlobDataBuilder builder1("uuid1");
+ builder1.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&builder1);
+ FakeFileStreamReader* reader = new FakeFileStreamReader(kData);
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+ reader->SetReturnError(net::ERR_FILE_NOT_FOUND);
+ ExpectLocalFileCall(kPath, kTime, 0, reader);
+
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, size_result);
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+
+ // Case: Error on read.
+ BlobDataBuilder builder2("uuid2");
+ builder2.AppendFile(kPath, 0, kData.size(), kTime);
+ this->InitializeReader(&builder2);
+ reader = new FakeFileStreamReader(kData);
+ ExpectLocalFileCall(kPath, kTime, 0, reader);
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ reader->SetReturnError(net::ERR_FILE_NOT_FOUND);
+ reader->SetAsyncRunner(message_loop_.task_runner().get());
+
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kData.size()));
+ int bytes_read = 0;
+ int async_bytes_read = 0;
+ EXPECT_EQ(BlobReader::Status::IO_PENDING,
+ reader_->Read(buffer.get(), kData.size(), &bytes_read,
+ base::Bind(&SetValue<int>, &async_bytes_read)));
+ EXPECT_EQ(net::OK, reader_->net_error());
+ message_loop_.RunUntilIdle();
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, async_bytes_read);
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+}
+
+TEST_F(BlobReaderTest, RangeError) {
+ const std::string kData("Hello!!!");
+ const size_t kDataSize = 8ul;
+ const uint64_t kReadLength = 4ull;
+
+ // Case: offset too high.
+ BlobDataBuilder b("uuid1");
+ b.AppendData(kData);
+ this->InitializeReader(&b);
+ int size_result = -1;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ scoped_refptr<net::IOBuffer> buffer = CreateBuffer(kDataSize);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->SetReadRange(kDataSize + 1, kReadLength));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+
+ // Case: length too long.
+ BlobDataBuilder b2("uuid2");
+ b2.AppendData(kData);
+ this->InitializeReader(&b2);
+ size_result = -1;
+ EXPECT_EQ(BlobReader::Status::DONE,
+ reader_->CalculateSize(base::Bind(&SetValue<int>, &size_result)));
+ buffer = CreateBuffer(kDataSize + 1);
+ EXPECT_EQ(BlobReader::Status::NET_ERROR,
+ reader_->SetReadRange(0, kDataSize + 1));
+ EXPECT_EQ(net::ERR_FILE_NOT_FOUND, reader_->net_error());
+}
+
+} // namespace
+} // namespace storage
« no previous file with comments | « no previous file | content/browser/fileapi/blob_url_request_job_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698