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