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