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