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