| // 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 <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| #include <utility> |
| |
| #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/macros.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_reader.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_(std::move(entry)) {} |
| ~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_t 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_t offset, |
| IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) override { |
| return entry_->ReadSparseData(offset, buf, buf_len, callback); |
| } |
| |
| int WriteSparseData(int64_t offset, |
| IOBuffer* buf, |
| int buf_len, |
| const CompletionCallback& callback) override { |
| return entry_->WriteSparseData(offset, buf, buf_len, callback); |
| } |
| |
| int GetAvailableRange(int64_t offset, |
| int len, |
| int64_t* 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; |
| } |
| |
| 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; |
| } |
| |
| 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_t 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) : nullptr; |
| provider_ = new MockFileStreamReaderProvider(); |
| scoped_ptr<BlobReader::FileStreamReaderProvider> temp_ptr(provider_); |
| reader_.reset(new BlobReader(blob_handle_.get(), std::move(temp_ptr), |
| 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))); |
| 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_t::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 |