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