Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(703)

Side by Side Diff: chrome/browser/sync/profile_sync_service_password_unittest.cc

Issue 1851004: Adding sync support for Passwords (Closed)
Patch Set: Ready for checkin Created 10 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/browser/sync/profile_sync_service.cc ('k') | chrome/browser/sync/syncable/syncable.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2010 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 <vector>
6
7 #include "testing/gtest/include/gtest/gtest.h"
8
9 #include "base/task.h"
10 #include "base/time.h"
11 #include "base/utf_string_conversions.h"
12 #include "base/waitable_event.h"
13 #include "chrome/browser/password_manager/password_store.h"
14 #include "chrome/browser/sync/engine/syncapi.h"
15 #include "chrome/browser/sync/glue/password_change_processor.h"
16 #include "chrome/browser/sync/glue/password_data_type_controller.h"
17 #include "chrome/browser/sync/glue/password_model_associator.h"
18 #include "chrome/browser/sync/glue/sync_backend_host_mock.h"
19 #include "chrome/browser/sync/profile_sync_factory.h"
20 #include "chrome/browser/sync/profile_sync_factory_mock.h"
21 #include "chrome/browser/sync/profile_sync_service.h"
22 #include "chrome/browser/sync/profile_sync_test_util.h"
23 #include "chrome/browser/sync/protocol/password_specifics.pb.h"
24 #include "chrome/browser/sync/syncable/directory_manager.h"
25 #include "chrome/browser/sync/syncable/syncable.h"
26 #include "chrome/browser/sync/test_profile_sync_service.h"
27 #include "chrome/common/notification_source.h"
28 #include "chrome/common/notification_type.h"
29 #include "chrome/test/sync/engine/test_id_factory.h"
30 #include "chrome/test/profile_mock.h"
31 #include "testing/gmock/include/gmock/gmock.h"
32 #include "webkit/glue/password_form.h"
33
34 using base::Time;
35 using base::WaitableEvent;
36 using browser_sync::PasswordChangeProcessor;
37 using browser_sync::PasswordDataTypeController;
38 using browser_sync::PasswordModelAssociator;
39 using browser_sync::SyncBackendHostMock;
40 using browser_sync::TestIdFactory;
41 using browser_sync::UnrecoverableErrorHandler;
42 using sync_api::SyncManager;
43 using sync_api::UserShare;
44 using syncable::BASE_VERSION;
45 using syncable::CREATE;
46 using syncable::DirectoryManager;
47 using syncable::ID;
48 using syncable::IS_DEL;
49 using syncable::IS_DIR;
50 using syncable::IS_UNAPPLIED_UPDATE;
51 using syncable::IS_UNSYNCED;
52 using syncable::MutableEntry;
53 using syncable::SERVER_IS_DIR;
54 using syncable::SERVER_VERSION;
55 using syncable::SPECIFICS;
56 using syncable::ScopedDirLookup;
57 using syncable::UNIQUE_SERVER_TAG;
58 using syncable::UNITTEST;
59 using syncable::WriteTransaction;
60 using testing::_;
61 using testing::DoAll;
62 using testing::DoDefault;
63 using testing::ElementsAre;
64 using testing::Eq;
65 using testing::Invoke;
66 using testing::Return;
67 using testing::SaveArg;
68 using testing::SetArgumentPointee;
69 using webkit_glue::PasswordForm;
70
71 ACTION_P3(MakePasswordSyncComponents, service, ps, dtc) {
72 DCHECK(ChromeThread::CurrentlyOn(ChromeThread::DB));
73 PasswordModelAssociator* model_associator =
74 new PasswordModelAssociator(service, ps, dtc);
75 PasswordChangeProcessor* change_processor =
76 new PasswordChangeProcessor(model_associator, ps, dtc);
77 return ProfileSyncFactory::SyncComponents(model_associator,
78 change_processor);
79 }
80
81 class MockPasswordStore : public PasswordStore {
82 public:
83 MOCK_METHOD1(RemoveLogin, void(const PasswordForm&));
84 MOCK_METHOD2(GetLogins, int(const PasswordForm&, PasswordStoreConsumer*));
85 MOCK_METHOD1(AddLogin, void(const PasswordForm&));
86 MOCK_METHOD1(UpdateLogin, void(const PasswordForm&));
87 MOCK_METHOD1(AddLoginImpl, void(const PasswordForm&));
88 MOCK_METHOD1(UpdateLoginImpl, void(const PasswordForm&));
89 MOCK_METHOD1(RemoveLoginImpl, void(const PasswordForm&));
90 MOCK_METHOD2(RemoveLoginsCreatedBetweenImpl, void(const base::Time&,
91 const base::Time&));
92 MOCK_METHOD2(GetLoginsImpl, void(GetLoginsRequest*, const PasswordForm&));
93 MOCK_METHOD1(GetAutofillableLoginsImpl, void(GetLoginsRequest*));
94 MOCK_METHOD1(GetBlacklistLoginsImpl, void(GetLoginsRequest*));
95 MOCK_METHOD1(FillAutofillableLogins,
96 bool(std::vector<PasswordForm*>*));
97 MOCK_METHOD1(FillBlacklistLogins,
98 bool(std::vector<PasswordForm*>*));
99 };
100
101 class ProfileSyncServicePasswordTest : public testing::Test {
102 protected:
103 ProfileSyncServicePasswordTest()
104 : ui_thread_(ChromeThread::UI, &message_loop_),
105 db_thread_(ChromeThread::DB) {
106 }
107
108 virtual void SetUp() {
109 password_store_ = new MockPasswordStore();
110 db_thread_.Start();
111
112 notification_service_ = new ThreadNotificationService(&db_thread_);
113 notification_service_->Init();
114 }
115
116 virtual void TearDown() {
117 service_.reset();
118 notification_service_->TearDown();
119 db_thread_.Stop();
120 MessageLoop::current()->RunAllPending();
121 }
122
123 void StartSyncService(Task* task) {
124 if (!service_.get()) {
125 service_.reset(new TestProfileSyncService(&factory_, &profile_,
126 false, false));
127 service_->AddObserver(&observer_);
128 PasswordDataTypeController* data_type_controller =
129 new PasswordDataTypeController(&factory_,
130 &profile_,
131 service_.get());
132
133 EXPECT_CALL(factory_, CreatePasswordSyncComponents(_, _, _)).
134 WillOnce(MakePasswordSyncComponents(service_.get(),
135 password_store_.get(),
136 data_type_controller));
137 EXPECT_CALL(factory_, CreateDataTypeManager(_, _)).
138 WillOnce(MakeDataTypeManager(&backend_));
139
140 EXPECT_CALL(profile_, GetPasswordStore(_)).
141 WillOnce(Return(password_store_.get()));
142
143 // State changes once for the backend init and once for startup done.
144 EXPECT_CALL(observer_, OnStateChanged()).
145 WillOnce(InvokeTask(task)).
146 WillOnce(Return()).
147 WillOnce(QuitUIMessageLoop());
148 service_->RegisterDataTypeController(data_type_controller);
149 service_->Initialize();
150 MessageLoop::current()->Run();
151 }
152 }
153
154 void CreatePasswordRoot() {
155 UserShare* user_share = service_->backend()->GetUserShareHandle();
156 DirectoryManager* dir_manager = user_share->dir_manager.get();
157
158 ScopedDirLookup dir(dir_manager, user_share->authenticated_name);
159 ASSERT_TRUE(dir.good());
160
161 WriteTransaction wtrans(dir, UNITTEST, __FILE__, __LINE__);
162 MutableEntry node(&wtrans,
163 CREATE,
164 wtrans.root_id(),
165 browser_sync::kPasswordTag);
166 node.Put(UNIQUE_SERVER_TAG, browser_sync::kPasswordTag);
167 node.Put(IS_DIR, true);
168 node.Put(SERVER_IS_DIR, false);
169 node.Put(IS_UNSYNCED, false);
170 node.Put(IS_UNAPPLIED_UPDATE, false);
171 node.Put(SERVER_VERSION, 20);
172 node.Put(BASE_VERSION, 20);
173 node.Put(IS_DEL, false);
174 node.Put(ID, ids_.MakeServer(browser_sync::kPasswordTag));
175 sync_pb::EntitySpecifics specifics;
176 specifics.MutableExtension(sync_pb::password);
177 node.Put(SPECIFICS, specifics);
178 }
179
180 void AddPasswordSyncNode(const PasswordForm& entry) {
181 sync_api::WriteTransaction trans(
182 service_->backend()->GetUserShareHandle());
183 sync_api::ReadNode password_root(&trans);
184 ASSERT_TRUE(password_root.InitByTagLookup(browser_sync::kPasswordTag));
185
186 sync_api::WriteNode node(&trans);
187 std::string tag = PasswordModelAssociator::MakeTag(entry);
188 ASSERT_TRUE(node.InitUniqueByCreation(syncable::PASSWORD,
189 password_root,
190 tag));
191 PasswordModelAssociator::WriteToSyncNode(entry, &node);
192 }
193
194 void GetPasswordEntriesFromSyncDB(std::vector<PasswordForm>* entries) {
195 sync_api::ReadTransaction trans(service_->backend()->GetUserShareHandle());
196 sync_api::ReadNode password_root(&trans);
197 ASSERT_TRUE(password_root.InitByTagLookup(browser_sync::kPasswordTag));
198
199 int64 child_id = password_root.GetFirstChildId();
200 while (child_id != sync_api::kInvalidId) {
201 sync_api::ReadNode child_node(&trans);
202 ASSERT_TRUE(child_node.InitByIdLookup(child_id));
203
204 sync_pb::PasswordSpecificsData password;
205 ASSERT_TRUE(child_node.GetPasswordSpecifics(&password));
206
207 PasswordForm form;
208 PasswordModelAssociator::CopyPassword(password, &form);
209
210 entries->push_back(form);
211
212 child_id = child_node.GetSuccessorId();
213 }
214 }
215
216 bool ComparePasswords(const PasswordForm& lhs, const PasswordForm& rhs) {
217 return lhs.scheme == rhs.scheme &&
218 lhs.signon_realm == rhs.signon_realm &&
219 lhs.origin == rhs.origin &&
220 lhs.action == rhs.action &&
221 lhs.username_element == rhs.username_element &&
222 lhs.username_value == rhs.username_value &&
223 lhs.password_element == rhs.password_element &&
224 lhs.password_value == rhs.password_value &&
225 lhs.ssl_valid == rhs.ssl_valid &&
226 lhs.preferred == rhs.preferred &&
227 lhs.date_created == rhs.date_created &&
228 lhs.blacklisted_by_user == rhs.blacklisted_by_user;
229 }
230
231 void SetIdleChangeProcessorExpectations() {
232 EXPECT_CALL(*(password_store_.get()), AddLoginImpl(_)).Times(0);
233 EXPECT_CALL(*(password_store_.get()), UpdateLoginImpl(_)).Times(0);
234 EXPECT_CALL(*(password_store_.get()), RemoveLoginImpl(_)).Times(0);
235 }
236
237 friend class CreatePasswordRootTask;
238 friend class AddPasswordEntriesTask;
239
240 MessageLoopForUI message_loop_;
241 ChromeThread ui_thread_;
242 ChromeThread db_thread_;
243 scoped_refptr<ThreadNotificationService> notification_service_;
244
245 scoped_ptr<TestProfileSyncService> service_;
246 ProfileMock profile_;
247 ProfileSyncFactoryMock factory_;
248 ProfileSyncServiceObserverMock observer_;
249 SyncBackendHostMock backend_;
250 scoped_refptr<MockPasswordStore> password_store_;
251
252 TestIdFactory ids_;
253 };
254
255 class CreatePasswordRootTask : public Task {
256 public:
257 explicit CreatePasswordRootTask(ProfileSyncServicePasswordTest* test)
258 : test_(test) {
259 }
260
261 virtual void Run() {
262 test_->CreatePasswordRoot();
263 }
264
265 private:
266 ProfileSyncServicePasswordTest* test_;
267 };
268
269 class AddPasswordEntriesTask : public Task {
270 public:
271 AddPasswordEntriesTask(ProfileSyncServicePasswordTest* test,
272 const std::vector<PasswordForm>& entries)
273 : test_(test), entries_(entries) {
274 }
275
276 virtual void Run() {
277 test_->CreatePasswordRoot();
278 for (size_t i = 0; i < entries_.size(); ++i) {
279 test_->AddPasswordSyncNode(entries_[i]);
280 }
281 }
282
283 private:
284 ProfileSyncServicePasswordTest* test_;
285 const std::vector<PasswordForm>& entries_;
286 };
287
288 TEST_F(ProfileSyncServicePasswordTest, FailModelAssociation) {
289 // Backend will be paused but not resumed.
290 EXPECT_CALL(backend_, RequestPause()).
291 WillOnce(testing::DoAll(Notify(NotificationType::SYNC_PAUSED),
292 testing::Return(true)));
293 // Don't create the root password node so startup fails.
294 StartSyncService(NULL);
295 EXPECT_TRUE(service_->unrecoverable_error_detected());
296 }
297
298 TEST_F(ProfileSyncServicePasswordTest, EmptyNativeEmptySync) {
299 EXPECT_CALL(*(password_store_.get()), FillAutofillableLogins(_))
300 .WillOnce(Return(true));
301 EXPECT_CALL(*(password_store_.get()), FillBlacklistLogins(_))
302 .WillOnce(Return(true));
303 SetIdleChangeProcessorExpectations();
304 CreatePasswordRootTask task(this);
305 StartSyncService(&task);
306 std::vector<PasswordForm> sync_entries;
307 GetPasswordEntriesFromSyncDB(&sync_entries);
308 EXPECT_EQ(0U, sync_entries.size());
309 }
310
311 TEST_F(ProfileSyncServicePasswordTest, HasNativeEntriesEmptySync) {
312 std::vector<PasswordForm*> forms;
313 std::vector<PasswordForm> expected_forms;
314 PasswordForm* new_form = new PasswordForm;
315 new_form->scheme = PasswordForm::SCHEME_HTML;
316 new_form->signon_realm = "pie";
317 new_form->origin = GURL("http://pie.com");
318 new_form->action = GURL("http://pie.com/submit");
319 new_form->username_element = UTF8ToUTF16("name");
320 new_form->username_value = UTF8ToUTF16("tom");
321 new_form->password_element = UTF8ToUTF16("cork");
322 new_form->password_value = UTF8ToUTF16("password1");
323 new_form->ssl_valid = true;
324 new_form->preferred = false;
325 new_form->date_created = base::Time::FromInternalValue(1234);
326 new_form->blacklisted_by_user = false;
327 forms.push_back(new_form);
328 expected_forms.push_back(*new_form);
329 EXPECT_CALL(*(password_store_.get()), FillAutofillableLogins(_))
330 .WillOnce(DoAll(SetArgumentPointee<0>(forms), Return(true)));
331 EXPECT_CALL(*(password_store_.get()), FillBlacklistLogins(_))
332 .WillOnce(Return(true));
333 SetIdleChangeProcessorExpectations();
334 CreatePasswordRootTask task(this);
335 StartSyncService(&task);
336 std::vector<PasswordForm> sync_forms;
337 GetPasswordEntriesFromSyncDB(&sync_forms);
338 ASSERT_EQ(1U, sync_forms.size());
339 EXPECT_TRUE(ComparePasswords(expected_forms[0], sync_forms[0]));
340 }
341
342 TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncNoMerge) {
343 std::vector<PasswordForm*> native_forms;
344 std::vector<PasswordForm> sync_forms;
345 std::vector<PasswordForm> expected_forms;
346 {
347 PasswordForm* new_form = new PasswordForm;
348 new_form->scheme = PasswordForm::SCHEME_HTML;
349 new_form->signon_realm = "pie";
350 new_form->origin = GURL("http://pie.com");
351 new_form->action = GURL("http://pie.com/submit");
352 new_form->username_element = UTF8ToUTF16("name");
353 new_form->username_value = UTF8ToUTF16("tom");
354 new_form->password_element = UTF8ToUTF16("cork");
355 new_form->password_value = UTF8ToUTF16("password1");
356 new_form->ssl_valid = true;
357 new_form->preferred = false;
358 new_form->date_created = base::Time::FromInternalValue(1234);
359 new_form->blacklisted_by_user = false;
360
361 native_forms.push_back(new_form);
362 expected_forms.push_back(*new_form);
363 }
364
365 {
366 PasswordForm new_form;
367 new_form.scheme = PasswordForm::SCHEME_HTML;
368 new_form.signon_realm = "pie2";
369 new_form.origin = GURL("http://pie2.com");
370 new_form.action = GURL("http://pie2.com/submit");
371 new_form.username_element = UTF8ToUTF16("name2");
372 new_form.username_value = UTF8ToUTF16("tom2");
373 new_form.password_element = UTF8ToUTF16("cork2");
374 new_form.password_value = UTF8ToUTF16("password12");
375 new_form.ssl_valid = false;
376 new_form.preferred = true;
377 new_form.date_created = base::Time::FromInternalValue(12345);
378 new_form.blacklisted_by_user = false;
379 sync_forms.push_back(new_form);
380 expected_forms.push_back(new_form);
381 }
382
383 EXPECT_CALL(*(password_store_.get()), FillAutofillableLogins(_))
384 .WillOnce(DoAll(SetArgumentPointee<0>(native_forms), Return(true)));
385 EXPECT_CALL(*(password_store_.get()), FillBlacklistLogins(_))
386 .WillOnce(Return(true));
387
388 AddPasswordEntriesTask task(this, sync_forms);
389
390 EXPECT_CALL(*(password_store_.get()), AddLoginImpl(_)).Times(1);
391 StartSyncService(&task);
392
393 std::vector<PasswordForm> new_sync_forms;
394 GetPasswordEntriesFromSyncDB(&new_sync_forms);
395
396 EXPECT_EQ(2U, new_sync_forms.size());
397 EXPECT_TRUE(ComparePasswords(expected_forms[0], new_sync_forms[0]));
398 EXPECT_TRUE(ComparePasswords(expected_forms[1], new_sync_forms[1]));
399 }
400
401 TEST_F(ProfileSyncServicePasswordTest, HasNativeHasSyncMergeEntry) {
402 std::vector<PasswordForm*> native_forms;
403 std::vector<PasswordForm> sync_forms;
404 std::vector<PasswordForm> expected_forms;
405 {
406 PasswordForm* new_form = new PasswordForm;
407 new_form->scheme = PasswordForm::SCHEME_HTML;
408 new_form->signon_realm = "pie";
409 new_form->origin = GURL("http://pie.com");
410 new_form->action = GURL("http://pie.com/submit");
411 new_form->username_element = UTF8ToUTF16("name");
412 new_form->username_value = UTF8ToUTF16("tom");
413 new_form->password_element = UTF8ToUTF16("cork");
414 new_form->password_value = UTF8ToUTF16("password1");
415 new_form->ssl_valid = true;
416 new_form->preferred = false;
417 new_form->date_created = base::Time::FromInternalValue(1234);
418 new_form->blacklisted_by_user = false;
419
420 native_forms.push_back(new_form);
421 }
422
423 {
424 PasswordForm new_form;
425 new_form.scheme = PasswordForm::SCHEME_HTML;
426 new_form.signon_realm = "pie";
427 new_form.origin = GURL("http://pie.com");
428 new_form.action = GURL("http://pie.com/submit");
429 new_form.username_element = UTF8ToUTF16("name2");
430 new_form.username_value = UTF8ToUTF16("tom2");
431 new_form.password_element = UTF8ToUTF16("cork2");
432 new_form.password_value = UTF8ToUTF16("password12");
433 new_form.ssl_valid = false;
434 new_form.preferred = true;
435 new_form.date_created = base::Time::FromInternalValue(12345);
436 new_form.blacklisted_by_user = false;
437 sync_forms.push_back(new_form);
438 }
439
440 {
441 PasswordForm new_form;
442 new_form.scheme = PasswordForm::SCHEME_HTML;
443 new_form.signon_realm = "pie";
444 new_form.origin = GURL("http://pie.com");
445 new_form.action = GURL("http://pie.com/submit");
446 new_form.username_element = UTF8ToUTF16("name2");
447 new_form.username_value = UTF8ToUTF16("tom2");
448 new_form.password_element = UTF8ToUTF16("cork2");
449 new_form.password_value = UTF8ToUTF16("password12");
450 new_form.ssl_valid = false;
451 new_form.preferred = true;
452 new_form.date_created = base::Time::FromInternalValue(12345);
453 new_form.blacklisted_by_user = false;
454 expected_forms.push_back(new_form);
455 }
456
457 EXPECT_CALL(*(password_store_.get()), FillAutofillableLogins(_))
458 .WillOnce(DoAll(SetArgumentPointee<0>(native_forms), Return(true)));
459 EXPECT_CALL(*(password_store_.get()), FillBlacklistLogins(_))
460 .WillOnce(Return(true));
461
462 AddPasswordEntriesTask task(this, sync_forms);
463
464 EXPECT_CALL(*(password_store_.get()), UpdateLoginImpl(_)).Times(1);
465 StartSyncService(&task);
466
467 std::vector<PasswordForm> new_sync_forms;
468 GetPasswordEntriesFromSyncDB(&new_sync_forms);
469
470 EXPECT_EQ(1U, new_sync_forms.size());
471 EXPECT_TRUE(ComparePasswords(expected_forms[0], new_sync_forms[0]));
472 }
OLDNEW
« no previous file with comments | « chrome/browser/sync/profile_sync_service.cc ('k') | chrome/browser/sync/syncable/syncable.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698