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