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