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