sync_test.cc revision bbcdd45c55eb7c4641ab97aef9889b0fc828e7d3
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 65using content::BrowserThread; 66using invalidation::InvalidationServiceFactory; 67 68namespace switches { 69const char kPasswordFileForTest[] = "password-file-for-test"; 70const char kSyncUserForTest[] = "sync-user-for-test"; 71const char kSyncPasswordForTest[] = "sync-password-for-test"; 72const char kSyncServerCommandLine[] = "sync-server-command-line"; 73} 74 75// Helper class that checks whether a sync test server is running or not. 76class 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 95void 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 106SyncTest::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 131SyncTest::~SyncTest() {} 132 133void 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 183void 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 197void SyncTest::SetUpCommandLine(CommandLine* cl) { 198 AddTestSwitches(cl); 199 AddOptionalTypesToCommandLine(cl); 200} 201 202void 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 216void SyncTest::AddOptionalTypesToCommandLine(CommandLine* cl) {} 217 218// static 219Profile* 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 235Profile* 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 243Browser* 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 251ProfileSyncServiceHarness* 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 259Profile* SyncTest::verifier() { 260 if (verifier_ == NULL) 261 LOG(FATAL) << "SetupClients() has not yet been called."; 262 return verifier_; 263} 264 265void SyncTest::DisableVerifier() { 266 use_verifier_ = false; 267} 268 269bool 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 297void 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 335bool 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 365void SyncTest::CleanUpOnMainThread() { 366 // Some of the pending messages might rely on browser windows still being 367 // around, so run messages both before and after closing all browsers. 368 content::RunAllPendingInMessageLoop(); 369 // Close all browser windows. 370 chrome::CloseAllBrowsers(); 371 content::RunAllPendingInMessageLoop(); 372 373 // All browsers should be closed at this point, or else we could see memory 374 // corruption in QuitBrowser(). 375 CHECK_EQ(0U, chrome::GetTotalBrowserCount()); 376 clients_.clear(); 377} 378 379void SyncTest::SetUpInProcessBrowserTestFixture() { 380 // We don't take a reference to |resolver|, but mock_host_resolver_override_ 381 // does, so effectively assumes ownership. 382 net::RuleBasedHostResolverProc* resolver = 383 new net::RuleBasedHostResolverProc(host_resolver()); 384 resolver->AllowDirectLookup("*.google.com"); 385 // On Linux, we use Chromium's NSS implementation which uses the following 386 // hosts for certificate verification. Without these overrides, running the 387 // integration tests on Linux causes error as we make external DNS lookups. 388 resolver->AllowDirectLookup("*.thawte.com"); 389 resolver->AllowDirectLookup("*.geotrust.com"); 390 resolver->AllowDirectLookup("*.gstatic.com"); 391 mock_host_resolver_override_.reset( 392 new net::ScopedDefaultHostResolverProc(resolver)); 393} 394 395void SyncTest::TearDownInProcessBrowserTestFixture() { 396 mock_host_resolver_override_.reset(); 397} 398 399void SyncTest::ReadPasswordFile() { 400 CommandLine* cl = CommandLine::ForCurrentProcess(); 401 password_file_ = cl->GetSwitchValuePath(switches::kPasswordFileForTest); 402 if (password_file_.empty()) 403 LOG(FATAL) << "Can't run live server test without specifying --" 404 << switches::kPasswordFileForTest << "=<filename>"; 405 std::string file_contents; 406 file_util::ReadFileToString(password_file_, &file_contents); 407 ASSERT_NE(file_contents, "") << "Password file \"" 408 << password_file_.value() << "\" does not exist."; 409 std::vector<std::string> tokens; 410 std::string delimiters = "\r\n"; 411 Tokenize(file_contents, delimiters, &tokens); 412 ASSERT_EQ(2U, tokens.size()) << "Password file \"" 413 << password_file_.value() 414 << "\" must contain exactly two lines of text."; 415 username_ = tokens[0]; 416 password_ = tokens[1]; 417} 418 419void SyncTest::SetupMockGaiaResponses() { 420 username_ = "user@gmail.com"; 421 password_ = "password"; 422 factory_.reset(new net::URLFetcherImplFactory()); 423 fake_factory_.reset(new net::FakeURLFetcherFactory(factory_.get())); 424 fake_factory_->SetFakeResponse( 425 GaiaUrls::GetInstance()->client_login_url(), 426 "SID=sid\nLSID=lsid", 427 true); 428 fake_factory_->SetFakeResponse( 429 GaiaUrls::GetInstance()->get_user_info_url(), 430 "email=user@gmail.com\ndisplayEmail=user@gmail.com", 431 true); 432 fake_factory_->SetFakeResponse( 433 GaiaUrls::GetInstance()->issue_auth_token_url(), 434 "auth", 435 true); 436 fake_factory_->SetFakeResponse( 437 GoogleURLTracker::kSearchDomainCheckURL, 438 ".google.com", 439 true); 440 fake_factory_->SetFakeResponse( 441 GaiaUrls::GetInstance()->client_login_to_oauth2_url(), 442 "some_response", 443 true); 444 fake_factory_->SetFakeResponse( 445 GaiaUrls::GetInstance()->oauth2_token_url(), 446 "{" 447 " \"refresh_token\": \"rt1\"," 448 " \"access_token\": \"at1\"," 449 " \"expires_in\": 3600," 450 " \"token_type\": \"Bearer\"" 451 "}", 452 true); 453 fake_factory_->SetFakeResponse( 454 GaiaUrls::GetInstance()->oauth1_login_url(), 455 "SID=sid\nLSID=lsid\nAuth=auth_token", 456 true); 457} 458 459void SyncTest::ClearMockGaiaResponses() { 460 // Clear any mock gaia responses that might have been set. 461 if (fake_factory_) { 462 fake_factory_->ClearFakeResponses(); 463 fake_factory_.reset(); 464 } 465 466 // Cancel any outstanding URL fetches and destroy the URLFetcherImplFactory we 467 // created. 468 net::URLFetcher::CancelAll(); 469 factory_.reset(); 470} 471 472// Start up a local sync server based on the value of server_type_, which 473// was determined from the command line parameters. 474void SyncTest::SetUpTestServerIfRequired() { 475 if (server_type_ == LOCAL_PYTHON_SERVER) { 476 if (!SetUpLocalPythonTestServer()) 477 LOG(FATAL) << "Failed to set up local python sync and XMPP servers"; 478 } else if (server_type_ == LOCAL_LIVE_SERVER) { 479 // Using mock gaia credentials requires the use of a mock XMPP server. 480 if (username_ == "user@gmail.com" && !SetUpLocalPythonTestServer()) 481 LOG(FATAL) << "Failed to set up local python XMPP server"; 482 if (!SetUpLocalTestServer()) 483 LOG(FATAL) << "Failed to set up local test server"; 484 } else if (server_type_ == EXTERNAL_LIVE_SERVER) { 485 // Nothing to do; we'll just talk to the URL we were given. 486 } else { 487 LOG(FATAL) << "Don't know which server environment to run test in."; 488 } 489} 490 491bool SyncTest::SetUpLocalPythonTestServer() { 492 EXPECT_TRUE(sync_server_.Start()) 493 << "Could not launch local python test server."; 494 495 CommandLine* cl = CommandLine::ForCurrentProcess(); 496 if (server_type_ == LOCAL_PYTHON_SERVER) { 497 std::string sync_service_url = sync_server_.GetURL("chromiumsync").spec(); 498 cl->AppendSwitchASCII(switches::kSyncServiceURL, sync_service_url); 499 DVLOG(1) << "Started local python sync server at " << sync_service_url; 500 } 501 502 int xmpp_port = 0; 503 if (!sync_server_.server_data().GetInteger("xmpp_port", &xmpp_port)) { 504 LOG(ERROR) << "Could not find valid xmpp_port value"; 505 return false; 506 } 507 if ((xmpp_port <= 0) || (xmpp_port > kuint16max)) { 508 LOG(ERROR) << "Invalid xmpp port: " << xmpp_port; 509 return false; 510 } 511 512 net::HostPortPair xmpp_host_port_pair(sync_server_.host_port_pair()); 513 xmpp_host_port_pair.set_port(xmpp_port); 514 xmpp_port_.reset(new net::ScopedPortException(xmpp_port)); 515 516 if (!cl->HasSwitch(switches::kSyncNotificationHostPort)) { 517 cl->AppendSwitchASCII(switches::kSyncNotificationHostPort, 518 xmpp_host_port_pair.ToString()); 519 // The local XMPP server only supports insecure connections. 520 cl->AppendSwitch(switches::kSyncAllowInsecureXmppConnection); 521 } 522 DVLOG(1) << "Started local python XMPP server at " 523 << xmpp_host_port_pair.ToString(); 524 525 return true; 526} 527 528bool SyncTest::SetUpLocalTestServer() { 529 CommandLine* cl = CommandLine::ForCurrentProcess(); 530 CommandLine::StringType server_cmdline_string = cl->GetSwitchValueNative( 531 switches::kSyncServerCommandLine); 532 CommandLine::StringVector server_cmdline_vector; 533 CommandLine::StringType delimiters(FILE_PATH_LITERAL(" ")); 534 Tokenize(server_cmdline_string, delimiters, &server_cmdline_vector); 535 CommandLine server_cmdline(server_cmdline_vector); 536 base::LaunchOptions options; 537#if defined(OS_WIN) 538 options.start_hidden = true; 539#endif 540 if (!base::LaunchProcess(server_cmdline, options, &test_server_handle_)) 541 LOG(ERROR) << "Could not launch local test server."; 542 543 const base::TimeDelta kMaxWaitTime = TestTimeouts::action_max_timeout(); 544 const int kNumIntervals = 15; 545 if (WaitForTestServerToStart(kMaxWaitTime, kNumIntervals)) { 546 DVLOG(1) << "Started local test server at " 547 << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << "."; 548 return true; 549 } else { 550 LOG(ERROR) << "Could not start local test server at " 551 << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << "."; 552 return false; 553 } 554} 555 556bool SyncTest::TearDownLocalPythonTestServer() { 557 if (!sync_server_.Stop()) { 558 LOG(ERROR) << "Could not stop local python test server."; 559 return false; 560 } 561 xmpp_port_.reset(); 562 return true; 563} 564 565bool SyncTest::TearDownLocalTestServer() { 566 if (test_server_handle_ != base::kNullProcessHandle) { 567 EXPECT_TRUE(base::KillProcess(test_server_handle_, 0, false)) 568 << "Could not stop local test server."; 569 base::CloseProcessHandle(test_server_handle_); 570 test_server_handle_ = base::kNullProcessHandle; 571 } 572 return true; 573} 574 575bool SyncTest::WaitForTestServerToStart(base::TimeDelta wait, int intervals) { 576 for (int i = 0; i < intervals; ++i) { 577 if (IsTestServerRunning()) 578 return true; 579 base::PlatformThread::Sleep(wait / intervals); 580 } 581 return false; 582} 583 584bool SyncTest::IsTestServerRunning() { 585 CommandLine* cl = CommandLine::ForCurrentProcess(); 586 std::string sync_url = cl->GetSwitchValueASCII(switches::kSyncServiceURL); 587 GURL sync_url_status(sync_url.append("/healthz")); 588 SyncServerStatusChecker delegate; 589 scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create( 590 sync_url_status, net::URLFetcher::GET, &delegate)); 591 fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE | 592 net::LOAD_DO_NOT_SEND_COOKIES | 593 net::LOAD_DO_NOT_SAVE_COOKIES); 594 fetcher->SetRequestContext(g_browser_process->system_request_context()); 595 fetcher->Start(); 596 content::RunMessageLoop(); 597 return delegate.running(); 598} 599 600void SyncTest::EnableNetwork(Profile* profile) { 601 SetProxyConfig(profile->GetRequestContext(), 602 net::ProxyConfig::CreateDirect()); 603 if (notifications_enabled_) { 604 EnableNotificationsImpl(); 605 } 606 // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed. 607 net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); 608} 609 610void SyncTest::DisableNetwork(Profile* profile) { 611 DisableNotificationsImpl(); 612 // Set the current proxy configuration to a nonexistent proxy to effectively 613 // disable networking. 614 net::ProxyConfig config; 615 config.proxy_rules().ParseFromString("http=127.0.0.1:0"); 616 SetProxyConfig(profile->GetRequestContext(), config); 617 // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed. 618 net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests(); 619} 620 621bool SyncTest::EnableEncryption(int index, syncer::ModelType type) { 622 return GetClient(index)->EnableEncryptionForType(type); 623} 624 625bool SyncTest::IsEncrypted(int index, syncer::ModelType type) { 626 return GetClient(index)->IsTypeEncrypted(type); 627} 628 629bool SyncTest::AwaitQuiescence() { 630 return ProfileSyncServiceHarness::AwaitQuiescence(clients()); 631} 632 633bool SyncTest::ServerSupportsNotificationControl() const { 634 EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_); 635 636 // Supported only if we're using the python testserver. 637 return server_type_ == LOCAL_PYTHON_SERVER; 638} 639 640void SyncTest::DisableNotificationsImpl() { 641 ASSERT_TRUE(ServerSupportsNotificationControl()); 642 std::string path = "chromiumsync/disablenotifications"; 643 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 644 ASSERT_EQ("Notifications disabled", 645 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 646 GetTitle())); 647} 648 649void SyncTest::DisableNotifications() { 650 DisableNotificationsImpl(); 651 notifications_enabled_ = false; 652} 653 654void SyncTest::EnableNotificationsImpl() { 655 ASSERT_TRUE(ServerSupportsNotificationControl()); 656 std::string path = "chromiumsync/enablenotifications"; 657 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 658 ASSERT_EQ("Notifications enabled", 659 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 660 GetTitle())); 661} 662 663void SyncTest::EnableNotifications() { 664 EnableNotificationsImpl(); 665 notifications_enabled_ = true; 666} 667 668void SyncTest::TriggerNotification(syncer::ModelTypeSet changed_types) { 669 ASSERT_TRUE(ServerSupportsNotificationControl()); 670 const std::string& data = 671 syncer::P2PNotificationData( 672 "from_server", 673 syncer::NOTIFY_ALL, 674 syncer::ObjectIdSetToInvalidationMap( 675 syncer::ModelTypeSetToObjectIdSet(changed_types), 676 syncer::Invalidation::kUnknownVersion, 677 std::string()) 678 ).ToString(); 679 const std::string& path = 680 std::string("chromiumsync/sendnotification?channel=") + 681 syncer::kSyncP2PNotificationChannel + "&data=" + data; 682 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 683 ASSERT_EQ("Notification sent", 684 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 685 GetTitle())); 686} 687 688bool SyncTest::ServerSupportsErrorTriggering() const { 689 EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_); 690 691 // Supported only if we're using the python testserver. 692 return server_type_ == LOCAL_PYTHON_SERVER; 693} 694 695void SyncTest::TriggerMigrationDoneError(syncer::ModelTypeSet model_types) { 696 ASSERT_TRUE(ServerSupportsErrorTriggering()); 697 std::string path = "chromiumsync/migrate"; 698 char joiner = '?'; 699 for (syncer::ModelTypeSet::Iterator it = model_types.First(); 700 it.Good(); it.Inc()) { 701 path.append( 702 base::StringPrintf( 703 "%ctype=%d", joiner, 704 syncer::GetSpecificsFieldNumberFromModelType(it.Get()))); 705 joiner = '&'; 706 } 707 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 708 ASSERT_EQ("Migration: 200", 709 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 710 GetTitle())); 711} 712 713void SyncTest::TriggerBirthdayError() { 714 ASSERT_TRUE(ServerSupportsErrorTriggering()); 715 std::string path = "chromiumsync/birthdayerror"; 716 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 717 ASSERT_EQ("Birthday error", 718 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 719 GetTitle())); 720} 721 722void SyncTest::TriggerTransientError() { 723 ASSERT_TRUE(ServerSupportsErrorTriggering()); 724 std::string path = "chromiumsync/transienterror"; 725 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 726 ASSERT_EQ("Transient error", 727 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 728 GetTitle())); 729} 730 731void SyncTest::TriggerAuthError() { 732 ASSERT_TRUE(ServerSupportsErrorTriggering()); 733 std::string path = "chromiumsync/cred"; 734 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 735} 736 737void SyncTest::TriggerXmppAuthError() { 738 ASSERT_TRUE(ServerSupportsErrorTriggering()); 739 std::string path = "chromiumsync/xmppcred"; 740 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 741} 742 743namespace { 744 745sync_pb::SyncEnums::ErrorType 746 GetClientToServerResponseErrorType( 747 syncer::SyncProtocolErrorType error) { 748 switch (error) { 749 case syncer::SYNC_SUCCESS: 750 return sync_pb::SyncEnums::SUCCESS; 751 case syncer::NOT_MY_BIRTHDAY: 752 return sync_pb::SyncEnums::NOT_MY_BIRTHDAY; 753 case syncer::THROTTLED: 754 return sync_pb::SyncEnums::THROTTLED; 755 case syncer::CLEAR_PENDING: 756 return sync_pb::SyncEnums::CLEAR_PENDING; 757 case syncer::TRANSIENT_ERROR: 758 return sync_pb::SyncEnums::TRANSIENT_ERROR; 759 case syncer::MIGRATION_DONE: 760 return sync_pb::SyncEnums::MIGRATION_DONE; 761 case syncer::UNKNOWN_ERROR: 762 return sync_pb::SyncEnums::UNKNOWN; 763 default: 764 NOTREACHED(); 765 return sync_pb::SyncEnums::UNKNOWN; 766 } 767} 768 769sync_pb::SyncEnums::Action GetClientToServerResponseAction( 770 const syncer::ClientAction& action) { 771 switch (action) { 772 case syncer::UPGRADE_CLIENT: 773 return sync_pb::SyncEnums::UPGRADE_CLIENT; 774 case syncer::CLEAR_USER_DATA_AND_RESYNC: 775 return sync_pb::SyncEnums::CLEAR_USER_DATA_AND_RESYNC; 776 case syncer::ENABLE_SYNC_ON_ACCOUNT: 777 return sync_pb::SyncEnums::ENABLE_SYNC_ON_ACCOUNT; 778 case syncer::STOP_AND_RESTART_SYNC: 779 return sync_pb::SyncEnums::STOP_AND_RESTART_SYNC; 780 case syncer::DISABLE_SYNC_ON_CLIENT: 781 return sync_pb::SyncEnums::DISABLE_SYNC_ON_CLIENT; 782 case syncer::UNKNOWN_ACTION: 783 return sync_pb::SyncEnums::UNKNOWN_ACTION; 784 default: 785 NOTREACHED(); 786 return sync_pb::SyncEnums::UNKNOWN_ACTION; 787 } 788} 789 790} // namespace 791 792void SyncTest::TriggerSyncError(const syncer::SyncProtocolError& error, 793 SyncErrorFrequency frequency) { 794 ASSERT_TRUE(ServerSupportsErrorTriggering()); 795 std::string path = "chromiumsync/error"; 796 int error_type = 797 static_cast<int>(GetClientToServerResponseErrorType( 798 error.error_type)); 799 int action = static_cast<int>(GetClientToServerResponseAction( 800 error.action)); 801 802 path.append(base::StringPrintf("?error=%d", error_type)); 803 path.append(base::StringPrintf("&action=%d", action)); 804 805 path.append(base::StringPrintf("&error_description=%s", 806 error.error_description.c_str())); 807 path.append(base::StringPrintf("&url=%s", error.url.c_str())); 808 path.append(base::StringPrintf("&frequency=%d", frequency)); 809 810 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 811 std::string output = UTF16ToASCII( 812 browser()->tab_strip_model()->GetActiveWebContents()->GetTitle()); 813 ASSERT_TRUE(output.find("SetError: 200") != string16::npos); 814} 815 816void SyncTest::TriggerCreateSyncedBookmarks() { 817 ASSERT_TRUE(ServerSupportsErrorTriggering()); 818 std::string path = "chromiumsync/createsyncedbookmarks"; 819 ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path)); 820 ASSERT_EQ("Synced Bookmarks", 821 UTF16ToASCII(browser()->tab_strip_model()->GetActiveWebContents()-> 822 GetTitle())); 823} 824 825int SyncTest::NumberOfDefaultSyncItems() const { 826 return number_of_default_sync_items_; 827} 828 829void SyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter, 830 const net::ProxyConfig& proxy_config) { 831 base::WaitableEvent done(false, false); 832 BrowserThread::PostTask( 833 BrowserThread::IO, FROM_HERE, 834 base::Bind(&SetProxyConfigCallback, &done, 835 make_scoped_refptr(context_getter), proxy_config)); 836 done.Wait(); 837} 838