download_operation_unittest.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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/file_system/download_operation.h"
6
7#include "base/file_util.h"
8#include "base/task_runner_util.h"
9#include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
10#include "chrome/browser/chromeos/drive/file_cache.h"
11#include "chrome/browser/chromeos/drive/file_system/operation_test_base.h"
12#include "chrome/browser/chromeos/drive/file_system_util.h"
13#include "chrome/browser/drive/fake_drive_service.h"
14#include "google_apis/drive/test_util.h"
15#include "testing/gtest/include/gtest/gtest.h"
16#include "third_party/cros_system_api/constants/cryptohome.h"
17
18namespace drive {
19namespace file_system {
20
21class DownloadOperationTest : public OperationTestBase {
22 protected:
23  virtual void SetUp() OVERRIDE {
24    OperationTestBase::SetUp();
25
26    operation_.reset(new DownloadOperation(
27        blocking_task_runner(), observer(), scheduler(), metadata(), cache(),
28        temp_dir()));
29  }
30
31  scoped_ptr<DownloadOperation> operation_;
32};
33
34TEST_F(DownloadOperationTest,
35       EnsureFileDownloadedByPath_FromServer_EnoughSpace) {
36  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
37  ResourceEntry src_entry;
38  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
39  const int64 file_size = src_entry.file_info().size();
40
41  // Pretend we have enough space.
42  fake_free_disk_space_getter()->set_default_value(
43      file_size + cryptohome::kMinFreeSpaceInBytes);
44
45  FileError error = FILE_ERROR_FAILED;
46  base::FilePath file_path;
47  scoped_ptr<ResourceEntry> entry;
48  operation_->EnsureFileDownloadedByPath(
49      file_in_root,
50      ClientContext(USER_INITIATED),
51      GetFileContentInitializedCallback(),
52      google_apis::GetContentCallback(),
53      google_apis::test_util::CreateCopyResultCallback(
54          &error, &file_path, &entry));
55  test_util::RunBlockingPoolTask();
56
57  EXPECT_EQ(FILE_ERROR_OK, error);
58  ASSERT_TRUE(entry);
59  EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
60
61  // The transfered file is cached and the change of "offline available"
62  // attribute is notified.
63  EXPECT_EQ(1U, observer()->get_changed_paths().size());
64  EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
65
66  // Verify that readable permission is set.
67  int permission = 0;
68  EXPECT_TRUE(base::GetPosixFilePermissions(file_path, &permission));
69  EXPECT_EQ(base::FILE_PERMISSION_READ_BY_USER |
70            base::FILE_PERMISSION_WRITE_BY_USER |
71            base::FILE_PERMISSION_READ_BY_GROUP |
72            base::FILE_PERMISSION_READ_BY_OTHERS, permission);
73}
74
75TEST_F(DownloadOperationTest,
76       EnsureFileDownloadedByPath_FromServer_NoSpaceAtAll) {
77  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
78
79  // Pretend we have no space at all.
80  fake_free_disk_space_getter()->set_default_value(0);
81
82  FileError error = FILE_ERROR_OK;
83  base::FilePath file_path;
84  scoped_ptr<ResourceEntry> entry;
85  operation_->EnsureFileDownloadedByPath(
86      file_in_root,
87      ClientContext(USER_INITIATED),
88      GetFileContentInitializedCallback(),
89      google_apis::GetContentCallback(),
90      google_apis::test_util::CreateCopyResultCallback(
91          &error, &file_path, &entry));
92  test_util::RunBlockingPoolTask();
93
94  EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
95}
96
97TEST_F(DownloadOperationTest,
98       EnsureFileDownloadedByPath_FromServer_NoEnoughSpaceButCanFreeUp) {
99  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
100  ResourceEntry src_entry;
101  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
102  const int64 file_size = src_entry.file_info().size();
103
104  // Pretend we have no space first (checked before downloading a file),
105  // but then start reporting we have space. This is to emulate that
106  // the disk space was freed up by removing temporary files.
107  fake_free_disk_space_getter()->PushFakeValue(
108      file_size + cryptohome::kMinFreeSpaceInBytes);
109  fake_free_disk_space_getter()->PushFakeValue(0);
110  fake_free_disk_space_getter()->set_default_value(
111      file_size + cryptohome::kMinFreeSpaceInBytes);
112
113  // Store something of the file size in the temporary cache directory.
114  const std::string content(file_size, 'x');
115  base::ScopedTempDir temp_dir;
116  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
117  const base::FilePath tmp_file =
118      temp_dir.path().AppendASCII("something.txt");
119  ASSERT_TRUE(google_apis::test_util::WriteStringToFile(tmp_file, content));
120
121  FileError error = FILE_ERROR_FAILED;
122  base::PostTaskAndReplyWithResult(
123      blocking_task_runner(),
124      FROM_HERE,
125      base::Bind(&internal::FileCache::Store,
126                 base::Unretained(cache()),
127                 "<id>", "<md5>", tmp_file,
128                 internal::FileCache::FILE_OPERATION_COPY),
129      google_apis::test_util::CreateCopyResultCallback(&error));
130  test_util::RunBlockingPoolTask();
131  EXPECT_EQ(FILE_ERROR_OK, error);
132
133  base::FilePath file_path;
134  scoped_ptr<ResourceEntry> entry;
135  operation_->EnsureFileDownloadedByPath(
136      file_in_root,
137      ClientContext(USER_INITIATED),
138      GetFileContentInitializedCallback(),
139      google_apis::GetContentCallback(),
140      google_apis::test_util::CreateCopyResultCallback(
141          &error, &file_path, &entry));
142  test_util::RunBlockingPoolTask();
143
144  EXPECT_EQ(FILE_ERROR_OK, error);
145  ASSERT_TRUE(entry);
146  EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
147
148  // The transfered file is cached and the change of "offline available"
149  // attribute is notified.
150  EXPECT_EQ(1U, observer()->get_changed_paths().size());
151  EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
152
153  // The cache entry should be removed in order to free up space.
154  FileCacheEntry cache_entry;
155  bool result = true;
156  base::PostTaskAndReplyWithResult(
157      blocking_task_runner(),
158      FROM_HERE,
159      base::Bind(&internal::FileCache::GetCacheEntry,
160                 base::Unretained(cache()),
161                 "<id>",
162                 &cache_entry),
163      google_apis::test_util::CreateCopyResultCallback(&result));
164  test_util::RunBlockingPoolTask();
165  ASSERT_FALSE(result);
166}
167
168TEST_F(DownloadOperationTest,
169       EnsureFileDownloadedByPath_FromServer_EnoughSpaceButBecomeFull) {
170  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
171  ResourceEntry src_entry;
172  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
173  const int64 file_size = src_entry.file_info().size();
174
175  // Pretend we have enough space first (checked before downloading a file),
176  // but then start reporting we have not enough space. This is to emulate that
177  // the disk space becomes full after the file is downloaded for some reason
178  // (ex. the actual file was larger than the expected size).
179  fake_free_disk_space_getter()->PushFakeValue(
180      file_size + cryptohome::kMinFreeSpaceInBytes);
181  fake_free_disk_space_getter()->set_default_value(
182      cryptohome::kMinFreeSpaceInBytes - 1);
183
184  FileError error = FILE_ERROR_OK;
185  base::FilePath file_path;
186  scoped_ptr<ResourceEntry> entry;
187  operation_->EnsureFileDownloadedByPath(
188      file_in_root,
189      ClientContext(USER_INITIATED),
190      GetFileContentInitializedCallback(),
191      google_apis::GetContentCallback(),
192      google_apis::test_util::CreateCopyResultCallback(
193          &error, &file_path, &entry));
194  test_util::RunBlockingPoolTask();
195
196  EXPECT_EQ(FILE_ERROR_NO_LOCAL_SPACE, error);
197}
198
199TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_FromCache) {
200  base::FilePath temp_file;
201  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file));
202
203  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
204  ResourceEntry src_entry;
205  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
206
207  // Store something as cached version of this file.
208  FileError error = FILE_ERROR_OK;
209  base::PostTaskAndReplyWithResult(
210      blocking_task_runner(),
211      FROM_HERE,
212      base::Bind(&internal::FileCache::Store,
213                 base::Unretained(cache()),
214                 GetLocalId(file_in_root),
215                 src_entry.file_specific_info().md5(),
216                 temp_file,
217                 internal::FileCache::FILE_OPERATION_COPY),
218      google_apis::test_util::CreateCopyResultCallback(&error));
219  test_util::RunBlockingPoolTask();
220  EXPECT_EQ(FILE_ERROR_OK, error);
221
222  base::FilePath file_path;
223  scoped_ptr<ResourceEntry> entry;
224  operation_->EnsureFileDownloadedByPath(
225      file_in_root,
226      ClientContext(USER_INITIATED),
227      GetFileContentInitializedCallback(),
228      google_apis::GetContentCallback(),
229      google_apis::test_util::CreateCopyResultCallback(
230          &error, &file_path, &entry));
231  test_util::RunBlockingPoolTask();
232
233  EXPECT_EQ(FILE_ERROR_OK, error);
234  ASSERT_TRUE(entry);
235  EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
236}
237
238TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_HostedDocument) {
239  base::FilePath file_in_root(FILE_PATH_LITERAL(
240      "drive/root/Document 1 excludeDir-test.gdoc"));
241
242  FileError error = FILE_ERROR_FAILED;
243  base::FilePath file_path;
244  scoped_ptr<ResourceEntry> entry;
245  operation_->EnsureFileDownloadedByPath(
246      file_in_root,
247      ClientContext(USER_INITIATED),
248      GetFileContentInitializedCallback(),
249      google_apis::GetContentCallback(),
250      google_apis::test_util::CreateCopyResultCallback(
251          &error, &file_path, &entry));
252  test_util::RunBlockingPoolTask();
253
254  EXPECT_EQ(FILE_ERROR_OK, error);
255  ASSERT_TRUE(entry);
256  EXPECT_TRUE(entry->file_specific_info().is_hosted_document());
257  EXPECT_FALSE(file_path.empty());
258
259  EXPECT_EQ(GURL(entry->file_specific_info().alternate_url()),
260            util::ReadUrlFromGDocFile(file_path));
261  EXPECT_EQ(entry->resource_id(), util::ReadResourceIdFromGDocFile(file_path));
262  EXPECT_EQ(FILE_PATH_LITERAL(".gdoc"), file_path.Extension());
263}
264
265TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId) {
266  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
267  ResourceEntry src_entry;
268  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
269
270  FileError error = FILE_ERROR_OK;
271  base::FilePath file_path;
272  scoped_ptr<ResourceEntry> entry;
273  operation_->EnsureFileDownloadedByLocalId(
274      GetLocalId(file_in_root),
275      ClientContext(USER_INITIATED),
276      GetFileContentInitializedCallback(),
277      google_apis::GetContentCallback(),
278      google_apis::test_util::CreateCopyResultCallback(
279          &error, &file_path, &entry));
280  test_util::RunBlockingPoolTask();
281
282  EXPECT_EQ(FILE_ERROR_OK, error);
283  ASSERT_TRUE(entry);
284  EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
285
286  // The transfered file is cached and the change of "offline available"
287  // attribute is notified.
288  EXPECT_EQ(1U, observer()->get_changed_paths().size());
289  EXPECT_EQ(1U, observer()->get_changed_paths().count(file_in_root.DirName()));
290}
291
292TEST_F(DownloadOperationTest,
293       EnsureFileDownloadedByPath_WithGetContentCallback) {
294  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
295
296  {
297    FileError initialized_error = FILE_ERROR_FAILED;
298    scoped_ptr<ResourceEntry> entry, entry_dontcare;
299    base::FilePath local_path, local_path_dontcare;
300    base::Closure cancel_download;
301    google_apis::test_util::TestGetContentCallback get_content_callback;
302
303    FileError completion_error = FILE_ERROR_FAILED;
304
305    operation_->EnsureFileDownloadedByPath(
306        file_in_root,
307        ClientContext(USER_INITIATED),
308        google_apis::test_util::CreateCopyResultCallback(
309            &initialized_error, &entry, &local_path, &cancel_download),
310        get_content_callback.callback(),
311        google_apis::test_util::CreateCopyResultCallback(
312            &completion_error, &local_path_dontcare, &entry_dontcare));
313    test_util::RunBlockingPoolTask();
314
315    // For the first time, file is downloaded from the remote server.
316    // In this case, |local_path| is empty while |cancel_download| is not.
317    EXPECT_EQ(FILE_ERROR_OK, initialized_error);
318    ASSERT_TRUE(entry);
319    ASSERT_TRUE(local_path.empty());
320    EXPECT_TRUE(!cancel_download.is_null());
321    // Content is available through the second callback argument.
322    EXPECT_EQ(static_cast<size_t>(entry->file_info().size()),
323              get_content_callback.GetConcatenatedData().size());
324    EXPECT_EQ(FILE_ERROR_OK, completion_error);
325
326    // The transfered file is cached and the change of "offline available"
327    // attribute is notified.
328    EXPECT_EQ(1U, observer()->get_changed_paths().size());
329    EXPECT_EQ(1U,
330              observer()->get_changed_paths().count(file_in_root.DirName()));
331  }
332
333  {
334    FileError initialized_error = FILE_ERROR_FAILED;
335    scoped_ptr<ResourceEntry> entry, entry_dontcare;
336    base::FilePath local_path, local_path_dontcare;
337    base::Closure cancel_download;
338    google_apis::test_util::TestGetContentCallback get_content_callback;
339
340    FileError completion_error = FILE_ERROR_FAILED;
341
342    operation_->EnsureFileDownloadedByPath(
343        file_in_root,
344        ClientContext(USER_INITIATED),
345        google_apis::test_util::CreateCopyResultCallback(
346            &initialized_error, &entry, &local_path, &cancel_download),
347        get_content_callback.callback(),
348        google_apis::test_util::CreateCopyResultCallback(
349            &completion_error, &local_path_dontcare, &entry_dontcare));
350    test_util::RunBlockingPoolTask();
351
352    // Try second download. In this case, the file should be cached, so
353    // |local_path| should not be empty while |cancel_download| is empty.
354    EXPECT_EQ(FILE_ERROR_OK, initialized_error);
355    ASSERT_TRUE(entry);
356    ASSERT_TRUE(!local_path.empty());
357    EXPECT_TRUE(cancel_download.is_null());
358    // The content is available from the cache file.
359    EXPECT_TRUE(get_content_callback.data().empty());
360    int64 local_file_size = 0;
361    base::GetFileSize(local_path, &local_file_size);
362    EXPECT_EQ(entry->file_info().size(), local_file_size);
363    EXPECT_EQ(FILE_ERROR_OK, completion_error);
364  }
365}
366
367TEST_F(DownloadOperationTest, EnsureFileDownloadedByLocalId_FromCache) {
368  base::FilePath temp_file;
369  ASSERT_TRUE(base::CreateTemporaryFileInDir(temp_dir(), &temp_file));
370
371  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
372  ResourceEntry src_entry;
373  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
374
375  // Store something as cached version of this file.
376  FileError error = FILE_ERROR_FAILED;
377  base::PostTaskAndReplyWithResult(
378      blocking_task_runner(),
379      FROM_HERE,
380      base::Bind(&internal::FileCache::Store,
381                 base::Unretained(cache()),
382                 GetLocalId(file_in_root),
383                 src_entry.file_specific_info().md5(),
384                 temp_file,
385                 internal::FileCache::FILE_OPERATION_COPY),
386      google_apis::test_util::CreateCopyResultCallback(&error));
387  test_util::RunBlockingPoolTask();
388  EXPECT_EQ(FILE_ERROR_OK, error);
389
390  // The file is obtained from the cache.
391  // Hence the downloading should work even if the drive service is offline.
392  fake_service()->set_offline(true);
393
394  base::FilePath file_path;
395  scoped_ptr<ResourceEntry> entry;
396  operation_->EnsureFileDownloadedByLocalId(
397      GetLocalId(file_in_root),
398      ClientContext(USER_INITIATED),
399      GetFileContentInitializedCallback(),
400      google_apis::GetContentCallback(),
401      google_apis::test_util::CreateCopyResultCallback(
402          &error, &file_path, &entry));
403  test_util::RunBlockingPoolTask();
404
405  EXPECT_EQ(FILE_ERROR_OK, error);
406  ASSERT_TRUE(entry);
407  EXPECT_FALSE(entry->file_specific_info().is_hosted_document());
408}
409
410TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_DirtyCache) {
411  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
412  ResourceEntry src_entry;
413  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_in_root, &src_entry));
414
415  // Prepare a dirty file to store to cache that has a different size than
416  // stored in resource metadata.
417  base::FilePath dirty_file = temp_dir().AppendASCII("dirty.txt");
418  size_t dirty_size = src_entry.file_info().size() + 10;
419  google_apis::test_util::WriteStringToFile(dirty_file,
420                                            std::string(dirty_size, 'x'));
421
422  // Store the file as a cache, marking it to be dirty.
423  FileError error = FILE_ERROR_FAILED;
424  base::PostTaskAndReplyWithResult(
425      blocking_task_runner(),
426      FROM_HERE,
427      base::Bind(&internal::FileCache::Store,
428                 base::Unretained(cache()),
429                 GetLocalId(file_in_root),
430                 std::string(),
431                 dirty_file,
432                 internal::FileCache::FILE_OPERATION_COPY),
433      google_apis::test_util::CreateCopyResultCallback(&error));
434  test_util::RunBlockingPoolTask();
435  EXPECT_EQ(FILE_ERROR_OK, error);
436
437  // Record values passed to GetFileContentInitializedCallback().
438  FileError init_error;
439  base::FilePath init_path;
440  scoped_ptr<ResourceEntry> init_entry;
441  base::Closure cancel_callback;
442
443  base::FilePath file_path;
444  scoped_ptr<ResourceEntry> entry;
445  operation_->EnsureFileDownloadedByPath(
446      file_in_root,
447      ClientContext(USER_INITIATED),
448      google_apis::test_util::CreateCopyResultCallback(
449          &init_error, &init_entry, &init_path, &cancel_callback),
450      google_apis::GetContentCallback(),
451      google_apis::test_util::CreateCopyResultCallback(
452          &error, &file_path, &entry));
453  test_util::RunBlockingPoolTask();
454
455  EXPECT_EQ(FILE_ERROR_OK, error);
456  // Check that the result of local modification is propagated.
457  EXPECT_EQ(static_cast<int64>(dirty_size), init_entry->file_info().size());
458  EXPECT_EQ(static_cast<int64>(dirty_size), entry->file_info().size());
459}
460
461TEST_F(DownloadOperationTest, EnsureFileDownloadedByPath_LocallyCreatedFile) {
462  // Add a new file with an empty resource ID.
463  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/New File.txt"));
464  ResourceEntry parent;
465  ASSERT_EQ(FILE_ERROR_OK, GetLocalResourceEntry(file_path.DirName(), &parent));
466
467  ResourceEntry new_file;
468  new_file.set_title("New File.txt");
469  new_file.set_parent_local_id(parent.local_id());
470
471  FileError error = FILE_ERROR_FAILED;
472  std::string local_id;
473  base::PostTaskAndReplyWithResult(
474      blocking_task_runner(),
475      FROM_HERE,
476      base::Bind(&internal::ResourceMetadata::AddEntry,
477                 base::Unretained(metadata()),
478                 new_file,
479                 &local_id),
480      google_apis::test_util::CreateCopyResultCallback(&error));
481  test_util::RunBlockingPoolTask();
482  EXPECT_EQ(FILE_ERROR_OK, error);
483
484  // Empty cache file should be returned.
485  base::FilePath cache_file_path;
486  scoped_ptr<ResourceEntry> entry;
487  operation_->EnsureFileDownloadedByPath(
488      file_path,
489      ClientContext(USER_INITIATED),
490      GetFileContentInitializedCallback(),
491      google_apis::GetContentCallback(),
492      google_apis::test_util::CreateCopyResultCallback(
493          &error, &cache_file_path, &entry));
494  test_util::RunBlockingPoolTask();
495  EXPECT_EQ(FILE_ERROR_OK, error);
496
497  int64 cache_file_size = 0;
498  EXPECT_TRUE(base::GetFileSize(cache_file_path, &cache_file_size));
499  EXPECT_EQ(static_cast<int64>(0), cache_file_size);
500  ASSERT_TRUE(entry);
501  EXPECT_EQ(cache_file_size, entry->file_info().size());
502}
503
504}  // namespace file_system
505}  // namespace drive
506