• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ProfileSyncServiceTest()40   ProfileSyncServiceTest()
41       : ui_thread_(BrowserThread::UI, &message_loop_) {
42     profile_.reset(new TestingProfile());
43   }
~ProfileSyncServiceTest()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   }
SetUp()52   virtual void SetUp() {
53     profile_->CreateRequestContext();
54   }
TearDown()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 
StartSyncService()67   void StartSyncService() {
68     StartSyncServiceAndSetInitialSyncEnded(true, true, false, true);
69   }
70 
StartSyncServiceAndSetInitialSyncEnded(bool set_initial_sync_ended,bool issue_auth_token,bool synchronous_sync_configuration,bool sync_setup_completed)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 
TEST_F(ProfileSyncServiceTest,InitialState)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 
TEST_F(ProfileSyncServiceTest,DisabledByPolicy)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 
TEST_F(ProfileSyncServiceTest,AbortedByShutdown)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
TEST_F(ProfileSyncServiceTest,MAYBE_JsFrontendHandlersBasic)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 
TEST_F(ProfileSyncServiceTest,JsFrontendHandlersDelayedBackendInitialization)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 
TEST_F(ProfileSyncServiceTest,JsFrontendProcessMessageBasic)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 
TEST_F(ProfileSyncServiceTest,JsFrontendProcessMessageBasicDelayedBackendInitialization)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.
TEST_F(ProfileSyncServiceTest,TestStartupWithOldSyncData)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