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 |