OLD | NEW |
(Empty) | |
| 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 "chrome/browser/password_manager/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 "chrome/browser/password_manager/mock_password_store.h" |
| 16 #include "sync/api/sync_change_processor.h" |
| 17 #include "sync/api/sync_error.h" |
| 18 #include "sync/api/sync_error_factory.h" |
| 19 #include "testing/gmock/include/gmock/gmock.h" |
| 20 #include "testing/gtest/include/gtest/gtest.h" |
| 21 |
| 22 using syncer::SyncChange; |
| 23 using syncer::SyncData; |
| 24 using syncer::SyncDataList; |
| 25 using syncer::SyncError; |
| 26 using testing::Invoke; |
| 27 using testing::Return; |
| 28 using testing::SetArgPointee; |
| 29 using testing::_; |
| 30 |
| 31 namespace { |
| 32 |
| 33 typedef std::vector<SyncChange> SyncChangeList; |
| 34 |
| 35 const sync_pb::PasswordSpecificsData& GetPasswordSpecifics( |
| 36 const syncer::SyncData& sync_change) { |
| 37 const sync_pb::EntitySpecifics& specifics = sync_change.GetSpecifics(); |
| 38 return specifics.password().client_only_encrypted_data(); |
| 39 } |
| 40 |
| 41 void PasswordsEqual(const sync_pb::PasswordSpecificsData& expected_password, |
| 42 const sync_pb::PasswordSpecificsData& actual_password) { |
| 43 EXPECT_EQ(expected_password.scheme(), actual_password.scheme()); |
| 44 EXPECT_EQ(expected_password.signon_realm(), actual_password.signon_realm()); |
| 45 EXPECT_EQ(expected_password.origin(), actual_password.origin()); |
| 46 EXPECT_EQ(expected_password.action(), actual_password.action()); |
| 47 EXPECT_EQ(expected_password.username_element(), |
| 48 actual_password.username_element()); |
| 49 EXPECT_EQ(expected_password.password_element(), |
| 50 actual_password.password_element()); |
| 51 EXPECT_EQ(expected_password.username_value(), |
| 52 actual_password.username_value()); |
| 53 EXPECT_EQ(expected_password.password_value(), |
| 54 actual_password.password_value()); |
| 55 EXPECT_EQ(expected_password.ssl_valid(), actual_password.ssl_valid()); |
| 56 EXPECT_EQ(expected_password.preferred(), actual_password.preferred()); |
| 57 EXPECT_EQ(expected_password.date_created(), actual_password.date_created()); |
| 58 EXPECT_EQ(expected_password.blacklisted(), actual_password.blacklisted()); |
| 59 } |
| 60 |
| 61 // Creates a sync data consisting of password specifics. The sign on realm is |
| 62 // set to |signon_realm|. |
| 63 SyncData CreateSyncData(const std::string& signon_realm) { |
| 64 sync_pb::EntitySpecifics password_data; |
| 65 sync_pb::PasswordSpecificsData* password_specifics = |
| 66 password_data.mutable_password()->mutable_client_only_encrypted_data(); |
| 67 password_specifics->set_signon_realm(signon_realm); |
| 68 |
| 69 std::string tag = MakePasswordSyncTag(*password_specifics); |
| 70 return syncer::SyncData::CreateLocalData(tag, tag, password_data); |
| 71 } |
| 72 |
| 73 SyncChange CreateSyncChange(const autofill::PasswordForm& password, |
| 74 SyncChange::SyncChangeType type) { |
| 75 SyncData data = SyncDataFromPassword(password); |
| 76 return SyncChange(FROM_HERE, type, data); |
| 77 } |
| 78 |
| 79 // A testable implementation of the |PasswordSyncableService| that mocks |
| 80 // out all interaction with the password database. |
| 81 class MockPasswordSyncableService : public PasswordSyncableService { |
| 82 public: |
| 83 explicit MockPasswordSyncableService(PasswordStore* password_store) |
| 84 : PasswordSyncableService(password_store) {} |
| 85 virtual ~MockPasswordSyncableService() {} |
| 86 |
| 87 MOCK_METHOD1(NotifyPasswordStoreOfLoginChanges, |
| 88 void (const PasswordStoreChangeList&)); |
| 89 }; |
| 90 |
| 91 // Class to verify the arguments passed to |PasswordStore|. |
| 92 class PasswordStoreDataVerifier { |
| 93 public: |
| 94 PasswordStoreDataVerifier() {} |
| 95 ~PasswordStoreDataVerifier() { |
| 96 EXPECT_TRUE(expected_db_add_changes_.empty()); |
| 97 EXPECT_TRUE(expected_db_update_changes_.empty()); |
| 98 } |
| 99 |
| 100 class TestSyncChangeProcessor; |
| 101 |
| 102 // Sets expected changes to the password database. |
| 103 void SetExpectedDBChanges( |
| 104 const SyncDataList& add_forms, |
| 105 const std::vector<autofill::PasswordForm*>& update_forms, |
| 106 MockPasswordStore* password_store); |
| 107 // Sets expected changes to TestSyncChangeProcessor. |
| 108 void SetExpectedSyncChanges(SyncChangeList list); |
| 109 |
| 110 private: |
| 111 // Checks that |change_list| matches |expected_sync_change_list_|. |
| 112 SyncError TestSyncChanges(const SyncChangeList& change_list); |
| 113 |
| 114 // Verifies that the |password| is present in the |expected_db_add_changes_| |
| 115 // list. If found |password| would be removed from |
| 116 // |expected_db_add_changes_| list. |
| 117 PasswordStoreChangeList VerifyAdd(const autofill::PasswordForm& password) { |
| 118 return VerifyChange(PasswordStoreChange::ADD, password, |
| 119 &expected_db_add_changes_); |
| 120 } |
| 121 |
| 122 // Verifies that the |password| is present in the |
| 123 // |expected_db_update_changes_| list. |
| 124 PasswordStoreChangeList VerifyUpdate(const autofill::PasswordForm& password) { |
| 125 return VerifyChange(PasswordStoreChange::UPDATE, password, |
| 126 &expected_db_update_changes_); |
| 127 } |
| 128 |
| 129 static PasswordStoreChangeList VerifyChange( |
| 130 PasswordStoreChange::Type type, |
| 131 const autofill::PasswordForm& password, |
| 132 std::vector<autofill::PasswordForm>* password_list); |
| 133 |
| 134 std::vector<autofill::PasswordForm> expected_db_add_changes_; |
| 135 std::vector<autofill::PasswordForm> expected_db_update_changes_; |
| 136 SyncChangeList expected_sync_change_list_; |
| 137 |
| 138 DISALLOW_COPY_AND_ASSIGN(PasswordStoreDataVerifier); |
| 139 }; |
| 140 |
| 141 class PasswordStoreDataVerifier::TestSyncChangeProcessor |
| 142 : public syncer::SyncChangeProcessor { |
| 143 public: |
| 144 explicit TestSyncChangeProcessor(PasswordStoreDataVerifier* verifier) |
| 145 : verifier_(verifier) { |
| 146 } |
| 147 virtual ~TestSyncChangeProcessor() {} |
| 148 |
| 149 virtual SyncError ProcessSyncChanges(const tracked_objects::Location&, |
| 150 const SyncChangeList& list) OVERRIDE { |
| 151 return verifier_->TestSyncChanges(list); |
| 152 } |
| 153 |
| 154 virtual SyncDataList GetAllSyncData(syncer::ModelType type) const OVERRIDE { |
| 155 return SyncDataList(); |
| 156 } |
| 157 private: |
| 158 PasswordStoreDataVerifier* verifier_; |
| 159 |
| 160 DISALLOW_COPY_AND_ASSIGN(TestSyncChangeProcessor); |
| 161 }; |
| 162 |
| 163 void PasswordStoreDataVerifier::SetExpectedDBChanges( |
| 164 const SyncDataList& add_forms, |
| 165 const std::vector<autofill::PasswordForm*>& update_forms, |
| 166 MockPasswordStore* password_store) { |
| 167 DCHECK(expected_db_add_changes_.empty()); |
| 168 DCHECK(expected_db_update_changes_.empty()); |
| 169 DCHECK(password_store); |
| 170 |
| 171 for (SyncDataList::const_iterator i = add_forms.begin(); |
| 172 i != add_forms.end(); ++i) { |
| 173 autofill::PasswordForm form; |
| 174 PasswordFromSpecifics(GetPasswordSpecifics(*i), &form); |
| 175 expected_db_add_changes_.push_back(form); |
| 176 } |
| 177 if (expected_db_add_changes_.empty()) { |
| 178 EXPECT_CALL(*password_store, AddLoginImpl(_)).Times(0); |
| 179 } else { |
| 180 EXPECT_CALL(*password_store, AddLoginImpl(_)) |
| 181 .Times(expected_db_add_changes_.size()) |
| 182 .WillRepeatedly(Invoke(this, &PasswordStoreDataVerifier::VerifyAdd)); |
| 183 } |
| 184 |
| 185 for (std::vector<autofill::PasswordForm*>::const_iterator i = |
| 186 update_forms.begin(); |
| 187 i != update_forms.end(); ++i) { |
| 188 expected_db_update_changes_.push_back(**i); |
| 189 } |
| 190 if (expected_db_update_changes_.empty()) { |
| 191 EXPECT_CALL(*password_store, UpdateLoginImpl(_)).Times(0); |
| 192 } else { |
| 193 EXPECT_CALL(*password_store, UpdateLoginImpl(_)) |
| 194 .Times(expected_db_update_changes_.size()) |
| 195 .WillRepeatedly(Invoke(this, &PasswordStoreDataVerifier::VerifyUpdate)); |
| 196 } |
| 197 } |
| 198 |
| 199 void PasswordStoreDataVerifier::SetExpectedSyncChanges(SyncChangeList list) { |
| 200 expected_sync_change_list_.swap(list); |
| 201 } |
| 202 |
| 203 SyncError PasswordStoreDataVerifier::TestSyncChanges( |
| 204 const SyncChangeList& change_list) { |
| 205 for (SyncChangeList::const_iterator it = change_list.begin(); |
| 206 it != change_list.end(); ++it) { |
| 207 const SyncChange& data = *it; |
| 208 const sync_pb::PasswordSpecificsData& actual_password( |
| 209 GetPasswordSpecifics(data.sync_data())); |
| 210 std::string actual_tag = MakePasswordSyncTag(actual_password); |
| 211 |
| 212 bool matched = false; |
| 213 for (SyncChangeList::iterator expected_it = |
| 214 expected_sync_change_list_.begin(); |
| 215 expected_it != expected_sync_change_list_.end(); |
| 216 ++expected_it) { |
| 217 const sync_pb::PasswordSpecificsData& expected_password( |
| 218 GetPasswordSpecifics(expected_it->sync_data())); |
| 219 if (actual_tag == MakePasswordSyncTag(expected_password)) { |
| 220 PasswordsEqual(expected_password, actual_password); |
| 221 EXPECT_EQ(expected_it->change_type(), data.change_type()); |
| 222 matched = true; |
| 223 break; |
| 224 } |
| 225 } |
| 226 EXPECT_TRUE(matched) << actual_tag; |
| 227 } |
| 228 EXPECT_EQ(expected_sync_change_list_.size(), change_list.size()); |
| 229 return SyncError(); |
| 230 } |
| 231 |
| 232 // static |
| 233 PasswordStoreChangeList PasswordStoreDataVerifier::VerifyChange( |
| 234 PasswordStoreChange::Type type, |
| 235 const autofill::PasswordForm& password, |
| 236 std::vector<autofill::PasswordForm>* password_list) { |
| 237 std::vector<autofill::PasswordForm>::iterator it = |
| 238 std::find(password_list->begin(), password_list->end(), password); |
| 239 EXPECT_NE(password_list->end(), it); |
| 240 password_list->erase(it); |
| 241 return PasswordStoreChangeList(1, PasswordStoreChange(type, password)); |
| 242 } |
| 243 |
| 244 class PasswordSyncableServiceTest : public testing::Test { |
| 245 public: |
| 246 PasswordSyncableServiceTest() {} |
| 247 virtual ~PasswordSyncableServiceTest() {} |
| 248 |
| 249 virtual void SetUp() OVERRIDE { |
| 250 password_store_ = new MockPasswordStore; |
| 251 service_.reset(new MockPasswordSyncableService(password_store_)); |
| 252 } |
| 253 |
| 254 virtual void TearDown() OVERRIDE { |
| 255 password_store_->Shutdown(); |
| 256 } |
| 257 |
| 258 PasswordStoreDataVerifier* verifier() { |
| 259 return &verifier_; |
| 260 } |
| 261 |
| 262 scoped_ptr<syncer::SyncChangeProcessor> ReleaseSyncChangeProcessor() { |
| 263 return make_scoped_ptr<syncer::SyncChangeProcessor>( |
| 264 new PasswordStoreDataVerifier::TestSyncChangeProcessor(verifier())); |
| 265 } |
| 266 |
| 267 // Sets the data that will be returned to the caller accessing password store. |
| 268 void SetPasswordStoreData(const std::vector<autofill::PasswordForm*>& forms) { |
| 269 EXPECT_CALL(*password_store_, FillAutofillableLogins(_)) |
| 270 .WillOnce(DoAll(SetArgPointee<0>(forms), Return(true))); |
| 271 } |
| 272 |
| 273 protected: |
| 274 scoped_refptr<MockPasswordStore> password_store_; |
| 275 scoped_ptr<MockPasswordSyncableService> service_; |
| 276 PasswordStoreDataVerifier verifier_; |
| 277 }; |
| 278 |
| 279 |
| 280 // Both sync and password db have data that are not present in the other. |
| 281 TEST_F(PasswordSyncableServiceTest, AdditionsInBoth) { |
| 282 autofill::PasswordForm* form1 = new autofill::PasswordForm; |
| 283 form1->signon_realm = "abc"; |
| 284 std::vector<autofill::PasswordForm*> forms; |
| 285 forms.push_back(form1); |
| 286 SetPasswordStoreData(forms); |
| 287 |
| 288 SyncData sync_data = CreateSyncData("def"); |
| 289 SyncDataList list; |
| 290 list.push_back(sync_data); |
| 291 |
| 292 verifier()->SetExpectedDBChanges(list, |
| 293 std::vector<autofill::PasswordForm*>(), |
| 294 password_store_); |
| 295 verifier()->SetExpectedSyncChanges( |
| 296 SyncChangeList(1, CreateSyncChange(*form1, SyncChange::ACTION_ADD))); |
| 297 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges(_)); |
| 298 |
| 299 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 300 list, |
| 301 ReleaseSyncChangeProcessor(), |
| 302 scoped_ptr<syncer::SyncErrorFactory>()); |
| 303 } |
| 304 |
| 305 // Sync has data that is not present in the password db. |
| 306 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInSync) { |
| 307 SetPasswordStoreData(std::vector<autofill::PasswordForm*>()); |
| 308 |
| 309 SyncData sync_data = CreateSyncData("def"); |
| 310 SyncDataList list; |
| 311 list.push_back(sync_data); |
| 312 |
| 313 verifier()->SetExpectedDBChanges(list, |
| 314 std::vector<autofill::PasswordForm*>(), |
| 315 password_store_); |
| 316 verifier()->SetExpectedSyncChanges(SyncChangeList()); |
| 317 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges(_)); |
| 318 |
| 319 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 320 list, |
| 321 ReleaseSyncChangeProcessor(), |
| 322 scoped_ptr<syncer::SyncErrorFactory>()); |
| 323 } |
| 324 |
| 325 // Passwords db has data that is not present in sync. |
| 326 TEST_F(PasswordSyncableServiceTest, AdditionOnlyInPasswordStore) { |
| 327 autofill::PasswordForm* form1 = new autofill::PasswordForm; |
| 328 form1->signon_realm = "abc"; |
| 329 std::vector<autofill::PasswordForm*> forms; |
| 330 forms.push_back(form1); |
| 331 SetPasswordStoreData(forms); |
| 332 |
| 333 verifier()->SetExpectedDBChanges(SyncDataList(), |
| 334 std::vector<autofill::PasswordForm*>(), |
| 335 password_store_); |
| 336 verifier()->SetExpectedSyncChanges( |
| 337 SyncChangeList(1, CreateSyncChange(*form1, SyncChange::ACTION_ADD))); |
| 338 EXPECT_CALL(*service_, |
| 339 NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList())); |
| 340 |
| 341 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 342 SyncDataList(), |
| 343 ReleaseSyncChangeProcessor(), |
| 344 scoped_ptr<syncer::SyncErrorFactory>()); |
| 345 } |
| 346 |
| 347 // Both passwords db and sync contain the same data. |
| 348 TEST_F(PasswordSyncableServiceTest, BothInSync) { |
| 349 autofill::PasswordForm *form1 = new autofill::PasswordForm; |
| 350 form1->signon_realm = "abc"; |
| 351 std::vector<autofill::PasswordForm*> forms; |
| 352 forms.push_back(form1); |
| 353 SetPasswordStoreData(forms); |
| 354 |
| 355 verifier()->SetExpectedDBChanges(SyncDataList(), |
| 356 std::vector<autofill::PasswordForm*>(), |
| 357 password_store_); |
| 358 verifier()->SetExpectedSyncChanges(SyncChangeList()); |
| 359 EXPECT_CALL(*service_, |
| 360 NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList())); |
| 361 |
| 362 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 363 SyncDataList(1, CreateSyncData("abc")), |
| 364 ReleaseSyncChangeProcessor(), |
| 365 scoped_ptr<syncer::SyncErrorFactory>()); |
| 366 } |
| 367 |
| 368 // Both passwords db and sync have the same data but they need to be merged |
| 369 // as some fields of the data differ. |
| 370 TEST_F(PasswordSyncableServiceTest, Merge) { |
| 371 autofill::PasswordForm *form1 = new autofill::PasswordForm; |
| 372 form1->signon_realm = "abc"; |
| 373 form1->action = GURL("http://pie.com"); |
| 374 form1->date_created = base::Time::Now(); |
| 375 std::vector<autofill::PasswordForm*> forms; |
| 376 forms.push_back(form1); |
| 377 SetPasswordStoreData(forms); |
| 378 |
| 379 verifier()->SetExpectedDBChanges(SyncDataList(), |
| 380 forms, |
| 381 password_store_); |
| 382 verifier()->SetExpectedSyncChanges( |
| 383 SyncChangeList(1, CreateSyncChange(*form1, SyncChange::ACTION_UPDATE))); |
| 384 |
| 385 EXPECT_CALL(*service_, NotifyPasswordStoreOfLoginChanges(_)); |
| 386 |
| 387 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 388 SyncDataList(1, CreateSyncData("abc")), |
| 389 ReleaseSyncChangeProcessor(), |
| 390 scoped_ptr<syncer::SyncErrorFactory>()); |
| 391 } |
| 392 |
| 393 // Initiate sync due to local DB changes. |
| 394 TEST_F(PasswordSyncableServiceTest, PasswordStoreChanges) { |
| 395 // Set the sync change processor first. |
| 396 SetPasswordStoreData(std::vector<autofill::PasswordForm*>()); |
| 397 verifier()->SetExpectedSyncChanges(SyncChangeList()); |
| 398 EXPECT_CALL(*service_, |
| 399 NotifyPasswordStoreOfLoginChanges(PasswordStoreChangeList())); |
| 400 service_->MergeDataAndStartSyncing(syncer::PASSWORDS, |
| 401 SyncDataList(), |
| 402 ReleaseSyncChangeProcessor(), |
| 403 scoped_ptr<syncer::SyncErrorFactory>()); |
| 404 |
| 405 autofill::PasswordForm form1; |
| 406 form1.signon_realm = "abc"; |
| 407 autofill::PasswordForm form2; |
| 408 form2.signon_realm = "def"; |
| 409 autofill::PasswordForm form3; |
| 410 form3.signon_realm = "xyz"; |
| 411 |
| 412 SyncChangeList sync_list; |
| 413 sync_list.push_back(CreateSyncChange(form1, SyncChange::ACTION_ADD)); |
| 414 sync_list.push_back(CreateSyncChange(form2, SyncChange::ACTION_UPDATE)); |
| 415 sync_list.push_back(CreateSyncChange(form3, SyncChange::ACTION_DELETE)); |
| 416 |
| 417 verifier()->SetExpectedDBChanges(SyncDataList(), |
| 418 std::vector<autofill::PasswordForm*>(), |
| 419 password_store_); |
| 420 verifier()->SetExpectedSyncChanges(sync_list); |
| 421 |
| 422 PasswordStoreChangeList list; |
| 423 list.push_back(PasswordStoreChange(PasswordStoreChange::ADD, form1)); |
| 424 list.push_back(PasswordStoreChange(PasswordStoreChange::UPDATE, form2)); |
| 425 list.push_back(PasswordStoreChange(PasswordStoreChange::REMOVE, form3)); |
| 426 service_->ActOnPasswordStoreChanges(list); |
| 427 } |
| 428 |
| 429 } // namespace |
OLD | NEW |