Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / components / password_manager / core / browser / password_syncable_service_unittest.cc
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.
4
5 #include "components/password_manager/core/browser/password_syncable_service.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
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"
22
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;
35 using testing::_;
36
37 namespace password_manager {
38
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);
45
46 namespace {
47
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;
58
59 typedef std::vector<SyncChange> SyncChangeList;
60
61 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics(
62     const syncer::SyncData& sync_data) {
63   return sync_data.GetSpecifics().password().client_only_encrypted_data();
64 }
65
66 MATCHER(HasDateSynced, "") {
67   return !arg.date_synced.is_null() && !arg.date_synced.is_max();
68 }
69
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())
94     return true;
95
96   *result_listener << "Password protobuf does not match; expected:\n"
97                    << form << '\n'
98                    << "actual:" << '\n'
99                    << arg;
100   return false;
101 }
102
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)));
112 }
113
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));
118   return true;
119 }
120
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");
133
134   std::string tag = MakePasswordSyncTag(*password_specifics);
135   return syncer::SyncData::CreateLocalData(tag, tag, password_data);
136 }
137
138 SyncChange CreateSyncChange(const autofill::PasswordForm& password,
139                             SyncChange::SyncChangeType type) {
140   SyncData data = SyncDataFromPassword(password);
141   return SyncChange(FROM_HERE, type, data);
142 }
143
144 // A testable implementation of the |PasswordSyncableService| that mocks
145 // out all interaction with the password database.
146 class MockPasswordSyncableService : public PasswordSyncableService {
147  public:
148   explicit MockPasswordSyncableService(PasswordStoreSync* password_store)
149       : PasswordSyncableService(password_store) {}
150
151   MOCK_METHOD1(StartSyncFlare, void(syncer::ModelType));
152
153  private:
154   DISALLOW_COPY_AND_ASSIGN(MockPasswordSyncableService);
155 };
156
157 // Mock implementation of SyncChangeProcessor.
158 class MockSyncChangeProcessor : public syncer::SyncChangeProcessor {
159  public:
160   MockSyncChangeProcessor() {}
161
162   MOCK_METHOD2(ProcessSyncChanges,
163                SyncError(const tracked_objects::Location&,
164                          const SyncChangeList& list));
165   virtual SyncDataList GetAllSyncData(syncer::ModelType type) const override {
166     NOTREACHED();
167     return SyncDataList();
168   }
169
170  private:
171   DISALLOW_COPY_AND_ASSIGN(MockSyncChangeProcessor);
172 };
173
174 // Convenience wrapper around a PasswordSyncableService and PasswordStore
175 // pair.
176 class PasswordSyncableServiceWrapper {
177  public:
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());
189   }
190
191   ~PasswordSyncableServiceWrapper() {
192     password_store_->Shutdown();
193   }
194
195   MockPasswordStore* password_store() { return password_store_.get(); }
196
197   MockPasswordSyncableService* service() { return service_.get(); }
198
199   // Returnes the scoped_ptr to |service_| thus NULLing out it.
200   scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncableService() {
201     return service_.Pass();
202   }
203
204  private:
205   scoped_refptr<MockPasswordStore> password_store_;
206   scoped_ptr<MockPasswordSyncableService> service_;
207
208   DISALLOW_COPY_AND_ASSIGN(PasswordSyncableServiceWrapper);
209 };
210
211 class PasswordSyncableServiceTest : public testing::Test {
212  public:
213   PasswordSyncableServiceTest()
214       : processor_(new testing::StrictMock<MockSyncChangeProcessor>) {
215     ON_CALL(*processor_, ProcessSyncChanges(_, _))
216         .WillByDefault(Return(SyncError()));
217   }
218   MockPasswordStore* password_store() { return wrapper_.password_store(); }
219   MockPasswordSyncableService* service() { return wrapper_.service(); }
220
221  protected:
222   scoped_ptr<MockSyncChangeProcessor> processor_;
223
224  private:
225   PasswordSyncableServiceWrapper wrapper_;
226 };
227
228
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;
233
234   SyncDataList list;
235   list.push_back(CreateSyncData(kSignonRealm2));
236   autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
237       GetPasswordSpecifics(list.back()));
238
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))));
246
247   service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
248                                       list,
249                                       processor_.Pass(),
250                                       scoped_ptr<syncer::SyncErrorFactory>());
251 }
252
253 // Sync has data that is not present in the password db.
254 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) {
255   SyncDataList list;
256   list.push_back(CreateSyncData(kSignonRealm));
257   autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
258       GetPasswordSpecifics(list.back()));
259
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()));
266
267   service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
268                                       list,
269                                       processor_.Pass(),
270                                       scoped_ptr<syncer::SyncErrorFactory>());
271 }
272
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));
286
287   EXPECT_CALL(*processor_, ProcessSyncChanges(_, ElementsAre(
288       SyncChangeIs(SyncChange::ACTION_ADD, form))));
289
290   service()->MergeDataAndStartSyncing(syncer::PASSWORDS,
291                                       SyncDataList(),
292                                       processor_.Pass(),
293                                       scoped_ptr<syncer::SyncErrorFactory>());
294 }
295
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));
306
307   EXPECT_CALL(*processor_, ProcessSyncChanges(_, IsEmpty()));
308
309   service()->MergeDataAndStartSyncing(
310       syncer::PASSWORDS,
311       SyncDataList(1, SyncDataFromPassword(form)),
312       processor_.Pass(),
313       scoped_ptr<syncer::SyncErrorFactory>());
314 }
315
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;
324
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()));
333
334   service()->MergeDataAndStartSyncing(
335       syncer::PASSWORDS,
336       SyncDataList(1, SyncDataFromPassword(form2)),
337       processor_.Pass(),
338       scoped_ptr<syncer::SyncErrorFactory>());
339 }
340
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,
352                                       SyncDataList(),
353                                       processor_.Pass(),
354                                       scoped_ptr<syncer::SyncErrorFactory>());
355
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;
362
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));
367
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);
377 }
378
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;
389
390   SyncData add_data = CreateSyncData(kSignonRealm3);
391   autofill::PasswordForm new_from_sync = PasswordFromSpecifics(
392       GetPasswordSpecifics(add_data));
393
394   SyncChangeList list;
395   list.push_back(SyncChange(FROM_HERE,
396                             syncer::SyncChange::ACTION_ADD,
397                             add_data));
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);
406 }
407
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));
426
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)));
433   }
434   EXPECT_THAT(actual_form_list, UnorderedElementsAre(PasswordIs(form1),
435                                                      PasswordIs(form2)));
436 }
437
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");
444
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));
457
458   EXPECT_CALL(*password_store(), AddLoginImpl(PasswordIs(form2)));
459   EXPECT_CALL(*other_service_wrapper.password_store(),
460               AddLoginImpl(PasswordIs(form1)));
461
462   syncer::SyncDataList other_service_data =
463       other_service_wrapper.service()->GetAllSyncData(syncer::PASSWORDS);
464   service()->MergeDataAndStartSyncing(
465       syncer::PASSWORDS,
466       other_service_data,
467       other_service_wrapper.ReleaseSyncableService(),
468       scoped_ptr<syncer::SyncErrorFactory>());
469 }
470
471 // Calls ActOnPasswordStoreChanges without SyncChangeProcessor. StartSyncFlare
472 // should be called.
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));
478
479   // No flare and no SyncChangeProcessor, the call shouldn't crash.
480   service()->ActOnPasswordStoreChanges(list);
481
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);
488 }
489
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(),
504                                       processor_.Pass(),
505                                       error_factory.Pass());
506
507   autofill::PasswordForm form;
508   form.signon_realm = kSignonRealm;
509   PasswordStoreChangeList list;
510   list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form));
511   service()->ActOnPasswordStoreChanges(list);
512 }
513
514 }  // namespace
515
516 }  // namespace password_manager