file_system_unittest.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
1// Copyright (c) 2012 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.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/file_util.h"
12#include "base/files/file_path.h"
13#include "base/files/scoped_temp_dir.h"
14#include "base/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop_proxy.h"
16#include "base/run_loop.h"
17#include "chrome/browser/chromeos/drive/change_list_loader.h"
18#include "chrome/browser/chromeos/drive/drive.pb.h"
19#include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
20#include "chrome/browser/chromeos/drive/file_system_util.h"
21#include "chrome/browser/chromeos/drive/job_scheduler.h"
22#include "chrome/browser/chromeos/drive/mock_directory_change_observer.h"
23#include "chrome/browser/chromeos/drive/sync_client.h"
24#include "chrome/browser/chromeos/drive/test_util.h"
25#include "chrome/browser/drive/fake_drive_service.h"
26#include "chrome/browser/google_apis/drive_api_parser.h"
27#include "chrome/browser/google_apis/test_util.h"
28#include "chrome/test/base/testing_profile.h"
29#include "content/public/test/test_browser_thread_bundle.h"
30#include "testing/gmock/include/gmock/gmock.h"
31#include "testing/gtest/include/gtest/gtest.h"
32
33using ::testing::AtLeast;
34using ::testing::Eq;
35using ::testing::StrictMock;
36using ::testing::_;
37
38namespace drive {
39namespace {
40
41const int64 kLotsOfSpace = internal::kMinFreeSpace * 10;
42
43// Counts the number of invocation, and if it increased up to |expected_counter|
44// quits the current message loop by calling |quit|.
45void AsyncInitializationCallback(
46    int* counter, int expected_counter, const base::Closure& quit,
47    FileError error, scoped_ptr<ResourceEntry> entry) {
48  if (error != FILE_ERROR_OK || !entry) {
49    // If we hit an error case, quit the message loop immediately.
50    // Then the expectation in the test case can find it because the actual
51    // value of |counter| is different from the expected one.
52    quit.Run();
53    return;
54  }
55
56  (*counter)++;
57  if (*counter >= expected_counter)
58    quit.Run();
59}
60
61}  // namespace
62
63class FileSystemTest : public testing::Test {
64 protected:
65  virtual void SetUp() OVERRIDE {
66    profile_.reset(new TestingProfile);
67
68    // The fake object will be manually deleted in TearDown().
69    fake_drive_service_.reset(new google_apis::FakeDriveService);
70    fake_drive_service_->LoadResourceListForWapi(
71        "chromeos/gdata/root_feed.json");
72    fake_drive_service_->LoadAccountMetadataForWapi(
73        "chromeos/gdata/account_metadata.json");
74
75    fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
76
77    scheduler_.reset(new JobScheduler(profile_.get(),
78                                      fake_drive_service_.get()));
79
80    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
81        profile_.get()).Append(util::kMetadataDirectory)));
82    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
83        profile_.get()).Append(util::kCacheFileDirectory)));
84    ASSERT_TRUE(file_util::CreateDirectory(util::GetCacheRootPath(
85        profile_.get()).Append(util::kTemporaryFileDirectory)));
86
87    cache_.reset(new internal::FileCache(
88        util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory),
89        util::GetCacheRootPath(profile_.get()).Append(
90            util::kCacheFileDirectory),
91        base::MessageLoopProxy::current(),
92        fake_free_disk_space_getter_.get()));
93
94    mock_directory_observer_.reset(new StrictMock<MockDirectoryChangeObserver>);
95
96    ASSERT_TRUE(cache_->Initialize());
97
98    SetUpResourceMetadataAndFileSystem();
99  }
100
101  void SetUpResourceMetadataAndFileSystem() {
102    resource_metadata_.reset(new internal::ResourceMetadata(
103        util::GetCacheRootPath(profile_.get()).Append(util::kMetadataDirectory),
104        base::MessageLoopProxy::current()));
105
106    file_system_.reset(new FileSystem(
107        profile_.get(),
108        cache_.get(),
109        fake_drive_service_.get(),
110        scheduler_.get(),
111        resource_metadata_.get(),
112        base::MessageLoopProxy::current(),
113        util::GetCacheRootPath(profile_.get()).Append(
114            util::kTemporaryFileDirectory)));
115    file_system_->AddObserver(mock_directory_observer_.get());
116    file_system_->Initialize();
117
118    // Disable delaying so that the sync starts immediately.
119    file_system_->sync_client_for_testing()->set_delay_for_testing(
120        base::TimeDelta::FromSeconds(0));
121
122    ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
123  }
124
125  virtual void TearDown() OVERRIDE {
126    ASSERT_TRUE(file_system_);
127    file_system_.reset();
128    scheduler_.reset();
129    fake_drive_service_.reset();
130    cache_.reset();
131    profile_.reset(NULL);
132  }
133
134  // Loads the full resource list via FakeDriveService.
135  bool LoadFullResourceList() {
136    FileError error = FILE_ERROR_FAILED;
137    file_system_->change_list_loader()->LoadIfNeeded(
138        DirectoryFetchInfo(),
139        google_apis::test_util::CreateCopyResultCallback(&error));
140    google_apis::test_util::RunBlockingPoolTask();
141    return error == FILE_ERROR_OK;
142  }
143
144  // Gets resource entry by path synchronously.
145  scoped_ptr<ResourceEntry> GetResourceEntryByPathSync(
146      const base::FilePath& file_path) {
147    FileError error = FILE_ERROR_FAILED;
148    scoped_ptr<ResourceEntry> entry;
149    file_system_->GetResourceEntryByPath(
150        file_path,
151        google_apis::test_util::CreateCopyResultCallback(&error, &entry));
152    google_apis::test_util::RunBlockingPoolTask();
153
154    return entry.Pass();
155  }
156
157  // Gets directory info by path synchronously.
158  scoped_ptr<ResourceEntryVector> ReadDirectoryByPathSync(
159      const base::FilePath& file_path) {
160    FileError error = FILE_ERROR_FAILED;
161    bool unused_hide_hosted_documents;
162    scoped_ptr<ResourceEntryVector> entries;
163    file_system_->ReadDirectoryByPath(
164        file_path,
165        google_apis::test_util::CreateCopyResultCallback(
166            &error, &unused_hide_hosted_documents, &entries));
167    google_apis::test_util::RunBlockingPoolTask();
168
169    return entries.Pass();
170  }
171
172  // Returns true if an entry exists at |file_path|.
173  bool EntryExists(const base::FilePath& file_path) {
174    return GetResourceEntryByPathSync(file_path);
175  }
176
177  // Gets the resource ID of |file_path|. Returns an empty string if not found.
178  std::string GetResourceIdByPath(const base::FilePath& file_path) {
179    scoped_ptr<ResourceEntry> entry =
180        GetResourceEntryByPathSync(file_path);
181    if (entry)
182      return entry->resource_id();
183    else
184      return "";
185  }
186
187  // Flag for specifying the timestamp of the test filesystem cache.
188  enum SetUpTestFileSystemParam {
189    USE_OLD_TIMESTAMP,
190    USE_SERVER_TIMESTAMP,
191  };
192
193  // Sets up a filesystem with directories: drive/root, drive/root/Dir1,
194  // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2,
195  // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets
196  // the changestamp to 654321, equal to that of "account_metadata.json" test
197  // data, indicating the cache is holding the latest file system info.
198  bool SetUpTestFileSystem(SetUpTestFileSystemParam param) {
199    // Destroy the existing resource metadata to close DB.
200    resource_metadata_.reset();
201
202    const std::string root_resource_id =
203        fake_drive_service_->GetRootResourceId();
204    scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
205        resource_metadata(new internal::ResourceMetadata(
206            util::GetCacheRootPath(profile_.get()).Append(
207                util::kMetadataDirectory),
208            base::MessageLoopProxy::current()));
209
210    if (resource_metadata->Initialize() != FILE_ERROR_OK)
211      return false;
212
213    const int64 changestamp = param == USE_SERVER_TIMESTAMP ? 654321 : 1;
214    if (resource_metadata->SetLargestChangestamp(changestamp) != FILE_ERROR_OK)
215      return false;
216
217    // drive/root
218    if (resource_metadata->AddEntry(util::CreateMyDriveRootEntry(
219            root_resource_id)) != FILE_ERROR_OK)
220      return false;
221
222    // drive/root/File1
223    ResourceEntry file1;
224    file1.set_title("File1");
225    file1.set_resource_id("resource_id:File1");
226    file1.set_parent_resource_id(root_resource_id);
227    file1.mutable_file_specific_info()->set_md5("md5");
228    file1.mutable_file_info()->set_is_directory(false);
229    file1.mutable_file_info()->set_size(1048576);
230    if (resource_metadata->AddEntry(file1) != FILE_ERROR_OK)
231      return false;
232
233    // drive/root/Dir1
234    ResourceEntry dir1;
235    dir1.set_title("Dir1");
236    dir1.set_resource_id("resource_id:Dir1");
237    dir1.set_parent_resource_id(root_resource_id);
238    dir1.mutable_file_info()->set_is_directory(true);
239    if (resource_metadata->AddEntry(dir1) != FILE_ERROR_OK)
240      return false;
241
242    // drive/root/Dir1/File2
243    ResourceEntry file2;
244    file2.set_title("File2");
245    file2.set_resource_id("resource_id:File2");
246    file2.set_parent_resource_id(dir1.resource_id());
247    file2.mutable_file_specific_info()->set_md5("md5");
248    file2.mutable_file_info()->set_is_directory(false);
249    file2.mutable_file_info()->set_size(555);
250    if (resource_metadata->AddEntry(file2) != FILE_ERROR_OK)
251      return false;
252
253    // drive/root/Dir1/SubDir2
254    ResourceEntry dir2;
255    dir2.set_title("SubDir2");
256    dir2.set_resource_id("resource_id:SubDir2");
257    dir2.set_parent_resource_id(dir1.resource_id());
258    dir2.mutable_file_info()->set_is_directory(true);
259    if (resource_metadata->AddEntry(dir2) != FILE_ERROR_OK)
260      return false;
261
262    // drive/root/Dir1/SubDir2/File3
263    ResourceEntry file3;
264    file3.set_title("File3");
265    file3.set_resource_id("resource_id:File3");
266    file3.set_parent_resource_id(dir2.resource_id());
267    file3.mutable_file_specific_info()->set_md5("md5");
268    file3.mutable_file_info()->set_is_directory(false);
269    file3.mutable_file_info()->set_size(12345);
270    if (resource_metadata->AddEntry(file3) != FILE_ERROR_OK)
271      return false;
272
273    // Recreate resource metadata.
274    SetUpResourceMetadataAndFileSystem();
275
276    return true;
277  }
278
279  content::TestBrowserThreadBundle thread_bundle_;
280  scoped_ptr<TestingProfile> profile_;
281
282  scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_;
283  scoped_ptr<FileSystem> file_system_;
284  scoped_ptr<google_apis::FakeDriveService> fake_drive_service_;
285  scoped_ptr<JobScheduler> scheduler_;
286  scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
287      resource_metadata_;
288  scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
289  scoped_ptr<StrictMock<MockDirectoryChangeObserver> > mock_directory_observer_;
290};
291
292TEST_F(FileSystemTest, DuplicatedAsyncInitialization) {
293  // "Fast fetch" will fire an OnirectoryChanged event.
294  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(
295      Eq(base::FilePath(FILE_PATH_LITERAL("drive"))))).Times(1);
296
297  base::RunLoop loop;
298
299  int counter = 0;
300  const GetResourceEntryCallback& callback = base::Bind(
301      &AsyncInitializationCallback, &counter, 2, loop.QuitClosure());
302
303  file_system_->GetResourceEntryByPath(
304      base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
305  file_system_->GetResourceEntryByPath(
306      base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
307  loop.Run();  // Wait to get our result
308  EXPECT_EQ(2, counter);
309
310  // Although GetResourceEntryByPath() was called twice, the resource list
311  // should only be loaded once. In the past, there was a bug that caused
312  // it to be loaded twice.
313  EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
314  // See the comment in GetMyDriveRoot test case why this is 2.
315  EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
316}
317
318TEST_F(FileSystemTest, GetGrandRootEntry) {
319  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive"));
320  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
321  ASSERT_TRUE(entry);
322  EXPECT_EQ(util::kDriveGrandRootSpecialResourceId, entry->resource_id());
323
324  // Getting the grand root entry should not cause the resource load to happen.
325  EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
326  EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
327}
328
329TEST_F(FileSystemTest, GetOtherDirEntry) {
330  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/other"));
331  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
332  ASSERT_TRUE(entry);
333  EXPECT_EQ(util::kDriveOtherDirSpecialResourceId, entry->resource_id());
334
335  // Getting the "other" directory entry should not cause the resource load to
336  // happen.
337  EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
338  EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
339}
340
341TEST_F(FileSystemTest, GetMyDriveRoot) {
342  // "Fast fetch" will fire an OnirectoryChanged event.
343  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(
344      Eq(base::FilePath(FILE_PATH_LITERAL("drive"))))).Times(1);
345
346  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root"));
347  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
348  ASSERT_TRUE(entry);
349  EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id());
350
351  // Absence of "drive/root" in the local metadata triggers the "fast fetch"
352  // of "drive" directory. Fetch of "drive" grand root directory has a special
353  // implementation. Instead of normal GetResourceListInDirectory(), it is
354  // emulated by calling GetAboutResource() so that the resource_id of
355  // "drive/root" is listed.
356  // Together with the normal GetAboutResource() call to retrieve the largest
357  // changestamp, the method is called twice.
358  EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
359
360  // After "fast fetch" is done, full resource list is fetched.
361  EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
362}
363
364TEST_F(FileSystemTest, GetExistingFile) {
365  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/File 1.txt"));
366  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
367  ASSERT_TRUE(entry);
368  EXPECT_EQ("file:2_file_resource_id", entry->resource_id());
369
370  EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
371  EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
372}
373
374TEST_F(FileSystemTest, GetExistingDocument) {
375  const base::FilePath kFilePath(
376      FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc"));
377  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
378  ASSERT_TRUE(entry);
379  EXPECT_EQ("document:5_document_resource_id", entry->resource_id());
380}
381
382TEST_F(FileSystemTest, GetNonExistingFile) {
383  const base::FilePath kFilePath(
384      FILE_PATH_LITERAL("drive/root/nonexisting.file"));
385  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
386  EXPECT_FALSE(entry);
387}
388
389TEST_F(FileSystemTest, GetEncodedFileNames) {
390  const base::FilePath kFilePath1(
391      FILE_PATH_LITERAL("drive/root/Slash / in file 1.txt"));
392  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath1);
393  ASSERT_FALSE(entry);
394
395  const base::FilePath kFilePath2 = base::FilePath::FromUTF8Unsafe(
396      "drive/root/Slash \xE2\x88\x95 in file 1.txt");
397  entry = GetResourceEntryByPathSync(kFilePath2);
398  ASSERT_TRUE(entry);
399  EXPECT_EQ("file:slash_file_resource_id", entry->resource_id());
400
401  const base::FilePath kFilePath3 = base::FilePath::FromUTF8Unsafe(
402      "drive/root/Slash \xE2\x88\x95 in directory/Slash SubDir File.txt");
403  entry = GetResourceEntryByPathSync(kFilePath3);
404  ASSERT_TRUE(entry);
405  EXPECT_EQ("file:slash_subdir_file", entry->resource_id());
406}
407
408TEST_F(FileSystemTest, GetDuplicateNames) {
409  const base::FilePath kFilePath1(
410      FILE_PATH_LITERAL("drive/root/Duplicate Name.txt"));
411  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath1);
412  ASSERT_TRUE(entry);
413  const std::string resource_id1 = entry->resource_id();
414
415  const base::FilePath kFilePath2(
416      FILE_PATH_LITERAL("drive/root/Duplicate Name (2).txt"));
417  entry = GetResourceEntryByPathSync(kFilePath2);
418  ASSERT_TRUE(entry);
419  const std::string resource_id2 = entry->resource_id();
420
421  // The entries are de-duped non-deterministically, so we shouldn't rely on the
422  // names matching specific resource ids.
423  const std::string file3_resource_id = "file:3_file_resource_id";
424  const std::string file4_resource_id = "file:4_file_resource_id";
425  EXPECT_TRUE(file3_resource_id == resource_id1 ||
426              file3_resource_id == resource_id2);
427  EXPECT_TRUE(file4_resource_id == resource_id1 ||
428              file4_resource_id == resource_id2);
429}
430
431TEST_F(FileSystemTest, GetExistingDirectory) {
432  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/Directory 1"));
433  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
434  ASSERT_TRUE(entry);
435  ASSERT_EQ("folder:1_folder_resource_id", entry->resource_id());
436
437  // The changestamp should be propagated to the directory.
438  EXPECT_EQ(fake_drive_service_->largest_changestamp(),
439            entry->directory_specific_info().changestamp());
440}
441
442TEST_F(FileSystemTest, GetInSubSubdir) {
443  const base::FilePath kFilePath(
444      FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/"
445                        "Sub Sub Directory Folder"));
446  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
447  ASSERT_TRUE(entry);
448  ASSERT_EQ("folder:sub_sub_directory_folder_id", entry->resource_id());
449}
450
451TEST_F(FileSystemTest, GetOrphanFile) {
452  const base::FilePath kFilePath(
453      FILE_PATH_LITERAL("drive/other/Orphan File 1.txt"));
454  scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
455  ASSERT_TRUE(entry);
456  EXPECT_EQ("file:1_orphanfile_resource_id", entry->resource_id());
457}
458
459TEST_F(FileSystemTest, ReadDirectoryByPath_Root) {
460  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(
461      Eq(base::FilePath(FILE_PATH_LITERAL("drive"))))).Times(1);
462
463  // ReadDirectoryByPath() should kick off the resource list loading.
464  scoped_ptr<ResourceEntryVector> entries(
465      ReadDirectoryByPathSync(base::FilePath::FromUTF8Unsafe("drive")));
466  // The root directory should be read correctly.
467  ASSERT_TRUE(entries);
468  ASSERT_EQ(2U, entries->size());
469
470  // The found two directories should be /drive/root and /drive/other.
471  bool found_other = false;
472  bool found_my_drive = false;
473  for (size_t i = 0; i < entries->size(); ++i) {
474    const base::FilePath title =
475        base::FilePath::FromUTF8Unsafe((*entries)[i].title());
476    if (title == base::FilePath(util::kDriveOtherDirName)) {
477      found_other = true;
478    } else if (title == base::FilePath(util::kDriveMyDriveRootDirName)) {
479      found_my_drive = true;
480    }
481  }
482
483  EXPECT_TRUE(found_other);
484  EXPECT_TRUE(found_my_drive);
485}
486
487TEST_F(FileSystemTest, ReadDirectoryByPath_NonRootDirectory) {
488  // ReadDirectoryByPath() should kick off the resource list loading.
489  scoped_ptr<ResourceEntryVector> entries(
490      ReadDirectoryByPathSync(
491          base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
492  // The non root directory should also be read correctly.
493  // There was a bug (crbug.com/181487), which broke this behavior.
494  // Make sure this is fixed.
495  ASSERT_TRUE(entries);
496  EXPECT_EQ(3U, entries->size());
497}
498
499TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) {
500  ASSERT_TRUE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
501
502  // Kicks loading of cached file system and query for server update.
503  EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
504
505  // SetUpTestFileSystem and "account_metadata.json" have the same
506  // changestamp (i.e. the local metadata is up-to-date), so no request for
507  // new resource list (i.e., call to GetResourceList) should happen.
508  EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
509  EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
510
511  // Since the file system has verified that it holds the latest snapshot,
512  // it should change its state to "loaded", which admits periodic refresh.
513  // To test it, call CheckForUpdates and verify it does try to check updates.
514  file_system_->CheckForUpdates();
515  google_apis::test_util::RunBlockingPoolTask();
516  EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
517}
518
519TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) {
520  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
521
522  // Make GetResourceList fail for simulating offline situation. This will
523  // leave the file system "loaded from cache, but not synced with server"
524  // state.
525  fake_drive_service_->set_offline(true);
526
527  // Kicks loading of cached file system and query for server update.
528  EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
529  // Loading of about resource should not happen as it's offline.
530  EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
531
532  // Tests that cached data can be loaded even if the server is not reachable.
533  EXPECT_TRUE(EntryExists(base::FilePath(
534      FILE_PATH_LITERAL("drive/root/File1"))));
535  EXPECT_TRUE(EntryExists(base::FilePath(
536      FILE_PATH_LITERAL("drive/root/Dir1"))));
537  EXPECT_TRUE(
538      EntryExists(base::FilePath(FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
539  EXPECT_TRUE(EntryExists(base::FilePath(
540      FILE_PATH_LITERAL("drive/root/Dir1/SubDir2"))));
541  EXPECT_TRUE(EntryExists(
542      base::FilePath(FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"))));
543
544  // Since the file system has at least succeeded to load cached snapshot,
545  // the file system should be able to start periodic refresh.
546  // To test it, call CheckForUpdates and verify it does try to check
547  // updates, which will cause directory changes.
548  fake_drive_service_->set_offline(false);
549
550  file_system_->CheckForUpdates();
551  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(_))
552      .Times(AtLeast(1));
553
554  google_apis::test_util::RunBlockingPoolTask();
555  EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
556  EXPECT_EQ(1, fake_drive_service_->change_list_load_count());
557}
558
559TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) {
560  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(_))
561      .Times(AtLeast(1));
562
563  // Enter the "refreshing" state so the fast fetch will be performed.
564  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
565  file_system_->CheckForUpdates();
566
567  // The list of resources in "drive/root/Dir1" should be fetched.
568  EXPECT_TRUE(ReadDirectoryByPathSync(base::FilePath(
569      FILE_PATH_LITERAL("drive/root/Dir1"))));
570  EXPECT_EQ(1, fake_drive_service_->directory_load_count());
571}
572
573TEST_F(FileSystemTest, GetResourceEntryExistingWhileRefreshing) {
574  // Enter the "refreshing" state.
575  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
576  file_system_->CheckForUpdates();
577
578  // If an entry is already found in local metadata, no directory fetch happens.
579  EXPECT_TRUE(GetResourceEntryByPathSync(base::FilePath(
580      FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
581  EXPECT_EQ(0, fake_drive_service_->directory_load_count());
582}
583
584TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) {
585  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(_))
586      .Times(AtLeast(1));
587
588  // Enter the "refreshing" state so the fast fetch will be performed.
589  ASSERT_TRUE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
590  file_system_->CheckForUpdates();
591
592  // If an entry is not found, parent directory's resource list is fetched.
593  EXPECT_FALSE(GetResourceEntryByPathSync(base::FilePath(
594      FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile"))));
595  EXPECT_EQ(1, fake_drive_service_->directory_load_count());
596}
597
598TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) {
599  // Intentionally *not* calling LoadFullResourceList(), for testing that
600  // CreateDirectory ensures the resource list is loaded before it runs.
601
602  base::FilePath existing_directory(
603      FILE_PATH_LITERAL("drive/root/Directory 1"));
604  FileError error = FILE_ERROR_FAILED;
605  file_system_->CreateDirectory(
606      existing_directory,
607      true,  // is_exclusive
608      false,  // is_recursive
609      google_apis::test_util::CreateCopyResultCallback(&error));
610  google_apis::test_util::RunBlockingPoolTask();
611
612  // It should fail because is_exclusive is set to true.
613  EXPECT_EQ(FILE_ERROR_EXISTS, error);
614}
615
616TEST_F(FileSystemTest, PinAndUnpin) {
617  // Pinned file gets synced and it results in entry state changes.
618  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(
619      Eq(base::FilePath(FILE_PATH_LITERAL("drive/root"))))).Times(AtLeast(1));
620
621  ASSERT_TRUE(LoadFullResourceList());
622
623  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
624
625  // Get the file info.
626  scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
627  ASSERT_TRUE(entry);
628
629  // Pin the file.
630  FileError error = FILE_ERROR_FAILED;
631  file_system_->Pin(file_path,
632                    google_apis::test_util::CreateCopyResultCallback(&error));
633  google_apis::test_util::RunBlockingPoolTask();
634  EXPECT_EQ(FILE_ERROR_OK, error);
635
636  FileCacheEntry cache_entry;
637  EXPECT_TRUE(cache_->GetCacheEntry(
638      entry->resource_id(), std::string(), &cache_entry));
639  EXPECT_TRUE(cache_entry.is_pinned());
640  EXPECT_TRUE(cache_entry.is_present());
641
642  // Unpin the file.
643  error = FILE_ERROR_FAILED;
644  file_system_->Unpin(file_path,
645                      google_apis::test_util::CreateCopyResultCallback(&error));
646  google_apis::test_util::RunBlockingPoolTask();
647  EXPECT_EQ(FILE_ERROR_OK, error);
648
649  EXPECT_TRUE(cache_->GetCacheEntry(
650      entry->resource_id(), std::string(), &cache_entry));
651  EXPECT_FALSE(cache_entry.is_pinned());
652}
653
654TEST_F(FileSystemTest, PinAndUnpin_NotSynced) {
655  ASSERT_TRUE(LoadFullResourceList());
656
657  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
658
659  // Get the file info.
660  scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
661  ASSERT_TRUE(entry);
662
663  // Unpin the file just after pinning. File fetch should be cancelled.
664  FileError error_pin = FILE_ERROR_FAILED;
665  file_system_->Pin(
666      file_path,
667      google_apis::test_util::CreateCopyResultCallback(&error_pin));
668
669  FileError error_unpin = FILE_ERROR_FAILED;
670  file_system_->Unpin(
671      file_path,
672      google_apis::test_util::CreateCopyResultCallback(&error_unpin));
673
674  google_apis::test_util::RunBlockingPoolTask();
675  EXPECT_EQ(FILE_ERROR_OK, error_pin);
676  EXPECT_EQ(FILE_ERROR_OK, error_unpin);
677
678  // No cache file available because the sync was cancelled by Unpin().
679  FileCacheEntry cache_entry;
680  EXPECT_FALSE(cache_->GetCacheEntry(
681      entry->resource_id(), std::string(), &cache_entry));
682}
683
684TEST_F(FileSystemTest, GetAvailableSpace) {
685  FileError error = FILE_ERROR_OK;
686  int64 bytes_total;
687  int64 bytes_used;
688  file_system_->GetAvailableSpace(
689      google_apis::test_util::CreateCopyResultCallback(
690          &error, &bytes_total, &bytes_used));
691  google_apis::test_util::RunBlockingPoolTask();
692  EXPECT_EQ(GG_LONGLONG(6789012345), bytes_used);
693  EXPECT_EQ(GG_LONGLONG(9876543210), bytes_total);
694}
695
696TEST_F(FileSystemTest, RefreshDirectory) {
697  ASSERT_TRUE(LoadFullResourceList());
698
699  // We'll notify the directory change to the observer.
700  EXPECT_CALL(*mock_directory_observer_,
701              OnDirectoryChanged(Eq(util::GetDriveMyDriveRootPath()))).Times(1);
702
703  FileError error = FILE_ERROR_FAILED;
704  file_system_->RefreshDirectory(
705      util::GetDriveMyDriveRootPath(),
706      google_apis::test_util::CreateCopyResultCallback(&error));
707  google_apis::test_util::RunBlockingPoolTask();
708  EXPECT_EQ(FILE_ERROR_OK, error);
709}
710
711TEST_F(FileSystemTest, OpenAndCloseFile) {
712  ASSERT_TRUE(LoadFullResourceList());
713
714  // The transfered file is cached and the change of "offline available"
715  // attribute is notified.
716  EXPECT_CALL(*mock_directory_observer_, OnDirectoryChanged(
717      Eq(base::FilePath(FILE_PATH_LITERAL("drive/root"))))).Times(AtLeast(1));
718
719  const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt"));
720  scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(kFileInRoot));
721  const std::string& file_resource_id = entry->resource_id();
722  const std::string& md5 = entry->file_specific_info().md5();
723
724  // Open kFileInRoot ("drive/root/File 1.txt").
725  FileError error = FILE_ERROR_FAILED;
726  base::FilePath file_path;
727  file_system_->OpenFile(
728      kFileInRoot,
729      google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
730  google_apis::test_util::RunBlockingPoolTask();
731  const base::FilePath opened_file_path = file_path;
732
733  // Verify that the file was properly opened.
734  EXPECT_EQ(FILE_ERROR_OK, error);
735
736  // Try to open the already opened file.
737  file_system_->OpenFile(
738      kFileInRoot,
739      google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
740  google_apis::test_util::RunBlockingPoolTask();
741
742  // It must fail.
743  EXPECT_EQ(FILE_ERROR_IN_USE, error);
744
745  // Verify that the file contents match the expected contents.
746  const std::string kExpectedContent = "This is some test content.";
747  std::string cache_file_data;
748  EXPECT_TRUE(file_util::ReadFileToString(opened_file_path, &cache_file_data));
749  EXPECT_EQ(kExpectedContent, cache_file_data);
750
751  FileCacheEntry cache_entry;
752  EXPECT_TRUE(cache_->GetCacheEntry(file_resource_id, md5, &cache_entry));
753  EXPECT_TRUE(cache_entry.is_present());
754  EXPECT_TRUE(cache_entry.is_dirty());
755
756  base::FilePath cache_file_path;
757  EXPECT_EQ(FILE_ERROR_OK,
758            cache_->GetFile(file_resource_id, md5, &cache_file_path));
759  EXPECT_EQ(cache_file_path, opened_file_path);
760
761  // Write a new content.
762  const std::string kNewContent = kExpectedContent + kExpectedContent;
763  EXPECT_TRUE(google_apis::test_util::WriteStringToFile(cache_file_path,
764                                                        kNewContent));
765
766  // Close kFileInRoot ("drive/root/File 1.txt").
767  file_system_->CloseFile(
768      kFileInRoot,
769      google_apis::test_util::CreateCopyResultCallback(&error));
770  google_apis::test_util::RunBlockingPoolTask();
771
772  // Verify that the file was properly closed.
773  EXPECT_EQ(FILE_ERROR_OK, error);
774
775  // Verify that the file was synced as expected.
776  google_apis::GDataErrorCode gdata_error = google_apis::GDATA_FILE_ERROR;
777  scoped_ptr<google_apis::ResourceEntry> gdata_entry;
778  fake_drive_service_->GetResourceEntry(
779      file_resource_id,
780      google_apis::test_util::CreateCopyResultCallback(
781          &gdata_error, &gdata_entry));
782  google_apis::test_util::RunBlockingPoolTask();
783  EXPECT_EQ(gdata_error, google_apis::HTTP_SUCCESS);
784  ASSERT_TRUE(gdata_entry);
785  EXPECT_EQ(static_cast<int>(kNewContent.size()), gdata_entry->file_size());
786
787  // Try to close the same file twice.
788  file_system_->CloseFile(
789      kFileInRoot,
790      google_apis::test_util::CreateCopyResultCallback(&error));
791  google_apis::test_util::RunBlockingPoolTask();
792
793  // It must fail.
794  EXPECT_EQ(FILE_ERROR_NOT_FOUND, error);
795}
796
797TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) {
798  ASSERT_TRUE(LoadFullResourceList());
799
800  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
801  scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_in_root));
802  ASSERT_TRUE(entry);
803
804  // Write to cache.
805  ASSERT_EQ(FILE_ERROR_OK, cache_->Store(
806      entry->resource_id(),
807      entry->file_specific_info().md5(),
808      google_apis::test_util::GetTestFilePath("chromeos/gdata/root_feed.json"),
809      internal::FileCache::FILE_OPERATION_COPY));
810
811  // Test for mounting.
812  FileError error = FILE_ERROR_FAILED;
813  base::FilePath file_path;
814  file_system_->MarkCacheFileAsMounted(
815      file_in_root,
816      google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
817  google_apis::test_util::RunBlockingPoolTask();
818  EXPECT_EQ(FILE_ERROR_OK, error);
819
820  // Cannot remove a cache entry while it's being mounted.
821  EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->resource_id()));
822
823  // Test for unmounting.
824  error = FILE_ERROR_FAILED;
825  file_system_->MarkCacheFileAsUnmounted(
826      file_path,
827      google_apis::test_util::CreateCopyResultCallback(&error));
828  google_apis::test_util::RunBlockingPoolTask();
829  EXPECT_EQ(FILE_ERROR_OK, error);
830
831  // Now able to remove the cache entry.
832  EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->resource_id()));
833}
834
835}   // namespace drive
836