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..48a8376dfa80b4871e3eddc608b6a6028f67eafc |
--- /dev/null |
+++ b/chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_stream_reader_unittest.cc |
@@ -0,0 +1,292 @@ |
+// 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 "net/base/test_completion_callback.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 }; |
+ |
+// Registers and counts completion callback calls. |
+class CountedTestCompletionCallback : public net::TestCompletionCallback { |
+ public: |
+ CountedTestCompletionCallback() : called_times_(0) {} |
+ virtual ~CountedTestCompletionCallback() {} |
+ |
+ // Returns how many times the callback has been called. |
+ int called_times() const { return called_times_; } |
+ |
+ // GetResult() can't be used if we don't know the argument. So we need another |
+ // method for such case. Note, that the result may be undefined if SetResult |
+ // was never called. |
+ int MaybeGetResultWithoutWaiting() const { return result_; } |
+ |
+ protected: |
+ void SetResult(int result) { |
hashimoto
2014/06/12 12:22:06
Why you can't just EXPECT_FALSE(have_result()) ins
mtomasz
2014/06/13 00:32:51
EXPECT_FALSE(have_result()) within SetResult? That
hashimoto
2014/06/13 06:49:56
If you really want to leave that information, you
mtomasz
2014/06/16 03:29:36
I really don't think that having a class, which ov
hashimoto
2014/06/19 06:45:06
The one thing I don't totally understand from the
mtomasz
2014/06/20 01:16:20
This comparison is not fair. You didn't add commen
hashimoto
2014/06/27 01:35:55
What you need is only one template function.
hashimoto
2014/06/27 01:35:55
What you need is only one template function.
mtomasz
2014/06/27 05:27:54
It's hard to test everything. But if we can test m
|
+ net::TestCompletionCallback::SetResult(result); |
+ called_times_++; |
+ } |
+ |
+ private: |
+ int called_times_; |
+ DISALLOW_COPY_AND_ASSIGN(CountedTestCompletionCallback); |
+}; |
+ |
+// Fake internal file stream reader. |
+class FakeFileStreamReader : public webkit_blob::FileStreamReader { |
+ public: |
+ FakeFileStreamReader(const net::CompletionCallback& read_called_callback, |
hashimoto
2014/06/12 12:22:06
Sorry for not emphasizing this:
mtomasz
2014/06/13 00:32:51
Got it. However, still a lot of tricks remain, if
hashimoto
2014/06/13 06:49:56
This code is messed up because TestCompletionCallb
mtomasz
2014/06/16 03:29:36
The code will be still messed up if we don't use i
hashimoto
2014/06/19 06:45:06
Why don't you demonstrate it in this CL? (IIUC dem
mtomasz
2014/06/20 01:16:20
It's already there. Lines #137 - #149 are very com
|
+ TestMode mode, |
+ bool return_error) |
+ : read_called_callback_(read_called_callback), |
+ 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 { |
+ read_called_callback_.Run(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: |
+ net::CompletionCallback read_called_callback_; |
+ 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) { |
+ CountedTestCompletionCallback inner_result; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ inner_result.callback(), 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. |
+ { |
+ CountedTestCompletionCallback read_result; |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = reader.Read(buffer, kChunkSize, read_result.callback()); |
+ // Calling WaitForResult() would freeze the test if the callback is not |
+ // called. It is better to assert than timeout. |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ base::RunLoop().RunUntilIdle(); |
+ // If the callback wasn't called, then read_result_value is undefined. |
mtomasz
2014/06/20 03:24:11
This is not true that it would be undefined. Inste
|
+ ASSERT_EQ(1, read_result.called_times()); |
+ // GetResult() will not freeze, because we are sure that the callback is |
+ // already called (#144). |
mtomasz
2014/06/12 04:13:16
#144 -> #145.
|
+ const int read_result_value = read_result.GetResult(result); |
+ EXPECT_EQ(kChunkSize, read_result_value); |
+ |
+ // Calling WaitForResult() would freeze the test if the callback is not |
+ // called. It is better to assert than timeout. |
+ base::RunLoop().RunUntilIdle(); |
+ // If the callback wasn't called, then inner_result_value is undefined. |
+ ASSERT_EQ(1, inner_result.called_times()); |
+ // We don't know the result returned by the internal Read(), so we can't |
+ // call inner_result.GetResult(), simply because we don't know the arugment. |
+ const int inner_result_value = inner_result.MaybeGetResultWithoutWaiting(); |
+ EXPECT_EQ(kBufferSize, inner_result_value); |
+ } |
+ |
+ // Second read should return data from the preloading buffer, without calling |
+ // the internal file stream reader. |
+ { |
+ CountedTestCompletionCallback read_result; |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = reader.Read(buffer, kChunkSize, read_result.callback()); |
+ // Results returned synchronously, so the callback should not be called. |
+ // GetResult() may cause RunLoop::Run(), and hence freeze the test if |
+ // |result| is ERR_IO_PENDING, and the callback is not called. |
+ EXPECT_EQ(kChunkSize, result); |
+ base::RunLoop() |
+ .RunUntilIdle(); // read_result.WaitForResult() would freeze! |
+ EXPECT_FALSE(read_result.have_result()); |
+ // Results returned synchronously, so no new read result events. |
+ EXPECT_EQ(1, inner_result.called_times()); |
+ } |
+ |
+ // Third read should return partial result from the preloading buffer. It is |
+ // valid to return less bytes than requested. |
+ { |
+ CountedTestCompletionCallback read_result; |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = reader.Read(buffer, kChunkSize, read_result.callback()); |
+ EXPECT_EQ(kBufferSize - 2 * kChunkSize, result); |
+ base::RunLoop() |
+ .RunUntilIdle(); // read_result.WaitForResult() would freeze! |
+ EXPECT_FALSE(read_result.have_result()); |
+ // Results returned synchronously, so no new read result events. |
+ EXPECT_EQ(1, inner_result.called_times()); |
+ } |
+ |
+ // The preloading buffer is now empty, so reading should invoke the internal |
+ // file stream reader. |
+ { |
+ CountedTestCompletionCallback read_result; |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = reader.Read(buffer, kChunkSize, read_result.callback()); |
+ EXPECT_EQ(net::ERR_IO_PENDING, result); |
+ const int read_result_value = read_result.GetResult(result); |
+ EXPECT_EQ(kChunkSize, read_result_value); |
+ // If the callback is not called, then we will freeze. Asserting is better |
+ // than freezing until the test timeouts. |
+ // const int inner_result_value = inner_result.WaitForResult(); |
+ base::RunLoop().RunUntilIdle(); |
+ ASSERT_EQ(2, inner_result.called_times()); |
+ // We don't know the result returned by the internal Read(), so we can't |
+ // call inner_result.GetResult(), simply because we don't know the arugment. |
+ EXPECT_EQ(kBufferSize, inner_result.MaybeGetResultWithoutWaiting()); |
+ } |
+} |
+ |
+/* |
+TEST_P(FileSystemProviderBufferingFileStreamReaderTest, |
+ Read_MoreThanBufferSize) { |
+ Logger logger; |
+ BufferingFileStreamReader reader( |
+ scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( |
+ &logger, GetParam(), false)), |
+ 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, base::Unretained(&logger))); |
+ 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)), |
+ kBufferSize); |
+ |
+ scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); |
+ const int result = |
+ reader.Read(buffer, |
+ kChunkSize, |
+ base::Bind(&Logger::OnReadResult, base::Unretained(&logger))); |
+ 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)), |
+ kBufferSize); |
+ |
+ const int64 result = reader.GetLength( |
+ base::Bind(&Logger::OnGetLengthResult, base::Unretained(&logger))); |
+ 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 |