Chromium Code Reviews| 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 |