Chromium Code Reviews| 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..dd709eeeda30b9c18b4e2a71c323d52cea3aab78 |
| --- /dev/null |
| +++ b/content/browser/fileapi/blob_reader_unittest.cc |
| @@ -0,0 +1,1121 @@ |
| +// 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) {} |
| + |
| + void ReturnError(int net_error) { net_error_ = net_error; } |
| + |
| + void DoAsync(base::SingleThreadTaskRunner* runner) { |
| + async_task_runner_ = runner; |
| + } |
| + |
| + ~FakeFileStreamReader() override {} |
| + |
| + int Read(net::IOBuffer* buf, |
| + int buf_length, |
| + const net::CompletionCallback& done) override { |
| + if (net_error_ == net::OK) { |
| + if (async_task_runner_.get()) { |
| + async_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(base::IgnoreResult(&FakeFileStreamReader::ReadImpl), |
| + base::Unretained(this), scoped_refptr<IOBuffer>(buf), |
| + buf_length, done)); |
| + return net::ERR_IO_PENDING; |
| + } else { |
| + return ReadImpl(buf, buf_length, net::CompletionCallback()); |
| + } |
| + } |
| + if (async_task_runner_.get()) { |
| + async_task_runner_->PostTask(FROM_HERE, base::Bind(done, net_error_)); |
| + return net::ERR_IO_PENDING; |
| + } |
| + return net_error_; |
| + } |
| + |
| + int64 GetLength(const net::Int64CompletionCallback& size_callback) override { |
| + if (net_error_ == net::OK) { |
| + if (async_task_runner_.get()) { |
| + async_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(base::IgnoreResult(&FakeFileStreamReader::GetLengthImpl), |
| + base::Unretained(this), size_callback)); |
| + return net::ERR_IO_PENDING; |
| + } else { |
| + return GetLengthImpl(net::Int64CompletionCallback()); |
|
michaeln
2015/09/23 18:52:45
What is the purpose of the GetLenghtImpl helper? S
dmurph
2015/09/25 17:11:43
Yeah I'll just return size.
|
| + } |
| + } |
| + if (async_task_runner_.get()) { |
| + async_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(size_callback, static_cast<int64_t>(net_error_))); |
| + return net::ERR_IO_PENDING; |
| + } |
| + return net_error_; |
| + } |
| + |
| + 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; |
| + } |
| + |
| + int64 GetLengthImpl(const net::Int64CompletionCallback& size_callback) { |
| + if (size_callback.is_null()) { |
| + return size_; |
| + } |
| + size_callback.Run(size_); |
| + return net::ERR_IO_PENDING; |
|
michaeln
2015/09/23 18:52:45
looks odd to be calling the callback and returning
dmurph
2015/09/25 17:11:43
Yeah, I'll only use this as a callback, and switch
|
| + } |
| + |
| + 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->DoAsync(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->DoAsync(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->DoAsync(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->DoAsync(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->DoAsync(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->DoAsync(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))); |
| + EXPECT_EQ(net::ERR_FAILED, reader_->net_error()); |
| + EXPECT_EQ(0, 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))); |
| + EXPECT_EQ(net::ERR_FAILED, reader_->net_error()); |
| + EXPECT_EQ(0, 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->ReturnError(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->ReturnError(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->DoAsync(message_loop_.task_runner().get()); |
| + reader->ReturnError(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->ReturnError(net::ERR_FILE_NOT_FOUND); |
| + reader->DoAsync(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 |