Home | History | Annotate | Download | only in sync
      1 // Copyright (c) 2011 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 "base/file_util.h"
      6 #include "base/memory/scoped_ptr.h"
      7 #include "base/message_loop.h"
      8 #include "base/values.h"
      9 #include "chrome/browser/net/gaia/token_service.h"
     10 #include "chrome/browser/sync/glue/bookmark_data_type_controller.h"
     11 #include "chrome/browser/sync/glue/data_type_controller.h"
     12 #include "chrome/browser/sync/js_arg_list.h"
     13 #include "chrome/browser/sync/js_test_util.h"
     14 #include "chrome/browser/sync/profile_sync_factory_mock.h"
     15 #include "chrome/browser/sync/test_profile_sync_service.h"
     16 #include "chrome/common/net/gaia/gaia_constants.h"
     17 #include "chrome/common/pref_names.h"
     18 #include "chrome/test/testing_pref_service.h"
     19 #include "chrome/test/testing_profile.h"
     20 #include "content/browser/browser_thread.h"
     21 #include "testing/gmock/include/gmock/gmock.h"
     22 #include "testing/gtest/include/gtest/gtest.h"
     23 
     24 // TODO(akalin): Add tests here that exercise the whole
     25 // ProfileSyncService/SyncBackendHost stack while mocking out as
     26 // little as possible.
     27 
     28 namespace browser_sync {
     29 
     30 namespace {
     31 
     32 using testing::_;
     33 using testing::AtLeast;
     34 using testing::AtMost;
     35 using testing::Return;
     36 using testing::StrictMock;
     37 
     38 class ProfileSyncServiceTest : public testing::Test {
     39  protected:
     40   ProfileSyncServiceTest()
     41       : ui_thread_(BrowserThread::UI, &message_loop_) {
     42     profile_.reset(new TestingProfile());
     43   }
     44   virtual ~ProfileSyncServiceTest() {
     45     // Kill the service before the profile.
     46     service_.reset();
     47     profile_.reset();
     48 
     49     // Ensure that the sync objects destruct to avoid memory leaks.
     50     MessageLoop::current()->RunAllPending();
     51   }
     52   virtual void SetUp() {
     53     profile_->CreateRequestContext();
     54   }
     55   virtual void TearDown() {
     56     {
     57       // The request context gets deleted on the I/O thread. To prevent a leak
     58       // supply one here.
     59       BrowserThread io_thread(BrowserThread::IO, MessageLoop::current());
     60       profile_->ResetRequestContext();
     61     }
     62     MessageLoop::current()->RunAllPending();
     63   }
     64 
     65   // TODO(akalin): Refactor the StartSyncService*() functions below.
     66 
     67   void StartSyncService() {
     68     StartSyncServiceAndSetInitialSyncEnded(true, true, false, true);
     69   }
     70 
     71   void StartSyncServiceAndSetInitialSyncEnded(
     72       bool set_initial_sync_ended,
     73       bool issue_auth_token,
     74       bool synchronous_sync_configuration,
     75       bool sync_setup_completed) {
     76     if (!service_.get()) {
     77       // Set bootstrap to true and it will provide a logged in user for test
     78       service_.reset(new TestProfileSyncService(&factory_,
     79                                                 profile_.get(),
     80                                                 "test", true, NULL));
     81       if (!set_initial_sync_ended)
     82         service_->dont_set_initial_sync_ended_on_init();
     83       if (synchronous_sync_configuration)
     84         service_->set_synchronous_sync_configuration();
     85       if (!sync_setup_completed)
     86         profile_->GetPrefs()->SetBoolean(prefs::kSyncHasSetupCompleted, false);
     87 
     88       // Register the bookmark data type.
     89       EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
     90           WillOnce(ReturnNewDataTypeManager());
     91 
     92       if (issue_auth_token) {
     93         profile_->GetTokenService()->IssueAuthTokenForTest(
     94             GaiaConstants::kSyncService, "token");
     95       }
     96       service_->Initialize();
     97     }
     98   }
     99 
    100   // This serves as the "UI loop" on which the ProfileSyncService lives and
    101   // operates. It is needed because the SyncBackend can post tasks back to
    102   // the service, meaning it can't be null. It doesn't have to be running,
    103   // though -- OnInitializationCompleted is the only example (so far) in this
    104   // test where we need to Run the loop to swallow a task and then quit, to
    105   // avoid leaking the ProfileSyncService (the PostTask will retain the callee
    106   // and caller until the task is run).
    107   MessageLoop message_loop_;
    108   BrowserThread ui_thread_;
    109 
    110   scoped_ptr<TestProfileSyncService> service_;
    111   scoped_ptr<TestingProfile> profile_;
    112   ProfileSyncFactoryMock factory_;
    113 };
    114 
    115 TEST_F(ProfileSyncServiceTest, InitialState) {
    116   service_.reset(new TestProfileSyncService(&factory_, profile_.get(),
    117                                             "", true, NULL));
    118   EXPECT_TRUE(
    119       service_->sync_service_url().spec() ==
    120         ProfileSyncService::kSyncServerUrl ||
    121       service_->sync_service_url().spec() ==
    122         ProfileSyncService::kDevServerUrl);
    123 }
    124 
    125 TEST_F(ProfileSyncServiceTest, DisabledByPolicy) {
    126   profile_->GetTestingPrefService()->SetManagedPref(
    127       prefs::kSyncManaged,
    128       Value::CreateBooleanValue(true));
    129   service_.reset(new TestProfileSyncService(&factory_, profile_.get(),
    130                                             "", true, NULL));
    131   service_->Initialize();
    132   EXPECT_TRUE(service_->IsManaged());
    133 }
    134 
    135 TEST_F(ProfileSyncServiceTest, AbortedByShutdown) {
    136   service_.reset(new TestProfileSyncService(&factory_, profile_.get(),
    137                                             "test", true, NULL));
    138   EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).Times(0);
    139   EXPECT_CALL(factory_, CreateBookmarkSyncComponents(_, _)).Times(0);
    140   service_->RegisterDataTypeController(
    141       new BookmarkDataTypeController(&factory_,
    142                                      profile_.get(),
    143                                      service_.get()));
    144 
    145   service_->Initialize();
    146   service_.reset();
    147 }
    148 #if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD)
    149 #define MAYBE_JsFrontendHandlersBasic DISABLED_JsFrontendHandlersBasic
    150 #else
    151 #define MAYBE_JsFrontendHandlersBasic JsFrontendHandlersBasic
    152 #endif
    153 TEST_F(ProfileSyncServiceTest, MAYBE_JsFrontendHandlersBasic) {
    154   StartSyncService();
    155 
    156   StrictMock<MockJsEventHandler> event_handler;
    157 
    158   SyncBackendHostForProfileSyncTest* test_backend =
    159       service_->GetBackendForTest();
    160 
    161   EXPECT_TRUE(service_->sync_initialized());
    162   ASSERT_TRUE(test_backend != NULL);
    163   ASSERT_TRUE(test_backend->GetJsBackend() != NULL);
    164   EXPECT_EQ(NULL, test_backend->GetJsBackend()->GetParentJsEventRouter());
    165 
    166   JsFrontend* js_backend = service_->GetJsFrontend();
    167   js_backend->AddHandler(&event_handler);
    168   ASSERT_TRUE(test_backend->GetJsBackend() != NULL);
    169   EXPECT_TRUE(test_backend->GetJsBackend()->GetParentJsEventRouter() != NULL);
    170 
    171   js_backend->RemoveHandler(&event_handler);
    172   EXPECT_EQ(NULL, test_backend->GetJsBackend()->GetParentJsEventRouter());
    173 }
    174 
    175 TEST_F(ProfileSyncServiceTest,
    176        JsFrontendHandlersDelayedBackendInitialization) {
    177   StartSyncServiceAndSetInitialSyncEnded(true, false, false, true);
    178 
    179   StrictMock<MockJsEventHandler> event_handler;
    180   EXPECT_CALL(event_handler,
    181               HandleJsEvent("onSyncServiceStateChanged",
    182                             HasArgs(JsArgList()))).Times(AtLeast(3));
    183   // For some reason, these events may or may not fire.
    184   //
    185   // TODO(akalin): Figure out exactly why there's non-determinism
    186   // here, and if possible remove it.
    187   EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
    188       .Times(AtMost(1));
    189   EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
    190       .Times(AtMost(1));
    191   EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
    192       .Times(AtMost(1));
    193 
    194   EXPECT_EQ(NULL, service_->GetBackendForTest());
    195   EXPECT_FALSE(service_->sync_initialized());
    196 
    197   JsFrontend* js_backend = service_->GetJsFrontend();
    198   js_backend->AddHandler(&event_handler);
    199   // Since we're doing synchronous initialization, backend should be
    200   // initialized by this call.
    201   profile_->GetTokenService()->IssueAuthTokenForTest(
    202       GaiaConstants::kSyncService, "token");
    203 
    204   SyncBackendHostForProfileSyncTest* test_backend =
    205       service_->GetBackendForTest();
    206 
    207   EXPECT_TRUE(service_->sync_initialized());
    208   ASSERT_TRUE(test_backend != NULL);
    209   ASSERT_TRUE(test_backend->GetJsBackend() != NULL);
    210   EXPECT_TRUE(test_backend->GetJsBackend()->GetParentJsEventRouter() != NULL);
    211 
    212   js_backend->RemoveHandler(&event_handler);
    213   EXPECT_EQ(NULL, test_backend->GetJsBackend()->GetParentJsEventRouter());
    214 }
    215 
    216 TEST_F(ProfileSyncServiceTest, JsFrontendProcessMessageBasic) {
    217   StartSyncService();
    218 
    219   StrictMock<MockJsEventHandler> event_handler;
    220   // For some reason, these events may or may not fire.
    221   EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
    222       .Times(AtMost(1));
    223   EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
    224       .Times(AtMost(1));
    225   EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
    226       .Times(AtMost(1));
    227 
    228   ListValue arg_list1;
    229   arg_list1.Append(Value::CreateBooleanValue(true));
    230   arg_list1.Append(Value::CreateIntegerValue(5));
    231   JsArgList args1(arg_list1);
    232   EXPECT_CALL(event_handler, HandleJsEvent("testMessage1", HasArgs(args1)));
    233 
    234   ListValue arg_list2;
    235   arg_list2.Append(Value::CreateStringValue("test"));
    236   arg_list2.Append(arg_list1.DeepCopy());
    237   JsArgList args2(arg_list2);
    238   EXPECT_CALL(event_handler,
    239               HandleJsEvent("delayTestMessage2", HasArgs(args2)));
    240 
    241   ListValue arg_list3;
    242   arg_list3.Append(arg_list1.DeepCopy());
    243   arg_list3.Append(arg_list2.DeepCopy());
    244   JsArgList args3(arg_list3);
    245 
    246   JsFrontend* js_backend = service_->GetJsFrontend();
    247 
    248   // Never replied to.
    249   js_backend->ProcessMessage("notRepliedTo", args3, &event_handler);
    250 
    251   // Replied to later.
    252   js_backend->ProcessMessage("delayTestMessage2", args2, &event_handler);
    253 
    254   js_backend->AddHandler(&event_handler);
    255 
    256   // Replied to immediately.
    257   js_backend->ProcessMessage("testMessage1", args1, &event_handler);
    258 
    259   // Fires off reply for delayTestMessage2.
    260   message_loop_.RunAllPending();
    261 
    262   // Never replied to.
    263   js_backend->ProcessMessage("delayNotRepliedTo", args3, &event_handler);
    264 
    265   js_backend->RemoveHandler(&event_handler);
    266 
    267   message_loop_.RunAllPending();
    268 
    269   // Never replied to.
    270   js_backend->ProcessMessage("notRepliedTo", args3, &event_handler);
    271 }
    272 
    273 TEST_F(ProfileSyncServiceTest,
    274        JsFrontendProcessMessageBasicDelayedBackendInitialization) {
    275   StartSyncServiceAndSetInitialSyncEnded(true, false, false, true);
    276 
    277   StrictMock<MockJsEventHandler> event_handler;
    278   // For some reason, these events may or may not fire.
    279   EXPECT_CALL(event_handler, HandleJsEvent("onChangesApplied", _))
    280       .Times(AtMost(1));
    281   EXPECT_CALL(event_handler, HandleJsEvent("onChangesComplete", _))
    282       .Times(AtMost(1));
    283   EXPECT_CALL(event_handler, HandleJsEvent("onSyncNotificationStateChange", _))
    284       .Times(AtMost(1));
    285 
    286   ListValue arg_list1;
    287   arg_list1.Append(Value::CreateBooleanValue(true));
    288   arg_list1.Append(Value::CreateIntegerValue(5));
    289   JsArgList args1(arg_list1);
    290   EXPECT_CALL(event_handler, HandleJsEvent("testMessage1", HasArgs(args1)));
    291 
    292   ListValue arg_list2;
    293   arg_list2.Append(Value::CreateStringValue("test"));
    294   arg_list2.Append(arg_list1.DeepCopy());
    295   JsArgList args2(arg_list2);
    296   EXPECT_CALL(event_handler, HandleJsEvent("testMessage2", HasArgs(args2)));
    297 
    298   ListValue arg_list3;
    299   arg_list3.Append(arg_list1.DeepCopy());
    300   arg_list3.Append(arg_list2.DeepCopy());
    301   JsArgList args3(arg_list3);
    302   EXPECT_CALL(event_handler,
    303               HandleJsEvent("delayTestMessage3", HasArgs(args3)));
    304 
    305   const JsArgList kNoArgs;
    306 
    307   EXPECT_CALL(event_handler, HandleJsEvent("onSyncServiceStateChanged",
    308       HasArgs(kNoArgs))).Times(AtLeast(3));
    309 
    310   JsFrontend* js_backend = service_->GetJsFrontend();
    311 
    312   // We expect a reply for this message, even though its sent before
    313   // |event_handler| is added as a handler.
    314   js_backend->ProcessMessage("testMessage1", args1, &event_handler);
    315 
    316   js_backend->AddHandler(&event_handler);
    317 
    318   js_backend->ProcessMessage("testMessage2", args2, &event_handler);
    319   js_backend->ProcessMessage("delayTestMessage3", args3, &event_handler);
    320 
    321   // Fires testMessage1 and testMessage2.
    322   profile_->GetTokenService()->IssueAuthTokenForTest(
    323       GaiaConstants::kSyncService, "token");
    324 
    325   // Fires delayTestMessage3.
    326   message_loop_.RunAllPending();
    327 
    328   js_backend->ProcessMessage("delayNotRepliedTo", kNoArgs, &event_handler);
    329 
    330   js_backend->RemoveHandler(&event_handler);
    331 
    332   message_loop_.RunAllPending();
    333 
    334   js_backend->ProcessMessage("notRepliedTo", kNoArgs, &event_handler);
    335 }
    336 
    337 // Make sure that things still work if sync is not enabled, but some old sync
    338 // databases are lingering in the "Sync Data" folder.
    339 TEST_F(ProfileSyncServiceTest, TestStartupWithOldSyncData) {
    340   const char* nonsense1 = "reginald";
    341   const char* nonsense2 = "beartato";
    342   const char* nonsense3 = "harrison";
    343   FilePath temp_directory = profile_->GetPath().AppendASCII("Sync Data");
    344   FilePath sync_file1 =
    345       temp_directory.AppendASCII("BookmarkSyncSettings.sqlite3");
    346   FilePath sync_file2 = temp_directory.AppendASCII("SyncData.sqlite3");
    347   FilePath sync_file3 = temp_directory.AppendASCII("nonsense_file");
    348   ASSERT_TRUE(file_util::CreateDirectory(temp_directory));
    349   ASSERT_NE(-1,
    350             file_util::WriteFile(sync_file1, nonsense1, strlen(nonsense1)));
    351   ASSERT_NE(-1,
    352             file_util::WriteFile(sync_file2, nonsense2, strlen(nonsense2)));
    353   ASSERT_NE(-1,
    354             file_util::WriteFile(sync_file3, nonsense3, strlen(nonsense3)));
    355 
    356   StartSyncServiceAndSetInitialSyncEnded(false, false, true, false);
    357   EXPECT_FALSE(service_->HasSyncSetupCompleted());
    358 
    359   // Since we're doing synchronous initialization, backend should be
    360   // initialized by this call.
    361   profile_->GetTokenService()->IssueAuthTokenForTest(
    362       GaiaConstants::kSyncService, "token");
    363 
    364   // Stop the service so we can read the new Sync Data files that were
    365   // created.
    366   service_.reset();
    367 
    368   // This file should have been deleted when the whole directory was nuked.
    369   ASSERT_FALSE(file_util::PathExists(sync_file3));
    370   ASSERT_FALSE(file_util::PathExists(sync_file1));
    371 
    372   // This will still exist, but the text should have changed.
    373   ASSERT_TRUE(file_util::PathExists(sync_file2));
    374   std::string file2text;
    375   ASSERT_TRUE(file_util::ReadFileToString(sync_file2, &file2text));
    376   ASSERT_NE(file2text.compare(nonsense2), 0);
    377 }
    378 
    379 }  // namespace
    380 
    381 }  // namespace browser_sync
    382