1 // Copyright (c) 2014 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 "base/location.h"
6 #include "base/run_loop.h"
7 #include "base/single_thread_task_runner.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "base/synchronization/waitable_event.h"
10 #include "base/thread_task_runner_handle.h"
11 #include "base/time/time.h"
12 #include "chrome/browser/sync/profile_sync_service.h"
13 #include "chrome/browser/sync/test/integration/bookmarks_helper.h"
14 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
15 #include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
16 #include "chrome/browser/sync/test/integration/sync_test.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "sync/syncable/directory.h"
19 #include "sync/test/directory_backing_store_corruption_testing.h"
22 using content::BrowserThread
;
23 using sync_integration_test_util::AwaitCommitActivityCompletion
;
24 using syncer::syncable::corruption_testing::kNumEntriesRequiredForCorruption
;
25 using syncer::syncable::corruption_testing::CorruptDatabase
;
27 class SingleClientDirectorySyncTest
: public SyncTest
{
29 SingleClientDirectorySyncTest() : SyncTest(SINGLE_CLIENT
) {}
30 ~SingleClientDirectorySyncTest() override
{}
33 DISALLOW_COPY_AND_ASSIGN(SingleClientDirectorySyncTest
);
36 void SignalEvent(base::WaitableEvent
* e
) {
40 bool WaitForExistingTasksOnLoop(base::MessageLoop
* loop
) {
41 base::WaitableEvent
e(true, false);
42 loop
->task_runner()->PostTask(FROM_HERE
, base::Bind(&SignalEvent
, &e
));
43 // Timeout stolen from StatusChangeChecker::GetTimeoutDuration().
44 return e
.TimedWait(base::TimeDelta::FromSeconds(45));
47 // A status change checker that waits for an unrecoverable sync error to occur.
48 class SyncUnrecoverableErrorChecker
: public SingleClientStatusChangeChecker
{
50 explicit SyncUnrecoverableErrorChecker(ProfileSyncService
* service
)
51 : SingleClientStatusChangeChecker(service
) {}
53 bool IsExitConditionSatisfied() override
{
54 return service()->HasUnrecoverableError();
57 std::string
GetDebugMessage() const override
{
58 return "Sync Unrecoverable Error";
62 IN_PROC_BROWSER_TEST_F(SingleClientDirectorySyncTest
,
63 StopThenDisableDeletesDirectory
) {
64 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
65 ProfileSyncService
* sync_service
= GetSyncService(0);
66 base::FilePath directory_path
= sync_service
->GetDirectoryPathForTest();
67 ASSERT_TRUE(base::DirectoryExists(directory_path
));
68 sync_service
->RequestStop(ProfileSyncService::CLEAR_DATA
);
70 // Wait for StartupController::StartUp()'s tasks to finish.
71 base::RunLoop run_loop
;
72 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE
,
73 run_loop
.QuitClosure());
75 // Wait for the directory deletion to finish.
76 base::MessageLoop
* sync_loop
= sync_service
->GetSyncLoopForTest();
77 ASSERT_TRUE(WaitForExistingTasksOnLoop(sync_loop
));
79 ASSERT_FALSE(base::DirectoryExists(directory_path
));
82 // Verify that when the sync directory's backing store becomes corrupted, we
83 // trigger an unrecoverable error and delete the database.
85 // If this test fails, see the definition of kNumEntriesRequiredForCorruption
86 // for one possible cause.
87 IN_PROC_BROWSER_TEST_F(SingleClientDirectorySyncTest
,
88 DeleteDirectoryWhenCorrupted
) {
89 ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
90 // Sync and wait for syncing to complete.
91 ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
92 ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
93 ASSERT_TRUE(bookmarks_helper::ModelMatchesVerifier(0));
95 // Flush the directory to the backing store and wait until the flush
97 ProfileSyncService
* sync_service
= GetSyncService(0);
98 sync_service
->FlushDirectory();
99 base::MessageLoop
* sync_loop
= sync_service
->GetSyncLoopForTest();
100 ASSERT_TRUE(WaitForExistingTasksOnLoop(sync_loop
));
102 // Now corrupt the database.
103 const base::FilePath
directory_path(sync_service
->GetDirectoryPathForTest());
104 const base::FilePath
sync_db(directory_path
.Append(
105 syncer::syncable::Directory::kSyncDatabaseFilename
));
106 ASSERT_TRUE(CorruptDatabase(sync_db
));
108 // Write a bunch of bookmarks and flush the directory to ensure sync notices
109 // the corruption. The key here is to force sync to actually write a lot of
110 // data to its DB so it will see the corruption we introduced above.
114 "gooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
115 "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
116 "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
117 "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
118 "oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo"
119 "oooooooooooooooooooogle.com");
120 const bookmarks::BookmarkNode
* top
= bookmarks_helper::AddFolder(
121 0, bookmarks_helper::GetOtherNode(0), 0, "top");
122 for (int i
= 0; i
< kNumEntriesRequiredForCorruption
; ++i
) {
124 bookmarks_helper::AddURL(0, top
, 0, base::Int64ToString(i
), url
));
126 sync_service
->FlushDirectory();
128 // Wait for an unrecoverable error to occur.
129 SyncUnrecoverableErrorChecker
checker(sync_service
);
131 ASSERT_TRUE(!checker
.TimedOut());
132 ASSERT_TRUE(sync_service
->HasUnrecoverableError());
134 // Wait until the sync loop has processed any existing tasks and see that the
135 // directory no longer exists.
136 ASSERT_TRUE(WaitForExistingTasksOnLoop(sync_loop
));
137 ASSERT_FALSE(base::DirectoryExists(directory_path
));