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