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