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