1 // Copyright 2014 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 "components/password_manager/core/browser/password_syncable_service.h"
11 #include "base/basictypes.h"
12 #include "base/location.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "components/password_manager/core/browser/mock_password_store.h"
17 #include "sync/api/sync_change_processor.h"
18 #include "sync/api/sync_error.h"
19 #include "sync/api/sync_error_factory_mock.h"
20 #include "testing/gmock/include/gmock/gmock.h"
21 #include "testing/gtest/include/gtest/gtest.h"
23 using syncer::SyncChange;
24 using syncer::SyncData;
25 using syncer::SyncDataList;
26 using syncer::SyncError;
27 using testing::AnyNumber;
28 using testing::ElementsAre;
29 using testing::Invoke;
30 using testing::IsEmpty;
31 using testing::Matches;
32 using testing::Return;
33 using testing::SetArgPointee;
34 using testing::UnorderedElementsAre;
37 namespace password_manager {
39 // Defined in the implementation file corresponding to this test.
40 syncer::SyncData SyncDataFromPassword(const autofill::PasswordForm& password);
41 autofill::PasswordForm PasswordFromSpecifics(
42 const sync_pb::PasswordSpecificsData& password);
43 std::string MakePasswordSyncTag(const sync_pb::PasswordSpecificsData& password);
44 std::string MakePasswordSyncTag(const autofill::PasswordForm& password);
48 // PasswordForm values for tests.
49 const autofill::PasswordForm::Type kArbitraryType =
50 autofill::PasswordForm::TYPE_GENERATED;
51 const char kAvatarUrl[] = "https://fb.com/Avatar";
52 const char kDisplayName[] = "Agent Smith";
53 const char kFederationUrl[] = "https://fb.com/federation_url";
54 const char kSignonRealm[] = "abc";
55 const char kSignonRealm2[] = "def";
56 const char kSignonRealm3[] = "xyz";
57 const int kTimesUsed = 5;
59 typedef std::vector<SyncChange> SyncChangeList;
61 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics(
62 const syncer::SyncData& sync_data) {
63 return sync_data.GetSpecifics().password().client_only_encrypted_data();
66 MATCHER(HasDateSynced, "") {
67 return !arg.date_synced.is_null() && !arg.date_synced.is_max();
70 MATCHER_P(PasswordIs, form, "") {
71 sync_pb::PasswordSpecificsData actual_password =
72 GetPasswordSpecifics(SyncDataFromPassword(arg));
73 sync_pb::PasswordSpecificsData expected_password =
74 GetPasswordSpecifics(SyncDataFromPassword(form));
75 if (expected_password.scheme() == actual_password.scheme() &&
76 expected_password.signon_realm() == actual_password.signon_realm() &&
77 expected_password.origin() == actual_password.origin() &&
78 expected_password.action() == actual_password.action() &&
79 expected_password.username_element() ==
80 actual_password.username_element() &&
81 expected_password.password_element() ==
82 actual_password.password_element() &&
83 expected_password.username_value() == actual_password.username_value() &&
84 expected_password.password_value() == actual_password.password_value() &&
85 expected_password.ssl_valid() == actual_password.ssl_valid() &&
86 expected_password.preferred() == actual_password.preferred() &&
87 expected_password.date_created() == actual_password.date_created() &&
88 expected_password.blacklisted() == actual_password.blacklisted() &&
89 expected_password.type() == actual_password.type() &&
90 expected_password.times_used() == actual_password.times_used() &&
91 expected_password.display_name() == actual_password.display_name() &&
92 expected_password.avatar_url() == actual_password.avatar_url() &&
93 expected_password.federation_url() == actual_password.federation_url())
96 *result_listener << "Password protobuf does not match; expected:\n"
103 MATCHER_P2(SyncChangeIs, change_type, password, "") {
104 const SyncData& data = arg.sync_data();
105 autofill::PasswordForm form = PasswordFromSpecifics(
106 GetPasswordSpecifics(data));
107 return (arg.change_type() == change_type &&
108 syncer::SyncDataLocal(data).GetTag() ==
109 MakePasswordSyncTag(password) &&
110 (change_type == SyncChange::ACTION_DELETE ||
111 Matches(PasswordIs(password))(form)));
114 // The argument is std::vector<autofill::PasswordForm*>*. The caller is
115 // responsible for the lifetime of all the password forms.
116 ACTION_P(AppendForm, form) {
117 arg0->push_back(new autofill::PasswordForm(form));
121 // Creates a sync data consisting of password specifics. The sign on realm is
122 // set to |signon_realm|.
123 SyncData CreateSyncData(const std::string& signon_realm) {
124 sync_pb::EntitySpecifics password_data;
125 sync_pb::PasswordSpecificsData* password_specifics =
126 password_data.mutable_password()->mutable_client_only_encrypted_data();
127 password_specifics->set_signon_realm(signon_realm);
128 password_specifics->set_type(autofill::PasswordForm::TYPE_GENERATED);
129 password_specifics->set_times_used(3);
130 password_specifics->set_display_name("Mr. X");
131 password_specifics->set_avatar_url("https://accounts.google.com/Avatar");
132 password_specifics->set_federation_url("https://google.com/federation");
134 std::string tag = MakePasswordSyncTag(*password_specifics);
135 return syncer::SyncData::CreateLocalData(tag, tag, password_data);
138 SyncChange CreateSyncChange(const autofill::PasswordForm& password,
139 SyncChange::SyncChangeType type) {
140 SyncData data = SyncDataFromPassword(password);
141 return SyncChange(FROM_HERE, type, data);
144 // A testable implementation of the |PasswordSyncableService| that mocks
145 // out all interaction with the password database.
146 class MockPasswordSyncableService : public PasswordSyncableService {
148 explicit MockPasswordSyncableService(PasswordStoreSync* password_store)
149 : PasswordSyncableService(password_store) {}
151 MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
154 DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService);
157 // Mock implementation of SyncChangeProcessor.
158 class MockSyncChangeProcessor : public syncer::SyncChangeProcessor {
160 MockSyncChangeProcessor() {}
162 MOCK_METHOD2(ProcessSyncChanges,
163 SyncError(const tracked_objects::Location&,
164 const SyncChangeList& list));
165 virtual SyncDataList GetAllSyncData(syncer::ModelType type) const override {
167 return SyncDataList();
171 DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor);
174 // Convenience wrapper around a PasswordSyncableService and PasswordStore
176 class PasswordSyncableServiceWrapper {
178 PasswordSyncableServiceWrapper() {
179 password_store_ = new testing::StrictMock<MockPasswordStore>;
180 service_.reset(new MockPasswordSyncableService(
181 password_store_->GetSyncInterface()));
182 ON_CALL(*password_store_, AddLoginImpl(HasDateSynced()))
183 .WillByDefault(Return(PasswordStoreChangeList()));
184 ON_CALL(*password_store_, RemoveLoginImpl(_))
185 .WillByDefault(Return(PasswordStoreChangeList()));
186 ON_CALL(*password_store_, UpdateLoginImpl(HasDateSynced()))
187 .WillByDefault(Return(PasswordStoreChangeList()));
188 EXPECT_CALL(*password_store(), NotifyLoginsChanged(_)).Times(AnyNumber());
191 ~PasswordSyncableServiceWrapper() {
192 password_store_->Shutdown();
195 MockPasswordStore* password_store() { return password_store_.get(); }
197 MockPasswordSyncableService* service() { return service_.get(); }
199 // Returnes the scoped_ptr to |service_| thus NULLing out it.
200 scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() {
201 return service_.Pass();
205 scoped_refptr<MockPasswordStore> password_store_;
206 scoped_ptr<MockPasswordSyncableService> service_;
208 DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper);
211 class PasswordSyncableServiceTest : public testing::Test {
213 PasswordSyncableServiceTest()
214 : processor_(new testing::StrictMock<MockSyncChangeProcessor>) {
215 ON_CALL(*processor_, ProcessSyncChanges(_, _))
216 .WillByDefault(Return(SyncError()));
218 MockPasswordStore* password_store() { return wrapper_.password_store(); }
219 MockPasswordSyncableService* service() { return wrapper_.service(); }
222 scoped_ptr<MockSyncChangeProcessor> processor_;
225 PasswordSyncableServiceWrapper wrapper_;
229 // Both sync and password db have data that are not present in the other.
230 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) {
231 autofill::PasswordForm form;
232 form.signon_realm = kSignonRealm;
235 list.push_back(CreateSyncData(kSignonRealm2));
236 autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
237 GetPasswordSpecifics(list.back()));
239 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
240 .WillOnce(AppendForm(form));
241 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
242 .WillOnce(Return(true));
243 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
244 EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
245 SyncChangeIs(SyncChange::ACTION_ADD, form))));
247 service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
250 scoped_ptr<syncer::SyncErrorFactory>());
253 // Sync has data that is not present in the password db.
254 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) {
256 list.push_back(CreateSyncData(kSignonRealm));
257 autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
258 GetPasswordSpecifics(list.back()));
260 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
261 .WillOnce(Return(true));
262 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
263 .WillOnce(Return(true));
264 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
265 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
267 service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
270 scoped_ptr<syncer::SyncErrorFactory>());
273 // Passwords db has data that is not present in sync.
274 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) {
275 autofill::PasswordForm form;
276 form.signon_realm = kSignonRealm;
277 form.times_used = kTimesUsed;
278 form.type = kArbitraryType;
279 form.display_name = base::ASCIIToUTF16(kDisplayName);
280 form.avatar_url = GURL(kAvatarUrl);
281 form.federation_url = GURL(kFederationUrl);
282 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
283 .WillOnce(AppendForm(form));
284 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
285 .WillOnce(Return(true));
287 EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
288 SyncChangeIs(SyncChange::ACTION_ADD, form))));
290 service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
293 scoped_ptr<syncer::SyncErrorFactory>());
296 // Both passwords db and sync contain the same data.
297 TEST_F(PasswordSyncableServiceTest, BothInSync) {
298 autofill::PasswordForm form;
299 form.signon_realm = kSignonRealm;
300 form.times_used = kTimesUsed;
301 form.type = kArbitraryType;
302 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
303 .WillOnce(AppendForm(form));
304 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
305 .WillOnce(Return(true));
307 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
309 service()->MergeDataAndStartSyncing(
311 SyncDataList(1, SyncDataFromPassword(form)),
313 scoped_ptr<syncer::SyncErrorFactory>());
316 // Both passwords db and sync have the same data but they need to be merged
317 // as some fields of the data differ.
318 TEST_F(PasswordSyncableServiceTest, Merge) {
319 autofill::PasswordForm form1;
320 form1.signon_realm = kSignonRealm;
321 form1.action = GURL("http://pie.com");
322 form1.date_created = base::Time::Now();
323 form1.preferred = true;
325 autofill::PasswordForm form2(form1);
326 form2.preferred = false;
327 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
328 .WillOnce(AppendForm(form1));
329 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
330 .WillOnce(Return(true));
331 EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(form2)));
332 EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
334 service()->MergeDataAndStartSyncing(
336 SyncDataList(1, SyncDataFromPassword(form2)),
338 scoped_ptr<syncer::SyncErrorFactory>());
341 // Initiate sync due to local DB changes.
342 TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) {
343 // Save the reference to the processor because |processor_| is NULL after
344 // MergeDataAndStartSyncing().
345 MockSyncChangeProcessor& weak_processor = *processor_;
346 EXPECT_CALL(weak_processor, ProcessSyncChanges(_, IsEmpty()));
347 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
348 .WillOnce(Return(true));
349 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
350 .WillOnce(Return(true));
351 service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
354 scoped_ptr<syncer::SyncErrorFactory>());
356 autofill::PasswordForm form1;
357 form1.signon_realm = kSignonRealm;
358 autofill::PasswordForm form2;
359 form2.signon_realm = kSignonRealm2;
360 autofill::PasswordForm form3;
361 form3.signon_realm = kSignonRealm3;
363 SyncChangeList sync_list;
364 sync_list.push_back(CreateSyncChange(form1, SyncChange::ACTION_ADD));
365 sync_list.push_back(CreateSyncChange(form2, SyncChange::ACTION_UPDATE));
366 sync_list.push_back(CreateSyncChange(form3, SyncChange::ACTION_DELETE));
368 PasswordStoreChangeList list;
369 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form1));
370 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form2));
371 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form3));
372 EXPECT_CALL(weak_processor, ProcessSyncChanges(_, ElementsAre(
373 SyncChangeIs(SyncChange::ACTION_ADD, form1),
374 SyncChangeIs(SyncChange::ACTION_UPDATE, form2),
375 SyncChangeIs(SyncChange::ACTION_DELETE, form3))));
376 service()->ActOnPasswordStoreChanges(list);
379 // Process all types of changes from sync.
380 TEST_F(PasswordSyncableServiceTest, ProcessSyncChanges) {
381 autofill::PasswordForm updated_form;
382 updated_form.signon_realm = kSignonRealm;
383 updated_form.action = GURL("http://foo.com");
384 updated_form.date_created = base::Time::Now();
385 autofill::PasswordForm deleted_form;
386 deleted_form.signon_realm = kSignonRealm2;
387 deleted_form.action = GURL("http://bar.com");
388 deleted_form.blacklisted_by_user = true;
390 SyncData add_data = CreateSyncData(kSignonRealm3);
391 autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
392 GetPasswordSpecifics(add_data));
395 list.push_back(SyncChange(FROM_HERE,
396 syncer::SyncChange::ACTION_ADD,
398 list.push_back(CreateSyncChange(updated_form,
399 syncer::SyncChange::ACTION_UPDATE));
400 list.push_back(CreateSyncChange(deleted_form,
401 syncer::SyncChange::ACTION_DELETE));
402 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(new_from_sync)));
403 EXPECT_CALL(*password_store(), UpdateLoginImpl(PasswordIs(updated_form)));
404 EXPECT_CALL(*password_store(), RemoveLoginImpl(PasswordIs(deleted_form)));
405 service()->ProcessSyncChanges(FROM_HERE, list);
408 // Retrives sync data from the model.
409 TEST_F(PasswordSyncableServiceTest, GetAllSyncData) {
410 autofill::PasswordForm form1;
411 form1.signon_realm = kSignonRealm;
412 form1.action = GURL("http://foo.com");
413 form1.times_used = kTimesUsed;
414 form1.type = kArbitraryType;
415 form1.display_name = base::ASCIIToUTF16(kDisplayName);
416 form1.avatar_url = GURL(kAvatarUrl);
417 form1.federation_url = GURL(kFederationUrl);
418 autofill::PasswordForm form2;
419 form2.signon_realm = kSignonRealm2;
420 form2.action = GURL("http://bar.com");
421 form2.blacklisted_by_user = true;
422 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
423 .WillOnce(AppendForm(form1));
424 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
425 .WillOnce(AppendForm(form2));
427 SyncDataList actual_list = service()->GetAllSyncData(syncer::PASSWORDS);
428 std::vector<autofill::PasswordForm> actual_form_list;
429 for (SyncDataList::iterator it = actual_list.begin();
430 it != actual_list.end(); ++it) {
431 actual_form_list.push_back(
432 PasswordFromSpecifics(GetPasswordSpecifics(*it)));
434 EXPECT_THAT(actual_form_list, UnorderedElementsAre(PasswordIs(form1),
438 // Creates 2 PasswordSyncableService instances, merges the content of the first
439 // one to the second one and back.
440 TEST_F(PasswordSyncableServiceTest, MergeDataAndPushBack) {
441 autofill::PasswordForm form1;
442 form1.signon_realm = kSignonRealm;
443 form1.action = GURL("http://foo.com");
445 PasswordSyncableServiceWrapper other_service_wrapper;
446 autofill::PasswordForm form2;
447 form2.signon_realm = kSignonRealm2;
448 form2.action = GURL("http://bar.com");
449 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
450 .WillOnce(AppendForm(form1));
451 EXPECT_CALL(*password_store(), FillBlacklistLogins(_))
452 .WillOnce(Return(true));
453 EXPECT_CALL(*other_service_wrapper.password_store(),
454 FillAutofillableLogins(_)).WillOnce(AppendForm(form2));
455 EXPECT_CALL(*other_service_wrapper.password_store(),
456 FillBlacklistLogins(_)).WillOnce(Return(true));
458 EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(form2)));
459 EXPECT_CALL(*other_service_wrapper.password_store(),
460 AddLoginImpl(PasswordIs(form1)));
462 syncer::SyncDataList other_service_data =
463 other_service_wrapper.service()->GetAllSyncData(syncer::PASSWORDS);
464 service()->MergeDataAndStartSyncing(
467 other_service_wrapper.ReleaseSyncableService(),
468 scoped_ptr<syncer::SyncErrorFactory>());
471 // Calls ActOnPasswordStoreChanges without SyncChangeProcessor. StartSyncFlare
473 TEST_F(PasswordSyncableServiceTest, StartSyncFlare) {
474 autofill::PasswordForm form;
475 form.signon_realm = kSignonRealm;
476 PasswordStoreChangeList list;
477 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
479 // No flare and no SyncChangeProcessor, the call shouldn't crash.
480 service()->ActOnPasswordStoreChanges(list);
482 // Set the flare. It should be called as there is no SyncChangeProcessor.
483 service()->InjectStartSyncFlare(
484 base::Bind(&MockPasswordSyncableService::StartSyncFlare,
485 base::Unretained(service())));
486 EXPECT_CALL(*service(), StartSyncFlare(syncer::PASSWORDS));
487 service()->ActOnPasswordStoreChanges(list);
490 // Start syncing with an error. Subsequent password store updates shouldn't be
491 // propagated to Sync.
492 TEST_F(PasswordSyncableServiceTest, FailedReadFromPasswordStore) {
493 scoped_ptr<syncer::SyncErrorFactoryMock> error_factory(
494 new syncer::SyncErrorFactoryMock);
495 EXPECT_CALL(*password_store(), FillAutofillableLogins(_))
496 .WillOnce(Return(false));
497 EXPECT_CALL(*error_factory, CreateAndUploadError(_, _))
498 .WillOnce(Return(SyncError()));
499 // ActOnPasswordStoreChanges() below shouldn't generate any changes for Sync.
500 // |processor_| will be destroyed in MergeDataAndStartSyncing().
501 EXPECT_CALL(*processor_, ProcessSyncChanges(_, _)).Times(0);
502 service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
503 syncer::SyncDataList(),
505 error_factory.Pass());
507 autofill::PasswordForm form;
508 form.signon_realm = kSignonRealm;
509 PasswordStoreChangeList list;
510 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
511 service()->ActOnPasswordStoreChanges(list);
516 } // namespace password_manager