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