sync_test.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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.h"
13#include "base/path_service.h"
14#include "base/string_util.h"
15#include "base/stringprintf.h"
16#include "base/synchronization/waitable_event.h"
17#include "base/test/test_timeouts.h"
18#include "base/threading/platform_thread.h"
19#include "base/utf_string_conversions.h"
20#include "base/values.h"
21#include "chrome/browser/bookmarks/bookmark_model_factory.h"
22#include "chrome/browser/google/google_url_tracker.h"
23#include "chrome/browser/history/history_service_factory.h"
24#include "chrome/browser/lifetime/application_lifetime.h"
25#include "chrome/browser/password_manager/encryptor.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/profiles/profile_manager.h"
28#include "chrome/browser/search_engines/template_url_service.h"
29#include "chrome/browser/search_engines/template_url_service_factory.h"
30#include "chrome/browser/sync/profile_sync_service_factory.h"
31#include "chrome/browser/sync/profile_sync_service_harness.h"
32#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
33#include "chrome/browser/ui/browser.h"
34#include "chrome/browser/ui/browser_list.h"
35#include "chrome/browser/ui/browser_tabstrip.h"
36#include "chrome/common/chrome_paths.h"
37#include "chrome/common/chrome_switches.h"
38#include "chrome/test/base/testing_browser_process.h"
39#include "chrome/test/base/ui_test_utils.h"
40#include "content/public/browser/web_contents.h"
41#include "content/public/test/test_browser_thread.h"
42#include "google_apis/gaia/gaia_urls.h"
43#include "googleurl/src/gurl.h"
44#include "net/base/escape.h"
45#include "net/base/load_flags.h"
46#include "net/base/network_change_notifier.h"
47#include "net/proxy/proxy_config.h"
48#include "net/proxy/proxy_config_service_fixed.h"
49#include "net/proxy/proxy_service.h"
50#include "net/test/test_server.h"
51#include "net/url_request/test_url_fetcher_factory.h"
52#include "net/url_request/url_fetcher.h"
53#include "net/url_request/url_fetcher_delegate.h"
54#include "net/url_request/url_request_context.h"
55#include "net/url_request/url_request_context_getter.h"
56#include "net/url_request/url_request_status.h"
57#include "sync/engine/sync_scheduler_impl.h"
58#include "sync/notifier/p2p_invalidator.h"
59#include "sync/protocol/sync.pb.h"
60
61using content::BrowserThread;
62
63namespace switches {
64const char kPasswordFileForTest[] = "password-file-for-test";
65const char kSyncUserForTest[] = "sync-user-for-test";
66const char kSyncPasswordForTest[] = "sync-password-for-test";
67const char kSyncServerCommandLine[] = "sync-server-command-line";
68}
69
70// Helper class that checks whether a sync test server is running or not.
71class SyncServerStatusChecker : public net::URLFetcherDelegate {
72 public:
73  SyncServerStatusChecker() : running_(false) {}
74
75  virtual void OnURLFetchComplete(const net::URLFetcher* source) {
76    std::string data;
77    source->GetResponseAsString(&data);
78    running_ =
79        (source->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
80        source->GetResponseCode() == 200 && data.find("ok") == 0);
81    MessageLoop::current()->Quit();
82  }
83
84  bool running() const { return running_; }
85
86 private:
87  bool running_;
88};
89
90void SetProxyConfigCallback(
91    base::WaitableEvent* done,
92    net::URLRequestContextGetter* url_request_context_getter,
93    const net::ProxyConfig& proxy_config) {
94  net::ProxyService* proxy_service =
95      url_request_context_getter->GetURLRequestContext()->proxy_service();
96  proxy_service->ResetConfigService(
97      new net::ProxyConfigServiceFixed(proxy_config));
98  done->Signal();
99}
100
101SyncTest::SyncTest(TestType test_type)
102    : test_type_(test_type),
103      server_type_(SERVER_TYPE_UNDECIDED),
104      num_clients_(-1),
105      use_verifier_(true),
106      notifications_enabled_(true),
107      test_server_handle_(base::kNullProcessHandle),
108      number_of_default_sync_items_(0) {
109  sync_datatype_helper::AssociateWithTest(this);
110  switch (test_type_) {
111    case SINGLE_CLIENT: {
112      num_clients_ = 1;
113      break;
114    }
115    case TWO_CLIENT: {
116      num_clients_ = 2;
117      break;
118    }
119    case MULTIPLE_CLIENT: {
120      num_clients_ = 3;
121      break;
122    }
123  }
124}
125
126SyncTest::~SyncTest() {}
127
128void SyncTest::SetUp() {
129  CommandLine* cl = CommandLine::ForCurrentProcess();
130  if (cl->HasSwitch(switches::kPasswordFileForTest)) {
131    ReadPasswordFile();
132  } else if (cl->HasSwitch(switches::kSyncUserForTest) &&
133             cl->HasSwitch(switches::kSyncPasswordForTest)) {
134    username_ = cl->GetSwitchValueASCII(switches::kSyncUserForTest);
135    password_ = cl->GetSwitchValueASCII(switches::kSyncPasswordForTest);
136  } else {
137    SetupMockGaiaResponses();
138  }
139
140  if (!cl->HasSwitch(switches::kSyncServiceURL) &&
141      !cl->HasSwitch(switches::kSyncServerCommandLine)) {
142    // If neither a sync server URL nor a sync server command line is
143    // provided, start up a local python sync test server and point Chrome
144    // to its URL.  This is the most common configuration, and the only
145    // one that makes sense for most developers.
146    server_type_ = LOCAL_PYTHON_SERVER;
147  } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
148             cl->HasSwitch(switches::kSyncServerCommandLine)) {
149    // If a sync server URL and a sync server command line are provided,
150    // start up a local sync server by running the command line. Chrome
151    // will connect to the server at the URL that was provided.
152    server_type_ = LOCAL_LIVE_SERVER;
153  } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
154             !cl->HasSwitch(switches::kSyncServerCommandLine)) {
155    // If a sync server URL is provided, but not a server command line,
156    // it is assumed that the server is already running. Chrome will
157    // automatically connect to it at the URL provided. There is nothing
158    // to do here.
159    server_type_ = EXTERNAL_LIVE_SERVER;
160  } else {
161    // If a sync server command line is provided, but not a server URL,
162    // we flag an error.
163    LOG(FATAL) << "Can't figure out how to run a server.";
164  }
165
166  if (username_.empty() || password_.empty())
167    LOG(FATAL) << "Cannot run sync tests without GAIA credentials.";
168
169  // Mock the Mac Keychain service.  The real Keychain can block on user input.
170#if defined(OS_MACOSX)
171  Encryptor::UseMockKeychain(true);
172#endif
173
174  // Yield control back to the InProcessBrowserTest framework.
175  InProcessBrowserTest::SetUp();
176}
177
178void SyncTest::TearDown() {
179  // Clear any mock gaia responses that might have been set.
180  ClearMockGaiaResponses();
181
182  // Allow the InProcessBrowserTest framework to perform its tear down.
183  InProcessBrowserTest::TearDown();
184
185  // Stop the local python test server. This is a no-op if one wasn't started.
186  TearDownLocalPythonTestServer();
187
188  // Stop the local sync test server. This is a no-op if one wasn't started.
189  TearDownLocalTestServer();
190}
191
192void SyncTest::SetUpCommandLine(CommandLine* cl) {
193  AddTestSwitches(cl);
194  AddOptionalTypesToCommandLine(cl);
195}
196
197void SyncTest::AddTestSwitches(CommandLine* cl) {
198  // TODO(rsimha): Until we implement a fake Tango server against which tests
199  // can run, we need to set the --sync-notification-method to "p2p".
200  if (!cl->HasSwitch(switches::kSyncNotificationMethod))
201    cl->AppendSwitchASCII(switches::kSyncNotificationMethod, "p2p");
202
203  // Disable non-essential access of external network resources.
204  if (!cl->HasSwitch(switches::kDisableBackgroundNetworking))
205    cl->AppendSwitch(switches::kDisableBackgroundNetworking);
206
207  // TODO(sync): remove this once keystore encryption is enabled by default.
208  if (!cl->HasSwitch(switches::kSyncKeystoreEncryption))
209    cl->AppendSwitch(switches::kSyncKeystoreEncryption);
210
211  if (!cl->HasSwitch(switches::kSyncShortInitialRetryOverride))
212    cl->AppendSwitch(switches::kSyncShortInitialRetryOverride);
213}
214
215void SyncTest::AddOptionalTypesToCommandLine(CommandLine* cl) {}
216
217// static
218Profile* SyncTest::MakeProfile(const FilePath::StringType name) {
219  FilePath path;
220  PathService::Get(chrome::DIR_USER_DATA, &path);
221  path = path.Append(name);
222
223  if (!file_util::PathExists(path))
224    CHECK(file_util::CreateDirectory(path));
225
226  Profile* profile =
227      Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
228  g_browser_process->profile_manager()->RegisterTestingProfile(profile, true);
229  return profile;
230}
231
232Profile* SyncTest::GetProfile(int index) {
233  if (profiles_.empty())
234    LOG(FATAL) << "SetupClients() has not yet been called.";
235  if (index < 0 || index >= static_cast<int>(profiles_.size()))
236    LOG(FATAL) << "GetProfile(): Index is out of bounds.";
237  return profiles_[index];
238}
239
240Browser* SyncTest::GetBrowser(int index) {
241  if (browsers_.empty())
242    LOG(FATAL) << "SetupClients() has not yet been called.";
243  if (index < 0 || index >= static_cast<int>(browsers_.size()))
244    LOG(FATAL) << "GetBrowser(): Index is out of bounds.";
245  return browsers_[index];
246}
247
248ProfileSyncServiceHarness* SyncTest::GetClient(int index) {
249  if (clients_.empty())
250    LOG(FATAL) << "SetupClients() has not yet been called.";
251  if (index < 0 || index >= static_cast<int>(clients_.size()))
252    LOG(FATAL) << "GetClient(): Index is out of bounds.";
253  return clients_[index];
254}
255
256Profile* SyncTest::verifier() {
257  if (verifier_ == NULL)
258    LOG(FATAL) << "SetupClients() has not yet been called.";
259  return verifier_;
260}
261
262void SyncTest::DisableVerifier() {
263  use_verifier_ = false;
264}
265
266bool SyncTest::SetupClients() {
267  if (num_clients_ <= 0)
268    LOG(FATAL) << "num_clients_ incorrectly initialized.";
269  if (!profiles_.empty() || !browsers_.empty() || !clients_.empty())
270    LOG(FATAL) << "SetupClients() has already been called.";
271
272  // Start up a sync test server if one is needed.
273  SetUpTestServerIfRequired();
274
275  // Create the required number of sync profiles, browsers and clients.
276  profiles_.resize(num_clients_);
277  browsers_.resize(num_clients_);
278  clients_.resize(num_clients_);
279  for (int i = 0; i < num_clients_; ++i) {
280    InitializeInstance(i);
281  }
282
283  // Create the verifier profile.
284  verifier_ = MakeProfile(FILE_PATH_LITERAL("Verifier"));
285  ui_test_utils::WaitForBookmarkModelToLoad(
286      BookmarkModelFactory::GetForProfile(verifier()));
287  ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
288      verifier(), Profile::EXPLICIT_ACCESS));
289  ui_test_utils::WaitForTemplateURLServiceToLoad(
290      TemplateURLServiceFactory::GetForProfile(verifier()));
291  return (verifier_ != NULL);
292}
293
294void SyncTest::InitializeInstance(int index) {
295  profiles_[index] = MakeProfile(
296      base::StringPrintf(FILE_PATH_LITERAL("Profile%d"), index));
297  EXPECT_FALSE(GetProfile(index) == NULL) << "Could not create Profile "
298                                          << index << ".";
299
300  browsers_[index] = new Browser(Browser::CreateParams(GetProfile(index)));
301  EXPECT_FALSE(GetBrowser(index) == NULL) << "Could not create Browser "
302                                          << index << ".";
303
304  clients_[index] = new ProfileSyncServiceHarness(GetProfile(index),
305                                                  username_,
306                                                  password_);
307  EXPECT_FALSE(GetClient(index) == NULL) << "Could not create Client "
308                                         << index << ".";
309
310  ui_test_utils::WaitForBookmarkModelToLoad(
311      BookmarkModelFactory::GetForProfile(GetProfile(index)));
312  ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
313      GetProfile(index), Profile::EXPLICIT_ACCESS));
314  ui_test_utils::WaitForTemplateURLServiceToLoad(
315      TemplateURLServiceFactory::GetForProfile(GetProfile(index)));
316}
317
318void SyncTest::RestartSyncService(int index) {
319  DVLOG(1) << "Restarting profile sync service for profile " << index << ".";
320  delete clients_[index];
321  Profile* profile = GetProfile(index);
322  ProfileSyncService* service =
323      ProfileSyncServiceFactory::GetForProfile(profile);
324  service->ResetForTest();
325  clients_[index] = new ProfileSyncServiceHarness(profile,
326                                                  username_,
327                                                  password_);
328  service->Initialize();
329  GetClient(index)->AwaitSyncRestart();
330}
331
332bool SyncTest::SetupSync() {
333  // Create sync profiles and clients if they haven't already been created.
334  if (profiles_.empty()) {
335    if (!SetupClients())
336      LOG(FATAL) << "SetupClients() failed.";
337  }
338
339  // Sync each of the profiles.
340  for (int i = 0; i < num_clients_; ++i) {
341    if (!GetClient(i)->SetupSync())
342      LOG(FATAL) << "SetupSync() failed.";
343  }
344
345  // Because clients may modify sync data as part of startup (for example local
346  // session-releated data is rewritten), we need to ensure all startup-based
347  // changes have propagated between the clients.
348  AwaitQuiescence();
349
350  // The number of default entries is the number of entries existing after
351  // sync startup excluding top level folders and other permanent items.
352  // This value must be updated whenever new permanent items are added (although
353  // this should handle new datatype-specific top level folders).
354  number_of_default_sync_items_ = GetClient(0)->GetNumEntries() -
355                                  GetClient(0)->GetNumDatatypes() - 6;
356  DVLOG(1) << "Setting " << number_of_default_sync_items_ << " as default "
357           << " number of entries.";
358
359  return true;
360}
361
362void SyncTest::CleanUpOnMainThread() {
363  // Close all browser windows.
364  browser::CloseAllBrowsers();
365  content::RunAllPendingInMessageLoop();
366
367  // All browsers should be closed at this point, or else we could see memory
368  // corruption in QuitBrowser().
369  CHECK_EQ(0U, BrowserList::size());
370  clients_.clear();
371}
372
373void SyncTest::SetUpInProcessBrowserTestFixture() {
374  // We don't take a reference to |resolver|, but mock_host_resolver_override_
375  // does, so effectively assumes ownership.
376  net::RuleBasedHostResolverProc* resolver =
377      new net::RuleBasedHostResolverProc(host_resolver());
378  resolver->AllowDirectLookup("*.google.com");
379  // On Linux, we use Chromium's NSS implementation which uses the following
380  // hosts for certificate verification. Without these overrides, running the
381  // integration tests on Linux causes error as we make external DNS lookups.
382  resolver->AllowDirectLookup("*.thawte.com");
383  resolver->AllowDirectLookup("*.geotrust.com");
384  resolver->AllowDirectLookup("*.gstatic.com");
385  mock_host_resolver_override_.reset(
386      new net::ScopedDefaultHostResolverProc(resolver));
387}
388
389void SyncTest::TearDownInProcessBrowserTestFixture() {
390  mock_host_resolver_override_.reset();
391}
392
393void SyncTest::ReadPasswordFile() {
394  CommandLine* cl = CommandLine::ForCurrentProcess();
395  password_file_ = cl->GetSwitchValuePath(switches::kPasswordFileForTest);
396  if (password_file_.empty())
397    LOG(FATAL) << "Can't run live server test without specifying --"
398               << switches::kPasswordFileForTest << "=<filename>";
399  std::string file_contents;
400  file_util::ReadFileToString(password_file_, &file_contents);
401  ASSERT_NE(file_contents, "") << "Password file \""
402      << password_file_.value() << "\" does not exist.";
403  std::vector<std::string> tokens;
404  std::string delimiters = "\r\n";
405  Tokenize(file_contents, delimiters, &tokens);
406  ASSERT_EQ(2U, tokens.size()) << "Password file \""
407      << password_file_.value()
408      << "\" must contain exactly two lines of text.";
409  username_ = tokens[0];
410  password_ = tokens[1];
411}
412
413void SyncTest::SetupMockGaiaResponses() {
414  username_ = "user@gmail.com";
415  password_ = "password";
416  factory_.reset(new net::URLFetcherImplFactory());
417  fake_factory_.reset(new net::FakeURLFetcherFactory(factory_.get()));
418  fake_factory_->SetFakeResponse(
419      GaiaUrls::GetInstance()->client_login_url(),
420      "SID=sid\nLSID=lsid",
421      true);
422  fake_factory_->SetFakeResponse(
423      GaiaUrls::GetInstance()->get_user_info_url(),
424      "email=user@gmail.com",
425      true);
426  fake_factory_->SetFakeResponse(
427      GaiaUrls::GetInstance()->issue_auth_token_url(),
428      "auth",
429      true);
430  fake_factory_->SetFakeResponse(
431      GoogleURLTracker::kSearchDomainCheckURL,
432      ".google.com",
433      true);
434  fake_factory_->SetFakeResponse(
435      GaiaUrls::GetInstance()->client_login_to_oauth2_url(),
436      "some_response",
437      true);
438  fake_factory_->SetFakeResponse(
439      GaiaUrls::GetInstance()->oauth2_token_url(),
440      "{"
441      "  \"refresh_token\": \"rt1\","
442      "  \"access_token\": \"at1\","
443      "  \"expires_in\": 3600,"
444      "  \"token_type\": \"Bearer\""
445      "}",
446      true);
447  fake_factory_->SetFakeResponse(
448      GaiaUrls::GetInstance()->client_oauth_url(),
449      "{"
450      "  \"oauth2\": {"
451      "    \"refresh_token\": \"rt1\","
452      "    \"access_token\": \"at1\","
453      "    \"expires_in\": 3600,"
454      "    \"token_type\": \"Bearer\""
455      "  }"
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_.get()) {
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(chrome::GetActiveWebContents(browser())->GetTitle()));
651}
652
653void SyncTest::DisableNotifications() {
654  DisableNotificationsImpl();
655  notifications_enabled_ = false;
656}
657
658void SyncTest::EnableNotificationsImpl() {
659  ASSERT_TRUE(ServerSupportsNotificationControl());
660  std::string path = "chromiumsync/enablenotifications";
661  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
662  ASSERT_EQ("Notifications enabled",
663            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
664}
665
666void SyncTest::EnableNotifications() {
667  EnableNotificationsImpl();
668  notifications_enabled_ = true;
669}
670
671void SyncTest::TriggerNotification(syncer::ModelTypeSet changed_types) {
672  ASSERT_TRUE(ServerSupportsNotificationControl());
673  const std::string& data =
674      syncer::P2PNotificationData(
675          "from_server",
676          syncer::NOTIFY_ALL,
677          syncer::ObjectIdSetToInvalidationMap(
678              syncer::ModelTypeSetToObjectIdSet(changed_types), std::string()),
679          syncer::REMOTE_INVALIDATION).ToString();
680  const std::string& path =
681      std::string("chromiumsync/sendnotification?channel=") +
682      syncer::kSyncP2PNotificationChannel + "&data=" + data;
683  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
684  ASSERT_EQ("Notification sent",
685            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
686}
687
688bool SyncTest::ServerSupportsErrorTriggering() const {
689  EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_);
690
691  // Supported only if we're using the python testserver.
692  return server_type_ == LOCAL_PYTHON_SERVER;
693}
694
695void SyncTest::TriggerMigrationDoneError(syncer::ModelTypeSet model_types) {
696  ASSERT_TRUE(ServerSupportsErrorTriggering());
697  std::string path = "chromiumsync/migrate";
698  char joiner = '?';
699  for (syncer::ModelTypeSet::Iterator it = model_types.First();
700       it.Good(); it.Inc()) {
701    path.append(
702        base::StringPrintf(
703            "%ctype=%d", joiner,
704            syncer::GetSpecificsFieldNumberFromModelType(it.Get())));
705    joiner = '&';
706  }
707  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
708  ASSERT_EQ("Migration: 200",
709            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
710}
711
712void SyncTest::TriggerBirthdayError() {
713  ASSERT_TRUE(ServerSupportsErrorTriggering());
714  std::string path = "chromiumsync/birthdayerror";
715  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
716  ASSERT_EQ("Birthday error",
717            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
718}
719
720void SyncTest::TriggerTransientError() {
721  ASSERT_TRUE(ServerSupportsErrorTriggering());
722  std::string path = "chromiumsync/transienterror";
723  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
724  ASSERT_EQ("Transient error",
725            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
726}
727
728void SyncTest::TriggerAuthError() {
729  ASSERT_TRUE(ServerSupportsErrorTriggering());
730  std::string path = "chromiumsync/cred";
731  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
732}
733
734namespace {
735
736sync_pb::SyncEnums::ErrorType
737    GetClientToServerResponseErrorType(
738        syncer::SyncProtocolErrorType error) {
739  switch (error) {
740    case syncer::SYNC_SUCCESS:
741      return sync_pb::SyncEnums::SUCCESS;
742    case syncer::NOT_MY_BIRTHDAY:
743      return sync_pb::SyncEnums::NOT_MY_BIRTHDAY;
744    case syncer::THROTTLED:
745      return sync_pb::SyncEnums::THROTTLED;
746    case syncer::CLEAR_PENDING:
747      return sync_pb::SyncEnums::CLEAR_PENDING;
748    case syncer::TRANSIENT_ERROR:
749      return sync_pb::SyncEnums::TRANSIENT_ERROR;
750    case syncer::MIGRATION_DONE:
751      return sync_pb::SyncEnums::MIGRATION_DONE;
752    case syncer::UNKNOWN_ERROR:
753      return sync_pb::SyncEnums::UNKNOWN;
754    default:
755      NOTREACHED();
756      return sync_pb::SyncEnums::UNKNOWN;
757  }
758}
759
760sync_pb::SyncEnums::Action GetClientToServerResponseAction(
761    const syncer::ClientAction& action) {
762  switch (action) {
763    case syncer::UPGRADE_CLIENT:
764      return sync_pb::SyncEnums::UPGRADE_CLIENT;
765    case syncer::CLEAR_USER_DATA_AND_RESYNC:
766      return sync_pb::SyncEnums::CLEAR_USER_DATA_AND_RESYNC;
767    case syncer::ENABLE_SYNC_ON_ACCOUNT:
768      return sync_pb::SyncEnums::ENABLE_SYNC_ON_ACCOUNT;
769    case syncer::STOP_AND_RESTART_SYNC:
770      return sync_pb::SyncEnums::STOP_AND_RESTART_SYNC;
771    case syncer::DISABLE_SYNC_ON_CLIENT:
772      return sync_pb::SyncEnums::DISABLE_SYNC_ON_CLIENT;
773    case syncer::UNKNOWN_ACTION:
774      return sync_pb::SyncEnums::UNKNOWN_ACTION;
775    default:
776      NOTREACHED();
777      return sync_pb::SyncEnums::UNKNOWN_ACTION;
778  }
779}
780
781}  // namespace
782
783void SyncTest::TriggerSyncError(const syncer::SyncProtocolError& error,
784                                SyncErrorFrequency frequency) {
785  ASSERT_TRUE(ServerSupportsErrorTriggering());
786  std::string path = "chromiumsync/error";
787  int error_type =
788      static_cast<int>(GetClientToServerResponseErrorType(
789          error.error_type));
790  int action = static_cast<int>(GetClientToServerResponseAction(
791      error.action));
792
793  path.append(base::StringPrintf("?error=%d", error_type));
794  path.append(base::StringPrintf("&action=%d", action));
795
796  path.append(base::StringPrintf("&error_description=%s",
797                                 error.error_description.c_str()));
798  path.append(base::StringPrintf("&url=%s", error.url.c_str()));
799  path.append(base::StringPrintf("&frequency=%d", frequency));
800
801  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
802  std::string output = UTF16ToASCII(
803      chrome::GetActiveWebContents(browser())->GetTitle());
804  ASSERT_TRUE(output.find("SetError: 200") != string16::npos);
805}
806
807void SyncTest::TriggerCreateSyncedBookmarks() {
808  ASSERT_TRUE(ServerSupportsErrorTriggering());
809  std::string path = "chromiumsync/createsyncedbookmarks";
810  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
811  ASSERT_EQ("Synced Bookmarks",
812            UTF16ToASCII(chrome::GetActiveWebContents(browser())->GetTitle()));
813}
814
815int SyncTest::NumberOfDefaultSyncItems() const {
816  return number_of_default_sync_items_;
817}
818
819void SyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter,
820                              const net::ProxyConfig& proxy_config) {
821  base::WaitableEvent done(false, false);
822  BrowserThread::PostTask(
823      BrowserThread::IO, FROM_HERE,
824      base::Bind(&SetProxyConfigCallback, &done,
825                 make_scoped_refptr(context_getter), proxy_config));
826  done.Wait();
827}
828