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 "base/prefs/pref_member.h"
6#include "base/prefs/pref_service.h"
7#include "base/run_loop.h"
8#include "chrome/browser/sync/profile_sync_service.h"
9#include "chrome/browser/sync/test/integration/bookmarks_helper.h"
10#include "chrome/browser/sync/test/integration/passwords_helper.h"
11#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
12#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
13#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
14#include "chrome/browser/sync/test/integration/sync_test.h"
15#include "chrome/common/pref_names.h"
16#include "google_apis/gaia/google_service_auth_error.h"
17#include "sync/protocol/sync_protocol_error.h"
18
19using bookmarks_helper::AddFolder;
20using bookmarks_helper::SetTitle;
21using sync_integration_test_util::AwaitCommitActivityCompletion;
22
23namespace {
24
25class SyncDisabledChecker : public SingleClientStatusChangeChecker {
26 public:
27  explicit SyncDisabledChecker(ProfileSyncService* service)
28      : SingleClientStatusChangeChecker(service) {}
29
30  virtual bool IsExitConditionSatisfied() OVERRIDE {
31    return !service()->setup_in_progress() &&
32           !service()->HasSyncSetupCompleted();
33  }
34
35  virtual std::string GetDebugMessage() const OVERRIDE {
36    return "Sync Disabled";
37  }
38};
39
40class TypeDisabledChecker : public SingleClientStatusChangeChecker {
41 public:
42  explicit TypeDisabledChecker(ProfileSyncService* service,
43                               syncer::ModelType type)
44      : SingleClientStatusChangeChecker(service), type_(type) {}
45
46  virtual bool IsExitConditionSatisfied() OVERRIDE {
47    return !service()->GetActiveDataTypes().Has(type_);
48  }
49
50  virtual std::string GetDebugMessage() const OVERRIDE {
51    return "Type disabled";
52  }
53 private:
54   syncer::ModelType type_;
55};
56
57bool AwaitSyncDisabled(ProfileSyncService* service) {
58  SyncDisabledChecker checker(service);
59  checker.Wait();
60  return !checker.TimedOut();
61}
62
63bool AwaitTypeDisabled(ProfileSyncService* service,
64                       syncer::ModelType type) {
65  TypeDisabledChecker checker(service, type);
66  checker.Wait();
67  return !checker.TimedOut();
68}
69
70class SyncErrorTest : public SyncTest {
71 public:
72  SyncErrorTest() : SyncTest(SINGLE_CLIENT) {}
73  virtual ~SyncErrorTest() {}
74
75 private:
76  DISALLOW_COPY_AND_ASSIGN(SyncErrorTest);
77};
78
79// TODO(pvalenzuela): Remove this class when all tests here are converted to
80// use FakeServer.
81class LegacySyncErrorTest : public SyncTest {
82 public:
83  LegacySyncErrorTest() : SyncTest(SINGLE_CLIENT_LEGACY) {}
84  virtual ~LegacySyncErrorTest() {}
85
86 private:
87  DISALLOW_COPY_AND_ASSIGN(LegacySyncErrorTest);
88};
89
90// Helper class that waits until the sync engine has hit an actionable error.
91class ActionableErrorChecker : public SingleClientStatusChangeChecker {
92 public:
93  explicit ActionableErrorChecker(ProfileSyncService* service)
94      : SingleClientStatusChangeChecker(service) {}
95
96  virtual ~ActionableErrorChecker() {}
97
98  // Checks if an actionable error has been hit. Called repeatedly each time PSS
99  // notifies observers of a state change.
100  virtual bool IsExitConditionSatisfied() OVERRIDE {
101    ProfileSyncService::Status status;
102    service()->QueryDetailedSyncStatus(&status);
103    return (status.sync_protocol_error.action != syncer::UNKNOWN_ACTION &&
104            service()->HasUnrecoverableError());
105  }
106
107  virtual std::string GetDebugMessage() const OVERRIDE {
108    return "ActionableErrorChecker";
109  }
110
111 private:
112  DISALLOW_COPY_AND_ASSIGN(ActionableErrorChecker);
113};
114
115IN_PROC_BROWSER_TEST_F(SyncErrorTest, BirthdayErrorTest) {
116  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
117
118  // Add an item, wait for sync, and trigger a birthday error on the server.
119  const BookmarkNode* node1 = AddFolder(0, 0, "title1");
120  SetTitle(0, node1, "new_title1");
121  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
122  ASSERT_TRUE(GetFakeServer()->SetNewStoreBirthday("new store birthday"));
123
124  // Now make one more change so we will do another sync.
125  const BookmarkNode* node2 = AddFolder(0, 0, "title2");
126  SetTitle(0, node2, "new_title2");
127  ASSERT_TRUE(AwaitSyncDisabled(GetSyncService((0))));
128}
129
130IN_PROC_BROWSER_TEST_F(LegacySyncErrorTest, ActionableErrorTest) {
131  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
132
133  const BookmarkNode* node1 = AddFolder(0, 0, "title1");
134  SetTitle(0, node1, "new_title1");
135  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
136
137  syncer::SyncProtocolError protocol_error;
138  protocol_error.error_type = syncer::TRANSIENT_ERROR;
139  protocol_error.action = syncer::UPGRADE_CLIENT;
140  protocol_error.error_description = "Not My Fault";
141  protocol_error.url = "www.google.com";
142  TriggerSyncError(protocol_error, SyncTest::ERROR_FREQUENCY_ALWAYS);
143
144  // Now make one more change so we will do another sync.
145  const BookmarkNode* node2 = AddFolder(0, 0, "title2");
146  SetTitle(0, node2, "new_title2");
147
148  // Wait until an actionable error is encountered.
149  ActionableErrorChecker actionable_error_checker(GetSyncService((0)));
150  actionable_error_checker.Wait();
151  ASSERT_FALSE(actionable_error_checker.TimedOut());
152
153  ProfileSyncService::Status status;
154  GetSyncService((0))->QueryDetailedSyncStatus(&status);
155  ASSERT_EQ(status.sync_protocol_error.error_type, protocol_error.error_type);
156  ASSERT_EQ(status.sync_protocol_error.action, protocol_error.action);
157  ASSERT_EQ(status.sync_protocol_error.url, protocol_error.url);
158  ASSERT_EQ(status.sync_protocol_error.error_description,
159      protocol_error.error_description);
160}
161
162// Disabled, http://crbug.com/351160 .
163IN_PROC_BROWSER_TEST_F(LegacySyncErrorTest, DISABLED_ErrorWhileSettingUp) {
164  ASSERT_TRUE(SetupClients());
165
166  syncer::SyncProtocolError protocol_error;
167  protocol_error.error_type = syncer::TRANSIENT_ERROR;
168  protocol_error.error_description = "Not My Fault";
169  protocol_error.url = "www.google.com";
170
171  if (GetSyncService(0)->auto_start_enabled()) {
172    // In auto start enabled platforms like chrome os we should be
173    // able to set up even if the first sync while setting up fails.
174    // Trigger error on every 2 out of 3 requests.
175    TriggerSyncError(protocol_error, SyncTest::ERROR_FREQUENCY_TWO_THIRDS);
176    // Now setup sync and it should succeed.
177    ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
178  } else {
179    // In Non auto start enabled environments if the setup sync fails then
180    // the setup would fail. So setup sync normally.
181    ASSERT_TRUE(SetupSync()) << "Setup sync failed";
182    ASSERT_TRUE(GetClient(0)->DisableSyncForDatatype(syncer::AUTOFILL));
183
184    // Trigger error on every 2 out of 3 requests.
185    TriggerSyncError(protocol_error, SyncTest::ERROR_FREQUENCY_TWO_THIRDS);
186
187    // Now enable a datatype, whose first 2 syncs would fail, but we should
188    // recover and setup succesfully on the third attempt.
189    ASSERT_TRUE(GetClient(0)->EnableSyncForDatatype(syncer::AUTOFILL));
190  }
191}
192
193IN_PROC_BROWSER_TEST_F(LegacySyncErrorTest,
194    DISABLED_BirthdayErrorUsingActionableErrorTest) {
195  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
196
197  const BookmarkNode* node1 = AddFolder(0, 0, "title1");
198  SetTitle(0, node1, "new_title1");
199  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
200
201  syncer::SyncProtocolError protocol_error;
202  protocol_error.error_type = syncer::NOT_MY_BIRTHDAY;
203  protocol_error.action = syncer::DISABLE_SYNC_ON_CLIENT;
204  protocol_error.error_description = "Not My Fault";
205  protocol_error.url = "www.google.com";
206  TriggerSyncError(protocol_error, SyncTest::ERROR_FREQUENCY_ALWAYS);
207
208  // Now make one more change so we will do another sync.
209  const BookmarkNode* node2 = AddFolder(0, 0, "title2");
210  SetTitle(0, node2, "new_title2");
211  ASSERT_TRUE(AwaitSyncDisabled(GetSyncService((0))));
212  ProfileSyncService::Status status;
213  GetSyncService((0))->QueryDetailedSyncStatus(&status);
214  ASSERT_EQ(status.sync_protocol_error.error_type, protocol_error.error_type);
215  ASSERT_EQ(status.sync_protocol_error.action, protocol_error.action);
216  ASSERT_EQ(status.sync_protocol_error.url, protocol_error.url);
217  ASSERT_EQ(status.sync_protocol_error.error_description,
218      protocol_error.error_description);
219}
220
221IN_PROC_BROWSER_TEST_F(LegacySyncErrorTest, DisableDatatypeWhileRunning) {
222  ASSERT_TRUE(SetupSync()) << "SetupSync() failed.";
223  syncer::ModelTypeSet synced_datatypes =
224      GetSyncService((0))->GetActiveDataTypes();
225  ASSERT_TRUE(synced_datatypes.Has(syncer::TYPED_URLS));
226  ASSERT_TRUE(synced_datatypes.Has(syncer::SESSIONS));
227  GetProfile(0)->GetPrefs()->SetBoolean(
228      prefs::kSavingBrowserHistoryDisabled, true);
229
230  // Wait for reconfigurations.
231  ASSERT_TRUE(AwaitTypeDisabled(GetSyncService(0), syncer::TYPED_URLS));
232  ASSERT_TRUE(AwaitTypeDisabled(GetSyncService(0), syncer::SESSIONS));
233
234  const BookmarkNode* node1 = AddFolder(0, 0, "title1");
235  SetTitle(0, node1, "new_title1");
236  ASSERT_TRUE(AwaitCommitActivityCompletion(GetSyncService((0))));
237  // TODO(lipalani)" Verify initial sync ended for typed url is false.
238}
239
240}  // namespace
241