Index: chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc |
diff --git a/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc b/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c7cd257c3cc0ddb8da130f5b5027622f0dfb6836 |
--- /dev/null |
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc |
@@ -0,0 +1,293 @@ |
+// Copyright 2014 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 <string> |
+#include <vector> |
+ |
+#include "base/memory/ref_counted.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "base/memory/weak_ptr.h" |
+#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
+#include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader.h" |
+#include "content/public/test/test_browser_thread_bundle.h" |
+#include "net/base/io_buffer.h" |
+#include "net/base/net_errors.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace chromeos { |
+namespace file_system_provider { |
+namespace { |
+ |
+// Size of the fake file in bytes. |
+const int kFileSize = 1024; |
+ |
+// Size of the preloading buffer in bytes. |
+const int kBufferSize = 8; |
+ |
+// Number of bytes requested per BufferingFileStreamReader::Read(). |
+const int kChunkSize = 3; |
+ |
+// Behaviour tested by a test case. |
+enum TestMode { SYNCHRONOUS, ASYNCHRONOUS }; |
+ |
+// Logs invocations on the fake file stream reader. |
+class Logger { |
+ public: |
+ Logger() : weak_ptr_factory_(this) {} |
+ virtual ~Logger() {} |
+ |
+ // Registers calls to the internal file stream reader, for the Read() method. |
+ void OnInnerReadRequested(int buf_len) { |
+ inner_read_requested_events_.push_back(buf_len); |
+ } |
+ |
+ // Registers results returned asynchronously by the buffering stream reader, |
+ // for the Read() method. |
+ void OnReadResult(int result) { read_result_events_.push_back(result); } |
+ |
+ // Registers results returned asynchronously by the buffering stream reader, |
+ // for the GetLength() method. |
+ void OnGetLengthResult(int64 result) { |
+ get_length_result_events_.push_back(result); |
+ } |
+ |
+ // Gets a weak pointer for asynchronous callback invocations on the logger. |
+ base::WeakPtr<Logger> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } |
+ |
+ const std::vector<int>& inner_read_requested_events() const { |
+ return inner_read_requested_events_; |
+ } |
+ |
+ const std::vector<int>& read_result_events() const { |
+ return read_result_events_; |
+ } |
+ |
+ const std::vector<int64>& get_length_result_events() const { |
+ return get_length_result_events_; |
+ } |
+ |
+ private: |
+ std::vector<int> inner_read_requested_events_; |
+ std::vector<int> read_result_events_; |
+ std::vector<int64> get_length_result_events_; |
+ |
+ base::WeakPtrFactory<Logger> weak_ptr_factory_; |
+ DISALLOW_COPY_AND_ASSIGN(Logger); |
+}; |
+ |
+// Fake internal file stream reader. |
+class FakeFileStreamReader : public webkit_blob::FileStreamReader { |
+ public: |
+ FakeFileStreamReader(Logger* logger, TestMode mode, bool return_error) |
+ : logger_(logger), mode_(mode), return_error_(return_error) {} |
+ virtual ~FakeFileStreamReader() {} |
+ |
+ // webkit_blob::FileStreamReader overrides. |
+ virtual int Read(net::IOBuffer* buf, |
+ int buf_len, |
+ const net::CompletionCallback& callback) OVERRIDE { |
+ logger_->OnInnerReadRequested(buf_len); |
+ |
+ if (return_error_) { |
+ if (mode_ == SYNCHRONOUS) |
+ return net::ERR_ACCESS_DENIED; |
+ |
+ base::MessageLoopProxy::current()->PostTask( |
+ FROM_HERE, base::Bind(callback, net::ERR_ACCESS_DENIED)); |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
+ const std::string fake_data('X', buf_len); |
+ memcpy(buf->data(), fake_data.c_str(), buf_len); |
+ |
+ if (mode_ == SYNCHRONOUS) |
+ return buf_len; |
+ |
+ base::MessageLoopProxy::current()->PostTask(FROM_HERE, |
+ base::Bind(callback, buf_len)); |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
+ virtual int64 GetLength( |
+ const net::Int64CompletionCallback& callback) OVERRIDE { |
+ if (mode_ == SYNCHRONOUS) |
+ return kFileSize; |
+ |
+ base::MessageLoopProxy::current()->PostTask( |
+ FROM_HERE, base::Bind(callback, kFileSize)); |
+ return net::ERR_IO_PENDING; |
+ } |
+ |
+ private: |
+ Logger* logger_; // Not owned. |
+ TestMode mode_; |
+ bool return_error_; |
+ DISALLOW_COPY_AND_ASSIGN(FakeFileStreamReader); |
+}; |
+ |
+} // namespace |
+ |
+class FileSystemProviderBufferingFileStreamReaderTest |
+ : public testing::Test, |
+ public ::testing::WithParamInterface<TestMode> { |
+ protected: |
+ FileSystemProviderBufferingFileStreamReaderTest() {} |
+ virtual ~FileSystemProviderBufferingFileStreamReaderTest() {} |
+ |
+ content::TestBrowserThreadBundle thread_bundle_; |
+}; |
+ |
+TEST_P(FileSystemProviderBufferingFileStreamReaderTest, Read) { |
+ Logger logger; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ &logger, GetParam(), false /* return_error */)), |
+ kBufferSize); |
+ |
+ // For the first read, the internal file stream reader is fired, as there is |
+ // no data in the preloading buffer. |
+ { |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ ASSERT_EQ(1u, logger.inner_read_requested_events().size()); |
+ EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); |
+ ASSERT_EQ(1u, logger.read_result_events().size()); |
+ EXPECT_EQ(kChunkSize, logger.read_result_events()[0]); |
+ } |
+ |
+ // Second read should return data from the preloading buffer, without calling |
+ // the internal file stream reader. |
+ { |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(kChunkSize, result); |
+ EXPECT_EQ(1u, logger.inner_read_requested_events().size()); |
+ // Results returned synchronously, so no new read result events. |
+ EXPECT_EQ(1u, logger.read_result_events().size()); |
+ } |
+ |
+ // Third read should return partial result from the preloading buffer. It is |
+ // valid to return less bytes than requested. |
+ { |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(kBufferSize - 2 * kChunkSize, result); |
+ EXPECT_EQ(1u, logger.inner_read_requested_events().size()); |
+ // Results returned synchronously, so no new read result events. |
+ EXPECT_EQ(1u, logger.read_result_events().size()); |
+ } |
+ |
+ // The preloading buffer is now empty, so reading should invoke the internal |
+ // file stream reader. |
+ { |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ ASSERT_EQ(2u, logger.inner_read_requested_events().size()); |
+ EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); |
+ ASSERT_EQ(2u, logger.read_result_events().size()); |
+ EXPECT_EQ(kChunkSize, logger.read_result_events()[1]); |
+ } |
+} |
+ |
+TEST_P(FileSystemProviderBufferingFileStreamReaderTest, |
+ Read_MoreThanBufferSize) { |
+ Logger logger; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ &logger, GetParam(), false /* return_error */)), |
+ kBufferSize); |
+ |
+ // Returning less than requested number of bytes is valid, and should not |
+ // fail. |
+ const int chunk_size = 20; |
+ ASSERT_LT(kBufferSize, chunk_size); |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(chunk_size)); |
+ const int result = |
+ reader.Read(buffer, |
+ chunk_size, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ ASSERT_EQ(1u, logger.inner_read_requested_events().size()); |
+ EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); |
+ ASSERT_EQ(1u, logger.read_result_events().size()); |
+ EXPECT_EQ(kBufferSize, logger.read_result_events()[0]); |
+} |
+ |
+TEST_P(FileSystemProviderBufferingFileStreamReaderTest, Read_WithError) { |
+ Logger logger; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ &logger, GetParam(), true /* return_error */)), |
+ kBufferSize); |
+ |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ ASSERT_EQ(1u, logger.inner_read_requested_events().size()); |
+ EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); |
+ ASSERT_EQ(1u, logger.read_result_events().size()); |
+ EXPECT_EQ(net::ERR_ACCESS_DENIED, logger.read_result_events()[0]); |
+} |
+ |
+TEST_P(FileSystemProviderBufferingFileStreamReaderTest, GetLength) { |
+ Logger logger; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ &logger, GetParam(), false /* return_error */)), |
+ kBufferSize); |
+ |
+ const int64 result = reader.GetLength( |
+ base::Bind(&Logger::OnGetLengthResult, logger.GetWeakPtr())); |
+ base::RunLoop().RunUntilIdle(); |
+ |
+ if (GetParam() == SYNCHRONOUS) { |
+ EXPECT_EQ(kFileSize, result); |
+ EXPECT_EQ(0u, logger.get_length_result_events().size()); |
+ } else { |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ ASSERT_EQ(1u, logger.get_length_result_events().size()); |
+ EXPECT_EQ(kFileSize, logger.get_length_result_events()[0]); |
+ } |
+} |
+ |
+INSTANTIATE_TEST_CASE_P(Synchronous, |
+ FileSystemProviderBufferingFileStreamReaderTest, |
+ testing::Values(SYNCHRONOUS)); |
+ |
+INSTANTIATE_TEST_CASE_P(Asynchronous, |
+ FileSystemProviderBufferingFileStreamReaderTest, |
+ testing::Values(ASYNCHRONOUS)); |
+ |
+} // namespace file_system_provider |
+} // namespace chromeos |