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 "chrome/browser/sync/test/integration/sync_test.h" 6 7#include <vector> 8 9#include "base/basictypes.h" 10#include "base/bind.h" 11#include "base/command_line.h" 12#include "base/memory/ref_counted.h" 13#include "base/message_loop/message_loop.h" 14#include "base/path_service.h" 15#include "base/process/launch.h" 16#include "base/strings/string_util.h" 17#include "base/strings/stringprintf.h" 18#include "base/strings/utf_string_conversions.h" 19#include "base/synchronization/waitable_event.h" 20#include "base/test/test_timeouts.h" 21#include "base/threading/platform_thread.h" 22#include "base/values.h" 23#include "chrome/browser/bookmarks/bookmark_model_factory.h" 24#include "chrome/browser/history/history_service_factory.h" 25#include "chrome/browser/invalidation/profile_invalidation_provider_factory.h" 26#include "chrome/browser/lifetime/application_lifetime.h" 27#include "chrome/browser/profiles/profile.h" 28#include "chrome/browser/profiles/profile_manager.h" 29#include "chrome/browser/search_engines/template_url_service.h" 30#include "chrome/browser/search_engines/template_url_service_factory.h" 31#include "chrome/browser/signin/profile_identity_provider.h" 32#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 33#include "chrome/browser/signin/signin_manager_factory.h" 34#include "chrome/browser/sync/profile_sync_service.h" 35#include "chrome/browser/sync/profile_sync_service_factory.h" 36#include "chrome/browser/sync/test/integration/fake_server_invalidation_service.h" 37#include "chrome/browser/sync/test/integration/p2p_invalidation_forwarder.h" 38#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h" 39#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h" 40#include "chrome/browser/sync/test/integration/sync_datatype_helper.h" 41#include "chrome/browser/sync/test/integration/sync_integration_test_util.h" 42#include "chrome/browser/ui/browser.h" 43#include "chrome/browser/ui/browser_finder.h" 44#include "chrome/browser/ui/host_desktop.h" 45#include "chrome/browser/ui/tabs/tab_strip_model.h" 46#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h" 47#include "chrome/common/chrome_paths.h" 48#include "chrome/common/chrome_switches.h" 49#include "chrome/test/base/testing_browser_process.h" 50#include "chrome/test/base/ui_test_utils.h" 51#include "components/bookmarks/test/bookmark_test_helpers.h" 52#include "components/google/core/browser/google_url_tracker.h" 53#include "components/invalidation/invalidation_service.h" 54#include "components/invalidation/invalidation_switches.h" 55#include "components/invalidation/p2p_invalidation_service.h" 56#include "components/invalidation/p2p_invalidator.h" 57#include "components/invalidation/profile_invalidation_provider.h" 58#include "components/keyed_service/core/keyed_service.h" 59#include "components/os_crypt/os_crypt.h" 60#include "components/signin/core/browser/signin_manager.h" 61#include "content/public/browser/web_contents.h" 62#include "content/public/test/test_browser_thread.h" 63#include "google_apis/gaia/gaia_urls.h" 64#include "net/base/escape.h" 65#include "net/base/load_flags.h" 66#include "net/base/network_change_notifier.h" 67#include "net/proxy/proxy_config.h" 68#include "net/proxy/proxy_config_service_fixed.h" 69#include "net/proxy/proxy_service.h" 70#include "net/test/spawned_test_server/spawned_test_server.h" 71#include "net/url_request/test_url_fetcher_factory.h" 72#include "net/url_request/url_fetcher.h" 73#include "net/url_request/url_fetcher_delegate.h" 74#include "net/url_request/url_request_context.h" 75#include "net/url_request/url_request_context_getter.h" 76#include "sync/engine/sync_scheduler_impl.h" 77#include "sync/protocol/sync.pb.h" 78#include "sync/test/fake_server/fake_server.h" 79#include "sync/test/fake_server/fake_server_network_resources.h" 80#include "url/gurl.h" 81 82#if defined(OS_CHROMEOS) 83#include "chromeos/chromeos_switches.h" 84#endif 85 86using content::BrowserThread; 87 88namespace switches { 89const char kPasswordFileForTest[] = "password-file-for-test"; 90const char kSyncUserForTest[] = "sync-user-for-test"; 91const char kSyncPasswordForTest[] = "sync-password-for-test"; 92const char kSyncServerCommandLine[] = "sync-server-command-line"; 93} 94 95namespace { 96 97// Helper class that checks whether a sync test server is running or not. 98class SyncServerStatusChecker : public net::URLFetcherDelegate { 99 public: 100 SyncServerStatusChecker() : running_(false) {} 101 102 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE { 103 std::string data; 104 source->GetResponseAsString(&data); 105 running_ = 106 (source->GetStatus().status() == net::URLRequestStatus::SUCCESS && 107 source->GetResponseCode() == 200 && data.find("ok") == 0); 108 base::MessageLoop::current()->Quit(); 109 } 110 111 bool running() const { return running_; } 112 113 private: 114 bool running_; 115}; 116 117bool IsEncryptionComplete(const ProfileSyncService* service) { 118 return service->EncryptEverythingEnabled() && !service->encryption_pending(); 119} 120 121// Helper class to wait for encryption to complete. 122class EncryptionChecker : public SingleClientStatusChangeChecker { 123 public: 124 explicit EncryptionChecker(ProfileSyncService* service) 125 : SingleClientStatusChangeChecker(service) {} 126 127 virtual bool IsExitConditionSatisfied() OVERRIDE { 128 return IsEncryptionComplete(service()); 129 } 130 131 virtual std::string GetDebugMessage() const OVERRIDE { 132 return "Encryption"; 133 } 134}; 135 136void SetProxyConfigCallback( 137 base::WaitableEvent* done, 138 net::URLRequestContextGetter* url_request_context_getter, 139 const net::ProxyConfig& proxy_config) { 140 net::ProxyService* proxy_service = 141 url_request_context_getter->GetURLRequestContext()->proxy_service(); 142 proxy_service->ResetConfigService( 143 new net::ProxyConfigServiceFixed(proxy_config)); 144 done->Signal(); 145} 146 147KeyedService* BuildFakeServerProfileInvalidationProvider( 148 content::BrowserContext* context) { 149 return new invalidation::ProfileInvalidationProvider( 150 scoped_ptr<invalidation::InvalidationService>( 151 new fake_server::FakeServerInvalidationService)); 152} 153 154KeyedService* BuildP2PProfileInvalidationProvider( 155 content::BrowserContext* context, 156 syncer::P2PNotificationTarget notification_target) { 157 Profile* profile = static_cast<Profile*>(context); 158 return new invalidation::ProfileInvalidationProvider( 159 scoped_ptr<invalidation::InvalidationService>( 160 new invalidation::P2PInvalidationService( 161 scoped_ptr<IdentityProvider>(new ProfileIdentityProvider( 162 SigninManagerFactory::GetForProfile(profile), 163 ProfileOAuth2TokenServiceFactory::GetForProfile(profile), 164 LoginUIServiceFactory::GetForProfile(profile))), 165 profile->GetRequestContext(), 166 notification_target))); 167} 168 169KeyedService* BuildSelfNotifyingP2PProfileInvalidationProvider( 170 content::BrowserContext* context) { 171 return BuildP2PProfileInvalidationProvider(context, syncer::NOTIFY_ALL); 172} 173 174KeyedService* BuildRealisticP2PProfileInvalidationProvider( 175 content::BrowserContext* context) { 176 return BuildP2PProfileInvalidationProvider(context, syncer::NOTIFY_OTHERS); 177} 178 179} // namespace 180 181SyncTest::SyncTest(TestType test_type) 182 : test_type_(test_type), 183 server_type_(SERVER_TYPE_UNDECIDED), 184 num_clients_(-1), 185 use_verifier_(true), 186 notifications_enabled_(true), 187 test_server_handle_(base::kNullProcessHandle) { 188 sync_datatype_helper::AssociateWithTest(this); 189 switch (test_type_) { 190 case SINGLE_CLIENT: 191 case SINGLE_CLIENT_LEGACY: { 192 num_clients_ = 1; 193 break; 194 } 195 case TWO_CLIENT: 196 case TWO_CLIENT_LEGACY: { 197 num_clients_ = 2; 198 break; 199 } 200 case MULTIPLE_CLIENT: { 201 num_clients_ = 3; 202 break; 203 } 204 } 205} 206 207SyncTest::~SyncTest() {} 208 209void SyncTest::SetUp() { 210 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 211 if (cl->HasSwitch(switches::kPasswordFileForTest)) { 212 ReadPasswordFile(); 213 } else if (cl->HasSwitch(switches::kSyncUserForTest) && 214 cl->HasSwitch(switches::kSyncPasswordForTest)) { 215 username_ = cl->GetSwitchValueASCII(switches::kSyncUserForTest); 216 password_ = cl->GetSwitchValueASCII(switches::kSyncPasswordForTest); 217 } else { 218 username_ = "user@gmail.com"; 219 password_ = "password"; 220 } 221 222 if (username_.empty() || password_.empty()) 223 LOG(FATAL) << "Cannot run sync tests without GAIA credentials."; 224 225 // Sets |server_type_| if it wasn't specified by the test. 226 DecideServerType(); 227 228 // Mock the Mac Keychain service. The real Keychain can block on user input. 229#if defined(OS_MACOSX) 230 OSCrypt::UseMockKeychain(true); 231#endif 232 233 // Start up a sync test server if one is needed and setup mock gaia responses. 234 // Note: This must be done prior to the call to SetupClients() because we want 235 // the mock gaia responses to be available before GaiaUrls is initialized. 236 SetUpTestServerIfRequired(); 237 238 // Yield control back to the InProcessBrowserTest framework. 239 InProcessBrowserTest::SetUp(); 240} 241 242void SyncTest::TearDown() { 243 // Clear any mock gaia responses that might have been set. 244 ClearMockGaiaResponses(); 245 246 // Allow the InProcessBrowserTest framework to perform its tear down. 247 InProcessBrowserTest::TearDown(); 248 249 // Stop the local python test server. This is a no-op if one wasn't started. 250 TearDownLocalPythonTestServer(); 251 252 // Stop the local sync test server. This is a no-op if one wasn't started. 253 TearDownLocalTestServer(); 254 255 fake_server_.reset(); 256} 257 258void SyncTest::SetUpCommandLine(base::CommandLine* cl) { 259 AddTestSwitches(cl); 260 AddOptionalTypesToCommandLine(cl); 261 262#if defined(OS_CHROMEOS) 263 cl->AppendSwitch(chromeos::switches::kIgnoreUserProfileMappingForTests); 264#endif 265} 266 267void SyncTest::AddTestSwitches(base::CommandLine* cl) { 268 // Disable non-essential access of external network resources. 269 if (!cl->HasSwitch(switches::kDisableBackgroundNetworking)) 270 cl->AppendSwitch(switches::kDisableBackgroundNetworking); 271 272 if (!cl->HasSwitch(switches::kSyncShortInitialRetryOverride)) 273 cl->AppendSwitch(switches::kSyncShortInitialRetryOverride); 274} 275 276void SyncTest::AddOptionalTypesToCommandLine(base::CommandLine* cl) {} 277 278// static 279Profile* SyncTest::MakeProfile(const base::FilePath::StringType name) { 280 base::FilePath path; 281 PathService::Get(chrome::DIR_USER_DATA, &path); 282 path = path.Append(name); 283 284 if (!base::PathExists(path)) 285 CHECK(base::CreateDirectory(path)); 286 287 Profile* profile = 288 Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS); 289 g_browser_process->profile_manager()->RegisterTestingProfile(profile, 290 true, 291 true); 292 return profile; 293} 294 295Profile* SyncTest::GetProfile(int index) { 296 if (profiles_.empty()) 297 LOG(FATAL) << "SetupClients() has not yet been called."; 298 if (index < 0 || index >= static_cast<int>(profiles_.size())) 299 LOG(FATAL) << "GetProfile(): Index is out of bounds."; 300 return profiles_[index]; 301} 302 303Browser* SyncTest::GetBrowser(int index) { 304 if (browsers_.empty()) 305 LOG(FATAL) << "SetupClients() has not yet been called."; 306 if (index < 0 || index >= static_cast<int>(browsers_.size())) 307 LOG(FATAL) << "GetBrowser(): Index is out of bounds."; 308 return browsers_[index]; 309} 310 311ProfileSyncServiceHarness* SyncTest::GetClient(int index) { 312 if (clients_.empty()) 313 LOG(FATAL) << "SetupClients() has not yet been called."; 314 if (index < 0 || index >= static_cast<int>(clients_.size())) 315 LOG(FATAL) << "GetClient(): Index is out of bounds."; 316 return clients_[index]; 317} 318 319ProfileSyncService* SyncTest::GetSyncService(int index) { 320 return ProfileSyncServiceFactory::GetForProfile(GetProfile(index)); 321} 322 323std::vector<ProfileSyncService*> SyncTest::GetSyncServices() { 324 std::vector<ProfileSyncService*> services; 325 for (int i = 0; i < num_clients(); ++i) { 326 services.push_back(GetSyncService(i)); 327 } 328 return services; 329} 330 331Profile* SyncTest::verifier() { 332 if (verifier_ == NULL) 333 LOG(FATAL) << "SetupClients() has not yet been called."; 334 return verifier_; 335} 336 337void SyncTest::DisableVerifier() { 338 use_verifier_ = false; 339} 340 341bool SyncTest::SetupClients() { 342 if (num_clients_ <= 0) 343 LOG(FATAL) << "num_clients_ incorrectly initialized."; 344 if (!profiles_.empty() || !browsers_.empty() || !clients_.empty()) 345 LOG(FATAL) << "SetupClients() has already been called."; 346 347 // Create the required number of sync profiles, browsers and clients. 348 profiles_.resize(num_clients_); 349 browsers_.resize(num_clients_); 350 clients_.resize(num_clients_); 351 invalidation_forwarders_.resize(num_clients_); 352 fake_server_invalidation_services_.resize(num_clients_); 353 for (int i = 0; i < num_clients_; ++i) { 354 InitializeInstance(i); 355 } 356 357 // Create the verifier profile. 358 verifier_ = MakeProfile(FILE_PATH_LITERAL("Verifier")); 359 test::WaitForBookmarkModelToLoad( 360 BookmarkModelFactory::GetForProfile(verifier())); 361 ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile( 362 verifier(), Profile::EXPLICIT_ACCESS)); 363 ui_test_utils::WaitForTemplateURLServiceToLoad( 364 TemplateURLServiceFactory::GetForProfile(verifier())); 365 return (verifier_ != NULL); 366} 367 368void SyncTest::InitializeInstance(int index) { 369 profiles_[index] = MakeProfile( 370 base::StringPrintf(FILE_PATH_LITERAL("Profile%d"), index)); 371 EXPECT_FALSE(GetProfile(index) == NULL) << "Could not create Profile " 372 << index << "."; 373 374 browsers_[index] = new Browser(Browser::CreateParams( 375 GetProfile(index), chrome::GetActiveDesktop())); 376 EXPECT_FALSE(GetBrowser(index) == NULL) << "Could not create Browser " 377 << index << "."; 378 379 380 // Make sure the ProfileSyncService has been created before creating the 381 // ProfileSyncServiceHarness - some tests expect the ProfileSyncService to 382 // already exist. 383 ProfileSyncService* profile_sync_service = 384 ProfileSyncServiceFactory::GetForProfile(GetProfile(index)); 385 386 if (server_type_ == IN_PROCESS_FAKE_SERVER) { 387 // TODO(pvalenzuela): Run the fake server via EmbeddedTestServer. 388 profile_sync_service->OverrideNetworkResourcesForTest( 389 make_scoped_ptr<syncer::NetworkResources>( 390 new fake_server::FakeServerNetworkResources(fake_server_.get()))); 391 } 392 393 clients_[index] = 394 ProfileSyncServiceHarness::Create( 395 GetProfile(index), 396 username_, 397 password_); 398 EXPECT_FALSE(GetClient(index) == NULL) << "Could not create Client " 399 << index << "."; 400 InitializeInvalidations(index); 401 402 test::WaitForBookmarkModelToLoad( 403 BookmarkModelFactory::GetForProfile(GetProfile(index))); 404 ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile( 405 GetProfile(index), Profile::EXPLICIT_ACCESS)); 406 ui_test_utils::WaitForTemplateURLServiceToLoad( 407 TemplateURLServiceFactory::GetForProfile(GetProfile(index))); 408} 409 410void SyncTest::InitializeInvalidations(int index) { 411 if (server_type_ == IN_PROCESS_FAKE_SERVER) { 412 CHECK(fake_server_.get()); 413 fake_server::FakeServerInvalidationService* invalidation_service = 414 static_cast<fake_server::FakeServerInvalidationService*>( 415 static_cast<invalidation::ProfileInvalidationProvider*>( 416 invalidation::ProfileInvalidationProviderFactory:: 417 GetInstance()->SetTestingFactoryAndUse( 418 GetProfile(index), 419 BuildFakeServerProfileInvalidationProvider))-> 420 GetInvalidationService()); 421 fake_server_->AddObserver(invalidation_service); 422 if (TestUsesSelfNotifications()) { 423 invalidation_service->EnableSelfNotifications(); 424 } else { 425 invalidation_service->DisableSelfNotifications(); 426 } 427 fake_server_invalidation_services_[index] = invalidation_service; 428 } else { 429 invalidation::P2PInvalidationService* p2p_invalidation_service = 430 static_cast<invalidation::P2PInvalidationService*>( 431 static_cast<invalidation::ProfileInvalidationProvider*>( 432 invalidation::ProfileInvalidationProviderFactory:: 433 GetInstance()->SetTestingFactoryAndUse( 434 GetProfile(index), 435 TestUsesSelfNotifications() ? 436 BuildSelfNotifyingP2PProfileInvalidationProvider : 437 BuildRealisticP2PProfileInvalidationProvider))-> 438 GetInvalidationService()); 439 p2p_invalidation_service->UpdateCredentials(username_, password_); 440 // Start listening for and emitting notifications of commits. 441 invalidation_forwarders_[index] = 442 new P2PInvalidationForwarder(clients_[index]->service(), 443 p2p_invalidation_service); 444 } 445} 446 447bool SyncTest::SetupSync() { 448 // Create sync profiles and clients if they haven't already been created. 449 if (profiles_.empty()) { 450 if (!SetupClients()) 451 LOG(FATAL) << "SetupClients() failed."; 452 } 453 454 // Sync each of the profiles. 455 for (int i = 0; i < num_clients_; ++i) { 456 if (!GetClient(i)->SetupSync()) 457 LOG(FATAL) << "SetupSync() failed."; 458 } 459 460 // Because clients may modify sync data as part of startup (for example local 461 // session-releated data is rewritten), we need to ensure all startup-based 462 // changes have propagated between the clients. 463 // 464 // Tests that don't use self-notifications can't await quiescense. They'll 465 // have to find their own way of waiting for an initial state if they really 466 // need such guarantees. 467 if (TestUsesSelfNotifications()) { 468 AwaitQuiescence(); 469 } 470 471 return true; 472} 473 474void SyncTest::CleanUpOnMainThread() { 475 for (size_t i = 0; i < clients_.size(); ++i) { 476 clients_[i]->service()->DisableForUser(); 477 } 478 479 // Some of the pending messages might rely on browser windows still being 480 // around, so run messages both before and after closing all browsers. 481 content::RunAllPendingInMessageLoop(); 482 // Close all browser windows. 483 chrome::CloseAllBrowsers(); 484 content::RunAllPendingInMessageLoop(); 485 486 if (fake_server_.get()) { 487 std::vector<fake_server::FakeServerInvalidationService*>::const_iterator it; 488 for (it = fake_server_invalidation_services_.begin(); 489 it != fake_server_invalidation_services_.end(); ++it) { 490 fake_server_->RemoveObserver(*it); 491 } 492 } 493 494 // All browsers should be closed at this point, or else we could see memory 495 // corruption in QuitBrowser(). 496 CHECK_EQ(0U, chrome::GetTotalBrowserCount()); 497 invalidation_forwarders_.clear(); 498 fake_server_invalidation_services_.clear(); 499 clients_.clear(); 500} 501 502void SyncTest::SetUpInProcessBrowserTestFixture() { 503 // We don't take a reference to |resolver|, but mock_host_resolver_override_ 504 // does, so effectively assumes ownership. 505 net::RuleBasedHostResolverProc* resolver = 506 new net::RuleBasedHostResolverProc(host_resolver()); 507 resolver->AllowDirectLookup("*.google.com"); 508 // On Linux, we use Chromium's NSS implementation which uses the following 509 // hosts for certificate verification. Without these overrides, running the 510 // integration tests on Linux causes error as we make external DNS lookups. 511 resolver->AllowDirectLookup("*.thawte.com"); 512 resolver->AllowDirectLookup("*.geotrust.com"); 513 resolver->AllowDirectLookup("*.gstatic.com"); 514 mock_host_resolver_override_.reset( 515 new net::ScopedDefaultHostResolverProc(resolver)); 516} 517 518void SyncTest::TearDownInProcessBrowserTestFixture() { 519 mock_host_resolver_override_.reset(); 520} 521 522void SyncTest::ReadPasswordFile() { 523 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 524 password_file_ = cl->GetSwitchValuePath(switches::kPasswordFileForTest); 525 if (password_file_.empty()) 526 LOG(FATAL) << "Can't run live server test without specifying --" 527 << switches::kPasswordFileForTest << "=<filename>"; 528 std::string file_contents; 529 base::ReadFileToString(password_file_, &file_contents); 530 ASSERT_NE(file_contents, "") << "Password file \"" 531 << password_file_.value() << "\" does not exist."; 532 std::vector<std::string> tokens; 533 std::string delimiters = "\r\n"; 534 Tokenize(file_contents, delimiters, &tokens); 535 ASSERT_EQ(2U, tokens.size()) << "Password file \"" 536 << password_file_.value() 537 << "\" must contain exactly two lines of text."; 538 username_ = tokens[0]; 539 password_ = tokens[1]; 540} 541 542void SyncTest::SetupMockGaiaResponses() { 543 factory_.reset(new net::URLFetcherImplFactory()); 544 fake_factory_.reset(new net::FakeURLFetcherFactory(factory_.get())); 545 fake_factory_->SetFakeResponse( 546 GaiaUrls::GetInstance()->get_user_info_url(), 547 "email=user@gmail.com\ndisplayEmail=user@gmail.com", 548 net::HTTP_OK, 549 net::URLRequestStatus::SUCCESS); 550 fake_factory_->SetFakeResponse( 551 GaiaUrls::GetInstance()->issue_auth_token_url(), 552 "auth", 553 net::HTTP_OK, 554 net::URLRequestStatus::SUCCESS); 555 fake_factory_->SetFakeResponse( 556 GURL(GoogleURLTracker::kSearchDomainCheckURL), 557 ".google.com", 558 net::HTTP_OK, 559 net::URLRequestStatus::SUCCESS); 560 fake_factory_->SetFakeResponse( 561 GaiaUrls::GetInstance()->client_login_to_oauth2_url(), 562 "some_response", 563 net::HTTP_OK, 564 net::URLRequestStatus::SUCCESS); 565 fake_factory_->SetFakeResponse( 566 GaiaUrls::GetInstance()->oauth2_token_url(), 567 "{" 568 " \"refresh_token\": \"rt1\"," 569 " \"access_token\": \"at1\"," 570 " \"expires_in\": 3600," 571 " \"token_type\": \"Bearer\"" 572 "}", 573 net::HTTP_OK, 574 net::URLRequestStatus::SUCCESS); 575 fake_factory_->SetFakeResponse( 576 GaiaUrls::GetInstance()->oauth_user_info_url(), 577 "{" 578 " \"id\": \"12345\"" 579 "}", 580 net::HTTP_OK, 581 net::URLRequestStatus::SUCCESS); 582 fake_factory_->SetFakeResponse( 583 GaiaUrls::GetInstance()->oauth1_login_url(), 584 "SID=sid\nLSID=lsid\nAuth=auth_token", 585 net::HTTP_OK, 586 net::URLRequestStatus::SUCCESS); 587 fake_factory_->SetFakeResponse( 588 GaiaUrls::GetInstance()->oauth2_revoke_url(), 589 "", 590 net::HTTP_OK, 591 net::URLRequestStatus::SUCCESS); 592} 593 594void SyncTest::SetOAuth2TokenResponse(const std::string& response_data, 595 net::HttpStatusCode response_code, 596 net::URLRequestStatus::Status status) { 597 ASSERT_TRUE(NULL != fake_factory_.get()); 598 fake_factory_->SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(), 599 response_data, response_code, status); 600} 601 602void SyncTest::ClearMockGaiaResponses() { 603 // Clear any mock gaia responses that might have been set. 604 if (fake_factory_) { 605 fake_factory_->ClearFakeResponses(); 606 fake_factory_.reset(); 607 } 608 609 // Cancel any outstanding URL fetches and destroy the URLFetcherImplFactory we 610 // created. 611 net::URLFetcher::CancelAll(); 612 factory_.reset(); 613} 614 615void SyncTest::DecideServerType() { 616 // Only set |server_type_| if it hasn't already been set. This allows for 617 // tests to explicitly set this value in each test class if needed. 618 if (server_type_ == SERVER_TYPE_UNDECIDED) { 619 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 620 if (!cl->HasSwitch(switches::kSyncServiceURL) && 621 !cl->HasSwitch(switches::kSyncServerCommandLine)) { 622 // If neither a sync server URL nor a sync server command line is 623 // provided, start up a local sync test server and point Chrome 624 // to its URL. This is the most common configuration, and the only 625 // one that makes sense for most developers. FakeServer is the 626 // current solution but some scenarios are only supported by the 627 // legacy python server. 628 switch (test_type_) { 629 case SINGLE_CLIENT: 630 case TWO_CLIENT: 631 case MULTIPLE_CLIENT: 632 server_type_ = IN_PROCESS_FAKE_SERVER; 633 break; 634 default: 635 server_type_ = LOCAL_PYTHON_SERVER; 636 } 637 } else if (cl->HasSwitch(switches::kSyncServiceURL) && 638 cl->HasSwitch(switches::kSyncServerCommandLine)) { 639 // If a sync server URL and a sync server command line are provided, 640 // start up a local sync server by running the command line. Chrome 641 // will connect to the server at the URL that was provided. 642 server_type_ = LOCAL_LIVE_SERVER; 643 } else if (cl->HasSwitch(switches::kSyncServiceURL) && 644 !cl->HasSwitch(switches::kSyncServerCommandLine)) { 645 // If a sync server URL is provided, but not a server command line, 646 // it is assumed that the server is already running. Chrome will 647 // automatically connect to it at the URL provided. There is nothing 648 // to do here. 649 server_type_ = EXTERNAL_LIVE_SERVER; 650 } else { 651 // If a sync server command line is provided, but not a server URL, 652 // we flag an error. 653 LOG(FATAL) << "Can't figure out how to run a server."; 654 } 655 } 656} 657 658// Start up a local sync server based on the value of server_type_, which 659// was determined from the command line parameters. 660void SyncTest::SetUpTestServerIfRequired() { 661 if (server_type_ == LOCAL_PYTHON_SERVER) { 662 if (!SetUpLocalPythonTestServer()) 663 LOG(FATAL) << "Failed to set up local python sync and XMPP servers"; 664 SetupMockGaiaResponses(); 665 } else if (server_type_ == LOCAL_LIVE_SERVER) { 666 // Using mock gaia credentials requires the use of a mock XMPP server. 667 if (username_ == "user@gmail.com" && !SetUpLocalPythonTestServer()) 668 LOG(FATAL) << "Failed to set up local python XMPP server"; 669 if (!SetUpLocalTestServer()) 670 LOG(FATAL) << "Failed to set up local test server"; 671 } else if (server_type_ == IN_PROCESS_FAKE_SERVER) { 672 fake_server_.reset(new fake_server::FakeServer()); 673 SetupMockGaiaResponses(); 674 } else if (server_type_ == EXTERNAL_LIVE_SERVER) { 675 // Nothing to do; we'll just talk to the URL we were given. 676 } else { 677 LOG(FATAL) << "Don't know which server environment to run test in."; 678 } 679} 680 681bool SyncTest::SetUpLocalPythonTestServer() { 682 EXPECT_TRUE(sync_server_.Start()) 683 << "Could not launch local python test server."; 684 685 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 686 if (server_type_ == LOCAL_PYTHON_SERVER) { 687 std::string sync_service_url = sync_server_.GetURL("chromiumsync").spec(); 688 cl->AppendSwitchASCII(switches::kSyncServiceURL, sync_service_url); 689 DVLOG(1) << "Started local python sync server at " << sync_service_url; 690 } 691 692 int xmpp_port = 0; 693 if (!sync_server_.server_data().GetInteger("xmpp_port", &xmpp_port)) { 694 LOG(ERROR) << "Could not find valid xmpp_port value"; 695 return false; 696 } 697 if ((xmpp_port <= 0) || (xmpp_port > kuint16max)) { 698 LOG(ERROR) << "Invalid xmpp port: " << xmpp_port; 699 return false; 700 } 701 702 net::HostPortPair xmpp_host_port_pair(sync_server_.host_port_pair()); 703 xmpp_host_port_pair.set_port(xmpp_port); 704 xmpp_port_.reset(new net::ScopedPortException(xmpp_port)); 705 706 if (!cl->HasSwitch(invalidation::switches::kSyncNotificationHostPort)) { 707 cl->AppendSwitchASCII(invalidation::switches::kSyncNotificationHostPort, 708 xmpp_host_port_pair.ToString()); 709 // The local XMPP server only supports insecure connections. 710 cl->AppendSwitch(invalidation::switches::kSyncAllowInsecureXmppConnection); 711 } 712 DVLOG(1) << "Started local python XMPP server at " 713 << xmpp_host_port_pair.ToString(); 714 715 return true; 716} 717 718bool SyncTest::SetUpLocalTestServer() { 719 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 720 base::CommandLine::StringType server_cmdline_string = 721 cl->GetSwitchValueNative(switches::kSyncServerCommandLine); 722 base::CommandLine::StringVector server_cmdline_vector; 723 base::CommandLine::StringType delimiters(FILE_PATH_LITERAL(" ")); 724 Tokenize(server_cmdline_string, delimiters, &server_cmdline_vector); 725 base::CommandLine server_cmdline(server_cmdline_vector); 726 base::LaunchOptions options; 727#if defined(OS_WIN) 728 options.start_hidden = true; 729#endif 730 if (!base::LaunchProcess(server_cmdline, options, &test_server_handle_)) 731 LOG(ERROR) << "Could not launch local test server."; 732 733 const base::TimeDelta kMaxWaitTime = TestTimeouts::action_max_timeout(); 734 const int kNumIntervals = 15; 735 if (WaitForTestServerToStart(kMaxWaitTime, kNumIntervals)) { 736 DVLOG(1) << "Started local test server at " 737 << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << "."; 738 return true; 739 } else { 740 LOG(ERROR) << "Could not start local test server at " 741 << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << "."; 742 return false; 743 } 744} 745 746bool SyncTest::TearDownLocalPythonTestServer() { 747 if (!sync_server_.Stop()) { 748 LOG(ERROR) << "Could not stop local python test server."; 749 return false; 750 } 751 xmpp_port_.reset(); 752 return true; 753} 754 755bool SyncTest::TearDownLocalTestServer() { 756 if (test_server_handle_ != base::kNullProcessHandle) { 757 EXPECT_TRUE(base::KillProcess(test_server_handle_, 0, false)) 758 << "Could not stop local test server."; 759 base::CloseProcessHandle(test_server_handle_); 760 test_server_handle_ = base::kNullProcessHandle; 761 } 762 return true; 763} 764 765bool SyncTest::WaitForTestServerToStart(base::TimeDelta wait, int intervals) { 766 for (int i = 0; i < intervals; ++i) { 767 if (IsTestServerRunning()) 768 return true; 769 base::PlatformThread::Sleep(wait / intervals); 770 } 771 return false; 772} 773 774bool SyncTest::IsTestServerRunning() { 775 base::CommandLine* cl = base::CommandLine::ForCurrentProcess(); 776 std::string sync_url = cl->GetSwitchValueASCII(switches::kSyncServiceURL); 777 GURL sync_url_status(sync_url.append("/healthz")); 778 SyncServerStatusChecker delegate; 779 scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create( 780 sync_url_status, net::URLFetcher::GET, &delegate)); 781 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | 782 net::LOAD_DO_NOT_SEND_COOKIES | 783 net::LOAD_DO_NOT_SAVE_COOKIES); 784 fetcher->SetRequestContext(g_browser_process->system_request_context()); 785 fetcher->Start(); 786 content::RunMessageLoop(); 787 return delegate.running(); 788} 789 790void SyncTest::EnableNetwork(Profile* profile) { 791 // TODO(pvalenzuela): Remove this restriction when FakeServer's observers 792 // (namely FakeServerInvaldationService) are aware of a network disconnect. 793 ASSERT_NE(IN_PROCESS_FAKE_SERVER, server_type_) 794 << "FakeServer does not support EnableNetwork."; 795 SetProxyConfig(profile->GetRequestContext(), 796 net::ProxyConfig::CreateDirect()); 797 if (notifications_enabled_) { 798 EnableNotificationsImpl(); 799 } 800 // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed. 801 net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); 802} 803 804void SyncTest::DisableNetwork(Profile* profile) { 805 // TODO(pvalenzuela): Remove this restriction when FakeServer's observers 806 // (namely FakeServerInvaldationService) are aware of a network disconnect. 807 ASSERT_NE(IN_PROCESS_FAKE_SERVER, server_type_) 808 << "FakeServer does not support DisableNetwork."; 809 DisableNotificationsImpl(); 810 // Set the current proxy configuration to a nonexistent proxy to effectively 811 // disable networking. 812 net::ProxyConfig config; 813 config.proxy_rules().ParseFromString("http=127.0.0.1:0"); 814 SetProxyConfig(profile->GetRequestContext(), config); 815 // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed. 816 net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); 817} 818 819bool SyncTest::TestUsesSelfNotifications() { 820 return true; 821} 822 823bool SyncTest::EnableEncryption(int index) { 824 ProfileSyncService* service = GetClient(index)->service(); 825 826 if (::IsEncryptionComplete(service)) 827 return true; 828 829 service->EnableEncryptEverything(); 830 831 // In order to kick off the encryption we have to reconfigure. Just grab the 832 // currently synced types and use them. 833 const syncer::ModelTypeSet synced_datatypes = 834 service->GetPreferredDataTypes(); 835 bool sync_everything = synced_datatypes.Equals(syncer::ModelTypeSet::All()); 836 service->OnUserChoseDatatypes(sync_everything, synced_datatypes); 837 838 // Wait some time to let the enryption finish. 839 EncryptionChecker checker(service); 840 checker.Wait(); 841 842 return !checker.TimedOut(); 843} 844 845bool SyncTest::IsEncryptionComplete(int index) { 846 return ::IsEncryptionComplete(GetClient(index)->service()); 847} 848 849bool SyncTest::AwaitQuiescence() { 850 return ProfileSyncServiceHarness::AwaitQuiescence(clients()); 851} 852 853bool SyncTest::ServerSupportsNotificationControl() const { 854 EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_); 855 856 // Supported only if we're using the python testserver. 857 return server_type_ == LOCAL_PYTHON_SERVER; 858} 859 860void SyncTest::DisableNotificationsImpl() { 861 ASSERT_TRUE(ServerSupportsNotificationControl()); 862 std::string path = "chromiumsync/disablenotifications"; 863 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 864 ASSERT_EQ("Notifications disabled", 865 base::UTF16ToASCII( 866 browser()->tab_strip_model()->GetActiveWebContents()-> 867 GetTitle())); 868} 869 870void SyncTest::DisableNotifications() { 871 DisableNotificationsImpl(); 872 notifications_enabled_ = false; 873} 874 875void SyncTest::EnableNotificationsImpl() { 876 ASSERT_TRUE(ServerSupportsNotificationControl()); 877 std::string path = "chromiumsync/enablenotifications"; 878 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 879 ASSERT_EQ("Notifications enabled", 880 base::UTF16ToASCII( 881 browser()->tab_strip_model()->GetActiveWebContents()-> 882 GetTitle())); 883} 884 885void SyncTest::EnableNotifications() { 886 EnableNotificationsImpl(); 887 notifications_enabled_ = true; 888} 889 890void SyncTest::TriggerNotification(syncer::ModelTypeSet changed_types) { 891 ASSERT_TRUE(ServerSupportsNotificationControl()); 892 const std::string& data = 893 syncer::P2PNotificationData( 894 "from_server", 895 syncer::NOTIFY_ALL, 896 syncer::ObjectIdInvalidationMap::InvalidateAll( 897 syncer::ModelTypeSetToObjectIdSet(changed_types))).ToString(); 898 const std::string& path = 899 std::string("chromiumsync/sendnotification?channel=") + 900 syncer::kSyncP2PNotificationChannel + "&data=" + data; 901 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 902 ASSERT_EQ("Notification sent", 903 base::UTF16ToASCII( 904 browser()->tab_strip_model()->GetActiveWebContents()-> 905 GetTitle())); 906} 907 908bool SyncTest::ServerSupportsErrorTriggering() const { 909 EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_); 910 911 // Supported only if we're using the python testserver. 912 return server_type_ == LOCAL_PYTHON_SERVER; 913} 914 915void SyncTest::TriggerMigrationDoneError(syncer::ModelTypeSet model_types) { 916 ASSERT_TRUE(ServerSupportsErrorTriggering()); 917 std::string path = "chromiumsync/migrate"; 918 char joiner = '?'; 919 for (syncer::ModelTypeSet::Iterator it = model_types.First(); 920 it.Good(); it.Inc()) { 921 path.append( 922 base::StringPrintf( 923 "%ctype=%d", joiner, 924 syncer::GetSpecificsFieldNumberFromModelType(it.Get()))); 925 joiner = '&'; 926 } 927 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 928 ASSERT_EQ("Migration: 200", 929 base::UTF16ToASCII( 930 browser()->tab_strip_model()->GetActiveWebContents()-> 931 GetTitle())); 932} 933 934void SyncTest::TriggerTransientError() { 935 ASSERT_TRUE(ServerSupportsErrorTriggering()); 936 std::string path = "chromiumsync/transienterror"; 937 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 938 ASSERT_EQ("Transient error", 939 base::UTF16ToASCII( 940 browser()->tab_strip_model()->GetActiveWebContents()-> 941 GetTitle())); 942} 943 944void SyncTest::TriggerXmppAuthError() { 945 ASSERT_TRUE(ServerSupportsErrorTriggering()); 946 std::string path = "chromiumsync/xmppcred"; 947 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 948} 949 950namespace { 951 952sync_pb::SyncEnums::ErrorType 953 GetClientToServerResponseErrorType( 954 syncer::SyncProtocolErrorType error) { 955 switch (error) { 956 case syncer::SYNC_SUCCESS: 957 return sync_pb::SyncEnums::SUCCESS; 958 case syncer::NOT_MY_BIRTHDAY: 959 return sync_pb::SyncEnums::NOT_MY_BIRTHDAY; 960 case syncer::THROTTLED: 961 return sync_pb::SyncEnums::THROTTLED; 962 case syncer::CLEAR_PENDING: 963 return sync_pb::SyncEnums::CLEAR_PENDING; 964 case syncer::TRANSIENT_ERROR: 965 return sync_pb::SyncEnums::TRANSIENT_ERROR; 966 case syncer::MIGRATION_DONE: 967 return sync_pb::SyncEnums::MIGRATION_DONE; 968 case syncer::UNKNOWN_ERROR: 969 return sync_pb::SyncEnums::UNKNOWN; 970 case syncer::INVALID_CREDENTIAL: 971 NOTREACHED(); // NOTREACHED() because auth error is not set through 972 // error code in sync response. 973 return sync_pb::SyncEnums::UNKNOWN; 974 case syncer::DISABLED_BY_ADMIN: 975 return sync_pb::SyncEnums::DISABLED_BY_ADMIN; 976 case syncer::USER_ROLLBACK: 977 return sync_pb::SyncEnums::USER_ROLLBACK; 978 case syncer::NON_RETRIABLE_ERROR: 979 return sync_pb::SyncEnums::UNKNOWN; 980 } 981 return sync_pb::SyncEnums::UNKNOWN; 982} 983 984sync_pb::SyncEnums::Action GetClientToServerResponseAction( 985 const syncer::ClientAction& action) { 986 switch (action) { 987 case syncer::UPGRADE_CLIENT: 988 return sync_pb::SyncEnums::UPGRADE_CLIENT; 989 case syncer::CLEAR_USER_DATA_AND_RESYNC: 990 return sync_pb::SyncEnums::CLEAR_USER_DATA_AND_RESYNC; 991 case syncer::ENABLE_SYNC_ON_ACCOUNT: 992 return sync_pb::SyncEnums::ENABLE_SYNC_ON_ACCOUNT; 993 case syncer::STOP_AND_RESTART_SYNC: 994 return sync_pb::SyncEnums::STOP_AND_RESTART_SYNC; 995 case syncer::DISABLE_SYNC_ON_CLIENT: 996 return sync_pb::SyncEnums::DISABLE_SYNC_ON_CLIENT; 997 case syncer::STOP_SYNC_FOR_DISABLED_ACCOUNT: 998 case syncer::DISABLE_SYNC_AND_ROLLBACK: 999 case syncer::ROLLBACK_DONE: 1000 NOTREACHED(); // No corresponding proto action for these. Shouldn't 1001 // test. 1002 return sync_pb::SyncEnums::UNKNOWN_ACTION; 1003 case syncer::UNKNOWN_ACTION: 1004 return sync_pb::SyncEnums::UNKNOWN_ACTION; 1005 } 1006 return sync_pb::SyncEnums::UNKNOWN_ACTION; 1007} 1008 1009} // namespace 1010 1011void SyncTest::TriggerSyncError(const syncer::SyncProtocolError& error, 1012 SyncErrorFrequency frequency) { 1013 ASSERT_TRUE(ServerSupportsErrorTriggering()); 1014 std::string path = "chromiumsync/error"; 1015 int error_type = 1016 static_cast<int>(GetClientToServerResponseErrorType( 1017 error.error_type)); 1018 int action = static_cast<int>(GetClientToServerResponseAction( 1019 error.action)); 1020 1021 path.append(base::StringPrintf("?error=%d", error_type)); 1022 path.append(base::StringPrintf("&action=%d", action)); 1023 1024 path.append(base::StringPrintf("&error_description=%s", 1025 error.error_description.c_str())); 1026 path.append(base::StringPrintf("&url=%s", error.url.c_str())); 1027 path.append(base::StringPrintf("&frequency=%d", frequency)); 1028 1029 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 1030 std::string output = base::UTF16ToASCII( 1031 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); 1032 ASSERT_TRUE(output.find("SetError: 200") != base::string16::npos); 1033} 1034 1035void SyncTest::TriggerCreateSyncedBookmarks() { 1036 ASSERT_TRUE(ServerSupportsErrorTriggering()); 1037 std::string path = "chromiumsync/createsyncedbookmarks"; 1038 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 1039 ASSERT_EQ("Synced Bookmarks", 1040 base::UTF16ToASCII( 1041 browser()->tab_strip_model()->GetActiveWebContents()-> 1042 GetTitle())); 1043} 1044 1045void SyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter, 1046 const net::ProxyConfig& proxy_config) { 1047 base::WaitableEvent done(false, false); 1048 BrowserThread::PostTask( 1049 BrowserThread::IO, FROM_HERE, 1050 base::Bind(&SetProxyConfigCallback, &done, 1051 make_scoped_refptr(context_getter), proxy_config)); 1052 done.Wait(); 1053} 1054 1055fake_server::FakeServer* SyncTest::GetFakeServer() const { 1056 return fake_server_.get(); 1057} 1058