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 "base/basictypes.h"
6 #include "base/compiler_specific.h"
7 #include "base/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop.h"
10 #include "base/values.h"
11 #include "chrome/browser/signin/signin_manager.h"
12 #include "chrome/browser/signin/signin_manager_factory.h"
13 #include "chrome/browser/signin/token_service.h"
14 #include "chrome/browser/signin/token_service_factory.h"
15 #include "chrome/browser/sync/glue/bookmark_data_type_controller.h"
16 #include "chrome/browser/sync/glue/data_type_controller.h"
17 #include "chrome/browser/sync/profile_sync_components_factory_mock.h"
18 #include "chrome/browser/sync/test_profile_sync_service.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/pref_names.h"
21 #include "chrome/test/base/testing_pref_service.h"
22 #include "chrome/test/base/testing_profile.h"
23 #include "content/public/common/content_client.h"
24 #include "content/public/test/test_browser_thread.h"
25 #include "google/cacheinvalidation/include/types.h"
26 #include "google_apis/gaia/gaia_constants.h"
27 #include "sync/js/js_arg_list.h"
28 #include "sync/js/js_event_details.h"
29 #include "sync/js/js_test_util.h"
30 #include "sync/notifier/fake_invalidation_handler.h"
31 #include "sync/notifier/invalidator.h"
32 #include "sync/notifier/invalidator_test_template.h"
33 #include "sync/notifier/object_id_invalidation_map_test_util.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "webkit/user_agent/user_agent.h"
38 // TODO(akalin): Add tests here that exercise the whole
39 // ProfileSyncService/SyncBackendHost stack while mocking out as
40 // little as possible.
42 namespace browser_sync
{
46 using content::BrowserThread
;
48 using testing::AtLeast
;
49 using testing::AtMost
;
51 using testing::Return
;
52 using testing::StrictMock
;
54 class ProfileSyncServiceTestHarness
{
56 ProfileSyncServiceTestHarness()
57 : ui_thread_(BrowserThread::UI
, &ui_loop_
),
58 db_thread_(BrowserThread::DB
),
59 file_thread_(BrowserThread::FILE),
60 io_thread_(BrowserThread::IO
) {}
62 ~ProfileSyncServiceTestHarness() {}
66 io_thread_
.StartIOThread();
67 profile
.reset(new TestingProfile());
68 profile
->CreateRequestContext();
70 // We need to set the user agent before the backend host can call
71 // webkit_glue::GetUserAgent().
72 webkit_glue::SetUserAgent(content::GetContentClient()->GetUserAgent(),
77 // Kill the service before the profile.
83 // Pump messages posted by the sync thread (which may end up
84 // posting on the IO thread).
85 ui_loop_
.RunAllPending();
88 // Ensure that the sync objects destruct to avoid memory leaks.
89 ui_loop_
.RunAllPending();
92 // TODO(akalin): Refactor the StartSyncService*() functions below.
94 void StartSyncService() {
95 StartSyncServiceAndSetInitialSyncEnded(
96 true, true, false, true, syncer::STORAGE_IN_MEMORY
);
99 void StartSyncServiceAndSetInitialSyncEnded(
100 bool set_initial_sync_ended
,
101 bool issue_auth_token
,
102 bool synchronous_sync_configuration
,
103 bool sync_setup_completed
,
104 syncer::StorageOption storage_option
) {
105 if (!service
.get()) {
106 SigninManager
* signin
=
107 SigninManagerFactory::GetForProfile(profile
.get());
108 signin
->SetAuthenticatedUsername("test");
109 ProfileSyncComponentsFactoryMock
* factory
=
110 new ProfileSyncComponentsFactoryMock();
111 service
.reset(new TestProfileSyncService(
115 ProfileSyncService::AUTO_START
,
118 if (!set_initial_sync_ended
)
119 service
->dont_set_initial_sync_ended_on_init();
120 if (synchronous_sync_configuration
)
121 service
->set_synchronous_sync_configuration();
122 service
->set_storage_option(storage_option
);
123 if (!sync_setup_completed
)
124 profile
->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted
, false);
126 // Register the bookmark data type.
127 ON_CALL(*factory
, CreateDataTypeManager(_
, _
, _
, _
)).
128 WillByDefault(ReturnNewDataTypeManager());
130 if (issue_auth_token
) {
133 service
->Initialize();
137 void IssueTestTokens() {
138 TokenService
* token_service
=
139 TokenServiceFactory::GetForProfile(profile
.get());
140 token_service
->IssueAuthTokenForTest(
141 GaiaConstants::kSyncService
, "token1");
142 token_service
->IssueAuthTokenForTest(
143 GaiaConstants::kGaiaOAuth2LoginRefreshToken
, "token2");
146 scoped_ptr
<TestProfileSyncService
> service
;
147 scoped_ptr
<TestingProfile
> profile
;
150 MessageLoop ui_loop_
;
151 // Needed by |service|.
152 content::TestBrowserThread ui_thread_
;
153 content::TestBrowserThread db_thread_
;
154 // Needed by DisableAndEnableSyncTemporarily test case.
155 content::TestBrowserThread file_thread_
;
156 // Needed by |service| and |profile|'s request context.
157 content::TestBrowserThread io_thread_
;
160 class ProfileSyncServiceTest
: public testing::Test
{
162 virtual void SetUp() {
166 virtual void TearDown() {
170 ProfileSyncServiceTestHarness harness_
;
173 TEST_F(ProfileSyncServiceTest
, InitialState
) {
174 SigninManager
* signin
=
175 SigninManagerFactory::GetForProfile(harness_
.profile
.get());
176 harness_
.service
.reset(new TestProfileSyncService(
177 new ProfileSyncComponentsFactoryMock(),
178 harness_
.profile
.get(),
180 ProfileSyncService::MANUAL_START
,
183 harness_
.service
->Initialize();
185 harness_
.service
->sync_service_url().spec() ==
186 ProfileSyncService::kSyncServerUrl
||
187 harness_
.service
->sync_service_url().spec() ==
188 ProfileSyncService::kDevServerUrl
);
191 TEST_F(ProfileSyncServiceTest
, DisabledByPolicy
) {
192 harness_
.profile
->GetTestingPrefService()->SetManagedPref(
194 Value::CreateBooleanValue(true));
195 SigninManager
* signin
=
196 SigninManagerFactory::GetForProfile(harness_
.profile
.get());
197 harness_
.service
.reset(new TestProfileSyncService(
198 new ProfileSyncComponentsFactoryMock(),
199 harness_
.profile
.get(),
201 ProfileSyncService::MANUAL_START
,
204 harness_
.service
->Initialize();
205 EXPECT_TRUE(harness_
.service
->IsManaged());
208 TEST_F(ProfileSyncServiceTest
, AbortedByShutdown
) {
209 SigninManager
* signin
=
210 SigninManagerFactory::GetForProfile(harness_
.profile
.get());
211 signin
->SetAuthenticatedUsername("test");
212 ProfileSyncComponentsFactoryMock
* factory
=
213 new ProfileSyncComponentsFactoryMock();
214 harness_
.service
.reset(new TestProfileSyncService(
216 harness_
.profile
.get(),
218 ProfileSyncService::AUTO_START
,
221 EXPECT_CALL(*factory
, CreateDataTypeManager(_
, _
, _
, _
)).Times(0);
222 EXPECT_CALL(*factory
, CreateBookmarkSyncComponents(_
, _
)).
224 harness_
.service
->RegisterDataTypeController(
225 new BookmarkDataTypeController(harness_
.service
->factory(),
226 harness_
.profile
.get(),
227 harness_
.service
.get()));
229 harness_
.service
->Initialize();
230 harness_
.service
.reset();
233 TEST_F(ProfileSyncServiceTest
, DisableAndEnableSyncTemporarily
) {
234 SigninManager
* signin
=
235 SigninManagerFactory::GetForProfile(harness_
.profile
.get());
236 signin
->SetAuthenticatedUsername("test");
237 ProfileSyncComponentsFactoryMock
* factory
=
238 new ProfileSyncComponentsFactoryMock();
239 harness_
.service
.reset(new TestProfileSyncService(
241 harness_
.profile
.get(),
243 ProfileSyncService::AUTO_START
,
246 // Register the bookmark data type.
247 EXPECT_CALL(*factory
, CreateDataTypeManager(_
, _
, _
, _
)).
248 WillRepeatedly(ReturnNewDataTypeManager());
250 harness_
.IssueTestTokens();
252 harness_
.service
->Initialize();
253 EXPECT_TRUE(harness_
.service
->sync_initialized());
254 EXPECT_TRUE(harness_
.service
->GetBackendForTest() != NULL
);
256 harness_
.profile
->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart
));
258 harness_
.service
->StopAndSuppress();
259 EXPECT_FALSE(harness_
.service
->sync_initialized());
261 harness_
.profile
->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart
));
263 harness_
.service
->UnsuppressAndStart();
264 EXPECT_TRUE(harness_
.service
->sync_initialized());
266 harness_
.profile
->GetPrefs()->GetBoolean(prefs::kSyncSuppressStart
));
269 TEST_F(ProfileSyncServiceTest
, JsControllerHandlersBasic
) {
270 harness_
.StartSyncService();
271 EXPECT_TRUE(harness_
.service
->sync_initialized());
272 EXPECT_TRUE(harness_
.service
->GetBackendForTest() != NULL
);
274 syncer::JsController
* js_controller
= harness_
.service
->GetJsController();
275 StrictMock
<syncer::MockJsEventHandler
> event_handler
;
276 js_controller
->AddJsEventHandler(&event_handler
);
277 js_controller
->RemoveJsEventHandler(&event_handler
);
280 TEST_F(ProfileSyncServiceTest
,
281 JsControllerHandlersDelayedBackendInitialization
) {
282 harness_
.StartSyncServiceAndSetInitialSyncEnded(true, false, false, true,
283 syncer::STORAGE_IN_MEMORY
);
285 StrictMock
<syncer::MockJsEventHandler
> event_handler
;
286 EXPECT_CALL(event_handler
, HandleJsEvent(_
, _
)).Times(AtLeast(1));
288 EXPECT_EQ(NULL
, harness_
.service
->GetBackendForTest());
289 EXPECT_FALSE(harness_
.service
->sync_initialized());
291 syncer::JsController
* js_controller
= harness_
.service
->GetJsController();
292 js_controller
->AddJsEventHandler(&event_handler
);
293 // Since we're doing synchronous initialization, backend should be
294 // initialized by this call.
295 harness_
.IssueTestTokens();
296 EXPECT_TRUE(harness_
.service
->sync_initialized());
297 js_controller
->RemoveJsEventHandler(&event_handler
);
300 TEST_F(ProfileSyncServiceTest
, JsControllerProcessJsMessageBasic
) {
301 harness_
.StartSyncService();
303 StrictMock
<syncer::MockJsReplyHandler
> reply_handler
;
306 arg_list1
.Append(Value::CreateStringValue("TRANSIENT_INVALIDATION_ERROR"));
307 syncer::JsArgList
args1(&arg_list1
);
308 EXPECT_CALL(reply_handler
,
309 HandleJsReply("getNotificationState", HasArgs(args1
)));
312 syncer::JsController
* js_controller
= harness_
.service
->GetJsController();
313 js_controller
->ProcessJsMessage("getNotificationState", args1
,
314 reply_handler
.AsWeakHandle());
317 // This forces the sync thread to process the message and reply.
321 TEST_F(ProfileSyncServiceTest
,
322 JsControllerProcessJsMessageBasicDelayedBackendInitialization
) {
323 harness_
.StartSyncServiceAndSetInitialSyncEnded(true, false, false, true,
324 syncer::STORAGE_IN_MEMORY
);
326 StrictMock
<syncer::MockJsReplyHandler
> reply_handler
;
329 arg_list1
.Append(Value::CreateStringValue("TRANSIENT_INVALIDATION_ERROR"));
330 syncer::JsArgList
args1(&arg_list1
);
331 EXPECT_CALL(reply_handler
,
332 HandleJsReply("getNotificationState", HasArgs(args1
)));
335 syncer::JsController
* js_controller
= harness_
.service
->GetJsController();
336 js_controller
->ProcessJsMessage("getNotificationState",
337 args1
, reply_handler
.AsWeakHandle());
340 harness_
.IssueTestTokens();
342 // This forces the sync thread to process the message and reply.
346 // Make sure that things still work if sync is not enabled, but some old sync
347 // databases are lingering in the "Sync Data" folder.
348 TEST_F(ProfileSyncServiceTest
, TestStartupWithOldSyncData
) {
349 const char* nonsense1
= "reginald";
350 const char* nonsense2
= "beartato";
351 const char* nonsense3
= "harrison";
352 FilePath temp_directory
=
353 harness_
.profile
->GetPath().AppendASCII("Sync Data");
354 FilePath sync_file1
=
355 temp_directory
.AppendASCII("BookmarkSyncSettings.sqlite3");
356 FilePath sync_file2
= temp_directory
.AppendASCII("SyncData.sqlite3");
357 FilePath sync_file3
= temp_directory
.AppendASCII("nonsense_file");
358 ASSERT_TRUE(file_util::CreateDirectory(temp_directory
));
360 file_util::WriteFile(sync_file1
, nonsense1
, strlen(nonsense1
)));
362 file_util::WriteFile(sync_file2
, nonsense2
, strlen(nonsense2
)));
364 file_util::WriteFile(sync_file3
, nonsense3
, strlen(nonsense3
)));
366 harness_
.StartSyncServiceAndSetInitialSyncEnded(false, false, true, false,
367 syncer::STORAGE_ON_DISK
);
368 EXPECT_FALSE(harness_
.service
->HasSyncSetupCompleted());
369 EXPECT_FALSE(harness_
.service
->sync_initialized());
371 // Since we're doing synchronous initialization, backend should be
372 // initialized by this call.
373 harness_
.IssueTestTokens();
375 // Stop the service so we can read the new Sync Data files that were
377 harness_
.service
->Shutdown();
378 harness_
.service
.reset();
380 // This file should have been deleted when the whole directory was nuked.
381 ASSERT_FALSE(file_util::PathExists(sync_file3
));
382 ASSERT_FALSE(file_util::PathExists(sync_file1
));
384 // This will still exist, but the text should have changed.
385 ASSERT_TRUE(file_util::PathExists(sync_file2
));
386 std::string file2text
;
387 ASSERT_TRUE(file_util::ReadFileToString(sync_file2
, &file2text
));
388 ASSERT_NE(file2text
.compare(nonsense2
), 0);
391 // Simulates a scenario where a database is corrupted and it is impossible to
392 // recreate it. This test is useful mainly when it is run under valgrind. Its
393 // expectations are not very interesting.
394 TEST_F(ProfileSyncServiceTest
, FailToOpenDatabase
) {
395 harness_
.StartSyncServiceAndSetInitialSyncEnded(false, true, true, true,
396 syncer::STORAGE_INVALID
);
398 // The backend is not ready. Ensure the PSS knows this.
399 EXPECT_FALSE(harness_
.service
->sync_initialized());
402 // Register a handler with the ProfileSyncService, and disable and
403 // reenable sync. The handler should get notified of the state
405 // Flaky on all platforms. http://crbug.com/154491
406 TEST_F(ProfileSyncServiceTest
, FLAKY_DisableInvalidationsOnStop
) {
407 harness_
.StartSyncServiceAndSetInitialSyncEnded(
408 true, true, true, true, syncer::STORAGE_IN_MEMORY
);
410 syncer::FakeInvalidationHandler handler
;
411 harness_
.service
->RegisterInvalidationHandler(&handler
);
413 SyncBackendHostForProfileSyncTest
* const backend
=
414 harness_
.service
->GetBackendForTest();
416 backend
->EmitOnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED
);
417 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED
, handler
.GetInvalidatorState());
419 harness_
.service
->StopAndSuppress();
420 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR
,
421 handler
.GetInvalidatorState());
423 harness_
.service
->UnsuppressAndStart();
424 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED
, handler
.GetInvalidatorState());
426 harness_
.service
->UnregisterInvalidationHandler(&handler
);
429 // Register for some IDs with the ProfileSyncService, restart sync,
430 // and trigger some invalidation messages. They should still be
431 // received by the handler.
432 TEST_F(ProfileSyncServiceTest
, UpdateRegisteredInvalidationIdsPersistence
) {
433 harness_
.StartSyncService();
435 syncer::ObjectIdSet ids
;
436 ids
.insert(invalidation::ObjectId(3, "id3"));
437 const syncer::ObjectIdInvalidationMap
& states
=
438 syncer::ObjectIdSetToInvalidationMap(ids
, "payload");
440 syncer::FakeInvalidationHandler handler
;
442 harness_
.service
->RegisterInvalidationHandler(&handler
);
443 harness_
.service
->UpdateRegisteredInvalidationIds(&handler
, ids
);
445 harness_
.service
->StopAndSuppress();
446 harness_
.service
->UnsuppressAndStart();
448 SyncBackendHostForProfileSyncTest
* const backend
=
449 harness_
.service
->GetBackendForTest();
451 backend
->EmitOnInvalidatorStateChange(syncer::INVALIDATIONS_ENABLED
);
452 EXPECT_EQ(syncer::INVALIDATIONS_ENABLED
, handler
.GetInvalidatorState());
454 backend
->EmitOnIncomingInvalidation(states
, syncer::REMOTE_INVALIDATION
);
455 EXPECT_THAT(states
, Eq(handler
.GetLastInvalidationMap()));
456 EXPECT_EQ(syncer::REMOTE_INVALIDATION
, handler
.GetLastInvalidationSource());
458 backend
->EmitOnInvalidatorStateChange(syncer::TRANSIENT_INVALIDATION_ERROR
);
459 EXPECT_EQ(syncer::TRANSIENT_INVALIDATION_ERROR
,
460 handler
.GetInvalidatorState());
462 harness_
.service
->UnregisterInvalidationHandler(&handler
);
465 // Thin Invalidator wrapper around ProfileSyncService.
466 class ProfileSyncServiceInvalidator
: public syncer::Invalidator
{
468 explicit ProfileSyncServiceInvalidator(ProfileSyncService
* service
)
469 : service_(service
) {}
471 virtual ~ProfileSyncServiceInvalidator() {}
473 // Invalidator implementation.
474 virtual void RegisterHandler(syncer::InvalidationHandler
* handler
) OVERRIDE
{
475 service_
->RegisterInvalidationHandler(handler
);
478 virtual void UpdateRegisteredIds(syncer::InvalidationHandler
* handler
,
479 const syncer::ObjectIdSet
& ids
) OVERRIDE
{
480 service_
->UpdateRegisteredInvalidationIds(handler
, ids
);
483 virtual void UnregisterHandler(
484 syncer::InvalidationHandler
* handler
) OVERRIDE
{
485 service_
->UnregisterInvalidationHandler(handler
);
488 virtual syncer::InvalidatorState
GetInvalidatorState() const OVERRIDE
{
489 return service_
->GetInvalidatorState();
492 virtual void SetUniqueId(const std::string
& unique_id
) OVERRIDE
{
496 virtual void SetStateDeprecated(const std::string
& state
) OVERRIDE
{
500 virtual void UpdateCredentials(
501 const std::string
& email
, const std::string
& token
) OVERRIDE
{
505 virtual void SendInvalidation(
506 const syncer::ObjectIdInvalidationMap
& invalidation_map
) OVERRIDE
{
511 ProfileSyncService
* const service_
;
513 DISALLOW_COPY_AND_ASSIGN(ProfileSyncServiceInvalidator
);
519 // ProfileSyncServiceInvalidatorTestDelegate has to be visible from
520 // the syncer namespace (where InvalidatorTest lives).
521 class ProfileSyncServiceInvalidatorTestDelegate
{
523 ProfileSyncServiceInvalidatorTestDelegate() {}
525 ~ProfileSyncServiceInvalidatorTestDelegate() {
526 DestroyInvalidator();
529 void CreateInvalidator(
530 const std::string
& initial_state
,
531 const base::WeakPtr
<syncer::InvalidationStateTracker
>&
532 invalidation_state_tracker
) {
533 DCHECK(!invalidator_
.get());
535 harness_
.StartSyncService();
537 new ProfileSyncServiceInvalidator(harness_
.service
.get()));
540 ProfileSyncServiceInvalidator
* GetInvalidator() {
541 return invalidator_
.get();
544 void DestroyInvalidator() {
545 invalidator_
.reset();
549 void WaitForInvalidator() {
553 void TriggerOnInvalidatorStateChange(syncer::InvalidatorState state
) {
554 harness_
.service
->GetBackendForTest()->EmitOnInvalidatorStateChange(state
);
557 void TriggerOnIncomingInvalidation(
558 const syncer::ObjectIdInvalidationMap
& invalidation_map
,
559 syncer::IncomingInvalidationSource source
) {
560 harness_
.service
->GetBackendForTest()->EmitOnIncomingInvalidation(
561 invalidation_map
, source
);
564 static bool InvalidatorHandlesDeprecatedState() {
569 ProfileSyncServiceTestHarness harness_
;
570 scoped_ptr
<ProfileSyncServiceInvalidator
> invalidator_
;
573 } // namespace browser_sync
578 // ProfileSyncService should behave just like an invalidator.
579 INSTANTIATE_TYPED_TEST_CASE_P(
580 ProfileSyncServiceInvalidatorTest
, InvalidatorTest
,
581 ::browser_sync::ProfileSyncServiceInvalidatorTestDelegate
);
584 } // namespace syncer