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