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