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.
5 #include "chrome/browser/chromeos/drive/sync_client.h"
7 #include "base/files/file_path.h"
8 #include "base/files/file_util.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_change.h"
19 #include "chrome/browser/chromeos/drive/file_system/move_operation.h"
20 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
21 #include "chrome/browser/chromeos/drive/file_system/remove_operation.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/resource_entry_conversion.h"
25 #include "chrome/browser/chromeos/drive/resource_metadata.h"
26 #include "chrome/browser/chromeos/drive/test_util.h"
27 #include "chrome/browser/drive/event_logger.h"
28 #include "chrome/browser/drive/fake_drive_service.h"
29 #include "content/public/test/test_browser_thread_bundle.h"
30 #include "google_apis/drive/drive_api_parser.h"
31 #include "google_apis/drive/test_util.h"
32 #include "testing/gtest/include/gtest/gtest.h"
39 // The content of files initially stored in the cache.
40 const char kLocalContent
[] = "Hello!";
42 // The content of files stored in the service.
43 const char kRemoteContent
[] = "World!";
45 // SyncClientTestDriveService will return DRIVE_CANCELLED when a request is
46 // made with the specified resource ID.
47 class SyncClientTestDriveService
: public ::drive::FakeDriveService
{
49 SyncClientTestDriveService() : download_file_count_(0) {}
51 // FakeDriveService override:
52 google_apis::CancelCallback
DownloadFile(
53 const base::FilePath
& local_cache_path
,
54 const std::string
& resource_id
,
55 const google_apis::DownloadActionCallback
& download_action_callback
,
56 const google_apis::GetContentCallback
& get_content_callback
,
57 const google_apis::ProgressCallback
& progress_callback
) override
{
58 ++download_file_count_
;
59 if (resource_id
== resource_id_to_be_cancelled_
) {
60 base::MessageLoopProxy::current()->PostTask(
62 base::Bind(download_action_callback
,
63 google_apis::DRIVE_CANCELLED
,
65 return google_apis::CancelCallback();
67 if (resource_id
== resource_id_to_be_paused_
) {
68 paused_action_
= base::Bind(download_action_callback
,
69 google_apis::DRIVE_OTHER_ERROR
,
71 return google_apis::CancelCallback();
73 return FakeDriveService::DownloadFile(local_cache_path
,
75 download_action_callback
,
80 int download_file_count() const { return download_file_count_
; }
82 void set_resource_id_to_be_cancelled(const std::string
& resource_id
) {
83 resource_id_to_be_cancelled_
= resource_id
;
86 void set_resource_id_to_be_paused(const std::string
& resource_id
) {
87 resource_id_to_be_paused_
= resource_id
;
90 const base::Closure
& paused_action() const { return paused_action_
; }
93 int download_file_count_
;
94 std::string resource_id_to_be_cancelled_
;
95 std::string resource_id_to_be_paused_
;
96 base::Closure paused_action_
;
101 class SyncClientTest
: public testing::Test
{
103 void SetUp() override
{
104 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
106 pref_service_
.reset(new TestingPrefServiceSimple
);
107 test_util::RegisterDrivePrefs(pref_service_
->registry());
109 fake_network_change_notifier_
.reset(
110 new test_util::FakeNetworkChangeNotifier
);
112 logger_
.reset(new EventLogger
);
114 drive_service_
.reset(new SyncClientTestDriveService
);
116 scheduler_
.reset(new JobScheduler(pref_service_
.get(),
118 drive_service_
.get(),
119 base::MessageLoopProxy::current().get()));
121 metadata_storage_
.reset(new ResourceMetadataStorage(
122 temp_dir_
.path(), base::MessageLoopProxy::current().get()));
123 ASSERT_TRUE(metadata_storage_
->Initialize());
125 cache_
.reset(new FileCache(metadata_storage_
.get(),
127 base::MessageLoopProxy::current().get(),
128 NULL
/* free_disk_space_getter */));
129 ASSERT_TRUE(cache_
->Initialize());
131 metadata_
.reset(new internal::ResourceMetadata(
132 metadata_storage_
.get(), cache_
.get(),
133 base::MessageLoopProxy::current()));
134 ASSERT_EQ(FILE_ERROR_OK
, metadata_
->Initialize());
136 about_resource_loader_
.reset(new AboutResourceLoader(scheduler_
.get()));
137 loader_controller_
.reset(new LoaderController
);
138 change_list_loader_
.reset(new ChangeListLoader(
140 base::MessageLoopProxy::current().get(),
143 about_resource_loader_
.get(),
144 loader_controller_
.get()));
145 ASSERT_NO_FATAL_FAILURE(SetUpTestData());
147 sync_client_
.reset(new SyncClient(base::MessageLoopProxy::current().get(),
152 loader_controller_
.get(),
155 // Disable delaying so that DoSyncLoop() starts immediately.
156 sync_client_
->set_delay_for_testing(base::TimeDelta::FromSeconds(0));
159 // Adds a file to the service root and |resource_ids_|.
160 void AddFileEntry(const std::string
& title
) {
161 google_apis::DriveApiErrorCode error
= google_apis::DRIVE_FILE_ERROR
;
162 scoped_ptr
<google_apis::FileResource
> entry
;
163 drive_service_
->AddNewFile(
166 drive_service_
->GetRootResourceId(),
168 false, // shared_with_me
169 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
170 base::RunLoop().RunUntilIdle();
171 ASSERT_EQ(google_apis::HTTP_CREATED
, error
);
173 resource_ids_
[title
] = entry
->file_id();
176 // Sets up data for tests.
177 void SetUpTestData() {
178 // Prepare a temp file.
179 base::FilePath temp_file
;
180 EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_
.path(), &temp_file
));
181 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(temp_file
,
184 // Add file entries to the service.
185 ASSERT_NO_FATAL_FAILURE(AddFileEntry("foo"));
186 ASSERT_NO_FATAL_FAILURE(AddFileEntry("bar"));
187 ASSERT_NO_FATAL_FAILURE(AddFileEntry("baz"));
188 ASSERT_NO_FATAL_FAILURE(AddFileEntry("fetched"));
189 ASSERT_NO_FATAL_FAILURE(AddFileEntry("dirty"));
190 ASSERT_NO_FATAL_FAILURE(AddFileEntry("removed"));
191 ASSERT_NO_FATAL_FAILURE(AddFileEntry("moved"));
193 // Load data from the service to the metadata.
194 FileError error
= FILE_ERROR_FAILED
;
195 change_list_loader_
->LoadIfNeeded(
196 google_apis::test_util::CreateCopyResultCallback(&error
));
197 base::RunLoop().RunUntilIdle();
198 EXPECT_EQ(FILE_ERROR_OK
, error
);
200 // Prepare 3 pinned-but-not-present files.
201 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("foo")));
202 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("bar")));
203 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("baz")));
205 // Prepare a pinned-and-fetched file.
206 const std::string md5_fetched
= "md5";
207 EXPECT_EQ(FILE_ERROR_OK
,
208 cache_
->Store(GetLocalId("fetched"), md5_fetched
,
209 temp_file
, FileCache::FILE_OPERATION_COPY
));
210 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("fetched")));
212 // Prepare a pinned-and-fetched-and-dirty file.
213 EXPECT_EQ(FILE_ERROR_OK
,
214 cache_
->Store(GetLocalId("dirty"), std::string(),
215 temp_file
, FileCache::FILE_OPERATION_COPY
));
216 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("dirty")));
218 // Prepare a removed file.
219 file_system::RemoveOperation
remove_operation(
220 base::MessageLoopProxy::current().get(), &delegate_
, metadata_
.get(),
222 remove_operation
.Remove(
223 util::GetDriveMyDriveRootPath().AppendASCII("removed"),
224 false, // is_recursive
225 google_apis::test_util::CreateCopyResultCallback(&error
));
226 base::RunLoop().RunUntilIdle();
227 EXPECT_EQ(FILE_ERROR_OK
, error
);
229 // Prepare a moved file.
230 file_system::MoveOperation
move_operation(
231 base::MessageLoopProxy::current().get(), &delegate_
, metadata_
.get());
233 util::GetDriveMyDriveRootPath().AppendASCII("moved"),
234 util::GetDriveMyDriveRootPath().AppendASCII("moved_new_title"),
235 google_apis::test_util::CreateCopyResultCallback(&error
));
236 base::RunLoop().RunUntilIdle();
237 EXPECT_EQ(FILE_ERROR_OK
, error
);
241 std::string
GetLocalId(const std::string
& title
) {
242 EXPECT_EQ(1U, resource_ids_
.count(title
));
243 std::string local_id
;
244 EXPECT_EQ(FILE_ERROR_OK
,
245 metadata_
->GetIdByResourceId(resource_ids_
[title
], &local_id
));
249 content::TestBrowserThreadBundle thread_bundle_
;
250 base::ScopedTempDir temp_dir_
;
251 scoped_ptr
<TestingPrefServiceSimple
> pref_service_
;
252 scoped_ptr
<test_util::FakeNetworkChangeNotifier
>
253 fake_network_change_notifier_
;
254 scoped_ptr
<EventLogger
> logger_
;
255 scoped_ptr
<SyncClientTestDriveService
> drive_service_
;
256 file_system::OperationDelegate delegate_
;
257 scoped_ptr
<JobScheduler
> scheduler_
;
258 scoped_ptr
<ResourceMetadataStorage
,
259 test_util::DestroyHelperForTests
> metadata_storage_
;
260 scoped_ptr
<FileCache
, test_util::DestroyHelperForTests
> cache_
;
261 scoped_ptr
<ResourceMetadata
, test_util::DestroyHelperForTests
> metadata_
;
262 scoped_ptr
<AboutResourceLoader
> about_resource_loader_
;
263 scoped_ptr
<LoaderController
> loader_controller_
;
264 scoped_ptr
<ChangeListLoader
> change_list_loader_
;
265 scoped_ptr
<SyncClient
> sync_client_
;
267 std::map
<std::string
, std::string
> resource_ids_
; // Name-to-id map.
270 TEST_F(SyncClientTest
, StartProcessingBacklog
) {
271 sync_client_
->StartProcessingBacklog();
272 base::RunLoop().RunUntilIdle();
275 // Pinned files get downloaded.
276 EXPECT_EQ(FILE_ERROR_OK
,
277 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
278 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
280 EXPECT_EQ(FILE_ERROR_OK
,
281 metadata_
->GetResourceEntryById(GetLocalId("bar"), &entry
));
282 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
284 EXPECT_EQ(FILE_ERROR_OK
,
285 metadata_
->GetResourceEntryById(GetLocalId("baz"), &entry
));
286 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
288 // Dirty file gets uploaded.
289 EXPECT_EQ(FILE_ERROR_OK
,
290 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
291 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_dirty());
293 // Removed entry is not found.
294 google_apis::DriveApiErrorCode status
= google_apis::DRIVE_OTHER_ERROR
;
295 scoped_ptr
<google_apis::FileResource
> server_entry
;
296 drive_service_
->GetFileResource(
297 resource_ids_
["removed"],
298 google_apis::test_util::CreateCopyResultCallback(&status
, &server_entry
));
299 base::RunLoop().RunUntilIdle();
300 EXPECT_EQ(google_apis::HTTP_SUCCESS
, status
);
301 ASSERT_TRUE(server_entry
);
302 EXPECT_TRUE(server_entry
->labels().is_trashed());
304 // Moved entry was moved.
305 status
= google_apis::DRIVE_OTHER_ERROR
;
306 drive_service_
->GetFileResource(
307 resource_ids_
["moved"],
308 google_apis::test_util::CreateCopyResultCallback(&status
, &server_entry
));
309 base::RunLoop().RunUntilIdle();
310 EXPECT_EQ(google_apis::HTTP_SUCCESS
, status
);
311 ASSERT_TRUE(server_entry
);
312 EXPECT_EQ("moved_new_title", server_entry
->title());
315 TEST_F(SyncClientTest
, AddFetchTask
) {
316 sync_client_
->AddFetchTask(GetLocalId("foo"));
317 base::RunLoop().RunUntilIdle();
320 EXPECT_EQ(FILE_ERROR_OK
,
321 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
322 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
325 TEST_F(SyncClientTest
, AddFetchTaskAndCancelled
) {
326 // Trigger fetching of a file which results in cancellation.
327 drive_service_
->set_resource_id_to_be_cancelled(resource_ids_
["foo"]);
328 sync_client_
->AddFetchTask(GetLocalId("foo"));
329 base::RunLoop().RunUntilIdle();
331 // The file should be unpinned if the user wants the download to be cancelled.
333 EXPECT_EQ(FILE_ERROR_OK
,
334 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
335 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_pinned());
338 TEST_F(SyncClientTest
, RemoveFetchTask
) {
339 sync_client_
->AddFetchTask(GetLocalId("foo"));
340 sync_client_
->AddFetchTask(GetLocalId("bar"));
341 sync_client_
->AddFetchTask(GetLocalId("baz"));
343 sync_client_
->RemoveFetchTask(GetLocalId("foo"));
344 sync_client_
->RemoveFetchTask(GetLocalId("baz"));
345 base::RunLoop().RunUntilIdle();
347 // Only "bar" should be fetched.
349 EXPECT_EQ(FILE_ERROR_OK
,
350 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
351 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
353 EXPECT_EQ(FILE_ERROR_OK
,
354 metadata_
->GetResourceEntryById(GetLocalId("bar"), &entry
));
355 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
357 EXPECT_EQ(FILE_ERROR_OK
,
358 metadata_
->GetResourceEntryById(GetLocalId("baz"), &entry
));
359 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
363 TEST_F(SyncClientTest
, ExistingPinnedFiles
) {
364 // Start checking the existing pinned files. This will collect the resource
365 // IDs of pinned files, with stale local cache files.
366 sync_client_
->StartCheckingExistingPinnedFiles();
367 base::RunLoop().RunUntilIdle();
369 // "fetched" and "dirty" are the existing pinned files.
370 // The non-dirty one should be synced, but the dirty one should not.
371 base::FilePath cache_file
;
373 EXPECT_EQ(FILE_ERROR_OK
, cache_
->GetFile(GetLocalId("fetched"), &cache_file
));
374 EXPECT_TRUE(base::ReadFileToString(cache_file
, &content
));
375 EXPECT_EQ(kRemoteContent
, content
);
378 EXPECT_EQ(FILE_ERROR_OK
, cache_
->GetFile(GetLocalId("dirty"), &cache_file
));
379 EXPECT_TRUE(base::ReadFileToString(cache_file
, &content
));
380 EXPECT_EQ(kLocalContent
, content
);
383 TEST_F(SyncClientTest
, RetryOnDisconnection
) {
384 // Let the service go down.
385 drive_service_
->set_offline(true);
386 // Change the network connection state after some delay, to test that
387 // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly.
388 // Without this delay, JobScheduler will keep the jobs unrun and SyncClient
389 // will receive no error.
390 base::MessageLoopProxy::current()->PostDelayedTask(
392 base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType
,
393 base::Unretained(fake_network_change_notifier_
.get()),
394 net::NetworkChangeNotifier::CONNECTION_NONE
),
395 TestTimeouts::tiny_timeout());
397 // Try fetch and upload.
398 sync_client_
->AddFetchTask(GetLocalId("foo"));
399 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
),
400 GetLocalId("dirty"));
401 base::RunLoop().RunUntilIdle();
403 // Not yet fetched nor uploaded.
405 EXPECT_EQ(FILE_ERROR_OK
,
406 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
407 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
408 EXPECT_EQ(FILE_ERROR_OK
,
409 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
410 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_dirty());
413 fake_network_change_notifier_
->SetConnectionType(
414 net::NetworkChangeNotifier::CONNECTION_WIFI
);
415 drive_service_
->set_offline(false);
416 base::RunLoop().RunUntilIdle();
418 // Fetched and uploaded.
419 EXPECT_EQ(FILE_ERROR_OK
,
420 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
421 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
422 EXPECT_EQ(FILE_ERROR_OK
,
423 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
424 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_dirty());
427 TEST_F(SyncClientTest
, ScheduleRerun
) {
428 // Add a fetch task for "foo", this should result in being paused.
429 drive_service_
->set_resource_id_to_be_paused(resource_ids_
["foo"]);
430 sync_client_
->AddFetchTask(GetLocalId("foo"));
431 base::RunLoop().RunUntilIdle();
433 // While the first task is paused, add a task again.
434 // This results in scheduling rerun of the task.
435 sync_client_
->AddFetchTask(GetLocalId("foo"));
436 base::RunLoop().RunUntilIdle();
438 // Resume the paused task.
439 drive_service_
->set_resource_id_to_be_paused(std::string());
440 ASSERT_FALSE(drive_service_
->paused_action().is_null());
441 drive_service_
->paused_action().Run();
442 base::RunLoop().RunUntilIdle();
444 // Task should be run twice.
445 EXPECT_EQ(2, drive_service_
->download_file_count());
448 TEST_F(SyncClientTest
, Dependencies
) {
449 // Create directories locally.
450 const base::FilePath
kPath1(FILE_PATH_LITERAL("drive/root/dir1"));
451 const base::FilePath kPath2
= kPath1
.AppendASCII("dir2");
453 ResourceEntry parent
;
454 EXPECT_EQ(FILE_ERROR_OK
,
455 metadata_
->GetResourceEntryByPath(kPath1
.DirName(), &parent
));
457 ResourceEntry entry1
;
458 entry1
.set_parent_local_id(parent
.local_id());
459 entry1
.set_title(kPath1
.BaseName().AsUTF8Unsafe());
460 entry1
.mutable_file_info()->set_is_directory(true);
461 entry1
.set_metadata_edit_state(ResourceEntry::DIRTY
);
462 std::string local_id1
;
463 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry1
, &local_id1
));
465 ResourceEntry entry2
;
466 entry2
.set_parent_local_id(local_id1
);
467 entry2
.set_title(kPath2
.BaseName().AsUTF8Unsafe());
468 entry2
.mutable_file_info()->set_is_directory(true);
469 entry2
.set_metadata_edit_state(ResourceEntry::DIRTY
);
470 std::string local_id2
;
471 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry2
, &local_id2
));
473 // Start syncing the child first.
474 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id2
);
475 // Start syncing the parent later.
476 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id1
);
477 base::RunLoop().RunUntilIdle();
479 // Both entries are synced.
480 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->GetResourceEntryById(local_id1
, &entry1
));
481 EXPECT_EQ(ResourceEntry::CLEAN
, entry1
.metadata_edit_state());
482 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->GetResourceEntryById(local_id2
, &entry2
));
483 EXPECT_EQ(ResourceEntry::CLEAN
, entry2
.metadata_edit_state());
486 TEST_F(SyncClientTest
, WaitForUpdateTaskToComplete
) {
487 // Create a directory locally.
488 const base::FilePath
kPath(FILE_PATH_LITERAL("drive/root/dir1"));
490 ResourceEntry parent
;
491 EXPECT_EQ(FILE_ERROR_OK
,
492 metadata_
->GetResourceEntryByPath(kPath
.DirName(), &parent
));
495 entry
.set_parent_local_id(parent
.local_id());
496 entry
.set_title(kPath
.BaseName().AsUTF8Unsafe());
497 entry
.mutable_file_info()->set_is_directory(true);
498 entry
.set_metadata_edit_state(ResourceEntry::DIRTY
);
499 std::string local_id
;
500 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry
, &local_id
));
502 // Sync task is not yet avialable.
503 FileError error
= FILE_ERROR_FAILED
;
504 EXPECT_FALSE(sync_client_
->WaitForUpdateTaskToComplete(
505 local_id
, google_apis::test_util::CreateCopyResultCallback(&error
)));
507 // Start syncing the directory and wait for it to complete.
508 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id
);
510 EXPECT_TRUE(sync_client_
->WaitForUpdateTaskToComplete(
511 local_id
, google_apis::test_util::CreateCopyResultCallback(&error
)));
513 base::RunLoop().RunUntilIdle();
515 // The callback is called.
516 EXPECT_EQ(FILE_ERROR_OK
, error
);
519 } // namespace internal