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