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

Side by Side Diff: chrome/browser/password_manager/password_syncable_service_unittest.cc

Issue 27233003: [Sync] Implementation of model association for passwords using sync API (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: For review Created 7 years, 1 month 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 | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
Ilya Sherman 2013/11/19 22:49:04 Ultra nit: Please omit the "(c)".
lipalani1 2013/11/26 23:31:35 Done.
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 <string>
6 #include <vector>
7
8 #include "base/basictypes.h"
9 #include "base/location.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/scoped_vector.h"
13 #include "chrome/browser/password_manager/mock_password_store.h"
14 #include "chrome/browser/password_manager/password_store_factory.h"
15 #include "chrome/browser/password_manager/password_syncable_service.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/test/base/testing_profile.h"
18 #include "sync/api/sync_change_processor.h"
19 #include "sync/api/sync_error.h"
20 #include "sync/api/sync_error_factory.h"
21 #include "sync/protocol/password_specifics.pb.h"
22 #include "sync/protocol/sync.pb.h"
23 #include "testing/gmock/include/gmock/gmock.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25
26 using syncer::SyncChange;
27 using syncer::SyncData;
28 using syncer::SyncDataList;
29 using syncer::SyncError;
30 using testing::Invoke;
31 using testing::Return;
32 using testing::SetArgumentPointee;
33 using testing::_;
34
35 namespace {
Ilya Sherman 2013/11/19 22:49:04 nit: Please leave a blank line after this one.
lipalani1 2013/11/26 23:31:35 Done.
36 typedef std::vector<SyncChange> SyncChangeList;
37
38 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics(
39 const syncer::SyncData& sync_change) {
40 const sync_pb::EntitySpecifics& specifics = sync_change.GetSpecifics();
41 return specifics.password().client_only_encrypted_data();
42 }
43
44 } // namespace
45
46 // This class will be instantiated by the tests rather than the
47 // |PasswordSyncableService| as this class will mock away any calls
48 // that touch the password db.
Ilya Sherman 2013/11/19 22:49:04 nit: Suggested rephrasing: "A testable implementat
lipalani1 2013/11/26 23:31:35 Done.
49 class TestPasswordSyncableService : public PasswordSyncableService {
50 public:
51 explicit TestPasswordSyncableService(PasswordStore* password_store)
52 : PasswordSyncableService(password_store) {}
53 virtual ~TestPasswordSyncableService() {}
54 MOCK_METHOD0(NotifyPasswordStoreOfLoginChanges, void());
55 };
Ilya Sherman 2013/11/19 22:49:04 Rather than mocking out the method from the Passwo
lipalani1 2013/11/26 23:31:35 It is calling private methods in password store. O
56
57 // Concrete implementation of SyncChangeProcessor. The methods will
58 // verify that the |PasswordSyncableService| is calling the
59 // |SyncChangeProcessor| with right arguments.
60 class TestSyncChangeProcessor : public syncer::SyncChangeProcessor {
61 public:
62 TestSyncChangeProcessor() {}
63 virtual ~TestSyncChangeProcessor() {}
64 virtual SyncError ProcessSyncChanges(
65 const tracked_objects::Location& from_here,
66 const SyncChangeList& change_list) {
Ilya Sherman 2013/11/19 22:49:04 nit: OVERRIDE.
lipalani1 2013/11/26 23:31:35 Done.
67 // Loop through the |change_list| and verify they are present in the
Ilya Sherman 2013/11/19 22:49:04 nit: Suggested rephrasing: "Verify that the |chang
lipalani1 2013/11/26 23:31:35 Done.
68 // |expected_changes_| list.
69 for (SyncChangeList::const_iterator it = change_list.begin();
70 it != change_list.end();
71 ++it) {
72 SyncChange data = *it;
Ilya Sherman 2013/11/19 22:49:04 nit: Can this be a const-ref?
lipalani1 2013/11/26 23:31:35 Done.
73 const sync_pb::PasswordSpecificsData& password_specifics(
74 GetPasswordSpecifics(data.sync_data()));
75 std::string actual_tag = PasswordSyncableService::MakeTag(
76 password_specifics);
77
78 bool matched = false;
79 for (SyncChangeList::iterator expected_it = expected_changes_.begin();
80 expected_it != expected_changes_.end();
81 ++expected_it) {
82 SyncChange expected_data = *expected_it;
83 const sync_pb::PasswordSpecificsData& password_specifics(
84 GetPasswordSpecifics(expected_data.sync_data()));
85 std::string expected_tag = PasswordSyncableService::MakeTag(
86 password_specifics);
87 if (expected_tag == actual_tag) {
88 if (data.change_type() == expected_data.change_type()) {
Ilya Sherman 2013/11/19 22:49:04 It looks like this only verifies that the tags and
lipalani1 2013/11/26 23:31:35 Done.
89 matched = true;
90 }
91 break;
92 }
93 }
94 EXPECT_TRUE(matched);
95 }
96 EXPECT_EQ(change_list.size(), expected_changes_.size());
97 return SyncError();
98 }
99
100 virtual SyncDataList GetAllSyncData(syncer::ModelType type) const {
Ilya Sherman 2013/11/19 22:49:04 nit: OVERRIDE.
lipalani1 2013/11/26 23:31:35 Done.
101 return SyncDataList();
102 }
103
104 // Adds a password entry to the |expected_changes_| list.
105 void AddExpectedChange(const autofill::PasswordForm& password,
106 SyncChange::SyncChangeType type) {
107 SyncData data = PasswordSyncableService::CreateSyncData(password);
108 SyncChange change(FROM_HERE, type, data);
109 expected_changes_.push_back(change);
110 }
111
112 private:
113 SyncChangeList expected_changes_;
Ilya Sherman 2013/11/19 22:49:04 What do you think of storing this as a map from ta
lipalani1 2013/11/26 23:31:35 After using the maps in production code I am shyin
114 DISALLOW_COPY_AND_ASSIGN(TestSyncChangeProcessor);
115 };
116
117 // Class to verify the arguments passed to |PasswordStore|.
118 class PasswordStoreDataVerifier {
119 public:
120 PasswordStoreDataVerifier() {}
121 virtual ~PasswordStoreDataVerifier() {}
122
123 // Adds an expected add change.
124 void AddExpectedAddChange(const SyncData& data) {
Ilya Sherman 2013/11/19 22:49:04 This is a strange API. Why doesn't this function
lipalani1 2013/11/26 23:31:35 The callers generally have the element in this for
125 const sync_pb::EntitySpecifics& specifics = data.GetSpecifics();
Ilya Sherman 2013/11/19 22:49:04 nit: Looks like this variable is unused.
lipalani1 2013/11/26 23:31:35 Done.
126 const sync_pb::PasswordSpecificsData& password_specifics(
127 GetPasswordSpecifics(data));
128
129 autofill::PasswordForm form;
130 PasswordSyncableService::ExtractPasswordFromSpecifics(password_specifics,
131 &form);
132 add_changes_.push_back(form);
133 }
134
135 // Adds an expected update change.
136 void AddExpectedUpdateChange(const autofill::PasswordForm& form) {
137 update_changes_.push_back(form);
138 }
139
140 // Verifies that the |password| is present in the |add_changes_| list. If
141 // found |password| would be removed from |add_changes_| list.
142 void VerifyAdd(const autofill::PasswordForm& password) {
143 VerifyChange(password, &add_changes_);
144 }
145
146 // Verifies that the |password| is present in the |update_changes_| list.
147 void VerifyUpdate(const autofill::PasswordForm& password) {
148 VerifyChange(password, &update_changes_);
149 }
150
151 size_t AddChangeCount() const {
152 return add_changes_.size();
153 }
154
155 size_t UpdateChangeCount() const {
156 return update_changes_.size();
157 }
158
159 private:
160 void VerifyChange(const autofill::PasswordForm& password,
Ilya Sherman 2013/11/19 22:49:04 nit: Docs, especially mentioning the side-effect (
lipalani1 2013/11/26 23:31:35 That is an internal detail which the callers need
Ilya Sherman 2013/12/05 07:01:54 This is a private method, so the only callers are
161 std::vector<autofill::PasswordForm>* password_list) {
162 bool matched = false;
163 for (std::vector<autofill::PasswordForm>::iterator it
164 = password_list->begin();
Ilya Sherman 2013/11/19 22:49:04 nit: The equals sign should be on the previous lin
lipalani1 2013/11/26 23:31:35 Done.
165 it != password_list->end();
166 ++it) {
167 if (password == *it) {
168 password_list->erase(it);
169 matched = true;
170 break;
171 }
172 }
173 EXPECT_TRUE(matched);
174 }
175
176 std::vector<autofill::PasswordForm> add_changes_;
177 std::vector<autofill::PasswordForm> update_changes_;
Ilya Sherman 2013/11/19 22:49:04 nit: Please leave a blank line after this one.
Ilya Sherman 2013/11/19 22:49:04 nit: Consider adding an "expected_" prefix to thes
lipalani1 2013/11/26 23:31:35 Done.
lipalani1 2013/11/26 23:31:35 Done.
178 DISALLOW_COPY_AND_ASSIGN(PasswordStoreDataVerifier);
179 };
Ilya Sherman 2013/11/19 22:49:04 nit: These two classes ought to be in the anonymou
lipalani1 2013/11/26 23:31:35 They need to be friended to PasswordStore and Pass
180
181 namespace {
182
183 // Creates a sync data consisting of password specifics. The sign on real is
Ilya Sherman 2013/11/19 22:49:04 nit: real -> realm
lipalani1 2013/11/26 23:31:35 Done.
184 // set to |signon_realm|.
Ilya Sherman 2013/11/19 22:49:04 nit: Suggested rephrasing: Creates a SyncData inst
lipalani1 2013/11/26 23:31:35 Done.
185 SyncData CreateSyncData(const std::string& signon_realm) {
186 sync_pb::EntitySpecifics password_data;
187 sync_pb::PasswordSpecificsData* password_specifics =
188 password_data.mutable_password()->mutable_client_only_encrypted_data();
189 password_specifics->set_signon_realm(signon_realm);
190
191 std::string tag = PasswordSyncableService::MakeTag(*password_specifics);
192 return syncer::SyncData::CreateLocalData(tag, tag, password_data);
193 }
194
195 class PasswordSyncableServiceTest : public testing::Test {
Ilya Sherman 2013/11/19 22:49:04 nit: The test code itself should not be in the ano
lipalani1 2013/11/26 23:31:35 Done.
196 public:
197 PasswordSyncableServiceTest() {}
198 ~PasswordSyncableServiceTest() {}
199
200 virtual void SetUp() OVERRIDE {
201 TestingProfile::Builder builder;
202 scoped_ptr<Profile> profile = builder.Build().Pass();
203 password_store_ = static_cast<MockPasswordStore*>(
204 PasswordStoreFactory::GetInstance()->SetTestingFactoryAndUse(
205 profile.get(), MockPasswordStore::Build).get());
206 service_.reset(new TestPasswordSyncableService(password_store_));
207 sync_change_processor_.reset(new TestSyncChangeProcessor);
208 }
209
210 PasswordStoreDataVerifier* verifier() {
211 return &verifier_;
212 }
213
214 scoped_ptr<TestSyncChangeProcessor> ReleaseSyncChangeProcessor() {
215 return sync_change_processor_.Pass();
216 }
Ilya Sherman 2013/11/19 22:49:04 nit: IMO a better API would be to provide a wrappe
lipalani1 2013/11/26 23:31:35 There will be test cases in future that pass in di
217
218 // Sets the data that will be returned to the caller accessing password store.
219 void SetPasswordStoreData(
220 const std::vector<autofill::PasswordForm*>& forms) {
221 EXPECT_CALL(*(password_store_.get()), FillAutofillableLogins(_))
222 .WillOnce(DoAll(SetArgumentPointee<0>(forms), Return(true)));
223 EXPECT_CALL(*(password_store_.get()), FillBlacklistLogins(_))
224 .WillOnce(Return(true));
225 // TODO(lipalani): Add tests that return data for calls to
226 // FillBlacklistLogins.
Ilya Sherman 2013/11/19 22:49:04 I'm not comfortable with checking in untested prod
lipalani1 2013/11/26 23:31:35 Done.
Ilya Sherman 2013/12/05 07:01:54 Could you please annotate the corresponding produc
227 }
228
229 protected:
230 scoped_refptr<MockPasswordStore> password_store_;
231 scoped_ptr<TestPasswordSyncableService> service_;
232 PasswordStoreDataVerifier verifier_;
233 scoped_ptr<TestSyncChangeProcessor> sync_change_processor_;
234 };
235
236 // Both sync and password db have data that are not present in the other.
237 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) {
238 scoped_ptr<autofill::PasswordForm> form1(new autofill::PasswordForm);
239 form1->signon_realm = "abc";
240
241 sync_change_processor_->AddExpectedChange(*form1,
242 SyncChange::ACTION_ADD);
243
244 std::vector<autofill::PasswordForm*> forms;
245 forms.push_back(form1.release());
246
247 SyncData sync_data = CreateSyncData("def");
248 SyncDataList list;
249 list.push_back(sync_data);
250
251 verifier()->AddExpectedAddChange(sync_data);
252
253 SetPasswordStoreData(forms);
254 EXPECT_CALL(*password_store_, AddLoginImpl(_))
255 .WillRepeatedly(Invoke(
256 verifier(), &PasswordStoreDataVerifier::VerifyAdd));
257
258 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges());
259
260 service_->MergeDataAndStartSyncing(syncer::PASSWORDS,
261 list,
262 ReleaseSyncChangeProcessor(),
263 scoped_ptr<syncer::SyncErrorFactory>());
Ilya Sherman 2013/11/19 22:49:04 nit: Indentation is slightly off.
lipalani1 2013/11/26 23:31:35 Done.
264
265 EXPECT_EQ(0, verifier()->AddChangeCount());
266 }
267
268 // Sync has data that is not present in the password db.
269 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) {
270 std::vector<autofill::PasswordForm*> forms;
271
272 SyncData sync_data = CreateSyncData("def");
273 SyncDataList list;
274 list.push_back(sync_data);
275
276 verifier()->AddExpectedAddChange(sync_data);
277
278 SetPasswordStoreData(forms);
279
280 EXPECT_CALL(*password_store_, AddLoginImpl(_))
281 .WillRepeatedly(Invoke(
282 verifier(), &PasswordStoreDataVerifier::VerifyAdd));
283
284 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges());
285
286 service_->MergeDataAndStartSyncing(syncer::PASSWORDS,
287 list,
288 ReleaseSyncChangeProcessor(),
289 scoped_ptr<syncer::SyncErrorFactory>());
290
291 EXPECT_EQ(0, verifier()->AddChangeCount());
292 }
293
294 // Passwords db has data that is not present in sync.
295 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) {
296 autofill::PasswordForm *form1 = new autofill::PasswordForm;
297 form1->signon_realm = "abc";
298
299 std::vector<autofill::PasswordForm*> forms;
300 forms.push_back(form1);
301
302 SyncDataList list;
303
304 sync_change_processor_->AddExpectedChange(*form1,
305 SyncChange::ACTION_ADD);
306
307 SetPasswordStoreData(forms);
308
309 service_->MergeDataAndStartSyncing(syncer::PASSWORDS,
310 list,
311 ReleaseSyncChangeProcessor(),
312 scoped_ptr<syncer::SyncErrorFactory>());
313
314 EXPECT_EQ(0, verifier()->AddChangeCount());
315 }
316
317 // Both passwords db and sync contain the same data.
318 TEST_F(PasswordSyncableServiceTest, BothInSync) {
319 autofill::PasswordForm *form1 = new autofill::PasswordForm;
320 form1->signon_realm = "abc";
321
322 std::vector<autofill::PasswordForm*> forms;
323 forms.push_back(form1);
324
325 SyncData sync_data = CreateSyncData("abc");
326 SyncDataList list;
327 list.push_back(sync_data);
328
329 SetPasswordStoreData(forms);
330
331 service_->MergeDataAndStartSyncing(syncer::PASSWORDS,
332 list,
333 ReleaseSyncChangeProcessor(),
334 scoped_ptr<syncer::SyncErrorFactory>());
335 EXPECT_EQ(0, verifier()->AddChangeCount());
336 EXPECT_EQ(0, verifier()->UpdateChangeCount());
337 }
338
339 // Both passwords db and sync have the same data but they need to be merged
340 // as some fields of the data differ.
341 TEST_F(PasswordSyncableServiceTest, Merge) {
342 autofill::PasswordForm *form1 = new autofill::PasswordForm;
343 form1->signon_realm = "abc";
344 form1->action = GURL("http://pie.com");
345
346 std::vector<autofill::PasswordForm*> forms;
347 forms.push_back(form1);
348
349 SyncData sync_data = CreateSyncData("abc");
350 SyncDataList list;
351 list.push_back(sync_data);
352
353 sync_change_processor_->AddExpectedChange(*form1,
354 SyncChange::ACTION_UPDATE);
355
356 verifier()->AddExpectedUpdateChange(*form1);
357
358 SetPasswordStoreData(forms);
359
360 EXPECT_CALL(*password_store_, UpdateLoginImpl(_))
361 .WillRepeatedly(Invoke(verifier(),
362 &PasswordStoreDataVerifier::VerifyUpdate));
363
364 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges());
365
366 service_->MergeDataAndStartSyncing(syncer::PASSWORDS,
367 list,
368 ReleaseSyncChangeProcessor(),
369 scoped_ptr<syncer::SyncErrorFactory>());
370
371 EXPECT_EQ(0, verifier()->UpdateChangeCount());
372 }
373
374 } // namespace
Ilya Sherman 2013/11/19 22:49:04 Please include test coverage to make sure that all
lipalani1 2013/11/26 23:31:35 It is verified when mergedataandstartsyncing is ca
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698