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/files/file_path.h"
12#include "base/files/file_util.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/drive.pb.h"
20#include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
21#include "chrome/browser/chromeos/drive/file_change.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/drive_api_util.h"
28#include "chrome/browser/drive/event_logger.h"
29#include "chrome/browser/drive/fake_drive_service.h"
30#include "chrome/browser/drive/test_util.h"
31#include "content/public/test/test_browser_thread_bundle.h"
32#include "google_apis/drive/drive_api_parser.h"
33#include "google_apis/drive/test_util.h"
34#include "testing/gtest/include/gtest/gtest.h"
35
36namespace drive {
37namespace {
38
39// Counts the number of invocation, and if it increased up to |expected_counter|
40// quits the current message loop by calling |quit|.
41void AsyncInitializationCallback(
42    int* counter, int expected_counter, const base::Closure& quit,
43    FileError error, scoped_ptr<ResourceEntry> entry) {
44  if (error != FILE_ERROR_OK || !entry) {
45    // If we hit an error case, quit the message loop immediately.
46    // Then the expectation in the test case can find it because the actual
47    // value of |counter| is different from the expected one.
48    quit.Run();
49    return;
50  }
51
52  (*counter)++;
53  if (*counter >= expected_counter)
54    quit.Run();
55}
56
57// This class is used to record directory changes and examine them later.
58class MockDirectoryChangeObserver : public FileSystemObserver {
59 public:
60  MockDirectoryChangeObserver() {}
61  virtual ~MockDirectoryChangeObserver() {}
62
63  // FileSystemObserver overrides.
64  virtual void OnDirectoryChanged(
65      const base::FilePath& directory_path) OVERRIDE {
66    changed_directories_.push_back(directory_path);
67  }
68
69  virtual void OnFileChanged(const FileChange& new_file_change) OVERRIDE {
70    changed_files_.Apply(new_file_change);
71  }
72
73  const std::vector<base::FilePath>& changed_directories() const {
74    return changed_directories_;
75  }
76
77  const FileChange& changed_files() const { return changed_files_; }
78
79 private:
80  std::vector<base::FilePath> changed_directories_;
81  FileChange changed_files_;
82  DISALLOW_COPY_AND_ASSIGN(MockDirectoryChangeObserver);
83};
84
85}  // namespace
86
87class FileSystemTest : public testing::Test {
88 protected:
89  virtual void SetUp() OVERRIDE {
90    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
91    pref_service_.reset(new TestingPrefServiceSimple);
92    test_util::RegisterDrivePrefs(pref_service_->registry());
93
94    logger_.reset(new EventLogger);
95    fake_drive_service_.reset(new FakeDriveService);
96    test_util::SetUpTestEntries(fake_drive_service_.get());
97
98    fake_free_disk_space_getter_.reset(new FakeFreeDiskSpaceGetter);
99
100    scheduler_.reset(new JobScheduler(pref_service_.get(),
101                                      logger_.get(),
102                                      fake_drive_service_.get(),
103                                      base::MessageLoopProxy::current().get()));
104
105    mock_directory_observer_.reset(new MockDirectoryChangeObserver);
106
107    SetUpResourceMetadataAndFileSystem();
108  }
109
110  void SetUpResourceMetadataAndFileSystem() {
111    const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
112    ASSERT_TRUE(base::CreateDirectory(metadata_dir));
113    metadata_storage_.reset(new internal::ResourceMetadataStorage(
114        metadata_dir, base::MessageLoopProxy::current().get()));
115    ASSERT_TRUE(metadata_storage_->Initialize());
116
117    const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files");
118    ASSERT_TRUE(base::CreateDirectory(cache_dir));
119    cache_.reset(new internal::FileCache(
120        metadata_storage_.get(),
121        cache_dir,
122        base::MessageLoopProxy::current().get(),
123        fake_free_disk_space_getter_.get()));
124    ASSERT_TRUE(cache_->Initialize());
125
126    resource_metadata_.reset(new internal::ResourceMetadata(
127        metadata_storage_.get(), cache_.get(),
128        base::MessageLoopProxy::current()));
129    ASSERT_EQ(FILE_ERROR_OK, resource_metadata_->Initialize());
130
131    const base::FilePath temp_file_dir = temp_dir_.path().AppendASCII("tmp");
132    ASSERT_TRUE(base::CreateDirectory(temp_file_dir));
133    file_system_.reset(new FileSystem(
134        pref_service_.get(),
135        logger_.get(),
136        cache_.get(),
137        fake_drive_service_.get(),
138        scheduler_.get(),
139        resource_metadata_.get(),
140        base::MessageLoopProxy::current().get(),
141        temp_file_dir));
142    file_system_->AddObserver(mock_directory_observer_.get());
143
144    // Disable delaying so that the sync starts immediately.
145    file_system_->sync_client_for_testing()->set_delay_for_testing(
146        base::TimeDelta::FromSeconds(0));
147  }
148
149  // Loads the full resource list via FakeDriveService.
150  bool LoadFullResourceList() {
151    FileError error = FILE_ERROR_FAILED;
152    file_system_->change_list_loader_for_testing()->LoadIfNeeded(
153        google_apis::test_util::CreateCopyResultCallback(&error));
154    content::RunAllBlockingPoolTasksUntilIdle();
155    return error == FILE_ERROR_OK;
156  }
157
158  // Gets resource entry by path synchronously.
159  scoped_ptr<ResourceEntry> GetResourceEntrySync(
160      const base::FilePath& file_path) {
161    FileError error = FILE_ERROR_FAILED;
162    scoped_ptr<ResourceEntry> entry;
163    file_system_->GetResourceEntry(
164        file_path,
165        google_apis::test_util::CreateCopyResultCallback(&error, &entry));
166    content::RunAllBlockingPoolTasksUntilIdle();
167
168    return entry.Pass();
169  }
170
171  // Gets directory info by path synchronously.
172  scoped_ptr<ResourceEntryVector> ReadDirectorySync(
173      const base::FilePath& file_path) {
174    FileError error = FILE_ERROR_FAILED;
175    scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector);
176    file_system_->ReadDirectory(
177        file_path,
178        base::Bind(&AccumulateReadDirectoryResult, entries.get()),
179        google_apis::test_util::CreateCopyResultCallback(&error));
180    content::RunAllBlockingPoolTasksUntilIdle();
181    if (error != FILE_ERROR_OK)
182      entries.reset();
183    return entries.Pass();
184  }
185
186  // Used to implement ReadDirectorySync().
187  static void AccumulateReadDirectoryResult(
188      ResourceEntryVector* out_entries,
189      scoped_ptr<ResourceEntryVector> entries) {
190    ASSERT_TRUE(entries);
191    out_entries->insert(out_entries->end(), entries->begin(), entries->end());
192  }
193
194  // Returns true if an entry exists at |file_path|.
195  bool EntryExists(const base::FilePath& file_path) {
196    return GetResourceEntrySync(file_path);
197  }
198
199  // Flag for specifying the timestamp of the test filesystem cache.
200  enum SetUpTestFileSystemParam {
201    USE_OLD_TIMESTAMP,
202    USE_SERVER_TIMESTAMP,
203  };
204
205  // Sets up a filesystem with directories: drive/root, drive/root/Dir1,
206  // drive/root/Dir1/SubDir2 and files drive/root/File1, drive/root/Dir1/File2,
207  // drive/root/Dir1/SubDir2/File3. If |use_up_to_date_timestamp| is true, sets
208  // the changestamp to that of FakeDriveService, indicating the cache is
209  // holding the latest file system info.
210  void SetUpTestFileSystem(SetUpTestFileSystemParam param) {
211    // Destroy the existing resource metadata to close DB.
212    resource_metadata_.reset();
213
214    const base::FilePath metadata_dir = temp_dir_.path().AppendASCII("meta");
215    ASSERT_TRUE(base::CreateDirectory(metadata_dir));
216    scoped_ptr<internal::ResourceMetadataStorage,
217               test_util::DestroyHelperForTests> metadata_storage(
218        new internal::ResourceMetadataStorage(
219            metadata_dir, base::MessageLoopProxy::current().get()));
220
221    const base::FilePath cache_dir = temp_dir_.path().AppendASCII("files");
222    scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache(
223        new internal::FileCache(metadata_storage.get(),
224                                cache_dir,
225                                base::MessageLoopProxy::current().get(),
226                                fake_free_disk_space_getter_.get()));
227
228    scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
229        resource_metadata(new internal::ResourceMetadata(
230            metadata_storage_.get(), cache.get(),
231            base::MessageLoopProxy::current()));
232
233    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->Initialize());
234
235    const int64 changestamp = param == USE_SERVER_TIMESTAMP ?
236        fake_drive_service_->about_resource().largest_change_id() : 1;
237    ASSERT_EQ(FILE_ERROR_OK,
238              resource_metadata->SetLargestChangestamp(changestamp));
239
240    // drive/root
241    ResourceEntry root;
242    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->GetResourceEntryByPath(
243        util::GetDriveMyDriveRootPath(), &root));
244    root.set_resource_id(fake_drive_service_->GetRootResourceId());
245    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->RefreshEntry(root));
246
247    std::string local_id;
248
249    // drive/root/File1
250    ResourceEntry file1;
251    file1.set_title("File1");
252    file1.set_resource_id("resource_id:File1");
253    file1.set_parent_local_id(root.local_id());
254    file1.mutable_file_specific_info()->set_md5("md5");
255    file1.mutable_file_info()->set_is_directory(false);
256    file1.mutable_file_info()->set_size(1048576);
257    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file1, &local_id));
258
259    // drive/root/Dir1
260    ResourceEntry dir1;
261    dir1.set_title("Dir1");
262    dir1.set_resource_id("resource_id:Dir1");
263    dir1.set_parent_local_id(root.local_id());
264    dir1.mutable_file_info()->set_is_directory(true);
265    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir1, &local_id));
266    const std::string dir1_local_id = local_id;
267
268    // drive/root/Dir1/File2
269    ResourceEntry file2;
270    file2.set_title("File2");
271    file2.set_resource_id("resource_id:File2");
272    file2.set_parent_local_id(dir1_local_id);
273    file2.mutable_file_specific_info()->set_md5("md5");
274    file2.mutable_file_info()->set_is_directory(false);
275    file2.mutable_file_info()->set_size(555);
276    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file2, &local_id));
277
278    // drive/root/Dir1/SubDir2
279    ResourceEntry dir2;
280    dir2.set_title("SubDir2");
281    dir2.set_resource_id("resource_id:SubDir2");
282    dir2.set_parent_local_id(dir1_local_id);
283    dir2.mutable_file_info()->set_is_directory(true);
284    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(dir2, &local_id));
285    const std::string dir2_local_id = local_id;
286
287    // drive/root/Dir1/SubDir2/File3
288    ResourceEntry file3;
289    file3.set_title("File3");
290    file3.set_resource_id("resource_id:File3");
291    file3.set_parent_local_id(dir2_local_id);
292    file3.mutable_file_specific_info()->set_md5("md5");
293    file3.mutable_file_info()->set_is_directory(false);
294    file3.mutable_file_info()->set_size(12345);
295    ASSERT_EQ(FILE_ERROR_OK, resource_metadata->AddEntry(file3, &local_id));
296
297    // Recreate resource metadata.
298    SetUpResourceMetadataAndFileSystem();
299  }
300
301  content::TestBrowserThreadBundle thread_bundle_;
302  base::ScopedTempDir temp_dir_;
303  // We don't use TestingProfile::GetPrefs() in favor of having less
304  // dependencies to Profile in general.
305  scoped_ptr<TestingPrefServiceSimple> pref_service_;
306
307  scoped_ptr<EventLogger> logger_;
308  scoped_ptr<FakeDriveService> fake_drive_service_;
309  scoped_ptr<FakeFreeDiskSpaceGetter> fake_free_disk_space_getter_;
310  scoped_ptr<JobScheduler> scheduler_;
311  scoped_ptr<MockDirectoryChangeObserver> mock_directory_observer_;
312
313  scoped_ptr<internal::ResourceMetadataStorage,
314             test_util::DestroyHelperForTests> metadata_storage_;
315  scoped_ptr<internal::FileCache, test_util::DestroyHelperForTests> cache_;
316  scoped_ptr<internal::ResourceMetadata, test_util::DestroyHelperForTests>
317      resource_metadata_;
318  scoped_ptr<FileSystem> file_system_;
319};
320
321TEST_F(FileSystemTest, Copy) {
322  base::FilePath src_file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
323  base::FilePath dest_file_path(FILE_PATH_LITERAL("drive/root/Copied.txt"));
324  EXPECT_TRUE(GetResourceEntrySync(src_file_path));
325  EXPECT_FALSE(GetResourceEntrySync(dest_file_path));
326
327  FileError error = FILE_ERROR_FAILED;
328  file_system_->Copy(src_file_path,
329                     dest_file_path,
330                     false,  // preserve_last_modified,
331                     google_apis::test_util::CreateCopyResultCallback(&error));
332  content::RunAllBlockingPoolTasksUntilIdle();
333  EXPECT_EQ(FILE_ERROR_OK, error);
334
335  // Entry is added on the server.
336  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(dest_file_path);
337  ASSERT_TRUE(entry);
338
339  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
340  scoped_ptr<google_apis::FileResource> server_entry;
341  fake_drive_service_->GetFileResource(
342      entry->resource_id(),
343      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
344  content::RunAllBlockingPoolTasksUntilIdle();
345  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
346  ASSERT_TRUE(server_entry);
347  EXPECT_EQ(entry->title(), server_entry->title());
348  EXPECT_FALSE(server_entry->IsDirectory());
349}
350
351TEST_F(FileSystemTest, Move) {
352  base::FilePath src_file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
353  base::FilePath dest_file_path(
354      FILE_PATH_LITERAL("drive/root/Directory 1/Moved.txt"));
355  EXPECT_TRUE(GetResourceEntrySync(src_file_path));
356  EXPECT_FALSE(GetResourceEntrySync(dest_file_path));
357  scoped_ptr<ResourceEntry> parent =
358      GetResourceEntrySync(dest_file_path.DirName());
359  ASSERT_TRUE(parent);
360
361  FileError error = FILE_ERROR_FAILED;
362  file_system_->Move(src_file_path,
363                     dest_file_path,
364                     google_apis::test_util::CreateCopyResultCallback(&error));
365  content::RunAllBlockingPoolTasksUntilIdle();
366  EXPECT_EQ(FILE_ERROR_OK, error);
367
368  // Entry is moved on the server.
369  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(dest_file_path);
370  ASSERT_TRUE(entry);
371
372  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
373  scoped_ptr<google_apis::FileResource> server_entry;
374  fake_drive_service_->GetFileResource(
375      entry->resource_id(),
376      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
377  content::RunAllBlockingPoolTasksUntilIdle();
378  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
379  ASSERT_TRUE(server_entry);
380  EXPECT_EQ(entry->title(), server_entry->title());
381
382  ASSERT_FALSE(server_entry->parents().empty());
383  EXPECT_EQ(parent->resource_id(), server_entry->parents()[0].file_id());
384}
385
386TEST_F(FileSystemTest, Remove) {
387  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
388  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path);
389  ASSERT_TRUE(entry);
390
391  FileError error = FILE_ERROR_FAILED;
392  file_system_->Remove(
393      file_path,
394      false,  // is_resursive
395      google_apis::test_util::CreateCopyResultCallback(&error));
396  content::RunAllBlockingPoolTasksUntilIdle();
397  EXPECT_EQ(FILE_ERROR_OK, error);
398
399  // Entry is removed on the server.
400  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
401  scoped_ptr<google_apis::FileResource> server_entry;
402  fake_drive_service_->GetFileResource(
403      entry->resource_id(),
404      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
405  content::RunAllBlockingPoolTasksUntilIdle();
406  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
407  ASSERT_TRUE(server_entry);
408  EXPECT_TRUE(server_entry->labels().is_trashed());
409}
410
411TEST_F(FileSystemTest, CreateDirectory) {
412  base::FilePath directory_path(FILE_PATH_LITERAL("drive/root/New Directory"));
413  EXPECT_FALSE(GetResourceEntrySync(directory_path));
414
415  FileError error = FILE_ERROR_FAILED;
416  file_system_->CreateDirectory(
417      directory_path,
418      true,  // is_exclusive
419      false,  // is_recursive
420      google_apis::test_util::CreateCopyResultCallback(&error));
421  content::RunAllBlockingPoolTasksUntilIdle();
422  EXPECT_EQ(FILE_ERROR_OK, error);
423
424  // Directory is created on the server.
425  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(directory_path);
426  ASSERT_TRUE(entry);
427
428  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
429  scoped_ptr<google_apis::FileResource> server_entry;
430  fake_drive_service_->GetFileResource(
431      entry->resource_id(),
432      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
433  content::RunAllBlockingPoolTasksUntilIdle();
434  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
435  ASSERT_TRUE(server_entry);
436  EXPECT_EQ(entry->title(), server_entry->title());
437  EXPECT_TRUE(server_entry->IsDirectory());
438}
439
440TEST_F(FileSystemTest, CreateFile) {
441  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/New File.txt"));
442  EXPECT_FALSE(GetResourceEntrySync(file_path));
443
444  FileError error = FILE_ERROR_FAILED;
445  file_system_->CreateFile(
446      file_path,
447      true,  // is_exclusive
448      "text/plain",
449      google_apis::test_util::CreateCopyResultCallback(&error));
450  content::RunAllBlockingPoolTasksUntilIdle();
451  EXPECT_EQ(FILE_ERROR_OK, error);
452
453  // File is created on the server.
454  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path);
455  ASSERT_TRUE(entry);
456
457  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
458  scoped_ptr<google_apis::FileResource> server_entry;
459  fake_drive_service_->GetFileResource(
460      entry->resource_id(),
461      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
462  content::RunAllBlockingPoolTasksUntilIdle();
463  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
464  ASSERT_TRUE(server_entry);
465  EXPECT_EQ(entry->title(), server_entry->title());
466  EXPECT_FALSE(server_entry->IsDirectory());
467}
468
469TEST_F(FileSystemTest, TouchFile) {
470  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
471  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path);
472  ASSERT_TRUE(entry);
473
474  base::Time last_accessed =
475      base::Time::FromInternalValue(entry->file_info().last_accessed()) +
476      base::TimeDelta::FromSeconds(1);
477  base::Time last_modified =
478      base::Time::FromInternalValue(entry->file_info().last_modified()) +
479      base::TimeDelta::FromSeconds(1);
480
481  FileError error = FILE_ERROR_FAILED;
482  file_system_->TouchFile(
483      file_path,
484      last_accessed,
485      last_modified,
486      google_apis::test_util::CreateCopyResultCallback(&error));
487  content::RunAllBlockingPoolTasksUntilIdle();
488  EXPECT_EQ(FILE_ERROR_OK, error);
489
490  // File is touched on the server.
491  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
492  scoped_ptr<google_apis::FileResource> server_entry;
493  fake_drive_service_->GetFileResource(
494      entry->resource_id(),
495      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
496  content::RunAllBlockingPoolTasksUntilIdle();
497  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
498  ASSERT_TRUE(server_entry);
499  EXPECT_EQ(last_accessed, server_entry->last_viewed_by_me_date());
500  EXPECT_EQ(last_modified, server_entry->modified_date());
501}
502
503TEST_F(FileSystemTest, TruncateFile) {
504  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
505  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(file_path);
506  ASSERT_TRUE(entry);
507
508  const int64 kLength = entry->file_info().size() + 100;
509
510  FileError error = FILE_ERROR_FAILED;
511  file_system_->TruncateFile(
512      file_path,
513      kLength,
514      google_apis::test_util::CreateCopyResultCallback(&error));
515  content::RunAllBlockingPoolTasksUntilIdle();
516  EXPECT_EQ(FILE_ERROR_OK, error);
517
518  // File is touched on the server.
519  google_apis::GDataErrorCode status = google_apis::GDATA_OTHER_ERROR;
520  scoped_ptr<google_apis::FileResource> server_entry;
521  fake_drive_service_->GetFileResource(
522      entry->resource_id(),
523      google_apis::test_util::CreateCopyResultCallback(&status, &server_entry));
524  content::RunAllBlockingPoolTasksUntilIdle();
525  EXPECT_EQ(google_apis::HTTP_SUCCESS, status);
526  ASSERT_TRUE(server_entry);
527  EXPECT_EQ(kLength, server_entry->file_size());
528}
529
530TEST_F(FileSystemTest, DuplicatedAsyncInitialization) {
531  base::RunLoop loop;
532
533  int counter = 0;
534  const GetResourceEntryCallback& callback = base::Bind(
535      &AsyncInitializationCallback, &counter, 2, loop.QuitClosure());
536
537  file_system_->GetResourceEntry(
538      base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
539  file_system_->GetResourceEntry(
540      base::FilePath(FILE_PATH_LITERAL("drive/root")), callback);
541  loop.Run();  // Wait to get our result
542  EXPECT_EQ(2, counter);
543
544  EXPECT_EQ(1, fake_drive_service_->file_list_load_count());
545}
546
547TEST_F(FileSystemTest, GetGrandRootEntry) {
548  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive"));
549  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
550  ASSERT_TRUE(entry);
551  EXPECT_EQ(util::kDriveGrandRootLocalId, entry->local_id());
552}
553
554TEST_F(FileSystemTest, GetOtherDirEntry) {
555  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/other"));
556  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
557  ASSERT_TRUE(entry);
558  EXPECT_EQ(util::kDriveOtherDirLocalId, entry->local_id());
559}
560
561TEST_F(FileSystemTest, GetMyDriveRoot) {
562  const base::FilePath kFilePath(FILE_PATH_LITERAL("drive/root"));
563  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
564  ASSERT_TRUE(entry);
565  EXPECT_EQ(fake_drive_service_->GetRootResourceId(), entry->resource_id());
566
567  // After "fast fetch" is done, full resource list is fetched.
568  EXPECT_EQ(1, fake_drive_service_->file_list_load_count());
569}
570
571TEST_F(FileSystemTest, GetExistingFile) {
572  // Simulate the situation that full feed fetching takes very long time,
573  // to test the recursive "fast fetch" feature is properly working.
574  fake_drive_service_->set_never_return_all_file_list(true);
575
576  const base::FilePath kFilePath(
577      FILE_PATH_LITERAL("drive/root/Directory 1/SubDirectory File 1.txt"));
578  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
579  ASSERT_TRUE(entry);
580  EXPECT_EQ("subdirectory_file_1_id", entry->resource_id());
581
582  EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
583  EXPECT_EQ(2, fake_drive_service_->directory_load_count());
584  EXPECT_EQ(1, fake_drive_service_->blocked_file_list_load_count());
585}
586
587TEST_F(FileSystemTest, GetExistingDocument) {
588  const base::FilePath kFilePath(
589      FILE_PATH_LITERAL("drive/root/Document 1 excludeDir-test.gdoc"));
590  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
591  ASSERT_TRUE(entry);
592  EXPECT_EQ("5_document_resource_id", entry->resource_id());
593}
594
595TEST_F(FileSystemTest, GetNonExistingFile) {
596  const base::FilePath kFilePath(
597      FILE_PATH_LITERAL("drive/root/nonexisting.file"));
598  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
599  EXPECT_FALSE(entry);
600}
601
602TEST_F(FileSystemTest, GetInSubSubdir) {
603  const base::FilePath kFilePath(
604      FILE_PATH_LITERAL("drive/root/Directory 1/Sub Directory Folder/"
605                        "Sub Sub Directory Folder"));
606  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
607  ASSERT_TRUE(entry);
608  ASSERT_EQ("sub_sub_directory_folder_id", entry->resource_id());
609}
610
611TEST_F(FileSystemTest, GetOrphanFile) {
612  ASSERT_TRUE(LoadFullResourceList());
613
614  // Entry without parents are placed under "drive/other".
615  const base::FilePath kFilePath(
616      FILE_PATH_LITERAL("drive/other/Orphan File 1.txt"));
617  scoped_ptr<ResourceEntry> entry = GetResourceEntrySync(kFilePath);
618  ASSERT_TRUE(entry);
619  EXPECT_EQ("1_orphanfile_resource_id", entry->resource_id());
620}
621
622TEST_F(FileSystemTest, ReadDirectory_Root) {
623  // ReadDirectory() should kick off the resource list loading.
624  scoped_ptr<ResourceEntryVector> entries(
625      ReadDirectorySync(base::FilePath::FromUTF8Unsafe("drive")));
626  // The root directory should be read correctly.
627  ASSERT_TRUE(entries);
628  ASSERT_EQ(3U, entries->size());
629
630  // The found three directories should be /drive/root, /drive/other and
631  // /drive/trash.
632  std::set<base::FilePath> found;
633  for (size_t i = 0; i < entries->size(); ++i)
634    found.insert(base::FilePath::FromUTF8Unsafe((*entries)[i].title()));
635  EXPECT_EQ(3U, found.size());
636  EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveMyDriveRootDirName)));
637  EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveOtherDirName)));
638  EXPECT_EQ(1U, found.count(base::FilePath(util::kDriveTrashDirName)));
639}
640
641TEST_F(FileSystemTest, ReadDirectory_NonRootDirectory) {
642  // ReadDirectory() should kick off the resource list loading.
643  scoped_ptr<ResourceEntryVector> entries(
644      ReadDirectorySync(
645          base::FilePath::FromUTF8Unsafe("drive/root/Directory 1")));
646  // The non root directory should also be read correctly.
647  // There was a bug (crbug.com/181487), which broke this behavior.
648  // Make sure this is fixed.
649  ASSERT_TRUE(entries);
650  EXPECT_EQ(3U, entries->size());
651}
652
653TEST_F(FileSystemTest, LoadFileSystemFromUpToDateCache) {
654  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_SERVER_TIMESTAMP));
655
656  // Kicks loading of cached file system and query for server update.
657  EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath()));
658
659  // SetUpTestFileSystem and FakeDriveService have the same
660  // changestamp (i.e. the local metadata is up-to-date), so no request for
661  // new resource list (i.e., call to GetResourceList) should happen.
662  EXPECT_EQ(0, fake_drive_service_->file_list_load_count());
663
664  // Since the file system has verified that it holds the latest snapshot,
665  // it should change its state to "loaded", which admits periodic refresh.
666  // To test it, call CheckForUpdates and verify it does try to check updates.
667  const int about_resource_load_count_before =
668      fake_drive_service_->about_resource_load_count();
669  file_system_->CheckForUpdates();
670  content::RunAllBlockingPoolTasksUntilIdle();
671  EXPECT_LT(about_resource_load_count_before,
672            fake_drive_service_->about_resource_load_count());
673}
674
675TEST_F(FileSystemTest, LoadFileSystemFromCacheWhileOffline) {
676  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
677
678  // Make GetResourceList fail for simulating offline situation. This will
679  // leave the file system "loaded from cache, but not synced with server"
680  // state.
681  fake_drive_service_->set_offline(true);
682
683  // Load the root.
684  EXPECT_TRUE(ReadDirectorySync(util::GetDriveGrandRootPath()));
685  // Loading of about resource should not happen as it's offline.
686  EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
687
688  // Load "My Drive".
689  EXPECT_TRUE(ReadDirectorySync(util::GetDriveMyDriveRootPath()));
690  EXPECT_EQ(0, fake_drive_service_->about_resource_load_count());
691
692  // Tests that cached data can be loaded even if the server is not reachable.
693  EXPECT_TRUE(EntryExists(base::FilePath(
694      FILE_PATH_LITERAL("drive/root/File1"))));
695  EXPECT_TRUE(EntryExists(base::FilePath(
696      FILE_PATH_LITERAL("drive/root/Dir1"))));
697  EXPECT_TRUE(EntryExists(base::FilePath(
698      FILE_PATH_LITERAL("drive/root/Dir1/File2"))));
699  EXPECT_TRUE(EntryExists(base::FilePath(
700      FILE_PATH_LITERAL("drive/root/Dir1/SubDir2"))));
701  EXPECT_TRUE(EntryExists(base::FilePath(
702      FILE_PATH_LITERAL("drive/root/Dir1/SubDir2/File3"))));
703
704  // Since the file system has at least succeeded to load cached snapshot,
705  // the file system should be able to start periodic refresh.
706  // To test it, call CheckForUpdates and verify it does try to check
707  // updates, which will cause directory changes.
708  fake_drive_service_->set_offline(false);
709
710  file_system_->CheckForUpdates();
711
712  content::RunAllBlockingPoolTasksUntilIdle();
713  EXPECT_EQ(1, fake_drive_service_->about_resource_load_count());
714  EXPECT_EQ(1, fake_drive_service_->change_list_load_count());
715
716  ASSERT_LE(0u, mock_directory_observer_->changed_directories().size());
717  ASSERT_LE(1u, mock_directory_observer_->changed_files().size());
718}
719
720TEST_F(FileSystemTest, ReadDirectoryWhileRefreshing) {
721  // Use old timestamp so the fast fetch will be performed.
722  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
723
724  // The list of resources in "drive/root/Dir1" should be fetched.
725  EXPECT_TRUE(ReadDirectorySync(base::FilePath(
726      FILE_PATH_LITERAL("drive/root/Dir1"))));
727  EXPECT_EQ(1, fake_drive_service_->directory_load_count());
728
729  ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
730}
731
732TEST_F(FileSystemTest, GetResourceEntryNonExistentWhileRefreshing) {
733  // Use old timestamp so the fast fetch will be performed.
734  ASSERT_NO_FATAL_FAILURE(SetUpTestFileSystem(USE_OLD_TIMESTAMP));
735
736  // If an entry is not found, parent directory's resource list is fetched.
737  EXPECT_FALSE(GetResourceEntrySync(base::FilePath(
738      FILE_PATH_LITERAL("drive/root/Dir1/NonExistentFile"))));
739  EXPECT_EQ(1, fake_drive_service_->directory_load_count());
740
741  ASSERT_LE(1u, mock_directory_observer_->changed_directories().size());
742}
743
744TEST_F(FileSystemTest, CreateDirectoryByImplicitLoad) {
745  // Intentionally *not* calling LoadFullResourceList(), for testing that
746  // CreateDirectory ensures the resource list is loaded before it runs.
747
748  base::FilePath existing_directory(
749      FILE_PATH_LITERAL("drive/root/Directory 1"));
750  FileError error = FILE_ERROR_FAILED;
751  file_system_->CreateDirectory(
752      existing_directory,
753      true,  // is_exclusive
754      false,  // is_recursive
755      google_apis::test_util::CreateCopyResultCallback(&error));
756  content::RunAllBlockingPoolTasksUntilIdle();
757
758  // It should fail because is_exclusive is set to true.
759  EXPECT_EQ(FILE_ERROR_EXISTS, error);
760}
761
762TEST_F(FileSystemTest, CreateDirectoryRecursively) {
763  // Intentionally *not* calling LoadFullResourceList(), for testing that
764  // CreateDirectory ensures the resource list is loaded before it runs.
765
766  base::FilePath new_directory(
767      FILE_PATH_LITERAL("drive/root/Directory 1/a/b/c/d"));
768  FileError error = FILE_ERROR_FAILED;
769  file_system_->CreateDirectory(
770      new_directory,
771      true,  // is_exclusive
772      true,  // is_recursive
773      google_apis::test_util::CreateCopyResultCallback(&error));
774  content::RunAllBlockingPoolTasksUntilIdle();
775
776  EXPECT_EQ(FILE_ERROR_OK, error);
777
778  scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(new_directory));
779  ASSERT_TRUE(entry);
780  EXPECT_TRUE(entry->file_info().is_directory());
781}
782
783TEST_F(FileSystemTest, ReadDirectoryAfterUpdateWhileLoading) {
784  // Simulate the situation that full feed fetching takes very long time,
785  // to test the recursive "fast fetch" feature is properly working.
786  fake_drive_service_->set_never_return_all_file_list(true);
787
788  // On the fake server, create the test directory.
789  scoped_ptr<google_apis::FileResource> parent;
790  {
791    google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
792    fake_drive_service_->AddNewDirectory(
793        fake_drive_service_->GetRootResourceId(),
794        "UpdateWhileLoadingTestDir",
795        DriveServiceInterface::AddNewDirectoryOptions(),
796        google_apis::test_util::CreateCopyResultCallback(&error, &parent));
797    base::RunLoop().RunUntilIdle();
798    ASSERT_EQ(google_apis::HTTP_CREATED, error);
799  }
800
801  // Fetch the directory. Currently it is empty.
802  scoped_ptr<ResourceEntryVector> before = ReadDirectorySync(base::FilePath(
803      FILE_PATH_LITERAL("drive/root/UpdateWhileLoadingTestDir")));
804  ASSERT_TRUE(before);
805  EXPECT_EQ(0u, before->size());
806
807  // Create a file in the test directory.
808  scoped_ptr<google_apis::FileResource> entry;
809  {
810    google_apis::GDataErrorCode error = google_apis::GDATA_OTHER_ERROR;
811    fake_drive_service_->AddNewFile(
812        "text/plain",
813        "(dummy data)",
814        parent->file_id(),
815        "TestFile",
816        false,  // shared_with_me
817        google_apis::test_util::CreateCopyResultCallback(&error, &entry));
818    base::RunLoop().RunUntilIdle();
819    ASSERT_EQ(google_apis::HTTP_CREATED, error);
820  }
821
822  // Notify the update to the file system.
823  file_system_->CheckForUpdates();
824
825  // Read the directory once again. Although the full feed fetching is not yet
826  // finished, the "fast fetch" of the directory works and the refreshed content
827  // is returned.
828  scoped_ptr<ResourceEntryVector> after = ReadDirectorySync(base::FilePath(
829      FILE_PATH_LITERAL("drive/root/UpdateWhileLoadingTestDir")));
830  ASSERT_TRUE(after);
831  EXPECT_EQ(1u, after->size());
832}
833
834TEST_F(FileSystemTest, PinAndUnpin) {
835  ASSERT_TRUE(LoadFullResourceList());
836
837  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
838
839  // Get the file info.
840  scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path));
841  ASSERT_TRUE(entry);
842
843  // Pin the file.
844  FileError error = FILE_ERROR_FAILED;
845  file_system_->Pin(file_path,
846                    google_apis::test_util::CreateCopyResultCallback(&error));
847  content::RunAllBlockingPoolTasksUntilIdle();
848  EXPECT_EQ(FILE_ERROR_OK, error);
849
850  entry = GetResourceEntrySync(file_path);
851  ASSERT_TRUE(entry);
852  EXPECT_TRUE(entry->file_specific_info().cache_state().is_pinned());
853  EXPECT_TRUE(entry->file_specific_info().cache_state().is_present());
854
855  // Unpin the file.
856  error = FILE_ERROR_FAILED;
857  file_system_->Unpin(file_path,
858                      google_apis::test_util::CreateCopyResultCallback(&error));
859  content::RunAllBlockingPoolTasksUntilIdle();
860  EXPECT_EQ(FILE_ERROR_OK, error);
861
862  entry = GetResourceEntrySync(file_path);
863  ASSERT_TRUE(entry);
864  EXPECT_FALSE(entry->file_specific_info().cache_state().is_pinned());
865
866  // Pinned file gets synced and it results in entry state changes.
867  ASSERT_EQ(0u, mock_directory_observer_->changed_directories().size());
868  ASSERT_EQ(1u, mock_directory_observer_->changed_files().size());
869  EXPECT_EQ(1u,
870            mock_directory_observer_->changed_files().CountDirectory(
871                base::FilePath(FILE_PATH_LITERAL("drive/root"))));
872}
873
874TEST_F(FileSystemTest, PinAndUnpin_NotSynced) {
875  ASSERT_TRUE(LoadFullResourceList());
876
877  base::FilePath file_path(FILE_PATH_LITERAL("drive/root/File 1.txt"));
878
879  // Get the file info.
880  scoped_ptr<ResourceEntry> entry(GetResourceEntrySync(file_path));
881  ASSERT_TRUE(entry);
882
883  // Unpin the file just after pinning. File fetch should be cancelled.
884  FileError error_pin = FILE_ERROR_FAILED;
885  file_system_->Pin(
886      file_path,
887      google_apis::test_util::CreateCopyResultCallback(&error_pin));
888
889  FileError error_unpin = FILE_ERROR_FAILED;
890  file_system_->Unpin(
891      file_path,
892      google_apis::test_util::CreateCopyResultCallback(&error_unpin));
893
894  content::RunAllBlockingPoolTasksUntilIdle();
895  EXPECT_EQ(FILE_ERROR_OK, error_pin);
896  EXPECT_EQ(FILE_ERROR_OK, error_unpin);
897
898  // No cache file available because the sync was cancelled by Unpin().
899  entry = GetResourceEntrySync(file_path);
900  ASSERT_TRUE(entry);
901  EXPECT_FALSE(entry->file_specific_info().cache_state().is_present());
902}
903
904TEST_F(FileSystemTest, GetAvailableSpace) {
905  FileError error = FILE_ERROR_OK;
906  int64 bytes_total;
907  int64 bytes_used;
908  file_system_->GetAvailableSpace(
909      google_apis::test_util::CreateCopyResultCallback(
910          &error, &bytes_total, &bytes_used));
911  content::RunAllBlockingPoolTasksUntilIdle();
912  EXPECT_EQ(6789012345LL, bytes_used);
913  EXPECT_EQ(9876543210LL, bytes_total);
914}
915
916TEST_F(FileSystemTest, MarkCacheFileAsMountedAndUnmounted) {
917  ASSERT_TRUE(LoadFullResourceList());
918
919  base::FilePath file_in_root(FILE_PATH_LITERAL("drive/root/File 1.txt"));
920
921  // Make the file cached.
922  FileError error = FILE_ERROR_FAILED;
923  base::FilePath file_path;
924  scoped_ptr<ResourceEntry> entry;
925  file_system_->GetFile(
926      file_in_root,
927      google_apis::test_util::CreateCopyResultCallback(
928          &error, &file_path, &entry));
929  content::RunAllBlockingPoolTasksUntilIdle();
930  EXPECT_EQ(FILE_ERROR_OK, error);
931
932  // Test for mounting.
933  error = FILE_ERROR_FAILED;
934  file_path.clear();
935  file_system_->MarkCacheFileAsMounted(
936      file_in_root,
937      google_apis::test_util::CreateCopyResultCallback(&error, &file_path));
938  content::RunAllBlockingPoolTasksUntilIdle();
939  EXPECT_EQ(FILE_ERROR_OK, error);
940
941  // Cannot remove a cache entry while it's being mounted.
942  EXPECT_EQ(FILE_ERROR_IN_USE, cache_->Remove(entry->local_id()));
943
944  // Test for unmounting.
945  error = FILE_ERROR_FAILED;
946  file_system_->MarkCacheFileAsUnmounted(
947      file_path,
948      google_apis::test_util::CreateCopyResultCallback(&error));
949  content::RunAllBlockingPoolTasksUntilIdle();
950  EXPECT_EQ(FILE_ERROR_OK, error);
951
952  // Now able to remove the cache entry.
953  EXPECT_EQ(FILE_ERROR_OK, cache_->Remove(entry->local_id()));
954}
955
956TEST_F(FileSystemTest, GetShareUrl) {
957  ASSERT_TRUE(LoadFullResourceList());
958
959  const base::FilePath kFileInRoot(FILE_PATH_LITERAL("drive/root/File 1.txt"));
960  const GURL kEmbedOrigin("chrome-extension://test-id");
961
962  // Try to fetch the URL for the sharing dialog.
963  FileError error = FILE_ERROR_FAILED;
964  GURL share_url;
965  file_system_->GetShareUrl(
966      kFileInRoot,
967      kEmbedOrigin,
968      google_apis::test_util::CreateCopyResultCallback(&error, &share_url));
969  content::RunAllBlockingPoolTasksUntilIdle();
970
971  // Verify the share url to the sharing dialog.
972  EXPECT_EQ(FILE_ERROR_OK, error);
973  EXPECT_TRUE(share_url.is_valid());
974}
975
976}   // namespace drive
977