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