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