drive_file_stream_reader_unittest.cc revision a93a17c8d99d686bd4a1511e5504e5e6cc9fcadf
1// Copyright 2013 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 "chrome/browser/chromeos/drive/drive_file_stream_reader.h"
6
7#include <string>
8
9#include "base/bind.h"
10#include "base/files/file_path.h"
11#include "base/files/scoped_temp_dir.h"
12#include "base/message_loop.h"
13#include "base/rand_util.h"
14#include "base/sequenced_task_runner.h"
15#include "base/threading/thread.h"
16#include "chrome/browser/chromeos/drive/fake_file_system.h"
17#include "chrome/browser/chromeos/drive/file_system_util.h"
18#include "chrome/browser/chromeos/drive/local_file_reader.h"
19#include "chrome/browser/chromeos/drive/test_util.h"
20#include "chrome/browser/google_apis/fake_drive_service.h"
21#include "chrome/browser/google_apis/task_util.h"
22#include "chrome/browser/google_apis/test_util.h"
23#include "content/public/test/test_browser_thread.h"
24#include "net/base/io_buffer.h"
25#include "net/base/net_errors.h"
26#include "net/base/test_completion_callback.h"
27#include "net/http/http_byte_range.h"
28#include "testing/gtest/include/gtest/gtest.h"
29
30using content::BrowserThread;
31
32namespace drive {
33namespace internal {
34namespace {
35
36// Increments the |num_called|, when this method is invoked.
37void IncrementCallback(int* num_called) {
38  DCHECK(num_called);
39  ++*num_called;
40}
41
42}  // namespace
43
44class LocalReaderProxyTest : public ::testing::Test {
45 protected:
46  LocalReaderProxyTest() : io_thread_(BrowserThread::IO, &message_loop_) {
47  }
48
49  virtual void SetUp() OVERRIDE {
50    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
51    ASSERT_TRUE(google_apis::test_util::CreateFileOfSpecifiedSize(
52        temp_dir_.path(), 1024, &file_path_, &file_content_));
53
54    worker_thread_.reset(new base::Thread("ReaderProxyTest"));
55    ASSERT_TRUE(worker_thread_->Start());
56  }
57
58  virtual void TearDown() OVERRIDE {
59    worker_thread_.reset();
60  }
61
62  MessageLoopForIO message_loop_;
63  content::TestBrowserThread io_thread_;
64
65  base::ScopedTempDir temp_dir_;
66  base::FilePath file_path_;
67  std::string file_content_;
68
69  scoped_ptr<base::Thread> worker_thread_;
70};
71
72TEST_F(LocalReaderProxyTest, Read) {
73  // Open the file first.
74  scoped_ptr<util::LocalFileReader> file_reader(
75      new util::LocalFileReader(worker_thread_->message_loop_proxy()));
76  net::TestCompletionCallback callback;
77  file_reader->Open(file_path_, 0, callback.callback());
78  ASSERT_EQ(net::OK, callback.WaitForResult());
79
80  // Test instance.
81  LocalReaderProxy proxy(file_reader.Pass(), file_content_.size());
82
83  // Make sure the read contant is as same as the file.
84  std::string content;
85  ASSERT_EQ(net::OK, test_util::ReadAllData(&proxy, &content));
86  EXPECT_EQ(file_content_, content);
87}
88
89TEST_F(LocalReaderProxyTest, ReadWithLimit) {
90  // This test case, we only read first half of the file.
91  const std::string expected_content =
92      file_content_.substr(0, file_content_.size() / 2);
93
94  // Open the file first.
95  scoped_ptr<util::LocalFileReader> file_reader(
96      new util::LocalFileReader(worker_thread_->message_loop_proxy()));
97  net::TestCompletionCallback callback;
98  file_reader->Open(file_path_, 0, callback.callback());
99  ASSERT_EQ(net::OK, callback.WaitForResult());
100
101  // Test instance.
102  LocalReaderProxy proxy(file_reader.Pass(), expected_content.size());
103
104  // Make sure the read contant is as same as the file.
105  std::string content;
106  ASSERT_EQ(net::OK, test_util::ReadAllData(&proxy, &content));
107  EXPECT_EQ(expected_content, content);
108}
109
110class NetworkReaderProxyTest : public ::testing::Test {
111 protected:
112  NetworkReaderProxyTest() : io_thread_(BrowserThread::IO, &message_loop_) {
113  }
114
115  MessageLoopForIO message_loop_;
116  content::TestBrowserThread io_thread_;
117};
118
119TEST_F(NetworkReaderProxyTest, EmptyFile) {
120  NetworkReaderProxy proxy(0, 0, base::Bind(&base::DoNothing));
121
122  net::TestCompletionCallback callback;
123  const int kBufferSize = 10;
124  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
125  int result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
126
127  // For empty file, Read() should return 0 immediately.
128  EXPECT_EQ(0, result);
129}
130
131TEST_F(NetworkReaderProxyTest, Read) {
132  NetworkReaderProxy proxy(0, 10, base::Bind(&base::DoNothing));
133
134  net::TestCompletionCallback callback;
135  const int kBufferSize = 3;
136  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
137
138  // If no data is available yet, ERR_IO_PENDING should be returned.
139  int result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
140  EXPECT_EQ(net::ERR_IO_PENDING, result);
141
142  // And when the data is supplied, the callback will be called.
143  scoped_ptr<std::string> data(new std::string("abcde"));
144  proxy.OnGetContent(data.Pass());
145
146  // The returned data should be fit to the buffer size.
147  result = callback.GetResult(result);
148  EXPECT_EQ(3, result);
149  EXPECT_EQ("abc", std::string(buffer->data(), result));
150
151  // The next Read should return immediately because there is pending data
152  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
153  EXPECT_EQ(2, result);
154  EXPECT_EQ("de", std::string(buffer->data(), result));
155
156  // Supply the data before calling Read operation.
157  data.reset(new std::string("fg"));
158  proxy.OnGetContent(data.Pass());
159  data.reset(new std::string("hij"));
160  proxy.OnGetContent(data.Pass());  // Now 10 bytes are supplied.
161
162  // The data should be concatenated if possible.
163  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
164  EXPECT_EQ(3, result);
165  EXPECT_EQ("fgh", std::string(buffer->data(), result));
166
167  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
168  EXPECT_EQ(2, result);
169  EXPECT_EQ("ij", std::string(buffer->data(), result));
170
171  // The whole data is read, so Read() should return 0 immediately by then.
172  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
173  EXPECT_EQ(0, result);
174}
175
176TEST_F(NetworkReaderProxyTest, ReadWithLimit) {
177  NetworkReaderProxy proxy(10, 10, base::Bind(&base::DoNothing));
178
179  net::TestCompletionCallback callback;
180  const int kBufferSize = 3;
181  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
182
183  // If no data is available yet, ERR_IO_PENDING should be returned.
184  int result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
185  EXPECT_EQ(net::ERR_IO_PENDING, result);
186
187  // And when the data is supplied, the callback will be called.
188  scoped_ptr<std::string> data(new std::string("abcde"));
189  proxy.OnGetContent(data.Pass());
190  data.reset(new std::string("fgh"));
191  proxy.OnGetContent(data.Pass());
192  data.reset(new std::string("ijklmno"));
193  proxy.OnGetContent(data.Pass());
194
195  // The returned data should be fit to the buffer size.
196  result = callback.GetResult(result);
197  EXPECT_EQ(3, result);
198  EXPECT_EQ("klm", std::string(buffer->data(), result));
199
200  // The next Read should return immediately because there is pending data
201  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
202  EXPECT_EQ(2, result);
203  EXPECT_EQ("no", std::string(buffer->data(), result));
204
205  // Supply the data before calling Read operation.
206  data.reset(new std::string("pqrs"));
207  proxy.OnGetContent(data.Pass());
208  data.reset(new std::string("tuvwxyz"));
209  proxy.OnGetContent(data.Pass());  // 't' is the 20-th byte.
210
211  // The data should be concatenated if possible.
212  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
213  EXPECT_EQ(3, result);
214  EXPECT_EQ("pqr", std::string(buffer->data(), result));
215
216  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
217  EXPECT_EQ(2, result);
218  EXPECT_EQ("st", std::string(buffer->data(), result));
219
220  // The whole data is read, so Read() should return 0 immediately by then.
221  result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
222  EXPECT_EQ(0, result);
223}
224
225TEST_F(NetworkReaderProxyTest, ErrorWithPendingCallback) {
226  NetworkReaderProxy proxy(0, 10, base::Bind(&base::DoNothing));
227
228  net::TestCompletionCallback callback;
229  const int kBufferSize = 3;
230  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
231
232  // Set pending callback.
233  int result = proxy.Read(buffer.get(), kBufferSize, callback.callback());
234  EXPECT_EQ(net::ERR_IO_PENDING, result);
235
236  // Emulate that an error is found. The callback should be called internally.
237  proxy.OnCompleted(FILE_ERROR_FAILED);
238  result = callback.GetResult(result);
239  EXPECT_EQ(net::ERR_FAILED, result);
240
241  // The next Read call should also return the same error code.
242  EXPECT_EQ(net::ERR_FAILED,
243            proxy.Read(buffer.get(), kBufferSize, callback.callback()));
244}
245
246TEST_F(NetworkReaderProxyTest, ErrorWithPendingData) {
247  NetworkReaderProxy proxy(0, 10, base::Bind(&base::DoNothing));
248
249  net::TestCompletionCallback callback;
250  const int kBufferSize = 3;
251  scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize));
252
253  // Supply the data before an error.
254  scoped_ptr<std::string> data(new std::string("abcde"));
255  proxy.OnGetContent(data.Pass());
256
257  // Emulate that an error is found.
258  proxy.OnCompleted(FILE_ERROR_FAILED);
259
260  // The next Read call should return the error code, even if there is
261  // pending data (the pending data should be released in OnCompleted.
262  EXPECT_EQ(net::ERR_FAILED,
263            proxy.Read(buffer.get(), kBufferSize, callback.callback()));
264}
265
266TEST_F(NetworkReaderProxyTest, CancelJob) {
267  int num_called = 0;
268  {
269    NetworkReaderProxy proxy(
270        0, 0, base::Bind(&IncrementCallback, &num_called));
271    proxy.OnCompleted(FILE_ERROR_OK);
272    // Destroy the instance after the network operation is completed.
273    // The cancelling callback shouldn't be called.
274  }
275  EXPECT_EQ(0, num_called);
276
277  num_called = 0;
278  {
279    NetworkReaderProxy proxy(
280        0, 0, base::Bind(&IncrementCallback, &num_called));
281    // Destroy the instance before the network operation is completed.
282    // The cancelling callback should be called.
283  }
284  EXPECT_EQ(1, num_called);
285}
286
287}  // namespace internal
288
289class DriveFileStreamReaderTest : public ::testing::Test {
290 protected:
291  DriveFileStreamReaderTest()
292      : ui_thread_(BrowserThread::UI),
293        io_thread_(BrowserThread::IO, &message_loop_) {
294  }
295
296  virtual void SetUp() OVERRIDE {
297    ui_thread_.Start();
298
299    worker_thread_.reset(new base::Thread("DriveFileStreamReaderTest"));
300    ASSERT_TRUE(worker_thread_->Start());
301
302    BrowserThread::PostTaskAndReply(
303        BrowserThread::UI,
304        FROM_HERE,
305        base::Bind(&DriveFileStreamReaderTest::SetUpOnUIThread,
306                   base::Unretained(this)),
307        base::MessageLoop::QuitClosure());
308    message_loop_.Run();
309  }
310
311  virtual void TearDown() OVERRIDE {
312    BrowserThread::PostTaskAndReply(
313        BrowserThread::UI,
314        FROM_HERE,
315        base::Bind(&DriveFileStreamReaderTest::TearDownOnUIThread,
316                   base::Unretained(this)),
317        base::MessageLoop::QuitClosure());
318    message_loop_.Run();
319
320    worker_thread_.reset();
321  }
322
323  void SetUpOnUIThread() {
324    // Initialize FakeDriveService.
325    fake_drive_service_.reset(new google_apis::FakeDriveService);
326    fake_drive_service_->LoadResourceListForWapi(
327        "chromeos/gdata/root_feed.json");
328    fake_drive_service_->LoadAccountMetadataForWapi(
329        "chromeos/gdata/account_metadata.json");
330    fake_drive_service_->LoadAppListForDriveApi("chromeos/drive/applist.json");
331
332    // Create a testee instance.
333    fake_file_system_.reset(
334        new test_util::FakeFileSystem(fake_drive_service_.get()));
335    fake_file_system_->Initialize();
336  }
337
338  void TearDownOnUIThread() {
339    fake_file_system_.reset();
340    fake_drive_service_.reset();
341  }
342
343  FileSystemInterface* GetFileSystem() {
344    return fake_file_system_.get();
345  }
346
347  DriveFileStreamReader::FileSystemGetter GetFileSystemGetter() {
348    return base::Bind(&DriveFileStreamReaderTest::GetFileSystem,
349                      base::Unretained(this));
350  }
351
352  MessageLoopForIO message_loop_;
353  content::TestBrowserThread ui_thread_;
354  content::TestBrowserThread io_thread_;
355
356  scoped_ptr<base::Thread> worker_thread_;
357
358  scoped_ptr<google_apis::FakeDriveService> fake_drive_service_;
359  scoped_ptr<test_util::FakeFileSystem> fake_file_system_;
360};
361
362TEST_F(DriveFileStreamReaderTest, Read) {
363  const base::FilePath kDriveFile =
364      util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
365  // Create the reader, and initialize it.
366  // In this case, the file is not yet locally cached.
367  scoped_ptr<DriveFileStreamReader> reader(new DriveFileStreamReader(
368      GetFileSystemGetter(),
369      worker_thread_->message_loop_proxy()));
370  EXPECT_FALSE(reader->IsInitialized());
371
372  int error = net::ERR_FAILED;
373  scoped_ptr<ResourceEntry> entry;
374  reader->Initialize(
375      kDriveFile,
376      net::HttpByteRange(),
377      google_apis::CreateComposedCallback(
378          base::Bind(&google_apis::test_util::RunAndQuit),
379                     google_apis::test_util::CreateCopyResultCallback(
380                         &error, &entry)));
381  message_loop_.Run();
382  EXPECT_EQ(net::OK, error);
383  ASSERT_TRUE(entry);
384  EXPECT_TRUE(reader->IsInitialized());
385  size_t content_size = entry->file_info().size();
386
387  // Read data from the reader.
388  std::string first_content;
389  ASSERT_EQ(net::OK, test_util::ReadAllData(reader.get(), &first_content));
390  EXPECT_EQ(content_size, first_content.size());
391
392  // Create second instance and initialize it.
393  // In this case, the file should be cached one.
394  reader.reset(
395      new DriveFileStreamReader(GetFileSystemGetter(),
396                                worker_thread_->message_loop_proxy()));
397  EXPECT_FALSE(reader->IsInitialized());
398
399  error = net::ERR_FAILED;
400  entry.reset();
401  reader->Initialize(
402      kDriveFile,
403      net::HttpByteRange(),
404      google_apis::CreateComposedCallback(
405          base::Bind(&google_apis::test_util::RunAndQuit),
406                     google_apis::test_util::CreateCopyResultCallback(
407                         &error, &entry)));
408  message_loop_.Run();
409  EXPECT_EQ(net::OK, error);
410  ASSERT_TRUE(entry);
411  EXPECT_TRUE(reader->IsInitialized());
412
413  // The size should be same.
414  EXPECT_EQ(content_size, static_cast<size_t>(entry->file_info().size()));
415
416  // Read data from the reader, again.
417  std::string second_content;
418  ASSERT_EQ(net::OK, test_util::ReadAllData(reader.get(), &second_content));
419
420  // The same content is expected.
421  EXPECT_EQ(first_content, second_content);
422}
423
424TEST_F(DriveFileStreamReaderTest, ReadRange) {
425  // In this test case, we just confirm that the part of file is read.
426  const int64 kRangeOffset = 3;
427  const int64 kRangeLength = 4;
428
429  const base::FilePath kDriveFile =
430      util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
431  // Create the reader, and initialize it.
432  // In this case, the file is not yet locally cached.
433  scoped_ptr<DriveFileStreamReader> reader(new DriveFileStreamReader(
434      GetFileSystemGetter(),
435      worker_thread_->message_loop_proxy()));
436  EXPECT_FALSE(reader->IsInitialized());
437
438  int error = net::ERR_FAILED;
439  scoped_ptr<ResourceEntry> entry;
440  net::HttpByteRange byte_range;
441  byte_range.set_first_byte_position(kRangeOffset);
442  // Last byte position is inclusive.
443  byte_range.set_last_byte_position(kRangeOffset + kRangeLength - 1);
444  reader->Initialize(
445      kDriveFile,
446      byte_range,
447      google_apis::CreateComposedCallback(
448          base::Bind(&google_apis::test_util::RunAndQuit),
449                     google_apis::test_util::CreateCopyResultCallback(
450                         &error, &entry)));
451  message_loop_.Run();
452  EXPECT_EQ(net::OK, error);
453  ASSERT_TRUE(entry);
454  EXPECT_TRUE(reader->IsInitialized());
455
456  // Read data from the reader.
457  std::string first_content;
458  ASSERT_EQ(net::OK, test_util::ReadAllData(reader.get(), &first_content));
459
460  // The length should be equal to range length.
461  EXPECT_EQ(kRangeLength, static_cast<int64>(first_content.size()));
462
463  // Create second instance and initialize it.
464  // In this case, the file should be cached one.
465  reader.reset(
466      new DriveFileStreamReader(GetFileSystemGetter(),
467                                worker_thread_->message_loop_proxy()));
468  EXPECT_FALSE(reader->IsInitialized());
469
470  error = net::ERR_FAILED;
471  entry.reset();
472  reader->Initialize(
473      kDriveFile,
474      byte_range,
475      google_apis::CreateComposedCallback(
476          base::Bind(&google_apis::test_util::RunAndQuit),
477                     google_apis::test_util::CreateCopyResultCallback(
478                         &error, &entry)));
479  message_loop_.Run();
480  EXPECT_EQ(net::OK, error);
481  ASSERT_TRUE(entry);
482  EXPECT_TRUE(reader->IsInitialized());
483
484  // Read data from the reader, again.
485  std::string second_content;
486  ASSERT_EQ(net::OK, test_util::ReadAllData(reader.get(), &second_content));
487
488  // The same content is expected.
489  EXPECT_EQ(first_content, second_content);
490}
491
492TEST_F(DriveFileStreamReaderTest, OutOfRangeError) {
493  const int64 kRangeOffset = 1000000;  // Out of range.
494  const int64 kRangeLength = 4;
495
496  const base::FilePath kDriveFile =
497      util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
498  // Create the reader, and initialize it.
499  // In this case, the file is not yet locally cached.
500  scoped_ptr<DriveFileStreamReader> reader(new DriveFileStreamReader(
501      GetFileSystemGetter(),
502      worker_thread_->message_loop_proxy()));
503  EXPECT_FALSE(reader->IsInitialized());
504
505  int error = net::ERR_FAILED;
506  scoped_ptr<ResourceEntry> entry;
507  net::HttpByteRange byte_range;
508  byte_range.set_first_byte_position(kRangeOffset);
509  // Last byte position is inclusive.
510  byte_range.set_last_byte_position(kRangeOffset + kRangeLength - 1);
511  reader->Initialize(
512      kDriveFile,
513      byte_range,
514      google_apis::CreateComposedCallback(
515          base::Bind(&google_apis::test_util::RunAndQuit),
516                     google_apis::test_util::CreateCopyResultCallback(
517                         &error, &entry)));
518  message_loop_.Run();
519  EXPECT_EQ(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, error);
520  EXPECT_FALSE(entry);
521}
522
523}  // namespace drive
524