Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include <string> | |
| 6 #include <vector> | |
| 7 | |
| 8 #include "base/memory/ref_counted.h" | |
| 9 #include "base/memory/scoped_ptr.h" | |
| 10 #include "base/memory/weak_ptr.h" | |
| 11 #include "base/run_loop.h" | |
| 12 #include "chrome/browser/chromeos/file_system_provider/fileapi/buffering_file_st ream_reader.h" | |
| 13 #include "content/public/test/test_browser_thread_bundle.h" | |
| 14 #include "net/base/io_buffer.h" | |
| 15 #include "net/base/net_errors.h" | |
| 16 #include "testing/gtest/include/gtest/gtest.h" | |
| 17 | |
| 18 namespace chromeos { | |
| 19 namespace file_system_provider { | |
| 20 namespace { | |
| 21 | |
| 22 // Size of the fake file in bytes. | |
| 23 const int kFileSize = 1024; | |
| 24 | |
| 25 // Size of the preloading buffer in bytes. | |
| 26 const int kBufferSize = 8; | |
| 27 | |
| 28 // Number of bytes requested per BufferingFileStreamReader::Read(). | |
| 29 const int kChunkSize = 3; | |
| 30 | |
| 31 // Behaviour tested by a test case. | |
| 32 enum TestMode { SYNCHRONOUS, ASYNCHRONOUS }; | |
| 33 | |
| 34 // Logs invocations on the fake file stream reader. | |
| 35 class Logger { | |
| 36 public: | |
| 37 Logger() : weak_ptr_factory_(this) {} | |
| 38 virtual ~Logger() {} | |
| 39 | |
| 40 // Registers calls to the internal file stream reader, for the Read() method. | |
| 41 void OnInnerReadRequested(int buf_len) { | |
| 42 inner_read_requested_events_.push_back(buf_len); | |
| 43 } | |
| 44 | |
| 45 // Registers results returned asynchronously by the buffering stream reader, | |
| 46 // for the Read() method. | |
| 47 void OnReadResult(int result) { read_result_events_.push_back(result); } | |
| 48 | |
| 49 // Registers eresults returned asynchronously by the buffering strem reader, | |
|
hashimoto
2014/06/05 03:18:36
nit: s/eresults/results/?
mtomasz
2014/06/05 09:22:27
Done.
| |
| 50 // for the GetLength() method. | |
| 51 void OnGetLengthResult(int64 result) { | |
| 52 get_length_result_events_.push_back(result); | |
| 53 } | |
| 54 | |
| 55 // Gets a weak pointer for asynchronous callback invocations on the logger. | |
| 56 base::WeakPtr<Logger> GetWeakPtr() { return weak_ptr_factory_.GetWeakPtr(); } | |
|
hashimoto
2014/06/05 03:18:36
No need to have this method?
I don't see any dange
mtomasz
2014/06/05 09:22:27
We are passing it to the BufferingFileStreamReader
hashimoto
2014/06/05 10:07:10
I think it's OK to take advantage of the single th
mtomasz
2014/06/06 01:31:03
Even if all threads are backed by the same message
hashimoto
2014/06/06 03:36:52
(1) Here we know that doesn't happen with the test
mtomasz
2014/06/06 04:22:56
Your suggestion also uses vectors and base::Bind,
hashimoto
2014/06/09 11:12:04
I still don't understand why you need this class:
mtomasz
2014/06/09 13:51:15
I think we are talking about two different things.
hashimoto
2014/06/10 10:43:24
Sorry, I forgot to note that inner_read_requested_
mtomasz
2014/06/10 12:23:55
1. inner_read_requested_events_ can't be replaced.
hashimoto
2014/06/11 06:01:03
You can do it because you are passing a new callba
mtomasz
2014/06/12 04:10:09
I made the change as you suggested for comparison.
| |
| 57 | |
| 58 const std::vector<int>& inner_read_requested_events() const { | |
| 59 return inner_read_requested_events_; | |
| 60 } | |
| 61 | |
| 62 const std::vector<int>& read_result_events() const { | |
| 63 return read_result_events_; | |
| 64 } | |
| 65 | |
| 66 const std::vector<int64>& get_length_result_events() const { | |
| 67 return get_length_result_events_; | |
| 68 } | |
| 69 | |
| 70 private: | |
| 71 std::vector<int> inner_read_requested_events_; | |
| 72 std::vector<int> read_result_events_; | |
| 73 std::vector<int64> get_length_result_events_; | |
| 74 | |
| 75 base::WeakPtrFactory<Logger> weak_ptr_factory_; | |
| 76 DISALLOW_COPY_AND_ASSIGN(Logger); | |
| 77 }; | |
| 78 | |
| 79 // Fake internal file stream reader. | |
| 80 class FakeFileStreamReader : public webkit_blob::FileStreamReader { | |
| 81 public: | |
| 82 FakeFileStreamReader(Logger* logger, TestMode mode, bool return_error) | |
| 83 : logger_(logger), mode_(mode), return_error_(return_error) {} | |
| 84 virtual ~FakeFileStreamReader() {} | |
| 85 | |
| 86 // webkit_blob::FileStreamReader overrides. | |
| 87 virtual int Read(net::IOBuffer* buf, | |
| 88 int buf_len, | |
| 89 const net::CompletionCallback& callback) OVERRIDE { | |
| 90 logger_->OnInnerReadRequested(buf_len); | |
| 91 | |
| 92 if (return_error_) { | |
| 93 if (mode_ == SYNCHRONOUS) | |
| 94 return net::ERR_ACCESS_DENIED; | |
| 95 | |
| 96 base::MessageLoopProxy::current()->PostTask( | |
| 97 FROM_HERE, base::Bind(callback, net::ERR_ACCESS_DENIED)); | |
| 98 return net::ERR_IO_PENDING; | |
| 99 } | |
| 100 | |
| 101 const std::string fake_data('X', buf_len); | |
| 102 memcpy(buf->data(), fake_data.c_str(), buf_len); | |
| 103 | |
| 104 if (mode_ == SYNCHRONOUS) | |
| 105 return buf_len; | |
| 106 | |
| 107 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
| 108 base::Bind(callback, buf_len)); | |
| 109 return net::ERR_IO_PENDING; | |
| 110 } | |
| 111 | |
| 112 virtual int64 GetLength( | |
| 113 const net::Int64CompletionCallback& callback) OVERRIDE { | |
| 114 if (mode_ == SYNCHRONOUS) | |
| 115 return kFileSize; | |
| 116 | |
| 117 base::MessageLoopProxy::current()->PostTask( | |
| 118 FROM_HERE, base::Bind(callback, kFileSize)); | |
| 119 return net::ERR_IO_PENDING; | |
| 120 } | |
| 121 | |
| 122 private: | |
| 123 Logger* logger_; // Not owned. | |
| 124 TestMode mode_; | |
| 125 bool return_error_; | |
| 126 DISALLOW_COPY_AND_ASSIGN(FakeFileStreamReader); | |
| 127 }; | |
| 128 | |
| 129 } // namespace | |
| 130 | |
| 131 class FileSystemProviderBufferingFileStreamReaderTest | |
| 132 : public testing::Test, | |
| 133 public ::testing::WithParamInterface<TestMode> { | |
| 134 protected: | |
| 135 FileSystemProviderBufferingFileStreamReaderTest() {} | |
| 136 virtual ~FileSystemProviderBufferingFileStreamReaderTest() {} | |
| 137 | |
| 138 content::TestBrowserThreadBundle thread_bundle_; | |
| 139 }; | |
| 140 | |
| 141 TEST_P(FileSystemProviderBufferingFileStreamReaderTest, Read) { | |
| 142 Logger logger; | |
| 143 BufferingFileStreamReader reader( | |
| 144 scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( | |
| 145 &logger, GetParam(), false /* return_error */)), | |
| 146 kBufferSize); | |
| 147 | |
| 148 // For the first read, the internal file stream reader is fired, as there is | |
| 149 // no data in the preloading buffer. | |
| 150 { | |
| 151 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); | |
| 152 const int result = | |
| 153 reader.Read(buffer, | |
| 154 kChunkSize, | |
| 155 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
|
hashimoto
2014/06/05 03:18:36
How about using TestCompletionCallback?
The same g
mtomasz
2014/06/05 09:22:27
With TestCompletionCallback we can't control how m
hashimoto
2014/06/06 03:36:52
What do you mean?
TestCompletionCallback::WaitForR
mtomasz
2014/06/06 04:22:56
If the tested class by accident calls the callback
hashimoto
2014/06/09 11:12:04
At least, we can catch it because it crashes as yo
mtomasz
2014/06/09 13:51:15
It would crash. But I don't think crashing is bett
hashimoto
2014/06/10 10:43:24
You cannot prepare for every possible failure whic
mtomasz
2014/06/10 12:23:55
I cannot test everything, but I'm not sure if it i
hashimoto
2014/06/11 06:01:03
This doesn't affect the test coverage (i.e. how mu
mtomasz
2014/06/16 04:12:17
It affects how detailed the tests are. IMHO tests
hashimoto
2014/06/19 07:27:45
TestCompletionCallback is widely used and it makes
mtomasz
2014/06/20 04:20:23
TestCompletionCallback makes the code nice and sho
| |
| 156 base::RunLoop().RunUntilIdle(); | |
| 157 | |
| 158 EXPECT_EQ(net::ERR_IO_PENDING, result); | |
| 159 ASSERT_EQ(1u, logger.inner_read_requested_events().size()); | |
| 160 EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); | |
| 161 ASSERT_EQ(1u, logger.read_result_events().size()); | |
| 162 EXPECT_EQ(kChunkSize, logger.read_result_events()[0]); | |
| 163 } | |
| 164 | |
| 165 // Second read should return data from the preloading buffer, without calling | |
| 166 // the internal file stream reader. | |
| 167 { | |
| 168 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); | |
| 169 const int result = | |
| 170 reader.Read(buffer, | |
| 171 kChunkSize, | |
| 172 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
| 173 base::RunLoop().RunUntilIdle(); | |
| 174 | |
| 175 EXPECT_EQ(kChunkSize, result); | |
| 176 EXPECT_EQ(1u, logger.inner_read_requested_events().size()); | |
| 177 // Results returned synchronously, so no new read result events. | |
| 178 EXPECT_EQ(1u, logger.read_result_events().size()); | |
| 179 } | |
| 180 | |
| 181 // Third read should return partial result from the preloading buffer. It is | |
| 182 // valid to return less bytes than requested. | |
| 183 { | |
| 184 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); | |
| 185 const int result = | |
| 186 reader.Read(buffer, | |
| 187 kChunkSize, | |
| 188 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
| 189 base::RunLoop().RunUntilIdle(); | |
| 190 | |
| 191 EXPECT_EQ(kBufferSize - 2 * kChunkSize, result); | |
| 192 EXPECT_EQ(1u, logger.inner_read_requested_events().size()); | |
| 193 // Results returned synchronously, so no new read result events. | |
| 194 EXPECT_EQ(1u, logger.read_result_events().size()); | |
| 195 } | |
| 196 | |
| 197 // The preloading buffer is now empty, so reading should invoke the internal | |
| 198 // file stream reader. | |
| 199 { | |
| 200 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); | |
| 201 const int result = | |
| 202 reader.Read(buffer, | |
| 203 kChunkSize, | |
| 204 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
| 205 base::RunLoop().RunUntilIdle(); | |
| 206 | |
| 207 EXPECT_EQ(net::ERR_IO_PENDING, result); | |
| 208 ASSERT_EQ(2u, logger.inner_read_requested_events().size()); | |
| 209 EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); | |
| 210 ASSERT_EQ(2u, logger.read_result_events().size()); | |
| 211 EXPECT_EQ(kChunkSize, logger.read_result_events()[1]); | |
| 212 } | |
| 213 } | |
| 214 | |
| 215 TEST_P(FileSystemProviderBufferingFileStreamReaderTest, | |
| 216 Read_MoreThanBufferSize) { | |
| 217 Logger logger; | |
| 218 BufferingFileStreamReader reader( | |
| 219 scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( | |
| 220 &logger, GetParam(), false /* return_error */)), | |
| 221 kBufferSize); | |
| 222 | |
| 223 // Returning less than requested number of bytes is valid, and should not | |
| 224 // fail. | |
| 225 const int chunk_size = 20; | |
| 226 ASSERT_LT(kBufferSize, chunk_size); | |
| 227 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(chunk_size)); | |
| 228 const int result = | |
| 229 reader.Read(buffer, | |
| 230 chunk_size, | |
| 231 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
| 232 base::RunLoop().RunUntilIdle(); | |
| 233 | |
| 234 EXPECT_EQ(net::ERR_IO_PENDING, result); | |
| 235 ASSERT_EQ(1u, logger.inner_read_requested_events().size()); | |
| 236 EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); | |
| 237 ASSERT_EQ(1u, logger.read_result_events().size()); | |
| 238 EXPECT_EQ(kBufferSize, logger.read_result_events()[0]); | |
| 239 } | |
| 240 | |
| 241 TEST_P(FileSystemProviderBufferingFileStreamReaderTest, Read_WithError) { | |
| 242 Logger logger; | |
| 243 BufferingFileStreamReader reader( | |
| 244 scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( | |
| 245 &logger, GetParam(), true /* return_error */)), | |
| 246 kBufferSize); | |
| 247 | |
| 248 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kChunkSize)); | |
| 249 const int result = | |
| 250 reader.Read(buffer, | |
| 251 kChunkSize, | |
| 252 base::Bind(&Logger::OnReadResult, logger.GetWeakPtr())); | |
| 253 base::RunLoop().RunUntilIdle(); | |
| 254 | |
| 255 EXPECT_EQ(net::ERR_IO_PENDING, result); | |
| 256 ASSERT_EQ(1u, logger.inner_read_requested_events().size()); | |
| 257 EXPECT_EQ(kBufferSize, logger.inner_read_requested_events()[0]); | |
| 258 ASSERT_EQ(1u, logger.read_result_events().size()); | |
| 259 EXPECT_EQ(net::ERR_ACCESS_DENIED, logger.read_result_events()[0]); | |
| 260 } | |
| 261 | |
| 262 TEST_P(FileSystemProviderBufferingFileStreamReaderTest, GetLength) { | |
| 263 Logger logger; | |
| 264 BufferingFileStreamReader reader( | |
| 265 scoped_ptr<webkit_blob::FileStreamReader>(new FakeFileStreamReader( | |
| 266 &logger, GetParam(), false /* return_error */)), | |
| 267 kBufferSize); | |
| 268 | |
| 269 const int64 result = reader.GetLength( | |
| 270 base::Bind(&Logger::OnGetLengthResult, logger.GetWeakPtr())); | |
| 271 base::RunLoop().RunUntilIdle(); | |
| 272 | |
| 273 if (GetParam() == SYNCHRONOUS) { | |
| 274 EXPECT_EQ(kFileSize, result); | |
| 275 EXPECT_EQ(0u, logger.get_length_result_events().size()); | |
| 276 } else { | |
| 277 EXPECT_EQ(net::ERR_IO_PENDING, result); | |
| 278 ASSERT_EQ(1u, logger.get_length_result_events().size()); | |
| 279 EXPECT_EQ(kFileSize, logger.get_length_result_events()[0]); | |
| 280 } | |
| 281 } | |
| 282 | |
| 283 INSTANTIATE_TEST_CASE_P(Synchronous, | |
| 284 FileSystemProviderBufferingFileStreamReaderTest, | |
| 285 testing::Values(SYNCHRONOUS)); | |
| 286 | |
| 287 INSTANTIATE_TEST_CASE_P(Asynchronous, | |
| 288 FileSystemProviderBufferingFileStreamReaderTest, | |
| 289 testing::Values(ASYNCHRONOUS)); | |
| 290 | |
| 291 } // namespace file_system_provider | |
| 292 } // namespace chromeos | |
| OLD | NEW |