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