sync_test.cc revision f8ee788a64d60abd8f2d742a5fdedde054ecd910
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/memory/ref_counted.h"
13#include "base/message_loop/message_loop.h"
14#include "base/path_service.h"
15#include "base/process/launch.h"
16#include "base/strings/string_util.h"
17#include "base/strings/stringprintf.h"
18#include "base/strings/utf_string_conversions.h"
19#include "base/synchronization/waitable_event.h"
20#include "base/test/test_timeouts.h"
21#include "base/threading/platform_thread.h"
22#include "base/values.h"
23#include "chrome/browser/bookmarks/bookmark_model_factory.h"
24#include "chrome/browser/history/history_service_factory.h"
25#include "chrome/browser/invalidation/profile_invalidation_provider_factory.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/signin/profile_identity_provider.h"
32#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
33#include "chrome/browser/signin/signin_manager_factory.h"
34#include "chrome/browser/sync/profile_sync_service.h"
35#include "chrome/browser/sync/profile_sync_service_factory.h"
36#include "chrome/browser/sync/test/integration/fake_server_invalidation_service.h"
37#include "chrome/browser/sync/test/integration/p2p_invalidation_forwarder.h"
38#include "chrome/browser/sync/test/integration/profile_sync_service_harness.h"
39#include "chrome/browser/sync/test/integration/single_client_status_change_checker.h"
40#include "chrome/browser/sync/test/integration/sync_datatype_helper.h"
41#include "chrome/browser/sync/test/integration/sync_integration_test_util.h"
42#include "chrome/browser/ui/browser.h"
43#include "chrome/browser/ui/browser_finder.h"
44#include "chrome/browser/ui/host_desktop.h"
45#include "chrome/browser/ui/tabs/tab_strip_model.h"
46#include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
47#include "chrome/common/chrome_paths.h"
48#include "chrome/common/chrome_switches.h"
49#include "chrome/test/base/testing_browser_process.h"
50#include "chrome/test/base/ui_test_utils.h"
51#include "components/bookmarks/test/bookmark_test_helpers.h"
52#include "components/google/core/browser/google_url_tracker.h"
53#include "components/invalidation/invalidation_service.h"
54#include "components/invalidation/invalidation_switches.h"
55#include "components/invalidation/p2p_invalidation_service.h"
56#include "components/invalidation/p2p_invalidator.h"
57#include "components/invalidation/profile_invalidation_provider.h"
58#include "components/keyed_service/core/keyed_service.h"
59#include "components/os_crypt/os_crypt.h"
60#include "components/signin/core/browser/signin_manager.h"
61#include "content/public/browser/web_contents.h"
62#include "content/public/test/test_browser_thread.h"
63#include "google_apis/gaia/gaia_urls.h"
64#include "net/base/escape.h"
65#include "net/base/load_flags.h"
66#include "net/base/network_change_notifier.h"
67#include "net/proxy/proxy_config.h"
68#include "net/proxy/proxy_config_service_fixed.h"
69#include "net/proxy/proxy_service.h"
70#include "net/test/spawned_test_server/spawned_test_server.h"
71#include "net/url_request/test_url_fetcher_factory.h"
72#include "net/url_request/url_fetcher.h"
73#include "net/url_request/url_fetcher_delegate.h"
74#include "net/url_request/url_request_context.h"
75#include "net/url_request/url_request_context_getter.h"
76#include "sync/engine/sync_scheduler_impl.h"
77#include "sync/protocol/sync.pb.h"
78#include "sync/test/fake_server/fake_server.h"
79#include "sync/test/fake_server/fake_server_network_resources.h"
80#include "url/gurl.h"
81
82#if defined(OS_CHROMEOS)
83#include "chromeos/chromeos_switches.h"
84#endif
85
86using content::BrowserThread;
87
88namespace switches {
89const char kPasswordFileForTest[] = "password-file-for-test";
90const char kSyncUserForTest[] = "sync-user-for-test";
91const char kSyncPasswordForTest[] = "sync-password-for-test";
92const char kSyncServerCommandLine[] = "sync-server-command-line";
93}
94
95namespace {
96
97// Helper class that checks whether a sync test server is running or not.
98class SyncServerStatusChecker : public net::URLFetcherDelegate {
99 public:
100  SyncServerStatusChecker() : running_(false) {}
101
102  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
103    std::string data;
104    source->GetResponseAsString(&data);
105    running_ =
106        (source->GetStatus().status() == net::URLRequestStatus::SUCCESS &&
107        source->GetResponseCode() == 200 && data.find("ok") == 0);
108    base::MessageLoop::current()->Quit();
109  }
110
111  bool running() const { return running_; }
112
113 private:
114  bool running_;
115};
116
117bool IsEncryptionComplete(const ProfileSyncService* service) {
118  return service->EncryptEverythingEnabled() && !service->encryption_pending();
119}
120
121// Helper class to wait for encryption to complete.
122class EncryptionChecker : public SingleClientStatusChangeChecker {
123 public:
124  explicit EncryptionChecker(ProfileSyncService* service)
125      : SingleClientStatusChangeChecker(service) {}
126
127  virtual bool IsExitConditionSatisfied() OVERRIDE {
128    return IsEncryptionComplete(service());
129  }
130
131  virtual std::string GetDebugMessage() const OVERRIDE {
132    return "Encryption";
133  }
134};
135
136void SetProxyConfigCallback(
137    base::WaitableEvent* done,
138    net::URLRequestContextGetter* url_request_context_getter,
139    const net::ProxyConfig& proxy_config) {
140  net::ProxyService* proxy_service =
141      url_request_context_getter->GetURLRequestContext()->proxy_service();
142  proxy_service->ResetConfigService(
143      new net::ProxyConfigServiceFixed(proxy_config));
144  done->Signal();
145}
146
147KeyedService* BuildFakeServerProfileInvalidationProvider(
148    content::BrowserContext* context) {
149  return new invalidation::ProfileInvalidationProvider(
150      scoped_ptr<invalidation::InvalidationService>(
151          new fake_server::FakeServerInvalidationService));
152}
153
154KeyedService* BuildP2PProfileInvalidationProvider(
155    content::BrowserContext* context,
156    syncer::P2PNotificationTarget notification_target) {
157  Profile* profile = static_cast<Profile*>(context);
158  return new invalidation::ProfileInvalidationProvider(
159      scoped_ptr<invalidation::InvalidationService>(
160          new invalidation::P2PInvalidationService(
161              scoped_ptr<IdentityProvider>(new ProfileIdentityProvider(
162                  SigninManagerFactory::GetForProfile(profile),
163                  ProfileOAuth2TokenServiceFactory::GetForProfile(profile),
164                  LoginUIServiceFactory::GetForProfile(profile))),
165              profile->GetRequestContext(),
166              notification_target)));
167}
168
169KeyedService* BuildSelfNotifyingP2PProfileInvalidationProvider(
170    content::BrowserContext* context) {
171  return BuildP2PProfileInvalidationProvider(context, syncer::NOTIFY_ALL);
172}
173
174KeyedService* BuildRealisticP2PProfileInvalidationProvider(
175    content::BrowserContext* context) {
176  return BuildP2PProfileInvalidationProvider(context, syncer::NOTIFY_OTHERS);
177}
178
179}  // namespace
180
181SyncTest::SyncTest(TestType test_type)
182    : test_type_(test_type),
183      server_type_(SERVER_TYPE_UNDECIDED),
184      num_clients_(-1),
185      use_verifier_(true),
186      notifications_enabled_(true),
187      test_server_handle_(base::kNullProcessHandle) {
188  sync_datatype_helper::AssociateWithTest(this);
189  switch (test_type_) {
190    case SINGLE_CLIENT:
191    case SINGLE_CLIENT_LEGACY: {
192      num_clients_ = 1;
193      break;
194    }
195    case TWO_CLIENT:
196    case TWO_CLIENT_LEGACY: {
197      num_clients_ = 2;
198      break;
199    }
200    case MULTIPLE_CLIENT: {
201      num_clients_ = 3;
202      break;
203    }
204  }
205}
206
207SyncTest::~SyncTest() {}
208
209void SyncTest::SetUp() {
210  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
211  if (cl->HasSwitch(switches::kPasswordFileForTest)) {
212    ReadPasswordFile();
213  } else if (cl->HasSwitch(switches::kSyncUserForTest) &&
214             cl->HasSwitch(switches::kSyncPasswordForTest)) {
215    username_ = cl->GetSwitchValueASCII(switches::kSyncUserForTest);
216    password_ = cl->GetSwitchValueASCII(switches::kSyncPasswordForTest);
217  } else {
218    username_ = "user@gmail.com";
219    password_ = "password";
220  }
221
222  if (username_.empty() || password_.empty())
223    LOG(FATAL) << "Cannot run sync tests without GAIA credentials.";
224
225  // Sets |server_type_| if it wasn't specified by the test.
226  DecideServerType();
227
228  // Mock the Mac Keychain service.  The real Keychain can block on user input.
229#if defined(OS_MACOSX)
230  OSCrypt::UseMockKeychain(true);
231#endif
232
233  // Start up a sync test server if one is needed and setup mock gaia responses.
234  // Note: This must be done prior to the call to SetupClients() because we want
235  // the mock gaia responses to be available before GaiaUrls is initialized.
236  SetUpTestServerIfRequired();
237
238  // Yield control back to the InProcessBrowserTest framework.
239  InProcessBrowserTest::SetUp();
240}
241
242void SyncTest::TearDown() {
243  // Clear any mock gaia responses that might have been set.
244  ClearMockGaiaResponses();
245
246  // Allow the InProcessBrowserTest framework to perform its tear down.
247  InProcessBrowserTest::TearDown();
248
249  // Stop the local python test server. This is a no-op if one wasn't started.
250  TearDownLocalPythonTestServer();
251
252  // Stop the local sync test server. This is a no-op if one wasn't started.
253  TearDownLocalTestServer();
254
255  fake_server_.reset();
256}
257
258void SyncTest::SetUpCommandLine(base::CommandLine* cl) {
259  AddTestSwitches(cl);
260  AddOptionalTypesToCommandLine(cl);
261
262#if defined(OS_CHROMEOS)
263  cl->AppendSwitch(chromeos::switches::kIgnoreUserProfileMappingForTests);
264#endif
265}
266
267void SyncTest::AddTestSwitches(base::CommandLine* cl) {
268  // Disable non-essential access of external network resources.
269  if (!cl->HasSwitch(switches::kDisableBackgroundNetworking))
270    cl->AppendSwitch(switches::kDisableBackgroundNetworking);
271
272  if (!cl->HasSwitch(switches::kSyncShortInitialRetryOverride))
273    cl->AppendSwitch(switches::kSyncShortInitialRetryOverride);
274}
275
276void SyncTest::AddOptionalTypesToCommandLine(base::CommandLine* cl) {}
277
278// static
279Profile* SyncTest::MakeProfile(const base::FilePath::StringType name) {
280  base::FilePath path;
281  PathService::Get(chrome::DIR_USER_DATA, &path);
282  path = path.Append(name);
283
284  if (!base::PathExists(path))
285    CHECK(base::CreateDirectory(path));
286
287  Profile* profile =
288      Profile::CreateProfile(path, NULL, Profile::CREATE_MODE_SYNCHRONOUS);
289  g_browser_process->profile_manager()->RegisterTestingProfile(profile,
290                                                               true,
291                                                               true);
292  return profile;
293}
294
295Profile* SyncTest::GetProfile(int index) {
296  if (profiles_.empty())
297    LOG(FATAL) << "SetupClients() has not yet been called.";
298  if (index < 0 || index >= static_cast<int>(profiles_.size()))
299    LOG(FATAL) << "GetProfile(): Index is out of bounds.";
300  return profiles_[index];
301}
302
303Browser* SyncTest::GetBrowser(int index) {
304  if (browsers_.empty())
305    LOG(FATAL) << "SetupClients() has not yet been called.";
306  if (index < 0 || index >= static_cast<int>(browsers_.size()))
307    LOG(FATAL) << "GetBrowser(): Index is out of bounds.";
308  return browsers_[index];
309}
310
311ProfileSyncServiceHarness* SyncTest::GetClient(int index) {
312  if (clients_.empty())
313    LOG(FATAL) << "SetupClients() has not yet been called.";
314  if (index < 0 || index >= static_cast<int>(clients_.size()))
315    LOG(FATAL) << "GetClient(): Index is out of bounds.";
316  return clients_[index];
317}
318
319ProfileSyncService* SyncTest::GetSyncService(int index) {
320  return ProfileSyncServiceFactory::GetForProfile(GetProfile(index));
321}
322
323std::vector<ProfileSyncService*> SyncTest::GetSyncServices() {
324  std::vector<ProfileSyncService*> services;
325  for (int i = 0; i < num_clients(); ++i) {
326    services.push_back(GetSyncService(i));
327  }
328  return services;
329}
330
331Profile* SyncTest::verifier() {
332  if (verifier_ == NULL)
333    LOG(FATAL) << "SetupClients() has not yet been called.";
334  return verifier_;
335}
336
337void SyncTest::DisableVerifier() {
338  use_verifier_ = false;
339}
340
341bool SyncTest::SetupClients() {
342  if (num_clients_ <= 0)
343    LOG(FATAL) << "num_clients_ incorrectly initialized.";
344  if (!profiles_.empty() || !browsers_.empty() || !clients_.empty())
345    LOG(FATAL) << "SetupClients() has already been called.";
346
347  // Create the required number of sync profiles, browsers and clients.
348  profiles_.resize(num_clients_);
349  browsers_.resize(num_clients_);
350  clients_.resize(num_clients_);
351  invalidation_forwarders_.resize(num_clients_);
352  fake_server_invalidation_services_.resize(num_clients_);
353  for (int i = 0; i < num_clients_; ++i) {
354    InitializeInstance(i);
355  }
356
357  // Create the verifier profile.
358  verifier_ = MakeProfile(FILE_PATH_LITERAL("Verifier"));
359  test::WaitForBookmarkModelToLoad(
360      BookmarkModelFactory::GetForProfile(verifier()));
361  ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
362      verifier(), Profile::EXPLICIT_ACCESS));
363  ui_test_utils::WaitForTemplateURLServiceToLoad(
364      TemplateURLServiceFactory::GetForProfile(verifier()));
365  return (verifier_ != NULL);
366}
367
368void SyncTest::InitializeInstance(int index) {
369  profiles_[index] = MakeProfile(
370      base::StringPrintf(FILE_PATH_LITERAL("Profile%d"), index));
371  EXPECT_FALSE(GetProfile(index) == NULL) << "Could not create Profile "
372                                          << index << ".";
373
374  browsers_[index] = new Browser(Browser::CreateParams(
375      GetProfile(index), chrome::GetActiveDesktop()));
376  EXPECT_FALSE(GetBrowser(index) == NULL) << "Could not create Browser "
377                                          << index << ".";
378
379
380  // Make sure the ProfileSyncService has been created before creating the
381  // ProfileSyncServiceHarness - some tests expect the ProfileSyncService to
382  // already exist.
383  ProfileSyncService* profile_sync_service =
384      ProfileSyncServiceFactory::GetForProfile(GetProfile(index));
385
386  if (server_type_ == IN_PROCESS_FAKE_SERVER) {
387    // TODO(pvalenzuela): Run the fake server via EmbeddedTestServer.
388    profile_sync_service->OverrideNetworkResourcesForTest(
389        make_scoped_ptr<syncer::NetworkResources>(
390            new fake_server::FakeServerNetworkResources(fake_server_.get())));
391  }
392
393  clients_[index] =
394      ProfileSyncServiceHarness::Create(
395          GetProfile(index),
396          username_,
397          password_);
398  EXPECT_FALSE(GetClient(index) == NULL) << "Could not create Client "
399                                         << index << ".";
400  InitializeInvalidations(index);
401
402  test::WaitForBookmarkModelToLoad(
403      BookmarkModelFactory::GetForProfile(GetProfile(index)));
404  ui_test_utils::WaitForHistoryToLoad(HistoryServiceFactory::GetForProfile(
405      GetProfile(index), Profile::EXPLICIT_ACCESS));
406  ui_test_utils::WaitForTemplateURLServiceToLoad(
407      TemplateURLServiceFactory::GetForProfile(GetProfile(index)));
408}
409
410void SyncTest::InitializeInvalidations(int index) {
411  if (server_type_ == IN_PROCESS_FAKE_SERVER) {
412    CHECK(fake_server_.get());
413    fake_server::FakeServerInvalidationService* invalidation_service =
414        static_cast<fake_server::FakeServerInvalidationService*>(
415            static_cast<invalidation::ProfileInvalidationProvider*>(
416                invalidation::ProfileInvalidationProviderFactory::
417                    GetInstance()->SetTestingFactoryAndUse(
418                        GetProfile(index),
419                        BuildFakeServerProfileInvalidationProvider))->
420                            GetInvalidationService());
421    fake_server_->AddObserver(invalidation_service);
422    if (TestUsesSelfNotifications()) {
423      invalidation_service->EnableSelfNotifications();
424    } else {
425      invalidation_service->DisableSelfNotifications();
426    }
427    fake_server_invalidation_services_[index] = invalidation_service;
428  } else {
429    invalidation::P2PInvalidationService* p2p_invalidation_service =
430        static_cast<invalidation::P2PInvalidationService*>(
431            static_cast<invalidation::ProfileInvalidationProvider*>(
432                invalidation::ProfileInvalidationProviderFactory::
433                    GetInstance()->SetTestingFactoryAndUse(
434                        GetProfile(index),
435                        TestUsesSelfNotifications() ?
436                            BuildSelfNotifyingP2PProfileInvalidationProvider :
437                            BuildRealisticP2PProfileInvalidationProvider))->
438                                GetInvalidationService());
439    p2p_invalidation_service->UpdateCredentials(username_, password_);
440    // Start listening for and emitting notifications of commits.
441    invalidation_forwarders_[index] =
442        new P2PInvalidationForwarder(clients_[index]->service(),
443                                     p2p_invalidation_service);
444  }
445}
446
447bool SyncTest::SetupSync() {
448  // Create sync profiles and clients if they haven't already been created.
449  if (profiles_.empty()) {
450    if (!SetupClients())
451      LOG(FATAL) << "SetupClients() failed.";
452  }
453
454  // Sync each of the profiles.
455  for (int i = 0; i < num_clients_; ++i) {
456    if (!GetClient(i)->SetupSync())
457      LOG(FATAL) << "SetupSync() failed.";
458  }
459
460  // Because clients may modify sync data as part of startup (for example local
461  // session-releated data is rewritten), we need to ensure all startup-based
462  // changes have propagated between the clients.
463  //
464  // Tests that don't use self-notifications can't await quiescense.  They'll
465  // have to find their own way of waiting for an initial state if they really
466  // need such guarantees.
467  if (TestUsesSelfNotifications()) {
468    AwaitQuiescence();
469  }
470
471  return true;
472}
473
474void SyncTest::CleanUpOnMainThread() {
475  for (size_t i = 0; i < clients_.size(); ++i) {
476    clients_[i]->service()->DisableForUser();
477  }
478
479  // Some of the pending messages might rely on browser windows still being
480  // around, so run messages both before and after closing all browsers.
481  content::RunAllPendingInMessageLoop();
482  // Close all browser windows.
483  chrome::CloseAllBrowsers();
484  content::RunAllPendingInMessageLoop();
485
486  if (fake_server_.get()) {
487    std::vector<fake_server::FakeServerInvalidationService*>::const_iterator it;
488    for (it = fake_server_invalidation_services_.begin();
489         it != fake_server_invalidation_services_.end(); ++it) {
490      fake_server_->RemoveObserver(*it);
491    }
492  }
493
494  // All browsers should be closed at this point, or else we could see memory
495  // corruption in QuitBrowser().
496  CHECK_EQ(0U, chrome::GetTotalBrowserCount());
497  invalidation_forwarders_.clear();
498  fake_server_invalidation_services_.clear();
499  clients_.clear();
500}
501
502void SyncTest::SetUpInProcessBrowserTestFixture() {
503  // We don't take a reference to |resolver|, but mock_host_resolver_override_
504  // does, so effectively assumes ownership.
505  net::RuleBasedHostResolverProc* resolver =
506      new net::RuleBasedHostResolverProc(host_resolver());
507  resolver->AllowDirectLookup("*.google.com");
508  // On Linux, we use Chromium's NSS implementation which uses the following
509  // hosts for certificate verification. Without these overrides, running the
510  // integration tests on Linux causes error as we make external DNS lookups.
511  resolver->AllowDirectLookup("*.thawte.com");
512  resolver->AllowDirectLookup("*.geotrust.com");
513  resolver->AllowDirectLookup("*.gstatic.com");
514  mock_host_resolver_override_.reset(
515      new net::ScopedDefaultHostResolverProc(resolver));
516}
517
518void SyncTest::TearDownInProcessBrowserTestFixture() {
519  mock_host_resolver_override_.reset();
520}
521
522void SyncTest::ReadPasswordFile() {
523  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
524  password_file_ = cl->GetSwitchValuePath(switches::kPasswordFileForTest);
525  if (password_file_.empty())
526    LOG(FATAL) << "Can't run live server test without specifying --"
527               << switches::kPasswordFileForTest << "=<filename>";
528  std::string file_contents;
529  base::ReadFileToString(password_file_, &file_contents);
530  ASSERT_NE(file_contents, "") << "Password file \""
531      << password_file_.value() << "\" does not exist.";
532  std::vector<std::string> tokens;
533  std::string delimiters = "\r\n";
534  Tokenize(file_contents, delimiters, &tokens);
535  ASSERT_EQ(2U, tokens.size()) << "Password file \""
536      << password_file_.value()
537      << "\" must contain exactly two lines of text.";
538  username_ = tokens[0];
539  password_ = tokens[1];
540}
541
542void SyncTest::SetupMockGaiaResponses() {
543  factory_.reset(new net::URLFetcherImplFactory());
544  fake_factory_.reset(new net::FakeURLFetcherFactory(factory_.get()));
545  fake_factory_->SetFakeResponse(
546      GaiaUrls::GetInstance()->get_user_info_url(),
547      "email=user@gmail.com\ndisplayEmail=user@gmail.com",
548      net::HTTP_OK,
549      net::URLRequestStatus::SUCCESS);
550  fake_factory_->SetFakeResponse(
551      GaiaUrls::GetInstance()->issue_auth_token_url(),
552      "auth",
553      net::HTTP_OK,
554      net::URLRequestStatus::SUCCESS);
555  fake_factory_->SetFakeResponse(
556      GURL(GoogleURLTracker::kSearchDomainCheckURL),
557      ".google.com",
558      net::HTTP_OK,
559      net::URLRequestStatus::SUCCESS);
560  fake_factory_->SetFakeResponse(
561      GaiaUrls::GetInstance()->client_login_to_oauth2_url(),
562      "some_response",
563      net::HTTP_OK,
564      net::URLRequestStatus::SUCCESS);
565  fake_factory_->SetFakeResponse(
566      GaiaUrls::GetInstance()->oauth2_token_url(),
567      "{"
568      "  \"refresh_token\": \"rt1\","
569      "  \"access_token\": \"at1\","
570      "  \"expires_in\": 3600,"
571      "  \"token_type\": \"Bearer\""
572      "}",
573      net::HTTP_OK,
574      net::URLRequestStatus::SUCCESS);
575  fake_factory_->SetFakeResponse(
576      GaiaUrls::GetInstance()->people_get_url(),
577      "{"
578      "  \"id\": \"12345\""
579      "}",
580      net::HTTP_OK,
581      net::URLRequestStatus::SUCCESS);
582  fake_factory_->SetFakeResponse(
583      GaiaUrls::GetInstance()->oauth1_login_url(),
584      "SID=sid\nLSID=lsid\nAuth=auth_token",
585      net::HTTP_OK,
586      net::URLRequestStatus::SUCCESS);
587  fake_factory_->SetFakeResponse(
588      GaiaUrls::GetInstance()->oauth2_revoke_url(),
589      "",
590      net::HTTP_OK,
591      net::URLRequestStatus::SUCCESS);
592}
593
594void SyncTest::SetOAuth2TokenResponse(const std::string& response_data,
595                                      net::HttpStatusCode response_code,
596                                      net::URLRequestStatus::Status status) {
597  ASSERT_TRUE(NULL != fake_factory_.get());
598  fake_factory_->SetFakeResponse(GaiaUrls::GetInstance()->oauth2_token_url(),
599                                 response_data, response_code, status);
600}
601
602void SyncTest::ClearMockGaiaResponses() {
603  // Clear any mock gaia responses that might have been set.
604  if (fake_factory_) {
605    fake_factory_->ClearFakeResponses();
606    fake_factory_.reset();
607  }
608
609  // Cancel any outstanding URL fetches and destroy the URLFetcherImplFactory we
610  // created.
611  net::URLFetcher::CancelAll();
612  factory_.reset();
613}
614
615void SyncTest::DecideServerType() {
616  // Only set |server_type_| if it hasn't already been set. This allows for
617  // tests to explicitly set this value in each test class if needed.
618  if (server_type_ == SERVER_TYPE_UNDECIDED) {
619    base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
620    if (!cl->HasSwitch(switches::kSyncServiceURL) &&
621        !cl->HasSwitch(switches::kSyncServerCommandLine)) {
622      // If neither a sync server URL nor a sync server command line is
623      // provided, start up a local sync test server and point Chrome
624      // to its URL.  This is the most common configuration, and the only
625      // one that makes sense for most developers. FakeServer is the
626      // current solution but some scenarios are only supported by the
627      // legacy python server.
628      switch (test_type_) {
629        case SINGLE_CLIENT:
630        case TWO_CLIENT:
631        case MULTIPLE_CLIENT:
632          server_type_ = IN_PROCESS_FAKE_SERVER;
633          break;
634        default:
635          server_type_ = LOCAL_PYTHON_SERVER;
636      }
637    } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
638               cl->HasSwitch(switches::kSyncServerCommandLine)) {
639      // If a sync server URL and a sync server command line are provided,
640      // start up a local sync server by running the command line. Chrome
641      // will connect to the server at the URL that was provided.
642      server_type_ = LOCAL_LIVE_SERVER;
643    } else if (cl->HasSwitch(switches::kSyncServiceURL) &&
644               !cl->HasSwitch(switches::kSyncServerCommandLine)) {
645      // If a sync server URL is provided, but not a server command line,
646      // it is assumed that the server is already running. Chrome will
647      // automatically connect to it at the URL provided. There is nothing
648      // to do here.
649      server_type_ = EXTERNAL_LIVE_SERVER;
650    } else {
651      // If a sync server command line is provided, but not a server URL,
652      // we flag an error.
653      LOG(FATAL) << "Can't figure out how to run a server.";
654    }
655  }
656}
657
658// Start up a local sync server based on the value of server_type_, which
659// was determined from the command line parameters.
660void SyncTest::SetUpTestServerIfRequired() {
661  if (server_type_ == LOCAL_PYTHON_SERVER) {
662    if (!SetUpLocalPythonTestServer())
663      LOG(FATAL) << "Failed to set up local python sync and XMPP servers";
664    SetupMockGaiaResponses();
665  } else if (server_type_ == LOCAL_LIVE_SERVER) {
666    // Using mock gaia credentials requires the use of a mock XMPP server.
667    if (username_ == "user@gmail.com" && !SetUpLocalPythonTestServer())
668      LOG(FATAL) << "Failed to set up local python XMPP server";
669    if (!SetUpLocalTestServer())
670      LOG(FATAL) << "Failed to set up local test server";
671  } else if (server_type_ == IN_PROCESS_FAKE_SERVER) {
672    fake_server_.reset(new fake_server::FakeServer());
673    SetupMockGaiaResponses();
674  } else if (server_type_ == EXTERNAL_LIVE_SERVER) {
675    // Nothing to do; we'll just talk to the URL we were given.
676  } else {
677    LOG(FATAL) << "Don't know which server environment to run test in.";
678  }
679}
680
681bool SyncTest::SetUpLocalPythonTestServer() {
682  EXPECT_TRUE(sync_server_.Start())
683      << "Could not launch local python test server.";
684
685  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
686  if (server_type_ == LOCAL_PYTHON_SERVER) {
687    std::string sync_service_url = sync_server_.GetURL("chromiumsync").spec();
688    cl->AppendSwitchASCII(switches::kSyncServiceURL, sync_service_url);
689    DVLOG(1) << "Started local python sync server at " << sync_service_url;
690  }
691
692  int xmpp_port = 0;
693  if (!sync_server_.server_data().GetInteger("xmpp_port", &xmpp_port)) {
694    LOG(ERROR) << "Could not find valid xmpp_port value";
695    return false;
696  }
697  if ((xmpp_port <= 0) || (xmpp_port > kuint16max)) {
698    LOG(ERROR) << "Invalid xmpp port: " << xmpp_port;
699    return false;
700  }
701
702  net::HostPortPair xmpp_host_port_pair(sync_server_.host_port_pair());
703  xmpp_host_port_pair.set_port(xmpp_port);
704  xmpp_port_.reset(new net::ScopedPortException(xmpp_port));
705
706  if (!cl->HasSwitch(invalidation::switches::kSyncNotificationHostPort)) {
707    cl->AppendSwitchASCII(invalidation::switches::kSyncNotificationHostPort,
708                          xmpp_host_port_pair.ToString());
709    // The local XMPP server only supports insecure connections.
710    cl->AppendSwitch(invalidation::switches::kSyncAllowInsecureXmppConnection);
711  }
712  DVLOG(1) << "Started local python XMPP server at "
713           << xmpp_host_port_pair.ToString();
714
715  return true;
716}
717
718bool SyncTest::SetUpLocalTestServer() {
719  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
720  base::CommandLine::StringType server_cmdline_string =
721      cl->GetSwitchValueNative(switches::kSyncServerCommandLine);
722  base::CommandLine::StringVector server_cmdline_vector;
723  base::CommandLine::StringType delimiters(FILE_PATH_LITERAL(" "));
724  Tokenize(server_cmdline_string, delimiters, &server_cmdline_vector);
725  base::CommandLine server_cmdline(server_cmdline_vector);
726  base::LaunchOptions options;
727#if defined(OS_WIN)
728  options.start_hidden = true;
729#endif
730  if (!base::LaunchProcess(server_cmdline, options, &test_server_handle_))
731    LOG(ERROR) << "Could not launch local test server.";
732
733  const base::TimeDelta kMaxWaitTime = TestTimeouts::action_max_timeout();
734  const int kNumIntervals = 15;
735  if (WaitForTestServerToStart(kMaxWaitTime, kNumIntervals)) {
736    DVLOG(1) << "Started local test server at "
737             << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << ".";
738    return true;
739  } else {
740    LOG(ERROR) << "Could not start local test server at "
741               << cl->GetSwitchValueASCII(switches::kSyncServiceURL) << ".";
742    return false;
743  }
744}
745
746bool SyncTest::TearDownLocalPythonTestServer() {
747  if (!sync_server_.Stop()) {
748    LOG(ERROR) << "Could not stop local python test server.";
749    return false;
750  }
751  xmpp_port_.reset();
752  return true;
753}
754
755bool SyncTest::TearDownLocalTestServer() {
756  if (test_server_handle_ != base::kNullProcessHandle) {
757    EXPECT_TRUE(base::KillProcess(test_server_handle_, 0, false))
758        << "Could not stop local test server.";
759    base::CloseProcessHandle(test_server_handle_);
760    test_server_handle_ = base::kNullProcessHandle;
761  }
762  return true;
763}
764
765bool SyncTest::WaitForTestServerToStart(base::TimeDelta wait, int intervals) {
766  for (int i = 0; i < intervals; ++i) {
767    if (IsTestServerRunning())
768      return true;
769    base::PlatformThread::Sleep(wait / intervals);
770  }
771  return false;
772}
773
774bool SyncTest::IsTestServerRunning() {
775  base::CommandLine* cl = base::CommandLine::ForCurrentProcess();
776  std::string sync_url = cl->GetSwitchValueASCII(switches::kSyncServiceURL);
777  GURL sync_url_status(sync_url.append("/healthz"));
778  SyncServerStatusChecker delegate;
779  scoped_ptr<net::URLFetcher> fetcher(net::URLFetcher::Create(
780    sync_url_status, net::URLFetcher::GET, &delegate));
781  fetcher->SetLoadFlags(net::LOAD_DISABLE_CACHE |
782                        net::LOAD_DO_NOT_SEND_COOKIES |
783                        net::LOAD_DO_NOT_SAVE_COOKIES);
784  fetcher->SetRequestContext(g_browser_process->system_request_context());
785  fetcher->Start();
786  content::RunMessageLoop();
787  return delegate.running();
788}
789
790void SyncTest::EnableNetwork(Profile* profile) {
791  // TODO(pvalenzuela): Remove this restriction when FakeServer's observers
792  // (namely FakeServerInvaldationService) are aware of a network disconnect.
793  ASSERT_NE(IN_PROCESS_FAKE_SERVER, server_type_)
794      << "FakeServer does not support EnableNetwork.";
795  SetProxyConfig(profile->GetRequestContext(),
796                 net::ProxyConfig::CreateDirect());
797  if (notifications_enabled_) {
798    EnableNotificationsImpl();
799  }
800  // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed.
801  net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
802}
803
804void SyncTest::DisableNetwork(Profile* profile) {
805  // TODO(pvalenzuela): Remove this restriction when FakeServer's observers
806  // (namely FakeServerInvaldationService) are aware of a network disconnect.
807  ASSERT_NE(IN_PROCESS_FAKE_SERVER, server_type_)
808      << "FakeServer does not support DisableNetwork.";
809  DisableNotificationsImpl();
810  // Set the current proxy configuration to a nonexistent proxy to effectively
811  // disable networking.
812  net::ProxyConfig config;
813  config.proxy_rules().ParseFromString("http=127.0.0.1:0");
814  SetProxyConfig(profile->GetRequestContext(), config);
815  // TODO(rsimha): Remove this line once http://crbug.com/53857 is fixed.
816  net::NetworkChangeNotifier::NotifyObserversOfIPAddressChangeForTests();
817}
818
819bool SyncTest::TestUsesSelfNotifications() {
820  return true;
821}
822
823bool SyncTest::EnableEncryption(int index) {
824  ProfileSyncService* service = GetClient(index)->service();
825
826  if (::IsEncryptionComplete(service))
827    return true;
828
829  service->EnableEncryptEverything();
830
831  // In order to kick off the encryption we have to reconfigure. Just grab the
832  // currently synced types and use them.
833  const syncer::ModelTypeSet synced_datatypes =
834      service->GetPreferredDataTypes();
835  bool sync_everything = synced_datatypes.Equals(syncer::ModelTypeSet::All());
836  service->OnUserChoseDatatypes(sync_everything, synced_datatypes);
837
838  // Wait some time to let the enryption finish.
839  EncryptionChecker checker(service);
840  checker.Wait();
841
842  return !checker.TimedOut();
843}
844
845bool SyncTest::IsEncryptionComplete(int index) {
846  return ::IsEncryptionComplete(GetClient(index)->service());
847}
848
849bool SyncTest::AwaitQuiescence() {
850  return ProfileSyncServiceHarness::AwaitQuiescence(clients());
851}
852
853bool SyncTest::ServerSupportsNotificationControl() const {
854  EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_);
855
856  // Supported only if we're using the python testserver.
857  return server_type_ == LOCAL_PYTHON_SERVER;
858}
859
860void SyncTest::DisableNotificationsImpl() {
861  ASSERT_TRUE(ServerSupportsNotificationControl());
862  std::string path = "chromiumsync/disablenotifications";
863  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
864  ASSERT_EQ("Notifications disabled",
865            base::UTF16ToASCII(
866                browser()->tab_strip_model()->GetActiveWebContents()->
867                    GetTitle()));
868}
869
870void SyncTest::DisableNotifications() {
871  DisableNotificationsImpl();
872  notifications_enabled_ = false;
873}
874
875void SyncTest::EnableNotificationsImpl() {
876  ASSERT_TRUE(ServerSupportsNotificationControl());
877  std::string path = "chromiumsync/enablenotifications";
878  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
879  ASSERT_EQ("Notifications enabled",
880            base::UTF16ToASCII(
881                browser()->tab_strip_model()->GetActiveWebContents()->
882                    GetTitle()));
883}
884
885void SyncTest::EnableNotifications() {
886  EnableNotificationsImpl();
887  notifications_enabled_ = true;
888}
889
890void SyncTest::TriggerNotification(syncer::ModelTypeSet changed_types) {
891  ASSERT_TRUE(ServerSupportsNotificationControl());
892  const std::string& data =
893      syncer::P2PNotificationData(
894          "from_server",
895          syncer::NOTIFY_ALL,
896          syncer::ObjectIdInvalidationMap::InvalidateAll(
897              syncer::ModelTypeSetToObjectIdSet(changed_types))).ToString();
898  const std::string& path =
899      std::string("chromiumsync/sendnotification?channel=") +
900      syncer::kSyncP2PNotificationChannel + "&data=" + data;
901  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
902  ASSERT_EQ("Notification sent",
903            base::UTF16ToASCII(
904                browser()->tab_strip_model()->GetActiveWebContents()->
905                    GetTitle()));
906}
907
908bool SyncTest::ServerSupportsErrorTriggering() const {
909  EXPECT_NE(SERVER_TYPE_UNDECIDED, server_type_);
910
911  // Supported only if we're using the python testserver.
912  return server_type_ == LOCAL_PYTHON_SERVER;
913}
914
915void SyncTest::TriggerMigrationDoneError(syncer::ModelTypeSet model_types) {
916  ASSERT_TRUE(ServerSupportsErrorTriggering());
917  std::string path = "chromiumsync/migrate";
918  char joiner = '?';
919  for (syncer::ModelTypeSet::Iterator it = model_types.First();
920       it.Good(); it.Inc()) {
921    path.append(
922        base::StringPrintf(
923            "%ctype=%d", joiner,
924            syncer::GetSpecificsFieldNumberFromModelType(it.Get())));
925    joiner = '&';
926  }
927  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
928  ASSERT_EQ("Migration: 200",
929            base::UTF16ToASCII(
930                browser()->tab_strip_model()->GetActiveWebContents()->
931                    GetTitle()));
932}
933
934void SyncTest::TriggerTransientError() {
935  ASSERT_TRUE(ServerSupportsErrorTriggering());
936  std::string path = "chromiumsync/transienterror";
937  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
938  ASSERT_EQ("Transient error",
939            base::UTF16ToASCII(
940                browser()->tab_strip_model()->GetActiveWebContents()->
941                    GetTitle()));
942}
943
944void SyncTest::TriggerXmppAuthError() {
945  ASSERT_TRUE(ServerSupportsErrorTriggering());
946  std::string path = "chromiumsync/xmppcred";
947  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
948}
949
950namespace {
951
952sync_pb::SyncEnums::ErrorType
953    GetClientToServerResponseErrorType(
954        syncer::SyncProtocolErrorType error) {
955  switch (error) {
956    case syncer::SYNC_SUCCESS:
957      return sync_pb::SyncEnums::SUCCESS;
958    case syncer::NOT_MY_BIRTHDAY:
959      return sync_pb::SyncEnums::NOT_MY_BIRTHDAY;
960    case syncer::THROTTLED:
961      return sync_pb::SyncEnums::THROTTLED;
962    case syncer::CLEAR_PENDING:
963      return sync_pb::SyncEnums::CLEAR_PENDING;
964    case syncer::TRANSIENT_ERROR:
965      return sync_pb::SyncEnums::TRANSIENT_ERROR;
966    case syncer::MIGRATION_DONE:
967      return sync_pb::SyncEnums::MIGRATION_DONE;
968    case syncer::UNKNOWN_ERROR:
969      return sync_pb::SyncEnums::UNKNOWN;
970    case syncer::INVALID_CREDENTIAL:
971      NOTREACHED();   // NOTREACHED() because auth error is not set through
972                      // error code in sync response.
973      return sync_pb::SyncEnums::UNKNOWN;
974    case syncer::DISABLED_BY_ADMIN:
975      return sync_pb::SyncEnums::DISABLED_BY_ADMIN;
976    case syncer::USER_ROLLBACK:
977      return sync_pb::SyncEnums::USER_ROLLBACK;
978    case syncer::NON_RETRIABLE_ERROR:
979      return sync_pb::SyncEnums::UNKNOWN;
980  }
981  return sync_pb::SyncEnums::UNKNOWN;
982}
983
984sync_pb::SyncEnums::Action GetClientToServerResponseAction(
985    const syncer::ClientAction& action) {
986  switch (action) {
987    case syncer::UPGRADE_CLIENT:
988      return sync_pb::SyncEnums::UPGRADE_CLIENT;
989    case syncer::CLEAR_USER_DATA_AND_RESYNC:
990      return sync_pb::SyncEnums::CLEAR_USER_DATA_AND_RESYNC;
991    case syncer::ENABLE_SYNC_ON_ACCOUNT:
992      return sync_pb::SyncEnums::ENABLE_SYNC_ON_ACCOUNT;
993    case syncer::STOP_AND_RESTART_SYNC:
994      return sync_pb::SyncEnums::STOP_AND_RESTART_SYNC;
995    case syncer::DISABLE_SYNC_ON_CLIENT:
996      return sync_pb::SyncEnums::DISABLE_SYNC_ON_CLIENT;
997    case syncer::STOP_SYNC_FOR_DISABLED_ACCOUNT:
998    case syncer::DISABLE_SYNC_AND_ROLLBACK:
999    case syncer::ROLLBACK_DONE:
1000      NOTREACHED();   // No corresponding proto action for these. Shouldn't
1001                      // test.
1002      return sync_pb::SyncEnums::UNKNOWN_ACTION;
1003    case syncer::UNKNOWN_ACTION:
1004      return sync_pb::SyncEnums::UNKNOWN_ACTION;
1005  }
1006  return sync_pb::SyncEnums::UNKNOWN_ACTION;
1007}
1008
1009}  // namespace
1010
1011void SyncTest::TriggerSyncError(const syncer::SyncProtocolError& error,
1012                                SyncErrorFrequency frequency) {
1013  ASSERT_TRUE(ServerSupportsErrorTriggering());
1014  std::string path = "chromiumsync/error";
1015  int error_type =
1016      static_cast<int>(GetClientToServerResponseErrorType(
1017          error.error_type));
1018  int action = static_cast<int>(GetClientToServerResponseAction(
1019      error.action));
1020
1021  path.append(base::StringPrintf("?error=%d", error_type));
1022  path.append(base::StringPrintf("&action=%d", action));
1023
1024  path.append(base::StringPrintf("&error_description=%s",
1025                                 error.error_description.c_str()));
1026  path.append(base::StringPrintf("&url=%s", error.url.c_str()));
1027  path.append(base::StringPrintf("&frequency=%d", frequency));
1028
1029  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
1030  std::string output = base::UTF16ToASCII(
1031      browser()->tab_strip_model()->GetActiveWebContents()->GetTitle());
1032  ASSERT_TRUE(output.find("SetError: 200") != base::string16::npos);
1033}
1034
1035void SyncTest::TriggerCreateSyncedBookmarks() {
1036  ASSERT_TRUE(ServerSupportsErrorTriggering());
1037  std::string path = "chromiumsync/createsyncedbookmarks";
1038  ui_test_utils::NavigateToURL(browser(), sync_server_.GetURL(path));
1039  ASSERT_EQ("Synced Bookmarks",
1040            base::UTF16ToASCII(
1041                browser()->tab_strip_model()->GetActiveWebContents()->
1042                    GetTitle()));
1043}
1044
1045void SyncTest::SetProxyConfig(net::URLRequestContextGetter* context_getter,
1046                              const net::ProxyConfig& proxy_config) {
1047  base::WaitableEvent done(false, false);
1048  BrowserThread::PostTask(
1049      BrowserThread::IO, FROM_HERE,
1050      base::Bind(&SetProxyConfigCallback, &done,
1051                 make_scoped_refptr(context_getter), proxy_config));
1052  done.Wait();
1053}
1054
1055fake_server::FakeServer* SyncTest::GetFakeServer() const {
1056  return fake_server_.get();
1057}
1058