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