1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/chromeos/drive/change_list_loader.h"
6
7#include "base/files/scoped_temp_dir.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/prefs/testing_pref_service.h"
10#include "base/run_loop.h"
11#include "chrome/browser/chromeos/drive/change_list_loader_observer.h"
12#include "chrome/browser/chromeos/drive/change_list_processor.h"
13#include "chrome/browser/chromeos/drive/file_cache.h"
14#include "chrome/browser/chromeos/drive/file_system_util.h"
15#include "chrome/browser/chromeos/drive/job_scheduler.h"
16#include "chrome/browser/chromeos/drive/resource_metadata.h"
17#include "chrome/browser/chromeos/drive/test_util.h"
18#include "chrome/browser/drive/fake_drive_service.h"
19#include "content/public/test/test_browser_thread_bundle.h"
20#include "google_apis/drive/test_util.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23namespace drive {
24namespace internal {
25
26class TestChangeListLoaderObserver : public ChangeListLoaderObserver {
27 public:
28  explicit TestChangeListLoaderObserver(ChangeListLoader* loader)
29      : loader_(loader),
30        load_from_server_complete_count_(0),
31        initial_load_complete_count_(0) {
32    loader_->AddObserver(this);
33  }
34
35  virtual ~TestChangeListLoaderObserver() {
36    loader_->RemoveObserver(this);
37  }
38
39  const std::set<base::FilePath>& changed_directories() const {
40    return changed_directories_;
41  }
42  void clear_changed_directories() { changed_directories_.clear(); }
43
44  int load_from_server_complete_count() const {
45    return load_from_server_complete_count_;
46  }
47  int initial_load_complete_count() const {
48    return initial_load_complete_count_;
49  }
50
51  // ChageListObserver overrides:
52  virtual void OnDirectoryChanged(
53      const base::FilePath& directory_path) OVERRIDE {
54    changed_directories_.insert(directory_path);
55  }
56  virtual void OnLoadFromServerComplete() OVERRIDE {
57    ++load_from_server_complete_count_;
58  }
59  virtual void OnInitialLoadComplete() OVERRIDE {
60    ++initial_load_complete_count_;
61  }
62
63 private:
64  ChangeListLoader* loader_;
65  std::set<base::FilePath> changed_directories_;
66  int load_from_server_complete_count_;
67  int initial_load_complete_count_;
68
69  DISALLOW_COPY_AND_ASSIGN(TestChangeListLoaderObserver);
70};
71
72class ChangeListLoaderTest : public testing::Test {
73 protected:
74  virtual void SetUp() OVERRIDE {
75    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
76    pref_service_.reset(new TestingPrefServiceSimple);
77    test_util::RegisterDrivePrefs(pref_service_->registry());
78
79    drive_service_.reset(new FakeDriveService);
80    ASSERT_TRUE(drive_service_->LoadResourceListForWapi(
81        "gdata/root_feed.json"));
82    ASSERT_TRUE(drive_service_->LoadAccountMetadataForWapi(
83        "gdata/account_metadata.json"));
84
85    scheduler_.reset(new JobScheduler(pref_service_.get(),
86                                      drive_service_.get(),
87                                      base::MessageLoopProxy::current().get()));
88    metadata_storage_.reset(new ResourceMetadataStorage(
89        temp_dir_.path(), base::MessageLoopProxy::current().get()));
90    ASSERT_TRUE(metadata_storage_->Initialize());
91
92    metadata_.reset(new ResourceMetadata(
93        metadata_storage_.get(), base::MessageLoopProxy::current().get()));
94    ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
95
96    cache_.reset(new FileCache(metadata_storage_.get(),
97                               temp_dir_.path(),
98                               base::MessageLoopProxy::current().get(),
99                               NULL /* free_disk_space_getter */));
100    ASSERT_TRUE(cache_->Initialize());
101
102    change_list_loader_.reset(
103        new ChangeListLoader(base::MessageLoopProxy::current().get(),
104                             metadata_.get(),
105                             scheduler_.get(),
106                             drive_service_.get()));
107  }
108
109  // Adds a new file to the root directory of the service.
110  scoped_ptr<google_apis::ResourceEntry> AddNewFile(const std::string& title) {
111    google_apis::GDataErrorCode error = google_apis::GDATA_FILE_ERROR;
112    scoped_ptr<google_apis::ResourceEntry> entry;
113    drive_service_->AddNewFile(
114        "text/plain",
115        "content text",
116        drive_service_->GetRootResourceId(),
117        title,
118        false,  // shared_with_me
119        google_apis::test_util::CreateCopyResultCallback(&error, &entry));
120    base::RunLoop().RunUntilIdle();
121    EXPECT_EQ(google_apis::HTTP_CREATED, error);
122    return entry.Pass();
123  }
124
125  content::TestBrowserThreadBundle thread_bundle_;
126  base::ScopedTempDir temp_dir_;
127  scoped_ptr<TestingPrefServiceSimple> pref_service_;
128  scoped_ptr<FakeDriveService> drive_service_;
129  scoped_ptr<JobScheduler> scheduler_;
130  scoped_ptr<ResourceMetadataStorage,
131             test_util::DestroyHelperForTests> metadata_storage_;
132  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
133  scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
134  scoped_ptr<ChangeListLoader> change_list_loader_;
135};
136
137TEST_F(ChangeListLoaderTest, LoadIfNeeded) {
138  EXPECT_FALSE(change_list_loader_->IsRefreshing());
139
140  // Start initial load.
141  TestChangeListLoaderObserver observer(change_list_loader_.get());
142
143  EXPECT_EQ(0, drive_service_->about_resource_load_count());
144
145  FileError error = FILE_ERROR_FAILED;
146  change_list_loader_->LoadIfNeeded(
147      DirectoryFetchInfo(),
148      google_apis::test_util::CreateCopyResultCallback(&error));
149  EXPECT_TRUE(change_list_loader_->IsRefreshing());
150  base::RunLoop().RunUntilIdle();
151  EXPECT_EQ(FILE_ERROR_OK, error);
152
153  EXPECT_FALSE(change_list_loader_->IsRefreshing());
154  EXPECT_LT(0, metadata_->GetLargestChangestamp());
155  EXPECT_EQ(1, drive_service_->resource_list_load_count());
156  EXPECT_EQ(1, drive_service_->about_resource_load_count());
157  EXPECT_EQ(1, observer.initial_load_complete_count());
158  EXPECT_EQ(1, observer.load_from_server_complete_count());
159  EXPECT_TRUE(observer.changed_directories().empty());
160
161  base::FilePath file_path =
162      util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
163  ResourceEntry entry;
164  EXPECT_EQ(FILE_ERROR_OK,
165            metadata_->GetResourceEntryByPath(file_path, &entry));
166
167  // Reload. This should result in no-op.
168  int64 previous_changestamp = metadata_->GetLargestChangestamp();
169  int previous_resource_list_load_count =
170      drive_service_->resource_list_load_count();
171  change_list_loader_->LoadIfNeeded(
172      DirectoryFetchInfo(),
173      google_apis::test_util::CreateCopyResultCallback(&error));
174  EXPECT_FALSE(change_list_loader_->IsRefreshing());
175  base::RunLoop().RunUntilIdle();
176  EXPECT_EQ(FILE_ERROR_OK, error);
177
178  EXPECT_FALSE(change_list_loader_->IsRefreshing());
179  // Cached value is used.
180  EXPECT_EQ(1, drive_service_->about_resource_load_count());
181  EXPECT_EQ(previous_changestamp, metadata_->GetLargestChangestamp());
182  EXPECT_EQ(previous_resource_list_load_count,
183            drive_service_->resource_list_load_count());
184}
185
186TEST_F(ChangeListLoaderTest, LoadIfNeeded_LocalMetadataAvailable) {
187  // Prepare metadata.
188  FileError error = FILE_ERROR_FAILED;
189  change_list_loader_->LoadIfNeeded(
190      DirectoryFetchInfo(),
191      google_apis::test_util::CreateCopyResultCallback(&error));
192  base::RunLoop().RunUntilIdle();
193  EXPECT_EQ(FILE_ERROR_OK, error);
194
195  // Reset loader.
196  change_list_loader_.reset(
197      new ChangeListLoader(base::MessageLoopProxy::current().get(),
198                           metadata_.get(),
199                           scheduler_.get(),
200                           drive_service_.get()));
201
202  // Add a file to the service.
203  scoped_ptr<google_apis::ResourceEntry> gdata_entry = AddNewFile("New File");
204  ASSERT_TRUE(gdata_entry);
205
206  // Start loading. Because local metadata is available, the load results in
207  // returning FILE_ERROR_OK without fetching full list of resources.
208  const int previous_resource_list_load_count =
209      drive_service_->resource_list_load_count();
210  TestChangeListLoaderObserver observer(change_list_loader_.get());
211
212  change_list_loader_->LoadIfNeeded(
213      DirectoryFetchInfo(),
214      google_apis::test_util::CreateCopyResultCallback(&error));
215  EXPECT_TRUE(change_list_loader_->IsRefreshing());
216  base::RunLoop().RunUntilIdle();
217  EXPECT_EQ(FILE_ERROR_OK, error);
218  EXPECT_EQ(previous_resource_list_load_count,
219            drive_service_->resource_list_load_count());
220  EXPECT_EQ(1, observer.initial_load_complete_count());
221
222  // Update should be checked by LoadIfNeeded().
223  EXPECT_EQ(drive_service_->largest_changestamp(),
224            metadata_->GetLargestChangestamp());
225  EXPECT_EQ(1, drive_service_->change_list_load_count());
226  EXPECT_EQ(1, observer.load_from_server_complete_count());
227  EXPECT_EQ(1U, observer.changed_directories().count(
228      util::GetDriveMyDriveRootPath()));
229
230  base::FilePath file_path =
231      util::GetDriveMyDriveRootPath().AppendASCII(gdata_entry->title());
232  ResourceEntry entry;
233  EXPECT_EQ(FILE_ERROR_OK,
234            metadata_->GetResourceEntryByPath(file_path, &entry));
235}
236
237TEST_F(ChangeListLoaderTest, LoadIfNeeded_MyDrive) {
238  TestChangeListLoaderObserver observer(change_list_loader_.get());
239
240  // Emulate the slowness of GetAllResourceList().
241  drive_service_->set_never_return_all_resource_list(true);
242
243  // Load grand root.
244  FileError error = FILE_ERROR_FAILED;
245  change_list_loader_->LoadIfNeeded(
246      DirectoryFetchInfo(util::kDriveGrandRootLocalId, 0),
247      google_apis::test_util::CreateCopyResultCallback(&error));
248  base::RunLoop().RunUntilIdle();
249  EXPECT_EQ(FILE_ERROR_OK, error);
250  EXPECT_EQ(1U, observer.changed_directories().count(
251      util::GetDriveGrandRootPath()));
252  observer.clear_changed_directories();
253
254  // GetAllResourceList() was called.
255  EXPECT_EQ(1, drive_service_->blocked_resource_list_load_count());
256
257  // My Drive is present in the local metadata, but its child is not.
258  ResourceEntry entry;
259  EXPECT_EQ(FILE_ERROR_OK,
260            metadata_->GetResourceEntryByPath(util::GetDriveMyDriveRootPath(),
261                                              &entry));
262  const int64 mydrive_changestamp =
263      entry.directory_specific_info().changestamp();
264
265  base::FilePath file_path =
266      util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt");
267  EXPECT_EQ(FILE_ERROR_NOT_FOUND,
268            metadata_->GetResourceEntryByPath(file_path, &entry));
269
270  // Load My Drive.
271  change_list_loader_->LoadIfNeeded(
272      DirectoryFetchInfo(drive_service_->GetRootResourceId(),
273                         mydrive_changestamp),
274      google_apis::test_util::CreateCopyResultCallback(&error));
275  base::RunLoop().RunUntilIdle();
276  EXPECT_EQ(FILE_ERROR_OK, error);
277  EXPECT_EQ(1U, observer.changed_directories().count(
278      util::GetDriveMyDriveRootPath()));
279
280  // Now the file is present.
281  EXPECT_EQ(FILE_ERROR_OK,
282            metadata_->GetResourceEntryByPath(file_path, &entry));
283}
284
285TEST_F(ChangeListLoaderTest, LoadIfNeeded_NewDirectories) {
286  // Make local metadata up to date.
287  FileError error = FILE_ERROR_FAILED;
288  change_list_loader_->LoadIfNeeded(
289      DirectoryFetchInfo(),
290      google_apis::test_util::CreateCopyResultCallback(&error));
291  base::RunLoop().RunUntilIdle();
292  EXPECT_EQ(FILE_ERROR_OK, error);
293
294  // Add a new file.
295  scoped_ptr<google_apis::ResourceEntry> file = AddNewFile("New File");
296  ASSERT_TRUE(file);
297
298  // Emulate the slowness of GetAllResourceList().
299  drive_service_->set_never_return_all_resource_list(true);
300
301  // Enter refreshing state.
302  FileError check_for_updates_error = FILE_ERROR_FAILED;
303  change_list_loader_->CheckForUpdates(
304      google_apis::test_util::CreateCopyResultCallback(
305          &check_for_updates_error));
306  EXPECT_TRUE(change_list_loader_->IsRefreshing());
307
308  // Load My Drive.
309  TestChangeListLoaderObserver observer(change_list_loader_.get());
310  change_list_loader_->LoadIfNeeded(
311      DirectoryFetchInfo(drive_service_->GetRootResourceId(),
312                         metadata_->GetLargestChangestamp()),
313      google_apis::test_util::CreateCopyResultCallback(&error));
314  base::RunLoop().RunUntilIdle();
315  EXPECT_EQ(FILE_ERROR_OK, error);
316  EXPECT_EQ(1U, observer.changed_directories().count(
317      util::GetDriveMyDriveRootPath()));
318
319  // The new file is present in the local metadata.
320  base::FilePath file_path =
321      util::GetDriveMyDriveRootPath().AppendASCII(file->title());
322  ResourceEntry entry;
323  EXPECT_EQ(FILE_ERROR_OK,
324            metadata_->GetResourceEntryByPath(file_path, &entry));
325}
326
327TEST_F(ChangeListLoaderTest, LoadIfNeeded_MultipleCalls) {
328  TestChangeListLoaderObserver observer(change_list_loader_.get());
329
330  // Load grand root.
331  FileError error = FILE_ERROR_FAILED;
332  change_list_loader_->LoadIfNeeded(
333      DirectoryFetchInfo(util::kDriveGrandRootLocalId, 0),
334      google_apis::test_util::CreateCopyResultCallback(&error));
335
336  // Load grand root again without waiting for the result.
337  FileError error2 = FILE_ERROR_FAILED;
338  change_list_loader_->LoadIfNeeded(
339      DirectoryFetchInfo(util::kDriveGrandRootLocalId, 0),
340      google_apis::test_util::CreateCopyResultCallback(&error2));
341  base::RunLoop().RunUntilIdle();
342
343  // Callback is called for each method call.
344  EXPECT_EQ(FILE_ERROR_OK, error);
345  EXPECT_EQ(FILE_ERROR_OK, error2);
346
347  // No duplicated resource list load and observer events.
348  EXPECT_EQ(1, drive_service_->resource_list_load_count());
349  EXPECT_EQ(1, observer.initial_load_complete_count());
350  EXPECT_EQ(1, observer.load_from_server_complete_count());
351}
352
353TEST_F(ChangeListLoaderTest, CheckForUpdates) {
354  // CheckForUpdates() results in no-op before load.
355  FileError check_for_updates_error = FILE_ERROR_FAILED;
356  change_list_loader_->CheckForUpdates(
357      google_apis::test_util::CreateCopyResultCallback(
358          &check_for_updates_error));
359  EXPECT_FALSE(change_list_loader_->IsRefreshing());
360  base::RunLoop().RunUntilIdle();
361  EXPECT_EQ(FILE_ERROR_FAILED,
362            check_for_updates_error);  // Callback was not run.
363  EXPECT_EQ(0, metadata_->GetLargestChangestamp());
364  EXPECT_EQ(0, drive_service_->resource_list_load_count());
365  EXPECT_EQ(0, drive_service_->about_resource_load_count());
366
367  // Start initial load.
368  FileError load_error = FILE_ERROR_FAILED;
369  change_list_loader_->LoadIfNeeded(
370      DirectoryFetchInfo(),
371      google_apis::test_util::CreateCopyResultCallback(&load_error));
372  EXPECT_TRUE(change_list_loader_->IsRefreshing());
373
374  // CheckForUpdates() while loading.
375  change_list_loader_->CheckForUpdates(
376      google_apis::test_util::CreateCopyResultCallback(
377          &check_for_updates_error));
378
379  base::RunLoop().RunUntilIdle();
380  EXPECT_FALSE(change_list_loader_->IsRefreshing());
381  EXPECT_EQ(FILE_ERROR_OK, load_error);
382  EXPECT_EQ(FILE_ERROR_OK, check_for_updates_error);
383  EXPECT_LT(0, metadata_->GetLargestChangestamp());
384  EXPECT_EQ(1, drive_service_->resource_list_load_count());
385
386  int64 previous_changestamp = metadata_->GetLargestChangestamp();
387  // CheckForUpdates() results in no update.
388  change_list_loader_->CheckForUpdates(
389      google_apis::test_util::CreateCopyResultCallback(
390          &check_for_updates_error));
391  EXPECT_TRUE(change_list_loader_->IsRefreshing());
392  base::RunLoop().RunUntilIdle();
393  EXPECT_FALSE(change_list_loader_->IsRefreshing());
394  EXPECT_EQ(previous_changestamp, metadata_->GetLargestChangestamp());
395
396  // Add a file to the service.
397  scoped_ptr<google_apis::ResourceEntry> gdata_entry = AddNewFile("New File");
398  ASSERT_TRUE(gdata_entry);
399
400  // CheckForUpdates() results in update.
401  TestChangeListLoaderObserver observer(change_list_loader_.get());
402  change_list_loader_->CheckForUpdates(
403      google_apis::test_util::CreateCopyResultCallback(
404          &check_for_updates_error));
405  EXPECT_TRUE(change_list_loader_->IsRefreshing());
406  base::RunLoop().RunUntilIdle();
407  EXPECT_FALSE(change_list_loader_->IsRefreshing());
408  EXPECT_LT(previous_changestamp, metadata_->GetLargestChangestamp());
409  EXPECT_EQ(1, observer.load_from_server_complete_count());
410  EXPECT_EQ(1U, observer.changed_directories().count(
411      util::GetDriveMyDriveRootPath()));
412
413  // The new file is found in the local metadata.
414  base::FilePath new_file_path =
415      util::GetDriveMyDriveRootPath().AppendASCII(gdata_entry->title());
416  ResourceEntry entry;
417  EXPECT_EQ(FILE_ERROR_OK,
418            metadata_->GetResourceEntryByPath(new_file_path, &entry));
419}
420
421}  // namespace internal
422}  // namespace drive
423