1// Copyright 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.
4
5#include "base/command_line.h"
6#include "base/files/file_util.h"
7#include "base/message_loop/message_loop.h"
8#include "base/prefs/pref_service.h"
9#include "base/run_loop.h"
10#include "base/test/test_timeouts.h"
11#include "chrome/browser/browsing_data/browsing_data_remover.h"
12#include "chrome/browser/profiles/profile.h"
13#include "chrome/browser/sync/profile_sync_service.h"
14#include "chrome/browser/sync/test/integration/bookmarks_helper.h"
15#include "chrome/browser/sync/test/integration/preferences_helper.h"
16#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
17#include "chrome/browser/sync/test/integration/sync_test.h"
18#include "chrome/common/chrome_switches.h"
19#include "chrome/common/pref_names.h"
20#include "components/bookmarks/browser/bookmark_model.h"
21#include "sync/internal_api/public/util/sync_db_util.h"
22#include "sync/test/fake_server/fake_server_verifier.h"
23#include "sync/util/time.h"
24
25using bookmarks_helper::AddFolder;
26using bookmarks_helper::AddURL;
27using bookmarks_helper::GetOtherNode;
28using bookmarks_helper::ModelMatchesVerifier;
29using bookmarks_helper::Move;
30using bookmarks_helper::Remove;
31using sync_integration_test_util::AwaitCommitActivityCompletion;
32
33namespace {
34const char kUrl1[] = "http://www.google.com";
35const char kUrl2[] = "http://map.google.com";
36const char kUrl3[] = "http://plus.google.com";
37}  // anonymous namespace
38
39class SingleClientBackupRollbackTest : public SyncTest {
40 public:
41  SingleClientBackupRollbackTest() : SyncTest(SINGLE_CLIENT) {}
42  virtual ~SingleClientBackupRollbackTest() {}
43
44  void DisableBackup() {
45    CommandLine::ForCurrentProcess()->AppendSwitch(
46          switches::kSyncDisableBackup);
47  }
48
49  void DisableRollback() {
50    CommandLine::ForCurrentProcess()->AppendSwitch(
51          switches::kSyncDisableRollback);
52  }
53
54  base::Time GetBackupDbLastModified() {
55    base::RunLoop run_loop;
56
57    base::Time backup_time;
58    syncer::CheckSyncDbLastModifiedTime(
59        GetProfile(0)->GetPath().Append(FILE_PATH_LITERAL("Sync Data Backup")),
60        base::MessageLoopProxy::current(),
61        base::Bind(&SingleClientBackupRollbackTest::CheckDbCallback,
62                   base::Unretained(this), &backup_time));
63    base::MessageLoopProxy::current()->PostTask(
64        FROM_HERE, run_loop.QuitClosure());
65    run_loop.Run();
66    return backup_time;
67  }
68
69 private:
70  void CheckDbCallback(base::Time* time_out, base::Time time_in) {
71    *time_out = syncer::ProtoTimeToTime(syncer::TimeToProtoTime(time_in));
72  }
73
74  DISALLOW_COPY_AND_ASSIGN(SingleClientBackupRollbackTest);
75};
76
77// Waits until the ProfileSyncService's backend is in IDLE mode.
78class SyncBackendStoppedChecker : public ProfileSyncServiceBase::Observer {
79 public:
80  explicit SyncBackendStoppedChecker(ProfileSyncService* service)
81      : pss_(service),
82        timeout_(TestTimeouts::action_max_timeout()),
83        done_(false) {}
84
85  virtual void OnStateChanged() OVERRIDE {
86    if (ProfileSyncService::IDLE == pss_->backend_mode()) {
87      done_ = true;
88      run_loop_.Quit();
89    }
90  }
91
92  bool Wait() {
93    pss_->AddObserver(this);
94    if (ProfileSyncService::IDLE == pss_->backend_mode())
95      return true;
96    base::MessageLoop::current()->PostDelayedTask(
97        FROM_HERE,
98        run_loop_.QuitClosure(),
99        timeout_);
100    run_loop_.Run();
101    pss_->RemoveObserver(this);
102    return done_;
103  }
104
105 private:
106
107  ProfileSyncService* const pss_;
108  const base::TimeDelta timeout_;
109  base::RunLoop run_loop_;
110  bool done_;
111};
112
113// Waits until a rollback finishes.
114class SyncRollbackChecker : public ProfileSyncServiceBase::Observer,
115                            public BrowsingDataRemover::Observer {
116 public:
117  explicit SyncRollbackChecker(ProfileSyncService* service)
118      : pss_(service),
119        timeout_(TestTimeouts::action_max_timeout()),
120        rollback_started_(false),
121        clear_done_(false) {}
122
123  // ProfileSyncServiceBase::Observer implementation.
124  virtual void OnStateChanged() OVERRIDE {
125    if (ProfileSyncService::ROLLBACK == pss_->backend_mode()) {
126      rollback_started_ = true;
127      if (clear_done_)
128        run_loop_.Quit();
129    }
130  }
131
132  // BrowsingDataRemoverObserver::Observer implementation.
133  virtual void OnBrowsingDataRemoverDone() OVERRIDE {
134    clear_done_ = true;
135    if (rollback_started_) {
136      run_loop_.Quit();
137    }
138  }
139
140  bool Wait() {
141    pss_->AddObserver(this);
142    pss_->SetBrowsingDataRemoverObserverForTesting(this);
143    base::MessageLoop::current()->PostDelayedTask(
144        FROM_HERE,
145        run_loop_.QuitClosure(),
146        timeout_);
147    run_loop_.Run();
148    pss_->RemoveObserver(this);
149    return rollback_started_ && clear_done_;
150  }
151
152  ProfileSyncService* const pss_;
153  const base::TimeDelta timeout_;
154  base::RunLoop run_loop_;
155  bool rollback_started_;
156  bool clear_done_;
157};
158
159#if defined(ENABLE_PRE_SYNC_BACKUP)
160#define MAYBE_TestBackup TestBackup
161#else
162#define MAYBE_TestBackup DISABLED_TestBackup
163#endif
164IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
165                       MAYBE_TestBackup) {
166  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
167
168  // Setup sync, wait for its completion, and make sure changes were synced.
169  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
170  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
171  ASSERT_TRUE(ModelMatchesVerifier(0));
172
173  // Verify backup DB is created and backup time is set on device info.
174  base::Time backup_time = GetBackupDbLastModified();
175  ASSERT_FALSE(backup_time.is_null());
176  ASSERT_EQ(backup_time, GetSyncService(0)->GetDeviceBackupTimeForTesting());
177}
178
179#if defined(ENABLE_PRE_SYNC_BACKUP)
180#define MAYBE_TestBackupDisabled TestBackupDisabled
181#else
182#define MAYBE_TestBackupDisabled DISABLED_TestBackupDisabled
183#endif
184IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
185                       MAYBE_TestBackupDisabled) {
186  DisableBackup();
187
188  // Setup sync, wait for its completion, and make sure changes were synced.
189  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
190  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
191  ASSERT_TRUE(ModelMatchesVerifier(0));
192
193  // Verify backup DB is not created and backup time is not set on device info.
194  ASSERT_FALSE(base::PathExists(
195      GetProfile(0)->GetPath().Append(FILE_PATH_LITERAL("Sync Data Backup"))));
196  ASSERT_TRUE(GetSyncService(0)->GetDeviceBackupTimeForTesting().is_null());
197}
198
199#if defined(ENABLE_PRE_SYNC_BACKUP)
200#define MAYBE_TestRollback TestRollback
201#else
202#define MAYBE_TestRollback DISABLED_TestRollback
203#endif
204IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
205                       MAYBE_TestRollback) {
206  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
207
208  // Starting state:
209  // other_node
210  //    -> top
211  //      -> tier1_a
212  //        -> http://mail.google.com  "tier1_a_url0"
213  //      -> tier1_b
214  //        -> http://www.nhl.com "tier1_b_url0"
215  const BookmarkNode* top = AddFolder(0, GetOtherNode(0), 0, "top");
216  const BookmarkNode* tier1_a = AddFolder(0, top, 0, "tier1_a");
217  const BookmarkNode* tier1_b = AddFolder(0, top, 1, "tier1_b");
218  ASSERT_TRUE(AddURL(0, tier1_a, 0, "tier1_a_url0",
219                     GURL("http://mail.google.com")));
220  ASSERT_TRUE(AddURL(0, tier1_b, 0, "tier1_b_url0",
221                     GURL("http://www.nhl.com")));
222
223  // Setup sync, wait for its completion, and make sure changes were synced.
224  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
225  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
226  ASSERT_TRUE(ModelMatchesVerifier(0));
227
228  // Made bookmark changes while sync is on.
229  Move(0, tier1_a->GetChild(0), tier1_b, 1);
230  Remove(0, tier1_b, 0);
231  ASSERT_TRUE(AddFolder(0, tier1_b, 1, "tier2_c"));
232  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
233  ASSERT_TRUE(ModelMatchesVerifier(0));
234
235  // Let server to return rollback command on next sync request.
236  GetFakeServer()->TriggerError(sync_pb::SyncEnums::USER_ROLLBACK);
237
238  // Make another change to trigger downloading of rollback command.
239  Remove(0, tier1_b, 0);
240
241  // Wait for rollback to finish and sync backend is completely shut down.
242  SyncRollbackChecker rollback_checker(GetSyncService(0));
243  ASSERT_TRUE(rollback_checker.Wait());
244  SyncBackendStoppedChecker shutdown_checker(GetSyncService(0));
245  ASSERT_TRUE(shutdown_checker.Wait());
246
247  // Verify bookmarks are restored.
248  ASSERT_EQ(1, tier1_a->child_count());
249  const BookmarkNode* url1 = tier1_a->GetChild(0);
250  ASSERT_EQ(GURL("http://mail.google.com"), url1->url());
251
252  ASSERT_EQ(1, tier1_b->child_count());
253  const BookmarkNode* url2 = tier1_b->GetChild(0);
254  ASSERT_EQ(GURL("http://www.nhl.com"), url2->url());
255}
256
257#if defined(ENABLE_PRE_SYNC_BACKUP)
258#define MAYBE_TestRollbackDisabled TestRollbackDisabled
259#else
260#define MAYBE_TestRollbackDisabled DISABLED_TestRollbackDisabled
261#endif
262IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
263                       MAYBE_TestRollbackDisabled) {
264  DisableRollback();
265
266  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
267
268  // Starting state:
269  // other_node
270  //    -> http://mail.google.com  "url0"
271  //    -> http://www.nhl.com "url1"
272  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 0, "url0",
273                     GURL("http://mail.google.com")));
274  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 1, "url1",
275                     GURL("http://www.nhl.com")));
276
277  // Setup sync, wait for its completion, and make sure changes were synced.
278  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
279  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
280  ASSERT_TRUE(ModelMatchesVerifier(0));
281
282  // Made bookmark changes while sync is on.
283  Remove(0, GetOtherNode(0), 1);
284  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 1, "url2",
285                     GURL("http://www.yahoo.com")));
286  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
287  ASSERT_TRUE(ModelMatchesVerifier(0));
288
289  // Let server to return rollback command on next sync request.
290  GetFakeServer()->TriggerError(sync_pb::SyncEnums::USER_ROLLBACK);
291
292  // Make another change to trigger downloading of rollback command.
293  Remove(0, GetOtherNode(0), 0);
294
295  // Wait for sync backend is completely shut down.
296  SyncBackendStoppedChecker shutdown_checker(GetSyncService(0));
297  ASSERT_TRUE(shutdown_checker.Wait());
298
299  // With rollback disabled, bookmarks in backup DB should not be restored.
300  // Only bookmark added during sync is present.
301  ASSERT_EQ(1, GetOtherNode(0)->child_count());
302  ASSERT_EQ(GURL("http://www.yahoo.com"),
303            GetOtherNode(0)->GetChild(0)->url());
304}
305
306#if defined(ENABLE_PRE_SYNC_BACKUP)
307#define MAYBE_TestSyncDisabled TestSyncDisabled
308#else
309#define MAYBE_TestSyncDisabled DISABLED_TestSyncDisabled
310#endif
311IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
312                       MAYBE_TestSyncDisabled) {
313  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
314
315  // Starting state:
316  // other_node
317  //    -> http://mail.google.com  "url0"
318  //    -> http://www.nhl.com "url1"
319  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 0, "url0",
320                     GURL("http://mail.google.com")));
321  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 1, "url1",
322                     GURL("http://www.nhl.com")));
323
324  // Setup sync, wait for its completion, and make sure changes were synced.
325  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
326  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
327  ASSERT_TRUE(ModelMatchesVerifier(0));
328
329  // Made bookmark changes while sync is on.
330  Remove(0, GetOtherNode(0), 1);
331  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 1, "url2",
332                     GURL("http://www.yahoo.com")));
333  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
334  ASSERT_TRUE(ModelMatchesVerifier(0));
335
336  // Let server to return birthday error on next sync request.
337  GetFakeServer()->TriggerError(sync_pb::SyncEnums::NOT_MY_BIRTHDAY);
338
339  // Make another change to trigger downloading of rollback command.
340  Remove(0, GetOtherNode(0), 0);
341
342  // Wait sync backend is completely shut down.
343  SyncBackendStoppedChecker shutdown_checker(GetSyncService(0));
344  ASSERT_TRUE(shutdown_checker.Wait());
345
346  // Shouldn't restore bookmarks with sign-out only.
347  ASSERT_EQ(1, GetOtherNode(0)->child_count());
348  ASSERT_EQ(GURL("http://www.yahoo.com"),
349            GetOtherNode(0)->GetChild(0)->url());
350}
351
352#if defined(ENABLE_PRE_SYNC_BACKUP)
353#define MAYBE_RollbackNoBackup RollbackNoBackup
354#else
355#define MAYBE_RollbackNoBackup DISABLED_RollbackNoBackup
356#endif
357IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
358                       MAYBE_RollbackNoBackup) {
359  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
360
361  // Starting state:
362  // other_node
363  //    -> http://mail.google.com  "url0"
364  //    -> http://www.nhl.com "url1"
365  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 0, "url0",
366                     GURL("http://mail.google.com")));
367
368  // Setup sync, wait for its completion, and make sure changes were synced.
369  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
370  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
371  ASSERT_TRUE(ModelMatchesVerifier(0));
372
373  ASSERT_TRUE(AddURL(0, GetOtherNode(0), 1, "url1",
374                     GURL("http://www.nhl.com")));
375
376  // Delete backup DB.
377  base::DeleteFile(
378      GetProfile(0)->GetPath().Append(FILE_PATH_LITERAL("Sync Data Backup")),
379      true);
380
381  // Let server to return rollback command on next sync request.
382  GetFakeServer()->TriggerError(sync_pb::SyncEnums::USER_ROLLBACK);
383
384  // Make another change to trigger downloading of rollback command.
385  Remove(0, GetOtherNode(0), 0);
386
387  // Wait for rollback to finish and sync backend is completely shut down.
388  SyncRollbackChecker rollback_checker(GetSyncService(0));
389  ASSERT_TRUE(rollback_checker.Wait());
390  SyncBackendStoppedChecker checker(GetSyncService(0));
391  ASSERT_TRUE(checker.Wait());
392
393  // Without backup DB, bookmarks remain at the state when sync stops.
394  ASSERT_EQ(1, GetOtherNode(0)->child_count());
395  ASSERT_EQ(GURL("http://www.nhl.com"),
396            GetOtherNode(0)->GetChild(0)->url());
397}
398
399#if defined(ENABLE_PRE_SYNC_BACKUP)
400#define MAYBE_DontChangeBookmarkOrdering DontChangeBookmarkOrdering
401#else
402#define MAYBE_DontChangeBookmarkOrdering DISABLED_DontChangeBookmarkOrdering
403#endif
404IN_PROC_BROWSER_TEST_F(SingleClientBackupRollbackTest,
405                       MAYBE_DontChangeBookmarkOrdering) {
406  ASSERT_TRUE(SetupClients()) << "SetupClients() failed.";
407
408  const BookmarkNode* sub_folder = AddFolder(0, GetOtherNode(0), 0, "test");
409  ASSERT_TRUE(AddURL(0, sub_folder, 0, "", GURL(kUrl1)));
410  ASSERT_TRUE(AddURL(0, sub_folder, 1, "", GURL(kUrl2)));
411  ASSERT_TRUE(AddURL(0, sub_folder, 2, "", GURL(kUrl3)));
412
413  // Setup sync, wait for its completion, and make sure changes were synced.
414  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
415  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
416  ASSERT_TRUE(ModelMatchesVerifier(0));
417
418  // Made bookmark changes while sync is on.
419  Remove(0, sub_folder, 0);
420  Remove(0, sub_folder, 0);
421  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService(0)));
422  ASSERT_TRUE(ModelMatchesVerifier(0));
423
424  // Let server to return rollback command on next sync request.
425  GetFakeServer()->TriggerError(sync_pb::SyncEnums::USER_ROLLBACK);
426
427  // Make another change to trigger downloading of rollback command.
428  Remove(0, sub_folder, 0);
429
430  // Wait for rollback to finish and sync backend is completely shut down.
431  SyncRollbackChecker rollback_checker(GetSyncService(0));
432  ASSERT_TRUE(rollback_checker.Wait());
433  SyncBackendStoppedChecker shutdown_checker(GetSyncService(0));
434  ASSERT_TRUE(shutdown_checker.Wait());
435
436  // Verify bookmarks are unchanged.
437  ASSERT_EQ(3, sub_folder->child_count());
438  ASSERT_EQ(GURL(kUrl1), sub_folder->GetChild(0)->url());
439  ASSERT_EQ(GURL(kUrl2), sub_folder->GetChild(1)->url());
440  ASSERT_EQ(GURL(kUrl3), sub_folder->GetChild(2)->url());
441}
442