Fire an error if a pref used in the UI is missing once all prefs are fetched.
[chromium-blink-merge.git] / chrome / browser / sync / test / integration / profile_sync_service_harness.cc
blob2f4450be96bf91ee381c37b78d160fe66be7cc11
1 // Copyright 2013 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/sync/test/integration/profile_sync_service_harness.h"
7 #include <cstddef>
8 #include <iterator>
9 #include <ostream>
10 #include <sstream>
11 #include <vector>
13 #include "base/compiler_specific.h"
14 #include "base/json/json_writer.h"
15 #include "base/logging.h"
16 #include "base/strings/stringprintf.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
19 #include "chrome/browser/sync/about_sync_util.h"
20 #include "chrome/browser/sync/profile_sync_service.h"
21 #include "chrome/browser/sync/profile_sync_service_factory.h"
22 #include "chrome/browser/sync/test/integration/quiesce_status_change_checker.h"
23 #include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/browser/ui/browser_finder.h"
26 #include "chrome/browser/ui/webui/signin/login_ui_test_utils.h"
27 #include "chrome/common/chrome_switches.h"
28 #include "components/invalidation/p2p_invalidation_service.h"
29 #include "components/signin/core/browser/profile_oauth2_token_service.h"
30 #include "components/signin/core/browser/signin_manager_base.h"
31 #include "components/sync_driver/data_type_controller.h"
32 #include "google_apis/gaia/gaia_constants.h"
33 #include "sync/internal_api/public/base/progress_marker_map.h"
34 #include "sync/internal_api/public/util/sync_string_conversions.h"
36 using syncer::sessions::SyncSessionSnapshot;
38 namespace {
40 bool HasAuthError(ProfileSyncService* service) {
41 return service->GetAuthError().state() ==
42 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS ||
43 service->GetAuthError().state() ==
44 GoogleServiceAuthError::SERVICE_ERROR ||
45 service->GetAuthError().state() ==
46 GoogleServiceAuthError::REQUEST_CANCELED;
49 class BackendInitializeChecker : public SingleClientStatusChangeChecker {
50 public:
51 explicit BackendInitializeChecker(ProfileSyncService* service)
52 : SingleClientStatusChangeChecker(service) {}
54 bool IsExitConditionSatisfied() override {
55 if (service()->backend_mode() != ProfileSyncService::SYNC)
56 return false;
57 if (service()->backend_initialized())
58 return true;
59 // Backend initialization is blocked by an auth error.
60 if (HasAuthError(service()))
61 return true;
62 // Backend initialization is blocked by a failure to fetch Oauth2 tokens.
63 if (service()->IsRetryingAccessTokenFetchForTest())
64 return true;
65 // Still waiting on backend initialization.
66 return false;
69 std::string GetDebugMessage() const override { return "Backend Initialize"; }
72 class SyncSetupChecker : public SingleClientStatusChangeChecker {
73 public:
74 explicit SyncSetupChecker(ProfileSyncService* service)
75 : SingleClientStatusChangeChecker(service) {}
77 bool IsExitConditionSatisfied() override {
78 if (!service()->SyncActive())
79 return false;
80 if (service()->ConfigurationDone())
81 return true;
82 // Sync is blocked because a custom passphrase is required.
83 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION)
84 return true;
85 // Sync is blocked by an auth error.
86 if (HasAuthError(service()))
87 return true;
88 // Still waiting on sync setup.
89 return false;
92 std::string GetDebugMessage() const override { return "Sync Setup"; }
95 } // namespace
97 // static
98 ProfileSyncServiceHarness* ProfileSyncServiceHarness::Create(
99 Profile* profile,
100 const std::string& username,
101 const std::string& password,
102 SigninType signin_type) {
103 return new ProfileSyncServiceHarness(profile,
104 username,
105 password,
106 signin_type);
109 ProfileSyncServiceHarness::ProfileSyncServiceHarness(
110 Profile* profile,
111 const std::string& username,
112 const std::string& password,
113 SigninType signin_type)
114 : profile_(profile),
115 service_(ProfileSyncServiceFactory::GetForProfile(profile)),
116 username_(username),
117 password_(password),
118 signin_type_(signin_type),
119 oauth2_refesh_token_number_(0),
120 profile_debug_name_(profile->GetDebugName()) {
123 ProfileSyncServiceHarness::~ProfileSyncServiceHarness() { }
125 void ProfileSyncServiceHarness::SetCredentials(const std::string& username,
126 const std::string& password) {
127 username_ = username;
128 password_ = password;
131 bool ProfileSyncServiceHarness::SetupSync() {
132 bool result = SetupSync(syncer::ModelTypeSet::All());
133 if (result == false) {
134 std::string status = GetServiceStatus();
135 LOG(ERROR) << profile_debug_name_
136 << ": SetupSync failed. Syncer status:\n" << status;
137 } else {
138 DVLOG(1) << profile_debug_name_ << ": SetupSync successful.";
140 return result;
143 bool ProfileSyncServiceHarness::SetupSync(
144 syncer::ModelTypeSet synced_datatypes) {
145 DCHECK(!profile_->IsSupervised())
146 << "SetupSync should not be used for supervised users.";
148 // Initialize the sync client's profile sync service object.
149 if (service() == NULL) {
150 LOG(ERROR) << "SetupSync(): service() is null.";
151 return false;
154 // Tell the sync service that setup is in progress so we don't start syncing
155 // until we've finished configuration.
156 service()->SetSetupInProgress(true);
158 DCHECK(!username_.empty());
159 if (signin_type_ == SigninType::UI_SIGNIN) {
160 Browser* browser =
161 FindBrowserWithProfile(profile_, chrome::GetActiveDesktop());
162 DCHECK(browser);
163 if (!login_ui_test_utils::SignInWithUI(browser, username_, password_)) {
164 LOG(ERROR) << "Could not sign in to GAIA servers.";
165 return false;
167 } else if (signin_type_ == SigninType::FAKE_SIGNIN) {
168 // Authenticate sync client using GAIA credentials.
169 service()->signin()->SetAuthenticatedUsername(username_);
170 service()->GoogleSigninSucceeded(username_, username_, password_);
171 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
172 UpdateCredentials(username_, GenerateFakeOAuth2RefreshTokenString());
173 } else {
174 LOG(ERROR) << "Unsupported profile signin type.";
177 if (!AwaitBackendInitialization()) {
178 return false;
181 // Choose the datatypes to be synced. If all datatypes are to be synced,
182 // set sync_everything to true; otherwise, set it to false.
183 bool sync_everything =
184 synced_datatypes.Equals(syncer::ModelTypeSet::All());
185 service()->OnUserChoseDatatypes(sync_everything, synced_datatypes);
187 // Notify ProfileSyncService that we are done with configuration.
188 FinishSyncSetup();
190 // Set an implicit passphrase for encryption if an explicit one hasn't already
191 // been set. If an explicit passphrase has been set, immediately return false,
192 // since a decryption passphrase is required.
193 if (!service()->IsUsingSecondaryPassphrase()) {
194 service()->SetEncryptionPassphrase(password_, ProfileSyncService::IMPLICIT);
195 } else {
196 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
197 " until SetDecryptionPassphrase is called.";
198 return false;
201 // Wait for initial sync cycle to be completed.
202 if (!AwaitSyncSetupCompletion()) {
203 LOG(ERROR) << "Initial sync cycle timed out.";
204 return false;
207 return true;
210 bool ProfileSyncServiceHarness::AwaitMutualSyncCycleCompletion(
211 ProfileSyncServiceHarness* partner) {
212 std::vector<ProfileSyncServiceHarness*> harnesses;
213 harnesses.push_back(this);
214 harnesses.push_back(partner);
215 return AwaitQuiescence(harnesses);
218 bool ProfileSyncServiceHarness::AwaitGroupSyncCycleCompletion(
219 std::vector<ProfileSyncServiceHarness*>& partners) {
220 return AwaitQuiescence(partners);
223 // static
224 bool ProfileSyncServiceHarness::AwaitQuiescence(
225 std::vector<ProfileSyncServiceHarness*>& clients) {
226 std::vector<ProfileSyncService*> services;
227 if (clients.empty()) {
228 return true;
231 for (std::vector<ProfileSyncServiceHarness*>::iterator it = clients.begin();
232 it != clients.end(); ++it) {
233 services.push_back((*it)->service());
235 QuiesceStatusChangeChecker checker(services);
236 checker.Wait();
237 return !checker.TimedOut();
240 bool ProfileSyncServiceHarness::AwaitBackendInitialization() {
241 BackendInitializeChecker checker(service());
242 checker.Wait();
244 if (checker.TimedOut()) {
245 LOG(ERROR) << "BackendInitializeChecker timed out.";
246 return false;
249 if (!service()->backend_initialized()) {
250 LOG(ERROR) << "Service backend not initialized.";
251 return false;
254 // Make sure that initial sync wasn't blocked by a missing passphrase.
255 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
256 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
257 " until SetDecryptionPassphrase is called.";
258 return false;
261 if (HasAuthError(service())) {
262 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
263 return false;
266 return true;
269 bool ProfileSyncServiceHarness::AwaitSyncSetupCompletion() {
270 SyncSetupChecker checker(service());
271 checker.Wait();
273 if (checker.TimedOut()) {
274 LOG(ERROR) << "SyncSetupChecker timed out.";
275 return false;
278 // Make sure that initial sync wasn't blocked by a missing passphrase.
279 if (service()->passphrase_required_reason() == syncer::REASON_DECRYPTION) {
280 LOG(ERROR) << "A passphrase is required for decryption. Sync cannot proceed"
281 " until SetDecryptionPassphrase is called.";
282 return false;
285 if (HasAuthError(service())) {
286 LOG(ERROR) << "Credentials were rejected. Sync cannot proceed.";
287 return false;
290 return true;
293 std::string ProfileSyncServiceHarness::GenerateFakeOAuth2RefreshTokenString() {
294 return base::StringPrintf("oauth2_refresh_token_%d",
295 ++oauth2_refesh_token_number_);
298 bool ProfileSyncServiceHarness::IsSyncDisabled() const {
299 return !service()->setup_in_progress() &&
300 !service()->HasSyncSetupCompleted();
303 void ProfileSyncServiceHarness::FinishSyncSetup() {
304 service()->SetSetupInProgress(false);
305 service()->SetSyncSetupCompleted();
308 SyncSessionSnapshot ProfileSyncServiceHarness::GetLastSessionSnapshot() const {
309 DCHECK(service() != NULL) << "Sync service has not yet been set up.";
310 if (service()->SyncActive()) {
311 return service()->GetLastSessionSnapshot();
313 return SyncSessionSnapshot();
316 bool ProfileSyncServiceHarness::EnableSyncForDatatype(
317 syncer::ModelType datatype) {
318 DVLOG(1) << GetClientInfoString(
319 "EnableSyncForDatatype("
320 + std::string(syncer::ModelTypeToString(datatype)) + ")");
322 if (IsSyncDisabled())
323 return SetupSync(syncer::ModelTypeSet(datatype));
325 if (service() == NULL) {
326 LOG(ERROR) << "EnableSyncForDatatype(): service() is null.";
327 return false;
330 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
331 if (synced_datatypes.Has(datatype)) {
332 DVLOG(1) << "EnableSyncForDatatype(): Sync already enabled for datatype "
333 << syncer::ModelTypeToString(datatype)
334 << " on " << profile_debug_name_ << ".";
335 return true;
338 synced_datatypes.Put(syncer::ModelTypeFromInt(datatype));
339 service()->OnUserChoseDatatypes(false, synced_datatypes);
340 if (AwaitSyncSetupCompletion()) {
341 DVLOG(1) << "EnableSyncForDatatype(): Enabled sync for datatype "
342 << syncer::ModelTypeToString(datatype)
343 << " on " << profile_debug_name_ << ".";
344 return true;
347 DVLOG(0) << GetClientInfoString("EnableSyncForDatatype failed");
348 return false;
351 bool ProfileSyncServiceHarness::DisableSyncForDatatype(
352 syncer::ModelType datatype) {
353 DVLOG(1) << GetClientInfoString(
354 "DisableSyncForDatatype("
355 + std::string(syncer::ModelTypeToString(datatype)) + ")");
357 if (service() == NULL) {
358 LOG(ERROR) << "DisableSyncForDatatype(): service() is null.";
359 return false;
362 syncer::ModelTypeSet synced_datatypes = service()->GetPreferredDataTypes();
363 if (!synced_datatypes.Has(datatype)) {
364 DVLOG(1) << "DisableSyncForDatatype(): Sync already disabled for datatype "
365 << syncer::ModelTypeToString(datatype)
366 << " on " << profile_debug_name_ << ".";
367 return true;
370 synced_datatypes.RetainAll(syncer::UserSelectableTypes());
371 synced_datatypes.Remove(datatype);
372 service()->OnUserChoseDatatypes(false, synced_datatypes);
373 if (AwaitSyncSetupCompletion()) {
374 DVLOG(1) << "DisableSyncForDatatype(): Disabled sync for datatype "
375 << syncer::ModelTypeToString(datatype)
376 << " on " << profile_debug_name_ << ".";
377 return true;
380 DVLOG(0) << GetClientInfoString("DisableSyncForDatatype failed");
381 return false;
384 bool ProfileSyncServiceHarness::EnableSyncForAllDatatypes() {
385 DVLOG(1) << GetClientInfoString("EnableSyncForAllDatatypes");
387 if (IsSyncDisabled())
388 return SetupSync();
390 if (service() == NULL) {
391 LOG(ERROR) << "EnableSyncForAllDatatypes(): service() is null.";
392 return false;
395 service()->OnUserChoseDatatypes(true, syncer::ModelTypeSet::All());
396 if (AwaitSyncSetupCompletion()) {
397 DVLOG(1) << "EnableSyncForAllDatatypes(): Enabled sync for all datatypes "
398 << "on " << profile_debug_name_ << ".";
399 return true;
402 DVLOG(0) << GetClientInfoString("EnableSyncForAllDatatypes failed");
403 return false;
406 bool ProfileSyncServiceHarness::DisableSyncForAllDatatypes() {
407 DVLOG(1) << GetClientInfoString("DisableSyncForAllDatatypes");
409 if (service() == NULL) {
410 LOG(ERROR) << "DisableSyncForAllDatatypes(): service() is null.";
411 return false;
414 service()->DisableForUser();
416 DVLOG(1) << "DisableSyncForAllDatatypes(): Disabled sync for all "
417 << "datatypes on " << profile_debug_name_;
418 return true;
421 // TODO(sync): Clean up this method in a separate CL. Remove all snapshot fields
422 // and log shorter, more meaningful messages.
423 std::string ProfileSyncServiceHarness::GetClientInfoString(
424 const std::string& message) const {
425 std::stringstream os;
426 os << profile_debug_name_ << ": " << message << ": ";
427 if (service()) {
428 const SyncSessionSnapshot& snap = GetLastSessionSnapshot();
429 ProfileSyncService::Status status;
430 service()->QueryDetailedSyncStatus(&status);
431 // Capture select info from the sync session snapshot and syncer status.
432 os << ", has_unsynced_items: "
433 << (service()->SyncActive() ? service()->HasUnsyncedItems() : 0)
434 << ", did_commit: "
435 << (snap.model_neutral_state().num_successful_commits == 0 &&
436 snap.model_neutral_state().commit_result == syncer::SYNCER_OK)
437 << ", encryption conflicts: "
438 << snap.num_encryption_conflicts()
439 << ", hierarchy conflicts: "
440 << snap.num_hierarchy_conflicts()
441 << ", server conflicts: "
442 << snap.num_server_conflicts()
443 << ", num_updates_downloaded : "
444 << snap.model_neutral_state().num_updates_downloaded_total
445 << ", passphrase_required_reason: "
446 << syncer::PassphraseRequiredReasonToString(
447 service()->passphrase_required_reason())
448 << ", notifications_enabled: "
449 << status.notifications_enabled
450 << ", service_is_active: "
451 << service()->SyncActive();
452 } else {
453 os << "Sync service not available";
455 return os.str();
458 bool ProfileSyncServiceHarness::IsTypePreferred(syncer::ModelType type) {
459 return service()->GetPreferredDataTypes().Has(type);
462 std::string ProfileSyncServiceHarness::GetServiceStatus() {
463 scoped_ptr<base::DictionaryValue> value(
464 sync_ui_util::ConstructAboutInformation(service()));
465 std::string service_status;
466 base::JSONWriter::WriteWithOptions(value.get(),
467 base::JSONWriter::OPTIONS_PRETTY_PRINT,
468 &service_status);
469 return service_status;