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/single_thread_task_runner.h"
14 #include "base/test/test_timeouts.h"
15 #include "base/thread_task_runner_handle.h"
16 #include "chrome/browser/chromeos/drive/change_list_loader.h"
17 #include "chrome/browser/chromeos/drive/file_system/move_operation.h"
18 #include "chrome/browser/chromeos/drive/file_system/operation_delegate.h"
19 #include "chrome/browser/chromeos/drive/file_system/remove_operation.h"
20 #include "components/drive/drive.pb.h"
21 #include "components/drive/drive_test_util.h"
22 #include "components/drive/event_logger.h"
23 #include "components/drive/fake_free_disk_space_getter.h"
24 #include "components/drive/file_cache.h"
25 #include "components/drive/file_change.h"
26 #include "components/drive/file_system_core_util.h"
27 #include "components/drive/job_scheduler.h"
28 #include "components/drive/resource_entry_conversion.h"
29 #include "components/drive/resource_metadata.h"
30 #include "components/drive/service/fake_drive_service.h"
31 #include "content/public/test/test_browser_thread_bundle.h"
32 #include "google_apis/drive/drive_api_parser.h"
33 #include "google_apis/drive/test_util.h"
34 #include "testing/gtest/include/gtest/gtest.h"
41 // The content of files initially stored in the cache.
42 const char kLocalContent
[] = "Hello!";
44 // The content of files stored in the service.
45 const char kRemoteContent
[] = "World!";
47 // SyncClientTestDriveService will return DRIVE_CANCELLED when a request is
48 // made with the specified resource ID.
49 class SyncClientTestDriveService
: public ::drive::FakeDriveService
{
51 SyncClientTestDriveService() : download_file_count_(0) {}
53 // FakeDriveService override:
54 google_apis::CancelCallback
DownloadFile(
55 const base::FilePath
& local_cache_path
,
56 const std::string
& resource_id
,
57 const google_apis::DownloadActionCallback
& download_action_callback
,
58 const google_apis::GetContentCallback
& get_content_callback
,
59 const google_apis::ProgressCallback
& progress_callback
) override
{
60 ++download_file_count_
;
61 if (resource_id
== resource_id_to_be_cancelled_
) {
62 base::ThreadTaskRunnerHandle::Get()->PostTask(
64 base::Bind(download_action_callback
,
65 google_apis::DRIVE_CANCELLED
,
67 return google_apis::CancelCallback();
69 if (resource_id
== resource_id_to_be_paused_
) {
70 paused_action_
= base::Bind(download_action_callback
,
71 google_apis::DRIVE_OTHER_ERROR
,
73 return google_apis::CancelCallback();
75 return FakeDriveService::DownloadFile(local_cache_path
,
77 download_action_callback
,
82 int download_file_count() const { return download_file_count_
; }
84 void set_resource_id_to_be_cancelled(const std::string
& resource_id
) {
85 resource_id_to_be_cancelled_
= resource_id
;
88 void set_resource_id_to_be_paused(const std::string
& resource_id
) {
89 resource_id_to_be_paused_
= resource_id
;
92 const base::Closure
& paused_action() const { return paused_action_
; }
95 int download_file_count_
;
96 std::string resource_id_to_be_cancelled_
;
97 std::string resource_id_to_be_paused_
;
98 base::Closure paused_action_
;
103 class SyncClientTest
: public testing::Test
{
105 void SetUp() override
{
106 ASSERT_TRUE(temp_dir_
.CreateUniqueTempDir());
108 pref_service_
.reset(new TestingPrefServiceSimple
);
109 test_util::RegisterDrivePrefs(pref_service_
->registry());
111 fake_network_change_notifier_
.reset(
112 new test_util::FakeNetworkChangeNotifier
);
114 logger_
.reset(new EventLogger
);
116 drive_service_
.reset(new SyncClientTestDriveService
);
118 scheduler_
.reset(new JobScheduler(
121 drive_service_
.get(),
122 base::ThreadTaskRunnerHandle::Get().get()));
124 metadata_storage_
.reset(new ResourceMetadataStorage(
125 temp_dir_
.path(), base::ThreadTaskRunnerHandle::Get().get()));
126 ASSERT_TRUE(metadata_storage_
->Initialize());
128 cache_
.reset(new FileCache(metadata_storage_
.get(),
130 base::ThreadTaskRunnerHandle::Get().get(),
131 NULL
/* free_disk_space_getter */));
132 ASSERT_TRUE(cache_
->Initialize());
134 metadata_
.reset(new internal::ResourceMetadata(
135 metadata_storage_
.get(), cache_
.get(),
136 base::ThreadTaskRunnerHandle::Get()));
137 ASSERT_EQ(FILE_ERROR_OK
, metadata_
->Initialize());
139 about_resource_loader_
.reset(new AboutResourceLoader(scheduler_
.get()));
140 loader_controller_
.reset(new LoaderController
);
141 change_list_loader_
.reset(new ChangeListLoader(
143 base::ThreadTaskRunnerHandle::Get().get(),
146 about_resource_loader_
.get(),
147 loader_controller_
.get()));
148 ASSERT_NO_FATAL_FAILURE(SetUpTestData());
150 sync_client_
.reset(new SyncClient(base::ThreadTaskRunnerHandle::Get().get(),
155 loader_controller_
.get(),
158 // Disable delaying so that DoSyncLoop() starts immediately.
159 sync_client_
->set_delay_for_testing(base::TimeDelta::FromSeconds(0));
162 // Adds a file to the service root and |resource_ids_|.
163 void AddFileEntry(const std::string
& title
) {
164 google_apis::DriveApiErrorCode error
= google_apis::DRIVE_FILE_ERROR
;
165 scoped_ptr
<google_apis::FileResource
> entry
;
166 drive_service_
->AddNewFile(
169 drive_service_
->GetRootResourceId(),
171 false, // shared_with_me
172 google_apis::test_util::CreateCopyResultCallback(&error
, &entry
));
173 base::RunLoop().RunUntilIdle();
174 ASSERT_EQ(google_apis::HTTP_CREATED
, error
);
176 resource_ids_
[title
] = entry
->file_id();
179 // Sets up data for tests.
180 void SetUpTestData() {
181 // Prepare a temp file.
182 base::FilePath temp_file
;
183 EXPECT_TRUE(base::CreateTemporaryFileInDir(temp_dir_
.path(), &temp_file
));
184 ASSERT_TRUE(google_apis::test_util::WriteStringToFile(temp_file
,
187 // Add file entries to the service.
188 ASSERT_NO_FATAL_FAILURE(AddFileEntry("foo"));
189 ASSERT_NO_FATAL_FAILURE(AddFileEntry("bar"));
190 ASSERT_NO_FATAL_FAILURE(AddFileEntry("baz"));
191 ASSERT_NO_FATAL_FAILURE(AddFileEntry("fetched"));
192 ASSERT_NO_FATAL_FAILURE(AddFileEntry("dirty"));
193 ASSERT_NO_FATAL_FAILURE(AddFileEntry("removed"));
194 ASSERT_NO_FATAL_FAILURE(AddFileEntry("moved"));
196 // Load data from the service to the metadata.
197 FileError error
= FILE_ERROR_FAILED
;
198 change_list_loader_
->LoadIfNeeded(
199 google_apis::test_util::CreateCopyResultCallback(&error
));
200 base::RunLoop().RunUntilIdle();
201 EXPECT_EQ(FILE_ERROR_OK
, error
);
203 // Prepare 3 pinned-but-not-present files.
204 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("foo")));
205 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("bar")));
206 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("baz")));
208 // Prepare a pinned-and-fetched file.
209 const std::string md5_fetched
= "md5";
210 EXPECT_EQ(FILE_ERROR_OK
,
211 cache_
->Store(GetLocalId("fetched"), md5_fetched
,
212 temp_file
, FileCache::FILE_OPERATION_COPY
));
213 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("fetched")));
215 // Prepare a pinned-and-fetched-and-dirty file.
216 EXPECT_EQ(FILE_ERROR_OK
,
217 cache_
->Store(GetLocalId("dirty"), std::string(),
218 temp_file
, FileCache::FILE_OPERATION_COPY
));
219 EXPECT_EQ(FILE_ERROR_OK
, cache_
->Pin(GetLocalId("dirty")));
221 // Prepare a removed file.
222 file_system::RemoveOperation
remove_operation(
223 base::ThreadTaskRunnerHandle::Get().get(), &delegate_
, metadata_
.get(),
225 remove_operation
.Remove(
226 util::GetDriveMyDriveRootPath().AppendASCII("removed"),
227 false, // is_recursive
228 google_apis::test_util::CreateCopyResultCallback(&error
));
229 base::RunLoop().RunUntilIdle();
230 EXPECT_EQ(FILE_ERROR_OK
, error
);
232 // Prepare a moved file.
233 file_system::MoveOperation
move_operation(
234 base::ThreadTaskRunnerHandle::Get().get(), &delegate_
, metadata_
.get());
236 util::GetDriveMyDriveRootPath().AppendASCII("moved"),
237 util::GetDriveMyDriveRootPath().AppendASCII("moved_new_title"),
238 google_apis::test_util::CreateCopyResultCallback(&error
));
239 base::RunLoop().RunUntilIdle();
240 EXPECT_EQ(FILE_ERROR_OK
, error
);
244 std::string
GetLocalId(const std::string
& title
) {
245 EXPECT_EQ(1U, resource_ids_
.count(title
));
246 std::string local_id
;
247 EXPECT_EQ(FILE_ERROR_OK
,
248 metadata_
->GetIdByResourceId(resource_ids_
[title
], &local_id
));
252 content::TestBrowserThreadBundle thread_bundle_
;
253 base::ScopedTempDir temp_dir_
;
254 scoped_ptr
<TestingPrefServiceSimple
> pref_service_
;
255 scoped_ptr
<test_util::FakeNetworkChangeNotifier
>
256 fake_network_change_notifier_
;
257 scoped_ptr
<EventLogger
> logger_
;
258 scoped_ptr
<SyncClientTestDriveService
> drive_service_
;
259 file_system::OperationDelegate delegate_
;
260 scoped_ptr
<JobScheduler
> scheduler_
;
261 scoped_ptr
<ResourceMetadataStorage
,
262 test_util::DestroyHelperForTests
> metadata_storage_
;
263 scoped_ptr
<FileCache
, test_util::DestroyHelperForTests
> cache_
;
264 scoped_ptr
<ResourceMetadata
, test_util::DestroyHelperForTests
> metadata_
;
265 scoped_ptr
<AboutResourceLoader
> about_resource_loader_
;
266 scoped_ptr
<LoaderController
> loader_controller_
;
267 scoped_ptr
<ChangeListLoader
> change_list_loader_
;
268 scoped_ptr
<SyncClient
> sync_client_
;
270 std::map
<std::string
, std::string
> resource_ids_
; // Name-to-id map.
273 TEST_F(SyncClientTest
, StartProcessingBacklog
) {
274 sync_client_
->StartProcessingBacklog();
275 base::RunLoop().RunUntilIdle();
278 // Pinned files get downloaded.
279 EXPECT_EQ(FILE_ERROR_OK
,
280 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
281 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
283 EXPECT_EQ(FILE_ERROR_OK
,
284 metadata_
->GetResourceEntryById(GetLocalId("bar"), &entry
));
285 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
287 EXPECT_EQ(FILE_ERROR_OK
,
288 metadata_
->GetResourceEntryById(GetLocalId("baz"), &entry
));
289 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
291 // Dirty file gets uploaded.
292 EXPECT_EQ(FILE_ERROR_OK
,
293 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
294 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_dirty());
296 // Removed entry is not found.
297 google_apis::DriveApiErrorCode status
= google_apis::DRIVE_OTHER_ERROR
;
298 scoped_ptr
<google_apis::FileResource
> server_entry
;
299 drive_service_
->GetFileResource(
300 resource_ids_
["removed"],
301 google_apis::test_util::CreateCopyResultCallback(&status
, &server_entry
));
302 base::RunLoop().RunUntilIdle();
303 EXPECT_EQ(google_apis::HTTP_SUCCESS
, status
);
304 ASSERT_TRUE(server_entry
);
305 EXPECT_TRUE(server_entry
->labels().is_trashed());
307 // Moved entry was moved.
308 status
= google_apis::DRIVE_OTHER_ERROR
;
309 drive_service_
->GetFileResource(
310 resource_ids_
["moved"],
311 google_apis::test_util::CreateCopyResultCallback(&status
, &server_entry
));
312 base::RunLoop().RunUntilIdle();
313 EXPECT_EQ(google_apis::HTTP_SUCCESS
, status
);
314 ASSERT_TRUE(server_entry
);
315 EXPECT_EQ("moved_new_title", server_entry
->title());
318 TEST_F(SyncClientTest
, AddFetchTask
) {
319 sync_client_
->AddFetchTask(GetLocalId("foo"));
320 base::RunLoop().RunUntilIdle();
323 EXPECT_EQ(FILE_ERROR_OK
,
324 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
325 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
328 TEST_F(SyncClientTest
, AddFetchTaskAndCancelled
) {
329 // Trigger fetching of a file which results in cancellation.
330 drive_service_
->set_resource_id_to_be_cancelled(resource_ids_
["foo"]);
331 sync_client_
->AddFetchTask(GetLocalId("foo"));
332 base::RunLoop().RunUntilIdle();
334 // The file should be unpinned if the user wants the download to be cancelled.
336 EXPECT_EQ(FILE_ERROR_OK
,
337 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
338 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_pinned());
341 TEST_F(SyncClientTest
, RemoveFetchTask
) {
342 sync_client_
->AddFetchTask(GetLocalId("foo"));
343 sync_client_
->AddFetchTask(GetLocalId("bar"));
344 sync_client_
->AddFetchTask(GetLocalId("baz"));
346 sync_client_
->RemoveFetchTask(GetLocalId("foo"));
347 sync_client_
->RemoveFetchTask(GetLocalId("baz"));
348 base::RunLoop().RunUntilIdle();
350 // Only "bar" should be fetched.
352 EXPECT_EQ(FILE_ERROR_OK
,
353 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
354 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
356 EXPECT_EQ(FILE_ERROR_OK
,
357 metadata_
->GetResourceEntryById(GetLocalId("bar"), &entry
));
358 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
360 EXPECT_EQ(FILE_ERROR_OK
,
361 metadata_
->GetResourceEntryById(GetLocalId("baz"), &entry
));
362 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
366 TEST_F(SyncClientTest
, ExistingPinnedFiles
) {
367 // Start checking the existing pinned files. This will collect the resource
368 // IDs of pinned files, with stale local cache files.
369 sync_client_
->StartCheckingExistingPinnedFiles();
370 base::RunLoop().RunUntilIdle();
372 // "fetched" and "dirty" are the existing pinned files.
373 // The non-dirty one should be synced, but the dirty one should not.
374 base::FilePath cache_file
;
376 EXPECT_EQ(FILE_ERROR_OK
, cache_
->GetFile(GetLocalId("fetched"), &cache_file
));
377 EXPECT_TRUE(base::ReadFileToString(cache_file
, &content
));
378 EXPECT_EQ(kRemoteContent
, content
);
381 EXPECT_EQ(FILE_ERROR_OK
, cache_
->GetFile(GetLocalId("dirty"), &cache_file
));
382 EXPECT_TRUE(base::ReadFileToString(cache_file
, &content
));
383 EXPECT_EQ(kLocalContent
, content
);
386 TEST_F(SyncClientTest
, RetryOnDisconnection
) {
387 // Let the service go down.
388 drive_service_
->set_offline(true);
389 // Change the network connection state after some delay, to test that
390 // FILE_ERROR_NO_CONNECTION is handled by SyncClient correctly.
391 // Without this delay, JobScheduler will keep the jobs unrun and SyncClient
392 // will receive no error.
393 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
395 base::Bind(&test_util::FakeNetworkChangeNotifier::SetConnectionType
,
396 base::Unretained(fake_network_change_notifier_
.get()),
397 net::NetworkChangeNotifier::CONNECTION_NONE
),
398 TestTimeouts::tiny_timeout());
400 // Try fetch and upload.
401 sync_client_
->AddFetchTask(GetLocalId("foo"));
402 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
),
403 GetLocalId("dirty"));
404 base::RunLoop().RunUntilIdle();
406 // Not yet fetched nor uploaded.
408 EXPECT_EQ(FILE_ERROR_OK
,
409 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
410 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_present());
411 EXPECT_EQ(FILE_ERROR_OK
,
412 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
413 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_dirty());
416 fake_network_change_notifier_
->SetConnectionType(
417 net::NetworkChangeNotifier::CONNECTION_WIFI
);
418 drive_service_
->set_offline(false);
419 base::RunLoop().RunUntilIdle();
421 // Fetched and uploaded.
422 EXPECT_EQ(FILE_ERROR_OK
,
423 metadata_
->GetResourceEntryById(GetLocalId("foo"), &entry
));
424 EXPECT_TRUE(entry
.file_specific_info().cache_state().is_present());
425 EXPECT_EQ(FILE_ERROR_OK
,
426 metadata_
->GetResourceEntryById(GetLocalId("dirty"), &entry
));
427 EXPECT_FALSE(entry
.file_specific_info().cache_state().is_dirty());
430 TEST_F(SyncClientTest
, ScheduleRerun
) {
431 // Add a fetch task for "foo", this should result in being paused.
432 drive_service_
->set_resource_id_to_be_paused(resource_ids_
["foo"]);
433 sync_client_
->AddFetchTask(GetLocalId("foo"));
434 base::RunLoop().RunUntilIdle();
436 // While the first task is paused, add a task again.
437 // This results in scheduling rerun of the task.
438 sync_client_
->AddFetchTask(GetLocalId("foo"));
439 base::RunLoop().RunUntilIdle();
441 // Resume the paused task.
442 drive_service_
->set_resource_id_to_be_paused(std::string());
443 ASSERT_FALSE(drive_service_
->paused_action().is_null());
444 drive_service_
->paused_action().Run();
445 base::RunLoop().RunUntilIdle();
447 // Task should be run twice.
448 EXPECT_EQ(2, drive_service_
->download_file_count());
451 TEST_F(SyncClientTest
, Dependencies
) {
452 // Create directories locally.
453 const base::FilePath
kPath1(FILE_PATH_LITERAL("drive/root/dir1"));
454 const base::FilePath kPath2
= kPath1
.AppendASCII("dir2");
456 ResourceEntry parent
;
457 EXPECT_EQ(FILE_ERROR_OK
,
458 metadata_
->GetResourceEntryByPath(kPath1
.DirName(), &parent
));
460 ResourceEntry entry1
;
461 entry1
.set_parent_local_id(parent
.local_id());
462 entry1
.set_title(kPath1
.BaseName().AsUTF8Unsafe());
463 entry1
.mutable_file_info()->set_is_directory(true);
464 entry1
.set_metadata_edit_state(ResourceEntry::DIRTY
);
465 std::string local_id1
;
466 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry1
, &local_id1
));
468 ResourceEntry entry2
;
469 entry2
.set_parent_local_id(local_id1
);
470 entry2
.set_title(kPath2
.BaseName().AsUTF8Unsafe());
471 entry2
.mutable_file_info()->set_is_directory(true);
472 entry2
.set_metadata_edit_state(ResourceEntry::DIRTY
);
473 std::string local_id2
;
474 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry2
, &local_id2
));
476 // Start syncing the child first.
477 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id2
);
478 // Start syncing the parent later.
479 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id1
);
480 base::RunLoop().RunUntilIdle();
482 // Both entries are synced.
483 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->GetResourceEntryById(local_id1
, &entry1
));
484 EXPECT_EQ(ResourceEntry::CLEAN
, entry1
.metadata_edit_state());
485 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->GetResourceEntryById(local_id2
, &entry2
));
486 EXPECT_EQ(ResourceEntry::CLEAN
, entry2
.metadata_edit_state());
489 TEST_F(SyncClientTest
, WaitForUpdateTaskToComplete
) {
490 // Create a directory locally.
491 const base::FilePath
kPath(FILE_PATH_LITERAL("drive/root/dir1"));
493 ResourceEntry parent
;
494 EXPECT_EQ(FILE_ERROR_OK
,
495 metadata_
->GetResourceEntryByPath(kPath
.DirName(), &parent
));
498 entry
.set_parent_local_id(parent
.local_id());
499 entry
.set_title(kPath
.BaseName().AsUTF8Unsafe());
500 entry
.mutable_file_info()->set_is_directory(true);
501 entry
.set_metadata_edit_state(ResourceEntry::DIRTY
);
502 std::string local_id
;
503 EXPECT_EQ(FILE_ERROR_OK
, metadata_
->AddEntry(entry
, &local_id
));
505 // Sync task is not yet avialable.
506 FileError error
= FILE_ERROR_FAILED
;
507 EXPECT_FALSE(sync_client_
->WaitForUpdateTaskToComplete(
508 local_id
, google_apis::test_util::CreateCopyResultCallback(&error
)));
510 // Start syncing the directory and wait for it to complete.
511 sync_client_
->AddUpdateTask(ClientContext(USER_INITIATED
), local_id
);
513 EXPECT_TRUE(sync_client_
->WaitForUpdateTaskToComplete(
514 local_id
, google_apis::test_util::CreateCopyResultCallback(&error
)));
516 base::RunLoop().RunUntilIdle();
518 // The callback is called.
519 EXPECT_EQ(FILE_ERROR_OK
, error
);
522 } // namespace internal