- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system_unittest.cc
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/prefs/testing_pref_service.h"
17 #include "base/run_loop.h"
18 #include "chrome/browser/chromeos/drive/change_list_loader.h"
19 #include "chrome/browser/chromeos/drive/change_list_processor.h"
20 #include "chrome/browser/chromeos/drive/drive.pb.h"
21 #include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
22 #include "chrome/browser/chromeos/drive/file_system_observer.h"
23 #include "chrome/browser/chromeos/drive/file_system_util.h"
24 #include "chrome/browser/chromeos/drive/job_scheduler.h"
25 #include "chrome/browser/chromeos/drive/sync_client.h"
26 #include "chrome/browser/chromeos/drive/test_util.h"
27 #include "chrome/browser/drive/fake_drive_service.h"
28 #include "chrome/browser/google_apis/drive_api_parser.h"
29 #include "chrome/browser/google_apis/test_util.h"
30 #include "content/public/test/test_browser_thread_bundle.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32
33 namespace drive {
34 namespace {
35
36 // Counts the number of invocation, and if it increased up to |expected_counter|
37 // quits the current message loop by calling |quit|.
38 void AsyncInitializationCallback(
39     int* counter, int expected_counter, const base::Closure& quit,
40     FileError error, scoped_ptr<ResourceEntry> entry) {
41   if (error != FILE_ERROR_OK || !entry) {
42     // If we hit an error case, quit the message loop immediately.
43     // Then the expectation in the test case can find it because the actual
44     // value of |counter| is different from the expected one.
45     quit.Run();
46     return;
47   }
48
49   (*counter)++;
50   if (*counter >= expected_counter)
51     quit.Run();
52 }
53
54 // This class is used to record directory changes and examine them later.
55 class MockDirectoryChangeObserver : public FileSystemObserver {
56  public:
57   MockDirectoryChangeObserver() {}
58   virtual ~MockDirectoryChangeObserver() {}
59
60   // FileSystemObserver overrides.
61   virtual void OnDirectoryChanged(
62       const base::FilePath& directory_path) OVERRIDE {
63     changed_directories_.push_back(directory_path);
64   }
65
66   const std::vector<base::FilePath>& changed_directories() const {
67     return changed_directories_;
68   }
69
70  private:
71   std::vector<base::FilePath> changed_directories_;
72   DISALLOW_COPY_AND_ASSIGN(MockDirectoryChangeObserver);
73 };
74
75 }  // namespace
76
77 class FileSystemTest : public testing::Test {
78  protected:
79   virtual void SetUp() OVERRIDE {
80     ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
81     pref_service_.reset(new TestingPrefServiceSimple);
82     test_util::RegisterDrivePrefs(pref_service_->registry());
83
84     fake_drive_service_.reset(new FakeDriveService);
85     fake_drive_service_->LoadResourceListForWapi(
86         "gdata/root_feed.json");
87     fake_drive_service_->LoadAccountMetadataForWapi(
88         "gdata/account_metadata.json");
89
90     fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
91
92     scheduler_.reset(new JobScheduler(pref_service_.get(),
93                                       fake_drive_service_.get(),
94                                       base::MessageLoopProxy::current().get()));
95
96     mock_directory_observer_.reset(new MockDirectoryChangeObserver);
97
98     SetUpResourceMetadataAndFileSystem();
99   }
100
101   void SetUpResourceMetadataAndFileSystem() {
102     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
103     ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
104     metadata_storage_.reset(new internal::ResourceMetadataStorage(
105         metadata_dir, base::MessageLoopProxy::current().get()));
106     ASSERT_TRUE(metadata_storage_->Initialize());
107
108     const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files");
109     ASSERT_TRUE(file_util::CreateDirectory(cache_dir));
110     cache_.reset(new internal::FileCache(
111         metadata_storage_.get(),
112         cache_dir,
113         base::MessageLoopProxy::current().get(),
114         fake_free_disk_space_getter_.get()));
115     ASSERT_TRUE(cache_->Initialize());
116
117     resource_metadata_.reset(new internal::ResourceMetadata(
118         metadata_storage_.get(), base::MessageLoopProxy::current()));
119     ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
120
121     const base::FilePath temp_file_dir = temp_dir_.path().AppendASCII("tmp");
122     ASSERT_TRUE(file_util::CreateDirectory(temp_file_dir));
123     file_system_.reset(new FileSystem(
124         pref_service_.get(),
125         cache_.get(),
126         fake_drive_service_.get(),
127         scheduler_.get(),
128         resource_metadata_.get(),
129         base::MessageLoopProxy::current().get(),
130         temp_file_dir));
131     file_system_->AddObserver(mock_directory_observer_.get());
132
133     // Disable delaying so that the sync starts immediately.
134     file_system_->sync_client_for_testing()->set_delay_for_testing(
135         base::TimeDelta::FromSeconds(0));
136   }
137
138   // Loads the full resource list via FakeDriveService.
139   bool LoadFullResourceList() {
140     FileError error = FILE_ERROR_FAILED;
141     file_system_->change_list_loader_for_testing()->LoadIfNeeded(
142         internal::DirectoryFetchInfo(),
143         google_apis::test_util::CreateCopyResultCallback(&error));
144     test_util::RunBlockingPoolTask();
145     return error == FILE_ERROR_OK;
146   }
147
148   // Gets resource entry by path synchronously.
149   scoped_ptr<ResourceEntry> GetResourceEntryByPathSync(
150       const base::FilePath& file_path) {
151     FileError error = FILE_ERROR_FAILED;
152     scoped_ptr<ResourceEntry> entry;
153     file_system_->GetResourceEntryByPath(
154         file_path,
155         google_apis::test_util::CreateCopyResultCallback(&error, &entry));
156     test_util::RunBlockingPoolTask();
157
158     return entry.Pass();
159   }
160
161   // Gets directory info by path synchronously.
162   scoped_ptr<ResourceEntryVector> ReadDirectoryByPathSync(
163       const base::FilePath& file_path) {
164     FileError error = FILE_ERROR_FAILED;
165     scoped_ptr<ResourceEntryVector> entries;
166     file_system_->ReadDirectoryByPath(
167         file_path,
168         google_apis::test_util::CreateCopyResultCallback(&error, &entries));
169     test_util::RunBlockingPoolTask();
170
171     return entries.Pass();
172   }
173
174   // Returns true if an entry exists at |file_path|.
175   bool EntryExists(const base::FilePath& file_path) {
176     return GetResourceEntryByPathSync(file_path);
177   }
178
179   // Flag for specifying the timestamp of the test filesystem cache.
180   enum SetUpTestFileSystemParam {
181     USE_OLD_TIMESTAMP,
182     USE_SERVER_TIMESTAMP,
183   };
184
185   // Sets up a filesystem with directories: drive/root, drive/root/Dir1,
186   // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2,
187   // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets
188   // the changestamp to 654321, equal to that of "account_metadata.json" test
189   // data, indicating the cache is holding the latest file system info.
190   void SetUpTestFileSystem(SetUpTestFileSystemParam param) {
191     // Destroy the existing resource metadata to close DB.
192     resource_metadata_.reset();
193
194     const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
195     ASSERT_TRUE(file_util::CreateDirectory(metadata_dir));
196     scoped_ptr<internal::ResourceMetadataStorage,
197                test_util::DestroyHelperForTests> metadata_storage(
198         new internal::ResourceMetadataStorage(
199             metadata_dir, base::MessageLoopProxy::current().get()));
200
201     scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
202         resource_metadata(new internal::ResourceMetadata(
203             metadata_storage_.get(), base::MessageLoopProxy::current()));
204
205     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->Initialize());
206
207     const int64 changestamp = param == USE_SERVER_TIMESTAMP ? 654321 : 1;
208     ASSERT_EQ(FILE_ERROR_OK,
209               resource_metadata->SetLargestChangestamp(changestamp));
210
211     // drive/root
212     const std::string root_resource_id =
213         fake_drive_service_->GetRootResourceId();
214     std::string local_id;
215     ASSERT_EQ(FILE_ERROR_OK,
216               resource_metadata->AddEntry(util::CreateMyDriveRootEntry(
217                   root_resource_id), &local_id));
218     const std::string root_local_id = local_id;
219
220     // drive/root/File1
221     ResourceEntry file1;
222     file1.set_title("File1");
223     file1.set_resource_id("resource_id:File1");
224     file1.set_parent_local_id(root_local_id);
225     file1.mutable_file_specific_info()->set_md5("md5");
226     file1.mutable_file_info()->set_is_directory(false);
227     file1.mutable_file_info()->set_size(1048576);
228     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file1, &local_id));
229
230     // drive/root/Dir1
231     ResourceEntry dir1;
232     dir1.set_title("Dir1");
233     dir1.set_resource_id("resource_id:Dir1");
234     dir1.set_parent_local_id(root_local_id);
235     dir1.mutable_file_info()->set_is_directory(true);
236     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir1, &local_id));
237     const std::string dir1_local_id = local_id;
238
239     // drive/root/Dir1/File2
240     ResourceEntry file2;
241     file2.set_title("File2");
242     file2.set_resource_id("resource_id:File2");
243     file2.set_parent_local_id(dir1_local_id);
244     file2.mutable_file_specific_info()->set_md5("md5");
245     file2.mutable_file_info()->set_is_directory(false);
246     file2.mutable_file_info()->set_size(555);
247     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file2, &local_id));
248
249     // drive/root/Dir1/SubDir2
250     ResourceEntry dir2;
251     dir2.set_title("SubDir2");
252     dir2.set_resource_id("resource_id:SubDir2");
253     dir2.set_parent_local_id(dir1_local_id);
254     dir2.mutable_file_info()->set_is_directory(true);
255     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir2, &local_id));
256     const std::string dir2_local_id = local_id;
257
258     // drive/root/Dir1/SubDir2/File3
259     ResourceEntry file3;
260     file3.set_title("File3");
261     file3.set_resource_id("resource_id:File3");
262     file3.set_parent_local_id(dir2_local_id);
263     file3.mutable_file_specific_info()->set_md5("md5");
264     file3.mutable_file_info()->set_is_directory(false);
265     file3.mutable_file_info()->set_size(12345);
266     ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file3, &local_id));
267
268     // Recreate resource metadata.
269     SetUpResourceMetadataAndFileSystem();
270   }
271
272   content::TestBrowserThreadBundle thread_bundle_;
273   base::ScopedTempDir temp_dir_;
274   // We don't use TestingProfile::GetPrefs() in favor of having less
275   // dependencies to Profile in general.
276   scoped_ptr<TestingPrefServiceSimple> pref_service_;
277
278   scoped_ptr<FakeDriveService> fake_drive_service_;
279   scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
280   scoped_ptr<JobScheduler> scheduler_;
281   scoped_ptr<MockDirectoryChangeObserver> mock_directory_observer_;
282
283   scoped_ptr<internal::ResourceMetadataStorage,
284              test_util::DestroyHelperForTests> metadata_storage_;
285   scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_;
286   scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
287       resource_metadata_;
288   scoped_ptr<FileSystem> file_system_;
289 };
290
291 TEST_F(FileSystemTest, DuplicatedAsyncInitialization) {
292   base::RunLoop loop;
293
294   int counter = 0;
295   const GetResourceEntryCallback& callback = base::Bind(
296       &AsyncInitializationCallback, &counter, 2, loop.QuitClosure());
297
298   file_system_->GetResourceEntryByPath(
299       base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
300   file_system_->GetResourceEntryByPath(
301       base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
302   loop.Run();  // Wait to get our result
303   EXPECT_EQ(2, counter);
304
305   // Although GetResourceEntryByPath() was called twice, the resource list
306   // should only be loaded once. In the past, there was a bug that caused
307   // it to be loaded twice.
308   EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
309   // See the comment in GetMyDriveRoot test case why this is 2.
310   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
311
312   // "Fast fetch" will fire an OnirectoryChanged event.
313   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
314   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
315             mock_directory_observer_->changed_directories()[0]);
316 }
317
318 TEST_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
329 TEST_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
341 TEST_F(FileSystemTest, GetMyDriveRoot) {
342   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root"));
343   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
344   ASSERT_TRUE(entry);
345   EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id());
346
347   // Absence of "drive/root" in the local metadata triggers the "fast fetch"
348   // of "drive" directory. Fetch of "drive" grand root directory has a special
349   // implementation. Instead of normal GetResourceListInDirectory(), it is
350   // emulated by calling GetAboutResource() so that the resource_id of
351   // "drive/root" is listed.
352   // Together with the normal GetAboutResource() call to retrieve the largest
353   // changestamp, the method is called twice.
354   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
355
356   // After "fast fetch" is done, full resource list is fetched.
357   EXPECT_EQ(1, fake_drive_service_->resource_list_load_count());
358
359   // "Fast fetch" will fire an OnirectoryChanged event.
360   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
361   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
362             mock_directory_observer_->changed_directories()[0]);
363 }
364
365 TEST_F(FileSystemTest, GetExistingFile) {
366   // Simulate the situation that full feed fetching takes very long time,
367   // to test the recursive "fast fetch" feature is properly working.
368   fake_drive_service_->set_never_return_all_resource_list(true);
369
370   const base::FilePath kFilePath(
371       FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
372   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
373   ASSERT_TRUE(entry);
374   EXPECT_EQ("file:subdirectory_file_1_id", entry->resource_id());
375
376   // One server changestamp check (about_resource), three directory load for
377   // "drive", "drive/root", and "drive/root/Directory 1", and one background
378   // full resource list loading. Note that the directory load for "drive" is
379   // special and resorts to about_resource.
380   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
381   EXPECT_EQ(2, fake_drive_service_->directory_load_count());
382   EXPECT_EQ(1, fake_drive_service_->blocked_resource_list_load_count());
383 }
384
385 TEST_F(FileSystemTest, GetExistingDocument) {
386   const base::FilePath kFilePath(
387       FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc"));
388   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
389   ASSERT_TRUE(entry);
390   EXPECT_EQ("document:5_document_resource_id", entry->resource_id());
391 }
392
393 TEST_F(FileSystemTest, GetNonExistingFile) {
394   const base::FilePath kFilePath(
395       FILE_PATH_LITERAL("drive/root/nonexisting.file"));
396   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
397   EXPECT_FALSE(entry);
398 }
399
400 TEST_F(FileSystemTest, GetExistingDirectory) {
401   const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root/Directory 1"));
402   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
403   ASSERT_TRUE(entry);
404   ASSERT_EQ("folder:1_folder_resource_id", entry->resource_id());
405
406   // The changestamp should be propagated to the directory.
407   EXPECT_EQ(fake_drive_service_->largest_changestamp(),
408             entry->directory_specific_info().changestamp());
409 }
410
411 TEST_F(FileSystemTest, GetInSubSubdir) {
412   const base::FilePath kFilePath(
413       FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/"
414                         "Sub Sub Directory Folder"));
415   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
416   ASSERT_TRUE(entry);
417   ASSERT_EQ("folder:sub_sub_directory_folder_id", entry->resource_id());
418 }
419
420 TEST_F(FileSystemTest, GetOrphanFile) {
421   ASSERT_TRUE(LoadFullResourceList());
422
423   // Entry without parents are placed under "drive/other".
424   const base::FilePath kFilePath(
425       FILE_PATH_LITERAL("drive/other/Orphan File 1.txt"));
426   scoped_ptr<ResourceEntry> entry = GetResourceEntryByPathSync(kFilePath);
427   ASSERT_TRUE(entry);
428   EXPECT_EQ("file:1_orphanfile_resource_id", entry->resource_id());
429 }
430
431 TEST_F(FileSystemTest, ReadDirectoryByPath_Root) {
432   // ReadDirectoryByPath() should kick off the resource list loading.
433   scoped_ptr<ResourceEntryVector> entries(
434       ReadDirectoryByPathSync(base::FilePath::FromUTF8Unsafe("drive")));
435   // The root directory should be read correctly.
436   ASSERT_TRUE(entries);
437   ASSERT_EQ(2U, entries->size());
438
439   // The found two directories should be /drive/root and /drive/other.
440   bool found_other = false;
441   bool found_my_drive = false;
442   for (size_t i = 0; i < entries->size(); ++i) {
443     const base::FilePath title =
444         base::FilePath::FromUTF8Unsafe((*entries)[i].title());
445     if (title == base::FilePath(util::kDriveOtherDirName)) {
446       found_other = true;
447     } else if (title == base::FilePath(util::kDriveMyDriveRootDirName)) {
448       found_my_drive = true;
449     }
450   }
451
452   EXPECT_TRUE(found_other);
453   EXPECT_TRUE(found_my_drive);
454
455   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
456   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive")),
457             mock_directory_observer_->changed_directories()[0]);
458 }
459
460 TEST_F(FileSystemTest, ReadDirectoryByPath_NonRootDirectory) {
461   // ReadDirectoryByPath() should kick off the resource list loading.
462   scoped_ptr<ResourceEntryVector> entries(
463       ReadDirectoryByPathSync(
464           base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
465   // The non root directory should also be read correctly.
466   // There was a bug (crbug.com/181487), which broke this behavior.
467   // Make sure this is fixed.
468   ASSERT_TRUE(entries);
469   EXPECT_EQ(3U, entries->size());
470 }
471
472 TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) {
473   ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
474
475   // Kicks loading of cached file system and query for server update.
476   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
477
478   // SetUpTestFileSystem and "account_metadata.json" have the same
479   // changestamp (i.e. the local metadata is up-to-date), so no request for
480   // new resource list (i.e., call to GetResourceList) should happen.
481   EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
482   EXPECT_EQ(0, fake_drive_service_->resource_list_load_count());
483
484   // Since the file system has verified that it holds the latest snapshot,
485   // it should change its state to "loaded", which admits periodic refresh.
486   // To test it, call CheckForUpdates and verify it does try to check updates.
487   file_system_->CheckForUpdates();
488   test_util::RunBlockingPoolTask();
489   EXPECT_EQ(2, fake_drive_service_->about_resource_load_count());
490 }
491
492 TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) {
493   ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
494
495   // Make GetResourceList fail for simulating offline situation. This will
496   // leave the file system "loaded from cache, but not synced with server"
497   // state.
498   fake_drive_service_->set_offline(true);
499
500   // Load the root.
501   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveGrandRootPath()));
502   // Loading of about resource should not happen as it's offline.
503   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
504
505   // Load "My Drive".
506   EXPECT_TRUE(ReadDirectoryByPathSync(util::GetDriveMyDriveRootPath()));
507   EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
508
509   // Tests that cached data can be loaded even if the server is not reachable.
510   EXPECT_TRUE(EntryExists(base::FilePath(
511       FILE_PATH_LITERAL("drive/root/File1"))));
512   EXPECT_TRUE(EntryExists(base::FilePath(
513       FILE_PATH_LITERAL("drive/root/Dir1"))));
514   EXPECT_TRUE(EntryExists(base::FilePath(
515       FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
516   EXPECT_TRUE(EntryExists(base::FilePath(
517       FILE_PATH_LITERAL("drive/root/Dir1/SubDir2"))));
518   EXPECT_TRUE(EntryExists(base::FilePath(
519       FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"))));
520
521   // Since the file system has at least succeeded to load cached snapshot,
522   // the file system should be able to start periodic refresh.
523   // To test it, call CheckForUpdates and verify it does try to check
524   // updates, which will cause directory changes.
525   fake_drive_service_->set_offline(false);
526
527   file_system_->CheckForUpdates();
528
529   test_util::RunBlockingPoolTask();
530   EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
531   EXPECT_EQ(1, fake_drive_service_->change_list_load_count());
532
533   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
534 }
535
536 TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) {
537   // Enter the "refreshing" state so the fast fetch will be performed.
538   ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
539   file_system_->CheckForUpdates();
540
541   // The list of resources in "drive/root/Dir1" should be fetched.
542   EXPECT_TRUE(ReadDirectoryByPathSync(base::FilePath(
543       FILE_PATH_LITERAL("drive/root/Dir1"))));
544   EXPECT_EQ(1, fake_drive_service_->directory_load_count());
545
546   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
547 }
548
549 TEST_F(FileSystemTest, GetResourceEntryExistingWhileRefreshing) {
550   // Enter the "refreshing" state.
551   ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
552   file_system_->CheckForUpdates();
553
554   // If an entry is already found in local metadata, no directory fetch happens.
555   EXPECT_TRUE(GetResourceEntryByPathSync(base::FilePath(
556       FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
557   EXPECT_EQ(0, fake_drive_service_->directory_load_count());
558 }
559
560 TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) {
561   // Enter the "refreshing" state so the fast fetch will be performed.
562   ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
563   file_system_->CheckForUpdates();
564
565   // If an entry is not found, parent directory's resource list is fetched.
566   EXPECT_FALSE(GetResourceEntryByPathSync(base::FilePath(
567       FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile"))));
568   EXPECT_EQ(1, fake_drive_service_->directory_load_count());
569
570   ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
571 }
572
573 TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) {
574   // Intentionally *not* calling LoadFullResourceList(), for testing that
575   // CreateDirectory ensures the resource list is loaded before it runs.
576
577   base::FilePath existing_directory(
578       FILE_PATH_LITERAL("drive/root/Directory 1"));
579   FileError error = FILE_ERROR_FAILED;
580   file_system_->CreateDirectory(
581       existing_directory,
582       true,  // is_exclusive
583       false,  // is_recursive
584       google_apis::test_util::CreateCopyResultCallback(&error));
585   test_util::RunBlockingPoolTask();
586
587   // It should fail because is_exclusive is set to true.
588   EXPECT_EQ(FILE_ERROR_EXISTS, error);
589 }
590
591 TEST_F(FileSystemTest, PinAndUnpin) {
592   ASSERT_TRUE(LoadFullResourceList());
593
594   base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
595
596   // Get the file info.
597   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
598   ASSERT_TRUE(entry);
599
600   // Pin the file.
601   FileError error = FILE_ERROR_FAILED;
602   file_system_->Pin(file_path,
603                     google_apis::test_util::CreateCopyResultCallback(&error));
604   test_util::RunBlockingPoolTask();
605   EXPECT_EQ(FILE_ERROR_OK, error);
606
607   FileCacheEntry cache_entry;
608   EXPECT_TRUE(cache_->GetCacheEntry(entry->local_id(), &cache_entry));
609   EXPECT_TRUE(cache_entry.is_pinned());
610   EXPECT_TRUE(cache_entry.is_present());
611
612   // Unpin the file.
613   error = FILE_ERROR_FAILED;
614   file_system_->Unpin(file_path,
615                       google_apis::test_util::CreateCopyResultCallback(&error));
616   test_util::RunBlockingPoolTask();
617   EXPECT_EQ(FILE_ERROR_OK, error);
618
619   EXPECT_TRUE(cache_->GetCacheEntry(entry->local_id(), &cache_entry));
620   EXPECT_FALSE(cache_entry.is_pinned());
621
622   // Pinned file gets synced and it results in entry state changes.
623   ASSERT_EQ(1u, mock_directory_observer_->changed_directories().size());
624   EXPECT_EQ(base::FilePath(FILE_PATH_LITERAL("drive/root")),
625             mock_directory_observer_->changed_directories()[0]);
626 }
627
628 TEST_F(FileSystemTest, PinAndUnpin_NotSynced) {
629   ASSERT_TRUE(LoadFullResourceList());
630
631   base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
632
633   // Get the file info.
634   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_path));
635   ASSERT_TRUE(entry);
636
637   // Unpin the file just after pinning. File fetch should be cancelled.
638   FileError error_pin = FILE_ERROR_FAILED;
639   file_system_->Pin(
640       file_path,
641       google_apis::test_util::CreateCopyResultCallback(&error_pin));
642
643   FileError error_unpin = FILE_ERROR_FAILED;
644   file_system_->Unpin(
645       file_path,
646       google_apis::test_util::CreateCopyResultCallback(&error_unpin));
647
648   test_util::RunBlockingPoolTask();
649   EXPECT_EQ(FILE_ERROR_OK, error_pin);
650   EXPECT_EQ(FILE_ERROR_OK, error_unpin);
651
652   // No cache file available because the sync was cancelled by Unpin().
653   FileCacheEntry cache_entry;
654   EXPECT_FALSE(cache_->GetCacheEntry(entry->local_id(), &cache_entry));
655 }
656
657 TEST_F(FileSystemTest, GetAvailableSpace) {
658   FileError error = FILE_ERROR_OK;
659   int64 bytes_total;
660   int64 bytes_used;
661   file_system_->GetAvailableSpace(
662       google_apis::test_util::CreateCopyResultCallback(
663           &error, &bytes_total, &bytes_used));
664   test_util::RunBlockingPoolTask();
665   EXPECT_EQ(GG_LONGLONG(6789012345), bytes_used);
666   EXPECT_EQ(GG_LONGLONG(9876543210), bytes_total);
667 }
668
669 TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) {
670   ASSERT_TRUE(LoadFullResourceList());
671
672   base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
673   scoped_ptr<ResourceEntry> entry(GetResourceEntryByPathSync(file_in_root));
674   ASSERT_TRUE(entry);
675
676   // Write to cache.
677   ASSERT_EQ(FILE_ERROR_OK, cache_->Store(
678       entry->local_id(),
679       entry->file_specific_info().md5(),
680       google_apis::test_util::GetTestFilePath("gdata/root_feed.json"),
681       internal::FileCache::FILE_OPERATION_COPY));
682
683   // Test for mounting.
684   FileError error = FILE_ERROR_FAILED;
685   base::FilePath file_path;
686   file_system_->MarkCacheFileAsMounted(
687       file_in_root,
688       google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
689   test_util::RunBlockingPoolTask();
690   EXPECT_EQ(FILE_ERROR_OK, error);
691
692   // Cannot remove a cache entry while it's being mounted.
693   EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->local_id()));
694
695   // Test for unmounting.
696   error = FILE_ERROR_FAILED;
697   file_system_->MarkCacheFileAsUnmounted(
698       file_path,
699       google_apis::test_util::CreateCopyResultCallback(&error));
700   test_util::RunBlockingPoolTask();
701   EXPECT_EQ(FILE_ERROR_OK, error);
702
703   // Now able to remove the cache entry.
704   EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->local_id()));
705 }
706
707 TEST_F(FileSystemTest, GetShareUrl) {
708   ASSERT_TRUE(LoadFullResourceList());
709
710   const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt"));
711   const GURL kEmbedOrigin("chrome-extension://test-id");
712
713   // Try to fetch the URL for the sharing dialog.
714   FileError error = FILE_ERROR_FAILED;
715   GURL share_url;
716   file_system_->GetShareUrl(
717       kFileInRoot,
718       kEmbedOrigin,
719       google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
720   test_util::RunBlockingPoolTask();
721
722   // Verify the share url to the sharing dialog.
723   EXPECT_EQ(FILE_ERROR_OK, error);
724   EXPECT_EQ(GURL("https://file_link_share/"), share_url);
725 }
726
727 TEST_F(FileSystemTest, GetShareUrlNotAvailable) {
728   ASSERT_TRUE(LoadFullResourceList());
729
730   const base::FilePath kFileInRoot(
731       FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
732   const GURL kEmbedOrigin("chrome-extension://test-id");
733
734   // Try to fetch the URL for the sharing dialog.
735   FileError error = FILE_ERROR_FAILED;
736   GURL share_url;
737
738   file_system_->GetShareUrl(
739       kFileInRoot,
740       kEmbedOrigin,
741       google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
742   test_util::RunBlockingPoolTask();
743
744   // Verify the error and the share url, which should be empty.
745   EXPECT_EQ(FILE_ERROR_FAILED, error);
746   EXPECT_TRUE(share_url.is_empty());
747 }
748
749 }   // namespace drive