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