sync_client_unittest.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
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/sync_client.h"
6
7#include "base/file_util.h"
8#include "base/files/file_path.h"
9#include "base/files/scoped_temp_dir.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/prefs/testing_pref_service.h"
12#include "base/run_loop.h"
13#include "base/test/test_timeouts.h"
14#include "chrome/browser/chromeos/drive/change_list_loader.h"
15#include "chrome/browser/chromeos/drive/change_list_processor.h"
16#include "chrome/browser/chromeos/drive/drive.pb.h"
17#include "chrome/browser/chromeos/drive/fake_free_disk_space_getter.h"
18#include "chrome/browser/chromeos/drive/file_cache.h"
19#include "chrome/browser/chromeos/drive/file_system/operation_observer.h"
20#include "chrome/browser/chromeos/drive/job_scheduler.h"
21#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
22#include "chrome/browser/chromeos/drive/resource_metadata.h"
23#include "chrome/browser/chromeos/drive/test_util.h"
24#include "chrome/browser/drive/fake_drive_service.h"
25#include "chrome/browser/google_apis/test_util.h"
26#include "content/public/test/test_browser_thread_bundle.h"
27#include "testing/gtest/include/gtest/gtest.h"
28
29namespace drive {
30namespace internal {
31
32namespace {
33
34// The content of files initially stored in the cache.
35const char kLocalContent[] = "Hello!";
36
37// The content of files stored in the service.
38const char kRemoteContent[] = "World!";
39
40// SyncClientTestDriveService will return GDATA_CANCELLED when a request is
41// made with the specified resource ID.
42class SyncClientTestDriveService : public ::drive::FakeDriveService {
43 public:
44  // FakeDriveService override:
45  virtual google_apis::CancelCallback DownloadFile(
46      const base::FilePath& local_cache_path,
47      const std::string& resource_id,
48      const google_apis::DownloadActionCallback& download_action_callback,
49      const google_apis::GetContentCallback& get_content_callback,
50      const google_apis::ProgressCallback& progress_callback) OVERRIDE {
51    if (resource_id == resource_id_to_be_cancelled_) {
52      base::MessageLoopProxy::current()->PostTask(
53          FROM_HERE,
54          base::Bind(download_action_callback,
55                     google_apis::GDATA_CANCELLED,
56                     base::FilePath()));
57      return google_apis::CancelCallback();
58    }
59    return FakeDriveService::DownloadFile(local_cache_path,
60                                          resource_id,
61                                          download_action_callback,
62                                          get_content_callback,
63                                          progress_callback);
64  }
65
66  void set_resource_id_to_be_cancelled(const std::string& resource_id) {
67    resource_id_to_be_cancelled_ = resource_id;
68  }
69
70 private:
71  std::string resource_id_to_be_cancelled_;
72};
73
74class DummyOperationObserver : public file_system::OperationObserver {
75  // OperationObserver override:
76  virtual void OnDirectoryChangedByOperation(
77      const base::FilePath& path) OVERRIDE {}
78  virtual void OnCacheFileUploadNeededByOperation(
79      const std::string& local_id) OVERRIDE {}
80};
81
82}  // namespace
83
84class SyncClientTest : public testing::Test {
85 public:
86  virtual void SetUp() OVERRIDE {
87    ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
88
89    pref_service_.reset(new TestingPrefServiceSimple);
90    test_util::RegisterDrivePrefs(pref_service_->registry());
91
92    fake_network_change_notifier_.reset(
93        new test_util::FakeNetworkChangeNotifier);
94
95    drive_service_.reset(new SyncClientTestDriveService);
96    drive_service_->LoadResourceListForWapi("gdata/empty_feed.json");
97    drive_service_->LoadAccountMetadataForWapi(
98        "gdata/account_metadata.json");
99
100    scheduler_.reset(new JobScheduler(pref_service_.get(),
101                                      drive_service_.get(),
102                                      base::MessageLoopProxy::current().get()));
103
104    metadata_storage_.reset(new ResourceMetadataStorage(
105        temp_dir_.path(), base::MessageLoopProxy::current().get()));
106    ASSERT_TRUE(metadata_storage_->Initialize());
107
108    metadata_.reset(new internal::ResourceMetadata(
109        metadata_storage_.get(), base::MessageLoopProxy::current()));
110    ASSERT_EQ(FILE_ERROR_OK, metadata_->Initialize());
111
112    cache_.reset(new FileCache(metadata_storage_.get(),
113                               temp_dir_.path(),
114                               base::MessageLoopProxy::current().get(),
115                               NULL /* free_disk_space_getter */));
116    ASSERT_TRUE(cache_->Initialize());
117
118    ASSERT_NO_FATAL_FAILURE(SetUpTestData());
119
120    sync_client_.reset(new SyncClient(base::MessageLoopProxy::current().get(),
121                                      &observer_,
122                                      scheduler_.get(),
123                                      metadata_.get(),
124                                      cache_.get(),
125                                      temp_dir_.path()));
126
127    // Disable delaying so that DoSyncLoop() starts immediately.
128    sync_client_->set_delay_for_testing(base::TimeDelta::FromSeconds(0));
129  }
130
131  // Adds a file to the service root and |resource_ids_|.
132  void AddFileEntry(const std::string& title) {
133    google_apis::GDataErrorCode error = google_apis::GDATA_FILE_ERROR;
134    scoped_ptr<google_apis::ResourceEntry> entry;
135    drive_service_->AddNewFile(
136        "text/plain",
137        kRemoteContent,
138        drive_service_->GetRootResourceId(),
139        title,
140        false,  // shared_with_me
141        google_apis::test_util::CreateCopyResultCallback(&error, &entry));
142    base::RunLoop().RunUntilIdle();
143    ASSERT_EQ(google_apis::HTTP_CREATED, error);
144    ASSERT_TRUE(entry);
145    resource_ids_[title] = entry->resource_id();
146  }
147
148  // Sets up data for tests.
149  void SetUpTestData() {
150    // Prepare a temp file.
151    base::FilePath temp_file;
152    EXPECT_TRUE(file_util::CreateTemporaryFileInDir(temp_dir_.path(),
153                                                    &temp_file));
154    ASSERT_TRUE(google_apis::test_util::WriteStringToFile(temp_file,
155                                                          kLocalContent));
156
157    // Add file entries to the service.
158    ASSERT_NO_FATAL_FAILURE(AddFileEntry("foo"));
159    ASSERT_NO_FATAL_FAILURE(AddFileEntry("bar"));
160    ASSERT_NO_FATAL_FAILURE(AddFileEntry("baz"));
161    ASSERT_NO_FATAL_FAILURE(AddFileEntry("fetched"));
162    ASSERT_NO_FATAL_FAILURE(AddFileEntry("dirty"));
163
164    // Load data from the service to the metadata.
165    FileError error = FILE_ERROR_FAILED;
166    internal::ChangeListLoader change_list_loader(
167        base::MessageLoopProxy::current().get(),
168        metadata_.get(),
169        scheduler_.get(),
170        drive_service_.get());
171    change_list_loader.LoadIfNeeded(
172        DirectoryFetchInfo(),
173        google_apis::test_util::CreateCopyResultCallback(&error));
174    base::RunLoop().RunUntilIdle();
175    EXPECT_EQ(FILE_ERROR_OK, error);
176
177    // Prepare 3 pinned-but-not-present files.
178    EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("foo")));
179    EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("bar")));
180    EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("baz")));
181
182    // Prepare a pinned-and-fetched file.
183    const std::string md5_fetched = "md5";
184    EXPECT_EQ(FILE_ERROR_OK,
185              cache_->Store(GetLocalId("fetched"), md5_fetched,
186                            temp_file, FileCache::FILE_OPERATION_COPY));
187    EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("fetched")));
188
189    // Prepare a pinned-and-fetched-and-dirty file.
190    const std::string md5_dirty = "";  // Don't care.
191    EXPECT_EQ(FILE_ERROR_OK,
192              cache_->Store(GetLocalId("dirty"), md5_dirty,
193                            temp_file, FileCache::FILE_OPERATION_COPY));
194    EXPECT_EQ(FILE_ERROR_OK, cache_->Pin(GetLocalId("dirty")));
195    EXPECT_EQ(FILE_ERROR_OK, cache_->MarkDirty(GetLocalId("dirty")));
196
197  }
198
199 protected:
200  std::string GetLocalId(const std::string& title) {
201    EXPECT_EQ(1U, resource_ids_.count(title));
202    std::string local_id;
203    EXPECT_EQ(FILE_ERROR_OK,
204              metadata_->GetIdByResourceId(resource_ids_[title], &local_id));
205    return local_id;
206  }
207
208  content::TestBrowserThreadBundle thread_bundle_;
209  base::ScopedTempDir temp_dir_;
210  scoped_ptr<TestingPrefServiceSimple> pref_service_;
211  scoped_ptr<test_util::FakeNetworkChangeNotifier>
212      fake_network_change_notifier_;
213  scoped_ptr<SyncClientTestDriveService> drive_service_;
214  DummyOperationObserver observer_;
215  scoped_ptr<JobScheduler> scheduler_;
216  scoped_ptr<ResourceMetadataStorage,
217             test_util::DestroyHelperForTests> metadata_storage_;
218  scoped_ptr<ResourceMetadata, test_util::DestroyHelperForTests> metadata_;
219  scoped_ptr<FileCache, test_util::DestroyHelperForTests> cache_;
220  scoped_ptr<SyncClient> sync_client_;
221
222  std::map<std::string, std::string> resource_ids_;  // Name-to-id map.
223};
224
225TEST_F(SyncClientTest, StartProcessingBacklog) {
226  sync_client_->StartProcessingBacklog();
227  base::RunLoop().RunUntilIdle();
228
229  FileCacheEntry cache_entry;
230  // Pinned files get downloaded.
231  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
232  EXPECT_TRUE(cache_entry.is_present());
233
234  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("bar"), &cache_entry));
235  EXPECT_TRUE(cache_entry.is_present());
236
237  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("baz"), &cache_entry));
238  EXPECT_TRUE(cache_entry.is_present());
239
240  // Dirty file gets uploaded.
241  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("dirty"), &cache_entry));
242  EXPECT_FALSE(cache_entry.is_dirty());
243}
244
245TEST_F(SyncClientTest, AddFetchTask) {
246  sync_client_->AddFetchTask(GetLocalId("foo"));
247  base::RunLoop().RunUntilIdle();
248
249  FileCacheEntry cache_entry;
250  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
251  EXPECT_TRUE(cache_entry.is_present());
252}
253
254TEST_F(SyncClientTest, AddFetchTaskAndCancelled) {
255  // Trigger fetching of a file which results in cancellation.
256  drive_service_->set_resource_id_to_be_cancelled(resource_ids_["foo"]);
257  sync_client_->AddFetchTask(GetLocalId("foo"));
258  base::RunLoop().RunUntilIdle();
259
260  // The file should be unpinned if the user wants the download to be cancelled.
261  FileCacheEntry cache_entry;
262  EXPECT_FALSE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
263}
264
265TEST_F(SyncClientTest, RemoveFetchTask) {
266  sync_client_->AddFetchTask(GetLocalId("foo"));
267  sync_client_->AddFetchTask(GetLocalId("bar"));
268  sync_client_->AddFetchTask(GetLocalId("baz"));
269
270  sync_client_->RemoveFetchTask(GetLocalId("foo"));
271  sync_client_->RemoveFetchTask(GetLocalId("baz"));
272  base::RunLoop().RunUntilIdle();
273
274  // Only "bar" should be fetched.
275  FileCacheEntry cache_entry;
276  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
277  EXPECT_FALSE(cache_entry.is_present());
278
279  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("bar"), &cache_entry));
280  EXPECT_TRUE(cache_entry.is_present());
281
282  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("baz"), &cache_entry));
283  EXPECT_FALSE(cache_entry.is_present());
284
285}
286
287TEST_F(SyncClientTest, ExistingPinnedFiles) {
288  // Start checking the existing pinned files. This will collect the resource
289  // IDs of pinned files, with stale local cache files.
290  sync_client_->StartCheckingExistingPinnedFiles();
291  base::RunLoop().RunUntilIdle();
292
293  // "fetched" and "dirty" are the existing pinned files.
294  // The non-dirty one should be synced, but the dirty one should not.
295  base::FilePath cache_file;
296  std::string content;
297  EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(GetLocalId("fetched"), &cache_file));
298  EXPECT_TRUE(base::ReadFileToString(cache_file, &content));
299  EXPECT_EQ(kRemoteContent, content);
300  content.clear();
301
302  EXPECT_EQ(FILE_ERROR_OK, cache_->GetFile(GetLocalId("dirty"), &cache_file));
303  EXPECT_TRUE(base::ReadFileToString(cache_file, &content));
304  EXPECT_EQ(kLocalContent, content);
305}
306
307TEST_F(SyncClientTest, RetryOnDisconnection) {
308  // Let the service go down.
309  drive_service_->set_offline(true);
310  // Change the network connection state after some delay, to test that
311  // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly.
312  // Without this delay, JobScheduler will keep the jobs unrun and SyncClient
313  // will receive no error.
314  base::MessageLoopProxy::current()->PostDelayedTask(
315      FROM_HERE,
316      base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType,
317                 base::Unretained(fake_network_change_notifier_.get()),
318                 net::NetworkChangeNotifier::CONNECTION_NONE),
319      TestTimeouts::tiny_timeout());
320
321  // Try fetch and upload.
322  sync_client_->AddFetchTask(GetLocalId("foo"));
323  sync_client_->AddUploadTask(GetLocalId("dirty"));
324  base::RunLoop().RunUntilIdle();
325
326  // Not yet fetched nor uploaded.
327  FileCacheEntry cache_entry;
328  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
329  EXPECT_FALSE(cache_entry.is_present());
330  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("dirty"), &cache_entry));
331  EXPECT_TRUE(cache_entry.is_dirty());
332
333  // Switch to online.
334  fake_network_change_notifier_->SetConnectionType(
335      net::NetworkChangeNotifier::CONNECTION_WIFI);
336  drive_service_->set_offline(false);
337  base::RunLoop().RunUntilIdle();
338
339  // Fetched and uploaded.
340  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("foo"), &cache_entry));
341  EXPECT_TRUE(cache_entry.is_present());
342  EXPECT_TRUE(cache_->GetCacheEntry(GetLocalId("dirty"), &cache_entry));
343  EXPECT_FALSE(cache_entry.is_dirty());
344}
345
346}  // namespace internal
347}  // namespace drive
348